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 /** 057 * Convert to string representation with the given format 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) _x += 0.5; 073 if (_y >= 0.0) _y += 0.5; 074 s += String.format(format, (int) x, (int) y, val); 075 } 076 077 return s; 078 } 079 080 /** 081 * Write feature as binary data 082 * @param os 083 * @throws IOException 084 */ 085 public void writeFeatureBin(DataOutputStream os) throws IOException { 086 os.writeFloat(x); 087 os.writeFloat(y); 088 os.writeInt(val); 089 } 090 091 @Override 092 public float getX() { 093 return x; 094 } 095 096 @Override 097 public float getY() { 098 return y; 099 } 100 101 @Override 102 public void setX(float x) { 103 this.x = x; 104 } 105 106 @Override 107 public void setY(float y) { 108 this.y = y; 109 } 110 111 @Override 112 public Feature clone() { 113 Feature f = new Feature(); 114 115 f.x = x; 116 f.y = y; 117 f.val = val; 118// f.aff_img = aff_img; 119// f.aff_img_gradx = aff_img_gradx; 120// f.aff_img_grady = aff_img_grady; 121// f.aff_x = aff_x; 122// f.aff_y = aff_y; 123// f.aff_Axx = aff_Axx; 124// f.aff_Ayx = aff_Ayx; 125// f.aff_Axy = aff_Axy; 126// f.aff_Ayy = aff_Ayy; 127 128 return f; 129 } 130 131 @Override 132 public boolean equals(Object o) { 133 if (this == o) return true; 134 if (!(o instanceof Feature)) return false; 135 136 if (((Feature)o).x == x && ((Feature)o).y == y && ((Feature)o).val == val) return true; 137 return false; 138 } 139 140 @Override 141 public int hashCode() { 142 int hash = 17; 143 hash = (int) ((31 * hash) + x); 144 hash = (int) ((31 * hash) + y); 145 hash = ((31 * hash) + val); 146// hash = (int) ((31 * hash) + aff_img; 147// hash = (int) ((31 * hash) + aff_img_gradx; 148// hash = (int) ((31 * hash) + aff_img_grady; 149// hash = (int) ((31 * hash) + aff_x; 150// hash = (int) ((31 * hash) + aff_y; 151// hash = (int) ((31 * hash) + aff_Axx; 152// hash = (int) ((31 * hash) + aff_Ayx; 153// hash = (int) ((31 * hash) + aff_Axy; 154// hash = (int) ((31 * hash) + aff_Ayy; 155 156 return hash; 157 } 158 159 @Override 160 public String toString() { 161 return "Feature(" + x + ", " + y + ", " + val + ")"; 162 } 163 164 @Override 165 public void copyFrom( Point2d p ) 166 { 167 setX( p.getX() ); 168 setY( p.getY() ); 169 } 170 171 @Override 172 public Float getOrdinate(int dimension) { 173 if (dimension == 0) return x; 174 return y; 175 } 176 177 @Override 178 public int getDimensions() { 179 return 2; 180 } 181 182 @Override 183 public void translate(float x, float y) { 184 this.x += x; 185 this.y += y; 186 } 187 188 @Override 189 public Feature transform(Matrix transform) { 190 if (transform.getRowDimension() == 3) { 191 float xt = (float)transform.get(0, 0) * getX() + (float)transform.get(0, 1) * getY() + (float)transform.get(0, 2); 192 float yt = (float)transform.get(1, 0) * getX() + (float)transform.get(1, 1) * getY() + (float)transform.get(1, 2); 193 float zt = (float)transform.get(2, 0) * getX() + (float)transform.get(2, 1) * getY() + (float)transform.get(2, 2); 194 195 xt /= zt; 196 yt /= zt; 197 198 Feature f = this.clone(); 199 f.x = xt; 200 f.y = yt; 201 return f; 202 } else if (transform.getRowDimension() == 2) { 203 float xt = (float)transform.get(0, 0) * getX() + (float)transform.get(0, 1) * getY(); 204 float yt = (float)transform.get(1, 0) * getX() + (float)transform.get(1, 1) * getY(); 205 206 Feature f = this.clone(); 207 f.x = xt; 208 f.y = yt; 209 return f; 210 } 211 throw new IllegalArgumentException("Transform matrix has unexpected size"); 212 } 213 214 @Override 215 public Point2d minus(Point2d a) { 216 Point2d p = this.clone(); 217 p.setX(this.getX() - a.getX()); 218 p.setY(this.getY() - a.getY()); 219 return p; 220 } 221 222 @Override 223 public void readASCII(Scanner in) throws IOException { 224 x = in.nextFloat(); 225 y = in.nextFloat(); 226 val = in.nextInt(); 227 } 228 229 @Override 230 public String asciiHeader() { 231 return this.getClass().getName(); 232 } 233 234 @Override 235 public void readBinary(DataInput in) throws IOException { 236 x = in.readFloat(); 237 y = in.readFloat(); 238 val = in.readInt(); 239 } 240 241 @Override 242 public byte[] binaryHeader() { 243 return this.getClass().getName().getBytes(); 244 } 245 246 @Override 247 public void writeASCII(PrintWriter out) throws IOException { 248 out.format("%f %f %d", x, y, val); 249 } 250 251 @Override 252 public void writeBinary(DataOutput out) throws IOException { 253 out.writeFloat(x); 254 out.writeFloat(y); 255 out.writeInt(val); 256 } 257 258 @Override 259 public void translate(Point2d v) { 260 this.translate(v.getX(), v.getY()); 261 } 262 263 @Override 264 public Feature copy() { 265 return clone(); 266 } 267}