Files
rawTherapee/rtengine/klt/klt.cc
Hombre 8b2eac9a3d Pipette and "On Preview Widgets" branch. See issue 227
The pipette part is already working quite nice but need to be finished. The widgets part needs more work...
2014-01-21 23:37:36 +01:00

536 lines
15 KiB
C++

/*********************************************************************
* klt.c
*
* Kanade-Lucas-Tomasi tracker
*********************************************************************/
/* Standard includes */
#include <cassert>
#include <cmath> /* logf() */
#include <cstdlib> /* malloc() */
#include "../rt_math.h"
/* Our includes */
#include "base.h"
#include "convolve.h"
#include "error.h"
#include "klt.h"
#include "pyramid.h"
using namespace std;
static const int mindist = 10;
static const int window_size = 7;
static const int min_eigenvalue = 1;
static const float min_determinant = 0.01f;
static const float min_displacement = 0.1f;
static const int max_iterations = 10;
static const float max_residue = 10.0f;
static const float grad_sigma = 1.0f;
static const float smooth_sigma_fact = 0.1f;
static const float pyramid_sigma_fact = 0.9f;
static const float step_factor = 1.0f;
static const KLT_BOOL sequentialMode = FALSE;
static const KLT_BOOL lighting_insensitive = FALSE;
/* for affine mapping*/
static const int affineConsistencyCheck = -1;
static const int affine_window_size = 15;
static const int affine_max_iterations = 10;
static const float affine_max_residue = 10.0;
static const float affine_min_displacement = 0.02f;
static const float affine_max_displacement_differ = 1.5f;
static const KLT_BOOL smoothBeforeSelecting = TRUE;
static const KLT_BOOL writeInternalImages = FALSE;
static const int search_range = 15;
static const int nSkippedPixels = 0;
extern int KLT_verbose;
/*********************************************************************
* _createArray2D
*
* Creates a two-dimensional array.
*
* INPUTS
* ncols: no. of columns
* nrows: no. of rows
* nbytes: no. of bytes per entry
*
* RETURNS
* Pointer to an array. Must be coerced.
*
* EXAMPLE
* char **ar;
* ar = (char **) createArray2D(8, 5, sizeof(char));
*/
static void** _createArray2D(int ncols, int nrows, int nbytes)
{
char **tt;
int i;
tt = (char **) malloc(nrows * sizeof(void *) +
ncols * nrows * nbytes);
if (tt == NULL) {
KLTError("(createArray2D) Out of memory");
exit(1);
}
for (i = 0 ; i < nrows ; i++)
tt[i] = ((char *) tt) + (nrows * sizeof(void *) +
i * ncols * nbytes);
return((void **) tt);
}
/*********************************************************************
* KLTCreateTrackingContext
*
*/
KLT_TrackingContext KLTCreateTrackingContext()
{
KLT_TrackingContext tc;
/* Allocate memory */
tc = (KLT_TrackingContext) malloc(sizeof(KLT_TrackingContextRec));
/* Set values to default values */
tc->mindist = mindist;
tc->window_width = window_size;
tc->window_height = window_size;
tc->sequentialMode = sequentialMode;
tc->smoothBeforeSelecting = smoothBeforeSelecting;
tc->writeInternalImages = writeInternalImages;
tc->lighting_insensitive = lighting_insensitive;
tc->min_eigenvalue = min_eigenvalue;
tc->min_determinant = min_determinant;
tc->max_iterations = max_iterations;
tc->min_displacement = min_displacement;
tc->max_residue = max_residue;
tc->grad_sigma = grad_sigma;
tc->smooth_sigma_fact = smooth_sigma_fact;
tc->pyramid_sigma_fact = pyramid_sigma_fact;
tc->step_factor = step_factor;
tc->nSkippedPixels = nSkippedPixels;
tc->pyramid_last = NULL;
tc->pyramid_last_gradx = NULL;
tc->pyramid_last_grady = NULL;
/* for affine mapping */
tc->affineConsistencyCheck = affineConsistencyCheck;
tc->affine_window_width = affine_window_size;
tc->affine_window_height = affine_window_size;
tc->affine_max_iterations = affine_max_iterations;
tc->affine_max_residue = affine_max_residue;
tc->affine_min_displacement = affine_min_displacement;
tc->affine_max_displacement_differ = affine_max_displacement_differ;
/* Change nPyramidLevels and subsampling */
KLTChangeTCPyramid(tc, search_range);
/* Update border, which is dependent upon */
/* smooth_sigma_fact, pyramid_sigma_fact, window_size, and subsampling */
KLTUpdateTCBorder(tc);
return(tc);
}
/*********************************************************************
* KLTCreateFeatureList
*
*/
KLT_FeatureList KLTCreateFeatureList(
int nFeatures)
{
KLT_FeatureList fl;
KLT_Feature first;
int nbytes = sizeof(KLT_FeatureListRec) +
nFeatures * sizeof(KLT_Feature) +
nFeatures * sizeof(KLT_FeatureRec);
int i;
/* Allocate memory for feature list */
fl = (KLT_FeatureList) malloc(nbytes);
/* Set parameters */
fl->nFeatures = nFeatures;
/* Set pointers */
fl->feature = (KLT_Feature *) (fl + 1);
first = (KLT_Feature) (fl->feature + nFeatures);
for (i = 0 ; i < nFeatures ; i++) {
fl->feature[i] = first + i;
fl->feature[i]->aff_img = NULL; /* initialization fixed by Sinisa Segvic */
fl->feature[i]->aff_img_gradx = NULL;
fl->feature[i]->aff_img_grady = NULL;
}
/* Return feature list */
return(fl);
}
/*********************************************************************
* KLTCreateFeatureHistory
*
*/
KLT_FeatureHistory KLTCreateFeatureHistory(
int nFrames)
{
KLT_FeatureHistory fh;
KLT_Feature first;
int nbytes = sizeof(KLT_FeatureHistoryRec) +
nFrames * sizeof(KLT_Feature) +
nFrames * sizeof(KLT_FeatureRec);
int i;
/* Allocate memory for feature history */
fh = (KLT_FeatureHistory) malloc(nbytes);
/* Set parameters */
fh->nFrames = nFrames;
/* Set pointers */
fh->feature = (KLT_Feature *) (fh + 1);
first = (KLT_Feature) (fh->feature + nFrames);
for (i = 0 ; i < nFrames ; i++)
fh->feature[i] = first + i;
/* Return feature history */
return(fh);
}
/*********************************************************************
* KLTCreateFeatureTable
*
*/
KLT_FeatureTable KLTCreateFeatureTable(
int nFrames,
int nFeatures)
{
KLT_FeatureTable ft;
KLT_Feature first;
int nbytes = sizeof(KLT_FeatureTableRec);
int i, j;
/* Allocate memory for feature history */
ft = (KLT_FeatureTable) malloc(nbytes);
/* Set parameters */
ft->nFrames = nFrames;
ft->nFeatures = nFeatures;
/* Set pointers */
ft->feature = (KLT_Feature **)
_createArray2D(nFrames, nFeatures, sizeof(KLT_Feature));
first = (KLT_Feature) malloc(nFrames * nFeatures * sizeof(KLT_FeatureRec));
for (j = 0 ; j < nFeatures ; j++)
for (i = 0 ; i < nFrames ; i++)
ft->feature[j][i] = first + j*nFrames + i;
//free(first);
/* Return feature table */
return(ft);
}
/*********************************************************************
* KLTPrintTrackingContext
*/
void KLTPrintTrackingContext(
KLT_TrackingContext tc)
{
fprintf(stderr, "\n\nTracking context:\n\n");
fprintf(stderr, "\tmindist = %d\n", tc->mindist);
fprintf(stderr, "\twindow_width = %d\n", tc->window_width);
fprintf(stderr, "\twindow_height = %d\n", tc->window_height);
fprintf(stderr, "\tsequentialMode = %s\n",
tc->sequentialMode ? "TRUE" : "FALSE");
fprintf(stderr, "\tsmoothBeforeSelecting = %s\n",
tc->smoothBeforeSelecting ? "TRUE" : "FALSE");
fprintf(stderr, "\twriteInternalImages = %s\n",
tc->writeInternalImages ? "TRUE" : "FALSE");
fprintf(stderr, "\tmin_eigenvalue = %d\n", tc->min_eigenvalue);
fprintf(stderr, "\tmin_determinant = %f\n", tc->min_determinant);
fprintf(stderr, "\tmin_displacement = %f\n", tc->min_displacement);
fprintf(stderr, "\tmax_iterations = %d\n", tc->max_iterations);
fprintf(stderr, "\tmax_residue = %f\n", tc->max_residue);
fprintf(stderr, "\tgrad_sigma = %f\n", tc->grad_sigma);
fprintf(stderr, "\tsmooth_sigma_fact = %f\n", tc->smooth_sigma_fact);
fprintf(stderr, "\tpyramid_sigma_fact = %f\n", tc->pyramid_sigma_fact);
fprintf(stderr, "\tnSkippedPixels = %d\n", tc->nSkippedPixels);
fprintf(stderr, "\tborderx = %d\n", tc->borderx);
fprintf(stderr, "\tbordery = %d\n", tc->bordery);
fprintf(stderr, "\tnPyramidLevels = %d\n", tc->nPyramidLevels);
fprintf(stderr, "\tsubsampling = %d\n", tc->subsampling);
fprintf(stderr, "\n\tpyramid_last = %s\n", (tc->pyramid_last!=NULL) ?
"points to old image" : "NULL");
fprintf(stderr, "\tpyramid_last_gradx = %s\n",
(tc->pyramid_last_gradx!=NULL) ?
"points to old image" : "NULL");
fprintf(stderr, "\tpyramid_last_grady = %s\n",
(tc->pyramid_last_grady!=NULL) ?
"points to old image" : "NULL");
fprintf(stderr, "\n\n");
}
/*********************************************************************
* KLTChangeTCPyramid
*
*/
void KLTChangeTCPyramid(
KLT_TrackingContext tc,
int search_range)
{
float window_halfwidth;
float subsampling;
/* Check window size (and correct if necessary) */
if (tc->window_width % 2 != 1) {
tc->window_width = tc->window_width+1;
KLTWarning("(KLTChangeTCPyramid) Window width must be odd. "
"Changing to %d.\n", tc->window_width);
}
if (tc->window_height % 2 != 1) {
tc->window_height = tc->window_height+1;
KLTWarning("(KLTChangeTCPyramid) Window height must be odd. "
"Changing to %d.\n", tc->window_height);
}
if (tc->window_width < 3) {
tc->window_width = 3;
KLTWarning("(KLTChangeTCPyramid) Window width must be at least three. \n"
"Changing to %d.\n", tc->window_width);
}
if (tc->window_height < 3) {
tc->window_height = 3;
KLTWarning("(KLTChangeTCPyramid) Window height must be at least three. \n"
"Changing to %d.\n", tc->window_height);
}
window_halfwidth = min(tc->window_width,tc->window_height)/2.0f;
subsampling = ((float) search_range) / window_halfwidth;
if (subsampling < 1.0) { /* 1.0 = 0+1 */
tc->nPyramidLevels = 1;
} else if (subsampling <= 3.0) { /* 3.0 = 2+1 */
tc->nPyramidLevels = 2;
tc->subsampling = 2;
} else if (subsampling <= 5.0) { /* 5.0 = 4+1 */
tc->nPyramidLevels = 2;
tc->subsampling = 4;
} else if (subsampling <= 9.0) { /* 9.0 = 8+1 */
tc->nPyramidLevels = 2;
tc->subsampling = 8;
} else {
/* The following lines are derived from the formula:
search_range =
window_halfwidth * \sum_{i=0}^{nPyramidLevels-1} 8^i,
which is the same as:
search_range =
window_halfwidth * (8^nPyramidLevels - 1)/(8 - 1).
Then, the value is rounded up to the nearest integer. */
float val = (float) (log(7.0*subsampling+1.0)/log(8.0));
tc->nPyramidLevels = (int) (val + 0.99);
tc->subsampling = 8;
}
}
/*********************************************************************
* NOTE: Manually must ensure consistency with _KLTComputePyramid()
*/
static float _pyramidSigma(
KLT_TrackingContext tc)
{
return (tc->pyramid_sigma_fact * tc->subsampling);
}
/*********************************************************************
* Updates border, which is dependent upon
* smooth_sigma_fact, pyramid_sigma_fact, window_size, and subsampling
*/
void KLTUpdateTCBorder(
KLT_TrackingContext tc)
{
float val;
int pyramid_gauss_hw;
int smooth_gauss_hw;
int gauss_width, gaussderiv_width;
int num_levels = tc->nPyramidLevels;
int n_invalid_pixels;
int window_hw;
int ss = tc->subsampling;
int ss_power;
int border;
int i;
/* Check window size (and correct if necessary) */
if (tc->window_width % 2 != 1) {
tc->window_width = tc->window_width+1;
KLTWarning("(KLTUpdateTCBorder) Window width must be odd. "
"Changing to %d.\n", tc->window_width);
}
if (tc->window_height % 2 != 1) {
tc->window_height = tc->window_height+1;
KLTWarning("(KLTUpdateTCBorder) Window height must be odd. "
"Changing to %d.\n", tc->window_height);
}
if (tc->window_width < 3) {
tc->window_width = 3;
KLTWarning("(KLTUpdateTCBorder) Window width must be at least three. \n"
"Changing to %d.\n", tc->window_width);
}
if (tc->window_height < 3) {
tc->window_height = 3;
KLTWarning("(KLTUpdateTCBorder) Window height must be at least three. \n"
"Changing to %d.\n", tc->window_height);
}
window_hw = max(tc->window_width, tc->window_height)/2;
/* Find widths of convolution windows */
_KLTGetKernelWidths(_KLTComputeSmoothSigma(tc),
&gauss_width, &gaussderiv_width);
smooth_gauss_hw = gauss_width/2;
_KLTGetKernelWidths(_pyramidSigma(tc),
&gauss_width, &gaussderiv_width);
pyramid_gauss_hw = gauss_width/2;
/* Compute the # of invalid pixels at each level of the pyramid.
n_invalid_pixels is computed with respect to the ith level
of the pyramid. So, e.g., if n_invalid_pixels = 5 after
the first iteration, then there are 5 invalid pixels in
level 1, which translated means 5*subsampling invalid pixels
in the original level 0. */
n_invalid_pixels = smooth_gauss_hw;
for (i = 1 ; i < num_levels ; i++) {
val = ((float) n_invalid_pixels + pyramid_gauss_hw) / ss;
n_invalid_pixels = (int) (val + 0.99); /* Round up */
}
/* ss_power = ss^(num_levels-1) */
ss_power = 1;
for (i = 1 ; i < num_levels ; i++)
ss_power *= ss;
/* Compute border by translating invalid pixels back into */
/* original image */
border = (n_invalid_pixels + window_hw) * ss_power;
tc->borderx = border;
tc->bordery = border;
}
/*********************************************************************
* KLTFreeTrackingContext
* KLTFreeFeatureList
* KLTFreeFeatureHistory
* KLTFreeFeatureTable
*/
void KLTFreeTrackingContext(
KLT_TrackingContext tc)
{
if (tc->pyramid_last)
_KLTFreePyramid((_KLT_Pyramid) tc->pyramid_last);
if (tc->pyramid_last_gradx)
_KLTFreePyramid((_KLT_Pyramid) tc->pyramid_last_gradx);
if (tc->pyramid_last_grady)
_KLTFreePyramid((_KLT_Pyramid) tc->pyramid_last_grady);
free(tc);
}
void KLTFreeFeatureList(
KLT_FeatureList fl)
{
/* for affine mapping */
int indx;
for (indx = 0 ; indx < fl->nFeatures ; indx++) {
/* free image and gradient */
_KLTFreeFloatImage(fl->feature[indx]->aff_img);
_KLTFreeFloatImage(fl->feature[indx]->aff_img_gradx);
_KLTFreeFloatImage(fl->feature[indx]->aff_img_grady);
fl->feature[indx]->aff_img = NULL;
fl->feature[indx]->aff_img_gradx = NULL;
fl->feature[indx]->aff_img_grady = NULL;
}
free(fl);
}
void KLTFreeFeatureHistory(
KLT_FeatureHistory fh)
{
free(fh);
}
void KLTFreeFeatureTable(
KLT_FeatureTable ft)
{
free(ft->feature[0][0]); /* this plugs a memory leak found by Stefan Wachter */
free(ft->feature);
free(ft);
}
/*********************************************************************
* KLTStopSequentialMode
*/
void KLTStopSequentialMode(
KLT_TrackingContext tc)
{
tc->sequentialMode = FALSE;
_KLTFreePyramid((_KLT_Pyramid) tc->pyramid_last);
_KLTFreePyramid((_KLT_Pyramid) tc->pyramid_last_gradx);
_KLTFreePyramid((_KLT_Pyramid) tc->pyramid_last_grady);
tc->pyramid_last = NULL;
tc->pyramid_last_gradx = NULL;
tc->pyramid_last_grady = NULL;
}
/*********************************************************************
* KLTCountRemainingFeatures
*/
int KLTCountRemainingFeatures(
KLT_FeatureList fl)
{
int count = 0;
int i;
for (i = 0 ; i < fl->nFeatures ; i++)
if (fl->feature[i]->val >= 0)
count++;
return count;
}
/*********************************************************************
* KLTSetVerbosity
*/
void KLTSetVerbosity(
int verbosity)
{
KLT_verbose = verbosity;
}