contrib/brl/bseg/brip/brip_vil_float_ops.cxx
Go to the documentation of this file.
00001 #include "brip_vil_float_ops.h"
00002 //:
00003 // \file
00004 
00005 #include <vcl_fstream.h>
00006 #include <vcl_complex.h>
00007 #include <vcl_limits.h>
00008 #include <vgl/vgl_box_2d.h>
00009 #include <vgl/vgl_point_2d.h>
00010 #include <vgl/vgl_vector_2d.h>
00011 #include <vgl/vgl_polygon_scan_iterator.h>
00012 #include <vul/vul_timer.h>
00013 #include <vbl/vbl_array_1d.h>
00014 #include <vbl/vbl_array_2d.h>
00015 #include <vbl/vbl_bounding_box.h>
00016 #include <vnl/vnl_numeric_traits.h>
00017 #include <vnl/vnl_math.h>
00018 #include <vnl/vnl_double_2x3.h>
00019 #include <vnl/algo/vnl_fft_prime_factors.h>
00020 #include <vnl/algo/vnl_svd.h>
00021 #include <vil/vil_pixel_format.h>
00022 #include <vil/vil_transpose.h>
00023 
00024 #include <vil/vil_convert.h>
00025 #include <vil/vil_save.h>
00026 #include <vil/vil_new.h>
00027 
00028 #include <vil/vil_math.h>
00029 #include <vil/algo/vil_convolve_1d.h>
00030 #include <vsol/vsol_box_2d.h>
00031 #include <vsol/vsol_polygon_2d_sptr.h>
00032 #include <vsol/vsol_polygon_2d.h>
00033 #include <bsol/bsol_algs.h>
00034 #include <bsta/bsta_histogram.h>
00035 #include <bsta/bsta_joint_histogram.h>
00036 #include <brip/brip_roi.h>
00037 
00038 //Local utility functions
00039 
00040 //:compute normalized cross correlation from the intensity moment sums.
00041 static float cross_corr(double area, double si1, double si2,
00042                         double si1i1, double si2i2, double si1i2,
00043                         float intensity_thresh)
00044 {
00045   if (!area)
00046     return 0.f;
00047   //the mean values
00048   double u1 = si1/area, u2 = si2/area;
00049   if (u1<intensity_thresh||u2<intensity_thresh)
00050     return -1.f;
00051   double neu = si1i2 - area*u1*u2;
00052   double sd1 = vcl_sqrt(vcl_fabs(si1i1-area*u1*u1)),
00053     sd2 = vcl_sqrt(vcl_fabs(si2i2-area*u2*u2));
00054   if (!neu)
00055     return 0.f;
00056   if (!sd1||!sd2) {
00057     if (neu>0)
00058       return 1.f;
00059     else
00060       return -1.f;
00061   }
00062   double den = sd1*sd2;
00063   return float(neu/den);
00064 }
00065 
00066 //------------------------------------------------------------
00067 //:  Convolve with a kernel
00068 //   It's assumed that the kernel is square with odd dimensions
00069 vil_image_view<float>
00070 brip_vil_float_ops::convolve(vil_image_view<float> const& input,
00071                              vbl_array_2d<float> const& kernel)
00072 {
00073   int w = static_cast<int>(input.ni()), h = static_cast<int>(input.nj());
00074   int kw = kernel.cols(); // kh = kernel.rows();
00075   // add a check for kernels that are not equal dimensions of odd size JLM
00076   int n = (kw-1)/2;
00077   vil_image_view<float> output;
00078   output.set_size(w,h);
00079   for (int y = n; y<(h-n); y++)
00080     for (int x = n; x<(w-n); x++)
00081     {
00082       float accum = 0;
00083       for (int j = -n; j<=n; j++)
00084         for (int i = -n; i<=n; i++)
00085         {
00086           float x1 = input(x+i,y+j);
00087           float x2 = kernel[i+n][j+n];
00088           accum += x1*x2;
00089         }
00090       output(x,y)=accum;
00091     }
00092   brip_vil_float_ops::fill_x_border(output, n, 0.0f);
00093   brip_vil_float_ops::fill_y_border(output, n, 0.0f);
00094   return output;
00095 }
00096 
00097 static void fill_1d_array(vil_image_view<float> const& input,
00098                           int y, float* output)
00099 {
00100   unsigned w = input.ni();
00101   for (unsigned x = 0; x<w; x++)
00102     output[x] = input(x,y);
00103 }
00104 
00105 //: Downsamples the 1-d array by 2 using the Burt-Adelson reduction algorithm.
00106 void brip_vil_float_ops::half_resolution_1d(const float* input, unsigned width,
00107                                             float k0, float k1, float k2,
00108                                             float* output)
00109 {
00110   float w[5];
00111   int n = 0;
00112   for (; n<5; n++)
00113     w[n]=input[n];
00114   output[0]=k0*w[0]+ 2.0f*(k1*w[1] + k2*w[2]);//reflect at boundary
00115   for (unsigned x = 1; x<width; x++)
00116   {
00117     output[x]=k0*w[2]+ k1*(w[1]+w[3]) + k2*(w[0]+w[4]);
00118     //shift the window, w, over by two pixels
00119     w[0] = w[2];       w[1] = w[3];     w[2] = w[4];
00120     //handle the boundary conditions
00121     if (x+2<width)
00122       w[3] = input[n++], w[4] = input[n++];
00123     else
00124       w[3] = w[1], w[4] = w[0];
00125   }
00126 }
00127 
00128 //: Downsamples the image by 2 using the Burt-Adelson reduction algorithm.
00129 // Convolution with a 5-point kernel [(0.5-ka)/2, 0.25, ka, 0.25, (0.5-ka)/2]
00130 // ka = 0.6  maximum decorrelation, wavelet for image compression.
00131 // ka = 0.5  linear interpolation,
00132 // ka = 0.4  Gaussian filter
00133 // ka = 0.359375 min aliasing, wider than Gaussian
00134 // The image sizes are related by: output_dimension = (input_dimension +1)/2.
00135 vil_image_view<float>
00136 brip_vil_float_ops::half_resolution(vil_image_view<float> const& input,
00137                                     float filter_coef)
00138 {
00139   vul_timer t;
00140   float k0 = filter_coef, k1 = 0.25f*filter_coef, k2 = 0.5f*(0.5f-filter_coef);
00141   unsigned w = input.ni(), h = input.nj();
00142   int half_w =(w+1)/2, half_h = (h+1)/2;
00143   vil_image_view<float> output;
00144   output.set_size(half_w, half_h);
00145   //Generate input/output arrays
00146   int n = 0;
00147   float* in0 = new float[w];  float* in1 = new float[w];
00148   float* in2 = new float[w];  float* in3 = new float[w];
00149   float* in4 = new float[w];
00150 
00151   float* out0 = new float[half_w];  float* out1 = new float[half_w];
00152   float* out2 = new float[half_w];  float* out3 = new float[half_w];
00153   float* out4 = new float[half_w];
00154   //Initialize arrays
00155   fill_1d_array(input, n++, in0);   fill_1d_array(input, n++, in1);
00156   fill_1d_array(input, n++, in2);   fill_1d_array(input, n++, in3);
00157   fill_1d_array(input, n++, in4);
00158 
00159   //downsample initial arrays
00160   brip_vil_float_ops::half_resolution_1d(in0, half_w, k0, k1, k2, out0);
00161   brip_vil_float_ops::half_resolution_1d(in1, half_w, k0, k1, k2, out1);
00162   brip_vil_float_ops::half_resolution_1d(in2, half_w, k0, k1, k2, out2);
00163   brip_vil_float_ops::half_resolution_1d(in3, half_w, k0, k1, k2, out3);
00164   brip_vil_float_ops::half_resolution_1d(in4, half_w, k0, k1, k2, out4);
00165   int x=0, y;
00166   //do the first output line
00167   for (;x<half_w;x++)
00168     output(x,0)= k0*out0[x]+ 2.0f*(k1*out1[x]+k2*out2[x]);
00169   //normal lines
00170   for (y=1; y<half_h; y++)
00171   {
00172     for (x=0; x<half_w; x++)
00173       output(x,y) = k0*out2[x]+ k1*(out1[x]+out3[x]) + k2*(out0[x]+out4[x]);
00174     //shift the neighborhood down two lines
00175     float* temp0 = out0;
00176     float* temp1 = out1;
00177     out0 = out2;  out1 = out3;  out2 = out4;
00178     out3 = temp0; out4 = temp1;//reflect values
00179     //test border condition
00180     if (y<half_h-2)
00181     {
00182       //normal processing, so don't reflect
00183       fill_1d_array(input, n++, in3);
00184       fill_1d_array(input, n++, in4);
00185       brip_vil_float_ops::half_resolution_1d(in3, half_w, k0, k1, k2, out3);
00186       brip_vil_float_ops::half_resolution_1d(in4, half_w, k0, k1, k2, out4);
00187     }
00188   }
00189   delete [] in0;  delete [] in1; delete [] in2;
00190   delete [] in3;  delete [] in4;
00191   delete [] out0;  delete [] out1; delete [] out2;
00192   delete [] out3;  delete [] out4;
00193 #ifdef DEBUG
00194   vcl_cout << "\nDownsample a "<< w <<" x " << h << " image in "<< t.real() << " msecs.\n";
00195 #endif
00196   return output;
00197 }
00198 
00199 void brip_vil_float_ops::double_resolution_1d(const float* input, const unsigned n_input,
00200                                               const float k0, const float k1,
00201                                               const float k2, float* output)
00202 {
00203   float w[3];
00204   unsigned i = 0;
00205   w[1]=input[i]; w[2]=input[i++];
00206   w[0]=w[2];
00207   for (unsigned c = 0; c<2*n_input; c+=2)
00208   {
00209     output[c] = k0*w[1] + k2*(w[0]+w[2]);
00210     output[c+1] = k1*(w[1]+w[2]);
00211     w[0]=w[1];
00212     w[1]=w[2];
00213     if (c<2*(n_input-2))
00214       w[2]=input[i++];
00215     else
00216       w[2]=w[0];
00217   }
00218 }
00219 
00220 //: interpolates the input using the Bert-Adelson algorithm
00221 vil_image_view<float>
00222 brip_vil_float_ops::double_resolution(vil_image_view<float> const& input,
00223                                       float filter_coef)
00224 {
00225   unsigned ni_in = input.ni();
00226   unsigned nj_in = input.nj();
00227   unsigned ni_out = 2*ni_in;
00228   unsigned nj_out = 2*nj_in;
00229   vil_image_view<float> out(ni_out, nj_out);
00230   float* input_1d = new float[ni_in];
00231 
00232   //An interpolation neighborhood of three lines
00233   float* output0 = new float[ni_out];
00234   float* output1 = new float[ni_out];
00235   float* output2 = new float[ni_out];
00236 
00237   //The filter coefficients
00238   float k0 = filter_coef*2.0f;
00239   float k1 = 0.5f;
00240   float k2 = 0.5f-filter_coef;
00241 
00242   //initialize
00243   unsigned i = 0;
00244   fill_1d_array(input, i++, input_1d);
00245   brip_vil_float_ops::double_resolution_1d(input_1d, ni_in, k0, k1, k2, output1);
00246   fill_1d_array(input, i++, input_1d);
00247   brip_vil_float_ops::double_resolution_1d(input_1d, ni_in, k0, k1, k2, output2);
00248   for (unsigned k = 0; k<ni_out; ++k)
00249     output0[k]=output2[k];
00250   for (unsigned r = 0; r<nj_out; r+=2)
00251   {
00252     unsigned rp = r+1;
00253     for (unsigned c=0; c<ni_out; ++c)
00254     {
00255       out(c, r) = k0*output1[c] + k2*(output0[c]+output2[c]);
00256       out(c, rp) = k1*(output1[c]+output2[c]);
00257     }
00258     float* next = output0;
00259     output0 = output1;
00260     output1 = output2;
00261     output2 = next;
00262     if (r<nj_out-4)
00263     {
00264       fill_1d_array(input, i++, input_1d);
00265       brip_vil_float_ops::double_resolution_1d(input_1d, ni_in,
00266                                                k0, k1, k2, output2);
00267     }
00268     else
00269       for (unsigned k = 0; k<ni_out; ++k)
00270         output2[k]=output0[k];
00271   }
00272   delete [] input_1d;
00273   delete [] output0;
00274   delete [] output1;
00275   delete [] output2;
00276   return out;
00277 }
00278 
00279 static double brip_vil_gaussian(double x, double sigma)
00280 {
00281   double x_on_sigma = x / sigma;
00282   return (double)vcl_exp(- x_on_sigma * x_on_sigma / 2);
00283 }
00284 
00285 unsigned brip_vil_float_ops::gaussian_radius(const double sigma,
00286                                              const double fuzz)
00287 {
00288   unsigned radius;
00289   for (radius = 0; (float)brip_vil_gaussian((double)radius, sigma) > fuzz; ++radius)
00290     ;
00291   return radius;
00292 }
00293 
00294 //: generate a 1-d Gaussian kernel  fuzz=0.02 is a good value
00295 static void brip_1d_gaussian_kernel(double sigma, double fuzz,
00296                                     unsigned& radius, double*& kernel)
00297 {
00298   radius = brip_vil_float_ops::gaussian_radius(sigma, fuzz);
00299 
00300   kernel = new double[2*radius + 1];
00301   if (!radius)
00302   {
00303     kernel[0]=1;
00304     return;
00305   }
00306   for (unsigned i=0; i<=radius; ++i)
00307     kernel[radius+i] = kernel[radius-i] = brip_vil_gaussian(double(i), sigma);
00308   double sum = 0;
00309   for (unsigned i= 0; i <= 2*radius; ++i)
00310     sum += kernel[i];                           // find integral of weights
00311   for (unsigned i= 0; i <= 2*radius; ++i)
00312     kernel[i] /= sum;                           // normalize by integral
00313 }
00314 
00315 vil_image_view<float>
00316 brip_vil_float_ops::gaussian(vil_image_view<float> const& input, float sigma,
00317                              float fill)
00318 {
00319   vil_image_view<float> dest(input.ni(), input.nj());
00320   dest.fill(fill);
00321   unsigned r;
00322   double* ker;
00323   brip_1d_gaussian_kernel(sigma, 0.02, r, ker);
00324   vil_image_view<float> work(input.ni(), input.nj());
00325   work.fill(fill);
00326 
00327   // filter horizontal
00328   int ksize = 2*r + 1 ;
00329   float accum=0;
00330   vil_convolve_1d(input, work, ker + ksize/2,
00331                   -ksize/2, r, accum,
00332                   vil_convolve_ignore_edge,
00333                   vil_convolve_ignore_edge);
00334 
00335   // filter vertical
00336   vil_image_view<float> work_t = vil_transpose(work);
00337   vil_image_view<float> dest_t = vil_transpose(dest);
00338   vil_convolve_1d(work_t, dest_t, ker+ ksize/2,
00339                   -ksize/2, r, accum,
00340                   vil_convolve_ignore_edge,
00341                   vil_convolve_ignore_edge);
00342 
00343   delete ker;
00344   return vil_transpose(dest_t);
00345 }
00346 
00347 #ifdef VIL_CONVOLVE_WITH_MASK_EXISTS // TODO
00348 vil_image_view<float>
00349 brip_vil_float_ops::gaussian(vil_image_view<float> const& input,
00350                              float sigma,
00351                              vil_image_view<float> const& mask)
00352 {
00353   vil_image_view<float> dest(input.ni(), input.nj());
00354 
00355   int r;
00356   double* ker;
00357   brip_1d_gaussian_kernel(sigma, 0.02, r, ker);
00358   vil_image_view<float> work(input.ni(), input.nj());
00359   work.deep_copy(input);
00360   // filter horizontal
00361   int ksize = 2*r + 1 ;
00362   float accum=0;
00363   vil_convolve_1d(input, work, mask, ker + ksize/2,
00364                   -ksize/2, r, accum,
00365                   vil_convolve_trim,
00366                   vil_convolve_trim);
00367 
00368   // filter vertical
00369   vil_image_view<float> work_t = vil_transpose(work);
00370   vil_image_view<float> dest_t = vil_transpose(dest);
00371   vil_convolve_1d(work_t, dest_t, vil_transpose(mask), ker+ ksize/2,
00372                   -ksize/2, r, accum,
00373                   vil_convolve_constant_extend,
00374                   vil_convolve_constant_extend);
00375 
00376   delete ker;
00377   return dest;
00378 }
00379 #endif // VIL_CONVOLVE_WITH_MASK_EXISTS
00380 
00381 //-------------------------------------------------------------------
00382 //: Determine if the center of a (2n+1)x(2n+1) neighborhood is a local maximum
00383 //
00384 bool brip_vil_float_ops::
00385 local_maximum(vbl_array_2d<float> const& neighborhood, int n, float& value)
00386 {
00387   bool local_max = true;
00388   value = 0;
00389   float center = neighborhood[n][n];
00390   for (int y = -n; y<=n; y++)
00391     for (int x = -n; x<=n; x++)
00392       local_max = local_max&&(neighborhood[y+n][x+n]<=center);
00393   if (!local_max)
00394     return false;
00395   value = center;
00396   return true;
00397 }
00398 
00399 //-------------------------------------------------------------------
00400 // Interpolate the sub-pixel position of a neighborhood using a
00401 // second order expansion on a 3x3 sub-neighborhood. Return the
00402 // offset to the maximum, i.e. x=x0+dx, y = y0+dy. The design is
00403 // similar to the droid counterpoint by fsm, which uses the Beaudet Hessian
00404 //
00405 void brip_vil_float_ops::
00406 interpolate_center(vbl_array_2d<float>const& neighborhood, float& dx, float& dy)
00407 {
00408   dx = 0; dy=0;
00409   //extract the neighborhood
00410   float n_m1_m1 = neighborhood[0][0];
00411   float n_m1_0 = neighborhood[0][1];
00412   float n_m1_1 = neighborhood[0][2];
00413   float n_0_m1 = neighborhood[1][0];
00414   float n_0_0 = neighborhood[1][1];
00415   float n_0_1 = neighborhood[1][2];
00416   float n_1_m1 = neighborhood[2][0];
00417   float n_1_0 = neighborhood[2][1];
00418   float n_1_1 = neighborhood[2][2];
00419 
00420   //Compute the 2nd order quadratic coefficients
00421   //      1/6 * [ -1  0 +1 ]
00422   // Ix =       [ -1  0 +1 ]
00423   //            [ -1  0 +1 ]
00424   float Ix =(-n_m1_m1+n_m1_1-n_0_m1+n_0_1-n_1_m1+n_1_1)/6.0f;
00425   //      1/6 * [ -1 -1 -1 ]
00426   // Iy =       [  0  0  0 ]
00427   //            [ +1 +1 +1 ]
00428   float Iy =(-n_m1_m1-n_m1_0-n_m1_1+n_1_m1+n_1_0+n_1_1)/6.0f;
00429   //      1/3 * [ +1 -2 +1 ]
00430   // Ixx =      [ +1 -2 +1 ]
00431   //            [ +1 -2 +1 ]
00432   float Ixx = ((n_m1_m1+n_0_m1+n_1_m1+n_m1_1+n_0_1+n_1_1)
00433                -2.0f*(n_m1_0+n_0_0+n_1_0))/3.0f;
00434   //      1/4 * [ +1  0 -1 ]
00435   // Ixy =      [  0  0  0 ]
00436   //            [ -1  0 +1 ]
00437   float Ixy = (n_m1_m1-n_m1_1+n_1_m1+n_1_1)/4.0f;
00438   //      1/3 * [ +1 +1 +1 ]
00439   // Iyy =      [ -2 -2 -2 ]
00440   //            [ +1 +1 +1 ]
00441   float Iyy = ((n_m1_m1+n_m1_0+n_m1_1+n_1_m1+n_1_0+n_1_1)
00442                -2.0f*(n_0_m1 + n_0_0 + n_1_0))/3.0f;
00443   //
00444   // The next bit is to find the extremum of the fitted surface by setting its
00445   // partial derivatives to zero. We need to solve the following linear system :
00446   // Given the fitted surface is
00447   // I(x,y) = Io + Ix x + Iy y + 1/2 Ixx x^2 + Ixy x y + 1/2 Iyy y^2
00448   // we solve for the maximum (x,y),
00449   //
00450   //  [ Ixx Ixy ] [ dx ] + [ Ix ] = [ 0 ]      (dI/dx = 0)
00451   //  [ Ixy Iyy ] [ dy ]   [ Iy ]   [ 0 ]      (dI/dy = 0)
00452   //
00453   float det = Ixx*Iyy - Ixy*Ixy;
00454   // det>0 corresponds to a true local extremum otherwise a saddle point
00455   if (det>0)
00456   {
00457     dx = (Iy*Ixy - Ix*Iyy) / det;
00458     dy = (Ix*Ixy - Iy*Ixx) / det;
00459     // more than one pixel away
00460     if (vcl_fabs(dx) > 1.0 || vcl_fabs(dy) > 1.0)
00461       dx = 0; dy = 0;
00462   }
00463 }
00464 
00465 //---------------------------------------------------------------
00466 // Compute the local maxima of the input on a (2n+1)x(2n+2)
00467 // neighborhood above the given threshold. At each local maximum,
00468 // compute the sub-pixel location, (x_pos, y_pos).
00469 void brip_vil_float_ops::
00470 non_maximum_suppression(vil_image_view<float> const& input,
00471                         int n, float thresh,
00472                         vcl_vector<float>& x_pos,
00473                         vcl_vector<float>& y_pos,
00474                         vcl_vector<float>& value)
00475 {
00476   vul_timer t;
00477   int N = 2*n+1;
00478   int w = static_cast<int>(input.ni()), h = static_cast<int>(input.nj());
00479   x_pos.clear();  x_pos.clear();   value.clear();
00480   vbl_array_2d<float> neighborhood(N,N);
00481   for (int y =n; y<h-n; y++)
00482     for (int x = n; x<w-n; x++)
00483     {
00484       //If the center is not above threshold then there is
00485       //no hope
00486       if (input(x,y)<thresh)
00487         continue;
00488       //Fill the neighborhood
00489       for (int i = -n; i<=n; i++)
00490         for (int j = -n; j<=n; j++)
00491           neighborhood.put(j+n,i+n,input(x+i, y+j));
00492       //Check if the center is a local maximum
00493       float dx, dy, max_v;
00494       if (brip_vil_float_ops::local_maximum(neighborhood, n, max_v))
00495       {
00496         //if so sub-pixel interpolate (3x3) and output results
00497         brip_vil_float_ops::interpolate_center(neighborhood, dx, dy);
00498         x_pos.push_back(x+dx);
00499         y_pos.push_back(y+dy);
00500         value.push_back(max_v);
00501       }
00502     }
00503 #ifdef DEBUG
00504   vcl_cout << "\nCompute non-maximum suppression on a "<< w <<" x " << h << " image in "<< t.real() << " msecs.\n";
00505 #endif
00506 }
00507 
00508 // -----------------------------------------------------------------
00509 //: Subtract image_1 from image_2.
00510 // Will not operate unless the two input images are the same dimensions
00511 //
00512 vil_image_view<float>
00513 brip_vil_float_ops::difference(vil_image_view<float> const& image_1,
00514                                vil_image_view<float> const& image_2)
00515 {
00516   unsigned w1 = image_1.ni(), h1 = image_1.nj();
00517   unsigned w2 = image_2.ni(), h2 = image_2.nj();
00518   vil_image_view<float> temp(w1, h1);
00519   if (w1!=w2||h1!=h2)
00520   {
00521     vcl_cout << "In brip_vil_float_ops::difference(..) - images are not the same dimensions\n";
00522     return temp;
00523   }
00524   vil_image_view<float> out;
00525   out.set_size(w1, h1);
00526   for (unsigned y = 0; y<h1; y++)
00527     for (unsigned x = 0; x<w1; x++)
00528       out(x,y) = image_2(x,y)-image_1(x,y);
00529   return out;
00530 }
00531 
00532 //: negate an image returning the same pixel type (only greyscale)
00533 vil_image_resource_sptr brip_vil_float_ops::negate(vil_image_resource_sptr const& imgr)
00534 {
00535   vil_image_resource_sptr outr;
00536   if (!imgr)
00537     return outr;
00538 
00539   vil_pixel_format fmt = imgr->pixel_format();
00540   switch (fmt)
00541     {
00542 #define NEGATE_CASE(FORMAT, T) \
00543    case FORMAT: { \
00544     vil_image_view<T> view = imgr->get_copy_view(); \
00545     T mxv = vcl_numeric_limits<T>::max(); \
00546     vil_math_scale_and_offset_values(view, -1.0, mxv); \
00547     outr = vil_new_image_resource_of_view(view);  \
00548     break; \
00549                 }
00550       NEGATE_CASE(VIL_PIXEL_FORMAT_BYTE, vxl_byte);
00551       NEGATE_CASE(VIL_PIXEL_FORMAT_UINT_32, vxl_uint_32);
00552       NEGATE_CASE(VIL_PIXEL_FORMAT_UINT_16, vxl_uint_16);
00553       NEGATE_CASE(VIL_PIXEL_FORMAT_INT_16, vxl_int_16);
00554       NEGATE_CASE(VIL_PIXEL_FORMAT_FLOAT, float);
00555       NEGATE_CASE(VIL_PIXEL_FORMAT_DOUBLE, double);
00556 #undef NEGATE_CASE
00557     default:
00558       vcl_cout << "Unknown image format\n";
00559     }
00560   return outr;
00561 }
00562 
00563 vil_image_view<float>
00564 brip_vil_float_ops::threshold(vil_image_view<float> const & image,
00565                               const float thresh, const float level)
00566 {
00567   vil_image_view<float> out;
00568   unsigned w = image.ni(), h = image.nj();
00569   out.set_size(w, h);
00570   for (unsigned y = 0; y<h; y++)
00571     for (unsigned x = 0; x<w; x++)
00572     {
00573       if (image(x,y)>thresh)
00574         out(x,y) = level;
00575       else
00576         out(x,y) = 0;
00577     }
00578   return out;
00579 }
00580 
00581 vil_image_view<float>
00582 brip_vil_float_ops::abs_clip_to_level(vil_image_view<float> const& image,
00583                                       float thresh, float level)
00584 {
00585   vil_image_view<float> out;
00586   unsigned w = image.ni(), h = image.nj();
00587   out.set_size(w, h);
00588   for (unsigned y = 0; y<h; y++)
00589     for (unsigned x = 0; x<w; x++)
00590     {
00591       if (vcl_fabs(image(x,y))>thresh)
00592         out(x,y) = level;
00593       else
00594         out(x,y) = image(x,y);
00595     }
00596   return out;
00597 }
00598 
00599 vil_image_view<float> brip_vil_float_ops::average_NxN(vil_image_view<float> const & img, int N)
00600 {
00601   vil_image_view<float> result;
00602   unsigned w = img.ni(), h = img.nj();
00603   result.set_size (w, h);
00604 
00605   vbl_array_2d <float> averaging_filt(N, N);
00606   averaging_filt.fill( float(1.00/(N*N)) );
00607   result = brip_vil_float_ops::convolve(img, averaging_filt);
00608   return result;
00609 }
00610 
00611 //----------------------------------------------------------------
00612 //: Compute the gradient of the input, use a 3x3 mask
00613 // \verbatim
00614 //         1  |-1  0  1|         1  |-1 -1 -1|
00615 //   Ix = --- |-1  0  1|   Iy = --- | 0  0  0|
00616 //         6  |-1  0  1|         6  | 1  1  1|
00617 // \endverbatim
00618 // Larger masks are computed by pre-convolving with a Gaussian
00619 //
00620 void brip_vil_float_ops::gradient_3x3(vil_image_view<float> const& input,
00621                                       vil_image_view<float>& grad_x,
00622                                       vil_image_view<float>& grad_y)
00623 {
00624   vul_timer t;
00625   unsigned w = input.ni(), h = input.nj();
00626   float scale = 1.0f/6.0f;
00627   for (unsigned y = 1; y+1<h; ++y)
00628     for (unsigned x = 1; x+1<w; ++x)
00629     {
00630       float gx = input(x+1,y-1)+input(x+1,y)+input(x+1,y-1)
00631                 -input(x-1,y-1)-input(x-1,y)-input(x-1,y-1);
00632       float gy = input(x+1,y+1)+input(x,y+1)+input(x-1,y+1)
00633                 -input(x+1,y-1)-input(x,y-1)-input(x-1,y-1);
00634       grad_x(x,y) = scale*gx;
00635       grad_y(x,y) = scale*gy;
00636     }
00637   brip_vil_float_ops::fill_x_border(grad_x, 1, 0.0f);
00638   brip_vil_float_ops::fill_y_border(grad_x, 1, 0.0f);
00639   brip_vil_float_ops::fill_x_border(grad_y, 1, 0.0f);
00640   brip_vil_float_ops::fill_y_border(grad_y, 1, 0.0f);
00641 #ifdef DEBUG
00642   vcl_cout << "\nCompute Gradient in " << t.real() << " msecs.\n";
00643 #endif
00644 }
00645 
00646 void brip_vil_float_ops::gradient_mag_3x3(vil_image_view<float> const& input,
00647                                           vil_image_view<float>& mag)
00648 {
00649   unsigned w = input.ni(), h = input.nj();
00650   float scale = 1.0f/6.0f;
00651   for (unsigned y = 1; y+1<h; ++y)
00652     for (unsigned x = 1; x+1<w; ++x)
00653     {
00654       float gx = input(x+1,y-1)+input(x+1,y)+input(x+1,y-1)
00655                 -input(x-1,y-1)-input(x-1,y)-input(x-1,y-1);
00656       float gy = input(x+1,y+1)+input(x,y+1)+input(x-1,y+1)
00657                 -input(x+1,y-1)-input(x,y-1)-input(x-1,y-1);
00658       mag(x,y) = scale*vcl_sqrt(gx*gx+gy*gy);
00659     }
00660   brip_vil_float_ops::fill_x_border(mag, 1, 0.0f);
00661   brip_vil_float_ops::fill_y_border(mag, 1, 0.0f);
00662 }
00663 
00664 //----------------------------------------------------------------
00665 //: Compute the Hessian of the input, use a 3x3 mask
00666 // \verbatim
00667 //          1 | 1  -2  1|          1 |  1  1  1|         1  | 1  0 -1|
00668 //   Ixx = ---| 1  -2  1|   Iyy = ---| -2 -2 -2|  Ixy = --- | 0  0  0|
00669 //          3 | 1  -2  1|          3 |  1  1  1|         4  |-1  0  1|
00670 // \endverbatim
00671 // Larger masks are computed by pre-convolving with a Gaussian
00672 //
00673 void brip_vil_float_ops::hessian_3x3(vil_image_view<float> const& input,
00674                                      vil_image_view<float>& Ixx,
00675                                      vil_image_view<float>& Ixy,
00676                                      vil_image_view<float>& Iyy)
00677 {
00678   vul_timer t;
00679   unsigned w = input.ni(), h = input.nj();
00680   for (unsigned y = 1; y+1<h; ++y)
00681     for (unsigned x = 1; x+1<w; ++x)
00682     {
00683       float xx = input(x-1,y-1)+input(x-1,y)+input(x+1,y+1)+
00684                  input(x+1,y-1)+input(x+1,y)+input(x+1,y+1)-
00685            2.0f*(input(x,y-1)+input(x,y)+input(x,y+1));
00686 
00687       float xy = (input(x-1,y-1)+input(x+1,y+1))-
00688                  (input(x-1,y+1)+input(x+1,y-1));
00689 
00690       float yy = input(x-1,y-1)+input(x,y-1)+input(x+1,y-1)+
00691                  input(x-1,y+1)+input(x,y+1)+input(x+1,y+1)-
00692            2.0f*(input(x-1,y)+input(x,y)+input(x+1,y));
00693 
00694       Ixx(x,y) = xx/3.0f;
00695       Ixy(x,y) = xy/4.0f;
00696       Iyy(x,y) = yy/3.0f;
00697     }
00698   brip_vil_float_ops::fill_x_border(Ixx, 1, 0.0f);
00699   brip_vil_float_ops::fill_y_border(Ixx, 1, 0.0f);
00700   brip_vil_float_ops::fill_x_border(Ixy, 1, 0.0f);
00701   brip_vil_float_ops::fill_y_border(Ixy, 1, 0.0f);
00702   brip_vil_float_ops::fill_x_border(Iyy, 1, 0.0f);
00703   brip_vil_float_ops::fill_y_border(Iyy, 1, 0.0f);
00704 #ifdef DEBUG
00705   vcl_cout << "\nCompute a hessian matrix "<< w <<" x " << h << " image in "<< t.real() << " msecs.\n";
00706 #endif
00707 }
00708 
00709 vil_image_view<float>
00710 brip_vil_float_ops::beaudet(vil_image_view<float> const& Ixx,
00711                             vil_image_view<float> const& Ixy,
00712                             vil_image_view<float> const& Iyy,
00713                             bool determinant)
00714 {
00715   unsigned w = Ixx.ni(), h = Ixx.nj();
00716   vil_image_view<float> output;
00717   output.set_size(w, h);
00718   for (unsigned y = 0; y<h; y++)
00719     for (unsigned x = 0; x<w; x++)
00720     {
00721       float xx = Ixx(x,y), xy = Ixy(x,y), yy = Iyy(x,y);
00722 
00723       //compute eigenvalues for experimentation
00724       float det = xx*yy-xy*xy;
00725       float tr = xx+yy;
00726       float arg = tr*tr-4.f*det, lambda0 = 0, lambda1=0;
00727       if (arg>0)
00728       {
00729         lambda0 = tr+vcl_sqrt(arg);
00730         lambda1 = tr-vcl_sqrt(arg);
00731       }
00732       if (determinant)
00733         output(x,y) = det;
00734       else
00735         output(x,y) = tr;
00736     }
00737   return output;
00738 }
00739 
00740 //----------------------------------------------------------------
00741 //: $Ix\cdot Ix^t$ gradient matrix elements
00742 // That is,
00743 // \verbatim
00744 //                        _                           _
00745 //                       | (dI/dx)^2    (dI/dx)(dI/dy) |
00746 //                       |                             |
00747 //  A = Sum(neighborhood)|                             |
00748 //                       |(dI/dx)(dI/dy)   (dI/dx)^2   |
00749 //                       |_                           _|
00750 // \endverbatim
00751 // over a 2n+1 x 2n+1 neigborhood
00752 //
00753 void
00754 brip_vil_float_ops::grad_matrix_NxN(vil_image_view<float> const& input,
00755                                     unsigned n,
00756                                     vil_image_view<float>& IxIx,
00757                                     vil_image_view<float>& IxIy,
00758                                     vil_image_view<float>& IyIy)
00759 {
00760   int w = static_cast<int>(input.ni()), h = static_cast<int>(input.nj());
00761   int N = (2*n+1)*(2*n+1);
00762   int ni = static_cast<int>(n);
00763   vil_image_view<float> grad_x, grad_y, output;
00764   grad_x.set_size(w,h);
00765   grad_y.set_size(w,h);
00766   output.set_size(w,h);
00767   brip_vil_float_ops::gradient_3x3(input, grad_x, grad_y);
00768   vul_timer t;
00769   for (int y = ni; y<h-ni;y++)
00770     for (int x = ni; x<w-ni;x++)
00771     {
00772       float xx=0, xy=0, yy=0;
00773       for (int i = -ni; i<=ni; i++)
00774         for (int j = -ni; j<=ni; j++)
00775         {
00776           float gx = grad_x(x+i, y+j), gy = grad_y(x+i, y+j);
00777           xx += gx*gx;
00778           xy += gx*gy;
00779           yy += gy*gy;
00780         }
00781       IxIx(x,y) = xx/N;
00782       IxIy(x,y) = xy/N;
00783       IyIy(x,y) = yy/N;
00784     }
00785   brip_vil_float_ops::fill_x_border(IxIx, ni, 0.0f);
00786   brip_vil_float_ops::fill_y_border(IxIx, ni, 0.0f);
00787   brip_vil_float_ops::fill_x_border(IxIy, ni, 0.0f);
00788   brip_vil_float_ops::fill_y_border(IxIy, ni, 0.0f);
00789   brip_vil_float_ops::fill_x_border(IyIy, ni, 0.0f);
00790   brip_vil_float_ops::fill_y_border(IyIy, ni, 0.0f);
00791 #ifdef DEBUG
00792   vcl_cout << "\nCompute a gradient matrix "<< w <<" x " << h << " image in "<< t.real() << " msecs.\n";
00793 #endif
00794 }
00795 
00796 vil_image_view<float> brip_vil_float_ops::
00797 trace_grad_matrix_NxN(vil_image_view<float> const& input, unsigned n)
00798 {
00799   unsigned ni = input.ni(), nj = input.nj();
00800   vil_image_view<float> IxIx;
00801   vil_image_view<float> IxIy;
00802   vil_image_view<float> IyIy;
00803   vil_image_view<float> tr;
00804   IxIx.set_size(ni, nj);   IxIy.set_size(ni, nj);   IyIy.set_size(ni, nj);
00805   tr.set_size(ni, nj);
00806   brip_vil_float_ops::grad_matrix_NxN(input, n, IxIx, IxIy, IyIy);
00807   vil_math_image_sum<float, float, float>(IxIx, IyIy, tr);
00808   return tr;
00809 }
00810 
00811 vil_image_view<float>
00812 brip_vil_float_ops::harris(vil_image_view<float> const& IxIx,
00813                            vil_image_view<float> const& IxIy,
00814                            vil_image_view<float> const& IyIy,
00815                            double scale)
00816 {
00817   unsigned w = IxIx.ni(), h = IxIx.nj();
00818   float norm = 1e-3f; // Scale the output to values in the 10->1000 range
00819   vil_image_view<float> output;
00820   output.set_size(w, h);
00821   for (unsigned y = 0; y<h; y++)
00822     for (unsigned x = 0; x<w; x++)
00823     {
00824       float xx = IxIx(x,y), xy = IxIy(x,y), yy = IyIy(x,y);
00825       float det = xx*yy-xy*xy, trace = xx+yy;
00826       output(x,y) = float(det - scale*trace*trace)*norm;
00827     }
00828   return output;
00829 }
00830 
00831 //----------------------------------------------------------------
00832 // Compute the sqrt of the product of the eigenvalues of the
00833 // gradient matrix over a 2n+1 x 2n+1 neigborhood
00834 // That is,
00835 // \verbatim
00836 //                        _                           _
00837 //                       | (dI/dx)^2    (dI/dx)(dI/dy) |
00838 //                       |                             |
00839 //  A = Sum(neighborhood)|                             |
00840 //                       |(dI/dx)(dI/dy)   (dI/dx)^2   |
00841 //                       |_                           _|
00842 // \endverbatim
00843 // The output image is sqrt(lamba_1*lambda_2) where lambda_i are the eigenvalues
00844 //
00845 vil_image_view<float>
00846 brip_vil_float_ops::sqrt_grad_singular_values(vil_image_view<float>& input,
00847                                               int n)
00848 {
00849   int N = (2*n+1)*(2*n+1);
00850   int w = static_cast<int>(input.ni()), h = static_cast<int>(input.nj());
00851   vil_image_view<float> grad_x, grad_y, output;
00852   grad_x.set_size(w,h);
00853   grad_y.set_size(w,h);
00854   output.set_size(w,h);
00855   brip_vil_float_ops::gradient_3x3(input, grad_x, grad_y);
00856   vul_timer t;
00857   for (int y = n; y<h-n;y++)
00858     for (int x = n; x<w-n;x++)
00859     {
00860       float IxIx=0, IxIy=0, IyIy=0;
00861       for (int i = -n; i<=n; i++)
00862         for (int j = -n; j<=n; j++)
00863         {
00864           float gx = grad_x(x+i, y+j), gy = grad_y(x+i, y+j);
00865           IxIx += gx*gx;
00866           IxIy += gx*gy;
00867           IyIy += gy*gy;
00868         }
00869       float det = (IxIx*IyIy-IxIy*IxIy)/N;
00870       output(x,y)=vcl_sqrt(vcl_fabs(det));
00871     }
00872   brip_vil_float_ops::fill_x_border(output, n, 0.0f);
00873   brip_vil_float_ops::fill_y_border(output, n, 0.0f);
00874 #ifdef DEBUG
00875   vcl_cout << "\nCompute sqrt(sigma0*sigma1) in" << t.real() << " msecs.\n";
00876 #endif
00877   return output;
00878 }
00879 
00880 vil_image_view<float> brip_vil_float_ops::
00881 max_scale_trace(vil_image_view<float> input,
00882                 float min_scale, float max_scale, float scale_inc)
00883 {
00884   unsigned ni = input.ni(), nj = input.nj();
00885   vil_image_view<float> tr_max, sc;
00886   tr_max.set_size(ni, nj);
00887   tr_max.fill(0.0f);
00888   sc.set_size(ni, nj);
00889   sc.fill(min_scale);
00890   for (float s = min_scale; s<=max_scale; s+=scale_inc)
00891   {
00892     vil_image_view<float> smooth = brip_vil_float_ops::gaussian(input, s);
00893     unsigned N = static_cast<unsigned>(2.0f*s);
00894     vil_image_view<float> tr =
00895       brip_vil_float_ops::trace_grad_matrix_NxN(smooth, N);
00896     for (unsigned r = 0; r<nj; ++r)
00897       for (unsigned c = 0; c<ni; ++c)
00898       {
00899         float trv = s*s*tr(c,r);
00900         if (trv>tr_max(c,r))
00901         {
00902           tr_max(c,r) = trv;
00903           sc(c,r) = s;
00904         }
00905       }
00906   }
00907   return sc;
00908 }
00909 
00910 //: exactly same as max_scale_trace, only return the image with actual trace values at max scales instead of the image with max scale values
00911 vil_image_view<float> brip_vil_float_ops::
00912 max_scale_trace_value(vil_image_view<float> input,
00913                       float min_scale, float max_scale, float scale_inc)
00914 {
00915   unsigned ni = input.ni(), nj = input.nj();
00916   vil_image_view<float> tr_max, sc;
00917   tr_max.set_size(ni, nj);
00918   tr_max.fill(0.0f);
00919   sc.set_size(ni, nj);
00920   sc.fill(min_scale);
00921   for (float s = min_scale; s<=max_scale; s+=scale_inc)
00922   {
00923     vil_image_view<float> smooth = brip_vil_float_ops::gaussian(input, s);
00924     unsigned N = static_cast<unsigned>(2.0f*s);
00925     vil_image_view<float> tr =
00926       brip_vil_float_ops::trace_grad_matrix_NxN(smooth, N);
00927     for (unsigned r = 0; r<nj; ++r)
00928       for (unsigned c = 0; c<ni; ++c)
00929       {
00930         float trv = s*s*tr(c,r);
00931         if (trv>tr_max(c,r))
00932         {
00933           tr_max(c,r) = trv;
00934           sc(c,r) = s;
00935         }
00936       }
00937   }
00938   // mask the region where integration region extends outside the image borders
00939   unsigned N = static_cast<unsigned>(5.0f*max_scale);
00940   unsigned njmax = nj-N;
00941   unsigned nimax = ni-N;
00942   for (unsigned r = 0; r<nj; ++r)
00943     for (unsigned c = 0; c<ni; ++c)
00944     {
00945       if (r <= N || r >= njmax || c <= N || c >= nimax)
00946         tr_max(c,r) = 0.0f;
00947     }
00948 
00949   // normalize the trace values
00950   float min_b,max_b;
00951   vil_math_value_range(tr_max,min_b,max_b);
00952   vcl_cout << "in trace max image min value: " << min_b << " max value: " << max_b << vcl_endl;
00953 
00954   vil_image_view<double> tr_normalized, tr_stretched;
00955   vil_convert_stretch_range(tr_max, tr_normalized, 0.0f, 1.0f);
00956   vil_convert_stretch_range(tr_max, tr_stretched, 0.0f, 255.0f);
00957   vil_image_view<float> tr_cast;
00958   vil_convert_cast(tr_stretched, tr_cast);
00959   vil_image_view<vxl_byte> tr_cast_byte;
00960   vil_convert_cast(tr_stretched, tr_cast_byte);
00961   vil_save_image_resource(vil_new_image_resource_of_view(tr_cast_byte), "D:\\projects\\vehicle_rec_on_models\\trace_image.png");
00962 
00963 #if 0 // commented out
00964   // investigate illumination invariance
00965   vil_image_view<float> modified_input = input;
00966   vil_image_view<float> const_image(ni, nj);
00967   const_image.fill(3.0f);
00968   for (unsigned r = 0; r<nj; ++r)
00969     for (unsigned c = 0; c<ni; ++c)
00970     {
00971       if (modified_input(c,r) < 120 || modified_input(c,r) > 95) = modified_input(c,r)*const_image(c,r);
00972     }
00973 
00974   vil_image_view<vxl_byte> mod_cast;
00975   vil_convert_cast(modified_input, mod_cast);
00976   vil_save_image_resource(vil_new_image_resource_of_view(mod_cast), "D:\\projects\\vehicle_rec_on_models\\modified_image.png");
00977 
00978   vil_image_view<float> tr_max2, sc2;
00979   tr_max2.set_size(ni, nj);
00980   tr_max2.fill(0.0f);
00981   sc2.set_size(ni, nj);
00982   sc2.fill(min_scale);
00983   for (float s = min_scale; s<=max_scale; s+=scale_inc)
00984   {
00985     vil_image_view<float> smooth = brip_vil_float_ops::gaussian(modified_input, s);
00986     unsigned N = static_cast<unsigned>(2.0f*s);
00987     vil_image_view<float> tr =
00988       brip_vil_float_ops::trace_grad_matrix_NxN(smooth, N);
00989     for (unsigned r = 0; r<nj; ++r)
00990       for (unsigned c = 0; c<ni; ++c)
00991       {
00992         float trv = s*s*tr(c,r);
00993         if (trv>tr_max2(c,r))
00994         {
00995           tr_max2(c,r) = trv;
00996           sc2(c,r) = s;
00997         }
00998       }
00999   }
01000   // mask the region where integration region extends outside the image borders
01001   for (unsigned r = 0; r<nj; ++r)
01002     for (unsigned c = 0; c<ni; ++c)
01003     {
01004       if (r <= N || r >= njmax || c <= N || c >= nimax)
01005         tr_max2(c,r) = 0.0f;
01006     }
01007 
01008   vil_math_value_range(tr_max2,min_b,max_b);
01009   vcl_cout << "in trace max2 image min value: " << min_b << " max value: " << max_b << vcl_endl;
01010 
01011   // normalize
01012   vil_image_view<double> tr_normalized2;
01013   vil_convert_stretch_range(tr_max2, tr_normalized2, 0.0f, 1.0f);
01014 
01015   vil_image_view<double> difference_image = tr_normalized;
01016   for (unsigned r = 0; r<nj; ++r)
01017     for (unsigned c = 0; c<ni; ++c)
01018     {
01019      difference_image(c,r) = vcl_abs(difference_image(c,r) - tr_normalized2(c,r));
01020     }
01021   double min_bd, max_bd;
01022   vil_math_value_range(difference_image,min_bd,max_bd);
01023   vcl_cout << "in difference image of normalized trace images min value: " << min_bd << " max value: " << max_bd << vcl_endl;
01024   vil_image_view<vxl_byte> dif_cast;
01025   vil_convert_cast(difference_image, dif_cast);
01026   vil_save_image_resource(vil_new_image_resource_of_view(dif_cast), "D:\\projects\\vehicle_rec_on_models\\difference_image.png");
01027 
01028   vil_image_view<double> tr_stretched2;
01029   vil_convert_stretch_range(tr_max2, tr_stretched2, 0.0f, 255.0f);
01030   vil_image_view<float> tr_cast2;
01031   vil_convert_cast(tr_stretched2, tr_cast2);
01032   vil_image_view<vxl_byte> tr_cast2_byte;
01033   vil_convert_cast(tr_stretched2, tr_cast2_byte);
01034   vil_save_image_resource(vil_new_image_resource_of_view(tr_cast2_byte), "D:\\projects\\vehicle_rec_on_models\\trace_image2.png");
01035 
01036   return tr_cast2;
01037 #else // 0
01038   return tr_cast;
01039 #endif // 0
01040 }
01041 
01042 
01043 //---------------------------------------------------------------------
01044 // Lucas-Kanade motion vectors:  Solve for the motion vectors over a
01045 // (2n+1)x(2n+1) neighborhood. The time derivative of intensity is computed
01046 // from the previous_frame. The threshold eliminates small values of
01047 // the product of the time derivative and the motion matrix eigenvalues,
01048 // i.e, |lambda_1*lambda_2*dI/dt|<thresh.  Thus motion is only reported when
01049 // the solution is well-conditioned.
01050 //
01051 void
01052 brip_vil_float_ops::Lucas_KanadeMotion(vil_image_view<float> & current_frame,
01053                                        vil_image_view<float> & previous_frame,
01054                                        int n, double thresh,
01055                                        vil_image_view<float>& vx,
01056                                        vil_image_view<float>& vy)
01057 {
01058   int N = (2*n+1)*(2*n+1);
01059   int w = static_cast<int>(current_frame.ni()), h = static_cast<int>(current_frame.nj());
01060   vil_image_view<float> grad_x, grad_y, diff;
01061   grad_x.set_size(w,h);
01062   grad_y.set_size(w,h);
01063   //compute the gradient vector and the time derivative
01064   brip_vil_float_ops::gradient_3x3(current_frame, grad_x, grad_y);
01065   diff = brip_vil_float_ops::difference(previous_frame, current_frame);
01066   vul_timer t;
01067   //sum the motion terms over the (2n+1)x(2n+1) neighborhood.
01068   for (int y = n; y<h-n;y++)
01069     for (int x = n; x<w-n;x++)
01070     {
01071       float IxIx=0, IxIy=0, IyIy=0, IxIt=0, IyIt=0;
01072       for (int i = -n; i<=n; i++)
01073         for (int j = -n; j<=n; j++)
01074         {
01075           float gx = grad_x(x+i, y+j), gy = grad_y(x+i, y+j);
01076           float dt = diff(x+i, y+j);
01077           IxIx += gx*gx;
01078           IxIy += gx*gy;
01079           IyIy += gy*gy;
01080           IxIt += gx*dt;
01081           IyIt += gy*dt;
01082         }
01083       //Divide by the number of pixels in the neighborhood
01084       IxIx/=N;  IxIy/=N; IyIy/=N; IxIt/=N; IyIt/=N;
01085       float det = float(IxIx*IyIy-IxIy*IxIy);
01086       //Eliminate small motion factors
01087       float dif = diff(x,y);
01088       float motion_factor = vcl_fabs(det*dif);
01089       if (motion_factor<thresh)
01090       {
01091         vx(x,y) = 0.0f;
01092         vy(x,y) = 0.0f;
01093         continue;
01094       }
01095       //solve for the motion vector
01096       vx(x,y) = (IyIy*IxIt-IxIy*IyIt)/det;
01097       vy(x,y) = (-IxIy*IxIt + IxIx*IyIt)/det;
01098     }
01099   brip_vil_float_ops::fill_x_border(vx, n, 0.0f);
01100   brip_vil_float_ops::fill_y_border(vx, n, 0.0f);
01101   brip_vil_float_ops::fill_x_border(vy, n, 0.0f);
01102   brip_vil_float_ops::fill_y_border(vy, n, 0.0f);
01103 #ifdef DEBUG
01104   vcl_cout << "\nCompute Lucas-Kanade in " << t.real() << " msecs.\n";
01105 #endif
01106 }
01107 
01108 //: computes Lucas-Kanade optical flow on the complete input views
01109 void brip_vil_float_ops::
01110 lucas_kanade_motion_on_view(vil_image_view<float> const& curr_frame,
01111                             vil_image_view<float> const& prev_frame,
01112                             const double thresh,
01113                             float& vx,
01114                             float& vy)
01115 {
01116   unsigned w = curr_frame.ni(), h = curr_frame.nj();
01117   unsigned N  = w*h;
01118   vil_image_view<float> grad_x, grad_y;
01119   grad_x.set_size(w,h);
01120   grad_y.set_size(w,h);
01121   //compute the gradient vector and the time derivative
01122   brip_vil_float_ops::gradient_3x3(curr_frame, grad_x, grad_y);
01123   vil_image_view<float> diff =
01124     brip_vil_float_ops::difference(prev_frame, curr_frame);
01125 
01126   //sum the motion terms over the view
01127   float IxIx=0, IxIy=0, IyIy=0, IxIt=0, IyIt=0, dsum = 0;
01128   for (unsigned j = 0; j<h; j++)
01129     for (unsigned i = 0; i<w; i++)
01130     {
01131       float gx = grad_x(i, j), gy = grad_y(i, j);
01132       float dt = diff(i, j);
01133       dsum += dt*dt;
01134       IxIx += gx*gx;
01135       IxIy += gx*gy;
01136       IyIy += gy*gy;
01137       IxIt += gx*dt;
01138       IyIt += gy*dt;
01139     }
01140   //Divide by the number of pixels in the neighborhood
01141   IxIx/=N;  IxIy/=N; IyIy/=N; IxIt/=N; IyIt/=N; dsum/=N;
01142   float det = float(IxIx*IyIy-IxIy*IxIy);
01143   //Eliminate small motion factors
01144   float dif = vcl_sqrt(dsum);
01145   float motion_factor = vcl_fabs(det*dif);
01146   if (motion_factor<thresh)
01147   {
01148     vx = 0.0f;
01149     vy = 0.0f;
01150     return;
01151   }
01152   //solve for the motion vector
01153   vx = (IyIy*IxIt-IxIy*IyIt)/det;
01154   vy = (-IxIy*IxIt + IxIx*IyIt)/det;
01155 }
01156 
01157 //------------------------------------------------------------------------
01158 // Assume that curr mtch region is larger than prev_region by the required
01159 // search ranges. Step through the search and output the shift that
01160 // maximizes the correlation. zero_i and zero_j indicate the curr_image
01161 // pixel location corresponding to no velocity between frames
01162 void brip_vil_float_ops::
01163 velocity_by_correlation(vil_image_view<float> const& curr_image,
01164                         vil_image_view<float> const& prev_region,
01165                         const unsigned start_i, const unsigned end_i,
01166                         const unsigned start_j, const unsigned end_j,
01167                         const unsigned zero_i, const unsigned zero_j,
01168                         float& vx,
01169                         float& vy)
01170 {
01171   unsigned ni = prev_region.ni(), nj = prev_region.nj();
01172   float corr_max = -10;
01173   float vx0 = static_cast<float>(zero_i);
01174   float vy0 = static_cast<float>(zero_j);
01175   float area = static_cast<float>(ni*nj);
01176   vx = 0; vy = 0;
01177   unsigned max_i = start_i, max_j = start_j;
01178   for (unsigned j = start_j; j<=end_j; ++j)
01179     for (unsigned i = start_i; i<=end_i; ++i)
01180     {
01181       float si1 = 0, si2 = 0, si1i1 = 0, si2i2 = 0, si1i2 = 0;
01182       //sum over the region
01183       for (unsigned r = 0; r<nj; ++r)
01184         for (unsigned c = 0; c<ni; ++c)
01185         {
01186           float I1 = prev_region(c, r);
01187           float I2 = curr_image(i+c, j+r);
01188           si1 += I1; si2 += I2;
01189           si1i1 += I1*I1;
01190           si2i2 += I2*I2;
01191           si1i2 += I1*I2;
01192         }
01193       float corr = cross_corr(area, si1, si2, si1i1, si2i2,si1i2, 1.0f);
01194       if (corr>corr_max)
01195       {
01196         corr_max = corr;
01197         max_i = i; max_j = j;
01198       }
01199 #if 0
01200       float di = i-vx0, dj = j-vy0;
01201       vcl_cout <<  di << '\t' << dj << '\t' << corr << '\n';
01202 #endif
01203     }
01204   // the velocity is given by the max indices relative to the zero location
01205   vx = static_cast<float>(max_i)- vx0;
01206   vy = static_cast<float>(max_j) - vy0;
01207   // vcl_cout << '(' << vx << ' ' << vy << "): " << corr_max << '\n';
01208 }
01209 
01210 //---------------------------------------------------------------------
01211 // Horn-Schunk method for calc. motion vectors:  Solve for the motion vectors
01212 // iteratively using a cost function with two terms (RHS of optical flow eqn
01213 // and the magnitude of spatial derivatives of the velocity field
01214 // (so that pixel-to-pixel variations are small). The second term is
01215 // weighted by alpha_coef term. The iteration goes on until the error
01216 // reaches below err_thresh
01217 //  Error conditions:
01218 //  -  -1  -  current_frame and previous_frame are equal
01219 //  -  -2  -  at least one input frame or internal process image is all zeros
01220 //  -   0  -  routine was successful
01221 
01222 int brip_vil_float_ops::
01223 Horn_SchunckMotion(vil_image_view<float> const& current_frame,
01224                    vil_image_view<float> const& previous_frame,
01225                    vil_image_view<float>& vx,
01226                    vil_image_view<float>& vy,
01227                    const float alpha_coef,
01228                    const int no_of_iterations)
01229 {
01230   //Check for equal images
01231   if (vil_image_view_deep_equality (previous_frame, current_frame ) )
01232   {
01233     vcl_cout<<"Images are same";
01234     return -1;
01235   }
01236 
01237   //Declarations
01238   unsigned w = current_frame.ni(), h = current_frame.nj();
01239 
01240   vil_image_view<float> grad_x, grad_y, diff;
01241 
01242   vil_image_view<float> temp1;
01243   vil_image_view<float> temp2;
01244 
01245   vil_image_view<float> emptyimg;
01246 
01247   //Size Init
01248 
01249   grad_x.set_size(w,h);
01250   grad_y.set_size(w,h);
01251   diff.set_size(w,h);
01252   temp1.set_size(w,h);
01253   temp2.set_size(w,h);
01254 
01255   emptyimg.set_size(w,h);
01256 
01257   temp1.fill(0.0);
01258   temp2.fill(0.0);
01259 
01260   //Initialization
01261   for (unsigned y = 0; y<h; y++)
01262     for (unsigned x = 0; x<w; x++)
01263     {
01264       vx(x,y)=0.0f;
01265       vy(x,y)=0.0f;
01266       diff (x,y)=0.0f;
01267       grad_x (x, y)= 0.0f;
01268       grad_y (x, y)= 0.0f;
01269 
01270       emptyimg (x, y) = 0.0f;
01271     }
01272 
01273   //Check for empty images
01274   if ( (vil_image_view_deep_equality (emptyimg, current_frame )) || (vil_image_view_deep_equality(emptyimg, previous_frame)))
01275   {
01276     vcl_cout<<"Image is empty";
01277     return -2;
01278   }
01279 
01280   //compute the gradient vector for current and previous
01281   brip_vil_float_ops::gradient_3x3 (current_frame , grad_x , grad_y);
01282   brip_vil_float_ops::gradient_3x3 (previous_frame , temp1 , temp2);
01283 
01284   //  Grad = 0.5* Grad(current) + 0.5 * Grad(previous)
01285   vil_math_add_image_fraction(grad_x, 0.5, temp1, 0.5);
01286   vil_math_add_image_fraction(grad_y, 0.5, temp2, 0.5);
01287   if ( (vil_image_view_deep_equality(emptyimg, grad_x)) ||
01288        (vil_image_view_deep_equality(emptyimg, grad_y)) )
01289   {
01290     vcl_cout<<"Gradient Image is empty";
01291     return -2;
01292   }
01293 
01294   temp1.fill(0.0);
01295   temp2.fill(0.0);
01296 
01297   //Averge the local intensites over 3x3 region
01298   temp1 = brip_vil_float_ops::average_NxN(previous_frame, 3);
01299   temp2 = brip_vil_float_ops::average_NxN(current_frame, 3);
01300   if (vil_image_view_deep_equality(emptyimg, temp1) ||
01301       vil_image_view_deep_equality(emptyimg, temp2))
01302     {
01303       vcl_cout<<"Averaged Image is empty";
01304       return -2;
01305     }
01306 
01307   //Compute the time derivative (difference of local average intensities)
01308   // diff = dI/dt
01309   diff = brip_vil_float_ops::difference(temp1 , temp2);
01310   if (vil_image_view_deep_equality(emptyimg, diff) )
01311   {
01312     vcl_cout<<"Difference Image is empty";
01313     return -2;
01314   }
01315 
01316   temp1.fill(0.0);
01317   temp2.fill(0.0);
01318   //Iterate
01319 #ifdef DEBUG
01320   vul_timer t;
01321 #endif
01322   for (int i=0;i<no_of_iterations;i++)
01323   {
01324     // Update vx and vy
01325     //Smoothed velocities on 3x3 region
01326     temp1 = brip_vil_float_ops::average_NxN (vx,  3);
01327     temp2 = brip_vil_float_ops::average_NxN (vy,  3);
01328 
01329     for (unsigned y = 1; y+1<h; ++y)
01330       for (unsigned x = 1; x+1<w; ++x)
01331       {
01332         float tempx = temp1(x,y);
01333         float tempy = temp2(x,y);
01334 
01335         float gx = grad_x(x, y), gy = grad_y(x, y);
01336 
01337         float dt = diff(x, y);
01338         //         _____
01339         // term = (v(x,y).Grad(x,y) + dI/dt(x,y))/(alpha + |Grad(x,y)|^2)
01340         // term is the brightness constraint normalized by gradient mag.
01341         //
01342         float term =
01343            ( (gx * tempx) + (gy * tempy) + dt )/ (alpha_coef + gx*gx + gy*gy);
01344 
01345         //         ______
01346         //v(x,y) = v(x,y) - Grad(x,y)* term
01347         vx(x,y) = tempx - (gx *  term);
01348         vy(x,y) = tempy - (gy *  term);
01349       }
01350 
01351 #ifdef DEBUG
01352     vcl_cout << "Iteration No " << i << '\n';
01353 #endif
01354     brip_vil_float_ops::fill_x_border(vx, 1, 0.0f);
01355     brip_vil_float_ops::fill_y_border(vx, 1, 0.0f);
01356     brip_vil_float_ops::fill_x_border(vy, 1, 0.0f);
01357     brip_vil_float_ops::fill_y_border(vy, 1, 0.0f);
01358   }
01359 #ifdef DEBUG
01360   vcl_cout << "\nCompute Horn-Schunck iteration in " << t.real() << " msecs.\n";
01361 #endif
01362   return 0;
01363 }
01364 
01365 void brip_vil_float_ops::fill_x_border(vil_image_view<float> & image,
01366                                        unsigned w, float value)
01367 {
01368   unsigned width = image.ni(), height = image.nj();
01369   if (2*w>width)
01370   {
01371     vcl_cout << "In brip_vil_float_ops::fill_x_border(..) - 2xborder exceeds image width\n";
01372     return;
01373   }
01374   for (unsigned y = 0; y<height; y++)
01375     for (unsigned x = 0; x<w; x++)
01376       image(x, y) = value;
01377 
01378   for (unsigned y = 0; y<height; y++)
01379     for (unsigned x = width-w; x<width; x++)
01380       image(x, y) = value;
01381 }
01382 
01383 void brip_vil_float_ops::fill_y_border(vil_image_view<float> & image,
01384                                        unsigned h, float value)
01385 {
01386   unsigned width = image.ni(), height = image.nj();
01387   if (2*h>height)
01388   {
01389     vcl_cout << "In brip_vil_float_ops::fill_y_border(..) - 2xborder exceeds image height\n";
01390     return;
01391   }
01392   for (unsigned y = 0; y<h; y++)
01393     for (unsigned x = 0; x<width; x++)
01394       image(x, y) = value;
01395 
01396   for (unsigned y = height-h; y<height; y++)
01397     for (unsigned x = 0; x<width; x++)
01398       image(x, y) = value;
01399 }
01400 
01401 vil_image_view<unsigned char>
01402 brip_vil_float_ops::convert_to_byte(vil_image_view<float> const& image)
01403 {
01404   //determine the max min values
01405   float min_val = vnl_numeric_traits<float>::maxval;
01406   float max_val = -min_val;
01407   unsigned w = image.ni(), h = image.nj();
01408   vil_image_view<unsigned char> output;
01409   output.set_size(w,h);
01410   for (unsigned y = 0; y<h; y++)
01411     for (unsigned x = 0; x<w; x++)
01412     {
01413       min_val = vnl_math_min(min_val, image(x,y));
01414       max_val = vnl_math_max(max_val, image(x,y));
01415     }
01416   float range = max_val-min_val;
01417   if (range == 0.f)
01418     range = 1.f;
01419   else
01420     range = 255.f/range;
01421   for (unsigned y = 0; y<h; y++)
01422     for (unsigned x = 0; x<w; x++)
01423     {
01424       float v = (image(x,y)-min_val)*range;
01425       output(x,y) = (unsigned char)v;
01426     }
01427   return output;
01428 }
01429 
01430 //------------------------------------------------------------
01431 //: Convert the range between min_val and max_val to 255
01432 vil_image_view<unsigned char>
01433 brip_vil_float_ops::convert_to_byte(vil_image_view<float> const& image,
01434                                     float min_val, float max_val)
01435 {
01436   unsigned w = image.ni(), h = image.nj();
01437   vil_image_view<unsigned char> output;
01438   output.set_size(w,h);
01439   float range = max_val-min_val;
01440   if (range == 0.f)
01441     range = 1.f;
01442   else
01443     range = 255.f/range;
01444   for (unsigned y = 0; y<h; y++)
01445     for (unsigned x = 0; x<w; x++)
01446     {
01447       float v = (image(x,y)-min_val)*range;
01448       if (v>255)
01449         v=255;
01450       if (v<0)
01451         v=0;
01452       output(x,y) = (unsigned char)v;
01453     }
01454   return output;
01455 }
01456 
01457 vil_image_view<unsigned char> brip_vil_float_ops::
01458 convert_to_byte(vil_image_view<unsigned short> const& image,
01459                 unsigned short min_val, unsigned short max_val)
01460 {
01461   unsigned ni = image.ni(), nj = image.nj();
01462   vil_image_view<unsigned char> output;
01463   output.set_size(ni, nj);
01464   float range = static_cast<float>(max_val-min_val);
01465   if (!range)
01466     range = 1;
01467   else
01468     range = 255/range;
01469   for (unsigned r = 0; r<nj; r++)
01470     for (unsigned c = 0; c<ni; c++)
01471     {
01472       float v = (image(c, r)-min_val)*range;
01473       if (v>255)
01474         v=255;
01475       if (v<0)
01476         v=0;
01477       output(c, r) = static_cast<unsigned char>(v);
01478     }
01479   return output;
01480 }
01481 
01482 //Note this is a more standard interface than convert_to_grey
01483 vil_image_view<unsigned char>
01484 brip_vil_float_ops::convert_to_byte(vil_image_resource_sptr const& image)
01485 {
01486   return brip_vil_float_ops::convert_to_grey(*image);
01487 }
01488 
01489 vil_image_view<unsigned short>
01490 brip_vil_float_ops::convert_to_short(vil_image_view<float> const& image,
01491                                      float min_val, float max_val)
01492 {
01493   unsigned w = image.ni(), h = image.nj();
01494   float max_short = 65355.f;
01495   vil_image_view<unsigned short> output;
01496   output.set_size(w,h);
01497   float range = max_val-min_val;
01498   if (!range)
01499     range = 1;
01500   else
01501     range = max_short/range;
01502   for (unsigned y = 0; y<h; y++)
01503     for (unsigned x = 0; x<w; x++)
01504     {
01505       float v = (image(x,y)-min_val)*range;
01506       if (v>max_short)
01507         v=max_short;
01508       if (v<0)
01509         v=0;
01510       output(x,y) = (unsigned short)v;
01511     }
01512   return output;
01513 }
01514 
01515 //: converts a float image to an unsigned short image.
01516 // range determined automatically
01517 vil_image_view<unsigned short>
01518 brip_vil_float_ops::convert_to_short(vil_image_view<float> const& image)
01519 {
01520   float minv = vnl_numeric_traits<float>::maxval;
01521   float maxv = -minv;
01522   unsigned ni = image.ni(), nj = image.nj();
01523   for (unsigned j = 0; j<nj; ++j)
01524     for (unsigned i = 0; i<ni; ++i)
01525     {
01526       float v = image(i,j);
01527       if (v<minv)
01528         minv = v;
01529       if (v>maxv)
01530         maxv = v;
01531     }
01532   return brip_vil_float_ops::convert_to_short(image, minv, maxv);
01533 }
01534 
01535 vil_image_view<unsigned short>
01536 brip_vil_float_ops::convert_to_short(vil_image_resource_sptr const& image)
01537 {
01538   //Check if the image is a float
01539   if (image->nplanes()==1 &&image->pixel_format()==VIL_PIXEL_FORMAT_FLOAT)
01540   {
01541     vil_image_view<float> temp = image->get_view();
01542     float vmin=0, vmax= 65355;
01543     vil_math_value_range<float>(temp, vmin, vmax);
01544     return brip_vil_float_ops::convert_to_short(temp, vmin, vmax);
01545   }
01546 
01547   //Here we assume that the image is an unsigned char
01548   if (image->nplanes()==1&&image->pixel_format()==VIL_PIXEL_FORMAT_BYTE)
01549   {
01550     vil_image_view<unsigned char > temp = image->get_view();
01551     vil_image_view<unsigned short> short_image;
01552     unsigned width = temp.ni(), height = temp.nj();
01553     short_image.set_size(width, height);
01554     for (unsigned y = 0; y<height; y++)
01555       for (unsigned x = 0; x<width; x++)
01556         short_image(x,y) = static_cast<unsigned short>(temp(x,y));
01557     return temp;
01558   }
01559 
01560   //Here the image is an unsigned short image so just return it
01561   if (image->nplanes()==1&&image->pixel_format()==VIL_PIXEL_FORMAT_UINT_16)
01562   {
01563     vil_image_view<unsigned short > temp = image->get_view();
01564     return temp;
01565   }
01566   // the image is color so we should convert it to greyscale
01567   // Here we assume the color elements are unsigned char.
01568   if (image->nplanes()==3&&image->pixel_format()==VIL_PIXEL_FORMAT_BYTE)
01569   {
01570     vil_image_view<vxl_byte> color_image = image->get_view();
01571     unsigned width = color_image.ni(), height = color_image.nj();
01572     // the output image
01573     vil_image_view<unsigned short> short_image;
01574     short_image.set_size(width, height);
01575     for (unsigned y = 0; y<height; y++)
01576       for (unsigned x = 0; x<width; x++)
01577       {
01578         double v = color_image(x,y,0)+color_image(x,y,1)+color_image(x,y,2);
01579         v/=3.0;
01580         short_image(x,y) = static_cast<unsigned short>(v);
01581       }
01582     return short_image;
01583   }
01584   // the image is multispectral so we should convert it to greyscale
01585   // Here we assume the color elements are unsigned short.
01586   if (image->nplanes()==4&&image->pixel_format()==VIL_PIXEL_FORMAT_UINT_16)
01587   {
01588     vil_image_view<unsigned short > mband_image = image->get_view();
01589     unsigned width = mband_image.ni(), height = mband_image.nj();
01590     // the output image
01591     vil_image_view<unsigned short> short_image;
01592     short_image.set_size(width, height);
01593     for (unsigned y = 0; y<height; y++)
01594       for (unsigned x = 0; x<width; x++)
01595       {
01596         unsigned short v = 0;
01597         for (unsigned p = 0; p<4; ++p)
01598           v += mband_image(x, y, p);
01599         v/=4;
01600         short_image(x,y) = v;
01601       }
01602     return short_image;
01603   }
01604   //If we get here then the input is not a type we handle so return a null view
01605   return vil_image_view<unsigned short>();
01606 }
01607 
01608 vil_image_view<float>
01609 brip_vil_float_ops::convert_to_float(vil_image_view<unsigned char> const& image)
01610 {
01611   vil_image_view<float> output;
01612   unsigned w = image.ni(), h = image.nj();
01613   output.set_size(w,h);
01614   for (unsigned y = 0; y<h; y++)
01615     for (unsigned x = 0; x<w; x++)
01616       output(x,y) = (float)image(x,y);
01617   return output;
01618 }
01619 
01620 vil_image_view<float>
01621 brip_vil_float_ops::convert_to_float(vil_image_view<unsigned short> const& image)
01622 {
01623   vil_image_view<float> output;
01624   unsigned w = image.ni(), h = image.nj();
01625   output.set_size(w,h);
01626   for (unsigned y = 0; y<h; y++)
01627     for (unsigned x = 0; x<w; x++)
01628       output(x,y) = (float)image(x,y);
01629   return output;
01630 }
01631 
01632 vil_image_view<bool>
01633 brip_vil_float_ops::convert_to_bool(vil_image_view<unsigned char> const& image)
01634 {
01635   vil_image_view<bool> output;
01636   unsigned w = image.ni(), h = image.nj();
01637   output.set_size(w,h);
01638   for (unsigned y = 0; y<h; y++)
01639     for (unsigned x = 0; x<w; x++)
01640       if (image(x,y) >128)
01641         output(x,y)=true;
01642       else
01643         output(x,y)=false;
01644   return output;
01645 }
01646 
01647 vil_image_view<float>
01648 brip_vil_float_ops::convert_to_float(vil_image_view<vil_rgb<vxl_byte> > const& image)
01649 {
01650   vil_image_view<float> output;
01651   unsigned w = image.ni(), h = image.nj();
01652   output.set_size(w,h);
01653   for (unsigned y = 0; y<h; y++)
01654     for (unsigned x = 0; x<w; x++)
01655     {
01656       vil_rgb<vxl_byte> rgb = image(x,y);
01657       output(x,y) = (float)rgb.grey();
01658     }
01659   return output;
01660 }
01661 
01662 void brip_vil_float_ops::rgb_to_ihs(vil_rgb<vxl_byte> const& rgb,
01663                                     float& i, float& h, float& s)
01664 {
01665   float r,g,b;
01666   r=rgb.R();
01667   g=rgb.G();
01668   b=rgb.B();
01669 
01670   float maxval = vnl_math_max(r,vnl_math_max(g,b));
01671   float minval = vnl_math_min(r,vnl_math_min(g,b));
01672 
01673   float delta = maxval - minval;
01674   i = maxval;
01675   if (maxval == 0)
01676     s = 0;
01677   else
01678     s = delta / maxval;
01679 
01680   if (s== 0)
01681     h = 0;                   //!< (Hue is undefined)
01682 
01683   if (r== maxval)
01684     h = (g - b) / delta ;    //!< (between yellow and magenta)
01685   if (g == maxval)
01686     h = 2 + (b - r)/delta ;  //!< (between cyan and yellow)
01687   if (b == maxval)
01688     h = 4 + (r - g) / delta; //!< (between magenta and cyan)
01689   h = h * 60;                //!< (convert Hue to degrees)
01690   if (h < 0)
01691     h = h + 360 ;            //!< (Hue must be positive)
01692   if (h >= 360)
01693     h = h - 360;             //!< (Hue must be less than 360)
01694 
01695   h = (vxl_byte)(h * (255.0 / 360.0));
01696   s = (vxl_byte)(s * 255.0);
01697 }
01698 
01699 void brip_vil_float_ops::ihs_to_rgb(vil_rgb<vxl_byte> & rgb,
01700                                     const float i, const float h, const float s)
01701 {
01702   // Reference: page 593 of Foley & van Dam
01703   float R = 0.0f;
01704   float G = 0.0f;
01705   float B = 0.0f;
01706 
01707   if (s == 0) {
01708     R=i;
01709     G=i;
01710     B=i;
01711   }
01712   else if (s > 0.0)
01713   {
01714     float ss = s, hh = h;
01715     ss *= 1.f / 255.f;
01716     hh *= 6.f / 255.f;
01717 
01718     float J = vcl_floor(hh);
01719     float F = hh - J;
01720     float P =( i * (1 - ss));
01721     float Q = (i * (1 - (ss * F)));
01722     float T = (i * (1 - (ss * (1 - F))));
01723 
01724     if (J == 0) { R=i; G=T; B=P; }
01725     if (J == 1) { R=Q; G=i; B=P; }
01726     if (J == 2) { R=P; G=i; B=T; }
01727     if (J == 3) { R=P; G=Q; B=i; }
01728     if (J == 4) { R=T; G=P; B=i; }
01729     if (J == 5) { R=i; G=P; B=Q; }
01730   }
01731   rgb.r = (vxl_byte)R;
01732   rgb.g = (vxl_byte)G;
01733   rgb.b = (vxl_byte)B;
01734 }
01735 
01736 void brip_vil_float_ops::
01737 convert_to_IHS(vil_image_view<vil_rgb<vxl_byte> >const& image,
01738                vil_image_view<float>& I,
01739                vil_image_view<float>& H,
01740                vil_image_view<float>& S)
01741 {
01742   unsigned w = image.ni(), h = image.nj();
01743   I.set_size(w,h);
01744   H.set_size(w,h);
01745   S.set_size(w,h);
01746   for (unsigned r = 0; r < h; r++)
01747     for (unsigned c = 0; c < w; c++)
01748     {
01749       float in, hue, sat;
01750       rgb_to_ihs(image(c,r), in, hue, sat);
01751       I(c,r) = in;
01752       H(c,r) = hue;
01753       S(c,r) = sat;
01754     }
01755 }
01756 
01757 void brip_vil_float_ops::
01758 convert_to_IHS(vil_image_view<unsigned char >const& image,
01759                vil_image_view<float>& I,
01760                vil_image_view<float>& H,
01761                vil_image_view<float>& S)
01762 {
01763   unsigned w = image.ni(), h = image.nj();
01764   I.set_size(w,h);
01765   H.set_size(w,h);
01766   S.set_size(w,h);
01767   for (unsigned r = 0; r < h; r++)
01768     for (unsigned c = 0; c < w; c++)
01769     {
01770       float in, hue, sat;
01771       vil_rgb<vxl_byte> imint(image(c,r,0),image(c,r,1),image(c,r,2));
01772       rgb_to_ihs(imint, in, hue, sat);
01773       I(c,r) = in;
01774       H(c,r) = hue;
01775       S(c,r) = sat;
01776     }
01777 }
01778 
01779 #if 0 // commented out
01780 void brip_vil_float_ops::
01781 display_IHS_as_RGB(vil_image_view<float> const& I,
01782                    vil_image_view<float> const& H,
01783                    vil_image_view<float> const& S,
01784                    vil_image_view<vil_rgb<vxl_byte> >& image)
01785 {
01786   unsigned w = I.ni(), h = I.nj();
01787   image.set_size(w,h);
01788   float s = 255.0f/360.0f;
01789   for (unsigned r = 0; r < h; r++)
01790     for (unsigned c = 0; c < w; c++)
01791     {
01792       float in = I(c,r);
01793       float hue = s * H(c,r);
01794       float sat = S(c,r);
01795       if (in<0) in = 0;
01796       if (sat<0) sat = 0;
01797       if (hue<0) hue = 0;
01798       if (in>255) in = 255;
01799       if (hue>255) hue = 255;
01800       if (sat>255) sat = 255;
01801       image(c,r).r = (vxl_byte)in;
01802       image(c,r).g = (vxl_byte)hue;
01803       image(c,r).b = (vxl_byte)sat;
01804     }
01805 }
01806 #endif // 0
01807 
01808 //: map so that intensity is proportional to saturation and hue is color
01809 void brip_vil_float_ops::
01810 display_IHS_as_RGB(vil_image_view<float> const& I,
01811                    vil_image_view<float> const& H,
01812                    vil_image_view<float> const& S,
01813                    vil_image_view<vil_rgb<vxl_byte> >& image)
01814 {
01815   unsigned w = I.ni(), h = I.nj();
01816   image.set_size(w,h);
01817 
01818   const float deg_to_rad = float(vnl_math::pi/180);
01819   for (unsigned r = 0; r < h; r++)
01820     for (unsigned c = 0; c < w; c++)
01821     {
01822       float hue = H(c,r);
01823       float sat = 2.f*S(c,r);
01824       if (sat<0)
01825         sat = 0.f;
01826       if (sat>255)
01827         sat = 255.f;
01828       float ang = deg_to_rad*hue;
01829       float cs = vcl_cos(ang), si = vcl_fabs(vcl_sin(ang));
01830       float red=0.0f, blue=0.0f;
01831       float green = si*sat;
01832       if (cs>=0)
01833         red = cs*sat;
01834       else
01835         blue = sat*(-cs);
01836       image(c,r).r = (vxl_byte)red;
01837       image(c,r).g = (vxl_byte)green;
01838       image(c,r).b = (vxl_byte)blue;
01839     }
01840 }
01841 
01842 vil_image_view<vil_rgb<vxl_byte> > brip_vil_float_ops::
01843 combine_color_planes(vil_image_view<unsigned char> const& R,
01844                      vil_image_view<unsigned char> const& G,
01845                      vil_image_view<unsigned char> const& B)
01846 {
01847   unsigned w = R.ni(), h = R.nj();
01848   vil_image_view<vil_rgb<vxl_byte> > image(w,h);
01849   for (unsigned r = 0; r < h; r++)
01850     for (unsigned c = 0; c < w; c++)
01851     {
01852       image(c,r).r = R(c,r);
01853       image(c,r).g = G(c,r);
01854       image(c,r).b = B(c,r);
01855     }
01856   return image;
01857 }
01858 
01859 vil_image_view<vil_rgb<vxl_byte> >
01860 brip_vil_float_ops::combine_color_planes(vil_image_resource_sptr const& R,
01861                                          vil_image_resource_sptr const& G,
01862                                          vil_image_resource_sptr const& B)
01863 {
01864   vil_image_view<vil_rgb<vxl_byte> > view(0,0);
01865   if (!R||!G||!B)
01866     return view;//return an empty view
01867   //determine the union of all the resources
01868   vbl_bounding_box<unsigned short, 2> b;
01869   unsigned short r_ni = R->ni(), r_nj = R->nj();
01870   unsigned short g_ni = G->ni(), g_nj = G->nj();
01871   unsigned short b_ni = B->ni(), b_nj = B->nj();
01872   b.update(r_ni, r_nj);
01873   b.update(g_ni, g_nj);
01874   b.update(b_ni, b_nj);
01875   unsigned short n_i = b.xmax(), n_j = b.ymax();
01876   view.set_size(n_i, n_j);
01877   vil_rgb<vxl_byte> zero(0, 0, 0);
01878   vil_image_view<float> fR = brip_vil_float_ops::convert_to_float(R);
01879   vil_image_view<unsigned char> cR = brip_vil_float_ops::convert_to_byte(fR);
01880   vil_image_view<float> fG = brip_vil_float_ops::convert_to_float(G);
01881   vil_image_view<unsigned char> cG = brip_vil_float_ops::convert_to_byte(fG);
01882   vil_image_view<float> fB = brip_vil_float_ops::convert_to_float(B);
01883   vil_image_view<unsigned char> cB = brip_vil_float_ops::convert_to_byte(fB);
01884   for (unsigned short j = 0; j<n_j; ++j)
01885     for (unsigned short i = 0; i<n_i; ++i)
01886     {
01887       vil_rgb<vxl_byte> v = zero;
01888       if (i<r_ni&&j<r_nj)
01889         v.r = cR(i,j);
01890       if (i<g_ni&&j<g_nj)
01891         v.g= cG(i,j);
01892       if (i<b_ni&&j<b_nj)
01893         v.b= cB(i,j);
01894       view(i,j)=v;
01895     }
01896   return view;
01897 }
01898 
01899 vil_image_view<float>
01900 brip_vil_float_ops::convert_to_float(vil_image_resource const& image)
01901 {
01902   vil_image_view<float> fimg;
01903   if (vil_pixel_format_num_components(image.pixel_format())==1)
01904   {
01905     if (image.pixel_format()==VIL_PIXEL_FORMAT_UINT_16)
01906     {
01907       vil_image_view<unsigned short> temp=image.get_view();
01908       fimg = brip_vil_float_ops::convert_to_float(temp);
01909     }
01910     else if (image.pixel_format()==VIL_PIXEL_FORMAT_BYTE)
01911     {
01912       vil_image_view<unsigned char> temp=image.get_view();
01913       fimg = brip_vil_float_ops::convert_to_float(temp);
01914     }
01915     else if (image.pixel_format()==VIL_PIXEL_FORMAT_FLOAT)
01916       return image.get_view();
01917   }
01918   else if (vil_pixel_format_num_components(image.pixel_format())==3)
01919   {
01920     vil_image_view<vil_rgb<vxl_byte> > temp= image.get_view();
01921     fimg = brip_vil_float_ops::convert_to_float(temp);
01922   }
01923   else
01924   {
01925     vcl_cout << "In brip_vil_float_ops::convert_to_float - input not color or grey\n";
01926     return vil_image_view<float>();
01927   }
01928   return fimg;
01929 }
01930 
01931 //-----------------------------------------------------------------
01932 //: Convert any image to an unsigned_char image
01933 vil_image_view<unsigned char>
01934 brip_vil_float_ops::convert_to_grey(vil_image_resource const& image)
01935 {
01936   //Check if the image is a float
01937   if (image.nplanes()==1 &&image.pixel_format()==VIL_PIXEL_FORMAT_FLOAT)
01938   {
01939     vil_image_view<float> temp = image.get_view();
01940     float vmin=0, vmax=255;
01941     vil_math_value_range<float>(temp, vmin, vmax);
01942     return brip_vil_float_ops::convert_to_byte(temp, vmin, vmax);
01943   }
01944   if (image.nplanes()==1 &&image.pixel_format()==VIL_PIXEL_FORMAT_BOOL)
01945   {
01946     vil_image_view<bool> temp = image.get_view();
01947     unsigned nj = temp.nj(), ni = temp.ni();
01948     vil_image_view<unsigned char> out(ni, nj);
01949     out.fill(0);
01950     for (unsigned j = 0; j<nj; ++j)
01951       for (unsigned i = 0; i<ni; ++i)
01952         if (temp(i,j)) out(i,j) = 255;
01953     return out;
01954   }
01955 
01956   //Here we assume that the image is an unsigned char
01957   //In this case we should just return it.
01958   if (image.nplanes()==1&&image.pixel_format()==VIL_PIXEL_FORMAT_BYTE)
01959   {
01960     vil_image_view<unsigned char > temp = image.get_view();
01961     return temp;
01962   }
01963 
01964   if (image.nplanes()==1&&image.pixel_format()==VIL_PIXEL_FORMAT_UINT_16)
01965   {
01966     vil_image_view<unsigned short > temp = image.get_view();
01967     unsigned short vmin=0, vmax=255;
01968     vil_math_value_range<unsigned short>(temp, vmin, vmax);
01969     return brip_vil_float_ops::convert_to_byte(temp, vmin, vmax);
01970   }
01971   // the image is color so we should convert it to greyscale
01972   // Here we assume the color elements are unsigned char.
01973   if (image.nplanes()==3&&image.pixel_format()==VIL_PIXEL_FORMAT_BYTE)
01974   {
01975     vil_image_view<vil_rgb<vxl_byte> > color_image = image.get_view();
01976     unsigned width = color_image.ni(), height = color_image.nj();
01977     // the output image
01978     vil_image_view<unsigned char> grey_image;
01979     grey_image.set_size(width, height);
01980     for (unsigned y = 0; y<height; y++)
01981       for (unsigned x = 0; x<width; x++)
01982         grey_image(x,y) = color_image(x,y).grey();
01983     return grey_image;
01984   }
01985   if (image.nplanes()==3&&image.pixel_format()==VIL_PIXEL_FORMAT_FLOAT)
01986   {
01987     vil_image_view<float> color_image = image.get_view();
01988     unsigned width = color_image.ni(), height = color_image.nj();
01989     // the output image
01990     vil_image_view<float> grey_image_f;
01991     grey_image_f.set_size(width, height);
01992     for (unsigned y = 0; y<height; y++)
01993       for (unsigned x = 0; x<width; x++) {
01994         float v = 0;
01995         for (unsigned p = 0; p<3; ++p)
01996           v += color_image(x,y,p);
01997         grey_image_f(x,y) = v/3.0f;
01998       }
01999     float vmin=0, vmax=255;
02000     vil_math_value_range<float>(grey_image_f, vmin, vmax);
02001     return brip_vil_float_ops::convert_to_byte(grey_image_f, vmin, vmax);
02002   }
02003   //If we get here then the input is not a type we handle so return a null view
02004   return vil_image_view<unsigned char>();
02005 }
02006 
02007 //--------------------------------------------------------------
02008 //: Read a convolution kernel from file
02009 // Assumes a square kernel with odd dimensions, i.e., w,h = 2n+1
02010 // format:
02011 // \verbatim
02012 //     n
02013 //     scale
02014 //     k00  k01  ... k02n
02015 //           ...
02016 //     k2n0 k2n1 ... k2n2n
02017 // \endverbatim
02018 //
02019 vbl_array_2d<float> brip_vil_float_ops::load_kernel(vcl_string const& file)
02020 {
02021   vcl_ifstream instr(file.c_str(), vcl_ios::in);
02022   if (!instr)
02023   {
02024     vcl_cout << "In brip_vil_float_ops::load_kernel - failed to load kernel\n";
02025     return vbl_array_2d<float>(0,0);
02026   }
02027   unsigned n;
02028   float scale;
02029   float v =0;
02030   instr >> n;
02031   instr >> scale;
02032   unsigned N = 2*n+1;
02033   vbl_array_2d<float> output(N, N);
02034   for (unsigned y = 0; y<N; y++)
02035     for (unsigned x = 0; x<N; x++)
02036     {
02037       instr >> v;
02038       output.put(x, y, v/scale);
02039     }
02040 #ifdef DEBUG
02041   vcl_cout << "The Kernel\n";
02042   for (unsigned y = 0; y<N; y++)
02043   {
02044     for (unsigned x = 0; x<N; x++)
02045       vcl_cout << ' ' <<  output[x][y];
02046     vcl_cout << '\n';
02047   }
02048 #endif
02049   return output;
02050 }
02051 
02052 static void insert_image(vil_image_view<float> const& image, int col,
02053                          vnl_matrix<float> & I)
02054 {
02055   unsigned width = image.ni(), height = image.nj(), row=0;
02056   for (unsigned y =0; y<height; y++)
02057     for (unsigned x = 0; x<width; x++, row++)
02058       I.put(row, col, image(x,y));
02059 }
02060 
02061 void brip_vil_float_ops::
02062 basis_images(vcl_vector<vil_image_view<float> > const& input_images,
02063              vcl_vector<vil_image_view<float> >& basis)
02064 {
02065   basis.clear();
02066   unsigned n_images = input_images.size();
02067   if (!n_images)
02068   {
02069     vcl_cout << "In brip_vil_float_ops::basis_images(.) - no input images\n";
02070     return;
02071   }
02072   unsigned width = input_images[0].ni(), height = input_images[0].nj();
02073   unsigned npix = width*height;
02074 
02075   //Insert the images into matrix I
02076   vnl_matrix<float> I(npix, n_images, 0.f);
02077   for (unsigned i = 0; i<n_images; i++)
02078     insert_image(input_images[i], i, I);
02079 
02080   //Compute the SVD of matrix I
02081 #ifdef DEBUG
02082   vcl_cout << "Computing Singular values of a " <<  npix << " by "
02083            << n_images << " matrix\n";
02084   vul_timer t;
02085 #endif
02086   vnl_svd<float> svd(I);
02087 #ifdef DEBUG
02088   vcl_cout << "SVD Took " << t.real() << " msecs\n"
02089            << "Eigenvalues:\n";
02090   for (unsigned i = 0; i<n_images; i++)
02091     vcl_cout << svd.W(i) << '\n';
02092 #endif
02093   //Extract the Basis images
02094   unsigned rank = svd.rank();
02095   if (!rank)
02096   {
02097     vcl_cout << "In brip_vil_float_ops::basis_images(.) - I has zero rank\n";
02098     return;
02099   }
02100   vnl_matrix<float> U = svd.U();
02101   //Output the basis images
02102   unsigned rows = U.rows();
02103   for (unsigned k = 0; k<rank; k++)
02104   {
02105     vil_image_view<float> out(width, height);
02106     unsigned x =0, y = 0;
02107     for (unsigned r = 0; r<rows; r++)
02108     {
02109       out(x, y) = U(r,k);
02110       x++;
02111       if (x>=width)
02112       {
02113         y++;
02114         x=0;
02115       }
02116       if (y>=width)
02117       {
02118         vcl_cout<<"In brip_vil_float_ops::basis_images(.) - shouldn't happen\n";
02119         return;
02120       }
02121     }
02122     basis.push_back(out);
02123   }
02124 }
02125 
02126 //: 1d fourier transform
02127 //-------------------------------------------------------------------------
02128 // This computes an in-place complex-to-complex FFT
02129 // x and y are the real and imaginary arrays of 2^m points.
02130 // dir =  1 gives forward transform
02131 // dir = -1 gives reverse transform
02132 //
02133 //   Formula: forward
02134 // \verbatim
02135 //                N-1
02136 //                ---
02137 //            1   \          - j k 2 pi n / N
02138 //    X(n) = ---   >   x(k) e                    = forward transform
02139 //            N   /                                n=0..N-1
02140 //                ---
02141 //                k=0
02142 // \endverbatim
02143 //
02144 //    Formula: reverse
02145 // \verbatim
02146 //                N-1
02147 //                ---
02148 //                \          j k 2 pi n / N
02149 //    X(n) =       >   x(k) e                    = forward transform
02150 //                /                                n=0..N-1
02151 //                ---
02152 //                k=0
02153 // \endverbatim
02154 //
02155 bool brip_vil_float_ops::fft_1d(int dir, int m, double* x, double* y)
02156 {
02157   long nn,i,i1,j,k,i2,l,l1,l2;
02158   double c1,c2,tx,ty,t1,t2,u1,u2,z;
02159 
02160   // Calculate the number of points
02161   nn = 1;
02162   for (i=0;i<m;i++)
02163     nn *= 2;
02164 
02165   // Do the bit reversal
02166   i2 = nn >> 1;
02167   j = 0;
02168   for (i=0;i<nn-1;i++) {
02169     if (i < j) {
02170       tx = x[i];
02171       ty = y[i];
02172       x[i] = x[j];
02173       y[i] = y[j];
02174       x[j] = tx;
02175       y[j] = ty;
02176     }
02177     k = i2;
02178     while (k <= j) {
02179       j -= k;
02180       k >>= 1;
02181     }
02182     j += k;
02183   }
02184 
02185   // Compute the FFT
02186   c1 = -1.0;
02187   c2 = 0.0;
02188   l2 = 1;
02189   for (l=0;l<m;l++) {
02190     l1 = l2;
02191     l2 <<= 1;
02192     u1 = 1.0;
02193     u2 = 0.0;
02194     for (j=0;j<l1;j++) {
02195       for (i=j;i<nn;i+=l2) {
02196         i1 = i + l1;
02197         t1 = u1 * x[i1] - u2 * y[i1];
02198         t2 = u1 * y[i1] + u2 * x[i1];
02199         x[i1] = x[i] - t1;
02200         y[i1] = y[i] - t2;
02201         x[i] += t1;
02202         y[i] += t2;
02203       }
02204       z =  u1 * c1 - u2 * c2;
02205       u2 = u1 * c2 + u2 * c1;
02206       u1 = z;
02207     }
02208     c2 = vcl_sqrt((1.0 - c1) / 2.0);
02209     if (dir == 1)
02210       c2 = -c2;
02211     c1 = vcl_sqrt((1.0 + c1) / 2.0);
02212   }
02213 
02214   // Scaling for forward transform
02215   if (dir == 1) {
02216     for (i=0;i<nn;i++) {
02217       x[i] /= (double)nn;
02218       y[i] /= (double)nn;
02219     }
02220   }
02221 
02222   return true;
02223 }
02224 
02225 //-------------------------------------------------------------------------
02226 //: Perform a 2D FFT inplace given a complex 2D array
02227 //  The direction dir, 1 for forward, -1 for reverse
02228 //  The size of the array (nx,ny)
02229 //  Return false if there are memory problems or
02230 //  the dimensions are not powers of 2
02231 //
02232 bool brip_vil_float_ops::fft_2d(vnl_matrix<vcl_complex<double> >& c,int nx,int ny,int dir)
02233 {
02234   int i,j;
02235   int mx, my;
02236   double *real,*imag;
02237   vnl_fft_prime_factors<double> pfx (nx);
02238   vnl_fft_prime_factors<double> pfy (ny);
02239   mx = (int)pfx.pqr()[0];
02240   my = (int)pfy.pqr()[0];
02241   // Transform the rows
02242   real = new double[nx];
02243   imag = new double[nx];
02244   if (real == 0 || imag == 0)
02245     return false;
02246   for (j=0;j<ny;j++) {
02247     for (i=0;i<nx;i++) {
02248       real[i] = c[j][i].real();
02249       imag[i] = c[j][i].imag();
02250     }
02251     brip_vil_float_ops::fft_1d(dir,mx,real,imag);
02252     for (i=0;i<nx;i++) {
02253       vcl_complex<double> v(real[i], imag[i]);
02254       c[j][i] = v;
02255     }
02256   }
02257   delete [] real;
02258   delete [] imag;
02259   // Transform the columns
02260   real = new double[ny];
02261   imag = new double[ny];
02262   if (real == 0 || imag == 0)
02263     return false;
02264   for (i=0;i<nx;i++) {
02265     for (j=0;j<ny;j++) {
02266       real[j] = c[j][i].real();
02267       imag[j] = c[j][i].imag();
02268     }
02269     fft_1d(dir,my,real,imag);
02270     for (j=0;j<ny;j++) {
02271       vcl_complex<double> v(real[j], imag[j]);
02272       c[j][i] =  v;
02273     }
02274   }
02275   delete [] real;
02276   delete [] imag;
02277   return true;
02278 }
02279 
02280 //: reorder the transform values to sequential frequencies as in conventional Fourier transforms.
02281 //  The transformation is its self-inverse.
02282 void brip_vil_float_ops::
02283 ftt_fourier_2d_reorder(vnl_matrix<vcl_complex<double> > const& F1,
02284                        vnl_matrix<vcl_complex<double> > & F2)
02285 {
02286   int rows = static_cast<int>(F1.rows()), cols = static_cast<int>(F1.cols());
02287   int half_rows = rows/2, half_cols = cols/2;
02288   int ri, ci;
02289   for (int r = 0; r<rows; r++)
02290   {
02291     if (r<half_rows)
02292       ri = half_rows+r;
02293     else
02294       ri = r-half_rows;
02295     for (int c = 0; c<cols; c++)
02296     {
02297       if (c<half_cols)
02298         ci = half_cols+c;
02299       else
02300         ci = c-half_cols;
02301       F2[ri][ci]=F1[r][c];
02302     }
02303   }
02304 }
02305 
02306 //: Compute the fourier transform.
02307 //  If the image dimensions are not a power of 2 then the operation fails.
02308 bool brip_vil_float_ops::
02309 fourier_transform(vil_image_view<float> const& input,
02310                   vil_image_view<float>& mag,
02311                   vil_image_view<float>& phase)
02312 {
02313   unsigned w = input.ni(), h = input.nj();
02314   vnl_fft_prime_factors<float> pfx (w);
02315   vnl_fft_prime_factors<float> pfy (h);
02316   if (!pfx.pqr()[0]||!pfy.pqr()[0])
02317     return false;
02318   //fill the fft matrix
02319   vnl_matrix<vcl_complex<double> > fft_matrix(h, w), fourier_matrix(h,w);
02320   for (unsigned y = 0; y<h; y++)
02321     for (unsigned x =0; x<w; x++)
02322     {
02323       vcl_complex<double> cv(input(x,y), 0.0);
02324       fft_matrix.put(y, x, cv);
02325     }
02326 #ifdef DEBUG
02327   for (unsigned r = 0; r<h; r++)
02328     for (unsigned c =0; c<w; c++)
02329     {
02330       vcl_complex<double> res = fft_matrix[r][c];
02331       vcl_cout << res << '\n';
02332     }
02333 #endif
02334 
02335   brip_vil_float_ops::fft_2d(fft_matrix, w, h, 1);
02336   brip_vil_float_ops::ftt_fourier_2d_reorder(fft_matrix, fourier_matrix);
02337   mag.set_size(w,h);
02338   phase.set_size(w,h);
02339 
02340   //extract magnitude and phase
02341   for (unsigned r = 0; r<h; r++)
02342     for (unsigned c = 0; c<w; c++)
02343     {
02344       float re = (float)fourier_matrix[r][c].real(),
02345         im = (float)fourier_matrix[r][c].imag();
02346       mag(c,r) = vcl_sqrt(re*re + im*im);
02347       phase(c,r) = vcl_atan2(im, re);
02348     }
02349 
02350   return true;
02351 }
02352 
02353 bool brip_vil_float_ops::
02354 inverse_fourier_transform(vil_image_view<float> const& mag,
02355                           vil_image_view<float> const& phase,
02356                           vil_image_view<float>& output)
02357 {
02358   unsigned w = mag.ni(), h = mag.nj();
02359   vnl_matrix<vcl_complex<double> > fft_matrix(h, w), fourier_matrix(h, w);
02360   for (unsigned y = 0; y<h; y++)
02361     for (unsigned x =0; x<w; x++)
02362     {
02363       float m = mag(x,y);
02364       float p = phase(x,y);
02365       vcl_complex<double> cv(m*vcl_cos(p), m*vcl_sin(p));
02366       fourier_matrix.put(y, x, cv);
02367     }
02368 
02369   brip_vil_float_ops::ftt_fourier_2d_reorder(fourier_matrix, fft_matrix);
02370   brip_vil_float_ops::fft_2d(fft_matrix, w, h, -1);
02371 
02372   output.set_size(w,h);
02373 
02374   for (unsigned y = 0; y<h; y++)
02375     for (unsigned x = 0; x<w; x++)
02376       output(x,y) = (float)fft_matrix[y][x].real();
02377   return true;
02378 }
02379 
02380 void brip_vil_float_ops::resize(vil_image_view<float> const& input,
02381                                 unsigned width, unsigned height,
02382                                 vil_image_view<float>& output)
02383 {
02384   unsigned w = input.ni(), h = input.nj();
02385   output.set_size(width, height);
02386   for (unsigned y = 0; y<height; y++)
02387     for (unsigned x = 0; x<width; x++)
02388       if (x<w && y<h)
02389         output(x,y) = input(x,y);
02390       else
02391         output(x,y) = 0;//pad with zeroes
02392 }
02393 
02394 //: resize the input to the closest power of two image dimensions
02395 bool brip_vil_float_ops::
02396 resize_to_power_of_two(vil_image_view<float> const& input,
02397                        vil_image_view<float>& output)
02398 {
02399   unsigned max_exp = 13; //we wouldn't want to have such large images in memory
02400   unsigned w = input.ni(), h = input.nj();
02401   unsigned prodw = 1, prodh = 1;
02402   //Find power of two width
02403   unsigned nw, nh;
02404   for (nw = 1; nw<=max_exp; nw++)
02405     if (prodw>w)
02406       break;
02407     else
02408       prodw *= 2;
02409   if (nw==max_exp)
02410     return false;
02411   //Find power of two height
02412   for (nh = 1; nh<=max_exp; nh++)
02413     if (prodh>h)
02414       break;
02415     else
02416       prodh *= 2;
02417   if (nh==max_exp)
02418     return false;
02419   brip_vil_float_ops::resize(input, prodw, prodh, output);
02420 
02421   return true;
02422 }
02423 
02424 //
02425 //: block a periodic signal by suppressing two Gaussian lobes in the frequency domain.
02426 //  The lobes are on the line defined by dir_fx and dir_fy through the
02427 //  dc origin, assumed (0, 0).  The center frequency, f0, is the distance along
02428 //  the line to the center of each blocking lobe (+- f0). radius is the
02429 //  standard deviation of each lobe. Later we can define a "filter" class.
02430 //
02431 float brip_vil_float_ops::gaussian_blocking_filter(float dir_fx,
02432                                                    float dir_fy,
02433                                                    float f0,
02434                                                    float radius,
02435                                                    float fx,
02436                                                    float fy)
02437 {
02438   // normalize dir_fx and dir_fy
02439   float mag = vcl_sqrt(dir_fx*dir_fx + dir_fy*dir_fy);
02440   if (!mag)
02441     return 0.f;
02442   float r2 = 2.f*radius*radius;
02443   float dx = dir_fx/mag, dy = dir_fy/mag;
02444   // compute the centers of each lobe
02445   float fx0p = dx*f0, fy0p = dy*f0;
02446   float fx0m = -dx*f0, fy0m = -dy*f0;
02447   // the squared distance of fx, fy from each center
02448   float d2p = (fx-fx0p)*(fx-fx0p) + (fy-fy0p)*(fy-fy0p);
02449   float d2m = (fx-fx0m)*(fx-fx0m) + (fy-fy0m)*(fy-fy0m);
02450   // use the closest lobe
02451   float d = d2p;
02452   if (d2m<d2p)
02453     d = d2m;
02454   // the gaussian blocking function
02455   float gb = 1.f-(float)vcl_exp(-d/r2);
02456   return gb;
02457 }
02458 
02459 bool brip_vil_float_ops::
02460 spatial_frequency_filter(vil_image_view<float> const& input,
02461                          float dir_fx, float dir_fy,
02462                          float f0, float radius,
02463                          bool output_fourier_mag,
02464                          vil_image_view<float>& output)
02465 {
02466   //Compute the fourier transform of the image.
02467   vil_image_view<float> pow_two, mag, bmag, phase, pow_two_filt;
02468   brip_vil_float_ops::resize_to_power_of_two(input, pow_two);
02469   unsigned Nfx = pow_two.ni(), Nfy = pow_two.nj();
02470 
02471   if (!brip_vil_float_ops::fourier_transform(pow_two, mag, phase))
02472     return false;
02473   bmag.set_size(Nfx, Nfy);
02474 
02475   //filter the magnitude function
02476   float Ofx = Nfx*0.5f, Ofy = Nfy*0.5f;
02477   for (unsigned fy =0; fy<Nfy; fy++)
02478     for (unsigned fx =0; fx<Nfx; fx++)
02479     {
02480       float gb = gaussian_blocking_filter(dir_fx, dir_fy, f0,
02481                                           radius,
02482                                           fx-Ofx, fy-Ofy);
02483       bmag(fx,fy) = mag(fx,fy)*gb;
02484     }
02485   if (output_fourier_mag)
02486   {
02487     output = bmag;
02488     return true;
02489   }
02490   //Transform back
02491   pow_two_filt.set_size(Nfx, Nfy);
02492   brip_vil_float_ops::inverse_fourier_transform(bmag, phase, pow_two_filt);
02493 
02494   //Resize to original input size
02495   brip_vil_float_ops::resize(pow_two_filt, input.ni(), input.nj(), output);
02496   return true;
02497 }
02498 
02499 //----------------------------------------------------------------------
02500 //: Bi-linear interpolation on the neigborhood below.
02501 // \verbatim
02502 //      xr
02503 //   yr 0  x
02504 //      x  x
02505 // \endverbatim
02506 //
02507 double brip_vil_float_ops::
02508 bilinear_interpolation(vil_image_view<float> const& input, double x, double y)
02509 {
02510   //check bounds
02511   int w = static_cast<int>(input.ni()), h = static_cast<int>(input.nj());
02512   //the pixel containing the interpolated point
02513   int xr = (int)x, yr = (int)y;
02514   double fx = x-xr, fy = y-yr;
02515   if (xr<0||xr>w-2)
02516     return 0.0;
02517   if (yr<0||yr>h-2)
02518     return 0.0;
02519   double int00 = input(xr, yr), int10 = input(xr+1,yr);
02520   double int01 = input(xr, yr+1), int11 = input(xr+1,yr+1);
02521   double int0 = int00 + fy * (int01 - int00);
02522   double int1 = int10 + fy * (int11 - int10);
02523   float val = (float) (int0 + fx * (int1 - int0));
02524   return val;
02525 }
02526 
02527 //: Transform the input to the output by a homography.
02528 //  If the output size is fixed then only the corresponding
02529 //  region of input image space is transformed.
02530 bool brip_vil_float_ops::homography(vil_image_view<float> const& input,
02531                                     vgl_h_matrix_2d<double> const& H,
02532                                     vil_image_view<float>& output,
02533                                     bool output_size_fixed,
02534                                     float output_fill_value)
02535 {
02536   if (!input)
02537     return false;
02538   // smooth the input to condition interpolation
02539   vil_image_view<float> gimage =
02540     brip_vil_float_ops::gaussian(input, 0.5f);
02541 
02542   //First, there is some rather complex bookeeping to insure that
02543   //the input and output image rois are consistent with the homography.
02544 
02545   // the bounding boxes corresponding to input and output rois
02546   // We also construct polygons since homgraphies turn boxes into arbitrary
02547   // quadrilaterals.
02548   vsol_box_2d_sptr input_roi, output_roi;
02549   vsol_polygon_2d_sptr input_poly, output_poly;
02550   vgl_h_matrix_2d<double> Hinv;
02551   // set up the roi and poly for the input image
02552   unsigned win = gimage.ni(), hin = gimage.nj();
02553   input_roi = new vsol_box_2d();
02554   input_roi->add_point(0, 0);
02555   input_roi->add_point(win, hin);
02556   input_poly = bsol_algs::poly_from_box(input_roi);
02557   //Case I
02558   // the output image size and input transform can be adjusted
02559   // to map the transformed image onto the full range
02560   if (!output_size_fixed)
02561   {
02562     if (!bsol_algs::homography(input_poly, H, output_poly))
02563       return false;
02564     vsol_box_2d_sptr temp = output_poly->get_bounding_box();
02565     output.set_size((int)temp->width(), (int)temp->height());
02566     output.fill(output_fill_value);
02567     //offset the transform so the origin is (0,0)
02568     output_roi = new vsol_box_2d();
02569     output_roi->add_point(0, 0);
02570     output_roi->add_point(temp->width(), temp->height());
02571     vnl_matrix_fixed<double,3, 3> Mt = H.get_matrix();
02572     vnl_matrix_fixed<double,3, 3> t, tMt;
02573     t[0][0]=1;  t[0][1]=0; t[0][2]=-temp->get_min_x();
02574     t[1][0]=0;  t[1][1]=1; t[1][2]=-temp->get_min_y();
02575     t[2][0]=0;  t[2][1]=0; t[2][2]=1;
02576     tMt = t*Mt;
02577     vnl_svd<double> svd(tMt);
02578     vnl_matrix_fixed<double,3, 3> Mtinv = svd.inverse();
02579     Hinv = vgl_h_matrix_2d<double> (Mtinv);
02580   }
02581   else // Case II, the output image size is fixed so we have to find the
02582   {    // inverse mapping of the output roi and intersect with the input roi
02583     // to determine the domain of the mapping
02584     if (!output)
02585       return false;
02586     //The output roi and poly
02587     unsigned wout = output.ni(), hout = output.nj();
02588     output.fill(output_fill_value);
02589     output_roi = new vsol_box_2d();
02590     output_roi->add_point(0, 0);
02591     output_roi->add_point(wout, hout);
02592     output_poly = bsol_algs::poly_from_box(output_roi);
02593 
02594     //Construct the reverse mapping of the output bounds
02595     vsol_polygon_2d_sptr tpoly;
02596     Hinv = H.get_inverse();
02597     if (!bsol_algs::homography(output_poly, Hinv, tpoly))
02598       return false;
02599 
02600     //form the roi corresponding to the inverse mapped output bounds
02601     vsol_box_2d_sptr tbox = tpoly->get_bounding_box();
02602 
02603     //intersect with the input image bounds to get the input roi
02604     vsol_box_2d_sptr temp;
02605     if (!bsol_algs::intersection(tbox, input_roi, temp))
02606       return false;
02607     input_roi = temp;
02608   }
02609   //At this point we have the correct bounds for the input and
02610   //the output image
02611 
02612   //Iterate over the output image space and map the location of each
02613   //pixel into the input image space. Then carry out interpolation to
02614   //get the value of each output pixel
02615 
02616   // Dimensions of the input image
02617   int ailow  = int(input_roi->get_min_x()+0.9999f); // round up to nearest int
02618   int aihigh = int(input_roi->get_max_x());      // round down to nearest int
02619   int ajlow  = int(input_roi->get_min_y()+0.9999f);
02620   int ajhigh = int(input_roi->get_max_y());
02621 
02622   // Dimensions of the output image
02623   int bilow  = int(output_roi->get_min_x()+0.9999f);
02624   int bihigh = int(output_roi->get_max_x());
02625   int bjlow  = int(output_roi->get_min_y()+0.9999f);
02626   int bjhigh = int(output_roi->get_max_y());
02627 
02628   // The inverse transform is used to map backwards from the output
02629   const vnl_matrix_fixed<double,3,3>& Minv = Hinv.get_matrix();
02630 
02631   // Now use Hinv to transform the image
02632   for (int i = bilow; i<bihigh; i++)
02633     for (int j = bjlow; j<bjhigh; j++)
02634     {
02635       // Transform the pixel
02636       float val;
02637       double u = Minv[0][0] * i + Minv[0][1] * j + Minv[0][2];
02638       double v = Minv[1][0] * i + Minv[1][1] * j + Minv[1][2];
02639       double w = Minv[2][0] * i + Minv[2][1] * j + Minv[2][2];
02640       u /= w;
02641       v /= w;
02642 
02643       // Now do linear interpolation
02644       {
02645         int iu = (int) u;
02646         int iv = (int) v;
02647         double fu = u - iu;
02648         double fv = v - iv;
02649 
02650         if ((iu < ailow || iu >= aihigh-1) ||
02651             (iv < ajlow || iv >= ajhigh-1))
02652           continue;
02653         else
02654         {
02655           // Get the neighbouring pixels
02656           //      (u  v)    (u+1  v)
02657           //      (u v+1)   (u+1 v+1)
02658           //
02659           double v00 = gimage(iu, iv);
02660           double v01 = gimage(iu, iv+1);
02661           double v10 = gimage(iu+1,iv);
02662           double v11 = gimage(iu+1, iv+1);
02663 
02664           double v0 = v00 + fv * (v01 - v00);
02665           double v1 = v10 + fv * (v11 - v10);
02666           val = (float) (v0 + fu * (v1 - v0));
02667         }
02668         // Set the value
02669         output(i,j) = val;
02670       }
02671     }
02672   return true;
02673 }
02674 
02675 //: rotate the input image counter-clockwise about the image origin.
02676 // Demonstrates the use of image homography
02677 vil_image_view<float>
02678 brip_vil_float_ops::rotate(vil_image_view<float> const& input,
02679                            double theta_deg)
02680 {
02681   vil_image_view<float> out;
02682   if (!input)
02683     return out;
02684   double ang = theta_deg;
02685   //map theta_deg to [0 360]
02686   while (ang>360)
02687     ang-=360;
02688   while (ang<0)
02689     ang+=360;
02690   //convert to radians
02691   double deg_to_rad = vnl_math::pi/180.0;
02692   double rang = deg_to_rad*ang;
02693   vgl_h_matrix_2d<double> H;
02694   H.set_identity();
02695   H.set_rotation(rang);
02696   vil_image_view<float> temp;
02697   //The transform is adjusted to map the full input domain onto
02698   //the output image.
02699   if (!brip_vil_float_ops::homography(input, H, temp))
02700     return out;
02701   return temp;
02702 }
02703 
02704 bool brip_vil_float_ops::chip(vil_image_view<float> const& input,
02705                               vsol_box_2d_sptr const& roi,
02706                               vil_image_view<float>& chip)
02707 {
02708   if (!input||!roi)
02709     return false;
02710   int w = static_cast<int>(input.ni()), h = static_cast<int>(input.nj());
02711   int x_min = (int)roi->get_min_x(), y_min = (int)roi->get_min_y();
02712   int x_max = (int)roi->get_max_x(), y_max = (int)roi->get_max_y();
02713   if (x_min<0)
02714     x_min = 0;
02715   if (y_min<0)
02716     y_min = 0;
02717   if (x_max>w-1)
02718     x_max=w-1;
02719   if (y_max>h-1)
02720     y_max=h-1;
02721   int rw = x_max-x_min, rh = y_max-y_min;
02722   if (rw<=0||rh<=0)
02723     return false;
02724   vil_image_view<float> temp(rw+1, rh+1, 1);
02725   for (int y = y_min; y<=y_max; y++)  //!< changed < to <= to include the boundary points too
02726     for (int x =x_min; x<=x_max; x++)
02727       temp(x-x_min, y-y_min) = input(x, y);
02728   chip = temp;
02729   return true;
02730 }
02731 
02732 //:convert image resource to cropped view according to a roi.
02733 bool brip_vil_float_ops::chip(vil_image_resource_sptr const& image,
02734                               brip_roi_sptr const& roi,
02735                               vil_image_resource_sptr & chip)
02736 {
02737   //image bounds
02738   unsigned ni = image->ni(), nj = image->nj();
02739 
02740   //image bounds for the chip
02741   unsigned cm = roi->cmin(0), rm = roi->rmin(0);
02742   unsigned niv = roi->csize(0), njv = roi->rsize(0);
02743 
02744   //check bounds
02745   if (cm>ni-1||rm>nj-1||cm+niv>ni||rm+njv>nj)
02746     return false;
02747   vil_pixel_format pix_format = image->pixel_format();
02748   // get an appropriate image view for scalar images we care about
02749   if (image->nplanes()==1)
02750   {
02751     if (pix_format==VIL_PIXEL_FORMAT_BYTE)
02752     {
02753       vil_image_view<unsigned char> temp = image->get_view(cm, niv, rm, njv);
02754       if (!temp) return false;
02755       chip = vil_new_image_resource_of_view(temp);
02756       return true;
02757     }
02758     else if (pix_format==VIL_PIXEL_FORMAT_UINT_16)
02759     {
02760       vil_image_view<unsigned short> temp = image->get_view(cm, niv, rm, njv);
02761       if (!temp) return false;
02762       chip = vil_new_image_resource_of_view(temp);
02763       return true;
02764     }
02765     else if (pix_format==VIL_PIXEL_FORMAT_FLOAT)
02766     {
02767       vil_image_view<float> temp = image->get_view(cm, niv, rm, njv);
02768       if (!temp) return false;
02769       chip = vil_new_image_resource_of_view(temp);
02770       return true;
02771     }
02772   }
02773 
02774   //color data
02775   if (image->nplanes()==3)
02776   {
02777     if (pix_format==VIL_PIXEL_FORMAT_BYTE) //the only way now
02778     {
02779       //extract view corresponding to region of interest
02780       vil_image_view<vil_rgb<vxl_byte> > temp = image->get_view(cm, niv, rm, njv);
02781       if (!temp) return false;
02782       chip = vil_new_image_resource_of_view(temp);
02783       return true;
02784     }
02785   }
02786   return false;
02787 }
02788 
02789 
02790 //: perform normalized cross-correlation at a sub-pixel location.
02791 // Thus all the pixel values are interpolated.
02792 float brip_vil_float_ops::
02793 cross_correlate(vil_image_view<float> const& image1,
02794                 vil_image_view<float> const& image2,
02795                 float x, float y, int radius, float intensity_thresh)
02796 {
02797   unsigned w1 = image1.ni(), h1 = image1.nj();
02798   unsigned w2 = image1.ni(), h2 = image1.nj();
02799   //bounds checks
02800   if (w1!=w2||h1!=h2)
02801     return -1;
02802   if (x<radius||x>w1-radius-1||y<radius||y>h1-radius-1)
02803     return -1;
02804 
02805   //accumulate correlation sums,
02806   //bi-linear interpolate the values
02807   int s = 2*radius+1;
02808   double area = s*s;
02809   double sI1=0, sI2=0, sI1I1=0, sI2I2=0, sI1I2=0;
02810   for (int y0 = -10*radius; y0<=10*radius; ++y0)
02811     for (int x0 = -10*radius; x0<=10*radius; ++x0)
02812     {
02813       float xp = x+0.1f*x0, yp = y+0.1f*y0;
02814       double v1 =
02815         brip_vil_float_ops::bilinear_interpolation(image1, xp, yp);
02816       double v2 =
02817         brip_vil_float_ops::bilinear_interpolation(image2, xp, yp);
02818       sI1 += v1;
02819       sI2 += v2;
02820       sI1I1 += v1*v1;
02821       sI2I2 += v2*v2;
02822       sI1I2 += v1*v2;
02823     }
02824   // compute correlation.
02825   float cc = cross_corr(area, sI1, sI2, sI1I1, sI2I2, sI1I2, intensity_thresh);
02826   return cc;
02827 }
02828 
02829 //: r0 is the image from from which to read the new intensity values.
02830 //  \param r is the summing array row in which the values are to be accumulated
02831 static bool update_row(vil_image_view<float> const& image1,
02832                        vil_image_view<float> const& image2,
02833                        int r0, int r,
02834                        vbl_array_2d<double>& SI1,
02835                        vbl_array_2d<double>& SI2,
02836                        vbl_array_2d<double>& SI1I1,
02837                        vbl_array_2d<double>& SI2I2,
02838                        vbl_array_2d<double>& SI1I2)
02839 {
02840   unsigned w1 = image1.ni();
02841   unsigned w2 = image2.ni();
02842   int h1 = image1.nj();
02843   int h2 = image2.nj();
02844   if (w1!=w2||h1!=h2||r<0||r>=h1)
02845     return false;
02846   double i10 = image1(0,r0), i20 = image2(0,r0);
02847   SI1[r][0] = i10; SI2[r][0] = i20; SI1I1[r][0]=i10*i10;
02848   SI2I2[r][0]=i20*i20; SI1I2[r][0]=i10*i20;
02849   for (unsigned c = 1; c<w1; c++)
02850   {
02851     double i1c = image1(c,r0);
02852     double i2c = image2(c,r0);
02853     SI1[r][c]    = SI1[r][c-1]+i1c;
02854     SI2[r][c]    = SI2[r][c-1]+i2c;
02855     SI1I1[r][c]  = SI1I1[r][c-1]+ i1c*i1c;
02856     SI2I2[r][c]  = SI2I2[r][c-1]+ i2c*i2c;
02857     SI1I2[r][c]  = SI1I2[r][c-1]+ i1c*i2c;
02858   }
02859   return true;
02860 }
02861 
02862 static bool initialize_slice(vil_image_view<float> const& image1,
02863                              vil_image_view<float> const& image2,
02864                              unsigned radius,
02865                              vbl_array_2d<double>& SI1,
02866                              vbl_array_2d<double>& SI2,
02867                              vbl_array_2d<double>& SI1I1,
02868                              vbl_array_2d<double>& SI2I2,
02869                              vbl_array_2d<double>& SI1I2)
02870 {
02871   for (unsigned r = 0; r<=2*radius; r++)
02872     if (!update_row(image1, image2, r, r, SI1, SI2, SI1I1, SI2I2, SI1I2))
02873       return false;
02874   return true;
02875 }
02876 
02877 static bool collapse_slice(vbl_array_2d<double> const& SI1,
02878                            vbl_array_2d<double> const& SI2,
02879                            vbl_array_2d<double> const& SI1I1,
02880                            vbl_array_2d<double> const& SI2I2,
02881                            vbl_array_2d<double> const& SI1I2,
02882                            vbl_array_1d<double>& dSI1,
02883                            vbl_array_1d<double>& dSI2,
02884                            vbl_array_1d<double>& dSI1I1,
02885                            vbl_array_1d<double>& dSI2I2,
02886                            vbl_array_1d<double>& dSI1I2)
02887 {
02888   //sanity check
02889   unsigned w = SI1.cols(), h = SI1.rows();
02890   unsigned dw = dSI1.size();
02891   if (dw!=w)
02892     return false;
02893 
02894   for (unsigned c = 0; c<w; c++)
02895   {
02896     dSI1[c]=0; dSI2[c]=0; dSI1I1[c]=0;
02897     dSI2I2[c]=0; dSI1I2[c]=0;
02898     for (unsigned r = 0; r<h; r++)
02899     {
02900       dSI1[c] += SI1[r][c];
02901       dSI2[c] += SI2[r][c];
02902       dSI1I1[c] += SI1I1[r][c];
02903       dSI2I2[c] += SI2I2[r][c];
02904       dSI1I2[c] += SI1I2[r][c];
02905     }
02906   }
02907   return true;
02908 }
02909 
02910 static bool cross_correlate_row(int radius,
02911                                 vbl_array_1d<double>& dSI1,
02912                                 vbl_array_1d<double>& dSI2,
02913                                 vbl_array_1d<double>& dSI1I1,
02914                                 vbl_array_1d<double>& dSI2I2,
02915                                 vbl_array_1d<double>& dSI1I2,
02916                                 float intensity_thresh,
02917                                 vbl_array_1d<float>& cc)
02918 {
02919   //sanity check
02920   unsigned w = dSI1.size(), wc = cc.size();
02921   if (!w||!wc||w!=wc)
02922     return false;
02923   int s = 2*radius+1;
02924   double area = s*s;
02925   //the general case
02926   double si1=dSI1[s-1], si2=dSI2[s-1], si1i1=dSI1I1[s-1],
02927     si2i2=dSI2I2[s-1], si1i2=dSI1I2[s-1];
02928   cc[radius]= cross_corr(area, si1, si2, si1i1, si2i2, si1i2, intensity_thresh);
02929   //the remaining columns
02930   for (unsigned c = radius+1; c+radius<w; c++)
02931   {
02932     si1=dSI1[c+radius]-dSI1[c-radius-1];
02933     si2=dSI2[c+radius]-dSI2[c-radius-1];
02934     si1i1=dSI1I1[c+radius]-dSI1I1[c-radius-1];
02935     si2i2=dSI2I2[c+radius]-dSI2I2[c-radius-1];
02936     si1i2=dSI1I2[c+radius]-dSI1I2[c-radius-1];
02937     cc[c] = cross_corr(area, si1, si2, si1i1, si2i2, si1i2, intensity_thresh);
02938   }
02939   return true;
02940 }
02941 
02942 static void advance_rows(vbl_array_2d<double>& S)
02943 {
02944   unsigned nr = S.rows(), nc = S.cols();
02945   for (unsigned r = 0; r<nr-1; r++)
02946     for (unsigned c =0; c<nc; c++)
02947       S[r][c]=S[r+1][c];
02948 }
02949 
02950 static bool output_cc_row(int r0, vbl_array_1d<float> const& cc,
02951                           vil_image_view<float>& out)
02952 {
02953   unsigned n = cc.size(), w = out.ni();
02954   if (n!=w)
02955     return false;
02956   for (unsigned c = 0; c<w; c++)
02957     out(c, r0) = cc[c];
02958   return true;
02959 }
02960 
02961 bool brip_vil_float_ops::
02962 cross_correlate(vil_image_view<float> const& image1,
02963                 vil_image_view<float> const& image2,
02964                 vil_image_view<float>& out,
02965                 int radius, float intensity_thresh)
02966 {
02967   vul_timer t;
02968   unsigned w = image1.ni(), h = image1.nj();
02969   unsigned w2 = image2.ni(), h2 = image2.nj();
02970   //sizes must match
02971   if (w!=w2||h!=h2)
02972   {
02973     vcl_cout << "In brip_vil_float_ops::cross_correlate(..) -"
02974              << " image sizes don't match\n";
02975     return out;
02976   }
02977   out.set_size(w, h);
02978   out.fill(0.f);
02979   int s = 2*radius+1;
02980   //Create the running sum slices
02981   vbl_array_2d<double> SI1(s,w), SI2(s,w),
02982     SI1I1(s,w), SI2I2(s,w), SI1I2(s,w);
02983   vbl_array_1d<float> cc(w, 0.f);
02984   vbl_array_1d<double> dSI1(w, 0.0), dSI2(w, 0.0),
02985     dSI1I1(w, 0.0), dSI2I2(w, 0.0), dSI1I2(w, 0.0);
02986   initialize_slice(image1, image2, radius, SI1, SI2, SI1I1, SI2I2, SI1I2);
02987   if (!collapse_slice(SI1,  SI2,  SI1I1,  SI2I2,  SI1I2,
02988                       dSI1, dSI2, dSI1I1, dSI2I2, dSI1I2))
02989     return false;
02990   unsigned r0 = radius;
02991   for (; r0+radius+1<h; r0++)
02992   {
02993     if (r0==5)
02994       r0=r0;
02995 #ifdef DEBUG
02996     vcl_cout << "r0 " << r0 << '\n';
02997 #endif
02998     if (!cross_correlate_row(radius, dSI1, dSI2, dSI1I1, dSI2I2, dSI1I2,
02999                              intensity_thresh, cc))
03000       return false;
03001 #ifdef DEBUG
03002     vcl_cout << '\n';
03003 #endif
03004     advance_rows(SI1); advance_rows(SI2);  advance_rows(SI1I1);
03005     advance_rows(SI2I2); advance_rows(SI1I2);
03006     if (!update_row(image1, image2, r0+radius+1, 2*radius,
03007                     SI1, SI2, SI1I1, SI2I2, SI1I2))
03008       return false;
03009     if (!collapse_slice(SI1,  SI2,  SI1I1,  SI2I2,  SI1I2,
03010                         dSI1, dSI2, dSI1I1, dSI2I2, dSI1I2))
03011       return false;
03012     if (!output_cc_row(r0, cc, out))
03013       return out;
03014   }
03015   //handle the last row
03016 #ifdef DEBUG
03017   vcl_cout << "r0 " << r0 << '\n';
03018 #endif
03019   if (!cross_correlate_row(radius, dSI1, dSI2, dSI1I1, dSI2I2, dSI1I2,
03020                            intensity_thresh, cc))
03021     return false;
03022 #ifdef DEBUG
03023   vcl_cout << '\n';
03024 #endif
03025   if (!output_cc_row(r0, cc, out))
03026     return false;
03027 #ifdef DEBUG
03028   vcl_cout << "RunningSumCrossCorrelation for " << w*h/1000.0f << " k pixels in "
03029            << t.real() << " msecs\n"<< vcl_flush;
03030 #endif
03031   return true;
03032 }
03033 
03034 vil_image_resource_sptr
03035 brip_vil_float_ops::sum(vil_image_resource_sptr const& img0,
03036                         vil_image_resource_sptr const& img1)
03037 {
03038   vil_image_view<float> op0 = brip_vil_float_ops::convert_to_float(img0);
03039   vil_image_view<float> op1 = brip_vil_float_ops::convert_to_float(img1);
03040   vil_image_view<float> sum;
03041   vil_math_image_sum(op0, op1, sum);
03042 
03043   //find out the types of the input images for now, only do greyscale operands
03044   vil_pixel_format pix_format0 = img0->pixel_format();
03045   vil_pixel_format pix_format1 = img1->pixel_format();
03046   if (pix_format0 == VIL_PIXEL_FORMAT_FLOAT ||
03047       pix_format1 == VIL_PIXEL_FORMAT_FLOAT)
03048     {
03049       return vil_new_image_resource_of_view(sum);
03050     }
03051 
03052   if (pix_format0 == VIL_PIXEL_FORMAT_UINT_16 ||
03053       pix_format1 == VIL_PIXEL_FORMAT_UINT_16)
03054     {
03055       vil_image_view<unsigned short> res =
03056         brip_vil_float_ops::convert_to_short(vil_new_image_resource_of_view(sum));
03057       return vil_new_image_resource_of_view(res);
03058     }
03059 
03060   if (pix_format0 == VIL_PIXEL_FORMAT_BYTE ||
03061       pix_format1 == VIL_PIXEL_FORMAT_BYTE)
03062     {
03063       vil_image_view<unsigned char> res =
03064         brip_vil_float_ops::convert_to_byte(sum);
03065       return vil_new_image_resource_of_view(res);
03066     }
03067   //for color return the float image
03068   return vil_new_image_resource_of_view(sum);
03069 }
03070 
03071 // Compute img0 - img1
03072 vil_image_resource_sptr
03073 brip_vil_float_ops::difference(vil_image_resource_sptr const& img0,
03074                                vil_image_resource_sptr const& img1)
03075 {
03076   vil_image_view<float> op0 = brip_vil_float_ops::convert_to_float(img0);
03077   vil_image_view<float> op1 = brip_vil_float_ops::convert_to_float(img1);
03078   vil_image_view<float> diff;
03079   vil_math_image_difference(op0, op1, diff);
03080 
03081   //find out the types of the input images for now, only do greyscale operands
03082   vil_pixel_format pix_format0 = img0->pixel_format();
03083   vil_pixel_format pix_format1 = img1->pixel_format();
03084   if (pix_format0 == VIL_PIXEL_FORMAT_FLOAT ||
03085       pix_format1 == VIL_PIXEL_FORMAT_FLOAT)
03086     {
03087       return vil_new_image_resource_of_view(diff);
03088     }
03089 
03090   if (pix_format0 == VIL_PIXEL_FORMAT_UINT_16 ||
03091       pix_format1 == VIL_PIXEL_FORMAT_UINT_16)
03092     {
03093       vil_image_view<unsigned short> res =
03094         brip_vil_float_ops::convert_to_short(vil_new_image_resource_of_view(diff));
03095       return vil_new_image_resource_of_view(res);
03096     }
03097 
03098   if (pix_format0 == VIL_PIXEL_FORMAT_BYTE ||
03099       pix_format1 == VIL_PIXEL_FORMAT_BYTE)
03100     {
03101       vil_image_view<unsigned char> res =
03102         brip_vil_float_ops::convert_to_byte(diff);
03103       return vil_new_image_resource_of_view(res);
03104     }
03105   // for color return the float image
03106   return vil_new_image_resource_of_view(diff);
03107 }
03108 
03109 //: Compute the entropy of the intensity of a region
03110 //  Note no bounds checking!
03111 float brip_vil_float_ops::entropy_i(const unsigned i, const unsigned j,
03112                                     const unsigned i_radius,
03113                                     const unsigned j_radius,
03114                                     vil_image_view<float> const& intensity,
03115                                     const float range, const unsigned bins)
03116 {
03117   bsta_histogram<float> hi(range, bins);
03118   int ir = static_cast<int>(i_radius), jr = static_cast<int>(j_radius);
03119   for (int dj = -jr; dj<=jr; ++dj)
03120     for (int di = -ir; di<=ir; ++di)
03121     {
03122       float inten = intensity(i+di, j+dj);
03123       hi.upcount(inten, 1.0f);
03124     }
03125   return hi.entropy();
03126 }
03127 
03128 //: Compute the entropy of the gradient direction of a region
03129 //  Note no bounds checking!
03130 float brip_vil_float_ops::entropy_g(const unsigned i, const unsigned j,
03131                                     const unsigned i_radius,
03132                                     const unsigned j_radius,
03133                                     vil_image_view<float> const& gradx,
03134                                     vil_image_view<float> const& grady,
03135                                     const float range, const unsigned bins)
03136 {
03137   bsta_histogram<float> hg(range, bins);
03138   static const float deg_rad = (float)(180.0/vnl_math::pi);
03139   int ir = static_cast<int>(i_radius), jr = static_cast<int>(j_radius);
03140   for (int dj = -jr; dj<=jr; ++dj)
03141     for (int di = -ir; di<=ir; ++di)
03142     {
03143       float Ix = gradx(i+di, j+dj), Iy = grady(i+di, j+dj);
03144       float ang = deg_rad*vcl_atan2(Iy, Ix) + 180.0f;
03145       float mag = vcl_abs(Ix)+vcl_abs(Iy);
03146       hg.upcount(ang, mag);
03147     }
03148   return hg.entropy();
03149 }
03150 
03151 //: Compute the hue and saturation entropy of a region about the specified pixel
03152 //  Note no bounds checking!
03153 float brip_vil_float_ops::entropy_hs(const unsigned i, const unsigned j,
03154                                      const unsigned i_radius,
03155                                      const unsigned j_radius,
03156                                      vil_image_view<float> const& hue,
03157                                      vil_image_view<float> const& sat,
03158                                      const float range, const unsigned bins)
03159 {
03160   bsta_histogram<float> hg(range, bins);
03161   int ir = static_cast<int>(i_radius), jr = static_cast<int>(j_radius);
03162   for (int dj = -jr; dj<=jr; ++dj)
03163     for (int di = -ir; di<=ir; ++di)
03164     {
03165       float h = hue(i+di, j+dj), s = sat(i+di, j+dj);
03166       hg.upcount(h, s);
03167     }
03168   return hg.entropy();
03169 }
03170 
03171 vil_image_view<float>
03172 brip_vil_float_ops::entropy(const unsigned i_radius,
03173                             const unsigned j_radius,
03174                             const unsigned step,
03175                             vil_image_resource_sptr const& img,
03176                             const float sigma,
03177                             const bool intensity,
03178                             const bool gradient,
03179                             const bool ihs)
03180 {
03181   vil_image_view<float> ent;
03182   if (!intensity&&!gradient&&!ihs)
03183   {
03184     vcl_cout << "In brip_vil_float_ops::entropy(.) - No computation to do\n";
03185     return ent;
03186   }
03187 
03188   vil_image_view<float> fimage = brip_vil_float_ops::convert_to_float(img);
03189   vil_image_view<float> gimage =
03190     brip_vil_float_ops::gaussian(fimage, sigma);
03191 
03192   unsigned ni = img->ni(), nj = img->nj();
03193   ent.set_size(ni/step+1, nj/step+1);
03194   ent.fill(0.0f);
03195   if (intensity)
03196     for (unsigned j = j_radius; j<(nj-j_radius); j+=step)
03197       for (unsigned i = i_radius; i<(ni-i_radius); i+=step)
03198         ent(i/step,j/step) =
03199           brip_vil_float_ops::entropy_i(i, j, i_radius, j_radius, gimage);
03200 
03201   if (gradient)
03202   {
03203     vil_image_view<float> grad_x, grad_y;
03204     grad_x.set_size(ni, nj);
03205     grad_y.set_size(ni, nj);
03206     brip_vil_float_ops::gradient_3x3 (gimage , grad_x , grad_y);
03207     for (unsigned j = j_radius; j<(nj-j_radius); j+=step)
03208       for (unsigned i = i_radius; i<(ni-i_radius); i+=step)
03209         ent(i/step,j/step) +=
03210           brip_vil_float_ops::entropy_g(i, j, i_radius, j_radius,
03211                                         grad_x, grad_y);
03212   }
03213   if (ihs&&img->nplanes()==3)
03214   {
03215     vil_image_view<float> inten, hue, sat;
03216     vil_image_view<vil_rgb<vxl_byte> > cimage = img->get_view();
03217     brip_vil_float_ops::convert_to_IHS(cimage, inten, hue, sat);
03218     for (unsigned j = j_radius; j<(nj-j_radius); j+=step)
03219       for (unsigned i = i_radius; i<(ni-i_radius); i+=step)
03220         ent(i/step,j/step) +=
03221           brip_vil_float_ops::entropy_hs(i, j, i_radius, j_radius,
03222                                          hue, sat);
03223   }
03224   return ent;
03225 }
03226 
03227 float brip_vil_float_ops::minfo_i(const unsigned i0, const unsigned j0,
03228                                   const unsigned i1, const unsigned j1,
03229                                   const unsigned i_radius,
03230                                   const unsigned j_radius,
03231                                   vil_image_view<float> const& int0,
03232                                   vil_image_view<float> const& int1,
03233                                   const float range, const unsigned bins)
03234 {
03235   bsta_histogram<float> hi0(range, bins);
03236   bsta_histogram<float> hi1(range, bins);
03237   bsta_joint_histogram<float> hji(range, bins);
03238   int ir = static_cast<int>(i_radius), jr = static_cast<int>(j_radius);
03239   for (int dj = -jr; dj<=jr; ++dj)
03240     for (int di = -ir; di<=ir; ++di)
03241     {
03242       float inten0 = int0(i0+di, j0+dj);
03243       float inten1 = int1(i1+di, j1+dj);
03244       hi0.upcount(inten0, 1.0f);
03245       hi1.upcount(inten1, 1.0f);
03246       hji.upcount(inten0, 1.0f, inten1, 1.0f);
03247     }
03248   float H0 = hi0.entropy();
03249   float H1 = hi1.entropy();
03250   float HJ = hji.entropy();
03251   float minfo_i = H0 + H1 - HJ;
03252 #ifdef DEBUG
03253   if (minfo<0)
03254     vcl_cout << "intensity MI LT 0 " << minfo << vcl_endl;
03255 #endif
03256   return minfo_i;
03257 }
03258 
03259 float brip_vil_float_ops::minfo_g(const unsigned i0, const unsigned j0,
03260                                   const unsigned i1, const unsigned j1,
03261                                   const unsigned i_radius,
03262                                   const unsigned j_radius,
03263                                   vil_image_view<float> const& gradx0,
03264                                   vil_image_view<float> const& grady0,
03265                                   vil_image_view<float> const& gradx1,
03266                                   vil_image_view<float> const& grady1,
03267                                   const float range, const unsigned bins)
03268 {
03269   bsta_histogram<float> hg0(range, bins);
03270   bsta_histogram<float> hg1(range, bins);
03271   bsta_joint_histogram<float> hjg(range, bins);
03272   static const float deg_rad = (float)(180.0/vnl_math::pi);
03273   int ir = static_cast<int>(i_radius), jr = static_cast<int>(j_radius);
03274   for (int dj = -jr; dj<=jr; ++dj)
03275     for (int di = -ir; di<=ir; ++di)
03276     {
03277       float Ix0 = gradx0(i0+di, j0+dj), Iy0 = grady0(i0+di, j0+dj);
03278       float Ix1 = gradx1(i1+di, j1+dj), Iy1 = grady1(i1+di, j1+dj);
03279       float ang0 = deg_rad*vcl_atan2(Iy0, Ix0) + 180.0f;
03280       float ang1 = deg_rad*vcl_atan2(Iy1, Ix1) + 180.0f;
03281       float mag0 = vcl_abs(Ix0)+vcl_abs(Iy0);
03282       float mag1 = vcl_abs(Ix1)+vcl_abs(Iy1);
03283       hg0.upcount(ang0, mag0);
03284       hg1.upcount(ang1, mag1);
03285       hjg.upcount(ang0, mag0, ang1, mag1);
03286     }
03287   float H0 = hg0.entropy();
03288   float H1 = hg1.entropy();
03289   float HJ = hjg.entropy();
03290   float minfo_g = H0 + H1 - HJ;
03291 #ifdef DEBUG
03292   if (minfo<0)
03293     vcl_cout << "gradient MI LT 0 " << minfo << vcl_endl;
03294 #endif
03295   return minfo_g;
03296 }
03297 
03298 float brip_vil_float_ops::minfo_hs(const unsigned i0, const unsigned j0,
03299                                    const unsigned i1, const unsigned j1,
03300                                    const unsigned i_radius,
03301                                    const unsigned j_radius,
03302                                    vil_image_view<float> const& hue0,
03303                                    vil_image_view<float> const& sat0,
03304                                    vil_image_view<float> const& hue1,
03305                                    vil_image_view<float> const& sat1,
03306                                    const float range, const unsigned bins)
03307 {
03308   bsta_histogram<float> hh0(range, bins);
03309   bsta_histogram<float> hh1(range, bins);
03310   bsta_joint_histogram<float> hjh(range, bins);
03311   int ir = static_cast<int>(i_radius), jr = static_cast<int>(j_radius);
03312   for (int dj = -jr; dj<=jr; ++dj)
03313     for (int di = -ir; di<=ir; ++di)
03314     {
03315       float h0 = hue0(i0+di, j0+dj), s0 = sat0(i0+di, j0+dj);
03316       float h1 = hue1(i1+di, j1+dj), s1 = sat1(i1+di, j1+dj);
03317       hh0.upcount(h0, s0);
03318       hh1.upcount(h1, s1);
03319       hjh.upcount(h0, s0, h1, s1);
03320     }
03321   float H0 = hh0.entropy();
03322   float H1 = hh1.entropy();
03323   float HJ = hjh.entropy();
03324   float minfo_h = H0 + H1 - HJ;
03325 #ifdef DEBUG
03326   if (minfo<0)
03327     vcl_cout << "color MI LT 0 " << minfo << vcl_endl;
03328 #endif
03329   return minfo_h;
03330 }
03331 
03332 bool brip_vil_float_ops::minfo(const unsigned i_radius,
03333                                const unsigned j_radius,
03334                                const unsigned step,
03335                                vil_image_resource_sptr const& img0,
03336                                vil_image_resource_sptr const& img1,
03337                                vil_image_view<float>& MI0,
03338                                vil_image_view<float>& MI1,
03339                                const float sigma,
03340                                const bool intensity,
03341                                const bool gradient,
03342                                const bool ihs)
03343 {
03344   if (!intensity&&!gradient&&!ihs)
03345   {
03346     vcl_cout << "In brip_vil_float_ops::minforopy(.) - No computation to do\n";
03347     return false;
03348   }
03349 
03350   vil_image_view<float> fimage0 = brip_vil_float_ops::convert_to_float(img0);
03351   vil_image_view<float> gimage0 =
03352     brip_vil_float_ops::gaussian(fimage0, sigma);
03353 
03354   vil_image_view<float> fimage1 = brip_vil_float_ops::convert_to_float(img1);
03355   vil_image_view<float> gimage1 =
03356     brip_vil_float_ops::gaussian(fimage1, sigma);
03357 
03358   unsigned ni0 = img0->ni(), nj0 = img0->nj();
03359   unsigned ni1 = img1->ni(), nj1 = img1->nj();
03360   unsigned ilimit = 2*i_radius +1, jlimit = 2*j_radius+1;
03361   if (ni0<ilimit||nj0<jlimit||ni1<ilimit||nj1<jlimit)
03362   {
03363     vcl_cout << "In brip_vil_float_ops::minfo(...) - image too small\n";
03364     return false;
03365   }
03366   MI0.set_size(ni0/step+1, nj0/step+1); MI0.fill(0.0f);
03367   MI1.set_size(ni1/step+1, nj1/step+1); MI1.fill(0.0f);
03368   if (intensity)
03369     for (unsigned j0 = j_radius; j0<(nj0-j_radius); j0+=step)
03370       for (unsigned i0 = i_radius; i0<(ni0-i_radius); i0+=step)
03371         for (unsigned j1 = j_radius; j1<(nj1-j_radius); j1+=step)
03372           for (unsigned i1 = i_radius; i1<(ni1-i_radius); i1+=step)
03373           {
03374             float minfo = brip_vil_float_ops::minfo_i(i0, j0,i1, j1,
03375                                                       i_radius, j_radius,
03376                                                       gimage0, gimage1);
03377             MI0(i0/step,j0/step) = minfo;
03378             MI1(i1/step,j1/step) = minfo;
03379           }
03380   if (gradient)
03381   {
03382     vil_image_view<float> grad_x0, grad_y0, grad_x1, grad_y1;
03383     grad_x0.set_size(ni0, nj0);
03384     grad_y0.set_size(ni0, nj0);
03385     grad_x1.set_size(ni1, nj1);
03386     grad_y1.set_size(ni1, nj1);
03387     brip_vil_float_ops::gradient_3x3 (gimage0 , grad_x0 , grad_y0);
03388     brip_vil_float_ops::gradient_3x3 (gimage1 , grad_x1 , grad_y1);
03389     for (unsigned j0 = j_radius; j0<(nj0-j_radius); j0+=step)
03390       for (unsigned i0 = i_radius; i0<(ni0-i_radius); i0+=step)
03391         for (unsigned j1 = j_radius; j1<(nj1-j_radius); j1+=step)
03392           for (unsigned i1 = i_radius; i1<(ni1-i_radius); i1+=step)
03393           {
03394             float minfo = brip_vil_float_ops::minfo_g(i0, j0,i1, j1,
03395                                                       i_radius, j_radius,
03396                                                       grad_x0, grad_y0,
03397                                                       grad_x1, grad_y1);
03398             MI0(i0/step,j0/step) += minfo;
03399             MI1(i1/step,j1/step) += minfo;
03400           }
03401   }
03402   if (ihs&&img0->nplanes()==3&&img1->nplanes()==3)
03403   {
03404     vil_image_view<float> inten0, hue0, sat0;
03405     vil_image_view<float> inten1, hue1, sat1;
03406     vil_image_view<vil_rgb<vxl_byte> > cimage0 = img0->get_view();
03407     vil_image_view<vil_rgb<vxl_byte> > cimage1 = img1->get_view();
03408     brip_vil_float_ops::convert_to_IHS(cimage0, inten0, hue0, sat0);
03409     brip_vil_float_ops::convert_to_IHS(cimage1, inten1, hue1, sat1);
03410 
03411     for (unsigned j0 = j_radius; j0<(nj0-j_radius); j0+=step)
03412       for (unsigned i0 = i_radius; i0<(ni0-i_radius); i0+=step)
03413         for (unsigned j1 = j_radius; j1<(nj1-j_radius); j1+=step)
03414           for (unsigned i1 = i_radius; i1<(ni1-i_radius); i1+=step)
03415           {
03416             float minfo = brip_vil_float_ops::minfo_hs(i0, j0,i1, j1,
03417                                                        i_radius, j_radius,
03418                                                        hue0, sat0,
03419                                                        hue1, sat1);
03420             MI0(i0/step,j0/step) += minfo;
03421             MI1(i1/step,j1/step) += minfo;
03422           }
03423   }
03424   return true;
03425 }
03426 
03427 //compute the average of the image intensity within the specified region
03428 float brip_vil_float_ops::
03429 average_in_box(vil_image_view<float> const& v, vgl_box_2d<double> const&  box)
03430 {
03431   vgl_point_2d<double> p0 = box.min_point();
03432   unsigned i0 = static_cast<unsigned>(p0.x()), j0 = static_cast<unsigned>(p0.y());
03433   vgl_point_2d<double> p1 = box.max_point();
03434   unsigned i1 = static_cast<unsigned>(p1.x()), j1 = static_cast<unsigned>(p1.y());
03435   float n = 0;
03436   float sum = 0;
03437   for (unsigned i = i0; i<=i1; ++i)
03438     for (unsigned j = j0; j<=j1; ++j, ++n)
03439       sum += v(i,j);
03440   if (n>0)
03441     sum /= n;
03442   return sum;
03443 }
03444 
03445 #if 0 //For now remove dependency on vimt. Save for illustration
03446 bool brip_vil_float_ops::vimt_homography(vil_image_view<float> const& curr_view,
03447                                          vgl_h_matrix_2d<double>const& H,
03448                                          vil_image_view<float>& output)
03449 {
03450   int bimg_ni;
03451   int bimg_nj;
03452 
03453   int offset_i;
03454   int offset_j;
03455 
03456   vbl_bounding_box<double,2> box;
03457 
03458   unsigned ni =  curr_view.ni(), nj =  curr_view.nj();
03459   vimt_transform_2d p;
03460   vnl_double_3x3 Mh = H.get_matrix();
03461   vnl_double_2x3 M;
03462   for (unsigned c = 0; c<3; ++c)
03463     for (unsigned r = 0; r<2; ++r)
03464       M[r][c] = Mh[r][c]/Mh[2][2];
03465   p.set_affine(M);
03466   box.update(p(0,0).x(),p(0,0).y());
03467   box.update(p(0,nj).x(),p(0,nj).y());
03468   box.update(p(ni,0).x(),p(ni,0).y());
03469   box.update(p(ni,nj).x(),p(ni,nj).y());
03470 
03471   bimg_ni=(int)vcl_ceil(box.max()[0]-box.min()[0]);
03472   bimg_nj=(int)vcl_ceil(box.max()[1]-box.min()[1]);
03473 
03474   offset_i=(int)vcl_ceil(0-box.min()[0]);
03475   offset_j=(int)vcl_ceil(0-box.min()[1]);
03476 
03477   vimt_image_2d_of<float> sample_im;
03478   vgl_point_2d<double> q(-offset_i,-offset_j);
03479   vgl_vector_2d<double> u(1,0);
03480   vgl_vector_2d<double> v(0,1);
03481 
03482   vimt_image_2d_of<float> curr_img(curr_view,p);
03483   vimt_resample_bilin(curr_img,sample_im,q,u,v,bimg_ni,bimg_nj);
03484   output = sample_im.image();
03485   return true;
03486 }
03487 
03488 #endif // 0
03489 
03490 vcl_vector<float> brip_vil_float_ops::scan_region(vil_image_resource_sptr img,
03491                                                   vgl_polygon<double> poly,
03492                                                   float& min,
03493                                                   float& max)
03494 {
03495   vcl_vector<float> pixels;
03496   min = vnl_numeric_traits<float>::maxval;
03497   max = 0;
03498   unsigned np = img->nplanes();
03499   vgl_polygon_scan_iterator<double> si(poly, false);
03500   if (img->pixel_format()==VIL_PIXEL_FORMAT_BYTE)
03501   {
03502     if (np==1) // single plane
03503     {
03504       for (si.reset(); si.next();)
03505       {
03506         unsigned j = static_cast<unsigned>(si.scany());
03507         for (int x = si.startx(); x<=si.endx(); ++x)
03508         {
03509           unsigned i = static_cast<unsigned>(x);
03510           vil_image_view<unsigned char> v = img->get_view(i, 1,j, 1);
03511           float fv = static_cast<float>(v(0,0));
03512           if (fv<min) min = fv;
03513           if (fv>max) max = fv;
03514           pixels.push_back(fv);
03515         }
03516       }
03517       return pixels;
03518     }
03519     else
03520     {
03521       for (si.reset(); si.next();)
03522       {
03523         unsigned j = static_cast<unsigned>(si.scany());
03524         for (int x = si.startx(); x<=si.endx(); ++x)
03525         {
03526           unsigned i = static_cast<unsigned>(x);
03527           vil_image_view<unsigned char> v = img->get_view(i, 1,j, 1);
03528           float fv = 0;
03529           for (unsigned p = 0; p<np; ++p)
03530             fv += v(0,0,p);
03531           fv/=3;
03532           if (fv<min) min = fv;
03533           if (fv>max) max = fv;
03534           pixels.push_back(fv);
03535         }
03536       }
03537     }
03538     return pixels;
03539   }
03540   else if (img->pixel_format()==VIL_PIXEL_FORMAT_UINT_16)
03541   {
03542     if (np) // single plane
03543     {
03544       for (si.reset(); si.next();)
03545       {
03546         unsigned j = static_cast<unsigned>(si.scany());
03547         for (int x = si.startx(); x<=si.endx(); ++x)
03548         {
03549           unsigned i = static_cast<unsigned>(x);
03550           vil_image_view<unsigned short> v = img->get_view(i, 1,j, 1);
03551           float fv = static_cast<float>(v(0,0));
03552           if (fv<min) min = fv;
03553           if (fv>max) max = fv;
03554           pixels.push_back(fv);
03555         }
03556       }
03557       return pixels;
03558     }
03559     else
03560     {
03561       for (si.reset(); si.next();)
03562       {
03563         unsigned j = static_cast<unsigned>(si.scany());
03564         for (int x = si.startx(); x<=si.endx(); ++x)
03565         {
03566           unsigned i = static_cast<unsigned>(x);
03567           vil_image_view<unsigned short> v = img->get_view(i, 1,j, 1);
03568           float fv = 0;
03569           for (unsigned p = 0; p<np; ++p)
03570             fv += v(0,0,p);
03571           fv/=3;
03572           if (fv<min) min = fv;
03573           if (fv>max) max = fv;
03574           pixels.push_back(fv);
03575         }
03576       }
03577       return pixels;
03578     }
03579   }
03580   vcl_cerr << "In brip_vil_float_ops::scan_region() - unknown format\n";
03581   return pixels;
03582 }
03583 
03584 //It has been observed that color order is somewhat invariant to illumination
03585 //compute an index based on color order. The tolerance determines if two
03586 //color bands are too close to determine order, i.e. they should be considered
03587 //equal instead
03588 //the two relations being considered  are <, > and =, so the relationship
03589 //graph looks like:
03590 //
03591 //         G
03592 //       /   \.
03593 //   > < =   > < =
03594 //    /         \.
03595 //  R  - > < = -  B
03596 //
03597 // Thus, there are three graph edges with each of three possible labels or
03598 // 9 possible order codes. An easy coding scheme is to use the top 6 bits of
03599 // the byte output pixel. The relationship is encoded as states of bit pairs
03600 // Color relations  R*G  R*B  G*B    * indicates > < = (1,2,3)
03601 // Bit indices      7,6  5,4  3,2
03602 //
03603 vil_image_view<unsigned char> brip_vil_float_ops::
03604 color_order(vil_image_view<float> const& color_image, float eq_tol)
03605 {
03606   unsigned ni = color_image.ni(), nj = color_image.nj();
03607   unsigned np = color_image.nplanes();
03608   vil_image_view<unsigned char> temp;
03609   if (np!=3)
03610     return temp;//improper call
03611   temp.set_size(ni, nj);
03612   unsigned char rg_eq=192, rg_lt=128, rg_gt=64;
03613   unsigned char rb_eq=48, rb_lt=32, rb_gt=16;
03614   unsigned char gb_eq=12, gb_lt=8, gb_gt=4;
03615   for (unsigned j = 0; j<nj; ++j)
03616     for (unsigned i = 0; i<ni; ++i) {
03617       float r=color_image(i,j,0), g=color_image(i,j,1), b=color_image(i,j,2);
03618       unsigned char rg, rb, gb;
03619       //rg code
03620       if (vcl_fabs(r-g)<eq_tol)
03621         rg = rg_eq;
03622       else if (r<g)
03623         rg = rg_lt;
03624       else rg = rg_gt;
03625       //rb code
03626       if (vcl_fabs(r-b)<eq_tol)
03627         rb = rb_eq;
03628       else if (r<b)
03629         rb = rb_lt;
03630       else rb = rb_gt;
03631       //gb code
03632       if (vcl_fabs(g-b)<eq_tol)
03633         gb = gb_eq;
03634       else if (g<b)
03635         gb = gb_lt;
03636       else gb = gb_gt;
03637       unsigned char v = static_cast<unsigned char>(rg|rb|gb); // bitwise or
03638       temp(i,j) = v;
03639     }
03640   return temp;
03641 }
03642 
03643 #if 0 // only used in commented-out part of brip_vil_float_ops::extrema()
03644 static double zs(double x)
03645 { if (x < 0.0001 && x > -0.0001) return 0; else return x; }
03646 #endif // 0
03647 
03648 static double brip_vil_rot_gauss(double x, double y,
03649                                  double sigma_x, double sigma_y, double theta)
03650 {
03651   double theta_rad = theta*vnl_math::pi/180.0;
03652   double s = vcl_sin(theta_rad), c = vcl_cos(theta_rad);
03653   double sxr = 1.0/sigma_x, syr = 1.0/sigma_y;
03654   double ax = (c*x + s*y)*sxr, ay = (-s*x + c*y)*syr;
03655   double ret = vcl_exp(-0.5*(ax*ax + ay*ay));
03656   return ret*sxr*syr/(2.0*vnl_math::pi);
03657 }
03658 
03659 //revert angle to the range [-90, 90]
03660 float brip_vil_float_ops::extrema_revert_angle(float angle)
03661 {
03662   if (angle>=0.0f) {
03663     if (angle>90.0f&&angle<=270.0f)
03664       angle -= 180.0f;
03665     else if (angle>270.0f&&angle<=360.0f)
03666       angle -= 360.0f;
03667   }
03668   if (angle<0.0f) {
03669     if (angle<-90.0f&&angle>=-270.0f)
03670       angle += 180.0f;
03671     else if (angle<-270.0f&&angle>=-360.0f)
03672       angle += 360.0f;
03673   }
03674   return angle;
03675 }
03676 
03677 void brip_vil_float_ops::extrema_kernel_mask(float lambda0, float lambda1,
03678                                              float theta,
03679                                              vbl_array_2d<float>& kernel,
03680                                              vbl_array_2d<bool>& mask, float cutoff_percentage)
03681 {
03682   theta = extrema_revert_angle(theta);//in range [-90, 90]
03683   //convert theta to radians
03684   double theta_rad = theta*vnl_math::pi/180.0;
03685   double s = vcl_sin(theta_rad), c = vcl_cos(theta_rad);
03686   double s0 = lambda0, s1 = lambda1;
03687   double s1sq = 1.0/(s1*s1);
03688   //determine the size of the array
03689   double max_v = brip_vil_rot_gauss(0, 0, s0, s1, 0);
03690   double cutoff = max_v*cutoff_percentage; // if 0.01 then 1% tails removed
03691   unsigned ru = 0;
03692   while (brip_vil_rot_gauss(ru, 0, s0, s1, 0) >= cutoff)
03693     ++ru;
03694   unsigned rv = 0;
03695   while (brip_vil_rot_gauss(0, rv, s0, s1, 0) >= cutoff)
03696     ++rv;
03697 
03698   //rotate to get bounds
03699   int ri = static_cast<int>(vcl_fabs(ru*c+rv*s) +0.5);
03700   int rj = static_cast<int>(vcl_fabs(ru*s+rv*c) +0.5);
03701   if (s<0) {
03702     ri = static_cast<int>(vcl_fabs(ru*c-rv*s) +0.5);
03703     rj = static_cast<int>(vcl_fabs(ru*s-rv*c) +0.5);
03704   }
03705 
03706   unsigned ncols = 2*ri +1, nrows = 2*rj+1;
03707   vbl_array_2d<double> coef(nrows, ncols);
03708   mask.resize(nrows,ncols);
03709   double residual = 0.0, total  = 0.0;
03710   for (int ry = -rj; ry<=rj; ++ry)
03711     for (int rx = -ri; rx<=ri; ++rx)
03712     {
03713       double g = brip_vil_rot_gauss(rx, ry, s0, s1, theta);
03714       mask[ry+rj][rx+ri] = g>=cutoff;
03715       double temp = (-s*rx + c*ry);
03716       temp = temp*temp*s1sq;
03717       double v = (temp -1)*g*s1sq;
03718       coef[ry+rj][rx+ri] = v;
03719       residual+=v;
03720       total += vcl_fabs(v);
03721     }
03722   double cor = 0.0;
03723   if (total)
03724     cor = -residual/total;
03725   kernel.resize(nrows, ncols);
03726   //correct any residual offset in coefficients
03727   //distribute proportionally to coefficient magnitude
03728   for (unsigned j = 0; j<nrows; ++j)
03729     for (unsigned i = 0; i<ncols; ++i)
03730     {
03731       double v = vcl_fabs(coef[j][i]);
03732       coef[j][i]+=v*cor;
03733       kernel[j][i]=static_cast<float>(coef[j][i]);
03734     }
03735 }
03736 
03737 // Compute the standard deviation of an operator response
03738 // given the image intensity standard deviation at each pixel
03739 vil_image_view<float> brip_vil_float_ops::
03740 std_dev_operator(vil_image_view<float> const& sd_image,
03741                  vbl_array_2d<float> const& kernel)
03742 {
03743   unsigned ni = sd_image.ni(), nj = sd_image.nj();
03744   unsigned nrows = kernel.rows(), ncols = kernel.cols();
03745   int rrad = (nrows-1)/2, crad = (ncols-1)/2;
03746   float sd_min, sd_max;
03747   vil_math_value_range(sd_image, sd_min, sd_max);
03748 
03749   vil_image_view<float> res(ni, nj);
03750   res.fill(sd_max);
03751 
03752   vbl_array_2d<float>ksq(nrows, ncols);
03753   for (unsigned r = 0; r<nrows; ++r)
03754     for (unsigned c = 0; c<ncols; ++c)
03755       ksq[r][c] = kernel[r][c]*kernel[r][c];
03756 
03757   for (int j = rrad; j<static_cast<int>(nj-rrad); ++j)
03758     for (int i = crad; i<static_cast<int>(ni-crad); ++i)
03759     {
03760       float sum = 0;
03761       for (int jj = -rrad; jj<=rrad; ++jj)
03762         for (int ii = -crad; ii<=crad; ++ii) {
03763           float sd_sq = sd_image(i+ii, j+jj);
03764           sd_sq *= sd_sq;
03765           sum += sd_sq*ksq[jj+rrad][ii+crad];
03766         }
03767       res(i,j) = vcl_sqrt(sum);
03768     }
03769   return res;
03770 }
03771 
03772 // Compute the standard deviation of an operator response
03773 // given the image intensity standard deviation at each pixel
03774 // uses a modified formula to compute std_dev
03775 vil_image_view<float> brip_vil_float_ops::
03776 std_dev_operator_method2(vil_image_view<float> const& sd_image,
03777                          vbl_array_2d<float> const& kernel)
03778 {
03779   unsigned ni = sd_image.ni(), nj = sd_image.nj();
03780   unsigned nrows = kernel.rows(), ncols = kernel.cols();
03781   int rrad = (nrows-1)/2, crad = (ncols-1)/2;
03782   float sd_min, sd_max;
03783   vil_math_value_range(sd_image, sd_min, sd_max);
03784 
03785   vil_image_view<float> res(ni, nj);
03786   res.fill(sd_max);
03787 
03788   for (int j = rrad; j<static_cast<int>(nj-rrad); ++j)
03789     for (int i = crad; i<static_cast<int>(ni-crad); ++i)
03790     {
03791       float sum = 0;
03792       for (int jj = -rrad; jj<=rrad; ++jj)
03793         for (int ii = -crad; ii<=crad; ++ii) {
03794           float sd = sd_image(i+ii, j+jj);
03795           sum += (float)(sd*vcl_abs(kernel[jj+rrad][ii+crad]));
03796         }
03797       res(i,j) = sum;
03798     }
03799   return res;
03800 }
03801 
03802 vil_image_view<float>
03803 brip_vil_float_ops::extrema(vil_image_view<float> const& input,
03804                             float lambda0, float lambda1, float theta,
03805                             bool bright, bool output_response_mask,
03806                             bool unclipped_response)
03807 {
03808   vbl_array_2d<float> fa;
03809   vbl_array_2d<bool> mask;
03810   brip_vil_float_ops::extrema_kernel_mask(lambda0, lambda1, theta,
03811                                           fa, mask);
03812   unsigned nrows = fa.rows(), ncols = fa.cols();
03813   int rj = (nrows-1)/2, ri = (ncols-1)/2;
03814   vbl_array_2d<double> coef(nrows,ncols);
03815   for (unsigned r = 0; r<nrows; ++r)
03816     for (unsigned c = 0; c<ncols; ++c)
03817       coef[r][c]=fa[r][c];
03818 
03819 #if 0
03820   // (unused) double max_v = brip_vil_rot_gauss(0, 0, lambda0, lambda1, 0);
03821   // (unused) double cutoff=static_cast<float>(max_v*0.01); // 1% tails removed
03822   vcl_cout << "\ngauss ={";
03823   for (unsigned j = 0; j<nrows; ++j) {
03824     vcl_cout << '{';
03825     for (unsigned i = 0; i<ncols-1; ++i)
03826       vcl_cout << zs(coef[j][i]) << ',';
03827     if (j != nrows-1)
03828       vcl_cout << zs(coef[j][ncols-1]) << "},";
03829     else
03830       vcl_cout << zs(coef[j][ncols-1]) << '}';
03831   }
03832 
03833   vcl_cout << "};\n\nmask ={";
03834   for (unsigned j = 0; j<nrows; ++j) {
03835     vcl_cout << '{';
03836     for (unsigned i = 0; i<ncols-1; ++i)
03837       vcl_cout << mask[j][i] << ',';
03838     if (j != nrows-1)
03839       vcl_cout << mask[j][ncols-1] << "},";
03840     else
03841       vcl_cout << mask[j][ncols-1] << '}';
03842   }
03843   vcl_cout << "};" << vcl_flush;
03844 #endif
03845   //compute response image
03846   unsigned ni = input.ni(), nj = input.nj();
03847   vil_image_view<float> temp(ni, nj);
03848   vil_image_view<float> temp2(ni, nj);
03849   temp.fill(0.0f); temp2.fill(0.0f);
03850   for (unsigned j = rj; j<(nj-rj); j++)
03851     for (unsigned i = ri; i<(ni-ri); i++) {
03852       double sum = 0;
03853       for (int jj=-rj; jj<=rj; ++jj)
03854         for (int ii=-ri; ii<=ri; ++ii)
03855           if (mask[jj+rj][ii+ri])
03856             sum += coef[jj+rj][ii+ri]*input(i+ii, j+jj);
03857       temp2(i,j) = static_cast<float>(sum);
03858       if (bright) { // coefficients are negative at center
03859         if (sum<0) temp(i,j) = static_cast<float>(-sum);
03860       }
03861       else {
03862         if (sum>0) temp(i,j) = static_cast<float>(sum);
03863       }
03864     }
03865   //non-max suppression
03866   vil_image_view<float> res(temp);
03867 
03868   for (unsigned j = rj; j<(nj-rj); j++)
03869     for (unsigned i = ri; i<(ni-ri); i++)
03870     {
03871       float cv = temp(i,j);
03872       for (int jj=-rj; jj<=rj; ++jj)
03873         for (int ii=-ri; ii<=ri; ++ii)
03874           if ((ii==0&&jj==0)||!mask[jj+rj][ii+ri])
03875             continue;
03876           else if (temp(i+ii, j+jj)>cv)
03877             res(i,j)=0.0f;
03878     }
03879   if (!output_response_mask&&!unclipped_response)
03880     return res;
03881   unsigned np = 2;
03882   if (output_response_mask&&unclipped_response)
03883     np = 3;
03884   //response plane and mask plane and/or unclipped response
03885   vil_image_view<float> res_mask(ni, nj, np);
03886   res_mask.fill(0.0f);
03887   // always copy response to plane 0
03888   for (unsigned j = rj; j<(nj-rj); j++)
03889     for (unsigned i = ri; i<(ni-ri); i++)
03890     {
03891       float rv = res(i,j);
03892       res_mask(i,j,0)=rv;
03893     }
03894   // copy mask plane to plane 1 (or not)
03895   if (output_response_mask) {
03896   for (unsigned j = rj; j<(nj-rj); j++)
03897     for (unsigned i = ri; i<(ni-ri); i++)
03898     {
03899       float rv = res(i,j);
03900       if (!rv)
03901         continue;
03902       for (int jj=-rj; jj<=rj; ++jj)
03903         for (int ii=-ri; ii<=ri; ++ii)
03904           if (mask[jj+rj][ii+ri])
03905             if (rv>res_mask(i+ii,j+jj,1))
03906               res_mask(i+ii,j+jj,1) = rv;
03907     }
03908   }
03909   // copy unclipped response to plane 1 if no mask plane, otherwise plane 2
03910   if (unclipped_response) {
03911     unsigned p = np-1;
03912     for (unsigned j = rj; j<(nj-rj); j++)
03913       for (unsigned i = ri; i<(ni-ri); i++)
03914         res_mask(i,j,p) = temp2(i,j);
03915   }
03916 
03917   return res_mask;
03918 }
03919 
03920 
03921 //: Find anisotropic intensity extrema at a range of orientations and return the maximal response at the best orientation. Theta interval is in degrees
03922 //  if lambda0 == lambda1 then reduces to the normal extrema operator
03923 vil_image_view<float> brip_vil_float_ops::extrema_rotational(vil_image_view<float> const& input,
03924                                                              float lambda0, float lambda1,
03925                                                              float theta_interval, bool bright)
03926 {
03927   unsigned ni = input.ni();
03928   unsigned nj = input.nj();
03929 
03930   vil_image_view<float> output(ni, nj, 3); // the first plane is the response strength, the second is the best angle and the third is the mask for that angle
03931   output.fill(0.0f);
03932 
03933   if (lambda0 == lambda1) {  // if isotropic reduces to normal extrema calculation (perfect orientation symmetry)
03934     vil_image_view<float> res_mask_current = extrema(input, lambda0, lambda1, 0.0f, bright, true, true);
03935     for (unsigned j = 0; j<nj; j++)
03936       for (unsigned i = 0; i<ni; i++) {
03937         output(i,j,0) = res_mask_current(i,j,0);  // return the non-max suppressed for angle 0
03938         output(i,j,1) = 0.0f;
03939         output(i,j,2) = res_mask_current(i,j,1);
03940       }
03941     return output;
03942   }
03943 
03944   //: the kernel generator does not treat the x and y axis symmetrically, the method works correctly only when lambda0 > lambda1
03945   //  theoretically one can always call this method by switching the lambdas but the caller of the method should make this switch if needed hence the assertion
03946   if (lambda0 < lambda1) {
03947     vcl_cout << "In brip_vil_float_ops::extrema_rotational() - ERROR! rotational extrema operator requires lambda0 to be larger than lambda1! switch the lambdas and use the output angle map accordingly!\n";
03948     throw 0;
03949   }
03950   if (theta_interval < vcl_numeric_limits<float>::epsilon()) {
03951     vcl_cout << "In brip_vil_float_ops::extrema_rotational() - ERROR! theta_interval needs to be larger than 0!\n";
03952     throw 0;
03953   }
03954 
03955   vil_image_view<float> res_img(ni, nj);
03956   vil_image_view<int> res_angle(ni, nj);
03957   res_img.fill(0.0f); res_angle.fill(0);
03958 
03959   vcl_vector<float> angles;
03960   angles.push_back(-1.0f);
03961   for (float theta = 0; theta < 180; theta += theta_interval) { angles.push_back(theta); }
03962 
03963   vcl_vector<vbl_array_2d<bool> > mask_vect(angles.size(), vbl_array_2d<bool>());
03964   int max_rji = 0;
03965   //: elliptical operator has 180 degree rotational symmetry, so only the angles in the range [0,180] matter
03966   float theta = 0; unsigned theta_i = 1;
03967   for ( ; theta < 180; theta += theta_interval, theta_i++)
03968   {
03969     //: compute the response
03970     vbl_array_2d<float> fa; vbl_array_2d<bool> mask;
03971     brip_vil_float_ops::extrema_kernel_mask(lambda0, lambda1, theta, fa, mask);
03972     mask_vect[theta_i] = mask;
03973     unsigned nrows = fa.rows(), ncols = fa.cols();
03974     int rj = (nrows-1)/2, ri = (ncols-1)/2;
03975     if (rj > max_rji) max_rji = rj;
03976     if (ri > max_rji) max_rji = ri;
03977 
03978     for (unsigned j = rj; j<(nj-rj); j++)
03979       for (unsigned i = ri; i<(ni-ri); i++) {
03980         float res = 0.0f;
03981         double sum = 0;
03982         for (int jj=-rj; jj<=rj; ++jj)
03983           for (int ii=-ri; ii<=ri; ++ii)
03984             if (mask[jj+rj][ii+ri])
03985               sum += double(fa[jj+rj][ii+ri])*input(i+ii, j+jj);
03986 
03987         if (bright) { // coefficients are negative at center
03988           if (sum<0) res = static_cast<float>(-sum);
03989         }
03990         else {
03991           if (sum>0) res = static_cast<float>(sum);
03992         }
03993 
03994         if (res_img(i,j) < res) {
03995           res_img(i,j) = res;
03996           res_angle(i,j) = theta_i;
03997         }
03998       }
03999   }
04000 
04001   //: now we have pixel-wise best angle, run the non-max suppression around each non-zero pixel using the angles mask
04002   vil_image_view<float> res(res_img);
04003 
04004   for (unsigned j = max_rji; j<(nj-max_rji); j++)
04005     for (unsigned i = max_rji; i<(ni-max_rji); i++)
04006     {
04007       float cv = res_img(i,j);
04008       if (!cv)
04009         continue;
04010       int theta_i = res_angle(i,j);
04011       vbl_array_2d<bool> mask = mask_vect[theta_i];
04012       unsigned nrows = mask.rows(), ncols = mask.cols();
04013       int rj = (nrows-1)/2, ri = (ncols-1)/2;
04014 
04015       bool max = true;
04016       for (int jj=-rj; jj<=rj; ++jj) {
04017         for (int ii=-ri; ii<=ri; ++ii) {
04018           if ((ii==0&&jj==0)||!mask[jj+rj][ii+ri])
04019             continue;
04020           else if (res_img(i+ii, j+jj)>cv) {
04021             max = false;
04022             break;
04023           }
04024         }
04025         if (!max)
04026           break;
04027       }
04028       if (!max) {
04029         res(i,j) = 0.0f;
04030         res_angle(i, j) = 0; // the zeroth angle is -1.0f, so invalid
04031       }
04032     }
04033 
04034   vil_image_view<float> res_mask(ni, nj);
04035   res_mask.fill(0.0f);
04036   // now all the non-zero elements in res are our responses, create the mask image using the angle info
04037   for (unsigned j = max_rji; j<(nj-max_rji); j++)
04038     for (unsigned i = max_rji; i<(ni-max_rji); i++)
04039     {
04040       float rv = res(i,j);
04041       if (!rv)
04042         continue;
04043       int theta_i = res_angle(i,j);
04044       //: get the mask for this angle
04045       vbl_array_2d<bool> mask = mask_vect[theta_i];
04046       unsigned nrows = mask.rows(), ncols = mask.cols();
04047       int rj = (nrows-1)/2, ri = (ncols-1)/2;
04048 
04049       for (int jj=-rj; jj<=rj; ++jj)
04050         for (int ii=-ri; ii<=ri; ++ii)
04051           if (mask[jj+rj][ii+ri])
04052             if (rv>res_mask(i+ii,j+jj))
04053               res_mask(i+ii,j+jj) = rv;
04054     }
04055 
04056   //: now prepare the output accordingly
04057   for (unsigned j = max_rji; j<(nj-max_rji); j++)
04058     for (unsigned i = max_rji; i<(ni-max_rji); i++)
04059     {
04060       output(i,j,0) = res(i,j);
04061       output(i,j,1) = angles[res_angle(i,j)];
04062       output(i,j,2) = res_mask(i,j);
04063     }
04064   return output;
04065 }
04066 
04067 
04068 //: theta and phi are in radians
04069 float brip_vil_float_ops::elu(float phi, float lamda0,
04070                               float lambda1, float theta)
04071 {
04072   double sp = vcl_sin(phi), cp = vcl_cos(phi);
04073   double st = vcl_sin(theta), ct = vcl_cos(theta);
04074   double temp = 3.0*lamda0*cp*ct-3.0*lambda1*sp*st;
04075   return static_cast<float>(temp);
04076 }
04077 
04078 //: theta and phi are in radians
04079 float brip_vil_float_ops::elv(float phi, float lamda0,
04080                               float lambda1, float theta)
04081 {
04082   double sp = vcl_sin(phi), cp = vcl_cos(phi);
04083   double st = vcl_sin(theta), ct = vcl_cos(theta);
04084   double temp = 3.0*lamda0*st*cp + 3.0*lambda1*sp*ct;
04085   return static_cast<float>(temp);
04086 }
04087 
04088 //: theta and phi are in radians
04089 void brip_vil_float_ops::
04090 max_inscribed_rect(float lambda0, float lambda1, float theta,
04091                    float& u_rect, float& v_rect)
04092 {
04093   float sign = -1.0f;
04094   if (theta<0) sign = 1.0f;
04095   float th_rad = static_cast<float>(theta*vnl_math::pi/180.0);
04096   float maxa = 0.0f, max_phi = 0.0f;
04097   for (float phi = -float(vnl_math::pi); phi<=float(vnl_math::pi); phi+=0.01f)
04098   {
04099     float a = (1.0f+brip_vil_float_ops::elu(phi, lambda0, lambda1, th_rad));
04100     a *= (1.0f+ sign*brip_vil_float_ops::elv(phi, lambda0, lambda1, th_rad));
04101     if (a>maxa)
04102     {
04103       maxa = a;
04104       max_phi = phi;
04105     }
04106   }
04107   u_rect = brip_vil_float_ops::elu(max_phi, lambda0, lambda1, th_rad);
04108   v_rect = brip_vil_float_ops::elv(max_phi, lambda0, lambda1, th_rad);
04109   v_rect = vcl_fabs(v_rect);
04110 }
04111 
04112 static void eu(float lambda0, float cutoff_per, vcl_vector<float>& kernel)
04113 {
04114   kernel.clear();
04115   double l_sqi = 1/(lambda0*lambda0);
04116   double norm = 1/vcl_sqrt(2.0*vnl_math::pi);
04117   norm /= lambda0;
04118   int r = static_cast<int>(vcl_sqrt((-2.0*vcl_log(cutoff_per))/l_sqi)+0.5);
04119   for (int i = -r; i<=r; ++i)
04120   {
04121     double k = vcl_exp(-0.5*i*i*l_sqi);
04122     k*=norm;
04123     kernel.push_back(static_cast<float>(k));
04124   }
04125 }
04126 
04127 static void ev(float lambda1, float cutoff_per, vcl_vector<float>& kernel)
04128 {
04129   kernel.clear();
04130   double l1_sqi = 1/(lambda1*lambda1);
04131   int r = static_cast<int>(vcl_sqrt((-2.0*vcl_log(cutoff_per))/l1_sqi)+0.5);
04132   double norm = l1_sqi/lambda1;
04133   norm /= vcl_sqrt(2.0*vnl_math::pi);
04134   for (int i = -r; i<=r; ++i)
04135   {
04136     double s = i*i*l1_sqi;
04137     double k = vcl_exp(-0.5*s);
04138     k *= norm*(s-1.0);
04139     kernel.push_back(static_cast<float>(k));
04140   }
04141 }
04142 
04143 static vil_image_view<float> convolve_u(vil_image_view<float> const& image,
04144                                         vcl_vector<float> const& kernel,
04145                                         bool initialize = true)
04146 {
04147   int nk = kernel.size();
04148   if (nk<3)
04149     return image;
04150   int ni = image.ni(), nj = image.nj();
04151   int ru = (nk-1)/2;
04152   vil_image_view<float> Iu(ni, nj);
04153   if (initialize) Iu.fill(0.0f);
04154   for (int j = 0; j<nj; ++j)
04155     for (int i = ru; i<ni-ru; ++i) {
04156       float sum = 0.0f;
04157       for (int k = -ru; k<=ru; ++k)
04158       {
04159         float v = image(i+k, j);
04160         sum += kernel[k+ru]*v;
04161       }
04162       Iu(i,j) = sum;
04163     }
04164   return Iu;
04165 }
04166 
04167 static vil_image_view<float> convolve_v(vil_image_view<float> const& image,
04168                                         vcl_vector<float> const& kernel,
04169                                         bool initialize = true)
04170 {
04171   int nk = kernel.size();
04172   if (nk<3)
04173     return image;
04174   int ni = image.ni(), nj = image.nj();
04175   int rv = (nk-1)/2;
04176   vil_image_view<float> Iv(ni, nj);
04177   if (initialize) Iv.fill(0.0f);
04178   for (int j = rv; j<nj-rv; ++j)
04179     for (int i = 0; i<ni; ++i) {
04180       float sum = 0.0f;
04181       for (int k = -rv; k<=rv; ++k)
04182       {
04183         float v = image(i, j+k);
04184         sum += kernel[k+rv]*v;
04185       }
04186       Iv(i,j) = sum;
04187     }
04188   return Iv;
04189 }
04190 
04191 // this function tracks the image origin during rotation
04192 // the calculations are the same as used in the homography method.
04193 static void rotation_offset(int ni, int nj, float theta_deg,
04194                             int i, int j, int& ti, int& tj)
04195 {
04196   ti= 0; tj = 0;
04197   double deg_to_rad = vnl_math::pi/180.0;
04198   double rang = deg_to_rad*theta_deg;
04199   vgl_h_matrix_2d<double> H;
04200   H.set_identity();
04201   H.set_rotation(rang);
04202   vsol_box_2d_sptr input_roi;
04203   vsol_polygon_2d_sptr input_poly, output_poly;
04204   input_roi = new vsol_box_2d();
04205   input_roi->add_point(0, 0);
04206   input_roi->add_point(ni, nj);
04207   input_poly = bsol_algs::poly_from_box(input_roi);
04208   if (!bsol_algs::homography(input_poly, H, output_poly))
04209     return;
04210   vsol_box_2d_sptr temp = output_poly->get_bounding_box();
04211     vnl_matrix_fixed<double,3, 3> Mt = H.get_matrix();
04212     vnl_matrix_fixed<double,3, 3> t, tMt;
04213     t[0][0]=1;  t[0][1]=0; t[0][2]=-temp->get_min_x();
04214     t[1][0]=0;  t[1][1]=1; t[1][2]=-temp->get_min_y();
04215     t[2][0]=0;  t[2][1]=0; t[2][2]=1;
04216     tMt = t*Mt;
04217     vnl_vector_fixed<double, 3> org, torg;
04218     org[0]=i; org[1]=j; org[2]=1.0;
04219     torg = tMt*org;
04220     ti = static_cast<int>(torg[0]);
04221     tj = static_cast<int>(torg[1]);
04222 }
04223 
04224 vil_image_view<float> brip_vil_float_ops::
04225 fast_extrema(vil_image_view<float> const& input, float lambda0, float lambda1,
04226              float theta, bool bright, bool output_response_mask, bool unclipped_response,
04227              float cutoff)
04228 {
04229   // the kernels for u and v
04230   vcl_vector<float> euk, evk;
04231   eu(lambda0, cutoff, euk);
04232   ev(lambda1, cutoff, evk);
04233   vil_image_view<float> pre_res;
04234   int tuf=0, tvf=0, tui=0, tvi=0;
04235   vil_image_view<float> rot = input;
04236   bool ang90 = theta==90.0f||theta == 270.0f||
04237     theta ==-90.0f||theta == -270.0f;
04238   // rotate the image to the specified angle (minus due to flipped v axis)
04239   if (theta!=0&&!ang90)
04240     rot = brip_vil_float_ops::rotate(input, -theta);
04241   if (!ang90) {
04242     // convolve kernel across rows
04243     vil_image_view<float> rotu = convolve_u(rot, euk);
04244     // then convolve along columns
04245     vil_image_view<float> rotuv = convolve_v(rotu, evk);
04246     // rotate back
04247     if (theta!=0)
04248     {
04249       pre_res = brip_vil_float_ops::rotate(rotuv, +theta);
04250       // there is a border around the image to contain the rotated version
04251       // which should be removed.
04252       rotation_offset(input.ni(), input.nj(), -theta, 0, 0, tuf, tvf);
04253       rotation_offset(rotuv.ni(), rotuv.nj(), +theta, tuf, tvf, tui, tvi);
04254     }
04255     else pre_res = rotuv;
04256   }
04257   else {
04258     //convolve along columns
04259     vil_image_view<float> rotv = convolve_v(rot, euk);
04260     //convolve across rows
04261     vil_image_view<float> rotvu = convolve_u(rotv, evk);
04262     pre_res = rotvu;
04263   }
04264   int ni = input.ni(), nj = input.nj();
04265   vil_image_view<float> res(ni, nj);
04266   res.fill(0.0f);
04267   vil_image_view<float> temp2(ni, nj);
04268   temp2.fill(0.0f);
04269   for (int j = 0; j<nj; ++j)
04270     for (int i = 0; i<ni; ++i)
04271     {
04272       float v = pre_res(i + tui, j+ tvi);
04273       if (bright) { // coefficients are negative at center
04274         if (v<0) res(i,j) = -v;
04275       }
04276       else {
04277         if (v>0) res(i,j) = v;
04278       }
04279       temp2(i,j) = v;
04280     }
04281 
04282   //
04283   // the second derivative convolution is complete
04284   // non-maximal suppression
04285   // determine the inscribed rect in the response mask with max area
04286   float u_rect, v_rect;
04287   brip_vil_float_ops::max_inscribed_rect(lambda0,lambda1,theta,u_rect,v_rect);
04288   int ni_rect = static_cast<int>(u_rect);
04289   int nj_rect = static_cast<int>(v_rect);
04290   vil_image_view<float> res_sup(ni, nj);
04291   res_sup.fill(0.0f);
04292   for ( int i= ni_rect; i< ni-ni_rect; i+= ni_rect+1 )
04293     for ( int j = nj_rect; j < nj-nj_rect; j += nj_rect+1 ) {
04294       int ci= i, cj = j;
04295       bool find_response = false;
04296       for ( int di= 0; di<= ni_rect; di++ )
04297         for ( int dj = 0; dj <= nj_rect; dj++ )
04298         {
04299           float v = res(ci,cj);
04300           float dv = res(i+di,j+dj);
04301           if (v==0.0f&&dv==0.0f) continue;
04302           find_response = true;
04303           if ( v < dv )
04304           {
04305             ci= i+di;
04306             cj = j+dj;
04307           }
04308         }
04309       if (!find_response)
04310         continue;
04311       res_sup(ci,cj) = res(ci,cj);
04312     }
04313 
04314   // determine the outside bounding box
04315   vbl_array_2d<float> kern;
04316   vbl_array_2d<bool> mask;
04317   brip_vil_float_ops::extrema_kernel_mask(lambda0, lambda1, theta, kern, mask);
04318   int ri = (kern.cols()-1)/2, rj = (kern.rows()-1)/2;
04319   // go through the array again and search about each retained
04320   // local maximum using the outside bounding box
04321   // there will be at most one non-zero response to test in the inscribed rect
04322   for (int j = rj; j<(nj-rj); j++)
04323     for (int i = ri; i<(ni-ri); i++)
04324     {
04325       float v = res_sup(i,j);
04326       if (v==0.0f) continue;
04327       for (int jj=-rj; jj<=rj; ++jj)
04328         for (int ii=-ri; ii<=ri; ++ii)
04329           if (!mask[rj+jj][ri+ii])//suppress just inside mask region
04330             continue;
04331           else if (res_sup(i+ii, j+jj)<v)
04332             res_sup(i+ii,j+jj)=0.0f;
04333     }
04334 
04335   if (!output_response_mask&&!unclipped_response)
04336     return res_sup;
04337   unsigned np = 2;
04338   if (output_response_mask&&unclipped_response)
04339     np = 3;
04340   //response plane and mask plane and/or unclipped response
04341   vil_image_view<float> res_mask(ni, nj, np);
04342   res_mask.fill(0.0f);
04343   // always copy response to plane 0
04344   for (int j = rj; j<(nj-rj); j++)
04345     for (int i = ri; i<(ni-ri); i++)
04346     {
04347       float rv = res_sup(i,j);
04348       res_mask(i,j,0)=rv;
04349     }
04350   // copy mask plane to plane 1 (or not)
04351   if (output_response_mask) {
04352   for (int j = rj; j<(nj-rj); j++)
04353     for (int i = ri; i<(ni-ri); i++)
04354     {
04355       float rv = res_sup(i,j);
04356       if (!rv)
04357         continue;
04358       for (int jj=-rj; jj<=rj; ++jj)
04359         for (int ii=-ri; ii<=ri; ++ii)
04360           if (mask[jj+rj][ii+ri])
04361             if (rv>res_mask(i+ii,j+jj,1))
04362               res_mask(i+ii,j+jj,1) = rv;
04363     }
04364   }
04365   // copy unclipped response to plane 1 if no mask plane, otherwise plane 2
04366   if (unclipped_response) {
04367     unsigned p = np-1;
04368     for (int j = rj; j<(nj-rj); j++)
04369       for (int i = ri; i<(ni-ri); i++)
04370         res_mask(i,j,p) = temp2(i,j);
04371   }
04372   return res_mask;
04373 }