-
-
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. */ | |
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
how do I implement this autodetection file (canny.cpp) and make it detect the edges automatically?
Thanks