1 /*********************************************************************
2 * selectGoodFeatures.c
3 *
4 *********************************************************************/
5
6 /* Standard includes */
7 #include <stdlib.h> /* malloc(), qsort() */
8 #include <stdio.h> /* fflush() */
9 #include <string.h> /* memset() */
10 #include <math.h> /* sqrt() */
11
12 /* Our includes */
13 #include "base.h"
14 #include "error.h"
15 #include "convolve.h"
16 #include "klt.h"
17 #include "klt_util.h"
18 #include "pyramid.h"
19
20 typedef enum {SELECTING_ALL, REPLACING_SOME} selectionMode;
21
22 /*********************************************************************/
23
_fillFeaturemap(int x,int y,uchar * featuremap,int mindist,int ncols,int nrows)24 void _fillFeaturemap(
25 int x, int y,
26 uchar *featuremap,
27 int mindist,
28 int ncols,
29 int nrows)
30 {
31 int ix, iy;
32
33 for (iy = y - mindist ; iy <= y + mindist ; iy++)
34 for (ix = x - mindist ; ix <= x + mindist ; ix++)
35 if (ix >= 0 && ix < ncols && iy >= 0 && iy < nrows)
36 featuremap[iy*ncols+ix] = 1;
37 }
38
39
40 /*********************************************************************
41 * _enforceMinimumDistance
42 *
43 * Removes features that are within close proximity to better features.
44 *
45 * INPUTS
46 * featurelist: A list of features. The nFeatures property
47 * is used.
48 *
49 * OUTPUTS
50 * featurelist: Is overwritten. Nearby "redundant" features are removed.
51 * Writes -1's into the remaining elements.
52 *
53 * RETURNS
54 * The number of remaining features.
55 */
56
_enforceMinimumDistance(int * pointlist,int npoints,KLT_FeatureList featurelist,int ncols,int nrows,int mindist,int min_eigenvalue,KLT_BOOL overwriteAllFeatures)57 void _enforceMinimumDistance(
58 int *pointlist, /* featurepoints */
59 int npoints, /* number of featurepoints */
60 KLT_FeatureList featurelist, /* features */
61 int ncols, int nrows, /* size of images */
62 int mindist, /* min. dist b/w features */
63 int min_eigenvalue, /* min. eigenvalue */
64 KLT_BOOL overwriteAllFeatures)
65 {
66 int indx; /* Index into features */
67 int x, y, val; /* Location and trackability of pixel under consideration */
68 uchar *featuremap; /* Boolean array recording proximity of features */
69 int *ptr;
70
71 /* Cannot add features with an eigenvalue less than one */
72 if (min_eigenvalue < 1) min_eigenvalue = 1;
73
74 /* Allocate memory for feature map and clear it */
75 featuremap = (uchar *) malloc(ncols * nrows * sizeof(uchar));
76 memset(featuremap, 0, ncols*nrows);
77
78 /* Necessary because code below works with (mindist-1) */
79 mindist--;
80
81 /* If we are keeping all old good features, then add them to the featuremap */
82 if (!overwriteAllFeatures)
83 for (indx = 0 ; indx < featurelist->nFeatures ; indx++)
84 if (featurelist->feature[indx]->val >= 0) {
85 x = (int) featurelist->feature[indx]->x;
86 y = (int) featurelist->feature[indx]->y;
87 _fillFeaturemap(x, y, featuremap, mindist, ncols, nrows);
88 }
89
90 /* For each feature point, in descending order of importance, do ... */
91 ptr = pointlist;
92 indx = 0;
93 while (1) {
94
95 /* If we can't add all the points, then fill in the rest
96 of the featurelist with -1's */
97 if (ptr >= pointlist + 3*npoints) {
98 while (indx < featurelist->nFeatures) {
99 if (overwriteAllFeatures ||
100 featurelist->feature[indx]->val < 0) {
101 featurelist->feature[indx]->x = -1;
102 featurelist->feature[indx]->y = -1;
103 featurelist->feature[indx]->val = KLT_NOT_FOUND;
104 }
105 indx++;
106 }
107 break;
108 }
109
110 x = *ptr++;
111 y = *ptr++;
112 val = *ptr++;
113
114 while (!overwriteAllFeatures &&
115 indx < featurelist->nFeatures &&
116 featurelist->feature[indx]->val >= 0)
117 indx++;
118
119 if (indx >= featurelist->nFeatures) break;
120
121 /* If no neighbor has been selected, and if the minimum
122 eigenvalue is large enough, then add feature to the current list */
123 if (!featuremap[y*ncols+x] && val >= min_eigenvalue) {
124 featurelist->feature[indx]->x = (KLT_locType) x;
125 featurelist->feature[indx]->y = (KLT_locType) y;
126 featurelist->feature[indx]->val = (int) val;
127 indx++;
128
129 /* Fill in surrounding region of feature map, but
130 make sure that pixels are in-bounds */
131 _fillFeaturemap(x, y, featuremap, mindist, ncols, nrows);
132 }
133 }
134
135 /* Free feature map */
136 free(featuremap);
137 }
138
139
140 /*********************************************************************
141 * _comparePoints
142 *
143 * Used by qsort (in _KLTSelectGoodFeatures) to determine
144 * which feature is better.
145 * By switching the '>' with the '<', qsort is fooled into sorting
146 * in descending order.
147 */
148
_comparePoints(const void * a,const void * b)149 int _comparePoints(const void *a, const void *b)
150 {
151 int v1 = *(((int *) a) + 2);
152 int v2 = *(((int *) b) + 2);
153
154 if (v1 > v2) return(-1);
155 else if (v1 < v2) return(1);
156 else return(0);
157 }
158
159 /*********************************************************************
160 * _sortPointList
161 */
162
_sortPointList(int * pointlist,int npoints)163 void _sortPointList(
164 int *pointlist,
165 int npoints)
166 {
167 qsort(pointlist, npoints, 3*sizeof(int), _comparePoints);
168 }
169
170
171 /*********************************************************************
172 * _minEigenvalue
173 *
174 * Given the three distinct elements of the symmetric 2x2 matrix
175 * [gxx gxy]
176 * [gxy gyy],
177 * Returns the minimum eigenvalue of the matrix.
178 */
179
_minEigenvalue(float gxx,float gxy,float gyy)180 float _minEigenvalue(float gxx, float gxy, float gyy)
181 {
182 return (float) ((gxx + gyy - sqrt((gxx - gyy)*(gxx - gyy) + 4*gxy*gxy)) / 2.0);
183 }
184
185
186 /*********************************************************************/
187
_KLTSelectGoodFeatures(KLT_TrackingContext tc,KLT_PixelType * img,int ncols,int nrows,KLT_FeatureList featurelist,selectionMode mode)188 void _KLTSelectGoodFeatures(
189 KLT_TrackingContext tc,
190 KLT_PixelType *img,
191 int ncols,
192 int nrows,
193 KLT_FeatureList featurelist,
194 selectionMode mode)
195 {
196 _KLT_FloatImage floatimg, gradx, grady;
197 int window_hw, window_hh;
198 int *pointlist;
199 int npoints = 0;
200 KLT_BOOL overwriteAllFeatures = (mode == SELECTING_ALL) ? TRUE : FALSE;
201 KLT_BOOL floatimages_created = FALSE;
202
203 /* Check window size (and correct if necessary) */
204 if (tc->window_width % 2 != 1) {
205 tc->window_width = tc->window_width+1;
206 KLTWarning("Tracking context's window width must be odd. "
207 "Changing to %d.\n", tc->window_width);
208 }
209 if (tc->window_height % 2 != 1) {
210 tc->window_height = tc->window_height+1;
211 KLTWarning("Tracking context's window height must be odd. "
212 "Changing to %d.\n", tc->window_height);
213 }
214 if (tc->window_width < 3) {
215 tc->window_width = 3;
216 KLTWarning("Tracking context's window width must be at least three. \n"
217 "Changing to %d.\n", tc->window_width);
218 }
219 if (tc->window_height < 3) {
220 tc->window_height = 3;
221 KLTWarning("Tracking context's window height must be at least three. \n"
222 "Changing to %d.\n", tc->window_height);
223 }
224 window_hw = tc->window_width/2;
225 window_hh = tc->window_height/2;
226
227 /* Create pointlist, which is a simplified version of a featurelist, */
228 /* for speed. Contains only integer locations and values. */
229 pointlist = (int *) malloc(ncols * nrows * 3 * sizeof(int));
230
231 /* Create temporary images, etc. */
232 if (mode == REPLACING_SOME && tc->sequentialMode && tc->pyramid_last != NULL) {
233
234 floatimg = ((_KLT_Pyramid) tc->pyramid_last)->img[0];
235 gradx = ((_KLT_Pyramid) tc->pyramid_last_gradx)->img[0];
236 grady = ((_KLT_Pyramid) tc->pyramid_last_grady)->img[0];
237
238 } else {
239
240 floatimages_created = TRUE;
241 floatimg = _KLTCreateFloatImage(ncols, nrows);
242 gradx = _KLTCreateFloatImage(ncols, nrows);
243 grady = _KLTCreateFloatImage(ncols, nrows);
244
245 if (tc->smoothBeforeSelecting) {
246
247 _KLT_FloatImage tmpimg;
248 tmpimg = _KLTCreateFloatImage(ncols, nrows);
249 _KLTToFloatImage(img, ncols, nrows, tmpimg);
250 _KLTComputeSmoothedImage(tmpimg, _KLTComputeSmoothSigma(tc), floatimg);
251 _KLTFreeFloatImage(tmpimg);
252
253 } else {
254
255 _KLTToFloatImage(img, ncols, nrows, floatimg);
256 }
257
258 /* Compute gradient of image in x and y direction */
259 _KLTComputeGradients(floatimg, tc->grad_sigma, gradx, grady);
260 }
261
262 /* Compute trackability of each image pixel as the minimum
263 of the two eigenvalues of the Z matrix */
264 {
265 float gx, gy;
266 float gxx, gxy, gyy;
267 int xx, yy;
268 int *ptr;
269 float val;
270 unsigned int limit = 1;
271 int borderx = tc->borderx; /* Must not touch cols */
272 int bordery = tc->bordery; /* lost by convolution */
273 int x, y;
274 int i;
275
276 if (borderx < window_hw) borderx = window_hw;
277 if (bordery < window_hh) bordery = window_hh;
278
279 /* Find largest value of an int */
280 for (i = 0 ; i < sizeof(int) ; i++) limit *= 256;
281 limit = limit/2 - 1;
282
283 /* For most of the pixels in the image, do ... */
284 ptr = pointlist;
285 for (y = bordery ; y < nrows - bordery ; y += tc->nSkippedPixels + 1)
286 for (x = borderx ; x < ncols - borderx ; x += tc->nSkippedPixels + 1) {
287
288 /* Sum the gradients in the surrounding window */
289 gxx = 0; gxy = 0; gyy = 0;
290 for (yy = y-window_hh ; yy <= y+window_hh ; yy++)
291 for (xx = x-window_hw ; xx <= x+window_hw ; xx++) {
292 gx = *(gradx->data + ncols*yy+xx);
293 gy = *(grady->data + ncols*yy+xx);
294 gxx += gx * gx;
295 gxy += gx * gy;
296 gyy += gy * gy;
297 }
298
299 /* Store the trackability of the pixel as the minimum
300 of the two eigenvalues */
301 *ptr++ = x;
302 *ptr++ = y;
303 val = _minEigenvalue(gxx, gxy, gyy);
304 if (val > limit) {
305 KLTWarning("(_KLTSelectGoodFeatures) minimum eigenvalue %f is "
306 "greater than the capacity of an int; setting "
307 "to maximum value", val);
308 val = (float) limit;
309 }
310 *ptr++ = (int) val;
311 npoints++;
312 }
313 }
314
315 /* Sort the features */
316 _sortPointList(pointlist, npoints);
317
318 /* Check tc->mindist */
319 if (tc->mindist < 0) {
320 KLTWarning("(_KLTSelectGoodFeatures) Tracking context field tc->mindist "
321 "is negative (%d); setting to zero", tc->mindist);
322 tc->mindist = 0;
323 }
324
325 /* Enforce minimum distance between features */
326 _enforceMinimumDistance(
327 pointlist,
328 npoints,
329 featurelist,
330 ncols, nrows,
331 tc->mindist,
332 tc->min_eigenvalue,
333 overwriteAllFeatures);
334
335 /* Free memory */
336 free(pointlist);
337 if (floatimages_created) {
338 _KLTFreeFloatImage(floatimg);
339 _KLTFreeFloatImage(gradx);
340 _KLTFreeFloatImage(grady);
341 }
342 }
343
344
345 /*********************************************************************
346 * KLTSelectGoodFeatures
347 *
348 * Main routine, visible to the outside. Finds the good features in
349 * an image.
350 *
351 * INPUTS
352 * tc: Contains parameters used in computation (size of image,
353 * size of window, min distance b/w features, sigma to compute
354 * image gradients, # of features desired).
355 * img: Pointer to the data of an image (probably unsigned chars).
356 *
357 * OUTPUTS
358 * features: List of features. The member nFeatures is computed.
359 */
360
KLTSelectGoodFeatures(KLT_TrackingContext tc,KLT_PixelType * img,int ncols,int nrows,KLT_FeatureList fl)361 void KLTSelectGoodFeatures(
362 KLT_TrackingContext tc,
363 KLT_PixelType *img,
364 int ncols,
365 int nrows,
366 KLT_FeatureList fl)
367 {
368 if (tc->verbose >= 1) {
369 fprintf(stderr, "(KLT) Selecting the %d best features "
370 "from a %d by %d image... ", fl->nFeatures, ncols, nrows);
371 fflush(stderr);
372 }
373
374 _KLTSelectGoodFeatures(tc, img, ncols, nrows,
375 fl, SELECTING_ALL);
376
377 if (tc->verbose >= 1)
378 fprintf(
379 stderr,
380 "\n\t%d features found.\n",
381 KLTCountRemainingFeatures(fl)
382 );
383 }
384
385
386 /*********************************************************************
387 * KLTReplaceLostFeatures
388 *
389 * Main routine, visible to the outside. Replaces the lost features
390 * in an image.
391 *
392 * INPUTS
393 * tc: Contains parameters used in computation (size of image,
394 * size of window, min distance b/w features, sigma to compute
395 * image gradients, # of features desired).
396 * img: Pointer to the data of an image (probably unsigned chars).
397 *
398 * OUTPUTS
399 * features: List of features. The member nFeatures is computed.
400 */
401
KLTReplaceLostFeatures(KLT_TrackingContext tc,KLT_PixelType * img,int ncols,int nrows,KLT_FeatureList fl)402 void KLTReplaceLostFeatures(
403 KLT_TrackingContext tc,
404 KLT_PixelType *img,
405 int ncols,
406 int nrows,
407 KLT_FeatureList fl)
408 {
409 int nLostFeatures = fl->nFeatures - KLTCountRemainingFeatures(fl);
410
411 if (tc->verbose >= 1) {
412 fprintf(stderr, "(KLT) Attempting to replace %d features "
413 "in a %d by %d image... ", nLostFeatures, ncols, nrows);
414 fflush(stderr);
415 }
416
417 /* If there are any lost features, replace them */
418 if (nLostFeatures > 0)
419 _KLTSelectGoodFeatures(tc, img, ncols, nrows,
420 fl, REPLACING_SOME);
421
422 if (tc->verbose >= 1)
423 fprintf(
424 stderr,
425 "\n\t%d features replaced.\n",
426 nLostFeatures - fl->nFeatures + KLTCountRemainingFeatures(fl)
427 );
428 }
429
430
431