contrib/gel/gevd/gevd_fold.h
Go to the documentation of this file.
00001 #ifndef gevd_fold_h_
00002 #define gevd_fold_h_
00003 //:
00004 // \file
00005 // \brief detection of fold profiles in the intensity image
00006 //
00007 // Operator to implement Canny edge detector which finds elongated
00008 // fold/roof contours with ddG. Then junctions are found by extending
00009 // from end points of dangling contours.
00010 //
00011 // The recipe is:
00012 //    -  Convolution with Gaussian with sigma typically = 1,
00013 //       to well-condition the surface before taking second derivative.
00014 //       Result is a smoothed image.
00015 //
00016 //    -  Convolution with Laplacian, or second difference.
00017 //       Result is a gradient image. Canny proves that first-derivative
00018 //       of the Gaussian is the optimum filter to detect elongated
00019 //       step profiles.
00020 //
00021 //    -  Optionally estimate sensor/texture sigma, if given noise
00022 //       sigma is a negative interpolation factor -k in range -[0 1].
00023 //       noise_sigma = (1-k)*sensor_sigma + k*texture_sigma.
00024 //       Sensor and texture sigmas are estimated from the histogram
00025 //       of weak step edges, detected in an ROI centered on gradient image.
00026 //       (see Step constructor documentation in Step.C - JLM)
00027 //
00028 //    -  Non Maximum suppression in the direction of local Hessian,
00029 //       to detect pixels with maximum local curvature, above a priori
00030 //       noise response. Result is connected contours, width < 2,
00031 //       broken only at junctions with weaker chains.
00032 //       Also obtain subpixel accuracy normal to the contour.
00033 //
00034 //    -  Optionally extend from end points of contours to search for
00035 //       maximum curvature in the direction normal to the dangling contours,
00036 //       above some factor of the noise response.
00037 //       Result is a simple detection of all strong junctions.
00038 //
00039 // Input: Intensity image, smoothing sigma, sensor/texture noise sigma
00040 //        and threshold factors for detecting contour/junction edge elements.
00041 //
00042 // Output: Magnitude, direction, location of fold pixels, forming
00043 //         a connected network of contours.
00044 //
00045 // Complexity: O(|pixels|) time and space for convolutions.
00046 //             O(|edgels|) time for iterative extension to recover junctions.
00047 //
00048 // \verbatim
00049 //  Authors
00050 //   John Canny      (1986) SM Thesis
00051 //   Chris Connolly  (1987) use directional 1st-difference
00052 //   Van-Duc Nguyen  (1989) add subpixel location, extension to find junctions
00053 //   Arron Heller    (1992) translate from CLOS to C++
00054 //   Van-Duc Nguyen  (1995) add noise estimation, use FloatOperators.
00055 //   Joe Mundy       (1997) Added strength and direction arrays for transfer to
00056 //                          edgel chains. The flag "transfer" controls the
00057 //                          setting of these arrays. Note difference with
00058 //                          gevd_step.h.. Didn't want to affect global use of
00059 //                          DetectEdgels.
00060 // \endverbatim
00061 //-----------------------------------------------------------------------------
00062 
00063 #include <vcl_iosfwd.h>
00064 class gevd_bufferxy;
00065 
00066 class gevd_fold
00067 {
00068  public:
00069   //: Save parameters and create workspace for detecting fold profiles.
00070   // High frequency features are smoothed away by smooth_sigma.
00071   // Texture or white noise less than noise_sigma are not detected.
00072   // smooth_sigma = 0.5-2.0, 1.0 insures separation of independent folds >= 2.
00073   // noise_sigma = is standard deviation of intensity, in a uniform region.
00074   // Optionally estimate sensor/texture sigma, if given noise sigma
00075   // is a negative factor -k between -[0 1]. In this case,
00076   // noise_sigma = (1-k)*sensor_sigma + k*texture_sigma.
00077   // Response of white noise to the filter ddG is computed, and
00078   // then multiplied by contour_factor/junction_factor to obtain
00079   // thresholds for detecting contour/junction edges.
00080   //
00081   gevd_fold(float smooth_sigma=1,       //!< spatial smoothing [0.5 2.0]
00082             float noise_sigma=-0.5,     //!< sensor/texture intensity noise -[0 1]
00083             float contour_factor=1.0,   //!< threshold factor for contour edgels
00084             float junction_factor=1.5); //!< threshold factor for junction edgels
00085 
00086   //: Free space allocated for detecting fold profiles.  Does nothing.
00087   ~gevd_fold() {}
00088 
00089   static gevd_bufferxy* null_bufferxy;
00090 
00091   //: Detect fold profiles with ddG edge detector.
00092   // The image is convolved with a Gaussian to smooth away
00093   // high frequency noise, and insure separation of fold responses.
00094   // Then the largest absolute eigenvalue, and corresponding eigenvector
00095   // of the local Hessian are computed
00096   // using second difference [+1 -2 +1].
00097   // Optionally estimate sensor/texture sigma and set threshold.
00098   // Finally, non maximum suppression is done to find strict
00099   // local maxima of curvature.
00100   // The edge detector finds elongated contours only.
00101   // These contours are typically broken at junctions because non
00102   // maximum suppression is done along only the strongest direction.
00103   // Return contour (float), direction (byte), location (float) images.
00104   // Return true if no exception.
00105   // J. Canny, A Computational Approach to Edge Detection,
00106   // IEEE Trans on PAMI, vol 8, no 6, Nov 1986.
00107   bool DetectEdgels(const gevd_bufferxy& image, //!< float image
00108                     gevd_bufferxy*& edgels, //!< strength = dG * I
00109                     gevd_bufferxy*& direction, //!< direction % PI/4
00110                     gevd_bufferxy*& locationx, //!< subpixel loc
00111                     gevd_bufferxy*& locationy,
00112                     bool peaks_only = false,
00113                     bool valleys_only = false,
00114                     bool transfer = false, //if true, fill mag and angle arrays
00115                     gevd_bufferxy*& mag = null_bufferxy, // d2G magnitude
00116                     gevd_bufferxy*& angle = null_bufferxy); //Gradient orientation
00117 
00118   //:
00119   // Find junctions by searching for extensions of contours from
00120   // their dangling end points. Non maximum suppression insures that
00121   // contours have width < 2, and so we can find the left/right neighbors,
00122   // and deduce end points. By using a minimally smoothed image,
00123   // we find fold profiles up to joining with a stronger contour, thus
00124   // recovering the missing junction caused by NMS along only 1 direction.
00125   // The junctions are returned but are not set in the contour image,
00126   // to prevent incorrect tracing of stronger contours first.
00127   int RecoverJunctions(const gevd_bufferxy& image, //!< iterative extension
00128                        gevd_bufferxy& edgels, //!< from end points of contours
00129                        gevd_bufferxy& direction,
00130                        gevd_bufferxy& locationx, gevd_bufferxy& locationy,
00131                        int*& junctionx, int*& junctiony);
00132 
00133   //: query stored/estimated noise sigma
00134   // Return the standard deviation of raw noise, in the original image,
00135   // either estimated or given by the user. If the noise has not been
00136   // estimated, return 0.
00137   float NoiseSigma() const;
00138 
00139   //: response of noise sigma to filter ddG
00140   // Compute response of white noise through the filter ddG, or
00141   // second-derivative of the Gaussian. Using a threshold of 3 times
00142   // this noise response would eliminate 99% of the noise edges.
00143   float NoiseResponse() const;
00144 
00145   //: elongated/directional?
00146   // Return threshold for detecting contour or junction,
00147   // which is response of white gaussian noise, noise_sigma,
00148   // to fold edge detector, i.e. second-order derivative of Gaussian,
00149   // smooth_sigma.
00150   // noise_sigma can be estimated by finding the standard deviation
00151   // in a region of constant intensity, and no texture patterns.
00152   // Use short_factor*noise_sigma and smooth_sigma/2, when detecting
00153   // junctions, to account for multiple responses to fold edge detector.
00154   float NoiseThreshold(bool shortp=false) const;
00155 
00156   //:
00157   // Compute response of white noise through the filter ddG, or
00158   // second-derivative of the Gaussian. Using a threshold of 3 times
00159   // this noise response would eliminate 99% of the noise edges.
00160   static float NoiseResponseToFilter(float noiseSigma,
00161                                      float smoothSigma,
00162                                      float filterFactor);
00163 
00164   friend vcl_ostream& operator<<(vcl_ostream& os, const gevd_fold& st);
00165   friend vcl_ostream& operator<<(vcl_ostream& os, gevd_fold& st);
00166 
00167  protected:
00168   float smoothSigma;                   //!< spatial smoothing
00169   float noiseSigma;                    //!< sensor/texture noise
00170   float contourFactor, junctionFactor; //!< threshold factor for edgels
00171   float filterFactor;                  //!< factor in convolution filter
00172 };
00173 
00174 #endif // gevd_fold_h_