001/** 002 * This source code file is part of a direct port of Stan Birchfield's implementation 003 * of a Kanade-Lucas-Tomasi feature tracker. The original implementation can be found 004 * here: http://www.ces.clemson.edu/~stb/klt/ 005 * 006 * As per the original code, the source code is in the public domain, available 007 * for both commercial and non-commercial use. 008 */ 009package org.openimaj.video.tracking.klt; 010 011import java.io.DataInput; 012import java.io.DataOutput; 013import java.io.DataOutputStream; 014import java.io.IOException; 015import java.io.PrintWriter; 016import java.util.Scanner; 017 018import org.openimaj.math.geometry.point.Point2d; 019 020import Jama.Matrix; 021 022/** 023 * A tracked feature 024 * 025 * @author Stan Birchfield 026 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 027 */ 028public class Feature implements Point2d, Cloneable { 029 /** 030 * x ordinate of feature 031 */ 032 public float x; 033 034 /** 035 * y ordinate of feature 036 */ 037 public float y; 038 039 /** 040 * value of feature 041 */ 042 public int val; 043 044 /* for affine mapping */ 045 // public FImage aff_img; 046 // public FImage aff_img_gradx; 047 // public FImage aff_img_grady; 048 // public float aff_x; 049 // public float aff_y; 050 // public float aff_Axx; 051 // public float aff_Ayx; 052 // public float aff_Axy; 053 // public float aff_Ayy; 054 055 /** 056 * Convert to string representation with the given format 057 * 058 * @param format 059 * @param type 060 * @return formatted string 061 */ 062 public String toString(String format, String type) { 063 assert (type.equals("f") || type.equals("d")); 064 String s = ""; 065 066 if (type.equals("f")) 067 s += String.format(format, x, y, val); 068 else if (type.equals("d")) { 069 /* Round x & y to nearest integer, unless negative */ 070 float _x = x; 071 float _y = y; 072 if (_x >= 0.0) 073 _x += 0.5; 074 if (_y >= 0.0) 075 _y += 0.5; 076 s += String.format(format, (int) x, (int) y, val); 077 } 078 079 return s; 080 } 081 082 /** 083 * Write feature as binary data 084 * 085 * @param os 086 * @throws IOException 087 */ 088 public void writeFeatureBin(DataOutputStream os) throws IOException { 089 os.writeFloat(x); 090 os.writeFloat(y); 091 os.writeInt(val); 092 } 093 094 @Override 095 public float getX() { 096 return x; 097 } 098 099 @Override 100 public float getY() { 101 return y; 102 } 103 104 @Override 105 public void setX(float x) { 106 this.x = x; 107 } 108 109 @Override 110 public void setY(float y) { 111 this.y = y; 112 } 113 114 @Override 115 public Feature clone() { 116 final Feature f = new Feature(); 117 118 f.x = x; 119 f.y = y; 120 f.val = val; 121 // f.aff_img = aff_img; 122 // f.aff_img_gradx = aff_img_gradx; 123 // f.aff_img_grady = aff_img_grady; 124 // f.aff_x = aff_x; 125 // f.aff_y = aff_y; 126 // f.aff_Axx = aff_Axx; 127 // f.aff_Ayx = aff_Ayx; 128 // f.aff_Axy = aff_Axy; 129 // f.aff_Ayy = aff_Ayy; 130 131 return f; 132 } 133 134 @Override 135 public boolean equals(Object o) { 136 if (this == o) 137 return true; 138 if (!(o instanceof Feature)) 139 return false; 140 141 if (((Feature) o).x == x && ((Feature) o).y == y && ((Feature) o).val == val) 142 return true; 143 return false; 144 } 145 146 @Override 147 public int hashCode() { 148 int hash = 17; 149 hash = (int) ((31 * hash) + x); 150 hash = (int) ((31 * hash) + y); 151 hash = ((31 * hash) + val); 152 // hash = (int) ((31 * hash) + aff_img; 153 // hash = (int) ((31 * hash) + aff_img_gradx; 154 // hash = (int) ((31 * hash) + aff_img_grady; 155 // hash = (int) ((31 * hash) + aff_x; 156 // hash = (int) ((31 * hash) + aff_y; 157 // hash = (int) ((31 * hash) + aff_Axx; 158 // hash = (int) ((31 * hash) + aff_Ayx; 159 // hash = (int) ((31 * hash) + aff_Axy; 160 // hash = (int) ((31 * hash) + aff_Ayy; 161 162 return hash; 163 } 164 165 @Override 166 public String toString() { 167 return "Feature(" + x + ", " + y + ", " + val + ")"; 168 } 169 170 @Override 171 public void copyFrom(Point2d p) 172 { 173 setX(p.getX()); 174 setY(p.getY()); 175 } 176 177 @Override 178 public Float getOrdinate(int dimension) { 179 if (dimension == 0) 180 return x; 181 return y; 182 } 183 184 @Override 185 public int getDimensions() { 186 return 2; 187 } 188 189 @Override 190 public void translate(float x, float y) { 191 this.x += x; 192 this.y += y; 193 } 194 195 @Override 196 public Feature transform(Matrix transform) { 197 if (transform.getRowDimension() == 3) { 198 float xt = (float) transform.get(0, 0) * getX() + (float) transform.get(0, 1) * getY() 199 + (float) transform.get(0, 2); 200 float yt = (float) transform.get(1, 0) * getX() + (float) transform.get(1, 1) * getY() 201 + (float) transform.get(1, 2); 202 final float zt = (float) transform.get(2, 0) * getX() + (float) transform.get(2, 1) * getY() 203 + (float) transform.get(2, 2); 204 205 xt /= zt; 206 yt /= zt; 207 208 final Feature f = this.clone(); 209 f.x = xt; 210 f.y = yt; 211 return f; 212 } else if (transform.getRowDimension() == 2) { 213 final float xt = (float) transform.get(0, 0) * getX() + (float) transform.get(0, 1) * getY(); 214 final float yt = (float) transform.get(1, 0) * getX() + (float) transform.get(1, 1) * getY(); 215 216 final Feature f = this.clone(); 217 f.x = xt; 218 f.y = yt; 219 return f; 220 } 221 throw new IllegalArgumentException("Transform matrix has unexpected size"); 222 } 223 224 @Override 225 public Point2d minus(Point2d a) { 226 final Point2d p = this.clone(); 227 p.setX(this.getX() - a.getX()); 228 p.setY(this.getY() - a.getY()); 229 return p; 230 } 231 232 @Override 233 public void readASCII(Scanner in) throws IOException { 234 x = in.nextFloat(); 235 y = in.nextFloat(); 236 val = in.nextInt(); 237 } 238 239 @Override 240 public String asciiHeader() { 241 return this.getClass().getName(); 242 } 243 244 @Override 245 public void readBinary(DataInput in) throws IOException { 246 x = in.readFloat(); 247 y = in.readFloat(); 248 val = in.readInt(); 249 } 250 251 @Override 252 public byte[] binaryHeader() { 253 return this.getClass().getName().getBytes(); 254 } 255 256 @Override 257 public void writeASCII(PrintWriter out) throws IOException { 258 out.format("%f %f %d", x, y, val); 259 } 260 261 @Override 262 public void writeBinary(DataOutput out) throws IOException { 263 out.writeFloat(x); 264 out.writeFloat(y); 265 out.writeInt(val); 266 } 267 268 @Override 269 public void translate(Point2d v) { 270 this.translate(v.getX(), v.getY()); 271 } 272 273 @Override 274 public Feature copy() { 275 return clone(); 276 } 277 278 @Override 279 public void setOrdinate(int dimension, Number value) { 280 if (dimension == 0) 281 x = value.floatValue(); 282 if (dimension == 1) 283 y = value.floatValue(); 284 } 285}