Android之OpenCv简单人脸识别功能(Bitmap)

何星鹏
2023-12-01

Android之OpenCv简单人脸识别功能

OpenCv的下载

下载地址 - https://opencv.org/releases/

  • doc 文档目录
  • samples 示例代码
  • sdk 编译后的动态库以及头文件

案例代码

人脸识别

1.人脸信息录入
2.获取相机的Bitmap,检测人脸信息(保证人脸特征信息比较精准),正常,眨眼睛,张嘴巴
3.提取特征值

Mat转Bitmap

// 将Mat转bitmap
void mat2Bitmap(JNIEnv *env,jobject bitmap,Mat mat){
    // 1. 获取 bitmap 信息
    AndroidBitmapInfo info;
    void* pixels;
    AndroidBitmap_getInfo(env,bitmap,&info);

    // 锁定 Bitmap 画布
    AndroidBitmap_lockPixels(env,bitmap,&pixels);

    if(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888){// C4
        Mat temp(info.height,info.width,CV_8UC4,pixels);
        if(mat.type() == CV_8UC4){
            mat.copyTo(temp);
        }
        else if(mat.type() == CV_8UC2){
            cvtColor(mat,temp,COLOR_BGR5652BGRA);
        }
        else if(mat.type() == CV_8UC1){// 灰度 mat
            cvtColor(mat,temp,COLOR_GRAY2BGRA);
        }
    } else if(info.format == ANDROID_BITMAP_FORMAT_RGB_565){// C2
        Mat temp(info.height,info.width,CV_8UC2,pixels);
        if(mat.type() == CV_8UC4){
            cvtColor(mat,temp,COLOR_BGRA2BGR565);
        }
        else if(mat.type() == CV_8UC2){
            mat.copyTo(temp);

        }
        else if(mat.type() == CV_8UC1){// 灰度 mat
            cvtColor(mat,temp,COLOR_GRAY2BGR565);
        }
    }

    // 解锁 Bitmap 画布
    AndroidBitmap_unlockPixels(env,bitmap);
}

Bitmap转Mat

// 将bitmap转Mat
void bitmap2Mat(JNIEnv *env,jobject bitmap,Mat &mat){
    // Mat 里面有个 type : CV_8UC4 刚好对上我们的 Bitmap 中 ARGB_8888 , CV_8UC2 刚好对象我们的 Bitmap 中 RGB_565
    // 1. 获取 bitmap 信息
    AndroidBitmapInfo info;
    void* pixels;
    AndroidBitmap_getInfo(env,bitmap,&info);

    // 锁定 Bitmap 画布
    AndroidBitmap_lockPixels(env,bitmap,&pixels);
    // 指定 mat 的宽高和type  BGRA
    mat.create(info.height,info.width,CV_8UC4);

    if(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888){

        __android_log_print(ANDROID_LOG_INFO,"JNI_TAG","bitmap2Mat : RGBA_8888 -> CV_8UC4");

        // 对应的 mat 应该是  CV_8UC4
        Mat temp(info.height,info.width,CV_8UC4,pixels);
        // 把数据 temp 复制到 mat 里面
        temp.copyTo(mat);

    } else if(info.format == ANDROID_BITMAP_FORMAT_RGB_565){

        __android_log_print(ANDROID_LOG_INFO,"JNI_TAG","bitmap2Mat : RGB_565 -> CV_8UC4");

        // 对应的 mat 应该是  CV_8UC2
        Mat temp(info.height,info.width,CV_8UC2,pixels);
        // mat 是 CV_8UC4 ,CV_8UC2 -> CV_8UC4
        cvtColor(temp,mat,COLOR_BGR5652BGRA);
    }

    // 解锁 Bitmap 画布
    AndroidBitmap_unlockPixels(env,bitmap);
}

加载人脸分类器

Java部分代码

// 拷贝文件至可读取位置
public String copyCascadeClassifier() {
	String fileName = "lbpcascade_frontalface.xml";
	// 先判断该文件是否存在
	String filePath = getFilesDir().getAbsolutePath() + File.separator + fileName;
	File file = new File(filePath);
	if(!file.exists()){
		Log.d(FaceRecognition.TAG,"文件不存在开始拷贝");
		try {
			InputStream inputStream = getAssets().open(fileName);
			FileOutputStream fileOutputStream = new FileOutputStream(file);
			int length = 0;
			byte[] bytes = new byte[1024 * 1024];
			while ((length = inputStream.read(bytes))!= -1){
				fileOutputStream.write(bytes,0,length);
			}
			fileOutputStream.close();
			inputStream.close();
		}catch (Exception e){
			e.printStackTrace();
		}
	}
	return filePath;
}

C++部分代码


CascadeClassifier cascadeClassifier;

extern "C"
JNIEXPORT void JNICALL
Java_com_barray_opencvdemo_FaceRecognition_loadCascadeClassifier(JNIEnv *env, jobject thiz,
                                                                 jstring file_path) {
    const char * c_file_path = env->GetStringUTFChars(file_path,JNI_FALSE);
    cascadeClassifier.load(c_file_path);
    __android_log_print(ANDROID_LOG_INFO,"JNI_TAG","加载人脸分类器成功");
    env->ReleaseStringUTFChars(file_path,c_file_path);
}

识别人脸

extern "C"
JNIEXPORT void JNICALL
Java_com_barray_opencvdemo_FaceRecognition_saveFaceInfo(JNIEnv *env, jobject thiz,
                                                        jobject bitmap) {
    // 检测人脸
    Mat mat;
    bitmap2Mat(env,bitmap,mat);

    // 灰度处理
    Mat grey_mat;
    cvtColor(mat,grey_mat,COLOR_BGRA2GRAY);

    // 直方均衡补偿(轮廓会比较明显)
    Mat equalize_mat;
    equalizeHist(grey_mat,equalize_mat);

    // 识别人脸
    std::vector<Rect> faces;
    // 加载人脸分类器文件
    cascadeClassifier.detectMultiScale(equalize_mat,faces,1.1,5);

    __android_log_print(ANDROID_LOG_INFO,"JNI_TAG","人脸个数:%lu",faces.size());

    if(!faces.empty()){
        Rect faceRect = faces[0];
        // 在人脸上画个框
        rectangle(mat,faceRect,Scalar(255,155,155),8);
        // 将数据保存到bitmap中
        mat2Bitmap(env,bitmap,mat);
        Mat face_info_mat(equalize_mat,faceRect);
        // 可以保存到本地
    }
}

Android文件的配置

build.gradle

plugins {
    id 'com.android.application'
}

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "com.barray.opencvdemo"
        minSdkVersion 23
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags ""
                abiFilters 'arm64-v8a'
                arguments "-DANDROID_STL=c++_shared" // 很重要
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    externalNativeBuild {
        cmake {
            path file('src/main/cpp/CMakeLists.txt')
            version '3.10.2'
        }
    }
    buildFeatures {
        viewBinding true
    }
}

dependencies {
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.10.2)
project("opencvdemo")

include_directories(include)

set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../libs)


# 编解码(最重要的库)
add_library(
        opencv_java
        SHARED
        IMPORTED)

set_target_properties(
        opencv_java
        PROPERTIES IMPORTED_LOCATION
        ${distribution_DIR}/${ANDROID_ABI}/libopencv_java4.so)


add_library(
             native-lib
             SHARED
             native-lib.cpp )
find_library(
              log-lib
              log )
target_link_libraries(
        native-lib jnigraphics -landroid opencv_java
                       ${log-lib} )

注意

  • so文件放在 libs目录下,当前只配置了arm64-v8a
  • 头文件放在 cpp目录下

源码下载

 类似资料: