基于OpenCV的Android图像处理工具软件。本项目采用Android Studio开发安卓APP,使用C++(OpenCV)编写图片处理算法。主要处理图片的对比度,饱和度以及对图片进行一定程度的清晰度增强。
在网上找了许多关于开发Android APP时引入OpenCV库的资料,大多数都只说了导入OpenCV的SDK。这样确实可以使用OpenCV库,而且使用起来非常简单。但本项目是要使用C++(OpenCV)处理图片,然后Java调用C++(通过JNI)。下文将介绍如何搭建这样的一个开发环境。
三、OpenCV for Android 4.5.0
OpenCV for Android 4.5.0,链接:OpenCV
本项目采用“Android + OpenCV + C++” 的开发模式。传统的“Android + OpenCV”开发模式都是下载OpenCV SDK后直接导入官方已经写好的SDK模块然后调用;这样确实省时省力,而且非常好用。但是这样也存在一些不足,尤其是这种开发模式完全不符合本项目的设计需求。下面给出对比。
OpenCV SDK完全是调用Java接口,图片处理算法不可见。
本项目使用Java和C++分别编写Android app程序和图片处理算法。
OpenCV SDK导入后有1.3GB,整个项目有将近2GB的体积,项目太臃肿。
本项目只移植OpenCV中必要的库文件,整个项目体积减少了1GB。
OpenCV SDK包含非常多的图片处理接口,而且处理算法都很好。而本项目的初衷主要是学习图片处理,如果直接使用SDK,那么就无从知其原理;即使了解其原理,也没有实际经验。因此采用“Android + OpenCV + C++”这种开发模式不仅能够掌握处理图片的原理,还非常符号“理论+实践”的学习理念。
本项目采用“app + module”模块化方案进行开发,传统的“app”模块开发虽然构建速度快,但是项目模块的内聚性降低,耦合性升高,不太符合“高内聚低耦合”的软件设计原则。其中module将其命名为:“opencv450”,表示使用的OpenCV的版本为4.5.0
app模块只需要完成app的UI设计和业务逻辑处理,无需处理其他事物。
在本项目中,opencv450模块主要和底层(C++)互交,然后对其进行封装并对外提供接口。而app模块只需要和该opencv450模块互交即可,无需关心底层算法。
(1)将项目模块化,符合“高内聚低耦合”的软件设计原则。
(2)更新方便,如果以后需要对本项目做改动。例如使用更高版本的OpenCV库,只需要修改module即可;如果要修改app的UI,或者其他的东西,只需要修改app模块即可。
1. 打开Android Studio,点击左上角的“File”,再点击“Setting”。然后依次展开“Appearance & Behavior”、“System setting”,即可看到“Android SDK”。点击该选项,根据需要下载对应的SDK。一般来说现在开发安卓APP都是5.1以上,所以建议把5.1以上的SDK都安装了。步骤如下图所示:
2. 等待下载安装之后先不要重启软件,先把NDK安装了再重启Android Studio。
1. 步骤和安装SDK一样,在安装SDK的界面点击“SDK Tools”。在下面的列表中可看到“NDK”、“CMake”等选项。勾选并下载即可。如下图所示:
2. 下载完成之后Android Studio会自动安装,安装完成重启Android Studio即可。
3. 如果首次打开Android Studio,找不到上图中得选项,可先新建一个项目然后再进行上述操作。
1. File --> New --> New project
2.选择“Empty Activity”就可以了,不要选择“Native C++”。
1. File --> New --> New module
1. 在模块的src/main/目录下新建“cpp”和“jniLibs”文件夹(名字可以自取)。如下图所示:
2. 找到之前下载好的OpenCV目录(OpenCV-android-sdk),进入/sdk/native/jni,把该目录下的“include”文件夹复制到模块的/src/main/jniLibs中。如下图所示:
3. 进入OpenCV目录:sdk/native/staticlibs,复制所有的文件到模块的jniLibs中。这里可以根据需要复制,如果不使用模拟器可以不用复制“x86和x86_64”,本项目没有配置模拟器的环境。
4. 进入OpenCV目录:/sdk/native/3rdparty/libs,该目录下的文件夹和staticlibs中的一样,将各个文件夹下的文件(都是“*.a”的文件)复制到模块中jniLibs中对应的文件夹里面即可。其实有一些库文件本项目用不到,但是为了后期更新方便,所以才复制所有的库文件。
5. 新建cpp和CMakeLists.txt文件,在模块的cpp目录下新建“native-lib.cpp”和“CMakeLists.txt”文件,其中cpp文件名可以任取。如下图:
6. 配置C++的编译脚本:CMakeLists.txt,打开该文件,加入以下代码:
cmake_minimum_required(VERSION 3.10.2)
project("opencv450")
set(libs ${CMAKE_SOURCE_DIR}/..)
include_directories(${libs}/cpp/include)
add_library(libcpufeatures STATIC IMPORTED)
set_target_properties(libcpufeatures PROPERTIES
IMPORTED_LOCATION "${libs}/jniLibs/${ANDROID_ABI}/libcpufeatures.a")
add_library(ittnotify STATIC IMPORTED)
set_target_properties(ittnotify PROPERTIES
INTERFACE_LINK_LIBRARIES "dl"
)
set_target_properties(ittnotify PROPERTIES
IMPORTED_LOCATION "${libs}/jniLibs/${ANDROID_ABI}/libittnotify.a")
add_library(tegra_hal STATIC IMPORTED)
set_target_properties(tegra_hal PROPERTIES
IMPORTED_LOCATION "${libs}/jniLibs/${ANDROID_ABI}/libtegra_hal.a")
add_library(tbb STATIC IMPORTED)
set_target_properties(tbb PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "TBB_USE_GCC_BUILTINS=1;__TBB_GCC_BUILTIN_ATOMICS_PRESENT=1;TBB_SUPPRESS_DEPRECATED_MESSAGES=1"
INTERFACE_LINK_LIBRARIES "c;m;dl"
)
set_target_properties(tbb PROPERTIES
IMPORTED_LOCATION "${libs}/jniLibs/${ANDROID_ABI}/libtbb.a")
add_library(opencv_core STATIC IMPORTED)
set_target_properties(opencv_core PROPERTIES
INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:dl>;\$<LINK_ONLY:m>;\$<LINK_ONLY:log>;\$<LINK_ONLY:tegra_hal>;\$<LINK_ONLY:tbb>;\$<LINK_ONLY:z>;\$<LINK_ONLY:libcpufeatures>;\$<LINK_ONLY:ittnotify>;\$<LINK_ONLY:tegra_hal>"
)
set_target_properties(opencv_core PROPERTIES
IMPORTED_LOCATION "${libs}/jniLibs/${ANDROID_ABI}/libopencv_core.a")
add_library(opencv_imgproc STATIC IMPORTED)
set_target_properties(opencv_imgproc PROPERTIES
INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:opencv_core>;opencv_core;\$<LINK_ONLY:dl>;\$<LINK_ONLY:m>;\$<LINK_ONLY:log>;\$<LINK_ONLY:tegra_hal>"
)
set_target_properties(opencv_imgproc PROPERTIES
IMPORTED_LOCATION "${libs}/jniLibs/${ANDROID_ABI}/libopencv_imgproc.a")
add_library(
native-lib
SHARED
native-lib.cpp)
find_library(
log-lib
log)
target_link_libraries(
native-lib
opencv_core
opencv_imgproc
${log-lib})
需要注意的是,cpp文件名如果不是native-lib,则需要在上述代码中把native-lib改成cpp文件名。这样编译脚本才能找到C++源文件。
双击图中选择的文件,在android --> defaultConfig中添加如下代码:
externalNativeBuild {
cmake {
abiFilters 'armeabi-v7a','arm64-v8a'
cppFlags "-std=c++11","-frtti", "-fexceptions -lz"
}
}
ndk {
abiFilters 'armeabi-v7a','arm64-v8a'
}
// android 节点下
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}
}
// 新建jniLibs存放静态库
sourceSets {
main {
jniLibs.srcDirs = ['src/main/jniLibs', 'lib']
}
}
最后点击右上角的同步,等待项目构建。完成之后在模块中显示jniLibs和cpp了,如上文的“项目结构图”。
JNI是Java Native Interface(Java本地接口)的缩写。JNI作为java和操作系统间的一个直接接口,可以通过JNI使得java直接调用操作系统的资源。目前JNI只能通过c/C++实现,因为jni只是对操作系统资源调用的一个桥接过程。java代码编译之后是运行在一个jvm里,所以java的任何操作对操作系统而言都是隔着一层虚拟机外壳,这点也正式java的优点,帮助java实现了“Write Once, Run Everywhere”的可移植性。但是使用了jni之后必须要明白这个“Write Once, Run Everywhere”要被打破,必须要实现不同的操作系统的各种jni版本。
如下图所示,编写C++代码时,一个方法的命名需要符合下述规则(不考虑注释):
(2)“JNIEXPORT returnType JNICALL”,其中returnType是函数返回类型,在这里是jintArray,即int类型的数组。当然,也可以设为void,即无返回值。
(3)“Java_package_javaClass_function”,package是对应的Java的包名称,javaClass是声明该函数的Java类名,function是函数名。
(4)参数:“JNIEnv *env和jobject”是固定参数,后面的参数才是Java层需要传递的参数。当然,可以不用传参数。
(5)Java需要声明要调用的C++函数,格式为:“public native returnType function(params);”,其中returnType要和C++中对应的function的returnType一致。如下图所示。其中public可以是private或者proected。
完成上述操作后,在Java中调用C++就像普通的调用方法即可。
1. 在计算机中,一张图片可以使用矩阵来记录该图片的数据。OpenCV中使用类“Mat”来保存图片的数据,记录格式也是采用矩阵。该矩阵记录了图片的每一个像素点的各项参数,例如颜色参数。
2. 彩色图片,一张彩色图片的一个像素点有3~4个数值。如果是三通道,则这三个通道分别表示R、G、B(顺序不固定);如果是四通道,除了表示RGB之外还有一个通道表示图片的Alpha的色彩空间,该通道一般用作不透明度参数。
3. 灰度图片,灰度是指当RGB这三个通道相等时的像素点是灰色的。也就是说当把一张图片处理为灰度图时只需要遍历图片的各个像素点,将其RGB通道的值都设为平均数即可。
4. 对比度,图片的对比度是指一幅图像中明暗区域最亮的白和最暗的黑不同亮度层级的测量,对比度中画面黑与白的比值差异范围越大代表对比越大,反之越小。增强对比度的方法有多种,本设计采用“自适应直方图均衡”的方式处理,对比度参考文献 。
5. 饱和度,图片饱和度是指色彩的鲜艳程度,也称色彩的纯度。饱和度取决于该色中含色成分和消色成分(灰色)的比例。含色成分越大,饱和度越大;消色成分越大,饱和度越小。纯的颜色都是高度饱和的,如鲜红,鲜绿。混杂上白色,灰色或其他色调的颜色,是不饱和的颜色,如绛紫,粉红,黄褐等。完全不饱和的颜色根本没有色调,如黑白之间的各种灰色。
如果opencv450模块没有被添加到项目中,可在app模块的gradle的dependencies中加入:
implementation project(path: ':opencv450')
然后再在settting.gradle文件中加入:
1. 先进入gradle下载网页 下载对应的gradle,本项目使用的是gradle-6.5-bin。
2. 下载完成后不要解压,找到gradle存放目录。一般在C盘的Users/账户名称/.gradle目录下。也可以在Android Studio中查看:File --> Settings --> Build,Execution...下的gradle。
3. 将gradle包放到(复制剪切都行)该目录下的wrapper/dists。
4. 修改配置文件“gradle-wrapper.properties”,注释掉原来的(gradle-x.x-bin.zip这一行),然后新增一行:
distributionUrl=file:///D:/.gradle/wrapper/dists/gradle-6.5-bin.zip
这里的路径请根据实际情况修改。如果在Android Studio中找不到该文件,可直接打开项目的gradle/wrapper/文件夹找到该文件,使用记事本修改并保存。
5. 完成之后点击右上角的Sync now即可。如果在Android Studio中找不到“gradle-wrapper.properties”文件。按第四步完成之后在Android Studio中点击下图所示的图标
进入:“C:\Windows\System32\drivers\etc”目录,找到hosts.ics文件,该文件也有可能不带后缀,总之就是hosts文件。点击用记事本打开,加入下列代码,然后保存退出即可。
# GitHub Start
192.30.253.112 Build software better, together
192.30.253.119 gist.github.com
151.101.184.133 assets-cdn.github.com
151.101.184.133 raw.githubusercontent.com
151.101.184.133 gist.githubusercontent.com
151.101.184.133 cloud.githubusercontent.com
151.101.184.133 camo.githubusercontent.com
151.101.184.133 avatars0.githubusercontent.com
151.101.184.133 avatars1.githubusercontent.com
151.101.184.133 avatars2.githubusercontent.com
151.101.184.133 avatars3.githubusercontent.com
151.101.184.133 avatars4.githubusercontent.com
151.101.184.133 avatars5.githubusercontent.com
151.101.184.133 avatars6.githubusercontent.com
151.101.184.133 avatars7.githubusercontent.com
151.101.184.133 avatars8.githubusercontent.com
# GitHub End
如果还是不行,可以下载markdown编辑器阅读本文的。这里推荐一款比较好用的markdown编辑器:Typora 。下载安装完成之后用Typora打开Readme.md文件即可。