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-2011 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/accelerate.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/morphology.h"
68 #include "magick/paint.h"
69 #include "magick/pixel-private.h"
70 #include "magick/property.h"
71 #include "magick/quantize.h"
72 #include "magick/quantum.h"
73 #include "magick/random_.h"
74 #include "magick/random-private.h"
75 #include "magick/resample.h"
76 #include "magick/resample-private.h"
77 #include "magick/resize.h"
78 #include "magick/resource_.h"
79 #include "magick/segment.h"
80 #include "magick/shear.h"
81 #include "magick/signature-private.h"
82 #include "magick/string_.h"
83 #include "magick/thread-private.h"
84 #include "magick/transform.h"
85 #include "magick/threshold.h"
88 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92 % A d a p t i v e B l u r I m a g e %
96 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
98 % AdaptiveBlurImage() adaptively blurs the image by blurring less
99 % intensely near image edges and more intensely far from edges. We blur the
100 % image with a Gaussian operator of the given radius and standard deviation
101 % (sigma). For reasonable results, radius should be larger than sigma. Use a
102 % radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
104 % The format of the AdaptiveBlurImage method is:
106 % Image *AdaptiveBlurImage(const Image *image,const double radius,
107 % const double sigma,ExceptionInfo *exception)
108 % Image *AdaptiveBlurImageChannel(const Image *image,
109 % const ChannelType channel,double radius,const double sigma,
110 % ExceptionInfo *exception)
112 % A description of each parameter follows:
114 % o image: the image.
116 % o channel: the channel type.
118 % o radius: the radius of the Gaussian, in pixels, not counting the center
121 % o sigma: the standard deviation of the Laplacian, in pixels.
123 % o exception: return any errors or warnings in this structure.
127 MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
128 const double sigma,ExceptionInfo *exception)
133 blur_image=AdaptiveBlurImageChannel(image,DefaultChannels,radius,sigma,
138 MagickExport Image *AdaptiveBlurImageChannel(const Image *image,
139 const ChannelType channel,const double radius,const double sigma,
140 ExceptionInfo *exception)
142 #define AdaptiveBlurImageTag "Convolve/Image"
143 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
181 assert(image != (const Image *) NULL);
182 assert(image->signature == MagickSignature);
183 if (image->debug != MagickFalse)
184 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
185 assert(exception != (ExceptionInfo *) NULL);
186 assert(exception->signature == MagickSignature);
187 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
188 if (blur_image == (Image *) NULL)
189 return((Image *) NULL);
190 if (fabs(sigma) <= MagickEpsilon)
192 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
194 InheritException(exception,&blur_image->exception);
195 blur_image=DestroyImage(blur_image);
196 return((Image *) NULL);
199 Edge detect the image brighness channel, level, blur, and level again.
201 edge_image=EdgeImage(image,radius,exception);
202 if (edge_image == (Image *) NULL)
204 blur_image=DestroyImage(blur_image);
205 return((Image *) NULL);
207 (void) LevelImage(edge_image,"20%,95%");
208 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
209 if (gaussian_image != (Image *) NULL)
211 edge_image=DestroyImage(edge_image);
212 edge_image=gaussian_image;
214 (void) LevelImage(edge_image,"10%,95%");
216 Create a set of kernels from maximum (radius,sigma) to minimum.
218 width=GetOptimalKernelWidth2D(radius,sigma);
219 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
220 if (kernel == (double **) NULL)
222 edge_image=DestroyImage(edge_image);
223 blur_image=DestroyImage(blur_image);
224 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
226 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
227 for (i=0; i < (ssize_t) width; i+=2)
229 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
231 if (kernel[i] == (double *) NULL)
234 j=(ssize_t) (width-i)/2;
236 for (v=(-j); v <= j; v++)
238 for (u=(-j); u <= j; u++)
240 kernel[i][k]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
241 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
242 normalize+=kernel[i][k];
246 if (fabs(normalize) <= MagickEpsilon)
248 normalize=1.0/normalize;
249 for (k=0; k < (j*j); k++)
250 kernel[i][k]=normalize*kernel[i][k];
252 if (i < (ssize_t) width)
254 for (i-=2; i >= 0; i-=2)
255 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
256 kernel=(double **) RelinquishMagickMemory(kernel);
257 edge_image=DestroyImage(edge_image);
258 blur_image=DestroyImage(blur_image);
259 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
262 Adaptively blur image.
266 GetMagickPixelPacket(image,&bias);
267 SetMagickPixelPacketBias(image,&bias);
268 image_view=AcquireCacheView(image);
269 edge_view=AcquireCacheView(edge_image);
270 blur_view=AcquireCacheView(blur_image);
271 #if defined(MAGICKCORE_OPENMP_SUPPORT)
272 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
274 for (y=0; y < (ssize_t) blur_image->rows; y++)
276 register const IndexPacket
279 register const PixelPacket
284 *restrict blur_indexes;
292 if (status == MagickFalse)
294 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
295 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
297 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
302 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
303 for (x=0; x < (ssize_t) blur_image->columns; x++)
312 register const double
321 i=(ssize_t) ceil((double) width*QuantumScale*PixelIntensity(r)-0.5);
325 if (i > (ssize_t) width)
329 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-i)/2L),y-
330 (ssize_t) ((width-i)/2L),width-i,width-i,exception);
331 if (p == (const PixelPacket *) NULL)
333 indexes=GetCacheViewVirtualIndexQueue(image_view);
336 for (v=0; v < (ssize_t) (width-i); v++)
338 for (u=0; u < (ssize_t) (width-i); u++)
341 if (((channel & OpacityChannel) != 0) &&
342 (image->matte != MagickFalse))
343 alpha=(MagickRealType) (QuantumScale*GetAlphaPixelComponent(p));
344 if ((channel & RedChannel) != 0)
345 pixel.red+=(*k)*alpha*GetRedPixelComponent(p);
346 if ((channel & GreenChannel) != 0)
347 pixel.green+=(*k)*alpha*GetGreenPixelComponent(p);
348 if ((channel & BlueChannel) != 0)
349 pixel.blue+=(*k)*alpha*GetBluePixelComponent(p);
350 if ((channel & OpacityChannel) != 0)
351 pixel.opacity+=(*k)*GetOpacityPixelComponent(p);
352 if (((channel & IndexChannel) != 0) &&
353 (image->colorspace == CMYKColorspace))
354 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
360 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
361 if ((channel & RedChannel) != 0)
362 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
363 if ((channel & GreenChannel) != 0)
364 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
365 if ((channel & BlueChannel) != 0)
366 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
367 if ((channel & OpacityChannel) != 0)
368 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
369 if (((channel & IndexChannel) != 0) &&
370 (image->colorspace == CMYKColorspace))
371 blur_indexes[x]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
375 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
377 if (image->progress_monitor != (MagickProgressMonitor) NULL)
382 #if defined(MAGICKCORE_OPENMP_SUPPORT)
383 #pragma omp critical (MagickCore_AdaptiveBlurImageChannel)
385 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
387 if (proceed == MagickFalse)
391 blur_image->type=image->type;
392 blur_view=DestroyCacheView(blur_view);
393 edge_view=DestroyCacheView(edge_view);
394 image_view=DestroyCacheView(image_view);
395 edge_image=DestroyImage(edge_image);
396 for (i=0; i < (ssize_t) width; i+=2)
397 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
398 kernel=(double **) RelinquishMagickMemory(kernel);
399 if (status == MagickFalse)
400 blur_image=DestroyImage(blur_image);
405 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
409 % A d a p t i v e S h a r p e n I m a g e %
413 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
415 % AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
416 % intensely near image edges and less intensely far from edges. We sharpen the
417 % image with a Gaussian operator of the given radius and standard deviation
418 % (sigma). For reasonable results, radius should be larger than sigma. Use a
419 % radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
421 % The format of the AdaptiveSharpenImage method is:
423 % Image *AdaptiveSharpenImage(const Image *image,const double radius,
424 % const double sigma,ExceptionInfo *exception)
425 % Image *AdaptiveSharpenImageChannel(const Image *image,
426 % const ChannelType channel,double radius,const double sigma,
427 % ExceptionInfo *exception)
429 % A description of each parameter follows:
431 % o image: the image.
433 % o channel: the channel type.
435 % o radius: the radius of the Gaussian, in pixels, not counting the center
438 % o sigma: the standard deviation of the Laplacian, in pixels.
440 % o exception: return any errors or warnings in this structure.
444 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
445 const double sigma,ExceptionInfo *exception)
450 sharp_image=AdaptiveSharpenImageChannel(image,DefaultChannels,radius,sigma,
455 MagickExport Image *AdaptiveSharpenImageChannel(const Image *image,
456 const ChannelType channel,const double radius,const double sigma,
457 ExceptionInfo *exception)
459 #define AdaptiveSharpenImageTag "Convolve/Image"
460 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
498 assert(image != (const Image *) NULL);
499 assert(image->signature == MagickSignature);
500 if (image->debug != MagickFalse)
501 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
502 assert(exception != (ExceptionInfo *) NULL);
503 assert(exception->signature == MagickSignature);
504 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
505 if (sharp_image == (Image *) NULL)
506 return((Image *) NULL);
507 if (fabs(sigma) <= MagickEpsilon)
509 if (SetImageStorageClass(sharp_image,DirectClass) == MagickFalse)
511 InheritException(exception,&sharp_image->exception);
512 sharp_image=DestroyImage(sharp_image);
513 return((Image *) NULL);
516 Edge detect the image brighness channel, level, sharp, and level again.
518 edge_image=EdgeImage(image,radius,exception);
519 if (edge_image == (Image *) NULL)
521 sharp_image=DestroyImage(sharp_image);
522 return((Image *) NULL);
524 (void) LevelImage(edge_image,"20%,95%");
525 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
526 if (gaussian_image != (Image *) NULL)
528 edge_image=DestroyImage(edge_image);
529 edge_image=gaussian_image;
531 (void) LevelImage(edge_image,"10%,95%");
533 Create a set of kernels from maximum (radius,sigma) to minimum.
535 width=GetOptimalKernelWidth2D(radius,sigma);
536 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
537 if (kernel == (double **) NULL)
539 edge_image=DestroyImage(edge_image);
540 sharp_image=DestroyImage(sharp_image);
541 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
543 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
544 for (i=0; i < (ssize_t) width; i+=2)
546 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
548 if (kernel[i] == (double *) NULL)
551 j=(ssize_t) (width-i)/2;
553 for (v=(-j); v <= j; v++)
555 for (u=(-j); u <= j; u++)
557 kernel[i][k]=(double) (-exp(-((double) u*u+v*v)/(2.0*MagickSigma*
558 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
559 normalize+=kernel[i][k];
563 if (fabs(normalize) <= MagickEpsilon)
565 normalize=1.0/normalize;
566 for (k=0; k < (j*j); k++)
567 kernel[i][k]=normalize*kernel[i][k];
569 if (i < (ssize_t) width)
571 for (i-=2; i >= 0; i-=2)
572 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
573 kernel=(double **) RelinquishMagickMemory(kernel);
574 edge_image=DestroyImage(edge_image);
575 sharp_image=DestroyImage(sharp_image);
576 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
579 Adaptively sharpen image.
583 GetMagickPixelPacket(image,&bias);
584 SetMagickPixelPacketBias(image,&bias);
585 image_view=AcquireCacheView(image);
586 edge_view=AcquireCacheView(edge_image);
587 sharp_view=AcquireCacheView(sharp_image);
588 #if defined(MAGICKCORE_OPENMP_SUPPORT)
589 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
591 for (y=0; y < (ssize_t) sharp_image->rows; y++)
593 register const IndexPacket
596 register const PixelPacket
601 *restrict sharp_indexes;
609 if (status == MagickFalse)
611 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
612 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
614 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
619 sharp_indexes=GetCacheViewAuthenticIndexQueue(sharp_view);
620 for (x=0; x < (ssize_t) sharp_image->columns; x++)
629 register const double
638 i=(ssize_t) ceil((double) width*(QuantumRange-QuantumScale*
639 PixelIntensity(r))-0.5);
643 if (i > (ssize_t) width)
647 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-i)/2L),y-
648 (ssize_t) ((width-i)/2L),width-i,width-i,exception);
649 if (p == (const PixelPacket *) NULL)
651 indexes=GetCacheViewVirtualIndexQueue(image_view);
654 for (v=0; v < (ssize_t) (width-i); v++)
656 for (u=0; u < (ssize_t) (width-i); u++)
659 if (((channel & OpacityChannel) != 0) &&
660 (image->matte != MagickFalse))
661 alpha=(MagickRealType) (QuantumScale*GetAlphaPixelComponent(p));
662 if ((channel & RedChannel) != 0)
663 pixel.red+=(*k)*alpha*GetRedPixelComponent(p);
664 if ((channel & GreenChannel) != 0)
665 pixel.green+=(*k)*alpha*GetGreenPixelComponent(p);
666 if ((channel & BlueChannel) != 0)
667 pixel.blue+=(*k)*alpha*GetBluePixelComponent(p);
668 if ((channel & OpacityChannel) != 0)
669 pixel.opacity+=(*k)*GetOpacityPixelComponent(p);
670 if (((channel & IndexChannel) != 0) &&
671 (image->colorspace == CMYKColorspace))
672 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
678 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
679 if ((channel & RedChannel) != 0)
680 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
681 if ((channel & GreenChannel) != 0)
682 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
683 if ((channel & BlueChannel) != 0)
684 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
685 if ((channel & OpacityChannel) != 0)
686 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
687 if (((channel & IndexChannel) != 0) &&
688 (image->colorspace == CMYKColorspace))
689 sharp_indexes[x]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
693 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
695 if (image->progress_monitor != (MagickProgressMonitor) NULL)
700 #if defined(MAGICKCORE_OPENMP_SUPPORT)
701 #pragma omp critical (MagickCore_AdaptiveSharpenImageChannel)
703 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
705 if (proceed == MagickFalse)
709 sharp_image->type=image->type;
710 sharp_view=DestroyCacheView(sharp_view);
711 edge_view=DestroyCacheView(edge_view);
712 image_view=DestroyCacheView(image_view);
713 edge_image=DestroyImage(edge_image);
714 for (i=0; i < (ssize_t) width; i+=2)
715 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
716 kernel=(double **) RelinquishMagickMemory(kernel);
717 if (status == MagickFalse)
718 sharp_image=DestroyImage(sharp_image);
723 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727 % B l u r I m a g e %
731 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
733 % BlurImage() blurs an image. We convolve the image with a Gaussian operator
734 % of the given radius and standard deviation (sigma). For reasonable results,
735 % the radius should be larger than sigma. Use a radius of 0 and BlurImage()
736 % selects a suitable radius for you.
738 % BlurImage() differs from GaussianBlurImage() in that it uses a separable
739 % kernel which is faster but mathematically equivalent to the non-separable
742 % The format of the BlurImage method is:
744 % Image *BlurImage(const Image *image,const double radius,
745 % const double sigma,ExceptionInfo *exception)
746 % Image *BlurImageChannel(const Image *image,const ChannelType channel,
747 % const double radius,const double sigma,ExceptionInfo *exception)
749 % A description of each parameter follows:
751 % o image: the image.
753 % o channel: the channel type.
755 % o radius: the radius of the Gaussian, in pixels, not counting the center
758 % o sigma: the standard deviation of the Gaussian, in pixels.
760 % o exception: return any errors or warnings in this structure.
764 MagickExport Image *BlurImage(const Image *image,const double radius,
765 const double sigma,ExceptionInfo *exception)
770 blur_image=BlurImageChannel(image,DefaultChannels,radius,sigma,exception);
774 static double *GetBlurKernel(const size_t width,const double sigma)
788 Generate a 1-D convolution kernel.
790 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
791 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
792 if (kernel == (double *) NULL)
797 for (k=(-j); k <= j; k++)
799 kernel[i]=(double) (exp(-((double) k*k)/(2.0*MagickSigma*MagickSigma))/
800 (MagickSQ2PI*MagickSigma));
801 normalize+=kernel[i];
804 for (i=0; i < (ssize_t) width; i++)
805 kernel[i]/=normalize;
809 MagickExport Image *BlurImageChannel(const Image *image,
810 const ChannelType channel,const double radius,const double sigma,
811 ExceptionInfo *exception)
813 #define BlurImageTag "Blur/Image"
845 Initialize blur image attributes.
847 assert(image != (Image *) NULL);
848 assert(image->signature == MagickSignature);
849 if (image->debug != MagickFalse)
850 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
851 assert(exception != (ExceptionInfo *) NULL);
852 assert(exception->signature == MagickSignature);
853 blur_image=CloneImage(image,0,0,MagickTrue,exception);
854 if (blur_image == (Image *) NULL)
855 return((Image *) NULL);
856 if (fabs(sigma) <= MagickEpsilon)
858 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
860 InheritException(exception,&blur_image->exception);
861 blur_image=DestroyImage(blur_image);
862 return((Image *) NULL);
864 width=GetOptimalKernelWidth1D(radius,sigma);
865 kernel=GetBlurKernel(width,sigma);
866 if (kernel == (double *) NULL)
868 blur_image=DestroyImage(blur_image);
869 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
871 if (image->debug != MagickFalse)
874 format[MaxTextExtent],
877 register const double
880 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
881 " BlurImage with %.20g kernel:",(double) width);
882 message=AcquireString("");
884 for (i=0; i < (ssize_t) width; i++)
887 (void) FormatMagickString(format,MaxTextExtent,"%.20g: ",(double) i);
888 (void) ConcatenateString(&message,format);
889 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
890 (void) ConcatenateString(&message,format);
891 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
893 message=DestroyString(message);
900 GetMagickPixelPacket(image,&bias);
901 SetMagickPixelPacketBias(image,&bias);
902 image_view=AcquireCacheView(image);
903 blur_view=AcquireCacheView(blur_image);
904 #if defined(MAGICKCORE_OPENMP_SUPPORT)
905 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
907 for (y=0; y < (ssize_t) blur_image->rows; y++)
909 register const IndexPacket
912 register const PixelPacket
916 *restrict blur_indexes;
924 if (status == MagickFalse)
926 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y,
927 image->columns+width,1,exception);
928 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
930 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
935 indexes=GetCacheViewVirtualIndexQueue(image_view);
936 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
937 for (x=0; x < (ssize_t) blur_image->columns; x++)
942 register const double
945 register const PixelPacket
946 *restrict kernel_pixels;
954 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
956 for (i=0; i < (ssize_t) width; i++)
958 pixel.red+=(*k)*kernel_pixels->red;
959 pixel.green+=(*k)*kernel_pixels->green;
960 pixel.blue+=(*k)*kernel_pixels->blue;
964 if ((channel & RedChannel) != 0)
965 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
966 if ((channel & GreenChannel) != 0)
967 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
968 if ((channel & BlueChannel) != 0)
969 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
970 if ((channel & OpacityChannel) != 0)
974 for (i=0; i < (ssize_t) width; i++)
976 pixel.opacity+=(*k)*kernel_pixels->opacity;
980 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
982 if (((channel & IndexChannel) != 0) &&
983 (image->colorspace == CMYKColorspace))
985 register const IndexPacket
986 *restrict kernel_indexes;
989 kernel_indexes=indexes;
990 for (i=0; i < (ssize_t) width; i++)
992 pixel.index+=(*k)*(*kernel_indexes);
996 blur_indexes[x]=ClampToQuantum(pixel.index);
1006 for (i=0; i < (ssize_t) width; i++)
1008 alpha=(MagickRealType) (QuantumScale*
1009 GetAlphaPixelComponent(kernel_pixels));
1010 pixel.red+=(*k)*alpha*kernel_pixels->red;
1011 pixel.green+=(*k)*alpha*kernel_pixels->green;
1012 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
1017 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1018 if ((channel & RedChannel) != 0)
1019 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
1020 if ((channel & GreenChannel) != 0)
1021 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
1022 if ((channel & BlueChannel) != 0)
1023 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
1024 if ((channel & OpacityChannel) != 0)
1028 for (i=0; i < (ssize_t) width; i++)
1030 pixel.opacity+=(*k)*kernel_pixels->opacity;
1034 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1036 if (((channel & IndexChannel) != 0) &&
1037 (image->colorspace == CMYKColorspace))
1039 register const IndexPacket
1040 *restrict kernel_indexes;
1044 kernel_indexes=indexes;
1045 for (i=0; i < (ssize_t) width; i++)
1047 alpha=(MagickRealType) (QuantumScale*
1048 GetAlphaPixelComponent(kernel_pixels));
1049 pixel.index+=(*k)*alpha*(*kernel_indexes);
1054 blur_indexes[x]=ClampToQuantum(gamma*
1055 GetIndexPixelComponent(&pixel));
1062 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1064 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1069 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1070 #pragma omp critical (MagickCore_BlurImageChannel)
1072 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1073 blur_image->columns);
1074 if (proceed == MagickFalse)
1078 blur_view=DestroyCacheView(blur_view);
1079 image_view=DestroyCacheView(image_view);
1083 image_view=AcquireCacheView(blur_image);
1084 blur_view=AcquireCacheView(blur_image);
1085 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1086 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1088 for (x=0; x < (ssize_t) blur_image->columns; x++)
1090 register const IndexPacket
1093 register const PixelPacket
1096 register IndexPacket
1097 *restrict blur_indexes;
1099 register PixelPacket
1105 if (status == MagickFalse)
1107 p=GetCacheViewVirtualPixels(image_view,x,-((ssize_t) width/2L),1,
1108 image->rows+width,exception);
1109 q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
1110 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1115 indexes=GetCacheViewVirtualIndexQueue(image_view);
1116 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
1117 for (y=0; y < (ssize_t) blur_image->rows; y++)
1122 register const double
1125 register const PixelPacket
1126 *restrict kernel_pixels;
1134 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
1136 for (i=0; i < (ssize_t) width; i++)
1138 pixel.red+=(*k)*kernel_pixels->red;
1139 pixel.green+=(*k)*kernel_pixels->green;
1140 pixel.blue+=(*k)*kernel_pixels->blue;
1144 if ((channel & RedChannel) != 0)
1145 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
1146 if ((channel & GreenChannel) != 0)
1147 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
1148 if ((channel & BlueChannel) != 0)
1149 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
1150 if ((channel & OpacityChannel) != 0)
1154 for (i=0; i < (ssize_t) width; i++)
1156 pixel.opacity+=(*k)*kernel_pixels->opacity;
1160 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1162 if (((channel & IndexChannel) != 0) &&
1163 (image->colorspace == CMYKColorspace))
1165 register const IndexPacket
1166 *restrict kernel_indexes;
1169 kernel_indexes=indexes;
1170 for (i=0; i < (ssize_t) width; i++)
1172 pixel.index+=(*k)*(*kernel_indexes);
1176 blur_indexes[y]=ClampToQuantum(pixel.index);
1186 for (i=0; i < (ssize_t) width; i++)
1188 alpha=(MagickRealType) (QuantumScale*
1189 GetAlphaPixelComponent(kernel_pixels));
1190 pixel.red+=(*k)*alpha*kernel_pixels->red;
1191 pixel.green+=(*k)*alpha*kernel_pixels->green;
1192 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
1197 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1198 if ((channel & RedChannel) != 0)
1199 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
1200 if ((channel & GreenChannel) != 0)
1201 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
1202 if ((channel & BlueChannel) != 0)
1203 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
1204 if ((channel & OpacityChannel) != 0)
1208 for (i=0; i < (ssize_t) width; i++)
1210 pixel.opacity+=(*k)*kernel_pixels->opacity;
1214 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1216 if (((channel & IndexChannel) != 0) &&
1217 (image->colorspace == CMYKColorspace))
1219 register const IndexPacket
1220 *restrict kernel_indexes;
1224 kernel_indexes=indexes;
1225 for (i=0; i < (ssize_t) width; i++)
1227 alpha=(MagickRealType) (QuantumScale*
1228 GetAlphaPixelComponent(kernel_pixels));
1229 pixel.index+=(*k)*alpha*(*kernel_indexes);
1234 blur_indexes[y]=ClampToQuantum(gamma*
1235 GetIndexPixelComponent(&pixel));
1242 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1244 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1249 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1250 #pragma omp critical (MagickCore_BlurImageChannel)
1252 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1253 blur_image->columns);
1254 if (proceed == MagickFalse)
1258 blur_view=DestroyCacheView(blur_view);
1259 image_view=DestroyCacheView(image_view);
1260 kernel=(double *) RelinquishMagickMemory(kernel);
1261 if (status == MagickFalse)
1262 blur_image=DestroyImage(blur_image);
1263 blur_image->type=image->type;
1268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1272 % C o n v o l v e I m a g e %
1276 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1278 % ConvolveImage() applies a custom convolution kernel to the image.
1280 % The format of the ConvolveImage method is:
1282 % Image *ConvolveImage(const Image *image,const size_t order,
1283 % const double *kernel,ExceptionInfo *exception)
1284 % Image *ConvolveImageChannel(const Image *image,const ChannelType channel,
1285 % const size_t order,const double *kernel,ExceptionInfo *exception)
1287 % A description of each parameter follows:
1289 % o image: the image.
1291 % o channel: the channel type.
1293 % o order: the number of columns and rows in the filter kernel.
1295 % o kernel: An array of double representing the convolution kernel.
1297 % o exception: return any errors or warnings in this structure.
1301 MagickExport Image *ConvolveImage(const Image *image,const size_t order,
1302 const double *kernel,ExceptionInfo *exception)
1307 convolve_image=ConvolveImageChannel(image,DefaultChannels,order,kernel,
1309 return(convolve_image);
1312 MagickExport Image *ConvolveImageChannel(const Image *image,
1313 const ChannelType channel,const size_t order,const double *kernel,
1314 ExceptionInfo *exception)
1316 #define ConvolveImageTag "Convolve/Image"
1350 Initialize convolve image attributes.
1352 assert(image != (Image *) NULL);
1353 assert(image->signature == MagickSignature);
1354 if (image->debug != MagickFalse)
1355 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1356 assert(exception != (ExceptionInfo *) NULL);
1357 assert(exception->signature == MagickSignature);
1359 if ((width % 2) == 0)
1360 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
1361 convolve_image=CloneImage(image,0,0,MagickTrue,exception);
1362 if (convolve_image == (Image *) NULL)
1363 return((Image *) NULL);
1364 if (SetImageStorageClass(convolve_image,DirectClass) == MagickFalse)
1366 InheritException(exception,&convolve_image->exception);
1367 convolve_image=DestroyImage(convolve_image);
1368 return((Image *) NULL);
1370 if (image->debug != MagickFalse)
1373 format[MaxTextExtent],
1376 register const double
1383 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1384 " ConvolveImage with %.20gx%.20g kernel:",(double) width,(double)
1386 message=AcquireString("");
1388 for (v=0; v < (ssize_t) width; v++)
1391 (void) FormatMagickString(format,MaxTextExtent,"%.20g: ",(double) v);
1392 (void) ConcatenateString(&message,format);
1393 for (u=0; u < (ssize_t) width; u++)
1395 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
1396 (void) ConcatenateString(&message,format);
1398 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
1400 message=DestroyString(message);
1405 normal_kernel=(double *) AcquireQuantumMemory(width*width,
1406 sizeof(*normal_kernel));
1407 if (normal_kernel == (double *) NULL)
1409 convolve_image=DestroyImage(convolve_image);
1410 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1413 for (i=0; i < (ssize_t) (width*width); i++)
1415 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1416 for (i=0; i < (ssize_t) (width*width); i++)
1417 normal_kernel[i]=gamma*kernel[i];
1423 GetMagickPixelPacket(image,&bias);
1424 SetMagickPixelPacketBias(image,&bias);
1425 image_view=AcquireCacheView(image);
1426 convolve_view=AcquireCacheView(convolve_image);
1427 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1428 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1430 for (y=0; y < (ssize_t) image->rows; y++)
1435 register const IndexPacket
1438 register const PixelPacket
1441 register IndexPacket
1442 *restrict convolve_indexes;
1444 register PixelPacket
1450 if (status == MagickFalse)
1452 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
1453 (width/2L),image->columns+width,width,exception);
1454 q=GetCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
1456 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1461 indexes=GetCacheViewVirtualIndexQueue(image_view);
1462 convolve_indexes=GetCacheViewAuthenticIndexQueue(convolve_view);
1463 for (x=0; x < (ssize_t) image->columns; x++)
1468 register const double
1471 register const PixelPacket
1472 *restrict kernel_pixels;
1483 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
1485 for (v=0; v < (ssize_t) width; v++)
1487 for (u=0; u < (ssize_t) width; u++)
1489 pixel.red+=(*k)*kernel_pixels[u].red;
1490 pixel.green+=(*k)*kernel_pixels[u].green;
1491 pixel.blue+=(*k)*kernel_pixels[u].blue;
1494 kernel_pixels+=image->columns+width;
1496 if ((channel & RedChannel) != 0)
1497 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
1498 if ((channel & GreenChannel) != 0)
1499 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
1500 if ((channel & BlueChannel) != 0)
1501 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
1502 if ((channel & OpacityChannel) != 0)
1506 for (v=0; v < (ssize_t) width; v++)
1508 for (u=0; u < (ssize_t) width; u++)
1510 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1513 kernel_pixels+=image->columns+width;
1515 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1517 if (((channel & IndexChannel) != 0) &&
1518 (image->colorspace == CMYKColorspace))
1520 register const IndexPacket
1521 *restrict kernel_indexes;
1524 kernel_indexes=indexes;
1525 for (v=0; v < (ssize_t) width; v++)
1527 for (u=0; u < (ssize_t) width; u++)
1529 pixel.index+=(*k)*kernel_indexes[u];
1532 kernel_indexes+=image->columns+width;
1534 convolve_indexes[x]=ClampToQuantum(pixel.index);
1544 for (v=0; v < (ssize_t) width; v++)
1546 for (u=0; u < (ssize_t) width; u++)
1548 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1549 kernel_pixels[u].opacity));
1550 pixel.red+=(*k)*alpha*kernel_pixels[u].red;
1551 pixel.green+=(*k)*alpha*kernel_pixels[u].green;
1552 pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
1556 kernel_pixels+=image->columns+width;
1558 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1559 if ((channel & RedChannel) != 0)
1560 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
1561 if ((channel & GreenChannel) != 0)
1562 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
1563 if ((channel & BlueChannel) != 0)
1564 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
1565 if ((channel & OpacityChannel) != 0)
1569 for (v=0; v < (ssize_t) width; v++)
1571 for (u=0; u < (ssize_t) width; u++)
1573 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1576 kernel_pixels+=image->columns+width;
1578 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1580 if (((channel & IndexChannel) != 0) &&
1581 (image->colorspace == CMYKColorspace))
1583 register const IndexPacket
1584 *restrict kernel_indexes;
1588 kernel_indexes=indexes;
1589 for (v=0; v < (ssize_t) width; v++)
1591 for (u=0; u < (ssize_t) width; u++)
1593 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1594 kernel_pixels[u].opacity));
1595 pixel.index+=(*k)*alpha*kernel_indexes[u];
1598 kernel_pixels+=image->columns+width;
1599 kernel_indexes+=image->columns+width;
1601 convolve_indexes[x]=ClampToQuantum(gamma*
1602 GetIndexPixelComponent(&pixel));
1609 sync=SyncCacheViewAuthenticPixels(convolve_view,exception);
1610 if (sync == MagickFalse)
1612 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1617 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1618 #pragma omp critical (MagickCore_ConvolveImageChannel)
1620 proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1621 if (proceed == MagickFalse)
1625 convolve_image->type=image->type;
1626 convolve_view=DestroyCacheView(convolve_view);
1627 image_view=DestroyCacheView(image_view);
1628 normal_kernel=(double *) RelinquishMagickMemory(normal_kernel);
1629 if (status == MagickFalse)
1630 convolve_image=DestroyImage(convolve_image);
1631 return(convolve_image);
1635 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1639 % D e s p e c k l e I m a g e %
1643 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1645 % DespeckleImage() reduces the speckle noise in an image while perserving the
1646 % edges of the original image.
1648 % The format of the DespeckleImage method is:
1650 % Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1652 % A description of each parameter follows:
1654 % o image: the image.
1656 % o exception: return any errors or warnings in this structure.
1660 static void Hull(const ssize_t x_offset,const ssize_t y_offset,
1661 const size_t columns,const size_t rows,Quantum *f,Quantum *g,
1679 assert(f != (Quantum *) NULL);
1680 assert(g != (Quantum *) NULL);
1683 r=p+(y_offset*((ssize_t) columns+2)+x_offset);
1684 for (y=0; y < (ssize_t) rows; y++)
1690 for (x=(ssize_t) columns; x != 0; x--)
1692 v=(MagickRealType) (*p);
1693 if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1694 v+=ScaleCharToQuantum(1);
1701 for (x=(ssize_t) columns; x != 0; x--)
1703 v=(MagickRealType) (*p);
1704 if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
1705 v-=(ssize_t) ScaleCharToQuantum(1);
1717 r=q+(y_offset*((ssize_t) columns+2)+x_offset);
1718 s=q-(y_offset*((ssize_t) columns+2)+x_offset);
1719 for (y=0; y < (ssize_t) rows; y++)
1726 for (x=(ssize_t) columns; x != 0; x--)
1728 v=(MagickRealType) (*q);
1729 if (((MagickRealType) *s >=
1730 (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1731 ((MagickRealType) *r > v))
1732 v+=ScaleCharToQuantum(1);
1740 for (x=(ssize_t) columns; x != 0; x--)
1742 v=(MagickRealType) (*q);
1743 if (((MagickRealType) *s <=
1744 (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1745 ((MagickRealType) *r < v))
1746 v-=(MagickRealType) ScaleCharToQuantum(1);
1760 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1762 #define DespeckleImageTag "Despeckle/Image"
1785 static const ssize_t
1786 X[4] = {0, 1, 1,-1},
1787 Y[4] = {1, 0, 1, 1};
1790 Allocate despeckled image.
1792 assert(image != (const Image *) NULL);
1793 assert(image->signature == MagickSignature);
1794 if (image->debug != MagickFalse)
1795 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1796 assert(exception != (ExceptionInfo *) NULL);
1797 assert(exception->signature == MagickSignature);
1798 despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1800 if (despeckle_image == (Image *) NULL)
1801 return((Image *) NULL);
1802 if (SetImageStorageClass(despeckle_image,DirectClass) == MagickFalse)
1804 InheritException(exception,&despeckle_image->exception);
1805 despeckle_image=DestroyImage(despeckle_image);
1806 return((Image *) NULL);
1809 Allocate image buffers.
1811 length=(size_t) ((image->columns+2)*(image->rows+2));
1812 pixels=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1813 buffers=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1814 if ((pixels == (Quantum *) NULL) || (buffers == (Quantum *) NULL))
1816 if (buffers != (Quantum *) NULL)
1817 buffers=(Quantum *) RelinquishMagickMemory(buffers);
1818 if (pixels != (Quantum *) NULL)
1819 pixels=(Quantum *) RelinquishMagickMemory(pixels);
1820 despeckle_image=DestroyImage(despeckle_image);
1821 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1824 Reduce speckle in the image.
1827 number_channels=(size_t) (image->colorspace == CMYKColorspace ? 5 : 4);
1828 image_view=AcquireCacheView(image);
1829 despeckle_view=AcquireCacheView(despeckle_image);
1830 for (i=0; i < (ssize_t) number_channels; i++)
1844 if (status == MagickFalse)
1847 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
1849 j=(ssize_t) image->columns+2;
1850 for (y=0; y < (ssize_t) image->rows; y++)
1852 register const IndexPacket
1855 register const PixelPacket
1858 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1859 if (p == (const PixelPacket *) NULL)
1861 indexes=GetCacheViewVirtualIndexQueue(image_view);
1863 for (x=0; x < (ssize_t) image->columns; x++)
1867 case 0: pixel[j]=GetRedPixelComponent(p); break;
1868 case 1: pixel[j]=GetGreenPixelComponent(p); break;
1869 case 2: pixel[j]=GetBluePixelComponent(p); break;
1870 case 3: pixel[j]=GetOpacityPixelComponent(p); break;
1871 case 4: pixel[j]=GetBlackPixelComponent(indexes,x); break;
1879 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1880 for (k=0; k < 4; k++)
1882 Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,1);
1883 Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,1);
1884 Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,-1);
1885 Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,-1);
1887 j=(ssize_t) image->columns+2;
1888 for (y=0; y < (ssize_t) image->rows; y++)
1893 register IndexPacket
1896 register PixelPacket
1899 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1901 if (q == (PixelPacket *) NULL)
1903 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1905 for (x=0; x < (ssize_t) image->columns; x++)
1909 case 0: q->red=pixel[j]; break;
1910 case 1: q->green=pixel[j]; break;
1911 case 2: q->blue=pixel[j]; break;
1912 case 3: q->opacity=pixel[j]; break;
1913 case 4: indexes[x]=pixel[j]; break;
1919 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1920 if (sync == MagickFalse)
1927 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1932 proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1934 if (proceed == MagickFalse)
1938 despeckle_view=DestroyCacheView(despeckle_view);
1939 image_view=DestroyCacheView(image_view);
1940 buffers=(Quantum *) RelinquishMagickMemory(buffers);
1941 pixels=(Quantum *) RelinquishMagickMemory(pixels);
1942 despeckle_image->type=image->type;
1943 if (status == MagickFalse)
1944 despeckle_image=DestroyImage(despeckle_image);
1945 return(despeckle_image);
1949 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1953 % E d g e I m a g e %
1957 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1959 % EdgeImage() finds edges in an image. Radius defines the radius of the
1960 % convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1963 % The format of the EdgeImage method is:
1965 % Image *EdgeImage(const Image *image,const double radius,
1966 % ExceptionInfo *exception)
1968 % A description of each parameter follows:
1970 % o image: the image.
1972 % o radius: the radius of the pixel neighborhood.
1974 % o exception: return any errors or warnings in this structure.
1977 MagickExport Image *EdgeImage(const Image *image,const double radius,
1978 ExceptionInfo *exception)
1992 assert(image != (const Image *) NULL);
1993 assert(image->signature == MagickSignature);
1994 if (image->debug != MagickFalse)
1995 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1996 assert(exception != (ExceptionInfo *) NULL);
1997 assert(exception->signature == MagickSignature);
1998 width=GetOptimalKernelWidth1D(radius,0.5);
1999 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2000 if (kernel == (double *) NULL)
2001 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2002 for (i=0; i < (ssize_t) (width*width); i++)
2004 kernel[i/2]=(double) (width*width-1.0);
2005 edge_image=ConvolveImage(image,width,kernel,exception);
2006 kernel=(double *) RelinquishMagickMemory(kernel);
2011 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2015 % E m b o s s I m a g e %
2019 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2021 % EmbossImage() returns a grayscale image with a three-dimensional effect.
2022 % We convolve the image with a Gaussian operator of the given radius and
2023 % standard deviation (sigma). For reasonable results, radius should be
2024 % larger than sigma. Use a radius of 0 and Emboss() selects a suitable
2027 % The format of the EmbossImage method is:
2029 % Image *EmbossImage(const Image *image,const double radius,
2030 % const double sigma,ExceptionInfo *exception)
2032 % A description of each parameter follows:
2034 % o image: the image.
2036 % o radius: the radius of the pixel neighborhood.
2038 % o sigma: the standard deviation of the Gaussian, in pixels.
2040 % o exception: return any errors or warnings in this structure.
2043 MagickExport Image *EmbossImage(const Image *image,const double radius,
2044 const double sigma,ExceptionInfo *exception)
2064 assert(image != (Image *) NULL);
2065 assert(image->signature == MagickSignature);
2066 if (image->debug != MagickFalse)
2067 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2068 assert(exception != (ExceptionInfo *) NULL);
2069 assert(exception->signature == MagickSignature);
2070 width=GetOptimalKernelWidth2D(radius,sigma);
2071 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2072 if (kernel == (double *) NULL)
2073 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2074 j=(ssize_t) width/2;
2077 for (v=(-j); v <= j; v++)
2079 for (u=(-j); u <= j; u++)
2081 kernel[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*
2082 exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
2083 (2.0*MagickPI*MagickSigma*MagickSigma));
2090 emboss_image=ConvolveImage(image,width,kernel,exception);
2091 if (emboss_image != (Image *) NULL)
2092 (void) EqualizeImage(emboss_image);
2093 kernel=(double *) RelinquishMagickMemory(kernel);
2094 return(emboss_image);
2098 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2102 % F i l t e r I m a g e %
2106 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2108 % FilterImage() applies a custom convolution kernel to the image.
2110 % The format of the FilterImage method is:
2112 % Image *FilterImage(const Image *image,const KernelInfo *kernel,
2113 % ExceptionInfo *exception)
2114 % Image *FilterImageChannel(const Image *image,const ChannelType channel,
2115 % const KernelInfo *kernel,ExceptionInfo *exception)
2117 % A description of each parameter follows:
2119 % o image: the image.
2121 % o channel: the channel type.
2123 % o kernel: the filtering kernel.
2125 % o exception: return any errors or warnings in this structure.
2129 MagickExport Image *FilterImage(const Image *image,const KernelInfo *kernel,
2130 ExceptionInfo *exception)
2135 filter_image=FilterImageChannel(image,DefaultChannels,kernel,exception);
2136 return(filter_image);
2139 MagickExport Image *FilterImageChannel(const Image *image,
2140 const ChannelType channel,const KernelInfo *kernel,ExceptionInfo *exception)
2142 #define FilterImageTag "Filter/Image"
2164 Initialize filter image attributes.
2166 assert(image != (Image *) NULL);
2167 assert(image->signature == MagickSignature);
2168 if (image->debug != MagickFalse)
2169 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2170 assert(exception != (ExceptionInfo *) NULL);
2171 assert(exception->signature == MagickSignature);
2172 if ((kernel->width % 2) == 0)
2173 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
2174 filter_image=CloneImage(image,0,0,MagickTrue,exception);
2175 if (filter_image == (Image *) NULL)
2176 return((Image *) NULL);
2177 if (SetImageStorageClass(filter_image,DirectClass) == MagickFalse)
2179 InheritException(exception,&filter_image->exception);
2180 filter_image=DestroyImage(filter_image);
2181 return((Image *) NULL);
2183 if (image->debug != MagickFalse)
2186 format[MaxTextExtent],
2189 register const double
2196 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2197 " FilterImage with %.20gx%.20g kernel:",(double) kernel->width,(double)
2199 message=AcquireString("");
2201 for (v=0; v < (ssize_t) kernel->height; v++)
2204 (void) FormatMagickString(format,MaxTextExtent,"%.20g: ",(double) v);
2205 (void) ConcatenateString(&message,format);
2206 for (u=0; u < (ssize_t) kernel->width; u++)
2208 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
2209 (void) ConcatenateString(&message,format);
2211 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
2213 message=DestroyString(message);
2215 status=AccelerateConvolveImage(image,kernel,filter_image,exception);
2216 if (status == MagickTrue)
2217 return(filter_image);
2223 GetMagickPixelPacket(image,&bias);
2224 SetMagickPixelPacketBias(image,&bias);
2225 image_view=AcquireCacheView(image);
2226 filter_view=AcquireCacheView(filter_image);
2227 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2228 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2230 for (y=0; y < (ssize_t) image->rows; y++)
2235 register const IndexPacket
2238 register const PixelPacket
2241 register IndexPacket
2242 *restrict filter_indexes;
2244 register PixelPacket
2250 if (status == MagickFalse)
2252 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel->width/2L),
2253 y-(ssize_t) (kernel->height/2L),image->columns+kernel->width,
2254 kernel->height,exception);
2255 q=GetCacheViewAuthenticPixels(filter_view,0,y,filter_image->columns,1,
2257 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2262 indexes=GetCacheViewVirtualIndexQueue(image_view);
2263 filter_indexes=GetCacheViewAuthenticIndexQueue(filter_view);
2264 for (x=0; x < (ssize_t) image->columns; x++)
2269 register const double
2272 register const PixelPacket
2273 *restrict kernel_pixels;
2284 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
2286 for (v=0; v < (ssize_t) kernel->width; v++)
2288 for (u=0; u < (ssize_t) kernel->height; u++)
2290 pixel.red+=(*k)*kernel_pixels[u].red;
2291 pixel.green+=(*k)*kernel_pixels[u].green;
2292 pixel.blue+=(*k)*kernel_pixels[u].blue;
2295 kernel_pixels+=image->columns+kernel->width;
2297 if ((channel & RedChannel) != 0)
2298 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2299 if ((channel & GreenChannel) != 0)
2300 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2301 if ((channel & BlueChannel) != 0)
2302 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2303 if ((channel & OpacityChannel) != 0)
2307 for (v=0; v < (ssize_t) kernel->width; v++)
2309 for (u=0; u < (ssize_t) kernel->height; u++)
2311 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
2314 kernel_pixels+=image->columns+kernel->width;
2316 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2318 if (((channel & IndexChannel) != 0) &&
2319 (image->colorspace == CMYKColorspace))
2321 register const IndexPacket
2322 *restrict kernel_indexes;
2325 kernel_indexes=indexes;
2326 for (v=0; v < (ssize_t) kernel->width; v++)
2328 for (u=0; u < (ssize_t) kernel->height; u++)
2330 pixel.index+=(*k)*kernel_indexes[u];
2333 kernel_indexes+=image->columns+kernel->width;
2335 filter_indexes[x]=ClampToQuantum(pixel.index);
2345 for (v=0; v < (ssize_t) kernel->width; v++)
2347 for (u=0; u < (ssize_t) kernel->height; u++)
2349 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
2350 kernel_pixels[u].opacity));
2351 pixel.red+=(*k)*alpha*kernel_pixels[u].red;
2352 pixel.green+=(*k)*alpha*kernel_pixels[u].green;
2353 pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
2357 kernel_pixels+=image->columns+kernel->width;
2359 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2360 if ((channel & RedChannel) != 0)
2361 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2362 if ((channel & GreenChannel) != 0)
2363 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2364 if ((channel & BlueChannel) != 0)
2365 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2366 if ((channel & OpacityChannel) != 0)
2370 for (v=0; v < (ssize_t) kernel->width; v++)
2372 for (u=0; u < (ssize_t) kernel->height; u++)
2374 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
2377 kernel_pixels+=image->columns+kernel->width;
2379 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2381 if (((channel & IndexChannel) != 0) &&
2382 (image->colorspace == CMYKColorspace))
2384 register const IndexPacket
2385 *restrict kernel_indexes;
2389 kernel_indexes=indexes;
2390 for (v=0; v < (ssize_t) kernel->width; v++)
2392 for (u=0; u < (ssize_t) kernel->height; u++)
2394 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
2395 kernel_pixels[u].opacity));
2396 pixel.index+=(*k)*alpha*kernel_indexes[u];
2399 kernel_pixels+=image->columns+kernel->width;
2400 kernel_indexes+=image->columns+kernel->width;
2402 filter_indexes[x]=ClampToQuantum(gamma*
2403 GetIndexPixelComponent(&pixel));
2410 sync=SyncCacheViewAuthenticPixels(filter_view,exception);
2411 if (sync == MagickFalse)
2413 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2418 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2419 #pragma omp critical (MagickCore_FilterImageChannel)
2421 proceed=SetImageProgress(image,FilterImageTag,progress++,image->rows);
2422 if (proceed == MagickFalse)
2426 filter_image->type=image->type;
2427 filter_view=DestroyCacheView(filter_view);
2428 image_view=DestroyCacheView(image_view);
2429 if (status == MagickFalse)
2430 filter_image=DestroyImage(filter_image);
2431 return(filter_image);
2435 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2439 % G a u s s i a n B l u r I m a g e %
2443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2445 % GaussianBlurImage() blurs an image. We convolve the image with a
2446 % Gaussian operator of the given radius and standard deviation (sigma).
2447 % For reasonable results, the radius should be larger than sigma. Use a
2448 % radius of 0 and GaussianBlurImage() selects a suitable radius for you
2450 % The format of the GaussianBlurImage method is:
2452 % Image *GaussianBlurImage(const Image *image,onst double radius,
2453 % const double sigma,ExceptionInfo *exception)
2454 % Image *GaussianBlurImageChannel(const Image *image,
2455 % const ChannelType channel,const double radius,const double sigma,
2456 % ExceptionInfo *exception)
2458 % A description of each parameter follows:
2460 % o image: the image.
2462 % o channel: the channel type.
2464 % o radius: the radius of the Gaussian, in pixels, not counting the center
2467 % o sigma: the standard deviation of the Gaussian, in pixels.
2469 % o exception: return any errors or warnings in this structure.
2473 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
2474 const double sigma,ExceptionInfo *exception)
2479 blur_image=GaussianBlurImageChannel(image,DefaultChannels,radius,sigma,
2484 MagickExport Image *GaussianBlurImageChannel(const Image *image,
2485 const ChannelType channel,const double radius,const double sigma,
2486 ExceptionInfo *exception)
2505 assert(image != (const Image *) NULL);
2506 assert(image->signature == MagickSignature);
2507 if (image->debug != MagickFalse)
2508 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2509 assert(exception != (ExceptionInfo *) NULL);
2510 assert(exception->signature == MagickSignature);
2511 width=GetOptimalKernelWidth2D(radius,sigma);
2512 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2513 if (kernel == (double *) NULL)
2514 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2515 j=(ssize_t) width/2;
2517 for (v=(-j); v <= j; v++)
2519 for (u=(-j); u <= j; u++)
2520 kernel[i++]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
2521 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
2523 blur_image=ConvolveImageChannel(image,channel,width,kernel,exception);
2524 kernel=(double *) RelinquishMagickMemory(kernel);
2529 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2533 % M e d i a n F i l t e r I m a g e %
2537 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2539 % MedianFilterImage() applies a digital filter that improves the quality
2540 % of a noisy image. Each pixel is replaced by the median in a set of
2541 % neighboring pixels as defined by radius.
2543 % The algorithm was contributed by Mike Edmonds and implements an insertion
2544 % sort for selecting median color-channel values. For more on this algorithm
2545 % see "Skip Lists: A probabilistic Alternative to Balanced Trees" by William
2546 % Pugh in the June 1990 of Communications of the ACM.
2548 % The format of the MedianFilterImage method is:
2550 % Image *MedianFilterImage(const Image *image,const double radius,
2551 % ExceptionInfo *exception)
2553 % A description of each parameter follows:
2555 % o image: the image.
2557 % o radius: the radius of the pixel neighborhood.
2559 % o exception: return any errors or warnings in this structure.
2563 #define ListChannels 5
2565 typedef struct _ListNode
2573 typedef struct _SkipList
2582 typedef struct _PixelList
2590 lists[ListChannels];
2593 static PixelList *DestroyPixelList(PixelList *pixel_list)
2598 if (pixel_list == (PixelList *) NULL)
2599 return((PixelList *) NULL);
2600 for (i=0; i < ListChannels; i++)
2601 if (pixel_list->lists[i].nodes != (ListNode *) NULL)
2602 pixel_list->lists[i].nodes=(ListNode *) RelinquishMagickMemory(
2603 pixel_list->lists[i].nodes);
2604 pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
2608 static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
2613 assert(pixel_list != (PixelList **) NULL);
2614 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
2615 if (pixel_list[i] != (PixelList *) NULL)
2616 pixel_list[i]=DestroyPixelList(pixel_list[i]);
2617 pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
2621 static PixelList *AcquirePixelList(const size_t width)
2629 pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
2630 if (pixel_list == (PixelList *) NULL)
2632 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
2633 pixel_list->center=width*width/2;
2634 for (i=0; i < ListChannels; i++)
2636 pixel_list->lists[i].nodes=(ListNode *) AcquireQuantumMemory(65537UL,
2637 sizeof(*pixel_list->lists[i].nodes));
2638 if (pixel_list->lists[i].nodes == (ListNode *) NULL)
2639 return(DestroyPixelList(pixel_list));
2640 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
2641 sizeof(*pixel_list->lists[i].nodes));
2643 pixel_list->signature=MagickSignature;
2647 static PixelList **AcquirePixelListThreadSet(const size_t width)
2658 number_threads=GetOpenMPMaximumThreads();
2659 pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
2660 sizeof(*pixel_list));
2661 if (pixel_list == (PixelList **) NULL)
2662 return((PixelList **) NULL);
2663 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
2664 for (i=0; i < (ssize_t) number_threads; i++)
2666 pixel_list[i]=AcquirePixelList(width);
2667 if (pixel_list[i] == (PixelList *) NULL)
2668 return(DestroyPixelListThreadSet(pixel_list));
2673 static void AddNodePixelList(PixelList *pixel_list,const ssize_t channel,
2687 Initialize the node.
2689 list=pixel_list->lists+channel;
2690 list->nodes[color].signature=pixel_list->signature;
2691 list->nodes[color].count=1;
2693 Determine where it belongs in the list.
2696 for (level=list->level; level >= 0; level--)
2698 while (list->nodes[search].next[level] < color)
2699 search=list->nodes[search].next[level];
2700 update[level]=search;
2703 Generate a pseudo-random level for this node.
2705 for (level=0; ; level++)
2707 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
2708 if ((pixel_list->seed & 0x300) != 0x300)
2713 if (level > (list->level+2))
2714 level=list->level+2;
2716 If we're raising the list's level, link back to the root node.
2718 while (level > list->level)
2721 update[list->level]=65536UL;
2724 Link the node into the skip-list.
2728 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
2729 list->nodes[update[level]].next[level]=color;
2731 while (level-- > 0);
2734 static MagickPixelPacket GetPixelList(PixelList *pixel_list)
2751 channels[ListChannels];
2754 Find the median value for each of the color.
2756 center=pixel_list->center;
2757 for (channel=0; channel < 5; channel++)
2759 list=pixel_list->lists+channel;
2764 color=list->nodes[color].next[0];
2765 count+=list->nodes[color].count;
2767 while (count <= center);
2768 channels[channel]=(unsigned short) color;
2770 GetMagickPixelPacket((const Image *) NULL,&pixel);
2771 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
2772 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
2773 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
2774 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
2775 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
2779 static inline void InsertPixelList(const Image *image,const PixelPacket *pixel,
2780 const IndexPacket *indexes,PixelList *pixel_list)
2788 index=ScaleQuantumToShort(pixel->red);
2789 signature=pixel_list->lists[0].nodes[index].signature;
2790 if (signature == pixel_list->signature)
2791 pixel_list->lists[0].nodes[index].count++;
2793 AddNodePixelList(pixel_list,0,index);
2794 index=ScaleQuantumToShort(pixel->green);
2795 signature=pixel_list->lists[1].nodes[index].signature;
2796 if (signature == pixel_list->signature)
2797 pixel_list->lists[1].nodes[index].count++;
2799 AddNodePixelList(pixel_list,1,index);
2800 index=ScaleQuantumToShort(pixel->blue);
2801 signature=pixel_list->lists[2].nodes[index].signature;
2802 if (signature == pixel_list->signature)
2803 pixel_list->lists[2].nodes[index].count++;
2805 AddNodePixelList(pixel_list,2,index);
2806 index=ScaleQuantumToShort(pixel->opacity);
2807 signature=pixel_list->lists[3].nodes[index].signature;
2808 if (signature == pixel_list->signature)
2809 pixel_list->lists[3].nodes[index].count++;
2811 AddNodePixelList(pixel_list,3,index);
2812 if (image->colorspace == CMYKColorspace)
2813 index=ScaleQuantumToShort(*indexes);
2814 signature=pixel_list->lists[4].nodes[index].signature;
2815 if (signature == pixel_list->signature)
2816 pixel_list->lists[4].nodes[index].count++;
2818 AddNodePixelList(pixel_list,4,index);
2821 static void ResetPixelList(PixelList *pixel_list)
2836 Reset the skip-list.
2838 for (channel=0; channel < 5; channel++)
2840 list=pixel_list->lists+channel;
2841 root=list->nodes+65536UL;
2843 for (level=0; level < 9; level++)
2844 root->next[level]=65536UL;
2846 pixel_list->seed=pixel_list->signature++;
2849 MagickExport Image *MedianFilterImage(const Image *image,const double radius,
2850 ExceptionInfo *exception)
2852 #define MedianFilterImageTag "MedianFilter/Image"
2868 **restrict pixel_list;
2877 Initialize median image attributes.
2879 assert(image != (Image *) NULL);
2880 assert(image->signature == MagickSignature);
2881 if (image->debug != MagickFalse)
2882 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2883 assert(exception != (ExceptionInfo *) NULL);
2884 assert(exception->signature == MagickSignature);
2885 width=GetOptimalKernelWidth2D(radius,0.5);
2886 median_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2888 if (median_image == (Image *) NULL)
2889 return((Image *) NULL);
2890 if (SetImageStorageClass(median_image,DirectClass) == MagickFalse)
2892 InheritException(exception,&median_image->exception);
2893 median_image=DestroyImage(median_image);
2894 return((Image *) NULL);
2896 pixel_list=AcquirePixelListThreadSet(width);
2897 if (pixel_list == (PixelList **) NULL)
2899 median_image=DestroyImage(median_image);
2900 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2903 Median filter each image row.
2907 image_view=AcquireCacheView(image);
2908 median_view=AcquireCacheView(median_image);
2909 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2910 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2912 for (y=0; y < (ssize_t) median_image->rows; y++)
2915 id = GetOpenMPThreadId();
2917 register const IndexPacket
2920 register const PixelPacket
2923 register IndexPacket
2924 *restrict median_indexes;
2926 register PixelPacket
2932 if (status == MagickFalse)
2934 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
2935 (width/2L),image->columns+width,width,exception);
2936 q=QueueCacheViewAuthenticPixels(median_view,0,y,median_image->columns,1,
2938 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2943 indexes=GetCacheViewVirtualIndexQueue(image_view);
2944 median_indexes=GetCacheViewAuthenticIndexQueue(median_view);
2945 for (x=0; x < (ssize_t) median_image->columns; x++)
2950 register const IndexPacket
2953 register const PixelPacket
2962 ResetPixelList(pixel_list[id]);
2963 for (v=0; v < (ssize_t) width; v++)
2965 for (u=0; u < (ssize_t) width; u++)
2966 InsertPixelList(image,r+u,s+u,pixel_list[id]);
2967 r+=image->columns+width;
2968 s+=image->columns+width;
2970 pixel=GetPixelList(pixel_list[id]);
2971 SetPixelPacket(median_image,&pixel,q,median_indexes+x);
2975 if (SyncCacheViewAuthenticPixels(median_view,exception) == MagickFalse)
2977 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2982 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2983 #pragma omp critical (MagickCore_MedianFilterImage)
2985 proceed=SetImageProgress(image,MedianFilterImageTag,progress++,
2987 if (proceed == MagickFalse)
2991 median_view=DestroyCacheView(median_view);
2992 image_view=DestroyCacheView(image_view);
2993 pixel_list=DestroyPixelListThreadSet(pixel_list);
2994 return(median_image);
2998 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3002 % M o d e I m a g e %
3006 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3008 % ModeImage() makes each pixel the 'predominate color' of the neighborhood
3009 % of the specified radius.
3011 % The format of the ModeImage method is:
3013 % Image *ModeImage(const Image *image,const double radius,
3014 % ExceptionInfo *exception)
3016 % A description of each parameter follows:
3018 % o image: the image.
3020 % o radius: the radius of the pixel neighborhood.
3022 % o exception: return any errors or warnings in this structure.
3026 static MagickPixelPacket GetModePixelList(PixelList *pixel_list)
3048 Make each pixel the 'predominate color' of the specified neighborhood.
3050 width=pixel_list->center << 1;
3051 for (channel=0; channel < 5; channel++)
3053 list=pixel_list->lists+channel;
3056 max_count=list->nodes[mode].count;
3060 color=list->nodes[color].next[0];
3061 if (list->nodes[color].count > max_count)
3064 max_count=list->nodes[mode].count;
3066 count+=list->nodes[color].count;
3068 while (count <= width);
3069 channels[channel]=(unsigned short) mode;
3071 GetMagickPixelPacket((const Image *) NULL,&pixel);
3072 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
3073 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
3074 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
3075 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
3076 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
3080 MagickExport Image *ModeImage(const Image *image,const double radius,
3081 ExceptionInfo *exception)
3083 #define ModeImageTag "Mode/Image"
3099 **restrict pixel_list;
3108 Initialize mode image attributes.
3110 assert(image != (Image *) NULL);
3111 assert(image->signature == MagickSignature);
3112 if (image->debug != MagickFalse)
3113 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3114 assert(exception != (ExceptionInfo *) NULL);
3115 assert(exception->signature == MagickSignature);
3116 width=GetOptimalKernelWidth2D(radius,0.5);
3117 mode_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3119 if (mode_image == (Image *) NULL)
3120 return((Image *) NULL);
3121 if (SetImageStorageClass(mode_image,DirectClass) == MagickFalse)
3123 InheritException(exception,&mode_image->exception);
3124 mode_image=DestroyImage(mode_image);
3125 return((Image *) NULL);
3127 pixel_list=AcquirePixelListThreadSet(width);
3128 if (pixel_list == (PixelList **) NULL)
3130 mode_image=DestroyImage(mode_image);
3131 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3138 image_view=AcquireCacheView(image);
3139 mode_view=AcquireCacheView(mode_image);
3140 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3141 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3143 for (y=0; y < (ssize_t) mode_image->rows; y++)
3146 id = GetOpenMPThreadId();
3148 register const IndexPacket
3151 register const PixelPacket
3154 register IndexPacket
3155 *restrict mode_indexes;
3157 register PixelPacket
3163 if (status == MagickFalse)
3165 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
3166 (width/2L),image->columns+width,width,exception);
3167 q=QueueCacheViewAuthenticPixels(mode_view,0,y,mode_image->columns,1,
3169 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3174 indexes=GetCacheViewVirtualIndexQueue(image_view);
3175 mode_indexes=GetCacheViewAuthenticIndexQueue(mode_view);
3176 for (x=0; x < (ssize_t) mode_image->columns; x++)
3181 register const PixelPacket
3184 register const IndexPacket
3193 ResetPixelList(pixel_list[id]);
3194 for (v=0; v < (ssize_t) width; v++)
3196 for (u=0; u < (ssize_t) width; u++)
3197 InsertPixelList(image,r+u,s+u,pixel_list[id]);
3198 r+=image->columns+width;
3199 s+=image->columns+width;
3201 pixel=GetModePixelList(pixel_list[id]);
3202 SetPixelPacket(mode_image,&pixel,q,mode_indexes+x);
3206 if (SyncCacheViewAuthenticPixels(mode_view,exception) == MagickFalse)
3208 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3213 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3214 #pragma omp critical (MagickCore_ModeImage)
3216 proceed=SetImageProgress(image,ModeImageTag,progress++,image->rows);
3217 if (proceed == MagickFalse)
3221 mode_view=DestroyCacheView(mode_view);
3222 image_view=DestroyCacheView(image_view);
3223 pixel_list=DestroyPixelListThreadSet(pixel_list);
3228 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3232 % M o t i o n B l u r I m a g e %
3236 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3238 % MotionBlurImage() simulates motion blur. We convolve the image with a
3239 % Gaussian operator of the given radius and standard deviation (sigma).
3240 % For reasonable results, radius should be larger than sigma. Use a
3241 % radius of 0 and MotionBlurImage() selects a suitable radius for you.
3242 % Angle gives the angle of the blurring motion.
3244 % Andrew Protano contributed this effect.
3246 % The format of the MotionBlurImage method is:
3248 % Image *MotionBlurImage(const Image *image,const double radius,
3249 % const double sigma,const double angle,ExceptionInfo *exception)
3250 % Image *MotionBlurImageChannel(const Image *image,const ChannelType channel,
3251 % const double radius,const double sigma,const double angle,
3252 % ExceptionInfo *exception)
3254 % A description of each parameter follows:
3256 % o image: the image.
3258 % o channel: the channel type.
3260 % o radius: the radius of the Gaussian, in pixels, not counting the center
3261 % o radius: the radius of the Gaussian, in pixels, not counting
3264 % o sigma: the standard deviation of the Gaussian, in pixels.
3266 % o angle: Apply the effect along this angle.
3268 % o exception: return any errors or warnings in this structure.
3272 static double *GetMotionBlurKernel(const size_t width,const double sigma)
3282 Generate a 1-D convolution kernel.
3284 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3285 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
3286 if (kernel == (double *) NULL)
3289 for (i=0; i < (ssize_t) width; i++)
3291 kernel[i]=(double) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
3292 MagickSigma)))/(MagickSQ2PI*MagickSigma));
3293 normalize+=kernel[i];
3295 for (i=0; i < (ssize_t) width; i++)
3296 kernel[i]/=normalize;
3300 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
3301 const double sigma,const double angle,ExceptionInfo *exception)
3306 motion_blur=MotionBlurImageChannel(image,DefaultChannels,radius,sigma,angle,
3308 return(motion_blur);
3311 MagickExport Image *MotionBlurImageChannel(const Image *image,
3312 const ChannelType channel,const double radius,const double sigma,
3313 const double angle,ExceptionInfo *exception)
3349 assert(image != (Image *) NULL);
3350 assert(image->signature == MagickSignature);
3351 if (image->debug != MagickFalse)
3352 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3353 assert(exception != (ExceptionInfo *) NULL);
3354 width=GetOptimalKernelWidth1D(radius,sigma);
3355 kernel=GetMotionBlurKernel(width,sigma);
3356 if (kernel == (double *) NULL)
3357 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3358 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
3359 if (offset == (OffsetInfo *) NULL)
3361 kernel=(double *) RelinquishMagickMemory(kernel);
3362 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3364 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3365 if (blur_image == (Image *) NULL)
3367 kernel=(double *) RelinquishMagickMemory(kernel);
3368 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3369 return((Image *) NULL);
3371 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3373 kernel=(double *) RelinquishMagickMemory(kernel);
3374 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3375 InheritException(exception,&blur_image->exception);
3376 blur_image=DestroyImage(blur_image);
3377 return((Image *) NULL);
3379 point.x=(double) width*sin(DegreesToRadians(angle));
3380 point.y=(double) width*cos(DegreesToRadians(angle));
3381 for (i=0; i < (ssize_t) width; i++)
3383 offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
3384 offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
3391 GetMagickPixelPacket(image,&bias);
3392 image_view=AcquireCacheView(image);
3393 blur_view=AcquireCacheView(blur_image);
3394 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3395 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
3397 for (y=0; y < (ssize_t) image->rows; y++)
3399 register IndexPacket
3400 *restrict blur_indexes;
3402 register PixelPacket
3408 if (status == MagickFalse)
3410 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3412 if (q == (PixelPacket *) NULL)
3417 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
3418 for (x=0; x < (ssize_t) image->columns; x++)
3426 register const IndexPacket
3437 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
3439 for (i=0; i < (ssize_t) width; i++)
3441 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
3442 offset[i].y,&pixel,exception);
3443 qixel.red+=(*k)*pixel.red;
3444 qixel.green+=(*k)*pixel.green;
3445 qixel.blue+=(*k)*pixel.blue;
3446 qixel.opacity+=(*k)*pixel.opacity;
3447 if (image->colorspace == CMYKColorspace)
3449 indexes=GetCacheViewVirtualIndexQueue(image_view);
3450 qixel.index+=(*k)*(*indexes);
3454 if ((channel & RedChannel) != 0)
3455 q->red=ClampToQuantum(qixel.red);
3456 if ((channel & GreenChannel) != 0)
3457 q->green=ClampToQuantum(qixel.green);
3458 if ((channel & BlueChannel) != 0)
3459 q->blue=ClampToQuantum(qixel.blue);
3460 if ((channel & OpacityChannel) != 0)
3461 q->opacity=ClampToQuantum(qixel.opacity);
3462 if (((channel & IndexChannel) != 0) &&
3463 (image->colorspace == CMYKColorspace))
3464 blur_indexes[x]=(IndexPacket) ClampToQuantum(qixel.index);
3474 for (i=0; i < (ssize_t) width; i++)
3476 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
3477 offset[i].y,&pixel,exception);
3478 alpha=(MagickRealType) (QuantumScale*
3479 GetAlphaPixelComponent(&pixel));
3480 qixel.red+=(*k)*alpha*pixel.red;
3481 qixel.green+=(*k)*alpha*pixel.green;
3482 qixel.blue+=(*k)*alpha*pixel.blue;
3483 qixel.opacity+=(*k)*pixel.opacity;
3484 if (image->colorspace == CMYKColorspace)
3486 indexes=GetCacheViewVirtualIndexQueue(image_view);
3487 qixel.index+=(*k)*alpha*(*indexes);
3492 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3493 if ((channel & RedChannel) != 0)
3494 q->red=ClampToQuantum(gamma*qixel.red);
3495 if ((channel & GreenChannel) != 0)
3496 q->green=ClampToQuantum(gamma*qixel.green);
3497 if ((channel & BlueChannel) != 0)
3498 q->blue=ClampToQuantum(gamma*qixel.blue);
3499 if ((channel & OpacityChannel) != 0)
3500 q->opacity=ClampToQuantum(qixel.opacity);
3501 if (((channel & IndexChannel) != 0) &&
3502 (image->colorspace == CMYKColorspace))
3503 blur_indexes[x]=(IndexPacket) ClampToQuantum(gamma*qixel.index);
3507 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3509 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3514 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3515 #pragma omp critical (MagickCore_MotionBlurImageChannel)
3517 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3518 if (proceed == MagickFalse)
3522 blur_view=DestroyCacheView(blur_view);
3523 image_view=DestroyCacheView(image_view);
3524 kernel=(double *) RelinquishMagickMemory(kernel);
3525 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3526 if (status == MagickFalse)
3527 blur_image=DestroyImage(blur_image);
3532 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3536 % P r e v i e w I m a g e %
3540 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3542 % PreviewImage() tiles 9 thumbnails of the specified image with an image
3543 % processing operation applied with varying parameters. This may be helpful
3544 % pin-pointing an appropriate parameter for a particular image processing
3547 % The format of the PreviewImages method is:
3549 % Image *PreviewImages(const Image *image,const PreviewType preview,
3550 % ExceptionInfo *exception)
3552 % A description of each parameter follows:
3554 % o image: the image.
3556 % o preview: the image processing operation.
3558 % o exception: return any errors or warnings in this structure.
3561 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
3562 ExceptionInfo *exception)
3564 #define NumberTiles 9
3565 #define PreviewImageTag "Preview/Image"
3566 #define DefaultPreviewGeometry "204x204+10+10"
3569 factor[MaxTextExtent],
3570 label[MaxTextExtent];
3612 Open output image file.
3614 assert(image != (Image *) NULL);
3615 assert(image->signature == MagickSignature);
3616 if (image->debug != MagickFalse)
3617 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3621 preview_info=AcquireImageInfo();
3622 SetGeometry(image,&geometry);
3623 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
3624 &geometry.width,&geometry.height);
3625 images=NewImageList();
3627 GetQuantizeInfo(&quantize_info);
3633 for (i=0; i < NumberTiles; i++)
3635 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
3636 if (thumbnail == (Image *) NULL)
3638 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
3640 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
3641 if (i == (NumberTiles/2))
3643 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
3644 AppendImageToList(&images,thumbnail);
3652 preview_image=RotateImage(thumbnail,degrees,exception);
3653 (void) FormatMagickString(label,MaxTextExtent,"rotate %g",degrees);
3659 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
3660 (void) FormatMagickString(label,MaxTextExtent,"shear %gx%g",
3661 degrees,2.0*degrees);
3666 x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
3667 y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
3668 preview_image=RollImage(thumbnail,x,y,exception);
3669 (void) FormatMagickString(label,MaxTextExtent,"roll %+.20gx%+.20g",
3670 (double) x,(double) y);
3675 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3676 if (preview_image == (Image *) NULL)
3678 (void) FormatMagickString(factor,MaxTextExtent,"100,100,%g",
3680 (void) ModulateImage(preview_image,factor);
3681 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3684 case SaturationPreview:
3686 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3687 if (preview_image == (Image *) NULL)
3689 (void) FormatMagickString(factor,MaxTextExtent,"100,%g",
3691 (void) ModulateImage(preview_image,factor);
3692 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3695 case BrightnessPreview:
3697 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3698 if (preview_image == (Image *) NULL)
3700 (void) FormatMagickString(factor,MaxTextExtent,"%g",2.0*percentage);
3701 (void) ModulateImage(preview_image,factor);
3702 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3708 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3709 if (preview_image == (Image *) NULL)
3712 (void) GammaImageChannel(preview_image,DefaultChannels,gamma);
3713 (void) FormatMagickString(label,MaxTextExtent,"gamma %g",gamma);
3718 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3719 if (preview_image != (Image *) NULL)
3720 for (x=0; x < i; x++)
3721 (void) ContrastImage(preview_image,MagickTrue);
3722 (void) FormatMagickString(label,MaxTextExtent,"contrast (%.20g)",
3728 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3729 if (preview_image == (Image *) NULL)
3731 for (x=0; x < i; x++)
3732 (void) ContrastImage(preview_image,MagickFalse);
3733 (void) FormatMagickString(label,MaxTextExtent,"+contrast (%.20g)",
3737 case GrayscalePreview:
3739 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3740 if (preview_image == (Image *) NULL)
3743 quantize_info.number_colors=colors;
3744 quantize_info.colorspace=GRAYColorspace;
3745 (void) QuantizeImage(&quantize_info,preview_image);
3746 (void) FormatMagickString(label,MaxTextExtent,
3747 "-colorspace gray -colors %.20g",(double) colors);
3750 case QuantizePreview:
3752 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3753 if (preview_image == (Image *) NULL)
3756 quantize_info.number_colors=colors;
3757 (void) QuantizeImage(&quantize_info,preview_image);
3758 (void) FormatMagickString(label,MaxTextExtent,"colors %.20g",(double)
3762 case DespecklePreview:
3764 for (x=0; x < (i-1); x++)
3766 preview_image=DespeckleImage(thumbnail,exception);
3767 if (preview_image == (Image *) NULL)
3769 thumbnail=DestroyImage(thumbnail);
3770 thumbnail=preview_image;
3772 preview_image=DespeckleImage(thumbnail,exception);
3773 if (preview_image == (Image *) NULL)
3775 (void) FormatMagickString(label,MaxTextExtent,"despeckle (%.20g)",
3779 case ReduceNoisePreview:
3781 preview_image=ReduceNoiseImage(thumbnail,radius,exception);
3782 (void) FormatMagickString(label,MaxTextExtent,"noise %g",radius);
3785 case AddNoisePreview:
3791 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
3796 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
3801 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
3806 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
3811 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
3816 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
3821 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
3825 preview_image=ReduceNoiseImage(thumbnail,(double) i,exception);
3826 (void) FormatMagickString(label,MaxTextExtent,"+noise %s",factor);
3829 case SharpenPreview:
3831 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
3832 (void) FormatMagickString(label,MaxTextExtent,"sharpen %gx%g",
3838 preview_image=BlurImage(thumbnail,radius,sigma,exception);
3839 (void) FormatMagickString(label,MaxTextExtent,"blur %gx%g",radius,
3843 case ThresholdPreview:
3845 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3846 if (preview_image == (Image *) NULL)
3848 (void) BilevelImage(thumbnail,
3849 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3850 (void) FormatMagickString(label,MaxTextExtent,"threshold %g",
3851 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3854 case EdgeDetectPreview:
3856 preview_image=EdgeImage(thumbnail,radius,exception);
3857 (void) FormatMagickString(label,MaxTextExtent,"edge %g",radius);
3862 preview_image=SpreadImage(thumbnail,radius,exception);
3863 (void) FormatMagickString(label,MaxTextExtent,"spread %g",
3867 case SolarizePreview:
3869 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3870 if (preview_image == (Image *) NULL)
3872 (void) SolarizeImage(preview_image,(double) QuantumRange*
3874 (void) FormatMagickString(label,MaxTextExtent,"solarize %g",
3875 (QuantumRange*percentage)/100.0);
3881 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
3883 (void) FormatMagickString(label,MaxTextExtent,"shade %gx%g",
3889 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3890 if (preview_image == (Image *) NULL)
3892 geometry.width=(size_t) (2*i+2);
3893 geometry.height=(size_t) (2*i+2);
3896 (void) RaiseImage(preview_image,&geometry,MagickTrue);
3897 (void) FormatMagickString(label,MaxTextExtent,
3898 "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
3899 geometry.height,(double) geometry.x,(double) geometry.y);
3902 case SegmentPreview:
3904 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3905 if (preview_image == (Image *) NULL)
3908 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
3910 (void) FormatMagickString(label,MaxTextExtent,"segment %gx%g",
3911 threshold,threshold);
3916 preview_image=SwirlImage(thumbnail,degrees,exception);
3917 (void) FormatMagickString(label,MaxTextExtent,"swirl %g",degrees);
3921 case ImplodePreview:
3924 preview_image=ImplodeImage(thumbnail,degrees,exception);
3925 (void) FormatMagickString(label,MaxTextExtent,"implode %g",degrees);
3931 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
3932 (void) FormatMagickString(label,MaxTextExtent,"wave %gx%g",
3933 0.5*degrees,2.0*degrees);
3936 case OilPaintPreview:
3938 preview_image=OilPaintImage(thumbnail,(double) radius,exception);
3939 (void) FormatMagickString(label,MaxTextExtent,"paint %g",radius);
3942 case CharcoalDrawingPreview:
3944 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
3946 (void) FormatMagickString(label,MaxTextExtent,"charcoal %gx%g",
3953 filename[MaxTextExtent];
3961 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3962 if (preview_image == (Image *) NULL)
3964 preview_info->quality=(size_t) percentage;
3965 (void) FormatMagickString(factor,MaxTextExtent,"%.20g",(double)
3966 preview_info->quality);
3967 file=AcquireUniqueFileResource(filename);
3970 (void) FormatMagickString(preview_image->filename,MaxTextExtent,
3971 "jpeg:%s",filename);
3972 status=WriteImage(preview_info,preview_image);
3973 if (status != MagickFalse)
3978 (void) CopyMagickString(preview_info->filename,
3979 preview_image->filename,MaxTextExtent);
3980 quality_image=ReadImage(preview_info,exception);
3981 if (quality_image != (Image *) NULL)
3983 preview_image=DestroyImage(preview_image);
3984 preview_image=quality_image;
3987 (void) RelinquishUniqueFileResource(preview_image->filename);
3988 if ((GetBlobSize(preview_image)/1024) >= 1024)
3989 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gmb ",
3990 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
3993 if (GetBlobSize(preview_image) >= 1024)
3994 (void) FormatMagickString(label,MaxTextExtent,
3995 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
3996 GetBlobSize(preview_image))/1024.0);
3998 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%.20gb ",
3999 factor,(double) GetBlobSize(thumbnail));
4003 thumbnail=DestroyImage(thumbnail);
4007 if (preview_image == (Image *) NULL)
4009 (void) DeleteImageProperty(preview_image,"label");
4010 (void) SetImageProperty(preview_image,"label",label);
4011 AppendImageToList(&images,preview_image);
4012 proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
4014 if (proceed == MagickFalse)
4017 if (images == (Image *) NULL)
4019 preview_info=DestroyImageInfo(preview_info);
4020 return((Image *) NULL);
4025 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
4026 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
4027 montage_info->shadow=MagickTrue;
4028 (void) CloneString(&montage_info->tile,"3x3");
4029 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
4030 (void) CloneString(&montage_info->frame,DefaultTileFrame);
4031 montage_image=MontageImages(images,montage_info,exception);
4032 montage_info=DestroyMontageInfo(montage_info);
4033 images=DestroyImageList(images);
4034 if (montage_image == (Image *) NULL)
4035 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4036 if (montage_image->montage != (char *) NULL)
4039 Free image directory.
4041 montage_image->montage=(char *) RelinquishMagickMemory(
4042 montage_image->montage);
4043 if (image->directory != (char *) NULL)
4044 montage_image->directory=(char *) RelinquishMagickMemory(
4045 montage_image->directory);
4047 preview_info=DestroyImageInfo(preview_info);
4048 return(montage_image);
4052 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4056 % R a d i a l B l u r I m a g e %
4060 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4062 % RadialBlurImage() applies a radial blur to the image.
4064 % Andrew Protano contributed this effect.
4066 % The format of the RadialBlurImage method is:
4068 % Image *RadialBlurImage(const Image *image,const double angle,
4069 % ExceptionInfo *exception)
4070 % Image *RadialBlurImageChannel(const Image *image,const ChannelType channel,
4071 % const double angle,ExceptionInfo *exception)
4073 % A description of each parameter follows:
4075 % o image: the image.
4077 % o channel: the channel type.
4079 % o angle: the angle of the radial blur.
4081 % o exception: return any errors or warnings in this structure.
4085 MagickExport Image *RadialBlurImage(const Image *image,const double angle,
4086 ExceptionInfo *exception)
4091 blur_image=RadialBlurImageChannel(image,DefaultChannels,angle,exception);
4095 MagickExport Image *RadialBlurImageChannel(const Image *image,
4096 const ChannelType channel,const double angle,ExceptionInfo *exception)
4134 Allocate blur image.
4136 assert(image != (Image *) NULL);
4137 assert(image->signature == MagickSignature);
4138 if (image->debug != MagickFalse)
4139 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4140 assert(exception != (ExceptionInfo *) NULL);
4141 assert(exception->signature == MagickSignature);
4142 blur_image=CloneImage(image,0,0,MagickTrue,exception);
4143 if (blur_image == (Image *) NULL)
4144 return((Image *) NULL);
4145 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
4147 InheritException(exception,&blur_image->exception);
4148 blur_image=DestroyImage(blur_image);
4149 return((Image *) NULL);
4151 blur_center.x=(double) image->columns/2.0;
4152 blur_center.y=(double) image->rows/2.0;
4153 blur_radius=hypot(blur_center.x,blur_center.y);
4154 n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
4155 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
4156 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
4157 sizeof(*cos_theta));
4158 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
4159 sizeof(*sin_theta));
4160 if ((cos_theta == (MagickRealType *) NULL) ||
4161 (sin_theta == (MagickRealType *) NULL))
4163 blur_image=DestroyImage(blur_image);
4164 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4166 offset=theta*(MagickRealType) (n-1)/2.0;
4167 for (i=0; i < (ssize_t) n; i++)
4169 cos_theta[i]=cos((double) (theta*i-offset));
4170 sin_theta[i]=sin((double) (theta*i-offset));
4177 GetMagickPixelPacket(image,&bias);
4178 image_view=AcquireCacheView(image);
4179 blur_view=AcquireCacheView(blur_image);
4180 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4181 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4183 for (y=0; y < (ssize_t) blur_image->rows; y++)
4185 register const IndexPacket
4188 register IndexPacket
4189 *restrict blur_indexes;
4191 register PixelPacket
4197 if (status == MagickFalse)
4199 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
4201 if (q == (PixelPacket *) NULL)
4206 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
4207 for (x=0; x < (ssize_t) blur_image->columns; x++)
4228 center.x=(double) x-blur_center.x;
4229 center.y=(double) y-blur_center.y;
4230 radius=hypot((double) center.x,center.y);
4235 step=(size_t) (blur_radius/radius);
4244 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
4246 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
4248 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
4249 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
4250 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
4251 cos_theta[i]+0.5),&pixel,exception);
4252 qixel.red+=pixel.red;
4253 qixel.green+=pixel.green;
4254 qixel.blue+=pixel.blue;
4255 qixel.opacity+=pixel.opacity;
4256 if (image->colorspace == CMYKColorspace)
4258 indexes=GetCacheViewVirtualIndexQueue(image_view);
4259 qixel.index+=(*indexes);
4263 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
4265 if ((channel & RedChannel) != 0)
4266 q->red=ClampToQuantum(normalize*qixel.red);
4267 if ((channel & GreenChannel) != 0)
4268 q->green=ClampToQuantum(normalize*qixel.green);
4269 if ((channel & BlueChannel) != 0)
4270 q->blue=ClampToQuantum(normalize*qixel.blue);
4271 if ((channel & OpacityChannel) != 0)
4272 q->opacity=ClampToQuantum(normalize*qixel.opacity);
4273 if (((channel & IndexChannel) != 0) &&
4274 (image->colorspace == CMYKColorspace))
4275 blur_indexes[x]=(IndexPacket) ClampToQuantum(normalize*qixel.index);
4285 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
4287 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
4288 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
4289 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
4290 cos_theta[i]+0.5),&pixel,exception);
4291 alpha=(MagickRealType) (QuantumScale*
4292 GetAlphaPixelComponent(&pixel));
4293 qixel.red+=alpha*pixel.red;
4294 qixel.green+=alpha*pixel.green;
4295 qixel.blue+=alpha*pixel.blue;
4296 qixel.opacity+=pixel.opacity;
4297 if (image->colorspace == CMYKColorspace)
4299 indexes=GetCacheViewVirtualIndexQueue(image_view);
4300 qixel.index+=alpha*(*indexes);
4305 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4306 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
4308 if ((channel & RedChannel) != 0)
4309 q->red=ClampToQuantum(gamma*qixel.red);
4310 if ((channel & GreenChannel) != 0)
4311 q->green=ClampToQuantum(gamma*qixel.green);
4312 if ((channel & BlueChannel) != 0)
4313 q->blue=ClampToQuantum(gamma*qixel.blue);
4314 if ((channel & OpacityChannel) != 0)
4315 q->opacity=ClampToQuantum(normalize*qixel.opacity);
4316 if (((channel & IndexChannel) != 0) &&
4317 (image->colorspace == CMYKColorspace))
4318 blur_indexes[x]=(IndexPacket) ClampToQuantum(gamma*qixel.index);
4322 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
4324 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4329 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4330 #pragma omp critical (MagickCore_RadialBlurImageChannel)
4332 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
4333 if (proceed == MagickFalse)
4337 blur_view=DestroyCacheView(blur_view);
4338 image_view=DestroyCacheView(image_view);
4339 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
4340 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
4341 if (status == MagickFalse)
4342 blur_image=DestroyImage(blur_image);
4347 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4351 % R e d u c e N o i s e I m a g e %
4355 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4357 % ReduceNoiseImage() smooths the contours of an image while still preserving
4358 % edge information. The algorithm works by replacing each pixel with its
4359 % neighbor closest in value. A neighbor is defined by radius. Use a radius
4360 % of 0 and ReduceNoise() selects a suitable radius for you.
4362 % The format of the ReduceNoiseImage method is:
4364 % Image *ReduceNoiseImage(const Image *image,const double radius,
4365 % ExceptionInfo *exception)
4367 % A description of each parameter follows:
4369 % o image: the image.
4371 % o radius: the radius of the pixel neighborhood.
4373 % o exception: return any errors or warnings in this structure.
4377 static MagickPixelPacket GetNonpeakPixelList(PixelList *pixel_list)
4399 Finds the median value for each of the color.
4401 center=pixel_list->center;
4402 for (channel=0; channel < 5; channel++)
4404 list=pixel_list->lists+channel;
4406 next=list->nodes[color].next[0];
4412 next=list->nodes[color].next[0];
4413 count+=list->nodes[color].count;
4415 while (count <= center);
4416 if ((previous == 65536UL) && (next != 65536UL))
4419 if ((previous != 65536UL) && (next == 65536UL))
4421 channels[channel]=(unsigned short) color;
4423 GetMagickPixelPacket((const Image *) NULL,&pixel);
4424 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4425 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4426 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
4427 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
4428 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
4432 MagickExport Image *ReduceNoiseImage(const Image *image,const double radius,
4433 ExceptionInfo *exception)
4435 #define ReduceNoiseImageTag "ReduceNoise/Image"
4451 **restrict pixel_list;
4460 Initialize noise image attributes.
4462 assert(image != (Image *) NULL);
4463 assert(image->signature == MagickSignature);
4464 if (image->debug != MagickFalse)
4465 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4466 assert(exception != (ExceptionInfo *) NULL);
4467 assert(exception->signature == MagickSignature);
4468 width=GetOptimalKernelWidth2D(radius,0.5);
4469 noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4471 if (noise_image == (Image *) NULL)
4472 return((Image *) NULL);
4473 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
4475 InheritException(exception,&noise_image->exception);
4476 noise_image=DestroyImage(noise_image);
4477 return((Image *) NULL);
4479 pixel_list=AcquirePixelListThreadSet(width);
4480 if (pixel_list == (PixelList **) NULL)
4482 noise_image=DestroyImage(noise_image);
4483 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4490 image_view=AcquireCacheView(image);
4491 noise_view=AcquireCacheView(noise_image);
4492 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4493 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4495 for (y=0; y < (ssize_t) noise_image->rows; y++)
4498 id = GetOpenMPThreadId();
4500 register const IndexPacket
4503 register const PixelPacket
4506 register IndexPacket
4507 *restrict noise_indexes;
4509 register PixelPacket
4515 if (status == MagickFalse)
4517 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
4518 (width/2L),image->columns+width,width,exception);
4519 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
4521 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4526 indexes=GetCacheViewVirtualIndexQueue(image_view);
4527 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
4528 for (x=0; x < (ssize_t) noise_image->columns; x++)
4533 register const PixelPacket
4536 register const IndexPacket
4545 ResetPixelList(pixel_list[id]);
4546 for (v=0; v < (ssize_t) width; v++)
4548 for (u=0; u < (ssize_t) width; u++)
4549 InsertPixelList(image,r+u,s+u,pixel_list[id]);
4550 r+=image->columns+width;
4551 s+=image->columns+width;
4553 pixel=GetNonpeakPixelList(pixel_list[id]);
4554 SetPixelPacket(noise_image,&pixel,q,noise_indexes+x);
4558 if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
4560 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4565 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4566 #pragma omp critical (MagickCore_ReduceNoiseImage)
4568 proceed=SetImageProgress(image,ReduceNoiseImageTag,progress++,
4570 if (proceed == MagickFalse)
4574 noise_view=DestroyCacheView(noise_view);
4575 image_view=DestroyCacheView(image_view);
4576 pixel_list=DestroyPixelListThreadSet(pixel_list);
4577 return(noise_image);
4581 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4585 % S e l e c t i v e B l u r I m a g e %
4589 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4591 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
4592 % It is similar to the unsharpen mask that sharpens everything with contrast
4593 % above a certain threshold.
4595 % The format of the SelectiveBlurImage method is:
4597 % Image *SelectiveBlurImage(const Image *image,const double radius,
4598 % const double sigma,const double threshold,ExceptionInfo *exception)
4599 % Image *SelectiveBlurImageChannel(const Image *image,
4600 % const ChannelType channel,const double radius,const double sigma,
4601 % const double threshold,ExceptionInfo *exception)
4603 % A description of each parameter follows:
4605 % o image: the image.
4607 % o channel: the channel type.
4609 % o radius: the radius of the Gaussian, in pixels, not counting the center
4612 % o sigma: the standard deviation of the Gaussian, in pixels.
4614 % o threshold: only pixels within this contrast threshold are included
4615 % in the blur operation.
4617 % o exception: return any errors or warnings in this structure.
4621 static inline MagickBooleanType SelectiveContrast(const PixelPacket *p,
4622 const PixelPacket *q,const double threshold)
4624 if (fabs(PixelIntensity(p)-PixelIntensity(q)) < threshold)
4626 return(MagickFalse);
4629 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
4630 const double sigma,const double threshold,ExceptionInfo *exception)
4635 blur_image=SelectiveBlurImageChannel(image,DefaultChannels,radius,sigma,
4636 threshold,exception);
4640 MagickExport Image *SelectiveBlurImageChannel(const Image *image,
4641 const ChannelType channel,const double radius,const double sigma,
4642 const double threshold,ExceptionInfo *exception)
4644 #define SelectiveBlurImageTag "SelectiveBlur/Image"
4678 Initialize blur image attributes.
4680 assert(image != (Image *) NULL);
4681 assert(image->signature == MagickSignature);
4682 if (image->debug != MagickFalse)
4683 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4684 assert(exception != (ExceptionInfo *) NULL);
4685 assert(exception->signature == MagickSignature);
4686 width=GetOptimalKernelWidth1D(radius,sigma);
4687 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
4688 if (kernel == (double *) NULL)
4689 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4690 j=(ssize_t) width/2;
4692 for (v=(-j); v <= j; v++)
4694 for (u=(-j); u <= j; u++)
4695 kernel[i++]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
4696 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
4698 if (image->debug != MagickFalse)
4701 format[MaxTextExtent],
4704 register const double
4711 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
4712 " SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
4714 message=AcquireString("");
4716 for (v=0; v < (ssize_t) width; v++)
4719 (void) FormatMagickString(format,MaxTextExtent,"%.20g: ",(double) v);
4720 (void) ConcatenateString(&message,format);
4721 for (u=0; u < (ssize_t) width; u++)
4723 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
4724 (void) ConcatenateString(&message,format);
4726 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
4728 message=DestroyString(message);
4730 blur_image=CloneImage(image,0,0,MagickTrue,exception);
4731 if (blur_image == (Image *) NULL)
4732 return((Image *) NULL);
4733 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
4735 InheritException(exception,&blur_image->exception);
4736 blur_image=DestroyImage(blur_image);
4737 return((Image *) NULL);
4740 Threshold blur image.
4744 GetMagickPixelPacket(image,&bias);
4745 SetMagickPixelPacketBias(image,&bias);
4746 image_view=AcquireCacheView(image);
4747 blur_view=AcquireCacheView(blur_image);
4748 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4749 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4751 for (y=0; y < (ssize_t) image->rows; y++)
4759 register const IndexPacket
4762 register const PixelPacket
4765 register IndexPacket
4766 *restrict blur_indexes;
4768 register PixelPacket
4774 if (status == MagickFalse)
4776 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
4777 (width/2L),image->columns+width,width,exception);
4778 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
4780 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4785 indexes=GetCacheViewVirtualIndexQueue(image_view);
4786 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
4787 for (x=0; x < (ssize_t) image->columns; x++)
4792 register const double
4806 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
4808 for (v=0; v < (ssize_t) width; v++)
4810 for (u=0; u < (ssize_t) width; u++)
4812 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4814 pixel.red+=(*k)*(p+u+j)->red;
4815 pixel.green+=(*k)*(p+u+j)->green;
4816 pixel.blue+=(*k)*(p+u+j)->blue;
4821 j+=(ssize_t) (image->columns+width);
4825 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4826 if ((channel & RedChannel) != 0)
4827 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
4828 if ((channel & GreenChannel) != 0)
4829 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
4830 if ((channel & BlueChannel) != 0)
4831 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
4833 if ((channel & OpacityChannel) != 0)
4837 for (v=0; v < (ssize_t) width; v++)
4839 for (u=0; u < (ssize_t) width; u++)
4841 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4843 pixel.opacity+=(*k)*(p+u+j)->opacity;
4848 j+=(ssize_t) (image->columns+width);
4852 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4854 SetOpacityPixelComponent(q,ClampToQuantum(gamma*
4855 GetOpacityPixelComponent(&pixel)));
4858 if (((channel & IndexChannel) != 0) &&
4859 (image->colorspace == CMYKColorspace))
4863 for (v=0; v < (ssize_t) width; v++)
4865 for (u=0; u < (ssize_t) width; u++)
4867 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4869 pixel.index+=(*k)*indexes[x+u+j];
4874 j+=(ssize_t) (image->columns+width);
4878 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4880 blur_indexes[x]=ClampToQuantum(gamma*
4881 GetIndexPixelComponent(&pixel));
4890 for (v=0; v < (ssize_t) width; v++)
4892 for (u=0; u < (ssize_t) width; u++)
4894 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4896 alpha=(MagickRealType) (QuantumScale*
4897 GetAlphaPixelComponent(p+u+j));
4898 pixel.red+=(*k)*alpha*(p+u+j)->red;
4899 pixel.green+=(*k)*alpha*(p+u+j)->green;
4900 pixel.blue+=(*k)*alpha*(p+u+j)->blue;
4901 pixel.opacity+=(*k)*(p+u+j)->opacity;
4906 j+=(ssize_t) (image->columns+width);
4910 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4911 if ((channel & RedChannel) != 0)
4912 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
4913 if ((channel & GreenChannel) != 0)
4914 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
4915 if ((channel & BlueChannel) != 0)
4916 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
4918 if ((channel & OpacityChannel) != 0)
4922 for (v=0; v < (ssize_t) width; v++)
4924 for (u=0; u < (ssize_t) width; u++)
4926 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4928 pixel.opacity+=(*k)*(p+u+j)->opacity;
4933 j+=(ssize_t) (image->columns+width);
4937 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4939 SetOpacityPixelComponent(q,
4940 ClampOpacityPixelComponent(&pixel));
4943 if (((channel & IndexChannel) != 0) &&
4944 (image->colorspace == CMYKColorspace))
4948 for (v=0; v < (ssize_t) width; v++)
4950 for (u=0; u < (ssize_t) width; u++)
4952 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4954 alpha=(MagickRealType) (QuantumScale*
4955 GetAlphaPixelComponent(p+u+j));
4956 pixel.index+=(*k)*alpha*indexes[x+u+j];
4961 j+=(ssize_t) (image->columns+width);
4965 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4967 blur_indexes[x]=ClampToQuantum(gamma*
4968 GetIndexPixelComponent(&pixel));
4976 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
4977 if (sync == MagickFalse)
4979 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4984 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4985 #pragma omp critical (MagickCore_SelectiveBlurImageChannel)
4987 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
4989 if (proceed == MagickFalse)
4993 blur_image->type=image->type;
4994 blur_view=DestroyCacheView(blur_view);
4995 image_view=DestroyCacheView(image_view);
4996 kernel=(double *) RelinquishMagickMemory(kernel);
4997 if (status == MagickFalse)
4998 blur_image=DestroyImage(blur_image);
5003 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5007 % S h a d e I m a g e %
5011 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5013 % ShadeImage() shines a distant light on an image to create a
5014 % three-dimensional effect. You control the positioning of the light with
5015 % azimuth and elevation; azimuth is measured in degrees off the x axis
5016 % and elevation is measured in pixels above the Z axis.
5018 % The format of the ShadeImage method is:
5020 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
5021 % const double azimuth,const double elevation,ExceptionInfo *exception)
5023 % A description of each parameter follows:
5025 % o image: the image.
5027 % o gray: A value other than zero shades the intensity of each pixel.
5029 % o azimuth, elevation: Define the light source direction.
5031 % o exception: return any errors or warnings in this structure.
5034 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
5035 const double azimuth,const double elevation,ExceptionInfo *exception)
5037 #define ShadeImageTag "Shade/Image"
5059 Initialize shaded image attributes.
5061 assert(image != (const Image *) NULL);
5062 assert(image->signature == MagickSignature);
5063 if (image->debug != MagickFalse)
5064 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5065 assert(exception != (ExceptionInfo *) NULL);
5066 assert(exception->signature == MagickSignature);
5067 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5068 if (shade_image == (Image *) NULL)
5069 return((Image *) NULL);
5070 if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
5072 InheritException(exception,&shade_image->exception);
5073 shade_image=DestroyImage(shade_image);
5074 return((Image *) NULL);
5077 Compute the light vector.
5079 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
5080 cos(DegreesToRadians(elevation));
5081 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
5082 cos(DegreesToRadians(elevation));
5083 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
5089 image_view=AcquireCacheView(image);
5090 shade_view=AcquireCacheView(shade_image);
5091 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5092 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5094 for (y=0; y < (ssize_t) image->rows; y++)
5104 register const PixelPacket
5110 register PixelPacket
5116 if (status == MagickFalse)
5118 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
5119 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
5121 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5127 Shade this row of pixels.
5129 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
5131 s1=s0+image->columns+2;
5132 s2=s1+image->columns+2;
5133 for (x=0; x < (ssize_t) image->columns; x++)
5136 Determine the surface normal and compute shading.
5138 normal.x=(double) (PixelIntensity(s0-1)+PixelIntensity(s1-1)+
5139 PixelIntensity(s2-1)-PixelIntensity(s0+1)-PixelIntensity(s1+1)-
5140 PixelIntensity(s2+1));
5141 normal.y=(double) (PixelIntensity(s2-1)+PixelIntensity(s2)+
5142 PixelIntensity(s2+1)-PixelIntensity(s0-1)-PixelIntensity(s0)-
5143 PixelIntensity(s0+1));
5144 if ((normal.x == 0.0) && (normal.y == 0.0))
5149 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
5150 if (distance > MagickEpsilon)
5153 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
5154 if (normal_distance > (MagickEpsilon*MagickEpsilon))
5155 shade=distance/sqrt((double) normal_distance);
5158 if (gray != MagickFalse)
5160 q->red=(Quantum) shade;
5161 q->green=(Quantum) shade;
5162 q->blue=(Quantum) shade;
5166 q->red=ClampToQuantum(QuantumScale*shade*s1->red);
5167 q->green=ClampToQuantum(QuantumScale*shade*s1->green);
5168 q->blue=ClampToQuantum(QuantumScale*shade*s1->blue);
5170 q->opacity=s1->opacity;
5176 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
5178 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5183 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5184 #pragma omp critical (MagickCore_ShadeImage)
5186 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
5187 if (proceed == MagickFalse)
5191 shade_view=DestroyCacheView(shade_view);
5192 image_view=DestroyCacheView(image_view);
5193 if (status == MagickFalse)
5194 shade_image=DestroyImage(shade_image);
5195 return(shade_image);
5199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5203 % S h a r p e n I m a g e %
5207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5209 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
5210 % operator of the given radius and standard deviation (sigma). For
5211 % reasonable results, radius should be larger than sigma. Use a radius of 0
5212 % and SharpenImage() selects a suitable radius for you.
5214 % Using a separable kernel would be faster, but the negative weights cancel
5215 % out on the corners of the kernel producing often undesirable ringing in the
5216 % filtered result; this can be avoided by using a 2D gaussian shaped image
5217 % sharpening kernel instead.
5219 % The format of the SharpenImage method is:
5221 % Image *SharpenImage(const Image *image,const double radius,
5222 % const double sigma,ExceptionInfo *exception)
5223 % Image *SharpenImageChannel(const Image *image,const ChannelType channel,
5224 % const double radius,const double sigma,ExceptionInfo *exception)
5226 % A description of each parameter follows:
5228 % o image: the image.
5230 % o channel: the channel type.
5232 % o radius: the radius of the Gaussian, in pixels, not counting the center
5235 % o sigma: the standard deviation of the Laplacian, in pixels.
5237 % o exception: return any errors or warnings in this structure.
5241 MagickExport Image *SharpenImage(const Image *image,const double radius,
5242 const double sigma,ExceptionInfo *exception)
5247 sharp_image=SharpenImageChannel(image,DefaultChannels,radius,sigma,exception);
5248 return(sharp_image);
5251 MagickExport Image *SharpenImageChannel(const Image *image,
5252 const ChannelType channel,const double radius,const double sigma,
5253 ExceptionInfo *exception)
5273 assert(image != (const Image *) NULL);
5274 assert(image->signature == MagickSignature);
5275 if (image->debug != MagickFalse)
5276 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5277 assert(exception != (ExceptionInfo *) NULL);
5278 assert(exception->signature == MagickSignature);
5279 width=GetOptimalKernelWidth2D(radius,sigma);
5280 kernel=(double *) AcquireQuantumMemory((size_t) width*width,sizeof(*kernel));
5281 if (kernel == (double *) NULL)
5282 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5284 j=(ssize_t) width/2;
5286 for (v=(-j); v <= j; v++)
5288 for (u=(-j); u <= j; u++)
5290 kernel[i]=(double) (-exp(-((double) u*u+v*v)/(2.0*MagickSigma*
5291 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
5292 normalize+=kernel[i];
5296 kernel[i/2]=(double) ((-2.0)*normalize);
5297 sharp_image=ConvolveImageChannel(image,channel,width,kernel,exception);
5298 kernel=(double *) RelinquishMagickMemory(kernel);
5299 return(sharp_image);
5303 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5307 % S p r e a d I m a g e %
5311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5313 % SpreadImage() is a special effects method that randomly displaces each
5314 % pixel in a block defined by the radius parameter.
5316 % The format of the SpreadImage method is:
5318 % Image *SpreadImage(const Image *image,const double radius,
5319 % ExceptionInfo *exception)
5321 % A description of each parameter follows:
5323 % o image: the image.
5325 % o radius: Choose a random pixel in a neighborhood of this extent.
5327 % o exception: return any errors or warnings in this structure.
5330 MagickExport Image *SpreadImage(const Image *image,const double radius,
5331 ExceptionInfo *exception)
5333 #define SpreadImageTag "Spread/Image"
5351 **restrict random_info;
5354 **restrict resample_filter;
5363 Initialize spread image attributes.
5365 assert(image != (Image *) NULL);
5366 assert(image->signature == MagickSignature);
5367 if (image->debug != MagickFalse)
5368 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5369 assert(exception != (ExceptionInfo *) NULL);
5370 assert(exception->signature == MagickSignature);
5371 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
5373 if (spread_image == (Image *) NULL)
5374 return((Image *) NULL);
5375 if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
5377 InheritException(exception,&spread_image->exception);
5378 spread_image=DestroyImage(spread_image);
5379 return((Image *) NULL);
5386 GetMagickPixelPacket(spread_image,&bias);
5387 width=GetOptimalKernelWidth1D(radius,0.5);
5388 resample_filter=AcquireResampleFilterThreadSet(image,
5389 UndefinedVirtualPixelMethod,MagickTrue,exception);
5390 random_info=AcquireRandomInfoThreadSet();
5391 image_view=AcquireCacheView(spread_image);
5392 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5393 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
5395 for (y=0; y < (ssize_t) spread_image->rows; y++)
5398 id = GetOpenMPThreadId();
5403 register IndexPacket
5406 register PixelPacket
5412 if (status == MagickFalse)
5414 q=QueueCacheViewAuthenticPixels(image_view,0,y,spread_image->columns,1,
5416 if (q == (PixelPacket *) NULL)
5421 indexes=GetCacheViewAuthenticIndexQueue(image_view);
5423 for (x=0; x < (ssize_t) spread_image->columns; x++)
5425 (void) ResamplePixelColor(resample_filter[id],(double) x+width*
5426 (GetPseudoRandomValue(random_info[id])-0.5),(double) y+width*
5427 (GetPseudoRandomValue(random_info[id])-0.5),&pixel);
5428 SetPixelPacket(spread_image,&pixel,q,indexes+x);
5431 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5433 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5438 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5439 #pragma omp critical (MagickCore_SpreadImage)
5441 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
5442 if (proceed == MagickFalse)
5446 image_view=DestroyCacheView(image_view);
5447 random_info=DestroyRandomInfoThreadSet(random_info);
5448 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5449 return(spread_image);
5453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5457 % U n s h a r p M a s k I m a g e %
5461 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5463 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
5464 % image with a Gaussian operator of the given radius and standard deviation
5465 % (sigma). For reasonable results, radius should be larger than sigma. Use a
5466 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
5468 % The format of the UnsharpMaskImage method is:
5470 % Image *UnsharpMaskImage(const Image *image,const double radius,
5471 % const double sigma,const double amount,const double threshold,
5472 % ExceptionInfo *exception)
5473 % Image *UnsharpMaskImageChannel(const Image *image,
5474 % const ChannelType channel,const double radius,const double sigma,
5475 % const double amount,const double threshold,ExceptionInfo *exception)
5477 % A description of each parameter follows:
5479 % o image: the image.
5481 % o channel: the channel type.
5483 % o radius: the radius of the Gaussian, in pixels, not counting the center
5486 % o sigma: the standard deviation of the Gaussian, in pixels.
5488 % o amount: the percentage of the difference between the original and the
5489 % blur image that is added back into the original.
5491 % o threshold: the threshold in pixels needed to apply the diffence amount.
5493 % o exception: return any errors or warnings in this structure.
5497 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
5498 const double sigma,const double amount,const double threshold,
5499 ExceptionInfo *exception)
5504 sharp_image=UnsharpMaskImageChannel(image,DefaultChannels,radius,sigma,amount,
5505 threshold,exception);
5506 return(sharp_image);
5509 MagickExport Image *UnsharpMaskImageChannel(const Image *image,
5510 const ChannelType channel,const double radius,const double sigma,
5511 const double amount,const double threshold,ExceptionInfo *exception)
5513 #define SharpenImageTag "Sharpen/Image"
5537 assert(image != (const Image *) NULL);
5538 assert(image->signature == MagickSignature);
5539 if (image->debug != MagickFalse)
5540 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5541 assert(exception != (ExceptionInfo *) NULL);
5542 unsharp_image=BlurImageChannel(image,channel,radius,sigma,exception);
5543 if (unsharp_image == (Image *) NULL)
5544 return((Image *) NULL);
5545 quantum_threshold=(MagickRealType) QuantumRange*threshold;
5551 GetMagickPixelPacket(image,&bias);
5552 image_view=AcquireCacheView(image);
5553 unsharp_view=AcquireCacheView(unsharp_image);
5554 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5555 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5557 for (y=0; y < (ssize_t) image->rows; y++)
5562 register const IndexPacket
5565 register const PixelPacket
5568 register IndexPacket
5569 *restrict unsharp_indexes;
5571 register PixelPacket
5577 if (status == MagickFalse)
5579 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5580 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
5582 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5587 indexes=GetCacheViewVirtualIndexQueue(image_view);
5588 unsharp_indexes=GetCacheViewAuthenticIndexQueue(unsharp_view);
5590 for (x=0; x < (ssize_t) image->columns; x++)
5592 if ((channel & RedChannel) != 0)
5594 pixel.red=p->red-(MagickRealType) q->red;
5595 if (fabs(2.0*pixel.red) < quantum_threshold)
5596 pixel.red=(MagickRealType) GetRedPixelComponent(p);
5598 pixel.red=(MagickRealType) p->red+(pixel.red*amount);
5599 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
5601 if ((channel & GreenChannel) != 0)
5603 pixel.green=p->green-(MagickRealType) q->green;
5604 if (fabs(2.0*pixel.green) < quantum_threshold)
5605 pixel.green=(MagickRealType) GetGreenPixelComponent(p);
5607 pixel.green=(MagickRealType) p->green+(pixel.green*amount);
5608 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
5610 if ((channel & BlueChannel) != 0)
5612 pixel.blue=p->blue-(MagickRealType) q->blue;
5613 if (fabs(2.0*pixel.blue) < quantum_threshold)
5614 pixel.blue=(MagickRealType) GetBluePixelComponent(p);
5616 pixel.blue=(MagickRealType) p->blue+(pixel.blue*amount);
5617 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
5619 if ((channel & OpacityChannel) != 0)
5621 pixel.opacity=p->opacity-(MagickRealType) q->opacity;
5622 if (fabs(2.0*pixel.opacity) < quantum_threshold)
5623 pixel.opacity=(MagickRealType) GetOpacityPixelComponent(p);
5625 pixel.opacity=p->opacity+(pixel.opacity*amount);
5626 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
5628 if (((channel & IndexChannel) != 0) &&
5629 (image->colorspace == CMYKColorspace))
5631 pixel.index=indexes[x]-(MagickRealType) unsharp_indexes[x];
5632 if (fabs(2.0*pixel.index) < quantum_threshold)
5633 pixel.index=(MagickRealType) indexes[x];
5635 pixel.index=(MagickRealType) indexes[x]+(pixel.index*amount);
5636 unsharp_indexes[x]=ClampToQuantum(pixel.index);
5641 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
5643 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5648 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5649 #pragma omp critical (MagickCore_UnsharpMaskImageChannel)
5651 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
5652 if (proceed == MagickFalse)
5656 unsharp_image->type=image->type;
5657 unsharp_view=DestroyCacheView(unsharp_view);
5658 image_view=DestroyCacheView(image_view);
5659 if (status == MagickFalse)
5660 unsharp_image=DestroyImage(unsharp_image);
5661 return(unsharp_image);