001/**
002 * 
003 */
004package net.gdface.sdk;
005import java.io.ByteArrayOutputStream;
006import java.io.PrintStream;
007/**   
008 * @Title: CodeInfo.java 
009 * @Package net.gdface.sdk 
010 * @Description: 人脸信息存储类 
011 * @author guyadong   
012 * @date 2014-9-19 下午3:37:24 
013 * @version V1.0   
014 */
015import java.io.Serializable;
016import java.util.Arrays;
017
018/**
019 * 人脸特征码信息描述对象<br>
020 * 包括人脸位置,眼睛、嘴巴,、鼻子位置,人脸角度,特征码数据
021 * @author guyadong
022 *
023 */
024public class CodeInfo implements Serializable {
025        /**
026         * 
027         */
028        private static final long serialVersionUID = 1L;
029        
030        protected static final FInt2 ZERO_OFFSET = new FInt2(0,0);
031        /**
032         * 特征码数组
033         */
034        private byte[] code;
035        /**
036         * 人脸位置信息(相对于原始图像)
037         */
038        private FRect pos;
039        /**
040         * 眼睛位置信息(相对于原始图像)
041         */
042        private EyeInfo ei;
043
044        /**
045         * 嘴巴位置
046         */
047        private FInt2 mouth;
048        /**
049         * 鼻子位置
050         */
051        private FInt2 nose;
052        /**
053         * 人脸角度
054         */
055        private FAngle angle;   
056        /**
057         * 面部数据
058         */
059        private byte[] facialData;
060        /**
061         * 人脸信息坐标偏移量<br>
062         * 此字段属于状态描述字段,不属于人脸信息,
063         * 此字段不为{@code null}时用于描述当前人脸信息对象中所有的坐标数据的原点相对实际原点的偏移量
064         */
065        private FInt2 offset;
066        public CodeInfo() {
067                this(null,null,null,null,null,null, null);
068        }
069
070        public CodeInfo(byte[] code, FRect pos, EyeInfo ei, FInt2 mouth, FInt2 nose, FAngle angle, byte[] facialData) {
071                this.code = code;
072                this.pos = pos;
073                this.ei = ei;
074                this.mouth = mouth;
075                this.nose = nose;
076                this.angle = angle;
077                this.facialData=facialData;
078        }
079
080        public byte[] getCode() {
081                return code;
082        }
083
084        /**
085         * @return the ei
086         */
087        public EyeInfo getEi() {
088                return ei;
089        }
090
091        public FRect getPos() {
092                return pos;
093        }
094
095        public void setCode(byte[] code) {
096                this.code = code;
097        }
098
099        /**
100         * @param ei the ei to set
101         */
102        public void setEi(EyeInfo ei) {
103                this.ei = ei;
104        }
105
106        public void setPos(FRect pos) {
107                this.pos = pos;
108        }
109
110        /**
111         * @return mouth
112         */
113        public FInt2 getMouth() {
114                return mouth;
115        }
116
117        /**
118         * @param mouth 要设置的 mouth
119         */
120        public void setMouth(FInt2 mouth) {
121                this.mouth = mouth;
122        }
123
124        /**
125         * @return nose
126         */
127        public FInt2 getNose() {
128                return nose;
129        }
130
131        /**
132         * @param nose 要设置的 nose
133         */
134        public void setNose(FInt2 nose) {
135                this.nose = nose;
136        }
137
138        /**
139         * @return angle
140         */
141        public FAngle getAngle() {
142                return angle;
143        }
144
145        /**
146         * @param angle 要设置的 angle
147         */
148        public void setAngle(FAngle angle) {
149                this.angle = angle;
150        }
151
152        /**
153         * @return facialData
154         */
155        public byte[] getFacialData() {
156                return facialData;
157        }
158
159        /**
160         * @param facialData 要设置的 facialData
161         */
162        public void setFacialData(byte[] facialData) {
163                this.facialData = facialData;
164        }
165        /**
166         * 人脸信息坐标偏移量<br>
167         * 此字段属于状态描述字段,不是属于人脸信息部分,
168         * 此字段不为{@code null}时用于描述当前人脸信息对象中所有的坐标数据的原点相对实际原点的偏移量
169         * @return 偏移量对象,为{@code null}无偏移或偏移为0
170         */
171        public FInt2 getOffset() {
172                return offset;
173        }
174
175        public void setOffset(FInt2 offset) {
176                this.offset = offset;
177        }
178        public CodeInfo withOffset(int x,int y) {
179                setOffset(new FInt2(x, y));
180                return this;
181        }
182        /**
183         * 将当前人脸位置对象中的坐标数据根据偏移坐标({@link #offset})重新定位<br>
184         * 所有水平坐标 - offset.x<br>
185         * 所有垂直坐标 - offset.y<br>
186         * 返回重新定位的当前对象
187         * @return 返回重新定位的当前对象
188         */
189        public final CodeInfo relocate(){
190                if(null != offset && !ZERO_OFFSET.equals(offset) ){
191                        int x = offset.getX();
192                        int y = offset.getY();
193                        if(null != pos){
194                                pos.setLeft(pos.getLeft() - x);
195                                pos.setTop(pos.getTop() - y);
196                        }
197                        if(null != ei){
198                                ei.setLeftAndRight(ei.getLeftx() - x, ei.getLefty() - y, ei.getRightx() - x, ei.getRighty() - y);
199                        }
200                        if(null != mouth){
201                                mouth.setX(mouth.getX() - x);
202                                mouth.setY(mouth.getY() - y);
203                        }
204                        if(null != nose){
205                                nose.setX(nose.getX() - x);
206                                nose.setY(nose.getY() - y);
207                        }
208                        relocateFacialData();
209                        // 完成重定位后将 offset 置为null
210                        offset = null;
211                }
212                return this;
213        }
214        /**
215         * 对面部数据重新定位<br>
216         * 默认实现对面部数据({@link #facialData})不做任何修改,子类可重写此方法
217         */
218        protected void relocateFacialData(){
219                // DO NOTHING
220        }
221        @Override
222        public String toString() {
223                ByteArrayOutputStream bo = new ByteArrayOutputStream();
224                toStream(new PrintStream(bo));
225                return bo.toString();
226        }
227        /**
228         * 以文本形式向{@link PrintStream}输出对象内容 
229         * @param stream
230         */
231        public void toStream(PrintStream stream) {
232                stream.printf("[pos=%s,ei=%s,mouth=%s,nose=%s,angle=%s,offset=%s,code.length=%s,facialData.length=%s]"
233                                ,this.pos,this.ei,this.mouth,this.nose,this.angle,this.offset
234                                ,null==code?"null":Integer.toString(code.length)
235                                ,null==facialData?"null":Integer.toString(facialData.length));
236        }
237
238        @Override
239        public int hashCode() {
240                final int prime = 31;
241                int result = 1;
242                result = prime * result + ((angle == null) ? 0 : angle.hashCode());
243                result = prime * result + Arrays.hashCode(code);
244                result = prime * result + ((ei == null) ? 0 : ei.hashCode());
245                result = prime * result + Arrays.hashCode(facialData);
246                result = prime * result + ((mouth == null) ? 0 : mouth.hashCode());
247                result = prime * result + ((nose == null) ? 0 : nose.hashCode());
248                result = prime * result + ((pos == null) ? 0 : pos.hashCode());
249                return result;
250        }
251
252        @Override
253        public boolean equals(Object obj) {
254                if (this == obj) {
255                        return true;
256                }
257                if (obj == null) {
258                        return false;
259                }
260                if (!(obj instanceof CodeInfo)) {
261                        return false;
262                }
263                CodeInfo other = (CodeInfo) obj;
264                if (angle == null) {
265                        if (other.angle != null) {
266                                return false;
267                        }
268                } else if (!angle.equals(other.angle)) {
269                        return false;
270                }
271                if (!Arrays.equals(code, other.code)) {
272                        return false;
273                }
274                if (ei == null) {
275                        if (other.ei != null) {
276                                return false;
277                        }
278                } else if (!ei.equals(other.ei)) {
279                        return false;
280                }
281                if (!Arrays.equals(facialData, other.facialData)) {
282                        return false;
283                }
284                if (mouth == null) {
285                        if (other.mouth != null) {
286                                return false;
287                        }
288                } else if (!mouth.equals(other.mouth)) {
289                        return false;
290                }
291                if (nose == null) {
292                        if (other.nose != null) {
293                                return false;
294                        }
295                } else if (!nose.equals(other.nose)) {
296                        return false;
297                }
298                if (pos == null) {
299                        if (other.pos != null) {
300                                return false;
301                        }
302                } else if (!pos.equals(other.pos)) {
303                        return false;
304                }
305                return true;
306        }
307}