% The format of the EdgeImage method is:
%
% Image *CannyEdgeImage(const Image *image,const double radius,
-% const double sigma,const double lower_precent,
-% const double upper_percent,const size_t threshold,
-% ExceptionInfo *exception)
+% const double sigma,const double lower_percent,
+% const double upper_percent,ExceptionInfo *exception)
%
% A description of each parameter follows:
%
% o image: the image.
%
-% o channel: the channel type.
-%
% o radius: the radius of the gaussian smoothing filter.
%
% o sigma: the sigma of the gaussian smoothing filter.
return(MagickTrue);
}
-static MagickBooleanType TraceEdges(Image *edge_image,CacheView *trace_view,
- MatrixInfo *pixel_cache,const ssize_t x,const ssize_t y,
+static MagickBooleanType TraceEdges(Image *edge_image,CacheView *edge_view,
+ MatrixInfo *canny_cache,const ssize_t x,const ssize_t y,
const double lower_threshold,ExceptionInfo *exception)
{
CannyInfo
+ edge,
pixel;
- size_t
- number_edges;
+ MagickBooleanType
+ status;
- if (GetMatrixElement(pixel_cache,0,0,&pixel) == MagickFalse)
+ register Quantum
+ *q;
+
+ register ssize_t
+ i;
+
+ q=GetCacheViewAuthenticPixels(edge_view,x,y,1,1,exception);
+ if (q == (Quantum *) NULL)
+ return(MagickFalse);
+ *q=QuantumRange;
+ status=SyncCacheViewAuthenticPixels(edge_view,exception);
+ if (status == MagickFalse)
+ return(MagickFalse);;
+ if (GetMatrixElement(canny_cache,0,0,&edge) == MagickFalse)
return(MagickFalse);
- pixel.x=x;
- pixel.y=y;
- if (SetMatrixElement(pixel_cache,0,0,&pixel) == MagickFalse)
+ edge.x=x;
+ edge.y=y;
+ if (SetMatrixElement(canny_cache,0,0,&edge) == MagickFalse)
return(MagickFalse);
- number_edges=1;
- do
+ for (i=1; i != 0; )
{
- MagickBooleanType
- status;
-
ssize_t
- v,
- x_offset,
- y_offset;
+ v;
- number_edges--;
- status=GetMatrixElement(pixel_cache,(ssize_t) number_edges,0,&pixel);
+ i--;
+ status=GetMatrixElement(canny_cache,i,0,&edge);
if (status == MagickFalse)
return(MagickFalse);
- x_offset=pixel.x;
- y_offset=pixel.y;
for (v=(-1); v <= 1; v++)
{
ssize_t
for (u=(-1); u <= 1; u++)
{
- Quantum
- *q;
-
if ((u == 0) && (v == 0))
continue;
- if (IsAuthenticPixel(edge_image,x_offset+u,y_offset+v) == MagickFalse)
+ if (IsAuthenticPixel(edge_image,edge.x+u,edge.y+v) == MagickFalse)
continue;
/*
Not an edge if gradient value is below the lower threshold.
*/
- q=GetCacheViewAuthenticPixels(trace_view,x_offset+u,y_offset+v,1,1,
+ q=GetCacheViewAuthenticPixels(edge_view,edge.x+u,edge.y+v,1,1,
exception);
if (q == (Quantum *) NULL)
return(MagickFalse);
- status=GetMatrixElement(pixel_cache,x_offset+u,y_offset+v,&pixel);
+ status=GetMatrixElement(canny_cache,edge.x+u,edge.y+v,&pixel);
if (status == MagickFalse)
return(MagickFalse);
- if ((pixel.intensity >= lower_threshold) &&
- (GetPixelIntensity(edge_image,q) == 0))
+ if ((GetPixelIntensity(edge_image,q) == 0.0) &&
+ (pixel.intensity >= lower_threshold))
{
*q=QuantumRange;
- status=SyncCacheViewAuthenticPixels(trace_view,exception);
- if (status == MagickFalse)
- return(MagickFalse);
- status=GetMatrixElement(pixel_cache,(ssize_t) number_edges,0,
- &pixel);
+ status=SyncCacheViewAuthenticPixels(edge_view,exception);
if (status == MagickFalse)
return(MagickFalse);
- pixel.x=x_offset+u;
- pixel.y=y_offset+v;
- status=SetMatrixElement(pixel_cache,(ssize_t) number_edges,0,
- &pixel);
+ edge.x+=u;
+ edge.y+=v;
+ status=SetMatrixElement(canny_cache,i,0,&edge);
if (status == MagickFalse)
return(MagickFalse);
- number_edges++;
+ i++;
}
}
}
- } while (number_edges != 0);
+ }
return(MagickTrue);
}
-
MagickExport Image *CannyEdgeImage(const Image *image,const double radius,
- const double sigma,const double lower_precent,const double upper_percent,
+ const double sigma,const double lower_percent,const double upper_percent,
ExceptionInfo *exception)
{
CacheView
- *edge_view,
- *trace_view;
+ *edge_view;
+
+ CannyInfo
+ pixel;
char
geometry[MaxTextExtent];
double
lower_threshold,
+ max,
+ min,
upper_threshold;
Image
status;
MatrixInfo
- *pixel_cache;
-
- register ssize_t
- i;
-
- size_t
- *histogram,
- number_pixels;
+ *canny_cache;
ssize_t
- count,
y;
assert(image != (const Image *) NULL);
/*
Find the intensity gradient of the image.
*/
- pixel_cache=AcquireMatrixInfo(edge_image->columns,edge_image->rows,
+ canny_cache=AcquireMatrixInfo(edge_image->columns,edge_image->rows,
sizeof(CannyInfo),exception);
- if (pixel_cache == (MatrixInfo *) NULL)
+ if (canny_cache == (MatrixInfo *) NULL)
{
edge_image=DestroyImage(edge_image);
return((Image *) NULL);
dx,
dy;
- int
- orientation;
-
register const Quantum
*restrict kernel_pixels;
intensity;
intensity=GetPixelIntensity(edge_image,kernel_pixels+u);
- dx+=3.0*Gx[v][u]*intensity/2.0;
- dy+=3.0*Gy[v][u]*intensity/2.0;
+ dx+=0.5*Gx[v][u]*intensity;
+ dy+=0.5*Gy[v][u]*intensity;
}
kernel_pixels+=edge_image->columns+1;
}
- pixel.magnitude=sqrt(dx*dx+dy*dy);
+ pixel.magnitude=hypot(dx,dy);
pixel.orientation=0;
- if (dx != 0.0)
+ if (fabs(dx) > MagickEpsilon)
{
double
- theta;
+ slope;
- theta=dy/dx;
- if (theta < 0.0)
+ slope=dy/dx;
+ if (slope < 0.0)
{
- if (theta < -2.41421356237)
- pixel.orientation=2;
+ if (slope < -2.41421356237)
+ pixel.orientation=0;
else
- if (theta < -0.414213562373)
+ if (slope < -0.414213562373)
pixel.orientation=1;
else
- pixel.orientation=0;
+ pixel.orientation=2;
}
else
{
- if (theta > 2.41421356237)
- pixel.orientation=2;
+ if (slope > 2.41421356237)
+ pixel.orientation=0;
else
- if (theta > 0.414213562373)
+ if (slope > 0.414213562373)
pixel.orientation=3;
else
- pixel.orientation=0;
+ pixel.orientation=2;
}
}
- if (SetMatrixElement(pixel_cache,x,y,&pixel) == MagickFalse)
+ if (SetMatrixElement(canny_cache,x,y,&pixel) == MagickFalse)
continue;
p+=GetPixelChannels(edge_image);
}
Non-maxima suppression, remove pixels that are not considered to be part
of an edge.
*/
- histogram=(size_t *) AcquireQuantumMemory(65536,sizeof(*histogram));
- if (histogram == (size_t *) NULL)
- {
- pixel_cache=DestroyMatrixInfo(pixel_cache);
- edge_image=DestroyImage(edge_image);
- return((Image *) NULL);
- }
- (void) ResetMagickMemory(histogram,0,65536*sizeof(*histogram));
+ (void) GetMatrixElement(canny_cache,0,0,&pixel);
+ max=pixel.intensity;
+ min=pixel.intensity;
edge_view=AcquireAuthenticCacheView(edge_image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
#pragma omp parallel for schedule(static,4) shared(status) \
beta_pixel,
pixel;
- (void) GetMatrixElement(pixel_cache,x,y,&pixel);
+ (void) GetMatrixElement(canny_cache,x,y,&pixel);
switch (pixel.orientation)
{
case 0:
{
/*
- 0 degrees.
+ 0 degrees, north and south.
*/
- (void) GetMatrixElement(pixel_cache,x-1,y,&alpha_pixel);
- (void) GetMatrixElement(pixel_cache,x+1,y,&beta_pixel);
+ (void) GetMatrixElement(canny_cache,x,y-1,&alpha_pixel);
+ (void) GetMatrixElement(canny_cache,x,y+1,&beta_pixel);
break;
}
case 1:
{
/*
- 45 degrees.
+ 45 degrees, northwest and southeast.
*/
- (void) GetMatrixElement(pixel_cache,x-1,y-1,&alpha_pixel);
- (void) GetMatrixElement(pixel_cache,x+1,y+1,&beta_pixel);
+ (void) GetMatrixElement(canny_cache,x-1,y-1,&alpha_pixel);
+ (void) GetMatrixElement(canny_cache,x+1,y+1,&beta_pixel);
break;
}
case 2:
{
/*
- 90 degrees.
+ 90 degrees, east and west.
*/
- (void) GetMatrixElement(pixel_cache,x,y-1,&alpha_pixel);
- (void) GetMatrixElement(pixel_cache,x,y+1,&beta_pixel);
+ (void) GetMatrixElement(canny_cache,x-1,y,&alpha_pixel);
+ (void) GetMatrixElement(canny_cache,x+1,y,&beta_pixel);
break;
}
case 3:
{
/*
- 135 degrees.
+ 135 degrees, northeast and southwest.
*/
- (void) GetMatrixElement(pixel_cache,x-1,y+1,&alpha_pixel);
- (void) GetMatrixElement(pixel_cache,x+1,y-1,&beta_pixel);
+ (void) GetMatrixElement(canny_cache,x+1,y-1,&beta_pixel);
+ (void) GetMatrixElement(canny_cache,x-1,y+1,&alpha_pixel);
break;
}
}
if ((pixel.magnitude < alpha_pixel.magnitude) ||
(pixel.magnitude < beta_pixel.magnitude))
pixel.intensity=0;
- else
- if (pixel.magnitude > QuantumRange)
- pixel.intensity=QuantumRange;
- (void) SetMatrixElement(pixel_cache,x,y,&pixel);
+ (void) SetMatrixElement(canny_cache,x,y,&pixel);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
#pragma omp critical (MagickCore_CannyEdgeImage)
#endif
- histogram[ScaleQuantumToShort(ClampToQuantum(pixel.intensity))]++;
+ {
+ if (pixel.intensity < min)
+ min=pixel.intensity;
+ if (pixel.intensity > max)
+ max=pixel.intensity;
+ }
*q=0;
q+=GetPixelChannels(edge_image);
}
/*
Estimate hysteresis threshold.
*/
- number_pixels=(size_t) (lower_precent*(image->columns*image->rows-
- histogram[0]));
- count=0;
- for (i=65535; count < (ssize_t) number_pixels; i--)
- count+=histogram[i];
- upper_threshold=(double) ScaleShortToQuantum((unsigned short) i);
- for (i=0; histogram[i] == 0; i++) ;
- lower_threshold=upper_percent*(upper_threshold+
- ScaleShortToQuantum((unsigned short) i));
- histogram=(size_t *) RelinquishMagickMemory(histogram);
+ lower_threshold=lower_percent*(max-min)+min;
+ upper_threshold=upper_percent*(max-min)+min;
/*
Hysteresis threshold.
*/
edge_view=AcquireAuthenticCacheView(edge_image,exception);
- trace_view=AcquireAuthenticCacheView(edge_image,exception);
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(static,4) shared(status) \
- magick_threads(edge_image,edge_image,edge_image->rows,1)
-#endif
for (y=0; y < (ssize_t) edge_image->rows; y++)
{
- register Quantum
- *restrict q;
-
register ssize_t
x;
if (status == MagickFalse)
continue;
- q=GetCacheViewAuthenticPixels(edge_view,0,y,edge_image->columns,1,
- exception);
- if (q == (Quantum *) NULL)
- {
- status=MagickFalse;
- continue;
- }
for (x=0; x < (ssize_t) edge_image->columns; x++)
{
CannyInfo
pixel;
+ register const Quantum
+ *restrict p;
+
/*
Edge if pixel gradient higher than upper threshold.
*/
- status=GetMatrixElement(pixel_cache,x,y,&pixel);
+ p=GetCacheViewVirtualPixels(edge_view,x,y,1,1,exception);
+ if (p == (const Quantum *) NULL)
+ continue;
+ status=GetMatrixElement(canny_cache,x,y,&pixel);
if (status == MagickFalse)
- break;
- if ((pixel.intensity >= upper_threshold) &&
- (GetPixelIntensity(edge_image,q) == 0))
- {
- *q=QuantumRange;
- status=TraceEdges(edge_image,trace_view,pixel_cache,x,y,
- lower_threshold,exception);
- if (status == MagickFalse)
- break;
- }
- q+=GetPixelChannels(edge_image);
+ continue;
+ if ((GetPixelIntensity(edge_image,p) == 0.0) &&
+ (pixel.intensity >= upper_threshold))
+ status=TraceEdges(edge_image,edge_view,canny_cache,x,y,lower_threshold,
+ exception);
}
- if (SyncCacheViewAuthenticPixels(edge_view,exception) == MagickFalse)
- status=MagickFalse;
}
- trace_view=DestroyCacheView(trace_view);
edge_view=DestroyCacheView(edge_view);
- pixel_cache=DestroyMatrixInfo(pixel_cache);
+ /*
+ Free resources.
+ */
+ canny_cache=DestroyMatrixInfo(canny_cache);
return(edge_image);
}
\f