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}