OpenCV4Android开发实录(2): 使用OpenCV3.3.0库实现人脸检测

转载请声明出处:http://write.blog.csdn.net/postedit/78992490

OpenCV4Android系列:

1. OpenCV4Android开发实录(1):移植OpenCV3.3.0库到Android Studio

2.OpenCV4Android开发实录(2): 使用OpenCV3.3.0库实现人脸检测

上一篇文章OpenCV4Android开发实录(1):移植OpenCV3.3.0库到Android Studio大概介绍了下OpenCV库的基本情况,阐述了将OpenCV库移植到Android Studio项目中的具体步骤。本文将在此文的基础上,通过对OpenCV框架中的人脸检测模块做相应介绍,然后实现人脸检测功能。

一、人脸检测模块移植

1.拷贝opencv-3.3.0-android-sdk\OpenCV-android-sdk\samples\face-detection\jni目录到工程app module的main目录下

2.修改jni目录下的Android.mk

(1) 将

[cpp] view plaincopy
  1. #OPENCV_INSTALL_MODULES:=off
  2. #OPENCV_LIB_TYPE:=SHARED

修改为:

[cpp] view plaincopy
  1. OPENCV_INSTALL_MODULES:=on
  2. OPENCV_LIB_TYPE:=SHARED

其中,OPENCV_INSTALL_MODULES的作用是在打包apk时加载OpenCV的动态库;OPENCV_LIB_TYPE的作用是指定OpenCV库的类型为动态库。

(2) 将

[cpp] view plaincopy
  1. ifdef OPENCV_ANDROID_SDK
  2. ifneq ("","$(wildcard $(OPENCV_ANDROID_SDK)/OpenCV.mk)")
  3. <span style="white-space:pre;">  </span>include ${OPENCV_ANDROID_SDK}/OpenCV.mk
  4. else
  5. <span style="white-space:pre;">  </span>include ${OPENCV_ANDROID_SDK}/sdk/native/jni/OpenCV.mk
  6. endif
  7. <span style="white-space:pre;">    </span>include ../../sdk/native/jni/OpenCV.mk
  8. endif

修改为:

[cpp] view plaincopy
  1. include E:\Environment\opencv-3.3.0-android-sdk\OpenCV-android-sdk\sdk\native\jni\OpenCV.mk

其中,include包含的就是OpenCV SDK中OpenCV.mk文件所存储的绝对路径。最终Android.mk修改效果如下:

3.修改jni目录下Application.mk。由于在导入OpenCV libs时只拷贝了armeabi 、armeabi-v7a、arm64-v8a,因此这里指定编译平台也为上述三个;修改APP_PLaTFORM版本为android-16(可根据自身情况而定),具体如下:

[cpp] view plaincopy
  1. APP_STL := gnustl_static
  2. APP_CPPFLAGS := -frtti –fexceptions
  3. # 指定编译平台
  4. APP_ABI := armeabi armeabi-v7a arm64-v8a
  5. # 指定Android平台
  6. APP_PLATFORM := android-16

4.修改DetectionBasedTracker_jni.h和DetectionBasedTracker_jni.cpp文件,将源文件中所有包含前缀“Java_org_opencv_samples_facedetect_”替换为“Java_com_jiangdg_opencv4android_natives_”,其中com.jiangdg.opencv4android.natives是Java层类DetectionBasedTracker.java所在的包路径,该类包含了人脸检测相关的native方法,否则,在调用自己编译生成的so库时会提示找不到该本地函数错误,以DetectionBasedTracker_jni.h为例:

[cpp] view plaincopy
  1. /* DO NOT EDIT THIS FILE - it is machine generated */
  2. #include <jni.h>
  3. /* Header for class org_opencv_samples_fd_DetectionBasedTracker */
  4. #ifndef _Included_org_opencv_samples_fd_DetectionBasedTracker
  5. #define _Included_org_opencv_samples_fd_DetectionBasedTracker
  6. #ifdef __cplusplus
  7. extern "C" {
  8. #endif
  9. /*
  10. * Class:     org_opencv_samples_fd_DetectionBasedTracker
  11. * Method:    nativeCreateObject
  12. * Signature: (Ljava/lang/String;F)J
  13. */
  14. JNIEXPORT jlong JNICALL Java_com_jiangdg_opencv4android_natives_DetectionBasedTracker_nativeCreateObject
  15. (JNIEnv *, jclass, jstring, jint);
  16. /*
  17. * Class:     org_opencv_samples_fd_DetectionBasedTracker
  18. * Method:    nativeDestroyObject
  19. * Signature: (J)V
  20. */
  21. JNIEXPORT void JNICALL Java_com_jiangdg_opencv4android_natives_DetectionBasedTracker_nativeDestroyObject
  22. (JNIEnv *, jclass, jlong);
  23. /*
  24. * Class:     org_opencv_samples_fd_DetectionBasedTracker
  25. * Method:    nativeStart
  26. * Signature: (J)V
  27. */
  28. JNIEXPORT void JNICALL Java_com_jiangdg_opencv4android_natives_DetectionBasedTracker_nativeStart
  29. (JNIEnv *, jclass, jlong);
  30. /*
  31. * Class:     org_opencv_samples_fd_DetectionBasedTracker
  32. * Method:    nativeStop
  33. * Signature: (J)V
  34. */
  35. JNIEXPORT void JNICALL Java_com_jiangdg_opencv4android_natives_DetectionBasedTracker_nativeStop
  36. (JNIEnv *, jclass, jlong);
  37. /*
  38. * Class:     org_opencv_samples_fd_DetectionBasedTracker
  39. * Method:    nativeSetFaceSize
  40. * Signature: (JI)V
  41. */
  42. JNIEXPORT void JNICALL Java_com_jiangdg_opencv4android_natives_DetectionBasedTracker_nativeSetFaceSize
  43. (JNIEnv *, jclass, jlong, jint);
  44. /*
  45. * Class:     org_opencv_samples_fd_DetectionBasedTracker
  46. * Method:    nativeDetect
  47. * Signature: (JJJ)V
  48. */
  49. JNIEXPORT void JNICALL Java_com_jiangdg_opencv4android_natives_DetectionBasedTracker_nativeDetect
  50. (JNIEnv *, jclass, jlong, jlong, jlong);
  51. #ifdef __cplusplus
  52. }
  53. #endif
  54. #endif

5.打开Android Studio中的Terminal窗口,使用cd命令切换到工程jni目录所在位置,并执行ndk-build命令,然后会自动在工程的app/src/main目录下生成libs和obj目录,其中libs目录存放的是目标动态库libdetection_based_tracker.so。

生成so库:

注意:如果执行ndk-build命令提示命令不存在,说明你的ndk环境变量没有配置好。

6.修改app模块build.gradle中的sourceSets字段,禁止自动调用ndk-build命令,设置目标so的存放路径,代码如下:

[java] view plaincopy
  1. android {
  2. compileSdkVersion 25
  3. defaultConfig {
  4. applicationId "com.jiangdg.opencv4android"
  5. minSdkVersion 15
  6. targetSdkVersion 25
  7. versionCode 1
  8. versionName "1.0"
  9. }
  10. ….// 代码省略
  11. sourceSets {
  12. main {
  13. jni.srcDirs = []                 //禁止自动调用ndk-build命令
  14. jniLibs.srcDir 'src/main/libs'  // 设置目标的so存放路径
  15. }
  16. }
  17. ….// 代码省略
  18. }

其中,jni.srcDirs = []的作用是禁用gradle默认的ndk-build,防止AS自己生成android.mk编译jni工程,jniLibs.srcDir 'src/main/libs'的作用设置目标的so存放路径,以将自己生成的so组装到apk中。

二、源码解析
使用OpenCV3.3.0库实现人脸检测功能主要包含以下四个步骤,即:
(1) 初始化加载OpenCV库引擎;
(2) 通过OpenCV库开启Camera渲染;
(3) 加载人脸检测模型;
(4) 调用人脸检测本地动态库实现人脸识别;
1.初始化加载OpenCV库引擎
OpenCV库的加载有两种方式,一种通过OpenCV Manager进行动态加载,也就是官方推荐的方式,这种方式需要另外安装OpenCV Manager,主要通过调用OpenCVLoader.initAsync()方法进行初始化;另一种为静态加载,也就是本文所使用的方法,即先将相关架构的so包拷贝到工程的libs目录,通过调用OpenCVLoader.initDebug()方法进行初始化,类似于调用system.loadLibrary("opencv_java")。

[java] view plaincopy
  1. if (!OpenCVLoader.initDebug()) {
  2. // 静态加载OpenCV失败,使用OpenCV Manager初始化
  3. // 参数:OpenCV版本;上下文;加载结果回调接口
  4. OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_3_0,
  5. this, mLoaderCallback);
  6. } else {
  7. // 如果静态加载成功,直接调用onManagerConnected方法
  8. mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
  9. }

其中,mLoaderCallback为OpenCV库初始化状态回调接口,当OpenCV被初始化成功后其onManagerConnected(int status)方法会被调用,而我们就可以在该方法中处理本地动态库的加载、加载人脸检测模型文件、初始化人脸检测引擎以及开启Camera渲染等操作,具体代码如下:

[java] view plaincopy
  1. private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
  2. @Override
  3. public void onManagerConnected(int status) {
  4. switch (status) {
  5. case LoaderCallbackInterface.SUCCESS:
  6. // OpenCV初始化加载成功,再加载本地so库
  7. System.loadLibrary("detection_based_tracker");
  8. // 加载人脸检测模型
  9. …..
  10. // 初始化人脸检测引擎
  11. …..
  12. // 开启渲染Camera
  13. mCameraView.enableView();
  14. break;
  15. default:
  16. super.onManagerConnected(status);
  17. break;
  18. }
  19. }
  20. };

2. 通过OpenCV库开启Camera渲染
在OpenCV中与Camera紧密相关的主要有两个类,即CameraBridgeViewBase和JavaCameraView,其中,CameraBridgeViewBase是一个基类,继承于SuarfaceView和SurafaceHolder.Callback接口,用于实现Camera与OpenCV库之间的交互,它主要的作用是控制Camera、处理视频帧以及调用相关内部接口对视频帧做相关调整,然后将调整后的视频帧数据渲染到手机屏幕上。比如enableView()方法、disableView()方法用于连接到Camera和断开与Camera的连接,代码如下:

[java] view plaincopy
  1. public void enableView() {
  2. synchronized(mSyncObject) {
  3. mEnabled = true;
  4. checkCurrentState();
  5. }
  6. public void disableView() {
  7. synchronized(mSyncObject) {
  8. mEnabled = false;
  9. checkCurrentState();
  10. }

其中,checkCurrentState()方法用于更新Camera的渲染状态,它调用了processEnterState()方法来启动或停用Camera,以及将Camera的状态对外回调。为了方便开发者实时获取Camera的连接状态,CameraBridgeViewBase还提供了一个setCvCameraViewListener(CvCameraViewListener2 listener)方法,参数listener其一个内部接口,它包括三个方法:onCameraViewStarted(int width, int height)、void onCameraViewStopped()、Mat onCameraFrame(CvCameraViewFrame inputFrame),分别用于对外回调Camera连接状态和传递Camera的实时视频帧数据。

[java] view plaincopy
  1. private void checkCurrentState() {
  2. Log.d(TAG, "call checkCurrentState");
  3. int targetState;
  4. if (mEnabled && mSurfaceExist && getVisibility() == VISIBLE) {
  5. targetState = STARTED;
  6. } else {
  7. targetState = STOPPED;
  8. }
  9. if (targetState != mState) {
  10. /* The state change detected. Need to exit the current state and enter target state */
  11. processExitState(mState);
  12. mState = targetState;
  13. processEnterState(mState);
  14. }
  15. }
  16. private void processEnterState(int state) {
  17. Log.d(TAG, "call processEnterState: " + state);
  18. switch(state) {
  19. case STARTED:
  20. // 调用connectCamera()抽象方法,启动Camera
  21. onEnterStartedState();
  22. // 调用连接成功监听器接口方法
  23. if (mListener != null) {
  24. mListener.onCameraViewStarted(mFrameWidth, mFrameHeight);
  25. }
  26. break;
  27. case STOPPED:
  28. // 调用disconnectCamera()抽象方法,停用Camera
  29. onEnterStoppedState();
  30. // 调用断开连接监听器接口方法
  31. if (mListener != null) {
  32. mListener.onCameraViewStopped();
  33. }
  34. break;
  35. };

既然CameraBridgeViewBase是一个基类,与Camera紧密相关的connectCamera()和disconnectCamera()又是抽象方法,那么就必定会有一个子类来实现这两个方法,而这个子类就是JavaCameraView。JavaCameraView继承于CameraBridgeViewBase和PreviewCallback接口,是衔接OpenCV和Camera的桥梁,是Camera启动、禁止的实际实现者,在这个类里我们可以看到关于Camera很多熟悉的操作。源码如下:

[java] view plaincopy
  1. @Override
  2. protected boolean connectCamera(int width, int height) {
  3. // 初始化Camera,连接到Camera
  4. if (!initializeCamera(width, height))
  5. return false;
  6. mCameraFrameReady = false;
  7. // 开启一个与Camera相关的工作线程CameraWorker
  8. Log.d(TAG, "Starting processing thread");
  9. mStopThread = false;
  10. mThread = new Thread(new CameraWorker());
  11. mThread.start();
  12. return true;
  13. }
  14. @Override
  15. protected void disconnectCamera() {
  16. // 断开Camera连接,释放相关资源
  17. try {
  18. mStopThread = true;
  19. Log.d(TAG, "Notify thread");
  20. synchronized (this) {
  21. this.notify();
  22. }
  23. // 停止工作线程
  24. if (mThread != null)
  25. mThread.join();
  26. } catch (InterruptedException e) {
  27. e.printStackTrace();
  28. } finally {
  29. mThread =  null;
  30. }
  31. /* Now release camera */
  32. releaseCamera();
  33. mCameraFrameReady = false;

CameraWorker是一个工作线程,用于处理从onPreviewFrame获得的视频帧数据,其存储在一个Mat类型的数组中。它会不断调用父类CameraBridgeViewBase的deliverAndDrawFrame方法,将处理后的视频帧数据流通过调用内部接口CvCameraViewListener2的onCameraFrame(CvCameraViewFrame frame)对外回调。

[java] view plaincopy
  1. private class CameraWorker implements Runnable {
  2. @Override
  3. public void run() {
  4. do {
  5. …..//代码省略
  6. if (!mStopThread && hasFrame) {
  7. if (!mFrameChain[1 - mChainIdx].empty())
  8. deliverAndDrawFrame(mCameraFrame[1 - mChainIdx]);
  9. }
  10. } while (!mStopThread);
  11. }
  12. }

3. 加载人脸检测模型
    为了得到更好的人脸检测性能,OpenCV在SDK中提供了多个frontface检测器(人脸模型),存放在..\opencv-3.3.0-android-sdk\OpenCV-android-sdk\sdk\etc\目录下,这篇对OpenCV自带的人脸检测模型做了比较,结果显示LBP实时性要好些。因此,本文选用目lbpcascades录下lbpcascade_frontalface.xml模型,该模型包括了3000个正样本和1500个负样本,我们将其拷贝到AS工程的res/raw目录下,并通过getDir方法保存到/data/data/com.jiangdg.opencv4android/ cascade目录下。

[java] view plaincopy
  1. InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
  2. File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
  3. mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
  4. FileOutputStream os = new FileOutputStream(mCascadeFile);
  5. byte[] buffer = new byte[4096];
  6. int byteesRead;
  7. while ((byteesRead = is.read(buffer)) != -1) {
  8. os.write(buffer, 0, byteesRead);
  9. }
  10. is.close();
  11. os.close();

注:关于模型的训练在以后的博文中会讨论到。
4. 人脸检测
在opencv-3.3.0-android-sdk的face-detection示例项目中,提供了CascadeClassifier和
DetectionBasedTracker两种方式来实现人脸检测,其中,CascadeClassifier是OpenCV用于人脸检测的一个级联分类器,DetectionBasedTracker是通过JNI编程实现的人脸检测。两种方式我都试用了下,发现DetectionBasedTracker方式还是比CascadeClassifier稳定些,CascadeClassifier会存在一定频率的误检。

[java] view plaincopy
  1. public class DetectionBasedTracker {
  2. private long mNativeObj = 0;
  3. // 构造方法:初始化人脸检测引擎
  4. public DetectionBasedTracker(String cascadeName, int minFaceSize) {
  5. mNativeObj = nativeCreateObject(cascadeName, minFaceSize);
  6. }
  7. // 开始人脸检测
  8. public void start() {
  9. nativeStart(mNativeObj);
  10. }
  11. // 停止人脸检测
  12. public void stop() {
  13. nativeStop(mNativeObj);
  14. }
  15. // 设置人脸最小尺寸
  16. public void setMinFaceSize(int size) {
  17. nativeSetFaceSize(mNativeObj, size);
  18. }
  19. // 检测
  20. public void detect(Mat imageGray, MatOfRect faces) {
  21. nativeDetect(mNativeObj, imageGray.getNativeObjAddr(), faces.getNativeObjAddr());
  22. }
  23. // 释放资源
  24. public void release() {
  25. nativeDestroyObject(mNativeObj);
  26. mNativeObj = 0;
  27. }
  28. // native方法
  29. private static native long nativeCreateObject(String cascadeName, int minFaceSize);
  30. private static native void nativeDestroyObject(long thiz);
  31. private static native void nativeStart(long thiz);
  32. private static native void nativeStop(long thiz);
  33. private static native void nativeSetFaceSize(long thiz, int size);
  34. private static native void nativeDetect(long thiz, long inputImage, long faces);
  35. }

初始化DetectionBasedTracker后,我们只需要在CvCameraViewListener2接口的onCameraFrame方法中对每帧图片进行人脸检测即可。

[java] view plaincopy
  1. @Override
  2. public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
  3. ….// 代码省略
  4. // 获取检测到的脸部数据
  5. MatOfRect faces = new MatOfRect();
  6. …// 代码省略
  7. if (mNativeDetector != null) {
  8. mNativeDetector.detect(mGray, faces);
  9. }
  10. // 绘制检测框
  11. Rect[] facesArray = faces.toArray();
  12. for (int i = 0; i < facesArray.length; i++) {
  13. Imgproc.rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);
  14. }
  15. return mRgba;
  16. }

注:由于篇幅原因,关于人脸检测的C/C++实现代码(原理),我们将在后续文章中讨论。

三、效果演示

1. FaceDetectActivity.class

[java] view plaincopy
  1. /**
  2. * 人脸检测
  3. *
  4. * Created by jiangdongguo on 2018/1/4.
  5. */
  6. public class FaceDetectActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2 {
  7. private static final int JAVA_DETECTOR = 0;
  8. private static final int NATIVE_DETECTOR = 1;
  9. private static final String TAG = "FaceDetectActivity";
  10. @BindView(R.id.cameraView_face)
  11. CameraBridgeViewBase mCameraView;
  12. private Mat mGray;
  13. private Mat mRgba;
  14. private int mDetectorType = NATIVE_DETECTOR;
  15. private int mAbsoluteFaceSize = 0;
  16. private float mRelativeFaceSize = 0.2f;
  17. private DetectionBasedTracker mNativeDetector;
  18. private CascadeClassifier mJavaDetector;
  19. private static final Scalar FACE_RECT_COLOR = new Scalar(0, 255, 0, 255);
  20. private File mCascadeFile;
  21. private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
  22. @Override
  23. public void onManagerConnected(int status) {
  24. switch (status) {
  25. case LoaderCallbackInterface.SUCCESS:
  26. // OpenCV初始化加载成功,再加载本地so库
  27. System.loadLibrary("detection_based_tracker");
  28. try {
  29. // 加载人脸检测模式文件
  30. InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
  31. File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
  32. mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
  33. FileOutputStream os = new FileOutputStream(mCascadeFile);
  34. byte[] buffer = new byte[4096];
  35. int byteesRead;
  36. while ((byteesRead = is.read(buffer)) != -1) {
  37. os.write(buffer, 0, byteesRead);
  38. }
  39. is.close();
  40. os.close();
  41. // 使用模型文件初始化人脸检测引擎
  42. mJavaDetector = new CascadeClassifier(mCascadeFile.getAbsolutePath());
  43. if (mJavaDetector.empty()) {
  44. Log.e(TAG, "加载cascade classifier失败");
  45. mJavaDetector = null;
  46. } else {
  47. Log.d(TAG, "Loaded cascade classifier from " + mCascadeFile.getAbsolutePath());
  48. }
  49. mNativeDetector = new DetectionBasedTracker(mCascadeFile.getAbsolutePath(), 0);
  50. cascadeDir.delete();
  51. } catch (FileNotFoundException e) {
  52. e.printStackTrace();
  53. } catch (IOException e) {
  54. e.printStackTrace();
  55. }
  56. // 开启渲染Camera
  57. mCameraView.enableView();
  58. break;
  59. default:
  60. super.onManagerConnected(status);
  61. break;
  62. }
  63. }
  64. };
  65. @Override
  66. protected void onCreate(@Nullable Bundle savedInstanceState) {
  67. super.onCreate(savedInstanceState);
  68. getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
  69. setContentView(R.layout.activity_facedetect);
  70. // 绑定View
  71. ButterKnife.bind(this);
  72. mCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
  73. // 注册Camera渲染事件监听器
  74. mCameraView.setCvCameraViewListener(this);
  75. }
  76. @Override
  77. protected void onResume() {
  78. super.onResume();
  79. // 静态初始化OpenCV
  80. if (!OpenCVLoader.initDebug()) {
  81. Log.d(TAG, "无法加载OpenCV本地库,将使用OpenCV Manager初始化");
  82. OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_3_0, this, mLoaderCallback);
  83. } else {
  84. Log.d(TAG, "成功加载OpenCV本地库");
  85. mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
  86. }
  87. }
  88. @Override
  89. protected void onPause() {
  90. super.onPause();
  91. // 停止渲染Camera
  92. if (mCameraView != null) {
  93. mCameraView.disableView();
  94. }
  95. }
  96. @Override
  97. protected void onDestroy() {
  98. super.onDestroy();
  99. // 停止渲染Camera
  100. if (mCameraView != null) {
  101. mCameraView.disableView();
  102. }
  103. }
  104. @Override
  105. public void onCameraViewStarted(int width, int height) {
  106. // 灰度图像
  107. mGray = new Mat();
  108. // R、G、B彩色图像
  109. mRgba = new Mat();
  110. }
  111. @Override
  112. public void onCameraViewStopped() {
  113. mGray.release();
  114. mRgba.release();
  115. }
  116. @Override
  117. public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
  118. mRgba = inputFrame.rgba();
  119. mGray = inputFrame.gray();
  120. // 设置脸部大小
  121. if (mAbsoluteFaceSize == 0) {
  122. int height = mGray.rows();
  123. if (Math.round(height * mRelativeFaceSize) > 0) {
  124. mAbsoluteFaceSize = Math.round(height * mRelativeFaceSize);
  125. }
  126. mNativeDetector.setMinFaceSize(mAbsoluteFaceSize);
  127. }
  128. // 获取检测到的脸部数据
  129. MatOfRect faces = new MatOfRect();
  130. if (mDetectorType == JAVA_DETECTOR) {
  131. if (mJavaDetector != null) {
  132. mJavaDetector.detectMultiScale(mGray, faces, 1.1, 2, 2,
  133. new Size(mAbsoluteFaceSize, mAbsoluteFaceSize), new Size());
  134. }
  135. } else if (mDetectorType == NATIVE_DETECTOR) {
  136. if (mNativeDetector != null) {
  137. mNativeDetector.detect(mGray, faces);
  138. }
  139. } else {
  140. Log.e(TAG, "Detection method is not selected!");
  141. }
  142. // 绘制检测框
  143. Rect[] facesArray = faces.toArray();
  144. for (int i = 0; i < facesArray.length; i++) {
  145. Imgproc.rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);
  146. }
  147. Log.i(TAG, "共检测到 " + faces.toArray().length + " 张脸");
  148. return mRgba;
  149. }
  150. }

2. activity_facedetect.xml

[html] view plaincopy
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:opencv="http://schemas.android.com/apk/res-auto"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. android:orientation="vertical">
  7. <org.opencv.android.JavaCameraView
  8. android:id="@+id/cameraView_face"
  9. android:layout_width="match_parent"
  10. android:layout_height="match_parent"
  11. android:visibility="gone"
  12. opencv:camera_id="any"
  13. opencv:show_fps="true" />
  14. </RelativeLayout>

3. AndroidMnifest.xml

[html] view plaincopy
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.jiangdg.opencv4android">
  4. <uses-permission android:name="android.permission.CAMERA"/>
  5. <uses-feature android:name="android.hardware.camera" android:required="false"/>
  6. <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
  7. <uses-feature android:name="android.hardware.camera.front" android:required="false"/>
  8. <uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>
  9. <supports-screens android:resizeable="true"
  10. android:smallScreens="true"
  11. android:normalScreens="true"
  12. android:largeScreens="true"
  13. android:anyDensity="true" />
  14. <application
  15. android:allowBackup="true"
  16. android:icon="@mipmap/ic_launcher"
  17. android:label="@string/app_name"
  18. android:roundIcon="@mipmap/ic_launcher_round"
  19. android:supportsRtl="true"
  20. android:theme="@style/AppTheme">
  21. <activity android:name=".MainActivity">
  22. <intent-filter>
  23. <action android:name="android.intent.action.MAIN" />
  24. <category android:name="android.intent.category.LAUNCHER" />
  25. </intent-filter>
  26. </activity>
  27. <activity android:name=".HelloOpenCVActivity"
  28. android:screenOrientation="landscape"
  29. android:configChanges="keyboardHidden|orientation"/>
  30. <activity android:name=".FaceDetectActivity"
  31. android:screenOrientation="landscape"
  32. android:configChanges="keyboardHidden|orientation"/>
  33. </application>
  34. </manifest>

效果演示:

源码下载:https://github.com/jiangdongguo/OpenCV4Android(欢迎star & fork)

更新于2018-3-13

四、使用Cmake方式编译和升级到OpenCV3.4.1

在上面工程中,有两个不好的体验:(1)每次编译so时必须手动调用ndk-build命令;(2)在编写C/C++代码时没有代码提示,也没有报错警告。我想这两种情况无论是哪个都是让人感觉很不爽的,因此,今天打算在OpenCV3.4.1版本的基础上对项目进行重构下,使用Cmake方式来进行编译。

1. 新建main/app/cpp目录。将jni目录下的"DetectionBasedTracker_jni.cpp" 和"DetectionBasedTracker_jni.h" 文件拷贝到该目录下,并将opencv-3.4.1源码..\opencv-3.4.1-android-sdk\OpenCV-android-sdk\sdk\native\jni\目录下的整个include目录拷贝到该cpp目录下,同时可以删除整个app/src/main/jni目录

2. 新建main/app/jniLibs目录,将opencv-3.4.1源码中..\opencv-3.4.1-android-sdk\OpenCV-android-sdk\sdk\native\libs或staticlibs相关架构的动态库(.so)和静态库(.a)文件拷贝到该jniLibs目录下,同时删除libs、obj目录。

3. 在app目录下新建脚本文件CMakeLists.txt,该文件用于编写Cmake编译运行需要的脚本。需要注意的是,你在jniLibs目录导入了哪些静态库和动态库,在CmakeList.txt编写自动编译脚本时只能导入和链接这些库:

[cpp] view plaincopy
  1. # Sets the minimum version of CMake required to build the native
  2. # library. You should either keep the default value or only pass a
  3. # value of 3.4.0 or lower.
  4. cmake_minimum_required(VERSION 3.4.1)
  5. # Creates and names a library, sets it as either STATIC
  6. # or SHARED, and provides the relative paths to its source code.
  7. # You can define multiple libraries, and CMake builds it for you.
  8. # Gradle automatically packages shared libraries with your APK.
  9. set(CMAKE_VERBOSE_MAKEFILE on)
  10. set(libs "${CMAKE_SOURCE_DIR}/src/main/jniLibs")
  11. include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)
  12. #--------------------------------------------------- import ---------------------------------------------------#
  13. add_library(libopencv_java3 SHARED IMPORTED )
  14. set_target_properties(libopencv_java3 PROPERTIES
  15. IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_java3.so")
  16. add_library(libopencv_core STATIC IMPORTED )
  17. set_target_properties(libopencv_core PROPERTIES
  18. IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_core.a")
  19. add_library(libopencv_highgui STATIC IMPORTED )
  20. set_target_properties(libopencv_highgui PROPERTIES
  21. IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_highgui.a")
  22. add_library(libopencv_imgcodecs STATIC IMPORTED )
  23. set_target_properties(libopencv_imgcodecs PROPERTIES
  24. IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_imgcodecs.a")
  25. add_library(libopencv_imgproc STATIC IMPORTED )
  26. set_target_properties(libopencv_imgproc PROPERTIES
  27. IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_imgproc.a")
  28. add_library(libopencv_objdetect STATIC IMPORTED )
  29. set_target_properties(libopencv_objdetect PROPERTIES
  30. IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_objdetect.a")
  31. #add_library(libopencv_calib3d STATIC IMPORTED )
  32. #set_target_properties(libopencv_calib3d PROPERTIES
  33. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_calib3d.a")
  34. #
  35. #
  36. #add_library(libopencv_dnn STATIC IMPORTED )
  37. #set_target_properties(libopencv_core PROPERTIES
  38. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_dnn.a")
  39. #
  40. #add_library(libopencv_features2d STATIC IMPORTED )
  41. #set_target_properties(libopencv_features2d PROPERTIES
  42. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_features2d.a")
  43. #
  44. #add_library(libopencv_flann STATIC IMPORTED )
  45. #set_target_properties(libopencv_flann PROPERTIES
  46. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_flann.a")
  47. #
  48. #add_library(libopencv_ml STATIC IMPORTED )
  49. #set_target_properties(libopencv_ml PROPERTIES
  50. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_ml.a")
  51. #
  52. #add_library(libopencv_photo STATIC IMPORTED )
  53. #set_target_properties(libopencv_photo PROPERTIES
  54. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_photo.a")
  55. #
  56. #add_library(libopencv_shape STATIC IMPORTED )
  57. #set_target_properties(libopencv_shape PROPERTIES
  58. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_shape.a")
  59. #
  60. #add_library(libopencv_stitching STATIC IMPORTED )
  61. #set_target_properties(libopencv_stitching PROPERTIES
  62. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_stitching.a")
  63. #
  64. #add_library(libopencv_superres STATIC IMPORTED )
  65. #set_target_properties(libopencv_superres PROPERTIES
  66. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_superres.a")
  67. #
  68. #add_library(libopencv_video STATIC IMPORTED )
  69. #set_target_properties(libopencv_video PROPERTIES
  70. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_video.a")
  71. #
  72. #add_library(libopencv_videoio STATIC IMPORTED )
  73. #set_target_properties(libopencv_videoio PROPERTIES
  74. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_videoio.a")
  75. #
  76. #add_library(libopencv_videostab STATIC IMPORTED )
  77. #set_target_properties(libopencv_videostab PROPERTIES
  78. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_videostab.a")
  79. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -fexceptions -frtti")
  80. add_library( # Sets the name of the library.
  81. opencv341
  82. # Sets the library as a shared library.
  83. SHARED
  84. # Provides a relative path to your source file(s).
  85. # Associated headers in the same location as their source
  86. # file are automatically included.
  87. src/main/cpp/DetectionBasedTracker_jni.cpp)
  88. find_library( # Sets the name of the path variable.
  89. log-lib
  90. # Specifies the name of the NDK library that
  91. # you want CMake to locate.
  92. log)
  93. target_link_libraries(opencv341 android log
  94. libopencv_java3 #used for java sdk
  95. #17 static libs in total
  96. #libopencv_calib3d
  97. libopencv_core
  98. #libopencv_dnn
  99. #libopencv_features2d
  100. #libopencv_flann
  101. libopencv_highgui
  102. libopencv_imgcodecs
  103. libopencv_imgproc
  104. #libopencv_ml
  105. libopencv_objdetect
  106. #libopencv_photo
  107. #libopencv_shape
  108. #libopencv_stitching
  109. #libopencv_superres
  110. #libopencv_video
  111. #libopencv_videoio
  112. #libopencv_videostab
  113. ${log-lib}
  114. )

4. 右击选中app/src/main/cpp目录,选择"Link C++ Project with Gradle"并浏览选择本项目中的CmakeLists.txt文件,将C++环境关联到gradle

5. 将工程中的openCVLibrary330 module更新到openCVLibrary341,修改app目录下的gradle.build文件

[cpp] view plaincopy
  1. apply plugin: 'com.android.application'
  2. android {
  3. compileSdkVersion 25
  4. defaultConfig {
  5. //...代码省略
  6. externalNativeBuild {
  7. cmake {
  8. arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang","-DCMAKE_BUILD_TYPE=Release"
  9. //'-DANDROID_STL=gnustl_static'
  10. cppFlags "-std=c++11","-frtti", "-fexceptions"
  11. }
  12. }
  13. // 设置输出指定目标平台so
  14. ndk{
  15. abiFilters 'armeabi-v7a','arm64-v8a','armeabi'
  16. }
  17. }
  18. // ...代码省略
  19. externalNativeBuild {
  20. cmake {
  21. path 'CMakeLists.txt'
  22. }
  23. }
  24. }
  25. dependencies {
  26. //.. 代码省略
  27. implementation project(':openCVLibrary341')
  28. }

至此,该工程重构完毕,切换到cpp文件中,码个Mat看下成效~

OpenCV4Android开发实录(2): 使用OpenCV3.4.1库实现人脸检测相关推荐

  1. OpenCV4Android开发实录(3):数字图像基础与OpenCV开发入门

    转载请声明出处:https://blog.csdn.net/AndrExpert/article/details/79889136 俗话说:"工欲善其事,必先利其器".数字图像处理 ...

  2. 【CSON原创】HTML5游戏框架cnGameJS开发实录(外部输入模块篇)

    返回目录 1.为什么我们需要外部输入模块? 在游戏中我们常常用到类似这样的操作:鼠标点击某位置,玩家对象移动到该位置,或者按鼠标方向键,玩家向不同方向移动,等等.这些操作无一不用与外部输入设备打交道. ...

  3. React+Redux开发实录(一)搭建工程脚手架

    React+Redux开发实录(一)搭建工程脚手架 React+Redux开发实录(二)React技术栈一览 搭建工程脚手架 准备工作 安装node 安装git 安装一款前端IDE 推荐VSCode, ...

  4. Pixysoft.Framework.Noebe.Socket 开发实录

    本随笔作为此框架开发实录. 基本规划 1. 掌握一个最简单的socket编程,就是一个局域网im系统 2. 寻找一种安全的socket链接方式,包括: .查看已有的net关于socket编程,特别是f ...

  5. 【CSON原创】HTML5游戏框架cnGameJS开发实录(精灵对象篇)

    返回目录 1.什么是精灵对象(sprite)? 所谓的精灵对象,就是游戏中的一个具有行为的元素,以超级玛丽为例,玛丽,敌人都算是一个精灵对象.在cnGameJS框架中,精灵对象如下几个特点: 1.添加 ...

  6. ONVIF、RTSP/RTP、FFMPEG的开发实录

    ONVIF.RTSP/RTP.FFMPEG的开发实录 前言 本文从零基础一步步实现ONVIF协议.RTSP/RTP协议获取IPC实时视频流.FFMPEG解码.开发环境为WIN7 32位 + VS201 ...

  7. 微信小程序开发实录——每日速递

    微信小程序开发实录--每日速递 选题简介 工具使用心得 选题简介 新闻版块 创建项目 数据来源(知乎日报) 基本页编写 详情页编写 数据获取 WxParse 样式表转换 转换方法 样式文件的引用 天气 ...

  8. html 游戏 精灵,HTML5游戏框架cnGameJS开发实录-精灵对象篇

    返回目录 1.什么是精灵对象(sprite)? 所谓的精灵对象,就是游戏中的一个具有行为的元素,以超级玛丽为例,玛丽,敌人都算是一个精灵对象.在cnGameJS框架中,精灵对象如下几个特点: 1.添加 ...

  9. vue项目开发实录--仿去哪儿网App-张鹏-专题视频课程

    vue项目开发实录--仿去哪儿网App-160人已学习 课程介绍         本课程为vue项目开发实录(仿去哪儿网App)其中涉及到知识点有:组件搭建,路由,vuex,axios,webpack ...

最新文章

  1. HDU 2519 新生晚会【求组合数】
  2. 交叉编译及树莓派(或其他平台)交叉编译工具链的安装
  3. python画画用哪库好_小白开始学Python最著名的绘图库
  4. Python 薪资降温?不存在的
  5. BugkuCTF-MISC题多方法解决
  6. fedora 33 topbar_31省区市新增确诊33例,天津新增本地确诊1例
  7. Ubuntu安装python3虚拟环境
  8. [E]PSM算法简析
  9. VS2012错误之 warning LNK4075: 忽略“/EDITANDCONTINUE”(由于“/SAFESEH”规范)
  10. 远程桌面:6个最实用技巧
  11. 新建文件没有word、ppt、excel,office图标显示为白色,不能正常显示
  12. mac下bin格式文件解压
  13. python爬取微信公众号_python爬取微信公众号
  14. 各种说明方法的例句_句子的说明方法有哪些(说明方法的句子大全集)
  15. C语言经典案例——第六章 字符串
  16. matlab:已知传递函数,求单位阶跃响应
  17. excel如何拆分表格
  18. graduate计算机专业英语词汇,药物分析专业英语词汇.pdf
  19. 关于梅森素数定理(网上收集)
  20. 《趣学Python编程》——第1部分 学习编程 第1章 Python不是大蟒蛇 1.1 关于计算机语言...

热门文章

  1. 弹性网络_理论物理所建立解析模型研究凝胶网络弹性介导的液液相分离现象
  2. eclipse adt如何切换到设计界面_APP界面设计模板|引导页该如何设计?
  3. 宾阳 计算机 培训,宾阳县职业技术学校
  4. linux中的ln属性,linux 常用基础命令 ln 详细介绍
  5. 四人过桥问题c语言编程,SQL趣题:四人过桥的问题
  6. netcore 图片 文件大小_NetCore 3.0文件上传和大文件上传的限制详解
  7. python install scikit-image后,报错ImportError: DLL load failed: 找不到指定的模块
  8. 自然语言处理常用数据集
  9. BGR图像与HSV图像互相转换(opencv)
  10. Linux那些事儿之我是Sysfs(4)举例一lddbus