contrib/gel/gevd/gevd_float_operators.cxx
Go to the documentation of this file.
00001 // This is gel/gevd/gevd_float_operators.cxx
00002 #include "gevd_float_operators.h"
00003 //:
00004 // \file
00005 
00006 #include <vcl_iostream.h>
00007 #include <vcl_fstream.h>
00008 #include <vcl_algorithm.h>
00009 #include <vcl_cassert.h>
00010 #include <vnl/vnl_math.h> // for pi_over_2
00011 #include <vnl/vnl_float_3.h>
00012 #include <vnl/vnl_cross.h>
00013 
00014 #include <vcl_cmath.h>
00015 #include <vcl_cstring.h>
00016 
00017 #include "gevd_pixel.h"
00018 #include "gevd_xpixel.h"
00019 #include "gevd_bufferxy.h"
00020 #ifdef DEBUG
00021 # include <vul/vul_timer.h>
00022 #endif
00023 
00024 #if defined(VCL_VC) || defined(VCL_BORLAND)
00025 inline static double rint(double v)
00026 {
00027   return  v - vcl_floor(v) < 0.5  ?  vcl_floor(v)  :  vcl_ceil(v);
00028 }
00029 #endif
00030 
00031 const unsigned char DIR0 = 8, DIR1 = 9, DIR2 = 10, DIR3 = 11, DIR4 = 12;
00032 // const int DIS[] = { 1, 1, 0,-1,-1,-1, 0, 1, // 8-connected neighbors
00033 //                     1, 1, 0,-1,-1,-1, 0, 1, // wrapped by 2PI
00034 //                     1, 1, 0,-1,-1,-1, 0, 1};
00035 // const int DJS[] = { 0, 1, 1, 1, 0,-1,-1,-1,
00036 //                     0, 1, 1, 1, 0,-1,-1,-1,
00037 //                     0, 1, 1, 1, 0,-1,-1,-1};
00038 
00039 //: Convolves \a from image with 2D filter and stores values in \a to image.
00040 // O(m*n*k).
00041 // The filter \a kernel has odd width and height,
00042 // and is either even or odd along x- or y-axis.
00043 // Assume image data is in row-major order.
00044 //
00045 // Special care is taken when \a to and \a from are identical - Peter Vanroose.
00046 
00047 float
00048 gevd_float_operators::Convolve(const gevd_bufferxy& from,
00049                                const gevd_bufferxy& kernel,
00050                                gevd_bufferxy*& to)
00051 {
00052 #ifdef DEBUG
00053   vul_timer t;
00054   vcl_cout << "Convolve image";
00055 #endif
00056   bool overwrite = to == &from;
00057   gevd_bufferxy*& t = to;
00058   if (overwrite) to=0;
00059   to = gevd_float_operators::Allocate(to, from);
00060   const int wx = kernel.GetSizeX(), wy = kernel.GetSizeY();
00061   const int rx = wx/2, ry = wy/2;
00062   const int xhi = from.GetSizeX() - wx + 1;
00063   const int yhi = from.GetSizeY() - wy + 1;
00064   for (int y = 0; y < yhi; y++)
00065     for (int x = 0; x < xhi; x++) {
00066       float conv = 0;           // more efficient if use even/odd symmetry
00067       for (int j = 0; j < wy; j++)
00068         for (int i = 0; i < wx; i++)
00069           conv += floatPixel(from, x+i, y+j) * floatPixel(kernel, i, j);
00070       floatPixel(*to, x+rx, y+ry) = conv;
00071     }
00072   FillFrameX(*to, 0, rx);       // pad border with 0
00073   FillFrameY(*to, 0, ry);
00074 #ifdef DEBUG
00075   vcl_cout << " in " << t.real() << " msecs.\n";
00076 #endif
00077   if (overwrite) { gevd_float_operators::Update(*t,*to); delete to; to = t; }
00078   return 1;                     // extra scaling factor
00079 }
00080 
00081 //: Correlate \a from image with 2D filter and stores values in \a to image.
00082 // O(m*n*k).
00083 // The filter \a kernel has odd width and height,
00084 // and is either even or odd along x- or y-axis.
00085 // Assume image data is in row-major order.
00086 //
00087 // Special care is taken when \a to and \a from are identical - Peter Vanroose.
00088 
00089 float
00090 gevd_float_operators::Correlation(const gevd_bufferxy& from,
00091                                   const gevd_bufferxy& kernel,
00092                                   gevd_bufferxy*& to)
00093 {
00094 #ifdef DEBUG
00095   vul_timer t;
00096   vcl_cout << "Correlate image";
00097 #endif
00098   bool overwrite = to == &from;
00099   gevd_bufferxy*& t = to;
00100   if (overwrite) to=0;
00101   to = gevd_float_operators::Allocate(to, from);
00102   const int wx = kernel.GetSizeX(), wy = kernel.GetSizeY();
00103   const int rx = wx/2, ry = wy/2;
00104   const int xhi = from.GetSizeX() - wx + 1;
00105   const int yhi = from.GetSizeY() - wy + 1;
00106   const int sum1 = wx*wy;
00107   for (int y = 0; y < yhi; y++)
00108     for (int x = 0; x < xhi; x++) {
00109       register double sumx=0, sumy=0, sumxx=0, sumyy=0, sumxy=0, xval, yval;
00110       for (int j = 0; j < wy; j++)
00111         for (int i = 0; i < wx; i++) {
00112           xval = floatPixel(from, x+i, y+j);
00113           yval = floatPixel(kernel, i, j);
00114         sumxy += xval * yval;           // accumulate correlation value
00115         sumx += xval;
00116         sumy += yval;
00117         sumxx += xval * xval;
00118         sumyy += yval * yval;
00119       }
00120       double varx = sum1 * sumxx - sumx * sumx; // all multiplied with sum1
00121       double vary = sum1 * sumyy - sumy * sumy;
00122       double cvar = sum1 * sumxy - sumx * sumy; // linear correlation coeft
00123       if (varx!=0 && vary!=0) cvar /= vcl_sqrt(varx * vary);
00124       floatPixel(*to, x+rx, y+ry) = (float)cvar;
00125     }
00126   FillFrameX(*to, 0, rx);       // pad border with 0
00127   FillFrameY(*to, 0, ry);
00128 #ifdef DEBUG
00129   vcl_cout << " in " << t.real() << " msecs.\n";
00130 #endif
00131   if (overwrite) { gevd_float_operators::Update(*t,*to); delete to; to = t; }
00132   return 1;                     // extra scaling factor
00133 }
00134 
00135 
00136 //: Correlate \a from image with 2D filter and stores values in \a to image.
00137 //
00138 // Special care is taken when \a to and \a from are identical - Peter Vanroose.
00139 float
00140 gevd_float_operators::CorrelationAlongAxis(const gevd_bufferxy& from,
00141                                            const gevd_bufferxy& kernel,
00142                                            gevd_bufferxy*& to)
00143 {
00144 #ifdef DEBUG
00145   vul_timer t;
00146   vcl_cout << "Correlate image";
00147 #endif
00148   bool overwrite = to == &from;
00149   gevd_bufferxy*& t = to;
00150   if (overwrite) to=0;
00151   to = gevd_float_operators::Allocate(to, from);
00152   to->Clear();
00153   const int wx = kernel.GetSizeX(), wy = kernel.GetSizeY();
00154   const int rx = wx/2, ry = wy/2;
00155   const int xhi = from.GetSizeX() - wx + 1;
00156   const int yhi = from.GetSizeY() - wy + 1;
00157   const int sum1 = wx*wy;
00158   for (int x = 0, y = yhi/2; x < xhi; ++x) {
00159     register double sumx=0, sumy=0, sumxx=0, sumyy=0, sumxy=0, xval, yval;
00160     for (int j = 0; j < wy; j++)
00161       for (int i = 0; i < wx; i++) {
00162         xval = floatPixel(from, x+i, y+j);
00163         yval = floatPixel(kernel, i, j);
00164         sumxy += xval * yval;           // accumulate correlation value
00165         sumx += xval;
00166         sumy += yval;
00167         sumxx += xval * xval;
00168         sumyy += yval * yval;
00169       }
00170     double varx = sum1 * sumxx - sumx * sumx; // all multiplied with sum1
00171     double vary = sum1 * sumyy - sumy * sumy;
00172     double cvar = sum1 * sumxy - sumx * sumy;   // linear correlation coeft
00173     if (varx!=0 && vary!=0) cvar /= vcl_sqrt(varx * vary);
00174     floatPixel(*to, x+rx, y+ry) = (float)cvar;
00175   }
00176   for (int x = xhi/2, y = 0; y < yhi; ++y) {
00177     register double sumx=0, sumy=0, sumxx=0, sumyy=0, sumxy=0, xval, yval;
00178     for (int j = 0; j < wy; j++)
00179       for (int i = 0; i < wx; i++) {
00180         xval = floatPixel(from, x+i, y+j);
00181         yval = floatPixel(kernel, i, j);
00182         sumxy += xval * yval;           // accumulate correlation value
00183         sumx += xval;
00184         sumy += yval;
00185         sumxx += xval * xval;
00186         sumyy += yval * yval;
00187       }
00188     double varx = sum1 * sumxx - sumx * sumx; // all multiplied with sum1
00189     double vary = sum1 * sumyy - sumy * sumy;
00190     double cvar = sum1 * sumxy - sumx * sumy;   // linear correlation coeft
00191     if (varx!=0 && vary!=0) cvar /= vcl_sqrt(varx * vary);
00192     floatPixel(*to, x+rx, y+ry) = (float)cvar;
00193   }
00194 #ifdef DEBUG
00195   vcl_cout << " in " << t.real() << " msecs.\n";
00196 #endif
00197   if (overwrite) { gevd_float_operators::Update(*t,*to); delete to; to = t; }
00198   return 1;                     // extra scaling factor
00199 }
00200 
00201 
00202 //: Read 2D kernel from a file: width height k_x_y .....
00203 
00204 gevd_bufferxy*
00205 gevd_float_operators::Read2dKernel(const char* filename)
00206 {
00207   vcl_ifstream infile(filename, vcl_ios_in); // open the file
00208   if (!infile)
00209     return NULL;
00210   int width, height;
00211   infile >> width;
00212   infile >> height;
00213   if (width < 1 || height < 1) return NULL;
00214   gevd_bufferxy* kernel = new gevd_bufferxy(width, height, bits_per_float);
00215   for (int y = 0; y < height; y++)
00216     for (int x = 0; x < width; x++)
00217       infile >> floatPixel(*kernel, x, y);
00218   return kernel;
00219 }
00220 
00221 //: Convolves \a from image with two separable filters, along directions x and y, and stores values in \a to image.
00222 // O(m*n*k).
00223 // The filter \a kernel has length 2*radius + 1, and is either even or odd.
00224 // Assume image data is in row-major order.
00225 //
00226 // Special care is taken when \a to and \a from are identical - Peter Vanroose.
00227 
00228 float
00229 gevd_float_operators::Convolve(const gevd_bufferxy& from, gevd_bufferxy*& to,
00230                                const float* xkernel, const int xradius,
00231                                const bool xevenp,
00232                                const float* ykernel, const int yradius,
00233                                const bool yevenp,
00234                                const bool xwrap, const bool ywrap)
00235 {
00236 #ifdef DEBUG
00237   vul_timer t;
00238   vcl_cout << "Convolve image";
00239 #endif
00240   bool overwrite = to == &from;
00241   gevd_bufferxy*& t = to;
00242   if (overwrite) to=0;
00243   to = gevd_float_operators::Allocate(to, from);
00244   const int sizeX = to->GetSizeX(), sizeY = to->GetSizeY();
00245   const int ylo = yradius, yhi = sizeY - yradius;
00246   const int kborder = 2*yradius;
00247 
00248   // 1. Setup the pipeline of 4*yradius+1 lines, convolved along x-axis
00249   float** cache = new float*[4*yradius+1];
00250   float** pipeline = cache+yradius;
00251   float* row;
00252   for (int p = 0; p <= kborder; ++p) {
00253     pipeline[p] = row = new float[sizeX];
00254     gevd_float_operators::Convolve(&floatPixel(from, 0, p), // row-major order
00255                                    row, sizeX,
00256                                    xkernel, xradius, xevenp, xwrap);
00257   }
00258   if (ywrap)                    // circular wrap at 2 end rows
00259     for (int r = 1; r <= yradius; r++) {
00260       pipeline[-r] = row = new float[sizeX];
00261       gevd_float_operators::Convolve(&floatPixel(from, 0, sizeY-r), // row-major order
00262                                      row, sizeX,
00263                                      xkernel, xradius, xevenp, xwrap);
00264       pipeline[kborder+r] = row = new float[sizeX];
00265       for (int i = 0; i < sizeX; i++) // copy previous convolved result
00266         row[i] = pipeline[r-1][i];
00267     }
00268   else                          // reflection at 2 end rows
00269     for (int r = 1; r <= yradius; r++) {
00270       pipeline[-r] = row = new float[sizeX];
00271       for (int i = 0; i < sizeX; i++) // copy previous convolved result
00272         row[i] = pipeline[r][i];
00273       pipeline[kborder+r] = row = new float[sizeX];
00274       gevd_float_operators::Convolve(&floatPixel(from, 0, sizeY-1-r), // row-major order
00275                                      row, sizeX,
00276                                      xkernel, xradius, xevenp, xwrap);
00277     }
00278 
00279   // 2. Convolve along y-axis, shifting pipeline by 1 each time.
00280   if (yevenp)
00281   {
00282     int y=0;
00283     for (int yy = 0; y < ylo; ++y, ++yy) { // reflect/wrap at ymin
00284       row = &floatPixel(*to, 0, y); // row-major order
00285       for (int x = 0; x < sizeX; x++) {
00286         float sum = ykernel[yradius] * pipeline[yy][x];
00287         for (int k = 1; k <= yradius; k++)
00288           sum += ykernel[yradius-k] * (pipeline[yy-k][x] + pipeline[yy+k][x]);
00289         row[x] = sum;
00290       }
00291     }
00292     int p = kborder+1;
00293     for ( ; y < yhi; ++y) {     // convolution along y-axis
00294       row = &floatPixel(*to, 0, y);
00295       for (int x = 0; x < sizeX; x++) {
00296         float sum = ykernel[yradius] * pipeline[yradius][x];
00297         for (int k = 1; k <= yradius; k++)
00298           sum += ykernel[yradius-k] * (pipeline[yradius-k][x] + // even kernel
00299                                        pipeline[yradius+k][x]);
00300         row[x] = sum;
00301       }
00302       if (p < sizeY) {
00303         row = pipeline[0];      // next line
00304         for (int k = 0; k < kborder; k++) // shift the lines of
00305           pipeline[k] = pipeline[k+1]; // the pipeline by 1
00306         gevd_float_operators::Convolve(&floatPixel(from, 0, p++), // row-major order
00307                                        row, sizeX,
00308                                        xkernel, xradius, xevenp, xwrap); // new line x-conv
00309         pipeline[kborder] = row; // update pipeline
00310       }
00311     }
00312     for (int yy = yradius+1; y < sizeY; y++, yy++) {  // reflect/wrap at ymax
00313       row = &floatPixel(*to, 0, y); // row-major order
00314       for (int x = 0; x < sizeX; x++) {
00315         float sum = ykernel[yradius] * pipeline[yy][x];
00316         for (int k = 1; k <= yradius; k++)  // convolution along y-axis
00317           sum += ykernel[yradius-k] * (pipeline[yy-k][x] + pipeline[yy+k][x]);
00318         row[x] = sum;
00319       }
00320     }
00321   }
00322   else
00323   {
00324     int y=0;
00325     for (int yy = 0; y < ylo; y++, yy++) { // reflect at ymin
00326       row = &floatPixel(*to, 0, y); // row-major order
00327       for (int x = 0; x < sizeX; x++) {
00328         float sum = ykernel[yradius] * pipeline[yy][x];
00329         for (int k = 1; k <= yradius; k++)
00330           sum += ykernel[yradius-k] * (pipeline[yy-k][x] - pipeline[yy+k][x]);
00331         row[x] = sum;
00332       }
00333     }
00334     int p = kborder+1;
00335     for ( ; y < yhi; y++) {
00336       row = &floatPixel(*to, 0, y);
00337       for (int x = 0; x < sizeX; x++) {
00338         float sum = ykernel[yradius] * pipeline[yradius][x];
00339         for (int k = 1; k <= yradius; k++)  // convolution along y-axis
00340           sum += ykernel[yradius-k] * (pipeline[yradius-k][x] - // odd kernel
00341                                        pipeline[yradius+k][x]);
00342         row[x] = sum;
00343       }
00344       if (p < sizeY) {
00345         row = pipeline[0];      // next line
00346         for (int k = 0; k < kborder; k++)   // shift the lines of
00347           pipeline[k] = pipeline[k+1];  // the pipeline by 1
00348         gevd_float_operators::Convolve(&floatPixel(from, 0, p++), // row-major order
00349                                        row, sizeX,
00350                                        xkernel, xradius, xevenp, xwrap); // new line x-conv
00351         pipeline[kborder] = row; // update pipeline
00352       }
00353     }
00354     for (int yy = yradius+1; y < sizeY; y++, yy++) { // reflect/wrap at ymax
00355       row = &floatPixel(*to, 0, y); // row-major order
00356       for (int x = 0; x < sizeX; x++) {
00357         float sum = ykernel[yradius] * pipeline[yy][x];
00358         for (int k = 1; k <= yradius; k++)  // convolution along y-axis
00359           sum += ykernel[yradius-k] * (pipeline[yy-k][x] - pipeline[yy+k][x]);
00360         row[x] = sum;
00361       }
00362     }
00363   }
00364   for (int p = 0; p <= 4*yradius; p++)
00365     delete[] cache[p];         // Free lines in pipeline cache
00366   delete[] cache;
00367 #ifdef DEBUG
00368   vcl_cout << " in " << t.real() << " msecs.\n";
00369 #endif
00370   if (overwrite) { gevd_float_operators::Update(*t,*to); delete to; to = t; }
00371   return 1;                     // assume normalized kernel
00372 }
00373 
00374 
00375 //: Convolves \a from image with a filter along x, and a running sum along y-axis, then stores values in \a to image.
00376 // O(m*n*k).
00377 
00378 float
00379 gevd_float_operators::Convolve(const gevd_bufferxy& from, gevd_bufferxy*& to,
00380                                const float* xkernel, const int xradius,
00381                                const bool xevenp,
00382                                const int yradius,
00383                                const bool xwrap, const bool ywrap)
00384 {
00385   float* ykernel = new float[2*yradius+1];
00386   const bool yevenp = true;
00387   for (int i = 0; i < 2*yradius+1; i++)
00388     ykernel[i] = 1;
00389   float fact = gevd_float_operators::Convolve(from, to,
00390                                               xkernel, xradius, xevenp,
00391                                               ykernel, yradius, yevenp,
00392                                               xwrap, ywrap);
00393   delete[] ykernel;
00394   return fact;
00395 }
00396 
00397 #if 0 // commented out
00398 {
00399 #ifdef DEBUG
00400   vul_timer t;
00401   vcl_cout << "Convolve image";
00402 #endif
00403   to = gevd_float_operators::Allocate(to, from);
00404   const int sizeX = to->GetSizeX(), sizeY = to->GetSizeY();
00405   const int ylo = yradius, yhi = sizeY - yradius;
00406   const int kborder = 2*yradius;
00407 
00408   // 1. Setup the pipeline of 4*yradius+1 lines, convolved along x-axis
00409   float** cache = new float*[4*yradius+1];
00410   float** pipeline = cache+yradius;
00411   double* rsum = new double[sizeX]; // avoid addition/subtraction errors
00412   float* row = NULL;            // current row
00413   for (int p = 0; p <= kborder; p++) {
00414     pipeline[p] = row = new float[sizeX];
00415     gevd_float_operators::Convolve(&floatPixel(from, 0, p), // row-major order
00416                                    row, sizeX,
00417                                    xkernel, xradius, xevenp, xwrap);
00418   }
00419   // **** wrapping of the from buffer at the end ****
00420   if (ywrap)                    // circular wrap at 2 end rows
00421     for (int r = 1; r <= yradius; r++) {
00422       pipeline[-r] = row = new float[sizeX];
00423       gevd_float_operators::Convolve(&floatPixel(from, 0, sizeY-r), // row-major order
00424                                      row, sizeX,
00425                                      xkernel, xradius, xevenp, xwrap);
00426       pipeline[kborder+r] = row = new float[sizeX];
00427       for (int i = 0; i < sizeX; i++) // copy previous convolved result
00428         row[i] = pipeline[r-1][i];
00429     }
00430   else                          // reflection at 2 end rows
00431     for (int r = 1; r <= yradius; r++) {
00432       pipeline[-r] = row = new float[sizeX];
00433       for (int i = 0; i < sizeX; i++) // copy previous convolved result
00434         row[i] = pipeline[r][i];
00435       pipeline[kborder+r] = row = new float[sizeX];
00436       gevd_float_operators::Convolve(&floatPixel(from, 0, sizeY-1-r), // row-major order
00437                                      row, sizeX,
00438                                      xkernel, xradius, xevenp, xwrap);
00439     }
00440 
00441   // 2. Running sum along y-axis, shifting pipeline by 1 each time.
00442   int y = 0, yy = 0;
00443   row = &floatPixel(*to, 0, y); // row-major order
00444   for (int x = 0; x < sizeX; x++) { // running sum at first row
00445     double sum = pipeline[yy][x];
00446     for (int k = 1; k <= yradius; k++)
00447       sum += (pipeline[yy-k][x] + pipeline[yy+k][x]);
00448     row[x] = float(rsum[x] = sum);
00449   }
00450   for (y++, yy++; y < ylo; y++, yy++) { // reflect/wrap at ymin
00451     row = &floatPixel(*to, 0, y); // row-major order
00452     for (int x = 0; x < sizeX; x++)
00453       row[x] = float(rsum[x] = rsum[x] -
00454                      pipeline[yy-yradius-1][x] + pipeline[yy+yradius][x]);
00455   }
00456   for ( ; y < yhi; y++) {       // convolution along y-axis
00457     row = &floatPixel(*to, 0, y);
00458     for (int x = 0; x < sizeX; x++)
00459       row[x] = float(rsum[x] = rsum[x] -
00460                      pipeline[-1][x] + pipeline[kborder][x]);
00461     if (p < sizeY) {
00462       row = pipeline[0];        // next line
00463       for (int k = 0; k < kborder; k++) // shift the lines of
00464         pipeline[k] = pipeline[k+1]; // the pipeline by 1
00465       gevd_float_operators::Convolve(&floatPixel(from, 0, p++), // row-major order
00466                                      row, sizeX,
00467                                      xkernel, xradius, xevenp, xwrap); // new x-convolution
00468       pipeline[kborder] = row; // update pipeline
00469     }
00470   }
00471   for (yy = yradius+1; y < sizeY; y++, yy++) {  // reflect/wrap at ymax
00472     row = &floatPixel(*to, 0, y); // row-major order
00473     for (int x = 0; x < sizeX; x++)
00474       row[x] = float(rsum[x] = rsum[x] -
00475                      pipeline[yy-yradius-1][x] + pipeline[yy+yradius][x]);
00476   }
00477   for (int p = 0; p <= 4*yradius; p++)
00478     delete[] cache[p];         // Free lines in pipeline cache
00479   delete[] cache;
00480   delete[] rsum;
00481 #ifdef DEBUG
00482   vcl_cout << " in " << t.real() << " msecs.\n";
00483 #endif
00484   return 2*yradius+1;           // return multiplication factor
00485 }
00486 #endif
00487 
00488 //: Convolve a linear array of data, wrapping at the 2 ends.
00489 //
00490 // Special care is taken when \a to and \a from are identical - Peter Vanroose.
00491 
00492 float
00493 gevd_float_operators::Convolve(const float* from, float*& to, const int len,
00494                                const float* kernel, const int kradius,
00495                                const bool evenp, const bool wrap)
00496 {
00497   bool overwrite = to == from;
00498   float*& t = to;
00499   if (!to || overwrite) to = new float[len];
00500   const int xlo = kradius, xhi = len - kradius;
00501   const int kborder = 2*kradius;
00502   float *cache, *pipeline;
00503   int p = gevd_float_operators::SetupPipeline(from, len, kradius, wrap,
00504                                               cache, pipeline);
00505 
00506   // Convolution along x with above pipeline
00507   float sum;
00508   if (evenp)
00509   {
00510     int x=0;
00511     for (int xx = 0; x < xlo; ++x, ++xx) {
00512       sum = kernel[kradius] * pipeline[xx];
00513       for (int k = 1; k <= kradius; k++)
00514         sum += kernel[kradius-k] * (pipeline[xx-k] + pipeline[xx+k]);
00515       to[x] = sum;
00516     }
00517     for ( ; x < xhi; x++) {
00518       sum = kernel[kradius] * pipeline[kradius];
00519       for (int k = 1; k <= kradius; k++)
00520         sum += kernel[kradius-k] * (pipeline[kradius-k] + // plus for
00521                                     pipeline[kradius+k]); // symmetric
00522       to[x] = sum;
00523       if (p < len) {
00524         for (int k = 0; k < kborder; k++)   // shift the floats of
00525           pipeline[k] = pipeline[k+1];  // the pipeline by 1
00526         pipeline[kborder] = from[p++];  // update pipeline
00527       }
00528     }
00529     for (int xx = kradius+1; x < len; x++, xx++) {
00530       sum = kernel[kradius] * pipeline[xx];
00531       for (int k = 1; k <= kradius; k++)
00532         sum += kernel[kradius-k] * (pipeline[xx-k] + pipeline[xx+k]);
00533       to[x] = sum;
00534     }
00535   }
00536   else
00537   {
00538     int x=0;
00539     for (int xx = 0; x < xlo; ++x, ++xx) {
00540       sum = kernel[kradius] * pipeline[xx];
00541       for (int k = 1; k <= kradius; k++)
00542         sum += kernel[kradius-k] * (pipeline[xx-k] - pipeline[xx+k]);
00543       to[x] = sum;
00544     }
00545     for ( ; x < xhi; x++) {
00546       sum = kernel[kradius] * from[x];
00547       for (int k = 1; k <= kradius; k++)
00548         sum += kernel[kradius-k] * (pipeline[kradius-k] - // minus for
00549                                     pipeline[kradius+k]); // antisymmetric
00550       to[x] = sum;
00551       if (p < len) {
00552         for (int k = 0; k < kborder; k++)   // shift the floats of
00553           pipeline[k] = pipeline[k+1];  // the pipeline by 1
00554         pipeline[kborder] = from[p++];  // update pipeline
00555       }
00556     }
00557     for (int xx = kradius+1; x < len; x++, xx++) {
00558       sum = kernel[kradius] * pipeline[xx];
00559       for (int k = 1; k <= kradius; k++)
00560         sum += kernel[kradius-k] * (pipeline[xx-k] - pipeline[xx+k]);
00561       to[x] = sum;
00562     }
00563   }
00564   delete[] cache;
00565   if (overwrite) {
00566     for (int x=0 ; x < len; ++x) t[x] = to[x];
00567     delete[] to;
00568     to = t;
00569   }
00570   return 1;                     // assume normalized kernel
00571 }
00572 
00573 //: For large smoothing sigma, use running sum to avoid floating multiplications.
00574 //
00575 // Special care is taken when \a to and \a from are identical - Peter Vanroose.
00576 float
00577 gevd_float_operators::RunningSum(float* from, float*& to, const int len,
00578                                  const int kradius, const bool wrap)
00579 {
00580   bool overwrite = to == from;
00581   if (!to || overwrite) to = new float[len];
00582   const int xlo = kradius, xhi = len - kradius;
00583   const int kborder = 2*kradius;
00584   float *cache, *pipeline;
00585   int p = gevd_float_operators::SetupPipeline(from, len, kradius, wrap,
00586                                               cache, pipeline);
00587   // Running sum along x with above pipeline
00588   int x = 0, xx = 0;
00589   float sum = pipeline[x];      // pre-compute running sum
00590   for (int k = 1; k <= kradius; k++)
00591     sum += (pipeline[xx-k] + pipeline[xx+k]);
00592   to[x] = sum;
00593   for (x++, xx++; x < xlo; x++, xx++) {
00594     sum += pipeline[xx+kradius] - pipeline[xx-kradius-1];
00595     to[x] = sum;
00596   }
00597   for ( ; x < xhi; x++) {
00598     sum += pipeline[kborder] - pipeline[-1];
00599     to[x] = sum;
00600     if (p < len) {
00601       for (int k = -1; k < kborder; k++)    // shift the floats of
00602         pipeline[k] = pipeline[k+1];    // the pipeline by 1
00603       pipeline[kborder] = from[p++];    // update pipeline
00604     }
00605   }
00606   for ( ; x < len; x++, xx++) {
00607     sum += pipeline[xx+kradius] - pipeline[xx-kradius-1];
00608     to[x] = sum;
00609   }
00610   delete[] cache;
00611   if (overwrite) {
00612     for (int x=0 ; x < len; ++x) from[x] = to[x];
00613     delete[] to;
00614     to = from;
00615   }
00616   return float(2*kradius+1);            // magnification factor
00617 }
00618 
00619 //: Read 1d odd/even kernel from a file: width k_i .....
00620 
00621 bool
00622 gevd_float_operators::Read1dKernel(const char* filename,
00623                                    float*& kernel, int& radius, bool& evenp)
00624 {
00625   vcl_ifstream infile(filename, vcl_ios_in); // open the file
00626   if (!infile)
00627     return false;
00628   int width;
00629   infile >> width;
00630   if (width < 1) return false;
00631   radius = (width - 1)/2;
00632   width = 2*radius + 1;
00633   delete[] kernel;
00634   kernel = new float[width];
00635   for (int i = 0; i < width; i++)
00636     infile >> kernel[i];
00637   evenp = true;
00638   for (int i = 1; i <= radius; i++)
00639     if (kernel[radius-i] != kernel[radius+i])
00640       evenp = false;
00641   if (evenp)                    // double check that kernel is even
00642     return true;
00643   for (int i = 1; i <= radius; i++)
00644     if (kernel[radius-i] != -kernel[radius+i])
00645       evenp = true;
00646   if (!evenp)                   // double check that kernel is odd
00647     return true;
00648   delete[] kernel; kernel = NULL;
00649   return false;                 // invalid kernel
00650 }
00651 
00652 
00653 //: Convolves the \a from image with a 2D Gaussian filter with standard deviation \a sigma.
00654 // O(m*n*k).
00655 // The 2D convolution is decomposed into 2 1D convolutions:
00656 // Gxy * I = Gy * (Gx * I).
00657 // The frame with width equal to the radius of the Gaussian kernel is
00658 // filled with zero.
00659 
00660 float
00661 gevd_float_operators::Gaussian(gevd_bufferxy& from, gevd_bufferxy*& to, const float sigma,
00662                                const bool xwrap, const bool ywrap)
00663 {
00664   float* kernel = NULL;
00665   int radius = 0;
00666   if (!gevd_float_operators::Find1dGaussianKernel(sigma, kernel, radius)) {
00667     to = gevd_float_operators::Allocate(to, from);
00668     if (to != &from)
00669       gevd_float_operators::Update(*to, from); // just a copy, no smoothing needed
00670   }
00671   else {
00672     bool evenp = true;
00673     // 2 1D convolutions O(k), instead of a 2D convolution O(k^2).
00674     gevd_float_operators::Convolve(from, to,
00675                                    kernel, radius, evenp,
00676                                    kernel, radius, evenp,
00677                                    xwrap, ywrap);
00678   }
00679   delete[] kernel;
00680   return 1;                     // return multiplication factor
00681 }
00682 
00683 
00684 //:
00685 // Returns the kernel array for Gaussian with given standard deviation
00686 // \a sigma [pixel], and cutoff at min/max = \a fuzz.
00687 // The area under the discrete Gaussian is normalized to 1.
00688 
00689 bool
00690 gevd_float_operators::Find1dGaussianKernel(const float sigma,
00691                                            float*& kernel, int& radius,
00692                                            const float fuzz)
00693 {
00694   for (radius = 0; Gaussian(float(radius), sigma) > fuzz; radius++)
00695     {;}                                         // find radius
00696   if (radius == 0)
00697     return false;
00698 
00699   kernel = new float[2*radius + 1];             // create kernel
00700   for (int i=0; i<=radius; ++i)
00701     kernel[radius+i] = kernel[radius-i] = Gaussian(float(i), sigma);
00702   float sum = 0;
00703   for (int i= 0; i <= 2*radius; ++i)
00704     sum += kernel[i];                           // find integral of weights
00705   for (int i= 0; i <= 2*radius; ++i)
00706     kernel[i] /= sum;                           // normalize by integral
00707 #ifdef DEBUG
00708   vcl_cout << "Gaussian kernel = ";
00709   for (int i= 0; i <= 2*radius; ++i)
00710     vcl_cout << kernel[i] << ' ';
00711   vcl_cout << vcl_endl;
00712 #endif
00713   return true;
00714 }
00715 
00716 
00717 float
00718 gevd_float_operators::Gaussian(const float x, const float sigma)
00719 {
00720   double x_on_sigma = x / sigma;
00721   return (float)vcl_exp(- x_on_sigma * x_on_sigma / 2);
00722 }
00723 
00724 
00725 //:
00726 // Setup the cache for reflection/wrapping at the borders,
00727 // and the center pipeline. Only cache should be deleted after
00728 // you're done, not the pipeline, since pipeline shares the same space.
00729 
00730 int
00731 gevd_float_operators::SetupPipeline(const float* data, const int len,
00732                                     const int kradius, const bool wrap,
00733                                     float*& cache, float*& pipeline)
00734 {
00735   cache = new float[4*kradius+1]; // Setup the cache of 4*kradius+1 floats
00736   pipeline = cache+kradius;     // center pipeline is 2*kradius+1 floats
00737   int p = 0;
00738   for (; p <= 2*kradius; p++) pipeline[p] = data[p];
00739   if (wrap)                     // circular wrap at 2 end points
00740     for (int r = 1; r <= kradius; r++) {
00741       pipeline[-r] = data[len-r];
00742       pipeline[2*kradius+r] = data[r-1];
00743     }
00744   else                          // reflection at 2 end points
00745     for (int r = 1; r <= kradius; r++) {
00746       pipeline[-r] = data[r];
00747       pipeline[2*kradius+r] = data[len-1-r];
00748     }
00749   return p;                     // current index in pipeline
00750 }
00751 
00752 //: Create a buffer that wraps rows/columns.
00753 
00754 gevd_bufferxy*
00755 gevd_float_operators::WrapAlongX(const gevd_bufferxy& img)
00756 {
00757   const int lx = img.GetSizeX()-1, sy = img.GetSizeY();
00758   gevd_bufferxy* wrap = gevd_float_operators::SimilarBuffer(img, bits_per_float,  4, sy);
00759   for (int i = 0; i < 2; i++)
00760     for (int j = 0; j < sy; j++) {
00761       floatPixel(*wrap, 2+i, j) = floatPixel(img, i, j);
00762       floatPixel(*wrap, 1-i, j) = floatPixel(img, lx-i, j);
00763     }
00764   return wrap;
00765 }
00766 
00767 
00768 gevd_bufferxy*
00769 gevd_float_operators::WrapAlongY(const gevd_bufferxy& img)
00770 {
00771   const int sx = img.GetSizeX(), ly = img.GetSizeY()-1;
00772   gevd_bufferxy* wrap = gevd_float_operators::SimilarBuffer(img, bits_per_float, sx, 4);
00773   for (int j = 0; j < 2; j++)
00774     for (int i = 0; i < sx; i++) {
00775       floatPixel(*wrap, i, 2+j) = floatPixel(img, i, j);
00776       floatPixel(*wrap, i, 1-j) = floatPixel(img, i, ly-j);
00777     }
00778   return wrap;
00779 }
00780 
00781 // Local gradient magnitude and direction in 3x3 window
00782 
00783 inline void
00784 LocalGradient(const gevd_bufferxy& smooth, const int i, const int j,
00785               float& mag, float& gx, float& gy)
00786 {
00787   gx = floatPixel(smooth, i+1, j) - floatPixel(smooth, i-1, j);
00788   gy = floatPixel(smooth, i, j+1) - floatPixel(smooth, i, j-1);
00789   mag = vcl_sqrt(gx*gx + gy*gy);
00790 }
00791 
00792 //: Compute the gradient of the intensity surface.
00793 // O(m*n).
00794 // The intensity surface is assumed smoothed by a Gaussian filter,
00795 // or convolved with a low-pass filter.
00796 // Return at each pixel, the magnitude, the vector (dG * I),
00797 // and the multiplication factor to normalize the magnitude.
00798 // The product of detection |dG * I| and localization |dddG * I| of
00799 // the step relative to noise is invariant to filter-size for the
00800 // ideal step edge, and depends only on the shape of the step edge.
00801 // As the filter size (sigma) increases by k, detection or
00802 // signal-to-noise ratio increases by sqrt(k), but localization
00803 // decreases by 1/sqrt(k). This invariance is consistent with the
00804 // uncertainty principle.
00805 
00806 float
00807 gevd_float_operators::Gradient(const gevd_bufferxy& smooth,
00808                                gevd_bufferxy*& magnitude,
00809                                gevd_bufferxy*& gradx, gevd_bufferxy*& grady,
00810                                const bool xwrap, const bool ywrap)
00811 {
00812 #ifdef DEBUG
00813   vul_timer t;
00814   vcl_cout << "Compute local gradient magnitude/direction";
00815 #endif
00816   magnitude = gevd_float_operators::Allocate(magnitude, smooth);
00817   gradx = gevd_float_operators::Allocate(gradx, smooth);
00818   grady = gevd_float_operators::Allocate(grady, smooth);
00819 
00820   // 1. Inside image frame, compute values based on 3x3 window.
00821   const int frame = 1;
00822   const int highx = smooth.GetSizeX() - frame; // exclusive bounds
00823   const int highy = smooth.GetSizeY() - frame;
00824   for (int j = frame; j < highy; ++j)
00825     for (int i = frame; i < highx; ++i)
00826       LocalGradient(smooth, i, j,
00827                     floatPixel(*magnitude, i, j),
00828                     floatPixel(*gradx, i, j),
00829                     floatPixel(*grady, i, j));
00830 
00831   // 2. Along horizontal/vertical borders
00832   if (xwrap) {                  //  each row wrap at border
00833     const int lo = 2, hi = 1;
00834     gevd_bufferxy* pad = gevd_float_operators::WrapAlongX(smooth);
00835     for (int j = 1; j < highy; ++j) {
00836       LocalGradient(*pad, lo, j,
00837                     floatPixel(*magnitude, 0, j),
00838                     floatPixel(*gradx, 0, j),
00839                     floatPixel(*grady, 0, j));
00840       LocalGradient(*pad, hi, j,
00841                     floatPixel(*magnitude, highx, j),
00842                     floatPixel(*gradx, highx, j),
00843                     floatPixel(*grady, highx, j));
00844     }
00845     delete pad;
00846   }
00847   else {                      // zero by default
00848     gevd_float_operators::FillFrameX(*magnitude, 0, frame);
00849     gevd_float_operators::FillFrameX(*gradx, 0, frame);
00850     gevd_float_operators::FillFrameX(*grady, 0, frame);
00851   }
00852   if (ywrap) {                  // each column wrap at border
00853     const int lo = 2, hi = 1;
00854     gevd_bufferxy* pad = gevd_float_operators::WrapAlongY(smooth);
00855     for (int i = 1; i < highx; ++i) {
00856       LocalGradient(*pad, i, lo,
00857                     floatPixel(*magnitude, i, 0),
00858                     floatPixel(*gradx, i, 0),
00859                     floatPixel(*grady, i, 0));
00860       LocalGradient(*pad, i, hi,
00861                     floatPixel(*magnitude, i, highy),
00862                     floatPixel(*gradx, i, highy),
00863                     floatPixel(*grady, i, highy));
00864     }
00865     delete pad;
00866   }
00867   else {                      // zero by default
00868     gevd_float_operators::FillFrameY(*magnitude, 0, frame);
00869     gevd_float_operators::FillFrameY(*gradx, 0, frame);
00870     gevd_float_operators::FillFrameY(*grady, 0, frame);
00871   }
00872 #ifdef DEBUG
00873   vcl_cout << ", in " << t.real() << " msecs.\n";
00874 #endif
00875   return 2;                     // return multiplication factor
00876 }
00877 
00878 //: Compute slope or first-difference, in linear/circular array.
00879 //
00880 // Special care is taken when \a to and \a from are identical - Peter Vanroose.
00881 
00882 float
00883 gevd_float_operators::Slope(float* from, float*& to, const int len,
00884                             const bool wrap)
00885 {
00886   bool overwrite = to == from;
00887   if (!to || overwrite) to = new float[len];
00888   const int xlo = 1, xhi = len - 1;
00889   float *cache = NULL, *pipeline;
00890   int p = gevd_float_operators::SetupPipeline(from, len, 1, wrap, // current pipeline and index
00891                                               cache, pipeline);
00892   to[0] = pipeline[+1] - pipeline[-1];
00893   for (int x = xlo; x < xhi; x++) {
00894     to[x] = pipeline[2] - pipeline[0];
00895     if (p < len) {
00896       for (int k = 0; k < 2; k++) // shift the floats of
00897         pipeline[k] = pipeline[k+1]; // the pipeline by 1
00898       pipeline[2] = from[p++];  // update pipeline
00899     }
00900   }
00901   to[xhi] = pipeline[3] - pipeline[1];
00902   delete[] cache;
00903   if (overwrite) {
00904     for (int x=0 ; x < len; ++x) from[x] = to[x];
00905     delete[] to;
00906     to = from;
00907   }
00908   return 2;                     // magnification factor
00909 }
00910 
00911 // Local Hessian magnitude and direction in 3x3 window
00912 // corresponding to largest eigenvalue, in absolute magnitude.
00913 // Encode the sign of the curvature in the angle (dirx, diry).
00914 
00915 void
00916 LocalHessian(const gevd_bufferxy& smooth, const int i, const int j,
00917              float& mag, float& dirx, float& diry)
00918 {
00919   float two_pij = 2 * floatPixel(smooth, i, j);
00920   float ddx = floatPixel(smooth, i+1, j) + floatPixel(smooth, i-1, j) - two_pij;
00921   float ddy = floatPixel(smooth, i, j+1) + floatPixel(smooth, i, j-1) - two_pij;
00922   float two_dxdy = (floatPixel(smooth, i+1, j+1) +
00923                     floatPixel(smooth, i-1, j-1) -
00924                     floatPixel(smooth, i-1, j+1) -
00925                     floatPixel(smooth, i+1, j-1)) / 2;
00926   float ddx_plus_ddy = ddx + ddy;
00927   float ddx_minus_ddy = ddx - ddy;
00928   float theta = (two_dxdy==0 && ddx_minus_ddy==0) ? 0 : // DOMAIN cond. on atan2
00929                 (float)vcl_atan2(two_dxdy, ddx_minus_ddy) / 2; // modulo PI
00930   if (ddx_plus_ddy < 0) {
00931     mag = - ddx_plus_ddy        // most negative eigenvalue
00932           + vcl_sqrt((ddx_minus_ddy * ddx_minus_ddy) + (two_dxdy * two_dxdy));
00933     theta += (float)vnl_math::pi_over_2;// angle in range [0 pi]
00934   }
00935   else {
00936     mag = + ddx_plus_ddy        // most positive eigenvalue
00937           + vcl_sqrt((ddx_minus_ddy * ddx_minus_ddy) + (two_dxdy * two_dxdy));
00938     if (theta > 0)
00939       theta -= (float)vnl_math::pi;    // angle in range [-pi 0]
00940   }
00941   dirx = (float)vcl_cos(theta); // eigenvector corresponding to
00942   diry = (float)vcl_sin(theta); // largest eigenvalue/curvature
00943 }
00944 
00945 //: Compute the Hessian of the intensity surface.
00946 // O(m*n).
00947 // The Hessian is directional, and described by the largest absolute
00948 // eigenvalue, and its corresponding eigenvector.
00949 // The intensity surface is assumed smoothed by a Gaussian filter,
00950 // or convolved with a low-pass filter.
00951 // Return at each pixel, the largest absolute value of the two
00952 // eigenvalues, the corresponding eigenvector, and the
00953 // multiplication factor to normalize the magnitude.
00954 
00955 float
00956 gevd_float_operators::Hessian(const gevd_bufferxy& smooth,
00957                               gevd_bufferxy*& magnitude,
00958                               gevd_bufferxy*& dirx, gevd_bufferxy*& diry,
00959                               const bool xwrap, const bool ywrap)
00960 {
00961 #ifdef DEBUG
00962   vul_timer t;
00963   vcl_cout << "Compute local Hessian magnitude/direction";
00964 #endif
00965   magnitude = gevd_float_operators::Allocate(magnitude, smooth);
00966   dirx = gevd_float_operators::Allocate(dirx, smooth);
00967   diry = gevd_float_operators::Allocate(diry, smooth);
00968 
00969   // 1. Inside image frame, compute values based on 3x3 window.
00970   const int frame = 1;
00971   const int highx = smooth.GetSizeX() - frame;  // exclusive bounds
00972   const int highy = smooth.GetSizeY() - frame;
00973   for (int j = frame; j < highy; ++j)
00974     for (int i = frame; i < highx; ++i)
00975       LocalHessian(smooth, i, j,
00976                    floatPixel(*magnitude, i, j),
00977                    floatPixel(*dirx, i, j),
00978                    floatPixel(*diry, i, j));
00979 
00980   // 2. Along horizontal/vertical borders
00981   if (xwrap) {                  //  each row wrap at border
00982     const int lo = 2, hi = 1;
00983     gevd_bufferxy* pad = gevd_float_operators::WrapAlongX(smooth);
00984     for (int j = 1; j < highy; ++j) {
00985       LocalHessian(*pad, lo, j,
00986                    floatPixel(*magnitude, 0, j),
00987                    floatPixel(*dirx, 0, j),
00988                    floatPixel(*diry, 0, j));
00989       LocalHessian(*pad, hi, j,
00990                    floatPixel(*magnitude, highx, j),
00991                    floatPixel(*dirx, highx, j),
00992                    floatPixel(*diry, highx, j));
00993     }
00994     delete pad;
00995   }
00996   else {                      // zero by default
00997     gevd_float_operators::FillFrameX(*magnitude, 0, frame);
00998     gevd_float_operators::FillFrameX(*dirx, 0, frame);
00999     gevd_float_operators::FillFrameX(*diry, 0, frame);
01000   }
01001   if (ywrap) {                  // each column wrap at border
01002     const int lo = 2, hi = 1;
01003     gevd_bufferxy* pad = gevd_float_operators::WrapAlongY(smooth);
01004     for (int i = 1; i < highx; ++i) {
01005       LocalHessian(*pad, i, lo,
01006                    floatPixel(*magnitude, i, 0),
01007                    floatPixel(*dirx, i, 0),
01008                    floatPixel(*diry, i, 0));
01009       LocalHessian(*pad, i, hi,
01010                    floatPixel(*magnitude, i, highy),
01011                    floatPixel(*dirx, i, highy),
01012                    floatPixel(*diry, i, highy));
01013     }
01014     delete pad;
01015   }
01016   else {                      // zero by default
01017     gevd_float_operators::FillFrameY(*magnitude, 0, frame);
01018     gevd_float_operators::FillFrameY(*dirx, 0, frame);
01019     gevd_float_operators::FillFrameY(*diry, 0, frame);
01020   }
01021 #ifdef DEBUG
01022   vcl_cout << ", in " << t.real() << " msecs.\n";
01023 #endif
01024   return 2;                     // multiplication factor for magnitude
01025 }
01026 
01027 // Local Laplacian in 3x3 neighborhood, sum of curvature,
01028 // and eigenvector corresponding to largest absolute eigenvalue.
01029 
01030 void
01031 LocalLaplacian(const gevd_bufferxy& smooth, const int i, const int j,
01032                float& mag, float& dirx, float& diry)
01033 {
01034   float two_pij = 2 * floatPixel(smooth, i, j);
01035   float ddx = floatPixel(smooth, i+1, j) + floatPixel(smooth, i-1, j) - two_pij;
01036   float ddy = floatPixel(smooth, i, j+1) + floatPixel(smooth, i, j-1) - two_pij;
01037   float diag1 = floatPixel(smooth, i+1, j+1) + floatPixel(smooth, i-1, j-1);
01038   float diag2 = floatPixel(smooth, i-1, j+1) + floatPixel(smooth, i+1, j-1);
01039   mag = 4*(ddx + ddy) - 2*two_pij + diag1 + diag2; // save division by 6
01040 #if 0 // commented out
01041   mag = (floatPixel(smooth, i+1, j+1) + floatPixel(smooth, i-1, j-1) +
01042          floatPixel(smooth, i-1, j+1) + floatPixel(smooth, i+1, j-1) +
01043          4 * (floatPixel(smooth, i+1, j) + floatPixel(smooth, i-1, j) +
01044               floatPixel(smooth, i, j+1) + floatPixel(smooth, i, j-1)) +
01045          -20 * floatPixel(smooth, i, j)) / 6;
01046 #endif
01047   float theta = (diag1==diag2 && ddx==ddy) ? 0 : // DOMAIN condition on atan2
01048                 (float)vcl_atan2((diag1 - diag2) / 2, ddx - ddy) / 2; // modulo PI
01049   if (mag < 0) {
01050     mag = - mag;                  // absolute magnitude
01051     theta += (float)vnl_math::pi_over_2; // other eigenvector
01052   }
01053   dirx = (float)vcl_cos(theta);   // eigenvector corresponding to
01054   diry = (float)vcl_sin(theta);   // largest eigenvalue/curvature
01055 }
01056 
01057 //: Compute the Laplacian of the intensity surface.
01058 // O(m*n).
01059 // The Laplacian is rotational symmetric, and is the sum of the 2
01060 // eigenvalues/curvatures, with positive/negative sign for concave/convex.
01061 // The intensity surface is assumed smoothed by a Gaussian filter,
01062 // or convolved with a low-pass filter.
01063 // Return at each pixel, the absolute value of the laplacian,
01064 // the eigenvector corresponding to the largest absolute
01065 // eigenvalue/curvature, and the multiplication factor to
01066 // normalize the magnitude.
01067 
01068 float
01069 gevd_float_operators::Laplacian(const gevd_bufferxy& smooth,
01070                                 gevd_bufferxy*& magnitude,
01071                                 gevd_bufferxy*& dirx, gevd_bufferxy*& diry,
01072                                 const bool xwrap, const bool ywrap)
01073 {
01074 #ifdef DEBUG
01075   vul_timer t;
01076   vcl_cout << "Compute local Laplacian magnitude/direction";
01077 #endif
01078   magnitude = gevd_float_operators::Allocate(magnitude, smooth);
01079   dirx = gevd_float_operators::Allocate(dirx, smooth);
01080   diry = gevd_float_operators::Allocate(diry, smooth);
01081 
01082   // 1. Inside image frame, compute values based on 3x3 window.
01083   const int frame = 1;
01084   const int highx = smooth.GetSizeX() - frame;// exclusive bounds
01085   const int highy = smooth.GetSizeY() - frame;
01086   for (int j = frame; j < highy; ++j)
01087     for (int i = frame; i < highx; ++i)
01088       LocalLaplacian(smooth, i, j,
01089                      floatPixel(*magnitude, i, j),
01090                      floatPixel(*dirx, i, j),
01091                      floatPixel(*diry, i, j));
01092 
01093   // 2. Along horizontal/vertical borders
01094   if (xwrap) {                  //  each row wrap at border
01095     const int lo = 2, hi = 1;
01096     gevd_bufferxy* pad = gevd_float_operators::WrapAlongX(smooth);
01097     for (int j = 1; j < highy; ++j) {
01098       LocalLaplacian(*pad, lo, j,
01099                      floatPixel(*magnitude, 0, j),
01100                      floatPixel(*dirx, 0, j),
01101                      floatPixel(*diry, 0, j));
01102       LocalLaplacian(*pad, hi, j,
01103                      floatPixel(*magnitude, highx, j),
01104                      floatPixel(*dirx, highx, j),
01105                      floatPixel(*diry, highx, j));
01106     }
01107     delete pad;
01108   }
01109   else {                      // zero by default
01110     gevd_float_operators::FillFrameX(*magnitude, 0, frame);
01111     gevd_float_operators::FillFrameX(*dirx, 0, frame);
01112     gevd_float_operators::FillFrameX(*diry, 0, frame);
01113   }
01114   if (ywrap) {                  // each column wrap at border
01115     const int lo = 2, hi = 1;
01116     gevd_bufferxy* pad = gevd_float_operators::WrapAlongY(smooth);
01117     for (int i = 1; i < highx; ++i) {
01118       LocalLaplacian(*pad, i, lo,
01119                      floatPixel(*magnitude, i, 0),
01120                      floatPixel(*dirx, i, 0),
01121                      floatPixel(*diry, i, 0));
01122       LocalLaplacian(*pad, i, hi,
01123                      floatPixel(*magnitude, i, highy),
01124                      floatPixel(*dirx, i, highy),
01125                      floatPixel(*diry, i, highy));
01126     }
01127     delete pad;
01128   }
01129   else {                      // zero by default
01130     gevd_float_operators::FillFrameY(*magnitude, 0, frame);
01131     gevd_float_operators::FillFrameY(*dirx, 0, frame);
01132     gevd_float_operators::FillFrameY(*diry, 0, frame);
01133   }
01134 #ifdef DEBUG
01135   vcl_cout << ", in " << t.real() << " msecs.\n";
01136 #endif
01137   return 6;                     // multiplication factor for magnitude
01138 }
01139 
01140 //: Compute curvature, or 2nd-difference in linear/circular array.
01141 //
01142 // Special care is taken when \a to and \a from are identical - Peter Vanroose.
01143 
01144 float
01145 gevd_float_operators::Curvature(float* from, float*& to, const int len,
01146                                 const bool wrap)
01147 {
01148   bool overwrite = to == from;
01149   if (!to || overwrite) to = new float[len];
01150   const int xlo = 1, xhi = len - 1;
01151   float *cache, *pipeline;
01152   int p = gevd_float_operators::SetupPipeline(from, len, 1, wrap, // current pipeline and index
01153                                               cache, pipeline);
01154   to[0] = pipeline[+1] + pipeline[-1] - 2*pipeline[0];
01155   for (int x = xlo; x < xhi; x++) {
01156     to[x] = pipeline[2] + pipeline[0] - 2*pipeline[1];
01157     if (p < len) {
01158       for (int k = 0; k < 2; k++)       // shift the floats of
01159         pipeline[k] = pipeline[k+1];    // the pipeline by 1
01160       pipeline[2] = from[p++];  // update pipeline
01161     }
01162   }
01163   to[xhi] = pipeline[3] + pipeline[1] - 2*pipeline[2];
01164   delete[] cache;
01165   if (overwrite) {
01166     for (int x=0 ; x < len; ++x) from[x] = to[x];
01167     delete[] to;
01168     to = from;
01169   }
01170   return 1;                     // magnification factor
01171 }
01172 
01173 
01174 //:
01175 // Compute the local orientation at each pixel in the image,
01176 // and returns 2 images: twice the angle, and coherence measure (0,1).
01177 
01178 float
01179 gevd_float_operators::Orientation(const gevd_bufferxy& smooth,
01180                                   gevd_bufferxy*& theta, gevd_bufferxy*& coherence,
01181                                   const int frame)
01182 {
01183   theta = gevd_float_operators::Allocate(theta, smooth);
01184   coherence = gevd_float_operators::Allocate(coherence, smooth);
01185   gevd_bufferxy& thetaI = *theta;
01186   gevd_bufferxy& coherenceI = *coherence;
01187   int highx = smooth.GetSizeX() - frame;        // exclusive bounds
01188   int highy = smooth.GetSizeY() - frame;
01189   float dx, dy;
01190   float p_ij, ox, oy;
01191   for (int j = frame; j < highy; ++j)
01192     for (int i = frame; i < highx; ++i) {
01193       p_ij = floatPixel(smooth, i, j);
01194       dx = floatPixel(smooth, i+1, j) - p_ij;   // assume 2D gradient
01195       dy = floatPixel(smooth, i, j+1) - p_ij;   // onto x and y axes
01196       ox = (dy * dy) - (dx * dx);
01197       oy = 2 * dx * dy;
01198       floatPixel(thetaI, i, j) = (float)vcl_atan2(oy, ox);
01199       floatPixel(coherenceI, i, j) = ((ox * ox + oy * oy) /
01200                                       (dx + dy) * (dx + dy));
01201     }
01202   gevd_float_operators::FillFrameX(thetaI, 0, frame);
01203   gevd_float_operators::FillFrameY(thetaI, 0, frame);
01204   gevd_float_operators::FillFrameX(coherenceI, 0, frame);
01205   gevd_float_operators::FillFrameY(coherenceI, 0, frame);
01206   return 1;
01207 }
01208 
01209 // Non maximum suppression in 3x3 window
01210 
01211 inline void
01212 LocalMaximum(const gevd_bufferxy& magnitude,
01213              const gevd_bufferxy& directionx, const gevd_bufferxy& directiony,
01214              const int i, const int j, const float threshold,
01215              float& contour, unsigned char& dir, // response & direction
01216              float& locx, float& locy)  // subpixel location
01217 {
01218   const float tan_pi_8 = (float)vcl_tan(vnl_math::pi_over_4/2);
01219   float strength = floatPixel(magnitude, i, j);
01220   if (strength > threshold) { // eliminate noisy responses
01221     float dx = floatPixel(directionx, i, j);
01222     float dy = floatPixel(directiony, i, j);
01223     if (dy < 0) {
01224       dx = -dx, dy = -dy;       // modulo PI
01225       dir = DIR4 - DIR0;
01226     }
01227     else
01228       dir = 0;
01229     float sl, sr, r;
01230     if (dx > 0) {               // which octant?
01231       if (dx > dy) {    // 0-45 degree
01232         r = dy / dx;
01233         sl = (r*floatPixel(magnitude, i-1, j-1) +
01234               (1-r)*floatPixel(magnitude, i-1, j));
01235         sr = (r*floatPixel(magnitude, i+1, j+1) +
01236               (1-r)*floatPixel(magnitude, i+1, j));
01237         dx = 1, dy = r; // range in location
01238         dir += (r < tan_pi_8)? DIR0 : DIR1;
01239       }
01240       else {          // 45-90 degree
01241         r = dx / dy;
01242         sl = (r*floatPixel(magnitude, i-1, j-1) +
01243               (1-r)*floatPixel(magnitude, i, j-1));
01244         sr = (r*floatPixel(magnitude, i+1, j+1) +
01245               (1-r)*floatPixel(magnitude, i, j+1));
01246         dy = 1, dx = r;
01247         dir += (r < tan_pi_8)? DIR2 : DIR1;
01248       }
01249     }
01250     else {
01251       dx = -dx;         // absolute value
01252       if (dy > dx) {    // 90-135 degree
01253         r = dx / dy;
01254         sl = (r*floatPixel(magnitude, i-1, j+1) +
01255               (1-r)*floatPixel(magnitude, i, j+1));
01256         sr = (r*floatPixel(magnitude, i+1, j-1) +
01257               (1-r)*floatPixel(magnitude, i, j-1));
01258         dy = -1, dx = r;
01259         dir += (r < tan_pi_8)? DIR2 : DIR3;
01260       }
01261       else {          // 135-180 degree
01262         r = dy / dx;
01263         sl = (r*floatPixel(magnitude, i+1, j-1) +
01264               (1-r)*floatPixel(magnitude, i+1, j));
01265         sr = (r*floatPixel(magnitude, i-1, j+1) +
01266               (1-r)*floatPixel(magnitude, i-1, j));
01267         dx = -1, dy = r;
01268         dir += (r < tan_pi_8)? DIR0 : DIR3;
01269       }
01270     }
01271     if (sl < strength &&        // usually a strict local maximum
01272         strength >= sr) {       // equality is for continuity for b&w images
01273       r = gevd_float_operators::InterpolateParabola(sl, strength, sr, // interpolated offset
01274                                                     contour); // interpolated max response
01275       locx = r*dx;
01276       locy = r*dy;
01277     }
01278     else
01279       contour = 0;              // mark NULL contour
01280   }
01281   else
01282     contour = 0;                // mark NULL contour
01283 }
01284 
01285 
01286 //:
01287 // Detect contour pixels as strict local maxima, and
01288 // interpolate the strength/location with a parabola through 3 points.
01289 // The location[xy]/direction[xy] buffers no longer share the same
01290 // space to avoid bogus values when the contour/junction pixels move
01291 // around by +/- 1 pixel.
01292 
01293 void
01294 gevd_float_operators::NonMaximumSuppression(const gevd_bufferxy& magnitude,
01295                                             const gevd_bufferxy& directionx,
01296                                             const gevd_bufferxy& directiony,
01297                                             const float threshold,
01298                                             gevd_bufferxy*& contour,
01299                                             gevd_bufferxy*& direction,
01300                                             gevd_bufferxy*& locationx,
01301                                             gevd_bufferxy*& locationy,
01302                                             const bool xwrap,
01303                                             const bool ywrap)
01304 {
01305 #ifdef DEBUG
01306   vul_timer t;
01307   vcl_cout << "Non maximum suppression to find edge elements > " << threshold;
01308 #endif
01309   contour = gevd_float_operators::Allocate(contour, magnitude);
01310   direction = gevd_float_operators::Allocate(direction, magnitude, bits_per_byte);
01311   locationx = gevd_float_operators::Allocate(locationx, magnitude);
01312   locationy = gevd_float_operators::Allocate(locationy, magnitude);
01313   locationx->Clear(); locationy->Clear(); // clear subpixel locs
01314 
01315   // 1. Inside image frame, compute values based on 3x3 window.
01316   const int frame = 1;
01317   const int highx = magnitude.GetSizeX() - frame;// exclusive bounds
01318   const int highy = magnitude.GetSizeY() - frame;
01319   for (int j = frame; j < highy; ++j)
01320     for (int i = frame; i < highx; ++i)
01321       LocalMaximum(magnitude, directionx, directiony,
01322                    i, j, threshold,
01323                    floatPixel(*contour, i, j),
01324                    bytePixel(*direction, i, j),
01325                    floatPixel(*locationx, i, j),
01326                    floatPixel(*locationy, i, j));
01327 
01328   // 2. Along horizontal/vertical borders
01329   if (xwrap) {                  //  each row wrap at border
01330     const int lo = 2, hi = 1;
01331     gevd_bufferxy* mag = gevd_float_operators::WrapAlongX(magnitude);
01332     gevd_bufferxy* dirx = gevd_float_operators::WrapAlongX(directionx);
01333     gevd_bufferxy* diry = gevd_float_operators::WrapAlongX(directiony);
01334     for (int j = 1; j < highy; ++j) {
01335       LocalMaximum(*mag, *dirx, *diry,
01336                    lo, j, threshold,
01337                    floatPixel(*contour, 0, j),
01338                    bytePixel(*direction, 0, j),
01339                    floatPixel(*locationx, 0, j),
01340                    floatPixel(*locationy, 0, j));
01341       LocalMaximum(*mag, *dirx, *diry,
01342                    hi, j, threshold,
01343                    floatPixel(*contour, highx, j),
01344                    bytePixel(*direction, highx, j),
01345                    floatPixel(*locationx, highx, j),
01346                    floatPixel(*locationy, highx, j));
01347     }
01348     delete mag; delete dirx; delete diry;
01349   }
01350   else {                      // zero by default
01351     gevd_float_operators::FillFrameX(*contour, 0, frame);
01352     for (int j = 1; j < highy; ++j) {
01353       bytePixel(*direction, 0, j) = 0;
01354       bytePixel(*direction, highx, j) = 0;
01355     }
01356   }
01357   if (ywrap) {                  // each column wrap at border
01358     const int lo = 2, hi = 1;
01359     gevd_bufferxy* mag = gevd_float_operators::WrapAlongY(magnitude);
01360     gevd_bufferxy* dirx = gevd_float_operators::WrapAlongY(directionx);
01361     gevd_bufferxy* diry = gevd_float_operators::WrapAlongY(directiony);
01362     for (int i = 1; i < highx; ++i) {
01363       LocalMaximum(*mag, *dirx, *diry,
01364                    i, lo, threshold,
01365                    floatPixel(*contour, i, 0),
01366                    bytePixel(*direction, i, 0),
01367                    floatPixel(*locationx, i, 0),
01368                    floatPixel(*locationy, i, 0));
01369       LocalMaximum(*mag, *dirx, *diry,
01370                    i, hi, threshold,
01371                    floatPixel(*contour, i, highy),
01372                    bytePixel(*direction, i, highy),
01373                    floatPixel(*locationx, i, highy),
01374                    floatPixel(*locationy, i, highy));
01375     }
01376     delete mag; delete dirx; delete diry;
01377   }
01378   else {                      // zero by default
01379     gevd_float_operators::FillFrameY(*contour, 0, frame);
01380     for (int i = 1; i < highx; ++i) {
01381       bytePixel(*direction, i, 0) = 0;
01382       bytePixel(*direction, i, highy) = 0;
01383     }
01384   }
01385 #ifdef DEBUG
01386   vcl_cout << ", in " << t.real() << " msecs.\n";
01387 #endif
01388 }
01389 
01390 //: Detect local maxima in linear/circular array.
01391 
01392 int
01393 gevd_float_operators::NonMaximumSuppression(const float* data, const int len,
01394                                             const float threshold,
01395                                             int*& index, float*& mag, float*& loc,
01396                                             const bool wrap)
01397 {
01398   if (!index) index = new int[len/2];
01399   if (!mag) mag = new float[len/2];
01400   if (!loc) loc = new float[len/2];
01401   const int xlo = 1, xhi = len - 1;
01402   float *cache, *pipeline;
01403   int p = gevd_float_operators::SetupPipeline(data, len, 1, wrap, // current pipeline and index
01404                                               cache, pipeline);
01405   int nmax = 0;                 // number of maxima found
01406   if (pipeline[0] > threshold &&
01407       pipeline[0] > pipeline[+1] &&
01408       pipeline[0] > pipeline[-1]) {
01409     index[nmax] = 0;
01410     loc[nmax] = gevd_float_operators::InterpolateParabola(pipeline[-1], pipeline[0],
01411                                                           pipeline[+1], mag[nmax]);
01412     nmax++;
01413   }
01414   for (int x = xlo; x < xhi; x++) {
01415     if (pipeline[1] > threshold &&
01416         pipeline[1] > pipeline[2] &&
01417         pipeline[1] > pipeline[0]) {
01418       index[nmax] = x;
01419       loc[nmax] = gevd_float_operators::InterpolateParabola(pipeline[0], pipeline[1],
01420                                                             pipeline[2], mag[nmax]);
01421       nmax++;
01422     }
01423     if (p < len) {
01424       for (int k = 0; k < 2; k++)       // shift the floats of
01425         pipeline[k] = pipeline[k+1];    // the pipeline by 1
01426       pipeline[2] = data[p++];  // update pipeline
01427     }
01428   }
01429   if (pipeline[2] > threshold &&
01430       pipeline[2] > pipeline[3] &&
01431       pipeline[2] > pipeline[1]) {
01432     index[nmax] = xhi;
01433     loc[nmax] = gevd_float_operators::InterpolateParabola(pipeline[1], pipeline[2],
01434                                                           pipeline[3], mag[nmax]);
01435     nmax++;
01436   }
01437   delete[] cache;
01438   return nmax;                  // number of maxima found
01439 }
01440 
01441 //: Fit a parabola through 3 points with strict local max/min.
01442 // Return the offset location, and value of the maximum/minimum.
01443 
01444 float
01445 gevd_float_operators::InterpolateParabola(float y_1, float y_0, float y_2,
01446                                           float&y)
01447 {
01448   float diff1 = y_2 - y_1;      // first derivative
01449   float diff2 = 2 * y_0 - y_1 - y_2; // second derivative
01450   y = y_0 + diff1 * diff1 / (8 * diff2);        // interpolate y as max/min
01451   return diff1 / (2 * diff2);   // interpolate x offset
01452 }
01453 
01454 //: Return the float value of pixel (x, y) in float buffer, using bilinear interpolation.
01455 
01456 float
01457 gevd_float_operators::BilinearInterpolate(const gevd_bufferxy& buffer,
01458                                           float x, float y)
01459 {
01460   register int ix = int(x);             // integral parts
01461   register int iy = int(y);
01462   float fx = x - ix;                    // fractional parts
01463   float fy = y - iy;
01464   float c00 = floatPixel(buffer, ix, iy); // 4 corners of rectangle
01465   float c01 = floatPixel(buffer, ix, iy+1);
01466   float c10 = floatPixel(buffer, ix+1, iy);
01467   float c11 = floatPixel(buffer, ix+1, iy+1);
01468   float c0 = c00 + fy * (c01 - c00);    // interpolate along y
01469   float c1 = c10 + fy * (c11 - c10);
01470   return  c0  + fx * (c1 - c0);         // interpolate along x
01471 }
01472 
01473 
01474 //: Find angle of the line of support, in degrees.
01475 
01476 void
01477 gevd_float_operators::SupportAngle(const gevd_bufferxy& dirx, const gevd_bufferxy& diry,
01478                                    const gevd_bufferxy& magnitude,
01479                                    gevd_bufferxy*& angLe)
01480 {
01481   angLe = gevd_float_operators::Allocate(angLe, magnitude);
01482   const int highx = magnitude.GetSizeX();       // exclusive bounds
01483   const int highy = magnitude.GetSizeY();
01484   float theta;
01485   for (int j = 0; j < highy; ++j)
01486     for (int i = 0; i < highx; ++i)
01487       if (floatPixel(magnitude, i, j) > 0) {
01488         theta = (float)vcl_atan2(floatPixel(diry, i, j), floatPixel(dirx, i, j));
01489         if (theta < 0) theta += (float)vnl_math::pi;
01490         floatPixel(*angLe, i, j) = theta * float(vnl_math::deg_per_rad);
01491       }
01492 }
01493 
01494 
01495 //: Find local surface normal at all pixels in the range z(x,y) image.
01496 // The normal is estimated from the cross-product of the two tangent
01497 // vectors along the x- and y- axes.
01498 
01499 void
01500 gevd_float_operators::SurfaceNormal(const gevd_bufferxy& range, gevd_bufferxy*& normal)
01501 {
01502   const int frame = 1;
01503   const int highx = range.GetSizeX()-frame, highy = range.GetSizeY()-frame;
01504   normal = gevd_float_operators::Allocate(normal, range, bits_per_ptr);
01505   normal->Clear();              // NULL vector on border
01506   vnl_float_3 tx(2.f,0.f,0.f), ty(0.f,2.f,0.f); // tangents x-y axes
01507   for (int j = frame; j < highy; j++)
01508     for (int i = frame; i < highx; i++) { // for all grid points
01509       tx[2] = floatPixel(range, i+1, j) - floatPixel(range, i-1, j);
01510       ty[2] = floatPixel(range, i, j+1) - floatPixel(range, i, j-1);
01511 
01512       vnl_vector<float> *nz= new vnl_vector<float>(vnl_cross_3d(tx,ty).as_ref());
01513 
01514       float mag = nz->magnitude();
01515       if (mag != 0) *nz /= mag; // make unit vector
01516       fvectorPixel(*normal, i, j) = nz;
01517     }
01518 }
01519 
01520 //:
01521 // Find local gaussian curvature at all pixels from the local surface
01522 // normals, previously computed from the range z(x,y) image.
01523 // The gaussian curvature is estimated as the root mean square of the
01524 // two curvatures along the x- and y- axes.
01525 
01526 void
01527 gevd_float_operators::SurfaceCurvature(const gevd_bufferxy& normal, gevd_bufferxy*& curvature)
01528 {
01529   const int frame = 2;
01530   const int highx = normal.GetSizeX()-frame, highy = normal.GetSizeY()-frame;
01531   curvature = gevd_float_operators::Allocate(curvature, normal, bits_per_float);
01532   for (int j = frame; j < highy; j++)
01533     for (int i = frame; i < highx; i++) { // for all grid points
01534       float max_curv2 =         // along 0 degree
01535         vnl_cross_3d(*fvectorPixel(normal, i+1, j),
01536                      *fvectorPixel(normal, i-1, j)).squared_magnitude();
01537       float curv2 =             // along 90 degree
01538         vnl_cross_3d(*fvectorPixel(normal, i, j+1),
01539                      *fvectorPixel(normal, i, j-1)).squared_magnitude();
01540       if (max_curv2 < curv2) max_curv2 = curv2;
01541       curv2 =                   // along 45 degree
01542         vnl_cross_3d(*fvectorPixel(normal, i+1, j+1),
01543                      *fvectorPixel(normal, i-1, j-1)).squared_magnitude()/2;
01544       if (max_curv2 < curv2) max_curv2 = curv2;
01545       curv2 =                   // along 135 degree
01546         vnl_cross_3d(*fvectorPixel(normal, i+1, j-1),
01547                      *fvectorPixel(normal, i-1, j+1)).squared_magnitude()/2;
01548       if (max_curv2 < curv2) max_curv2 = curv2;
01549       floatPixel(*curvature, i, j) = vcl_sqrt(max_curv2);
01550     }
01551   gevd_float_operators::FillFrameX(*curvature, 0, frame); // zero curvature around frame.
01552   gevd_float_operators::FillFrameY(*curvature, 0, frame);
01553 }
01554 
01555 
01556 //  Find the tangent at pixel x,y by looking at neighboring pixels
01557 //  low_x,low_y and high_x,high_y taken from along a given direction.
01558 //  If no value is recorded at x,y or no value is recorded at both
01559 //  low_x,low_y and high_x,high_y then no tangent value may be
01560 //  calculated.  Otherwise, the tangent is calculated from the two
01561 //  farthest apart pixels (preferably low_x,low_y and high_x,high_y).
01562 //  Returned are the pixel distance between points used to calculate
01563 //  the tangent and the change in z.  Together, these may be used to
01564 //  form the tangent vector.  Note that the distance returned is
01565 //  either 1.0 or 2.0, so this must be scaled by sqrt(2) if the points
01566 //  are taken from along a diagonal.
01567 static bool
01568 _TangentComponents(const gevd_bufferxy& range,
01569                    int low_x, int low_y,
01570                    int x, int y,
01571                    int high_x, int high_y,
01572                    float no_value,          // value if there is no valid Z
01573                    float & distance,
01574                    float & delta_z )
01575 {
01576   float low_z  = floatPixel( range, low_x, low_y );
01577   float z      = floatPixel( range, x, y );
01578   float high_z = floatPixel( range, high_x, high_y );
01579 
01580   if ( z == no_value || ( low_z == no_value && high_z == no_value ) )
01581     return false;
01582 
01583   if ( low_z == no_value ) {
01584     distance = 1.0;
01585     delta_z = high_z - z;
01586 #ifdef DEBUG
01587     vcl_cout << "TC  low missing:  distance = " << distance << ", delta_z = " << delta_z << vcl_endl;
01588 #endif
01589   }
01590   else if ( high_z == no_value ) {
01591     distance = 1.0;
01592     delta_z = z - low_z;
01593 #ifdef DEBUG
01594     vcl_cout << "TC  high missing:  distance = " << distance << ", delta_z = " << delta_z << vcl_endl;
01595 #endif
01596   }
01597   else {
01598     distance = 2.0;
01599     delta_z = high_z - low_z;
01600 #ifdef DEBUG
01601     vcl_cout << "TC  neither missing:  distance = " << distance << ", delta_z = " << delta_z << vcl_endl;
01602 #endif
01603   }
01604   return true;
01605 }
01606 
01607 
01608 //: Same as gevd_float_operators::SurfaceNormal with two exceptions.
01609 // First, it explicitly recognizes and avoids pixels that have no
01610 // range value.  These are often called "dropouts", hence the "D" at
01611 // the end of the function name.  Second, it uses an optional
01612 // "pixel_distance" value to put the inter-pixel distances in the same
01613 // coordinate system as the range values.
01614 //
01615 void
01616 gevd_float_operators::SurfaceNormalD(const gevd_bufferxy& range,
01617                                      gevd_bufferxy*& normal,
01618                                      float no_value,
01619                                      float pixel_distance)
01620 {
01621   const int frame = 1;
01622   const int highx = range.GetSizeX()-frame, highy = range.GetSizeY()-frame;
01623   normal = gevd_float_operators::Allocate(normal, range, bits_per_ptr);
01624   normal->Clear();              // NULL vector on border
01625 
01626   for (int j = frame; j < highy; j++)
01627     for (int i = frame; i < highx; i++) { // for all grid points
01628       float d_x, d_z_x;
01629       float d_y, d_z_y;
01630       if (_TangentComponents(range, i-1, j, i, j, i+1, j, no_value, d_x, d_z_x) &&
01631           _TangentComponents(range, i, j-1, i, j, i, j+1, no_value, d_y, d_z_y) )
01632         {
01633           vnl_float_3 tx(d_x*pixel_distance, 0.f, d_z_x),
01634                       ty(0.f, d_y*pixel_distance, d_z_y);
01635           vnl_vector<float>* nz = new vnl_vector<float>(vnl_cross_3d(tx,ty).as_ref());
01636 
01637 #ifdef DEBUG
01638           vcl_cout << "Tx = " << tx << ",  Ty = " << ty << vcl_endl;
01639 #endif
01640           float mag = nz->magnitude();
01641           if (mag != 0) {
01642             *nz /= mag;   // make unit vector
01643 #ifdef DEBUG
01644             vcl_cout << "Normal = " << *nz << vcl_endl;
01645 #endif
01646             fvectorPixel(*normal, i, j) = nz;
01647           }
01648           else {
01649             delete nz;
01650             fvectorPixel(*normal, i, j) = NULL;
01651           }
01652         }
01653       else {
01654         fvectorPixel(*normal, i, j) = NULL;
01655       }
01656     }
01657 }
01658 
01659 //:
01660 //  Approximate the curvature at pixel x,y along a given direction by
01661 //  looking at neighboring pixels low_x,low_y and high_x,high_y taken
01662 //  from along that direction.  If no normal is recorded at x,y or no normal
01663 //  is recorded at both low_x,low_y and high_x,high_y then no
01664 //  curvature may be calculated.  Otherwise, the curvature is
01665 //  calculated from the two farthest apart pixels (preferably
01666 //  low_x,low_y and high_x,high_y).  Returned are the pixel distance
01667 //  between points used to calculate the curvature and the
01668 //  unnormalized curvature approximation.
01669 //
01670 //  NOTE: This approximation breaks down at the boundaries for
01671 //  high curvature, because while in the normal case the
01672 //  curvature at point p is sampled (along whatever direction) at
01673 //  points p-1 and p+1, and then assigned to point p, at the
01674 //  boundaries it's sampled at p and p+1 (or p and p-1) and then
01675 //  assigned to p when it \e should be assigned to some point
01676 //  between the two.
01677 static bool
01678 _CurvatureInDir(const gevd_bufferxy& normal,
01679                 const gevd_bufferxy& surface,
01680                 int low_x, int low_y,
01681                 int x, int y,
01682                 int high_x, int high_y,
01683                 float & sq_dist,
01684                 float & sq_curve )
01685 {
01686   vnl_vector<float> * low_norm  = fvectorPixel( normal, low_x, low_y );
01687   vnl_vector<float> * norm      = fvectorPixel( normal, x, y );
01688   vnl_vector<float> * high_norm = fvectorPixel( normal, high_x, high_y );
01689 
01690   float zval1, zval2, dz;
01691   int dx, dy;
01692 
01693   if ( norm == NULL || ( low_norm == NULL && high_norm == NULL ) )
01694     return false;
01695 
01696   if ( low_norm == NULL ) {
01697     // -rgc-   sq_dist = 1.0;
01698     zval1 = floatPixel(surface, high_x, high_y);
01699     zval2 = floatPixel(surface, x, y);
01700     dz = zval1 - zval2;
01701     dx = high_x - x;
01702     dy = high_y - y;
01703     sq_dist = (dx*dx) + (dy*dy) + (dz*dz);
01704     sq_curve = vnl_cross_3d( *high_norm, *norm ).squared_magnitude();
01705 #ifdef DEBUG
01706     vcl_cout << "CinDir  low missing:  sq_dist = " << sq_dist
01707              << ",  high = " << *high_norm
01708              << ",  norm = " << *norm
01709              << ",  sq_curve = " << sq_curve << vcl_endl;
01710 #endif
01711   }
01712   else if ( high_norm == NULL ) {
01713     // -rgc-   sq_dist = 1.0;
01714     zval1 = floatPixel(surface, x, y);
01715     zval2 = floatPixel(surface, low_x, low_y);
01716     dz = zval1 - zval2;
01717     dx = x - low_x;
01718     dy = y - low_y;
01719     sq_dist = (dx*dx) + (dy*dy) + (dz*dz);
01720     sq_curve = vnl_cross_3d( *norm, *low_norm ).squared_magnitude();
01721 #ifdef DEBUG
01722     vcl_cout << "CinDir  high missing:  sq_dist = " << sq_dist
01723              << ",  norm = " << *norm
01724              << ",  low = " << *low_norm
01725              << ",  sq_curve = " << sq_curve << vcl_endl;
01726 #endif
01727   }
01728   else {
01729     // -rgc-   sq_dist = 4.0;
01730     zval1 = floatPixel(surface, high_x, high_y);
01731     zval2 = floatPixel(surface, low_x, low_y);
01732     dz = zval1 - zval2;
01733     dx = high_x - low_x;
01734     dy = high_y - low_y;
01735     sq_dist = (dx*dx) + (dy*dy) + (dz*dz);
01736     sq_curve = vnl_cross_3d( *high_norm, *low_norm ).squared_magnitude();
01737 #ifdef DEBUG
01738     vcl_cout << "CinDir  neither missing:  sq_dist = " << sq_dist
01739              << ",  high = " << *high_norm
01740              << ",  low = " << *low_norm
01741              << ",  sq_curve = " << sq_curve << vcl_endl;
01742 #endif
01743   }
01744   return true;
01745 }
01746 
01747 
01748 //:
01749 // Estimate the maximum curvature at all pixels from the local
01750 // surface normals, previously computed from the range z(x,y) image.
01751 // This is similar to SurfaceCurvature above with two exceptions.
01752 // First, it explicitly recognizes image locations where no normal has
01753 // been calculated.  Second, it normalizes the curvature by the
01754 // pixel_distance, converting the curvature to a real-world quantity
01755 // rather than a pixel quantity.
01756 //
01757 void
01758 gevd_float_operators::SurfaceCurvatureD(const gevd_bufferxy& normal,
01759                                         const gevd_bufferxy& surface,
01760                                         gevd_bufferxy*& curvature,
01761                                         float dflt,
01762                                         float pixel_distance )
01763 {
01764   const int frame = 2;
01765   const int highx = normal.GetSizeX()-frame, highy = normal.GetSizeY()-frame;
01766   curvature = gevd_float_operators::Allocate(curvature, normal, bits_per_float);
01767   float sq_unit_normalize = 1.0f/(pixel_distance*pixel_distance);
01768 
01769   for (int j = frame; j < highy; j++)
01770     for (int i = frame; i < highx; i++) { // for all grid points
01771       if ( fvectorPixel( normal, i, j ) == NULL ) {
01772         floatPixel(*curvature, i, j) = dflt;
01773         continue;
01774       }
01775 #ifdef DEBUG
01776       vcl_cout << "i,j:" << i << ' ' << j << vcl_endl;
01777 #endif
01778       float max_sq_curve = -1;
01779       float sq_dist, sq_curve;
01780       if ( _CurvatureInDir( normal, surface, i-1, j, i, j, i+1, j,  // horizontal
01781                             sq_dist, sq_curve ) ) {
01782         sq_curve *= sq_unit_normalize / sq_dist;
01783 #ifdef DEBUG
01784         vcl_cout << "  sq_curve(h) = " << sq_curve << vcl_endl;
01785 #endif
01786         if ( sq_curve > max_sq_curve ) max_sq_curve = sq_curve;
01787       }
01788       if ( _CurvatureInDir( normal, surface,i, j-1, i, j, i, j+1,  // vertical
01789                             sq_dist, sq_curve ) ) {
01790         sq_curve *= sq_unit_normalize / sq_dist;
01791 #ifdef DEBUG
01792         vcl_cout << "  sq_curve(v) = " << sq_curve << vcl_endl;
01793 #endif
01794        if ( sq_curve > max_sq_curve ) max_sq_curve = sq_curve;
01795       }
01796       if ( _CurvatureInDir( normal, surface, i-1, j-1, i, j, i+1, j+1, //  45 degrees
01797                             sq_dist, sq_curve ) ) {
01798         sq_curve *= sq_unit_normalize / sq_dist;
01799 #ifdef DEBUG
01800         vcl_cout << "  sq_curve(45) = " << sq_curve << vcl_endl;
01801 #endif
01802         if ( sq_curve > max_sq_curve ) max_sq_curve = sq_curve;
01803       }
01804       if ( _CurvatureInDir( normal, surface, i-1, j+1, i, j, i+1, j-1, // 135 degrees
01805                             sq_dist, sq_curve ) ) {
01806         sq_curve *= sq_unit_normalize / sq_dist;
01807 #ifdef DEBUG
01808         vcl_cout << "  sq_curve(135) = " << sq_curve << vcl_endl;
01809 #endif
01810         if ( sq_curve > max_sq_curve ) max_sq_curve = sq_curve;
01811       }
01812 #ifdef DEBUG
01813       vcl_cout << "  max_sq_curve = " << max_sq_curve << vcl_endl;
01814 #endif
01815       if ( max_sq_curve < 0 )
01816         floatPixel(*curvature, i, j) = dflt;
01817       else
01818         floatPixel(*curvature, i, j) = vcl_sqrt(max_sq_curve);
01819 #ifdef DEBUG
01820       vcl_cout << "curvature in 1/inches: " << i << ' ' << j << ' ' << floatPixel(*curvature, i, j) << vcl_endl;
01821 #endif
01822     }
01823   gevd_float_operators::FillFrameX(*curvature, dflt, frame);    // default curvature
01824   gevd_float_operators::FillFrameY(*curvature, dflt, frame);    // around frame.
01825 }
01826 
01827 
01828 //: Shrinks the \a from image by 2 and stores into \a to image, using Burt-Adelson reduction algorithm.
01829 // Convolution with a 5-point kernel [(0.5-ka)/2, 0.25, ka, 0.25, (0.5-ka)/2]
01830 // ka = 0.6  maximum decorrelation, wavelet for image compression.
01831 // ka = 0.5  linear interpolation,
01832 // ka = 0.4  Gaussian filter
01833 // ka = 0.359375 min aliasing, wider than Gaussian
01834 // The image indexes are mapped with: to.ij = from.ij / 2
01835 // The image sizes are related by: to.size = (from.size+1)/2.
01836 //
01837 // Special care is taken when \a to and \a from are identical - Peter Vanroose.
01838 
01839 float
01840 gevd_float_operators::ShrinkBy2(const gevd_bufferxy& from, gevd_bufferxy*& to,
01841                                 const float burt_ka)
01842 {
01843   const int sizeX = (from.GetSizeX() + 1) / 2;
01844   const int sizeY = (from.GetSizeY() + 1) / 2;
01845   bool overwrite = to == &from;
01846   gevd_bufferxy*& t = to;
01847   if (overwrite) to=0;
01848   to = gevd_float_operators::Allocate(to, from, 0, sizeX, sizeY);
01849   const float ka = burt_ka;
01850   const float kb = 0.25f;
01851   const float kc = (0.5f - burt_ka) / 2;
01852 
01853   int p = 0;                            // pipeline of 5 lines
01854   float* yline0 = new float[sizeX];     // shrink_by_2 along x
01855   float* yline1 = new float[sizeX];
01856   float* yline2 = new float[sizeX];
01857   float* yline3 = new float[sizeX];
01858   float* yline4 = new float[sizeX];
01859   gevd_float_operators::ShrinkBy2AlongX(from, p++, yline0, sizeX, ka, kb, kc);
01860   gevd_float_operators::ShrinkBy2AlongX(from, p++, yline1, sizeX, ka, kb, kc);
01861   gevd_float_operators::ShrinkBy2AlongX(from, p++, yline2, sizeX, ka, kb, kc);
01862   gevd_float_operators::ShrinkBy2AlongX(from, p++, yline3, sizeX, ka, kb, kc);
01863   gevd_float_operators::ShrinkBy2AlongX(from, p++, yline4, sizeX, ka, kb, kc);
01864 
01865   // shrink_by_2 along y-axis.
01866   for (int x = 0; x < sizeX; x++)
01867     floatPixel(*to, x, 0) = (ka * yline0[x] + // reflect at image border
01868                              2 * (kb * yline1[x] + kc * yline2[x]));
01869   for (int y = 1; y < sizeY; y++) {
01870     for (int x = 0; x < sizeX; x++)
01871       floatPixel(*to, x, y) = (ka * yline2[x] +
01872                                kb * (yline1[x] + yline3[x]) +
01873                                kc * (yline0[x] + yline4[x]));
01874     float* next0 = yline0;              // shift pipeline by 2 lines
01875     float* next1 = yline1;
01876     yline0 = yline2;
01877     yline1 = yline3;
01878     yline2 = yline4;
01879     yline3 = next0;
01880     yline4 = next1;
01881     if (y < sizeY-2) {
01882       gevd_float_operators::ShrinkBy2AlongX(from, p++, next0, sizeX, // new ylines
01883                                             ka, kb, kc);             // for pipeline
01884       gevd_float_operators::ShrinkBy2AlongX(from, p++, next1, sizeX, // new ylines
01885                                             ka, kb, kc);             // for pipeline
01886     }
01887     else {                            // reflect at image border
01888       vcl_memcpy(next0, yline1, sizeX*sizeof(float));
01889       vcl_memcpy(next1, yline0, sizeX*sizeof(float));
01890     }
01891   }
01892   delete[] yline0; delete[] yline1; delete[] yline2;
01893   delete[] yline3; delete[] yline4;
01894   if (overwrite) { gevd_float_operators::Update(*t,*to); delete to; to = t; }
01895   return 1;
01896 }
01897 
01898 //: Shrinks the yline by 2 along the x-axis.
01899 // We compute every 2 pixels, the convolution of the 5 pixels along x,
01900 // with the 5-point kernel [kc, kb, ka, kb, kc].
01901 
01902 float
01903 gevd_float_operators::ShrinkBy2AlongX(const gevd_bufferxy& from, const int y,
01904                                       float* yline, const int sizeX,
01905                                       const float ka, const float kb,
01906                                       const float kc)
01907 {
01908   int p = 0;                            // setup pipeline of 5 x values
01909   float x0 = floatPixel(from, p++, y);
01910   float x1 = floatPixel(from, p++, y);
01911   float x2 = floatPixel(from, p++, y);
01912   float x3 = floatPixel(from, p++, y);
01913   float x4 = floatPixel(from, p++, y);
01914   yline[0] = ka * x0 + 2 * (kb * x1 + kc * x2); // reflect at border
01915   for (int x = 1; x < sizeX; x++) {
01916     yline[x] = (ka * x2 +               // shrink_by_2 along x axis
01917                 kb * (x1 + x3) +
01918                 kc * (x0 + x4));
01919       x0 = x2;                                  // shift by 2
01920       x1 = x3;
01921       x2 = x4;
01922     if (x < sizeX-2) {
01923       x3 = floatPixel(from, p++, y);
01924       x4 = floatPixel(from, p++, y);
01925     }
01926     else {
01927       x3 = x1;
01928       x4 = x0;
01929     }
01930   }
01931   return 1;
01932 }
01933 
01934 
01935 void
01936 PrintAllPipes( float * y_s[],
01937                float * w_s[],
01938                int length )
01939 {
01940   for (int i=0; i<5; i++ ) {
01941     vcl_cout << "\nPipe " << i << vcl_endl;
01942     for (int j=0; j<length; j++ ) {
01943       vcl_cout << j << ",  yline[j] = " << y_s[i][j] << ",  wline[j] = "
01944                << w_s[i][j] << vcl_endl;
01945     }
01946   }
01947 }
01948 
01949 
01950 //: Shrinks the \a from image by 2 and stores into \a to image.
01951 // Same as ShrinkBy2 except that it properly handles pixels that
01952 // have unusable value ("dropouts" --- hence the appended "_D" in the
01953 // name).  These are pixel values "no_value".  One problem with this
01954 // function is that one pixel wide structures may or may not be
01955 // retained in the shrunken image, depending on the position of the
01956 // structure.  This should not be a problem for the initial application.
01957 //
01958 // Special care is taken when \a to and \a from are identical - Peter Vanroose.
01959 
01960 void
01961 gevd_float_operators::ShrinkBy2_D(const gevd_bufferxy& from,
01962                                   gevd_bufferxy*& to,
01963                                   float no_value,
01964                                   float burt_ka )
01965 {
01966   const int sizeX = (from.GetSizeX() + 1) / 2;
01967   const int sizeY = (from.GetSizeY() + 1) / 2;
01968   bool overwrite = to == &from;
01969   gevd_bufferxy*& t = to;
01970   if (overwrite) to=0;
01971   to = gevd_float_operators::Allocate(to, from, 0, sizeX, sizeY);
01972 
01973   //  Build a kernel of smoothing weights.  kernel[2] is the center.
01974   float kernel[5];
01975   kernel[2] = burt_ka;
01976   kernel[1] = kernel[3] = 0.25f;
01977   kernel[0] = kernel[4] = (0.5f - burt_ka) / 2;
01978 
01979   //  Smoothing and subsampling will occur first along each row and
01980   //  then between rows.  So, a pipeline of 5 smoothed and subsampled
01981   //  rows is needed.  There is a pipeline of rows and a pipeline of
01982   //  associated weights.  Allocate these and, in addition, allocate
01983   //  and fill space for an empty row.
01984   //
01985   float * yline[5];
01986   for (int i=0; i<5; i++ ) yline[i] = new float[sizeX];
01987   float * wline[5];
01988   for (int i=0; i<5; i++ ) wline[i] = new float[sizeX];
01989   float * y_empty = new float[sizeX];
01990   float * w_empty = new float[sizeX];
01991 
01992   //  Set the values for the empty rows.  These are used for the top
01993   //  and bottom of the image.
01994   //
01995   for (int i=0; i<sizeX; i++ ) {
01996     y_empty[i] = no_value;
01997     w_empty[i] = 0.0;
01998   }
01999 
02000   //  Fill the top two rows of the pipelines with "empty" values.
02001   //  This will allow the algorithm to mimic having a buffer of
02002   //  "no_value" surrounding the image.
02003   //
02004   vcl_memcpy(yline[0], y_empty, sizeX*sizeof(float));
02005   vcl_memcpy(wline[0], w_empty, sizeX*sizeof(float));
02006   vcl_memcpy(yline[1], y_empty, sizeX*sizeof(float));
02007   vcl_memcpy(wline[1], w_empty, sizeX*sizeof(float));
02008 
02009   //  Fill the center and bottom half of the pipelines with the top
02010   //  three rows of the image.
02011   //
02012   gevd_float_operators::ShrinkBy2AlongX_D(from, from.GetSizeX(), sizeX, 0,
02013                                           kernel, no_value, yline[2], wline[2]);
02014   gevd_float_operators::ShrinkBy2AlongX_D(from, from.GetSizeX(), sizeX, 1,
02015                                           kernel, no_value, yline[3], wline[3]);
02016   gevd_float_operators::ShrinkBy2AlongX_D(from, from.GetSizeX(), sizeX, 2,
02017                                           kernel, no_value, yline[4], wline[4]);
02018   int p = 3;
02019 
02020   //  Do the actual weighted smoothing and subsampling.  Only record a
02021   //  value at a pixel if the summed weights are above 0.5.  After all
02022   //  subsampled values are created in a row, shift the pipeline down.
02023   //
02024   for (int y=0; y<sizeY; y++ )
02025   {
02026 #ifdef DEBUG
02027     vcl_cout << "\nNew row:  y= " << y << "\nHere are the pipes.\n";
02028     PrintAllPipes( yline, wline, sizeX );
02029 #endif
02030     for (int x=0; x<sizeX; x++) {
02031       float sum_w = 0, sum_z = 0;
02032       for (int i=0; i<5; i++ ) {
02033         sum_w += kernel[i] * wline[i][x];
02034         sum_z += kernel[i] * yline[i][x];
02035       }
02036 #ifdef DEBUG
02037       vcl_cout << "Assigning:  x,y = " << x << ", " << y << "  ";
02038 #endif
02039       floatPixel(*to, x, y) = sum_w < 0.5 ? no_value : sum_z / sum_w;
02040 #ifdef DEBUG
02041       vcl_cout << ",  sum_w = " << sum_w << ",  value = " << floatPixel(*to, x, y) << vcl_endl;
02042 #endif
02043     }
02044 
02045     //  Shift the pipeline down by two rows.  Where it overlaps the
02046     //  bottom of the image use empty values.
02047     //
02048     float* prev_y0 = yline[0];
02049     float* prev_y1 = yline[1];
02050     float* prev_w0 = wline[0];
02051     float* prev_w1 = wline[1];
02052     for (int i=0; i<3; i++ ) {
02053       yline[i] = yline[i+2];
02054       wline[i] = wline[i+2];
02055     }
02056     yline[3] = prev_y0;
02057     wline[3] = prev_w0;
02058     yline[4] = prev_y1;
02059     wline[4] = prev_w1;
02060     if ( p < from.GetSizeY() )
02061       ShrinkBy2AlongX_D(from, from.GetSizeX(), sizeX, p++, kernel,
02062                         no_value, yline[3], wline[3] );
02063     else {
02064       vcl_memcpy(yline[3], y_empty, sizeX*sizeof(float));
02065       vcl_memcpy(wline[3], w_empty, sizeX*sizeof(float));
02066     }
02067     if ( p < from.GetSizeY() )
02068       ShrinkBy2AlongX_D(from, from.GetSizeX(), sizeX, p++, kernel,
02069                         no_value, yline[4], wline[4] );
02070     else {
02071       vcl_memcpy(yline[4], y_empty, sizeX*sizeof(float));
02072       vcl_memcpy(wline[4], w_empty, sizeX*sizeof(float));
02073     }
02074   }
02075 
02076   //  Delete the scratch memory.
02077   for (int i=0; i<5; i++ ) {
02078     delete[] yline[i];
02079     delete[] wline[i];
02080   }
02081   delete[] y_empty;
02082   delete[] w_empty;
02083   if (overwrite) { gevd_float_operators::Update(*t,*to); delete to; to = t; }
02084 }
02085 
02086 void
02087 PrintPipe( float values[] )
02088 {
02089   for (int i=0; i<4; i++ ) vcl_cout << values[i] << ", ";
02090   vcl_cout << values[4];
02091 }
02092 
02093 //:
02094 // Create a smoothed and subsampled array of x values in the given
02095 // row.  This will return the weighted (but unnormalized) values (in
02096 // yline) and the weights (wline).
02097 //
02098 void
02099 gevd_float_operators::ShrinkBy2AlongX_D(const gevd_bufferxy& from,
02100                                         int from_sizeX,
02101                                         int sizeX,
02102                                         int y,
02103                                         float kernel[],
02104                                         float no_value,
02105                                         float* yline,
02106                                         float* wline )
02107 {
02108 #ifdef DEBUG
02109   vcl_cout << "\nIn gevd_float_operators::ShrinkBy2AlongX_D:\n";
02110 
02111   vcl_cout << "Here is the original data:\n";
02112   for (int xx=0; xx<from.GetSizeX(); xx++ )
02113     vcl_cout << xx << ":  " << floatPixel( from, xx, y ) << vcl_endl;
02114 #endif
02115 
02116   // setup pipeline of 5 x values
02117   float xs[5];
02118   xs[0] = no_value;
02119   xs[1] = no_value;
02120   xs[2] = floatPixel( from, 0, y );
02121   xs[3] = floatPixel( from, 1, y );
02122   xs[4] = floatPixel( from, 2, y );
02123   int p = 3;
02124 
02125   for (int x = 0; x < sizeX; x++ ) {
02126 #ifdef DEBUG
02127     vcl_cout << "Data pipe:  ";  PrintPipe( xs ); vcl_cout << vcl_endl;
02128 #endif
02129     wline[x] = yline[x] = 0.0;
02130     for (int i = 0; i<5; i++ ) {
02131       if ( xs[i] != no_value ) {
02132         wline[x] += kernel[i];
02133         yline[x] += kernel[i] * xs[i];
02134       }
02135     }
02136 #ifdef DEBUG
02137     vcl_cout << "x = " << x << ",  yline[x] = " << yline[x]
02138              << ", wline[x] = " << wline[x] << vcl_endl;
02139 #endif
02140     for (int i=0; i<3; i++ ) xs[i] = xs[i+2];
02141     xs[3] = (p < from_sizeX) ? floatPixel(from, p++, y) : no_value;
02142     xs[4] = (p < from_sizeX) ? floatPixel(from, p++, y) : no_value;
02143   }
02144 }
02145 
02146 
02147 //: Expands the \a from image by 2 and store into \a to image, using Burt-Adelson expansion algorithm.
02148 // Convolution with a 5-point kernel [(0.5-ka), 0.5, 2*ka, 0.5, (0.5-ka)]
02149 // ka = 0.6  maximum decorrelation, low-pass filter for image compression.
02150 // ka = 0.5  linear interpolation,
02151 // ka = 0.4  Gaussian filter, smoothing at tail.
02152 // ka = 0.3  wider than Gaussian, for more smoothing.
02153 // The image indexes are mapped with: to.ij = from.ij * 2
02154 // The image sizes are related by: to.size = from.size * 2.
02155 //
02156 // Special care is taken when \a to and \a from are identical - Peter Vanroose.
02157 
02158 float
02159 gevd_float_operators::ExpandBy2(const gevd_bufferxy& from, gevd_bufferxy*& to,
02160                                 const float burt_ka)
02161 {
02162   const int sizeX = 2 * from.GetSizeX();
02163   const int sizeY = 2 * from.GetSizeY();
02164   bool overwrite = to == &from;
02165   gevd_bufferxy*& t = to;
02166   if (overwrite) to=0;
02167   to = gevd_float_operators::Allocate(to, from, 0, sizeX, sizeY);
02168   const float ka = burt_ka * 2;
02169   const float kb = 0.5f;
02170   const float kc = 0.5f - burt_ka;
02171   int p = 0;                            // pipeline of 3 lines
02172   float* yline0 = new float[sizeX];     // to cache ExpandBy2AlongX
02173   float* yline1 = new float[sizeX];
02174   float* yline2 = new float[sizeX];
02175   gevd_float_operators::ExpandBy2AlongX(from, p++, yline1, sizeX, ka, kb, kc);
02176   gevd_float_operators::ExpandBy2AlongX(from, p++, yline2, sizeX, ka, kb, kc);
02177   vcl_memcpy(yline0, yline2, sizeX*sizeof(float));// first line is wrapped
02178 
02179   // Convolve and expand along x-axis.
02180   for (int y = 0; y < sizeY; y += 2) {
02181     int yy = y+1;
02182     for (int x = 0; x < sizeX; x++) {
02183       floatPixel(*to, x, y) = (ka * yline1[x] +     // even index
02184                                kc * (yline0[x] + yline2[x]));
02185       floatPixel(*to, x, yy) = kb * (yline1[x] + yline2[x]); // odd index
02186     }
02187     float* next = yline0;
02188     yline0 = yline1;
02189     yline1 = yline2;
02190     yline2 = next;
02191     if (y < sizeY-4)
02192       gevd_float_operators::ExpandBy2AlongX(from, p++, next, sizeX, ka, kb, kc);
02193     else                        // last line is wrapped
02194       vcl_memcpy(next, yline0, sizeX*sizeof(float));
02195   }
02196   delete[] yline0;
02197   delete[] yline1;
02198   delete[] yline2;
02199   if (overwrite) { gevd_float_operators::Update(*t,*to); delete to; to = t; }
02200   return 1;
02201 }
02202 
02203 //: Expands the yline by 2 along the x-axis.
02204 // Interpolation is done by convolution with pixels at integral indexes only.
02205 
02206 float
02207 gevd_float_operators::ExpandBy2AlongX(const gevd_bufferxy& from, const int y,
02208                                       float* yline, const int sizeX,
02209                                       const float ka, const float kb,
02210                                       const float kc)
02211 {
02212   int p = 0;
02213   float x1 = floatPixel(from, p++, y);  // setup pipeline of values
02214   float x2 = floatPixel(from, p++, y);
02215   float x0 = x2;                // wrap at border
02216   for (int x = 0; x < sizeX; x += 2) {
02217     yline[x] = ka * x1 + kc * (x0 + x2); // even index
02218     yline[x+1] = kb * (x1 + x2); // odd index
02219     x0 = x1;
02220     x1 = x2;
02221     if (x < sizeX-4)
02222       x2 = floatPixel(from, p++, y);
02223     else                        // wrap at border
02224       x2 = x0;
02225   }
02226   return 1;
02227 }
02228 
02229 
02230 //:
02231 // Compute the pyramid by shrinking data sequence
02232 // by 2, nlevels-1 times, and return final shrunk length,
02233 // and reset number of levels in pyramid.
02234 // O(n) time.
02235 // Coarse to fine is stored from left to right.
02236 // The left and right trim borders are set to 0.
02237 
02238 int
02239 gevd_float_operators::Pyramid(const float* data, const int length,
02240                               float*& pyramid, int& nlevels, int trim,
02241                               const float burt_ka)
02242 {
02243   const int MINLENGTH = 16;     // coarsest length for reliable correlation
02244   if (!pyramid)
02245     pyramid = new float[length << 1]; // allocate or reuse space
02246   int i, ii;
02247   for (ii = 0; ii < length; ii++) // avoid boundary conditions
02248     pyramid[ii] = 0;
02249   for (i = 0; i < trim; i++, ii++) // trim left border to 0.
02250     pyramid[ii] = 0;
02251   for ( ; i < length-trim; i++, ii++) // level = 0
02252     pyramid[ii] = data[i];
02253   for ( ; i < length; i++, ii++) // trim right border to 0.
02254     pyramid[ii] = 0;
02255   int l, len1, len2;
02256   float *from, *to;             // pointers
02257   for (l = 1, len1 = length, len2 = length >> 1;
02258        l < nlevels && len2 > MINLENGTH; l++) {
02259     from = pyramid + len1, to = pyramid + len2;
02260     len1 = ShrinkBy2(from, len1, to, burt_ka);
02261     len2 = len1 >> 1;
02262   }
02263   nlevels = l;
02264   return len1;
02265 }
02266 
02267 
02268 int
02269 gevd_float_operators::ShrinkBy2(const float* from, const int length,
02270                                 float*& to, const float burt_ka)
02271 {
02272   const int slength = length >> 1;
02273   if (!to) to = new float[slength];    // allocate or reuse space
02274   const float ka = burt_ka;
02275   const float kb = 0.25f;
02276   const float kc = (0.5f - burt_ka) / 2;
02277   int p = 0;                            // setup pipeline of 5 x values
02278   float x0 = from[p++];
02279   float x1 = from[p++];
02280   float x2 = from[p++];
02281   float x3 = from[p++];
02282   float x4 = from[p++];
02283   to[0] = ka * x0 + 2 * (kb * x1 + kc * x2);    // reflect at border
02284   for (int x = 1; x <  slength; x++) {
02285     to[x] = (ka * x2 +          // shrink_by_2 along x axis
02286              kb * (x1 + x3) +
02287              kc * (x0 + x4));
02288       x0 = x2;                                  // shift by 2
02289       x1 = x3;
02290       x2 = x4;
02291     if (x < slength-2) {
02292       x3 = from[p++];
02293       x4 = from[p++];
02294     }
02295     else {
02296       x3 = x1;
02297       x4 = x0;
02298     }
02299   }
02300   return slength;
02301 }
02302 
02303 
02304 static float haar2 [] =
02305 {                                               // Haar wavelet
02306   0.7071067811865f,                             // 1/sqrt(2.0)
02307   0.7071067811865f,
02308   0.f
02309 };
02310 
02311 static float daubechies4 [] =
02312 {
02313   0.4829629131445341f,                           // Daubechies wavelet
02314   0.8365163037378079f,
02315   0.2241438680420134f,
02316  -0.1294095225512604f,
02317   0
02318 };
02319 
02320 static float daubechies6 [] =
02321 {
02322   0.3326705529500825f,
02323   0.8068915093110924f,
02324   0.4598775021184914f,
02325  -0.1350110200102546f,
02326  -0.0854412738820267f,
02327   0.0352262918857095f,
02328   0
02329 };
02330 
02331 static float daubechies8 [] =
02332 {
02333   0.2303778133088964f,
02334   0.7148465705529154f,
02335   0.6308807679398587f,
02336  -0.0279837694168599f,
02337  -0.1870348117190931f,
02338   0.0308413818355607f,
02339   0.0328830116668852f,
02340  -0.0105974017850690f,
02341   0
02342 };
02343 
02344 static float daubechies10 [] =
02345 {
02346   0.1601023979741929f,
02347   0.6038292697971895f,
02348   0.7243085284377726f,
02349   0.1384281459013203f,
02350  -0.2422948870663823f,
02351  -0.0322448695846381f,
02352   0.0775714938400459f,
02353  -0.0062414902127983f,
02354  -0.0125807519990820f,
02355   0.0033357252854738f,
02356   0
02357 };
02358 
02359 static float daubechies12 [] =
02360 {
02361   0.111540743350f,
02362   0.494623890398f,
02363   0.751133908021f,
02364   0.315250351709f,
02365  -0.226264693965f,
02366  -0.129766867567f,
02367   0.097501605587f,
02368   0.027522865530f,
02369  -0.031582039318f,
02370   0.000553842201f,
02371   0.004777257511f,
02372  -0.001077301085f,
02373   0
02374 };
02375 
02376 static float daubechies20 [] =
02377 {
02378   0.026670057901f,
02379   0.188176800078f,
02380   0.527201188932f,
02381   0.688459039454f,
02382   0.281172343661f,
02383  -0.249846424327f,
02384  -0.195946274377f,
02385   0.127369340336f,
02386   0.093057364604f,
02387  -0.071394147166f,
02388  -0.029457536822f,
02389   0.033212674059f,
02390   0.003606553567f,
02391  -0.010733175483f,
02392   0.001395351747f,
02393   0.001992405295f,
02394  -0.000685856695f,
02395  -0.000116466855f,
02396   0.000093588670f,
02397  -0.000013264203f,
02398   0
02399 };
02400 
02401 static float coifman9 [] =
02402 {
02403   0.019849565349356158f,                        // ***double-check as Coifman
02404  -0.04309091740554781f,                         // wavelets ****
02405  -0.05188793647806494f,
02406   0.2932311998087948f,
02407   0.5637961774509238f,
02408   0.2932311998087948f,
02409  -0.05188793647806494f,
02410  -0.04309091740554781f,
02411   0.019849565349356158f,
02412   0
02413 };
02414 
02415 static float coifman11 [] =
02416 {
02417   0.007987761489921101f,
02418   0.02011649866148413f,
02419  -0.05015758257647976f,
02420  -0.12422330961337678f,
02421   0.29216982108655865f,
02422   0.7082136219037853f,
02423   0.29216982108655865f,
02424  -0.12422330961337678f,
02425  -0.05015758257647976f,
02426   0.02011649866148413f,
02427   0.007987761489921101f,
02428   0
02429 };
02430 
02431 static float coifman15 [] =
02432 {
02433  -0.0012475221f,
02434  -0.0024950907f,
02435   0.0087309530f,
02436   0.0199579580f,
02437  -0.0505290000f,
02438  -0.1205509700f,
02439   0.2930455800f,
02440   0.7061761600f,
02441   0.2930455800f,
02442  -0.1205509700f,
02443  -0.0505290000f,
02444   0.0199579580f,
02445   0.0087309530f,
02446  -0.0024950907f,
02447  -0.0012475221f,
02448   0
02449 };
02450 
02451 static float dual_wavelet [20] = {0};
02452 
02453 //: Looks up and stores the wavelet pair in (\a lo_filter, \a hi_filter).
02454 // The wavelet number is 2 for Haar wavelet, 4-20 even numbers for Daubechies
02455 // wavelet, and 9-15 odd numbers for symmetric Coifman wavelets.
02456 
02457 bool
02458 gevd_float_operators::FindWavelet(const int waveletno,
02459                                   float*& lo_filter, float*& hi_filter, int& ncof)
02460 {
02461   ncof = waveletno;
02462   switch (waveletno)
02463   {
02464    case 2:
02465     lo_filter = haar2;
02466     break;
02467    case 4:
02468     lo_filter = daubechies4;
02469     break;
02470    case 6:
02471     lo_filter = daubechies6;
02472     break;
02473    case 8:
02474     lo_filter = daubechies8;
02475     break;
02476    case 10:
02477     lo_filter = daubechies10;
02478     break;
02479    case 12:
02480     lo_filter = daubechies12;
02481     break;
02482    case 20:
02483     lo_filter = daubechies20;
02484     break;
02485    case 9:                                       // wavelets from epic
02486     lo_filter = coifman9;
02487     break;
02488    case 11:
02489     lo_filter = coifman11;
02490     break;
02491    case 15:
02492     lo_filter = coifman15;
02493     break;
02494    default:
02495     ncof = 0;
02496     lo_filter = hi_filter = NULL;
02497     vcl_cerr << "Unknown wavelet: " << waveletno << vcl_endl;
02498     return false;
02499   }
02500   // find hi-filter wavelet, dual of the lo-filter wavelet
02501   if (lo_filter)
02502   {
02503     hi_filter = dual_wavelet;
02504     if ((waveletno%2) == 0) {
02505       int sign = -1;                            // reverse and
02506       for (int k = 0; k < ncof; k++) {          // flip sign on odd coefts
02507         hi_filter[ncof-1-k] = sign * lo_filter[k];
02508         sign = - sign;
02509       }
02510     }
02511     else {                                    // odd filter is symmetric
02512       int sign = 1;
02513       int ctr = ncof/2;                         // flip sign on odd coefts
02514       for (int k = 0; k <= ncof/2; k++) {       // starting from center
02515         hi_filter[ctr+k] = sign * lo_filter[ctr-k];
02516         hi_filter[ctr-k] = sign * lo_filter[ctr+k];
02517         sign = - sign;
02518       }
02519       vcl_cerr << "Scale factor need to be fixed up too!!!\n";
02520     }
02521     // find area of lo_filter and hi_filter
02522     float lo_area = 0;
02523     float hi_area = 0;
02524     for (int k = 0; k < ncof; k++) {
02525       lo_area += lo_filter[k];
02526       hi_area += hi_filter[k];
02527     }
02528     lo_filter[ncof] = lo_area;
02529     hi_filter[ncof] = hi_area;
02530   }
02531 #ifdef DEBUG
02532   vcl_cout << "lo-filter wavelet " << waveletno << ':'; // print wavelets
02533   for (int i = 0; i < ncof; i++)
02534     vcl_cout << ' ' << lo_filter[i];
02535   vcl_cout << "\nhi-filter wavelet " << waveletno << ':';
02536   for (int i = 0; i < ncof; i++)
02537     vcl_cout << ' ' << hi_filter[i];
02538   vcl_cout << vcl_endl;
02539 #endif
02540   return true;
02541 }
02542 
02543 //:
02544 // Convolution with wavelets (\a lo_filter, \a hi_filter) and gather results into
02545 // consecutive blocks, with convolution of \a lo_filter (resp. \a hi_filter)
02546 // on the sides of low (resp. high) indices.
02547 // Wrap around edges of the array is done with modulo(n) replaced by
02548 // bit masking with \a n-1, when \a n is a power of 2.
02549 // Assumes n >= 4.
02550 
02551 void
02552 gevd_float_operators::WaveletTransformStep(float* array, const int n,
02553                                            const bool forwardp,
02554                                            const float* lo_filter,
02555                                            const float* hi_filter,
02556                                            const int ncof,
02557                                            float* wksp)
02558 {
02559   for (int j = 0; j < n; j++)
02560     wksp[j] = 0;                                // clear workspace
02561   int nmid = n / 2;                             // round off towards 0
02562   int nmod = nmid * 2;                          // even and <= n.
02563   if (forwardp) {                               // forward transform
02564     for (int i=0, ii=0; i < nmod; i +=2, ++ii)  // every pair
02565       for (int k = 0; k < ncof; k++) {          // convolution with filters
02566         int j = (i + k) % nmod;                 // wrap around
02567         // int j = (i + k) & (nmod - 1);        // when n is power of 2
02568         wksp[ii] += lo_filter[k] * array[j];    // lo-filter results
02569         wksp[ii+nmid] += hi_filter[k] * array[j]; // hi-filter results
02570       }
02571     float scale = vcl_max(lo_filter[ncof], hi_filter[ncof]);
02572     for (int j = 0; j < nmod; j++)             // normalize results.
02573       wksp[j] /= scale;
02574   }
02575   else {                                      // inverse transform
02576     for (int i=0, ii=0; i < nmod; i+=2, ++ii) { // every pair
02577       float lo = array[ii];
02578       float hi = array[ii+nmid];
02579       for (int k = 0; k < ncof; k++) {          // multiplication with inverse
02580         int j = (i + k) % nmod;                 // matrix, or its transpose
02581         // int j = (i + k) & (nmod - 1);        // when n is power of 2
02582         wksp[j] += (lo_filter[k] * lo +
02583                     hi_filter[k] * hi);
02584       }
02585     }
02586     float scale = vcl_max(lo_filter[ncof], hi_filter[ncof]);
02587     for (int j = 0; j < nmod; j++)              // unnormalize results.
02588       wksp[j] *= scale;
02589   }
02590   for (int j = 0; j < nmod; j++)                // copy only nmod results
02591     array[j] = wksp[j];                         // back to array
02592 }
02593 
02594 
02595 //: Compute the forward/inverse wavelet transform of a 1d array of data.
02596 // Convolution with low (resp. high) filter is stored on the side
02597 // of low (resp. high) indices.
02598 
02599 bool
02600 gevd_float_operators::WaveletTransform(float* array, const int n,
02601                                        const bool forwardp, int nlevels,
02602                                        const int waveletno)
02603 {
02604   if (n >= 4) {
02605     float* lo_filter = NULL;
02606     float* hi_filter = NULL;
02607     int ncof = 0;
02608     FindWavelet(waveletno, lo_filter, hi_filter, ncof);
02609 #ifdef DEBUG
02610     vcl_cout << "Input:";
02611     for (int i = 0; i < n; i++)
02612       vcl_cout << ' ' << array[i];
02613     vcl_cout << vcl_endl;
02614 #endif
02615 
02616     float* wksp = new float[n];
02617     if (forwardp) {                             // forward transform
02618       for (int nn = n; nn >= 4 && nlevels > 0; nn /= 2, nlevels--)
02619         WaveletTransformStep(array, nn, forwardp,
02620                              lo_filter, hi_filter, ncof,
02621                              wksp);
02622     }
02623     else {                                    // inverse transform
02624       const int sz = int(vcl_log(double(n))/vcl_log(2.0));
02625       int* sizes = new int[sz];
02626       int s = 0;
02627       for (int nn = n; nn >= 4 && nlevels > 0; nn /= 2, nlevels--, s++)
02628         sizes[s] = nn;                          // reverse sequence of sizes nn
02629       for (s--; s >= 0; s--)                    // to undo forward transform
02630         WaveletTransformStep(array, sizes[s], forwardp,
02631                              lo_filter, hi_filter, ncof,
02632                              wksp);
02633       delete[] sizes;
02634     }
02635     delete[] wksp;
02636 #ifdef DEBUG
02637     vcl_cout << "Output:";
02638     for (int i = 0; i < n; i++)
02639       vcl_cout << ' ' << array[i];
02640     vcl_cout << vcl_endl;
02641 #endif
02642   }
02643   return true;
02644 }
02645 
02646 //: Find pyramid of low/high pass components, using wavelet transform.
02647 
02648 void
02649 gevd_float_operators::LowHighPyramid(float* highPass, float* lowPass,
02650                                      int n, int nlevels,
02651                                      const float* lo_filter,
02652                                      const float* hi_filter,
02653                                      int ncof,
02654                                      float* wksp)
02655 {
02656   const bool forwardp = true;   // use forward wavelet transform
02657   int nn = n;
02658   for (int l = nlevels;         // to find pyramid
02659        nn >= 4 && l > 0; nn /= 2, l--) {
02660     gevd_float_operators::WaveletTransformStep(highPass, nn, forwardp,
02661                                                lo_filter, hi_filter, ncof,
02662                                                wksp);
02663     int m = nn / 2;
02664     float* lows = lowPass + m;
02665     for (int j = 0; j < m; j++)         // copy over the low pass components
02666       lows[j] = highPass[j];            // for pyramid
02667     if ((nn % 2) == 1) {                // delete boundary effects
02668       int bound = nn-1;                 // because of odd size
02669       highPass[bound] = highPass[bound-1];
02670       lowPass[bound] = lowPass[bound-1];
02671     }
02672   }
02673   const float LO = lowPass[nn];
02674   for (int j = 0; j < nn; j++) {
02675     highPass[j] = 0;
02676     lowPass[j] = LO;
02677   }
02678 }
02679 
02680 
02681 //: n-dimensional wavelet transform of an n-dimensional array.
02682 // Convolution with low (resp. high) filter is stored on the side
02683 // of low (resp. high) indices.
02684 // Assumes data are stored consecutively with right-most index varying
02685 // fastest, consistent with convention in C m(i,j) = m[i][j].
02686 // This version does a 1d-FWT recursively on each dimension,
02687 // rather than an nd-FWT on each recursive nd sub-block that are convolved
02688 // with lo-filters.
02689 
02690 bool
02691 gevd_float_operators::WaveletTransformByIndex(float* array,
02692                                               const int* dims, const int ndim,
02693                                               const bool forwardp, int nlevels,
02694                                               const int waveletno)
02695 {
02696   int ntot = 1, maxn = 0;
02697   {for (int d = 0; d < ndim; d++) {
02698     int dim = dims[d];
02699     ntot *= dim;
02700     if (dim > maxn) maxn = dim;
02701   }}
02702   float* lo_filter = NULL;
02703   float* hi_filter = NULL;
02704   int ncof = 0;
02705   if (!FindWavelet(waveletno, lo_filter, hi_filter, ncof)) // look up wavelets
02706     return false;
02707   float* buffer = new float[maxn];              // working buffers for
02708   float* wksp = new float[maxn];                // 1d wavelet transformation
02709   const int sz = int(vcl_log(double(maxn))/vcl_log(2.0));
02710   int* sizes = new int[sz];                     // cache sizes in pyramid
02711 
02712 #ifdef DEBUG
02713   vcl_cout << "Input:";
02714   for (int i = 0; i < ntot; i++)
02715     vcl_cout << ' ' << array[i];
02716   vcl_cout << vcl_endl;
02717 #endif
02718 
02719   int nprev = 1;
02720   for (int d = ndim-1; d >= 0; d--) {           // dimension varies most, first
02721     int n = dims[d];
02722     int nnew = n * nprev;
02723     if (n >= 4)
02724     {
02725       for (int i2 = 0; i2 < ntot; i2 += nnew)
02726         for (int i1 = 0; i1 < nprev; i1++)
02727         {
02728           int i3 = i1 + i2;
02729           for (int k = 0; k < n; k++) {
02730             buffer[k] = array[i3];              // copy data to convolve
02731             i3 += nprev;
02732           }
02733           if (forwardp) {                       // forward 1d transform
02734             for (int nn = n, l = nlevels;
02735                  nn >= 4 && l > 0;
02736                  nn /= 2, l--)
02737               WaveletTransformStep(buffer, nn, forwardp,
02738                                    lo_filter, hi_filter, ncof,
02739                                    wksp);
02740           }
02741           else {                              // inverse 1d transform
02742             int s = 0;
02743             for (int nn = n, l = nlevels;
02744                  nn >= 4 && l > 0;
02745                  nn /= 2, l--, s++)
02746               sizes[s] = nn;                    // reverse sizes to undo
02747             for (s--; s >= 0; s--)              // forward transform
02748               WaveletTransformStep(buffer, sizes[s], forwardp,
02749                                    lo_filter, hi_filter, ncof,
02750                                    wksp);
02751           }
02752           i3 = i1 + i2;                         // copy back results
02753           for (int k = 0; k < n; k++) {
02754             array[i3] = buffer[k];
02755             i3 += nprev;
02756           }
02757         }
02758     }
02759     nprev = nnew;
02760   }
02761   delete[] buffer;                             // free working arrays
02762   delete[] wksp;
02763   delete[] sizes;
02764 #ifdef DEBUG
02765   vcl_cout << "Output:";
02766   for (int i = 0; i < ntot; i++)
02767     vcl_cout << ' ' << array[i];
02768   vcl_cout << vcl_endl;
02769 #endif
02770   return true;
02771 }
02772 
02773 // one step of n-dimensional wavelet transform of an n-dimensional array.
02774 
02775 void
02776 gevd_float_operators::WaveletTransformStep(float* array,
02777                                            const int* dims, const int ndim,
02778                                            const bool forwardp,
02779                                            const float* lo_filter,
02780                                            const float* hi_filter,
02781                                            const int ncof,
02782                                            float* buffer, float* wksp)
02783 {
02784   int ntot = dims[ndim];
02785   int nprev = 1;
02786   for (int d = ndim-1; d >= 0; d--) {   // dimension varies most, first
02787     int n = dims[d];
02788     int nnew = n * nprev;
02789     if (n >= 4) {
02790       for (int i2 = 0; i2 < ntot; i2 += nnew)
02791         for (int i1 = 0; i1 < nprev; i1++) {
02792           int i3 = i1 + i2;
02793           {for (int k = 0; k < n; k++) {
02794             buffer[k] = array[i3];              // copy data to convolve
02795             i3 += nprev;
02796           }}
02797           WaveletTransformStep(buffer, n, forwardp,
02798                                lo_filter, hi_filter, ncof,
02799                                wksp);
02800           i3 = i1 + i2;                         // copy back results
02801           for (int k = 0; k < n; k++) {
02802             array[i3] = buffer[k];
02803             i3 += nprev;
02804           }
02805         }
02806     }
02807     nprev = nnew;
02808   }
02809 }
02810 
02811 
02812 //: Recursively copies elements of n-dimensional arrays.
02813 // Extra elements that can not fit in both arrays are not
02814 // read or written. A flag fullp (default = true) is used to copy from/to
02815 // a full n-dimensional array or only a sub-block of the array.
02816 
02817 void
02818 gevd_float_operators::CopyNdRecursive(const float* from_array,
02819                                       const int from_size, const int* from_dims,
02820                                       float* to_array,
02821                                       const int to_size, const int* to_dims,
02822                                       const int ndim,
02823                                       const bool fullp)
02824 {
02825   if (ndim == 1) {                              // end of recursion
02826     int size = vcl_min(from_size, to_size);
02827     for (int i = 0; i < size; i++)              // copy 1d array for
02828       to_array[i] = from_array[i];              // common indices only
02829   }
02830   else {
02831     int from_n = from_dims[0], to_n = to_dims[0];
02832     int from_nsize = from_size / from_n;
02833     int to_nsize = to_size / to_n;
02834     int n = vcl_min(from_n, to_n);
02835     for (int i = 0; i < n; i++) {               // copy n common subarrays
02836       CopyNdRecursive(from_array, from_nsize, from_dims+1,
02837                       to_array, to_nsize, to_dims+1,
02838                       ndim-1, fullp);
02839       if (fullp) {
02840         from_array += from_nsize;
02841         to_array += to_nsize;
02842       }
02843       else {
02844         int block_size = vcl_max(from_nsize, to_nsize);
02845         from_array += block_size;               // inc pointer of arrays
02846         to_array += block_size;                 // to next block
02847       }
02848     }
02849   }
02850 }
02851 
02852 
02853 //: n-dimensional wavelet transform of an n-dimensional array.
02854 // Convolution with low (resp. high) filter is stored on the side
02855 // of low (resp. high) indices.
02856 // Assumes data are stored consecutively with right-most index varying
02857 // fastest, consistent with convention in C m(i,j) = m[i][j].
02858 // This version decomposes only the n-dimensional sub-block of
02859 // low frequency convolutions. The wavelet are self-similar in
02860 // n-dimensional space, rather than elongated.
02861 
02862 bool
02863 gevd_float_operators::WaveletTransformByBlock(float* array,
02864                                               const int* dims, const int ndim,
02865                                               const bool forwardp, int nlevels,
02866                                               const int waveletno)
02867 {
02868   int ntot = 1, maxn = 0;
02869   {for (int d = 0; d < ndim; d++) {
02870     int dim = dims[d];
02871     ntot *= dim;
02872     if (dim > maxn) maxn = dim;
02873   }}
02874 
02875   float* lo_filter = NULL;
02876   float* hi_filter = NULL;
02877   int ncof = 0;
02878   if (!FindWavelet(waveletno, lo_filter, hi_filter, ncof)) // look up wavelets
02879     return false;
02880 
02881   int** level_dims = new int*[nlevels];
02882   level_dims[0] =  new int[ndim+1];
02883   for (int d = 0; d < ndim; d++)
02884     level_dims[0][d] = dims[d];
02885   level_dims[0][ndim] = ntot;
02886   for (int l = 1; l < nlevels; l++) {
02887     level_dims[l] = new int[ndim+1];
02888     int n = 1;
02889     for (int d = 0; d < ndim; d++) {
02890       int nd = level_dims[l-1][d];
02891       if (nd >= 4)
02892         nd /= 2;
02893       level_dims[l][d] = nd;
02894       n *= nd;
02895     }
02896     level_dims[l][ndim] = n;
02897   }
02898   if (!forwardp) {                              // reverse order of the
02899     int * swap;                                 // dimensions of sub_array
02900     for (int l = 0; l < nlevels/2; l++) {
02901       swap = level_dims[l];
02902       level_dims[l] = level_dims[nlevels-1-l];
02903       level_dims[nlevels-1-l] = swap;
02904     }
02905   }
02906   float* sub_array = new float[ntot];           // sub-block of low frequency
02907   float* buffer = new float[maxn];              // working buffers for
02908   float* wksp = new float[maxn];                // 1d wavelet transformation
02909   {for (int l = 0; l < nlevels; l++) {
02910     int* sub_dims = level_dims[l];
02911     int n = sub_dims[ndim];
02912     CopyNdRecursive(array, ntot, dims,
02913                     sub_array, n, sub_dims,     // consecutive elements.
02914                     ndim);
02915     WaveletTransformStep(sub_array, sub_dims, ndim,
02916                          forwardp,
02917                          lo_filter, hi_filter, ncof,
02918                          buffer, wksp);
02919     CopyNdRecursive(sub_array, n, sub_dims,
02920                     array, ntot, dims,
02921                     ndim);
02922   }}
02923   delete[] sub_array;                          // free working arrays
02924   for (int l = 0; l < nlevels; l++)
02925     delete[] level_dims[l];
02926   delete[] level_dims;
02927   delete[] buffer;
02928   delete[] wksp;
02929   return true;
02930 }
02931 
02932 #if 0 // unimplemented - TODO
02933 int
02934 gevd_float_operators::DeleteMixedComponents
02935 (float* wave, // delete wavelet coefts
02936  const int* dims, const int ndim,
02937  const int nlevels)
02938 {
02939 }
02940 
02941 int
02942 gevd_float_operators::TruncateHighFrequencies
02943 (float* wave,
02944  const int* dims, const int ndim,
02945  const int nlevels,                  // throw all small
02946  const float threshold=0.1)          // components
02947 {
02948 }
02949 
02950 int
02951 gevd_float_operators::TruncateLowestFrequency
02952 (float* wave,
02953  const int* dims, const int ndim,
02954  const int nlevels,
02955  float& average)                     // uniform average
02956 {
02957 }
02958 #endif
02959 
02960 
02961 void
02962 gevd_float_operators::TestWavelets()
02963 {
02964 #if 0 // 1d testing commented out
02965   vcl_cout << "Testing wavelet transforms on 1d buffers\n";
02966   for (int n = 2; n <= 16; n++) {
02967     float* data = new float[n];
02968     for (int k = 2; k <= 12; k+=2) {
02969       for (int i = 0; i < n; i++)
02970         data[i] = i+1;
02971       wavelet_transform(data, n, true, k, 2);
02972       wavelet_transform(data, n, false, k, 2);
02973       float max_err = 0;
02974       for (int i = 0; i < n; i++) {
02975         float err = vcl_fabs(data[i] - i-1);
02976         if (err > max_err)
02977           max_err = err;
02978       }
02979       vcl_cout << "  |data| = " << n
02980                << "  |wavelet| = " << k
02981                << "  |error| = " << max_err << vcl_endl;
02982     }
02983     delete[] data;
02984   }
02985 #endif
02986 
02987   vcl_cout << "Testing wavelet transforms on nd buffers\n";
02988   for (int ndim = 1; ndim <= 4; ndim++)
02989     for (int s = 3; s <= 8; s++)
02990     {
02991       int* dims = new int[ndim+1];
02992       int ntot = 1;
02993       for (int d = 0; d < ndim; d++) {
02994         dims[d] = s;
02995         ntot *= s;
02996       }
02997       dims[ndim] = ntot;
02998       float* data = new float[ntot];
02999       for (int k = 2; k <= 12; k+=2)
03000       {
03001         for (int i = 0; i < ntot; i++) data[i] = float(i);
03002         int nlevels = 2;
03003         WaveletTransformByBlock(data, dims, ndim, true, nlevels, k);
03004         WaveletTransformByBlock(data, dims, ndim, false, nlevels, k);
03005         float max_err = 0;
03006         for (int i = 0; i < ntot; i++) {
03007           float err = (float)vcl_fabs(data[i] - i);
03008           if (err > max_err)
03009             max_err = err;
03010         }
03011         vcl_cout << "  |dims| = " << ndim
03012                  << "  |data| = " << ntot
03013                  << "  |wavelet| = " << k
03014                  << "  |error| = " << max_err << vcl_endl;
03015       }
03016       delete[] data;
03017       delete[] dims;
03018     }
03019 }
03020 
03021 
03022 //--------------------------------------------------------------------
03023 
03024 //: Compute the Fast Wavelet Transform of the 2d array.
03025 // Higher number wavelets are less compact, but give more accurate
03026 // decomposition of the image into linear combinations of mother wavelets.
03027 
03028 bool
03029 gevd_float_operators::WaveletTransformByIndex(const gevd_bufferxy& from, gevd_bufferxy*& to,
03030                                               const bool forwardp,    // or inverse
03031                                               int nlevels,
03032                                               const int waveletno)
03033 {
03034   to = gevd_float_operators::Allocate(to, from);
03035   int dims[3];
03036   dims[0] = to->GetSizeY();
03037   dims[1] = to->GetSizeX();
03038   dims[2] = dims[0] * dims[1];
03039   const float* from_array = (const float*) from.GetBuffer(); // copy the image
03040   float* to_array = (float*) to->GetBuffer();
03041   for (int i = 0; i < dims[2]; i++)
03042     to_array[i] = from_array[i];
03043   return gevd_float_operators::WaveletTransformByIndex(to_array, // transform in place
03044                                                        dims, 2,
03045                                                        forwardp, nlevels,
03046                                                        waveletno);
03047 }
03048 
03049 //: Compute the Fast Wavelet Transform of the image.
03050 // Higher number wavelets are less compact, but give more accurate
03051 // decomposition of the image into linear combinations of mother wavelets.
03052 // Image compression uses mother wavelets around 12, while image
03053 // segmentation uses compact wavelets around 2 or 4.
03054 
03055 bool
03056 gevd_float_operators::WaveletTransformByBlock(const gevd_bufferxy& from, gevd_bufferxy*& to,
03057                                               const bool forwardp,    // or inverse
03058                                               int nlevels,
03059                                               const int waveletno)
03060 {
03061   to = gevd_float_operators::Allocate(to, from);
03062   int dims[3];
03063   dims[0] = to->GetSizeY();
03064   dims[1] = to->GetSizeX();
03065   dims[2] = dims[0] * dims[1];
03066   const float* from_array = (const float*) from.GetBuffer(); // copy the image
03067   float* to_array = (float*) to->GetBuffer();
03068   for (int i = 0; i < dims[2]; i++)
03069     to_array[i] = from_array[i];
03070   return gevd_float_operators::WaveletTransformByBlock(to_array, // transform in place
03071                                                        dims, 2,
03072                                                        forwardp, nlevels,
03073                                                        waveletno);
03074 }
03075 
03076 //:
03077 // Delete all wavelet components, which have high frequency along both
03078 // x- and y- axes. They are diagonal blocks, except the lowest frequency block.
03079 // This will throw away high frequency point-like features.
03080 // Return the number of coefficients deleted.
03081 
03082 int
03083 gevd_float_operators::DeleteMixedComponents(gevd_bufferxy& wave,
03084                                             const int nlevels)
03085 {
03086   int sx = wave.GetSizeX(), sy = wave.GetSizeY(), count = 0;
03087   for (int l = 0; l < nlevels; l++) {
03088     int ssx = sx / 2, ssy = sy / 2;
03089     for (int y = ssy; y < sy; y++)
03090       for (int x = ssx; x < sx; x++)
03091         if (floatPixel(wave, x, y) != 0) {
03092           floatPixel(wave, x, y) = 0;
03093           count++;
03094         }
03095     sx = ssx; sy = ssy;
03096   }
03097   return count;
03098 }
03099 
03100 //:
03101 // Truncate to 0, all wavelet components with high frequency along
03102 // either x- or y- axes only, not both. The relative magnitude of the
03103 // components must be smaller than the given threshold.
03104 // This will simulate reduced accuracy in presence of transmission errors.
03105 // Return the number of coefficients deleted.
03106 
03107 int
03108 gevd_float_operators::TruncateHighFrequencies(gevd_bufferxy& wave,
03109                                               const int nlevels,
03110                                               const float threshold)
03111 {
03112   int sx = wave.GetSizeX(), sy = wave.GetSizeY(), count = 0;
03113   for (int l = 0; l < nlevels; l++) {
03114     int ssx = sx / 2, ssy = sy / 2;
03115     float low = gevd_float_operators::Maximum(wave, sx-ssx, ssy, ssx, 0) * threshold;
03116     for (int y = 0; y < ssy; y++)           // lowY x highX block
03117       for (int x = ssx; x < sx; x++)
03118         if (floatPixel(wave, x, y) < low) {
03119           floatPixel(wave, x, y) = 0;
03120           count++;
03121         }
03122     low = gevd_float_operators::Maximum(wave, ssx, sy-ssy, 0, ssy) * threshold;
03123     for (int y = ssy; y < sy; y++)          // highY x lowX block
03124       for (int x = 0; x < ssx; x++)
03125         if (floatPixel(wave, x, y) < low) {
03126           floatPixel(wave, x, y) = 0;
03127           count++;
03128         }
03129     sx = ssx; sy = ssy;
03130   }
03131   return count;
03132 }
03133 
03134 //: Truncate to average value, the lowest frequency component.
03135 // This will simulate throwing away the lowest frequencies in image.
03136 // Return the number of coefficients deleted.
03137 
03138 int
03139 gevd_float_operators::TruncateLowestFrequency(gevd_bufferxy& wave,
03140                                               const int nlevels)
03141 {
03142   int s = 1 << nlevels; // scaling factor
03143   int sx = wave.GetSizeX() / s, sy = wave.GetSizeY() / s; // size of block
03144   float average = gevd_float_operators::Sum(wave, sx, sy, 0, 0) / (sx * sy);
03145   int count = 0;
03146   for (int y = 0; y < sy; y++)
03147     for (int x = 0; x < sx; x++)
03148       if (floatPixel(wave, x, y) != average) {
03149         floatPixel(wave, x, y) = average;
03150         count++;
03151       }
03152   return count;                         // number of pixels changed
03153 }
03154 
03155 //:
03156 // Delete boundary artefacts in 1d-array, because its length is not
03157 // a power of 2, and so wrapping will skip the last odd element.
03158 
03159 int
03160 gevd_float_operators::DeleteBoundaryArtifacts(float* wave, const int n,
03161                                               const int nlevels)
03162 {
03163   if (nlevels == 0)
03164     return 0;
03165   else {
03166     int s = n, count = 0;
03167     for (int l = 0; l < nlevels; l++) {
03168       int ns = s / 2;                   // integer division rounds to
03169       int ss = ns * 2;                  // smaller value
03170       if (ss < s) {
03171         wave[ss] = 0;
03172         count += 1;
03173       }
03174       s = ns;
03175     }
03176     return count;
03177   }
03178 }
03179 
03180 //:
03181 // Delete boundary artefacts because the dimension of the image
03182 // is not a power of 2, and so wrapping will skip the last odd element.
03183 
03184 int
03185 gevd_float_operators::DeleteBoundaryArtifacts(gevd_bufferxy& wave, const int nlevels)
03186 {
03187   if (nlevels == 0)
03188     return 0;
03189   else {
03190     int sx = wave.GetSizeX(), sy = wave.GetSizeY(), count = 0;
03191     for (int l = 0; l < nlevels; l++) {
03192       int ssx = sx / 2, ssy = sy / 2;   // integer division rounds to
03193       int xx = ssx * 2, yy = ssy * 2;   // smaller value
03194       if (xx < sx) {
03195         for (int y = 0; y < sy; y++)
03196           floatPixel(wave, xx, y) = 0;
03197         count += sy;
03198       }
03199       if (yy < sy) {
03200         for (int x = 0; x < sx; x++)
03201           floatPixel(wave, x, yy) = 0;
03202         count += sx;
03203       }
03204       sx = ssx; sy = ssy;
03205     }
03206     return count;
03207   }
03208 }
03209 
03210 
03211 //: Project wavelet components onto the axes.
03212 
03213 void
03214 gevd_float_operators::ProjectWaveOntoX(const gevd_bufferxy& wave,
03215                                        float*& proj, const int nlevels)
03216 {
03217   if (nlevels == 0)
03218     gevd_float_operators::ProjectOntoX(wave, proj);
03219   else {
03220     int sx = wave.GetSizeX(), sy = wave.GetSizeY();
03221     for (int l = 0; l < nlevels; l++) {
03222       int ssx = sx / 2, ssy = sy / 2;
03223       gevd_float_operators::ProjectOntoX(wave, proj, sx-ssx, ssy, ssx, 0);
03224       sx = ssx; sy = ssy;
03225     }
03226   }
03227 }
03228 
03229 void
03230 gevd_float_operators::ProjectWaveOntoY(const gevd_bufferxy& wave, float*& proj,
03231                                        const int nlevels)
03232 {
03233   if (nlevels == 0)
03234     gevd_float_operators::ProjectOntoY(wave, proj);
03235   else {
03236     int sx = wave.GetSizeX(), sy = wave.GetSizeY();
03237     for (int l = 0; l < nlevels; l++) {
03238       int ssx = sx / 2, ssy = sy / 2;
03239       gevd_float_operators::ProjectOntoY(wave, proj, ssx, sy-ssy, 0, ssy);
03240       sx = ssx; sy = ssy;
03241     }
03242   }
03243 }
03244 
03245 
03246 //: Project the image data in ROI onto the x- and y- axes.
03247 // O(n*m).
03248 // The 1d-array is returned with projections being the sum
03249 // normalized by the number of elements projected.
03250 
03251 void
03252 gevd_float_operators::ProjectOntoX(const gevd_bufferxy& buf, float*& proj,
03253                                    int sizeX, int sizeY, int origX, int origY)
03254 {
03255   if (sizeX == 0)
03256     sizeX = buf.GetSizeX();
03257   if (sizeY == 0)
03258     sizeY = buf.GetSizeY();
03259   if (proj == NULL)
03260     proj = new float [buf.GetSizeX()];
03261   int hiX = origX + sizeX, hiY = origY + sizeY;
03262   for (int x = origX; x < hiX; x++) {   // projection onto the x-axis
03263     float projx = 0;
03264     for (int y = origY; y < hiY; y++)
03265       projx += floatPixel(buf, x, y);
03266     proj[x] = projx / sizeY;
03267   }
03268 }
03269 
03270 void
03271 gevd_float_operators::ProjectOntoY(const gevd_bufferxy& buf, float*& proj,
03272                                    int sizeX, int sizeY, int origX, int origY)
03273 {
03274   if (sizeX == 0)
03275     sizeX = buf.GetSizeX();
03276   if (sizeY == 0)
03277     sizeY = buf.GetSizeY();
03278   if (proj == NULL)
03279     proj = new float [buf.GetSizeY()];
03280   int hiX = origX + sizeX, hiY = origY + sizeY;
03281   for (int y = origY; y < hiY; y++) {   // projection onto the y-axis
03282     float projy = 0;
03283     for (int x = origX; x < hiX; x++)
03284       projy += floatPixel(buf, x, y);
03285     proj[y] = projy / sizeX;
03286   }
03287 }
03288 
03289 // Full correlation must be implemented with FFT.
03290 // Local correlation with limited search is faster with direct convolution.
03291 
03292 //:
03293 // Find correlation of given pattern to data, matching
03294 // pattern[radius+i] with data[index+i]. The linear correlation
03295 // coefficient, also called Pearson r, is computed, O(|pattern|).
03296 // Assumed pattern has length = 2*radius + 1.
03297 
03298 float
03299 gevd_float_operators::Correlation(const float* data, const int length,
03300                                   const float* pattern, const int radius,
03301                                   const int index)
03302 {
03303   int xmin = index-radius, xmax = index+radius+1; // pad boundary with 0
03304   int ymin = 0;                                   // or check for bounds
03305 
03306   if (xmin < 0) { ymin = -xmin; xmin = 0; }
03307   if (xmax > length) xmax = length;
03308   int sum1 = xmax - xmin;
03309   if (sum1 < radius)
03310     return 0;
03311   else {
03312     register double sumx=0, sumy=0, sumxx=0, sumyy=0, sumxy=0, xval, yval;
03313     for (register int x = xmin, y = ymin; x < xmax; x++, y++) {
03314       xval = data[x]; yval = pattern[y];
03315       sumxy += xval * yval;             // accumulate correlation value
03316       sumx += xval;
03317       sumy += yval;
03318       sumxx += xval * xval;
03319       sumyy += yval * yval;
03320     }
03321     double varx = sum1 * sumxx - sumx * sumx; // all multiplied with sum1
03322     double vary = sum1 * sumyy - sumy * sumy;
03323     double cvar = sum1 * sumxy - sumx * sumy;
03324     if (varx!=0 && vary!=0) cvar /= vcl_sqrt(varx * vary);
03325     return (float)cvar / (float)vcl_sqrt(varx * vary); // linear correlation coefficient
03326   }
03327 }
03328 
03329 
03330 //: Find correlations of given pattern to data, given maximum search from index.
03331 // O(|pattern|*shift).
03332 // Returns the array of correlation values, with positive translation starting
03333 // from result[search+1], and negative translation starting from result[search-1].
03334 
03335 float*
03336 gevd_float_operators::Correlations(const float* data, const int length,
03337                                    const float* pattern, const int radius,
03338                                    const int index, const int search)
03339 {
03340   int ns = 2*search + 1;
03341   float* result = new float[ns];
03342   result[search] = gevd_float_operators::Correlation(data, length,
03343                                                      pattern, radius,
03344                                                      index);
03345   for (int s = 1; s <= search; s++) {
03346     result[search+s] = gevd_float_operators::Correlation(data, length, // translate pattren in
03347                                                          pattern, radius, // positive direction
03348                                                          index+s);
03349     result[search-s] = gevd_float_operators::Correlation(data, length, // translate pattern in
03350                                                          pattern, radius, // negative direction
03351                                                          index-s);
03352   }
03353 #ifdef DEBUG
03354   vcl_cout << "correlations =";
03355   for (int s = 0; s < ns; s++)
03356     vcl_cout << ' ' << result[s];
03357   vcl_cout << vcl_endl;
03358 #endif
03359   return result;
03360 }
03361 
03362 
03363 //: Find correlation, and return correlation and shift values.
03364 
03365 float
03366 gevd_float_operators::BestCorrelation(const float* data, const int length,
03367                                       const float* pattern, const int radius,
03368                                       int& shift, const int search)
03369 {
03370   float* cors = gevd_float_operators::Correlations(data, length,
03371                                                    pattern, radius,
03372                                                    (length/2) + shift, // correlate at center
03373                                                    search);
03374   int index = 0; float peak = 0;
03375   bool found = gevd_float_operators::Maximum(cors, 2*search+1, index, peak);
03376   delete[] cors;
03377   if (found) {
03378     shift += index - search;
03379     return peak;
03380   }
03381   return 0;
03382 }
03383 
03384 
03385 //:
03386 // Search for best correlation, from coarse to fine,
03387 // starting at a priori shift, and requiring minimum overlap.
03388 // O(n) time.
03389 // Return last best correlation, and its corresponding shift.
03390 // The search is cutoff early, if no maximum is found, or
03391 // maximum correlation <= cutoff.
03392 // Accumulate match values if matches non null.
03393 // Assumes data and pattern are pyramids.
03394 // Compile with -DDEBUG to trace the coarse-to-fine search
03395 // of the best correlation,
03396 
03397 float
03398 gevd_float_operators::CoarseFineCorrelation(const float* dataPyr, const int dlength,
03399                                             const float* patternPyr, const int plength,
03400                                             float& shift,
03401                                             const int coarse, const int fine,
03402                                             const float cutoff,
03403                                             const float overlap,
03404                                             float* matches)
03405 {
03406   const float NOISE = 0.2f;      // valid maximum correlation
03407 
03408   // 1. Complete search of the best correlation at coarsest level
03409   // given required minimum overlap.
03410 #ifdef DEBUG
03411   vcl_cout << "shift0 = " << shift << vcl_endl;
03412 #endif
03413   if (shift != 0)               // search from a priori shift
03414     shift /= (1 << coarse);
03415   shift = (float)rint(shift);
03416   int dlen = dlength >> coarse, plen = plength >> coarse;
03417   int rmax = int((1 - overlap) * dlen + 0.5); // for minimum overlap
03418   if (rmax < 1) rmax = 1;       // boundary case, for 100% overlap
03419   float* cors = Correlations(dataPyr+dlen, dlen,
03420                              patternPyr+plen, plen,
03421                              int(shift), rmax);
03422   const int NOMATCH = -1;
03423   float match = NOISE; int mi = NOMATCH; // best correlation
03424   const int lmax = (rmax << 1);
03425   for (int i = 1; i < lmax; i++) {      // find largest correlation
03426     float cor = cors[i];
03427     if (cor > match &&
03428         cors[i-1] < cor && cor > cors[i+1]) {
03429       match = cor;
03430       mi = i;
03431     }
03432   }
03433   if (mi == NOMATCH)            // no local maximum found
03434     match = cors[rmax];         // search lower level, at same place
03435   else {
03436     float left = cors[mi-1], mid = cors[mi], right = cors[mi+1];
03437     shift += (mi - rmax) + InterpolateParabola(left, mid, right, match);
03438 #ifdef DEBUG
03439   vcl_cout << left << ' ' << mid << ' ' << right << vcl_endl
03440            << "level = " << coarse
03441            << "  shift = " << shift * (1 << coarse)
03442            << "  match = " << match << vcl_endl;
03443 #endif
03444   }
03445   delete[] cors;
03446 
03447   // 2. Track best correlation to finest level
03448   const int SEARCH = 1, RMAX = 3;// local search 3 times
03449   int k = coarse-1;
03450   for (; ; k--) {
03451     shift = (float)rint(2*shift);
03452     dlen = dlength >> k, plen = plength >> k;
03453     cors = Correlations(dataPyr+dlen, dlen,
03454                         patternPyr+plen, plen,
03455                         int(shift), SEARCH);
03456     float local = 0;            // radial search from center out
03457     int r = 0;
03458     for (; r < RMAX; r++) {
03459       float left = cors[0], mid = cors[1], right = cors[2];
03460 #ifdef DEBUG
03461       vcl_cout << left << ' ' << mid << ' ' << right << vcl_endl;
03462 #endif
03463       if (left <= mid && mid >= right && mid > NOISE) {
03464         local += InterpolateParabola(left, mid, right, match);
03465         if (matches) matches[k] += match;
03466         break;                  // radial search succeeds
03467       }
03468       if (r < RMAX-1) {         // search next radius?
03469         if (right > left) {     // search towards right
03470           local += 1;
03471           cors[0] = cors[1];
03472           cors[1] = cors[2];
03473           cors[2] = Correlation(dataPyr+dlen, dlen,
03474                                 patternPyr+plen, plen,
03475                                 int(local)+SEARCH);
03476         }
03477         if (left > right) {     // search towards left
03478           local -= 1;
03479           cors[2] = cors[1];
03480           cors[1] = cors[0];
03481           cors[0] = Correlation(dataPyr+dlen, dlen,
03482                                 patternPyr+plen, plen,
03483                                 int(local)-SEARCH);
03484         }
03485       }
03486     }
03487     delete[] cors;
03488     if (r == RMAX)              // fail local search
03489       break;                    // early exit
03490     shift += local;             // shift further
03491 #ifdef DEBUG
03492     vcl_cout << "level = " << k
03493              << "  shift = " << shift * (1 << k)
03494              << "  match = " << match << vcl_endl;
03495 #endif
03496     if (match <= cutoff || k == fine) // early cutoff because
03497       break;                    // weak correlation
03498   }
03499   if (k > 0) shift *= (1 << k); // shift at level 0
03500   if (k > fine)                 // penalize for no fine match
03501     match *= float(coarse - k + 1) / (coarse - fine + 1);
03502   return match;
03503 }
03504 
03505 
03506 //: Apply function to all elements in buffer.
03507 
03508 void
03509 gevd_float_operators::Apply(gevd_bufferxy& buf, float (*func)(float))
03510 {
03511   int size = buf.GetSizeX() * buf.GetSizeY();
03512   float* data = (float*) buf.GetBuffer();
03513   for (int i = 1; i < size; i++)
03514     data[i] = func(data[i]);
03515 }
03516 
03517 
03518 //: Allocate new space if desired depth and sizes are not the same.
03519 
03520 gevd_bufferxy*
03521 gevd_float_operators::Allocate(gevd_bufferxy* space, const gevd_bufferxy& model,
03522                                int bits_per_pixel, int sizeX, int sizeY)
03523 {
03524   if (!bits_per_pixel)          // find defaults from model
03525     bits_per_pixel = model.GetBitsPixel();
03526   if (!sizeX)
03527     sizeX = model.GetSizeX();
03528   if (!sizeY)
03529     sizeY = model.GetSizeY();
03530   if (space == NULL ||          // check if need reallocation
03531       space->GetBitsPixel() != bits_per_pixel ||
03532       space->GetSizeX() < sizeX || space->GetSizeY() < sizeY) {
03533     delete space;
03534     space = new gevd_bufferxy(sizeX, sizeY, bits_per_pixel);
03535   }
03536   return space;
03537 }
03538 
03539 //: Creates a new buffer similar to buf, unless dimension and precision are given.
03540 
03541 gevd_bufferxy*
03542 gevd_float_operators::SimilarBuffer(const gevd_bufferxy& buf,
03543                                     int bits_per_pixel,
03544                                     int sizeX, int sizeY)
03545 {
03546   if (bits_per_pixel == 0)                      // find default pixel
03547     bits_per_pixel = buf.GetBitsPixel();
03548   if (sizeX == 0)                               // find default size
03549     sizeX = buf.GetSizeX();
03550   if (sizeY == 0)
03551     sizeY = buf.GetSizeY();
03552   return new gevd_bufferxy(sizeX, sizeY, bits_per_pixel);
03553 }
03554 
03555 
03556 //: Two buffers are similar if they have the same dimensions, and precision (bits_per_pixel).
03557 
03558 bool
03559 gevd_float_operators::IsSimilarBuffer(const gevd_bufferxy& buf1,
03560                                       const gevd_bufferxy& buf2)
03561 {
03562   return buf1.GetSizeX() == buf2.GetSizeX() &&
03563          buf1.GetSizeY() == buf2.GetSizeY() &&
03564          buf1.GetBitsPixel() == buf2.GetBitsPixel();
03565 }
03566 
03567 
03568 //:
03569 // Extract from buf, a float sub-buffer with dimensions (sizeX, sizeY),
03570 // from top-left corner (origX, origY).
03571 // Faster copying can be done with read/write chunks of memory.
03572 
03573 gevd_bufferxy*
03574 gevd_float_operators::Extract(const gevd_bufferxy & buf,
03575                               int sizeX, int sizeY, int origX, int origY)
03576 {
03577   if (sizeX == 0)                               // find default size
03578     sizeX = buf.GetSizeX();
03579   if (sizeY == 0)
03580     sizeY = buf.GetSizeY();
03581   gevd_bufferxy& sub = *gevd_float_operators::SimilarBuffer(buf, 0, sizeX, sizeY);
03582   for (int y = 0; y < sizeY; y++)
03583     for (int x = 0; x < sizeX; x++)
03584       floatPixel(sub, x, y) = floatPixel(buf, origX+x, origY+y);
03585   return &sub;
03586 }
03587 
03588 //: Update a float sub-buffer of \a buf, from top-left corner (origX, origY), with values in \a sub.
03589 // Faster copying can be done with read/write chunks of memory.
03590 
03591 void
03592 gevd_float_operators::Update(gevd_bufferxy& buf, const gevd_bufferxy& sub,
03593                              int origX, int origY)
03594 {
03595   int sizeX = sub.GetSizeX(), sizeY = sub.GetSizeY();
03596   for (int y = 0; y < sizeY; y++)
03597     for (int x = 0; x < sizeX; x++)
03598       floatPixel(buf, origX+x, origY+y) = floatPixel(sub, x, y);
03599 }
03600 
03601 //: Set all pixels in ROI to value.
03602 
03603 void
03604 gevd_float_operators::Fill(gevd_bufferxy& buf, const float value,
03605                            int sizeX, int sizeY,
03606                            int origX, int origY)
03607 {
03608   if (sizeX == 0)                       // find default size
03609     sizeX = buf.GetSizeX();
03610   if (sizeY == 0)
03611     sizeY = buf.GetSizeY();
03612   int endX = origX + sizeX, endY = origY + sizeY;
03613   for (int y = origY; y < endY; y++)
03614     for (int x = origX; x < endX; x++)
03615       floatPixel(buf, x, y) = value;
03616 }
03617 
03618 //: Sets all pixels in the frame region to value (default = 0).
03619 // O((n+m)*width).
03620 // The frame region is a rectangular band with given width,
03621 // framing at two ends of x/y axes.
03622 
03623 void
03624 gevd_float_operators::FillFrameX(gevd_bufferxy& buf, const float value, const int width)
03625 {
03626   const int hix = buf.GetSizeX(), hiy = buf.GetSizeY();
03627   const int framelox = width, framehix = hix - width;
03628   for (int y = 0; y < hiy; ++y)             // left
03629     for (int x = 0; x < framelox; ++x)
03630       floatPixel(buf, x, y) =  value;
03631   for (int y = 0; y < hiy; ++y)             // right
03632     for (int x = framehix; x < hix; ++x)
03633       floatPixel(buf, x, y) =  value;
03634 }
03635 
03636 //:
03637 
03638 void
03639 gevd_float_operators::FillFrameY(gevd_bufferxy& buf, const float value, const int width)
03640 {
03641   int lox = 0, hix = buf.GetSizeX();
03642   int loy = 0, hiy = buf.GetSizeY();
03643   int frameloy = width, framehiy = hiy - width;
03644   for (int y = loy; y < frameloy; y++)              // bottom
03645     for (int x = lox; x < hix; x++)
03646       floatPixel(buf, x, y) =  value;
03647   for (int y = framehiy; y < hiy; y++)              // top
03648     for (int x = lox; x < hix; x++)
03649       floatPixel(buf, x, y) =  value;
03650 }
03651 
03652 //: Sets all pixels on frame contour, such as buf(loc, i), to value.
03653 // O(n+m).
03654 
03655 void
03656 gevd_float_operators::DrawFrame(gevd_bufferxy& buf, const int loc, const float value)
03657 {
03658   const int hix = buf.GetSizeX()-loc-1;
03659   const int hiy = buf.GetSizeY()-loc-1;
03660   for (int x = loc; x <= hix; x++) {
03661     floatPixel(buf, x, loc) = value; // bottom
03662     floatPixel(buf, x, hiy) = value; // top
03663   }
03664   for (int y = loc; y <= hiy; y++) {
03665     floatPixel(buf, loc, y) = value; // left
03666     floatPixel(buf, hix, y) = value; // right
03667   }
03668 }
03669 
03670 //: Returns the maximum value in float buffer.
03671 
03672 float
03673 gevd_float_operators::Maximum(const gevd_bufferxy& buf,
03674                               int sizeX, int sizeY, int origX, int origY)
03675 {
03676   if (sizeX == 0)                               // find default size
03677     sizeX = buf.GetSizeX();
03678   if (sizeY == 0)
03679     sizeY = buf.GetSizeY();
03680   int maxx = origX + sizeX, maxy = origY + sizeY;
03681   float hi = floatPixel(buf, origX, origY), f;
03682   for (int y = origY; y < maxy; y++)
03683     for (int x = origX; x < maxx; x++) {
03684       f = floatPixel(buf, x, y);
03685       if (f > hi) hi = f;
03686     }
03687   return hi;
03688 }
03689 
03690 //: Returns the minimum value in float buffer.
03691 
03692 float
03693 gevd_float_operators::Minimum(const gevd_bufferxy& buf,
03694                               int sizeX, int sizeY, int origX, int origY)
03695 {
03696   if (sizeX == 0)                               // find default size
03697     sizeX = buf.GetSizeX();
03698   if (sizeY == 0)
03699     sizeY = buf.GetSizeY();
03700   int maxx = origX + sizeX, maxy = origY + sizeY;
03701   float lo = floatPixel(buf, origX, origY), f;
03702   for (int y = origY; y < maxy; y++)
03703     for (int x = origX; x < maxx; x++) {
03704       f = floatPixel(buf, x, y);
03705       if (f < lo) lo = f;
03706     }
03707   return lo;
03708 }
03709 
03710 //: Returns the sum of all values in float buffer.
03711 
03712 float
03713 gevd_float_operators::Sum(const gevd_bufferxy& buf,
03714                           int sizeX, int sizeY, int origX, int origY)
03715 {
03716   if (sizeX == 0)                               // find default size
03717     sizeX = buf.GetSizeX();
03718   if (sizeY == 0)
03719     sizeY = buf.GetSizeY();
03720   int maxx = origX + sizeX, maxy = origY + sizeY;
03721   float sum = 0;
03722   for (int y = origY; y < maxy; y++)
03723     for (int x = origX; x < maxx; x++)
03724       sum += floatPixel(buf, x, y);
03725   return sum;
03726 }
03727 
03728 //: Truncate all values in ROI to 0, if below noise.
03729 // Return the number of pixels changed.
03730 
03731 int
03732 gevd_float_operators::Threshold(gevd_bufferxy& buf, float noise,
03733                                 int sizeX, int sizeY,
03734                                 int origX, int origY)
03735 {
03736   if (sizeX == 0)               // find default size
03737     sizeX = buf.GetSizeX();
03738   if (sizeY == 0)
03739     sizeY = buf.GetSizeY();
03740   int maxx = origX + sizeX, maxy = origY + sizeY;
03741   int count = 0; float pix;
03742   for (int y = origY; y < maxy; y++)
03743     for (int x = origX; x < maxx; x++)
03744       if ((pix = floatPixel(buf, x, y)) && pix < noise) {
03745         floatPixel(buf, x, y) = 0;
03746         count++;
03747       }
03748   return count;
03749 }
03750 
03751 
03752 //:
03753 // Normalizes a float buffer so that the pixel values range
03754 // from \a lo to \a hi, inclusive. If the buffer has constant value,
03755 // the value is mapped to \a lo.
03756 // O(n*m).
03757 
03758 void
03759 gevd_float_operators::Normalize(gevd_bufferxy& buf, const float lo, const float hi)
03760 {
03761   int size = (buf.GetSizeX() * buf.GetSizeY());
03762   float* data = (float*) buf.GetBuffer();
03763   float flo, fhi, f;
03764   flo = fhi = data[0];
03765   for (int i = 1; i < size; i++) {
03766     f = data[i];
03767     if (f < flo) flo = f;
03768     if (f > fhi) fhi = f;
03769   }
03770   if (fhi == flo)
03771     for (int i = 0; i < size; i++)
03772       data[i] = lo;
03773   else {
03774     float scale = (hi - lo) / (fhi - flo);
03775     for (int i = 0; i < size; i++)
03776       data[i] = scale * (data[i] - flo)  + lo;
03777   }
03778 }
03779 
03780 //: Shift to positive values, by adding 30.0000, and truncate all values to 0-60.000.
03781 //  O(n*m).
03782 
03783 void
03784 gevd_float_operators::ShiftToPositive(gevd_bufferxy& buf)
03785 {
03786   const float zero = 30000, lo = 0, hi = 60000;
03787   int size = (buf.GetSizeX() * buf.GetSizeY());
03788   float* data = (float*) buf.GetBuffer();
03789   for (int i = 0; i < size; i++) {
03790     float f = data[i] + zero;
03791     if (f < lo)
03792       f = lo;
03793     else if (f > hi)
03794       f = hi;
03795     data[i] = f;
03796   }
03797 }
03798 
03799 
03800 //: Zeros out all negative values.
03801 
03802 float
03803 gevd_float_operators::TruncateToPositive(gevd_bufferxy& buf)
03804 {
03805   int size = (buf.GetSizeX() * buf.GetSizeY());
03806   float* data = (float*) buf.GetBuffer();
03807   float diff = 0, d;
03808   for (int i = 0; i < size; i++) {
03809     d = data[i];
03810     if (d < 0) {
03811       data[i] = 0;
03812       d = - d;
03813       if (d > diff) diff = d;
03814     }
03815   }
03816   return diff;
03817 }
03818 
03819 //: Scale all values by factor.
03820 
03821 void
03822 gevd_float_operators::Scale(gevd_bufferxy& buf, float factor)
03823 {
03824   if (factor != 0) {
03825     int size = (buf.GetSizeX() * buf.GetSizeY());
03826     float* data = (float*) buf.GetBuffer();
03827     for (int i = 0; i < size; i++)
03828       data[i] *= factor;
03829   }
03830 }
03831 
03832 //: Replace with absolute values.
03833 
03834 void
03835 gevd_float_operators::Absolute(gevd_bufferxy& buf)
03836 {
03837   int size = (buf.GetSizeX() * buf.GetSizeY());
03838   float* data = (float*) buf.GetBuffer();
03839   for (int i = 0; i < size; i++)
03840     if (data[i] < 0)
03841       data[i] = - data[i];
03842 }
03843 
03844 //: Negate all values.
03845 
03846 void
03847 gevd_float_operators::Negate(gevd_bufferxy& buf)
03848 {
03849   int size = (buf.GetSizeX() * buf.GetSizeY());
03850   float* data = (float*) buf.GetBuffer();
03851   for (int i = 0; i < size; i++)
03852     data[i] = - data[i];
03853 }
03854 
03855 
03856 float
03857 gevd_float_operators::TruncateToCeiling(gevd_bufferxy& buf, float ceilng)
03858 {
03859   int size = (buf.GetSizeX() * buf.GetSizeY());
03860   float* data = (float*) buf.GetBuffer();
03861   float diff = 0, d;
03862   for (int i = 0; i < size; i++) {
03863     d = data[i];
03864     if (d > ceilng) {
03865       data[i] = ceilng;
03866       d -= ceilng;
03867       if (d > diff) diff = d;
03868     }
03869   }
03870   return diff;
03871 }
03872 
03873 
03874 //:
03875 // Converts from a unsigned char (8-bit) or a short (16-bit) buffer to a float buffer
03876 // to avoid overflow/underflow and conversion for subsequent math computations.
03877 // O(n*m).
03878 
03879 bool
03880 gevd_float_operators::BufferToFloat(const gevd_bufferxy& from, gevd_bufferxy& to)
03881 {
03882   assert (&to != &from);
03883   assert (from.GetSizeX() == to.GetSizeX() && from.GetSizeY() == to.GetSizeY());
03884   assert (to.GetBytesPixel() == sizeof(float));
03885   int size = (to.GetSizeX() * to.GetSizeY());
03886   switch (from.GetBytesPixel())
03887   {
03888    case sizeof(unsigned char): {
03889     const unsigned char* frombuf = (const unsigned char*) from.GetBuffer();
03890     float* tobuf = (float*) to.GetBuffer();
03891     for (int i = 0; i < size; i++)
03892       tobuf[i] = (float) frombuf[i];
03893     break;
03894    }
03895    case sizeof(short): {
03896     const short* frombuf = (const short*) from.GetBuffer();
03897     float* tobuf = (float*) to.GetBuffer();
03898     for (int i = 0; i < size; i++)
03899       tobuf[i] = (float) frombuf[i];
03900     break;
03901    }
03902    case 3*sizeof(unsigned char): { // assume RGB, and take luminance
03903     vcl_cerr << "gevd_float_operators::BufferToFloat: taking luminance of RGB buffer\n";
03904     const unsigned char* frombuf = (const unsigned char*) from.GetBuffer();
03905     float* tobuf = (float*) to.GetBuffer();
03906     for (int i = 0; i < size; i++)
03907       tobuf[i] = 0.299f*frombuf[3*i]+0.587f*frombuf[3*i+1]+0.114f*frombuf[3*i+2];
03908     break;
03909    }
03910    case sizeof(int): {
03911     const unsigned int* frombuf = (const unsigned int*) from.GetBuffer();
03912     float* tobuf = (float*) to.GetBuffer();
03913     for (int i = 0; i < size; i++)
03914       tobuf[i] = (float)frombuf[i];
03915     break;
03916    }
03917    default:
03918     vcl_cerr << "Can only convert unsigned char/short/int/RGB buffer to float\n";
03919     return false;
03920   }
03921   return true;
03922 }
03923 
03924 bool
03925 gevd_float_operators::FloatToBuffer (const gevd_bufferxy& from, gevd_bufferxy& to)
03926 {
03927   assert (&to != &from);
03928   assert (from.GetSizeX() == to.GetSizeX() && from.GetSizeY() == to.GetSizeY());
03929   assert (from.GetBytesPixel() == sizeof(float));
03930   int size = (to.GetSizeX() * to.GetSizeY());
03931   switch (to.GetBytesPixel())
03932   {
03933    case sizeof(unsigned char): {
03934     const float* frombuf = (const float*) from.GetBuffer();
03935     unsigned char* tobuf = (unsigned char*) to.GetBuffer();
03936     for (int i = 0; i < size; i++)
03937       tobuf[i] = (unsigned char) int(frombuf[i]);
03938     return true;
03939    }
03940    case sizeof(short): {
03941     const float* frombuf = (const float*) from.GetBuffer();
03942     short* tobuf = (short*) to.GetBuffer();
03943     for (int i = 0; i < size; i++)
03944       tobuf[i] = (short) int(frombuf[i]);
03945     return true;
03946    }
03947    case 3*sizeof(unsigned char): { // assume RGB ==> restore luminance
03948     const float* frombuf = (const float*) from.GetBuffer();
03949     unsigned char* tobuf = (unsigned char*) to.GetBuffer();
03950     for (int i = 0; i < size; i++)
03951       tobuf[3*i] = tobuf[3*i+1] = tobuf[3*i+2] = (unsigned char) int(frombuf[i]);
03952     return true;
03953    }
03954    default:
03955     vcl_cerr << "Can only convert float to unsigned char/short/RGB buffer\n";
03956     return false;
03957   }
03958 }
03959 
03960 
03961 //: Detect maximum in 1d-array of values.
03962 
03963 bool
03964 gevd_float_operators::Maximum(const float* data, const int length,
03965                               int& index, float& value)
03966 {
03967   index = 0;
03968   float left, mid = data[0], right = data[1];
03969   for (int r = 2; r < length; r++) {
03970     left = mid;
03971     mid = right;
03972     right = data[r];
03973     if (mid > left && mid > right) {
03974       if (index) {
03975         if (mid > value) {
03976           value = mid;
03977           index = r-1;
03978         }
03979       }
03980       else {
03981         value = mid;
03982         index = r-1;
03983       }
03984     }
03985   }
03986   return !(index == 0);
03987 }
03988 
03989 //:
03990 // Pad the buffer by repeating values at the border, so that the
03991 // new buffer has dimensions being powers of 2. The original buffer
03992 // is centered in the new buffer. Returns the buffer unchanged if it
03993 // already has dimensions being powers of 2.
03994 
03995 gevd_bufferxy*
03996 gevd_float_operators::PadToPowerOf2(gevd_bufferxy& buf)
03997 {
03998   int sizeX = buf.GetSizeX();
03999   int sizeY = buf.GetSizeY();
04000   int newSizeX = sizeX, newSizeY = sizeY;
04001   {
04002     double exptX = vcl_log(double(sizeX))/vcl_log(2.0),
04003            exptY = vcl_log(double(sizeY))/vcl_log(2.0);
04004     double ceilX = vcl_ceil(exptX), ceilY = vcl_ceil(exptY);
04005     if (exptX < ceilX)          // round up to nearest power of 2
04006       newSizeX = 1 << int(ceilX);
04007     if (exptY < ceilY)
04008       newSizeY = 1 << int(ceilY);
04009   }
04010   if (newSizeX == sizeX && newSizeY == sizeY)
04011     return &buf;
04012   int lpadX = (newSizeX - sizeX) / 2, lpadY = (newSizeY - sizeY) / 2;
04013   gevd_bufferxy& padded = *gevd_float_operators::SimilarBuffer(buf, bits_per_float,
04014                                                                newSizeX, newSizeY);
04015   padded.Clear();
04016   gevd_float_operators::Update(padded, buf, lpadX, lpadY);
04017   int hpadX = newSizeX-lpadX-sizeX, hpadY = newSizeY-lpadY-sizeY;
04018   lpadX++; lpadY++;
04019   hpadX++; hpadY++;
04020   float corner = (2*floatPixel(buf, 0, 0) +
04021                   floatPixel(buf, 1, 0) + floatPixel(buf, 0, 1)) / 4;
04022   gevd_float_operators::Fill(padded, corner, // fill corner values
04023                              lpadX, lpadY, 0, 0);
04024   corner = (2*floatPixel(buf, sizeX-1, 0) +
04025             floatPixel(buf, sizeX-2, 0) + floatPixel(buf, sizeX-1, 1)) / 4;
04026   gevd_float_operators::Fill(padded, corner,
04027                              hpadX, lpadY, newSizeX-hpadX, 0);
04028   corner = (2*floatPixel(buf, 0, sizeY-1) +
04029             floatPixel(buf, 1, sizeY-1) + floatPixel(buf, 0, sizeY-2)) / 4;
04030   gevd_float_operators::Fill(padded, corner,
04031                              lpadX, hpadY, 0, newSizeY-hpadY);
04032   corner = (2*floatPixel(buf, sizeX-1, sizeY-1) +
04033             floatPixel(buf, sizeX-2, sizeY-1) +
04034             floatPixel(buf, sizeX-1, sizeY-2)) / 4;
04035   gevd_float_operators::Fill(padded, corner,
04036                              hpadX, hpadY, newSizeX-hpadX, newSizeY-hpadY);
04037   int x_1, x_2, y_1, y_2;
04038   x_1 = lpadX; x_2 = newSizeX-hpadX;
04039   for (int x = x_1; x < x_2; x++) {
04040     float top = (floatPixel(buf, x-lpadX, 0) +
04041                  2*floatPixel(buf, x-lpadX+1, 0) +
04042                  floatPixel(buf, x-lpadX+2, 0)) / 4;
04043     y_1 = 0; y_2 = lpadY-1;
04044     for (int y = y_1; y < y_2; y++)
04045       floatPixel(padded, x, y) = top;
04046     float bottom = (floatPixel(buf, x-lpadX, sizeY-1) +
04047                     2*floatPixel(buf, x-lpadX+1, sizeY-1) +
04048                     floatPixel(buf, x-lpadX+2, sizeY-1)) / 4;
04049     y_1 = newSizeY-hpadY+1; y_2 = newSizeY;
04050     for (int y = y_1; y < y_2; y++)
04051       floatPixel(padded, x, y) = bottom;
04052   }
04053   y_1 = lpadY; y_2 = newSizeY-hpadY;
04054   for (int y = y_1; y < y_2; y++) {
04055     float left = (floatPixel(buf, 0, y-lpadY) +
04056                   2*floatPixel(buf, 0, y-lpadY+1) +
04057                   floatPixel(buf, 0, y-lpadY+2)) / 4;
04058     x_1 = 0; x_2 = lpadX-1;
04059     for (int x = x_1; x < x_2; x++)
04060       floatPixel(padded, x, y) = left;
04061     float right = (floatPixel(buf, sizeX-1, y-lpadY) +
04062                    2*floatPixel(buf, sizeX-1, y-lpadY+1) +
04063                    floatPixel(buf, sizeX-1, y-lpadY+2)) / 4;
04064     x_1 = newSizeX-hpadX+1; x_2 = newSizeX;
04065     for (int x = x_1; x < x_2; x++)
04066       floatPixel(padded, x, y) = right;
04067   }
04068   return &padded;
04069 }
04070 
04071 //: Inverse of the above operation.
04072 
04073 gevd_bufferxy*
04074 gevd_float_operators::UnpadFromPowerOf2(gevd_bufferxy& padded, int sizeX, int sizeY)
04075 {
04076   int newSizeX = padded.GetSizeX();
04077   int newSizeY = padded.GetSizeY();
04078   if (newSizeX == sizeX && newSizeY == sizeY)
04079     return &padded;
04080   else
04081     return gevd_float_operators::Extract(padded,
04082                                          sizeX, sizeY,
04083                                          (newSizeX - sizeX)/2,
04084                                          (newSizeY - sizeY)/2);
04085 }
04086 
04087 gevd_bufferxy*
04088 gevd_float_operators::TransposeExtract(const gevd_bufferxy & buf,
04089                                        int sizeX, int sizeY, int origX, int origY)
04090 {
04091   gevd_bufferxy& sub = *gevd_float_operators::SimilarBuffer(buf, 0, sizeY, sizeX);
04092   for (int y = 0; y < sizeY; y++)
04093     for (int x = 0; x < sizeX; x++)
04094       floatPixel(sub, y, x) = floatPixel(buf, origX+x, origY+y);
04095   return &sub;
04096 }
04097 
04098 void
04099 gevd_float_operators::TransposeUpdate(gevd_bufferxy& buf, const gevd_bufferxy& sub,
04100                                       int origX, int origY)
04101 {
04102   int sizeX = sub.GetSizeY(), sizeY = sub.GetSizeX();
04103   for (int y = 0; y < sizeY; y++)
04104     for (int x = 0; x < sizeX; x++)
04105       floatPixel(buf, origX+x, origY+y) = floatPixel(sub, y, x);
04106 }
04107 
04108 
04109 gevd_bufferxy*
04110 gevd_float_operators::AbsoluteDifference(const gevd_bufferxy& buf1,
04111                                          const gevd_bufferxy& buf2)
04112 {
04113   if (!IsSimilarBuffer(buf1, buf2))
04114     return NULL;
04115   else {
04116     int sizeX = buf1.GetSizeX(), sizeY = buf1.GetSizeY();
04117     gevd_bufferxy& buf = *gevd_float_operators::SimilarBuffer(buf1);
04118     for (int y = 0; y < sizeY; y++)
04119       for (int x = 0; x < sizeX; x++) {
04120         float diff = floatPixel(buf1, x, y) - floatPixel(buf2, x, y);
04121         if (diff < 0) diff = -diff;
04122         floatPixel(buf, x, y) = diff;
04123       }
04124     return &buf;
04125   }
04126 }