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