Chapter 2: Selecting and Tracking Features

Below is a sample program that selects the 100 best features in an image and tracks them in the next image. Instead of requiring images to be in a particular format, KLT needs only a pointer to the raw image data (in row-major format), along with the dimensions of the image. (The data is assumed to be unsigned char's, but this can be changed by modifying the code and recompiling.) We will now describe the code, line-by-line.

The header file "pnmio.h" is necessary only for the helper function pgmReadFile, while "klt.h" must always be included in programs using KLT.

KLTCreateTrackingContext() creates a tracking context, which is a structure containing various parameters (such as window size, sigma for Gaussian smoothing, minimum distance between features, etc.) that govern the computation. The nature of the structure is not important for now.

KLTCreateFeatureList() creates a feature list, which is a structure containing all the features in a given image. The parameter of the function is the number of features desired.

The helper function pgmReadFile() has been provided for reading PGM images, but it can be removed if the raw image data is obtained in another manner. Passing NULL as the second parameter causes pgmReadFile() to allocate memory for the image.

KLTSelectGoodFeatures() finds the N best features in an image and stores them in a feature list in descending order of goodness. N is the size of the feature list (in this case 100). (Note: It is also possible to select all the features above a certain level of goodness, rather than just the N best features, by simply changing the min_eigenvalue field of the tracking context, as described in Chapter 7.)

The for loop demonstrates how to access data in the feature list. A feature list is simply an array of fl->nFeatures features, where each feature has three members: x location, y location, and value. The first two are floats, and the second is an int. The location is given in pixels with the origin in the upper-left corner. The value is a constant multiplied by the minimum eigenvalue of the window surrounding the feature -- the larger the value, the better the feature. (NOTE: Because fl->nFeatures indicates the amount of memory allocated, it must not be changed by hand.)

KLTWriteFeatureListToPPM() writes the grey-level image to a PPM file and overlays the features in red. This image can then be viewed with a tool like xv.

KLTWriteFeatureList() writes the features to a file (If the second parameter is NULL, then the features are written to stderr instead). You will probably want to keep the third parameter NULL, which causes the file to be binary. Binary files are better because they are smaller and cause no truncation error. However, if a text file is desired, then the third parameter must be a string beginning with '%' and ending with either 'd' or 'f', as in "%3d" or "%5.1f", which specifies the format of the feature locations to be either decimal or floating point. Once the feature list is written, the file can be viewed off-line (if it is text) or read back into a feature list using the command KLTReadFeatureList().

KLTTrackFeatures() tracks the features from the first image to the second image and overwrites the feature list. If a feature is successfully tracked, then its value becomes zero; otherwise, it becomes negative. Note that this function does not resort the features, but rather keeps them in place so that correspondences between the images are maintained. Chapter 5 describes the KLT_FeatureTable structure, which is useful for storing the feature list before it is overwritten.

And that's all there is to selecting and tracking features. The remaining chapters describe various extensions that may be helpful.

Example 1

Finds the 100 best features in an image, and tracks these
features to the next image.  Saves the feature
locations (before and after tracking) to text files and to PPM files, 
and prints the features to the screen.

#include "pnmio.h"
#include "klt.h"

void main()
     unsigned char *img1, *img2;
     KLT_TrackingContext tc;
     KLT_FeatureList fl;
     int nFeatures = 100;
     int ncols, nrows;
     int i;

     tc = KLTCreateTrackingContext();
     fl = KLTCreateFeatureList(nFeatures);

     img1 = pgmReadFile("img0.pgm", NULL, &ncols, &nrows);
     img2 = pgmReadFile("img1.pgm", NULL, &ncols, &nrows);

     KLTSelectGoodFeatures(tc, img1, ncols, nrows, fl);

     printf("\nIn first image:\n");
     for (i = 0 ; i < fl->nFeatures ; i++)  {
          printf("Feature #%d:  (%f,%f) with value of %d\n",
               i, fl->feature[i]->x, fl->feature[i]->y, fl->feature[i]->val);

     KLTWriteFeatureListToPPM(fl, img1, ncols, nrows, "feat1.ppm");
     KLTWriteFeatureList(fl, "feat1.txt", "%3d");

     KLTTrackFeatures(tc, img1, img2, ncols, nrows, fl);

     printf("\nIn second image:\n");
     for (i = 0 ; i < fl->nFeatures ; i++)  {
          printf("Feature #%d:  (%f,%f) with value of %d\n",
               i, fl->feature[i]->x, fl->feature[i]->y, fl->feature[i]->val);

     KLTWriteFeatureListToPPM(fl, img2, ncols, nrows, "feat2.ppm");
     KLTWriteFeatureList(fl, "feat2.fl", NULL);  /* binary file */
     KLTWriteFeatureList(fl, "feat2.txt", "%5.1f");  /* text file */