From: cristy Date: Fri, 4 Apr 2014 00:55:24 +0000 (+0000) Subject: (no commit message) X-Git-Tag: 7.0.1-0~2518 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=dde3f7aae5b130b593039065886a4c74bac4b922;p=imagemagick --- diff --git a/MagickCore/effect.c b/MagickCore/effect.c index ee645be4e..5b16f6348 100644 --- a/MagickCore/effect.c +++ b/MagickCore/effect.c @@ -62,6 +62,7 @@ #include "MagickCore/image-private.h" #include "MagickCore/list.h" #include "MagickCore/log.h" +#include "MagickCore/matrix.h" #include "MagickCore/memory_.h" #include "MagickCore/memory-private.h" #include "MagickCore/monitor.h" @@ -861,7 +862,7 @@ MagickExport Image *BlurImage(const Image *image,const double radius, % The format of the EdgeImage method is: % % Image *CannyEdgeImage(const Image *image,const double radius, -% const double sigma,const double low_factor,const double high_factor, +% const double sigma,const double low_percent,const double high_percent, % const size_t threshold,ExceptionInfo *exception) % % A description of each parameter follows: @@ -874,27 +875,408 @@ MagickExport Image *BlurImage(const Image *image,const double radius, % % o sigma: the sigma of the gaussian smoothing filter. % -% o low_factor: use this low factor in hysteresis. +% o low_percent: percentage of pixels in the low threshold. % -% o hight_factor: use this high factor in hysteresis. +% o high_percent: percentage of pixels in the high threshold. % % o exception: return any errors or warnings in this structure. % */ + +typedef struct _CannyInfo +{ + double + Dx, + Dy, + magnitude, + intensity; +} CannyInfo; + +static 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 TraceEdge(Image *edge_image,CacheView *edge_view, + MatrixInfo *pixel_cache,const ssize_t x,const ssize_t y, + const double threshold,ExceptionInfo *exception) +{ + CannyInfo + pixel; + + Quantum + *q; + + MagickBooleanType + status; + + q=GetCacheViewAuthenticPixels(edge_view,x,y,1,1,exception); + if ((q != (Quantum *) NULL) && (GetPixelIntensity(edge_image,q) == 0)) + { + ssize_t + v; + + *q=QuantumRange; + status=SyncCacheViewAuthenticPixels(edge_view,exception); + if (status != MagickFalse) + { + for (v=(-1); v <= 1; v++) + { + ssize_t + u; + + for (u=(-1); u <= 1; u++) + { + if (((y != 0) || (u != 0)) && + (IsAuthenticPixel(edge_image,x+u,y+v) != MagickFalse)) + { + (void) GetMatrixElement(pixel_cache,x+u,y+v,&pixel); + if (pixel.intensity >= threshold) + { + status=TraceEdge(edge_image,edge_view,pixel_cache,x+u,y+v, + threshold,exception); + if (status != MagickFalse) + return(MagickTrue); + } + } + } + } + return(MagickTrue); + } + } + return(MagickFalse); +} + MagickExport Image *CannyEdgeImage(const Image *image,const double radius, - const double sigma,const double low_factor,const double high_factor, + const double sigma,const double low_percent,const double high_percent, ExceptionInfo *exception) { + CacheView + *edge_view; + + char + geometry[MaxTextExtent]; + + double + high_threshold, + low_threshold; + Image *edge_image; + KernelInfo + *kernel_info; + + MagickBooleanType + status; + + MatrixInfo + *pixel_cache; + + register ssize_t + i; + + size_t + *histogram, + number_pixels; + + ssize_t + count, + 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); - edge_image=(Image *) NULL; + /* + Filter out noise before trying to locate and detect any edges. + */ + (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 edge strength by taking the gradient of the image. + */ + pixel_cache=AcquireMatrixInfo(edge_image->columns,edge_image->rows, + sizeof(CannyInfo),exception); + if (pixel_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,-1,y-1,edge_image->columns+2,3, + exception); + if (p == (const Quantum *) NULL) + { + status=MagickFalse; + continue; + } + for (x=0; x < (ssize_t) edge_image->columns; x++) + { + CannyInfo + pixel; + + register const Quantum + *restrict kernel_pixels; + + ssize_t + v; + + static double + Gx[3][3] = + { + { -1.0, 0.0, +1.0 }, + { -2.0, 0.0, +2.0 }, + { -1.0, 0.0, +1.0 } + }, + Gy[3][3] = + { + { +1.0, +2.0, +1.0 }, + { 0.0, 0.0, 0.0 }, + { -1.0, -2.0, -1.0 } + }; + + (void) ResetMagickMemory(&pixel,0,sizeof(pixel)); + kernel_pixels=p; + for (v=0; v < 3; v++) + { + ssize_t + u; + + for (u=0; u < 3; u++) + { + double + intensity; + + intensity=GetPixelIntensity(edge_image,kernel_pixels+u); + pixel.Dx+=Gx[v][u]*intensity; + pixel.Dy+=Gy[v][u]*intensity; + } + kernel_pixels+=edge_image->columns+2; + } + pixel.magnitude=sqrt(pixel.Dx*pixel.Dx+pixel.Dy*pixel.Dy); + if (SetMatrixElement(pixel_cache,x,y,&pixel) == MagickFalse) + continue; + p+=GetPixelChannels(edge_image); + } + } + edge_view=DestroyCacheView(edge_view); + /* + Non-maxima suppression; reset edge image. + */ + 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)); + 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; + + ssize_t + direction; + + (void) GetMatrixElement(pixel_cache,x,y,&pixel); + direction=2; + if (pixel.Dx != 0.0) + { + double + sector; + + sector=pixel.Dy/pixel.Dx; + if (sector < 0.0) + { + if (sector < -2.41421356237) + direction=0; + else + if (sector < -0.414213562373) + direction=1; + else + direction=2; + } + else + { + if (sector > 2.41421356237) + direction=0; + else + if (sector > 0.414213562373) + direction=3; + else + direction=2; + } + } + switch (direction) + { + case 0: + { + /* + 0 degrees. + */ + (void) GetMatrixElement(pixel_cache,x,y-1,&alpha_pixel); + (void) GetMatrixElement(pixel_cache,x,y+1,&beta_pixel); + break; + } + case 1: + { + /* + 45 degrees. + */ + (void) GetMatrixElement(pixel_cache,x-1,y-1,&alpha_pixel); + (void) GetMatrixElement(pixel_cache,x+1,y+1,&beta_pixel); + break; + } + case 2: + { + /* + 90 degrees. + */ + (void) GetMatrixElement(pixel_cache,x-1,y,&alpha_pixel); + (void) GetMatrixElement(pixel_cache,x+1,y,&beta_pixel); + } + case 3: + { + /* + 135 degrees. + */ + (void) GetMatrixElement(pixel_cache,x+1,y-1,&alpha_pixel); + (void) GetMatrixElement(pixel_cache,x-1,y+1,&beta_pixel); + } + } + pixel.intensity=pixel.magnitude; + 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); +#if defined(MAGICKCORE_OPENMP_SUPPORT) + #pragma omp critical (MagickCore_CannyEdgeImage) +#endif + histogram[ScaleQuantumToShort(ClampToQuantum(pixel.intensity))]++; + *q=0; + q+=GetPixelChannels(edge_image); + } + if (SyncCacheViewAuthenticPixels(edge_view,exception) == MagickFalse) + status=MagickFalse; + } + edge_view=DestroyCacheView(edge_view); + /* + Estimate hysteresis threshold. + */ + number_pixels=(size_t) (low_percent*(image->columns*image->rows- + histogram[0])); + count=0; + for (i=65535; count < (ssize_t) number_pixels; i--) + count+=histogram[i]; + high_threshold=(double) ScaleShortToQuantum((unsigned short) i); + for (i=0; histogram[i] == 0; i++) ; + low_threshold=high_percent*(high_threshold+ + ScaleShortToQuantum((unsigned short) i)); + histogram=(size_t *) RelinquishMagickMemory(histogram); + /* + Hysteresis thresholding. + */ + 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 + pixel; + + (void) GetMatrixElement(pixel_cache,x,y,&pixel); + if (pixel.intensity >= high_threshold) + (void) TraceEdge(edge_image,edge_view,pixel_cache,x,y,low_threshold, + exception); + } + if (SyncCacheViewAuthenticPixels(edge_view,exception) == MagickFalse) + status=MagickFalse; + } + edge_view=DestroyCacheView(edge_view); + pixel_cache=DestroyMatrixInfo(pixel_cache); return(edge_image); }