From: cristy Date: Sat, 26 Apr 2014 14:13:53 +0000 (+0000) Subject: (no commit message) X-Git-Tag: 7.0.1-0~2411 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2fc10e5aedab9144531a4668dc7526e3caf514e1;p=imagemagick --- diff --git a/MagickCore/feature.c b/MagickCore/feature.c index 589af52b2..fa1e9c5bf 100644 --- a/MagickCore/feature.c +++ b/MagickCore/feature.c @@ -556,112 +556,146 @@ MagickExport Image *CannyEdgeImage(const Image *image,const double radius, % % % % % % -% H o u g h L i n e s I m a g e % +% G e t I m a g e F e a t u r e s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % -% HoughLinesImage() identifies lines in the image. +% GetImageFeatures() returns features for each channel in the image in +% each of four directions (horizontal, vertical, left and right diagonals) +% for the specified distance. The features include the angular second +% moment, contrast, correlation, sum of squares: variance, inverse difference +% moment, sum average, sum varience, sum entropy, entropy, difference variance,% difference entropy, information measures of correlation 1, information +% measures of correlation 2, and maximum correlation coefficient. You can +% access the red channel contrast, for example, like this: +% +% channel_features=GetImageFeatures(image,1,exception); +% contrast=channel_features[RedPixelChannel].contrast[0]; +% +% Use MagickRelinquishMemory() to free the features buffer. % -% The format of the HoughLinesImage method is: +% The format of the GetImageFeatures method is: % -% Image *HoughLinesImage(const Image *image,const size_t width, -% const size_t height,const size_t threshold,ExceptionInfo *exception) +% ChannelFeatures *GetImageFeatures(const Image *image, +% const size_t distance,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 distance: the distance. % % o exception: return any errors or warnings in this structure. % */ -static inline double MagickRound(double x) +static inline ssize_t MagickAbsoluteValue(const ssize_t x) { - /* - Round the fraction to nearest integer. - */ - if ((x-floor(x)) < (ceil(x)-x)) - return(floor(x)); - return(ceil(x)); + if (x < 0) + return(-x); + return(x); } -MagickExport Image *HoughLinesImage(const Image *image,const size_t width, - const size_t height,const size_t threshold,ExceptionInfo *exception) +static inline double MagickLog10(const double x) { - CacheView - *image_view; +#define Log10Epsilon (1.0e-11) - char - message[MaxTextExtent], - path[MaxTextExtent]; + if (fabs(x) < Log10Epsilon) + return(log10(Log10Epsilon)); + return(log10(fabs(x))); +} - const char - *artifact; +MagickExport ChannelFeatures *GetImageFeatures(const Image *image, + const size_t distance,ExceptionInfo *exception) +{ + typedef struct _ChannelStatistics + { + PixelInfo + direction[4]; /* horizontal, vertical, left and right diagonals */ + } ChannelStatistics; - double - hough_height; + CacheView + *image_view; - Image - *lines_image = NULL; + ChannelFeatures + *channel_features; - ImageInfo - *image_info; + ChannelStatistics + **cooccurrence, + correlation, + *density_x, + *density_xy, + *density_y, + entropy_x, + entropy_xy, + entropy_xy1, + entropy_xy2, + entropy_y, + mean, + **Q, + *sum, + sum_squares, + variance; - int - file; + PixelPacket + gray, + *grays; MagickBooleanType status; - MatrixInfo - *accumulator; + register ssize_t + i; - PointInfo - center; + size_t + length; - register ssize_t + ssize_t y; - size_t - accumulator_height, - accumulator_width, - line_count; + unsigned int + number_grays; - /* - Create the accumulator. - */ - assert(image != (const Image *) NULL); + assert(image != (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); - accumulator_width=180; - hough_height=((sqrt(2.0)*(double) (image->rows > image->columns ? - image->rows : image->columns))/2.0); - accumulator_height=(size_t) (2.0*hough_height); - accumulator=AcquireMatrixInfo(accumulator_width,accumulator_height, - sizeof(double),exception); - if (accumulator == (MatrixInfo *) NULL) - ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); - if (NullMatrix(accumulator) == MagickFalse) - { - accumulator=DestroyMatrixInfo(accumulator); - ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); - } + if ((image->columns < (distance+1)) || (image->rows < (distance+1))) + return((ChannelFeatures *) NULL); + length=CompositeChannels+1UL; + channel_features=(ChannelFeatures *) AcquireQuantumMemory(length, + sizeof(*channel_features)); + if (channel_features == (ChannelFeatures *) NULL) + ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); + (void) ResetMagickMemory(channel_features,0,length* + sizeof(*channel_features)); /* - Populate the accumulator. + Form grays. */ + grays=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*grays)); + if (grays == (PixelPacket *) NULL) + { + channel_features=(ChannelFeatures *) RelinquishMagickMemory( + channel_features); + (void) ThrowMagickException(exception,GetMagickModule(), + ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); + return(channel_features); + } + for (i=0; i <= (ssize_t) MaxMap; i++) + { + grays[i].red=(~0U); + grays[i].green=(~0U); + grays[i].blue=(~0U); + grays[i].alpha=(~0U); + grays[i].black=(~0U); + } status=MagickTrue; - center.x=(double) image->columns/2.0; - center.y=(double) image->rows/2.0; image_view=AcquireVirtualCacheView(image,exception); +#if defined(MAGICKCORE_OPENMP_SUPPORT) + #pragma omp parallel for schedule(static,4) shared(status) \ + magick_threads(image,image,image->rows,1) +#endif for (y=0; y < (ssize_t) image->rows; y++) { register const Quantum @@ -673,416 +707,82 @@ MagickExport Image *HoughLinesImage(const Image *image,const size_t width, if (status == MagickFalse) continue; p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); - if (p == (Quantum *) NULL) + if (p == (const Quantum *) NULL) { status=MagickFalse; continue; } for (x=0; x < (ssize_t) image->columns; x++) { - if (GetPixelIntensity(image,p) > (QuantumRange/2)) - { - register ssize_t - i; - - for (i=0; i < 180; i++) - { - double - count, - radius; - - radius=(((double) x-center.x)*cos(DegreesToRadians((double) i)))+ - (((double) y-center.y)*sin(DegreesToRadians((double) i))); - (void) GetMatrixElement(accumulator,i,(ssize_t) - MagickRound(radius+hough_height),&count); - count++; - (void) SetMatrixElement(accumulator,i,(ssize_t) - MagickRound(radius+hough_height),&count); - } - } + grays[ScaleQuantumToMap(GetPixelRed(image,p))].red= + ScaleQuantumToMap(GetPixelRed(image,p)); + grays[ScaleQuantumToMap(GetPixelGreen(image,p))].green= + ScaleQuantumToMap(GetPixelGreen(image,p)); + grays[ScaleQuantumToMap(GetPixelBlue(image,p))].blue= + ScaleQuantumToMap(GetPixelBlue(image,p)); + if (image->colorspace == CMYKColorspace) + grays[ScaleQuantumToMap(GetPixelBlack(image,p))].black= + ScaleQuantumToMap(GetPixelBlack(image,p)); + if (image->alpha_trait == BlendPixelTrait) + grays[ScaleQuantumToMap(GetPixelAlpha(image,p))].alpha= + ScaleQuantumToMap(GetPixelAlpha(image,p)); p+=GetPixelChannels(image); } } image_view=DestroyCacheView(image_view); if (status == MagickFalse) { - accumulator=DestroyMatrixInfo(accumulator); - return((Image *) NULL); + grays=(PixelPacket *) RelinquishMagickMemory(grays); + channel_features=(ChannelFeatures *) RelinquishMagickMemory( + channel_features); + return(channel_features); } + (void) ResetMagickMemory(&gray,0,sizeof(gray)); + for (i=0; i <= (ssize_t) MaxMap; i++) + { + if (grays[i].red != ~0U) + grays[gray.red++].red=grays[i].red; + if (grays[i].green != ~0U) + grays[gray.green++].green=grays[i].green; + if (grays[i].blue != ~0U) + grays[gray.blue++].blue=grays[i].blue; + if (image->colorspace == CMYKColorspace) + if (grays[i].black != ~0U) + grays[gray.black++].black=grays[i].black; + if (image->alpha_trait == BlendPixelTrait) + if (grays[i].alpha != ~0U) + grays[gray.alpha++].alpha=grays[i].alpha; + } /* - Generate line segments from accumulator. + Allocate spatial dependence matrix. */ - file=AcquireUniqueFileResource(path); - if (file == -1) - { - accumulator=DestroyMatrixInfo(accumulator); - return((Image *) NULL); - } - (void) FormatLocaleString(message,MaxTextExtent, - "# Hough line transform: %.20gx%.20g%+.20g\n",(double) width, - (double) height,(double) threshold); - (void) write(file,message,strlen(message)); - (void) FormatLocaleString(message,MaxTextExtent,"viewbox 0 0 %.20g %.20g\n", - (double) image->columns,(double) image->rows); - (void) write(file,message,strlen(message)); - line_count=image->columns > image->rows ? image->columns/4 : image->rows/4; - if (threshold != 0) - line_count=threshold; - for (y=0; y < (ssize_t) accumulator_height; y++) - { - register ssize_t - x; - - for (x=0; x < (ssize_t) accumulator_width; x++) - { - double - count; - - (void) GetMatrixElement(accumulator,x,y,&count); - if (count >= (double) line_count) - { - double - maxima; - - SegmentInfo - line; - - ssize_t - v; - - /* - Is point a local maxima? - */ - maxima=count; - for (v=(-((ssize_t) height/2)); v < (((ssize_t) height/2)); v++) - { - ssize_t - u; - - for (u=(-((ssize_t) width/2)); u < (((ssize_t) width/2)); u++) - { - if ((u != 0) || (v !=0)) - { - (void) GetMatrixElement(accumulator,x+u,y+v,&count); - if (count > maxima) - { - maxima=count; - break; - } - } - } - if (u < (ssize_t) (width/2)) - break; - } - (void) GetMatrixElement(accumulator,x,y,&count); - if (maxima > count) - continue; - if ((x >= 45) && (x <= 135)) - { - /* - y = (r-x cos(t))/sin(t) - */ - line.x1=0.0; - line.y1=((double) (y-(accumulator_height/2.0))-((line.x1- - (image->columns/2.0))*cos(DegreesToRadians((double) x))))/ - sin(DegreesToRadians((double) x))+(image->rows/2.0); - line.x2=(double) image->columns; - line.y2=((double) (y-(accumulator_height/2.0))-((line.x2- - (image->columns/2.0))*cos(DegreesToRadians((double) x))))/ - sin(DegreesToRadians((double) x))+(image->rows/2.0); - } - else - { - /* - x = (r-y cos(t))/sin(t) - */ - line.y1=0.0; - line.x1=((double) (y-(accumulator_height/2.0))-((line.y1- - (image->rows/2.0))*sin(DegreesToRadians((double) x))))/ - cos(DegreesToRadians((double) x))+(image->columns/2.0); - line.y2=(double) image->rows; - line.x2=((double) (y-(accumulator_height/2.0))-((line.y2- - (image->rows/2.0))*sin(DegreesToRadians((double) x))))/ - cos(DegreesToRadians((double) x))+(image->columns/2.0); - } - (void) FormatLocaleString(message,MaxTextExtent, - "line %g,%g %g,%g # %g\n",line.x1,line.y1,line.x2,line.y2,maxima); - (void) write(file,message,strlen(message)); - } - } - } - (void) close(file); - /* - Render lines to image canvas. - */ - image_info=AcquireImageInfo(); - image_info->background_color=image->background_color; - (void) FormatLocaleString(image_info->filename,MaxTextExtent,"mvg:%s",path); - artifact=GetImageArtifact(image,"background"); - if (artifact != (const char *) NULL) - (void) SetImageOption(image_info,"background",artifact); - artifact=GetImageArtifact(image,"fill"); - if (artifact != (const char *) NULL) - (void) SetImageOption(image_info,"fill",artifact); - artifact=GetImageArtifact(image,"stroke"); - if (artifact != (const char *) NULL) - (void) SetImageOption(image_info,"stroke",artifact); - artifact=GetImageArtifact(image,"strokewidth"); - if (artifact != (const char *) NULL) - (void) SetImageOption(image_info,"strokewidth",artifact); - lines_image=ReadImage(image_info,exception); - artifact=GetImageArtifact(image,"hough-lines:accumulator"); - if ((lines_image != (Image *) NULL) && - (IsStringTrue(artifact) != MagickFalse)) - { - Image - *accumulator_image; - - accumulator_image=MatrixToImage(accumulator,exception); - if (accumulator_image != (Image *) NULL) - AppendImageToList(&lines_image,accumulator_image); - } - /* - Free resources. - */ - accumulator=DestroyMatrixInfo(accumulator); - image_info=DestroyImageInfo(image_info); - (void) RelinquishUniqueFileResource(path); - return(GetFirstImageInList(lines_image)); -} - -/* -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% % -% % -% % -% G e t I m a g e F e a t u r e s % -% % -% % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% -% GetImageFeatures() returns features for each channel in the image in -% each of four directions (horizontal, vertical, left and right diagonals) -% for the specified distance. The features include the angular second -% moment, contrast, correlation, sum of squares: variance, inverse difference -% moment, sum average, sum varience, sum entropy, entropy, difference variance,% difference entropy, information measures of correlation 1, information -% measures of correlation 2, and maximum correlation coefficient. You can -% access the red channel contrast, for example, like this: -% -% channel_features=GetImageFeatures(image,1,exception); -% contrast=channel_features[RedPixelChannel].contrast[0]; -% -% Use MagickRelinquishMemory() to free the features buffer. -% -% The format of the GetImageFeatures method is: -% -% ChannelFeatures *GetImageFeatures(const Image *image, -% const size_t distance,ExceptionInfo *exception) -% -% A description of each parameter follows: -% -% o image: the image. -% -% o distance: the distance. -% -% o exception: return any errors or warnings in this structure. -% -*/ - -static inline ssize_t MagickAbsoluteValue(const ssize_t x) -{ - if (x < 0) - return(-x); - return(x); -} - -static inline double MagickLog10(const double x) -{ -#define Log10Epsilon (1.0e-11) - - if (fabs(x) < Log10Epsilon) - return(log10(Log10Epsilon)); - return(log10(fabs(x))); -} - -MagickExport ChannelFeatures *GetImageFeatures(const Image *image, - const size_t distance,ExceptionInfo *exception) -{ - typedef struct _ChannelStatistics - { - PixelInfo - direction[4]; /* horizontal, vertical, left and right diagonals */ - } ChannelStatistics; - - CacheView - *image_view; - - ChannelFeatures - *channel_features; - - ChannelStatistics - **cooccurrence, - correlation, - *density_x, - *density_xy, - *density_y, - entropy_x, - entropy_xy, - entropy_xy1, - entropy_xy2, - entropy_y, - mean, - **Q, - *sum, - sum_squares, - variance; - - PixelPacket - gray, - *grays; - - MagickBooleanType - status; - - register ssize_t - i; - - size_t - length; - - ssize_t - y; - - unsigned int - number_grays; - - assert(image != (Image *) NULL); - assert(image->signature == MagickSignature); - if (image->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); - if ((image->columns < (distance+1)) || (image->rows < (distance+1))) - return((ChannelFeatures *) NULL); - length=CompositeChannels+1UL; - channel_features=(ChannelFeatures *) AcquireQuantumMemory(length, - sizeof(*channel_features)); - if (channel_features == (ChannelFeatures *) NULL) - ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); - (void) ResetMagickMemory(channel_features,0,length* - sizeof(*channel_features)); - /* - Form grays. - */ - grays=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*grays)); - if (grays == (PixelPacket *) NULL) - { - channel_features=(ChannelFeatures *) RelinquishMagickMemory( - channel_features); - (void) ThrowMagickException(exception,GetMagickModule(), - ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); - return(channel_features); - } - for (i=0; i <= (ssize_t) MaxMap; i++) - { - grays[i].red=(~0U); - grays[i].green=(~0U); - grays[i].blue=(~0U); - grays[i].alpha=(~0U); - grays[i].black=(~0U); - } - status=MagickTrue; - image_view=AcquireVirtualCacheView(image,exception); -#if defined(MAGICKCORE_OPENMP_SUPPORT) - #pragma omp parallel for schedule(static,4) shared(status) \ - magick_threads(image,image,image->rows,1) -#endif - for (y=0; y < (ssize_t) image->rows; y++) - { - register const Quantum - *restrict p; - - register ssize_t - x; - - if (status == MagickFalse) - continue; - p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); - if (p == (const Quantum *) NULL) - { - status=MagickFalse; - continue; - } - for (x=0; x < (ssize_t) image->columns; x++) - { - grays[ScaleQuantumToMap(GetPixelRed(image,p))].red= - ScaleQuantumToMap(GetPixelRed(image,p)); - grays[ScaleQuantumToMap(GetPixelGreen(image,p))].green= - ScaleQuantumToMap(GetPixelGreen(image,p)); - grays[ScaleQuantumToMap(GetPixelBlue(image,p))].blue= - ScaleQuantumToMap(GetPixelBlue(image,p)); - if (image->colorspace == CMYKColorspace) - grays[ScaleQuantumToMap(GetPixelBlack(image,p))].black= - ScaleQuantumToMap(GetPixelBlack(image,p)); - if (image->alpha_trait == BlendPixelTrait) - grays[ScaleQuantumToMap(GetPixelAlpha(image,p))].alpha= - ScaleQuantumToMap(GetPixelAlpha(image,p)); - p+=GetPixelChannels(image); - } - } - image_view=DestroyCacheView(image_view); - if (status == MagickFalse) - { - grays=(PixelPacket *) RelinquishMagickMemory(grays); - channel_features=(ChannelFeatures *) RelinquishMagickMemory( - channel_features); - return(channel_features); - } - (void) ResetMagickMemory(&gray,0,sizeof(gray)); - for (i=0; i <= (ssize_t) MaxMap; i++) - { - if (grays[i].red != ~0U) - grays[gray.red++].red=grays[i].red; - if (grays[i].green != ~0U) - grays[gray.green++].green=grays[i].green; - if (grays[i].blue != ~0U) - grays[gray.blue++].blue=grays[i].blue; - if (image->colorspace == CMYKColorspace) - if (grays[i].black != ~0U) - grays[gray.black++].black=grays[i].black; - if (image->alpha_trait == BlendPixelTrait) - if (grays[i].alpha != ~0U) - grays[gray.alpha++].alpha=grays[i].alpha; - } - /* - Allocate spatial dependence matrix. - */ - number_grays=gray.red; - if (gray.green > number_grays) - number_grays=gray.green; - if (gray.blue > number_grays) - number_grays=gray.blue; - if (image->colorspace == CMYKColorspace) - if (gray.black > number_grays) - number_grays=gray.black; - if (image->alpha_trait == BlendPixelTrait) - if (gray.alpha > number_grays) - number_grays=gray.alpha; - cooccurrence=(ChannelStatistics **) AcquireQuantumMemory(number_grays, - sizeof(*cooccurrence)); - density_x=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1), - sizeof(*density_x)); - density_xy=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1), - sizeof(*density_xy)); - density_y=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1), - sizeof(*density_y)); - Q=(ChannelStatistics **) AcquireQuantumMemory(number_grays,sizeof(*Q)); - sum=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(*sum)); - if ((cooccurrence == (ChannelStatistics **) NULL) || - (density_x == (ChannelStatistics *) NULL) || - (density_xy == (ChannelStatistics *) NULL) || - (density_y == (ChannelStatistics *) NULL) || - (Q == (ChannelStatistics **) NULL) || - (sum == (ChannelStatistics *) NULL)) + number_grays=gray.red; + if (gray.green > number_grays) + number_grays=gray.green; + if (gray.blue > number_grays) + number_grays=gray.blue; + if (image->colorspace == CMYKColorspace) + if (gray.black > number_grays) + number_grays=gray.black; + if (image->alpha_trait == BlendPixelTrait) + if (gray.alpha > number_grays) + number_grays=gray.alpha; + cooccurrence=(ChannelStatistics **) AcquireQuantumMemory(number_grays, + sizeof(*cooccurrence)); + density_x=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1), + sizeof(*density_x)); + density_xy=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1), + sizeof(*density_xy)); + density_y=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1), + sizeof(*density_y)); + Q=(ChannelStatistics **) AcquireQuantumMemory(number_grays,sizeof(*Q)); + sum=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(*sum)); + if ((cooccurrence == (ChannelStatistics **) NULL) || + (density_x == (ChannelStatistics *) NULL) || + (density_xy == (ChannelStatistics *) NULL) || + (density_y == (ChannelStatistics *) NULL) || + (Q == (ChannelStatistics **) NULL) || + (sum == (ChannelStatistics *) NULL)) { if (Q != (ChannelStatistics **) NULL) { @@ -1889,114 +1589,453 @@ MagickExport ChannelFeatures *GetImageFeatures(const Image *image, entropy_xy.direction[i].alpha))))); } /* - Compute more texture features. + Compute more texture features. + */ +#if defined(MAGICKCORE_OPENMP_SUPPORT) + #pragma omp parallel for schedule(static,4) shared(status) \ + magick_threads(image,image,number_grays,1) +#endif + for (i=0; i < 4; i++) + { + ssize_t + z; + + for (z=0; z < (ssize_t) number_grays; z++) + { + register ssize_t + y; + + ChannelStatistics + pixel; + + (void) ResetMagickMemory(&pixel,0,sizeof(pixel)); + for (y=0; y < (ssize_t) number_grays; y++) + { + register ssize_t + x; + + for (x=0; x < (ssize_t) number_grays; x++) + { + /* + Contrast: amount of local variations present in an image. + */ + if (((y-x) == z) || ((x-y) == z)) + { + pixel.direction[i].red+=cooccurrence[x][y].direction[i].red; + pixel.direction[i].green+=cooccurrence[x][y].direction[i].green; + pixel.direction[i].blue+=cooccurrence[x][y].direction[i].blue; + if (image->colorspace == CMYKColorspace) + pixel.direction[i].black+=cooccurrence[x][y].direction[i].black; + if (image->alpha_trait == BlendPixelTrait) + pixel.direction[i].alpha+= + cooccurrence[x][y].direction[i].alpha; + } + /* + Maximum Correlation Coefficient. + */ + Q[z][y].direction[i].red+=cooccurrence[z][x].direction[i].red* + cooccurrence[y][x].direction[i].red/density_x[z].direction[i].red/ + density_y[x].direction[i].red; + Q[z][y].direction[i].green+=cooccurrence[z][x].direction[i].green* + cooccurrence[y][x].direction[i].green/ + density_x[z].direction[i].green/density_y[x].direction[i].red; + Q[z][y].direction[i].blue+=cooccurrence[z][x].direction[i].blue* + cooccurrence[y][x].direction[i].blue/density_x[z].direction[i].blue/ + density_y[x].direction[i].blue; + if (image->colorspace == CMYKColorspace) + Q[z][y].direction[i].black+=cooccurrence[z][x].direction[i].black* + cooccurrence[y][x].direction[i].black/ + density_x[z].direction[i].black/density_y[x].direction[i].black; + if (image->alpha_trait == BlendPixelTrait) + Q[z][y].direction[i].alpha+= + cooccurrence[z][x].direction[i].alpha* + cooccurrence[y][x].direction[i].alpha/ + density_x[z].direction[i].alpha/ + density_y[x].direction[i].alpha; + } + } + channel_features[RedPixelChannel].contrast[i]+=z*z* + pixel.direction[i].red; + channel_features[GreenPixelChannel].contrast[i]+=z*z* + pixel.direction[i].green; + channel_features[BluePixelChannel].contrast[i]+=z*z* + pixel.direction[i].blue; + if (image->colorspace == CMYKColorspace) + channel_features[BlackPixelChannel].contrast[i]+=z*z* + pixel.direction[i].black; + if (image->alpha_trait == BlendPixelTrait) + channel_features[AlphaPixelChannel].contrast[i]+=z*z* + pixel.direction[i].alpha; + } + /* + Maximum Correlation Coefficient. + Future: return second largest eigenvalue of Q. + */ + channel_features[RedPixelChannel].maximum_correlation_coefficient[i]= + sqrt((double) -1.0); + channel_features[GreenPixelChannel].maximum_correlation_coefficient[i]= + sqrt((double) -1.0); + channel_features[BluePixelChannel].maximum_correlation_coefficient[i]= + sqrt((double) -1.0); + if (image->colorspace == CMYKColorspace) + channel_features[BlackPixelChannel].maximum_correlation_coefficient[i]= + sqrt((double) -1.0); + if (image->alpha_trait == BlendPixelTrait) + channel_features[AlphaPixelChannel].maximum_correlation_coefficient[i]= + sqrt((double) -1.0); + } + /* + Relinquish resources. + */ + sum=(ChannelStatistics *) RelinquishMagickMemory(sum); + for (i=0; i < (ssize_t) number_grays; i++) + Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]); + Q=(ChannelStatistics **) RelinquishMagickMemory(Q); + density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y); + density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy); + density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x); + for (i=0; i < (ssize_t) number_grays; i++) + cooccurrence[i]=(ChannelStatistics *) + RelinquishMagickMemory(cooccurrence[i]); + cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence); + return(channel_features); +} + +/* +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% % +% % +% H o u g h L i n e I m a g e % +% % +% % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% HoughLineImage() identifies lines in the image. +% +% The format of the HoughLineImage method is: +% +% Image *HoughLineImage(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. +% +*/ + +static inline double MagickRound(double x) +{ + /* + Round the fraction to nearest integer. + */ + if ((x-floor(x)) < (ceil(x)-x)) + return(floor(x)); + return(ceil(x)); +} + +MagickExport Image *HoughLineImage(const Image *image,const size_t width, + const size_t height,const size_t threshold,ExceptionInfo *exception) +{ + CacheView + *image_view; + + char + message[MaxTextExtent], + path[MaxTextExtent]; + + const char + *artifact; + + double + hough_height; + + Image + *lines_image = NULL; + + ImageInfo + *image_info; + + int + file; + + MagickBooleanType + status; + + MatrixInfo + *accumulator; + + PointInfo + center; + + register ssize_t + y; + + size_t + accumulator_height, + accumulator_width, + line_count; + + /* + Create the accumulator. + */ + 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); + accumulator_width=180; + hough_height=((sqrt(2.0)*(double) (image->rows > image->columns ? + image->rows : image->columns))/2.0); + accumulator_height=(size_t) (2.0*hough_height); + accumulator=AcquireMatrixInfo(accumulator_width,accumulator_height, + sizeof(double),exception); + if (accumulator == (MatrixInfo *) NULL) + ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); + if (NullMatrix(accumulator) == MagickFalse) + { + accumulator=DestroyMatrixInfo(accumulator); + ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); + } + /* + Populate the accumulator. + */ + status=MagickTrue; + center.x=(double) image->columns/2.0; + center.y=(double) image->rows/2.0; + image_view=AcquireVirtualCacheView(image,exception); + for (y=0; y < (ssize_t) image->rows; y++) + { + register const Quantum + *restrict p; + + register ssize_t + x; + + if (status == MagickFalse) + continue; + p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); + if (p == (Quantum *) NULL) + { + status=MagickFalse; + continue; + } + for (x=0; x < (ssize_t) image->columns; x++) + { + if (GetPixelIntensity(image,p) > (QuantumRange/2)) + { + register ssize_t + i; + + for (i=0; i < 180; i++) + { + double + count, + radius; + + radius=(((double) x-center.x)*cos(DegreesToRadians((double) i)))+ + (((double) y-center.y)*sin(DegreesToRadians((double) i))); + (void) GetMatrixElement(accumulator,i,(ssize_t) + MagickRound(radius+hough_height),&count); + count++; + (void) SetMatrixElement(accumulator,i,(ssize_t) + MagickRound(radius+hough_height),&count); + } + } + p+=GetPixelChannels(image); + } + } + image_view=DestroyCacheView(image_view); + if (status == MagickFalse) + { + accumulator=DestroyMatrixInfo(accumulator); + return((Image *) NULL); + } + /* + Generate line segments from accumulator. */ -#if defined(MAGICKCORE_OPENMP_SUPPORT) - #pragma omp parallel for schedule(static,4) shared(status) \ - magick_threads(image,image,number_grays,1) -#endif - for (i=0; i < 4; i++) + file=AcquireUniqueFileResource(path); + if (file == -1) + { + accumulator=DestroyMatrixInfo(accumulator); + return((Image *) NULL); + } + (void) FormatLocaleString(message,MaxTextExtent, + "# Hough line transform: %.20gx%.20g%+.20g\n",(double) width, + (double) height,(double) threshold); + (void) write(file,message,strlen(message)); + (void) FormatLocaleString(message,MaxTextExtent,"viewbox 0 0 %.20g %.20g\n", + (double) image->columns,(double) image->rows); + (void) write(file,message,strlen(message)); + line_count=image->columns > image->rows ? image->columns/4 : image->rows/4; + if (threshold != 0) + line_count=threshold; + for (y=0; y < (ssize_t) accumulator_height; y++) { - ssize_t - z; + register ssize_t + x; - for (z=0; z < (ssize_t) number_grays; z++) + for (x=0; x < (ssize_t) accumulator_width; x++) { - register ssize_t - y; + double + count; - ChannelStatistics - pixel; + (void) GetMatrixElement(accumulator,x,y,&count); + if (count >= (double) line_count) + { + double + maxima; - (void) ResetMagickMemory(&pixel,0,sizeof(pixel)); - for (y=0; y < (ssize_t) number_grays; y++) - { - register ssize_t - x; + SegmentInfo + line; + + ssize_t + v; - for (x=0; x < (ssize_t) number_grays; x++) - { /* - Contrast: amount of local variations present in an image. + Is point a local maxima? */ - if (((y-x) == z) || ((x-y) == z)) + maxima=count; + for (v=(-((ssize_t) height/2)); v < (((ssize_t) height/2)); v++) + { + ssize_t + u; + + for (u=(-((ssize_t) width/2)); u < (((ssize_t) width/2)); u++) { - pixel.direction[i].red+=cooccurrence[x][y].direction[i].red; - pixel.direction[i].green+=cooccurrence[x][y].direction[i].green; - pixel.direction[i].blue+=cooccurrence[x][y].direction[i].blue; - if (image->colorspace == CMYKColorspace) - pixel.direction[i].black+=cooccurrence[x][y].direction[i].black; - if (image->alpha_trait == BlendPixelTrait) - pixel.direction[i].alpha+= - cooccurrence[x][y].direction[i].alpha; + if ((u != 0) || (v !=0)) + { + (void) GetMatrixElement(accumulator,x+u,y+v,&count); + if (count > maxima) + { + maxima=count; + break; + } + } } - /* - Maximum Correlation Coefficient. - */ - Q[z][y].direction[i].red+=cooccurrence[z][x].direction[i].red* - cooccurrence[y][x].direction[i].red/density_x[z].direction[i].red/ - density_y[x].direction[i].red; - Q[z][y].direction[i].green+=cooccurrence[z][x].direction[i].green* - cooccurrence[y][x].direction[i].green/ - density_x[z].direction[i].green/density_y[x].direction[i].red; - Q[z][y].direction[i].blue+=cooccurrence[z][x].direction[i].blue* - cooccurrence[y][x].direction[i].blue/density_x[z].direction[i].blue/ - density_y[x].direction[i].blue; - if (image->colorspace == CMYKColorspace) - Q[z][y].direction[i].black+=cooccurrence[z][x].direction[i].black* - cooccurrence[y][x].direction[i].black/ - density_x[z].direction[i].black/density_y[x].direction[i].black; - if (image->alpha_trait == BlendPixelTrait) - Q[z][y].direction[i].alpha+= - cooccurrence[z][x].direction[i].alpha* - cooccurrence[y][x].direction[i].alpha/ - density_x[z].direction[i].alpha/ - density_y[x].direction[i].alpha; + if (u < (ssize_t) (width/2)) + break; + } + (void) GetMatrixElement(accumulator,x,y,&count); + if (maxima > count) + continue; + if ((x >= 45) && (x <= 135)) + { + /* + y = (r-x cos(t))/sin(t) + */ + line.x1=0.0; + line.y1=((double) (y-(accumulator_height/2.0))-((line.x1- + (image->columns/2.0))*cos(DegreesToRadians((double) x))))/ + sin(DegreesToRadians((double) x))+(image->rows/2.0); + line.x2=(double) image->columns; + line.y2=((double) (y-(accumulator_height/2.0))-((line.x2- + (image->columns/2.0))*cos(DegreesToRadians((double) x))))/ + sin(DegreesToRadians((double) x))+(image->rows/2.0); + } + else + { + /* + x = (r-y cos(t))/sin(t) + */ + line.y1=0.0; + line.x1=((double) (y-(accumulator_height/2.0))-((line.y1- + (image->rows/2.0))*sin(DegreesToRadians((double) x))))/ + cos(DegreesToRadians((double) x))+(image->columns/2.0); + line.y2=(double) image->rows; + line.x2=((double) (y-(accumulator_height/2.0))-((line.y2- + (image->rows/2.0))*sin(DegreesToRadians((double) x))))/ + cos(DegreesToRadians((double) x))+(image->columns/2.0); + } + (void) FormatLocaleString(message,MaxTextExtent, + "line %g,%g %g,%g # %g\n",line.x1,line.y1,line.x2,line.y2,maxima); + (void) write(file,message,strlen(message)); } - } - channel_features[RedPixelChannel].contrast[i]+=z*z* - pixel.direction[i].red; - channel_features[GreenPixelChannel].contrast[i]+=z*z* - pixel.direction[i].green; - channel_features[BluePixelChannel].contrast[i]+=z*z* - pixel.direction[i].blue; - if (image->colorspace == CMYKColorspace) - channel_features[BlackPixelChannel].contrast[i]+=z*z* - pixel.direction[i].black; - if (image->alpha_trait == BlendPixelTrait) - channel_features[AlphaPixelChannel].contrast[i]+=z*z* - pixel.direction[i].alpha; } - /* - Maximum Correlation Coefficient. - Future: return second largest eigenvalue of Q. - */ - channel_features[RedPixelChannel].maximum_correlation_coefficient[i]= - sqrt((double) -1.0); - channel_features[GreenPixelChannel].maximum_correlation_coefficient[i]= - sqrt((double) -1.0); - channel_features[BluePixelChannel].maximum_correlation_coefficient[i]= - sqrt((double) -1.0); - if (image->colorspace == CMYKColorspace) - channel_features[BlackPixelChannel].maximum_correlation_coefficient[i]= - sqrt((double) -1.0); - if (image->alpha_trait == BlendPixelTrait) - channel_features[AlphaPixelChannel].maximum_correlation_coefficient[i]= - sqrt((double) -1.0); } + (void) close(file); /* - Relinquish resources. + Render lines to image canvas. */ - sum=(ChannelStatistics *) RelinquishMagickMemory(sum); - for (i=0; i < (ssize_t) number_grays; i++) - Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]); - Q=(ChannelStatistics **) RelinquishMagickMemory(Q); - density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y); - density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy); - density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x); - for (i=0; i < (ssize_t) number_grays; i++) - cooccurrence[i]=(ChannelStatistics *) - RelinquishMagickMemory(cooccurrence[i]); - cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence); - return(channel_features); + image_info=AcquireImageInfo(); + image_info->background_color=image->background_color; + (void) FormatLocaleString(image_info->filename,MaxTextExtent,"mvg:%s",path); + artifact=GetImageArtifact(image,"background"); + if (artifact != (const char *) NULL) + (void) SetImageOption(image_info,"background",artifact); + artifact=GetImageArtifact(image,"fill"); + if (artifact != (const char *) NULL) + (void) SetImageOption(image_info,"fill",artifact); + artifact=GetImageArtifact(image,"stroke"); + if (artifact != (const char *) NULL) + (void) SetImageOption(image_info,"stroke",artifact); + artifact=GetImageArtifact(image,"strokewidth"); + if (artifact != (const char *) NULL) + (void) SetImageOption(image_info,"strokewidth",artifact); + lines_image=ReadImage(image_info,exception); + artifact=GetImageArtifact(image,"hough-lines:accumulator"); + if ((lines_image != (Image *) NULL) && + (IsStringTrue(artifact) != MagickFalse)) + { + Image + *accumulator_image; + + accumulator_image=MatrixToImage(accumulator,exception); + if (accumulator_image != (Image *) NULL) + AppendImageToList(&lines_image,accumulator_image); + } + /* + Free resources. + */ + accumulator=DestroyMatrixInfo(accumulator); + image_info=DestroyImageInfo(image_info); + (void) RelinquishUniqueFileResource(path); + return(GetFirstImageInList(lines_image)); +} + +/* +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% % +% % +% M e a n S h i f t I m a g e % +% % +% % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% MeanShiftImage() delineate arbitrarily shaped clusters in the image. +% +% The format of the MeanShiftImage method is: +% +% Image *MeanShiftImage(const Image *image,const size_t width, +% const size_t height,const size_t shift,const size_t iterations, +% ExceptionInfo *exception) +% +% A description of each parameter follows: +% +% o image: the image. +% +% o width, height: find clusters as local maxima in this neighborhood. +% +% o shift: the shift threshold. +% +% o iterations: maximum iteration to find local maxima. +% +% o exception: return any errors or warnings in this structure. +% +*/ +MagickExport Image *MeanShiftImage(const Image *image,const size_t width, + const size_t height,const size_t shift,const size_t iterations, + ExceptionInfo *exception) +{ + return((Image *) NULL); } diff --git a/MagickCore/feature.h b/MagickCore/feature.h index b3267c54e..8e5e311dd 100644 --- a/MagickCore/feature.h +++ b/MagickCore/feature.h @@ -50,8 +50,10 @@ extern MagickExport ChannelFeatures extern MagickExport Image *CannyEdgeImage(const Image *,const double,const double,const double, const double,ExceptionInfo *), - *HoughLinesImage(const Image *,const size_t,const size_t,const size_t, - ExceptionInfo *); + *HoughLineImage(const Image *,const size_t,const size_t,const size_t, + ExceptionInfo *), + *MeanShiftImage(const Image *,const size_t,const size_t,const size_t, + const size_t,ExceptionInfo *); #if defined(__cplusplus) || defined(c_plusplus) } diff --git a/MagickCore/option.c b/MagickCore/option.c index 295aebdcb..75719d2eb 100644 --- a/MagickCore/option.c +++ b/MagickCore/option.c @@ -259,6 +259,7 @@ static const OptionInfo { "-brightness-contrast", 1L, SimpleOperatorFlag, MagickFalse }, { "+cache", 0L, GlobalOptionFlag, MagickFalse }, { "-cache", 1L, GlobalOptionFlag, MagickFalse }, + { "+canny", 1L, DeprecateOptionFlag, MagickTrue }, { "-canny", 1L, SimpleOperatorFlag, MagickTrue }, { "+caption", 0L, ImageInfoOptionFlag | NeverInterpretArgsFlag, MagickFalse }, { "-caption", 1L, ImageInfoOptionFlag | NeverInterpretArgsFlag, MagickFalse }, @@ -438,6 +439,7 @@ static const OptionInfo { "-hald-clut", 0L, ListOperatorFlag | FireOptionFlag, MagickFalse }, { "+highlight-color", 0L, NonMagickOptionFlag | ImageInfoOptionFlag, MagickFalse }, { "-highlight-color", 1L, NonMagickOptionFlag | ImageInfoOptionFlag, MagickFalse }, + { "+hough-lines", 1L, DeprecateOptionFlag, MagickTrue }, { "-hough-lines", 1L, SimpleOperatorFlag, MagickTrue }, { "+iconGeometry", 0L, NonMagickOptionFlag, MagickFalse }, { "-iconGeometry", 1L, NonMagickOptionFlag, MagickFalse }, @@ -505,6 +507,8 @@ static const OptionInfo { "-mattecolor", 1L, ImageInfoOptionFlag, MagickFalse }, { "+maximum", 0L, DeprecateOptionFlag | FireOptionFlag, MagickTrue }, { "-maximum", 0L, DeprecateOptionFlag | FireOptionFlag, MagickTrue }, + { "+mean-shift", 1L, DeprecateOptionFlag, MagickTrue }, + { "-mean-shift", 1L, SimpleOperatorFlag, MagickTrue }, { "+median", 1L, DeprecateOptionFlag, MagickTrue }, { "-median", 1L, ReplacedOptionFlag | SimpleOperatorFlag | FireOptionFlag, MagickTrue }, { "+metric", 0L, DeprecateOptionFlag | FireOptionFlag, MagickFalse }, @@ -780,8 +784,6 @@ static const OptionInfo { "-window-group", 1L, NonMagickOptionFlag, MagickFalse }, { "+write", 1L, NoImageOperatorFlag | NeverInterpretArgsFlag | FireOptionFlag, MagickFalse }, { "-write", 1L, NoImageOperatorFlag | NeverInterpretArgsFlag | FireOptionFlag, MagickFalse }, - { "+zzz", 0L, UndefinedOptionFlag, MagickTrue }, /* required by CLI */ - { "-zzz", 0L, UndefinedOptionFlag, MagickFalse }, /* binary search */ { (char *) NULL, 0L, UndefinedOptionFlag, MagickFalse } }, ComposeOptions[] = diff --git a/MagickWand/convert.c b/MagickWand/convert.c index 2940372b9..b750c2392 100644 --- a/MagickWand/convert.c +++ b/MagickWand/convert.c @@ -231,6 +231,7 @@ static MagickBooleanType ConvertUsage(void) " improve contrast by 'stretching with saturation'", "-liquid-rescale geometry", " rescale image with seam-carving", + "-mean-shift geometry delineate arbitrarily shaped clusters in the image", "-median geometry apply a median filter to the image", "-mode geometry make each pixel the 'predominant color' of the", " neighborhood", @@ -2055,6 +2056,17 @@ WandExport MagickBooleanType ConvertImageCommand(ImageInfo *image_info, } if (LocaleCompare("maximum",option+1) == 0) break; + if (LocaleCompare("mean-shift",option+1) == 0) + { + if (*option == '+') + break; + i++; + if (i == (ssize_t) (argc-1)) + ThrowConvertException(OptionError,"MissingArgument",option); + if (IsGeometry(argv[i]) == MagickFalse) + ThrowConvertInvalidArgumentException(option,argv[i]); + break; + } if (LocaleCompare("median",option+1) == 0) { if (*option == '+') diff --git a/MagickWand/mogrify.c b/MagickWand/mogrify.c index ec0c8e4be..cb982b93a 100644 --- a/MagickWand/mogrify.c +++ b/MagickWand/mogrify.c @@ -1806,7 +1806,7 @@ WandExport MagickBooleanType MogrifyImage(ImageInfo *image_info,const int argc, flags=ParseGeometry(argv[i+1],&geometry_info); if ((flags & SigmaValue) == 0) geometry_info.sigma=geometry_info.rho; - mogrify_image=HoughLinesImage(*image,(size_t) geometry_info.rho, + mogrify_image=HoughLineImage(*image,(size_t) geometry_info.rho, (size_t) geometry_info.sigma,(size_t) geometry_info.xi,exception); break; } @@ -2094,6 +2094,23 @@ WandExport MagickBooleanType MogrifyImage(ImageInfo *image_info,const int argc, SetAlphaChannel : DeactivateAlphaChannel,exception); break; } + if (LocaleCompare("mean-shift",option+1) == 0) + { + /* + Detect edges in the image. + */ + (void) SyncImageSettings(mogrify_info,*image,exception); + flags=ParseGeometry(argv[i+1],&geometry_info); + if ((flags & SigmaValue) == 0) + geometry_info.sigma=geometry_info.rho; + if ((flags & PsiValue) == 0) + geometry_info.psi=3; + if ((flags & XiValue) == 0) + geometry_info.xi=100; + mogrify_image=MeanShiftImage(*image,(size_t) geometry_info.rho, + (size_t) geometry_info.sigma,(size_t) geometry_info.xi,exception); + break; + } if (LocaleCompare("median",option+1) == 0) { /* @@ -3365,6 +3382,8 @@ static MagickBooleanType MogrifyUsage(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", @@ -3379,6 +3398,7 @@ static MagickBooleanType MogrifyUsage(void) "-liquid-rescale geometry", " rescale image with seam-carving", "-magnify double the size of the image with pixel art scaling", + "-mean-shift geometry delineate arbitrarily shaped clusters in the image", "-median geometry apply a median filter to the image", "-mode geometry make each pixel the 'predominant color' of the", " neighborhood", @@ -4869,6 +4889,17 @@ WandExport MagickBooleanType MogrifyImageCommand(ImageInfo *image_info, if ((LocaleCompare("help",option+1) == 0) || (LocaleCompare("-help",option+1) == 0)) return(MogrifyUsage()); + if (LocaleCompare("hough-lines",option+1) == 0) + { + if (*option == '+') + break; + i++; + if (i == (ssize_t) argc) + ThrowMogrifyException(OptionError,"MissingArgument",option); + if (IsGeometry(argv[i]) == MagickFalse) + ThrowMogrifyInvalidArgumentException(option,argv[i]); + break; + } ThrowMogrifyException(OptionError,"UnrecognizedOption",option) } case 'i': @@ -5160,6 +5191,28 @@ WandExport MagickBooleanType MogrifyImageCommand(ImageInfo *image_info, } if (LocaleCompare("maximum",option+1) == 0) break; + if (LocaleCompare("mean-shift",option+1) == 0) + { + if (*option == '+') + break; + i++; + if (i == (ssize_t) argc) + ThrowMogrifyException(OptionError,"MissingArgument",option); + if (IsGeometry(argv[i]) == MagickFalse) + ThrowMogrifyInvalidArgumentException(option,argv[i]); + break; + } + if (LocaleCompare("median",option+1) == 0) + { + if (*option == '+') + break; + i++; + if (i == (ssize_t) argc) + ThrowMogrifyException(OptionError,"MissingArgument",option); + if (IsGeometry(argv[i]) == MagickFalse) + ThrowMogrifyInvalidArgumentException(option,argv[i]); + break; + } if (LocaleCompare("metric",option+1) == 0) { ssize_t @@ -5189,17 +5242,6 @@ WandExport MagickBooleanType MogrifyImageCommand(ImageInfo *image_info, ThrowMogrifyInvalidArgumentException(option,argv[i]); break; } - if (LocaleCompare("median",option+1) == 0) - { - if (*option == '+') - break; - i++; - if (i == (ssize_t) argc) - ThrowMogrifyException(OptionError,"MissingArgument",option); - if (IsGeometry(argv[i]) == MagickFalse) - ThrowMogrifyInvalidArgumentException(option,argv[i]); - break; - } if (LocaleCompare("mode",option+1) == 0) { if (*option == '+') diff --git a/MagickWand/operation.c b/MagickWand/operation.c index 082870036..96ad3327b 100644 --- a/MagickWand/operation.c +++ b/MagickWand/operation.c @@ -1922,9 +1922,9 @@ static MagickBooleanType CLISimpleOperatorImage(MagickCLI *cli_wand, if ((flags & SigmaValue) == 0) geometry_info.sigma=1.0; if ((flags & XiValue) == 0) - geometry_info.xi=0.10; + geometry_info.xi=10; if ((flags & PsiValue) == 0) - geometry_info.psi=0.30; + geometry_info.psi=30; if ((flags & PercentValue) != 0) { geometry_info.xi/=100.0; @@ -2488,6 +2488,25 @@ static MagickBooleanType CLISimpleOperatorImage(MagickCLI *cli_wand, } CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option); } + case 'h': + { + if (LocaleCompare("hough-lines",option+1) == 0) + { + flags=ParseGeometry(arg1,&geometry_info); + if ((flags & (RhoValue|SigmaValue)) == 0) + CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1); + if ((flags & SigmaValue) == 0) + geometry_info.sigma=1.0; + if ((flags & XiValue) == 0) + geometry_info.xi=3; + if ((flags & PsiValue) == 0) + geometry_info.psi10030; + new_image=HoughLineImage(_image,geometry_info.rho, + geometry_info.sigma,geometry_info.xi,geometry_info.psi,_exception); + break; + } + CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option); + } case 'i': { if (LocaleCompare("identify",option+1) == 0) @@ -2703,6 +2722,21 @@ static MagickBooleanType CLISimpleOperatorImage(MagickCLI *cli_wand, DeactivateAlphaChannel, _exception); break; } + if (LocaleCompare("mean-shift",option+1) == 0) + { + flags=ParseGeometry(arg1,&geometry_info); + if ((flags & (RhoValue|SigmaValue)) == 0) + CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1); + if ((flags & SigmaValue) == 0) + geometry_info.sigma=1.0; + if ((flags & XiValue) == 0) + geometry_info.xi=3; + if ((flags & PsiValue) == 0) + geometry_info.psi10030; + new_image=MeanShiftImage(_image,geometry_info.rho, + geometry_info.sigma,geometry_info.xi,geometry_info.psi,_exception); + break; + } if (LocaleCompare("median",option+1) == 0) { CLIWandWarnReplaced("-statistic Median"); diff --git a/PerlMagick/Magick.xs b/PerlMagick/Magick.xs index 2df8ecd76..8e8af9ef7 100644 --- a/PerlMagick/Magick.xs +++ b/PerlMagick/Magick.xs @@ -547,9 +547,12 @@ static struct { "CannyEdge", { {"geometry", StringReference}, {"radius", RealReference}, {"sigma", RealReference}, {"lower-percent", RealReference}, {"upper-percent", RealReference} } }, - { "HoughLines", { {"geometry", StringReference}, + { "HoughLine", { {"geometry", StringReference}, {"width", IntegerReference}, {"height", IntegerReference}, {"threshold", IntegerReference} } }, + { "MeanShift", { {"geometry", StringReference}, + {"width", IntegerReference}, {"height", IntegerReference}, + {"shift", IntegerReference}, {"iterations", IntegerReference} } }, }; static SplayTreeInfo @@ -7533,8 +7536,10 @@ Mogrify(ref,...) GrayscaleImage = 276 CannyEdge = 278 CannyEdgeImage = 279 - HoughLines = 280 - HoughLinesImage = 281 + HoughLine = 280 + HoughLineImage = 281 + MeanShift = 282 + MeanShiftImage = 283 MogrifyRegion = 666 PPCODE: { @@ -11137,7 +11142,26 @@ Mogrify(ref,...) (void) SetImageChannelMask(image,channel_mask); break; } - case 139: /* HoughLines */ + case 139: /* HoughLine */ + { + if (attribute_flag[0] != 0) + { + flags=ParseGeometry(argument_list[0].string_reference, + &geometry_info); + if ((flags & SigmaValue) == 0) + geometry_info.sigma=geometry_info.rho; + } + if (attribute_flag[1] != 0) + geometry_info.rho=(double) argument_list[1].integer_reference; + if (attribute_flag[2] != 0) + geometry_info.sigma=(double) argument_list[2].integer_reference; + if (attribute_flag[3] != 0) + geometry_info.xi=(double) argument_list[3].integer_reference; + image=HoughLineImage(image,(size_t) geometry_info.rho,(size_t) + geometry_info.sigma,(size_t) geometry_info.xi,exception); + break; + } + case 140: /* MeanShift */ { if (attribute_flag[0] != 0) { @@ -11145,6 +11169,10 @@ Mogrify(ref,...) &geometry_info); if ((flags & SigmaValue) == 0) geometry_info.sigma=geometry_info.rho; + if ((flags & PsiValue) == 0) + geometry_info.psi=3; + if ((flags & XiValue) == 0) + geometry_info.xi=100; } if (attribute_flag[1] != 0) geometry_info.rho=(double) argument_list[1].integer_reference; @@ -11152,7 +11180,7 @@ Mogrify(ref,...) geometry_info.sigma=(double) argument_list[2].integer_reference; if (attribute_flag[3] != 0) geometry_info.xi=(double) argument_list[3].integer_reference; - image=HoughLinesImage(image,(size_t) geometry_info.rho,(size_t) + image=MeanShiftImage(image,(size_t) geometry_info.rho,(size_t) geometry_info.sigma,(size_t) geometry_info.xi,exception); break; } diff --git a/PerlMagick/quantum/quantum.xs.in b/PerlMagick/quantum/quantum.xs.in index db23d7767..c249599d7 100644 --- a/PerlMagick/quantum/quantum.xs.in +++ b/PerlMagick/quantum/quantum.xs.in @@ -547,9 +547,12 @@ static struct { "CannyEdge", { {"geometry", StringReference}, {"radius", RealReference}, {"sigma", RealReference}, {"lower-percent", RealReference}, {"upper-percent", RealReference} } }, - { "HoughLines", { {"geometry", StringReference}, + { "HoughLine", { {"geometry", StringReference}, {"width", IntegerReference}, {"height", IntegerReference}, {"threshold", IntegerReference} } }, + { "MeanShift", { {"geometry", StringReference}, + {"width", IntegerReference}, {"height", IntegerReference}, + {"shift", IntegerReference}, {"iterations", IntegerReference} } }, }; static SplayTreeInfo @@ -7533,8 +7536,10 @@ Mogrify(ref,...) GrayscaleImage = 276 CannyEdge = 278 CannyEdgeImage = 279 - HoughLines = 280 - HoughLinesImage = 281 + HoughLine = 280 + HoughLineImage = 281 + MeanShift = 282 + MeanShiftImage = 283 MogrifyRegion = 666 PPCODE: { @@ -11137,7 +11142,26 @@ Mogrify(ref,...) (void) SetImageChannelMask(image,channel_mask); break; } - case 139: /* HoughLines */ + case 139: /* HoughLine */ + { + if (attribute_flag[0] != 0) + { + flags=ParseGeometry(argument_list[0].string_reference, + &geometry_info); + if ((flags & SigmaValue) == 0) + geometry_info.sigma=geometry_info.rho; + } + if (attribute_flag[1] != 0) + geometry_info.rho=(double) argument_list[1].integer_reference; + if (attribute_flag[2] != 0) + geometry_info.sigma=(double) argument_list[2].integer_reference; + if (attribute_flag[3] != 0) + geometry_info.xi=(double) argument_list[3].integer_reference; + image=HoughLineImage(image,(size_t) geometry_info.rho,(size_t) + geometry_info.sigma,(size_t) geometry_info.xi,exception); + break; + } + case 140: /* MeanShift */ { if (attribute_flag[0] != 0) { @@ -11145,6 +11169,10 @@ Mogrify(ref,...) &geometry_info); if ((flags & SigmaValue) == 0) geometry_info.sigma=geometry_info.rho; + if ((flags & PsiValue) == 0) + geometry_info.psi=3; + if ((flags & XiValue) == 0) + geometry_info.xi=100; } if (attribute_flag[1] != 0) geometry_info.rho=(double) argument_list[1].integer_reference; @@ -11152,7 +11180,7 @@ Mogrify(ref,...) geometry_info.sigma=(double) argument_list[2].integer_reference; if (attribute_flag[3] != 0) geometry_info.xi=(double) argument_list[3].integer_reference; - image=HoughLinesImage(image,(size_t) geometry_info.rho,(size_t) + image=MeanShiftImage(image,(size_t) geometry_info.rho,(size_t) geometry_info.sigma,(size_t) geometry_info.xi,exception); break; } diff --git a/utilities/convert.1 b/utilities/convert.1 index 8306cfa54..1b25c9cfc 100644 --- a/utilities/convert.1 +++ b/utilities/convert.1 @@ -176,7 +176,7 @@ Image Operators: \-geometry geometry preferred size or location of the image \-grayscale method convert image to grayscale \-hough-lines geometry - identify lines in the image + 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 @@ -188,7 +188,8 @@ Image Operators: \-linear-stretch geometry improve contrast by `stretching with saturation' the intensity range \-liquid-rescale geometry - \ rescale image with seam-carving + rescale image with seam-carving + \-mean-shift geometry delineate arbitrarily shaped clusters in the image \-median geometry apply a median filter to the image \-mode geometry make each pixel the 'predominant color' of the neighborhood \-modulate value vary the brightness, saturation, and hue diff --git a/utilities/convert.1.in b/utilities/convert.1.in index 7e7992f7f..347df6798 100644 --- a/utilities/convert.1.in +++ b/utilities/convert.1.in @@ -176,7 +176,7 @@ Image Operators: \-geometry geometry preferred size or location of the image \-grayscale method convert image to grayscale \-hough-lines geometry - identify lines in the image + 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 @@ -188,7 +188,8 @@ Image Operators: \-linear-stretch geometry improve contrast by `stretching with saturation' the intensity range \-liquid-rescale geometry - \ rescale image with seam-carving + rescale image with seam-carving + \-mean-shift geometry delineate arbitrarily shaped clusters in the image \-median geometry apply a median filter to the image \-mode geometry make each pixel the 'predominant color' of the neighborhood \-modulate value vary the brightness, saturation, and hue diff --git a/utilities/mogrify.1 b/utilities/mogrify.1 index e1346d291..154df50fb 100644 --- a/utilities/mogrify.1 +++ b/utilities/mogrify.1 @@ -174,7 +174,7 @@ Image Operators: \-grayscale method convert image to grayscale \-help print program options \-hough-lines geometry - identify lines in the image + 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 @@ -190,6 +190,7 @@ Image Operators: \-liquid-rescale geometry rescale image with seam-carving \-magnify double the size of the image with pixel art scaling + \-mean-shift geometry delineate arbitrarily shaped clusters in the image \-median geometry apply a median filter to the image \-mode geometry make each pixel the 'predominant color' of the neighborhood \-modulate value vary the brightness, saturation, and hue diff --git a/utilities/mogrify.1.in b/utilities/mogrify.1.in index 40b2788c7..23acc6d55 100644 --- a/utilities/mogrify.1.in +++ b/utilities/mogrify.1.in @@ -174,7 +174,7 @@ Image Operators: \-grayscale method convert image to grayscale \-help print program options \-hough-lines geometry - identify lines in the image + 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 @@ -190,6 +190,7 @@ Image Operators: \-liquid-rescale geometry rescale image with seam-carving \-magnify double the size of the image with pixel art scaling + \-mean-shift geometry delineate arbitrarily shaped clusters in the image \-median geometry apply a median filter to the image \-mode geometry make each pixel the 'predominant color' of the neighborhood \-modulate value vary the brightness, saturation, and hue