contrib/gel/gevd/gevd_noise.cxx
Go to the documentation of this file.
00001 // This is gel/gevd/gevd_noise.cxx
00002 #include "gevd_noise.h"
00003 //:
00004 // \file
00005 
00006 #include <vcl_cmath.h> // for vcl_fabs()
00007 
00008 #include "gevd_pixel.h"
00009 #include "gevd_float_operators.h"
00010 
00011 const int INVALID = -1;
00012 const int KRADIUS = 10; // smooth raw histogram
00013 
00014 //: Generate the histogram curve at low responses to estimate the sensor/texture noise in data.
00015 gevd_noise::gevd_noise(const float* data, const int n, // data in typical region
00016                        const int number_of_bins) // granularity of histogram
00017   : hist(new float[number_of_bins]),
00018     nbin(number_of_bins)
00019 {
00020   // 1. Find the mean value, to estimate the range
00021   float range = 0;
00022   for (int i = 0; i < n; i++)
00023     range += data[i];
00024   range /= n;                   // sensor < texture < mean
00025   binsize = range/(nbin-1);     // first guess value
00026 #ifdef DEBUG
00027   vcl_cout << "Find mean of h(x) at: " << range << vcl_endl;
00028 #endif
00029 
00030   // 2. Search for visible peak in histogram
00031   while (true)                // search for range of low responses
00032   {
00033     for (int i = 0; i < nbin; i++)  // clear hist curve
00034       hist[i] = 0;
00035     for (int i = 0; i < n; i++) {   // create histogram with initial
00036       float strength = data[i]; // binsize and range
00037       if (strength < range)
00038         hist[int(strength/binsize)] += 1;
00039     }
00040     float peakh = 0; int peaki = INVALID; // find main peak of histogram
00041     for (int i = 0; i < nbin; i++)
00042       if (hist[i] > peakh) {
00043         peakh = hist[i];
00044         peaki = i;
00045       }
00046 #ifdef DEBUG
00047     vcl_cout << "Find peak of h(x) at: " << peaki*binsize
00048              << "  " << peakh << vcl_endl;
00049 #endif
00050     if (peaki < 10) {           // narrow range a whole lot
00051       range /= 10.0f, binsize /= 10.0f;
00052     } else if (peaki >= nbin-5) { // expand range a little
00053       range *= 1.5f, binsize *= 1.5f;
00054     } else {
00055       range = peaki*binsize*2;  // put peak near center of histogram
00056       binsize = range/(nbin-1);
00057       break;                    // range of histogram found
00058     }
00059   }
00060 
00061   // 3. Find smooth histogram around noise response
00062   for (int i = 0; i < nbin; i++) // clear hist curve
00063     hist[i] = 0;
00064   for (int i = 0; i < n; i++) { // create histogram with final
00065     float strength = data[i];   // binsize and range
00066     if (strength < range)
00067       hist[int(strength/binsize)] += 1;
00068   }
00069   gevd_float_operators::RunningSum(hist, hist, nbin, KRADIUS); // smooth by default
00070 #ifdef DEBUG
00071   for (int i = 0; i < nbin; i++)    // points of smoothed h(x)
00072     vcl_cout << hist[i] << ' ';
00073   vcl_cout << vcl_endl << vcl_endl;
00074 #endif
00075 }
00076 
00077 //: Free allocated space.
00078 
00079 gevd_noise::~gevd_noise()
00080 {
00081   delete [] hist;
00082 }
00083 
00084 //: Collect all edgels above zero, in an ROI at center of image.
00085 // This utility function is used to collect edgels from a
00086 // response image, i.e. gradient/hessian/laplacian, or other
00087 // edge response image.
00088 
00089 float*
00090 gevd_noise::EdgelsInCenteredROI(const gevd_bufferxy& magnitude,
00091                                 const gevd_bufferxy& dirx, const gevd_bufferxy& diry,
00092                                 int& nedgel, const int
00093 #ifdef MINROI
00094                                 roiArea
00095 #endif
00096                                )
00097 {
00098   nedgel = 0;
00099 #ifdef MINROI
00100   int area = magnitude.GetSizeX() * magnitude.GetSizeY();
00101   if (area < roiArea) {
00102     vcl_cerr << "Image is smaller than minimum ROI\n";
00103     return NULL;
00104   }
00105   float k = vcl_sqrt(float(roiArea) / area); // reduction factor
00106 #else
00107   float k = 1.0;
00108 #endif
00109   const int sx = int(k*magnitude.GetSizeX()), sy = int(k*magnitude.GetSizeY());
00110   float* edgels = new float [sx*sy];
00111   const int xmin = (magnitude.GetSizeX() - sx) / 2;
00112   const int xmax = xmin + sx;
00113   const int ymin = (magnitude.GetSizeY() - sy) / 2;
00114   const int ymax = ymin + sy;
00115   for (int j = ymin; j < ymax; j++)
00116     for (int i = xmin; i < xmax; i++)
00117     {
00118       float strength = floatPixel(magnitude, i, j);
00119       if (strength)
00120       {
00121         float x_s, s_x;
00122         float dx = floatPixel(dirx, i, j);
00123         float dy = floatPixel(diry, i, j);
00124         if (dy < 0) dx = -dx, dy = -dy; // modulo PI
00125         if (dx > 0) {           // which octant?
00126           if (dx > dy) {        // 0-45 degree
00127             float r = dy / dx;
00128             x_s = (r*floatPixel(magnitude, i-1, j-1) +
00129                    (1-r)*floatPixel(magnitude, i-1, j));
00130             s_x = (r*floatPixel(magnitude, i+1, j+1) +
00131                    (1-r)*floatPixel(magnitude, i+1, j));
00132           } else {              // 45-90 degree
00133             float r = dx / dy;
00134             x_s = (r*floatPixel(magnitude, i-1, j-1) +
00135                    (1-r)*floatPixel(magnitude, i, j-1));
00136             s_x = (r*floatPixel(magnitude, i+1, j+1) +
00137                    (1-r)*floatPixel(magnitude, i, j+1));
00138           }
00139         } else {
00140           dx = -dx;             // absolute value
00141           if (dy > dx) {        // 90-135 degree
00142             float r = dx / dy;
00143             x_s = (r*floatPixel(magnitude, i-1, j+1) +
00144                    (1-r)*floatPixel(magnitude, i, j+1));
00145             s_x = (r*floatPixel(magnitude, i+1, j-1) +
00146                    (1-r)*floatPixel(magnitude, i, j-1));
00147           } else {              // 135-180 degree
00148             float r = dy / dx;
00149             x_s = (r*floatPixel(magnitude, i+1, j-1) +
00150                    (1-r)*floatPixel(magnitude, i+1, j));
00151             s_x = (r*floatPixel(magnitude, i-1, j+1) +
00152                    (1-r)*floatPixel(magnitude, i-1, j));
00153           }
00154         }
00155         if (x_s < strength && strength > s_x)  // strict local maximum
00156           edgels[nedgel++] = strength;
00157       }
00158     }
00159   return edgels;
00160 }
00161 
00162 //:
00163 // Fit a Raleigh distribution to the histogram curve of
00164 // edgels with low magnitudes, h(x), to estimate the sensor noise,
00165 // as would be zero-crossing of dh(x), and texture noise as the dominant
00166 // peak in h(x). Setting the threshold at 3 times the sensor/texture
00167 // noise would eliminate 99% of all noisy edges.
00168 // The raw noise in the original image can be deduced from the filter
00169 // used to convolve with the image.
00170 // H. Voorhees & T. Poggio, Detecting Blobs as Textons in Natural Images,
00171 // Proc. 1987 IU Workshop, Los Angeles, CA.
00172 
00173 bool
00174 gevd_noise::EstimateSensorTexture(float& sensor, float& texture) const
00175 {
00176   // 1. Compute derivative of histogram, dh(x)
00177   float* dhist = new float[nbin];
00178 #ifdef DEBUG
00179   float mag =
00180 #endif
00181               gevd_float_operators::Slope(hist, dhist, nbin);
00182 #ifdef DEBUG
00183   mag *=
00184 #endif
00185          gevd_float_operators::RunningSum(dhist, dhist, nbin, KRADIUS);
00186 #ifdef DEBUG
00187   for (int i = 0; i < nbin; i++)        // points of smoothed dh(x)
00188     vcl_cout << dhist[i]/mag << ' ';
00189   vcl_cout << vcl_endl << vcl_endl;
00190 #endif
00191 
00192   // 2. Estimate sensor from first downward slope of dh(x)
00193   if (!WouldBeZeroCrossing(dhist, nbin, sensor)) {
00194     vcl_cerr << "Can not estimate sensor\n";
00195     return false;
00196   }
00197   sensor *= binsize;
00198 
00199   // 3. Find texture as zero-crossing of dh(x)
00200   if (!RealZeroCrossing(dhist, nbin, texture)) {
00201     vcl_cerr << "Can not estimate texture\n";
00202     return false;
00203   }
00204   texture *= binsize;
00205 
00206   // 4. Check for consistency
00207   if (sensor == 0 ||    // not found
00208       sensor > texture) // or inconsistency
00209     sensor = texture;
00210   delete [] dhist;
00211   return true;
00212 }
00213 
00214 
00215 //: Find would be zero-crossing of the derivative of the histogram from its downward curvature.
00216 // This is sensor noise in the ROI.  Protected.
00217 
00218 bool
00219 gevd_noise::WouldBeZeroCrossing(const float* dhist, const int nbin,
00220                                 float& index)
00221 {
00222   int imax=INVALID, i1=INVALID, i2=INVALID;// x-coord of downward slope
00223   float maxdh = 0, dh1 = 0, dh2 = 0;       // y-coord
00224   for (int i = 0; i < nbin; i++) {
00225     if (dhist[i] > maxdh) {     // going upward to max
00226       maxdh = dhist[i];
00227       imax = i;                 // peak in dh(x)
00228     }
00229   }
00230   if (imax == INVALID)          // can not find maximum in dh(x)
00231     return false;
00232 #ifdef DEBUG
00233   vcl_cout << "Find max of dh(x) at: " << imax << vcl_endl;
00234 #endif
00235   for (int i = imax; i < nbin; i++) // going forward to min
00236     if (dhist[i] < 0.5*maxdh) {
00237       dh2 = dhist[i];           // second point on downward slope
00238       i2 = i;
00239       break;
00240     }
00241   if (i2 == INVALID)            // range of histogram is too small
00242     return false;
00243 #ifdef DEBUG
00244   vcl_cout << "Find end of downward dh(x) at: " << i2 << ", " << dh2 << vcl_endl;
00245 #endif
00246   for (int i = i2; i > 0; i--)              // going backward to max
00247     if (dhist[i] > 0.9*maxdh) {
00248       dh1 = dhist[i];           // first point on downward slope
00249       i1 = i;
00250       break;
00251     }
00252   if (i1 == INVALID)            // can not first pt of downward slope
00253     return false;
00254 #ifdef DEBUG
00255   vcl_cout << "Find start of downward dh(x) at: " << i1 << ", " << dh1 << vcl_endl;
00256 #endif
00257   if (i1 >= i2 || dh1 <= dh2)   // downward slope is too short
00258     index = 0;
00259   else                          // from fitting the downward slope, find
00260     index = i2 + (i2-i1)*dh2/(dh1-dh2); // would-be zc
00261 #ifdef DEBUG
00262   vcl_cout << "Find would be zero-crossing dh(x) at: " << index << vcl_endl;
00263 #endif
00264   return true;
00265 }
00266 
00267 
00268 //: Find real zero-crossing of the derivative of the histogram.
00269 // This is texture noise in the ROI.  Protected.
00270 
00271 bool
00272 gevd_noise::RealZeroCrossing(const float* dhist, const int nbin,
00273                              float& index)
00274 {
00275   int i3=INVALID, imin=INVALID; // x-coord right of zero-crossing
00276   float dh3 = 0, mindh = 0;     // y-coord
00277   for (int i = 0; i < nbin; i++) {
00278     if (dhist[i] < mindh) {     // going downward to min
00279       mindh = dhist[i];
00280       imin = i;                 // bottom in dh(x)
00281     }
00282   }
00283   if (imin == INVALID)          // can not find minimum in dh(x)
00284     return false;
00285 #ifdef DEBUG
00286   vcl_cout << "Find min of dh(x) at: " << imin << vcl_endl;
00287 #endif
00288   for (int i = imin; i > 0; i--) // going backward from minimum
00289     if (dhist[i] >= 0) {
00290       dh3 = dhist[i];           // second point on downward slope
00291       i3 = i;
00292       break;
00293     }
00294   if (i3 == INVALID)            // range of histogram is too small
00295     return false;
00296   index = (float)i3;
00297   if (dh3 > 0) {                // interpolate zero-crossing
00298     int i4 = i3+1;
00299     float dh4 = (float)vcl_fabs(dhist[i4]);
00300     index = (i3*dh4 + i4*dh3) / (dh3 + dh4);
00301   }
00302 #ifdef DEBUG
00303   vcl_cout << "Find real zero-crossing dh(x) at: " << index << vcl_endl;
00304 #endif
00305   return true;
00306 }