1 #ifndef gevd_fold_h_ 2 #define gevd_fold_h_ 3 //: 4 // \file 5 // \brief detection of fold profiles in the intensity image 6 // 7 // Operator to implement Canny edge detector which finds elongated 8 // fold/roof contours with ddG. Then junctions are found by extending 9 // from end points of dangling contours. 10 // 11 // The recipe is: 12 // - Convolution with Gaussian with sigma typically = 1, 13 // to well-condition the surface before taking second derivative. 14 // Result is a smoothed image. 15 // 16 // - Convolution with Laplacian, or second difference. 17 // Result is a gradient image. Canny proves that first-derivative 18 // of the Gaussian is the optimum filter to detect elongated 19 // step profiles. 20 // 21 // - Optionally estimate sensor/texture sigma, if given noise 22 // sigma is a negative interpolation factor -k in range -[0 1]. 23 // noise_sigma = (1-k)*sensor_sigma + k*texture_sigma. 24 // Sensor and texture sigmas are estimated from the histogram 25 // of weak step edges, detected in an ROI centered on gradient image. 26 // (see Step constructor documentation in Step.C - JLM) 27 // 28 // - Non Maximum suppression in the direction of local Hessian, 29 // to detect pixels with maximum local curvature, above a priori 30 // noise response. Result is connected contours, width < 2, 31 // broken only at junctions with weaker chains. 32 // Also obtain subpixel accuracy normal to the contour. 33 // 34 // - Optionally extend from end points of contours to search for 35 // maximum curvature in the direction normal to the dangling contours, 36 // above some factor of the noise response. 37 // Result is a simple detection of all strong junctions. 38 // 39 // Input: Intensity image, smoothing sigma, sensor/texture noise sigma 40 // and threshold factors for detecting contour/junction edge elements. 41 // 42 // Output: Magnitude, direction, location of fold pixels, forming 43 // a connected network of contours. 44 // 45 // Complexity: O(|pixels|) time and space for convolutions. 46 // O(|edgels|) time for iterative extension to recover junctions. 47 // 48 // \verbatim 49 // Authors 50 // John Canny (1986) SM Thesis 51 // Chris Connolly (1987) use directional 1st-difference 52 // Van-Duc Nguyen (1989) add subpixel location, extension to find junctions 53 // Arron Heller (1992) translate from CLOS to C++ 54 // Van-Duc Nguyen (1995) add noise estimation, use FloatOperators. 55 // Joe Mundy (1997) Added strength and direction arrays for transfer to 56 // edgel chains. The flag "transfer" controls the 57 // setting of these arrays. Note difference with 58 // gevd_step.h.. Didn't want to affect global use of 59 // DetectEdgels. 60 // \endverbatim 61 //----------------------------------------------------------------------------- 62 63 #include <iostream> 64 #include <iosfwd> 65 #ifdef _MSC_VER 66 # include <vcl_msvc_warnings.h> 67 #endif 68 class gevd_bufferxy; 69 70 class gevd_fold 71 { 72 public: 73 //: Save parameters and create workspace for detecting fold profiles. 74 // High frequency features are smoothed away by smooth_sigma. 75 // Texture or white noise less than noise_sigma are not detected. 76 // smooth_sigma = 0.5-2.0, 1.0 insures separation of independent folds >= 2. 77 // noise_sigma = is standard deviation of intensity, in a uniform region. 78 // Optionally estimate sensor/texture sigma, if given noise sigma 79 // is a negative factor -k between -[0 1]. In this case, 80 // noise_sigma = (1-k)*sensor_sigma + k*texture_sigma. 81 // Response of white noise to the filter ddG is computed, and 82 // then multiplied by contour_factor/junction_factor to obtain 83 // thresholds for detecting contour/junction edges. 84 // 85 gevd_fold(float smooth_sigma=1, //!< spatial smoothing [0.5 2.0] 86 float noise_sigma=-0.5, //!< sensor/texture intensity noise -[0 1] 87 float contour_factor=1.0, //!< threshold factor for contour edgels 88 float junction_factor=1.5); //!< threshold factor for junction edgels 89 90 //: Free space allocated for detecting fold profiles. Does nothing. 91 ~gevd_fold() = default; 92 93 static gevd_bufferxy* null_bufferxy; 94 95 //: Detect fold profiles with ddG edge detector. 96 // The image is convolved with a Gaussian to smooth away 97 // high frequency noise, and insure separation of fold responses. 98 // Then the largest absolute eigenvalue, and corresponding eigenvector 99 // of the local Hessian are computed 100 // using second difference [+1 -2 +1]. 101 // Optionally estimate sensor/texture sigma and set threshold. 102 // Finally, non maximum suppression is done to find strict 103 // local maxima of curvature. 104 // The edge detector finds elongated contours only. 105 // These contours are typically broken at junctions because non 106 // maximum suppression is done along only the strongest direction. 107 // Return contour (float), direction (byte), location (float) images. 108 // Return true if no exception. 109 // J. Canny, A Computational Approach to Edge Detection, 110 // IEEE Trans on PAMI, vol 8, no 6, Nov 1986. 111 bool DetectEdgels(const gevd_bufferxy& image, //!< float image 112 gevd_bufferxy*& edgels, //!< strength = dG * I 113 gevd_bufferxy*& direction, //!< direction % PI/4 114 gevd_bufferxy*& locationx, //!< subpixel loc 115 gevd_bufferxy*& locationy, 116 bool peaks_only = false, 117 bool valleys_only = false, 118 bool transfer = false, //if true, fill mag and angle arrays 119 gevd_bufferxy*& mag = null_bufferxy, // d2G magnitude 120 gevd_bufferxy*& angle = null_bufferxy); //Gradient orientation 121 122 //: 123 // Find junctions by searching for extensions of contours from 124 // their dangling end points. Non maximum suppression insures that 125 // contours have width < 2, and so we can find the left/right neighbors, 126 // and deduce end points. By using a minimally smoothed image, 127 // we find fold profiles up to joining with a stronger contour, thus 128 // recovering the missing junction caused by NMS along only 1 direction. 129 // The junctions are returned but are not set in the contour image, 130 // to prevent incorrect tracing of stronger contours first. 131 int RecoverJunctions(const gevd_bufferxy& image, //!< iterative extension 132 gevd_bufferxy& edgels, //!< from end points of contours 133 gevd_bufferxy& direction, 134 gevd_bufferxy& locationx, gevd_bufferxy& locationy, 135 int*& junctionx, int*& junctiony) const; 136 137 //: query stored/estimated noise sigma 138 // Return the standard deviation of raw noise, in the original image, 139 // either estimated or given by the user. If the noise has not been 140 // estimated, return 0. 141 float NoiseSigma() const; 142 143 //: response of noise sigma to filter ddG 144 // Compute response of white noise through the filter ddG, or 145 // second-derivative of the Gaussian. Using a threshold of 3 times 146 // this noise response would eliminate 99% of the noise edges. 147 float NoiseResponse() const; 148 149 //: elongated/directional? 150 // Return threshold for detecting contour or junction, 151 // which is response of white gaussian noise, noise_sigma, 152 // to fold edge detector, i.e. second-order derivative of Gaussian, 153 // smooth_sigma. 154 // noise_sigma can be estimated by finding the standard deviation 155 // in a region of constant intensity, and no texture patterns. 156 // Use short_factor*noise_sigma and smooth_sigma/2, when detecting 157 // junctions, to account for multiple responses to fold edge detector. 158 float NoiseThreshold(bool shortp=false) const; 159 160 //: 161 // Compute response of white noise through the filter ddG, or 162 // second-derivative of the Gaussian. Using a threshold of 3 times 163 // this noise response would eliminate 99% of the noise edges. 164 static float NoiseResponseToFilter(float noiseSigma, 165 float smoothSigma, 166 float filterFactor); 167 168 friend std::ostream& operator<<(std::ostream& os, const gevd_fold& st); 169 friend std::ostream& operator<<(std::ostream& os, gevd_fold& st); 170 171 protected: 172 float smoothSigma; //!< spatial smoothing 173 float noiseSigma; //!< sensor/texture noise 174 float contourFactor, junctionFactor; //!< threshold factor for edgels 175 float filterFactor{6}; //!< factor in convolution filter 176 }; 177 178 #endif // gevd_fold_h_ 179