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