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