001package net.gdface.mtfsdk; 002 003import net.gdface.license.GfLicenseProvider; 004import net.gdface.license.LicenseManager; 005import net.gdface.license.LicenseUtils; 006import net.gdface.utils.Judge; 007import net.gdface.utils.SampleLog; 008 009import static net.gdface.mtfsdk.MtfAndroidConfigProvider.*; 010 011import java.util.Iterator; 012import java.util.ServiceLoader; 013 014/** 015 * MTFSDK for android/arm JNI 接口<br> 016 * 此类为非线程安全的,不可多线程共享调用,需要在外部进一步封装实现线程安全 017 * @author guyadong 018 * 019 */ 020public class MtfAndroidArmBridge { 021 /** 022 * 应用层提供的{@link MtfAndroidConfigProvider}接口实例,未提供则初始化为{@link DefaultMtfAndroidConfig}实例 023 */ 024 public static final MtfAndroidConfigProvider CONFIG = getConfigProvider(); 025 /** 授权管理器接口实例 */ 026 public static final LicenseManager LICENSE_MANAGER = LicenseUtils.getLicenseManager(); 027 public static final String SDK_VERSION = "MTFSDK512"; 028 final long[] detectHandle = new long[1]; 029 final long[] featureHandle = new long[1]; 030 final long[] liveHandle = new long[1]; 031 032 /** 033 * 初始化状态 034 */ 035 private SdkStatus status=SdkStatus.SDK_UNINITIALIZED; 036 037 static { 038 try { 039 // 加载算法动态库 040 System.loadLibrary("FS_AndroidFaceSDK"); 041 } catch (Exception e) { 042 SampleLog.log(e.getMessage()); 043 throw new ExceptionInInitializerError(e); 044 } 045 // 要在动态库加载后调用,因为方法中可能会调用动态库中的函数 046 LICENSE_MANAGER.installLicenseIfSPIAvailable(); 047 048 } 049 050 MtfAndroidArmBridge() { 051 } 052 /** 053 * SPI(Service Provider Interface)机制加载 {@link GfLicenseProvider}实例,没有找到返回{@code null} 054 * @return 055 */ 056 private static GfLicenseProvider getLicenseProvider() { 057 ServiceLoader<GfLicenseProvider> providers = ServiceLoader.load(GfLicenseProvider.class); 058 Iterator<GfLicenseProvider> itor = providers.iterator(); 059 if(!itor.hasNext()){ 060 return null; 061 } 062 return itor.next(); 063 } 064 065 /** 066 * SPI(Service Provider Interface)机制加载 {@link GfLicenseProvider}实例, 067 * 没有找到返回{@link DefaultMtfAndroidConfig}实例 068 * @return 069 */ 070 private static MtfAndroidConfigProvider getConfigProvider() { 071 ServiceLoader<MtfAndroidConfigProvider> providers = ServiceLoader.load(MtfAndroidConfigProvider.class); 072 Iterator<MtfAndroidConfigProvider> itor = providers.iterator(); 073 if(!itor.hasNext()){ 074 return new DefaultMtfAndroidConfig(); 075 } 076 return itor.next(); 077 } 078 /** 079 * 返回当前SDK初始化状态 080 * @return 081 */ 082 SdkStatus getStatus() { 083 return status; 084 } 085 086 /** 087 * 如果input非0结尾则添加'\0'返回,否则返回input 088 * @param input 089 * @return 090 */ 091 private static String zeroEnd(String input){ 092 if(input == null){ 093 return null; 094 } 095 if(input.endsWith("\0")){ 096 return input; 097 } 098 return input+"\0"; 099 } 100 /** 101 * 执行SDK(检测模块)初始化 102 * @param licenseKey 授权关键字 103 * @param licenseCode 授权码 104 * @param instance 105 * @return 初始化状态 106 */ 107 static SdkStatus fdInit(String licenseKey, String licenseCode,MtfAndroidArmBridge instance) { 108 if(SdkStatus.SDK_INIT_OK == instance.status){ 109 return instance.status; 110 } 111 licenseCode = zeroEnd(licenseCode); 112 String szFilePath = "\0"; 113 String szCompanyNames = zeroEnd(licenseKey); 114 // 检测模块初始化 115 int fdFlag = FDInit(licenseCode.getBytes(), 116 szCompanyNames.getBytes(), 117 szFilePath.getBytes(), 118 szFilePath, 119 instance.detectHandle); 120 return SdkStatus.jniCode(fdFlag); 121 } 122 /** 123 * 执行SDK(识别模块)初始化 124 * @param licenseKey 授权关键字 125 * @param licenseCode 授权码 126 * @return 初始化状态 127 */ 128 private static SdkStatus ffInit(String licenseKey, String licenseCode,MtfAndroidArmBridge instance) { 129 if(SdkStatus.SDK_INIT_OK == instance.status){ 130 return instance.status; 131 } 132 licenseCode = zeroEnd(licenseCode); 133 String szFilePath = "\0"; 134 String szCompanyNames = zeroEnd(licenseKey); 135 int ffFlag = FFInit(licenseCode.getBytes(), 136 szCompanyNames.getBytes(), 137 szFilePath.getBytes(), 138 szFilePath, 139 instance.featureHandle); 140 return SdkStatus.jniCode(ffFlag); 141 } 142 /** 143 * 执行SDK(活体检测)初始化 144 * @param licenseKey 授权关键字 145 * @param licenseCode 授权码 146 * @return 初始化状态 147 */ 148 private static SdkStatus flInit(String licenseKey, String licenseCode,MtfAndroidArmBridge instance) { 149 if(SdkStatus.SDK_INIT_OK == instance.status){ 150 return instance.status; 151 } 152 licenseCode = zeroEnd(licenseCode); 153 String szFilePath = "\0"; 154 String szCompanyNames = zeroEnd(licenseKey); 155 int ffFlag = FLInit(licenseCode.getBytes(), 156 szCompanyNames.getBytes(), 157 szFilePath.getBytes(), 158 instance.liveHandle); 159 return SdkStatus.jniCode(ffFlag); 160 } 161 /** 162 * 执行SDK(检测模块,识别模块,活体检测模块)初始化,初始化失败则抛出异常 163 * @param licenseKey 授权关键字 164 * @param licenseCode 授权码 165 * @throws SdkInitException 166 */ 167 private void sdkInit(String licenseKey, String licenseCode) throws SdkInitException { 168 if(SdkStatus.SDK_INIT_OK == status){ 169 return; 170 } 171 SdkStatus fdStatus = SdkStatus.SDK_UNINITIALIZED; 172 SdkStatus ffStatus = SdkStatus.SDK_UNINITIALIZED; 173 SdkStatus flStatus = SdkStatus.SDK_UNINITIALIZED; 174 try{ 175 fdStatus = fdInit(licenseKey, licenseCode, this); 176 if(fdStatus !=SdkStatus.SDK_INIT_OK){ 177 status = fdStatus; 178 SampleLog.log("detect module: {}", status.msg); 179 throw new SdkInitException(status); 180 } 181 182 if(CONFIG.getDetectThreadNumber() >= 0){ 183 FDSetThreadsNumber(detectHandle[0], CONFIG.getDetectThreadNumber()); 184 } 185 if(CONFIG.getMinFaceSize() >= 0){ 186 FDSetMinFaceSize(detectHandle[0], CONFIG.getMinFaceSize()); 187 } 188 ffStatus = ffInit(licenseKey, licenseCode, this); 189 if(ffStatus !=SdkStatus.SDK_INIT_OK){ 190 status = ffStatus; 191 SampleLog.log("feature module: {}", status.msg); 192 throw new SdkInitException(status); 193 } 194 195 if(CONFIG.needLive()){ 196 flStatus = flInit(licenseKey, licenseCode, this); 197 if(flStatus !=SdkStatus.SDK_INIT_OK){ 198 status = flStatus; 199 SampleLog.log("live module: {}", status.msg); 200 throw new SdkInitException(status); 201 } 202 }else{ 203 SampleLog.log("SKIP initialization for live module(跳过活体检测模块初始化)"); 204 } 205 // 检测模块和识别模块都初始化成功则置状态为成功 206 status = SdkStatus.SDK_INIT_OK; 207 }finally { 208 // 如果没有完全初始化成功,则destroy已经初始化模块 209 if(status != SdkStatus.SDK_INIT_OK){ 210 if(fdStatus !=SdkStatus.SDK_INIT_OK){ 211 FDDestroy(detectHandle[0]); 212 } 213 if(ffStatus !=SdkStatus.SDK_INIT_OK){ 214 FFDestroy(featureHandle[0]); 215 } 216 if(flStatus !=SdkStatus.SDK_INIT_OK){ 217 FLDestroy(liveHandle[0]); 218 } 219 } 220 } 221 } 222 /** 223 * SDK初始化<br> 224 * 初始化前{@link #licenseKey}和{@link licenseCode}必须已经初始化 225 * @return 226 * @throws SdkInitException 227 */ 228 MtfAndroidArmBridge init() throws SdkInitException{ 229 String licenseKey = LICENSE_MANAGER.getLicenseKey(); 230 String licenseCode = LICENSE_MANAGER.getLicenseCode(); 231 232 if(Judge.isEmpty(licenseKey)){ 233 throw new SdkInitException("EMPTY licenseKey,must call setLicenseKey() firstly"); 234 } 235 if(Judge.isEmpty(licenseCode)){ 236 throw new SdkInitException("EMPTY licenseCode,must call setLicenseCode() firstly"); 237 } 238 sdkInit(licenseKey, licenseCode); 239 return this; 240 } 241 /** 242 * 人脸识别SDK资源释放 243 * @see #FDDestroy(long) 244 * @see #FFDestroy(long) 245 * @see #FLDestroy(long) 246 */ 247 void destroy() { 248 if(SdkStatus.SDK_INIT_OK == status){ 249 status = SdkStatus.SDK_UNINITIALIZED; 250 FDDestroy(detectHandle[0]); 251 FFDestroy(featureHandle[0]); 252 FLDestroy(liveHandle[0]); 253 } 254 } 255 //////////////////////////////////// 256 /** 257 * 对输入图像进行人脸检测,检测结果由rect返回, 258 * 检测到人脸返回>0的人脸个数, 259 * 返回的是21个元素的脸部信息 可配合FDGetDetectInfo拿到人脸角度 人脸数量<=3 260 * @param BGR 待检测图像BGR格式 261 * @param width 图像宽度 262 * @param height 图像高度 263 * @param detectMod 0:用于检测图片,1:用于视频的循环检测 264 * @param rect 用于返回人脸位置及关键点信息 265 * @return 检测到的人脸个数(>0)或者错误代码(< 0),see also {@link SdkStatus} 266 * @see #FDDetect(long, byte[], int, int, int, double[]) 267 268 */ 269 public int detect(byte[] BGR, int width, int height, int detectMod, double[] rect){ 270 int ret = FDDetect(detectHandle[0], BGR, width, height, detectMod, rect); 271 if(ret<0){ 272 throw new SdkRuntimeException(SdkStatus.jniCode(ret)); 273 } 274 return ret; 275 } 276 277 /** 278 * 对输入图像进行人脸检测返回最大人脸,检测结果由rect返回, 279 * @param BGR 待检测图像BGR格式 280 * @param width 图像宽度 281 * @param height 图像高度 282 * @param detectMod 0:用于检测图片,1:用于视频的循环检测 283 * @param rect 用于返回人脸位置及关键点信息 284 * @return 检测到的人脸个数1,或者错误代码(< 0),see also {@link SdkStatus} 285 * @see #FDDetectMaxFace(long, byte[], int, int, int, double[]) 286 */ 287 public int detectMaxFace(byte[] BGR, int width, int height, int detectMod, double[] rect){ 288 int ret = FDDetectMaxFace(detectHandle[0], BGR, width, height, detectMod, rect); 289 if(ret<0){ 290 throw new SdkRuntimeException(SdkStatus.jniCode(ret)); 291 } 292 return ret; 293 } 294 /** 295 * @deprecated 人脸检测原始数据中已经包含了人脸角度,不再需要此函数 296 * @see #FDGetDetectInfo(long, byte[], int, int, double[], double[]) 297 */ 298 public int getDetectInfo(byte[] BGR, int width, int height, double[] rect, double[] rectinfo){ 299 int ret = FDGetDetectInfo(detectHandle[0], BGR, width, height, rect, rectinfo); 300 if(ret<0){ 301 throw new SdkRuntimeException(SdkStatus.jniCode(ret)); 302 } 303 return ret; 304 } 305 /** 306 * @param BGR 307 * @param width 308 * @param height 309 * @param rect 310 * @return 311 * @deprecated replaced by {@link #feaExtractByte(byte[], int, int, double[])} 312 */ 313 public double[] feaExtract(byte[] BGR, int width, int height, double[] rect){ 314 double[] buffer = new double[FEATURE_LEN]; 315 int ret = FFFeaExtract(featureHandle[0], BGR, width, height, buffer, rect); 316 if(ret<0){ 317 throw new SdkRuntimeException(SdkStatus.jniCode(ret)); 318 } 319 return buffer; 320 } 321 public byte[] feaExtractByte(byte[] BGR, int width, int height, double[] rect){ 322 byte[] buffer = new byte[FEATURE_BYTES]; 323 int ret = FFFeaExtractByte(featureHandle[0], BGR, width, height, buffer, rect); 324 if(ret<0){ 325 throw new SdkRuntimeException(SdkStatus.jniCode(ret)); 326 } 327 return buffer; 328 } 329 public int facePosInterrelate(int faceNums, double rectRate, double[] faceRect, double[] faceRectInfo){ 330 int ret = FSFacePosInterrelate(detectHandle[0], faceNums, rectRate,faceRect, faceRectInfo); 331 if(ret<0){ 332 throw new SdkRuntimeException(SdkStatus.jniCode(ret)); 333 } 334 return ret; 335 } 336 public int setFacePosStatus(int facePos, int flag){ 337 int ret = FSSetFacePosStatus(detectHandle[0], facePos, flag); 338 if(ret<0){ 339 throw new SdkRuntimeException(SdkStatus.jniCode(ret)); 340 } 341 return ret; 342 } 343 public boolean liveProcess(byte[] BGR, int width, int height, double[] rect){ 344 double[] socre = new double[1]; 345 int ret = FLProcess(liveHandle[0], BGR, width, height, rect, socre); 346 if(ret<0){ 347 throw new SdkRuntimeException(SdkStatus.jniCode(ret)); 348 } 349 return ret == 1; 350 } 351 /////////////////////////////////// 352 /** 353 * 获取设备加密信息 354 * @param encryptionSerial [out]获取的设备加密信息 355 * @param licenseKey 授权关键字 356 * @return 成功(>0 加密信息长度)或错误代码(< 0) 357 */ 358 public static native int FSGetDevicesSerial(byte[] encryptionSerial, byte[] licenseKey); 359 360 /** 361 * 人脸检测初始化函数 362 * @param licenseCode 授权码 363 * @param licenseKey 授权关键字 364 * @param path 授权码文件本地路径 365 * @param faceDetectionModelPath_ 模型路径 366 * @param handle [out]结构体 367 * @return 成功(0)或错误代码(< 0),see also {@link SdkStatus} 368 */ 369 private static native int FDInit(byte[] licenseCode, byte[] licenseKey, byte[] path, String faceDetectionModelPath_, long[] handle); 370 /** 371 * 人脸检测模块资源释放函数 372 * @param handle 373 */ 374 static native void FDDestroy(long handle); 375 376 /** 377 * 对输入图像进行人脸检测,检测结果由rect返回, 378 * 检测到人脸返回>0的人脸个数, 379 * 返回的是21个元素的脸部信息 可配合FDGetDetectInfo拿到人脸角度 人脸数量<=3 380 * @param handle 381 * @param BGR 待检测图像BGR格式 382 * @param width 图像宽度 383 * @param height 图像调试 384 * @param detectMod 0:用于检测图片,1:用于视频的循环检测 385 * @param rect 用于返回人脸位置及关键点信 息,大小在外部申请 (61 * MAX_FACE_COUNT)<br> 386 * rect格式:61 个数据为一组,依次为 人脸坐标(left, top, width, height) 387 * 人脸质量 人脸姿态(roll yaw pitch) 人脸清晰度 人脸亮度 人脸关键点(25*2) 388 * 人脸检测框置信度 389 * @return 检测到的人脸个数(>0)或者错误代码(< 0),see also {@link SdkStatus} 390 */ 391 private static native int FDDetect(long handle, byte[] BGR, int width, int height, int detectMod, double[] rect); 392 393 /** 394 * 对输入图像进行人脸检测返回最大人脸,检测结果由rect返回, 395 * 返回的是21个元素的脸部信息 可配合FDGetDetectInfo拿到人脸角度 人脸数量 = 1 396 * @param handle 397 * @param BGR 待检测图像BGR格式 398 * @param width 图像宽度 399 * @param height 图像高度 400 * @param detectMod 0:用于检测图片,1:用于视频的循环检测 401 * @param rect 用于返回人脸位置及关键点信 息,大小在外部申请 (61 * MAX_FACE_COUNT)<br> 402 * rect格式:61 个数据为一组,依次为 人脸坐标(left, top, width, height) 403 * 人脸质量 人脸姿态(roll yaw pitch) 人脸清晰度 人脸亮度 人脸关键点(25*2) 404 * 人脸检测框置信度 405 * @return 检测到的人脸个数1,或者错误代码(< 0),see also {@link SdkStatus} 406 */ 407 private static native int FDDetectMaxFace(long handle, byte[] BGR, int width, int height, int detectMod, double[] rect); 408 /** 409 * 获取人脸角度,在注册用到,从属于检测系列 410 * double [] rect 输入人脸区域 double [] rectinfo 返回3个元素 1: roll 2: yaw 3: pitch 411 * @deprecated 人脸检测原始数据中已经包含了人脸角度,不再需要此函数 412 */ 413 private static native int FDGetDetectInfo(long handle, byte[] BGR, int width, int height, double[] rect, double[] rectinfo); 414 // public static native int FDDetectMaxFaceTrack(long handle, byte[] BGR, int width, int height, double[] rect); //直接返回61个元素的人脸信息 415 416 private static native int FDSetMinFaceSize(long handle, int minSize); 417 private static native int FDSetThreadsNumber(long handle, int threadsNumber); 418 /** 419 * @return 人脸检测模块版本 420 */ 421 public static native String FDgetVersion(); 422 423 424 ////////////////////////////////////////特征提取系列/////////////////////////////////////////// 425 /** 426 * 人脸识别初始化函数 427 * @param licenseCode 授权码 428 * @param licenseKey 授权关键字 429 * @param path 授权码文件本地路径 430 * @param faceDetectionModelPath 模型路径 431 * @param handle [out]句柄 432 * @return 成功(0)或错误代码(< 0),see also {@link SdkStatus} 433 */ 434 private static native int FFInit(byte[] licenseCode, byte[] licenseKey, byte[] path, String faceDetectionModelPath, long[] handle); 435 /** 436 * 人脸识别模块资源释放函数 437 * @param handle 句柄 438 */ 439 private static native void FFDestroy(long handle); 440 441 /** 442 * 对输入图像中检测到的人脸提取特征 443 * @param handle 操作句柄 444 * @param BGR 待检测图像BGR格式 445 * @param width 图像宽度 446 * @param height 图像高度 447 * @param fea [out] 人脸特征 空间在外部申请,长度512 448 * @param rect 人脸位置及关键点信息 由人脸检测SDK 产生,参见 {@link #FDDetect(long, byte[], int, int, int, double[])} 449 * @return 成功(0)或错误代码(< 0),see also {@link SdkStatus} 450 */ 451 private static native int FFFeaExtract(long handle, byte[] BGR, int width, int height, double[] fea, double[] rect); 452 453 /** 454 * 人脸相似度比对函数<br> 455 * 对人脸特征feaA和feaB进行相似度比对,返回相似度结果 456 * @param feaA 128 double 457 * @param feaB 128 double 458 * @return 相似度(0.0~1.0) 459 * @deprecated replaced by {@link #FFSimilarityByte(byte[], byte[])} 460 */ 461 public static native double FFSimilarity(double[] feaA, double[] feaB); 462 /** 463 * 对输入图像中检测到的人脸提取特征 464 * @param handle 操作句柄 465 * @param BGR 待检测图像BGR格式 466 * @param width 图像宽度 467 * @param height 图像高度 468 * @param fea [out] 人脸特征 空间在外部申请,长度4096 469 * @param rect 人脸位置及关键点信息 由人脸检测SDK 产生,参见 {@link #FDDetect(long, byte[], int, int, int, double[])} 470 * @return 成功(0)或错误代码(< 0),see also {@link SdkStatus} 471 */ 472 private static native int FFFeaExtractByte(long handle, byte[] BGR, int width, int height, byte[] fea, double[] rect); 473 474 /** 475 * 人脸相似度比对函数<br> 476 * 对人脸特征feaA和feaB进行相似度比对,返回相似度结果 477 * @param feaA 1024 bytes 478 * @param feaB 1024 bytes 479 * @return 相似度(0.0~1.0) 480 */ 481 public static native double FFSimilarityByte(byte[] feaA, byte[] feaB); 482 483 /** 484 * @return 人脸识别模块版本 485 */ 486 public static native String FFgetVersion(); 487 488 /** 489 * 位置关联 490 * 在人脸检测函数后用到,无论有无检测到人脸,均需要调用,从属于检测系列 491 * @param handle 492 * @param faceNums 人脸个数 493 * @param rectRate 494 * @param faceRect 人脸信息(人脸检测数据rect) 21个元素 495 * @param faceRectInfo 返回的标记,最后一个元素为是否成功比对的标志,其他为人脸信息 496 * @return 位置关联总个数 497 */ 498 private static native int FSFacePosInterrelate(long handle, int faceNums, double rectRate ,double[] faceRect, double[] faceRectInfo); 499 /** 500 * 人脸标记(成功后) 501 *人脸识别成功后调用,告知该人脸已经识别成功,不用重复提取特征进行特征提取 502 * @param facePos 人脸数组位置下班 503 * @param flag 标记成功 1 504 * @return 0成功 不为0失败 505 */ 506 private static native int FSSetFacePosStatus(long handle, int facePos, int flag); 507 508 509 510 511// public static native int FDDetectMaxFaceTrack(long handle, byte[] BGR, int width, int height, double[] rect); //直接返回61个元素的人脸信息 512 513 ////////////////////////////////////////活体检测系列/////////////////////////////////////////// 514 /** 515 * 活体检测模块初始化函数 516 * @param licenseCode 授权码 517 * @param licenseKey 授权关键字 518 * @param path 授权码文件本地路径 519 * @param handle [out]结构体 520 * @return 521 */ 522 private static native int FLInit(byte[] licenseCode, byte[] licenseKey, byte[] path, long[] handle); 523 524 private static native void FLDestroy(long handle); 525 526 /** 527 * @param handle 528 * @param BGR 529 * @param width 530 * @param height 531 * @param rect 532 * @param socre 533 * @return 0--非活体,1--活体,<0 错误代码 534 */ 535 private static native int FLProcess(long handle, byte[] BGR, int width, int height, double[] rect, double[] socre); 536 537}