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}