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

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

Generated on Thu Jan 10 14:47:17 2008 for contrib/gel/gevd by  doxygen 1.4.4