seetaface6 android jni(二)
seetaface6 android 库 封装
·
前言
前一章已经讲述 seetaface6 库编译了
这章讲述 自己封装 seetaface6 库(JNI),提供几个简单的接口 供android 调用
1: java 接口类定义
1>主要就2个函数 1:加载模型文件 2 识别绘制人脸
package com.example.camerx;
import android.graphics.Bitmap;
public class SeetaFace {
static{
System.loadLibrary("testseetaface");
}
public native boolean loadModel(String datapath,String[] functions); //加载模型
public native int detectFaceEx(Bitmap input,int nflag); //nflag 1 detect 2 point5 4 point68 8 sex 16 age
}
2> seetaface hpp
按 windows demo 加载模型文件 提供几个函数供jni里调用
#include <iostream>
#include <string>
#include <vector>
#include <android/asset_manager_jni.h>
#include <seeta/FaceDetector.h>
#include <seeta/FaceLandmarker.h>
#include <seeta/FaceAntiSpoofing.h>
#include <seeta/Common/Struct.h>
#include <seeta/MaskDetector.h> //口罩检测
#include <seeta/EyeStateDetector.h> //眼睛状态检测
#include <seeta/AgePredictor.h> //年龄检测
#include <seeta/GenderPredictor.h> //性别检测
#include <seeta/QualityStructure.h> //遮挡评估
#include <seeta/QualityOfBrightness.h> //亮度评估
#include <seeta/QualityOfResolution.h> //分辨率评估
#include <seeta/QualityOfIntegrity.h> //完整性评估
#include <seeta/QualityOfClarity.h> //清晰度检测(传统)
#include <seeta/QualityOfPose.h> //姿态评估(传统)
//#include <seeta/QualityOfLBN.h> //清晰度评估(深度)
//#include <seeta/QualityOfPoseEx.h> //姿态评估(深度)
using namespace std;
class Seetaface
{
public:
Seetaface(){}
// 模型加载
bool Init_model(string path,int nflag){
int nResult = 0;
if((nflag &1) > 0){
//人脸框检测
seeta::ModelSetting setting;
setting.append(path+"/face_detector.csta");
faceDetector = new seeta::FaceDetector(setting);
nResult |= faceDetector?1:0 ;
}
if((nflag &2) > 0){
//人脸关键点模型初始化 5点
seeta::ModelSetting setting;
setting.append(path+"/face_landmarker_pts5.csta");
landDetector5 = new seeta::FaceLandmarker(setting);
nResult |= landDetector5?2:0 ;
}
if((nflag &4) > 0){
//人脸关键点模型初始化 68点
seeta::ModelSetting setting;
setting.append(path+"/face_landmarker_pts68.csta");
landDetector68 = new seeta::FaceLandmarker(setting);
nResult |= landDetector68?4:0 ;
}
if((nflag &8) > 0){
//性别检测
seeta::ModelSetting setting;
setting.append(path+"/gender_predictor.csta");
genderPredictor = new seeta::GenderPredictor(setting);
nResult |= genderPredictor?8:0 ;
}
if((nflag &16) > 0){
//性别检测
seeta::ModelSetting setting;
setting.append(path+"/age_predictor.csta");
agePredictor = new seeta::AgePredictor(setting);
nResult |= agePredictor?16:0 ;
}
//其他的自行增加
return nResult ;// nResult== nflag?1:0; //直接返回结果java更容易知道哪里模型加载失败
}
// 人脸检测
SeetaFaceInfoArray detect_face(const SeetaImageData &simage){
SeetaFaceInfoArray faces = faceDetector->detect(simage);
return faces;
}
//5特征点检测
std::vector<SeetaPointF> detect_land5(const SeetaImageData &simage,const SeetaRect &box){
std::vector<SeetaPointF> points = landDetector5->mark(simage, box);
return points;
}
//68特征点检测
std::vector<SeetaPointF> detect_land68(const SeetaImageData &simage,const SeetaRect &box){
std::vector<SeetaPointF> points = landDetector68->mark(simage, box);
return points;
}
//五官遮挡检测
int* detect_facemask(const SeetaImageData& simage,const SeetaRect& box){
int masks[4];
auto point_masks = faceMaskDetector->mark_v2(simage, box);
for (int i = 0; i < point_masks.size()-2; i++) {
masks[i]=point_masks[i].mask;
}
if(point_masks[3].mask==0 && point_masks[4].mask==0){
masks[3]=0;
}
else{
masks[3]=1;
}
return masks;
}
//年龄预测
int predict_age(const SeetaImageData& simage,const std::vector<SeetaPointF>& points5){
int age = 0;
agePredictor->PredictAgeWithCrop(simage, points5.data(), age);
return age;
}
//性别预测
int predict_gender_int(const SeetaImageData& simage,const std::vector<SeetaPointF>& points5){
seeta::GenderPredictor::GENDER gender;
int sex = 1;
genderPredictor->PredictGenderWithCrop(simage, points5.data(), gender);
if(gender == seeta::GenderPredictor::FEMALE)
sex = 0;
return sex;
}
private:
seeta::FaceDetector *faceDetector; //人脸框
seeta::FaceLandmarker *landDetector5; //5特征点
seeta::FaceLandmarker *landDetector68; //68特征点
seeta::FaceAntiSpoofing *liveDetector; //活体检测
seeta::FaceLandmarker *faceMaskDetector; //五官遮挡检测
seeta::AgePredictor *agePredictor; //年龄预测
seeta::GenderPredictor*genderPredictor; //性别评估
seeta::MaskDetector* maskDetector; //口罩检测
seeta::EyeStateDetector* eyeStateDetector; //眼睛状态检测
seeta::QualityRule* qualityClarity; //清晰度评估(传统)
seeta::QualityRule* qualityBright; //明亮度评估(传统)
seeta::QualityRule* qualityResolution; //分辨率评估
seeta::QualityOfPose* qualityPose; //姿态评估(传统)
seeta::QualityOfIntegrity* qualityIntegrity; //完整性评估
};
3> jni 文件
BitmapToMatrix/MatrixToBitmap 图像转换函数
#include <jni.h>
#include <string>
#include <android/asset_manager_jni.h>
#include <android/bitmap.h>
#include <android/log.h>
#include <seeta/FaceDetector.h>
#include <seeta/FaceLandmarker.h>
#include <seeta/FaceAntiSpoofing.h>
#include <seeta/Common/Struct.h>
#include "seetaface.hpp"
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/opencv.hpp>
using namespace seeta::SEETA_FACE_DETECTOR_NAMESPACE_VERSION;
using namespace std;
using namespace cv;
static Seetaface seetaNet;
#define ASSERT(status, ret) if (!(status)) { return ret; }
#define ASSERT_FALSE(status) ASSERT(status, false)
bool BitmapToMatrix(JNIEnv * env, jobject obj_bitmap, cv::Mat & matrix) {
void * bitmapPixels; // Save picture pixel data
AndroidBitmapInfo bitmapInfo; // Save picture parameters
ASSERT_FALSE( AndroidBitmap_getInfo(env, obj_bitmap, &bitmapInfo) >= 0); // Get picture parameters
ASSERT_FALSE( bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888
|| bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGB_565 ); // Only ARGB? 8888 and RGB? 565 are supported
ASSERT_FALSE( AndroidBitmap_lockPixels(env, obj_bitmap, &bitmapPixels) >= 0 ); // Get picture pixels (lock memory block)
ASSERT_FALSE( bitmapPixels );
if (bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
cv::Mat tmp(bitmapInfo.height, bitmapInfo.width, CV_8UC4, bitmapPixels); // Establish temporary mat
tmp.copyTo(matrix); // Copy to target matrix
} else {
cv::Mat tmp(bitmapInfo.height, bitmapInfo.width, CV_8UC2, bitmapPixels);
cv::cvtColor(tmp, matrix, cv::COLOR_BGR5652RGB);
}
//convert RGB to BGR
cv::cvtColor(matrix,matrix,cv::COLOR_RGB2BGR);
AndroidBitmap_unlockPixels(env, obj_bitmap); // Unlock
return true;
}
bool MatrixToBitmap(JNIEnv * env, cv::Mat & matrix, jobject obj_bitmap) {
void * bitmapPixels; // Save picture pixel data
AndroidBitmapInfo bitmapInfo; // Save picture parameters
ASSERT_FALSE( AndroidBitmap_getInfo(env, obj_bitmap, &bitmapInfo) >= 0); // Get picture parameters
ASSERT_FALSE( bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888
|| bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGB_565 ); // Only ARGB? 8888 and RGB? 565 are supported
ASSERT_FALSE( matrix.dims == 2
&& bitmapInfo.height == (uint32_t)matrix.rows
&& bitmapInfo.width == (uint32_t)matrix.cols ); // It must be a 2-dimensional matrix with the same length and width
ASSERT_FALSE( matrix.type() == CV_8UC1 || matrix.type() == CV_8UC3 || matrix.type() == CV_8UC4 );
ASSERT_FALSE( AndroidBitmap_lockPixels(env, obj_bitmap, &bitmapPixels) >= 0 ); // Get picture pixels (lock memory block)
ASSERT_FALSE( bitmapPixels );
if (bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
cv::Mat tmp(bitmapInfo.height, bitmapInfo.width, CV_8UC4, bitmapPixels);
switch (matrix.type()) {
case CV_8UC1: cv::cvtColor(matrix, tmp, cv::COLOR_GRAY2RGBA); break;
case CV_8UC3: cv::cvtColor(matrix, tmp, cv::COLOR_RGB2RGBA); break;
case CV_8UC4: matrix.copyTo(tmp); break;
default: AndroidBitmap_unlockPixels(env, obj_bitmap); return false;
}
} else {
cv::Mat tmp(bitmapInfo.height, bitmapInfo.width, CV_8UC2, bitmapPixels);
switch (matrix.type()) {
case CV_8UC1: cv::cvtColor(matrix, tmp, cv::COLOR_GRAY2BGR565); break;
case CV_8UC3: cv::cvtColor(matrix, tmp, cv::COLOR_RGB2BGR565); break;
case CV_8UC4: cv::cvtColor(matrix, tmp, cv::COLOR_RGBA2BGR565); break;
default: AndroidBitmap_unlockPixels(env, obj_bitmap); return false;
}
}
AndroidBitmap_unlockPixels(env, obj_bitmap); // Unlock
return true;
}
void draw_box(cv::Mat &img, SeetaRect& box) {
// 绘制单个人脸框
cv::rectangle(img, cv::Point2i{box.x, box.y}, cv::Point2i{box.width+box.x, box.height+box.y}, cv::Scalar(0, 255, 255), 3, 8, 0);
}
void draw_points(cv::Mat &img, std::vector<SeetaPointF> &pts) {
// 绘制特征点
cv::Scalar color=cv::Scalar(225, 0, 225);
if(pts.size()==68){
color=cv::Scalar(225, 0, 0);
}
for (int j = 0; j < pts.size(); ++j) {
cv::circle(img, cv::Point2d(pts[j].x, pts[j].y), 2, color, -1, 8,0);
}
}
//com.example.testcamera
extern "C" JNIEXPORT jboolean
Java_com_example_camerx_SeetaFace_loadModel(JNIEnv *env,jobject thiz,jstring cstapath,jobjectArray string_array) {
//模型初始化
const char *modelpath = env->GetStringUTFChars(cstapath, 0); //jstring 转char*
seetaNet.Init_face(modelpath); //加载模型
jint strlength = env->GetArrayLength(string_array); //string_array 为要加载模型名称的字符串数组
for (int i = 0; i < strlength; ++i) {
jstring str = static_cast<jstring>(env->GetObjectArrayElement(string_array, i));
const char* func = env->GetStringUTFChars(str,NULL);
LOGI("获取java的参数:%s",func);
string res = seetaNet.Init(modelpath,func);
if(res !="ok"){
LOGE("输入模型:%s名称不对",func);
return JNI_FALSE;
}
else{
LOGI("%s初始化成功",func);
}
env->ReleaseStringUTFChars(str,func);
}
env->ReleaseStringUTFChars(cstapath, modelpath);
return JNI_TRUE;
}
//扩展的 1 detect 2 point5 4 point68 8 sex 16 age
extern "C" JNIEXPORT jint JNICALL
Java_com_example_camerx_SeetaFace_detectFaceEx(JNIEnv *env, jobject thiz, jobject input,jint iflag) {//,jobject bitmapOut
cv::Mat timage ,tcimage;
SeetaImageData tsimage;
SeetaRect tsbox ;
bool bit_cv = BitmapToMatrix(env,input,timage); //bitmap转cvMat,格式还为RGBA
cv::cvtColor(timage,tcimage,COLOR_RGBA2BGR); //
// LOGI("Java_com_example_testcamera_SeetaFace_detectFace start 2 ");
//cv图片转Seeta图片
tsimage.width = tcimage.cols;
tsimage.height = tcimage.rows;
tsimage.channels = tcimage.channels();
tsimage.data = tcimage.data;
SeetaFaceInfoArray faces = seetaNet.detect_face(tsimage); //调用人脸检测
if (faces.size<=0){
// LOGI("Java_com_example_testcamera_SeetaFace_detectFace start 3 ");
return -1;
}else{
auto face = faces.data[0];
tsbox = face.pos;
// float score = face.score;@
// int x1 = int(tsbox.x), y1 = int(tsbox.y), width = int(tsbox.width), height = int(tsbox.height);
// int bbox[4] = {x1, y1, width, height};
// jintArray boxInfo = env->NewIntArray(4);
// env->SetIntArrayRegion(boxInfo, 0, 4, bbox);
// return boxInfo;
}
int age = 0;
{
iflag |= 1 ;
if((iflag &1 ) > 0){
draw_box(tcimage, tsbox); //人脸框与五官画图
}
if((iflag &2 ) > 0){
std::vector<SeetaPointF> tpoints5 = seetaNet.detect_land5(tsimage, tsbox);
if((iflag &16 ) > 0){
age = seetaNet.predict_age(tsimage,tpoints5)*2;
}
if((iflag &8 ) > 0){
int sex = seetaNet.predict_gender_int(tsimage,tpoints5);
age += sex ;
}
draw_points(tcimage, tpoints5); //五官画图
}else if((iflag & 24) > 0 ){
std::vector<SeetaPointF> tpoints5 = seetaNet.detect_land5(tsimage, tsbox);
if((iflag &16 ) > 0){
age = seetaNet.predict_age(tsimage,tpoints5)*2;
}
if((iflag &8 ) > 0){
int sex = seetaNet.predict_gender_int(tsimage,tpoints5);
age += sex ;
}
}
if((iflag &4 ) > 0){
std::vector<SeetaPointF> points68 = seetaNet.detect_land68(tsimage, tsbox);
draw_points(tcimage, points68); //五官画图
}
// LOGI("Java_com_example_testcamera_SeetaFace_detectDraw 000 cols=%d rows=%d",imageOut.cols,imageOut.rows);
bool cv_bit = MatrixToBitmap(env,tcimage,input);
// LOGI("Java_com_example_testcamera_SeetaFace_detectDraw %d ",(int)cv_bit);
}
return age;
}
4: mk 文件
目录结构
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := SeetaQualityAssessor300
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libSeetaQualityAssessor300.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := SeetaAgePredictor600
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libSeetaAgePredictor600.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := SeetaAuthorize
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libSeetaAuthorize.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := SeetaEyeStateDetector200
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libSeetaEyeStateDetector200.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := SeetaFaceAntiSpoofingX600
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libSeetaFaceAntiSpoofingX600.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := SeetaFaceDetector600
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libSeetaFaceDetector600.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := SeetaFaceLandmarker600
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libSeetaFaceLandmarker600.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := SeetaGenderPredictor600
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libSeetaGenderPredictor600.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := SeetaMaskDetector200
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libSeetaMaskDetector200.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := SeetaPoseEstimation600
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libSeetaPoseEstimation600.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := tennis
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libtennis.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := omp
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libomp.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE:=opencv_java4
LOCAL_SRC_FILES:=$(TARGET_ARCH_ABI)/libopencv_java4.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := testseetaface
#LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_SRC_FILES := \
seetaface_jni_1.cpp
LOCAL_C_INCLUDES += $(LOCAL_PATH)/seetaface6/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/opencv
#LOCAL_LDLIBS := -llog
LOCAL_LDLIBS := -L$(TARGET_ARCH_ABI) -lSeetaQualityAssessor300 \
-lSeetaAgePredictor600 -lSeetaAuthorize -lSeetaEyeStateDetector200 \
-lSeetaFaceAntiSpoofingX600 -lSeetaFaceDetector600 -lSeetaFaceLandmarker600 \
-lSeetaGenderPredictor600 -lSeetaMaskDetector200 -lSeetaPoseEstimation600 -ltennis -lopencv_java4 \
-llog -landroid -ldl -ljnigraphics
include $(BUILD_SHARED_LIBRARY)
Application.mk
APP_ABI := armeabi-v7a,arm64-v8a
APP_PLATFORM := android-23
APP_STL := c++_shared
APP_CPPFLAGS := -fexceptions -frtti
#APP_CPPFLAGS := -fexceptions -fno-rtti
APP_CPPFLAGS +=-std=c++11
APP_CPPFLAGS +=-fopenmp -static-openmp
5:ndk-build
生成
6:下章 创建android 工程测试
更多推荐
已为社区贡献11条内容
所有评论(0)