net.gdface.cassdk.JniBridge Maven / Gradle / Ivy
package net.gdface.cassdk;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.LinkedHashMap;
import com.sun.jna.NativeLibrary;
import net.gdface.cassdk.jna.EF_Param;
import net.gdface.cassdk.jna.THFI_FacePos;
import net.gdface.cassdk.jna.THFI_Param;
import net.gdface.cassdk.jna.THFI_Param_Ex;
import net.gdface.cassdk.jna.THFaceImageLibrary;
import net.gdface.cassdk.jna.THFeatureLibrary;
import net.gdface.utils.ResourcePool.IntResourcePool;
/**
* CASSDK 算法(线程安全)的调用实现
* 参见CASSDK的"THFaceImage_i.h","THFeature_i.h"
* @author guyadong
*
*/
public class JniBridge extends BaseJniBridge{
private static final THFaceImageLibrary faceImageLibrary;
protected static THFeatureLibrary featureLibrary;
static {
String cassdkBin = CASSDK_HOME + "/bin";
NativeLibrary.addSearchPath("THFaceImage", cassdkBin);
NativeLibrary.addSearchPath("THFeature", cassdkBin);
faceImageLibrary = THFaceImageLibrary.INSTANCE;
featureLibrary = THFeatureLibrary.INSTANCE;
FEATURE_SIZE = featureLibrary.EF_Size();
logger.info(String.format("FEATURE_SIZE=%d bytes", FEATURE_SIZE));
// 从配置文件读取初始化通道数
init();
// add hook for release JNI resource on JVM shutdown
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
logger.info("release cassdk JNI resoure...");
release();
// 如果用 logger 输出不能显示,why?
System.out.println("release cassdk JNI resoure finished");
}
});
}
/**
* 从参数配置对象中读取参数创建{@link THFIParam}
* @return THFIParam
*/
private static THFI_Param createTHFIParam(){
THFI_Param param= new THFI_Param();
param.nMinFaceSize = CasConfig.getMinFaceSize();
param.nRollAngle = CasConfig.getRollAngle();
return param;
}
/**
* 从参数配置对象中读取参数创建{@link THFI_Param_Ex}
* @return THFI_Param_Ex
*/
private static THFI_Param_Ex createTHFIParamEx(){
THFI_Param_Ex param= new THFI_Param_Ex(createTHFIParam(),CasConfig.getDeviceID());
return param;
}
private static EF_Param createEFParam(){
EF_Param param = new EF_Param(CasConfig.getDeviceID());
return param;
}
/**
* 人脸识别模块(THFaceImage和THFeature)初始化, 只需在应用程序初始化时调用一次
*
* @throws ExceptionInInitializerError
* @see #initTHFaceImage(int, int, THFIParam)
* @see #initTHFeature(int)
* @return 初始化失败则抛出异常{@link ExceptionInInitializerError}
*/
private static void init() throws ExceptionInInitializerError {
logger.info(String.format("CASSDK %s initializing(SDK正在初始化)",CasConfig.getVersion().version));
logger.info(String.format("model initializing(模型数据初始化) MODE:%s",
CasConfig.isGpuMode()?"GPU ID:"+CasConfig.getDeviceID():"CPU"));
int featureChannelNum = CasConfig.getFeatureChannelNum();
int detectChannelNum = CasConfig.getFeatureChannelNum();
try{
copyModelToUserDir();
{
logger.info(String.format("人脸检测通道: %d",detectChannelNum));
logger.info(String.format("特征提取通道: %d",featureChannelNum));
detectChannelPool = new IntResourcePool(detectChannelNum);
featureChannelPool = new IntResourcePool(featureChannelNum);
logger.info("detect module initializing(人脸检测模块初始化)");
int result = CasConfig.isGpuMode()
? faceImageLibrary.THFI_Create_Ex((short)detectChannelNum, createTHFIParamEx())
: faceImageLibrary.THFI_Create((short)detectChannelNum, createTHFIParam());
if(result <=0){
StringBuffer buffer = new StringBuffer("fail to initTHFaceImage,error code:");
buffer.append(result);
switch(result){
case -99:{
buffer.append("msg:").append(INVALID_LICENSE);
break;
}
default:
}
throw new JniException(buffer.toString());
}
}
{
logger.info("feature module initializing(特征提取模块初始化)");
short result = CasConfig.isGpuMode()
? featureLibrary.EF_Init_Ex(featureChannelNum,createEFParam())
: featureLibrary.EF_Init(featureChannelNum);
if(result <=0 ){
StringBuffer buffer = new StringBuffer("fail to initTHFeature,error code:");
buffer.append(result);
switch(result){
case -99:{
buffer.append("msg:").append(INVALID_LICENSE);
break;
}
case -1:{
buffer.append("msg:").append("open file \"feadb.db*\" error");
break;
}
case -2:{
buffer.append("msg:").append("check file \"feadb.db*\" error");
break;
}
case -3:{
buffer.append("msg:").append("read file \"feadb.db*\" error");
break;
}
default:
break;
}
throw new JniException(buffer.toString());
}
}
}catch(Exception e){
logger.info(String.format("%s:%s",e.getClass().getSimpleName(),e.getMessage()));
throw new ExceptionInInitializerError(e);
}
logger.info("CASSDK initialized(SDK初始化结束)");
}
/**
* 释放所有资源(THFeature SDK和THFaceImage SDK)
* JVM结束时会自动调用此方法
*/
private static void release(){
featureLibrary.EF_Release();
faceImageLibrary.THFI_Release();
}
/**
* 对图像进行人脸检测
* @param imgData
* 图像数据,长度必须为 (width*height*3),否则抛出异常 {@link IllegalArgumentException}
* @param bpp
* 图像像素位数(8或24),否则抛出异常 {@link IllegalArgumentException}
* @param width
* 图像宽度
* @param height
* 图像高度
* @return 返回人脸人眼等位置信息数组(没有检测到人脸则返回长度为0的空数组)
*/
public static THFI_FacePos[] detectFace(byte[] imgData, int bpp, int width, int height){
if(null == imgData){
throw new NullPointerException("imgData must not be null");
}
if(bpp != 24){
throw new IllegalArgumentException("must RGB24");
}
if(imgData.length != width*height*3){
throw new NullPointerException("INVALID imageData");
}
short nChannelID = detectChannelPool.apply().shortValue();
// logger.info(String.format("detectChannelD=%d", nChannelID));
THFI_FacePos[] pfps=new THFI_FacePos[maxDetectFaceNum];
try{
int result = faceImageLibrary.THFI_DetectFace(nChannelID,
ByteBuffer.wrap(imgData),
bpp,
width,
height,
pfps,
pfps.length,
sampleSize);
if(result<0){
StringBuffer buffer = new StringBuffer("fail to THFI_DetectFace,error code:");
buffer.append(result);
switch(result){
case -99:{
buffer.append("msg:").append(INVALID_LICENSE);
break;
}
case -1:{
buffer.append("msg:").append(INVALID_CHANNEL);
break;
}
case -2:{
buffer.append("msg:").append(INVALID_IMAGE);
break;
}
case -3:{
buffer.append("msg:").append(INVALID_FPS);
break;
}
default:
}
throw new JniException(buffer.toString());
}
return Arrays.copyOf(pfps, result);
}finally{
detectChannelPool.free();
}
}
/**
* 对人脸图像(RGB24)指定的位置进行特征提取
* @param imgData
* 图像数据,RGB24格式(注意不需要位图四字节对齐),在内存中人脸是正的,
* 长度必须为 (width*height*3),否则抛出异常 {@link IllegalArgumentException}
* @param width
* 图像宽度
* @param height
* 图像高度
* @param facePos
* 人脸位置信息,为{@code null}时抛出异常 {@link IllegalArgumentException}
*
* @return 人脸特征数组(提取失败返回null)
*/
public static byte[] getFaceFeature(byte[] imgData, int width, int height, THFI_FacePos facePos){
if(null == imgData){
throw new NullPointerException("imgData must not be null");
}
if(imgData.length != width*height*3){
throw new NullPointerException("INVALID imageData");
}
short nChannelID = featureChannelPool.apply().shortValue();
// logger.info(String.format("featureChannelD=%d", nChannelID));
ByteBuffer featureBuffer = ByteBuffer.allocate(FEATURE_SIZE);
try{
int result = featureLibrary.EF_Extract(nChannelID , ByteBuffer.wrap(imgData), width, height, 3, facePos, featureBuffer);
if(result<0){
StringBuffer buffer = new StringBuffer("fail to EF_Extract,error code:");
buffer.append(result);
switch(result){
case -99:{
buffer.append("msg:").append(INVALID_LICENSE);
break;
}
case -1:{
buffer.append("msg:").append(INVALID_PBUF);
break;
}
case -2:{
buffer.append("msg:").append(INVALID_CHANNEL);
break;
}
default:
}
throw new JniException(buffer.toString());
}
return featureBuffer.array();
}finally{
featureChannelPool.free();
}
}
/**
* 对图像(RGB24)中的一组人脸位置提取特征码
* @param imgData
* @param width
* @param height
* @param facePosArray
* 要提取特征的人脸位置对象数组,为{@code null}时抛出异常 {@link IllegalArgumentException}
*
* @see #getFaceFeature(byte[], int, int, FacePos)
* @return 返回facePosArray,对于提取到特征码的元素,{@link FacePos#feature}包含特征码数据,
* 特征提取失败则对应的 {@link FacePos#feature}为null
*/
public static byte[][] getFaceFeatures(byte[] imgData, int width, int height, THFI_FacePos[] facePosArray){
if(null == imgData ){
throw new NullPointerException("imgData must not be null");
}
if(null == facePosArray){
throw new NullPointerException("facePosArray must not be null");
}
if(imgData.length != width*height*3){
throw new NullPointerException("INVALID imageData");
}
short nChannelID = featureChannelPool.apply().shortValue();
// logger.info(String.format("featureChannelD=%d", nChannelID));
ByteBuffer featuresBuffer = ByteBuffer.allocate(FEATURE_SIZE*facePosArray.length);
try{
int result = featureLibrary.EF_Extract_M(nChannelID ,
ByteBuffer.wrap(imgData),
width,
height,
3,
facePosArray,
featuresBuffer,
facePosArray.length);
if(result<0){
StringBuffer buffer = new StringBuffer("fail to EF_Extract,error code:");
buffer.append(result);
switch(result){
case -99:{
buffer.append("msg:").append(INVALID_LICENSE);
break;
}
case -1:{
buffer.append("msg:").append(INVALID_PBUF);
break;
}
case -2:{
buffer.append("msg:").append(INVALID_CHANNEL);
break;
}
default:
}
throw new JniException(buffer.toString());
}
byte[][] features = new byte[facePosArray.length][FEATURE_SIZE];
for(int i = 0 ;i< facePosArray.length; ++i){
features[i] = Arrays.copyOfRange(featuresBuffer.array(),i*FEATURE_SIZE,(i + 1)*FEATURE_SIZE);
}
return features;
}finally{
featureChannelPool.free();
}
}
/**
* 对图像(RGB24)进行人脸检测并提取每个检测到的人脸特征码
* @param imgData
* @param width
* @param height
* @return 返回包含特征码数据的 {@link FacePos}对象数组(只包含成功提取特征码的位置对象)
* @see #detectFace(byte[], int, int, int, int)
* @see #getFaceFeatures(byte[], int, int, THFI_FacePos[])
*/
public static LinkedHashMap detectAndGetFeatures(byte[] imgData, int width, int height){
LinkedHashMap map = new LinkedHashMap(16);
THFI_FacePos[] facepos = detectFace(imgData,24,width,height);
byte[][] features = getFaceFeatures(imgData, width, height, facepos);
for(int i = 0 ;i< facepos.length ; ++i){
map.put(facepos[i], features[i]);
}
return map;
}
/**
* 人脸特征相似度比对
* 特征码为{@code null}或长度不等于 {@link #FEATURE_SIZE}则抛出{@link IllegalArgumentException}异常
* @param feature1
* 待比对的人脸特征
* @param feature2
* 待比对的人脸特征
* @return 两个人脸的相似度,范围在0-1之间,相似度值越高,表示比对的两个人脸越可能为同一个人
*/
public static float compareFeature(byte[] feature1, byte[] feature2){
if(null == feature1 || null == feature2){
throw new NullPointerException("feature1,feature2 must not be null");
}
if(feature1.length != FEATURE_SIZE || feature2.length != FEATURE_SIZE){
throw new IllegalArgumentException("INVALID length of feature1 or feature2");
}
return featureLibrary.EF_Compare(ByteBuffer.wrap(feature1), ByteBuffer.wrap(feature2));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy