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

gevd_fold.cxx

Go to the documentation of this file.
00001 // This is gel/gevd/gevd_fold.cxx
00002 #include "gevd_fold.h"
00003 //:
00004 // \file
00005 // Use 8 directions, with 45 degree angle in between them.
00006 //
00007 //\endverbatim
00008 
00009 #include <vcl_vector.h>
00010 #include <vcl_iostream.h>
00011 #include <vnl/vnl_math.h>
00012 #include <gevd/gevd_noise.h>
00013 #include <gevd/gevd_float_operators.h>
00014 #include <gevd/gevd_pixel.h>
00015 #include <gevd/gevd_bufferxy.h>
00016 #ifdef DEBUG
00017 # include <vul/vul_timer.h>
00018 #endif
00019 
00020 gevd_bufferxy* gevd_fold::null_bufferxy = 0;
00021 
00022 const unsigned char TWOPI = 8, FULLPI = 4, HALFPI = 2;
00023 const int DIS[] = { 1, 1, 0,-1,-1,-1, 0, 1, // 8-connected neighbors
00024                     1, 1, 0,-1,-1,-1, 0, 1, // wrapped by 2PI to
00025                     1, 1, 0,-1,-1,-1, 0, 1};// avoid modulo operations.
00026 const int DJS[] = { 0, 1, 1, 1, 0,-1,-1,-1,
00027                     0, 1, 1, 1, 0,-1,-1,-1,
00028                     0, 1, 1, 1, 0,-1,-1,-1};
00029 const int RDS[] = {0,-1, 1,-2, 2,-3, 3,-4, 4,-5, 5}; // radial search
00030 
00031 // const unsigned char DIR0 = 8, DIR1 = 9, DIR2 = 10, DIR3 = 11;
00032 const int FRAME = 4; // 3 for NMS and extension, 4 for contour
00033 
00034 gevd_fold::gevd_fold(float smooth_sigma, // width of filter dG
00035                      float noise_sigma,   // sensor/texture intensity noise -[0 1]
00036                      float contour_factor, float junction_factor)
00037   : smoothSigma(smooth_sigma), noiseSigma(noise_sigma),
00038     contourFactor(contour_factor), junctionFactor(junction_factor),
00039     filterFactor(6)              // factor from gevd_float_operators::Hessian
00040 {
00041   if (smoothSigma < 0.5)        // no guarrantee for 2-pixel separation
00042     vcl_cerr << "gevd_fold::gevd_fold -- too small smooth_sigma: "
00043              << smoothSigma << vcl_endl;
00044   if (smoothSigma > 2)          // smooth out too much the junctions
00045     vcl_cerr << "gevd_fold::gevd_fold -- too large smooth_sigma: "
00046              << smoothSigma << vcl_endl;
00047   if (noiseSigma < -1) {
00048     vcl_cerr << "gevd_fold::gevd_fold -- noiseSigma out of range -[0 1]: "
00049              << noiseSigma << ". Reset to -1.\n";
00050     noiseSigma = -1;
00051   }
00052 
00053   //vcl_cout << "Init Step\n" << *this << vcl_endl;
00054 }
00055 
00056 
00057 bool
00058 gevd_fold::DetectEdgels(const gevd_bufferxy& image,
00059                         gevd_bufferxy*& contour, gevd_bufferxy*& direction,
00060                         gevd_bufferxy*& locationx, gevd_bufferxy*& locationy,
00061                         bool peaks_only,
00062                         bool valleys_only,
00063                         bool transfer, //compute mag and angle? default=false
00064                         gevd_bufferxy*& mag, gevd_bufferxy*& angle)
00065 {
00066   //vcl_cout << "*** Detect step profiles with second-derivative of Gaussian"
00067   //         << *this
00068   //         << vcl_endl;
00069   if (image.GetBitsPixel() != bits_per_float) {
00070     vcl_cerr << "gevd_fold::DetectEdgels requires float image\n";
00071     return false;
00072   }
00073 
00074   // -tpk @@ missing check if the requested buffer size is too small to contain the convolution operations
00075 
00076   // 1. Smooth image to regularize data, before taking derivatives
00077   gevd_bufferxy* smooth = NULL;      // Gaussian smoothed image
00078   // use float to avoid overflow/truncation
00079   filterFactor = gevd_float_operators::Gaussian((gevd_bufferxy&)image, // well-condition before
00080                                                 smooth, smoothSigma); // 2nd-difference
00081 
00082   // 2. Use 2nd-difference to estimate local curvature, filter is ddG.
00083   gevd_bufferxy *curvature = NULL;
00084   // need to make new arrays since later NonMaximumSupression clears
00085   // locationx locationy
00086   gevd_bufferxy *dirx = gevd_float_operators::SimilarBuffer(image);
00087   gevd_bufferxy *diry = gevd_float_operators::SimilarBuffer(image);
00088   filterFactor *= gevd_float_operators::Hessian(*smooth, // 2nd-order difference
00089                                                  curvature, dirx, diry); // mult factor returned
00090   //If only peaks or valleys are asked for
00091   for (int j = 0; j < image.GetSizeY(); j++)
00092     for (int i = 0; i < image.GetSizeX(); i++)
00093       if ( (peaks_only   && floatPixel(*diry, i, j)<0) ||
00094            (valleys_only && floatPixel(*diry, i, j)>0)
00095          )
00096         floatPixel(*curvature, i, j) = 0;
00097 
00098   delete smooth;
00099 
00100   // 2.5 JLM - Fill the theta array for use in outputting continuous digital curve
00101   //           directions later.  The angle definition here is consistent with
00102   //           EdgeDetector, i.e. angle = (180/PI)*atan2(dI/dy, dI/dx);
00103   if (transfer) //Fill magnitude and angle arrays needed by EdgelChain const.
00104   {
00105     mag = gevd_float_operators::SimilarBuffer(image);
00106     angle = gevd_float_operators::SimilarBuffer(image);
00107     const float kdeg = 180.f*float(vnl_math::one_over_pi);
00108     for (int j = 0; j < image.GetSizeY(); j++)
00109       for (int i = 0; i < image.GetSizeX(); i++)
00110         if ((floatPixel(*mag, i, j) = floatPixel(*curvature, i, j)))
00111             floatPixel(*angle, i, j) = kdeg*vcl_atan2(floatPixel(*diry, i, j),
00112                                                       floatPixel(*dirx, i, j));
00113           else
00114             floatPixel(*angle, i, j) = 0;
00115   }
00116 
00117 
00118   // 3. Estimate sensor/texture sigmas from histogram of weak step edgels
00119   if (noiseSigma <= 0)  {
00120     int nedgel = 0;             // all edgels in ROI at center of image
00121     float* edgels = gevd_noise::EdgelsInCenteredROI(*curvature, *dirx, *diry, nedgel);
00122     if (edgels) {
00123       gevd_noise noise(edgels, nedgel); // histogram of weak edgels only
00124       delete [] edgels;
00125       float sensorNoise, textureNoise;
00126       if (noise.EstimateSensorTexture(sensorNoise, textureNoise)) {
00127         const float k = -noiseSigma; // given linear interpolation factor
00128         noiseSigma = ((1-k)*sensorNoise + k*textureNoise) /
00129           NoiseResponseToFilter(1, smoothSigma, filterFactor);
00130       } else {
00131         vcl_cout << "Can not estimate sensor & texture noise\n";
00132         noiseSigma = 1;         // reasonable default for 8-bit
00133       }
00134     } else {
00135       vcl_cout << "Not enough edge elements to estimate noise\n";
00136       noiseSigma = 1;
00137     }
00138     //vcl_cout << "Set noise sigma = " << noiseSigma << vcl_endl;
00139   }
00140 
00141   // 4. Find contour pixels as local maxima along slope direction
00142   //
00143   //                  [i,j+1]
00144   //                    ^
00145   //                    |
00146   // Note that [i-1 ,j] -> [i+1,j] indicates the sign of dirx and diry.
00147   //                    |          That is, if the intensities at the arrows
00148   //                 [i, j-1]      are larger than that at (i,j) then dirx or
00149   //                               diry are positive.  Again shown for a
00150   //                               right-handed (i,j) system -- JLM
00151   //
00152   // Thus for the following contour:
00153   //          ^ j            Normal Direction
00154   //          |________            ^
00155   //    light |        |           |    dirx = 0 and diry = +.  The direction
00156   //        __|________|__Contour__|    code returned by NonMaximumSupression
00157   //          |xxxxxxxx|                is 10 -- modulo 8 is 2, as shown.
00158   //    dark  |xxxxxxxx|
00159   //           ----------> i
00160   //
00161   // -----------------------------------------------------------------
00162 
00163   gevd_float_operators::NonMaximumSuppression(*curvature, *dirx, *diry,
00164                                               NoiseThreshold(), // above noise
00165                                               contour, direction,
00166                                               locationx, locationy);
00167   delete curvature; delete dirx; delete diry;
00168   gevd_float_operators::FillFrameX(*contour, 0, FRAME); // erase pixels in frame border
00169   gevd_float_operators::FillFrameY(*contour, 0, FRAME);
00170   return true;
00171 }
00172 
00173 //:
00174 // Return -/+ PI/2, to encode the existence of an end point
00175 // on the left/right side of the current contour point i,j.
00176 static int
00177 LeftXorRightEnd(const gevd_bufferxy& contour,
00178                 int i, int j, // pixel on contour
00179                 int dir) // normal to contour
00180 {
00181   int di = DIS[dir], dj = DJS[dir];
00182   bool normalp = (floatPixel(contour, i - di, j - dj) ||
00183                   floatPixel(contour, i + di, j + dj));
00184   if (normalp)                  // Substitute neighbor
00185     return 0;                   // for left or right side.
00186   bool leftp = false;
00187   int ndir = dir - HALFPI;      // left ndir
00188   for (int n = 0; n < 3; ++n) { // 3 neighbors
00189     int theta = ndir + RDS[n];
00190     if (floatPixel(contour, i+DIS[theta], j+DJS[theta])) {
00191       leftp = true;             // found neighbor on left side
00192       break;
00193     }
00194   }
00195   bool rightp = false;
00196   ndir = dir + HALFPI;          // right ndir
00197   for (int n = 0; n < 3; ++n) { // 3 neighbors
00198     int theta = ndir + RDS[n];
00199     if (floatPixel(contour, i+DIS[theta], j+DJS[theta])) {
00200     rightp = true;            // found neighbor on right side
00201       break;
00202     }
00203   }
00204   return (leftp? 0: -HALFPI) + (rightp? 0: HALFPI); // increment from dir
00205 }
00206 
00207 //: Find best fold extension from end point, which has largest local maximum curvature.
00208 // Search all 3x3=9 neighboring locations/directions.
00209 // Return location, direction and strength of this extension pixel.
00210 //
00211 static float
00212 BestFoldExtension(const gevd_bufferxy& smooth,
00213                   int i, int j,
00214                   int ndir, // tangential dir to neighbor
00215                   float threshold,
00216                   int& best_i, int& best_j, // pixel
00217                   int& best_d, float& best_l) // direction + subloc
00218 {
00219   float best_s = threshold;     // insure greater response
00220   const int direc = ndir + HALFPI; // fold direction
00221   for (int n = 0; n < 3; n++) { // all 3 neighboring pixels
00222     int ntheta = ndir + RDS[n];
00223     int ni = i + DIS[ntheta];
00224     int nj = j + DJS[ntheta];
00225     float pix = floatPixel(smooth, ni, nj); // center
00226     for (int d = 0; d < 3; d++) { // all 3 neighboring directions
00227       int dir = direc + RDS[d];
00228       int di = DIS[dir];
00229       int dj = DJS[dir];
00230       float pix_m = floatPixel(smooth, ni-di, nj-dj);
00231       float pix_p = floatPixel(smooth, ni+di, nj+dj);
00232       float curvature = (float)vcl_fabs(pix_p + pix_m - 2*pix);
00233       float max_s = (dir%HALFPI)? best_s*2: best_s;
00234       if (curvature > max_s) {      // find best strength
00235         int di2 = 2*di;
00236         int dj2 = 2*dj;
00237         if (curvature > vcl_fabs(pix + floatPixel(smooth, ni-di2, nj-dj2)
00238                                  - 2 * pix_m) &&
00239             curvature > vcl_fabs(pix + floatPixel(smooth, ni+di2, nj+dj2)
00240                                  - 2 * pix_p)) {
00241           best_i = ni;
00242           best_j = nj;
00243           best_s = (dir%HALFPI)? curvature/2 : curvature;
00244           best_d = dir%FULLPI + TWOPI; // in range [0 FULLPI) + TWOPI
00245         }
00246       }
00247     }
00248   }
00249   if (best_s > threshold) {     // interpolate with parabola
00250     float pix = floatPixel(smooth, best_i, best_j);
00251     int di = DIS[best_d], dj = DJS[best_d];
00252     int di2 = 2*di, dj2 = 2*dj;
00253     float s_m = (float)vcl_fabs(pix + floatPixel(smooth, best_i-di2, best_j-dj2)
00254                                 - 2*floatPixel(smooth, best_i-di, best_j-dj));
00255     float s_p = (float)vcl_fabs(pix + floatPixel(smooth, best_i+di2, best_j+dj2)
00256                                 - 2*floatPixel(smooth, best_i+di, best_j+dj));
00257     if (best_d%HALFPI) {
00258       s_m /= (float)2.0;
00259       s_p /= (float)2.0;
00260     }
00261     best_l = gevd_float_operators::InterpolateParabola(s_m, best_s, s_p, best_s);
00262     return best_s;
00263   } else                        // not found
00264     return 0;
00265 }
00266 
00267 
00268 int
00269 gevd_fold::RecoverJunctions(const gevd_bufferxy& image,
00270                             gevd_bufferxy& contour, gevd_bufferxy& direction,
00271                             gevd_bufferxy& locationx, gevd_bufferxy& locationy,
00272                             int*& junctionx, int*& junctiony)
00273 {
00274 #if defined(DEBUG)
00275   vul_timer t;
00276 #endif
00277   if (image.GetBitsPixel() != bits_per_float) {
00278     vcl_cerr << "gevd_fold::RecoverJunction requires float image\n";
00279     return false;
00280   }
00281   const int rmax = 1+FRAME;     // 1 + kernel radius of BestStepExtension
00282   const int kmax = int(4 * smoothSigma + 0.5) + 2; // gap = 2
00283   const int xmax = image.GetSizeX()-rmax-1; // fill fold direction
00284   const int ymax = image.GetSizeY()-rmax-1;
00285 #ifdef DEBUG
00286   vcl_cout << "RecoverJunctions: rmax, kmax, xmax, ymax:" << rmax << ' ' << kmax << ' ' << xmax << ' ' << ymax << '\n';
00287 #endif
00288   // 1. Find end points of dangling contours
00289   //const int length0 = xmax/kmax*ymax/kmax/4;// 25% size
00290   //const float growth = 2;     // growth ratio of the arrays
00291 
00292   vcl_vector<int> ndir; //  ndir.set_growth_ratio(growth);
00293   vcl_vector<int> xloc; //  xloc.set_growth_ratio(growth); // dynamic array instead of long lists
00294   vcl_vector<int> yloc; //  yloc.set_growth_ratio(growth);
00295   int xdir;
00296   for (int y = rmax; y <= ymax; y++) // find end points of long contours
00297     for (int x = rmax; x <= xmax; x++) // inside image border - rmax
00298       if (floatPixel(contour, x, y) && // on contour
00299           (xdir = LeftXorRightEnd(contour, x, y, // left xor right neighbor
00300                                   bytePixel(direction, x, y))) != 0)
00301       {
00302         ndir.push_back(xdir);   // save end point of elongated contours
00303         xloc.push_back(x);
00304         yloc.push_back(y);
00305       }
00306   const int length = ndir.size();
00307   //vcl_cout << "% end pats = "     // trace allocated size
00308   //          << length*100 / float((xmax/kmax)*(ymax/kmax)) << vcl_endl;
00309 
00310   // 2. Extend from end points until they touch other contours
00311   gevd_bufferxy* smooth = NULL;
00312   gevd_float_operators::Gaussian((gevd_bufferxy&)image, smooth, smoothSigma/2); // avoid oversmoothing
00313   const bool shortp = true;     // short contours
00314   const float threshold = NoiseThreshold(shortp);
00315   float curvature, loc;
00316   int njunction = 0;            // number of junctions found
00317   for (int r = 1; r <= kmax; r++) { // breadth-first extension
00318     int ntouch = 0, nextension = 0;
00319     for (int i = 0; i < length; i++)
00320       if ((xdir = ndir[i]) != 0 && xdir != TWOPI) // still extension?
00321       {
00322         int x = xloc[i], y = yloc[i];
00323         int dir = bytePixel(direction, x, y); // direction of fold
00324         curvature = BestFoldExtension(*smooth,
00325                                   x, y, dir + xdir,     // current end pt
00326                                   threshold,
00327                                   x, y, dir, loc);
00328         if (curvature) {                 // next end point
00329           xloc[i] = x, yloc[i] = y; // update new end point
00330           xdir = LeftXorRightEnd(contour,    // mark completed if
00331                                  x, y, dir); // another contour is reached,
00332                                              // indicated by xdir==0.
00333           if (xdir)
00334           {
00335             if (x < rmax || x > xmax || // check for reaching border
00336                 y < rmax || y > ymax)
00337               xdir = 0;               // junction with virtual border
00338             else
00339               nextension++;
00340             floatPixel(contour, x, y) = curvature; // still disconnected
00341           }
00342           else
00343           {           // touching another contour
00344             ntouch++;
00345             xdir = TWOPI;     // mark junction, without linking chains
00346           }
00347           ndir[i] = xdir;
00348           bytePixel(direction, x, y) = (unsigned char)(dir);
00349           floatPixel(locationx, x, y) = loc*DIS[dir];
00350           floatPixel(locationy, x, y) = loc*DJS[dir];
00351         } else                  // no further extension found
00352           ndir[i] = 0;
00353       }
00354     //vcl_cout << "Touch " << ntouch << " contours.\n";
00355     // vcl_cout << "Will extend " << nextension << " contours.\n";
00356     njunction += ntouch;
00357     if (!nextension) break;     // all either junction or termination
00358   }
00359   delete smooth;
00360 
00361   // 3. Return the end points or junctions found
00362   if (junctionx) delete [] junctionx;
00363   if (junctiony) delete [] junctiony;
00364   junctionx = new int[njunction];
00365   junctiony = new int[njunction];
00366   for (int i = 0, j = 0; i < length; i++)
00367     if (ndir[i] == TWOPI) {
00368       junctionx[j] = xloc[i];
00369       junctiony[j] = yloc[i];
00370       j++;
00371     }
00372 #if defined(DEBUG)
00373   vcl_cout << "Find " << length << " end points, and "
00374            << njunction << " junctions.\n"
00375            << "Recover " << 100.0*njunction/length
00376            << "% end points as junctions > "
00377            << threshold << ", in " << t.real() << " msecs.\n";
00378 #endif
00379   return njunction;
00380 }
00381 
00382 
00383 float
00384 gevd_fold::NoiseSigma() const
00385 {
00386   return (noiseSigma <= 0)? 0: noiseSigma;
00387 }
00388 
00389 
00390 float
00391 gevd_fold::NoiseResponse() const
00392 {
00393   return NoiseResponseToFilter(noiseSigma,
00394                                smoothSigma, filterFactor);
00395 }
00396 
00397 
00398 float
00399 gevd_fold::NoiseThreshold(bool shortp) const
00400 {
00401   float factor = (shortp? junctionFactor : contourFactor);
00402   float smooth = (shortp? smoothSigma/2 : smoothSigma);
00403   return factor * 3 *          // 3*sigma for 99% removal confidence
00404          NoiseResponseToFilter(noiseSigma, smooth, filterFactor);
00405 }
00406 
00407 
00408 float
00409 gevd_fold::NoiseResponseToFilter(float noiseSigma,
00410                                  float smoothSigma,
00411                                  float filterFactor)
00412 {
00413   return noiseSigma /          // white noise
00414          (float)vcl_pow((double)smoothSigma, 2.5) * // size of filter ddG
00415          ((float)vcl_sqrt(0.1875 * vnl_math::two_over_sqrtpi)) *
00416          filterFactor;        // factor in Hessian image
00417 }
00418 
00419 
00420 //: Output a snapshot of current control parameters
00421 vcl_ostream& operator<< (vcl_ostream& os, const gevd_fold& st)
00422 {
00423   os << "Fold:\n"
00424      << "   smoothSigma " << st.smoothSigma << vcl_endl
00425      << "   noiseSigma " << st.noiseSigma << vcl_endl
00426      << "   contourFactor " << st.contourFactor << vcl_endl
00427      << "   junctionFactor " << st.junctionFactor << vcl_endl
00428      << "   filterFactor " << st.filterFactor << vcl_endl;
00429     return os;
00430 }
00431 
00432 
00433 //: Output a snapshot of current control parameters
00434 vcl_ostream& operator<< (vcl_ostream& os, gevd_fold& st)
00435 {
00436   os << "Fold:\n"
00437      << "   smoothSigma " << st.smoothSigma << vcl_endl
00438      << "   noiseSigma " << st.noiseSigma << vcl_endl
00439      << "   contourFactor " << st.contourFactor << vcl_endl
00440      << "   junctionFactor " << st.junctionFactor << vcl_endl
00441      << "   filterFactor " << st.filterFactor << vcl_endl;
00442     return os;
00443 }

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