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
00067 template <ImageFormat FORMAT>
00068 Image<GRAY1>
00069 applyCanny(const Image<FORMAT>& inputImage,
00070 size_t gaussianSize = 5,
00071 double upperThreshold = 0.0,
00072 double lowerThreshold = 0.0,
00073 double autoUpperThresholdFactor = 3.0,
00074 double autoLowerThresholdFactor = 0.0);
00075
00076 }
00077
00078 }
00079
00080
00081
00082
00083 #include <limits>
00084 #include <list>
00085 #include <dlrComputerVision/filter.h>
00086 #include <dlrComputerVision/kernels.h>
00087 #include <dlrComputerVision/nonMaximumSuppress.h>
00088 #include <dlrComputerVision/sobel.h>
00089 #include <dlrComputerVision/utilities.h>
00090 #include <dlrNumeric/index2D.h>
00091 #include <dlrNumeric/utilities.h>
00092
00093 namespace dlr {
00094
00095 namespace computerVision {
00096
00098 namespace privateCode {
00099
00100 Image<GRAY1>
00101 traceEdges(const Image<GRAY_FLOAT64>& gradImage, double threshold)
00102 {
00103 Image<GRAY1> edgeImage(gradImage.rows(), gradImage.columns());
00104 edgeImage = false;
00105
00106
00107
00108 std::list<Index2D> seedList;
00109 size_t index0 = 0;
00110 for(size_t row = 0; row < gradImage.rows(); ++row) {
00111 for(size_t column = 0; column < gradImage.columns(); ++column) {
00112 if(gradImage[index0] > threshold) {
00113 edgeImage[index0] = true;
00114 seedList.push_front(Index2D(static_cast<int>(row),
00115 static_cast<int>(column)));
00116 }
00117 ++index0;
00118 }
00119 }
00120
00121
00122 size_t lastRow = gradImage.rows() - 1;
00123 size_t lastColumn = gradImage.columns() - 1;
00124 size_t columns = gradImage.columns();
00125 while(seedList.size() != 0) {
00126
00127
00128 Index2D seed = *seedList.begin();
00129 seedList.pop_front();
00130
00131
00132 size_t row = seed.getRow();
00133 size_t column = seed.getColumn();
00134 if(row == 0 || column == 0
00135 || row == lastRow || column == lastColumn) {
00136 continue;
00137 }
00138
00139
00140 index0 = row * columns + column;
00141
00142
00143
00144
00145 size_t neighborIndex = index0 - columns - 1;
00146 if(gradImage(neighborIndex) != 0.0
00147 && edgeImage(neighborIndex) == false) {
00148 edgeImage(neighborIndex) = true;
00149 seedList.push_front(Index2D(static_cast<int>(row) - 1,
00150 static_cast<int>(column) - 1));
00151 }
00152 ++neighborIndex;
00153 if(gradImage(neighborIndex) != 0.0
00154 && edgeImage(neighborIndex) == false) {
00155 edgeImage(neighborIndex) = true;
00156 seedList.push_front(Index2D(static_cast<int>(row) - 1,
00157 static_cast<int>(column)));
00158 }
00159 ++neighborIndex;
00160 if(gradImage(neighborIndex) != 0.0
00161 && edgeImage(neighborIndex) == false) {
00162 edgeImage(neighborIndex) = true;
00163 seedList.push_front(Index2D(static_cast<int>(row) - 1,
00164 static_cast<int>(column) + 1));
00165 }
00166 neighborIndex = index0 - 1;
00167 if(gradImage(neighborIndex) != 0.0
00168 && edgeImage(neighborIndex) == false) {
00169 edgeImage(neighborIndex) = true;
00170 seedList.push_front(Index2D(static_cast<int>(row),
00171 static_cast<int>(column) - 1));
00172 }
00173 neighborIndex = index0 + 1;
00174 if(gradImage(neighborIndex) != 0.0
00175 && edgeImage(neighborIndex) == false) {
00176 edgeImage(neighborIndex) = true;
00177 seedList.push_front(Index2D(static_cast<int>(row),
00178 static_cast<int>(column) + 1));
00179 }
00180 neighborIndex = index0 + columns - 1;
00181 if(gradImage(neighborIndex) != 0.0
00182 && edgeImage(neighborIndex) == false) {
00183 edgeImage(neighborIndex) = true;
00184 seedList.push_front(Index2D(static_cast<int>(row) + 1,
00185 static_cast<int>(column) - 1));
00186 }
00187 ++neighborIndex;
00188 if(gradImage(neighborIndex) != 0.0
00189 && edgeImage(neighborIndex) == false) {
00190 edgeImage(neighborIndex) = true;
00191 seedList.push_front(Index2D(static_cast<int>(row) + 1,
00192 static_cast<int>(column)));
00193 }
00194 ++neighborIndex;
00195 if(gradImage(neighborIndex) != 0.0
00196 && edgeImage(neighborIndex) == false) {
00197 edgeImage(neighborIndex) = true;
00198 seedList.push_front(Index2D(static_cast<int>(row) + 1,
00199 static_cast<int>(column) + 1));
00200 }
00201 }
00202 return edgeImage;
00203 }
00204
00205 }
00207
00208
00209
00210
00211 template <ImageFormat FORMAT>
00212 Image<GRAY1>
00213 applyCanny(const Image<FORMAT>& inputImage,
00214 size_t gaussianSize,
00215 double upperThreshold,
00216 double lowerThreshold,
00217 double autoUpperThresholdFactor,
00218 double autoLowerThresholdFactor)
00219 {
00220
00221 if(inputImage.rows() < gaussianSize + 3
00222 || inputImage.columns() < gaussianSize + 3) {
00223 DLR_THROW(ValueException, "applyCanny()",
00224 "Argument inputImage has insufficient size, or argument "
00225 "gaussianSize is too large.");
00226 }
00227 if(lowerThreshold > upperThreshold) {
00228 DLR_THROW(ValueException, "applyCanny()",
00229 "Argument lowerThreshold must be less than or equal to "
00230 "Arguments upperThreshold.");
00231 }
00232 autoLowerThresholdFactor =
00233 std::min(autoLowerThresholdFactor, autoUpperThresholdFactor);
00234
00235
00236
00237
00238
00239
00240
00241 lowerThreshold *= std::sqrt(2.0) * 8.0;
00242 upperThreshold *= std::sqrt(2.0) * 8.0;
00243
00244
00245 Image<GRAY_FLOAT64> blurredImage;
00246 if(gaussianSize == 0) {
00247 blurredImage = convertColorspace<GRAY_FLOAT64>(inputImage);
00248 } else {
00249 Kernel<double> gaussian =
00250 getGaussianKernel<double>(gaussianSize, gaussianSize);
00251 blurredImage = filter2D<GRAY_FLOAT64, GRAY_FLOAT64>(
00252 gaussian, inputImage, 0.0);
00253 }
00254
00255
00256
00257 Image<GRAY_FLOAT64> gradX = applySobelX(blurredImage);
00258 Image<GRAY_FLOAT64> gradY = applySobelY(blurredImage);
00259 Image<GRAY_FLOAT64> gradMagnitude(gradX.rows(), gradX.columns());
00260
00261 if(lowerThreshold > 0.0 && upperThreshold > 0.0) {
00262
00263 for(size_t index0 = 0; index0 < gradX.size(); ++index0) {
00264 double tmpVal = std::sqrt(
00265 gradX[index0] * gradX[index0] + gradY[index0] * gradY[index0]);
00266 gradMagnitude[index0] = (tmpVal > lowerThreshold) ? tmpVal : 0.0;
00267 }
00268 } else {
00269
00270 for(size_t index0 = 0; index0 < gradX.size(); ++index0) {
00271 gradMagnitude[index0] = std::sqrt(
00272 gradX[index0] * gradX[index0] + gradY[index0] * gradY[index0]);
00273 }
00274
00275
00276 size_t startRow = (gaussianSize + 1) / 2;
00277 size_t endRow = gradMagnitude.rows() - startRow;
00278 size_t startColumn = startRow;
00279 size_t endColumn = gradMagnitude.columns() - startColumn;
00280
00281
00282 if((startRow >= endRow) || (startColumn >= endColumn)) {
00283 DLR_THROW(ValueException, "applyCanny()",
00284 "Filter kernel is too large for image.");
00285 }
00286
00287
00288 for(size_t row = 0; row < startRow; ++row) {
00289 for(size_t column = 0; column < gradMagnitude.columns(); ++column) {
00290 gradMagnitude(row, column) = 0.0;
00291 }
00292 }
00293 for(size_t row = endRow; row < gradMagnitude.rows(); ++row) {
00294 for(size_t column = 0; column < gradMagnitude.columns(); ++column) {
00295 gradMagnitude(row, column) = 0.0;
00296 }
00297 }
00298 for(size_t row = 0; row < gradMagnitude.rows(); ++row) {
00299 for(size_t column = 0; column < startColumn; ++column) {
00300 gradMagnitude(row, column) = 0.0;
00301 }
00302 for(size_t column = endColumn; column < gradMagnitude.columns();
00303 ++column) {
00304 gradMagnitude(row, column) = 0.0;
00305 }
00306 }
00307
00308
00309 size_t numberOfPixels =
00310 (endRow - startRow) * (endColumn - startColumn);
00311 Float64 sumOfGradient = 0.0;
00312 Float64 sumOfGradientSquared = 0.0;
00313 for(size_t row = startRow; row < endRow; ++row) {
00314 Float64 subSum = 0.0;
00315 Float64 subSumOfSquares = 0.0;
00316 for(size_t column = startColumn; column < endColumn; ++column) {
00317 Float64 testValue = gradMagnitude(row, column);
00318
00319
00320
00321
00322 subSum += testValue;
00323 subSumOfSquares += testValue * testValue;
00324 }
00325 sumOfGradient += subSum;
00326 sumOfGradientSquared += subSumOfSquares;
00327 }
00328 double gradientMean = sumOfGradient / numberOfPixels;
00329 double gradientVariance =
00330 sumOfGradientSquared / numberOfPixels - gradientMean * gradientMean;
00331 double gradientSigma = std::sqrt(gradientVariance);
00332
00333 if(upperThreshold <= 0.0) {
00334 upperThreshold =
00335 gradientMean + autoUpperThresholdFactor * gradientSigma;
00336 upperThreshold = std::max(upperThreshold, 0.0);
00337 }
00338 if(lowerThreshold <= 0.0) {
00339 lowerThreshold =
00340 gradientMean + autoLowerThresholdFactor * gradientSigma;
00341 lowerThreshold = std::min(lowerThreshold, upperThreshold);
00342 lowerThreshold = std::max(lowerThreshold, 0.0);
00343 }
00344
00345
00346 for(size_t index0 = 0; index0 < gradX.size(); ++index0) {
00347 double tmpVal = gradMagnitude[index0];
00348 gradMagnitude[index0] = (tmpVal > lowerThreshold) ? tmpVal : 0.0;
00349 }
00350 }
00351
00352
00353 Image<GRAY_FLOAT64> edgeCandidates =
00354 nonMaximumSuppress(gradMagnitude, gradX, gradY);
00355
00356
00357 Image<GRAY1> edgeImage =
00358 privateCode::traceEdges(edgeCandidates, upperThreshold);
00359 return edgeImage;
00360 }
00361
00362 }
00363
00364 }
00365
00366 #endif