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