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}