001package net.gdface.mtfsdk; 002 003import static net.gdface.mtfsdk.MtfAndroidConfigProvider.*; 004 005import java.util.List; 006import org.apache.commons.pool2.impl.GenericObjectPool; 007import org.apache.commons.pool2.impl.GenericObjectPoolConfig; 008 009import android.graphics.Bitmap; 010import net.gdface.image.ImageErrorException; 011import net.gdface.image.LazyImage; 012import net.gdface.license.LicenseManager; 013import net.gdface.sdk.BaseFaceApiLocal; 014import net.gdface.sdk.CodeInfo; 015import net.gdface.sdk.FaceApiGenericDecorator; 016import net.gdface.sdk.NotFaceDetectedException; 017import net.gdface.sdk.fse.FeatureSe; 018import net.gdface.utils.ShareLock; 019 020/** 021 * MTFSDK android/arm平台 {@link net.gdface.sdk.FaceApi}接口实现类(线程安全)<br> 022 * 基于commons-pools将非线程安全的{@link MtfAndroidArmBridge}实例用资源池{@link GenericObjectPool}管理, 023 * 每次调用前从资源池申请一个{@link MtfAndroidArmBridge}实例来使用,用完后归还资源池, 024 * 以实现对{@link MtfAndroidArmBridge}实例线程安全的调用 025 * @author guyadong 026 * 027 */ 028public class FaceApiMtfAndroid extends BaseFaceApiLocal { 029 /** 030 * 用于人脸检测的数据缓冲区 031 */ 032 private static final ThreadLocal<double[]> faceBuffer = new ThreadLocal<double[]>(){ 033 034 @Override 035 protected double[] initialValue() { 036 return new double[FDDATA_LEN*MAX_FACE_COUNT]; 037 } 038 }; 039 private static final FaceApiMtfAndroid INSTANCE = new FaceApiMtfAndroid(); 040 private static final FaceApiGenericDecorator GENERIC_INSTANCE = new FaceApiGenericDecorator(INSTANCE); 041 protected FaceApiMtfAndroid() { 042 } 043 /** 044 * 返回单实例 045 * @return the instance 046 */ 047 public static FaceApiMtfAndroid getInstance() { 048 return INSTANCE; 049 } 050 051 /** 052 * 返回支持泛型封闭的单实例 053 * @return 054 */ 055 public static FaceApiGenericDecorator getGenericInstance(){ 056 return GENERIC_INSTANCE; 057 } 058 /** 059 * @return 返回SDK的授权信息管理实例 060 */ 061 public static LicenseManager licenseManager(){ 062 return MtfAndroidArmBridge.LICENSE_MANAGER; 063 } 064 @Override 065 public final String sdkVersion() { 066 return MtfAndroidArmBridge.SDK_VERSION; 067 } 068 @Override 069 protected double nativeCompareCode(byte[] code1, byte[] code2) { 070 return MtfAndroidArmBridge.FFSimilarityByte(code1,code2); 071 } 072 073 /** 074 * 人脸检测,最多返回三张人脸 075 */ 076 @Override 077 public void nativeDetectFace(byte[] imgMatrix, int width, int height, List<CodeInfo> faceInfo) { 078 nativeDetectFace(imgMatrix, width, height, faceInfo, 0); 079 } 080 @Override 081 public byte[] nativeGetFaceFeature(byte[] imgMatrix, int width, int height, CodeInfo faceInfo) { 082 NativeFaceInfo nativeFaceInfo = NativeFaceInfo.toNative(faceInfo); 083 MtfAndroidArmBridge instance = applyInstance(); 084 try{ 085 return instance.feaExtractByte(imgMatrix, width, height, nativeFaceInfo.nativeData); 086 }finally{ 087 returnInstance(instance); 088 } 089 } 090 091 /** 092 * 人脸检测,最多返回三张人脸 093 * @see MtfAndroidArmBridge#detect(byte[], int, int, int, double[]) 094 */ 095 public void nativeDetectFace(byte[] imgMatrix, int width, int height, List<CodeInfo> faceInfo,int detectMod) { 096 double[] buffer = faceBuffer.get(); 097 MtfAndroidArmBridge instance = applyInstance(); 098 try{ 099 int res = instance.detect(imgMatrix, width, height, detectMod, buffer); 100 for(int i=0; i < res; ++i){ 101 faceInfo.add(new NativeFaceInfo(buffer, i*FDDATA_LEN)); 102 } 103 }finally { 104 returnInstance(instance); 105 } 106 } 107 /** 108 * @deprecated replaced by {@link #nativeDetectMaxFace(byte[], int, int, int)} 109 */ 110 public NativeFaceInfo nativeDetectMaxFace(byte[] imgMatrix, int width, int height) { 111 return nativeDetectMaxFace(imgMatrix, width, height, 0); 112 } 113 /** 114 * 执行单人脸检测,返回最大人脸位置信息 115 * @param imgMatrix 116 * @param width 117 * @param height 118 * @param detectMod 0:用于检测图片,1:用于视频的循环检测 119 * @return 120 */ 121 public NativeFaceInfo nativeDetectMaxFace(byte[] imgMatrix, int width, int height, int detectMod) { 122 double[] buffer = faceBuffer.get(); 123 MtfAndroidArmBridge instance = applyInstance(); 124 try{ 125 int res = instance.detectMaxFace(imgMatrix, width, height, detectMod, buffer); 126 return res ==0 ? null : new NativeFaceInfo(buffer, 0); 127 }finally { 128 returnInstance(instance); 129 } 130 } 131 /** 132 * @deprecated replaced by {@link #detectMaxFace(Bitmap, int)} 133 */ 134 public NativeFaceInfo detectMaxFace(Bitmap bitmap) { 135 return detectMaxFace(bitmap,0); 136 } 137 /** 138 * @see #nativeDetectMaxFace(byte[], int, int, int) 139 */ 140 public NativeFaceInfo detectMaxFace(Bitmap bitmap, int detectMod) { 141 try { 142 byte[] matrix = getMatrixData(LazyImage.create(bitmap)); 143 return nativeDetectMaxFace(matrix,bitmap.getWidth(),bitmap.getHeight(), detectMod); 144 } catch (ImageErrorException e) { 145 throw new RuntimeException(e); 146 } 147 } 148 /** 149 * 位置关联 150 * 在人脸检测函数后用到,无论有无检测到人脸,均需要调用,从属于检测系列 151 * @param faceNums 人脸个数 152 * @param rectRate 153 * @param faceRect 人脸信息(人脸检测数据rect) 21个元素 154 * @param faceRectInfo [out]返回的标记,最后一个元素为是否成功比对的标志,其他为人脸信息 155 * @return 位置关联总个数 156 */ 157 public int facePosInterrelate(int faceNums, double rectRate, double[] faceRect, double[] faceRectInfo){ 158 MtfAndroidArmBridge instance = applyInstance(); 159 try{ 160 return instance.facePosInterrelate(faceNums, rectRate, faceRect, faceRectInfo); 161 }finally { 162 returnInstance(instance); 163 } 164 } 165 /** 166 * 从检测原始数据获取人脸角度 167 * @param BGR 168 * @param width 169 * @param height 170 * @param rect 人脸检测原始数据 171 * @param rectinfo [out] leng>=3,不可为null,返回3个元素 1: roll 2: yaw 3: pitch 172 * @return 173 * @deprecated 人脸检测原始数据中已经包含了人脸角度,不再需要此函数 174 */ 175 public int getDetectInfo(byte[] BGR, int width, int height, double[] rect, double[] rectinfo){ 176 MtfAndroidArmBridge instance = applyInstance(); 177 try{ 178 return instance.getDetectInfo(BGR, width, height, rect, rectinfo); 179 }finally { 180 returnInstance(instance); 181 } 182 } 183 public int setFacePosStatus(int facePos, int flag){ 184 MtfAndroidArmBridge instance = applyInstance(); 185 try{ 186 return instance.setFacePosStatus(facePos, flag); 187 }finally { 188 returnInstance(instance); 189 } 190 } 191 /** 192 * 执行活体检测 193 * @param BGR 输入的BGR格式图像 194 * @param width 图像宽度 195 * @param height 图像高度 196 * @param rect {@link #nativeDetectFace(byte[], int, int, List)}方法返回的人脸检测信息原始数据, 197 * 参见{@link NativeFaceInfo#getNativeData()} 198 * @return false--非活体,true--活体 199 */ 200 public boolean nativeLiveProcess(byte[] BGR, int width, int height, double[] rect){ 201 MtfAndroidArmBridge instance = applyInstance(); 202 try{ 203 return instance.liveProcess(BGR, width, height, rect); 204 }finally { 205 returnInstance(instance); 206 } 207 } 208 /** 209 * @see #nativeLiveProcess(byte[], int, int, double[]) 210 */ 211 public boolean liveProcess(Bitmap bitmap,NativeFaceInfo faceinfo) { 212 try { 213 byte[] matrix = getMatrixData(LazyImage.create(bitmap)); 214 return nativeLiveProcess(matrix,bitmap.getWidth(),bitmap.getHeight(),faceinfo.nativeData); 215 } catch (ImageErrorException e) { 216 throw new RuntimeException(e); 217 } 218 } 219 /** 220 * 检测人脸 221 * @param bitmap 222 * @return 223 * @see #detectFace(net.gdface.image.BaseLazyImage) 224 */ 225 public List<CodeInfo> detectFace(Bitmap bitmap){ 226 try { 227 return detectFace(LazyImage.create(bitmap)); 228 } catch (ImageErrorException e) { 229 throw new RuntimeException(e); 230 } 231 } 232 /** 233 * 提取特征 234 * @param bitmap 235 * @param faceNum 236 * @param facePos 237 * @return 238 * @throws NotFaceDetectedException 239 * @see #getCodeInfo(net.gdface.image.BaseLazyImage, int, List) 240 */ 241 public List<CodeInfo> getCodeInfo(Bitmap bitmap, int faceNum, List<CodeInfo> facePos) 242 throws NotFaceDetectedException{ 243 return getCodeInfo(LazyImage.create(bitmap), faceNum, facePos); 244 } 245 /** 246 * 提取单个人脸特征 247 * @param bitmap 248 * @param facePos 249 * @return 250 */ 251 public CodeInfo getCodeInfo(Bitmap bitmap, CodeInfo facePos){ 252 return getCodeInfo(LazyImage.create(bitmap), facePos); 253 } 254 /** 255 * 检测并提取人脸特征 256 * @param bitmap 257 * @param faceNum 258 * @return 259 * @throws NotFaceDetectedException 260 * @see {@link BaseFaceApiLocal#detectAndGetCodeInfo(net.gdface.image.BaseLazyImage, int)} 261 */ 262 public CodeInfo[] detectAndGetCodeInfo(Bitmap bitmap, int faceNum) 263 throws NotFaceDetectedException { 264 try { 265 return detectAndGetCodeInfo(LazyImage.create(bitmap).open(), faceNum); 266 } catch (ImageErrorException e) { 267 throw new RuntimeException(e); 268 } 269 } 270 @Override 271 public FeatureSe getFeatureSe() { 272 return FseMtfAndroidBridge.getFeatureSe(); 273 } 274 /** 275 * {@link MtfAndroidArmBridge}实例资源池<br> 276 * 每一次执行SDK调用时,要从资源池从申请一个{@link MtfAndroidArmBridge}实例,调用结束后归还到资源池, 277 * 实例的初始化和销毁都由资源负责,调用者不须负责{@link MtfAndroidArmBridge}实例的初始化与释放 278 */ 279 private static final GenericObjectPool<MtfAndroidArmBridge> pool; 280 static { 281 try { 282 // MTFSDK JNI接口静态区初始化 283 Class.forName(net.gdface.mtfsdk.MtfAndroidArmBridge.class.getName()); 284 } catch (ClassNotFoundException e) { 285 throw new ExceptionInInitializerError(e); 286 } 287 288 // 资源池初始化 289 GenericObjectPoolConfig config = new GenericObjectPoolConfig(); 290 // android平台不支持JMX,所以这里必须设置为false,否则抛出异常 291 config.setJmxEnabled(false); 292 pool = new GenericObjectPool<MtfAndroidArmBridge>(new BridgeInstanceFactory(),config); 293 294 // 根据配置参数设置并发线程数 295 int concurrenty = MtfAndroidArmBridge.CONFIG.getConcurrency(); 296 if(concurrenty > 0){ 297 concurrentLock = new ShareLock(concurrenty); 298 } 299 } 300 /** 301 * 从资源池申请一个实例 302 * @return 303 */ 304 private static MtfAndroidArmBridge applyInstance() { 305 try { 306 return pool.borrowObject(); 307 } catch(RuntimeException e){ 308 throw e; 309 }catch (Exception e) { 310 throw new RuntimeException(e); 311 } 312 } 313 /** 314 * 归还实例到资源池 315 * @param obj 316 */ 317 private static void returnInstance(MtfAndroidArmBridge obj) { 318 pool.returnObject(obj); 319 } 320}