Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members

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