001package net.gdface.sdk;
002
003import java.nio.ByteBuffer;
004import java.util.ArrayList;
005import java.util.List;
006import java.util.Map;
007
008import net.gdface.image.BaseLazyImage;
009import net.gdface.image.ImageErrorException;
010import net.gdface.image.MatType;
011import net.gdface.image.NotImageException;
012import net.gdface.image.UnsupportedFormatException;
013import net.gdface.sdk.fse.FeatureSe;
014import net.gdface.utils.Assert;
015
016/**
017 * {@link FaceApi}抽象实现,SDK接口类必须从此类派生<br>
018 * @author guyadong
019 *
020 */
021public abstract class BaseFaceApi implements FaceApi{
022
023        protected BaseFaceApi() {
024        }
025        @Override
026        public double compare2Face(byte[] imgData1, CodeInfo facePos1, byte[] imgData2, CodeInfo facePos2)
027                        throws NotFaceDetectedException {
028                if(0==imgData1.length){
029                        imgData1=null;
030                }
031                if(0==imgData2.length){
032                        imgData2=null;
033                }
034                byte[] code1, code2;
035                Assert.notNull(facePos1, "facePos1");
036                Assert.notNull(facePos2, "facePos2");                   
037                if (imgData1 == imgData2||null==imgData2) {
038                        CodeInfo[] codes = getCodeInfo(imgData1, 2, new CodeInfo[] { facePos1, facePos2 });                     
039                        code1 = codes[0].getCode();
040                        code2 = codes[1].getCode();
041                } else {
042                        code1 = getCodeInfo(imgData1, 1, new CodeInfo[] { facePos1 })[0].getCode();
043                        code2 = getCodeInfo(imgData2, 1, new CodeInfo[] { facePos2 })[0].getCode();
044                }
045                return compareCode(code1, code2);
046        }
047        @Override
048        public CompareResult compareFaces(byte[] code, byte[] imgData, int faceNum)
049                        throws NotFaceDetectedException, ImageErrorException {
050                CodeInfo[] codes = detectAndGetCodeInfo(imgData,faceNum);
051                double[] similartys = compareCodes(code,codes);
052                return new CompareResult(codes,similartys);
053        }
054        @Override
055        public double detectAndCompare2Face(byte[] imgData1, FRect detectRect1, byte[] imgData2, FRect detectRect2)
056                        throws ImageErrorException, NotFaceDetectedException {
057                if(null != imgData1 && 0==imgData1.length){
058                        imgData1=null;
059                }
060                if(null != imgData2 && 0==imgData2.length){
061                        imgData2=null;
062                }
063                byte[] code1, code2;
064                if (imgData1 == imgData2||null==imgData2) {
065                        if (detectRect1 == null || null == detectRect2){
066                                throw new IllegalArgumentException("detectRect1,detectRect2 must not be null");
067                        }
068                        CodeInfo[] codes = detectAndGetCodeInfo(imgData1, 2);
069                        if((detectRect1.contains(codes[0].getPos()) && detectRect2.contains(codes[1].getPos()))
070                                        || (detectRect2.contains(codes[0].getPos()) && detectRect1.contains(codes[1].getPos()))){
071                                code1 = codes[0].getCode();
072                                code2 = codes[1].getCode();
073                        }else{
074                                // 如果任何检测区域一个没有检测到人脸,则抛出异常
075                                throw new NotFaceDetectedException("not found face in detect rectangle");
076                        }
077                } else {
078                        code1 = detectAndGetCodeInfo(imgData1, 1)[0].getCode();
079                        code2 = detectAndGetCodeInfo(imgData2, 1)[0].getCode();
080                }
081                return compareCode(code1, code2);
082        }
083        @Override
084        public List<Double> compareFeatures(byte[] code1,List<byte[]> codes){
085                if(null == code1 || null == codes){
086                        throw new NullPointerException("code1 or codes is null");
087                }
088                ArrayList<Double> result = new ArrayList<Double>(codes.size());
089                for(byte[] code2:codes){
090                        result.add(compareCode(code1,code2));
091                }
092                return result;
093        }
094        @Override
095        public double[] compareCodes(byte[] code1,CodeInfo[] codes){
096                if(null == code1 || null == codes){
097                        throw new NullPointerException("code1 or codes is null");
098                }
099                double[] result = new double[codes.length];
100                for(int i = 0;i<result.length;++i){
101                        result[i] = compareCode(code1,codes[i].getCode());
102                }
103                return result;
104        }
105        /**
106         * 通过图像数据字节流(byte[])创建FaceImage对象<br>
107         * 调用{@link BaseLazyImage#setAutoClose(boolean)}设置 autClose为{@code false}<br>
108         * 如果有特殊要求,子类可以重写此方法
109         * @param imgData
110         * @return
111         * @throws UnsupportedFormatException
112         * @throws NotImageException
113         * @see BaseLazyImage#open()
114         * @see BaseLazyImage#setAutoClose(boolean)
115         */
116        protected BaseLazyImage makeOpenedLazyImage(byte[] imgData) throws UnsupportedFormatException, NotImageException {
117                return BaseLazyImage.getLazyImageFactory().create(imgData).setAutoClose(false).open();
118        }
119
120        @Override
121        public CodeInfo detectCenterFace(byte[] imgData) throws NotFaceDetectedException, ImageErrorException {
122                BaseLazyImage lazyImg = BaseLazyImage.getLazyImageFactory().create(imgData);
123                CodeInfo centerFace;
124                CodeInfo[] codes = detectFace(imgData);
125                if(codes.length == 0){
126                        throw new NotFaceDetectedException();
127                }
128                FInt2 center=new FInt2(lazyImg.getWidth()>>1,lazyImg.getHeight()>>1);
129                centerFace=codes[0];
130                double minDistance = RectUtils.distance(centerFace.getPos(), center);
131                for(int i=1;i<codes.length;++i){
132                        CodeInfo f=codes[i];
133                        double d = RectUtils.distance(f.getPos(), center);
134                        if(d<minDistance){
135                                centerFace=f;
136                                minDistance=d;
137                        }
138                }               
139                return centerFace;
140        }
141        protected CodeInfo findMaxFace(CodeInfo[] facePos){
142                if(facePos != null && facePos.length != 0){
143                        int maxArea = 0;
144                        CodeInfo maxFace = null;
145                        // 按矩形面积挑出最大人脸矩形
146                        for(CodeInfo c:facePos){
147                                if(c != null){
148                                        FRect rect = c.getPos();
149                                        if(rect != null){
150                                                int area =  rect.getWidth()*rect.getHeight();
151                                                if(maxArea < area){
152                                                        maxArea = area;
153                                                        maxFace = c;
154                                                }
155                                        }
156                                }
157                        }
158                        return maxFace;
159                }
160                return null;            
161        }
162        @Override
163        public CodeInfo detectMaxFace(byte[] imgData) throws NotFaceDetectedException, ImageErrorException {
164                CodeInfo[] codes = detectFace(imgData);
165                CodeInfo maxFace = findMaxFace(codes);
166                if(maxFace == null){
167                        throw new NotFaceDetectedException();
168                }
169                return maxFace;
170        }
171        @Override
172        public boolean hasFace(byte[] imgData) throws ImageErrorException {
173                try {
174                        detectAndGetCodeInfo(imgData, 0);
175                        return true;
176                } catch (NotFaceDetectedException e) {
177                        return false;
178                }
179        }
180        @Override
181        public boolean matHasFace(MatType matType,byte[] matData,int width, int height){
182                try {
183                        matDetectAndGetCodeInfo(matType, matData, width,height, 0);
184                        return true;
185                } catch (NotFaceDetectedException e) {
186                        return false;
187                }
188        }
189        @Override
190        public CodeInfo matDetectMaxFace(MatType matType,byte[] matData,int width, int height) throws NotFaceDetectedException {
191                CodeInfo[] facePos = matDetectFace(matType, matData, width,height);
192                CodeInfo maxFace = findMaxFace(facePos);
193                if(maxFace == null){
194                        throw new NotFaceDetectedException();
195                }
196                return maxFace;
197        }
198        @Override
199        public byte[] getFeature(Map<ByteBuffer, CodeInfo> faces) throws NotFaceDetectedException {
200                throw new UnsupportedOperationException();
201        }
202        @Override
203        public FseResult[] searchFaces(byte[] imgData, CodeInfo facePos, double similarty, int rows)
204                        throws NotFaceDetectedException, ImageErrorException {
205                if(null == facePos ){
206                        facePos = detectAndGetCodeInfo(imgData, 1)[0];
207                }else if(null == facePos.getCode()){
208                        facePos = getCodeInfo(imgData, 1, new CodeInfo[] {facePos})[0];
209                }
210                return this.searchFeatures(facePos.getCode(), similarty, rows);
211        }
212        @Override
213        public FseResult[] matSearchFaces(MatType matType, byte[] matData, int width, int height, CodeInfo facePos, double similarty,
214                        int rows) throws NotFaceDetectedException, ImageErrorException {
215                if(null == facePos ){
216                        facePos = matDetectAndGetCodeInfo(matType,matData,width,height, 1)[0];
217                }else if(null == facePos.getCode()){
218                        facePos = matGetCodeInfo(matType,matData,width,height, facePos);
219                }
220                return this.searchFeatures(facePos.getCode(), similarty, rows);
221        }
222        /**
223         * 返回用于图像检测和建模的图像矩阵的(色彩空间)类型<br>
224         * 不可返回{@code null}
225         * @return 矩阵类型
226         */
227        protected abstract MatType getNativeMatrixType();
228        /**
229         * 将指定的图像矩阵转为图像检测和建模的图像矩阵的(色彩空间)类型(参见 {@link #getNativeMatrixType()})
230         * @param matType 输入矩阵类型
231         * @param matData 矩阵数据
232         * @param width 矩阵宽度
233         * @param height 矩阵高度
234         * @return 转换后的矩阵数据
235         */
236        protected abstract byte[] toNativeMatrix(MatType matType, byte[] matData, int width, int height);
237        /**
238         * 从{@link FaceApi}实例获取内存搜索引擎接口实例<br>
239         * 如果{@link FaceApi}不是{@link BaseFaceApiLocal}实例返回{@code null}
240         * @param faceapi
241         * @return
242         */
243        public static final FeatureSe getFeatureSeInstance(FaceApi faceapi){
244                if(faceapi instanceof BaseFaceApiLocal){                        
245                        return ((BaseFaceApiLocal)faceapi).getFeatureSe();
246                }
247                return null;
248        }
249}