-
-
Save egonSchiele/756833 to your computer and use it in GitHub Desktop.
// new | |
/*M/////////////////////////////////////////////////////////////////////////////////////// | |
// | |
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. | |
// | |
// By downloading, copying, installing or using the software you agree to this license. | |
// If you do not agree to this license, do not download, install, | |
// copy or use the software. | |
// | |
// | |
// Intel License Agreement | |
// For Open Source Computer Vision Library | |
// | |
// Copyright (C) 2000, Intel Corporation, all rights reserved. | |
// Third party copyrights are property of their respective owners. | |
// | |
// Redistribution and use in source and binary forms, with or without modification, | |
// are permitted provided that the following conditions are met: | |
// | |
// * Redistribution's of source code must retain the above copyright notice, | |
// this list of conditions and the following disclaimer. | |
// | |
// * Redistribution's in binary form must reproduce the above copyright notice, | |
// this list of conditions and the following disclaimer in the documentation | |
// and/or other materials provided with the distribution. | |
// | |
// * The name of Intel Corporation may not be used to endorse or promote products | |
// derived from this software without specific prior written permission. | |
// | |
// This software is provided by the copyright holders and contributors "as is" and | |
// any express or implied warranties, including, but not limited to, the implied | |
// warranties of merchantability and fitness for a particular purpose are disclaimed. | |
// In no event shall the Intel Corporation or contributors be liable for any direct, | |
// indirect, incidental, special, exemplary, or consequential damages | |
// (including, but not limited to, procurement of substitute goods or services; | |
// loss of use, data, or profits; or business interruption) however caused | |
// and on any theory of liability, whether in contract, strict liability, | |
// or tort (including negligence or otherwise) arising in any way out of | |
// the use of this software, even if advised of the possibility of such damage. | |
// | |
//M*/ | |
#include "precomp.hpp" | |
CV_IMPL void cvCanny( const void* srcarr, void* dstarr, | |
double low_thresh, double high_thresh, | |
int aperture_size ) | |
{ | |
cv::Ptr<CvMat> dx, dy; | |
cv::AutoBuffer<char> buffer; | |
std::vector<uchar*> stack; | |
uchar **stack_top = 0, **stack_bottom = 0; | |
double percent = low_thresh; | |
CvMat srcstub, *src = cvGetMat( srcarr, &srcstub ); | |
CvMat dststub, *dst = cvGetMat( dstarr, &dststub ); | |
CvSize size; | |
int flags = aperture_size; | |
int low, high; | |
uchar* map; | |
ptrdiff_t mapstep; | |
int maxsize; | |
int i, j; | |
CvMat mag_row; | |
if( CV_MAT_TYPE( src->type ) != CV_8UC1 || | |
CV_MAT_TYPE( dst->type ) != CV_8UC1 ) | |
CV_Error( CV_StsUnsupportedFormat, "" ); | |
if( !CV_ARE_SIZES_EQ( src, dst )) | |
CV_Error( CV_StsUnmatchedSizes, "" ); | |
if( low_thresh > high_thresh ) | |
{ | |
double t; | |
CV_SWAP( low_thresh, high_thresh, t ); | |
} | |
aperture_size &= INT_MAX; | |
if( (aperture_size & 1) == 0 || aperture_size < 3 || aperture_size > 7 ) | |
CV_Error( CV_StsBadFlag, "" ); | |
size = cvGetMatSize( src ); | |
// convolve with sobel operator to get derivative approximations | |
dx = cvCreateMat( size.height, size.width, CV_16SC1 ); | |
dy = cvCreateMat( size.height, size.width, CV_16SC1 ); | |
cvSobel( src, dx, 1, 0, aperture_size ); | |
cvSobel( src, dy, 0, 1, aperture_size ); | |
if( flags & CV_CANNY_L2_GRADIENT ) | |
{ | |
Cv32suf ul, uh; | |
ul.f = (float)low_thresh; | |
uh.f = (float)high_thresh; | |
low = ul.i; | |
high = uh.i; | |
} | |
else | |
{ | |
low = cvFloor( low_thresh ); | |
high = cvFloor( high_thresh ); | |
} | |
// buffer structure will be: top half for 2d mag array, | |
// bottom half for map of edges (either 0, 1, or 2...see below) | |
buffer.allocate( (size.width+2)*(size.height+2) + (size.width + 2)*(size.height+2)*sizeof(int) ); | |
// mag is a pointer to the magnitude array | |
int *mag = (int*)(char*)buffer; | |
// map is a pointer to the edges array | |
map = (uchar*)(mag + (size.width+2)*(size.height+2)); | |
mapstep = size.width + 2; | |
maxsize = MAX( 1 << 10, size.width*size.height/10 ); | |
stack.resize( maxsize ); | |
stack_top = stack_bottom = &stack[0]; | |
memset( mag, 0, (size.width + 2) * (size.height + 2) * sizeof(int) ); | |
memset( map, 1, mapstep ); | |
memset( map + mapstep*(size.height + 1), 1, mapstep ); | |
/* 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_POP(d) (d) = *--stack_top | |
mag_row = cvMat( 1, size.width, CV_32F ); | |
// we actually want to start from (1,1), because there's a 1-cell border | |
// around the whole image for padding. | |
mag = mag + size.width + 2 + 1; | |
// calculate magnitude and angle of gradient, perform non-maxima supression. | |
// fill the map with one of the following values: | |
// 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++ ) | |
{ | |
// here we move one column over, b/c the first column is padding. | |
int *_mag = mag + (size.width + 2) * i; | |
float* _magf = (float*)_mag; | |
const short* _dx = (short*)(dx->data.ptr + dx->step*i); | |
const short* _dy = (short*)(dy->data.ptr + dy->step*i); | |
int x, y; | |
if( i < size.height ) { | |
_mag[-1] = _mag[size.width] = 0; | |
if( !(flags & CV_CANNY_L2_GRADIENT) ) { | |
for( j = 0; j < size.width; j++ ) { | |
_mag[j] = abs(_dx[j]) + abs(_dy[j]); | |
} | |
} | |
else { | |
for( j = 0; j < size.width; j++ ) { | |
x = _dx[j]; y = _dy[j]; | |
_magf[j] = (float)std::sqrt((double)x*x + (double)y*y); | |
} | |
} | |
} | |
else | |
memset( _mag-1, 0, (size.width + 2)*sizeof(int) ); | |
} | |
// Choose better thresholds | |
int max = 0; | |
for (i = 0; i < size.height; i++) { | |
int *_mag = mag + (size.width + 2) * i; | |
for( j = 0; j < size.width; j++ ) { | |
if (_mag[j] > max) { | |
max = _mag[j]; | |
} | |
} | |
} | |
// step 2: Get the histogram of the data. | |
#define NUM_BINS 64 | |
// might want to make this max - min / NUM_BINS after you have normalized. | |
int bin_size = max / NUM_BINS; | |
if (bin_size < 1) bin_size = 1; | |
int bins[NUM_BINS] = { 0 }; | |
for (i = 0; i < size.height; i++) { | |
int *_mag = mag + (size.width + 2) * i; | |
for( j = 0; j < size.width; j++ ) { | |
bins[_mag[j] / bin_size]++; | |
} | |
} | |
// step 3: get the high threshold | |
double percent_of_pixels_not_edges = 0.8; | |
double threshold_ratio = 0.4; | |
int total = 0; | |
high = 0; | |
// size.height should be here too, but right now we're going row-by-row | |
while (total < size.height * size.width * percent_of_pixels_not_edges) { | |
total+= bins[high]; | |
high++; | |
} | |
high *= bin_size; | |
low = threshold_ratio * high; | |
cout << "high: " << high << endl; | |
cout << "low: " << low << endl; | |
int adit = 10; | |
// non-maxima suppression | |
for( i = 1; i <= size.height; i++ ) | |
{ | |
int *_mag = mag + (size.width + 2) * i; | |
if( (stack_top - stack_bottom) + size.width > maxsize ) | |
{ | |
int sz = (int)(stack_top - stack_bottom); | |
maxsize = MAX( maxsize * 3/2, maxsize + 8 ); | |
stack.resize(maxsize); | |
stack_bottom = &stack[0]; | |
stack_top = stack_bottom + sz; | |
} | |
const short* _dx = (short*)(dx->data.ptr + dx->step*(i-1)); | |
const short* _dy = (short*)(dy->data.ptr + dy->step*(i-1)); | |
int prev_flag = 0; | |
int x, y; | |
uchar* _map; | |
ptrdiff_t magstep1, magstep2; | |
_map = map + mapstep*i + 1; | |
_map[-1] = _map[size.width] = 1; | |
if (i % 3 == 1) { | |
magstep1 = size.width + 2; | |
magstep2 = -(size.width + 2); | |
} else if (i % 3 == 2) { | |
magstep1 = -2 * (size.width + 2); | |
magstep2 = -(size.width + 2); | |
} else { | |
magstep1 = size.width + 2; | |
magstep2 = 2 * (size.width + 2); | |
} | |
for( j = 0; j < size.width; j++ ) | |
{ | |
#define CANNY_SHIFT 15 | |
// i.e. tan(pi/8) * (1 << CANNY_SHIFT etc...) | |
#define TG22 (int)(0.4142135623730950488016887242097*(1<<CANNY_SHIFT) + 0.5) | |
x = _dx[j]; | |
y = _dy[j]; | |
int s = x ^ y; | |
int m = _mag[j]; | |
x = abs(x); | |
y = abs(y); | |
if( m > low ) | |
{ | |
int tg22x = x * TG22; | |
int tg67x = tg22x + ((x + x) << CANNY_SHIFT); | |
y <<= CANNY_SHIFT; | |
if( y < tg22x ) | |
{ | |
if( m > _mag[j-1] && m >= _mag[j+1] ) | |
{ | |
if( m > high && !prev_flag && _map[j-mapstep] != 2 ) | |
{ | |
CANNY_PUSH( _map + j ); | |
prev_flag = 1; | |
} | |
else { | |
_map[j] = (uchar)0; | |
} | |
continue; | |
} | |
} | |
else if( y > tg67x ) | |
{ | |
if( m > _mag[j-magstep2] && m >= _mag[j+magstep1] ) | |
{ | |
if( m > high && !prev_flag && _map[j-mapstep] != 2 ) | |
{ | |
CANNY_PUSH( _map + j ); | |
prev_flag = 1; | |
} | |
else { | |
_map[j] = (uchar)0; | |
} | |
continue; | |
} | |
} | |
else | |
{ | |
s = s < 0 ? -1 : 1; | |
if( m > _mag[j+magstep2-s] && m > _mag[j+magstep1+s] ) | |
{ | |
if( m > high && !prev_flag && _map[j-mapstep] != 2 ) | |
{ | |
CANNY_PUSH( _map + j ); | |
prev_flag = 1; | |
} | |
else { | |
_map[j] = (uchar)0; | |
} | |
continue; | |
} | |
} | |
} | |
prev_flag = 0; | |
_map[j] = (uchar)1; | |
} | |
} | |
// now track the edges (hysteresis thresholding) | |
while( stack_top > stack_bottom ) | |
{ | |
uchar* m; | |
if( (stack_top - stack_bottom) + 8 > maxsize ) | |
{ | |
int sz = (int)(stack_top - stack_bottom); | |
maxsize = MAX( maxsize * 3/2, maxsize + 8 ); | |
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[-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* _map = map + mapstep*(i+1) + 1; | |
uchar* _dst = dst->data.ptr + dst->step*i; | |
for( j = 0; j < size.width; j++ ) | |
_dst[j] = (uchar)-(_map[j] >> 1); | |
} | |
adit = 20; | |
} | |
void cv::Canny( const Mat& image, Mat& edges, | |
double threshold1, double threshold2, | |
int apertureSize, bool L2gradient ) | |
{ | |
Mat src = image; | |
edges.create(src.size(), CV_8U); | |
CvMat _src = src, _dst = edges; | |
cvCanny( &_src, &_dst, threshold1, threshold2, | |
apertureSize + (L2gradient ? CV_CANNY_L2_GRADIENT : 0)); | |
} | |
/* End of file. */ | |
Hmm, if I recall correctly, the usage is:
edges = cv.CreateMat(image.rows, image.cols, image.type)
cv.Canny(image, edges, 0, 0)
And cv.Canny will automatically determine the high and low threshold.
how do I implement this autodetection file (canny.cpp) and make it detect the edges automatically?
Thanks
csharma2,
You need to download openCV and compile it. Before compiling, replace canny.cpp with this file. Then check out the openCV examples to learn how to detect edges: http://opencv.willowgarage.com/wiki/Welcome/Introduction
Silly question but with the OpenCV 2.1.0, I cant seem to find the canny.cpp file anywhere in any folders! is there a particular folder it is should be located in (or do i need to download a different version of openCV)
Hmm, things could've changed...this fix is pretty old. Sadly the only version I know that definitely works is this one: https://github.com/egonSchiele/OpenCV
could you provide a newer implement with opencv 2.4.5?
i got some problems in compiling with ios
thanks in advance
I don't suppose anyone has implemented this as a free function that doesn't need to be compiled into OpenCV?
Is this the original canny detector?
Is this the original canny detector?
I think this is socall auto-canny
Hi, do you have an example using this function?