OpenCV FaceDetector 人脸检测

1. OpenCV For Android

OpenCV 官网地址 https://opencv.org/releases/ ,提供了Android 平台使用的SDK
SDK For Android 均包含在官方提供的opencv moudle中。

2. 官方例子face-detection(仅分析Native实现)

2.1 face-detection 默认使用了opencv Camera1 (org.opencv.android.JavaCameraView),用来获取和显示数据,可以在face_detect_surface_view.xml中对View组件进行替换为如下

    
1
2
3
4
<org.opencv.android.JavaCamera2View
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/fd_activity_surface_view" />
通过替换,可以使用Camera2中的接口来获取数据和显示View

FaceDetector_Camera2

2.2 编译和构建步骤

2.2.1 通过在face-detection模块的build.gradle配置Cmake编译时2个参数
      OpenCV_DIR //OpenCV的路径 作用:提供编译c代码时,需要用到OpenCV的头文件 位置:sdk/native/jni/include 以及需要引入的OpenCV依赖库 
      targets "detection_based_tracker"

FaceDetector_Camera2
face-detection 默认引用opencv module,在通过构建脚本CMakeList.txt 编译os文件

2.2.2 Cmake

Cmake构建脚本,包含引入OpenCV,以及本仓库需要编译的cpp文件

1
2
3
4
5
6
7
8
9
10
11
cmake_minimum_required(VERSION 3.6)
set(target detection_based_tracker)
project(${target} CXX) #已通过build.gradle中手动添加的target
set(ANDROID_OPENCV_COMPONENTS "opencv_java" CACHE STRING "")
message(STATUS "ANDROID_ABI=${ANDROID_ABI}")#通过在Build.gradle中配置ndk abi filter,默认会全部编译
find_package(OpenCV REQUIRED COMPONENTS ${ANDROID_OPENCV_COMPONENTS})
file(GLOB srcs *.cpp *.c)
file(GLOB hdrs *.hpp *.h)
include_directories("${CMAKE_CURRENT_LIST_DIR}")
add_library(${target} SHARED ${srcs} ${hdrs})
target_link_libraries(${target} ${ANDROID_OPENCV_COMPONENTS})

2.2.3 Native以及JNI编写
此处无特别注意之处,均为标准规范

3. Face-detection调整为library

3.1 通过Camera2接口+ImageReader接口获取相机帧数据,通过OpenCV SDK中官方代码,将YUV420_888转换成为Mat
3.2 通过OpenCV对Java接口对Mat数据进行旋转等操作,保证送入识别接口的Mat(灰度)方向正常
3.3 此处相机分为前置后置,此处灵活处理

FaceDetector_Camera2

4 总结

4.1 我们可以将OpenCV已编译好的so库以及include头文件单独迁移出来,直接使用Native.load()方法直接调用。此方法可以加快构建工程速度,无需再次编译libopencv_java4.so

4.2 OpenCV FaceDetection中Nativce方法传递参数时,传递的为Mat索引,此方法减少内存以及快速将Java层Mat传递给Nativce.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    //第一个参数为灰度Mat,传递给Native的为Long型地址
public void detect(Mat imageGray, MatOfRect faces) {
nativeDetect(mNativeObj, imageGray.getNativeObjAddr(), faces.getNativeObjAddr());
}

JNIEXPORT void JNICALL Java_org_opencv_samples_facedetect_DetectionBasedTracker_nativeDetect
(JNIEnv * jenv, jclass, jlong thiz, jlong imageGray, jlong faces)
{
//LOGD("Java_org_opencv_samples_facedetect_DetectionBasedTracker_nativeDetect");

try
{
vector<Rect> RectFaces;
//*((Mat*)imageGray) 此处将地址索引再次转为Mat
((DetectorAgregator*)thiz)->tracker->process(*((Mat*)imageGray));
((DetectorAgregator*)thiz)->tracker->getObjects(RectFaces);
*((Mat*)faces) = Mat(RectFaces, true);
}
catch(const cv::Exception& e)
{
LOGD("nativeCreateObject caught cv::Exception: %s", e.what());
jclass je = jenv->FindClass("org/opencv/core/CvException");
if(!je)
je = jenv->FindClass("java/lang/Exception");
jenv->ThrowNew(je, e.what());
}
catch (...)
{
LOGD("nativeDetect caught unknown exception");
jclass je = jenv->FindClass("java/lang/Exception");
jenv->ThrowNew(je, "Unknown exception in JNI code DetectionBasedTracker.nativeDetect()");
}
//LOGD("Java_org_opencv_samples_facedetect_DetectionBasedTracker_nativeDetect END");
}

4.3 如果其他Native库使用OpenCV,我们在传入灰度数据时,也可以将Native设计为Mat地址索引

4.4 OpenCV FaceDetector可以跟其他人脸Landmark库配合使用,例如:FaceAlignmentSeeta,dlib等,在进行人脸关键点识别时,均需要传递为灰度Mat以及需要人脸框


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!