View Javadoc

1   /**
2    * This source code file is part of a direct port of Stan Birchfield's implementation
3    * of a Kanade-Lucas-Tomasi feature tracker. The original implementation can be found
4    * here: http://www.ces.clemson.edu/~stb/klt/
5    *
6    * As per the original code, the source code is in the public domain, available
7    * for both commercial and non-commercial use.
8    */
9   package org.openimaj.video.tracking.klt;
10  
11  import java.io.DataOutputStream;
12  import java.io.File;
13  import java.io.FileOutputStream;
14  import java.io.IOException;
15  import java.io.PrintWriter;
16  import java.util.Iterator;
17  
18  import org.openimaj.image.FImage;
19  import org.openimaj.image.MBFImage;
20  import org.openimaj.math.geometry.shape.Rectangle;
21  
22  
23  /**
24   * A list of features
25   * 
26   * @author Stan Birchfield
27   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
28   */
29  public class FeatureList implements Iterable<Feature> {
30  	/**
31  	 * The list of features
32  	 */
33  	public Feature[] features;
34  
35  	/*********************************************************************
36  	 * KLTCreateFeatureList
37  	 * @param nFeatures 
38  	 */
39  	public FeatureList(int nFeatures)
40  	{
41  		features = new Feature[nFeatures];
42  
43  		for (int i = 0 ; i < nFeatures ; i++) {
44  			features[i] = new Feature();
45  		}
46  	}
47  
48  	/*********************************************************************
49  	 * KLTCountRemainingFeatures
50  	 * @return the number of remaining features
51  	 */
52  	public int countRemainingFeatures()
53  	{
54  		int count = 0;
55  
56  		for (Feature f : features)
57  			if (f.val >= 0) count++;
58  
59  		return count;
60  	}
61  	
62  	@Override
63  	public FeatureList clone(){
64  		FeatureList ret = new FeatureList(this.features.length);
65  		for(int i = 0; i < this.features.length; i++){
66  			ret.features[i] = this.features[i].clone();
67  		}
68  		return ret;
69  		
70  	}
71  	
72  	/*********************************************************************
73  	 * KLTWriteFeatureListToPPM
74  	 * @param img 
75  	 * @return a new image 
76  	 */
77  	public MBFImage drawFeatures(FImage img)
78  	{
79  		/* Allocate memory for component images */
80  		FImage redimg = img.clone();
81  		FImage grnimg = img.clone();
82  		FImage bluimg = img.clone();  
83  
84  		/* Overlay features in red */
85  		for (int i = 0 ; i < features.length ; i++)
86  			if (features[i].val >= 0)  {
87  				int x = (int) (features[i].x + 0.5);
88  				int y = (int) (features[i].y + 0.5);
89  				for (int yy = y - 1 ; yy <= y + 1 ; yy++)
90  					for (int xx = x - 1 ; xx <= x + 1 ; xx++)  
91  						if (xx >= 0 && yy >= 0 && xx < img.width && yy < img.height)  {
92  							redimg.pixels[yy][xx] = 1f;
93  							grnimg.pixels[yy][xx] = 0f;
94  							bluimg.pixels[yy][xx] = 0f;
95  						}
96  			}
97  
98  		return new MBFImage(redimg, grnimg, bluimg);
99  	}
100 	
101 	/*********************************************************************
102 	 * KLTWriteFeatureListToPPM
103 	 * @param img 
104 	 * @return input image
105 	 */
106 	public MBFImage drawFeatures(MBFImage img)
107 	{
108 		/* Allocate memory for component images */
109 		MBFImage out = img;
110 
111 		/* Overlay features in red */
112 		for (int i = 0 ; i < features.length ; i++)
113 			if (features[i].val >= 0)  {
114 				int x = (int) (features[i].x + 0.5);
115 				int y = (int) (features[i].y + 0.5);
116 				for (int yy = y - 1 ; yy <= y + 1 ; yy++)
117 					for (int xx = x - 1 ; xx <= x + 1 ; xx++)  
118 						if (xx >= 0 && yy >= 0 && xx < img.getWidth()&& yy < img.getHeight())  {
119 							out.bands.get(0).setPixel(xx, yy, 1.0f);
120 						}
121 			}
122 
123 		return out;
124 	}
125 
126 	/*********************************************************************
127 	 * KLTWriteFeatureList()
128 	 * 
129 	 * Writes features to file or to screen.
130 	 *
131 	 * INPUTS
132 	 * @param fname name of file to write data; if NULL, then print to stderr
133 	 * @param fmt   format for printing (e.g., "%5.1f" or "%3d");
134 	 *        if NULL, and if fname is not NULL, then write to binary file.
135 	 * @throws IOException 
136 	 */
137 	public void writeFeatureList(File fname, String fmt) throws IOException
138 	{
139 		if (fmt != null) {  /* text file or stderr */ 
140 			if (fname != null) {
141 				PrintWriter bw = new PrintWriter(new FileOutputStream(fname)); 
142 				bw.write(toString(fmt, true));
143 				bw.close();
144 			} else {
145 				System.out.print(toString(fmt, false));
146 			}
147 		} else {  /* binary file */
148 			DataOutputStream dos = new DataOutputStream(new FileOutputStream(fname));
149 			
150 			dos.write(IOUtils.binheader_fl.getBytes("US-ASCII"));
151 			dos.writeInt(features.length);
152 			for (Feature f : features)  {
153 				f.writeFeatureBin(dos);
154 			}
155 			
156 			dos.close();
157 		}
158 	}
159 
160 
161 	/**
162 	 * Convert to a string representation with the given format.
163 	 * @param fmt
164 	 * @param comments
165 	 * @return the string representation.
166 	 */
167 	public String toString(String fmt, boolean comments) {
168 		String [] setup = IOUtils.setupTxtFormat(fmt);
169 		String format = setup[0];
170 		String type = setup[1];
171 
172 		String s = IOUtils.getHeader(format, IOUtils.StructureType.FEATURE_LIST, 0, features.length, comments);
173 
174 		for (int i = 0 ; i < features.length; i++)  {
175 			s += String.format("%7d | ", i);
176 			s += features[i].toString(format, type);
177 			s += String.format("\n");
178 		}
179 
180 		return s;
181 	}
182 
183 	@Override
184 	public String toString() {
185 		return toString("%3d", false);
186 	}
187 
188 	@Override
189 	public Iterator<Feature> iterator() {
190 		Iterator<Feature> iterator = new Iterator<Feature>() {
191 			private int index = 0;
192 			
193 			@Override
194 			public boolean hasNext() {
195 				int newindex = index;
196 				while (newindex<features.length) {
197 					if (features[newindex].val>=0) 
198 						return true;
199 					newindex++;
200 				}
201 
202 				return false;
203 			}
204 
205 			@Override
206 			public Feature next() {
207 				int newindex = index;
208 				while (newindex<features.length) {
209 					if (features[newindex].val>=0) 
210 						break;
211 					newindex++;
212 				}
213 				
214 				Feature f = features[newindex];
215 				
216 				index++;
217 				
218 				return f;
219 			}
220 
221 			@Override
222 			public void remove() {
223 				throw new UnsupportedOperationException("Remove not supported.");
224 			}			
225 		};
226 		
227 		return iterator;
228 	}
229 	
230 	/**
231 	 * 	Returns the bounding box of the features
232 	 *  @return the bounding box of the features
233 	 */
234 	public Rectangle getBounds()
235 	{
236 		float minX = Float.MAX_VALUE;
237 		float maxX = Float.MIN_VALUE;
238 		float minY = Float.MAX_VALUE;
239 		float maxY = Float.MIN_VALUE;
240 		
241 		for( Feature f : features )
242 		{
243 			if( f.val >= 0 )
244 			{
245 				minX = Math.min( minX, f.x );
246 				maxX = Math.max( maxX, f.x );
247 				minY = Math.min( minY, f.y );
248 				maxY = Math.max( maxY, f.y );
249 			}
250 		}
251 		
252 		return new Rectangle( minX, minY, maxX-minX, maxY-minY );
253 	}
254 }