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[x];
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[x];
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));
1061 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1063 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1068 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1069 #pragma omp critical (MagickCore_BlurImageChannel)
1071 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1072 blur_image->columns);
1073 if (proceed == MagickFalse)
1077 blur_view=DestroyCacheView(blur_view);
1078 image_view=DestroyCacheView(image_view);
1082 image_view=AcquireCacheView(blur_image);
1083 blur_view=AcquireCacheView(blur_image);
1084 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1085 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1087 for (x=0; x < (ssize_t) blur_image->columns; x++)
1089 register const IndexPacket
1092 register const PixelPacket
1095 register IndexPacket
1096 *restrict blur_indexes;
1098 register PixelPacket
1104 if (status == MagickFalse)
1106 p=GetCacheViewVirtualPixels(image_view,x,-((ssize_t) width/2L),1,
1107 image->rows+width,exception);
1108 q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
1109 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1114 indexes=GetCacheViewVirtualIndexQueue(image_view);
1115 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
1116 for (y=0; y < (ssize_t) blur_image->rows; y++)
1121 register const double
1124 register const PixelPacket
1125 *restrict kernel_pixels;
1133 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
1135 for (i=0; i < (ssize_t) width; i++)
1137 pixel.red+=(*k)*kernel_pixels->red;
1138 pixel.green+=(*k)*kernel_pixels->green;
1139 pixel.blue+=(*k)*kernel_pixels->blue;
1143 if ((channel & RedChannel) != 0)
1144 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
1145 if ((channel & GreenChannel) != 0)
1146 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
1147 if ((channel & BlueChannel) != 0)
1148 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
1149 if ((channel & OpacityChannel) != 0)
1153 for (i=0; i < (ssize_t) width; i++)
1155 pixel.opacity+=(*k)*kernel_pixels->opacity;
1159 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1161 if (((channel & IndexChannel) != 0) &&
1162 (image->colorspace == CMYKColorspace))
1164 register const IndexPacket
1165 *restrict kernel_indexes;
1168 kernel_indexes=indexes[x];
1169 for (i=0; i < (ssize_t) width; i++)
1171 pixel.index+=(*k)*(*kernel_indexes);
1175 blur_indexes[y]=ClampToQuantum(pixel.index);
1185 for (i=0; i < (ssize_t) width; i++)
1187 alpha=(MagickRealType) (QuantumScale*
1188 GetAlphaPixelComponent(kernel_pixels));
1189 pixel.red+=(*k)*alpha*kernel_pixels->red;
1190 pixel.green+=(*k)*alpha*kernel_pixels->green;
1191 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
1196 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1197 if ((channel & RedChannel) != 0)
1198 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
1199 if ((channel & GreenChannel) != 0)
1200 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
1201 if ((channel & BlueChannel) != 0)
1202 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
1203 if ((channel & OpacityChannel) != 0)
1207 for (i=0; i < (ssize_t) width; i++)
1209 pixel.opacity+=(*k)*kernel_pixels->opacity;
1213 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1215 if (((channel & IndexChannel) != 0) &&
1216 (image->colorspace == CMYKColorspace))
1218 register const IndexPacket
1219 *restrict kernel_indexes;
1223 kernel_indexes=indexes[x];
1224 for (i=0; i < (ssize_t) width; i++)
1226 alpha=(MagickRealType) (QuantumScale*
1227 GetAlphaPixelComponent(kernel_pixels));
1228 pixel.index+=(*k)*alpha*(*kernel_indexes);
1233 blur_indexes[y]=ClampToQuantum(gamma*
1234 GetIndexPixelComponent(&pixel));
1240 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1242 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1247 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1248 #pragma omp critical (MagickCore_BlurImageChannel)
1250 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1251 blur_image->columns);
1252 if (proceed == MagickFalse)
1256 blur_view=DestroyCacheView(blur_view);
1257 image_view=DestroyCacheView(image_view);
1258 kernel=(double *) RelinquishMagickMemory(kernel);
1259 if (status == MagickFalse)
1260 blur_image=DestroyImage(blur_image);
1261 blur_image->type=image->type;
1266 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1270 % C o n v o l v e I m a g e %
1274 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1276 % ConvolveImage() applies a custom convolution kernel to the image.
1278 % The format of the ConvolveImage method is:
1280 % Image *ConvolveImage(const Image *image,const size_t order,
1281 % const double *kernel,ExceptionInfo *exception)
1282 % Image *ConvolveImageChannel(const Image *image,const ChannelType channel,
1283 % const size_t order,const double *kernel,ExceptionInfo *exception)
1285 % A description of each parameter follows:
1287 % o image: the image.
1289 % o channel: the channel type.
1291 % o order: the number of columns and rows in the filter kernel.
1293 % o kernel: An array of double representing the convolution kernel.
1295 % o exception: return any errors or warnings in this structure.
1299 MagickExport Image *ConvolveImage(const Image *image,const size_t order,
1300 const double *kernel,ExceptionInfo *exception)
1305 convolve_image=ConvolveImageChannel(image,DefaultChannels,order,kernel,
1307 return(convolve_image);
1310 MagickExport Image *ConvolveImageChannel(const Image *image,
1311 const ChannelType channel,const size_t order,const double *kernel,
1312 ExceptionInfo *exception)
1314 #define ConvolveImageTag "Convolve/Image"
1348 Initialize convolve image attributes.
1350 assert(image != (Image *) NULL);
1351 assert(image->signature == MagickSignature);
1352 if (image->debug != MagickFalse)
1353 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1354 assert(exception != (ExceptionInfo *) NULL);
1355 assert(exception->signature == MagickSignature);
1357 if ((width % 2) == 0)
1358 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
1359 convolve_image=CloneImage(image,0,0,MagickTrue,exception);
1360 if (convolve_image == (Image *) NULL)
1361 return((Image *) NULL);
1362 if (SetImageStorageClass(convolve_image,DirectClass) == MagickFalse)
1364 InheritException(exception,&convolve_image->exception);
1365 convolve_image=DestroyImage(convolve_image);
1366 return((Image *) NULL);
1368 if (image->debug != MagickFalse)
1371 format[MaxTextExtent],
1374 register const double
1381 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1382 " ConvolveImage with %.20gx%.20g kernel:",(double) width,(double)
1384 message=AcquireString("");
1386 for (v=0; v < (ssize_t) width; v++)
1389 (void) FormatMagickString(format,MaxTextExtent,"%.20g: ",(double) v);
1390 (void) ConcatenateString(&message,format);
1391 for (u=0; u < (ssize_t) width; u++)
1393 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
1394 (void) ConcatenateString(&message,format);
1396 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
1398 message=DestroyString(message);
1403 normal_kernel=(double *) AcquireQuantumMemory(width*width,
1404 sizeof(*normal_kernel));
1405 if (normal_kernel == (double *) NULL)
1407 convolve_image=DestroyImage(convolve_image);
1408 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1411 for (i=0; i < (ssize_t) (width*width); i++)
1413 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1414 for (i=0; i < (ssize_t) (width*width); i++)
1415 normal_kernel[i]=gamma*kernel[i];
1421 GetMagickPixelPacket(image,&bias);
1422 SetMagickPixelPacketBias(image,&bias);
1423 image_view=AcquireCacheView(image);
1424 convolve_view=AcquireCacheView(convolve_image);
1425 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1426 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1428 for (y=0; y < (ssize_t) image->rows; y++)
1433 register const IndexPacket
1436 register const PixelPacket
1439 register IndexPacket
1440 *restrict convolve_indexes;
1442 register PixelPacket
1448 if (status == MagickFalse)
1450 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
1451 (width/2L),image->columns+width,width,exception);
1452 q=GetCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
1454 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1459 indexes=GetCacheViewVirtualIndexQueue(image_view);
1460 convolve_indexes=GetCacheViewAuthenticIndexQueue(convolve_view);
1461 for (x=0; x < (ssize_t) image->columns; x++)
1466 register const double
1469 register const PixelPacket
1470 *restrict kernel_pixels;
1481 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
1483 for (v=0; v < (ssize_t) width; v++)
1485 for (u=0; u < (ssize_t) width; u++)
1487 pixel.red+=(*k)*kernel_pixels[u].red;
1488 pixel.green+=(*k)*kernel_pixels[u].green;
1489 pixel.blue+=(*k)*kernel_pixels[u].blue;
1492 kernel_pixels+=image->columns+width;
1494 if ((channel & RedChannel) != 0)
1495 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
1496 if ((channel & GreenChannel) != 0)
1497 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
1498 if ((channel & BlueChannel) != 0)
1499 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
1500 if ((channel & OpacityChannel) != 0)
1504 for (v=0; v < (ssize_t) width; v++)
1506 for (u=0; u < (ssize_t) width; u++)
1508 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1511 kernel_pixels+=image->columns+width;
1513 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1515 if (((channel & IndexChannel) != 0) &&
1516 (image->colorspace == CMYKColorspace))
1518 register const IndexPacket
1519 *restrict kernel_indexes;
1522 kernel_indexes=indexes[x];
1523 for (v=0; v < (ssize_t) width; v++)
1525 for (u=0; u < (ssize_t) width; u++)
1527 pixel.index+=(*k)*kernel_indexes[u];
1530 kernel_indexes+=image->columns+width;
1532 convolve_indexes[x]=ClampToQuantum(pixel.index);
1542 for (v=0; v < (ssize_t) width; v++)
1544 for (u=0; u < (ssize_t) width; u++)
1546 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1547 kernel_pixels[u].opacity));
1548 pixel.red+=(*k)*alpha*kernel_pixels[u].red;
1549 pixel.green+=(*k)*alpha*kernel_pixels[u].green;
1550 pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
1554 kernel_pixels+=image->columns+width;
1556 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1557 if ((channel & RedChannel) != 0)
1558 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
1559 if ((channel & GreenChannel) != 0)
1560 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
1561 if ((channel & BlueChannel) != 0)
1562 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
1563 if ((channel & OpacityChannel) != 0)
1567 for (v=0; v < (ssize_t) width; v++)
1569 for (u=0; u < (ssize_t) width; u++)
1571 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1574 kernel_pixels+=image->columns+width;
1576 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1578 if (((channel & IndexChannel) != 0) &&
1579 (image->colorspace == CMYKColorspace))
1581 register const IndexPacket
1582 *restrict kernel_indexes;
1586 kernel_indexes=indexes[x];
1587 for (v=0; v < (ssize_t) width; v++)
1589 for (u=0; u < (ssize_t) width; u++)
1591 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1592 kernel_pixels[u].opacity));
1593 pixel.index+=(*k)*alpha*kernel_indexes[u];
1596 kernel_pixels+=image->columns+width;
1597 kernel_indexes+=image->columns+width;
1599 convolve_indexes[x]=ClampToQuantum(gamma*
1600 GetIndexPixelComponent(&pixel));
1606 sync=SyncCacheViewAuthenticPixels(convolve_view,exception);
1607 if (sync == MagickFalse)
1609 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1614 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1615 #pragma omp critical (MagickCore_ConvolveImageChannel)
1617 proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1618 if (proceed == MagickFalse)
1622 convolve_image->type=image->type;
1623 convolve_view=DestroyCacheView(convolve_view);
1624 image_view=DestroyCacheView(image_view);
1625 normal_kernel=(double *) RelinquishMagickMemory(normal_kernel);
1626 if (status == MagickFalse)
1627 convolve_image=DestroyImage(convolve_image);
1628 return(convolve_image);
1632 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1636 % D e s p e c k l e I m a g e %
1640 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1642 % DespeckleImage() reduces the speckle noise in an image while perserving the
1643 % edges of the original image.
1645 % The format of the DespeckleImage method is:
1647 % Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1649 % A description of each parameter follows:
1651 % o image: the image.
1653 % o exception: return any errors or warnings in this structure.
1657 static void Hull(const ssize_t x_offset,const ssize_t y_offset,
1658 const size_t columns,const size_t rows,Quantum *f,Quantum *g,
1676 assert(f != (Quantum *) NULL);
1677 assert(g != (Quantum *) NULL);
1680 r=p+(y_offset*((ssize_t) columns+2)+x_offset);
1681 for (y=0; y < (ssize_t) rows; y++)
1687 for (x=(ssize_t) columns; x != 0; x--)
1689 v=(MagickRealType) (*p);
1690 if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1691 v+=ScaleCharToQuantum(1);
1698 for (x=(ssize_t) columns; x != 0; x--)
1700 v=(MagickRealType) (*p);
1701 if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
1702 v-=(ssize_t) ScaleCharToQuantum(1);
1714 r=q+(y_offset*((ssize_t) columns+2)+x_offset);
1715 s=q-(y_offset*((ssize_t) columns+2)+x_offset);
1716 for (y=0; y < (ssize_t) rows; y++)
1723 for (x=(ssize_t) columns; x != 0; x--)
1725 v=(MagickRealType) (*q);
1726 if (((MagickRealType) *s >=
1727 (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1728 ((MagickRealType) *r > v))
1729 v+=ScaleCharToQuantum(1);
1737 for (x=(ssize_t) columns; x != 0; x--)
1739 v=(MagickRealType) (*q);
1740 if (((MagickRealType) *s <=
1741 (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1742 ((MagickRealType) *r < v))
1743 v-=(MagickRealType) ScaleCharToQuantum(1);
1757 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1759 #define DespeckleImageTag "Despeckle/Image"
1782 static const ssize_t
1783 X[4] = {0, 1, 1,-1},
1784 Y[4] = {1, 0, 1, 1};
1787 Allocate despeckled image.
1789 assert(image != (const Image *) NULL);
1790 assert(image->signature == MagickSignature);
1791 if (image->debug != MagickFalse)
1792 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1793 assert(exception != (ExceptionInfo *) NULL);
1794 assert(exception->signature == MagickSignature);
1795 despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1797 if (despeckle_image == (Image *) NULL)
1798 return((Image *) NULL);
1799 if (SetImageStorageClass(despeckle_image,DirectClass) == MagickFalse)
1801 InheritException(exception,&despeckle_image->exception);
1802 despeckle_image=DestroyImage(despeckle_image);
1803 return((Image *) NULL);
1806 Allocate image buffers.
1808 length=(size_t) ((image->columns+2)*(image->rows+2));
1809 pixels=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1810 buffers=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1811 if ((pixels == (Quantum *) NULL) || (buffers == (Quantum *) NULL))
1813 if (buffers != (Quantum *) NULL)
1814 buffers=(Quantum *) RelinquishMagickMemory(buffers);
1815 if (pixels != (Quantum *) NULL)
1816 pixels=(Quantum *) RelinquishMagickMemory(pixels);
1817 despeckle_image=DestroyImage(despeckle_image);
1818 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1821 Reduce speckle in the image.
1824 number_channels=(size_t) (image->colorspace == CMYKColorspace ? 5 : 4);
1825 image_view=AcquireCacheView(image);
1826 despeckle_view=AcquireCacheView(despeckle_image);
1827 for (i=0; i < (ssize_t) number_channels; i++)
1841 if (status == MagickFalse)
1844 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
1846 j=(ssize_t) image->columns+2;
1847 for (y=0; y < (ssize_t) image->rows; y++)
1849 register const IndexPacket
1852 register const PixelPacket
1855 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1856 if (p == (const PixelPacket *) NULL)
1858 indexes=GetCacheViewVirtualIndexQueue(image_view);
1860 for (x=0; x < (ssize_t) image->columns; x++)
1864 case 0: pixel[j]=GetRedPixelComponent(p); break;
1865 case 1: pixel[j]=GetGreenPixelComponent(p); break;
1866 case 2: pixel[j]=GetBluePixelComponent(p); break;
1867 case 3: pixel[j]=GetOpacityPixelComponent(p); break;
1868 case 4: pixel[j]=GetBlackPixelComponent(indexes,x); break;
1876 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1877 for (k=0; k < 4; k++)
1879 Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,1);
1880 Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,1);
1881 Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,-1);
1882 Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,-1);
1884 j=(ssize_t) image->columns+2;
1885 for (y=0; y < (ssize_t) image->rows; y++)
1890 register IndexPacket
1893 register PixelPacket
1896 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1898 if (q == (PixelPacket *) NULL)
1900 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1902 for (x=0; x < (ssize_t) image->columns; x++)
1906 case 0: q->red=pixel[j]; break;
1907 case 1: q->green=pixel[j]; break;
1908 case 2: q->blue=pixel[j]; break;
1909 case 3: q->opacity=pixel[j]; break;
1910 case 4: indexes[x]=pixel[j]; break;
1916 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1917 if (sync == MagickFalse)
1924 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1929 proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1931 if (proceed == MagickFalse)
1935 despeckle_view=DestroyCacheView(despeckle_view);
1936 image_view=DestroyCacheView(image_view);
1937 buffers=(Quantum *) RelinquishMagickMemory(buffers);
1938 pixels=(Quantum *) RelinquishMagickMemory(pixels);
1939 despeckle_image->type=image->type;
1940 if (status == MagickFalse)
1941 despeckle_image=DestroyImage(despeckle_image);
1942 return(despeckle_image);
1946 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1950 % E d g e I m a g e %
1954 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1956 % EdgeImage() finds edges in an image. Radius defines the radius of the
1957 % convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1960 % The format of the EdgeImage method is:
1962 % Image *EdgeImage(const Image *image,const double radius,
1963 % ExceptionInfo *exception)
1965 % A description of each parameter follows:
1967 % o image: the image.
1969 % o radius: the radius of the pixel neighborhood.
1971 % o exception: return any errors or warnings in this structure.
1974 MagickExport Image *EdgeImage(const Image *image,const double radius,
1975 ExceptionInfo *exception)
1989 assert(image != (const Image *) NULL);
1990 assert(image->signature == MagickSignature);
1991 if (image->debug != MagickFalse)
1992 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1993 assert(exception != (ExceptionInfo *) NULL);
1994 assert(exception->signature == MagickSignature);
1995 width=GetOptimalKernelWidth1D(radius,0.5);
1996 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
1997 if (kernel == (double *) NULL)
1998 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1999 for (i=0; i < (ssize_t) (width*width); i++)
2001 kernel[i/2]=(double) (width*width-1.0);
2002 edge_image=ConvolveImage(image,width,kernel,exception);
2003 kernel=(double *) RelinquishMagickMemory(kernel);
2008 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2012 % E m b o s s I m a g e %
2016 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2018 % EmbossImage() returns a grayscale image with a three-dimensional effect.
2019 % We convolve the image with a Gaussian operator of the given radius and
2020 % standard deviation (sigma). For reasonable results, radius should be
2021 % larger than sigma. Use a radius of 0 and Emboss() selects a suitable
2024 % The format of the EmbossImage method is:
2026 % Image *EmbossImage(const Image *image,const double radius,
2027 % const double sigma,ExceptionInfo *exception)
2029 % A description of each parameter follows:
2031 % o image: the image.
2033 % o radius: the radius of the pixel neighborhood.
2035 % o sigma: the standard deviation of the Gaussian, in pixels.
2037 % o exception: return any errors or warnings in this structure.
2040 MagickExport Image *EmbossImage(const Image *image,const double radius,
2041 const double sigma,ExceptionInfo *exception)
2061 assert(image != (Image *) NULL);
2062 assert(image->signature == MagickSignature);
2063 if (image->debug != MagickFalse)
2064 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2065 assert(exception != (ExceptionInfo *) NULL);
2066 assert(exception->signature == MagickSignature);
2067 width=GetOptimalKernelWidth2D(radius,sigma);
2068 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2069 if (kernel == (double *) NULL)
2070 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2071 j=(ssize_t) width/2;
2074 for (v=(-j); v <= j; v++)
2076 for (u=(-j); u <= j; u++)
2078 kernel[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*
2079 exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
2080 (2.0*MagickPI*MagickSigma*MagickSigma));
2087 emboss_image=ConvolveImage(image,width,kernel,exception);
2088 if (emboss_image != (Image *) NULL)
2089 (void) EqualizeImage(emboss_image);
2090 kernel=(double *) RelinquishMagickMemory(kernel);
2091 return(emboss_image);
2095 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2099 % F i l t e r I m a g e %
2103 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2105 % FilterImage() applies a custom convolution kernel to the image.
2107 % The format of the FilterImage method is:
2109 % Image *FilterImage(const Image *image,const KernelInfo *kernel,
2110 % ExceptionInfo *exception)
2111 % Image *FilterImageChannel(const Image *image,const ChannelType channel,
2112 % const KernelInfo *kernel,ExceptionInfo *exception)
2114 % A description of each parameter follows:
2116 % o image: the image.
2118 % o channel: the channel type.
2120 % o kernel: the filtering kernel.
2122 % o exception: return any errors or warnings in this structure.
2126 MagickExport Image *FilterImage(const Image *image,const KernelInfo *kernel,
2127 ExceptionInfo *exception)
2132 filter_image=FilterImageChannel(image,DefaultChannels,kernel,exception);
2133 return(filter_image);
2136 MagickExport Image *FilterImageChannel(const Image *image,
2137 const ChannelType channel,const KernelInfo *kernel,ExceptionInfo *exception)
2139 #define FilterImageTag "Filter/Image"
2161 Initialize filter image attributes.
2163 assert(image != (Image *) NULL);
2164 assert(image->signature == MagickSignature);
2165 if (image->debug != MagickFalse)
2166 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2167 assert(exception != (ExceptionInfo *) NULL);
2168 assert(exception->signature == MagickSignature);
2169 if ((kernel->width % 2) == 0)
2170 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
2171 filter_image=CloneImage(image,0,0,MagickTrue,exception);
2172 if (filter_image == (Image *) NULL)
2173 return((Image *) NULL);
2174 if (SetImageStorageClass(filter_image,DirectClass) == MagickFalse)
2176 InheritException(exception,&filter_image->exception);
2177 filter_image=DestroyImage(filter_image);
2178 return((Image *) NULL);
2180 if (image->debug != MagickFalse)
2183 format[MaxTextExtent],
2186 register const double
2193 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2194 " FilterImage with %.20gx%.20g kernel:",(double) kernel->width,(double)
2196 message=AcquireString("");
2198 for (v=0; v < (ssize_t) kernel->height; v++)
2201 (void) FormatMagickString(format,MaxTextExtent,"%.20g: ",(double) v);
2202 (void) ConcatenateString(&message,format);
2203 for (u=0; u < (ssize_t) kernel->width; u++)
2205 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
2206 (void) ConcatenateString(&message,format);
2208 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
2210 message=DestroyString(message);
2212 status=AccelerateConvolveImage(image,kernel,filter_image,exception);
2213 if (status == MagickTrue)
2214 return(filter_image);
2220 GetMagickPixelPacket(image,&bias);
2221 SetMagickPixelPacketBias(image,&bias);
2222 image_view=AcquireCacheView(image);
2223 filter_view=AcquireCacheView(filter_image);
2224 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2225 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2227 for (y=0; y < (ssize_t) image->rows; y++)
2232 register const IndexPacket
2235 register const PixelPacket
2238 register IndexPacket
2239 *restrict filter_indexes;
2241 register PixelPacket
2247 if (status == MagickFalse)
2249 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel->width/2L),
2250 y-(ssize_t) (kernel->height/2L),image->columns+kernel->width,
2251 kernel->height,exception);
2252 q=GetCacheViewAuthenticPixels(filter_view,0,y,filter_image->columns,1,
2254 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2259 indexes=GetCacheViewVirtualIndexQueue(image_view);
2260 filter_indexes=GetCacheViewAuthenticIndexQueue(filter_view);
2261 for (x=0; x < (ssize_t) image->columns; x++)
2266 register const double
2269 register const PixelPacket
2270 *restrict kernel_pixels;
2281 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
2283 for (v=0; v < (ssize_t) kernel->width; v++)
2285 for (u=0; u < (ssize_t) kernel->height; u++)
2287 pixel.red+=(*k)*kernel_pixels[u].red;
2288 pixel.green+=(*k)*kernel_pixels[u].green;
2289 pixel.blue+=(*k)*kernel_pixels[u].blue;
2292 kernel_pixels+=image->columns+kernel->width;
2294 if ((channel & RedChannel) != 0)
2295 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2296 if ((channel & GreenChannel) != 0)
2297 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2298 if ((channel & BlueChannel) != 0)
2299 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2300 if ((channel & OpacityChannel) != 0)
2304 for (v=0; v < (ssize_t) kernel->width; v++)
2306 for (u=0; u < (ssize_t) kernel->height; u++)
2308 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
2311 kernel_pixels+=image->columns+kernel->width;
2313 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2315 if (((channel & IndexChannel) != 0) &&
2316 (image->colorspace == CMYKColorspace))
2318 register const IndexPacket
2319 *restrict kernel_indexes;
2322 kernel_indexes=indexes[x];
2323 for (v=0; v < (ssize_t) kernel->width; v++)
2325 for (u=0; u < (ssize_t) kernel->height; u++)
2327 pixel.index+=(*k)*kernel_indexes[u];
2330 kernel_indexes+=image->columns+kernel->width;
2332 filter_indexes[x]=ClampToQuantum(pixel.index);
2342 for (v=0; v < (ssize_t) kernel->width; v++)
2344 for (u=0; u < (ssize_t) kernel->height; u++)
2346 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
2347 kernel_pixels[u].opacity));
2348 pixel.red+=(*k)*alpha*kernel_pixels[u].red;
2349 pixel.green+=(*k)*alpha*kernel_pixels[u].green;
2350 pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
2354 kernel_pixels+=image->columns+kernel->width;
2356 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2357 if ((channel & RedChannel) != 0)
2358 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2359 if ((channel & GreenChannel) != 0)
2360 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2361 if ((channel & BlueChannel) != 0)
2362 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2363 if ((channel & OpacityChannel) != 0)
2367 for (v=0; v < (ssize_t) kernel->width; v++)
2369 for (u=0; u < (ssize_t) kernel->height; u++)
2371 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
2374 kernel_pixels+=image->columns+kernel->width;
2376 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2378 if (((channel & IndexChannel) != 0) &&
2379 (image->colorspace == CMYKColorspace))
2381 register const IndexPacket
2382 *restrict kernel_indexes;
2386 kernel_indexes=indexes[x];
2387 for (v=0; v < (ssize_t) kernel->width; v++)
2389 for (u=0; u < (ssize_t) kernel->height; u++)
2391 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
2392 kernel_pixels[u].opacity));
2393 pixel.index+=(*k)*alpha*kernel_indexes[u];
2396 kernel_pixels+=image->columns+kernel->width;
2397 kernel_indexes+=image->columns+kernel->width;
2399 filter_indexes[x]=ClampToQuantum(gamma*
2400 GetIndexPixelComponent(&pixel));
2406 sync=SyncCacheViewAuthenticPixels(filter_view,exception);
2407 if (sync == MagickFalse)
2409 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2414 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2415 #pragma omp critical (MagickCore_FilterImageChannel)
2417 proceed=SetImageProgress(image,FilterImageTag,progress++,image->rows);
2418 if (proceed == MagickFalse)
2422 filter_image->type=image->type;
2423 filter_view=DestroyCacheView(filter_view);
2424 image_view=DestroyCacheView(image_view);
2425 if (status == MagickFalse)
2426 filter_image=DestroyImage(filter_image);
2427 return(filter_image);
2431 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2435 % G a u s s i a n B l u r I m a g e %
2439 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2441 % GaussianBlurImage() blurs an image. We convolve the image with a
2442 % Gaussian operator of the given radius and standard deviation (sigma).
2443 % For reasonable results, the radius should be larger than sigma. Use a
2444 % radius of 0 and GaussianBlurImage() selects a suitable radius for you
2446 % The format of the GaussianBlurImage method is:
2448 % Image *GaussianBlurImage(const Image *image,onst double radius,
2449 % const double sigma,ExceptionInfo *exception)
2450 % Image *GaussianBlurImageChannel(const Image *image,
2451 % const ChannelType channel,const double radius,const double sigma,
2452 % ExceptionInfo *exception)
2454 % A description of each parameter follows:
2456 % o image: the image.
2458 % o channel: the channel type.
2460 % o radius: the radius of the Gaussian, in pixels, not counting the center
2463 % o sigma: the standard deviation of the Gaussian, in pixels.
2465 % o exception: return any errors or warnings in this structure.
2469 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
2470 const double sigma,ExceptionInfo *exception)
2475 blur_image=GaussianBlurImageChannel(image,DefaultChannels,radius,sigma,
2480 MagickExport Image *GaussianBlurImageChannel(const Image *image,
2481 const ChannelType channel,const double radius,const double sigma,
2482 ExceptionInfo *exception)
2501 assert(image != (const Image *) NULL);
2502 assert(image->signature == MagickSignature);
2503 if (image->debug != MagickFalse)
2504 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2505 assert(exception != (ExceptionInfo *) NULL);
2506 assert(exception->signature == MagickSignature);
2507 width=GetOptimalKernelWidth2D(radius,sigma);
2508 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2509 if (kernel == (double *) NULL)
2510 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2511 j=(ssize_t) width/2;
2513 for (v=(-j); v <= j; v++)
2515 for (u=(-j); u <= j; u++)
2516 kernel[i++]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
2517 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
2519 blur_image=ConvolveImageChannel(image,channel,width,kernel,exception);
2520 kernel=(double *) RelinquishMagickMemory(kernel);
2525 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2529 % M e d i a n F i l t e r I m a g e %
2533 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2535 % MedianFilterImage() applies a digital filter that improves the quality
2536 % of a noisy image. Each pixel is replaced by the median in a set of
2537 % neighboring pixels as defined by radius.
2539 % The algorithm was contributed by Mike Edmonds and implements an insertion
2540 % sort for selecting median color-channel values. For more on this algorithm
2541 % see "Skip Lists: A probabilistic Alternative to Balanced Trees" by William
2542 % Pugh in the June 1990 of Communications of the ACM.
2544 % The format of the MedianFilterImage method is:
2546 % Image *MedianFilterImage(const Image *image,const double radius,
2547 % ExceptionInfo *exception)
2549 % A description of each parameter follows:
2551 % o image: the image.
2553 % o radius: the radius of the pixel neighborhood.
2555 % o exception: return any errors or warnings in this structure.
2559 #define ListChannels 5
2561 typedef struct _ListNode
2569 typedef struct _SkipList
2578 typedef struct _PixelList
2586 lists[ListChannels];
2589 static PixelList *DestroyPixelList(PixelList *pixel_list)
2594 if (pixel_list == (PixelList *) NULL)
2595 return((PixelList *) NULL);
2596 for (i=0; i < ListChannels; i++)
2597 if (pixel_list->lists[i].nodes != (ListNode *) NULL)
2598 pixel_list->lists[i].nodes=(ListNode *) RelinquishMagickMemory(
2599 pixel_list->lists[i].nodes);
2600 pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
2604 static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
2609 assert(pixel_list != (PixelList **) NULL);
2610 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
2611 if (pixel_list[i] != (PixelList *) NULL)
2612 pixel_list[i]=DestroyPixelList(pixel_list[i]);
2613 pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
2617 static PixelList *AcquirePixelList(const size_t width)
2625 pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
2626 if (pixel_list == (PixelList *) NULL)
2628 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
2629 pixel_list->center=width*width/2;
2630 for (i=0; i < ListChannels; i++)
2632 pixel_list->lists[i].nodes=(ListNode *) AcquireQuantumMemory(65537UL,
2633 sizeof(*pixel_list->lists[i].nodes));
2634 if (pixel_list->lists[i].nodes == (ListNode *) NULL)
2635 return(DestroyPixelList(pixel_list));
2636 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
2637 sizeof(*pixel_list->lists[i].nodes));
2639 pixel_list->signature=MagickSignature;
2643 static PixelList **AcquirePixelListThreadSet(const size_t width)
2654 number_threads=GetOpenMPMaximumThreads();
2655 pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
2656 sizeof(*pixel_list));
2657 if (pixel_list == (PixelList **) NULL)
2658 return((PixelList **) NULL);
2659 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
2660 for (i=0; i < (ssize_t) number_threads; i++)
2662 pixel_list[i]=AcquirePixelList(width);
2663 if (pixel_list[i] == (PixelList *) NULL)
2664 return(DestroyPixelListThreadSet(pixel_list));
2669 static void AddNodePixelList(PixelList *pixel_list,const ssize_t channel,
2683 Initialize the node.
2685 list=pixel_list->lists+channel;
2686 list->nodes[color].signature=pixel_list->signature;
2687 list->nodes[color].count=1;
2689 Determine where it belongs in the list.
2692 for (level=list->level; level >= 0; level--)
2694 while (list->nodes[search].next[level] < color)
2695 search=list->nodes[search].next[level];
2696 update[level]=search;
2699 Generate a pseudo-random level for this node.
2701 for (level=0; ; level++)
2703 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
2704 if ((pixel_list->seed & 0x300) != 0x300)
2709 if (level > (list->level+2))
2710 level=list->level+2;
2712 If we're raising the list's level, link back to the root node.
2714 while (level > list->level)
2717 update[list->level]=65536UL;
2720 Link the node into the skip-list.
2724 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
2725 list->nodes[update[level]].next[level]=color;
2727 while (level-- > 0);
2730 static MagickPixelPacket GetPixelList(PixelList *pixel_list)
2747 channels[ListChannels];
2750 Find the median value for each of the color.
2752 center=pixel_list->center;
2753 for (channel=0; channel < 5; channel++)
2755 list=pixel_list->lists+channel;
2760 color=list->nodes[color].next[0];
2761 count+=list->nodes[color].count;
2763 while (count <= center);
2764 channels[channel]=(unsigned short) color;
2766 GetMagickPixelPacket((const Image *) NULL,&pixel);
2767 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
2768 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
2769 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
2770 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
2771 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
2775 static inline void InsertPixelList(const Image *image,const PixelPacket *pixel,
2776 const IndexPacket *indexes,PixelList *pixel_list)
2784 index=ScaleQuantumToShort(pixel->red);
2785 signature=pixel_list->lists[0].nodes[index].signature;
2786 if (signature == pixel_list->signature)
2787 pixel_list->lists[0].nodes[index].count++;
2789 AddNodePixelList(pixel_list,0,index);
2790 index=ScaleQuantumToShort(pixel->green);
2791 signature=pixel_list->lists[1].nodes[index].signature;
2792 if (signature == pixel_list->signature)
2793 pixel_list->lists[1].nodes[index].count++;
2795 AddNodePixelList(pixel_list,1,index);
2796 index=ScaleQuantumToShort(pixel->blue);
2797 signature=pixel_list->lists[2].nodes[index].signature;
2798 if (signature == pixel_list->signature)
2799 pixel_list->lists[2].nodes[index].count++;
2801 AddNodePixelList(pixel_list,2,index);
2802 index=ScaleQuantumToShort(pixel->opacity);
2803 signature=pixel_list->lists[3].nodes[index].signature;
2804 if (signature == pixel_list->signature)
2805 pixel_list->lists[3].nodes[index].count++;
2807 AddNodePixelList(pixel_list,3,index);
2808 if (image->colorspace == CMYKColorspace)
2809 index=ScaleQuantumToShort(*indexes);
2810 signature=pixel_list->lists[4].nodes[index].signature;
2811 if (signature == pixel_list->signature)
2812 pixel_list->lists[4].nodes[index].count++;
2814 AddNodePixelList(pixel_list,4,index);
2817 static void ResetPixelList(PixelList *pixel_list)
2832 Reset the skip-list.
2834 for (channel=0; channel < 5; channel++)
2836 list=pixel_list->lists+channel;
2837 root=list->nodes+65536UL;
2839 for (level=0; level < 9; level++)
2840 root->next[level]=65536UL;
2842 pixel_list->seed=pixel_list->signature++;
2845 MagickExport Image *MedianFilterImage(const Image *image,const double radius,
2846 ExceptionInfo *exception)
2848 #define MedianFilterImageTag "MedianFilter/Image"
2864 **restrict pixel_list;
2873 Initialize median image attributes.
2875 assert(image != (Image *) NULL);
2876 assert(image->signature == MagickSignature);
2877 if (image->debug != MagickFalse)
2878 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2879 assert(exception != (ExceptionInfo *) NULL);
2880 assert(exception->signature == MagickSignature);
2881 width=GetOptimalKernelWidth2D(radius,0.5);
2882 median_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2884 if (median_image == (Image *) NULL)
2885 return((Image *) NULL);
2886 if (SetImageStorageClass(median_image,DirectClass) == MagickFalse)
2888 InheritException(exception,&median_image->exception);
2889 median_image=DestroyImage(median_image);
2890 return((Image *) NULL);
2892 pixel_list=AcquirePixelListThreadSet(width);
2893 if (pixel_list == (PixelList **) NULL)
2895 median_image=DestroyImage(median_image);
2896 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2899 Median filter each image row.
2903 image_view=AcquireCacheView(image);
2904 median_view=AcquireCacheView(median_image);
2905 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2906 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2908 for (y=0; y < (ssize_t) median_image->rows; y++)
2911 id = GetOpenMPThreadId();
2913 register const IndexPacket
2916 register const PixelPacket
2919 register IndexPacket
2920 *restrict median_indexes;
2922 register PixelPacket
2928 if (status == MagickFalse)
2930 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
2931 (width/2L),image->columns+width,width,exception);
2932 q=QueueCacheViewAuthenticPixels(median_view,0,y,median_image->columns,1,
2934 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2939 indexes=GetCacheViewVirtualIndexQueue(image_view);
2940 median_indexes=GetCacheViewAuthenticIndexQueue(median_view);
2941 for (x=0; x < (ssize_t) median_image->columns; x++)
2946 register const IndexPacket
2949 register const PixelPacket
2958 ResetPixelList(pixel_list[id]);
2959 for (v=0; v < (ssize_t) width; v++)
2961 for (u=0; u < (ssize_t) width; u++)
2962 InsertPixelList(image,r+u,s+u,pixel_list[id]);
2963 r+=image->columns+width;
2964 s+=image->columns+width;
2966 pixel=GetPixelList(pixel_list[id]);
2967 SetPixelPacket(median_image,&pixel,q,median_indexes+x);
2971 if (SyncCacheViewAuthenticPixels(median_view,exception) == MagickFalse)
2973 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2978 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2979 #pragma omp critical (MagickCore_MedianFilterImage)
2981 proceed=SetImageProgress(image,MedianFilterImageTag,progress++,
2983 if (proceed == MagickFalse)
2987 median_view=DestroyCacheView(median_view);
2988 image_view=DestroyCacheView(image_view);
2989 pixel_list=DestroyPixelListThreadSet(pixel_list);
2990 return(median_image);
2994 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2998 % M o d e I m a g e %
3002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3004 % ModeImage() makes each pixel the 'predominate color' of the neighborhood
3005 % of the specified radius.
3007 % The format of the ModeImage method is:
3009 % Image *ModeImage(const Image *image,const double radius,
3010 % ExceptionInfo *exception)
3012 % A description of each parameter follows:
3014 % o image: the image.
3016 % o radius: the radius of the pixel neighborhood.
3018 % o exception: return any errors or warnings in this structure.
3022 static MagickPixelPacket GetModePixelList(PixelList *pixel_list)
3044 Make each pixel the 'predominate color' of the specified neighborhood.
3046 width=pixel_list->center << 1;
3047 for (channel=0; channel < 5; channel++)
3049 list=pixel_list->lists+channel;
3052 max_count=list->nodes[mode].count;
3056 color=list->nodes[color].next[0];
3057 if (list->nodes[color].count > max_count)
3060 max_count=list->nodes[mode].count;
3062 count+=list->nodes[color].count;
3064 while (count <= width);
3065 channels[channel]=(unsigned short) mode;
3067 GetMagickPixelPacket((const Image *) NULL,&pixel);
3068 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
3069 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
3070 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
3071 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
3072 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
3076 MagickExport Image *ModeImage(const Image *image,const double radius,
3077 ExceptionInfo *exception)
3079 #define ModeImageTag "Mode/Image"
3095 **restrict pixel_list;
3104 Initialize mode image attributes.
3106 assert(image != (Image *) NULL);
3107 assert(image->signature == MagickSignature);
3108 if (image->debug != MagickFalse)
3109 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3110 assert(exception != (ExceptionInfo *) NULL);
3111 assert(exception->signature == MagickSignature);
3112 width=GetOptimalKernelWidth2D(radius,0.5);
3113 mode_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3115 if (mode_image == (Image *) NULL)
3116 return((Image *) NULL);
3117 if (SetImageStorageClass(mode_image,DirectClass) == MagickFalse)
3119 InheritException(exception,&mode_image->exception);
3120 mode_image=DestroyImage(mode_image);
3121 return((Image *) NULL);
3123 pixel_list=AcquirePixelListThreadSet(width);
3124 if (pixel_list == (PixelList **) NULL)
3126 mode_image=DestroyImage(mode_image);
3127 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3134 image_view=AcquireCacheView(image);
3135 mode_view=AcquireCacheView(mode_image);
3136 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3137 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3139 for (y=0; y < (ssize_t) mode_image->rows; y++)
3142 id = GetOpenMPThreadId();
3144 register const IndexPacket
3147 register const PixelPacket
3150 register IndexPacket
3151 *restrict mode_indexes;
3153 register PixelPacket
3159 if (status == MagickFalse)
3161 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
3162 (width/2L),image->columns+width,width,exception);
3163 q=QueueCacheViewAuthenticPixels(mode_view,0,y,mode_image->columns,1,
3165 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3170 indexes=GetCacheViewVirtualIndexQueue(image_view);
3171 mode_indexes=GetCacheViewAuthenticIndexQueue(mode_view);
3172 for (x=0; x < (ssize_t) mode_image->columns; x++)
3177 register const PixelPacket
3180 register const IndexPacket
3189 ResetPixelList(pixel_list[id]);
3190 for (v=0; v < (ssize_t) width; v++)
3192 for (u=0; u < (ssize_t) width; u++)
3193 InsertPixelList(image,r+u,s+u,pixel_list[id]);
3194 r+=image->columns+width;
3195 s+=image->columns+width;
3197 pixel=GetModePixelList(pixel_list[id]);
3198 SetPixelPacket(mode_image,&pixel,q,mode_indexes+x);
3202 if (SyncCacheViewAuthenticPixels(mode_view,exception) == MagickFalse)
3204 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3209 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3210 #pragma omp critical (MagickCore_ModeImage)
3212 proceed=SetImageProgress(image,ModeImageTag,progress++,image->rows);
3213 if (proceed == MagickFalse)
3217 mode_view=DestroyCacheView(mode_view);
3218 image_view=DestroyCacheView(image_view);
3219 pixel_list=DestroyPixelListThreadSet(pixel_list);
3224 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3228 % M o t i o n B l u r I m a g e %
3232 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3234 % MotionBlurImage() simulates motion blur. We convolve the image with a
3235 % Gaussian operator of the given radius and standard deviation (sigma).
3236 % For reasonable results, radius should be larger than sigma. Use a
3237 % radius of 0 and MotionBlurImage() selects a suitable radius for you.
3238 % Angle gives the angle of the blurring motion.
3240 % Andrew Protano contributed this effect.
3242 % The format of the MotionBlurImage method is:
3244 % Image *MotionBlurImage(const Image *image,const double radius,
3245 % const double sigma,const double angle,ExceptionInfo *exception)
3246 % Image *MotionBlurImageChannel(const Image *image,const ChannelType channel,
3247 % const double radius,const double sigma,const double angle,
3248 % ExceptionInfo *exception)
3250 % A description of each parameter follows:
3252 % o image: the image.
3254 % o channel: the channel type.
3256 % o radius: the radius of the Gaussian, in pixels, not counting the center
3257 % o radius: the radius of the Gaussian, in pixels, not counting
3260 % o sigma: the standard deviation of the Gaussian, in pixels.
3262 % o angle: Apply the effect along this angle.
3264 % o exception: return any errors or warnings in this structure.
3268 static double *GetMotionBlurKernel(const size_t width,const double sigma)
3278 Generate a 1-D convolution kernel.
3280 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3281 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
3282 if (kernel == (double *) NULL)
3285 for (i=0; i < (ssize_t) width; i++)
3287 kernel[i]=(double) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
3288 MagickSigma)))/(MagickSQ2PI*MagickSigma));
3289 normalize+=kernel[i];
3291 for (i=0; i < (ssize_t) width; i++)
3292 kernel[i]/=normalize;
3296 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
3297 const double sigma,const double angle,ExceptionInfo *exception)
3302 motion_blur=MotionBlurImageChannel(image,DefaultChannels,radius,sigma,angle,
3304 return(motion_blur);
3307 MagickExport Image *MotionBlurImageChannel(const Image *image,
3308 const ChannelType channel,const double radius,const double sigma,
3309 const double angle,ExceptionInfo *exception)
3345 assert(image != (Image *) NULL);
3346 assert(image->signature == MagickSignature);
3347 if (image->debug != MagickFalse)
3348 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3349 assert(exception != (ExceptionInfo *) NULL);
3350 width=GetOptimalKernelWidth1D(radius,sigma);
3351 kernel=GetMotionBlurKernel(width,sigma);
3352 if (kernel == (double *) NULL)
3353 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3354 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
3355 if (offset == (OffsetInfo *) NULL)
3357 kernel=(double *) RelinquishMagickMemory(kernel);
3358 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3360 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3361 if (blur_image == (Image *) NULL)
3363 kernel=(double *) RelinquishMagickMemory(kernel);
3364 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3365 return((Image *) NULL);
3367 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3369 kernel=(double *) RelinquishMagickMemory(kernel);
3370 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3371 InheritException(exception,&blur_image->exception);
3372 blur_image=DestroyImage(blur_image);
3373 return((Image *) NULL);
3375 point.x=(double) width*sin(DegreesToRadians(angle));
3376 point.y=(double) width*cos(DegreesToRadians(angle));
3377 for (i=0; i < (ssize_t) width; i++)
3379 offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
3380 offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
3387 GetMagickPixelPacket(image,&bias);
3388 image_view=AcquireCacheView(image);
3389 blur_view=AcquireCacheView(blur_image);
3390 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3391 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
3393 for (y=0; y < (ssize_t) image->rows; y++)
3395 register IndexPacket
3396 *restrict blur_indexes;
3398 register PixelPacket
3404 if (status == MagickFalse)
3406 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3408 if (q == (PixelPacket *) NULL)
3413 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
3414 for (x=0; x < (ssize_t) image->columns; x++)
3422 register const IndexPacket
3433 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
3435 for (i=0; i < (ssize_t) width; i++)
3437 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
3438 offset[i].y,&pixel,exception);
3439 qixel.red+=(*k)*pixel.red;
3440 qixel.green+=(*k)*pixel.green;
3441 qixel.blue+=(*k)*pixel.blue;
3442 qixel.opacity+=(*k)*pixel.opacity;
3443 if (image->colorspace == CMYKColorspace)
3445 indexes=GetCacheViewVirtualIndexQueue(image_view);
3446 qixel.index+=(*k)*(*indexes);
3450 if ((channel & RedChannel) != 0)
3451 q->red=ClampToQuantum(qixel.red);
3452 if ((channel & GreenChannel) != 0)
3453 q->green=ClampToQuantum(qixel.green);
3454 if ((channel & BlueChannel) != 0)
3455 q->blue=ClampToQuantum(qixel.blue);
3456 if ((channel & OpacityChannel) != 0)
3457 q->opacity=ClampToQuantum(qixel.opacity);
3458 if (((channel & IndexChannel) != 0) &&
3459 (image->colorspace == CMYKColorspace))
3460 blur_indexes[x]=(IndexPacket) ClampToQuantum(qixel.index);
3470 for (i=0; i < (ssize_t) width; i++)
3472 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
3473 offset[i].y,&pixel,exception);
3474 alpha=(MagickRealType) (QuantumScale*
3475 GetAlphaPixelComponent(&pixel));
3476 qixel.red+=(*k)*alpha*pixel.red;
3477 qixel.green+=(*k)*alpha*pixel.green;
3478 qixel.blue+=(*k)*alpha*pixel.blue;
3479 qixel.opacity+=(*k)*pixel.opacity;
3480 if (image->colorspace == CMYKColorspace)
3482 indexes=GetCacheViewVirtualIndexQueue(image_view);
3483 qixel.index+=(*k)*alpha*(*indexes);
3488 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3489 if ((channel & RedChannel) != 0)
3490 q->red=ClampToQuantum(gamma*qixel.red);
3491 if ((channel & GreenChannel) != 0)
3492 q->green=ClampToQuantum(gamma*qixel.green);
3493 if ((channel & BlueChannel) != 0)
3494 q->blue=ClampToQuantum(gamma*qixel.blue);
3495 if ((channel & OpacityChannel) != 0)
3496 q->opacity=ClampToQuantum(qixel.opacity);
3497 if (((channel & IndexChannel) != 0) &&
3498 (image->colorspace == CMYKColorspace))
3499 blur_indexes[x]=(IndexPacket) ClampToQuantum(gamma*qixel.index);
3503 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3505 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3510 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3511 #pragma omp critical (MagickCore_MotionBlurImageChannel)
3513 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3514 if (proceed == MagickFalse)
3518 blur_view=DestroyCacheView(blur_view);
3519 image_view=DestroyCacheView(image_view);
3520 kernel=(double *) RelinquishMagickMemory(kernel);
3521 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3522 if (status == MagickFalse)
3523 blur_image=DestroyImage(blur_image);
3528 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3532 % P r e v i e w I m a g e %
3536 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3538 % PreviewImage() tiles 9 thumbnails of the specified image with an image
3539 % processing operation applied with varying parameters. This may be helpful
3540 % pin-pointing an appropriate parameter for a particular image processing
3543 % The format of the PreviewImages method is:
3545 % Image *PreviewImages(const Image *image,const PreviewType preview,
3546 % ExceptionInfo *exception)
3548 % A description of each parameter follows:
3550 % o image: the image.
3552 % o preview: the image processing operation.
3554 % o exception: return any errors or warnings in this structure.
3557 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
3558 ExceptionInfo *exception)
3560 #define NumberTiles 9
3561 #define PreviewImageTag "Preview/Image"
3562 #define DefaultPreviewGeometry "204x204+10+10"
3565 factor[MaxTextExtent],
3566 label[MaxTextExtent];
3608 Open output image file.
3610 assert(image != (Image *) NULL);
3611 assert(image->signature == MagickSignature);
3612 if (image->debug != MagickFalse)
3613 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3617 preview_info=AcquireImageInfo();
3618 SetGeometry(image,&geometry);
3619 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
3620 &geometry.width,&geometry.height);
3621 images=NewImageList();
3623 GetQuantizeInfo(&quantize_info);
3629 for (i=0; i < NumberTiles; i++)
3631 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
3632 if (thumbnail == (Image *) NULL)
3634 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
3636 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
3637 if (i == (NumberTiles/2))
3639 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
3640 AppendImageToList(&images,thumbnail);
3648 preview_image=RotateImage(thumbnail,degrees,exception);
3649 (void) FormatMagickString(label,MaxTextExtent,"rotate %g",degrees);
3655 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
3656 (void) FormatMagickString(label,MaxTextExtent,"shear %gx%g",
3657 degrees,2.0*degrees);
3662 x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
3663 y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
3664 preview_image=RollImage(thumbnail,x,y,exception);
3665 (void) FormatMagickString(label,MaxTextExtent,"roll %+.20gx%+.20g",
3666 (double) x,(double) y);
3671 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3672 if (preview_image == (Image *) NULL)
3674 (void) FormatMagickString(factor,MaxTextExtent,"100,100,%g",
3676 (void) ModulateImage(preview_image,factor);
3677 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3680 case SaturationPreview:
3682 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3683 if (preview_image == (Image *) NULL)
3685 (void) FormatMagickString(factor,MaxTextExtent,"100,%g",
3687 (void) ModulateImage(preview_image,factor);
3688 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3691 case BrightnessPreview:
3693 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3694 if (preview_image == (Image *) NULL)
3696 (void) FormatMagickString(factor,MaxTextExtent,"%g",2.0*percentage);
3697 (void) ModulateImage(preview_image,factor);
3698 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3704 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3705 if (preview_image == (Image *) NULL)
3708 (void) GammaImageChannel(preview_image,DefaultChannels,gamma);
3709 (void) FormatMagickString(label,MaxTextExtent,"gamma %g",gamma);
3714 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3715 if (preview_image != (Image *) NULL)
3716 for (x=0; x < i; x++)
3717 (void) ContrastImage(preview_image,MagickTrue);
3718 (void) FormatMagickString(label,MaxTextExtent,"contrast (%.20g)",
3724 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3725 if (preview_image == (Image *) NULL)
3727 for (x=0; x < i; x++)
3728 (void) ContrastImage(preview_image,MagickFalse);
3729 (void) FormatMagickString(label,MaxTextExtent,"+contrast (%.20g)",
3733 case GrayscalePreview:
3735 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3736 if (preview_image == (Image *) NULL)
3739 quantize_info.number_colors=colors;
3740 quantize_info.colorspace=GRAYColorspace;
3741 (void) QuantizeImage(&quantize_info,preview_image);
3742 (void) FormatMagickString(label,MaxTextExtent,
3743 "-colorspace gray -colors %.20g",(double) colors);
3746 case QuantizePreview:
3748 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3749 if (preview_image == (Image *) NULL)
3752 quantize_info.number_colors=colors;
3753 (void) QuantizeImage(&quantize_info,preview_image);
3754 (void) FormatMagickString(label,MaxTextExtent,"colors %.20g",(double)
3758 case DespecklePreview:
3760 for (x=0; x < (i-1); x++)
3762 preview_image=DespeckleImage(thumbnail,exception);
3763 if (preview_image == (Image *) NULL)
3765 thumbnail=DestroyImage(thumbnail);
3766 thumbnail=preview_image;
3768 preview_image=DespeckleImage(thumbnail,exception);
3769 if (preview_image == (Image *) NULL)
3771 (void) FormatMagickString(label,MaxTextExtent,"despeckle (%.20g)",
3775 case ReduceNoisePreview:
3777 preview_image=ReduceNoiseImage(thumbnail,radius,exception);
3778 (void) FormatMagickString(label,MaxTextExtent,"noise %g",radius);
3781 case AddNoisePreview:
3787 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
3792 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
3797 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
3802 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
3807 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
3812 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
3817 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
3821 preview_image=ReduceNoiseImage(thumbnail,(double) i,exception);
3822 (void) FormatMagickString(label,MaxTextExtent,"+noise %s",factor);
3825 case SharpenPreview:
3827 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
3828 (void) FormatMagickString(label,MaxTextExtent,"sharpen %gx%g",
3834 preview_image=BlurImage(thumbnail,radius,sigma,exception);
3835 (void) FormatMagickString(label,MaxTextExtent,"blur %gx%g",radius,
3839 case ThresholdPreview:
3841 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3842 if (preview_image == (Image *) NULL)
3844 (void) BilevelImage(thumbnail,
3845 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3846 (void) FormatMagickString(label,MaxTextExtent,"threshold %g",
3847 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3850 case EdgeDetectPreview:
3852 preview_image=EdgeImage(thumbnail,radius,exception);
3853 (void) FormatMagickString(label,MaxTextExtent,"edge %g",radius);
3858 preview_image=SpreadImage(thumbnail,radius,exception);
3859 (void) FormatMagickString(label,MaxTextExtent,"spread %g",
3863 case SolarizePreview:
3865 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3866 if (preview_image == (Image *) NULL)
3868 (void) SolarizeImage(preview_image,(double) QuantumRange*
3870 (void) FormatMagickString(label,MaxTextExtent,"solarize %g",
3871 (QuantumRange*percentage)/100.0);
3877 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
3879 (void) FormatMagickString(label,MaxTextExtent,"shade %gx%g",
3885 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3886 if (preview_image == (Image *) NULL)
3888 geometry.width=(size_t) (2*i+2);
3889 geometry.height=(size_t) (2*i+2);
3892 (void) RaiseImage(preview_image,&geometry,MagickTrue);
3893 (void) FormatMagickString(label,MaxTextExtent,
3894 "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
3895 geometry.height,(double) geometry.x,(double) geometry.y);
3898 case SegmentPreview:
3900 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3901 if (preview_image == (Image *) NULL)
3904 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
3906 (void) FormatMagickString(label,MaxTextExtent,"segment %gx%g",
3907 threshold,threshold);
3912 preview_image=SwirlImage(thumbnail,degrees,exception);
3913 (void) FormatMagickString(label,MaxTextExtent,"swirl %g",degrees);
3917 case ImplodePreview:
3920 preview_image=ImplodeImage(thumbnail,degrees,exception);
3921 (void) FormatMagickString(label,MaxTextExtent,"implode %g",degrees);
3927 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
3928 (void) FormatMagickString(label,MaxTextExtent,"wave %gx%g",
3929 0.5*degrees,2.0*degrees);
3932 case OilPaintPreview:
3934 preview_image=OilPaintImage(thumbnail,(double) radius,exception);
3935 (void) FormatMagickString(label,MaxTextExtent,"paint %g",radius);
3938 case CharcoalDrawingPreview:
3940 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
3942 (void) FormatMagickString(label,MaxTextExtent,"charcoal %gx%g",
3949 filename[MaxTextExtent];
3957 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3958 if (preview_image == (Image *) NULL)
3960 preview_info->quality=(size_t) percentage;
3961 (void) FormatMagickString(factor,MaxTextExtent,"%.20g",(double)
3962 preview_info->quality);
3963 file=AcquireUniqueFileResource(filename);
3966 (void) FormatMagickString(preview_image->filename,MaxTextExtent,
3967 "jpeg:%s",filename);
3968 status=WriteImage(preview_info,preview_image);
3969 if (status != MagickFalse)
3974 (void) CopyMagickString(preview_info->filename,
3975 preview_image->filename,MaxTextExtent);
3976 quality_image=ReadImage(preview_info,exception);
3977 if (quality_image != (Image *) NULL)
3979 preview_image=DestroyImage(preview_image);
3980 preview_image=quality_image;
3983 (void) RelinquishUniqueFileResource(preview_image->filename);
3984 if ((GetBlobSize(preview_image)/1024) >= 1024)
3985 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gmb ",
3986 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
3989 if (GetBlobSize(preview_image) >= 1024)
3990 (void) FormatMagickString(label,MaxTextExtent,
3991 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
3992 GetBlobSize(preview_image))/1024.0);
3994 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%.20gb ",
3995 factor,(double) GetBlobSize(thumbnail));
3999 thumbnail=DestroyImage(thumbnail);
4003 if (preview_image == (Image *) NULL)
4005 (void) DeleteImageProperty(preview_image,"label");
4006 (void) SetImageProperty(preview_image,"label",label);
4007 AppendImageToList(&images,preview_image);
4008 proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
4010 if (proceed == MagickFalse)
4013 if (images == (Image *) NULL)
4015 preview_info=DestroyImageInfo(preview_info);
4016 return((Image *) NULL);
4021 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
4022 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
4023 montage_info->shadow=MagickTrue;
4024 (void) CloneString(&montage_info->tile,"3x3");
4025 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
4026 (void) CloneString(&montage_info->frame,DefaultTileFrame);
4027 montage_image=MontageImages(images,montage_info,exception);
4028 montage_info=DestroyMontageInfo(montage_info);
4029 images=DestroyImageList(images);
4030 if (montage_image == (Image *) NULL)
4031 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4032 if (montage_image->montage != (char *) NULL)
4035 Free image directory.
4037 montage_image->montage=(char *) RelinquishMagickMemory(
4038 montage_image->montage);
4039 if (image->directory != (char *) NULL)
4040 montage_image->directory=(char *) RelinquishMagickMemory(
4041 montage_image->directory);
4043 preview_info=DestroyImageInfo(preview_info);
4044 return(montage_image);
4048 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4052 % R a d i a l B l u r I m a g e %
4056 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4058 % RadialBlurImage() applies a radial blur to the image.
4060 % Andrew Protano contributed this effect.
4062 % The format of the RadialBlurImage method is:
4064 % Image *RadialBlurImage(const Image *image,const double angle,
4065 % ExceptionInfo *exception)
4066 % Image *RadialBlurImageChannel(const Image *image,const ChannelType channel,
4067 % const double angle,ExceptionInfo *exception)
4069 % A description of each parameter follows:
4071 % o image: the image.
4073 % o channel: the channel type.
4075 % o angle: the angle of the radial blur.
4077 % o exception: return any errors or warnings in this structure.
4081 MagickExport Image *RadialBlurImage(const Image *image,const double angle,
4082 ExceptionInfo *exception)
4087 blur_image=RadialBlurImageChannel(image,DefaultChannels,angle,exception);
4091 MagickExport Image *RadialBlurImageChannel(const Image *image,
4092 const ChannelType channel,const double angle,ExceptionInfo *exception)
4130 Allocate blur image.
4132 assert(image != (Image *) NULL);
4133 assert(image->signature == MagickSignature);
4134 if (image->debug != MagickFalse)
4135 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4136 assert(exception != (ExceptionInfo *) NULL);
4137 assert(exception->signature == MagickSignature);
4138 blur_image=CloneImage(image,0,0,MagickTrue,exception);
4139 if (blur_image == (Image *) NULL)
4140 return((Image *) NULL);
4141 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
4143 InheritException(exception,&blur_image->exception);
4144 blur_image=DestroyImage(blur_image);
4145 return((Image *) NULL);
4147 blur_center.x=(double) image->columns/2.0;
4148 blur_center.y=(double) image->rows/2.0;
4149 blur_radius=hypot(blur_center.x,blur_center.y);
4150 n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
4151 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
4152 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
4153 sizeof(*cos_theta));
4154 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
4155 sizeof(*sin_theta));
4156 if ((cos_theta == (MagickRealType *) NULL) ||
4157 (sin_theta == (MagickRealType *) NULL))
4159 blur_image=DestroyImage(blur_image);
4160 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4162 offset=theta*(MagickRealType) (n-1)/2.0;
4163 for (i=0; i < (ssize_t) n; i++)
4165 cos_theta[i]=cos((double) (theta*i-offset));
4166 sin_theta[i]=sin((double) (theta*i-offset));
4173 GetMagickPixelPacket(image,&bias);
4174 image_view=AcquireCacheView(image);
4175 blur_view=AcquireCacheView(blur_image);
4176 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4177 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4179 for (y=0; y < (ssize_t) blur_image->rows; y++)
4181 register const IndexPacket
4184 register IndexPacket
4185 *restrict blur_indexes;
4187 register PixelPacket
4193 if (status == MagickFalse)
4195 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
4197 if (q == (PixelPacket *) NULL)
4202 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
4203 for (x=0; x < (ssize_t) blur_image->columns; x++)
4224 center.x=(double) x-blur_center.x;
4225 center.y=(double) y-blur_center.y;
4226 radius=hypot((double) center.x,center.y);
4231 step=(size_t) (blur_radius/radius);
4240 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
4242 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
4244 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
4245 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
4246 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
4247 cos_theta[i]+0.5),&pixel,exception);
4248 qixel.red+=pixel.red;
4249 qixel.green+=pixel.green;
4250 qixel.blue+=pixel.blue;
4251 qixel.opacity+=pixel.opacity;
4252 if (image->colorspace == CMYKColorspace)
4254 indexes=GetCacheViewVirtualIndexQueue(image_view);
4255 qixel.index+=(*indexes);
4259 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
4261 if ((channel & RedChannel) != 0)
4262 q->red=ClampToQuantum(normalize*qixel.red);
4263 if ((channel & GreenChannel) != 0)
4264 q->green=ClampToQuantum(normalize*qixel.green);
4265 if ((channel & BlueChannel) != 0)
4266 q->blue=ClampToQuantum(normalize*qixel.blue);
4267 if ((channel & OpacityChannel) != 0)
4268 q->opacity=ClampToQuantum(normalize*qixel.opacity);
4269 if (((channel & IndexChannel) != 0) &&
4270 (image->colorspace == CMYKColorspace))
4271 blur_indexes[x]=(IndexPacket) ClampToQuantum(normalize*qixel.index);
4281 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
4283 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
4284 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
4285 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
4286 cos_theta[i]+0.5),&pixel,exception);
4287 alpha=(MagickRealType) (QuantumScale*
4288 GetAlphaPixelComponent(&pixel));
4289 qixel.red+=alpha*pixel.red;
4290 qixel.green+=alpha*pixel.green;
4291 qixel.blue+=alpha*pixel.blue;
4292 qixel.opacity+=pixel.opacity;
4293 if (image->colorspace == CMYKColorspace)
4295 indexes=GetCacheViewVirtualIndexQueue(image_view);
4296 qixel.index+=alpha*(*indexes);
4301 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4302 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
4304 if ((channel & RedChannel) != 0)
4305 q->red=ClampToQuantum(gamma*qixel.red);
4306 if ((channel & GreenChannel) != 0)
4307 q->green=ClampToQuantum(gamma*qixel.green);
4308 if ((channel & BlueChannel) != 0)
4309 q->blue=ClampToQuantum(gamma*qixel.blue);
4310 if ((channel & OpacityChannel) != 0)
4311 q->opacity=ClampToQuantum(normalize*qixel.opacity);
4312 if (((channel & IndexChannel) != 0) &&
4313 (image->colorspace == CMYKColorspace))
4314 blur_indexes[x]=(IndexPacket) ClampToQuantum(gamma*qixel.index);
4318 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
4320 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4325 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4326 #pragma omp critical (MagickCore_RadialBlurImageChannel)
4328 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
4329 if (proceed == MagickFalse)
4333 blur_view=DestroyCacheView(blur_view);
4334 image_view=DestroyCacheView(image_view);
4335 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
4336 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
4337 if (status == MagickFalse)
4338 blur_image=DestroyImage(blur_image);
4343 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4347 % R e d u c e N o i s e I m a g e %
4351 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4353 % ReduceNoiseImage() smooths the contours of an image while still preserving
4354 % edge information. The algorithm works by replacing each pixel with its
4355 % neighbor closest in value. A neighbor is defined by radius. Use a radius
4356 % of 0 and ReduceNoise() selects a suitable radius for you.
4358 % The format of the ReduceNoiseImage method is:
4360 % Image *ReduceNoiseImage(const Image *image,const double radius,
4361 % ExceptionInfo *exception)
4363 % A description of each parameter follows:
4365 % o image: the image.
4367 % o radius: the radius of the pixel neighborhood.
4369 % o exception: return any errors or warnings in this structure.
4373 static MagickPixelPacket GetNonpeakPixelList(PixelList *pixel_list)
4395 Finds the median value for each of the color.
4397 center=pixel_list->center;
4398 for (channel=0; channel < 5; channel++)
4400 list=pixel_list->lists+channel;
4402 next=list->nodes[color].next[0];
4408 next=list->nodes[color].next[0];
4409 count+=list->nodes[color].count;
4411 while (count <= center);
4412 if ((previous == 65536UL) && (next != 65536UL))
4415 if ((previous != 65536UL) && (next == 65536UL))
4417 channels[channel]=(unsigned short) color;
4419 GetMagickPixelPacket((const Image *) NULL,&pixel);
4420 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4421 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4422 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
4423 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
4424 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
4428 MagickExport Image *ReduceNoiseImage(const Image *image,const double radius,
4429 ExceptionInfo *exception)
4431 #define ReduceNoiseImageTag "ReduceNoise/Image"
4447 **restrict pixel_list;
4456 Initialize noise image attributes.
4458 assert(image != (Image *) NULL);
4459 assert(image->signature == MagickSignature);
4460 if (image->debug != MagickFalse)
4461 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4462 assert(exception != (ExceptionInfo *) NULL);
4463 assert(exception->signature == MagickSignature);
4464 width=GetOptimalKernelWidth2D(radius,0.5);
4465 noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4467 if (noise_image == (Image *) NULL)
4468 return((Image *) NULL);
4469 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
4471 InheritException(exception,&noise_image->exception);
4472 noise_image=DestroyImage(noise_image);
4473 return((Image *) NULL);
4475 pixel_list=AcquirePixelListThreadSet(width);
4476 if (pixel_list == (PixelList **) NULL)
4478 noise_image=DestroyImage(noise_image);
4479 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4486 image_view=AcquireCacheView(image);
4487 noise_view=AcquireCacheView(noise_image);
4488 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4489 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4491 for (y=0; y < (ssize_t) noise_image->rows; y++)
4494 id = GetOpenMPThreadId();
4496 register const IndexPacket
4499 register const PixelPacket
4502 register IndexPacket
4503 *restrict noise_indexes;
4505 register PixelPacket
4511 if (status == MagickFalse)
4513 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
4514 (width/2L),image->columns+width,width,exception);
4515 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
4517 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4522 indexes=GetCacheViewVirtualIndexQueue(image_view);
4523 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
4524 for (x=0; x < (ssize_t) noise_image->columns; x++)
4529 register const PixelPacket
4532 register const IndexPacket
4541 ResetPixelList(pixel_list[id]);
4542 for (v=0; v < (ssize_t) width; v++)
4544 for (u=0; u < (ssize_t) width; u++)
4545 InsertPixelList(image,r+u,s+u,pixel_list[id]);
4546 r+=image->columns+width;
4547 s+=image->columns+width;
4549 pixel=GetNonpeakPixelList(pixel_list[id]);
4550 SetPixelPacket(noise_image,&pixel,q,noise_indexes+x);
4554 if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
4556 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4561 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4562 #pragma omp critical (MagickCore_ReduceNoiseImage)
4564 proceed=SetImageProgress(image,ReduceNoiseImageTag,progress++,
4566 if (proceed == MagickFalse)
4570 noise_view=DestroyCacheView(noise_view);
4571 image_view=DestroyCacheView(image_view);
4572 pixel_list=DestroyPixelListThreadSet(pixel_list);
4573 return(noise_image);
4577 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4581 % S e l e c t i v e B l u r I m a g e %
4585 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4587 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
4588 % It is similar to the unsharpen mask that sharpens everything with contrast
4589 % above a certain threshold.
4591 % The format of the SelectiveBlurImage method is:
4593 % Image *SelectiveBlurImage(const Image *image,const double radius,
4594 % const double sigma,const double threshold,ExceptionInfo *exception)
4595 % Image *SelectiveBlurImageChannel(const Image *image,
4596 % const ChannelType channel,const double radius,const double sigma,
4597 % const double threshold,ExceptionInfo *exception)
4599 % A description of each parameter follows:
4601 % o image: the image.
4603 % o channel: the channel type.
4605 % o radius: the radius of the Gaussian, in pixels, not counting the center
4608 % o sigma: the standard deviation of the Gaussian, in pixels.
4610 % o threshold: only pixels within this contrast threshold are included
4611 % in the blur operation.
4613 % o exception: return any errors or warnings in this structure.
4617 static inline MagickBooleanType SelectiveContrast(const PixelPacket *p,
4618 const PixelPacket *q,const double threshold)
4620 if (fabs(PixelIntensity(p)-PixelIntensity(q)) < threshold)
4622 return(MagickFalse);
4625 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
4626 const double sigma,const double threshold,ExceptionInfo *exception)
4631 blur_image=SelectiveBlurImageChannel(image,DefaultChannels,radius,sigma,
4632 threshold,exception);
4636 MagickExport Image *SelectiveBlurImageChannel(const Image *image,
4637 const ChannelType channel,const double radius,const double sigma,
4638 const double threshold,ExceptionInfo *exception)
4640 #define SelectiveBlurImageTag "SelectiveBlur/Image"
4674 Initialize blur image attributes.
4676 assert(image != (Image *) NULL);
4677 assert(image->signature == MagickSignature);
4678 if (image->debug != MagickFalse)
4679 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4680 assert(exception != (ExceptionInfo *) NULL);
4681 assert(exception->signature == MagickSignature);
4682 width=GetOptimalKernelWidth1D(radius,sigma);
4683 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
4684 if (kernel == (double *) NULL)
4685 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4686 j=(ssize_t) width/2;
4688 for (v=(-j); v <= j; v++)
4690 for (u=(-j); u <= j; u++)
4691 kernel[i++]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
4692 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
4694 if (image->debug != MagickFalse)
4697 format[MaxTextExtent],
4700 register const double
4707 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
4708 " SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
4710 message=AcquireString("");
4712 for (v=0; v < (ssize_t) width; v++)
4715 (void) FormatMagickString(format,MaxTextExtent,"%.20g: ",(double) v);
4716 (void) ConcatenateString(&message,format);
4717 for (u=0; u < (ssize_t) width; u++)
4719 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
4720 (void) ConcatenateString(&message,format);
4722 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
4724 message=DestroyString(message);
4726 blur_image=CloneImage(image,0,0,MagickTrue,exception);
4727 if (blur_image == (Image *) NULL)
4728 return((Image *) NULL);
4729 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
4731 InheritException(exception,&blur_image->exception);
4732 blur_image=DestroyImage(blur_image);
4733 return((Image *) NULL);
4736 Threshold blur image.
4740 GetMagickPixelPacket(image,&bias);
4741 SetMagickPixelPacketBias(image,&bias);
4742 image_view=AcquireCacheView(image);
4743 blur_view=AcquireCacheView(blur_image);
4744 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4745 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4747 for (y=0; y < (ssize_t) image->rows; y++)
4755 register const IndexPacket
4758 register const PixelPacket
4761 register IndexPacket
4762 *restrict blur_indexes;
4764 register PixelPacket
4770 if (status == MagickFalse)
4772 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
4773 (width/2L),image->columns+width,width,exception);
4774 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
4776 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4781 indexes=GetCacheViewVirtualIndexQueue(image_view);
4782 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
4783 for (x=0; x < (ssize_t) image->columns; x++)
4788 register const double
4802 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
4804 for (v=0; v < (ssize_t) width; v++)
4806 for (u=0; u < (ssize_t) width; u++)
4808 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4810 pixel.red+=(*k)*(p+u+j)->red;
4811 pixel.green+=(*k)*(p+u+j)->green;
4812 pixel.blue+=(*k)*(p+u+j)->blue;
4817 j+=(ssize_t) (image->columns+width);
4821 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4822 if ((channel & RedChannel) != 0)
4823 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
4824 if ((channel & GreenChannel) != 0)
4825 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
4826 if ((channel & BlueChannel) != 0)
4827 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
4829 if ((channel & OpacityChannel) != 0)
4833 for (v=0; v < (ssize_t) width; v++)
4835 for (u=0; u < (ssize_t) width; u++)
4837 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4839 pixel.opacity+=(*k)*(p+u+j)->opacity;
4844 j+=(ssize_t) (image->columns+width);
4848 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4850 SetOpacityPixelComponent(q,ClampToQuantum(gamma*
4851 GetOpacityPixelComponent(&pixel)));
4854 if (((channel & IndexChannel) != 0) &&
4855 (image->colorspace == CMYKColorspace))
4859 for (v=0; v < (ssize_t) width; v++)
4861 for (u=0; u < (ssize_t) width; u++)
4863 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4865 pixel.index+=(*k)*indexes[x+u+j];
4870 j+=(ssize_t) (image->columns+width);
4874 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4876 blur_indexes[x]=ClampToQuantum(gamma*
4877 GetIndexPixelComponent(&pixel));
4886 for (v=0; v < (ssize_t) width; v++)
4888 for (u=0; u < (ssize_t) width; u++)
4890 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4892 alpha=(MagickRealType) (QuantumScale*
4893 GetAlphaPixelComponent(p+u+j));
4894 pixel.red+=(*k)*alpha*(p+u+j)->red;
4895 pixel.green+=(*k)*alpha*(p+u+j)->green;
4896 pixel.blue+=(*k)*alpha*(p+u+j)->blue;
4897 pixel.opacity+=(*k)*(p+u+j)->opacity;
4902 j+=(ssize_t) (image->columns+width);
4906 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4907 if ((channel & RedChannel) != 0)
4908 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
4909 if ((channel & GreenChannel) != 0)
4910 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
4911 if ((channel & BlueChannel) != 0)
4912 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
4914 if ((channel & OpacityChannel) != 0)
4918 for (v=0; v < (ssize_t) width; v++)
4920 for (u=0; u < (ssize_t) width; u++)
4922 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4924 pixel.opacity+=(*k)*(p+u+j)->opacity;
4929 j+=(ssize_t) (image->columns+width);
4933 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4935 SetOpacityPixelComponent(q,
4936 ClampOpacityPixelComponent(&pixel));
4939 if (((channel & IndexChannel) != 0) &&
4940 (image->colorspace == CMYKColorspace))
4944 for (v=0; v < (ssize_t) width; v++)
4946 for (u=0; u < (ssize_t) width; u++)
4948 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4950 alpha=(MagickRealType) (QuantumScale*
4951 GetAlphaPixelComponent(p+u+j));
4952 pixel.index+=(*k)*alpha*indexes[x+u+j];
4957 j+=(ssize_t) (image->columns+width);
4961 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4963 blur_indexes[x]=ClampToQuantum(gamma*
4964 GetIndexPixelComponent(&pixel));
4971 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
4972 if (sync == MagickFalse)
4974 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4979 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4980 #pragma omp critical (MagickCore_SelectiveBlurImageChannel)
4982 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
4984 if (proceed == MagickFalse)
4988 blur_image->type=image->type;
4989 blur_view=DestroyCacheView(blur_view);
4990 image_view=DestroyCacheView(image_view);
4991 kernel=(double *) RelinquishMagickMemory(kernel);
4992 if (status == MagickFalse)
4993 blur_image=DestroyImage(blur_image);
4998 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5002 % S h a d e I m a g e %
5006 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5008 % ShadeImage() shines a distant light on an image to create a
5009 % three-dimensional effect. You control the positioning of the light with
5010 % azimuth and elevation; azimuth is measured in degrees off the x axis
5011 % and elevation is measured in pixels above the Z axis.
5013 % The format of the ShadeImage method is:
5015 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
5016 % const double azimuth,const double elevation,ExceptionInfo *exception)
5018 % A description of each parameter follows:
5020 % o image: the image.
5022 % o gray: A value other than zero shades the intensity of each pixel.
5024 % o azimuth, elevation: Define the light source direction.
5026 % o exception: return any errors or warnings in this structure.
5029 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
5030 const double azimuth,const double elevation,ExceptionInfo *exception)
5032 #define ShadeImageTag "Shade/Image"
5054 Initialize shaded image attributes.
5056 assert(image != (const Image *) NULL);
5057 assert(image->signature == MagickSignature);
5058 if (image->debug != MagickFalse)
5059 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5060 assert(exception != (ExceptionInfo *) NULL);
5061 assert(exception->signature == MagickSignature);
5062 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5063 if (shade_image == (Image *) NULL)
5064 return((Image *) NULL);
5065 if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
5067 InheritException(exception,&shade_image->exception);
5068 shade_image=DestroyImage(shade_image);
5069 return((Image *) NULL);
5072 Compute the light vector.
5074 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
5075 cos(DegreesToRadians(elevation));
5076 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
5077 cos(DegreesToRadians(elevation));
5078 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
5084 image_view=AcquireCacheView(image);
5085 shade_view=AcquireCacheView(shade_image);
5086 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5087 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5089 for (y=0; y < (ssize_t) image->rows; y++)
5099 register const PixelPacket
5105 register PixelPacket
5111 if (status == MagickFalse)
5113 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
5114 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
5116 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5122 Shade this row of pixels.
5124 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
5126 s1=s0+image->columns+2;
5127 s2=s1+image->columns+2;
5128 for (x=0; x < (ssize_t) image->columns; x++)
5131 Determine the surface normal and compute shading.
5133 normal.x=(double) (PixelIntensity(s0-1)+PixelIntensity(s1-1)+
5134 PixelIntensity(s2-1)-PixelIntensity(s0+1)-PixelIntensity(s1+1)-
5135 PixelIntensity(s2+1));
5136 normal.y=(double) (PixelIntensity(s2-1)+PixelIntensity(s2)+
5137 PixelIntensity(s2+1)-PixelIntensity(s0-1)-PixelIntensity(s0)-
5138 PixelIntensity(s0+1));
5139 if ((normal.x == 0.0) && (normal.y == 0.0))
5144 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
5145 if (distance > MagickEpsilon)
5148 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
5149 if (normal_distance > (MagickEpsilon*MagickEpsilon))
5150 shade=distance/sqrt((double) normal_distance);
5153 if (gray != MagickFalse)
5155 q->red=(Quantum) shade;
5156 q->green=(Quantum) shade;
5157 q->blue=(Quantum) shade;
5161 q->red=ClampToQuantum(QuantumScale*shade*s1->red);
5162 q->green=ClampToQuantum(QuantumScale*shade*s1->green);
5163 q->blue=ClampToQuantum(QuantumScale*shade*s1->blue);
5165 q->opacity=s1->opacity;
5171 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
5173 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5178 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5179 #pragma omp critical (MagickCore_ShadeImage)
5181 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
5182 if (proceed == MagickFalse)
5186 shade_view=DestroyCacheView(shade_view);
5187 image_view=DestroyCacheView(image_view);
5188 if (status == MagickFalse)
5189 shade_image=DestroyImage(shade_image);
5190 return(shade_image);
5194 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5198 % S h a r p e n I m a g e %
5202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5204 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
5205 % operator of the given radius and standard deviation (sigma). For
5206 % reasonable results, radius should be larger than sigma. Use a radius of 0
5207 % and SharpenImage() selects a suitable radius for you.
5209 % Using a separable kernel would be faster, but the negative weights cancel
5210 % out on the corners of the kernel producing often undesirable ringing in the
5211 % filtered result; this can be avoided by using a 2D gaussian shaped image
5212 % sharpening kernel instead.
5214 % The format of the SharpenImage method is:
5216 % Image *SharpenImage(const Image *image,const double radius,
5217 % const double sigma,ExceptionInfo *exception)
5218 % Image *SharpenImageChannel(const Image *image,const ChannelType channel,
5219 % const double radius,const double sigma,ExceptionInfo *exception)
5221 % A description of each parameter follows:
5223 % o image: the image.
5225 % o channel: the channel type.
5227 % o radius: the radius of the Gaussian, in pixels, not counting the center
5230 % o sigma: the standard deviation of the Laplacian, in pixels.
5232 % o exception: return any errors or warnings in this structure.
5236 MagickExport Image *SharpenImage(const Image *image,const double radius,
5237 const double sigma,ExceptionInfo *exception)
5242 sharp_image=SharpenImageChannel(image,DefaultChannels,radius,sigma,exception);
5243 return(sharp_image);
5246 MagickExport Image *SharpenImageChannel(const Image *image,
5247 const ChannelType channel,const double radius,const double sigma,
5248 ExceptionInfo *exception)
5268 assert(image != (const Image *) NULL);
5269 assert(image->signature == MagickSignature);
5270 if (image->debug != MagickFalse)
5271 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5272 assert(exception != (ExceptionInfo *) NULL);
5273 assert(exception->signature == MagickSignature);
5274 width=GetOptimalKernelWidth2D(radius,sigma);
5275 kernel=(double *) AcquireQuantumMemory((size_t) width*width,sizeof(*kernel));
5276 if (kernel == (double *) NULL)
5277 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5279 j=(ssize_t) width/2;
5281 for (v=(-j); v <= j; v++)
5283 for (u=(-j); u <= j; u++)
5285 kernel[i]=(double) (-exp(-((double) u*u+v*v)/(2.0*MagickSigma*
5286 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
5287 normalize+=kernel[i];
5291 kernel[i/2]=(double) ((-2.0)*normalize);
5292 sharp_image=ConvolveImageChannel(image,channel,width,kernel,exception);
5293 kernel=(double *) RelinquishMagickMemory(kernel);
5294 return(sharp_image);
5298 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5302 % S p r e a d I m a g e %
5306 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5308 % SpreadImage() is a special effects method that randomly displaces each
5309 % pixel in a block defined by the radius parameter.
5311 % The format of the SpreadImage method is:
5313 % Image *SpreadImage(const Image *image,const double radius,
5314 % ExceptionInfo *exception)
5316 % A description of each parameter follows:
5318 % o image: the image.
5320 % o radius: Choose a random pixel in a neighborhood of this extent.
5322 % o exception: return any errors or warnings in this structure.
5325 MagickExport Image *SpreadImage(const Image *image,const double radius,
5326 ExceptionInfo *exception)
5328 #define SpreadImageTag "Spread/Image"
5346 **restrict random_info;
5349 **restrict resample_filter;
5358 Initialize spread image attributes.
5360 assert(image != (Image *) NULL);
5361 assert(image->signature == MagickSignature);
5362 if (image->debug != MagickFalse)
5363 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5364 assert(exception != (ExceptionInfo *) NULL);
5365 assert(exception->signature == MagickSignature);
5366 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
5368 if (spread_image == (Image *) NULL)
5369 return((Image *) NULL);
5370 if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
5372 InheritException(exception,&spread_image->exception);
5373 spread_image=DestroyImage(spread_image);
5374 return((Image *) NULL);
5381 GetMagickPixelPacket(spread_image,&bias);
5382 width=GetOptimalKernelWidth1D(radius,0.5);
5383 resample_filter=AcquireResampleFilterThreadSet(image,
5384 UndefinedVirtualPixelMethod,MagickTrue,exception);
5385 random_info=AcquireRandomInfoThreadSet();
5386 image_view=AcquireCacheView(spread_image);
5387 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5388 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
5390 for (y=0; y < (ssize_t) spread_image->rows; y++)
5393 id = GetOpenMPThreadId();
5398 register IndexPacket
5401 register PixelPacket
5407 if (status == MagickFalse)
5409 q=QueueCacheViewAuthenticPixels(image_view,0,y,spread_image->columns,1,
5411 if (q == (PixelPacket *) NULL)
5416 indexes=GetCacheViewAuthenticIndexQueue(image_view);
5418 for (x=0; x < (ssize_t) spread_image->columns; x++)
5420 (void) ResamplePixelColor(resample_filter[id],(double) x+width*
5421 (GetPseudoRandomValue(random_info[id])-0.5),(double) y+width*
5422 (GetPseudoRandomValue(random_info[id])-0.5),&pixel);
5423 SetPixelPacket(spread_image,&pixel,q,indexes+x);
5426 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5428 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5433 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5434 #pragma omp critical (MagickCore_SpreadImage)
5436 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
5437 if (proceed == MagickFalse)
5441 image_view=DestroyCacheView(image_view);
5442 random_info=DestroyRandomInfoThreadSet(random_info);
5443 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5444 return(spread_image);
5448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5452 % U n s h a r p M a s k I m a g e %
5456 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5458 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
5459 % image with a Gaussian operator of the given radius and standard deviation
5460 % (sigma). For reasonable results, radius should be larger than sigma. Use a
5461 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
5463 % The format of the UnsharpMaskImage method is:
5465 % Image *UnsharpMaskImage(const Image *image,const double radius,
5466 % const double sigma,const double amount,const double threshold,
5467 % ExceptionInfo *exception)
5468 % Image *UnsharpMaskImageChannel(const Image *image,
5469 % const ChannelType channel,const double radius,const double sigma,
5470 % const double amount,const double threshold,ExceptionInfo *exception)
5472 % A description of each parameter follows:
5474 % o image: the image.
5476 % o channel: the channel type.
5478 % o radius: the radius of the Gaussian, in pixels, not counting the center
5481 % o sigma: the standard deviation of the Gaussian, in pixels.
5483 % o amount: the percentage of the difference between the original and the
5484 % blur image that is added back into the original.
5486 % o threshold: the threshold in pixels needed to apply the diffence amount.
5488 % o exception: return any errors or warnings in this structure.
5492 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
5493 const double sigma,const double amount,const double threshold,
5494 ExceptionInfo *exception)
5499 sharp_image=UnsharpMaskImageChannel(image,DefaultChannels,radius,sigma,amount,
5500 threshold,exception);
5501 return(sharp_image);
5504 MagickExport Image *UnsharpMaskImageChannel(const Image *image,
5505 const ChannelType channel,const double radius,const double sigma,
5506 const double amount,const double threshold,ExceptionInfo *exception)
5508 #define SharpenImageTag "Sharpen/Image"
5532 assert(image != (const Image *) NULL);
5533 assert(image->signature == MagickSignature);
5534 if (image->debug != MagickFalse)
5535 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5536 assert(exception != (ExceptionInfo *) NULL);
5537 unsharp_image=BlurImageChannel(image,channel,radius,sigma,exception);
5538 if (unsharp_image == (Image *) NULL)
5539 return((Image *) NULL);
5540 quantum_threshold=(MagickRealType) QuantumRange*threshold;
5546 GetMagickPixelPacket(image,&bias);
5547 image_view=AcquireCacheView(image);
5548 unsharp_view=AcquireCacheView(unsharp_image);
5549 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5550 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5552 for (y=0; y < (ssize_t) image->rows; y++)
5557 register const IndexPacket
5560 register const PixelPacket
5563 register IndexPacket
5564 *restrict unsharp_indexes;
5566 register PixelPacket
5572 if (status == MagickFalse)
5574 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5575 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
5577 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5582 indexes=GetCacheViewVirtualIndexQueue(image_view);
5583 unsharp_indexes=GetCacheViewAuthenticIndexQueue(unsharp_view);
5585 for (x=0; x < (ssize_t) image->columns; x++)
5587 if ((channel & RedChannel) != 0)
5589 pixel.red=p->red-(MagickRealType) q->red;
5590 if (fabs(2.0*pixel.red) < quantum_threshold)
5591 pixel.red=(MagickRealType) GetRedPixelComponent(p);
5593 pixel.red=(MagickRealType) p->red+(pixel.red*amount);
5594 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
5596 if ((channel & GreenChannel) != 0)
5598 pixel.green=p->green-(MagickRealType) q->green;
5599 if (fabs(2.0*pixel.green) < quantum_threshold)
5600 pixel.green=(MagickRealType) GetGreenPixelComponent(p);
5602 pixel.green=(MagickRealType) p->green+(pixel.green*amount);
5603 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
5605 if ((channel & BlueChannel) != 0)
5607 pixel.blue=p->blue-(MagickRealType) q->blue;
5608 if (fabs(2.0*pixel.blue) < quantum_threshold)
5609 pixel.blue=(MagickRealType) GetBluePixelComponent(p);
5611 pixel.blue=(MagickRealType) p->blue+(pixel.blue*amount);
5612 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
5614 if ((channel & OpacityChannel) != 0)
5616 pixel.opacity=p->opacity-(MagickRealType) q->opacity;
5617 if (fabs(2.0*pixel.opacity) < quantum_threshold)
5618 pixel.opacity=(MagickRealType) GetOpacityPixelComponent(p);
5620 pixel.opacity=p->opacity+(pixel.opacity*amount);
5621 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
5623 if (((channel & IndexChannel) != 0) &&
5624 (image->colorspace == CMYKColorspace))
5626 pixel.index=indexes[x]-(MagickRealType) unsharp_indexes[x];
5627 if (fabs(2.0*pixel.index) < quantum_threshold)
5628 pixel.index=(MagickRealType) indexes[x];
5630 pixel.index=(MagickRealType) indexes[x]+(pixel.index*amount);
5631 unsharp_indexes[x]=ClampToQuantum(pixel.index);
5636 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
5638 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5643 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5644 #pragma omp critical (MagickCore_UnsharpMaskImageChannel)
5646 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
5647 if (proceed == MagickFalse)
5651 unsharp_image->type=image->type;
5652 unsharp_view=DestroyCacheView(unsharp_view);
5653 image_view=DestroyCacheView(image_view);
5654 if (status == MagickFalse)
5655 unsharp_image=DestroyImage(unsharp_image);
5656 return(unsharp_image);