您的位置:首页 > 移动开发 > Android开发

Android之OpenCV学习

2017-08-18 14:00 169 查看

OpenCV learn





测试源码

环境:android studio 配置好NDK

* download source from offical 下载的是3.3.0

* 运行sample需要先安装OpenCVManager,这个让人无语,不过从开发者的角度看,他们是 把OpenCV做成一个系统支持框架,这样需要先安装就很好理解了

* 发现OpenCV/sdk/native 里提供的 xx.a的静态库,所以考虑直接使用静态库

直接使用OpenCV 提供的 Java 库

新建一个工程, File > New > New Module, 选择Import Eclipse ADT Project, openCV-android-sdk/sdk/java 下的项目导入到项目里, 自动命名为 openCVLibrary330,

新建 src/main/jniLibs , 复制 OpenCV-android-sdk/sdk/native/libs 下所有文件放在jniLibs下,这样就可以不需要安装OpenCVManager, 直接在java层使用OpenCV

opencvjava 里是openCV提供的demo

使用C++协同开发 Cmake 我是这么一个步骤,有些可以不用这样

新建一个工程,勾选或者不勾选 include C++都可以

在新建一个openCVCode module

新建一个文件夹 比如我的叫 NativeDependLibs 用来存放相关的依赖包

复制 OpenCV-android-sdk/sdk/native/jni 下文件放在NativeDependLibs/OpenCV下

新建 src/main/jniLibs , 复制 OpenCV-android-sdk/sdk/native/libs 下文件放在jniLibs下,我只编译armeabi ,所以只复制了 armeabi

新建一个cpp文件夹 src/main/cpp,并暂时随便新建一个.cpp文件

新建 CMakeLists.txt

编辑文件如下

cmake_minimum_required(VERSION 3.4.1)

#配置加载native依赖

set(libs "${CMAKE_SOURCE_DIR}/src/main/jniLibs")
include_directories(
NativeDependLibs/OpenCV/jni/include
)

#CPP文件夹下带编译的cpp文件

file(GLOB_RECURSE ALL_PROJ_SRC "src/main/cpp/*.cpp" "src/main/cpp/*.h")

add_library( ${ANDROID_BUILD_TARGET}
SHARED
${ALL_PROJ_SRC})

#C++日志

find_library( log-lib
log )

file(GLOB OPENCV
"${libs}/${ANDROID_ABI}/libopencv_java3.so"
)

target_link_libraries(  ${ANDROID_BUILD_TARGET}
android
${log-lib}
${OPENCV}
)


修改build.gradle 支持Cmake编译

android {
compileSdkVersion 25
buildToolsVersion "25.0.2"

defaultConfig {
minSdkVersion 19
targetSdkVersion 25
versionCode 1
versionName "1.0"

externalNativeBuild {
cmake {
arguments "-DANDROID_TOOLCHAIN=gcc",
"-DANDROID_STUDIO=TRUE",
"-DANDROID_BUILD_TARGET=opencv-lib",// lib name
"-DANDROID_ABI=armeabi",
"-DBUILD_TYPE=debug",
"-DFOR_SDK=NO"

cppFlags "-std=c++11 -frtti -fexceptions -w"
abiFilters 'armeabi'//, 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a', 'mips', 'mips64'
}
}

}

externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}

}


build 一下项目,去那个临时的cpp文件里测试一下,可以成功引入OpenCV的库,下面就可以直接在C代码里使用OpenCV

大致文件结构

|---app
|---core(module)
|     |---NativeDependLibs/OpenCV
|---jni/include
|     |___build.gradle
|     |___CMakeLists.txt
|     |___src
|          |___main
|               |___AndroidManifest.xml
|               |___cpp (cpp src)
|               |___java (java src)
|               |___jniLibs (libs)


Face Detection

opencvSample/facedetect/FaceDetectionActivity.java

onResume 里加载OpenCV的库,回调里面加载人脸识别库

在 onCameraFrame 里拿到每一帧Camera Preview 的数据,进行人脸检测,

OpenCV 人脸检测用的是harr或LBP特征,分类算法用的是adaboost算法。这种算法需要提前训练大量的图片,非常耗时,因此opencv已经训练好了,把训练结果存放在一些xml文件里面。练好的文件放在 /sdk/etc 文件夹下,有两个文件夹haarcascades和lbpcascades,前者存放的是harr特征训练出来的文件,后者存放的是lbp特征训练出来的文件,比如

人脸检测主要用到的是CascadeClassifier这个类,以及该类下的detectMultiScale函数

/**
* detectMultiScale(
*                  Mat image, //输入图像,一般为灰度图
*                  MatOfRect objects, //检测到的Rect[],存放所有检测出的人脸,每个人脸是一个矩形
*                  double scaleFactor, //缩放比例,对图片进行缩放,默认为1.1
*                  int minNeighbors, //合并窗口时最小neighbor,每个候选矩阵至少包含的附近元素个数,默认为3
*                  int flags,  //检测标记,只对旧格式的分类器有效,与cvHaarDetectObjects的参数flags相同,在3.0版本中没用处, 默认为0,
*            可能的取值为CV_HAAR_DO_CANNY_PRUNING(CANNY边缘检测)、CV_HAAR_SCALE_IMAGE(缩放图像)、
*            CV_HAAR_FIND_BIGGEST_OBJECT(寻找最大的目标)、CV_HAAR_DO_ROUGH_SEARCH(做粗略搜索);
*            如果寻找最大的目标就不能缩放图像,也不能CANNY边缘检测
*                  Size minSize, //检测出的人脸最小尺寸
*                  Size maxSize //检测出的人脸最大尺寸
*                  )
*/
mJavaDetector.detectMultiScale(mGray, faces, 1.1, 2, 2, // TODO: objdetect.CV_HAAR_SCALE_IMAGE
new Size(mAbsoluteFaceSize, mAbsoluteFaceSize), new Size());


总结起来就是 OpenCV有一个自己的org.opencv.android.JavaCameraView自定义控件,它循环的从摄像头抓取数据,在回调方法中,我们能获取到Mat数据,然后通过调用检测当前是否有人脸,我们会获取到一个MatOfRect 是一个Rect数组,里面会有人脸数据,最后将人脸画在屏幕上

拿到人脸后,绘制人脸框使用的是 Imgproc.rectangle

/**

* Mat类型的图上绘制矩形

* rectangle(Mat img, //图像

*           Point pt1, //矩形的一个顶点

*           Point pt2, //矩形对角线上的另一个顶点

*           Scalar color, //线条颜色 (RGB) 或亮度(灰度图像 )

*           int thickness, //组成矩形的线条的粗细程度。取负值时(如 CV_FILLED)函数绘制填充了色彩的矩形

*           int lineType, //线条的类型

*           int shift //坐标点的小数点位数

*           )

*/


这位小哥的人脸眼睛检测 感觉检测的效果,模拟来说够了,但是实用就不行了

下面是在官方文档中列出的最重要的模块。C层和java层都有相应的库

core:简洁的核心模块,定义了基本的数据结构,包括稠密多维数组 Mat 和其他模块需要的基本函数。

imgproc:图像处理模块,包括线性和非线性图像滤波、几何图像转换 (缩放、仿射与透视变换、一般性基于表的重映射)、颜色空间转换、直方图等等。

video:视频分析模块,包括运动估计、背景消除、物体跟踪算法。

calib3d:包括基本的多视角几何算法、单体和立体相机的标定、对象姿态估计、双目立体匹配算法和元素的三维重建。

features2d:包含了显著特征检测算法、描述算子和算子匹配算法。

objdetect:物体检测和一些预定义的物体的检测 (如人脸、眼睛、杯子、人、汽车等)。

ml:多种机器学习算法,如 K 均值、支持向量机和神经网络。

highgui:一个简单易用的接口,提供视频捕捉、图像和视频编码等功能,还有简单的 UI 接口

… and so on

前后摄像头切换(需要判断是否存在改相机,部分手机只有一个摄像头)

预览画布调整 :

前置:默认是横屏的,需要转成竖屏显示, 前置镜像

后置:默认是横屏的,需要转成竖屏显示

protected Mat rotateMat(Mat src) {

if(mCameraIndex == CAMERA_ID_FRONT) {
// 竖屏需要选择 rotate
/**
* transpose : 矩阵转置
* 矩阵转置是将矩阵的行与列顺序对调(第i行转变为第i列)形成一个新的矩阵
*/
Core.transpose(src, mRgbaT); //转置函数,可以水平的图像变为垂直
Imgproc.resize(mRgbaT,src, src.size(), 0.0D, 0.0D, 0); //将转置后的图像缩放为src的大小
// 左右镜像
/**
* flip(Mat src, //输入矩阵
*      Mat dst, //翻转后矩阵,类型与src一致
*      int flipCode //翻转模式,flipCode==0垂直翻转(沿X轴翻转),flipCode>0水平翻转(沿Y轴翻转),
*                  flipCode<0水平垂直翻转(先沿X轴翻转,再沿Y轴翻转,等价于旋转180°)
*      )
*/
Core.flip(src, src, -1);
} else {
Core.transpose(src, mRgbaT);
Imgproc.resize(mRgbaT,src, src.size(), 0.0D, 0.0D, 0);
Core.flip(src, src, 1);
}
return super.rotateMat(src);
}


不过OpenCV的库,貌似是横屏是识别的,我改成竖屏后检测效果不好,不过横过来,人脸眼睛检测还是不错的

识别

Imgproc.matchTemplate

/**
* 可以用来做 识别
* 模板匹配是一种在图像中定位目标的方法
* 通过把输入图像在实际图像上逐像素点滑动,计算特征相似性,以此来判断当前滑块图像所在位置是目标图像的概率。
* 在目标特征变化不是特别快的情况下,跟踪效果还可以
* matchTemplate(Mat image, 搜索对象图像
*              Mat templ, 模板图像,小于image,并且和image有相同的数据类型
*              Mat result, 比较结果
*              int method 比较算法总共有六种
*              TM_SQDIFF 平方差匹配法:该方法采用平方差来进行匹配;最好的匹配值为0;匹配越差,匹配值越大。
*              TM_CCORR 相关匹配法:该方法采用乘法操作;数值越大表明匹配程度越好。
*              TM_CCOEFF 相关系数匹配法:1表示完美的匹配;-1表示最差的匹配。
*              TM_SQDIFF_NORMED 归一化平方差匹配法
*              TM_CCORR_NORMED 归一化相关匹配法
*              TM_CCOEFF_NORMED 归一化相关系数匹配法
*              )
*/


识别的思路参考眼睛的识别

检测到眼睛,获得眼睛的模板 Mat 见 com.cl.slack.opencv.facedetect.FaceDetectHelperImpl#getTemplate

调用 Imgproc.matchTemplate 识别 见 com.cl.slack.opencv.facedetect.FaceDetectHelperImpl#match_eye

TODO:

1. 这个是横屏可以检测,竖屏是无法检测的

1. 没有嘴巴,鼻子 眉毛,上下嘴唇的检测

1. 检测效率略低

注:可以参考的文章

* Android Studio 2.3利用CMAKE进行OpenCV 3.2的NDK开发

* OpenCV—基本矩阵操作与示例
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: