001/**
002 * This source code file is part of a direct port of Stan Birchfield's implementation
003 * of a Kanade-Lucas-Tomasi feature tracker. The original implementation can be found
004 * here: http://www.ces.clemson.edu/~stb/klt/
005 *
006 * As per the original code, the source code is in the public domain, available
007 * for both commercial and non-commercial use.
008 */
009package org.openimaj.video.tracking.klt;
010import org.openimaj.image.FImage;
011import org.openimaj.math.geometry.shape.Shape;
012
013
014/*********************************************************************
015 * klt.c
016 *
017 * Kanade-Lucas-Tomasi tracker
018 *********************************************************************/
019public class TrackingContext {
020        protected final static int _mindist = 10;
021        protected final static int _window_size = 7;
022        protected final static int _min_eigenvalue = 1;
023        protected final static float _min_determinant = 0.01f;
024        protected final static float _min_displacement = 0.1f;
025        protected final static int _max_iterations = 10;
026        protected final static float _max_residue = 10.0f;
027        protected final static float _grad_sigma = 1.0f;
028        protected final static float _smooth_sigma_fact = 0.1f;
029        protected final static float _pyramid_sigma_fact = 0.9f;
030        protected final static float _step_factor = 1.0f;
031        protected static boolean _sequentialMode = false;
032
033        protected final static boolean _lighting_insensitive = false;
034        /* for affine mapping*/
035        protected final static int _affineConsistencyCheck = -1;
036        protected final static int _affine_window_size = 15;
037        protected final static int _affine_max_iterations = 10;
038        protected final static float _affine_max_residue = 10.0f;
039        protected final static float _affine_min_displacement = 0.02f;
040        protected final static float _affine_max_displacement_differ = 1.5f;
041
042        protected final static boolean _smoothBeforeSelecting = true;
043        protected final static boolean _writeInternalImages = false;
044        protected final static int _search_range = 15;
045        protected final static int _nSkippedPixels = 0;
046
047        /* Available to user */
048        int mindist;                    /* min distance b/w features */
049        int window_width, window_height;
050        boolean sequentialMode; /* whether to save most recent image to save time */
051        /* can set to TRUE manually, but don't set to */
052        /* FALSE manually */
053        boolean smoothBeforeSelecting;  /* whether to smooth image before */
054        /* selecting features */
055        boolean writeInternalImages;    /* whether to write internal images */
056
057        /* tracking features */
058        boolean lighting_insensitive;  /* whether to normalize for gain and bias (not in original algorithm) */
059
060
061        /* Available, but hopefully can ignore */
062        int min_eigenvalue;             /* smallest eigenvalue allowed for selecting */
063        float min_determinant;  /* th for determining lost */
064        float min_displacement; /* th for stopping tracking when pixel changes little */
065        int max_iterations;             /* th for stopping tracking when too many iterations */
066        float max_residue;              /* th for stopping tracking when residue is large */
067        float grad_sigma;
068        float smooth_sigma_fact;
069        float pyramid_sigma_fact;
070        float step_factor;  /* size of Newton steps; 2.0 comes from equations, 1.0 seems to avoid overshooting */
071        int nSkippedPixels;             /* # of pixels skipped when finding features */
072        int borderx;                    /* border in which features will not be found */
073        int bordery;
074        int nPyramidLevels;             /* computed from search_ranges */
075        int subsampling;                /*              " */
076
077        /* for affine mapping */ 
078        int affine_window_width, affine_window_height;
079        int affineConsistencyCheck; /* whether to evaluates the consistency of features with affine mapping 
080                                         -1 = don't evaluates the consistency
081                                         0 = evaluates the consistency of features with translation mapping
082                                         1 = evaluates the consistency of features with similarity mapping
083                                         2 = evaluates the consistency of features with affine mapping
084         */
085        int affine_max_iterations;  
086        float affine_max_residue;
087        float affine_min_displacement;        
088        float affine_max_displacement_differ; /* th for the difference between the displacement calculated 
089                                                   by the affine tracker and the frame to frame tracker in pel*/
090        
091        private Shape targetArea = null;
092
093        /* User must not touch these */
094        private Pyramid pyramid_last;
095        private Pyramid pyramid_last_gradx;
096        private Pyramid pyramid_last_grady;
097        
098        /**
099         * @return a {@link PyramidSet} of the previous image's pyramids. Null if not previous image
100         */
101        public PyramidSet previousPyramidSet(){
102                if(pyramid_last == null)
103                        return null;
104                else
105                        return new PyramidSet(pyramid_last,pyramid_last_gradx,pyramid_last_grady);
106        }
107
108        /*********************************************************************
109         * KLTCreateTrackingContext
110         *
111         */
112
113        public TrackingContext()
114        {
115                /* Set values to default values */
116                this.mindist = _mindist;
117                this.window_width = _window_size;
118                this.window_height = _window_size;
119                this.sequentialMode = _sequentialMode;
120                this.smoothBeforeSelecting = _smoothBeforeSelecting;
121                this.writeInternalImages = _writeInternalImages;
122
123                this.lighting_insensitive = _lighting_insensitive;
124                this.min_eigenvalue = _min_eigenvalue;
125                this.min_determinant = _min_determinant;
126                this.max_iterations = _max_iterations;
127                this.min_displacement = _min_displacement;
128                this.max_residue = _max_residue;
129                this.grad_sigma = _grad_sigma;
130                this.smooth_sigma_fact = _smooth_sigma_fact;
131                this.pyramid_sigma_fact = _pyramid_sigma_fact;
132                this.step_factor = _step_factor;
133                this.nSkippedPixels = _nSkippedPixels;
134                this.pyramid_last = null;
135                this.pyramid_last_gradx = null;
136                this.pyramid_last_grady = null;
137                /* for affine mapping */
138                this.affineConsistencyCheck = _affineConsistencyCheck;
139                this.affine_window_width = _affine_window_size;
140                this.affine_window_height = _affine_window_size;
141                this.affine_max_iterations = _affine_max_iterations;
142                this.affine_max_residue = _affine_max_residue;
143                this.affine_min_displacement = _affine_min_displacement;
144                this.affine_max_displacement_differ = _affine_max_displacement_differ;
145
146                /* Change nPyramidLevels and subsampling */
147                changeTCPyramid(_search_range);
148
149                /* Update border, which is dependent upon */
150                /* smooth_sigma_fact, pyramid_sigma_fact, window_size, and subsampling */
151                updateTCBorder();
152        }
153
154        /*********************************************************************
155         * KLTPrintTrackingContext
156         */
157        @Override
158        public String toString()
159        {
160                String s = "";
161                s += String.format("\n\nTracking context:\n\n");
162                s += String.format("\tmindist = %d\n", this.mindist);
163                s += String.format("\twindow_width = %d\n", this.window_width);
164                s += String.format("\twindow_height = %d\n", this.window_height);
165                s += String.format("\tsequentialMode = %s\n", this.sequentialMode ? "true" : "false");
166                s += String.format("\tsmoothBeforeSelecting = %s\n", this.smoothBeforeSelecting ? "true" : "false");
167                s += String.format("\twriteInternalImages = %s\n", this.writeInternalImages ? "true" : "false");
168
169                s += String.format("\tmin_eigenvalue = %d\n", this.min_eigenvalue);
170                s += String.format("\tmin_determinant = %f\n", this.min_determinant);
171                s += String.format("\tmin_displacement = %f\n", this.min_displacement);
172                s += String.format("\tmax_iterations = %d\n", this.max_iterations);
173                s += String.format("\tmax_residue = %f\n", this.max_residue);
174                s += String.format("\tgrad_sigma = %f\n", this.grad_sigma);
175                s += String.format("\tsmooth_sigma_fact = %f\n", this.smooth_sigma_fact);
176                s += String.format("\tpyramid_sigma_fact = %f\n", this.pyramid_sigma_fact);
177                s += String.format("\tnSkippedPixels = %d\n", this.nSkippedPixels);
178                s += String.format("\tborderx = %d\n", this.borderx);
179                s += String.format("\tbordery = %d\n", this.bordery);
180                s += String.format("\tnPyramidLevels = %d\n", this.nPyramidLevels);
181                s += String.format("\tsubsampling = %d\n", this.subsampling);
182
183                s += String.format("\n\tpyramid_last = %s\n", (this.pyramid_last!=null) ? "points to old image" : "null");
184                s += String.format("\tpyramid_last_gradx = %s\n", (this.pyramid_last_gradx!=null) ? "points to old image" : "null");
185                s += String.format("\tpyramid_last_grady = %s\n", (this.pyramid_last_grady!=null) ? "points to old image" : "null");
186                s += String.format("\n\n");
187
188                return s;
189        }
190
191
192        /*********************************************************************
193         * KLTChangeTCPyramid
194         * @param search_range 
195         *
196         */
197        public void changeTCPyramid(int search_range) {
198                float window_halfwidth;
199                float subsampling;
200
201                /* Check window size (and correct if necessary) */
202                if (this.window_width % 2 != 1) {
203                        this.window_width = this.window_width+1;
204                        System.err.format("(KLTChangeTCPyramid) Window width must be odd. Changing to %d.\n", this.window_width);
205                }
206                if (this.window_height % 2 != 1) {
207                        this.window_height = this.window_height+1;
208                        System.err.format("(KLTChangeTCPyramid) Window height must be odd. Changing to %d.\n", this.window_height);
209                }
210                if (this.window_width < 3) {
211                        this.window_width = 3;
212                        System.err.format("(KLTChangeTCPyramid) Window width must be at least three. \nChanging to %d.\n", this.window_width);
213                }
214                if (this.window_height < 3) {
215                        this.window_height = 3;
216                        System.err.format("(KLTChangeTCPyramid) Window height must be at least three. \nChanging to %d.\n", this.window_height);
217                }
218                window_halfwidth = Math.min(this.window_width,this.window_height)/2.0f;
219
220                subsampling = search_range / window_halfwidth;
221
222                if (subsampling < 1.0) {                /* 1.0 = 0+1 */
223                        this.nPyramidLevels = 1;
224                } else if (subsampling <= 3.0) {        /* 3.0 = 2+1 */
225                        this.nPyramidLevels = 2;
226                        this.subsampling = 2;
227                } else if (subsampling <= 5.0) {        /* 5.0 = 4+1 */
228                        this.nPyramidLevels = 2;
229                        this.subsampling = 4;
230                } else if (subsampling <= 9.0) {        /* 9.0 = 8+1 */
231                        this.nPyramidLevels = 2;
232                        this.subsampling = 8;
233                } else {
234                        /* The following lines are derived from the formula:
235            search_range = 
236            window_halfwidth * \sum_{i=0}^{nPyramidLevels-1} 8^i,
237            which is the same as:
238            search_range = 
239            window_halfwidth * (8^nPyramidLevels - 1)/(8 - 1).
240            Then, the value is rounded up to the nearest integer. */
241                        float val = (float) (Math.log(7.0*subsampling+1.0)/Math.log(8.0));
242                        this.nPyramidLevels = (int) (val + 0.99);
243                        this.subsampling = 8;
244                }
245        }
246
247
248        /*********************************************************************
249         * NOTE: Manually must ensure consistency with _KLTComputePyramid()
250         */
251        float _pyramidSigma()
252        {
253                return (this.pyramid_sigma_fact * this.subsampling);
254        }
255
256
257        /*********************************************************************
258         * Updates border, which is dependent upon 
259         * smooth_sigma_fact, pyramid_sigma_fact, window_size, and subsampling
260         */
261
262        public void updateTCBorder() {
263                float val;
264                int pyramid_gauss_hw;
265                int smooth_gauss_hw;
266                int gauss_width;
267                int num_levels = this.nPyramidLevels;
268                int n_invalid_pixels;
269                int window_hw;
270                int ss = this.subsampling;
271                int ss_power;
272                int border;
273                int i;
274
275                /* Check window size (and correct if necessary) */
276                if (this.window_width % 2 != 1) {
277                        this.window_width = this.window_width+1;
278                        System.err.format("(KLTUpdateTCBorder) Window width must be odd. Changing to %d.\n", this.window_width);
279                }
280                if (this.window_height % 2 != 1) {
281                        this.window_height = this.window_height+1;
282                        System.err.format("(KLTUpdateTCBorder) Window height must be odd. Changing to %d.\n", this.window_height);
283                }
284                if (this.window_width < 3) {
285                        this.window_width = 3;
286                        System.err.format("(KLTUpdateTCBorder) Window width must be at least three. \nChanging to %d.\n", this.window_width);
287                }
288                if (this.window_height < 3) {
289                        this.window_height = 3;
290                        System.err.format("(KLTUpdateTCBorder) Window height must be at least three. \nChanging to %d.\n", this.window_height);
291                }
292                window_hw = Math.max(this.window_width, this.window_height)/2;
293
294                /* Find widths of convolution windows */
295                gauss_width=_getKernelWidths(computeSmoothSigma())[0];
296                smooth_gauss_hw = gauss_width/2;
297                
298                gauss_width = _getKernelWidths(_pyramidSigma())[0];
299                pyramid_gauss_hw = gauss_width/2;
300
301                /* Compute the # of invalid pixels at each level of the pyramid.
302           n_invalid_pixels is computed with respect to the ith level  
303           of the pyramid. So, e.g., if n_invalid_pixels = 5 after  
304           the first iteration, then there are 5 invalid pixels in  
305           level 1, which translated means 5*subsampling invalid pixels  
306           in the original level 0. */
307                n_invalid_pixels = smooth_gauss_hw;
308                for (i = 1 ; i < num_levels ; i++) {
309                        val = ((float) n_invalid_pixels + pyramid_gauss_hw) / ss;
310                        n_invalid_pixels = (int) (val + 0.99); /* Round up */
311                }
312
313                /* ss_power = ss^(num_levels-1) */
314                ss_power = 1;
315                for (i = 1 ; i < num_levels ; i++)
316                        ss_power *= ss;
317
318                /* Compute border by translating invalid pixels back into */
319                /* original image */
320                border = (n_invalid_pixels + window_hw) * ss_power;
321
322                this.borderx = border;
323                this.bordery = border;
324        }
325
326        /*********************************************************************
327         * KLTStopSequentialMode
328         */
329        void stopSequentialMode()
330        {
331                this.sequentialMode = false;
332
333                this.pyramid_last = null;
334                this.pyramid_last_gradx = null;
335                this.pyramid_last_grady = null;
336        }
337
338        float computeSmoothSigma() {
339                return (smooth_sigma_fact * Math.max(window_width, window_height));
340        }
341
342        private class ConvolutionKernel {
343                private static final int MAX_KERNEL_WIDTH = 71;
344
345                int width;
346                float [] data = new float[MAX_KERNEL_WIDTH];
347        }
348
349        ConvolutionKernel gauss_kernel = new ConvolutionKernel();
350        ConvolutionKernel gaussderiv_kernel = new ConvolutionKernel();
351        float sigma_last = -10.0f;
352
353        /*********************************************************************
354         * _computeKernels
355         */
356        void _computeKernels(float sigma, ConvolutionKernel gauss, ConvolutionKernel gaussderiv) {
357                final float factor = 0.01f;   /* for truncating tail */
358                int i;
359
360                /* Compute kernels, and automatically determine widths */
361                {
362                        final int hw = ConvolutionKernel.MAX_KERNEL_WIDTH / 2;
363                        float max_gauss = 1.0f, max_gaussderiv = (float) (sigma*Math.exp(-0.5f));
364
365                        /* Compute gauss and deriv */
366                        for (i = -hw ; i <= hw ; i++)  {
367                                gauss.data[i+hw]      = (float) Math.exp(-i*i / (2*sigma*sigma));
368                                gaussderiv.data[i+hw] = -i * gauss.data[i+hw];
369                        }
370
371                        /* Compute widths */
372                        gauss.width = ConvolutionKernel.MAX_KERNEL_WIDTH;
373                        for (i = -hw ; Math.abs(gauss.data[i+hw] / max_gauss) < factor ; i++) gauss.width -= 2;
374                        gaussderiv.width = ConvolutionKernel.MAX_KERNEL_WIDTH;
375                        for (i = -hw ; Math.abs(gaussderiv.data[i+hw] / max_gaussderiv) < factor ; i++) gaussderiv.width -= 2;
376                        if (gauss.width == ConvolutionKernel.MAX_KERNEL_WIDTH || gaussderiv.width == ConvolutionKernel.MAX_KERNEL_WIDTH)
377                                throw new RuntimeException(
378                                                String.format("(_computeKernels) MAX_KERNEL_WIDTH %d is too small for a sigma of %f", ConvolutionKernel.MAX_KERNEL_WIDTH, sigma)
379                                );
380                }
381
382                /* Shift if width less than MAX_KERNEL_WIDTH */
383                for (i = 0 ; i < gauss.width ; i++)
384                        gauss.data[i] = gauss.data[i+(ConvolutionKernel.MAX_KERNEL_WIDTH-gauss.width)/2];
385                for (i = 0 ; i < gaussderiv.width ; i++)
386                        gaussderiv.data[i] = gaussderiv.data[i+(ConvolutionKernel.MAX_KERNEL_WIDTH-gaussderiv.width)/2];
387                /* Normalize gauss and deriv */
388                {
389                        final int hw = gaussderiv.width / 2;
390                        float den;
391
392                        den = 0.0f;
393                        for (i = 0 ; i < gauss.width ; i++)  den += gauss.data[i];
394                        for (i = 0 ; i < gauss.width ; i++)  gauss.data[i] /= den;
395                        den = 0.0f;
396                        for (i = -hw ; i <= hw ; i++)  den -= i*gaussderiv.data[i+hw];
397                        for (i = -hw ; i <= hw ; i++)  gaussderiv.data[i+hw] /= den;
398                }
399
400                sigma_last = sigma;
401        }
402
403
404        /*********************************************************************
405         * _KLTGetKernelWidths
406         *
407         */
408        int [] _getKernelWidths(float sigma)
409        {
410                _computeKernels(sigma, gauss_kernel, gaussderiv_kernel);
411                int gauss_width = gauss_kernel.width;
412                int gaussderiv_width = gaussderiv_kernel.width;
413
414                return new int[] {gauss_width, gaussderiv_width};
415        }
416
417
418        /*********************************************************************
419         * _convolveImageHoriz
420         */
421        void _convolveImageHoriz(FImage imgin, ConvolutionKernel kernel, FImage imgout)
422        {
423                float sum;
424                int radius = kernel.width / 2;
425                int ncols = imgin.width, nrows = imgin.height;
426                int i, j, k;
427
428                /* Kernel width must be odd */
429                assert(kernel.width % 2 == 1);
430
431                /* Must read from and write to different images */
432                assert(imgin != imgout);
433
434                /* For each row, do ... */
435                for (j = 0 ; j < nrows ; j++)  {
436                        int ptrout = 0;
437                        
438                        /* Zero leftmost columns */
439                        for (i = 0 ; i < radius ; i++)
440                                imgout.pixels[j][ptrout++] = 0.0f; //*ptrout++ = 0.0;
441
442                        /* Convolve middle columns with kernel */
443                        for ( ; i < ncols - radius ; i++)  {
444                                //ppp = ptrrow + i - radius;
445                                int ppp = i - radius;
446                                
447                                sum = 0.0f;
448                                for (k = kernel.width-1 ; k >= 0 ; k--)
449                                        sum += imgin.pixels[j][ppp++] * kernel.data[k];//sum += *ppp++ * kernel.data[k];
450                                imgout.pixels[j][ptrout++] = sum;
451                        }
452
453                        /* Zero rightmost columns */
454                        for ( ; i < ncols ; i++)
455                                imgout.pixels[j][ptrout++] = 0.0f; //*ptrout++ = 0.0;
456                }
457        }
458
459
460        /*********************************************************************
461         * _convolveImageVert
462         */
463        void _convolveImageVert(FImage imgin, ConvolutionKernel kernel, FImage imgout) {
464                float sum;
465                int radius = kernel.width / 2;
466                int ncols = imgin.width, nrows = imgin.height;
467                int i, j, k;
468
469                /* Kernel width must be odd */
470                assert(kernel.width % 2 == 1);
471
472                /* Must read from and write to different images */
473                assert(imgin != imgout);
474
475                /* For each column, do ... */
476                for (i = 0 ; i < ncols ; i++)  {
477                        int ptrout = 0;
478                        /* Zero topmost rows */
479                        for (j = 0 ; j < radius ; j++)  {
480                                imgout.pixels[ptrout][i] = 0;
481                                ptrout++;
482                        }
483
484                        /* Convolve middle rows with kernel */
485                        for ( ; j < nrows - radius ; j++)  {
486                                int ppp = (j - radius);
487                                sum = 0.0f;
488                                for (k = kernel.width-1 ; k >= 0 ; k--)  {
489                                        sum += imgin.pixels[ppp][i] * kernel.data[k];
490                                        ppp++;
491                                }
492                                imgout.pixels[ptrout][i] = sum;
493                                ptrout ++;
494                        }
495
496                        /* Zero bottommost rows */
497                        for ( ; j < nrows ; j++)  {
498                                imgout.pixels[ptrout][i] = 0;
499                                ptrout++;
500                        }
501                }
502        }
503
504
505        /*********************************************************************
506         * _convolveSeparate
507         */
508        void _convolveSeparate(FImage imgin, ConvolutionKernel horiz_kernel, ConvolutionKernel vert_kernel, FImage imgout)
509        {
510                /* Create temporary image */
511                FImage tmpimg = new FImage(imgin.width, imgin.height);
512
513                /* Do convolution */
514                _convolveImageHoriz(imgin, horiz_kernel, tmpimg);
515
516                _convolveImageVert(tmpimg, vert_kernel, imgout);
517        }
518
519        /*********************************************************************
520         * _KLTComputeGradients
521         * @param img 
522         * @param sigma 
523         * @param gradx 
524         * @param grady 
525         */
526        public void computeGradients(FImage img, float sigma, FImage gradx, FImage grady) {
527                /* Compute kernels, if necessary */
528                if (Math.abs(sigma - sigma_last) > 0.05)
529                        _computeKernels(sigma, gauss_kernel, gaussderiv_kernel);
530
531                _convolveSeparate(img, gaussderiv_kernel, gauss_kernel, gradx);
532                _convolveSeparate(img, gauss_kernel, gaussderiv_kernel, grady);
533        }
534
535        /**
536         * @return the minimum distance
537         */
538        public int getMinDist() {
539                return mindist;
540        }
541
542        /**
543         * Set the minimum distance
544         * @param mindist
545         */
546        public void setMinDist(int mindist) {
547                this.mindist = mindist;
548        }
549
550        /**
551         * @return the window width
552         */
553        public int getWindowWidth() {
554                return window_width;
555        }
556
557        /**
558         * Set the window width
559         * @param window_width
560         */
561        public void setWindowWidth(int window_width) {
562                this.window_width = window_width;
563        }
564
565        /**
566         * @return the window height
567         */
568        public int getWindowHeight() {
569                return window_height;
570        }
571
572        /**
573         * Set the window height
574         * @param window_height
575         */
576        public void setWindowHeight(int window_height) {
577                this.window_height = window_height;
578        }
579
580        /**
581         * @return true if in sequential mode; false otherwise.
582         */
583        public boolean sequentialMode() {
584                return sequentialMode;
585        }
586
587        /**
588         * Enable or disable sequential mode
589         * @param sequentialMode
590         */
591        public void setSequentialMode(boolean sequentialMode) {
592                this.sequentialMode = sequentialMode;
593        }
594
595        /**
596         * @return true if internal images are written; false otherwise
597         */
598        public boolean writeInternalImages() {
599                return writeInternalImages;
600        }
601
602        /**
603         * Enable or disable writing of internal images to disk
604         * @param writeInternalImages
605         */
606        public void setWriteInternalImages(boolean writeInternalImages) {
607                this.writeInternalImages = writeInternalImages;
608        }
609
610        /**
611         * @return true if lighting insensitivity is enabled; false otherwise.
612         */
613        public boolean isLightingInsensitive() {
614                return lighting_insensitive;
615        }
616
617        /**
618         * Enable or disable lighting insensitivity
619         * @param lighting_insensitive
620         */
621        public void setLightingInsensitive(boolean lighting_insensitive) {
622                this.lighting_insensitive = lighting_insensitive;
623        }
624
625        /**
626         * @return the minimum eigenvalue
627         */
628        public int getMinEigenvalue() {
629                return min_eigenvalue;
630        }
631
632        /**
633         * Set the minimum eigenvalue
634         * @param min_eigenvalue
635         */
636        public void setMinEigenvalue(int min_eigenvalue) {
637                this.min_eigenvalue = min_eigenvalue;
638        }
639
640        /**
641         * @return the minimum determinant
642         */
643        public float getMinDeterminant() {
644                return min_determinant;
645        }
646
647        /**
648         * Set the minimum determinant
649         * @param min_determinant
650         */
651        public void setMinDeterminant(float min_determinant) {
652                this.min_determinant = min_determinant;
653        }
654
655        /**
656         * @return the minimum displacement
657         */
658        public float getMinDisplacement() {
659                return min_displacement;
660        }
661
662        /**
663         * Set the minimum displacement
664         * @param min_displacement
665         */
666        public void setMinDisplacement(float min_displacement) {
667                this.min_displacement = min_displacement;
668        }
669
670        /**
671         * @return the maximum number of iterations
672         */
673        public int getMaxIterations() {
674                return max_iterations;
675        }
676
677        /**
678         * Set the maximum number of iterations
679         * @param max_iterations
680         */
681        public void setMaxIterations(int max_iterations) {
682                this.max_iterations = max_iterations;
683        }
684
685        /**
686         * @return the maximum residue
687         */
688        public float getMaxResidue() {
689                return max_residue;
690        }
691
692        /**
693         * Set the maximum residue
694         * @param max_residue 
695         */
696        public void setMaxResidue(float max_residue) {
697                this.max_residue = max_residue;
698        }
699
700        /**
701         * @return the step factor
702         */
703        public float getStepFactor() {
704                return step_factor;
705        }
706
707        /**
708         * Set the step factor
709         * @param step_factor
710         */
711        public void setStepFactor(float step_factor) {
712                this.step_factor = step_factor;
713        }
714
715        /**
716         * @return the amount of subsampling
717         */
718        public int getSubsampling() {
719                return subsampling;
720        }
721
722        /**
723         * Set the amount of subsampling
724         * @param subsampling
725         */
726        public void setSubsampling(int subsampling) {
727                this.subsampling = subsampling;
728        }
729
730        /**
731         * @return true if the affine consistency check is enabled; false otherwise.
732         */
733        public int getAffineConsistencyCheck() {
734                return affineConsistencyCheck;
735        }
736
737        /**
738         * Enable or disable the affine consistency check
739         * @param affineConsistencyCheck
740         */
741        public void setAffineConsistencyCheck(int affineConsistencyCheck) {
742                this.affineConsistencyCheck = affineConsistencyCheck;
743        }
744
745        /**
746         * Set the target
747         * @param targetArea
748         */
749        public void setTargetArea(Shape targetArea) {
750                this.targetArea = targetArea;
751        }
752
753        /**
754         * @return the target area
755         */
756        public Shape getTargetArea() {
757                return targetArea;
758        }
759
760        /**
761         * @param pyr set the previous pyramids
762         */
763        public void setPreviousPyramid(PyramidSet pyr) {
764                this.pyramid_last = pyr.imgPyr;
765                this.pyramid_last_gradx = pyr.gradx;
766                this.pyramid_last_grady = pyr.grady;
767        }
768
769        /**
770         * @return the previous pyramid
771         */
772        public PyramidSet getPreviousPyramid() {
773                PyramidSet ret = new PyramidSet();
774                ret.imgPyr = this.pyramid_last;
775                ret.gradx = this.pyramid_last_gradx;
776                ret.grady = this.pyramid_last_grady;
777                return ret;
778        }
779}