Android基于OpenCV通过JNI人脸识别并显示位置 原创
文章目录
Android基于OpenCV通过JNI人脸识别并显示位置
OpenCV介绍地址:https://docs.opencv.org/2.4/doc/tutorials/introduction/android_binary_package/O4A_SDK.html
Android OpenCV Java Demo地址: https://github.com/kongqw/OpenCVForAndroid,其中人脸比对在Master分支
本文基于JNI实现,源码地址:Gitee:OpenCVJniFaceDetect
设计思路
取Camera API 中onPreviewFrame 回调的YUV数据送到JNI,
进一步用OpenCV的API识别并画出人脸区域,再通过ANativeWindow显示到surface。
代码设计说明
效果如下

代码结构如下

其中
app\src\main\cpp\include
来自 opencv-3.4.3-android-sdk\sdk\native\jni\include
app\src\main\assets\lbpcascade_frontalface.xml
来自 opencv-3.4.3-android-sdk\sdk\etc\lbpcascades
app\src\main\jniLibs\
来自 opencv-3.4.3-android-sdk\sdk\native\libs
JNI识别人脸并画区域代码如下
// nv21的数据
jbyte *data = env->GetByteArrayElements(data_, NULL);
//mat data->Mat
//1、高 2、宽
Mat src(h + h / 2, w, CV_8UC1, data);
//颜色格式的转换 nv21->RGBA
//将 nv21的yuv数据转成了rgba
cvtColor(src, src, COLOR_YUV2RGBA_NV21);
// 可以将Mat的数据写到存储卡,正在写的过程 退出了,导致文件丢失数据
//imwrite("/sdcard/src.jpg",src);
if (cameraId == 1) {
//前置摄像头,需要逆时针旋转90度
rotate(src, src, ROTATE_90_COUNTERCLOCKWISE);
//水平翻转 镜像
flip(src, src, 1);
} else {
//顺时针旋转90度
rotate(src, src, ROTATE_90_CLOCKWISE);
}
Mat gray;
//灰色
cvtColor(src, gray, COLOR_RGBA2GRAY);
//增强对比度 (直方图均衡)
equalizeHist(gray, gray);
std::vector<Rect> faces;
//定位人脸 N个
tracker->process(gray);
tracker->getObjects(faces);
for (Rect face : faces) {
//画矩形 分别指定 bgra
rectangle(src, face, Scalar(255, 0, 0));
}
通过ANativeWindow显示RGBA数据到surface代码如下
ANativeWindow_setBuffersGeometry(window, src.cols, src.rows, WINDOW_FORMAT_RGBA_8888);
ANativeWindow_Buffer buffer;
do {
//lock失败 直接brek出去
if (ANativeWindow_lock(window, &buffer, 0)) {
ANativeWindow_release(window);
window = 0;
break;
}
//src.data : rgba的数据
//把src.data 拷贝到 buffer.bits 里去
// 一行一行的拷贝
//一行需要多少像素 * 4(RGBA),当stride>width时直接memcpy会显示异常
//memcpy(buffer.bits, src.data, buffer.stride * buffer.height * 4);
UpdateFrameBuffer(&buffer, src.data);
//提交刷新
ANativeWindow_unlockAndPost(window);
} while (0);
将RGA数据填充到ANativeWindow_Buffer代码如下
参考自Github:ndk-samples:webp_view.cpp#UpdateFrameBuffer
*
* UpdateFrameBuffer():
* Internal function to perform bits copying onto current frame buffer
* src:
* - if nullptr, blank it
* - otherwise, copy to given buf
* assumption:
* src and bug MUST be in the same geometry format & layout
*/
void UpdateFrameBuffer(ANativeWindow_Buffer *buf, uint8_t *src) {
// src is either null: to blank the screen
// or holding exact pixels with the same fmt [stride is the SAME]
uint8_t *dst = reinterpret_cast<uint8_t *> (buf->bits);
uint32_t bpp;
switch (buf->format) {
case WINDOW_FORMAT_RGB_565:
bpp = 2;
break;
case WINDOW_FORMAT_RGBA_8888:
case WINDOW_FORMAT_RGBX_8888:
bpp = 4;
break;
default:
assert(0);
return;
}
uint32_t stride, width;
stride = buf->stride * bpp;
width = buf->width * bpp;
if (src) {
for (auto height = 0; height < buf->height; ++height) {
memcpy(dst, src, width);
dst += stride, src += width;
}
} else {
for (auto height = 0; height < buf->height; ++height) {
memset(dst, 0, width);
dst += stride;
}
}
}
注意问题说明
- OpenCV 需要依赖 gnustl_static,参考自
opencv-3.4.3-android-sdk\samples\face-detection\jni\Application.mk
, NDK r18b中 移除了gnustl_static,注意选择NDK17及以下版本 - 目前Camera预览数据是640X480,在骁龙820手机设备上单帧需要10ms左右,加大尺寸效率会降低
- OpenCV提供的模型对侧脸、脸部明暗相差大等情况的识别效果不是特别好
原创文章,转载请注明出处、原文链接!me@h89.cn 我的主页https://chenjim.com
本文来自博客园,作者:清霜辰,转载请注明原文链接:https://www.cnblogs.com/cnjim/p/18443496
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了