人脸识别效果
创建项目
这里没什么好说的,使用AS创建一个支持C++的项目即可
如果你的AS暂不支持NDK开发,请下载这三个工具,并且在Open Module Settings中配置NDK的路径,
导入OpenCV头文件和so库文件
-
下载OpenCV库
-
在main文件夹下创建jniLibs文件夹
-
导入头文件,把这个include文件夹复制到jniLibs中
-
导入so文件,把需要兼容的so库也复制到jniLibs中
- 在CMakeLists.txt中配置头文件位置
include_directories(../jniLibs/include)
注意:有些教程中的CMakeLists.txt文件是在app文件夹下面的,此项目是在cpp文件夹下路径是../jniLibs/include,..表示的是cpp的父文件夹,如果你的CMakeLists.txt在app文件夹下,路径应该这么写src/main/jniLibs/include
- 在CMakeLists.txt中配置so文件位置
#第一步
set(distribution_DIR ${CMAKE_SOURCE_DIR}/jniLibs)
add_library(opencv_java4
SHARED
IMPORTED)
set_target_properties(
opencv_java4
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libopencv_java4.so)
#第二步
target_link_libraries( # Specifies the target library.
native-lib
jnigraphics #此库是NDK bitmap所在地
opencv_java4 #OpenCV库
# Links the target library to the log library
# included in the NDK.
${log-lib})
完整的CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
include_directories(../jniLibs/include)
set(distribution_DIR ${CMAKE_SOURCE_DIR}/jniLibs)
add_library(opencv_java4
SHARED
IMPORTED)
set_target_properties(
opencv_java4
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libopencv_java4.so)
add_library(
native-lib
SHARED
native-lib.cpp)
find_library(
log-lib
log)
target_link_libraries(
native-lib
jnigraphics
opencv_java4
${log-lib})
bitMap2Mat
void bitmap2Mat(JNIEnv *env, jobject bitmap, Mat &mat) {
AndroidBitmapInfo info;
AndroidBitmap_getInfo(env, bitmap, &info);
Mat newMat(info.height, info.width, CV_8UC4);
void *pixels;
AndroidBitmap_lockPixels(env, bitmap, &pixels);
if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {//ARGB_8888
Mat tem(info.height, info.width, CV_8UC4, pixels);//创建一个空矩阵
tem.copyTo(newMat);
tem.release();
} else if (info.format == ANDROID_BITMAP_FORMAT_RGB_565) {//RGB_565
Mat tem(info.height, info.width, CV_8UC2, pixels);
tem.copyTo(newMat, COLOR_BGR5652BGRA);
tem.release();
}
newMat.copyTo(mat);
newMat.release();
AndroidBitmap_unlockPixels(env, bitmap);
}
mat2Bitmap
void mat2Bitmap(JNIEnv *env, Mat mat, jobject bitmap) {
AndroidBitmapInfo info;
AndroidBitmap_getInfo(env, bitmap, &info);
void *pixels;
AndroidBitmap_lockPixels(env, bitmap, &pixels);
if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
Mat tem(info.height, info.width, CV_8UC4, pixels);
switch (mat.type()) {
case CV_8UC4:
mat.copyTo(tem);
break;
case CV_8UC2:
cvtColor(mat, tem, COLOR_BGR5652BGRA);
break;
case CV_8UC1:
cvtColor(mat, tem, COLOR_GRAY2BGRA);
break;
}
} else if (info.format == ANDROID_BITMAP_FORMAT_RGB_565) {
Mat tem(info.height, info.width, CV_8UC2, pixels);
switch (mat.type()) {
case CV_8UC4:
cvtColor(mat, tem, COLOR_BGRA2BGR565);
break;
case CV_8UC2:
mat.copyTo(tem);
break;
case CV_8UC1:
cvtColor(mat, tem, COLOR_GRAY2BGR565);
break;
}
}
AndroidBitmap_unlockPixels(env, bitmap);
}
加载人脸识别的级联分类器
-
从OpenCV中复制文件至Android raw文件夹
获取该文件的路径
private void initCascadePath() {
File mCascadeFile;
try {
// load cascade file from application resources
InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
if (mCascadeFile.exists()) {
cascadePath=mCascadeFile.getAbsolutePath();
return;
}
FileOutputStream os = new FileOutputStream(mCascadeFile);
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
is.close();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
- 定义一个native方法,将路径传递给native层
public native void loadCascade(String filePath);
- 加载级联分类器
CascadeClassifier cascadeClassifier;
JNIEXPORT void JNICALL
Java_com_intent_opencv01_MainActivity_loadCascade(JNIEnv *env, jobject thiz, jstring file_path) {
cascadeClassifier.load(env->GetStringUTFChars(file_path, 0));
}
识别人脸
Java_com_intent_opencv01_MainActivity_faceDetection(JNIEnv *env, jobject thiz, jobject bitmap) {
Mat mat, grayMat;
//1、bitmap转mat
Bitmap2Mat(env, bitmap, mat);
//2、灰度图
cvtColor(mat, grayMat, COLOR_BGRA2GRAY);
//3、直方图均衡补偿
Mat equalizeMat;
/*
* 注意:此处第一个参数一定要使用灰度图
* 原图报异常(-215:Assertion failed) _src.type() == CV_8UC1 in function 'equalizeHist'
* */
equalizeHist(grayMat, equalizeMat);
//4、检测人脸
std::vector faces;
cascadeClassifier.detectMultiScale(equalizeMat, faces, 1.1, 5);
//CV_HAAR_SCALE_IMAGE(4.2.0无此参数了,有文章称用CASCADE_SCALE_IMAGE替换)
//cascadeClassifier.detectMultiScale(equalizeMat, faces, 1.1, 5, 0 | CASCADE_SCALE_IMAGE,
//Size(160, 160));
if (faces.size() == 1) {
Rect faceRect = faces[0];
//rectangle(mat, faceRect, CV_RGB(0, 255, 255));
//在原图mat上绘制圆形
circle(mat, Point(faceRect.x+ faceRect.width / 2, faceRect.y + faceRect.height / 2), 100,CV_RGB(255,0,0));
//将绘制好的mat转换成bitmap
Mat2Bitmap(env, mat, bitmap);
}
}
参考链接
1、Android native 中 Bitmap Mat 互转
2、NDK调用第三方so文件
3、cmake语法总结