canny.h

Go to the documentation of this file.
00001 
00015 #ifndef _DLRCOMPUTERVISION_CANNY_H_
00016 #define _DLRCOMPUTERVISION_CANNY_H_
00017 
00018 #include <dlrComputerVision/image.h>
00019 
00020 namespace dlr {
00021 
00022   namespace computerVision {
00023     
00024 
00028     template <ImageFormat FORMAT>
00029     Image<GRAY1>
00030     applyCanny(const Image<FORMAT>& inputImage,
00031                size_t gaussianSize=5,
00032                double upperThreshold=0.0,
00033                double lowerThreshold=0.0);
00034 
00035   } // namespace computerVision
00036 
00037 } // namespace dlr
00038 
00039 
00040 /* =============== Implementation follows =============== */
00041 
00042 #include <list>
00043 #include <dlrComputerVision/filter.h>
00044 #include <dlrComputerVision/kernels.h>
00045 #include <dlrComputerVision/nonMaximumSuppress.h>
00046 #include <dlrComputerVision/sobel.h>
00047 #include <dlrComputerVision/utilities.h>
00048 #include <dlrNumeric/index2D.h>
00049 #include <dlrNumeric/utilities.h>
00050 
00051 namespace dlr {
00052 
00053   namespace computerVision {
00054 
00056     namespace privateCode {
00057 
00058       Image<GRAY1>
00059       traceEdges(const Image<GRAY_FLOAT64>& gradImage, double threshold)
00060       {
00061         Image<GRAY1> edgeImage(gradImage.rows(), gradImage.columns());
00062         edgeImage = false;
00063 
00064         std::list<Index2D> seedList;
00065         size_t index0 = 0;
00066         for(size_t row = 0; row < gradImage.rows(); ++row) {
00067           for(size_t column = 0; column < gradImage.columns(); ++column) {
00068             if(gradImage[index0] > threshold) {
00069               edgeImage[index0] = true;
00070               seedList.push_front(Index2D(static_cast<int>(row), static_cast<int>(column)));
00071             }
00072             ++index0;
00073           }
00074         }
00075 
00076         size_t lastRow = gradImage.rows() - 1;
00077         size_t lastColumn = gradImage.columns() - 1;
00078         size_t columns = gradImage.columns();
00079         while(seedList.size() != 0) {
00080           Index2D seed = *seedList.begin();
00081           seedList.pop_front();
00082           size_t row = seed.getRow();
00083           size_t column = seed.getColumn();
00084           if(row == 0 || column == 0
00085              || row == lastRow || column == lastColumn) {
00086             continue;
00087           }
00088           index0 = row * columns + column;
00089 
00090           size_t neighborIndex = index0 - columns - 1;
00091           if(gradImage(neighborIndex) != 0.0
00092              && edgeImage(neighborIndex) == false) {
00093             edgeImage(neighborIndex) = true;
00094             seedList.push_front(Index2D(static_cast<int>(row) - 1, static_cast<int>(column) - 1));
00095           }
00096           neighborIndex = index0 - columns;
00097           if(gradImage(neighborIndex) != 0.0
00098              && edgeImage(neighborIndex) == false) {
00099             edgeImage(neighborIndex) = true;
00100             seedList.push_front(Index2D(static_cast<int>(row) - 1, static_cast<int>(column)));
00101           }
00102           neighborIndex = index0 - columns + 1;
00103           if(gradImage(neighborIndex) != 0.0
00104              && edgeImage(neighborIndex) == false) {
00105             edgeImage(neighborIndex) = true;
00106             seedList.push_front(Index2D(static_cast<int>(row) - 1, static_cast<int>(column) + 1));
00107           }
00108           neighborIndex = index0 - 1;
00109           if(gradImage(neighborIndex) != 0.0
00110              && edgeImage(neighborIndex) == false) {
00111             edgeImage(neighborIndex) = true;
00112             seedList.push_front(Index2D(static_cast<int>(row), static_cast<int>(column) - 1));
00113           }
00114           neighborIndex = index0 + 1;
00115           if(gradImage(neighborIndex) != 0.0
00116              && edgeImage(neighborIndex) == false) {
00117             edgeImage(neighborIndex) = true;
00118             seedList.push_front(Index2D(static_cast<int>(row), static_cast<int>(column) + 1));
00119           }
00120           neighborIndex = index0 + columns - 1;
00121           if(gradImage(neighborIndex) != 0.0
00122              && edgeImage(neighborIndex) == false) {
00123             edgeImage(neighborIndex) = true;
00124             seedList.push_front(Index2D(static_cast<int>(row) + 1, static_cast<int>(column) - 1));
00125           }
00126           neighborIndex = index0 + columns;
00127           if(gradImage(neighborIndex) != 0.0
00128              && edgeImage(neighborIndex) == false) {
00129             edgeImage(neighborIndex) = true;
00130             seedList.push_front(Index2D(static_cast<int>(row) + 1, static_cast<int>(column)));
00131           }
00132           neighborIndex = index0 + columns + 1;
00133           if(gradImage(neighborIndex) != 0.0
00134              && edgeImage(neighborIndex) == false) {
00135             edgeImage(neighborIndex) = true;
00136             seedList.push_front(Index2D(static_cast<int>(row) + 1, static_cast<int>(column) + 1));
00137           }
00138         }
00139         return edgeImage;
00140       }
00141       
00142     } // namespace privateCode
00144 
00145     
00146     // This function applies the canny edge operator in the X
00147     // direction.
00148     template <ImageFormat FORMAT>
00149     Image<GRAY1>
00150     applyCanny(const Image<FORMAT>& inputImage,
00151                size_t gaussianSize,
00152                double upperThreshold,
00153                double lowerThreshold)
00154     {
00155       // Argument checking.
00156       if(inputImage.rows() < gaussianSize + 3
00157          || inputImage.columns() < gaussianSize + 3) {
00158         DLR_THROW(ValueException, "applyCanny()",
00159                   "Argument inputImage has insufficient size.");
00160       }
00161       if(lowerThreshold > upperThreshold) {
00162         DLR_THROW(ValueException, "applyCanny()",
00163                   "Argument lowerThreshold must be less than or equal to "
00164                   "Arguments upperThreshold.");
00165       }
00166 
00167       // We use non-normalized convolution kernels for the gradient,
00168       // which means that our user-specified thresholds don't match
00169       // the magnitude of our gradients.  Each gradient component is
00170       // 8 times as large as it should be, so the magnitude of the
00171       // gradient is 8*sqrt(2) times as large.  We solve this by
00172       // scaling the thresholds here.
00173       lowerThreshold *= std::sqrt(2.0) * 8.0;
00174       upperThreshold *= std::sqrt(2.0) * 8.0;
00175       
00176       // Step 1: Blur with a gaussian kernel to reduce noise.
00177       Image<GRAY_FLOAT64> blurredImage;
00178       if(gaussianSize == 0) {
00179         blurredImage = convertColorspace<GRAY_FLOAT64>(inputImage);
00180       } else {
00181         Kernel<double> gaussian =
00182           getGaussianKernel<double>(gaussianSize, gaussianSize);
00183         blurredImage = filter2D<GRAY_FLOAT64, GRAY_FLOAT64>(
00184           gaussian, inputImage, 0.0);
00185       }
00186 
00187       // Step 2: Compute derivatives of the blurred image, and discard
00188       // any which are less than the lower threshold.
00189       Image<GRAY_FLOAT64> gradX = applySobelX(blurredImage);
00190       Image<GRAY_FLOAT64> gradY = applySobelY(blurredImage);
00191       Image<GRAY_FLOAT64> gradMagnitude(gradX.rows(), gradX.columns());
00192 
00193       if(lowerThreshold > 0.0 && upperThreshold > 0.0) {
00194         // Discard values less than the lower threshold.
00195         for(size_t index0 = 0; index0 < gradX.size(); ++index0) {
00196           double tmpVal = std::sqrt(
00197             gradX[index0] * gradX[index0] + gradY[index0] * gradY[index0]);
00198           gradMagnitude[index0] = (tmpVal > lowerThreshold) ? tmpVal : 0.0;
00199         }
00200       } else {
00201         // Temporarily retain all gradient values.
00202         for(size_t index0 = 0; index0 < gradX.size(); ++index0) {
00203           gradMagnitude[index0] = std::sqrt(
00204             gradX[index0] * gradX[index0] + gradX[index0] * gradX[index0]);
00205         }
00206 
00207         // Pick edge thresholds.
00208         if(upperThreshold <= 0.0) {
00209           Float64 maxGrad = maximum(gradMagnitude.ravel());
00210           Float64 minGrad = minimum(gradMagnitude.ravel());
00211           upperThreshold = minGrad + 0.9 * (maxGrad - minGrad);
00212         }
00213         if(lowerThreshold <= 0.0) {
00214           lowerThreshold = 0.9 * upperThreshold;
00215         }
00216         for(size_t index0 = 0; index0 < gradX.size(); ++index0) {
00217           double tmpVal = gradMagnitude[index0];
00218           gradMagnitude[index0] = (tmpVal > lowerThreshold) ? tmpVal : 0.0;
00219         }
00220       }
00221           
00222       // Step 3: Non-maximum suppression.
00223       Image<GRAY_FLOAT64> edgeCandidates =
00224         nonMaximumSuppress(gradMagnitude, gradX, gradY);
00225 
00226       // Step 4: Threshold with hysteresis.
00227       Image<GRAY1> edgeImage =
00228         privateCode::traceEdges(edgeCandidates, upperThreshold);
00229       return edgeImage;
00230     }
00231 
00232   } // namespace computerVision
00233   
00234 } // namespace dlr
00235 
00236 #endif /* #ifndef _DLRCOMPUTERVISION_KERNEL_H_ */

Generated on Mon Jul 9 20:34:02 2007 for dlrLibs Utility Libraries by  doxygen 1.5.2