net.facelib.faceapi.client.FaceApiMtfClient Maven / Gradle / Ivy
The newest version!
package net.facelib.faceapi.client;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.gitee.l0km.com4j.base.ResourcePool.IntResourcePool;
import com.gitee.l0km.com4j.base.SimpleLog;
import com.gitee.l0km.com4j.base.BaseVolatile;
import com.gitee.l0km.com4j.base.ILazyInitVariable;
import com.gitee.l0km.ximage.BaseLazyImage;
import com.gitee.l0km.ximage.ImageErrorException;
import com.gitee.l0km.ximage.MatType;
import com.gitee.l0km.ximage.NotFoundLazyImageFactoryException;
import com.gitee.l0km.ximage.UnsupportedFormatException;
import com.gitee.l0km.xthrift.thrift.ClientFactory;
import net.facelib.mtfsdk.jna.ZymtffacedetectsdkLibrary;
import net.facelib.sdk.ImageSupport;
import net.gdface.sdk.BaseFaceApi;
import net.gdface.sdk.CapacityFieldConstant;
import net.gdface.sdk.CodeInfo;
import net.gdface.sdk.ContextLoader;
import net.gdface.sdk.ContextLoader.GlobalContextField;
import net.gdface.sdk.FseResult;
import net.gdface.sdk.NotFaceDetectedException;
import net.gdface.sdk.thrift.FaceApiThriftClient;
import static com.gitee.l0km.com4j.base.ConditionChecks.checkNotNull;
/**
* {@link net.gdface.sdk.FaceApi} rpc client实现类(线程安全)
* 人脸检测基于MTFSDK本地实现,特征提取和比对以及特征搜索调用RPC实现
* @author guyadong
*
*/
public class FaceApiMtfClient extends BaseFaceApi implements MtfsdkConstant,CapacityFieldConstant {
/**
* RPC client 实例
*/
private FaceApiThriftClient client;
/**
* 用于人脸检测的数据缓冲区
*/
private static final ThreadLocal faceBuffer = new ThreadLocal(){
@Override
protected float[] initialValue() {
return new float[FDDATA_LEN*MAX_FACE_COUNT];
}
};
private static final ZymtffacedetectsdkLibrary JNA = ZymtffacedetectsdkLibrary.INSTANCE;
/** 管理通道的资源池对象 */
private static final IntResourcePool channelPool ;
/**
* 初始化状态
*/
private static volatile SdkStatus status =SdkStatus.SDK_UNINITIALIZED;
/**
* 返回并发参数
* 先尝试从全局上下文({@link ContextLoader})获取,
* 如果找不到则返回1作为默认值
*/
private static final ILazyInitVariable concurrency = new BaseVolatile(){
@Override
protected Integer doGet() {
Integer concurrency = ContextLoader.getGlobalContext(GlobalContextField.CONCURRENCY);
return null == concurrency ? 1 : concurrency;
}};
/**
* 返回人脸检测倾向
* 先尝试从全局上下文({@link ContextLoader})获取,
* 如果找不到则返回true为默认值
*/
private static final ILazyInitVariable mulitFaceDetectDrend = new BaseVolatile(){
@Override
protected Integer doGet() {
Boolean mfd = ContextLoader.getGlobalContext(GlobalContextField.MULIT_FACE_DETECT_TREND);
if(null == mfd){
return 0;
}
return mfd ? 0 : 1;
}};
/**
* 构造方法
* 通过该方法可以更精确控制创建 FaceApi 服务客户端的参数
* @param factory 客户端工厂类实例
*/
public FaceApiMtfClient(ClientFactory factory) {
init(factory);
}
/**
* 构造方法
* 使用指定的主机名和端口号构造客户端实例
* @param host FaceApi 服务主机名
* @param port FaceApi 服务端口号
*/
public FaceApiMtfClient(String host, int port) {
init(host, port);
}
/**
* 默认构造方法
* NOTE:使用该方法构造的实例时,FaceApi 服务客户端没有初始化,调用远端服务会抛出{@link IllegalStateException}异常,
* 可以使用 {@link #init(String, int)}或 {@link #init(ClientFactory)}方法初始化FaceApi 服务客户端.
*/
public FaceApiMtfClient() {
this.client = null;
}
/**
* 使用指定的主机名和端口号初始化 FaceApi 服务客户端实例
* @param factory
* @return 当前对象
*/
public FaceApiMtfClient init(ClientFactory factory) {
this.client = new FaceApiThriftClient(factory);
return this;
}
/**
* 使用指定的主机名和端口号初始化 FaceApi 服务客户端实例
* @param host FaceApi 服务主机名
* @param port FaceApi 服务端口号
* @return 当前对象
*/
public FaceApiMtfClient init(String host, int port){
this.client = new FaceApiThriftClient(host, port);
return this;
}
private FaceApiThriftClient getClient() {
if(null == client){
throw new IllegalStateException("client is uninitialized");
}
return client;
}
/**
* 测试 FaceApi 服务端是否可连接
* FaceApi 服务客户端未初始化时返回{@code false}
* @return FaceApi 服务可连接则返回{@code true},否则返回{@code false}
*/
public boolean testConnect() {
return null == client ? false : client.testConnect();
}
/**
* 以 ${host}:${port} 格式返回FaceApi 服务主机名和端口号
* @return ${host}:${port} 格式字符串
*/
public String getServiceLocation(){
return getClient().getFactory().getHostAndPort().toString();
}
/**
* 对输入图像进行人脸检测,检测结果由rect返回,
* 检测到人脸返回大于0的人脸个数,
* 返回的是21个元素的脸部信息
* @param matType 图像矩阵类型
* @param imgMatrix 待检测图像BGR格式
* @param width 图像宽度
* @param height 图像高度
* @param detectMod 0:用于普通多人脸检测,1:用于帧相关单人脸检测
*/
protected void nativeDetectFace(MatType matType, byte[] imgMatrix, int width, int height,List faceInfo, int detectMod) {
float[] buffer = faceBuffer.get();
try {
byte[] bgr = ImageSupport.toBGR(matType, imgMatrix, width, height);
int ret = JNA.FDDetect(channelPool.apply(),ByteBuffer.wrap(bgr), width, height, 3, detectMod, FloatBuffer.wrap(buffer));
if(ret<0){
throw new SdkRuntimeException(SdkStatus.jniCode(ret));
}
for(int i=0; i < ret; ++i){
faceInfo.add(new NativeFaceInfo(buffer, i*FDDATA_LEN));
}
} finally {
channelPool.free();
}
}
/**
* SDK初始化
* @throws SdkInitException
*/
private static void init() throws SdkInitException{
// double check
if(SdkStatus.SDK_INIT_OK != status){
synchronized (FaceApiMtfClient.class) {
if(SdkStatus.SDK_INIT_OK != status){
SimpleLog.log("MTFSDK native library initlalizing,wait a moment please.");
int channelNum = concurrency.get();
int channelId=0;
try {
for(; channelId < channelNum; ++channelId){
System.out.print('.');
int ret = JNA.FSSDKInit(channelId);
if(ret != 0){
throw new SdkRuntimeException(SdkStatus.jniCode(ret));
}
}
// 初始化成功则置状态为成功
status = SdkStatus.SDK_INIT_OK ;
} finally {
System.out.println();
// 如果没有完成循环代表初始化不完全成功,则执行release
if( channelId < channelNum){
for(int i=0;i
*
* @param lazyImg
* 图像对象,为{@code null}时抛出{@link IllegalArgumentException}
* @return 没有检测到人脸则返回空表
* @throws UnsupportedFormatException
*/
public List detectFace(BaseLazyImage lazyImg)
throws UnsupportedFormatException {
checkNotNull(lazyImg, "lazyImg is null");
List faceInfo = new ArrayList();
if (lazyImg.getWidth() > 0 && lazyImg.getHeight() > 0) {
byte[] imgMatrix = lazyImg.getMatrixData(getNativeMatrixType());
nativeDetectFace(getNativeMatrixType(),imgMatrix, lazyImg.getWidth(),lazyImg.getHeight(),faceInfo, mulitFaceDetectDrend.get());
}
return faceInfo;
}
/**
* 对平台支持的图像对象进行人脸检测
* @param 在标准java平台下,为 {@code java.awt.image.BufferedImage}
* 在android平台下为{@code android.graphics.Bitmap}
* @param imgObj 图像对象
* @return 没有检测到人脸则返回空表
* @throws IllegalArgumentException imgObj不是平台支持的类型
*/
public List detectFaceByImage(T imgObj) {
try {
BaseLazyImage lazyImg = BaseLazyImage.getLazyImageFactory().createByImageObject(imgObj);
return detectFace(lazyImg);
} catch (UnsupportedFormatException | NotFoundLazyImageFactoryException e) {
throw new RuntimeException(e);
}
}
@Override
public CodeInfo[] detectFace(byte[] imgData) throws ImageErrorException {
BaseLazyImage lazyImg = makeOpenedLazyImage(imgData);
try {
return detectFace(lazyImg).toArray(new CodeInfo[0]);
} finally {
try {
lazyImg.close();
lazyImg = null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Override
public CodeInfo[] detectAndGetCodeInfo(byte[] imgData, int faceNum)
throws ImageErrorException, NotFaceDetectedException {
CodeInfo[] codes = detectFace(imgData);
if((faceNum == 0 && 0 == codes.length) || (faceNum > 0 && faceNum > codes.length)){
throw new NotFaceDetectedException();
}
codes = getClient().getCodeInfo(imgData, faceNum, codes);
// 没有检测到要求的人脸则抛出异常
if(0 == codes.length){
throw new NotFaceDetectedException();
}
if(faceNum>0 && faceNum != codes.length){
throw new NotFaceDetectedException();
}
return codes;
}
@Override
public Boolean wearMask(byte[] imgData, CodeInfo faceInfo) throws ImageErrorException {
return getClient().wearMask(imgData, faceInfo);
}
@Override
public CodeInfo[] getCodeInfo(byte[] imgData, int faceNum, CodeInfo[] facePos) throws NotFaceDetectedException {
return getClient().getCodeInfo(imgData, faceNum, facePos);
}
@Override
public CodeInfo getCodeInfo(byte[] imgData, CodeInfo facePos) {
return getClient().getCodeInfo(imgData, facePos);
}
@Override
public CodeInfo[] matDetectFace(MatType matType, byte[] matData, int width, int height,int facenum)
throws NotFaceDetectedException{
List faceInfo = new ArrayList<>();
nativeDetectFace(matType,matData,width,height,faceInfo, mulitFaceDetectDrend.get());
// 没有检测到要求的人脸则抛出异常
if(faceInfo.isEmpty()){
throw new NotFaceDetectedException();
}
if(facenum>0 && facenum != faceInfo.size()){
throw new NotFaceDetectedException();
}
return faceInfo.toArray(new CodeInfo[0]);
}
@Override
public CodeInfo[] matDetectFace(MatType matType, byte[] matData, int width, int height) {
List faceInfo = new ArrayList<>();
nativeDetectFace(matType,matData,width,height,faceInfo, mulitFaceDetectDrend.get());
return faceInfo.toArray(new CodeInfo[0]);
}
@Override
public CodeInfo[] matGetCodeInfo(MatType matType, byte[] matData, int width, int height, int facenum, CodeInfo[] facePos) throws NotFaceDetectedException {
return getClient().matGetCodeInfo(matType, matData, width, height, facenum, facePos);
}
@Override
public CodeInfo matGetCodeInfo(MatType matType, byte[] matData, int width, int height, CodeInfo facePos) {
return getClient().matGetCodeInfo(matType, matData, width, height, facePos);
}
@Override
public CodeInfo[] matDetectAndGetCodeInfo(MatType matType, byte[] matData, int width, int height)
{
CodeInfo[] codes = matDetectFace(matType, matData, width, height);
try {
return getClient().matGetCodeInfo(matType, matData, width, height, codes.length, codes);
} catch (NotFaceDetectedException e) {
return new CodeInfo[0];
}
}
@Override
public CodeInfo[] matDetectAndGetCodeInfo(MatType matType, byte[] matData, int width, int height, int faceNum)
throws NotFaceDetectedException {
CodeInfo[] codes = matDetectFace(matType, matData, width, height);
codes = getClient().matGetCodeInfo(matType, matData, width, height, faceNum,codes);
// 没有检测到要求的人脸则抛出异常
if(0 == codes.length){
throw new NotFaceDetectedException();
}
if(faceNum>0 && faceNum != codes.length){
throw new NotFaceDetectedException();
}
return codes;
}
@Override
public Boolean matWearMask(MatType matType, byte[] matData, int width, int height, CodeInfo faceInfo) {
return getClient().matWearMask(matType, matData, width, height, faceInfo);
}
@Override
public FseResult[] searchFeatures(byte[] feature, double similarty, int rows, String[] imgMD5Set, int group) {
return getClient().searchFeatures(feature, similarty, rows, imgMD5Set, group);
}
@Override
public Map sdkCapacity() {
Map capacity = getClient().sdkCapacity();
capacity.put(C_LOCAL_DETECT, Boolean.TRUE.toString());
return capacity;
}
static {
try {
init();
} catch (SdkInitException e) {
SimpleLog.log(e);
throw new ExceptionInInitializerError(e);
}
// add hook for release JNI resource on JVM shutdown
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
SimpleLog.log("release mtfsdk JNI resoure...");
for(int channelId = 0,channelNum = concurrency.get();channelId < channelNum;++channelId){
JNA.FSSDKRelease(channelId);
}
// 如果用 logger 输出不能显示,why?
SimpleLog.log("release mtfsdk JNI resoure finished");
}
});
// 根据配置参数初始化通道资源池
channelPool = new IntResourcePool(concurrency.get());
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("FaceApiMtfClient [client=");
builder.append(client);
builder.append("]");
return builder.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy