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}