From ff2af7d8bb822d47743429a994cc16b02b55f625 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Tue, 21 Feb 2012 11:16:49 +0000 Subject: [PATCH] Fixed Canny --- modules/imgproc/src/canny.cpp | 318 ++++++++++++++-------------------- 1 file changed, 130 insertions(+), 188 deletions(-) diff --git a/modules/imgproc/src/canny.cpp b/modules/imgproc/src/canny.cpp index 95a0e35248..c6da70879b 100644 --- a/modules/imgproc/src/canny.cpp +++ b/modules/imgproc/src/canny.cpp @@ -50,68 +50,74 @@ void cv::Canny( InputArray _src, OutputArray _dst, _dst.create(src.size(), CV_8U); Mat dst = _dst.getMat(); + + if (!L2gradient && (aperture_size & CV_CANNY_L2_GRADIENT) == CV_CANNY_L2_GRADIENT) + { + //backward compatibility + aperture_size &= ~CV_CANNY_L2_GRADIENT; + L2gradient = true; + } + + if ((aperture_size & 1) == 0 || (aperture_size != -1 && (aperture_size < 3 || aperture_size > 7))) + CV_Error(CV_StsBadFlag, ""); + #ifdef HAVE_TEGRA_OPTIMIZATION if (tegra::canny(src, dst, low_thresh, high_thresh, aperture_size, L2gradient)) return; #endif - - if( low_thresh > high_thresh ) - std::swap(low_thresh, high_thresh); - if( (aperture_size & 1) == 0 || (aperture_size != -1 && (aperture_size < 3 || aperture_size > 7)) ) - CV_Error( CV_StsBadFlag, "" ); + const int cn = src.channels(); + cv::Mat dx(src.rows, src.cols, CV_16SC(cn)); + cv::Mat dy(src.rows, src.cols, CV_16SC(cn)); - Mat dx, dy; - Sobel(src, dx, CV_16S, 1, 0, aperture_size, 1, 0, BORDER_REFLECT_101); - Sobel(src, dy, CV_16S, 0, 1, aperture_size, 1, 0, BORDER_REFLECT_101); + cv::Sobel(src, dx, CV_16S, 1, 0, aperture_size, 1, 0, cv::BORDER_REPLICATE); + cv::Sobel(src, dy, CV_16S, 0, 1, aperture_size, 1, 0, cv::BORDER_REPLICATE); - int low, high; - if( L2gradient ) - { - Cv32suf ul, uh; - ul.f = (float)low_thresh; - uh.f = (float)high_thresh; + if (low_thresh > high_thresh) + std::swap(low_thresh, high_thresh); - low = ul.i; - high = uh.i; - } - else + if (L2gradient) { - low = cvFloor( low_thresh ); - high = cvFloor( high_thresh ); + low_thresh = std::min(32767.0, low_thresh); + high_thresh = std::min(32767.0, high_thresh); + + if (low_thresh > 0) low_thresh *= low_thresh; + if (high_thresh > 0) high_thresh *= high_thresh; } + int low = cvFloor(low_thresh); + int high = cvFloor(high_thresh); - Size size = src.size(); - int i, j, k, mstep = size.width + 2, cn = src.channels(); - - Mat mask(size.height + 2, mstep, CV_8U); - memset( mask.ptr(0), 1, mstep ); - memset( mask.ptr(size.height+1), 1, mstep ); + ptrdiff_t mapstep = src.cols + 2; + cv::AutoBuffer buffer((src.cols+2)*(src.rows+2) + cn * mapstep * 3 * sizeof(int)); - Mat mag(6+cn, mstep, CV_32S); - mag = Scalar::all(0); - int* mag_buf[3] = { mag.ptr(0), mag.ptr(1), mag.ptr(2) }; - short* dxybuf[3] = { (short*)mag.ptr(3), (short*)mag.ptr(4), (short*)mag.ptr(5) }; - int* mbuf = mag.ptr(6); - - int maxsize = MAX( 1 << 10, size.width*size.height/10 ); - std::vector stack( maxsize ); - uchar **stack_top, **stack_bottom; - stack_top = stack_bottom = &stack[0]; + int* mag_buf[3]; + mag_buf[0] = (int*)(uchar*)buffer; + mag_buf[1] = mag_buf[0] + mapstep*cn; + mag_buf[2] = mag_buf[1] + mapstep*cn; + memset(mag_buf[0], 0, /* cn* */mapstep*sizeof(int)); + + uchar* map = (uchar*)(mag_buf[2] + mapstep*cn); + memset(map, 1, mapstep); + memset(map + mapstep*(src.rows + 1), 1, mapstep); + + int maxsize = std::max(1 << 10, src.cols * src.rows / 10); + std::vector stack(maxsize); + uchar **stack_top = &stack[0]; + uchar **stack_bottom = &stack[0]; - /* sector numbers + /* sector numbers (Top-Left Origin) 1 2 3 - * * * - * * * + * * * + * * * 0*******0 - * * * - * * * + * * * + * * * 3 2 1 */ - #define CANNY_PUSH(d) *(d) = (uchar)2, *stack_top++ = (d) + #define CANNY_PUSH(d) *(d) = uchar(2), *stack_top++ = (d) #define CANNY_POP(d) (d) = *--stack_top // calculate magnitude and angle of gradient, perform non-maxima supression. @@ -119,217 +125,153 @@ void cv::Canny( InputArray _src, OutputArray _dst, // 0 - the pixel might belong to an edge // 1 - the pixel can not belong to an edge // 2 - the pixel does belong to an edge - for( i = 0; i <= size.height; i++ ) + for (int i = 0; i <= src.rows; i++) { - int *_mag = mag_buf[(i > 0) + 1] + 1; - float* _magf = (float*)_mag; - const short *_dx, *_dy; - short *_ddx, *_ddy; - uchar* _map; - int x, y; - ptrdiff_t magstep1, magstep2; - int prev_flag = 0; - - if( i < size.height ) + int* _norm = mag_buf[(i > 0) + 1] + 1; + if (i < src.rows) { - _dx = dx.ptr(i); - _dy = dy.ptr(i); - _ddx = dxybuf[(i > 0) + 1]; - _ddy = _ddx + size.width; - - if( cn > 1 ) + short* _dx = dx.ptr(i); + short* _dy = dy.ptr(i); + + if (!L2gradient) { - _mag = mbuf; - _magf = (float*)_mag; + for (int j = 0; j < src.cols*cn; j++) + _norm[j] = std::abs(int(_dx[j])) + std::abs(int(_dy[j])); } - - if( !L2gradient ) - for( j = 0; j < size.width*cn; j++ ) - _mag[j] = std::abs(_dx[j]) + std::abs(_dy[j]); else { - for( j = 0; j < size.width*cn; j++ ) - { - x = _dx[j]; y = _dy[j]; - _magf[j] = sqrtf((float)x*x + (float)y*y); - } + for (int j = 0; j < src.cols*cn; j++) + _norm[j] = int(_dx[j])*_dx[j] + int(_dy[j])*_dy[j]; } - - if( cn > 1 ) + + if (cn > 1) { - _mag = mag_buf[(i > 0) + 1] + 1; - for( j = 0; j < size.width; j++ ) + for(int j = 0, jn = 0; j < src.cols; ++j, jn += cn) { - _mag[j] = mbuf[(j+1)*cn]; - _ddx[j] = _dx[j*cn]; _ddy[j] = _dy[j*cn]; - } - - for( k = 1; k < cn; k++ ) - { - for( j = 0; j < size.width; j++ ) - if( mbuf[(j+1)*cn + k] > _mag[j] ) - { - _mag[j] = mbuf[(j+1)*cn + k]; - _ddx[j] = _dx[j*cn + k]; - _ddy[j] = _dy[j*cn + k]; - } + int maxIdx = jn; + for(int k = 1; k < cn; ++k) + if(_norm[jn + k] > _norm[maxIdx]) maxIdx = jn + k; + _norm[j] = _norm[maxIdx]; + _dx[j] = _dx[maxIdx]; + _dy[j] = _dy[maxIdx]; } } - else - { - for( j = 0; j < size.width; j++ ) - _ddx[j] = _dx[j]; _ddy[j] = _dy[j]; - } - - _mag[-1] = _mag[size.width] = 0; + _norm[-1] = _norm[src.cols] = 0; } else - memset( _mag-1, 0, (size.width + 2)*sizeof(_mag[0]) ); - + memset(_norm-1, 0, /* cn* */mapstep*sizeof(int)); + // at the very beginning we do not have a complete ring // buffer of 3 magnitude rows for non-maxima suppression - if( i == 0 ) + if (i == 0) continue; - _map = &mask.at(i, 1); - _map[-1] = _map[size.width] = 1; - - _mag = mag_buf[1] + 1; // take the central row - _dx = dxybuf[1]; - _dy = _dx + size.width; - - magstep1 = mag_buf[2] - mag_buf[1]; - magstep2 = mag_buf[0] - mag_buf[1]; - - if( (stack_top - stack_bottom) + size.width > maxsize ) + uchar* _map = map + mapstep*i + 1; + _map[-1] = _map[src.cols] = 1; + + int* _mag = mag_buf[1] + 1; // take the central row + ptrdiff_t magstep1 = mag_buf[2] - mag_buf[1]; + ptrdiff_t magstep2 = mag_buf[0] - mag_buf[1]; + + const short* _x = dx.ptr(i-1); + const short* _y = dy.ptr(i-1); + + if ((stack_top - stack_bottom) + src.cols > maxsize) { int sz = (int)(stack_top - stack_bottom); - maxsize = MAX( maxsize * 3/2, maxsize + size.width ); + maxsize = maxsize * 3/2; stack.resize(maxsize); stack_bottom = &stack[0]; stack_top = stack_bottom + sz; } - for( j = 0; j < size.width; j++ ) + int prev_flag = 0; + for (int j = 0; j < src.cols; j++) { #define CANNY_SHIFT 15 - #define TG22 (int)(0.4142135623730950488016887242097*(1< low ) + if (m > low) { - int tg22x = x * TG22; - int tg67x = tg22x + ((x + x) << CANNY_SHIFT); + int xs = _x[j]; + int ys = _y[j]; + int x = std::abs(xs); + int y = std::abs(ys) << CANNY_SHIFT; - y <<= CANNY_SHIFT; + int tg22x = x * TG22; - if( y < tg22x ) + if (y < tg22x) { - if( m > _mag[j-1] && m >= _mag[j+1] ) - { - if( m > high && !prev_flag && _map[j-mstep] != 2 ) - { - CANNY_PUSH( _map + j ); - prev_flag = 1; - } - else - _map[j] = (uchar)0; - continue; - } + if (m > _mag[j-1] && m >= _mag[j+1]) goto __ocv_canny_push; } - else if( y > tg67x ) + else { - if( m > _mag[j+magstep2] && m >= _mag[j+magstep1] ) + int tg67x = tg22x + (x << (CANNY_SHIFT+1)); + if (y > tg67x) { - if( m > high && !prev_flag && _map[j-mstep] != 2 ) - { - CANNY_PUSH( _map + j ); - prev_flag = 1; - } - else - _map[j] = (uchar)0; - continue; + if (m > _mag[j+magstep2] && m >= _mag[j+magstep1]) goto __ocv_canny_push; } - } - else - { - s = s < 0 ? -1 : 1; - if( m > _mag[j+magstep2-s] && m > _mag[j+magstep1+s] ) + else { - if( m > high && !prev_flag && _map[j-mstep] != 2 ) - { - CANNY_PUSH( _map + j ); - prev_flag = 1; - } - else - _map[j] = (uchar)0; - continue; + int s = (xs ^ ys) < 0 ? -1 : 1; + if (m > _mag[j+magstep2-s] && m > _mag[j+magstep1+s]) goto __ocv_canny_push; } } } prev_flag = 0; - _map[j] = (uchar)1; + _map[j] = uchar(1); + continue; +__ocv_canny_push: + if (!prev_flag && m > high && _map[j-mapstep] != 2) + { + CANNY_PUSH(_map + j); + prev_flag = 1; + } + else + _map[j] = 0; } - // scroll the ring buffers + // scroll the ring buffer _mag = mag_buf[0]; mag_buf[0] = mag_buf[1]; mag_buf[1] = mag_buf[2]; mag_buf[2] = _mag; - - _ddx = dxybuf[0]; - dxybuf[0] = dxybuf[1]; - dxybuf[1] = dxybuf[2]; - dxybuf[2] = _ddx; } // now track the edges (hysteresis thresholding) - while( stack_top > stack_bottom ) + while (stack_top > stack_bottom) { uchar* m; - if( (stack_top - stack_bottom) + 8 > maxsize ) + if ((stack_top - stack_bottom) + 8 > maxsize) { int sz = (int)(stack_top - stack_bottom); - maxsize = MAX( maxsize * 3/2, maxsize + 8 ); + maxsize = maxsize * 3/2; stack.resize(maxsize); stack_bottom = &stack[0]; stack_top = stack_bottom + sz; } CANNY_POP(m); - - if( !m[-1] ) - CANNY_PUSH( m - 1 ); - if( !m[1] ) - CANNY_PUSH( m + 1 ); - if( !m[-mstep-1] ) - CANNY_PUSH( m - mstep - 1 ); - if( !m[-mstep] ) - CANNY_PUSH( m - mstep ); - if( !m[-mstep+1] ) - CANNY_PUSH( m - mstep + 1 ); - if( !m[mstep-1] ) - CANNY_PUSH( m + mstep - 1 ); - if( !m[mstep] ) - CANNY_PUSH( m + mstep ); - if( !m[mstep+1] ) - CANNY_PUSH( m + mstep + 1 ); + + if (!m[-1]) CANNY_PUSH(m - 1); + if (!m[1]) CANNY_PUSH(m + 1); + if (!m[-mapstep-1]) CANNY_PUSH(m - mapstep - 1); + if (!m[-mapstep]) CANNY_PUSH(m - mapstep); + if (!m[-mapstep+1]) CANNY_PUSH(m - mapstep + 1); + if (!m[mapstep-1]) CANNY_PUSH(m + mapstep - 1); + if (!m[mapstep]) CANNY_PUSH(m + mapstep); + if (!m[mapstep+1]) CANNY_PUSH(m + mapstep + 1); } // the final pass, form the final image - for( i = 0; i < size.height; i++ ) + const uchar* pmap = map + mapstep + 1; + uchar* pdst = dst.ptr(); + for (int i = 0; i < src.rows; i++, pmap += mapstep, pdst += dst.step) { - const uchar* _map = mask.ptr(i+1) + 1; - uchar* _dst = dst.ptr(i); - - for( j = 0; j < size.width; j++ ) - _dst[j] = (uchar)-(_map[j] >> 1); + for (int j = 0; j < src.cols; j++) + pdst[j] = (uchar)-(pmap[j] >> 1); } }