2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % EEEEE FFFFF FFFFF EEEEE CCCC TTTTT %
8 % EEE FFF FFF EEE C T %
10 % EEEEE F F EEEEE CCCC T %
13 % MagickCore Image Effects Methods %
20 % Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "magick/studio.h"
44 #include "magick/property.h"
45 #include "magick/blob.h"
46 #include "magick/cache-view.h"
47 #include "magick/color.h"
48 #include "magick/color-private.h"
49 #include "magick/colorspace.h"
50 #include "magick/constitute.h"
51 #include "magick/decorate.h"
52 #include "magick/draw.h"
53 #include "magick/enhance.h"
54 #include "magick/exception.h"
55 #include "magick/exception-private.h"
56 #include "magick/effect.h"
57 #include "magick/fx.h"
58 #include "magick/gem.h"
59 #include "magick/geometry.h"
60 #include "magick/image-private.h"
61 #include "magick/list.h"
62 #include "magick/log.h"
63 #include "magick/memory_.h"
64 #include "magick/monitor.h"
65 #include "magick/monitor-private.h"
66 #include "magick/montage.h"
67 #include "magick/paint.h"
68 #include "magick/pixel-private.h"
69 #include "magick/property.h"
70 #include "magick/quantize.h"
71 #include "magick/quantum.h"
72 #include "magick/random_.h"
73 #include "magick/random-private.h"
74 #include "magick/resample.h"
75 #include "magick/resample-private.h"
76 #include "magick/resize.h"
77 #include "magick/resource_.h"
78 #include "magick/segment.h"
79 #include "magick/shear.h"
80 #include "magick/signature-private.h"
81 #include "magick/string_.h"
82 #include "magick/thread-private.h"
83 #include "magick/transform.h"
84 #include "magick/threshold.h"
87 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91 % A d a p t i v e B l u r I m a g e %
95 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
97 % AdaptiveBlurImage() adaptively blurs the image by blurring less
98 % intensely near image edges and more intensely far from edges. We blur the
99 % image with a Gaussian operator of the given radius and standard deviation
100 % (sigma). For reasonable results, radius should be larger than sigma. Use a
101 % radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
103 % The format of the AdaptiveBlurImage method is:
105 % Image *AdaptiveBlurImage(const Image *image,const double radius,
106 % const double sigma,ExceptionInfo *exception)
107 % Image *AdaptiveBlurImageChannel(const Image *image,
108 % const ChannelType channel,double radius,const double sigma,
109 % ExceptionInfo *exception)
111 % A description of each parameter follows:
113 % o image: the image.
115 % o channel: the channel type.
117 % o radius: the radius of the Gaussian, in pixels, not counting the center
120 % o sigma: the standard deviation of the Laplacian, in pixels.
122 % o exception: return any errors or warnings in this structure.
126 MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
127 const double sigma,ExceptionInfo *exception)
132 blur_image=AdaptiveBlurImageChannel(image,DefaultChannels,radius,sigma,
137 MagickExport Image *AdaptiveBlurImageChannel(const Image *image,
138 const ChannelType channel,const double radius,const double sigma,
139 ExceptionInfo *exception)
141 #define AdaptiveBlurImageTag "Convolve/Image"
142 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
178 assert(image != (const Image *) NULL);
179 assert(image->signature == MagickSignature);
180 if (image->debug != MagickFalse)
181 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
182 assert(exception != (ExceptionInfo *) NULL);
183 assert(exception->signature == MagickSignature);
184 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
185 if (blur_image == (Image *) NULL)
186 return((Image *) NULL);
187 if (fabs(sigma) <= MagickEpsilon)
189 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
191 InheritException(exception,&blur_image->exception);
192 blur_image=DestroyImage(blur_image);
193 return((Image *) NULL);
196 Edge detect the image brighness channel, level, blur, and level again.
198 edge_image=EdgeImage(image,radius,exception);
199 if (edge_image == (Image *) NULL)
201 blur_image=DestroyImage(blur_image);
202 return((Image *) NULL);
204 (void) LevelImage(edge_image,"20%,95%");
205 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
206 if (gaussian_image != (Image *) NULL)
208 edge_image=DestroyImage(edge_image);
209 edge_image=gaussian_image;
211 (void) LevelImage(edge_image,"10%,95%");
213 Create a set of kernels from maximum (radius,sigma) to minimum.
215 width=GetOptimalKernelWidth2D(radius,sigma);
216 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
217 if (kernel == (double **) NULL)
219 edge_image=DestroyImage(edge_image);
220 blur_image=DestroyImage(blur_image);
221 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
223 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
224 for (i=0; i < (long) width; i+=2)
226 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
228 if (kernel[i] == (double *) NULL)
231 j=(long) (width-i)/2;
233 for (v=(-j); v <= j; v++)
235 for (u=(-j); u <= j; u++)
237 kernel[i][k]=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
238 (2.0*MagickPI*MagickSigma*MagickSigma);
239 normalize+=kernel[i][k];
243 if (fabs(normalize) <= MagickEpsilon)
245 normalize=1.0/normalize;
246 for (k=0; k < (j*j); k++)
247 kernel[i][k]=normalize*kernel[i][k];
249 if (i < (long) width)
251 for (i-=2; i >= 0; i-=2)
252 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
253 kernel=(double **) RelinquishMagickMemory(kernel);
254 edge_image=DestroyImage(edge_image);
255 blur_image=DestroyImage(blur_image);
256 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
259 Adaptively blur image.
263 GetMagickPixelPacket(image,&bias);
264 SetMagickPixelPacketBias(image,&bias);
265 image_view=AcquireCacheView(image);
266 edge_view=AcquireCacheView(edge_image);
267 blur_view=AcquireCacheView(blur_image);
268 #if defined(MAGICKCORE_OPENMP_SUPPORT)
269 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
271 for (y=0; y < (long) blur_image->rows; y++)
273 register const IndexPacket
276 register const PixelPacket
281 *restrict blur_indexes;
289 if (status == MagickFalse)
291 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
292 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
294 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
299 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
300 for (x=0; x < (long) blur_image->columns; x++)
309 register const double
318 i=(long) (width*QuantumScale*PixelIntensity(r)+0.5);
322 if (i > (long) width)
326 p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
327 ((width-i)/2L),width-i,width-i,exception);
328 if (p == (const PixelPacket *) NULL)
330 indexes=GetCacheViewVirtualIndexQueue(image_view);
333 for (v=0; v < (long) (width-i); v++)
335 for (u=0; u < (long) (width-i); u++)
338 if (((channel & OpacityChannel) != 0) &&
339 (image->matte != MagickFalse))
340 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
341 GetOpacityPixelComponent(p)));
342 if ((channel & RedChannel) != 0)
343 pixel.red+=(*k)*alpha*GetRedPixelComponent(p);
344 if ((channel & GreenChannel) != 0)
345 pixel.green+=(*k)*alpha*GetGreenPixelComponent(p);
346 if ((channel & BlueChannel) != 0)
347 pixel.blue+=(*k)*alpha*GetBluePixelComponent(p);
348 if ((channel & OpacityChannel) != 0)
349 pixel.opacity+=(*k)*GetOpacityPixelComponent(p);
350 if (((channel & IndexChannel) != 0) &&
351 (image->colorspace == CMYKColorspace))
352 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
358 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
359 if ((channel & RedChannel) != 0)
360 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
361 if ((channel & GreenChannel) != 0)
362 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
363 if ((channel & BlueChannel) != 0)
364 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
365 if ((channel & OpacityChannel) != 0)
366 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
367 if (((channel & IndexChannel) != 0) &&
368 (image->colorspace == CMYKColorspace))
369 blur_indexes[x]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
373 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
375 if (image->progress_monitor != (MagickProgressMonitor) NULL)
380 #if defined(MAGICKCORE_OPENMP_SUPPORT)
381 #pragma omp critical (MagickCore_AdaptiveBlurImageChannel)
383 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
385 if (proceed == MagickFalse)
389 blur_image->type=image->type;
390 blur_view=DestroyCacheView(blur_view);
391 edge_view=DestroyCacheView(edge_view);
392 image_view=DestroyCacheView(image_view);
393 edge_image=DestroyImage(edge_image);
394 for (i=0; i < (long) width; i+=2)
395 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
396 kernel=(double **) RelinquishMagickMemory(kernel);
397 if (status == MagickFalse)
398 blur_image=DestroyImage(blur_image);
403 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
407 % A d a p t i v e S h a r p e n I m a g e %
411 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
413 % AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
414 % intensely near image edges and less intensely far from edges. We sharpen the
415 % image with a Gaussian operator of the given radius and standard deviation
416 % (sigma). For reasonable results, radius should be larger than sigma. Use a
417 % radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
419 % The format of the AdaptiveSharpenImage method is:
421 % Image *AdaptiveSharpenImage(const Image *image,const double radius,
422 % const double sigma,ExceptionInfo *exception)
423 % Image *AdaptiveSharpenImageChannel(const Image *image,
424 % const ChannelType channel,double radius,const double sigma,
425 % ExceptionInfo *exception)
427 % A description of each parameter follows:
429 % o image: the image.
431 % o channel: the channel type.
433 % o radius: the radius of the Gaussian, in pixels, not counting the center
436 % o sigma: the standard deviation of the Laplacian, in pixels.
438 % o exception: return any errors or warnings in this structure.
442 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
443 const double sigma,ExceptionInfo *exception)
448 sharp_image=AdaptiveSharpenImageChannel(image,DefaultChannels,radius,sigma,
453 MagickExport Image *AdaptiveSharpenImageChannel(const Image *image,
454 const ChannelType channel,const double radius,const double sigma,
455 ExceptionInfo *exception)
457 #define AdaptiveSharpenImageTag "Convolve/Image"
458 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
494 assert(image != (const Image *) NULL);
495 assert(image->signature == MagickSignature);
496 if (image->debug != MagickFalse)
497 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
498 assert(exception != (ExceptionInfo *) NULL);
499 assert(exception->signature == MagickSignature);
500 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
501 if (sharp_image == (Image *) NULL)
502 return((Image *) NULL);
503 if (fabs(sigma) <= MagickEpsilon)
505 if (SetImageStorageClass(sharp_image,DirectClass) == MagickFalse)
507 InheritException(exception,&sharp_image->exception);
508 sharp_image=DestroyImage(sharp_image);
509 return((Image *) NULL);
512 Edge detect the image brighness channel, level, sharp, and level again.
514 edge_image=EdgeImage(image,radius,exception);
515 if (edge_image == (Image *) NULL)
517 sharp_image=DestroyImage(sharp_image);
518 return((Image *) NULL);
520 (void) LevelImage(edge_image,"20%,95%");
521 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
522 if (gaussian_image != (Image *) NULL)
524 edge_image=DestroyImage(edge_image);
525 edge_image=gaussian_image;
527 (void) LevelImage(edge_image,"10%,95%");
529 Create a set of kernels from maximum (radius,sigma) to minimum.
531 width=GetOptimalKernelWidth2D(radius,sigma);
532 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
533 if (kernel == (double **) NULL)
535 edge_image=DestroyImage(edge_image);
536 sharp_image=DestroyImage(sharp_image);
537 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
539 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
540 for (i=0; i < (long) width; i+=2)
542 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
544 if (kernel[i] == (double *) NULL)
547 j=(long) (width-i)/2;
549 for (v=(-j); v <= j; v++)
551 for (u=(-j); u <= j; u++)
553 kernel[i][k]=(-exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
554 (2.0*MagickPI*MagickSigma*MagickSigma));
555 normalize+=kernel[i][k];
559 if (fabs(normalize) <= MagickEpsilon)
561 normalize=1.0/normalize;
562 for (k=0; k < (j*j); k++)
563 kernel[i][k]=normalize*kernel[i][k];
565 if (i < (long) width)
567 for (i-=2; i >= 0; i-=2)
568 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
569 kernel=(double **) RelinquishMagickMemory(kernel);
570 edge_image=DestroyImage(edge_image);
571 sharp_image=DestroyImage(sharp_image);
572 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
575 Adaptively sharpen image.
579 GetMagickPixelPacket(image,&bias);
580 SetMagickPixelPacketBias(image,&bias);
581 image_view=AcquireCacheView(image);
582 edge_view=AcquireCacheView(edge_image);
583 sharp_view=AcquireCacheView(sharp_image);
584 #if defined(MAGICKCORE_OPENMP_SUPPORT)
585 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
587 for (y=0; y < (long) sharp_image->rows; y++)
589 register const IndexPacket
592 register const PixelPacket
597 *restrict sharp_indexes;
605 if (status == MagickFalse)
607 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
608 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
610 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
615 sharp_indexes=GetCacheViewAuthenticIndexQueue(sharp_view);
616 for (x=0; x < (long) sharp_image->columns; x++)
625 register const double
634 i=(long) (width*(QuantumRange-QuantumScale*PixelIntensity(r))+0.5);
638 if (i > (long) width)
642 p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
643 ((width-i)/2L),width-i,width-i,exception);
644 if (p == (const PixelPacket *) NULL)
646 indexes=GetCacheViewVirtualIndexQueue(image_view);
649 for (v=0; v < (long) (width-i); v++)
651 for (u=0; u < (long) (width-i); u++)
654 if (((channel & OpacityChannel) != 0) &&
655 (image->matte != MagickFalse))
656 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
657 GetOpacityPixelComponent(p)));
658 if ((channel & RedChannel) != 0)
659 pixel.red+=(*k)*alpha*GetRedPixelComponent(p);
660 if ((channel & GreenChannel) != 0)
661 pixel.green+=(*k)*alpha*GetGreenPixelComponent(p);
662 if ((channel & BlueChannel) != 0)
663 pixel.blue+=(*k)*alpha*GetBluePixelComponent(p);
664 if ((channel & OpacityChannel) != 0)
665 pixel.opacity+=(*k)*GetOpacityPixelComponent(p);
666 if (((channel & IndexChannel) != 0) &&
667 (image->colorspace == CMYKColorspace))
668 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
674 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
675 if ((channel & RedChannel) != 0)
676 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
677 if ((channel & GreenChannel) != 0)
678 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
679 if ((channel & BlueChannel) != 0)
680 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
681 if ((channel & OpacityChannel) != 0)
682 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
683 if (((channel & IndexChannel) != 0) &&
684 (image->colorspace == CMYKColorspace))
685 sharp_indexes[x]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
689 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
691 if (image->progress_monitor != (MagickProgressMonitor) NULL)
696 #if defined(MAGICKCORE_OPENMP_SUPPORT)
697 #pragma omp critical (MagickCore_AdaptiveSharpenImageChannel)
699 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
701 if (proceed == MagickFalse)
705 sharp_image->type=image->type;
706 sharp_view=DestroyCacheView(sharp_view);
707 edge_view=DestroyCacheView(edge_view);
708 image_view=DestroyCacheView(image_view);
709 edge_image=DestroyImage(edge_image);
710 for (i=0; i < (long) width; i+=2)
711 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
712 kernel=(double **) RelinquishMagickMemory(kernel);
713 if (status == MagickFalse)
714 sharp_image=DestroyImage(sharp_image);
719 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
723 % B l u r I m a g e %
727 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
729 % BlurImage() blurs an image. We convolve the image with a Gaussian operator
730 % of the given radius and standard deviation (sigma). For reasonable results,
731 % the radius should be larger than sigma. Use a radius of 0 and BlurImage()
732 % selects a suitable radius for you.
734 % BlurImage() differs from GaussianBlurImage() in that it uses a separable
735 % kernel which is faster but mathematically equivalent to the non-separable
738 % The format of the BlurImage method is:
740 % Image *BlurImage(const Image *image,const double radius,
741 % const double sigma,ExceptionInfo *exception)
742 % Image *BlurImageChannel(const Image *image,const ChannelType channel,
743 % const double radius,const double sigma,ExceptionInfo *exception)
745 % A description of each parameter follows:
747 % o image: the image.
749 % o channel: the channel type.
751 % o radius: the radius of the Gaussian, in pixels, not counting the center
754 % o sigma: the standard deviation of the Gaussian, in pixels.
756 % o exception: return any errors or warnings in this structure.
760 MagickExport Image *BlurImage(const Image *image,const double radius,
761 const double sigma,ExceptionInfo *exception)
766 blur_image=BlurImageChannel(image,DefaultChannels,radius,sigma,exception);
770 static double *GetBlurKernel(const unsigned long width,const double sigma)
784 Generate a 1-D convolution kernel.
786 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
787 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
788 if (kernel == (double *) NULL)
793 for (k=(-j); k <= j; k++)
795 kernel[i]=exp(-((double) k*k)/(2.0*MagickSigma*MagickSigma))/
796 (MagickSQ2PI*MagickSigma);
797 normalize+=kernel[i];
800 for (i=0; i < (long) width; i++)
801 kernel[i]/=normalize;
805 MagickExport Image *BlurImageChannel(const Image *image,
806 const ChannelType channel,const double radius,const double sigma,
807 ExceptionInfo *exception)
809 #define BlurImageTag "Blur/Image"
839 Initialize blur image attributes.
841 assert(image != (Image *) NULL);
842 assert(image->signature == MagickSignature);
843 if (image->debug != MagickFalse)
844 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
845 assert(exception != (ExceptionInfo *) NULL);
846 assert(exception->signature == MagickSignature);
847 blur_image=CloneImage(image,0,0,MagickTrue,exception);
848 if (blur_image == (Image *) NULL)
849 return((Image *) NULL);
850 if (fabs(sigma) <= MagickEpsilon)
852 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
854 InheritException(exception,&blur_image->exception);
855 blur_image=DestroyImage(blur_image);
856 return((Image *) NULL);
858 width=GetOptimalKernelWidth1D(radius,sigma);
859 kernel=GetBlurKernel(width,sigma);
860 if (kernel == (double *) NULL)
862 blur_image=DestroyImage(blur_image);
863 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
865 if (image->debug != MagickFalse)
868 format[MaxTextExtent],
871 register const double
874 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
875 " BlurImage with %ld kernel:",width);
876 message=AcquireString("");
878 for (i=0; i < (long) width; i++)
881 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",i);
882 (void) ConcatenateString(&message,format);
883 (void) FormatMagickString(format,MaxTextExtent,"%.15g ",*k++);
884 (void) ConcatenateString(&message,format);
885 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
887 message=DestroyString(message);
894 GetMagickPixelPacket(image,&bias);
895 SetMagickPixelPacketBias(image,&bias);
896 image_view=AcquireCacheView(image);
897 blur_view=AcquireCacheView(blur_image);
898 #if defined(MAGICKCORE_OPENMP_SUPPORT)
899 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
901 for (y=0; y < (long) blur_image->rows; y++)
903 register const IndexPacket
906 register const PixelPacket
910 *restrict blur_indexes;
918 if (status == MagickFalse)
920 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y,image->columns+
922 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
924 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
929 indexes=GetCacheViewVirtualIndexQueue(image_view);
930 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
931 for (x=0; x < (long) blur_image->columns; x++)
936 register const double
939 register const PixelPacket
940 *restrict kernel_pixels;
948 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
950 for (i=0; i < (long) width; i++)
952 pixel.red+=(*k)*kernel_pixels->red;
953 pixel.green+=(*k)*kernel_pixels->green;
954 pixel.blue+=(*k)*kernel_pixels->blue;
958 if ((channel & RedChannel) != 0)
959 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
960 if ((channel & GreenChannel) != 0)
961 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
962 if ((channel & BlueChannel) != 0)
963 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
964 if ((channel & OpacityChannel) != 0)
968 for (i=0; i < (long) width; i++)
970 pixel.opacity+=(*k)*kernel_pixels->opacity;
974 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
976 if (((channel & IndexChannel) != 0) &&
977 (image->colorspace == CMYKColorspace))
979 register const IndexPacket
980 *restrict kernel_indexes;
983 kernel_indexes=indexes;
984 for (i=0; i < (long) width; i++)
986 pixel.index+=(*k)*(*kernel_indexes);
990 blur_indexes[x]=ClampToQuantum(pixel.index);
1000 for (i=0; i < (long) width; i++)
1002 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1003 kernel_pixels->opacity));
1004 pixel.red+=(*k)*alpha*kernel_pixels->red;
1005 pixel.green+=(*k)*alpha*kernel_pixels->green;
1006 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
1011 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1012 if ((channel & RedChannel) != 0)
1013 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
1014 if ((channel & GreenChannel) != 0)
1015 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
1016 if ((channel & BlueChannel) != 0)
1017 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
1018 if ((channel & OpacityChannel) != 0)
1022 for (i=0; i < (long) width; i++)
1024 pixel.opacity+=(*k)*kernel_pixels->opacity;
1028 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1030 if (((channel & IndexChannel) != 0) &&
1031 (image->colorspace == CMYKColorspace))
1033 register const IndexPacket
1034 *restrict kernel_indexes;
1038 kernel_indexes=indexes;
1039 for (i=0; i < (long) width; i++)
1041 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1042 kernel_pixels->opacity));
1043 pixel.index+=(*k)*alpha*(*kernel_indexes);
1048 blur_indexes[x]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
1054 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1056 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1061 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1062 #pragma omp critical (MagickCore_BlurImageChannel)
1064 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1065 blur_image->columns);
1066 if (proceed == MagickFalse)
1070 blur_view=DestroyCacheView(blur_view);
1071 image_view=DestroyCacheView(image_view);
1075 image_view=AcquireCacheView(blur_image);
1076 blur_view=AcquireCacheView(blur_image);
1077 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1078 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1080 for (x=0; x < (long) blur_image->columns; x++)
1082 register const IndexPacket
1085 register const PixelPacket
1088 register IndexPacket
1089 *restrict blur_indexes;
1094 register PixelPacket
1097 if (status == MagickFalse)
1099 p=GetCacheViewVirtualPixels(image_view,x,-((long) width/2L),1,image->rows+
1101 q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
1102 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1107 indexes=GetCacheViewVirtualIndexQueue(image_view);
1108 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
1109 for (y=0; y < (long) blur_image->rows; y++)
1114 register const double
1117 register const PixelPacket
1118 *restrict kernel_pixels;
1126 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
1128 for (i=0; i < (long) width; i++)
1130 pixel.red+=(*k)*kernel_pixels->red;
1131 pixel.green+=(*k)*kernel_pixels->green;
1132 pixel.blue+=(*k)*kernel_pixels->blue;
1136 if ((channel & RedChannel) != 0)
1137 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
1138 if ((channel & GreenChannel) != 0)
1139 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
1140 if ((channel & BlueChannel) != 0)
1141 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
1142 if ((channel & OpacityChannel) != 0)
1146 for (i=0; i < (long) width; i++)
1148 pixel.opacity+=(*k)*kernel_pixels->opacity;
1152 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1154 if (((channel & IndexChannel) != 0) &&
1155 (image->colorspace == CMYKColorspace))
1157 register const IndexPacket
1158 *restrict kernel_indexes;
1161 kernel_indexes=indexes;
1162 for (i=0; i < (long) width; i++)
1164 pixel.index+=(*k)*(*kernel_indexes);
1168 blur_indexes[y]=ClampToQuantum(pixel.index);
1178 for (i=0; i < (long) width; i++)
1180 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1181 kernel_pixels->opacity));
1182 pixel.red+=(*k)*alpha*kernel_pixels->red;
1183 pixel.green+=(*k)*alpha*kernel_pixels->green;
1184 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
1189 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1190 if ((channel & RedChannel) != 0)
1191 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
1192 if ((channel & GreenChannel) != 0)
1193 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
1194 if ((channel & BlueChannel) != 0)
1195 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
1196 if ((channel & OpacityChannel) != 0)
1200 for (i=0; i < (long) width; i++)
1202 pixel.opacity+=(*k)*kernel_pixels->opacity;
1206 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1208 if (((channel & IndexChannel) != 0) &&
1209 (image->colorspace == CMYKColorspace))
1211 register const IndexPacket
1212 *restrict kernel_indexes;
1216 kernel_indexes=indexes;
1217 for (i=0; i < (long) width; i++)
1219 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1220 kernel_pixels->opacity));
1221 pixel.index+=(*k)*alpha*(*kernel_indexes);
1226 blur_indexes[y]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
1232 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1234 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1239 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1240 #pragma omp critical (MagickCore_BlurImageChannel)
1242 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1243 blur_image->columns);
1244 if (proceed == MagickFalse)
1248 blur_view=DestroyCacheView(blur_view);
1249 image_view=DestroyCacheView(image_view);
1250 kernel=(double *) RelinquishMagickMemory(kernel);
1251 if (status == MagickFalse)
1252 blur_image=DestroyImage(blur_image);
1253 blur_image->type=image->type;
1258 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1262 % C o n v o l v e I m a g e %
1266 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1268 % ConvolveImage() applies a custom convolution kernel to the image.
1270 % The format of the ConvolveImage method is:
1272 % Image *ConvolveImage(const Image *image,const unsigned long order,
1273 % const double *kernel,ExceptionInfo *exception)
1274 % Image *ConvolveImageChannel(const Image *image,const ChannelType channel,
1275 % const unsigned long order,const double *kernel,
1276 % ExceptionInfo *exception)
1278 % A description of each parameter follows:
1280 % o image: the image.
1282 % o channel: the channel type.
1284 % o order: the number of columns and rows in the filter kernel.
1286 % o kernel: An array of double representing the convolution kernel.
1288 % o exception: return any errors or warnings in this structure.
1292 MagickExport Image *ConvolveImage(const Image *image,const unsigned long order,
1293 const double *kernel,ExceptionInfo *exception)
1298 convolve_image=ConvolveImageChannel(image,DefaultChannels,order,kernel,
1300 return(convolve_image);
1303 MagickExport Image *ConvolveImageChannel(const Image *image,
1304 const ChannelType channel,const unsigned long order,const double *kernel,
1305 ExceptionInfo *exception)
1307 #define ConvolveImageTag "Convolve/Image"
1339 Initialize convolve image attributes.
1341 assert(image != (Image *) NULL);
1342 assert(image->signature == MagickSignature);
1343 if (image->debug != MagickFalse)
1344 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1345 assert(exception != (ExceptionInfo *) NULL);
1346 assert(exception->signature == MagickSignature);
1348 if ((width % 2) == 0)
1349 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
1350 convolve_image=CloneImage(image,0,0,MagickTrue,exception);
1351 if (convolve_image == (Image *) NULL)
1352 return((Image *) NULL);
1353 if (SetImageStorageClass(convolve_image,DirectClass) == MagickFalse)
1355 InheritException(exception,&convolve_image->exception);
1356 convolve_image=DestroyImage(convolve_image);
1357 return((Image *) NULL);
1359 if (image->debug != MagickFalse)
1362 format[MaxTextExtent],
1369 register const double
1372 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1373 " ConvolveImage with %ldx%ld kernel:",width,width);
1374 message=AcquireString("");
1376 for (v=0; v < (long) width; v++)
1379 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
1380 (void) ConcatenateString(&message,format);
1381 for (u=0; u < (long) width; u++)
1383 (void) FormatMagickString(format,MaxTextExtent,"%.15g ",*k++);
1384 (void) ConcatenateString(&message,format);
1386 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
1388 message=DestroyString(message);
1393 normal_kernel=(double *) AcquireQuantumMemory(width*width,
1394 sizeof(*normal_kernel));
1395 if (normal_kernel == (double *) NULL)
1397 convolve_image=DestroyImage(convolve_image);
1398 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1401 for (i=0; i < (long) (width*width); i++)
1403 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1404 for (i=0; i < (long) (width*width); i++)
1405 normal_kernel[i]=gamma*kernel[i];
1411 GetMagickPixelPacket(image,&bias);
1412 SetMagickPixelPacketBias(image,&bias);
1413 image_view=AcquireCacheView(image);
1414 convolve_view=AcquireCacheView(convolve_image);
1415 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1416 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1418 for (y=0; y < (long) image->rows; y++)
1423 register const IndexPacket
1426 register const PixelPacket
1429 register IndexPacket
1430 *restrict convolve_indexes;
1435 register PixelPacket
1438 if (status == MagickFalse)
1440 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
1441 2L),image->columns+width,width,exception);
1442 q=GetCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
1444 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1449 indexes=GetCacheViewVirtualIndexQueue(image_view);
1450 convolve_indexes=GetCacheViewAuthenticIndexQueue(convolve_view);
1451 for (x=0; x < (long) image->columns; x++)
1459 register const double
1462 register const PixelPacket
1463 *restrict kernel_pixels;
1471 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
1473 for (v=0; v < (long) width; v++)
1475 for (u=0; u < (long) width; u++)
1477 pixel.red+=(*k)*kernel_pixels[u].red;
1478 pixel.green+=(*k)*kernel_pixels[u].green;
1479 pixel.blue+=(*k)*kernel_pixels[u].blue;
1482 kernel_pixels+=image->columns+width;
1484 if ((channel & RedChannel) != 0)
1485 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
1486 if ((channel & GreenChannel) != 0)
1487 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
1488 if ((channel & BlueChannel) != 0)
1489 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
1490 if ((channel & OpacityChannel) != 0)
1494 for (v=0; v < (long) width; v++)
1496 for (u=0; u < (long) width; u++)
1498 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1501 kernel_pixels+=image->columns+width;
1503 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1505 if (((channel & IndexChannel) != 0) &&
1506 (image->colorspace == CMYKColorspace))
1508 register const IndexPacket
1509 *restrict kernel_indexes;
1512 kernel_indexes=indexes;
1513 for (v=0; v < (long) width; v++)
1515 for (u=0; u < (long) width; u++)
1517 pixel.index+=(*k)*kernel_indexes[u];
1520 kernel_indexes+=image->columns+width;
1522 convolve_indexes[x]=ClampToQuantum(pixel.index);
1532 for (v=0; v < (long) width; v++)
1534 for (u=0; u < (long) width; u++)
1536 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1537 kernel_pixels[u].opacity));
1538 pixel.red+=(*k)*alpha*kernel_pixels[u].red;
1539 pixel.green+=(*k)*alpha*kernel_pixels[u].green;
1540 pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
1544 kernel_pixels+=image->columns+width;
1546 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1547 if ((channel & RedChannel) != 0)
1548 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
1549 if ((channel & GreenChannel) != 0)
1550 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
1551 if ((channel & BlueChannel) != 0)
1552 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
1553 if ((channel & OpacityChannel) != 0)
1557 for (v=0; v < (long) width; v++)
1559 for (u=0; u < (long) width; u++)
1561 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1564 kernel_pixels+=image->columns+width;
1566 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1568 if (((channel & IndexChannel) != 0) &&
1569 (image->colorspace == CMYKColorspace))
1571 register const IndexPacket
1572 *restrict kernel_indexes;
1576 kernel_indexes=indexes;
1577 for (v=0; v < (long) width; v++)
1579 for (u=0; u < (long) width; u++)
1581 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1582 kernel_pixels[u].opacity));
1583 pixel.index+=(*k)*alpha*kernel_indexes[u];
1586 kernel_pixels+=image->columns+width;
1587 kernel_indexes+=image->columns+width;
1589 convolve_indexes[x]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
1595 sync=SyncCacheViewAuthenticPixels(convolve_view,exception);
1596 if (sync == MagickFalse)
1598 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1603 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1604 #pragma omp critical (MagickCore_ConvolveImageChannel)
1606 proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1607 if (proceed == MagickFalse)
1611 convolve_image->type=image->type;
1612 convolve_view=DestroyCacheView(convolve_view);
1613 image_view=DestroyCacheView(image_view);
1614 normal_kernel=(double *) RelinquishMagickMemory(normal_kernel);
1615 if (status == MagickFalse)
1616 convolve_image=DestroyImage(convolve_image);
1617 return(convolve_image);
1621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1625 % D e s p e c k l e I m a g e %
1629 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1631 % DespeckleImage() reduces the speckle noise in an image while perserving the
1632 % edges of the original image.
1634 % The format of the DespeckleImage method is:
1636 % Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1638 % A description of each parameter follows:
1640 % o image: the image.
1642 % o exception: return any errors or warnings in this structure.
1646 static Quantum **DestroyPixelThreadSet(Quantum **pixels)
1651 assert(pixels != (Quantum **) NULL);
1652 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
1653 if (pixels[i] != (Quantum *) NULL)
1654 pixels[i]=(Quantum *) RelinquishMagickMemory(pixels[i]);
1655 pixels=(Quantum **) RelinquishAlignedMemory(pixels);
1659 static Quantum **AcquirePixelThreadSet(const size_t count)
1670 number_threads=GetOpenMPMaximumThreads();
1671 pixels=(Quantum **) AcquireAlignedMemory(number_threads,sizeof(*pixels));
1672 if (pixels == (Quantum **) NULL)
1673 return((Quantum **) NULL);
1674 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
1675 for (i=0; i < (long) number_threads; i++)
1677 pixels[i]=(Quantum *) AcquireQuantumMemory(count,sizeof(**pixels));
1678 if (pixels[i] == (Quantum *) NULL)
1679 return(DestroyPixelThreadSet(pixels));
1684 static void Hull(const long x_offset,const long y_offset,
1685 const unsigned long columns,const unsigned long rows,Quantum *f,Quantum *g,
1703 assert(f != (Quantum *) NULL);
1704 assert(g != (Quantum *) NULL);
1707 r=p+(y_offset*((long) columns+2)+x_offset);
1708 for (y=0; y < (long) rows; y++)
1714 for (x=(long) columns; x != 0; x--)
1716 v=(MagickRealType) (*p);
1717 if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1718 v+=ScaleCharToQuantum(1);
1725 for (x=(long) columns; x != 0; x--)
1727 v=(MagickRealType) (*p);
1728 if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
1729 v-=(long) ScaleCharToQuantum(1);
1741 r=q+(y_offset*((long) columns+2)+x_offset);
1742 s=q-(y_offset*((long) columns+2)+x_offset);
1743 for (y=0; y < (long) rows; y++)
1750 for (x=(long) columns; x != 0; x--)
1752 v=(MagickRealType) (*q);
1753 if (((MagickRealType) *s >=
1754 (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1755 ((MagickRealType) *r > v))
1756 v+=ScaleCharToQuantum(1);
1764 for (x=(long) columns; x != 0; x--)
1766 v=(MagickRealType) (*q);
1767 if (((MagickRealType) *s <=
1768 (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1769 ((MagickRealType) *r < v))
1770 v-=(MagickRealType) ScaleCharToQuantum(1);
1784 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1786 #define DespeckleImageTag "Despeckle/Image"
1809 X[4] = {0, 1, 1,-1},
1810 Y[4] = {1, 0, 1, 1};
1813 Allocate despeckled image.
1815 assert(image != (const Image *) NULL);
1816 assert(image->signature == MagickSignature);
1817 if (image->debug != MagickFalse)
1818 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1819 assert(exception != (ExceptionInfo *) NULL);
1820 assert(exception->signature == MagickSignature);
1821 despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1823 if (despeckle_image == (Image *) NULL)
1824 return((Image *) NULL);
1825 if (SetImageStorageClass(despeckle_image,DirectClass) == MagickFalse)
1827 InheritException(exception,&despeckle_image->exception);
1828 despeckle_image=DestroyImage(despeckle_image);
1829 return((Image *) NULL);
1832 Allocate image buffers.
1834 length=(size_t) ((image->columns+2)*(image->rows+2));
1835 pixels=AcquirePixelThreadSet(length);
1836 buffers=AcquirePixelThreadSet(length);
1837 if ((pixels == (Quantum **) NULL) || (buffers == (Quantum **) NULL))
1839 if (buffers != (Quantum **) NULL)
1840 buffers=DestroyPixelThreadSet(buffers);
1841 if (pixels != (Quantum **) NULL)
1842 pixels=DestroyPixelThreadSet(pixels);
1843 despeckle_image=DestroyImage(despeckle_image);
1844 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1847 Reduce speckle in the image.
1850 image_view=AcquireCacheView(image);
1851 despeckle_view=AcquireCacheView(despeckle_image);
1852 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1853 #pragma omp parallel for schedule(dynamic,4) shared(status)
1855 for (channel=0; channel <= 3; channel++)
1870 if (status == MagickFalse)
1872 id=GetOpenMPThreadId();
1874 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
1876 j=(long) image->columns+2;
1877 for (y=0; y < (long) image->rows; y++)
1879 register const PixelPacket
1882 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1883 if (p == (const PixelPacket *) NULL)
1886 for (x=0; x < (long) image->columns; x++)
1890 case 0: pixel[j]=GetRedPixelComponent(p); break;
1891 case 1: pixel[j]=GetGreenPixelComponent(p); break;
1892 case 2: pixel[j]=GetBluePixelComponent(p); break;
1893 case 3: pixel[j]=GetOpacityPixelComponent(p); break;
1901 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1902 for (i=0; i < 4; i++)
1904 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,1);
1905 Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,1);
1906 Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,-1);
1907 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,-1);
1909 j=(long) image->columns+2;
1910 for (y=0; y < (long) image->rows; y++)
1915 register PixelPacket
1918 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1920 if (q == (PixelPacket *) NULL)
1923 for (x=0; x < (long) image->columns; x++)
1927 case 0: q->red=pixel[j]; break;
1928 case 1: q->green=pixel[j]; break;
1929 case 2: q->blue=pixel[j]; break;
1930 case 3: q->opacity=pixel[j]; break;
1936 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1937 if (sync == MagickFalse)
1944 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1949 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1950 #pragma omp critical (MagickCore_DespeckleImage)
1952 proceed=SetImageProgress(image,DespeckleImageTag,channel,3);
1953 if (proceed == MagickFalse)
1957 despeckle_view=DestroyCacheView(despeckle_view);
1958 image_view=DestroyCacheView(image_view);
1959 buffers=DestroyPixelThreadSet(buffers);
1960 pixels=DestroyPixelThreadSet(pixels);
1961 despeckle_image->type=image->type;
1962 if (status == MagickFalse)
1963 despeckle_image=DestroyImage(despeckle_image);
1964 return(despeckle_image);
1968 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1972 % E d g e I m a g e %
1976 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1978 % EdgeImage() finds edges in an image. Radius defines the radius of the
1979 % convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1982 % The format of the EdgeImage method is:
1984 % Image *EdgeImage(const Image *image,const double radius,
1985 % ExceptionInfo *exception)
1987 % A description of each parameter follows:
1989 % o image: the image.
1991 % o radius: the radius of the pixel neighborhood.
1993 % o exception: return any errors or warnings in this structure.
1996 MagickExport Image *EdgeImage(const Image *image,const double radius,
1997 ExceptionInfo *exception)
2011 assert(image != (const Image *) NULL);
2012 assert(image->signature == MagickSignature);
2013 if (image->debug != MagickFalse)
2014 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2015 assert(exception != (ExceptionInfo *) NULL);
2016 assert(exception->signature == MagickSignature);
2017 width=GetOptimalKernelWidth1D(radius,0.5);
2018 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2019 if (kernel == (double *) NULL)
2020 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2021 for (i=0; i < (long) (width*width); i++)
2023 kernel[i/2]=(double) (width*width-1.0);
2024 edge_image=ConvolveImage(image,width,kernel,exception);
2025 kernel=(double *) RelinquishMagickMemory(kernel);
2030 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2034 % E m b o s s I m a g e %
2038 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2040 % EmbossImage() returns a grayscale image with a three-dimensional effect.
2041 % We convolve the image with a Gaussian operator of the given radius and
2042 % standard deviation (sigma). For reasonable results, radius should be
2043 % larger than sigma. Use a radius of 0 and Emboss() selects a suitable
2046 % The format of the EmbossImage method is:
2048 % Image *EmbossImage(const Image *image,const double radius,
2049 % const double sigma,ExceptionInfo *exception)
2051 % A description of each parameter follows:
2053 % o image: the image.
2055 % o radius: the radius of the pixel neighborhood.
2057 % o sigma: the standard deviation of the Gaussian, in pixels.
2059 % o exception: return any errors or warnings in this structure.
2062 MagickExport Image *EmbossImage(const Image *image,const double radius,
2063 const double sigma,ExceptionInfo *exception)
2083 assert(image != (Image *) NULL);
2084 assert(image->signature == MagickSignature);
2085 if (image->debug != MagickFalse)
2086 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2087 assert(exception != (ExceptionInfo *) NULL);
2088 assert(exception->signature == MagickSignature);
2089 width=GetOptimalKernelWidth2D(radius,sigma);
2090 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2091 if (kernel == (double *) NULL)
2092 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2096 for (v=(-j); v <= j; v++)
2098 for (u=(-j); u <= j; u++)
2100 kernel[i]=((u < 0) || (v < 0) ? -8.0 : 8.0)*
2101 exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
2102 (2.0*MagickPI*MagickSigma*MagickSigma);
2109 emboss_image=ConvolveImage(image,width,kernel,exception);
2110 if (emboss_image != (Image *) NULL)
2111 (void) EqualizeImage(emboss_image);
2112 kernel=(double *) RelinquishMagickMemory(kernel);
2113 return(emboss_image);
2117 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2121 % F i l t e r I m a g e %
2125 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2127 % FilterImage() applies a custom convolution kernel to the image.
2129 % The format of the FilterImage method is:
2131 % Image *FilterImage(const Image *image,const MagickKernel *kernel,
2132 % ExceptionInfo *exception)
2133 % Image *FilterImageChannel(const Image *image,const ChannelType channel,
2134 % const MagickKernel *kernel,ExceptionInfo *exception)
2136 % A description of each parameter follows:
2138 % o image: the image.
2140 % o channel: the channel type.
2142 % o kernel: the filtering kernel.
2144 % o exception: return any errors or warnings in this structure.
2148 MagickExport Image *FilterImage(const Image *image,const MagickKernel *kernel,
2149 ExceptionInfo *exception)
2154 filter_image=FilterImageChannel(image,DefaultChannels,kernel,exception);
2155 return(filter_image);
2158 MagickExport Image *FilterImageChannel(const Image *image,
2159 const ChannelType channel,const MagickKernel *kernel,ExceptionInfo *exception)
2161 #define FilterImageTag "Filter/Image"
2190 Initialize filter image attributes.
2192 assert(image != (Image *) NULL);
2193 assert(image->signature == MagickSignature);
2194 if (image->debug != MagickFalse)
2195 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2196 assert(exception != (ExceptionInfo *) NULL);
2197 assert(exception->signature == MagickSignature);
2198 if ((kernel->width % 2) == 0)
2199 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
2200 filter_image=CloneImage(image,0,0,MagickTrue,exception);
2201 if (filter_image == (Image *) NULL)
2202 return((Image *) NULL);
2203 if (SetImageStorageClass(filter_image,DirectClass) == MagickFalse)
2205 InheritException(exception,&filter_image->exception);
2206 filter_image=DestroyImage(filter_image);
2207 return((Image *) NULL);
2209 if (image->debug != MagickFalse)
2212 format[MaxTextExtent],
2219 register const double
2222 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2223 " FilterImage with %ldx%ld kernel:",kernel->width,kernel->height);
2224 message=AcquireString("");
2226 for (v=0; v < (long) kernel->height; v++)
2229 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
2230 (void) ConcatenateString(&message,format);
2231 for (u=0; u < (long) kernel->width; u++)
2233 (void) FormatMagickString(format,MaxTextExtent,"%.15g ",*k++);
2234 (void) ConcatenateString(&message,format);
2236 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
2238 message=DestroyString(message);
2243 normal_kernel=(double *) AcquireQuantumMemory(kernel->width*kernel->height,
2244 sizeof(*normal_kernel));
2245 if (normal_kernel == (double *) NULL)
2247 filter_image=DestroyImage(filter_image);
2248 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2251 for (i=0; i < (long) (kernel->width*kernel->height); i++)
2252 gamma+=kernel->values[i];
2253 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2254 for (i=0; i < (long) (kernel->width*kernel->height); i++)
2255 normal_kernel[i]=gamma*kernel->values[i];
2261 GetMagickPixelPacket(image,&bias);
2262 SetMagickPixelPacketBias(image,&bias);
2263 image_view=AcquireCacheView(image);
2264 filter_view=AcquireCacheView(filter_image);
2265 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2266 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2268 for (y=0; y < (long) image->rows; y++)
2273 register const IndexPacket
2276 register const PixelPacket
2279 register IndexPacket
2280 *restrict filter_indexes;
2285 register PixelPacket
2288 if (status == MagickFalse)
2290 p=GetCacheViewVirtualPixels(image_view,-((long) kernel->width/2L),
2291 y-(long) (kernel->height/2L),image->columns+kernel->width,kernel->height,
2293 q=GetCacheViewAuthenticPixels(filter_view,0,y,filter_image->columns,1,
2295 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2300 indexes=GetCacheViewVirtualIndexQueue(image_view);
2301 filter_indexes=GetCacheViewAuthenticIndexQueue(filter_view);
2302 for (x=0; x < (long) image->columns; x++)
2310 register const double
2313 register const PixelPacket
2314 *restrict kernel_pixels;
2322 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
2324 for (v=0; v < (long) kernel->width; v++)
2326 for (u=0; u < (long) kernel->height; u++)
2328 pixel.red+=(*k)*kernel_pixels[u].red;
2329 pixel.green+=(*k)*kernel_pixels[u].green;
2330 pixel.blue+=(*k)*kernel_pixels[u].blue;
2333 kernel_pixels+=image->columns+kernel->width;
2335 if ((channel & RedChannel) != 0)
2336 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2337 if ((channel & GreenChannel) != 0)
2338 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2339 if ((channel & BlueChannel) != 0)
2340 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2341 if ((channel & OpacityChannel) != 0)
2345 for (v=0; v < (long) kernel->width; v++)
2347 for (u=0; u < (long) kernel->height; u++)
2349 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
2352 kernel_pixels+=image->columns+kernel->width;
2354 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2356 if (((channel & IndexChannel) != 0) &&
2357 (image->colorspace == CMYKColorspace))
2359 register const IndexPacket
2360 *restrict kernel_indexes;
2363 kernel_indexes=indexes;
2364 for (v=0; v < (long) kernel->width; v++)
2366 for (u=0; u < (long) kernel->height; u++)
2368 pixel.index+=(*k)*kernel_indexes[u];
2371 kernel_indexes+=image->columns+kernel->width;
2373 filter_indexes[x]=ClampToQuantum(pixel.index);
2383 for (v=0; v < (long) kernel->width; v++)
2385 for (u=0; u < (long) kernel->height; u++)
2387 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
2388 kernel_pixels[u].opacity));
2389 pixel.red+=(*k)*alpha*kernel_pixels[u].red;
2390 pixel.green+=(*k)*alpha*kernel_pixels[u].green;
2391 pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
2395 kernel_pixels+=image->columns+kernel->width;
2397 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2398 if ((channel & RedChannel) != 0)
2399 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2400 if ((channel & GreenChannel) != 0)
2401 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2402 if ((channel & BlueChannel) != 0)
2403 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2404 if ((channel & OpacityChannel) != 0)
2408 for (v=0; v < (long) kernel->width; v++)
2410 for (u=0; u < (long) kernel->height; u++)
2412 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
2415 kernel_pixels+=image->columns+kernel->width;
2417 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2419 if (((channel & IndexChannel) != 0) &&
2420 (image->colorspace == CMYKColorspace))
2422 register const IndexPacket
2423 *restrict kernel_indexes;
2427 kernel_indexes=indexes;
2428 for (v=0; v < (long) kernel->width; v++)
2430 for (u=0; u < (long) kernel->height; u++)
2432 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
2433 kernel_pixels[u].opacity));
2434 pixel.index+=(*k)*alpha*kernel_indexes[u];
2437 kernel_pixels+=image->columns+kernel->width;
2438 kernel_indexes+=image->columns+kernel->width;
2440 filter_indexes[x]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
2446 sync=SyncCacheViewAuthenticPixels(filter_view,exception);
2447 if (sync == MagickFalse)
2449 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2454 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2455 #pragma omp critical (MagickCore_FilterImageChannel)
2457 proceed=SetImageProgress(image,FilterImageTag,progress++,image->rows);
2458 if (proceed == MagickFalse)
2462 filter_image->type=image->type;
2463 filter_view=DestroyCacheView(filter_view);
2464 image_view=DestroyCacheView(image_view);
2465 normal_kernel=(double *) RelinquishMagickMemory(normal_kernel);
2466 if (status == MagickFalse)
2467 filter_image=DestroyImage(filter_image);
2468 return(filter_image);
2472 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2476 % G a u s s i a n B l u r I m a g e %
2480 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2482 % GaussianBlurImage() blurs an image. We convolve the image with a
2483 % Gaussian operator of the given radius and standard deviation (sigma).
2484 % For reasonable results, the radius should be larger than sigma. Use a
2485 % radius of 0 and GaussianBlurImage() selects a suitable radius for you
2487 % The format of the GaussianBlurImage method is:
2489 % Image *GaussianBlurImage(const Image *image,onst double radius,
2490 % const double sigma,ExceptionInfo *exception)
2491 % Image *GaussianBlurImageChannel(const Image *image,
2492 % const ChannelType channel,const double radius,const double sigma,
2493 % ExceptionInfo *exception)
2495 % A description of each parameter follows:
2497 % o image: the image.
2499 % o channel: the channel type.
2501 % o radius: the radius of the Gaussian, in pixels, not counting the center
2504 % o sigma: the standard deviation of the Gaussian, in pixels.
2506 % o exception: return any errors or warnings in this structure.
2510 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
2511 const double sigma,ExceptionInfo *exception)
2516 blur_image=GaussianBlurImageChannel(image,DefaultChannels,radius,sigma,
2521 MagickExport Image *GaussianBlurImageChannel(const Image *image,
2522 const ChannelType channel,const double radius,const double sigma,
2523 ExceptionInfo *exception)
2542 assert(image != (const Image *) NULL);
2543 assert(image->signature == MagickSignature);
2544 if (image->debug != MagickFalse)
2545 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2546 assert(exception != (ExceptionInfo *) NULL);
2547 assert(exception->signature == MagickSignature);
2548 width=GetOptimalKernelWidth2D(radius,sigma);
2549 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2550 if (kernel == (double *) NULL)
2551 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2554 for (v=(-j); v <= j; v++)
2556 for (u=(-j); u <= j; u++)
2557 kernel[i++]=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
2558 (2.0*MagickPI*MagickSigma*MagickSigma);
2560 blur_image=ConvolveImageChannel(image,channel,width,kernel,exception);
2561 kernel=(double *) RelinquishMagickMemory(kernel);
2566 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2570 % M e d i a n F i l t e r I m a g e %
2574 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2576 % MedianFilterImage() applies a digital filter that improves the quality
2577 % of a noisy image. Each pixel is replaced by the median in a set of
2578 % neighboring pixels as defined by radius.
2580 % The algorithm was contributed by Mike Edmonds and implements an insertion
2581 % sort for selecting median color-channel values. For more on this algorithm
2582 % see "Skip Lists: A probabilistic Alternative to Balanced Trees" by William
2583 % Pugh in the June 1990 of Communications of the ACM.
2585 % The format of the MedianFilterImage method is:
2587 % Image *MedianFilterImage(const Image *image,const double radius,
2588 % ExceptionInfo *exception)
2590 % A description of each parameter follows:
2592 % o image: the image.
2594 % o radius: the radius of the pixel neighborhood.
2596 % o exception: return any errors or warnings in this structure.
2600 #define MedianListChannels 5
2602 typedef struct _MedianListNode
2610 typedef struct _MedianSkipList
2619 typedef struct _MedianPixelList
2627 lists[MedianListChannels];
2630 static MedianPixelList *DestroyMedianPixelList(MedianPixelList *pixel_list)
2635 if (pixel_list == (MedianPixelList *) NULL)
2636 return((MedianPixelList *) NULL);
2637 for (i=0; i < MedianListChannels; i++)
2638 if (pixel_list->lists[i].nodes != (MedianListNode *) NULL)
2639 pixel_list->lists[i].nodes=(MedianListNode *) RelinquishMagickMemory(
2640 pixel_list->lists[i].nodes);
2641 pixel_list=(MedianPixelList *) RelinquishAlignedMemory(pixel_list);
2645 static MedianPixelList **DestroyMedianPixelListThreadSet(
2646 MedianPixelList **pixel_list)
2651 assert(pixel_list != (MedianPixelList **) NULL);
2652 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
2653 if (pixel_list[i] != (MedianPixelList *) NULL)
2654 pixel_list[i]=DestroyMedianPixelList(pixel_list[i]);
2655 pixel_list=(MedianPixelList **) RelinquishAlignedMemory(pixel_list);
2659 static MedianPixelList *AcquireMedianPixelList(const unsigned long width)
2667 pixel_list=(MedianPixelList *) AcquireAlignedMemory(1,sizeof(*pixel_list));
2668 if (pixel_list == (MedianPixelList *) NULL)
2670 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
2671 pixel_list->center=width*width/2;
2672 for (i=0; i < MedianListChannels; i++)
2674 pixel_list->lists[i].nodes=(MedianListNode *) AcquireQuantumMemory(65537UL,
2675 sizeof(*pixel_list->lists[i].nodes));
2676 if (pixel_list->lists[i].nodes == (MedianListNode *) NULL)
2677 return(DestroyMedianPixelList(pixel_list));
2678 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
2679 sizeof(*pixel_list->lists[i].nodes));
2681 pixel_list->signature=MagickSignature;
2685 static MedianPixelList **AcquireMedianPixelListThreadSet(
2686 const unsigned long width)
2697 number_threads=GetOpenMPMaximumThreads();
2698 pixel_list=(MedianPixelList **) AcquireAlignedMemory(number_threads,
2699 sizeof(*pixel_list));
2700 if (pixel_list == (MedianPixelList **) NULL)
2701 return((MedianPixelList **) NULL);
2702 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
2703 for (i=0; i < (long) number_threads; i++)
2705 pixel_list[i]=AcquireMedianPixelList(width);
2706 if (pixel_list[i] == (MedianPixelList *) NULL)
2707 return(DestroyMedianPixelListThreadSet(pixel_list));
2712 static void AddNodeMedianPixelList(MedianPixelList *pixel_list,
2713 const long channel,const unsigned long color)
2718 register MedianSkipList
2726 Initialize the node.
2728 list=pixel_list->lists+channel;
2729 list->nodes[color].signature=pixel_list->signature;
2730 list->nodes[color].count=1;
2732 Determine where it belongs in the list.
2735 for (level=list->level; level >= 0; level--)
2737 while (list->nodes[search].next[level] < color)
2738 search=list->nodes[search].next[level];
2739 update[level]=search;
2742 Generate a pseudo-random level for this node.
2744 for (level=0; ; level++)
2746 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
2747 if ((pixel_list->seed & 0x300) != 0x300)
2752 if (level > (list->level+2))
2753 level=list->level+2;
2755 If we're raising the list's level, link back to the root node.
2757 while (level > list->level)
2760 update[list->level]=65536UL;
2763 Link the node into the skip-list.
2767 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
2768 list->nodes[update[level]].next[level]=color;
2770 while (level-- > 0);
2773 static MagickPixelPacket GetMedianPixelList(MedianPixelList *pixel_list)
2781 register MedianSkipList
2790 channels[MedianListChannels];
2793 Find the median value for each of the color.
2795 center=pixel_list->center;
2796 for (channel=0; channel < 5; channel++)
2798 list=pixel_list->lists+channel;
2803 color=list->nodes[color].next[0];
2804 count+=list->nodes[color].count;
2806 while (count <= center);
2807 channels[channel]=(unsigned short) color;
2809 GetMagickPixelPacket((const Image *) NULL,&pixel);
2810 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
2811 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
2812 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
2813 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
2814 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
2818 static inline void InsertMedianPixelList(const Image *image,
2819 const PixelPacket *pixel,const IndexPacket *indexes,
2820 MedianPixelList *pixel_list)
2828 index=ScaleQuantumToShort(pixel->red);
2829 signature=pixel_list->lists[0].nodes[index].signature;
2830 if (signature == pixel_list->signature)
2831 pixel_list->lists[0].nodes[index].count++;
2833 AddNodeMedianPixelList(pixel_list,0,index);
2834 index=ScaleQuantumToShort(pixel->green);
2835 signature=pixel_list->lists[1].nodes[index].signature;
2836 if (signature == pixel_list->signature)
2837 pixel_list->lists[1].nodes[index].count++;
2839 AddNodeMedianPixelList(pixel_list,1,index);
2840 index=ScaleQuantumToShort(pixel->blue);
2841 signature=pixel_list->lists[2].nodes[index].signature;
2842 if (signature == pixel_list->signature)
2843 pixel_list->lists[2].nodes[index].count++;
2845 AddNodeMedianPixelList(pixel_list,2,index);
2846 index=ScaleQuantumToShort(pixel->opacity);
2847 signature=pixel_list->lists[3].nodes[index].signature;
2848 if (signature == pixel_list->signature)
2849 pixel_list->lists[3].nodes[index].count++;
2851 AddNodeMedianPixelList(pixel_list,3,index);
2852 if (image->colorspace == CMYKColorspace)
2853 index=ScaleQuantumToShort(*indexes);
2854 signature=pixel_list->lists[4].nodes[index].signature;
2855 if (signature == pixel_list->signature)
2856 pixel_list->lists[4].nodes[index].count++;
2858 AddNodeMedianPixelList(pixel_list,4,index);
2861 static void ResetMedianPixelList(MedianPixelList *pixel_list)
2869 register MedianListNode
2872 register MedianSkipList
2876 Reset the skip-list.
2878 for (channel=0; channel < 5; channel++)
2880 list=pixel_list->lists+channel;
2881 root=list->nodes+65536UL;
2883 for (level=0; level < 9; level++)
2884 root->next[level]=65536UL;
2886 pixel_list->seed=pixel_list->signature++;
2889 MagickExport Image *MedianFilterImage(const Image *image,const double radius,
2890 ExceptionInfo *exception)
2892 #define MedianFilterImageTag "MedianFilter/Image"
2909 **restrict pixel_list;
2915 Initialize median image attributes.
2917 assert(image != (Image *) NULL);
2918 assert(image->signature == MagickSignature);
2919 if (image->debug != MagickFalse)
2920 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2921 assert(exception != (ExceptionInfo *) NULL);
2922 assert(exception->signature == MagickSignature);
2923 width=GetOptimalKernelWidth2D(radius,0.5);
2924 if ((image->columns < width) || (image->rows < width))
2925 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
2926 median_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2928 if (median_image == (Image *) NULL)
2929 return((Image *) NULL);
2930 if (SetImageStorageClass(median_image,DirectClass) == MagickFalse)
2932 InheritException(exception,&median_image->exception);
2933 median_image=DestroyImage(median_image);
2934 return((Image *) NULL);
2936 pixel_list=AcquireMedianPixelListThreadSet(width);
2937 if (pixel_list == (MedianPixelList **) NULL)
2939 median_image=DestroyImage(median_image);
2940 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2943 Median filter each image row.
2947 image_view=AcquireCacheView(image);
2948 median_view=AcquireCacheView(median_image);
2949 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2950 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2952 for (y=0; y < (long) median_image->rows; y++)
2954 register const IndexPacket
2957 register const PixelPacket
2960 register IndexPacket
2961 *restrict median_indexes;
2967 register PixelPacket
2970 if (status == MagickFalse)
2972 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
2973 2L),image->columns+width,width,exception);
2974 q=QueueCacheViewAuthenticPixels(median_view,0,y,median_image->columns,1,
2976 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2981 indexes=GetCacheViewVirtualIndexQueue(image_view);
2982 median_indexes=GetCacheViewAuthenticIndexQueue(median_view);
2983 id=GetOpenMPThreadId();
2984 for (x=0; x < (long) median_image->columns; x++)
2989 register const PixelPacket
2992 register const IndexPacket
3001 ResetMedianPixelList(pixel_list[id]);
3002 for (v=0; v < (long) width; v++)
3004 for (u=0; u < (long) width; u++)
3005 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
3006 r+=image->columns+width;
3007 s+=image->columns+width;
3009 pixel=GetMedianPixelList(pixel_list[id]);
3010 SetPixelPacket(median_image,&pixel,q,median_indexes+x);
3014 if (SyncCacheViewAuthenticPixels(median_view,exception) == MagickFalse)
3016 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3021 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3022 #pragma omp critical (MagickCore_MedianFilterImage)
3024 proceed=SetImageProgress(image,MedianFilterImageTag,progress++,
3026 if (proceed == MagickFalse)
3030 median_view=DestroyCacheView(median_view);
3031 image_view=DestroyCacheView(image_view);
3032 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
3033 return(median_image);
3037 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3041 % M o t i o n B l u r I m a g e %
3045 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3047 % MotionBlurImage() simulates motion blur. We convolve the image with a
3048 % Gaussian operator of the given radius and standard deviation (sigma).
3049 % For reasonable results, radius should be larger than sigma. Use a
3050 % radius of 0 and MotionBlurImage() selects a suitable radius for you.
3051 % Angle gives the angle of the blurring motion.
3053 % Andrew Protano contributed this effect.
3055 % The format of the MotionBlurImage method is:
3057 % Image *MotionBlurImage(const Image *image,const double radius,
3058 % const double sigma,const double angle,ExceptionInfo *exception)
3059 % Image *MotionBlurImageChannel(const Image *image,const ChannelType channel,
3060 % const double radius,const double sigma,const double angle,
3061 % ExceptionInfo *exception)
3063 % A description of each parameter follows:
3065 % o image: the image.
3067 % o channel: the channel type.
3069 % o radius: the radius of the Gaussian, in pixels, not counting the center
3070 % o radius: the radius of the Gaussian, in pixels, not counting
3073 % o sigma: the standard deviation of the Gaussian, in pixels.
3075 % o angle: Apply the effect along this angle.
3077 % o exception: return any errors or warnings in this structure.
3081 static double *GetMotionBlurKernel(const unsigned long width,const double sigma)
3091 Generate a 1-D convolution kernel.
3093 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3094 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
3095 if (kernel == (double *) NULL)
3098 for (i=0; i < (long) width; i++)
3100 kernel[i]=exp((-((double) i*i)/(double) (2.0*MagickSigma*MagickSigma)))/
3101 (MagickSQ2PI*MagickSigma);
3102 normalize+=kernel[i];
3104 for (i=0; i < (long) width; i++)
3105 kernel[i]/=normalize;
3109 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
3110 const double sigma,const double angle,ExceptionInfo *exception)
3115 motion_blur=MotionBlurImageChannel(image,DefaultChannels,radius,sigma,angle,
3117 return(motion_blur);
3120 MagickExport Image *MotionBlurImageChannel(const Image *image,
3121 const ChannelType channel,const double radius,const double sigma,
3122 const double angle,ExceptionInfo *exception)
3124 typedef struct _OffsetInfo
3163 assert(image != (Image *) NULL);
3164 assert(image->signature == MagickSignature);
3165 if (image->debug != MagickFalse)
3166 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3167 assert(exception != (ExceptionInfo *) NULL);
3168 width=GetOptimalKernelWidth1D(radius,sigma);
3169 kernel=GetMotionBlurKernel(width,sigma);
3170 if (kernel == (double *) NULL)
3171 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3172 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
3173 if (offset == (OffsetInfo *) NULL)
3175 kernel=(double *) RelinquishMagickMemory(kernel);
3176 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3178 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3179 if (blur_image == (Image *) NULL)
3181 kernel=(double *) RelinquishMagickMemory(kernel);
3182 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3183 return((Image *) NULL);
3185 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3187 kernel=(double *) RelinquishMagickMemory(kernel);
3188 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3189 InheritException(exception,&blur_image->exception);
3190 blur_image=DestroyImage(blur_image);
3191 return((Image *) NULL);
3193 point.x=(double) width*sin(DegreesToRadians(angle));
3194 point.y=(double) width*cos(DegreesToRadians(angle));
3195 for (i=0; i < (long) width; i++)
3197 offset[i].x=(long) ((i*point.y)/hypot(point.x,point.y)+0.5);
3198 offset[i].y=(long) ((i*point.x)/hypot(point.x,point.y)+0.5);
3205 GetMagickPixelPacket(image,&bias);
3206 image_view=AcquireCacheView(image);
3207 blur_view=AcquireCacheView(blur_image);
3208 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3209 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3211 for (y=0; y < (long) image->rows; y++)
3213 register IndexPacket
3214 *restrict blur_indexes;
3219 register PixelPacket
3222 if (status == MagickFalse)
3224 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3226 if (q == (PixelPacket *) NULL)
3231 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
3232 for (x=0; x < (long) image->columns; x++)
3246 register const IndexPacket
3251 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
3253 for (i=0; i < (long) width; i++)
3255 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
3256 offset[i].y,&pixel,exception);
3257 qixel.red+=(*k)*pixel.red;
3258 qixel.green+=(*k)*pixel.green;
3259 qixel.blue+=(*k)*pixel.blue;
3260 qixel.opacity+=(*k)*pixel.opacity;
3261 if (image->colorspace == CMYKColorspace)
3263 indexes=GetCacheViewVirtualIndexQueue(image_view);
3264 qixel.index+=(*k)*(*indexes);
3268 if ((channel & RedChannel) != 0)
3269 q->red=ClampToQuantum(qixel.red);
3270 if ((channel & GreenChannel) != 0)
3271 q->green=ClampToQuantum(qixel.green);
3272 if ((channel & BlueChannel) != 0)
3273 q->blue=ClampToQuantum(qixel.blue);
3274 if ((channel & OpacityChannel) != 0)
3275 q->opacity=ClampToQuantum(qixel.opacity);
3276 if (((channel & IndexChannel) != 0) &&
3277 (image->colorspace == CMYKColorspace))
3278 blur_indexes[x]=(IndexPacket) ClampToQuantum(qixel.index);
3288 for (i=0; i < (long) width; i++)
3290 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
3291 offset[i].y,&pixel,exception);
3292 alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
3293 qixel.red+=(*k)*alpha*pixel.red;
3294 qixel.green+=(*k)*alpha*pixel.green;
3295 qixel.blue+=(*k)*alpha*pixel.blue;
3296 qixel.opacity+=(*k)*pixel.opacity;
3297 if (image->colorspace == CMYKColorspace)
3299 indexes=GetCacheViewVirtualIndexQueue(image_view);
3300 qixel.index+=(*k)*alpha*(*indexes);
3305 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3306 if ((channel & RedChannel) != 0)
3307 q->red=ClampToQuantum(gamma*qixel.red);
3308 if ((channel & GreenChannel) != 0)
3309 q->green=ClampToQuantum(gamma*qixel.green);
3310 if ((channel & BlueChannel) != 0)
3311 q->blue=ClampToQuantum(gamma*qixel.blue);
3312 if ((channel & OpacityChannel) != 0)
3313 q->opacity=ClampToQuantum(qixel.opacity);
3314 if (((channel & IndexChannel) != 0) &&
3315 (image->colorspace == CMYKColorspace))
3316 blur_indexes[x]=(IndexPacket) ClampToQuantum(gamma*qixel.index);
3320 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3322 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3327 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3328 #pragma omp critical (MagickCore_MotionBlurImageChannel)
3330 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3331 if (proceed == MagickFalse)
3335 blur_view=DestroyCacheView(blur_view);
3336 image_view=DestroyCacheView(image_view);
3337 kernel=(double *) RelinquishMagickMemory(kernel);
3338 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3339 if (status == MagickFalse)
3340 blur_image=DestroyImage(blur_image);
3345 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3349 % P r e v i e w I m a g e %
3353 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3355 % PreviewImage() tiles 9 thumbnails of the specified image with an image
3356 % processing operation applied with varying parameters. This may be helpful
3357 % pin-pointing an appropriate parameter for a particular image processing
3360 % The format of the PreviewImages method is:
3362 % Image *PreviewImages(const Image *image,const PreviewType preview,
3363 % ExceptionInfo *exception)
3365 % A description of each parameter follows:
3367 % o image: the image.
3369 % o preview: the image processing operation.
3371 % o exception: return any errors or warnings in this structure.
3374 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
3375 ExceptionInfo *exception)
3377 #define NumberTiles 9
3378 #define PreviewImageTag "Preview/Image"
3379 #define DefaultPreviewGeometry "204x204+10+10"
3382 factor[MaxTextExtent],
3383 label[MaxTextExtent];
3425 Open output image file.
3427 assert(image != (Image *) NULL);
3428 assert(image->signature == MagickSignature);
3429 if (image->debug != MagickFalse)
3430 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3434 preview_info=AcquireImageInfo();
3435 SetGeometry(image,&geometry);
3436 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
3437 &geometry.width,&geometry.height);
3438 images=NewImageList();
3440 GetQuantizeInfo(&quantize_info);
3446 for (i=0; i < NumberTiles; i++)
3448 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
3449 if (thumbnail == (Image *) NULL)
3451 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
3453 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
3454 if (i == (NumberTiles/2))
3456 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
3457 AppendImageToList(&images,thumbnail);
3465 preview_image=RotateImage(thumbnail,degrees,exception);
3466 (void) FormatMagickString(label,MaxTextExtent,"rotate %.15g",degrees);
3472 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
3473 (void) FormatMagickString(label,MaxTextExtent,"shear %.15gx%.15g",
3474 degrees,2.0*degrees);
3479 x=(long) ((i+1)*thumbnail->columns)/NumberTiles;
3480 y=(long) ((i+1)*thumbnail->rows)/NumberTiles;
3481 preview_image=RollImage(thumbnail,x,y,exception);
3482 (void) FormatMagickString(label,MaxTextExtent,"roll %ldx%ld",x,y);
3487 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3488 if (preview_image == (Image *) NULL)
3490 (void) FormatMagickString(factor,MaxTextExtent,"100,100,%.15g",
3492 (void) ModulateImage(preview_image,factor);
3493 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3496 case SaturationPreview:
3498 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3499 if (preview_image == (Image *) NULL)
3501 (void) FormatMagickString(factor,MaxTextExtent,"100,%.15g",
3503 (void) ModulateImage(preview_image,factor);
3504 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3507 case BrightnessPreview:
3509 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3510 if (preview_image == (Image *) NULL)
3512 (void) FormatMagickString(factor,MaxTextExtent,"%.15g",2.0*percentage);
3513 (void) ModulateImage(preview_image,factor);
3514 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3520 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3521 if (preview_image == (Image *) NULL)
3524 (void) GammaImageChannel(preview_image,DefaultChannels,gamma);
3525 (void) FormatMagickString(label,MaxTextExtent,"gamma %.15g",gamma);
3530 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3531 if (preview_image != (Image *) NULL)
3532 for (x=0; x < i; x++)
3533 (void) ContrastImage(preview_image,MagickTrue);
3534 (void) FormatMagickString(label,MaxTextExtent,"contrast (%ld)",i+1);
3539 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3540 if (preview_image == (Image *) NULL)
3542 for (x=0; x < i; x++)
3543 (void) ContrastImage(preview_image,MagickFalse);
3544 (void) FormatMagickString(label,MaxTextExtent,"+contrast (%ld)",i+1);
3547 case GrayscalePreview:
3549 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3550 if (preview_image == (Image *) NULL)
3553 quantize_info.number_colors=colors;
3554 quantize_info.colorspace=GRAYColorspace;
3555 (void) QuantizeImage(&quantize_info,preview_image);
3556 (void) FormatMagickString(label,MaxTextExtent,
3557 "-colorspace gray -colors %ld",colors);
3560 case QuantizePreview:
3562 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3563 if (preview_image == (Image *) NULL)
3566 quantize_info.number_colors=colors;
3567 (void) QuantizeImage(&quantize_info,preview_image);
3568 (void) FormatMagickString(label,MaxTextExtent,"colors %ld",colors);
3571 case DespecklePreview:
3573 for (x=0; x < (i-1); x++)
3575 preview_image=DespeckleImage(thumbnail,exception);
3576 if (preview_image == (Image *) NULL)
3578 thumbnail=DestroyImage(thumbnail);
3579 thumbnail=preview_image;
3581 preview_image=DespeckleImage(thumbnail,exception);
3582 if (preview_image == (Image *) NULL)
3584 (void) FormatMagickString(label,MaxTextExtent,"despeckle (%ld)",i+1);
3587 case ReduceNoisePreview:
3589 preview_image=ReduceNoiseImage(thumbnail,radius,exception);
3590 (void) FormatMagickString(label,MaxTextExtent,"noise %.15g",radius);
3593 case AddNoisePreview:
3599 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
3604 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
3609 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
3614 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
3619 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
3624 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
3629 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
3633 preview_image=ReduceNoiseImage(thumbnail,(double) i,exception);
3634 (void) FormatMagickString(label,MaxTextExtent,"+noise %s",factor);
3637 case SharpenPreview:
3639 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
3640 (void) FormatMagickString(label,MaxTextExtent,"sharpen %.15gx%.15g",
3646 preview_image=BlurImage(thumbnail,radius,sigma,exception);
3647 (void) FormatMagickString(label,MaxTextExtent,"blur %.15gx%.15g",radius,
3651 case ThresholdPreview:
3653 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3654 if (preview_image == (Image *) NULL)
3656 (void) BilevelImage(thumbnail,
3657 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3658 (void) FormatMagickString(label,MaxTextExtent,"threshold %.15g",
3659 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3662 case EdgeDetectPreview:
3664 preview_image=EdgeImage(thumbnail,radius,exception);
3665 (void) FormatMagickString(label,MaxTextExtent,"edge %.15g",radius);
3670 preview_image=SpreadImage(thumbnail,radius,exception);
3671 (void) FormatMagickString(label,MaxTextExtent,"spread %.15g",
3675 case SolarizePreview:
3677 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3678 if (preview_image == (Image *) NULL)
3680 (void) SolarizeImage(preview_image,(double) QuantumRange*
3682 (void) FormatMagickString(label,MaxTextExtent,"solarize %.15g",
3683 (QuantumRange*percentage)/100.0);
3689 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
3691 (void) FormatMagickString(label,MaxTextExtent,"shade %.15gx%.15g",
3697 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3698 if (preview_image == (Image *) NULL)
3700 geometry.width=(unsigned long) (2*i+2);
3701 geometry.height=(unsigned long) (2*i+2);
3704 (void) RaiseImage(preview_image,&geometry,MagickTrue);
3705 (void) FormatMagickString(label,MaxTextExtent,"raise %lux%lu%+ld%+ld",
3706 geometry.width,geometry.height,geometry.x,geometry.y);
3709 case SegmentPreview:
3711 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3712 if (preview_image == (Image *) NULL)
3715 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
3717 (void) FormatMagickString(label,MaxTextExtent,"segment %.15gx%.15g",
3718 threshold,threshold);
3723 preview_image=SwirlImage(thumbnail,degrees,exception);
3724 (void) FormatMagickString(label,MaxTextExtent,"swirl %.15g",degrees);
3728 case ImplodePreview:
3731 preview_image=ImplodeImage(thumbnail,degrees,exception);
3732 (void) FormatMagickString(label,MaxTextExtent,"implode %.15g",degrees);
3738 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
3739 (void) FormatMagickString(label,MaxTextExtent,"wave %.15gx%.15g",
3740 0.5*degrees,2.0*degrees);
3743 case OilPaintPreview:
3745 preview_image=OilPaintImage(thumbnail,(double) radius,exception);
3746 (void) FormatMagickString(label,MaxTextExtent,"paint %.15g",radius);
3749 case CharcoalDrawingPreview:
3751 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
3753 (void) FormatMagickString(label,MaxTextExtent,"charcoal %.15gx%.15g",
3760 filename[MaxTextExtent];
3768 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3769 if (preview_image == (Image *) NULL)
3771 preview_info->quality=(unsigned long) percentage;
3772 (void) FormatMagickString(factor,MaxTextExtent,"%lu",
3773 preview_info->quality);
3774 file=AcquireUniqueFileResource(filename);
3777 (void) FormatMagickString(preview_image->filename,MaxTextExtent,
3778 "jpeg:%s",filename);
3779 status=WriteImage(preview_info,preview_image);
3780 if (status != MagickFalse)
3785 (void) CopyMagickString(preview_info->filename,
3786 preview_image->filename,MaxTextExtent);
3787 quality_image=ReadImage(preview_info,exception);
3788 if (quality_image != (Image *) NULL)
3790 preview_image=DestroyImage(preview_image);
3791 preview_image=quality_image;
3794 (void) RelinquishUniqueFileResource(preview_image->filename);
3795 if ((GetBlobSize(preview_image)/1024) >= 1024)
3796 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%.15gmb ",
3797 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
3800 if (GetBlobSize(preview_image) >= 1024)
3801 (void) FormatMagickString(label,MaxTextExtent,
3802 "quality %s\n%.15gkb ",factor,(double) ((MagickOffsetType)
3803 GetBlobSize(preview_image))/1024.0);
3805 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%lub ",
3806 factor,(unsigned long) GetBlobSize(thumbnail));
3810 thumbnail=DestroyImage(thumbnail);
3814 if (preview_image == (Image *) NULL)
3816 (void) DeleteImageProperty(preview_image,"label");
3817 (void) SetImageProperty(preview_image,"label",label);
3818 AppendImageToList(&images,preview_image);
3819 proceed=SetImageProgress(image,PreviewImageTag,i,NumberTiles);
3820 if (proceed == MagickFalse)
3823 if (images == (Image *) NULL)
3825 preview_info=DestroyImageInfo(preview_info);
3826 return((Image *) NULL);
3831 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
3832 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
3833 montage_info->shadow=MagickTrue;
3834 (void) CloneString(&montage_info->tile,"3x3");
3835 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
3836 (void) CloneString(&montage_info->frame,DefaultTileFrame);
3837 montage_image=MontageImages(images,montage_info,exception);
3838 montage_info=DestroyMontageInfo(montage_info);
3839 images=DestroyImageList(images);
3840 if (montage_image == (Image *) NULL)
3841 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3842 if (montage_image->montage != (char *) NULL)
3845 Free image directory.
3847 montage_image->montage=(char *) RelinquishMagickMemory(
3848 montage_image->montage);
3849 if (image->directory != (char *) NULL)
3850 montage_image->directory=(char *) RelinquishMagickMemory(
3851 montage_image->directory);
3853 preview_info=DestroyImageInfo(preview_info);
3854 return(montage_image);
3858 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3862 % R a d i a l B l u r I m a g e %
3866 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3868 % RadialBlurImage() applies a radial blur to the image.
3870 % Andrew Protano contributed this effect.
3872 % The format of the RadialBlurImage method is:
3874 % Image *RadialBlurImage(const Image *image,const double angle,
3875 % ExceptionInfo *exception)
3876 % Image *RadialBlurImageChannel(const Image *image,const ChannelType channel,
3877 % const double angle,ExceptionInfo *exception)
3879 % A description of each parameter follows:
3881 % o image: the image.
3883 % o channel: the channel type.
3885 % o angle: the angle of the radial blur.
3887 % o exception: return any errors or warnings in this structure.
3891 MagickExport Image *RadialBlurImage(const Image *image,const double angle,
3892 ExceptionInfo *exception)
3897 blur_image=RadialBlurImageChannel(image,DefaultChannels,angle,exception);
3901 MagickExport Image *RadialBlurImageChannel(const Image *image,
3902 const ChannelType channel,const double angle,ExceptionInfo *exception)
3938 Allocate blur image.
3940 assert(image != (Image *) NULL);
3941 assert(image->signature == MagickSignature);
3942 if (image->debug != MagickFalse)
3943 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3944 assert(exception != (ExceptionInfo *) NULL);
3945 assert(exception->signature == MagickSignature);
3946 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3947 if (blur_image == (Image *) NULL)
3948 return((Image *) NULL);
3949 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3951 InheritException(exception,&blur_image->exception);
3952 blur_image=DestroyImage(blur_image);
3953 return((Image *) NULL);
3955 blur_center.x=(double) image->columns/2.0;
3956 blur_center.y=(double) image->rows/2.0;
3957 blur_radius=hypot(blur_center.x,blur_center.y);
3958 n=(unsigned long) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+
3960 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
3961 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3962 sizeof(*cos_theta));
3963 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3964 sizeof(*sin_theta));
3965 if ((cos_theta == (MagickRealType *) NULL) ||
3966 (sin_theta == (MagickRealType *) NULL))
3968 blur_image=DestroyImage(blur_image);
3969 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3971 offset=theta*(MagickRealType) (n-1)/2.0;
3972 for (i=0; i < (long) n; i++)
3974 cos_theta[i]=cos((double) (theta*i-offset));
3975 sin_theta[i]=sin((double) (theta*i-offset));
3982 GetMagickPixelPacket(image,&bias);
3983 image_view=AcquireCacheView(image);
3984 blur_view=AcquireCacheView(blur_image);
3985 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3986 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3988 for (y=0; y < (long) blur_image->rows; y++)
3990 register const IndexPacket
3993 register IndexPacket
3994 *restrict blur_indexes;
3999 register PixelPacket
4002 if (status == MagickFalse)
4004 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
4006 if (q == (PixelPacket *) NULL)
4011 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
4012 for (x=0; x < (long) blur_image->columns; x++)
4033 center.x=(double) x-blur_center.x;
4034 center.y=(double) y-blur_center.y;
4035 radius=hypot((double) center.x,center.y);
4040 step=(unsigned long) (blur_radius/radius);
4049 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
4051 for (i=0; i < (long) n; i+=step)
4053 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
4054 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
4055 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
4057 qixel.red+=pixel.red;
4058 qixel.green+=pixel.green;
4059 qixel.blue+=pixel.blue;
4060 qixel.opacity+=pixel.opacity;
4061 if (image->colorspace == CMYKColorspace)
4063 indexes=GetCacheViewVirtualIndexQueue(image_view);
4064 qixel.index+=(*indexes);
4068 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
4070 if ((channel & RedChannel) != 0)
4071 q->red=ClampToQuantum(normalize*qixel.red);
4072 if ((channel & GreenChannel) != 0)
4073 q->green=ClampToQuantum(normalize*qixel.green);
4074 if ((channel & BlueChannel) != 0)
4075 q->blue=ClampToQuantum(normalize*qixel.blue);
4076 if ((channel & OpacityChannel) != 0)
4077 q->opacity=ClampToQuantum(normalize*qixel.opacity);
4078 if (((channel & IndexChannel) != 0) &&
4079 (image->colorspace == CMYKColorspace))
4080 blur_indexes[x]=(IndexPacket) ClampToQuantum(normalize*qixel.index);
4090 for (i=0; i < (long) n; i+=step)
4092 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
4093 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
4094 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
4096 alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
4097 qixel.red+=alpha*pixel.red;
4098 qixel.green+=alpha*pixel.green;
4099 qixel.blue+=alpha*pixel.blue;
4100 qixel.opacity+=pixel.opacity;
4101 if (image->colorspace == CMYKColorspace)
4103 indexes=GetCacheViewVirtualIndexQueue(image_view);
4104 qixel.index+=alpha*(*indexes);
4109 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4110 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
4112 if ((channel & RedChannel) != 0)
4113 q->red=ClampToQuantum(gamma*qixel.red);
4114 if ((channel & GreenChannel) != 0)
4115 q->green=ClampToQuantum(gamma*qixel.green);
4116 if ((channel & BlueChannel) != 0)
4117 q->blue=ClampToQuantum(gamma*qixel.blue);
4118 if ((channel & OpacityChannel) != 0)
4119 q->opacity=ClampToQuantum(normalize*qixel.opacity);
4120 if (((channel & IndexChannel) != 0) &&
4121 (image->colorspace == CMYKColorspace))
4122 blur_indexes[x]=(IndexPacket) ClampToQuantum(gamma*qixel.index);
4126 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
4128 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4133 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4134 #pragma omp critical (MagickCore_RadialBlurImageChannel)
4136 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
4137 if (proceed == MagickFalse)
4141 blur_view=DestroyCacheView(blur_view);
4142 image_view=DestroyCacheView(image_view);
4143 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
4144 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
4145 if (status == MagickFalse)
4146 blur_image=DestroyImage(blur_image);
4151 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4155 % R e d u c e N o i s e I m a g e %
4159 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4161 % ReduceNoiseImage() smooths the contours of an image while still preserving
4162 % edge information. The algorithm works by replacing each pixel with its
4163 % neighbor closest in value. A neighbor is defined by radius. Use a radius
4164 % of 0 and ReduceNoise() selects a suitable radius for you.
4166 % The format of the ReduceNoiseImage method is:
4168 % Image *ReduceNoiseImage(const Image *image,const double radius,
4169 % ExceptionInfo *exception)
4171 % A description of each parameter follows:
4173 % o image: the image.
4175 % o radius: the radius of the pixel neighborhood.
4177 % o exception: return any errors or warnings in this structure.
4181 static MagickPixelPacket GetNonpeakMedianPixelList(MedianPixelList *pixel_list)
4189 register MedianSkipList
4203 Finds the median value for each of the color.
4205 center=pixel_list->center;
4206 for (channel=0; channel < 5; channel++)
4208 list=pixel_list->lists+channel;
4210 next=list->nodes[color].next[0];
4216 next=list->nodes[color].next[0];
4217 count+=list->nodes[color].count;
4219 while (count <= center);
4220 if ((previous == 65536UL) && (next != 65536UL))
4223 if ((previous != 65536UL) && (next == 65536UL))
4225 channels[channel]=(unsigned short) color;
4227 GetMagickPixelPacket((const Image *) NULL,&pixel);
4228 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4229 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4230 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
4231 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
4232 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
4236 MagickExport Image *ReduceNoiseImage(const Image *image,const double radius,
4237 ExceptionInfo *exception)
4239 #define ReduceNoiseImageTag "ReduceNoise/Image"
4256 **restrict pixel_list;
4262 Initialize noise image attributes.
4264 assert(image != (Image *) NULL);
4265 assert(image->signature == MagickSignature);
4266 if (image->debug != MagickFalse)
4267 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4268 assert(exception != (ExceptionInfo *) NULL);
4269 assert(exception->signature == MagickSignature);
4270 width=GetOptimalKernelWidth2D(radius,0.5);
4271 if ((image->columns < width) || (image->rows < width))
4272 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
4273 noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4275 if (noise_image == (Image *) NULL)
4276 return((Image *) NULL);
4277 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
4279 InheritException(exception,&noise_image->exception);
4280 noise_image=DestroyImage(noise_image);
4281 return((Image *) NULL);
4283 pixel_list=AcquireMedianPixelListThreadSet(width);
4284 if (pixel_list == (MedianPixelList **) NULL)
4286 noise_image=DestroyImage(noise_image);
4287 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4294 image_view=AcquireCacheView(image);
4295 noise_view=AcquireCacheView(noise_image);
4296 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4297 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4299 for (y=0; y < (long) noise_image->rows; y++)
4301 register const IndexPacket
4304 register const PixelPacket
4307 register IndexPacket
4308 *restrict noise_indexes;
4314 register PixelPacket
4317 if (status == MagickFalse)
4319 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
4320 2L),image->columns+width,width,exception);
4321 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
4323 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4328 indexes=GetCacheViewVirtualIndexQueue(image_view);
4329 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
4330 id=GetOpenMPThreadId();
4331 for (x=0; x < (long) noise_image->columns; x++)
4336 register const PixelPacket
4339 register const IndexPacket
4348 ResetMedianPixelList(pixel_list[id]);
4349 for (v=0; v < (long) width; v++)
4351 for (u=0; u < (long) width; u++)
4352 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
4353 r+=image->columns+width;
4354 s+=image->columns+width;
4356 pixel=GetNonpeakMedianPixelList(pixel_list[id]);
4357 SetPixelPacket(noise_image,&pixel,q,noise_indexes+x);
4361 if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
4363 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4368 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4369 #pragma omp critical (MagickCore_ReduceNoiseImage)
4371 proceed=SetImageProgress(image,ReduceNoiseImageTag,progress++,
4373 if (proceed == MagickFalse)
4377 noise_view=DestroyCacheView(noise_view);
4378 image_view=DestroyCacheView(image_view);
4379 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
4380 return(noise_image);
4384 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4388 % S e l e c t i v e B l u r I m a g e %
4392 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4394 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
4395 % It is similar to the unsharpen mask that sharpens everything with contrast
4396 % above a certain threshold.
4398 % The format of the SelectiveBlurImage method is:
4400 % Image *SelectiveBlurImage(const Image *image,const double radius,
4401 % const double sigma,const double threshold,ExceptionInfo *exception)
4402 % Image *SelectiveBlurImageChannel(const Image *image,
4403 % const ChannelType channel,const double radius,const double sigma,
4404 % const double threshold,ExceptionInfo *exception)
4406 % A description of each parameter follows:
4408 % o image: the image.
4410 % o channel: the channel type.
4412 % o radius: the radius of the Gaussian, in pixels, not counting the center
4415 % o sigma: the standard deviation of the Gaussian, in pixels.
4417 % o threshold: only pixels within this contrast threshold are included
4418 % in the blur operation.
4420 % o exception: return any errors or warnings in this structure.
4424 static inline MagickBooleanType SelectiveContrast(const PixelPacket *p,
4425 const PixelPacket *q,const double threshold)
4427 if (fabs(PixelIntensity(p)-PixelIntensity(q)) < threshold)
4429 return(MagickFalse);
4432 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
4433 const double sigma,const double threshold,ExceptionInfo *exception)
4438 blur_image=SelectiveBlurImageChannel(image,DefaultChannels,radius,sigma,
4439 threshold,exception);
4443 MagickExport Image *SelectiveBlurImageChannel(const Image *image,
4444 const ChannelType channel,const double radius,const double sigma,
4445 const double threshold,ExceptionInfo *exception)
4447 #define SelectiveBlurImageTag "SelectiveBlur/Image"
4479 Initialize blur image attributes.
4481 assert(image != (Image *) NULL);
4482 assert(image->signature == MagickSignature);
4483 if (image->debug != MagickFalse)
4484 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4485 assert(exception != (ExceptionInfo *) NULL);
4486 assert(exception->signature == MagickSignature);
4487 width=GetOptimalKernelWidth1D(radius,sigma);
4488 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
4489 if (kernel == (double *) NULL)
4490 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4493 for (v=(-j); v <= j; v++)
4495 for (u=(-j); u <= j; u++)
4496 kernel[i++]=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
4497 (2.0*MagickPI*MagickSigma*MagickSigma);
4499 if (image->debug != MagickFalse)
4502 format[MaxTextExtent],
4509 register const double
4512 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
4513 " SelectiveBlurImage with %ldx%ld kernel:",width,width);
4514 message=AcquireString("");
4516 for (v=0; v < (long) width; v++)
4519 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
4520 (void) ConcatenateString(&message,format);
4521 for (u=0; u < (long) width; u++)
4523 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
4524 (void) ConcatenateString(&message,format);
4526 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
4528 message=DestroyString(message);
4530 blur_image=CloneImage(image,0,0,MagickTrue,exception);
4531 if (blur_image == (Image *) NULL)
4532 return((Image *) NULL);
4533 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
4535 InheritException(exception,&blur_image->exception);
4536 blur_image=DestroyImage(blur_image);
4537 return((Image *) NULL);
4540 Threshold blur image.
4544 GetMagickPixelPacket(image,&bias);
4545 SetMagickPixelPacketBias(image,&bias);
4546 image_view=AcquireCacheView(image);
4547 blur_view=AcquireCacheView(blur_image);
4548 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4549 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4551 for (y=0; y < (long) image->rows; y++)
4559 register const IndexPacket
4562 register const PixelPacket
4565 register IndexPacket
4566 *restrict blur_indexes;
4571 register PixelPacket
4574 if (status == MagickFalse)
4576 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
4577 2L),image->columns+width,width,exception);
4578 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
4580 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4585 indexes=GetCacheViewVirtualIndexQueue(image_view);
4586 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
4587 for (x=0; x < (long) image->columns; x++)
4596 register const double
4606 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
4608 for (v=0; v < (long) width; v++)
4610 for (u=0; u < (long) width; u++)
4612 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4614 pixel.red+=(*k)*(p+u+j)->red;
4615 pixel.green+=(*k)*(p+u+j)->green;
4616 pixel.blue+=(*k)*(p+u+j)->blue;
4621 j+=image->columns+width;
4625 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4626 if ((channel & RedChannel) != 0)
4627 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
4628 if ((channel & GreenChannel) != 0)
4629 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
4630 if ((channel & BlueChannel) != 0)
4631 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
4633 if ((channel & OpacityChannel) != 0)
4637 for (v=0; v < (long) width; v++)
4639 for (u=0; u < (long) width; u++)
4641 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4643 pixel.opacity+=(*k)*(p+u+j)->opacity;
4648 j+=image->columns+width;
4652 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4654 SetOpacityPixelComponent(q,ClampToQuantum(gamma*
4655 GetOpacityPixelComponent(&pixel)));
4658 if (((channel & IndexChannel) != 0) &&
4659 (image->colorspace == CMYKColorspace))
4663 for (v=0; v < (long) width; v++)
4665 for (u=0; u < (long) width; u++)
4667 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4669 pixel.index+=(*k)*indexes[x+u+j];
4674 j+=image->columns+width;
4678 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4680 blur_indexes[x]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
4689 for (v=0; v < (long) width; v++)
4691 for (u=0; u < (long) width; u++)
4693 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4695 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
4697 pixel.red+=(*k)*alpha*(p+u+j)->red;
4698 pixel.green+=(*k)*alpha*(p+u+j)->green;
4699 pixel.blue+=(*k)*alpha*(p+u+j)->blue;
4700 pixel.opacity+=(*k)*(p+u+j)->opacity;
4705 j+=image->columns+width;
4709 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4710 if ((channel & RedChannel) != 0)
4711 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
4712 if ((channel & GreenChannel) != 0)
4713 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
4714 if ((channel & BlueChannel) != 0)
4715 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
4717 if ((channel & OpacityChannel) != 0)
4721 for (v=0; v < (long) width; v++)
4723 for (u=0; u < (long) width; u++)
4725 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4727 pixel.opacity+=(*k)*(p+u+j)->opacity;
4732 j+=image->columns+width;
4736 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4738 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
4741 if (((channel & IndexChannel) != 0) &&
4742 (image->colorspace == CMYKColorspace))
4746 for (v=0; v < (long) width; v++)
4748 for (u=0; u < (long) width; u++)
4750 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4752 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
4754 pixel.index+=(*k)*alpha*indexes[x+u+j];
4759 j+=image->columns+width;
4763 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4765 blur_indexes[x]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
4772 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
4773 if (sync == MagickFalse)
4775 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4780 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4781 #pragma omp critical (MagickCore_SelectiveBlurImageChannel)
4783 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
4785 if (proceed == MagickFalse)
4789 blur_image->type=image->type;
4790 blur_view=DestroyCacheView(blur_view);
4791 image_view=DestroyCacheView(image_view);
4792 kernel=(double *) RelinquishMagickMemory(kernel);
4793 if (status == MagickFalse)
4794 blur_image=DestroyImage(blur_image);
4799 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4803 % S h a d e I m a g e %
4807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4809 % ShadeImage() shines a distant light on an image to create a
4810 % three-dimensional effect. You control the positioning of the light with
4811 % azimuth and elevation; azimuth is measured in degrees off the x axis
4812 % and elevation is measured in pixels above the Z axis.
4814 % The format of the ShadeImage method is:
4816 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4817 % const double azimuth,const double elevation,ExceptionInfo *exception)
4819 % A description of each parameter follows:
4821 % o image: the image.
4823 % o gray: A value other than zero shades the intensity of each pixel.
4825 % o azimuth, elevation: Define the light source direction.
4827 % o exception: return any errors or warnings in this structure.
4830 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4831 const double azimuth,const double elevation,ExceptionInfo *exception)
4833 #define ShadeImageTag "Shade/Image"
4853 Initialize shaded image attributes.
4855 assert(image != (const Image *) NULL);
4856 assert(image->signature == MagickSignature);
4857 if (image->debug != MagickFalse)
4858 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4859 assert(exception != (ExceptionInfo *) NULL);
4860 assert(exception->signature == MagickSignature);
4861 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4862 if (shade_image == (Image *) NULL)
4863 return((Image *) NULL);
4864 if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
4866 InheritException(exception,&shade_image->exception);
4867 shade_image=DestroyImage(shade_image);
4868 return((Image *) NULL);
4871 Compute the light vector.
4873 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
4874 cos(DegreesToRadians(elevation));
4875 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
4876 cos(DegreesToRadians(elevation));
4877 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
4883 image_view=AcquireCacheView(image);
4884 shade_view=AcquireCacheView(shade_image);
4885 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4886 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4888 for (y=0; y < (long) image->rows; y++)
4898 register const PixelPacket
4907 register PixelPacket
4910 if (status == MagickFalse)
4912 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
4913 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
4915 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4921 Shade this row of pixels.
4923 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
4925 s1=s0+image->columns+2;
4926 s2=s1+image->columns+2;
4927 for (x=0; x < (long) image->columns; x++)
4930 Determine the surface normal and compute shading.
4932 normal.x=(double) (PixelIntensity(s0-1)+PixelIntensity(s1-1)+
4933 PixelIntensity(s2-1)-PixelIntensity(s0+1)-PixelIntensity(s1+1)-
4934 PixelIntensity(s2+1));
4935 normal.y=(double) (PixelIntensity(s2-1)+PixelIntensity(s2)+
4936 PixelIntensity(s2+1)-PixelIntensity(s0-1)-PixelIntensity(s0)-
4937 PixelIntensity(s0+1));
4938 if ((normal.x == 0.0) && (normal.y == 0.0))
4943 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
4944 if (distance > MagickEpsilon)
4947 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
4948 if (normal_distance > (MagickEpsilon*MagickEpsilon))
4949 shade=distance/sqrt((double) normal_distance);
4952 if (gray != MagickFalse)
4954 q->red=(Quantum) shade;
4955 q->green=(Quantum) shade;
4956 q->blue=(Quantum) shade;
4960 q->red=ClampToQuantum(QuantumScale*shade*s1->red);
4961 q->green=ClampToQuantum(QuantumScale*shade*s1->green);
4962 q->blue=ClampToQuantum(QuantumScale*shade*s1->blue);
4964 q->opacity=s1->opacity;
4970 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
4972 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4977 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4978 #pragma omp critical (MagickCore_ShadeImage)
4980 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
4981 if (proceed == MagickFalse)
4985 shade_view=DestroyCacheView(shade_view);
4986 image_view=DestroyCacheView(image_view);
4987 if (status == MagickFalse)
4988 shade_image=DestroyImage(shade_image);
4989 return(shade_image);
4993 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4997 % S h a r p e n I m a g e %
5001 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5003 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
5004 % operator of the given radius and standard deviation (sigma). For
5005 % reasonable results, radius should be larger than sigma. Use a radius of 0
5006 % and SharpenImage() selects a suitable radius for you.
5008 % Using a separable kernel would be faster, but the negative weights cancel
5009 % out on the corners of the kernel producing often undesirable ringing in the
5010 % filtered result; this can be avoided by using a 2D gaussian shaped image
5011 % sharpening kernel instead.
5013 % The format of the SharpenImage method is:
5015 % Image *SharpenImage(const Image *image,const double radius,
5016 % const double sigma,ExceptionInfo *exception)
5017 % Image *SharpenImageChannel(const Image *image,const ChannelType channel,
5018 % const double radius,const double sigma,ExceptionInfo *exception)
5020 % A description of each parameter follows:
5022 % o image: the image.
5024 % o channel: the channel type.
5026 % o radius: the radius of the Gaussian, in pixels, not counting the center
5029 % o sigma: the standard deviation of the Laplacian, in pixels.
5031 % o exception: return any errors or warnings in this structure.
5035 MagickExport Image *SharpenImage(const Image *image,const double radius,
5036 const double sigma,ExceptionInfo *exception)
5041 sharp_image=SharpenImageChannel(image,DefaultChannels,radius,sigma,exception);
5042 return(sharp_image);
5045 MagickExport Image *SharpenImageChannel(const Image *image,
5046 const ChannelType channel,const double radius,const double sigma,
5047 ExceptionInfo *exception)
5067 assert(image != (const Image *) NULL);
5068 assert(image->signature == MagickSignature);
5069 if (image->debug != MagickFalse)
5070 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5071 assert(exception != (ExceptionInfo *) NULL);
5072 assert(exception->signature == MagickSignature);
5073 width=GetOptimalKernelWidth2D(radius,sigma);
5074 kernel=(double *) AcquireQuantumMemory((size_t) width*width,sizeof(*kernel));
5075 if (kernel == (double *) NULL)
5076 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5080 for (v=(-j); v <= j; v++)
5082 for (u=(-j); u <= j; u++)
5084 kernel[i]=(-exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
5085 (2.0*MagickPI*MagickSigma*MagickSigma));
5086 normalize+=kernel[i];
5090 kernel[i/2]=(double) ((-2.0)*normalize);
5091 sharp_image=ConvolveImageChannel(image,channel,width,kernel,exception);
5092 kernel=(double *) RelinquishMagickMemory(kernel);
5093 return(sharp_image);
5097 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5101 % S p r e a d I m a g e %
5105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5107 % SpreadImage() is a special effects method that randomly displaces each
5108 % pixel in a block defined by the radius parameter.
5110 % The format of the SpreadImage method is:
5112 % Image *SpreadImage(const Image *image,const double radius,
5113 % ExceptionInfo *exception)
5115 % A description of each parameter follows:
5117 % o image: the image.
5119 % o radius: Choose a random pixel in a neighborhood of this extent.
5121 % o exception: return any errors or warnings in this structure.
5124 MagickExport Image *SpreadImage(const Image *image,const double radius,
5125 ExceptionInfo *exception)
5127 #define SpreadImageTag "Spread/Image"
5146 **restrict random_info;
5149 **restrict resample_filter;
5155 Initialize spread image attributes.
5157 assert(image != (Image *) NULL);
5158 assert(image->signature == MagickSignature);
5159 if (image->debug != MagickFalse)
5160 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5161 assert(exception != (ExceptionInfo *) NULL);
5162 assert(exception->signature == MagickSignature);
5163 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
5165 if (spread_image == (Image *) NULL)
5166 return((Image *) NULL);
5167 if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
5169 InheritException(exception,&spread_image->exception);
5170 spread_image=DestroyImage(spread_image);
5171 return((Image *) NULL);
5178 GetMagickPixelPacket(spread_image,&bias);
5179 width=GetOptimalKernelWidth1D(radius,0.5);
5180 resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
5181 random_info=AcquireRandomInfoThreadSet();
5182 image_view=AcquireCacheView(spread_image);
5183 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5184 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5186 for (y=0; y < (long) spread_image->rows; y++)
5191 register IndexPacket
5198 register PixelPacket
5201 if (status == MagickFalse)
5203 q=QueueCacheViewAuthenticPixels(image_view,0,y,spread_image->columns,1,
5205 if (q == (PixelPacket *) NULL)
5210 indexes=GetCacheViewAuthenticIndexQueue(image_view);
5212 id=GetOpenMPThreadId();
5213 for (x=0; x < (long) spread_image->columns; x++)
5215 (void) ResamplePixelColor(resample_filter[id],(double) x+width*
5216 (GetPseudoRandomValue(random_info[id])-0.5),(double) y+width*
5217 (GetPseudoRandomValue(random_info[id])-0.5),&pixel);
5218 SetPixelPacket(spread_image,&pixel,q,indexes+x);
5221 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5223 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5228 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5229 #pragma omp critical (MagickCore_SpreadImage)
5231 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
5232 if (proceed == MagickFalse)
5236 image_view=DestroyCacheView(image_view);
5237 random_info=DestroyRandomInfoThreadSet(random_info);
5238 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5239 return(spread_image);
5243 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5247 % U n s h a r p M a s k I m a g e %
5251 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5253 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
5254 % image with a Gaussian operator of the given radius and standard deviation
5255 % (sigma). For reasonable results, radius should be larger than sigma. Use a
5256 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
5258 % The format of the UnsharpMaskImage method is:
5260 % Image *UnsharpMaskImage(const Image *image,const double radius,
5261 % const double sigma,const double amount,const double threshold,
5262 % ExceptionInfo *exception)
5263 % Image *UnsharpMaskImageChannel(const Image *image,
5264 % const ChannelType channel,const double radius,const double sigma,
5265 % const double amount,const double threshold,ExceptionInfo *exception)
5267 % A description of each parameter follows:
5269 % o image: the image.
5271 % o channel: the channel type.
5273 % o radius: the radius of the Gaussian, in pixels, not counting the center
5276 % o sigma: the standard deviation of the Gaussian, in pixels.
5278 % o amount: the percentage of the difference between the original and the
5279 % blur image that is added back into the original.
5281 % o threshold: the threshold in pixels needed to apply the diffence amount.
5283 % o exception: return any errors or warnings in this structure.
5287 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
5288 const double sigma,const double amount,const double threshold,
5289 ExceptionInfo *exception)
5294 sharp_image=UnsharpMaskImageChannel(image,DefaultChannels,radius,sigma,amount,
5295 threshold,exception);
5296 return(sharp_image);
5299 MagickExport Image *UnsharpMaskImageChannel(const Image *image,
5300 const ChannelType channel,const double radius,const double sigma,
5301 const double amount,const double threshold,ExceptionInfo *exception)
5303 #define SharpenImageTag "Sharpen/Image"
5325 assert(image != (const Image *) NULL);
5326 assert(image->signature == MagickSignature);
5327 if (image->debug != MagickFalse)
5328 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5329 assert(exception != (ExceptionInfo *) NULL);
5330 unsharp_image=BlurImageChannel(image,channel,radius,sigma,exception);
5331 if (unsharp_image == (Image *) NULL)
5332 return((Image *) NULL);
5333 quantum_threshold=(MagickRealType) QuantumRange*threshold;
5339 GetMagickPixelPacket(image,&bias);
5340 image_view=AcquireCacheView(image);
5341 unsharp_view=AcquireCacheView(unsharp_image);
5342 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5343 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5345 for (y=0; y < (long) image->rows; y++)
5350 register const IndexPacket
5353 register const PixelPacket
5356 register IndexPacket
5357 *restrict unsharp_indexes;
5362 register PixelPacket
5365 if (status == MagickFalse)
5367 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5368 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
5370 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5375 indexes=GetCacheViewVirtualIndexQueue(image_view);
5376 unsharp_indexes=GetCacheViewAuthenticIndexQueue(unsharp_view);
5378 for (x=0; x < (long) image->columns; x++)
5380 if ((channel & RedChannel) != 0)
5382 pixel.red=p->red-(MagickRealType) q->red;
5383 if (fabs(2.0*pixel.red) < quantum_threshold)
5384 pixel.red=(MagickRealType) GetRedPixelComponent(p);
5386 pixel.red=(MagickRealType) p->red+(pixel.red*amount);
5387 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
5389 if ((channel & GreenChannel) != 0)
5391 pixel.green=p->green-(MagickRealType) q->green;
5392 if (fabs(2.0*pixel.green) < quantum_threshold)
5393 pixel.green=(MagickRealType) GetGreenPixelComponent(p);
5395 pixel.green=(MagickRealType) p->green+(pixel.green*amount);
5396 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
5398 if ((channel & BlueChannel) != 0)
5400 pixel.blue=p->blue-(MagickRealType) q->blue;
5401 if (fabs(2.0*pixel.blue) < quantum_threshold)
5402 pixel.blue=(MagickRealType) GetBluePixelComponent(p);
5404 pixel.blue=(MagickRealType) p->blue+(pixel.blue*amount);
5405 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
5407 if ((channel & OpacityChannel) != 0)
5409 pixel.opacity=p->opacity-(MagickRealType) q->opacity;
5410 if (fabs(2.0*pixel.opacity) < quantum_threshold)
5411 pixel.opacity=(MagickRealType) GetOpacityPixelComponent(p);
5413 pixel.opacity=p->opacity+(pixel.opacity*amount);
5414 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
5416 if (((channel & IndexChannel) != 0) &&
5417 (image->colorspace == CMYKColorspace))
5419 pixel.index=unsharp_indexes[x]-(MagickRealType) indexes[x];
5420 if (fabs(2.0*pixel.index) < quantum_threshold)
5421 pixel.index=(MagickRealType) unsharp_indexes[x];
5423 pixel.index=(MagickRealType) unsharp_indexes[x]+(pixel.index*
5425 unsharp_indexes[x]=ClampToQuantum(pixel.index);
5430 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
5432 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5437 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5438 #pragma omp critical (MagickCore_UnsharpMaskImageChannel)
5440 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
5441 if (proceed == MagickFalse)
5445 unsharp_image->type=image->type;
5446 unsharp_view=DestroyCacheView(unsharp_view);
5447 image_view=DestroyCacheView(image_view);
5448 if (status == MagickFalse)
5449 unsharp_image=DestroyImage(unsharp_image);
5450 return(unsharp_image);