From: cristy Date: Sat, 19 Apr 2014 16:23:30 +0000 (+0000) Subject: (no commit message) X-Git-Tag: 7.0.1-0~2443 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c1510626cf0bd77ecc1a9681bd1e0f599108d1b3;p=imagemagick --- diff --git a/MagickCore/effect.c b/MagickCore/effect.c index cf6d59439..c406f3d2a 100644 --- a/MagickCore/effect.c +++ b/MagickCore/effect.c @@ -850,463 +850,6 @@ MagickExport Image *BlurImage(const Image *image,const double radius, % % % % % % -% C a n n y E d g e I m a g e % -% % -% % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% -% CannyEdgeImage() uses a multi-stage algorithm to detect a wide range of -% edges in images. -% -% The format of the EdgeImage method is: -% -% Image *CannyEdgeImage(const Image *image,const double radius, -% const double sigma,const double lower_percent, -% const double upper_percent,ExceptionInfo *exception) -% -% A description of each parameter follows: -% -% o image: the image. -% -% o radius: the radius of the gaussian smoothing filter. -% -% o sigma: the sigma of the gaussian smoothing filter. -% -% o lower_precent: percentage of edge pixels in the lower threshold. -% -% o upper_percent: percentage of edge pixels in the upper threshold. -% -% o exception: return any errors or warnings in this structure. -% -*/ - -typedef struct _CannyInfo -{ - double - magnitude, - intensity; - - int - orientation; - - ssize_t - x, - y; -} CannyInfo; - -static inline MagickBooleanType IsAuthenticPixel(const Image *image, - const ssize_t x,const ssize_t y) -{ - if ((x < 0) || (x >= (ssize_t) image->columns)) - return(MagickFalse); - if ((y < 0) || (y >= (ssize_t) image->rows)) - return(MagickFalse); - return(MagickTrue); -} - -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; - - MagickBooleanType - status; - - 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); - edge.x=x; - edge.y=y; - if (SetMatrixElement(canny_cache,0,0,&edge) == MagickFalse) - return(MagickFalse); - for (i=1; i != 0; ) - { - ssize_t - v; - - i--; - status=GetMatrixElement(canny_cache,i,0,&edge); - if (status == MagickFalse) - return(MagickFalse); - for (v=(-1); v <= 1; v++) - { - ssize_t - u; - - for (u=(-1); u <= 1; u++) - { - if ((u == 0) && (v == 0)) - continue; - 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(edge_view,edge.x+u,edge.y+v,1,1, - exception); - if (q == (Quantum *) NULL) - return(MagickFalse); - status=GetMatrixElement(canny_cache,edge.x+u,edge.y+v,&pixel); - if (status == MagickFalse) - return(MagickFalse); - if ((GetPixelIntensity(edge_image,q) == 0.0) && - (pixel.intensity >= lower_threshold)) - { - *q=QuantumRange; - status=SyncCacheViewAuthenticPixels(edge_view,exception); - if (status == MagickFalse) - return(MagickFalse); - edge.x+=u; - edge.y+=v; - status=SetMatrixElement(canny_cache,i,0,&edge); - if (status == MagickFalse) - return(MagickFalse); - i++; - } - } - } - } - return(MagickTrue); -} - -MagickExport Image *CannyEdgeImage(const Image *image,const double radius, - const double sigma,const double lower_percent,const double upper_percent, - ExceptionInfo *exception) -{ - CacheView - *edge_view; - - CannyInfo - pixel; - - char - geometry[MaxTextExtent]; - - double - lower_threshold, - max, - min, - upper_threshold; - - Image - *edge_image; - - KernelInfo - *kernel_info; - - MagickBooleanType - status; - - MatrixInfo - *canny_cache; - - ssize_t - y; - - assert(image != (const Image *) NULL); - assert(image->signature == MagickSignature); - if (image->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); - assert(exception != (ExceptionInfo *) NULL); - assert(exception->signature == MagickSignature); - /* - Filter out noise. - */ - (void) FormatLocaleString(geometry,MaxTextExtent, - "blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma); - kernel_info=AcquireKernelInfo(geometry); - if (kernel_info == (KernelInfo *) NULL) - ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); - edge_image=MorphologyApply(image,ConvolveMorphology,1,kernel_info, - UndefinedCompositeOp,0.0,exception); - kernel_info=DestroyKernelInfo(kernel_info); - if (edge_image == (Image *) NULL) - return((Image *) NULL); - if (SetImageColorspace(edge_image,GRAYColorspace,exception) == MagickFalse) - { - edge_image=DestroyImage(edge_image); - return((Image *) NULL); - } - /* - Find the intensity gradient of the image. - */ - canny_cache=AcquireMatrixInfo(edge_image->columns,edge_image->rows, - sizeof(CannyInfo),exception); - if (canny_cache == (MatrixInfo *) NULL) - { - edge_image=DestroyImage(edge_image); - return((Image *) NULL); - } - status=MagickTrue; - edge_view=AcquireVirtualCacheView(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 const Quantum - *restrict p; - - register ssize_t - x; - - if (status == MagickFalse) - continue; - p=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns+1,2, - exception); - if (p == (const Quantum *) NULL) - { - status=MagickFalse; - continue; - } - for (x=0; x < (ssize_t) edge_image->columns; x++) - { - CannyInfo - pixel; - - double - dx, - dy; - - register const Quantum - *restrict kernel_pixels; - - ssize_t - v; - - static double - Gx[2][2] = - { - { -1.0, +1.0 }, - { -1.0, +1.0 } - }, - Gy[2][2] = - { - { +1.0, +1.0 }, - { -1.0, -1.0 } - }; - - (void) ResetMagickMemory(&pixel,0,sizeof(pixel)); - dx=0.0; - dy=0.0; - kernel_pixels=p; - for (v=0; v < 2; v++) - { - ssize_t - u; - - for (u=0; u < 2; u++) - { - double - intensity; - - intensity=GetPixelIntensity(edge_image,kernel_pixels+u); - dx+=0.5*Gx[v][u]*intensity; - dy+=0.5*Gy[v][u]*intensity; - } - kernel_pixels+=edge_image->columns+1; - } - pixel.magnitude=hypot(dx,dy); - pixel.orientation=0; - if (fabs(dx) > MagickEpsilon) - { - double - slope; - - slope=dy/dx; - if (slope < 0.0) - { - if (slope < -2.41421356237) - pixel.orientation=0; - else - if (slope < -0.414213562373) - pixel.orientation=1; - else - pixel.orientation=2; - } - else - { - if (slope > 2.41421356237) - pixel.orientation=0; - else - if (slope > 0.414213562373) - pixel.orientation=3; - else - pixel.orientation=2; - } - } - if (SetMatrixElement(canny_cache,x,y,&pixel) == MagickFalse) - continue; - p+=GetPixelChannels(edge_image); - } - } - edge_view=DestroyCacheView(edge_view); - /* - Non-maxima suppression, remove pixels that are not considered to be part - of an edge. - */ - (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) \ - 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 - alpha_pixel, - beta_pixel, - pixel; - - (void) GetMatrixElement(canny_cache,x,y,&pixel); - switch (pixel.orientation) - { - case 0: - { - /* - 0 degrees, north and south. - */ - (void) GetMatrixElement(canny_cache,x,y-1,&alpha_pixel); - (void) GetMatrixElement(canny_cache,x,y+1,&beta_pixel); - break; - } - case 1: - { - /* - 45 degrees, northwest and southeast. - */ - (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, east and west. - */ - (void) GetMatrixElement(canny_cache,x-1,y,&alpha_pixel); - (void) GetMatrixElement(canny_cache,x+1,y,&beta_pixel); - break; - } - case 3: - { - /* - 135 degrees, northeast and southwest. - */ - (void) GetMatrixElement(canny_cache,x+1,y-1,&beta_pixel); - (void) GetMatrixElement(canny_cache,x-1,y+1,&alpha_pixel); - break; - } - } - pixel.intensity=pixel.magnitude; - if ((pixel.magnitude < alpha_pixel.magnitude) || - (pixel.magnitude < beta_pixel.magnitude)) - pixel.intensity=0; - (void) SetMatrixElement(canny_cache,x,y,&pixel); -#if defined(MAGICKCORE_OPENMP_SUPPORT) - #pragma omp critical (MagickCore_CannyEdgeImage) -#endif - { - if (pixel.intensity < min) - min=pixel.intensity; - if (pixel.intensity > max) - max=pixel.intensity; - } - *q=0; - q+=GetPixelChannels(edge_image); - } - if (SyncCacheViewAuthenticPixels(edge_view,exception) == MagickFalse) - status=MagickFalse; - } - edge_view=DestroyCacheView(edge_view); - /* - Estimate hysteresis threshold. - */ - lower_threshold=lower_percent*(max-min)+min; - upper_threshold=upper_percent*(max-min)+min; - /* - Hysteresis threshold. - */ - edge_view=AcquireAuthenticCacheView(edge_image,exception); - for (y=0; y < (ssize_t) edge_image->rows; y++) - { - register ssize_t - x; - - if (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. - */ - 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) - 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); - } - } - edge_view=DestroyCacheView(edge_view); - /* - Free resources. - */ - canny_cache=DestroyMatrixInfo(canny_cache); - return(edge_image); -} - -/* -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% % -% % -% % % C o n v o l v e I m a g e % % % % % diff --git a/MagickCore/effect.h b/MagickCore/effect.h index 37c06a3eb..91344d0ff 100644 --- a/MagickCore/effect.h +++ b/MagickCore/effect.h @@ -63,8 +63,6 @@ extern MagickExport Image *AdaptiveSharpenImage(const Image *,const double,const double, ExceptionInfo *), *BlurImage(const Image *,const double,const double,ExceptionInfo *), - *CannyEdgeImage(const Image *,const double,const double,const double, - const double,ExceptionInfo *), *ConvolveImage(const Image *,const KernelInfo *,ExceptionInfo *), *DespeckleImage(const Image *,ExceptionInfo *), *EdgeImage(const Image *,const double,ExceptionInfo *), diff --git a/MagickCore/feature.c b/MagickCore/feature.c index 308288cec..c68d417a4 100644 --- a/MagickCore/feature.c +++ b/MagickCore/feature.c @@ -69,10 +69,12 @@ #include "MagickCore/image-private.h" #include "MagickCore/magic.h" #include "MagickCore/magick.h" +#include "MagickCore/matrix.h" #include "MagickCore/memory_.h" #include "MagickCore/module.h" #include "MagickCore/monitor.h" #include "MagickCore/monitor-private.h" +#include "MagickCore/morphology-private.h" #include "MagickCore/option.h" #include "MagickCore/paint.h" #include "MagickCore/pixel-accessor.h" @@ -89,6 +91,499 @@ #include "MagickCore/timer.h" #include "MagickCore/utility.h" #include "MagickCore/version.h" + +/* +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% % +% % +% C a n n y E d g e I m a g e % +% % +% % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% CannyEdgeImage() uses a multi-stage algorithm to detect a wide range of +% edges in images. +% +% The format of the EdgeImage method is: +% +% Image *CannyEdgeImage(const Image *image,const double radius, +% const double sigma,const double lower_percent, +% const double upper_percent,ExceptionInfo *exception) +% +% A description of each parameter follows: +% +% o image: the image. +% +% o radius: the radius of the gaussian smoothing filter. +% +% o sigma: the sigma of the gaussian smoothing filter. +% +% o lower_precent: percentage of edge pixels in the lower threshold. +% +% o upper_percent: percentage of edge pixels in the upper threshold. +% +% o exception: return any errors or warnings in this structure. +% +*/ + +typedef struct _CannyInfo +{ + double + magnitude, + intensity; + + int + orientation; + + ssize_t + x, + y; +} CannyInfo; + +static inline MagickBooleanType IsAuthenticPixel(const Image *image, + const ssize_t x,const ssize_t y) +{ + if ((x < 0) || (x >= (ssize_t) image->columns)) + return(MagickFalse); + if ((y < 0) || (y >= (ssize_t) image->rows)) + return(MagickFalse); + return(MagickTrue); +} + +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; + + MagickBooleanType + status; + + 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); + edge.x=x; + edge.y=y; + if (SetMatrixElement(canny_cache,0,0,&edge) == MagickFalse) + return(MagickFalse); + for (i=1; i != 0; ) + { + ssize_t + v; + + i--; + status=GetMatrixElement(canny_cache,i,0,&edge); + if (status == MagickFalse) + return(MagickFalse); + for (v=(-1); v <= 1; v++) + { + ssize_t + u; + + for (u=(-1); u <= 1; u++) + { + if ((u == 0) && (v == 0)) + continue; + 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(edge_view,edge.x+u,edge.y+v,1,1, + exception); + if (q == (Quantum *) NULL) + return(MagickFalse); + status=GetMatrixElement(canny_cache,edge.x+u,edge.y+v,&pixel); + if (status == MagickFalse) + return(MagickFalse); + if ((GetPixelIntensity(edge_image,q) == 0.0) && + (pixel.intensity >= lower_threshold)) + { + *q=QuantumRange; + status=SyncCacheViewAuthenticPixels(edge_view,exception); + if (status == MagickFalse) + return(MagickFalse); + edge.x+=u; + edge.y+=v; + status=SetMatrixElement(canny_cache,i,0,&edge); + if (status == MagickFalse) + return(MagickFalse); + i++; + } + } + } + } + return(MagickTrue); +} + +MagickExport Image *CannyEdgeImage(const Image *image,const double radius, + const double sigma,const double lower_percent,const double upper_percent, + ExceptionInfo *exception) +{ + CacheView + *edge_view; + + CannyInfo + pixel; + + char + geometry[MaxTextExtent]; + + double + lower_threshold, + max, + min, + upper_threshold; + + Image + *edge_image; + + KernelInfo + *kernel_info; + + MagickBooleanType + status; + + MatrixInfo + *canny_cache; + + ssize_t + y; + + assert(image != (const Image *) NULL); + assert(image->signature == MagickSignature); + if (image->debug != MagickFalse) + (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); + assert(exception != (ExceptionInfo *) NULL); + assert(exception->signature == MagickSignature); + /* + Filter out noise. + */ + (void) FormatLocaleString(geometry,MaxTextExtent, + "blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma); + kernel_info=AcquireKernelInfo(geometry); + if (kernel_info == (KernelInfo *) NULL) + ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); + edge_image=MorphologyApply(image,ConvolveMorphology,1,kernel_info, + UndefinedCompositeOp,0.0,exception); + kernel_info=DestroyKernelInfo(kernel_info); + if (edge_image == (Image *) NULL) + return((Image *) NULL); + if (SetImageColorspace(edge_image,GRAYColorspace,exception) == MagickFalse) + { + edge_image=DestroyImage(edge_image); + return((Image *) NULL); + } + /* + Find the intensity gradient of the image. + */ + canny_cache=AcquireMatrixInfo(edge_image->columns,edge_image->rows, + sizeof(CannyInfo),exception); + if (canny_cache == (MatrixInfo *) NULL) + { + edge_image=DestroyImage(edge_image); + return((Image *) NULL); + } + status=MagickTrue; + edge_view=AcquireVirtualCacheView(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 const Quantum + *restrict p; + + register ssize_t + x; + + if (status == MagickFalse) + continue; + p=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns+1,2, + exception); + if (p == (const Quantum *) NULL) + { + status=MagickFalse; + continue; + } + for (x=0; x < (ssize_t) edge_image->columns; x++) + { + CannyInfo + pixel; + + double + dx, + dy; + + register const Quantum + *restrict kernel_pixels; + + ssize_t + v; + + static double + Gx[2][2] = + { + { -1.0, +1.0 }, + { -1.0, +1.0 } + }, + Gy[2][2] = + { + { +1.0, +1.0 }, + { -1.0, -1.0 } + }; + + (void) ResetMagickMemory(&pixel,0,sizeof(pixel)); + dx=0.0; + dy=0.0; + kernel_pixels=p; + for (v=0; v < 2; v++) + { + ssize_t + u; + + for (u=0; u < 2; u++) + { + double + intensity; + + intensity=GetPixelIntensity(edge_image,kernel_pixels+u); + dx+=0.5*Gx[v][u]*intensity; + dy+=0.5*Gy[v][u]*intensity; + } + kernel_pixels+=edge_image->columns+1; + } + pixel.magnitude=hypot(dx,dy); + pixel.orientation=0; + if (fabs(dx) > MagickEpsilon) + { + double + slope; + + slope=dy/dx; + if (slope < 0.0) + { + if (slope < -2.41421356237) + pixel.orientation=0; + else + if (slope < -0.414213562373) + pixel.orientation=1; + else + pixel.orientation=2; + } + else + { + if (slope > 2.41421356237) + pixel.orientation=0; + else + if (slope > 0.414213562373) + pixel.orientation=3; + else + pixel.orientation=2; + } + } + if (SetMatrixElement(canny_cache,x,y,&pixel) == MagickFalse) + continue; + p+=GetPixelChannels(edge_image); + } + } + edge_view=DestroyCacheView(edge_view); + /* + Non-maxima suppression, remove pixels that are not considered to be part + of an edge. + */ + (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) \ + 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 + alpha_pixel, + beta_pixel, + pixel; + + (void) GetMatrixElement(canny_cache,x,y,&pixel); + switch (pixel.orientation) + { + case 0: + { + /* + 0 degrees, north and south. + */ + (void) GetMatrixElement(canny_cache,x,y-1,&alpha_pixel); + (void) GetMatrixElement(canny_cache,x,y+1,&beta_pixel); + break; + } + case 1: + { + /* + 45 degrees, northwest and southeast. + */ + (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, east and west. + */ + (void) GetMatrixElement(canny_cache,x-1,y,&alpha_pixel); + (void) GetMatrixElement(canny_cache,x+1,y,&beta_pixel); + break; + } + case 3: + { + /* + 135 degrees, northeast and southwest. + */ + (void) GetMatrixElement(canny_cache,x+1,y-1,&beta_pixel); + (void) GetMatrixElement(canny_cache,x-1,y+1,&alpha_pixel); + break; + } + } + pixel.intensity=pixel.magnitude; + if ((pixel.magnitude < alpha_pixel.magnitude) || + (pixel.magnitude < beta_pixel.magnitude)) + pixel.intensity=0; + (void) SetMatrixElement(canny_cache,x,y,&pixel); +#if defined(MAGICKCORE_OPENMP_SUPPORT) + #pragma omp critical (MagickCore_CannyEdgeImage) +#endif + { + if (pixel.intensity < min) + min=pixel.intensity; + if (pixel.intensity > max) + max=pixel.intensity; + } + *q=0; + q+=GetPixelChannels(edge_image); + } + if (SyncCacheViewAuthenticPixels(edge_view,exception) == MagickFalse) + status=MagickFalse; + } + edge_view=DestroyCacheView(edge_view); + /* + Estimate hysteresis threshold. + */ + lower_threshold=lower_percent*(max-min)+min; + upper_threshold=upper_percent*(max-min)+min; + /* + Hysteresis threshold. + */ + edge_view=AcquireAuthenticCacheView(edge_image,exception); + for (y=0; y < (ssize_t) edge_image->rows; y++) + { + register ssize_t + x; + + if (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. + */ + 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) + 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); + } + } + edge_view=DestroyCacheView(edge_view); + /* + Free resources. + */ + canny_cache=DestroyMatrixInfo(canny_cache); + return(edge_image); +} + +/* +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% % +% % +% H o u g h T r a n s f o r m I m a g e % +% % +% % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% HoughTransformImage() detects lines in an image. +% +% The format of the HoughTransformImage method is: +% +% Image *HoughTransformImage(const Image *image,const size_t width, +% const size_t height,const size_t threshold,ExceptionInfo *exception) +% +% A description of each parameter follows: +% +% o image: the image. +% +% o width, height: find line pairs as local maxima in this neighborhood. +% +% o threshold: the line count threshold. +% +% o exception: return any errors or warnings in this structure. +% +*/ +MagickExport Image *HoughTransformImage(const Image *image,const size_t width, + const size_t height,const size_t threshold,ExceptionInfo *exception) +{ + return((Image *) NULL); +} + /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/MagickCore/feature.h b/MagickCore/feature.h index bfa205e20..625369505 100644 --- a/MagickCore/feature.h +++ b/MagickCore/feature.h @@ -47,6 +47,12 @@ typedef struct _ChannelFeatures extern MagickExport ChannelFeatures *GetImageFeatures(const Image *,const size_t,ExceptionInfo *); +extern MagickExport Image + *CannyEdgeImage(const Image *,const double,const double,const double, + const double,ExceptionInfo *), + *HoughTransformImage(const Image *,const size_t,const size_t,const size_t, + ExceptionInfo *); + #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/MagickWand/convert.c b/MagickWand/convert.c index d7ab8da2a..62738ae87 100644 --- a/MagickWand/convert.c +++ b/MagickWand/convert.c @@ -218,6 +218,8 @@ static MagickBooleanType ConvertUsage(void) " reduce image noise and reduce detail levels", "-geometry geometry preferred size or location of the image", "-grayscale method convert image to grayscale", + "-hough-lines geometry", + " identify lines in the image", "-identify identify the format and characteristics of the image", "-ift implements the inverse discrete Fourier transform (DFT)", "-implode amount implode image pixels about the center", diff --git a/utilities/convert.1.in b/utilities/convert.1.in index f80930c85..7e7992f7f 100644 --- a/utilities/convert.1.in +++ b/utilities/convert.1.in @@ -175,6 +175,8 @@ Image Operators: reduce image noise and reduce detail levels \-geometry geometry preferred size or location of the image \-grayscale method convert image to grayscale + \-hough-lines geometry + identify lines in the image \-identify identify the format and characteristics of the image \-ift implements the inverse discrete Fourier transform (DFT) \-implode amount implode image pixels about the center diff --git a/utilities/mogrify.1.in b/utilities/mogrify.1.in index 00af63ee2..40b2788c7 100644 --- a/utilities/mogrify.1.in +++ b/utilities/mogrify.1.in @@ -172,9 +172,11 @@ Image Operators: reduce image noise and reduce detail levels \-geometry geometry preferred size or location of the image \-grayscale method convert image to grayscale - \-ift implements the inverse discrete Fourier transform (DFT) \-help print program options + \-hough-lines geometry + identify lines in the image \-identify identify the format and characteristics of the image + \-ift implements the inverse discrete Fourier transform (DFT) \-implode amount implode image pixels about the center \-interpolative-resize geometry resize image using interpolation