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-2009 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "magick/studio.h"
44 #include "magick/property.h"
45 #include "magick/blob.h"
46 #include "magick/cache-view.h"
47 #include "magick/color.h"
48 #include "magick/color-private.h"
49 #include "magick/colorspace.h"
50 #include "magick/constitute.h"
51 #include "magick/decorate.h"
52 #include "magick/draw.h"
53 #include "magick/enhance.h"
54 #include "magick/exception.h"
55 #include "magick/exception-private.h"
56 #include "magick/effect.h"
57 #include "magick/fx.h"
58 #include "magick/gem.h"
59 #include "magick/geometry.h"
60 #include "magick/image-private.h"
61 #include "magick/list.h"
62 #include "magick/log.h"
63 #include "magick/memory_.h"
64 #include "magick/monitor.h"
65 #include "magick/monitor-private.h"
66 #include "magick/montage.h"
67 #include "magick/paint.h"
68 #include "magick/pixel-private.h"
69 #include "magick/property.h"
70 #include "magick/quantize.h"
71 #include "magick/quantum.h"
72 #include "magick/random_.h"
73 #include "magick/random-private.h"
74 #include "magick/resample.h"
75 #include "magick/resample-private.h"
76 #include "magick/resize.h"
77 #include "magick/resource_.h"
78 #include "magick/segment.h"
79 #include "magick/shear.h"
80 #include "magick/signature-private.h"
81 #include "magick/string_.h"
82 #include "magick/thread-private.h"
83 #include "magick/transform.h"
84 #include "magick/threshold.h"
87 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91 % A d a p t i v e B l u r I m a g e %
95 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
97 % AdaptiveBlurImage() adaptively blurs the image by blurring less
98 % intensely near image edges and more intensely far from edges. We blur the
99 % image with a Gaussian operator of the given radius and standard deviation
100 % (sigma). For reasonable results, radius should be larger than sigma. Use a
101 % radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
103 % The format of the AdaptiveBlurImage method is:
105 % Image *AdaptiveBlurImage(const Image *image,const double radius,
106 % const double sigma,ExceptionInfo *exception)
107 % Image *AdaptiveBlurImageChannel(const Image *image,
108 % const ChannelType channel,double radius,const double sigma,
109 % ExceptionInfo *exception)
111 % A description of each parameter follows:
113 % o image: the image.
115 % o channel: the channel type.
117 % o radius: the radius of the Gaussian, in pixels, not counting the center
120 % o sigma: the standard deviation of the Laplacian, in pixels.
122 % o exception: return any errors or warnings in this structure.
126 MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
127 const double sigma,ExceptionInfo *exception)
132 blur_image=AdaptiveBlurImageChannel(image,DefaultChannels,radius,sigma,
137 MagickExport Image *AdaptiveBlurImageChannel(const Image *image,
138 const ChannelType channel,const double radius,const double sigma,
139 ExceptionInfo *exception)
141 #define AdaptiveBlurImageTag "Convolve/Image"
142 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
180 assert(image != (const Image *) NULL);
181 assert(image->signature == MagickSignature);
182 if (image->debug != MagickFalse)
183 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
184 assert(exception != (ExceptionInfo *) NULL);
185 assert(exception->signature == MagickSignature);
186 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
187 if (blur_image == (Image *) NULL)
188 return((Image *) NULL);
189 if (fabs(sigma) <= MagickEpsilon)
191 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
193 InheritException(exception,&blur_image->exception);
194 blur_image=DestroyImage(blur_image);
195 return((Image *) NULL);
198 Edge detect the image brighness channel, level, blur, and level again.
200 edge_image=EdgeImage(image,radius,exception);
201 if (edge_image == (Image *) NULL)
203 blur_image=DestroyImage(blur_image);
204 return((Image *) NULL);
206 (void) LevelImage(edge_image,"20%,95%");
207 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
208 if (gaussian_image != (Image *) NULL)
210 edge_image=DestroyImage(edge_image);
211 edge_image=gaussian_image;
213 (void) LevelImage(edge_image,"10%,95%");
215 Create a set of kernels from maximum (radius,sigma) to minimum.
217 width=GetOptimalKernelWidth2D(radius,sigma);
218 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
219 if (kernel == (double **) NULL)
221 edge_image=DestroyImage(edge_image);
222 blur_image=DestroyImage(blur_image);
223 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
225 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
226 for (i=0; i < (long) width; i+=2)
228 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
230 if (kernel[i] == (double *) NULL)
233 for (v=(-((long) (width-i)/2)); v <= (long) ((width-i)/2); v++)
235 for (u=(-((long) (width-i)/2)); u <= (long) ((width-i)/2); u++)
237 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
238 kernel[i][j]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
243 for (j=0; j < (long) ((width-i)*(width-i)); j++)
244 normalize+=kernel[i][j];
245 if (fabs(normalize) <= MagickEpsilon)
247 normalize=1.0/normalize;
248 for (j=0; j < (long) ((width-i)*(width-i)); j++)
249 kernel[i][j]=(double) (normalize*kernel[i][j]);
251 if (i < (long) width)
253 for (i-=2; i >= 0; i-=2)
254 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
255 kernel=(double **) RelinquishMagickMemory(kernel);
256 edge_image=DestroyImage(edge_image);
257 blur_image=DestroyImage(blur_image);
258 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
261 Adaptively blur image.
265 GetMagickPixelPacket(image,&bias);
266 SetMagickPixelPacketBias(image,&bias);
267 image_view=AcquireCacheView(image);
268 edge_view=AcquireCacheView(edge_image);
269 blur_view=AcquireCacheView(blur_image);
270 #if defined(MAGICKCORE_OPENMP_SUPPORT)
271 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
273 for (y=0; y < (long) blur_image->rows; y++)
275 register const IndexPacket
278 register const PixelPacket
283 *restrict blur_indexes;
291 if (status == MagickFalse)
293 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
294 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
296 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
301 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
302 for (x=0; x < (long) blur_image->columns; x++)
311 register const double
320 i=(long) (width*QuantumScale*PixelIntensity(r)+0.5);
324 if (i > (long) width)
328 p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
329 ((width-i)/2L),width-i,width-i,exception);
330 if (p == (const PixelPacket *) NULL)
332 indexes=GetCacheViewVirtualIndexQueue(image_view);
335 for (v=0; v < (long) (width-i); v++)
337 for (u=0; u < (long) (width-i); u++)
340 if (((channel & OpacityChannel) != 0) &&
341 (image->matte != MagickFalse))
342 alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
343 if ((channel & RedChannel) != 0)
344 pixel.red+=(*k)*alpha*p->red;
345 if ((channel & GreenChannel) != 0)
346 pixel.green+=(*k)*alpha*p->green;
347 if ((channel & BlueChannel) != 0)
348 pixel.blue+=(*k)*alpha*p->blue;
349 if ((channel & OpacityChannel) != 0)
350 pixel.opacity+=(*k)*p->opacity;
351 if (((channel & IndexChannel) != 0) &&
352 (image->colorspace == CMYKColorspace))
353 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
359 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
360 if ((channel & RedChannel) != 0)
361 q->red=RoundToQuantum(gamma*pixel.red);
362 if ((channel & GreenChannel) != 0)
363 q->green=RoundToQuantum(gamma*pixel.green);
364 if ((channel & BlueChannel) != 0)
365 q->blue=RoundToQuantum(gamma*pixel.blue);
366 if ((channel & OpacityChannel) != 0)
367 q->opacity=RoundToQuantum(pixel.opacity);
368 if (((channel & IndexChannel) != 0) &&
369 (image->colorspace == CMYKColorspace))
370 blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
374 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
376 if (image->progress_monitor != (MagickProgressMonitor) NULL)
381 #if defined(MAGICKCORE_OPENMP_SUPPORT)
382 #pragma omp critical (MagickCore_AdaptiveBlurImageChannel)
384 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
386 if (proceed == MagickFalse)
390 blur_image->type=image->type;
391 blur_view=DestroyCacheView(blur_view);
392 edge_view=DestroyCacheView(edge_view);
393 image_view=DestroyCacheView(image_view);
394 edge_image=DestroyImage(edge_image);
395 for (i=0; i < (long) width; i+=2)
396 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
397 kernel=(double **) RelinquishMagickMemory(kernel);
398 if (status == MagickFalse)
399 blur_image=DestroyImage(blur_image);
404 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
408 % A d a p t i v e S h a r p e n I m a g e %
412 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
414 % AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
415 % intensely near image edges and less intensely far from edges. We sharpen the
416 % image with a Gaussian operator of the given radius and standard deviation
417 % (sigma). For reasonable results, radius should be larger than sigma. Use a
418 % radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
420 % The format of the AdaptiveSharpenImage method is:
422 % Image *AdaptiveSharpenImage(const Image *image,const double radius,
423 % const double sigma,ExceptionInfo *exception)
424 % Image *AdaptiveSharpenImageChannel(const Image *image,
425 % const ChannelType channel,double radius,const double sigma,
426 % ExceptionInfo *exception)
428 % A description of each parameter follows:
430 % o image: the image.
432 % o channel: the channel type.
434 % o radius: the radius of the Gaussian, in pixels, not counting the center
437 % o sigma: the standard deviation of the Laplacian, in pixels.
439 % o exception: return any errors or warnings in this structure.
443 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
444 const double sigma,ExceptionInfo *exception)
449 sharp_image=AdaptiveSharpenImageChannel(image,DefaultChannels,radius,sigma,
454 MagickExport Image *AdaptiveSharpenImageChannel(const Image *image,
455 const ChannelType channel,const double radius,const double sigma,
456 ExceptionInfo *exception)
458 #define AdaptiveSharpenImageTag "Convolve/Image"
459 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
497 assert(image != (const Image *) NULL);
498 assert(image->signature == MagickSignature);
499 if (image->debug != MagickFalse)
500 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
501 assert(exception != (ExceptionInfo *) NULL);
502 assert(exception->signature == MagickSignature);
503 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
504 if (sharp_image == (Image *) NULL)
505 return((Image *) NULL);
506 if (fabs(sigma) <= MagickEpsilon)
508 if (SetImageStorageClass(sharp_image,DirectClass) == MagickFalse)
510 InheritException(exception,&sharp_image->exception);
511 sharp_image=DestroyImage(sharp_image);
512 return((Image *) NULL);
515 Edge detect the image brighness channel, level, sharp, and level again.
517 edge_image=EdgeImage(image,radius,exception);
518 if (edge_image == (Image *) NULL)
520 sharp_image=DestroyImage(sharp_image);
521 return((Image *) NULL);
523 (void) LevelImage(edge_image,"20%,95%");
524 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
525 if (gaussian_image != (Image *) NULL)
527 edge_image=DestroyImage(edge_image);
528 edge_image=gaussian_image;
530 (void) LevelImage(edge_image,"10%,95%");
532 Create a set of kernels from maximum (radius,sigma) to minimum.
534 width=GetOptimalKernelWidth2D(radius,sigma);
535 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
536 if (kernel == (double **) NULL)
538 edge_image=DestroyImage(edge_image);
539 sharp_image=DestroyImage(sharp_image);
540 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
542 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
543 for (i=0; i < (long) width; i+=2)
545 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
547 if (kernel[i] == (double *) NULL)
550 for (v=(-((long) (width-i)/2)); v <= (long) ((width-i)/2); v++)
552 for (u=(-((long) (width-i)/2)); u <= (long) ((width-i)/2); u++)
554 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
555 kernel[i][j]=(double) (-alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
560 for (j=0; j < (long) ((width-i)*(width-i)); j++)
561 normalize+=kernel[i][j];
562 if (fabs(normalize) <= MagickEpsilon)
564 normalize=1.0/normalize;
565 for (j=0; j < (long) ((width-i)*(width-i)); j++)
566 kernel[i][j]=(double) (normalize*kernel[i][j]);
568 if (i < (long) width)
570 for (i-=2; i >= 0; i-=2)
571 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
572 kernel=(double **) RelinquishMagickMemory(kernel);
573 edge_image=DestroyImage(edge_image);
574 sharp_image=DestroyImage(sharp_image);
575 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
578 Adaptively sharpen image.
582 GetMagickPixelPacket(image,&bias);
583 SetMagickPixelPacketBias(image,&bias);
584 image_view=AcquireCacheView(image);
585 edge_view=AcquireCacheView(edge_image);
586 sharp_view=AcquireCacheView(sharp_image);
587 #if defined(MAGICKCORE_OPENMP_SUPPORT)
588 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
590 for (y=0; y < (long) sharp_image->rows; y++)
592 register const IndexPacket
595 register const PixelPacket
600 *restrict sharp_indexes;
608 if (status == MagickFalse)
610 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
611 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
613 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
618 sharp_indexes=GetCacheViewAuthenticIndexQueue(sharp_view);
619 for (x=0; x < (long) sharp_image->columns; x++)
628 register const double
637 i=(long) (width*(QuantumRange-QuantumScale*PixelIntensity(r))+0.5);
641 if (i > (long) width)
645 p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
646 ((width-i)/2L),width-i,width-i,exception);
647 if (p == (const PixelPacket *) NULL)
649 indexes=GetCacheViewVirtualIndexQueue(image_view);
652 for (v=0; v < (long) (width-i); v++)
654 for (u=0; u < (long) (width-i); u++)
657 if (((channel & OpacityChannel) != 0) &&
658 (image->matte != MagickFalse))
659 alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
660 if ((channel & RedChannel) != 0)
661 pixel.red+=(*k)*alpha*p->red;
662 if ((channel & GreenChannel) != 0)
663 pixel.green+=(*k)*alpha*p->green;
664 if ((channel & BlueChannel) != 0)
665 pixel.blue+=(*k)*alpha*p->blue;
666 if ((channel & OpacityChannel) != 0)
667 pixel.opacity+=(*k)*p->opacity;
668 if (((channel & IndexChannel) != 0) &&
669 (image->colorspace == CMYKColorspace))
670 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
676 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
677 if ((channel & RedChannel) != 0)
678 q->red=RoundToQuantum(gamma*pixel.red);
679 if ((channel & GreenChannel) != 0)
680 q->green=RoundToQuantum(gamma*pixel.green);
681 if ((channel & BlueChannel) != 0)
682 q->blue=RoundToQuantum(gamma*pixel.blue);
683 if ((channel & OpacityChannel) != 0)
684 q->opacity=RoundToQuantum(pixel.opacity);
685 if (((channel & IndexChannel) != 0) &&
686 (image->colorspace == CMYKColorspace))
687 sharp_indexes[x]=RoundToQuantum(gamma*pixel.index);
691 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
693 if (image->progress_monitor != (MagickProgressMonitor) NULL)
698 #if defined(MAGICKCORE_OPENMP_SUPPORT)
699 #pragma omp critical (MagickCore_AdaptiveSharpenImageChannel)
701 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
703 if (proceed == MagickFalse)
707 sharp_image->type=image->type;
708 sharp_view=DestroyCacheView(sharp_view);
709 edge_view=DestroyCacheView(edge_view);
710 image_view=DestroyCacheView(image_view);
711 edge_image=DestroyImage(edge_image);
712 for (i=0; i < (long) width; i+=2)
713 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
714 kernel=(double **) RelinquishMagickMemory(kernel);
715 if (status == MagickFalse)
716 sharp_image=DestroyImage(sharp_image);
721 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
725 % B l u r I m a g e %
729 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
731 % BlurImage() blurs an image. We convolve the image with a Gaussian operator
732 % of the given radius and standard deviation (sigma). For reasonable results,
733 % the radius should be larger than sigma. Use a radius of 0 and BlurImage()
734 % selects a suitable radius for you.
736 % BlurImage() differs from GaussianBlurImage() in that it uses a separable
737 % kernel which is faster but mathematically equivalent to the non-separable
740 % The format of the BlurImage method is:
742 % Image *BlurImage(const Image *image,const double radius,
743 % const double sigma,ExceptionInfo *exception)
744 % Image *BlurImageChannel(const Image *image,const ChannelType channel,
745 % const double radius,const double sigma,ExceptionInfo *exception)
747 % A description of each parameter follows:
749 % o image: the image.
751 % o channel: the channel type.
753 % o radius: the radius of the Gaussian, in pixels, not counting the center
756 % o sigma: the standard deviation of the Gaussian, in pixels.
758 % o exception: return any errors or warnings in this structure.
762 MagickExport Image *BlurImage(const Image *image,const double radius,
763 const double sigma,ExceptionInfo *exception)
768 blur_image=BlurImageChannel(image,DefaultChannels,radius,sigma,exception);
772 static double *GetBlurKernel(unsigned long width,const MagickRealType sigma)
790 Generate a 1-D convolution kernel.
792 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
793 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
794 if (kernel == (double *) NULL)
796 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
797 bias=KernelRank*(long) width/2;
798 for (i=(-bias); i <= bias; i++)
800 alpha=exp((-((double) (i*i))/(double) (2.0*KernelRank*KernelRank*
801 MagickSigma*MagickSigma)));
802 kernel[(i+bias)/KernelRank]+=(double) (alpha/(MagickSQ2PI*sigma));
805 for (i=0; i < (long) width; i++)
806 normalize+=kernel[i];
807 for (i=0; i < (long) width; i++)
808 kernel[i]/=normalize;
812 MagickExport Image *BlurImageChannel(const Image *image,
813 const ChannelType channel,const double radius,const double sigma,
814 ExceptionInfo *exception)
816 #define BlurImageTag "Blur/Image"
846 Initialize blur image attributes.
848 assert(image != (Image *) NULL);
849 assert(image->signature == MagickSignature);
850 if (image->debug != MagickFalse)
851 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
852 assert(exception != (ExceptionInfo *) NULL);
853 assert(exception->signature == MagickSignature);
854 blur_image=CloneImage(image,0,0,MagickTrue,exception);
855 if (blur_image == (Image *) NULL)
856 return((Image *) NULL);
857 if (fabs(sigma) <= MagickEpsilon)
859 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
861 InheritException(exception,&blur_image->exception);
862 blur_image=DestroyImage(blur_image);
863 return((Image *) NULL);
865 width=GetOptimalKernelWidth1D(radius,sigma);
866 kernel=GetBlurKernel(width,sigma);
867 if (kernel == (double *) NULL)
869 blur_image=DestroyImage(blur_image);
870 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
872 if (image->debug != MagickFalse)
875 format[MaxTextExtent],
878 register const double
881 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
882 " BlurImage with %ld kernel:",width);
883 message=AcquireString("");
885 for (i=0; i < (long) width; i++)
888 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",i);
889 (void) ConcatenateString(&message,format);
890 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
891 (void) ConcatenateString(&message,format);
892 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
894 message=DestroyString(message);
901 GetMagickPixelPacket(image,&bias);
902 SetMagickPixelPacketBias(image,&bias);
903 image_view=AcquireCacheView(image);
904 blur_view=AcquireCacheView(blur_image);
905 #if defined(MAGICKCORE_OPENMP_SUPPORT)
906 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
908 for (y=0; y < (long) blur_image->rows; y++)
910 register const IndexPacket
913 register const PixelPacket
917 *restrict blur_indexes;
925 if (status == MagickFalse)
927 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y,image->columns+
929 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
931 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
936 indexes=GetCacheViewVirtualIndexQueue(image_view);
937 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
938 for (x=0; x < (long) blur_image->columns; x++)
943 register const double
946 register const PixelPacket
947 *restrict kernel_pixels;
955 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
957 for (i=0; i < (long) width; i++)
959 pixel.red+=(*k)*kernel_pixels->red;
960 pixel.green+=(*k)*kernel_pixels->green;
961 pixel.blue+=(*k)*kernel_pixels->blue;
965 if ((channel & RedChannel) != 0)
966 q->red=RoundToQuantum(pixel.red);
967 if ((channel & GreenChannel) != 0)
968 q->green=RoundToQuantum(pixel.green);
969 if ((channel & BlueChannel) != 0)
970 q->blue=RoundToQuantum(pixel.blue);
971 if ((channel & OpacityChannel) != 0)
975 for (i=0; i < (long) width; i++)
977 pixel.opacity+=(*k)*kernel_pixels->opacity;
981 q->opacity=RoundToQuantum(pixel.opacity);
983 if (((channel & IndexChannel) != 0) &&
984 (image->colorspace == CMYKColorspace))
986 register const IndexPacket
987 *restrict kernel_indexes;
990 kernel_indexes=indexes;
991 for (i=0; i < (long) width; i++)
993 pixel.index+=(*k)*(*kernel_indexes);
997 blur_indexes[x]=RoundToQuantum(pixel.index);
1007 for (i=0; i < (long) width; i++)
1009 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1010 kernel_pixels->opacity));
1011 pixel.red+=(*k)*alpha*kernel_pixels->red;
1012 pixel.green+=(*k)*alpha*kernel_pixels->green;
1013 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
1018 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1019 if ((channel & RedChannel) != 0)
1020 q->red=RoundToQuantum(gamma*pixel.red);
1021 if ((channel & GreenChannel) != 0)
1022 q->green=RoundToQuantum(gamma*pixel.green);
1023 if ((channel & BlueChannel) != 0)
1024 q->blue=RoundToQuantum(gamma*pixel.blue);
1025 if ((channel & OpacityChannel) != 0)
1029 for (i=0; i < (long) width; i++)
1031 pixel.opacity+=(*k)*kernel_pixels->opacity;
1035 q->opacity=RoundToQuantum(pixel.opacity);
1037 if (((channel & IndexChannel) != 0) &&
1038 (image->colorspace == CMYKColorspace))
1040 register const IndexPacket
1041 *restrict kernel_indexes;
1045 kernel_indexes=indexes;
1046 for (i=0; i < (long) width; i++)
1048 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1049 kernel_pixels->opacity));
1050 pixel.index+=(*k)*alpha*(*kernel_indexes);
1055 blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
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 < (long) blur_image->columns; x++)
1089 register const IndexPacket
1092 register const PixelPacket
1095 register IndexPacket
1096 *restrict blur_indexes;
1101 register PixelPacket
1104 if (status == MagickFalse)
1106 p=GetCacheViewVirtualPixels(image_view,x,-((long) width/2L),1,image->rows+
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 < (long) 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 < (long) 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 q->red=RoundToQuantum(pixel.red);
1145 if ((channel & GreenChannel) != 0)
1146 q->green=RoundToQuantum(pixel.green);
1147 if ((channel & BlueChannel) != 0)
1148 q->blue=RoundToQuantum(pixel.blue);
1149 if ((channel & OpacityChannel) != 0)
1153 for (i=0; i < (long) width; i++)
1155 pixel.opacity+=(*k)*kernel_pixels->opacity;
1159 q->opacity=RoundToQuantum(pixel.opacity);
1161 if (((channel & IndexChannel) != 0) &&
1162 (image->colorspace == CMYKColorspace))
1164 register const IndexPacket
1165 *restrict kernel_indexes;
1168 kernel_indexes=indexes;
1169 for (i=0; i < (long) width; i++)
1171 pixel.index+=(*k)*(*kernel_indexes);
1175 blur_indexes[y]=RoundToQuantum(pixel.index);
1185 for (i=0; i < (long) width; i++)
1187 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1188 kernel_pixels->opacity));
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=RoundToQuantum(gamma*pixel.red);
1199 if ((channel & GreenChannel) != 0)
1200 q->green=RoundToQuantum(gamma*pixel.green);
1201 if ((channel & BlueChannel) != 0)
1202 q->blue=RoundToQuantum(gamma*pixel.blue);
1203 if ((channel & OpacityChannel) != 0)
1207 for (i=0; i < (long) width; i++)
1209 pixel.opacity+=(*k)*kernel_pixels->opacity;
1213 q->opacity=RoundToQuantum(pixel.opacity);
1215 if (((channel & IndexChannel) != 0) &&
1216 (image->colorspace == CMYKColorspace))
1218 register const IndexPacket
1219 *restrict kernel_indexes;
1223 kernel_indexes=indexes;
1224 for (i=0; i < (long) width; i++)
1226 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1227 kernel_pixels->opacity));
1228 pixel.index+=(*k)*alpha*(*kernel_indexes);
1233 blur_indexes[y]=RoundToQuantum(gamma*pixel.index);
1239 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1241 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1246 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1247 #pragma omp critical (MagickCore_BlurImageChannel)
1249 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1250 blur_image->columns);
1251 if (proceed == MagickFalse)
1255 blur_view=DestroyCacheView(blur_view);
1256 image_view=DestroyCacheView(image_view);
1257 kernel=(double *) RelinquishMagickMemory(kernel);
1258 if (status == MagickFalse)
1259 blur_image=DestroyImage(blur_image);
1260 blur_image->type=image->type;
1265 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1269 % D e s p e c k l e I m a g e %
1273 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1275 % DespeckleImage() reduces the speckle noise in an image while perserving the
1276 % edges of the original image.
1278 % The format of the DespeckleImage method is:
1280 % Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1282 % A description of each parameter follows:
1284 % o image: the image.
1286 % o exception: return any errors or warnings in this structure.
1290 static Quantum **DestroyPixelThreadSet(Quantum **pixels)
1295 assert(pixels != (Quantum **) NULL);
1296 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
1297 if (pixels[i] != (Quantum *) NULL)
1298 pixels[i]=(Quantum *) RelinquishMagickMemory(pixels[i]);
1299 pixels=(Quantum **) RelinquishAlignedMemory(pixels);
1303 static Quantum **AcquirePixelThreadSet(const size_t count)
1314 number_threads=GetOpenMPMaximumThreads();
1315 pixels=(Quantum **) AcquireAlignedMemory(number_threads,sizeof(*pixels));
1316 if (pixels == (Quantum **) NULL)
1317 return((Quantum **) NULL);
1318 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
1319 for (i=0; i < (long) number_threads; i++)
1321 pixels[i]=(Quantum *) AcquireQuantumMemory(count,sizeof(**pixels));
1322 if (pixels[i] == (Quantum *) NULL)
1323 return(DestroyPixelThreadSet(pixels));
1328 static void Hull(const long x_offset,const long y_offset,
1329 const unsigned long columns,const unsigned long rows,Quantum *f,Quantum *g,
1347 assert(f != (Quantum *) NULL);
1348 assert(g != (Quantum *) NULL);
1351 r=p+(y_offset*((long) columns+2)+x_offset);
1352 for (y=0; y < (long) rows; y++)
1358 for (x=(long) columns; x != 0; x--)
1360 v=(MagickRealType) (*p);
1361 if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1362 v+=ScaleCharToQuantum(1);
1369 for (x=(long) columns; x != 0; x--)
1371 v=(MagickRealType) (*p);
1372 if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
1373 v-=(long) ScaleCharToQuantum(1);
1385 r=q+(y_offset*((long) columns+2)+x_offset);
1386 s=q-(y_offset*((long) columns+2)+x_offset);
1387 for (y=0; y < (long) rows; y++)
1394 for (x=(long) columns; x != 0; x--)
1396 v=(MagickRealType) (*q);
1397 if (((MagickRealType) *s >=
1398 (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1399 ((MagickRealType) *r > v))
1400 v+=ScaleCharToQuantum(1);
1408 for (x=(long) columns; x != 0; x--)
1410 v=(MagickRealType) (*q);
1411 if (((MagickRealType) *s <=
1412 (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1413 ((MagickRealType) *r < v))
1414 v-=(MagickRealType) ScaleCharToQuantum(1);
1428 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1430 #define DespeckleImageTag "Despeckle/Image"
1453 X[4] = {0, 1, 1,-1},
1454 Y[4] = {1, 0, 1, 1};
1457 Allocate despeckled image.
1459 assert(image != (const Image *) NULL);
1460 assert(image->signature == MagickSignature);
1461 if (image->debug != MagickFalse)
1462 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1463 assert(exception != (ExceptionInfo *) NULL);
1464 assert(exception->signature == MagickSignature);
1465 despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1467 if (despeckle_image == (Image *) NULL)
1468 return((Image *) NULL);
1469 if (SetImageStorageClass(despeckle_image,DirectClass) == MagickFalse)
1471 InheritException(exception,&despeckle_image->exception);
1472 despeckle_image=DestroyImage(despeckle_image);
1473 return((Image *) NULL);
1476 Allocate image buffers.
1478 length=(size_t) ((image->columns+2)*(image->rows+2));
1479 pixels=AcquirePixelThreadSet(length);
1480 buffers=AcquirePixelThreadSet(length);
1481 if ((pixels == (Quantum **) NULL) || (buffers == (Quantum **) NULL))
1483 if (buffers != (Quantum **) NULL)
1484 buffers=DestroyPixelThreadSet(buffers);
1485 if (pixels != (Quantum **) NULL)
1486 pixels=DestroyPixelThreadSet(pixels);
1487 despeckle_image=DestroyImage(despeckle_image);
1488 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1491 Reduce speckle in the image.
1494 image_view=AcquireCacheView(image);
1495 despeckle_view=AcquireCacheView(despeckle_image);
1496 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1497 #pragma omp parallel for schedule(dynamic,4) shared(status)
1499 for (channel=0; channel <= 3; channel++)
1514 if (status == MagickFalse)
1516 id=GetOpenMPThreadId();
1518 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
1520 j=(long) image->columns+2;
1521 for (y=0; y < (long) image->rows; y++)
1523 register const PixelPacket
1526 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1527 if (p == (const PixelPacket *) NULL)
1530 for (x=0; x < (long) image->columns; x++)
1534 case 0: pixel[j]=p->red; break;
1535 case 1: pixel[j]=p->green; break;
1536 case 2: pixel[j]=p->blue; break;
1537 case 3: pixel[j]=p->opacity; break;
1545 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1546 for (i=0; i < 4; i++)
1548 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,1);
1549 Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,1);
1550 Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,-1);
1551 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,-1);
1553 j=(long) image->columns+2;
1554 for (y=0; y < (long) image->rows; y++)
1559 register PixelPacket
1562 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1564 if (q == (PixelPacket *) NULL)
1567 for (x=0; x < (long) image->columns; x++)
1571 case 0: q->red=pixel[j]; break;
1572 case 1: q->green=pixel[j]; break;
1573 case 2: q->blue=pixel[j]; break;
1574 case 3: q->opacity=pixel[j]; break;
1580 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1581 if (sync == MagickFalse)
1588 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1593 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1594 #pragma omp critical (MagickCore_DespeckleImage)
1596 proceed=SetImageProgress(image,DespeckleImageTag,channel,3);
1597 if (proceed == MagickFalse)
1601 despeckle_view=DestroyCacheView(despeckle_view);
1602 image_view=DestroyCacheView(image_view);
1603 buffers=DestroyPixelThreadSet(buffers);
1604 pixels=DestroyPixelThreadSet(pixels);
1605 despeckle_image->type=image->type;
1606 if (status == MagickFalse)
1607 despeckle_image=DestroyImage(despeckle_image);
1608 return(despeckle_image);
1612 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1616 % E d g e I m a g e %
1620 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1622 % EdgeImage() finds edges in an image. Radius defines the radius of the
1623 % convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1626 % The format of the EdgeImage method is:
1628 % Image *EdgeImage(const Image *image,const double radius,
1629 % ExceptionInfo *exception)
1631 % A description of each parameter follows:
1633 % o image: the image.
1635 % o radius: the radius of the pixel neighborhood.
1637 % o exception: return any errors or warnings in this structure.
1640 MagickExport Image *EdgeImage(const Image *image,const double radius,
1641 ExceptionInfo *exception)
1655 assert(image != (const Image *) NULL);
1656 assert(image->signature == MagickSignature);
1657 if (image->debug != MagickFalse)
1658 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1659 assert(exception != (ExceptionInfo *) NULL);
1660 assert(exception->signature == MagickSignature);
1661 width=GetOptimalKernelWidth1D(radius,0.5);
1662 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
1663 if (kernel == (double *) NULL)
1664 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1665 for (i=0; i < (long) (width*width); i++)
1667 kernel[i/2]=(double) (width*width-1.0);
1668 edge_image=ConvolveImage(image,width,kernel,exception);
1669 kernel=(double *) RelinquishMagickMemory(kernel);
1674 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1678 % E m b o s s I m a g e %
1682 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1684 % EmbossImage() returns a grayscale image with a three-dimensional effect.
1685 % We convolve the image with a Gaussian operator of the given radius and
1686 % standard deviation (sigma). For reasonable results, radius should be
1687 % larger than sigma. Use a radius of 0 and Emboss() selects a suitable
1690 % The format of the EmbossImage method is:
1692 % Image *EmbossImage(const Image *image,const double radius,
1693 % const double sigma,ExceptionInfo *exception)
1695 % A description of each parameter follows:
1697 % o image: the image.
1699 % o radius: the radius of the pixel neighborhood.
1701 % o sigma: the standard deviation of the Gaussian, in pixels.
1703 % o exception: return any errors or warnings in this structure.
1706 MagickExport Image *EmbossImage(const Image *image,const double radius,
1707 const double sigma,ExceptionInfo *exception)
1729 assert(image != (Image *) NULL);
1730 assert(image->signature == MagickSignature);
1731 if (image->debug != MagickFalse)
1732 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1733 assert(exception != (ExceptionInfo *) NULL);
1734 assert(exception->signature == MagickSignature);
1735 width=GetOptimalKernelWidth2D(radius,sigma);
1736 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
1737 if (kernel == (double *) NULL)
1738 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1741 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
1743 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
1745 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
1746 kernel[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*alpha/
1747 (2.0*MagickPI*MagickSigma*MagickSigma));
1754 emboss_image=ConvolveImage(image,width,kernel,exception);
1755 if (emboss_image != (Image *) NULL)
1756 (void) EqualizeImage(emboss_image);
1757 kernel=(double *) RelinquishMagickMemory(kernel);
1758 return(emboss_image);
1762 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1766 % G a u s s i a n B l u r I m a g e %
1770 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1772 % GaussianBlurImage() blurs an image. We convolve the image with a
1773 % Gaussian operator of the given radius and standard deviation (sigma).
1774 % For reasonable results, the radius should be larger than sigma. Use a
1775 % radius of 0 and GaussianBlurImage() selects a suitable radius for you
1777 % The format of the GaussianBlurImage method is:
1779 % Image *GaussianBlurImage(const Image *image,onst double radius,
1780 % const double sigma,ExceptionInfo *exception)
1781 % Image *GaussianBlurImageChannel(const Image *image,
1782 % const ChannelType channel,const double radius,const double sigma,
1783 % ExceptionInfo *exception)
1785 % A description of each parameter follows:
1787 % o image: the image.
1789 % o channel: the channel type.
1791 % o radius: the radius of the Gaussian, in pixels, not counting the center
1794 % o sigma: the standard deviation of the Gaussian, in pixels.
1796 % o exception: return any errors or warnings in this structure.
1800 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
1801 const double sigma,ExceptionInfo *exception)
1806 blur_image=GaussianBlurImageChannel(image,DefaultChannels,radius,sigma,
1811 MagickExport Image *GaussianBlurImageChannel(const Image *image,
1812 const ChannelType channel,const double radius,const double sigma,
1813 ExceptionInfo *exception)
1832 assert(image != (const Image *) NULL);
1833 assert(image->signature == MagickSignature);
1834 if (image->debug != MagickFalse)
1835 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1836 assert(exception != (ExceptionInfo *) NULL);
1837 assert(exception->signature == MagickSignature);
1838 width=GetOptimalKernelWidth2D(radius,sigma);
1839 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
1840 if (kernel == (double *) NULL)
1841 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1843 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
1845 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
1847 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
1848 kernel[i]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
1852 blur_image=ConvolveImageChannel(image,channel,width,kernel,exception);
1853 kernel=(double *) RelinquishMagickMemory(kernel);
1858 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1862 % M e d i a n F i l t e r I m a g e %
1866 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1868 % MedianFilterImage() applies a digital filter that improves the quality
1869 % of a noisy image. Each pixel is replaced by the median in a set of
1870 % neighboring pixels as defined by radius.
1872 % The algorithm was contributed by Mike Edmonds and implements an insertion
1873 % sort for selecting median color-channel values. For more on this algorithm
1874 % see "Skip Lists: A probabilistic Alternative to Balanced Trees" by William
1875 % Pugh in the June 1990 of Communications of the ACM.
1877 % The format of the MedianFilterImage method is:
1879 % Image *MedianFilterImage(const Image *image,const double radius,
1880 % ExceptionInfo *exception)
1882 % A description of each parameter follows:
1884 % o image: the image.
1886 % o radius: the radius of the pixel neighborhood.
1888 % o exception: return any errors or warnings in this structure.
1892 #define MedianListChannels 5
1894 typedef struct _MedianListNode
1902 typedef struct _MedianSkipList
1911 typedef struct _MedianPixelList
1919 lists[MedianListChannels];
1922 static MedianPixelList *DestroyMedianPixelList(MedianPixelList *pixel_list)
1927 if (pixel_list == (MedianPixelList *) NULL)
1928 return((MedianPixelList *) NULL);
1929 for (i=0; i < MedianListChannels; i++)
1930 if (pixel_list->lists[i].nodes != (MedianListNode *) NULL)
1931 pixel_list->lists[i].nodes=(MedianListNode *) RelinquishMagickMemory(
1932 pixel_list->lists[i].nodes);
1933 pixel_list=(MedianPixelList *) RelinquishAlignedMemory(pixel_list);
1937 static MedianPixelList **DestroyMedianPixelListThreadSet(
1938 MedianPixelList **pixel_list)
1943 assert(pixel_list != (MedianPixelList **) NULL);
1944 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
1945 if (pixel_list[i] != (MedianPixelList *) NULL)
1946 pixel_list[i]=DestroyMedianPixelList(pixel_list[i]);
1947 pixel_list=(MedianPixelList **) RelinquishAlignedMemory(pixel_list);
1951 static MedianPixelList *AcquireMedianPixelList(const unsigned long width)
1959 pixel_list=(MedianPixelList *) AcquireAlignedMemory(1,sizeof(*pixel_list));
1960 if (pixel_list == (MedianPixelList *) NULL)
1962 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
1963 pixel_list->center=width*width/2;
1964 for (i=0; i < MedianListChannels; i++)
1966 pixel_list->lists[i].nodes=(MedianListNode *) AcquireQuantumMemory(65537UL,
1967 sizeof(*pixel_list->lists[i].nodes));
1968 if (pixel_list->lists[i].nodes == (MedianListNode *) NULL)
1969 return(DestroyMedianPixelList(pixel_list));
1970 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
1971 sizeof(*pixel_list->lists[i].nodes));
1973 pixel_list->signature=MagickSignature;
1977 static MedianPixelList **AcquireMedianPixelListThreadSet(
1978 const unsigned long width)
1989 number_threads=GetOpenMPMaximumThreads();
1990 pixel_list=(MedianPixelList **) AcquireAlignedMemory(number_threads,
1991 sizeof(*pixel_list));
1992 if (pixel_list == (MedianPixelList **) NULL)
1993 return((MedianPixelList **) NULL);
1994 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
1995 for (i=0; i < (long) number_threads; i++)
1997 pixel_list[i]=AcquireMedianPixelList(width);
1998 if (pixel_list[i] == (MedianPixelList *) NULL)
1999 return(DestroyMedianPixelListThreadSet(pixel_list));
2004 static void AddNodeMedianPixelList(MedianPixelList *pixel_list,
2005 const long channel,const unsigned long color)
2010 register MedianSkipList
2018 Initialize the node.
2020 list=pixel_list->lists+channel;
2021 list->nodes[color].signature=pixel_list->signature;
2022 list->nodes[color].count=1;
2024 Determine where it belongs in the list.
2027 for (level=list->level; level >= 0; level--)
2029 while (list->nodes[search].next[level] < color)
2030 search=list->nodes[search].next[level];
2031 update[level]=search;
2034 Generate a pseudo-random level for this node.
2036 for (level=0; ; level++)
2038 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
2039 if ((pixel_list->seed & 0x300) != 0x300)
2044 if (level > (list->level+2))
2045 level=list->level+2;
2047 If we're raising the list's level, link back to the root node.
2049 while (level > list->level)
2052 update[list->level]=65536UL;
2055 Link the node into the skip-list.
2059 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
2060 list->nodes[update[level]].next[level]=color;
2062 while (level-- > 0);
2065 static MagickPixelPacket GetMedianPixelList(MedianPixelList *pixel_list)
2073 register MedianSkipList
2082 channels[MedianListChannels];
2085 Find the median value for each of the color.
2087 center=pixel_list->center;
2088 for (channel=0; channel < 5; channel++)
2090 list=pixel_list->lists+channel;
2095 color=list->nodes[color].next[0];
2096 count+=list->nodes[color].count;
2098 while (count <= center);
2099 channels[channel]=(unsigned short) color;
2101 GetMagickPixelPacket((const Image *) NULL,&pixel);
2102 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
2103 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
2104 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
2105 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
2106 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
2110 static inline void InsertMedianPixelList(const Image *image,
2111 const PixelPacket *pixel,const IndexPacket *indexes,
2112 MedianPixelList *pixel_list)
2120 index=ScaleQuantumToShort(pixel->red);
2121 signature=pixel_list->lists[0].nodes[index].signature;
2122 if (signature == pixel_list->signature)
2123 pixel_list->lists[0].nodes[index].count++;
2125 AddNodeMedianPixelList(pixel_list,0,index);
2126 index=ScaleQuantumToShort(pixel->green);
2127 signature=pixel_list->lists[1].nodes[index].signature;
2128 if (signature == pixel_list->signature)
2129 pixel_list->lists[1].nodes[index].count++;
2131 AddNodeMedianPixelList(pixel_list,1,index);
2132 index=ScaleQuantumToShort(pixel->blue);
2133 signature=pixel_list->lists[2].nodes[index].signature;
2134 if (signature == pixel_list->signature)
2135 pixel_list->lists[2].nodes[index].count++;
2137 AddNodeMedianPixelList(pixel_list,2,index);
2138 index=ScaleQuantumToShort(pixel->opacity);
2139 signature=pixel_list->lists[3].nodes[index].signature;
2140 if (signature == pixel_list->signature)
2141 pixel_list->lists[3].nodes[index].count++;
2143 AddNodeMedianPixelList(pixel_list,3,index);
2144 if (image->colorspace == CMYKColorspace)
2145 index=ScaleQuantumToShort(*indexes);
2146 signature=pixel_list->lists[4].nodes[index].signature;
2147 if (signature == pixel_list->signature)
2148 pixel_list->lists[4].nodes[index].count++;
2150 AddNodeMedianPixelList(pixel_list,4,index);
2153 static void ResetMedianPixelList(MedianPixelList *pixel_list)
2161 register MedianListNode
2164 register MedianSkipList
2168 Reset the skip-list.
2170 for (channel=0; channel < 5; channel++)
2172 list=pixel_list->lists+channel;
2173 root=list->nodes+65536UL;
2175 for (level=0; level < 9; level++)
2176 root->next[level]=65536UL;
2178 pixel_list->seed=pixel_list->signature++;
2181 MagickExport Image *MedianFilterImage(const Image *image,const double radius,
2182 ExceptionInfo *exception)
2184 #define MedianFilterImageTag "MedianFilter/Image"
2207 Initialize median image attributes.
2209 assert(image != (Image *) NULL);
2210 assert(image->signature == MagickSignature);
2211 if (image->debug != MagickFalse)
2212 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2213 assert(exception != (ExceptionInfo *) NULL);
2214 assert(exception->signature == MagickSignature);
2215 width=GetOptimalKernelWidth2D(radius,0.5);
2216 if ((image->columns < width) || (image->rows < width))
2217 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
2218 median_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2220 if (median_image == (Image *) NULL)
2221 return((Image *) NULL);
2222 if (SetImageStorageClass(median_image,DirectClass) == MagickFalse)
2224 InheritException(exception,&median_image->exception);
2225 median_image=DestroyImage(median_image);
2226 return((Image *) NULL);
2228 pixel_list=AcquireMedianPixelListThreadSet(width);
2229 if (pixel_list == (MedianPixelList **) NULL)
2231 median_image=DestroyImage(median_image);
2232 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2235 Median filter each image row.
2239 image_view=AcquireCacheView(image);
2240 median_view=AcquireCacheView(median_image);
2241 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2242 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2244 for (y=0; y < (long) median_image->rows; y++)
2246 register const IndexPacket
2249 register const PixelPacket
2252 register IndexPacket
2253 *restrict median_indexes;
2259 register PixelPacket
2262 if (status == MagickFalse)
2264 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
2265 2L),image->columns+width,width,exception);
2266 q=QueueCacheViewAuthenticPixels(median_view,0,y,median_image->columns,1,
2268 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2273 indexes=GetCacheViewVirtualIndexQueue(image_view);
2274 median_indexes=GetCacheViewAuthenticIndexQueue(median_view);
2275 id=GetOpenMPThreadId();
2276 for (x=0; x < (long) median_image->columns; x++)
2281 register const PixelPacket
2284 register const IndexPacket
2293 ResetMedianPixelList(pixel_list[id]);
2294 for (v=0; v < (long) width; v++)
2296 for (u=0; u < (long) width; u++)
2297 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
2298 r+=image->columns+width;
2299 s+=image->columns+width;
2301 pixel=GetMedianPixelList(pixel_list[id]);
2302 SetPixelPacket(median_image,&pixel,q,median_indexes+x);
2306 if (SyncCacheViewAuthenticPixels(median_view,exception) == MagickFalse)
2308 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2313 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2314 #pragma omp critical (MagickCore_MedianFilterImage)
2316 proceed=SetImageProgress(image,MedianFilterImageTag,progress++,
2318 if (proceed == MagickFalse)
2322 median_view=DestroyCacheView(median_view);
2323 image_view=DestroyCacheView(image_view);
2324 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
2325 return(median_image);
2329 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2333 % M o t i o n B l u r I m a g e %
2337 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2339 % MotionBlurImage() simulates motion blur. We convolve the image with a
2340 % Gaussian operator of the given radius and standard deviation (sigma).
2341 % For reasonable results, radius should be larger than sigma. Use a
2342 % radius of 0 and MotionBlurImage() selects a suitable radius for you.
2343 % Angle gives the angle of the blurring motion.
2345 % Andrew Protano contributed this effect.
2347 % The format of the MotionBlurImage method is:
2349 % Image *MotionBlurImage(const Image *image,const double radius,
2350 % const double sigma,const double angle,ExceptionInfo *exception)
2351 % Image *MotionBlurImageChannel(const Image *image,const ChannelType channel,
2352 % const double radius,const double sigma,const double angle,
2353 % ExceptionInfo *exception)
2355 % A description of each parameter follows:
2357 % o image: the image.
2359 % o channel: the channel type.
2361 % o radius: the radius of the Gaussian, in pixels, not counting the center
2362 % o radius: the radius of the Gaussian, in pixels, not counting
2365 % o sigma: the standard deviation of the Gaussian, in pixels.
2367 % o angle: Apply the effect along this angle.
2369 % o exception: return any errors or warnings in this structure.
2373 static double *GetMotionBlurKernel(unsigned long width,
2374 const MagickRealType sigma)
2376 #define KernelRank 3
2392 Generate a 1-D convolution kernel.
2394 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2395 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
2396 if (kernel == (double *) NULL)
2398 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
2399 bias=(long) (KernelRank*width);
2400 for (i=0; i < (long) bias; i++)
2402 alpha=exp((-((double) (i*i))/(double) (2.0*KernelRank*KernelRank*
2403 MagickSigma*MagickSigma)));
2404 kernel[i/KernelRank]+=(double) alpha/(MagickSQ2PI*sigma);
2407 for (i=0; i < (long) width; i++)
2408 normalize+=kernel[i];
2409 for (i=0; i < (long) width; i++)
2410 kernel[i]/=normalize;
2414 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
2415 const double sigma,const double angle,ExceptionInfo *exception)
2420 motion_blur=MotionBlurImageChannel(image,DefaultChannels,radius,sigma,angle,
2422 return(motion_blur);
2425 MagickExport Image *MotionBlurImageChannel(const Image *image,
2426 const ChannelType channel,const double radius,const double sigma,
2427 const double angle,ExceptionInfo *exception)
2429 typedef struct _OffsetInfo
2468 assert(image != (Image *) NULL);
2469 assert(image->signature == MagickSignature);
2470 if (image->debug != MagickFalse)
2471 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2472 assert(exception != (ExceptionInfo *) NULL);
2473 width=GetOptimalKernelWidth1D(radius,sigma);
2474 kernel=GetMotionBlurKernel(width,sigma);
2475 if (kernel == (double *) NULL)
2476 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2477 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2478 if (offset == (OffsetInfo *) NULL)
2480 kernel=(double *) RelinquishMagickMemory(kernel);
2481 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2483 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2484 if (blur_image == (Image *) NULL)
2486 kernel=(double *) RelinquishMagickMemory(kernel);
2487 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2488 return((Image *) NULL);
2490 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
2492 kernel=(double *) RelinquishMagickMemory(kernel);
2493 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2494 InheritException(exception,&blur_image->exception);
2495 blur_image=DestroyImage(blur_image);
2496 return((Image *) NULL);
2498 point.x=(double) width*sin(DegreesToRadians(angle));
2499 point.y=(double) width*cos(DegreesToRadians(angle));
2500 for (i=0; i < (long) width; i++)
2502 offset[i].x=(long) ((i*point.y)/hypot(point.x,point.y)+0.5);
2503 offset[i].y=(long) ((i*point.x)/hypot(point.x,point.y)+0.5);
2510 GetMagickPixelPacket(image,&bias);
2511 image_view=AcquireCacheView(image);
2512 blur_view=AcquireCacheView(blur_image);
2513 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2514 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2516 for (y=0; y < (long) image->rows; y++)
2518 register IndexPacket
2519 *restrict blur_indexes;
2524 register PixelPacket
2527 if (status == MagickFalse)
2529 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2531 if (q == (PixelPacket *) NULL)
2536 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
2537 for (x=0; x < (long) image->columns; x++)
2551 register const IndexPacket
2556 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
2558 for (i=0; i < (long) width; i++)
2560 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2561 offset[i].y,&pixel,exception);
2562 qixel.red+=(*k)*pixel.red;
2563 qixel.green+=(*k)*pixel.green;
2564 qixel.blue+=(*k)*pixel.blue;
2565 qixel.opacity+=(*k)*pixel.opacity;
2566 if (image->colorspace == CMYKColorspace)
2568 indexes=GetCacheViewVirtualIndexQueue(image_view);
2569 qixel.index+=(*k)*(*indexes);
2573 if ((channel & RedChannel) != 0)
2574 q->red=RoundToQuantum(qixel.red);
2575 if ((channel & GreenChannel) != 0)
2576 q->green=RoundToQuantum(qixel.green);
2577 if ((channel & BlueChannel) != 0)
2578 q->blue=RoundToQuantum(qixel.blue);
2579 if ((channel & OpacityChannel) != 0)
2580 q->opacity=RoundToQuantum(qixel.opacity);
2581 if (((channel & IndexChannel) != 0) &&
2582 (image->colorspace == CMYKColorspace))
2583 blur_indexes[x]=(IndexPacket) RoundToQuantum(qixel.index);
2593 for (i=0; i < (long) width; i++)
2595 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2596 offset[i].y,&pixel,exception);
2597 alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
2598 qixel.red+=(*k)*alpha*pixel.red;
2599 qixel.green+=(*k)*alpha*pixel.green;
2600 qixel.blue+=(*k)*alpha*pixel.blue;
2601 qixel.opacity+=(*k)*pixel.opacity;
2602 if (image->colorspace == CMYKColorspace)
2604 indexes=GetCacheViewVirtualIndexQueue(image_view);
2605 qixel.index+=(*k)*alpha*(*indexes);
2610 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2611 if ((channel & RedChannel) != 0)
2612 q->red=RoundToQuantum(gamma*qixel.red);
2613 if ((channel & GreenChannel) != 0)
2614 q->green=RoundToQuantum(gamma*qixel.green);
2615 if ((channel & BlueChannel) != 0)
2616 q->blue=RoundToQuantum(gamma*qixel.blue);
2617 if ((channel & OpacityChannel) != 0)
2618 q->opacity=RoundToQuantum(qixel.opacity);
2619 if (((channel & IndexChannel) != 0) &&
2620 (image->colorspace == CMYKColorspace))
2621 blur_indexes[x]=(IndexPacket) RoundToQuantum(gamma*qixel.index);
2625 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2627 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2632 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2633 #pragma omp critical (MagickCore_MotionBlurImageChannel)
2635 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2636 if (proceed == MagickFalse)
2640 blur_view=DestroyCacheView(blur_view);
2641 image_view=DestroyCacheView(image_view);
2642 kernel=(double *) RelinquishMagickMemory(kernel);
2643 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2644 if (status == MagickFalse)
2645 blur_image=DestroyImage(blur_image);
2650 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2654 % P r e v i e w I m a g e %
2658 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2660 % PreviewImage() tiles 9 thumbnails of the specified image with an image
2661 % processing operation applied with varying parameters. This may be helpful
2662 % pin-pointing an appropriate parameter for a particular image processing
2665 % The format of the PreviewImages method is:
2667 % Image *PreviewImages(const Image *image,const PreviewType preview,
2668 % ExceptionInfo *exception)
2670 % A description of each parameter follows:
2672 % o image: the image.
2674 % o preview: the image processing operation.
2676 % o exception: return any errors or warnings in this structure.
2679 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
2680 ExceptionInfo *exception)
2682 #define NumberTiles 9
2683 #define PreviewImageTag "Preview/Image"
2684 #define DefaultPreviewGeometry "204x204+10+10"
2687 factor[MaxTextExtent],
2688 label[MaxTextExtent];
2730 Open output image file.
2732 assert(image != (Image *) NULL);
2733 assert(image->signature == MagickSignature);
2734 if (image->debug != MagickFalse)
2735 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2739 preview_info=AcquireImageInfo();
2740 SetGeometry(image,&geometry);
2741 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2742 &geometry.width,&geometry.height);
2743 images=NewImageList();
2745 GetQuantizeInfo(&quantize_info);
2751 for (i=0; i < NumberTiles; i++)
2753 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2754 if (thumbnail == (Image *) NULL)
2756 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2758 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
2759 if (i == (NumberTiles/2))
2761 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
2762 AppendImageToList(&images,thumbnail);
2770 preview_image=RotateImage(thumbnail,degrees,exception);
2771 (void) FormatMagickString(label,MaxTextExtent,"rotate %g",degrees);
2777 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
2778 (void) FormatMagickString(label,MaxTextExtent,"shear %gx%g",
2779 degrees,2.0*degrees);
2784 x=(long) ((i+1)*thumbnail->columns)/NumberTiles;
2785 y=(long) ((i+1)*thumbnail->rows)/NumberTiles;
2786 preview_image=RollImage(thumbnail,x,y,exception);
2787 (void) FormatMagickString(label,MaxTextExtent,"roll %ldx%ld",x,y);
2792 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2793 if (preview_image == (Image *) NULL)
2795 (void) FormatMagickString(factor,MaxTextExtent,"100,100,%g",
2797 (void) ModulateImage(preview_image,factor);
2798 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
2801 case SaturationPreview:
2803 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2804 if (preview_image == (Image *) NULL)
2806 (void) FormatMagickString(factor,MaxTextExtent,"100,%g",2.0*percentage);
2807 (void) ModulateImage(preview_image,factor);
2808 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
2811 case BrightnessPreview:
2813 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2814 if (preview_image == (Image *) NULL)
2816 (void) FormatMagickString(factor,MaxTextExtent,"%g",2.0*percentage);
2817 (void) ModulateImage(preview_image,factor);
2818 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
2824 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2825 if (preview_image == (Image *) NULL)
2828 (void) GammaImageChannel(preview_image,DefaultChannels,gamma);
2829 (void) FormatMagickString(label,MaxTextExtent,"gamma %g",gamma);
2834 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2835 if (preview_image != (Image *) NULL)
2836 for (x=0; x < i; x++)
2837 (void) ContrastImage(preview_image,MagickTrue);
2838 (void) FormatMagickString(label,MaxTextExtent,"contrast (%ld)",i+1);
2843 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2844 if (preview_image == (Image *) NULL)
2846 for (x=0; x < i; x++)
2847 (void) ContrastImage(preview_image,MagickFalse);
2848 (void) FormatMagickString(label,MaxTextExtent,"+contrast (%ld)",i+1);
2851 case GrayscalePreview:
2853 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2854 if (preview_image == (Image *) NULL)
2857 quantize_info.number_colors=colors;
2858 quantize_info.colorspace=GRAYColorspace;
2859 (void) QuantizeImage(&quantize_info,preview_image);
2860 (void) FormatMagickString(label,MaxTextExtent,
2861 "-colorspace gray -colors %ld",colors);
2864 case QuantizePreview:
2866 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2867 if (preview_image == (Image *) NULL)
2870 quantize_info.number_colors=colors;
2871 (void) QuantizeImage(&quantize_info,preview_image);
2872 (void) FormatMagickString(label,MaxTextExtent,"colors %ld",colors);
2875 case DespecklePreview:
2877 for (x=0; x < (i-1); x++)
2879 preview_image=DespeckleImage(thumbnail,exception);
2880 if (preview_image == (Image *) NULL)
2882 thumbnail=DestroyImage(thumbnail);
2883 thumbnail=preview_image;
2885 preview_image=DespeckleImage(thumbnail,exception);
2886 if (preview_image == (Image *) NULL)
2888 (void) FormatMagickString(label,MaxTextExtent,"despeckle (%ld)",i+1);
2891 case ReduceNoisePreview:
2893 preview_image=ReduceNoiseImage(thumbnail,radius,exception);
2894 (void) FormatMagickString(label,MaxTextExtent,"noise %g",radius);
2897 case AddNoisePreview:
2903 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
2908 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
2913 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
2918 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
2923 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
2928 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
2933 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
2937 preview_image=ReduceNoiseImage(thumbnail,(double) i,exception);
2938 (void) FormatMagickString(label,MaxTextExtent,"+noise %s",factor);
2941 case SharpenPreview:
2943 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
2944 (void) FormatMagickString(label,MaxTextExtent,"sharpen %gx%g",radius,
2950 preview_image=BlurImage(thumbnail,radius,sigma,exception);
2951 (void) FormatMagickString(label,MaxTextExtent,"blur %gx%g",radius,
2955 case ThresholdPreview:
2957 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2958 if (preview_image == (Image *) NULL)
2960 (void) BilevelImage(thumbnail,
2961 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2962 (void) FormatMagickString(label,MaxTextExtent,"threshold %g",
2963 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2966 case EdgeDetectPreview:
2968 preview_image=EdgeImage(thumbnail,radius,exception);
2969 (void) FormatMagickString(label,MaxTextExtent,"edge %g",radius);
2974 preview_image=SpreadImage(thumbnail,radius,exception);
2975 (void) FormatMagickString(label,MaxTextExtent,"spread %g",radius+0.5);
2978 case SolarizePreview:
2980 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2981 if (preview_image == (Image *) NULL)
2983 (void) SolarizeImage(preview_image,(double) QuantumRange*
2985 (void) FormatMagickString(label,MaxTextExtent,"solarize %g",
2986 (QuantumRange*percentage)/100.0);
2992 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2994 (void) FormatMagickString(label,MaxTextExtent,"shade %gx%g",degrees,
3000 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3001 if (preview_image == (Image *) NULL)
3003 geometry.width=(unsigned long) (2*i+2);
3004 geometry.height=(unsigned long) (2*i+2);
3007 (void) RaiseImage(preview_image,&geometry,MagickTrue);
3008 (void) FormatMagickString(label,MaxTextExtent,"raise %lux%lu%+ld%+ld",
3009 geometry.width,geometry.height,geometry.x,geometry.y);
3012 case SegmentPreview:
3014 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3015 if (preview_image == (Image *) NULL)
3018 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
3020 (void) FormatMagickString(label,MaxTextExtent,"segment %gx%g",
3021 threshold,threshold);
3026 preview_image=SwirlImage(thumbnail,degrees,exception);
3027 (void) FormatMagickString(label,MaxTextExtent,"swirl %g",degrees);
3031 case ImplodePreview:
3034 preview_image=ImplodeImage(thumbnail,degrees,exception);
3035 (void) FormatMagickString(label,MaxTextExtent,"implode %g",degrees);
3041 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
3042 (void) FormatMagickString(label,MaxTextExtent,"wave %gx%g",0.5*degrees,
3046 case OilPaintPreview:
3048 preview_image=OilPaintImage(thumbnail,(double) radius,exception);
3049 (void) FormatMagickString(label,MaxTextExtent,"paint %g",radius);
3052 case CharcoalDrawingPreview:
3054 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
3056 (void) FormatMagickString(label,MaxTextExtent,"charcoal %gx%g",radius,
3063 filename[MaxTextExtent];
3071 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3072 if (preview_image == (Image *) NULL)
3074 preview_info->quality=(unsigned long) percentage;
3075 (void) FormatMagickString(factor,MaxTextExtent,"%lu",
3076 preview_info->quality);
3077 file=AcquireUniqueFileResource(filename);
3080 (void) FormatMagickString(preview_image->filename,MaxTextExtent,
3081 "jpeg:%s",filename);
3082 status=WriteImage(preview_info,preview_image);
3083 if (status != MagickFalse)
3088 (void) CopyMagickString(preview_info->filename,
3089 preview_image->filename,MaxTextExtent);
3090 quality_image=ReadImage(preview_info,exception);
3091 if (quality_image != (Image *) NULL)
3093 preview_image=DestroyImage(preview_image);
3094 preview_image=quality_image;
3097 (void) RelinquishUniqueFileResource(preview_image->filename);
3098 if ((GetBlobSize(preview_image)/1024) >= 1024)
3099 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gmb ",
3100 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
3103 if (GetBlobSize(preview_image) >= 1024)
3104 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gkb ",
3105 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
3108 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%lub ",
3109 factor,(unsigned long) GetBlobSize(thumbnail));
3113 thumbnail=DestroyImage(thumbnail);
3117 if (preview_image == (Image *) NULL)
3119 (void) DeleteImageProperty(preview_image,"label");
3120 (void) SetImageProperty(preview_image,"label",label);
3121 AppendImageToList(&images,preview_image);
3122 proceed=SetImageProgress(image,PreviewImageTag,i,NumberTiles);
3123 if (proceed == MagickFalse)
3126 if (images == (Image *) NULL)
3128 preview_info=DestroyImageInfo(preview_info);
3129 return((Image *) NULL);
3134 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
3135 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
3136 montage_info->shadow=MagickTrue;
3137 (void) CloneString(&montage_info->tile,"3x3");
3138 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
3139 (void) CloneString(&montage_info->frame,DefaultTileFrame);
3140 montage_image=MontageImages(images,montage_info,exception);
3141 montage_info=DestroyMontageInfo(montage_info);
3142 images=DestroyImageList(images);
3143 if (montage_image == (Image *) NULL)
3144 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3145 if (montage_image->montage != (char *) NULL)
3148 Free image directory.
3150 montage_image->montage=(char *) RelinquishMagickMemory(
3151 montage_image->montage);
3152 if (image->directory != (char *) NULL)
3153 montage_image->directory=(char *) RelinquishMagickMemory(
3154 montage_image->directory);
3156 preview_info=DestroyImageInfo(preview_info);
3157 return(montage_image);
3161 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3165 % R a d i a l B l u r I m a g e %
3169 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3171 % RadialBlurImage() applies a radial blur to the image.
3173 % Andrew Protano contributed this effect.
3175 % The format of the RadialBlurImage method is:
3177 % Image *RadialBlurImage(const Image *image,const double angle,
3178 % ExceptionInfo *exception)
3179 % Image *RadialBlurImageChannel(const Image *image,const ChannelType channel,
3180 % const double angle,ExceptionInfo *exception)
3182 % A description of each parameter follows:
3184 % o image: the image.
3186 % o channel: the channel type.
3188 % o angle: the angle of the radial blur.
3190 % o exception: return any errors or warnings in this structure.
3194 MagickExport Image *RadialBlurImage(const Image *image,const double angle,
3195 ExceptionInfo *exception)
3200 blur_image=RadialBlurImageChannel(image,DefaultChannels,angle,exception);
3204 MagickExport Image *RadialBlurImageChannel(const Image *image,
3205 const ChannelType channel,const double angle,ExceptionInfo *exception)
3241 Allocate blur image.
3243 assert(image != (Image *) NULL);
3244 assert(image->signature == MagickSignature);
3245 if (image->debug != MagickFalse)
3246 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3247 assert(exception != (ExceptionInfo *) NULL);
3248 assert(exception->signature == MagickSignature);
3249 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3250 if (blur_image == (Image *) NULL)
3251 return((Image *) NULL);
3252 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3254 InheritException(exception,&blur_image->exception);
3255 blur_image=DestroyImage(blur_image);
3256 return((Image *) NULL);
3258 blur_center.x=(double) image->columns/2.0;
3259 blur_center.y=(double) image->rows/2.0;
3260 blur_radius=hypot(blur_center.x,blur_center.y);
3261 n=(unsigned long) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+
3263 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
3264 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3265 sizeof(*cos_theta));
3266 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3267 sizeof(*sin_theta));
3268 if ((cos_theta == (MagickRealType *) NULL) ||
3269 (sin_theta == (MagickRealType *) NULL))
3271 blur_image=DestroyImage(blur_image);
3272 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3274 offset=theta*(MagickRealType) (n-1)/2.0;
3275 for (i=0; i < (long) n; i++)
3277 cos_theta[i]=cos((double) (theta*i-offset));
3278 sin_theta[i]=sin((double) (theta*i-offset));
3285 GetMagickPixelPacket(image,&bias);
3286 image_view=AcquireCacheView(image);
3287 blur_view=AcquireCacheView(blur_image);
3288 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3289 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3291 for (y=0; y < (long) blur_image->rows; y++)
3293 register const IndexPacket
3296 register IndexPacket
3297 *restrict blur_indexes;
3302 register PixelPacket
3305 if (status == MagickFalse)
3307 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3309 if (q == (PixelPacket *) NULL)
3314 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
3315 for (x=0; x < (long) blur_image->columns; x++)
3336 center.x=(double) x-blur_center.x;
3337 center.y=(double) y-blur_center.y;
3338 radius=hypot((double) center.x,center.y);
3343 step=(unsigned long) (blur_radius/radius);
3352 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
3354 for (i=0; i < (long) n; i+=step)
3356 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
3357 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
3358 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
3360 qixel.red+=pixel.red;
3361 qixel.green+=pixel.green;
3362 qixel.blue+=pixel.blue;
3363 qixel.opacity+=pixel.opacity;
3364 if (image->colorspace == CMYKColorspace)
3366 indexes=GetCacheViewVirtualIndexQueue(image_view);
3367 qixel.index+=(*indexes);
3371 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3373 if ((channel & RedChannel) != 0)
3374 q->red=RoundToQuantum(normalize*qixel.red);
3375 if ((channel & GreenChannel) != 0)
3376 q->green=RoundToQuantum(normalize*qixel.green);
3377 if ((channel & BlueChannel) != 0)
3378 q->blue=RoundToQuantum(normalize*qixel.blue);
3379 if ((channel & OpacityChannel) != 0)
3380 q->opacity=RoundToQuantum(normalize*qixel.opacity);
3381 if (((channel & IndexChannel) != 0) &&
3382 (image->colorspace == CMYKColorspace))
3383 blur_indexes[x]=(IndexPacket) RoundToQuantum(normalize*qixel.index);
3393 for (i=0; i < (long) n; i+=step)
3395 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
3396 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
3397 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
3399 alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
3400 qixel.red+=alpha*pixel.red;
3401 qixel.green+=alpha*pixel.green;
3402 qixel.blue+=alpha*pixel.blue;
3403 qixel.opacity+=pixel.opacity;
3404 if (image->colorspace == CMYKColorspace)
3406 indexes=GetCacheViewVirtualIndexQueue(image_view);
3407 qixel.index+=alpha*(*indexes);
3412 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3413 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3415 if ((channel & RedChannel) != 0)
3416 q->red=RoundToQuantum(gamma*qixel.red);
3417 if ((channel & GreenChannel) != 0)
3418 q->green=RoundToQuantum(gamma*qixel.green);
3419 if ((channel & BlueChannel) != 0)
3420 q->blue=RoundToQuantum(gamma*qixel.blue);
3421 if ((channel & OpacityChannel) != 0)
3422 q->opacity=RoundToQuantum(normalize*qixel.opacity);
3423 if (((channel & IndexChannel) != 0) &&
3424 (image->colorspace == CMYKColorspace))
3425 blur_indexes[x]=(IndexPacket) RoundToQuantum(gamma*qixel.index);
3429 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3431 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3436 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3437 #pragma omp critical (MagickCore_RadialBlurImageChannel)
3439 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3440 if (proceed == MagickFalse)
3444 blur_view=DestroyCacheView(blur_view);
3445 image_view=DestroyCacheView(image_view);
3446 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
3447 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
3448 if (status == MagickFalse)
3449 blur_image=DestroyImage(blur_image);
3454 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3458 % R e d u c e N o i s e I m a g e %
3462 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3464 % ReduceNoiseImage() smooths the contours of an image while still preserving
3465 % edge information. The algorithm works by replacing each pixel with its
3466 % neighbor closest in value. A neighbor is defined by radius. Use a radius
3467 % of 0 and ReduceNoise() selects a suitable radius for you.
3469 % The format of the ReduceNoiseImage method is:
3471 % Image *ReduceNoiseImage(const Image *image,const double radius,
3472 % ExceptionInfo *exception)
3474 % A description of each parameter follows:
3476 % o image: the image.
3478 % o radius: the radius of the pixel neighborhood.
3480 % o exception: return any errors or warnings in this structure.
3484 static MagickPixelPacket GetNonpeakMedianPixelList(MedianPixelList *pixel_list)
3492 register MedianSkipList
3506 Finds the median value for each of the color.
3508 center=pixel_list->center;
3509 for (channel=0; channel < 5; channel++)
3511 list=pixel_list->lists+channel;
3513 next=list->nodes[color].next[0];
3519 next=list->nodes[color].next[0];
3520 count+=list->nodes[color].count;
3522 while (count <= center);
3523 if ((previous == 65536UL) && (next != 65536UL))
3526 if ((previous != 65536UL) && (next == 65536UL))
3528 channels[channel]=(unsigned short) color;
3530 GetMagickPixelPacket((const Image *) NULL,&pixel);
3531 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
3532 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
3533 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
3534 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
3535 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
3539 MagickExport Image *ReduceNoiseImage(const Image *image,const double radius,
3540 ExceptionInfo *exception)
3542 #define ReduceNoiseImageTag "ReduceNoise/Image"
3565 Initialize noise image attributes.
3567 assert(image != (Image *) NULL);
3568 assert(image->signature == MagickSignature);
3569 if (image->debug != MagickFalse)
3570 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3571 assert(exception != (ExceptionInfo *) NULL);
3572 assert(exception->signature == MagickSignature);
3573 width=GetOptimalKernelWidth2D(radius,0.5);
3574 if ((image->columns < width) || (image->rows < width))
3575 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
3576 noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3578 if (noise_image == (Image *) NULL)
3579 return((Image *) NULL);
3580 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
3582 InheritException(exception,&noise_image->exception);
3583 noise_image=DestroyImage(noise_image);
3584 return((Image *) NULL);
3586 pixel_list=AcquireMedianPixelListThreadSet(width);
3587 if (pixel_list == (MedianPixelList **) NULL)
3589 noise_image=DestroyImage(noise_image);
3590 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3597 image_view=AcquireCacheView(image);
3598 noise_view=AcquireCacheView(noise_image);
3599 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3600 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3602 for (y=0; y < (long) noise_image->rows; y++)
3604 register const IndexPacket
3607 register const PixelPacket
3610 register IndexPacket
3611 *restrict noise_indexes;
3617 register PixelPacket
3620 if (status == MagickFalse)
3622 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
3623 2L),image->columns+width,width,exception);
3624 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
3626 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3631 indexes=GetCacheViewVirtualIndexQueue(image_view);
3632 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
3633 id=GetOpenMPThreadId();
3634 for (x=0; x < (long) noise_image->columns; x++)
3639 register const PixelPacket
3642 register const IndexPacket
3651 ResetMedianPixelList(pixel_list[id]);
3652 for (v=0; v < (long) width; v++)
3654 for (u=0; u < (long) width; u++)
3655 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
3656 r+=image->columns+width;
3657 s+=image->columns+width;
3659 pixel=GetNonpeakMedianPixelList(pixel_list[id]);
3660 SetPixelPacket(noise_image,&pixel,q,noise_indexes+x);
3664 if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
3666 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3671 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3672 #pragma omp critical (MagickCore_ReduceNoiseImage)
3674 proceed=SetImageProgress(image,ReduceNoiseImageTag,progress++,
3676 if (proceed == MagickFalse)
3680 noise_view=DestroyCacheView(noise_view);
3681 image_view=DestroyCacheView(image_view);
3682 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
3683 return(noise_image);
3687 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3691 % S e l e c t i v e B l u r I m a g e %
3695 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3697 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3698 % It is similar to the unsharpen mask that sharpens everything with contrast
3699 % above a certain threshold.
3701 % The format of the SelectiveBlurImage method is:
3703 % Image *SelectiveBlurImage(const Image *image,const double radius,
3704 % const double sigma,const double threshold,ExceptionInfo *exception)
3705 % Image *SelectiveBlurImageChannel(const Image *image,
3706 % const ChannelType channel,const double radius,const double sigma,
3707 % const double threshold,ExceptionInfo *exception)
3709 % A description of each parameter follows:
3711 % o image: the image.
3713 % o channel: the channel type.
3715 % o radius: the radius of the Gaussian, in pixels, not counting the center
3718 % o sigma: the standard deviation of the Gaussian, in pixels.
3720 % o threshold: only pixels within this contrast threshold are included
3721 % in the blur operation.
3723 % o exception: return any errors or warnings in this structure.
3727 static inline MagickBooleanType SelectiveContrast(const PixelPacket *p,
3728 const PixelPacket *q,const double threshold)
3730 if (fabs(PixelIntensity(p)-PixelIntensity(q)) < threshold)
3732 return(MagickFalse);
3735 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
3736 const double sigma,const double threshold,ExceptionInfo *exception)
3741 blur_image=SelectiveBlurImageChannel(image,DefaultChannels,radius,sigma,
3742 threshold,exception);
3746 MagickExport Image *SelectiveBlurImageChannel(const Image *image,
3747 const ChannelType channel,const double radius,const double sigma,
3748 const double threshold,ExceptionInfo *exception)
3750 #define SelectiveBlurImageTag "SelectiveBlur/Image"
3782 Initialize blur image attributes.
3784 assert(image != (Image *) NULL);
3785 assert(image->signature == MagickSignature);
3786 if (image->debug != MagickFalse)
3787 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3788 assert(exception != (ExceptionInfo *) NULL);
3789 assert(exception->signature == MagickSignature);
3790 width=GetOptimalKernelWidth1D(radius,sigma);
3791 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
3792 if (kernel == (double *) NULL)
3793 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3795 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
3797 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
3799 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
3800 kernel[i]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
3804 if (image->debug != MagickFalse)
3807 format[MaxTextExtent],
3814 register const double
3817 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
3818 " SelectiveBlurImage with %ldx%ld kernel:",width,width);
3819 message=AcquireString("");
3821 for (v=0; v < (long) width; v++)
3824 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
3825 (void) ConcatenateString(&message,format);
3826 for (u=0; u < (long) width; u++)
3828 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
3829 (void) ConcatenateString(&message,format);
3831 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3833 message=DestroyString(message);
3835 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3836 if (blur_image == (Image *) NULL)
3837 return((Image *) NULL);
3838 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3840 InheritException(exception,&blur_image->exception);
3841 blur_image=DestroyImage(blur_image);
3842 return((Image *) NULL);
3845 Threshold blur image.
3849 GetMagickPixelPacket(image,&bias);
3850 SetMagickPixelPacketBias(image,&bias);
3851 image_view=AcquireCacheView(image);
3852 blur_view=AcquireCacheView(blur_image);
3853 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3854 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3856 for (y=0; y < (long) image->rows; y++)
3864 register const IndexPacket
3867 register const PixelPacket
3870 register IndexPacket
3871 *restrict blur_indexes;
3876 register PixelPacket
3879 if (status == MagickFalse)
3881 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
3882 2L),image->columns+width,width,exception);
3883 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3885 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3890 indexes=GetCacheViewVirtualIndexQueue(image_view);
3891 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
3892 for (x=0; x < (long) image->columns; x++)
3901 register const double
3911 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
3913 for (v=0; v < (long) width; v++)
3915 for (u=0; u < (long) width; u++)
3917 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
3919 pixel.red+=(*k)*(p+u+j)->red;
3920 pixel.green+=(*k)*(p+u+j)->green;
3921 pixel.blue+=(*k)*(p+u+j)->blue;
3926 j+=image->columns+width;
3930 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3931 if ((channel & RedChannel) != 0)
3932 q->red=RoundToQuantum(gamma*pixel.red);
3933 if ((channel & GreenChannel) != 0)
3934 q->green=RoundToQuantum(gamma*pixel.green);
3935 if ((channel & BlueChannel) != 0)
3936 q->blue=RoundToQuantum(gamma*pixel.blue);
3938 if ((channel & OpacityChannel) != 0)
3942 for (v=0; v < (long) width; v++)
3944 for (u=0; u < (long) width; u++)
3946 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
3948 pixel.opacity+=(*k)*(p+u+j)->opacity;
3953 j+=image->columns+width;
3957 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3959 q->opacity=RoundToQuantum(gamma*pixel.opacity);
3962 if (((channel & IndexChannel) != 0) &&
3963 (image->colorspace == CMYKColorspace))
3967 for (v=0; v < (long) width; v++)
3969 for (u=0; u < (long) width; u++)
3971 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
3973 pixel.index+=(*k)*indexes[x+u+j];
3978 j+=image->columns+width;
3982 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3984 blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
3993 for (v=0; v < (long) width; v++)
3995 for (u=0; u < (long) width; u++)
3997 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
3999 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
4001 pixel.red+=(*k)*alpha*(p+u+j)->red;
4002 pixel.green+=(*k)*alpha*(p+u+j)->green;
4003 pixel.blue+=(*k)*alpha*(p+u+j)->blue;
4004 pixel.opacity+=(*k)*(p+u+j)->opacity;
4009 j+=image->columns+width;
4013 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4014 if ((channel & RedChannel) != 0)
4015 q->red=RoundToQuantum(gamma*pixel.red);
4016 if ((channel & GreenChannel) != 0)
4017 q->green=RoundToQuantum(gamma*pixel.green);
4018 if ((channel & BlueChannel) != 0)
4019 q->blue=RoundToQuantum(gamma*pixel.blue);
4021 if ((channel & OpacityChannel) != 0)
4025 for (v=0; v < (long) width; v++)
4027 for (u=0; u < (long) width; u++)
4029 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4031 pixel.opacity+=(*k)*(p+u+j)->opacity;
4036 j+=image->columns+width;
4040 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4042 q->opacity=RoundToQuantum(pixel.opacity);
4045 if (((channel & IndexChannel) != 0) &&
4046 (image->colorspace == CMYKColorspace))
4050 for (v=0; v < (long) width; v++)
4052 for (u=0; u < (long) width; u++)
4054 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4056 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
4058 pixel.index+=(*k)*alpha*indexes[x+u+j];
4063 j+=image->columns+width;
4067 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4069 blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
4076 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
4077 if (sync == MagickFalse)
4079 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4084 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4085 #pragma omp critical (MagickCore_SelectiveBlurImageChannel)
4087 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
4089 if (proceed == MagickFalse)
4093 blur_image->type=image->type;
4094 blur_view=DestroyCacheView(blur_view);
4095 image_view=DestroyCacheView(image_view);
4096 kernel=(double *) RelinquishMagickMemory(kernel);
4097 if (status == MagickFalse)
4098 blur_image=DestroyImage(blur_image);
4103 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4107 % S h a d e I m a g e %
4111 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4113 % ShadeImage() shines a distant light on an image to create a
4114 % three-dimensional effect. You control the positioning of the light with
4115 % azimuth and elevation; azimuth is measured in degrees off the x axis
4116 % and elevation is measured in pixels above the Z axis.
4118 % The format of the ShadeImage method is:
4120 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4121 % const double azimuth,const double elevation,ExceptionInfo *exception)
4123 % A description of each parameter follows:
4125 % o image: the image.
4127 % o gray: A value other than zero shades the intensity of each pixel.
4129 % o azimuth, elevation: Define the light source direction.
4131 % o exception: return any errors or warnings in this structure.
4134 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4135 const double azimuth,const double elevation,ExceptionInfo *exception)
4137 #define ShadeImageTag "Shade/Image"
4157 Initialize shaded image attributes.
4159 assert(image != (const Image *) NULL);
4160 assert(image->signature == MagickSignature);
4161 if (image->debug != MagickFalse)
4162 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4163 assert(exception != (ExceptionInfo *) NULL);
4164 assert(exception->signature == MagickSignature);
4165 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4166 if (shade_image == (Image *) NULL)
4167 return((Image *) NULL);
4168 if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
4170 InheritException(exception,&shade_image->exception);
4171 shade_image=DestroyImage(shade_image);
4172 return((Image *) NULL);
4175 Compute the light vector.
4177 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
4178 cos(DegreesToRadians(elevation));
4179 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
4180 cos(DegreesToRadians(elevation));
4181 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
4187 image_view=AcquireCacheView(image);
4188 shade_view=AcquireCacheView(shade_image);
4189 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4190 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4192 for (y=0; y < (long) image->rows; y++)
4202 register const PixelPacket
4211 register PixelPacket
4214 if (status == MagickFalse)
4216 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
4217 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
4219 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4225 Shade this row of pixels.
4227 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
4229 s1=s0+image->columns+2;
4230 s2=s1+image->columns+2;
4231 for (x=0; x < (long) image->columns; x++)
4234 Determine the surface normal and compute shading.
4236 normal.x=(double) (PixelIntensity(s0-1)+PixelIntensity(s1-1)+
4237 PixelIntensity(s2-1)-PixelIntensity(s0+1)-PixelIntensity(s1+1)-
4238 PixelIntensity(s2+1));
4239 normal.y=(double) (PixelIntensity(s2-1)+PixelIntensity(s2)+
4240 PixelIntensity(s2+1)-PixelIntensity(s0-1)-PixelIntensity(s0)-
4241 PixelIntensity(s0+1));
4242 if ((normal.x == 0.0) && (normal.y == 0.0))
4247 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
4248 if (distance > MagickEpsilon)
4251 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
4252 if (normal_distance > (MagickEpsilon*MagickEpsilon))
4253 shade=distance/sqrt((double) normal_distance);
4256 if (gray != MagickFalse)
4258 q->red=(Quantum) shade;
4259 q->green=(Quantum) shade;
4260 q->blue=(Quantum) shade;
4264 q->red=RoundToQuantum(QuantumScale*shade*s1->red);
4265 q->green=RoundToQuantum(QuantumScale*shade*s1->green);
4266 q->blue=RoundToQuantum(QuantumScale*shade*s1->blue);
4268 q->opacity=s1->opacity;
4274 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
4276 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4281 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4282 #pragma omp critical (MagickCore_ShadeImage)
4284 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
4285 if (proceed == MagickFalse)
4289 shade_view=DestroyCacheView(shade_view);
4290 image_view=DestroyCacheView(image_view);
4291 if (status == MagickFalse)
4292 shade_image=DestroyImage(shade_image);
4293 return(shade_image);
4297 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4301 % S h a r p e n I m a g e %
4305 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4307 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
4308 % operator of the given radius and standard deviation (sigma). For
4309 % reasonable results, radius should be larger than sigma. Use a radius of 0
4310 % and SharpenImage() selects a suitable radius for you.
4312 % Using a separable kernel would be faster, but the negative weights cancel
4313 % out on the corners of the kernel producing often undesirable ringing in the
4314 % filtered result; this can be avoided by using a 2D gaussian shaped image
4315 % sharpening kernel instead.
4317 % The format of the SharpenImage method is:
4319 % Image *SharpenImage(const Image *image,const double radius,
4320 % const double sigma,ExceptionInfo *exception)
4321 % Image *SharpenImageChannel(const Image *image,const ChannelType channel,
4322 % const double radius,const double sigma,ExceptionInfo *exception)
4324 % A description of each parameter follows:
4326 % o image: the image.
4328 % o channel: the channel type.
4330 % o radius: the radius of the Gaussian, in pixels, not counting the center
4333 % o sigma: the standard deviation of the Laplacian, in pixels.
4335 % o exception: return any errors or warnings in this structure.
4339 MagickExport Image *SharpenImage(const Image *image,const double radius,
4340 const double sigma,ExceptionInfo *exception)
4345 sharp_image=SharpenImageChannel(image,DefaultChannels,radius,sigma,exception);
4346 return(sharp_image);
4349 MagickExport Image *SharpenImageChannel(const Image *image,
4350 const ChannelType channel,const double radius,const double sigma,
4351 ExceptionInfo *exception)
4371 assert(image != (const Image *) NULL);
4372 assert(image->signature == MagickSignature);
4373 if (image->debug != MagickFalse)
4374 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4375 assert(exception != (ExceptionInfo *) NULL);
4376 assert(exception->signature == MagickSignature);
4377 width=GetOptimalKernelWidth2D(radius,sigma);
4378 kernel=(double *) AcquireQuantumMemory((size_t) width*width,sizeof(*kernel));
4379 if (kernel == (double *) NULL)
4380 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4383 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
4385 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
4387 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
4388 kernel[i]=(double) (-alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
4389 normalize+=kernel[i];
4393 kernel[i/2]=(double) ((-2.0)*normalize);
4394 sharp_image=ConvolveImageChannel(image,channel,width,kernel,exception);
4395 kernel=(double *) RelinquishMagickMemory(kernel);
4396 return(sharp_image);
4400 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4404 % S p r e a d I m a g e %
4408 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4410 % SpreadImage() is a special effects method that randomly displaces each
4411 % pixel in a block defined by the radius parameter.
4413 % The format of the SpreadImage method is:
4415 % Image *SpreadImage(const Image *image,const double radius,
4416 % ExceptionInfo *exception)
4418 % A description of each parameter follows:
4420 % o image: the image.
4422 % o radius: Choose a random pixel in a neighborhood of this extent.
4424 % o exception: return any errors or warnings in this structure.
4427 MagickExport Image *SpreadImage(const Image *image,const double radius,
4428 ExceptionInfo *exception)
4430 #define SpreadImageTag "Spread/Image"
4458 Initialize spread image attributes.
4460 assert(image != (Image *) NULL);
4461 assert(image->signature == MagickSignature);
4462 if (image->debug != MagickFalse)
4463 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4464 assert(exception != (ExceptionInfo *) NULL);
4465 assert(exception->signature == MagickSignature);
4466 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4468 if (spread_image == (Image *) NULL)
4469 return((Image *) NULL);
4470 if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
4472 InheritException(exception,&spread_image->exception);
4473 spread_image=DestroyImage(spread_image);
4474 return((Image *) NULL);
4481 GetMagickPixelPacket(spread_image,&bias);
4482 width=GetOptimalKernelWidth1D(radius,0.5);
4483 resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
4484 random_info=AcquireRandomInfoThreadSet();
4485 image_view=AcquireCacheView(spread_image);
4486 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4487 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4489 for (y=0; y < (long) spread_image->rows; y++)
4494 register IndexPacket
4501 register PixelPacket
4504 if (status == MagickFalse)
4506 q=QueueCacheViewAuthenticPixels(image_view,0,y,spread_image->columns,1,
4508 if (q == (PixelPacket *) NULL)
4513 indexes=GetCacheViewAuthenticIndexQueue(image_view);
4515 id=GetOpenMPThreadId();
4516 for (x=0; x < (long) spread_image->columns; x++)
4518 (void) ResamplePixelColor(resample_filter[id],(double) x+width*
4519 (GetPseudoRandomValue(random_info[id])-0.5),(double) y+width*
4520 (GetPseudoRandomValue(random_info[id])-0.5),&pixel);
4521 SetPixelPacket(spread_image,&pixel,q,indexes+x);
4524 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4526 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4531 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4532 #pragma omp critical (MagickCore_SpreadImage)
4534 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
4535 if (proceed == MagickFalse)
4539 image_view=DestroyCacheView(image_view);
4540 random_info=DestroyRandomInfoThreadSet(random_info);
4541 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
4542 return(spread_image);
4546 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4550 % U n s h a r p M a s k I m a g e %
4554 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4556 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
4557 % image with a Gaussian operator of the given radius and standard deviation
4558 % (sigma). For reasonable results, radius should be larger than sigma. Use a
4559 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
4561 % The format of the UnsharpMaskImage method is:
4563 % Image *UnsharpMaskImage(const Image *image,const double radius,
4564 % const double sigma,const double amount,const double threshold,
4565 % ExceptionInfo *exception)
4566 % Image *UnsharpMaskImageChannel(const Image *image,
4567 % const ChannelType channel,const double radius,const double sigma,
4568 % const double amount,const double threshold,ExceptionInfo *exception)
4570 % A description of each parameter follows:
4572 % o image: the image.
4574 % o channel: the channel type.
4576 % o radius: the radius of the Gaussian, in pixels, not counting the center
4579 % o sigma: the standard deviation of the Gaussian, in pixels.
4581 % o amount: the percentage of the difference between the original and the
4582 % blur image that is added back into the original.
4584 % o threshold: the threshold in pixels needed to apply the diffence amount.
4586 % o exception: return any errors or warnings in this structure.
4590 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
4591 const double sigma,const double amount,const double threshold,
4592 ExceptionInfo *exception)
4597 sharp_image=UnsharpMaskImageChannel(image,DefaultChannels,radius,sigma,amount,
4598 threshold,exception);
4599 return(sharp_image);
4602 MagickExport Image *UnsharpMaskImageChannel(const Image *image,
4603 const ChannelType channel,const double radius,const double sigma,
4604 const double amount,const double threshold,ExceptionInfo *exception)
4606 #define SharpenImageTag "Sharpen/Image"
4628 assert(image != (const Image *) NULL);
4629 assert(image->signature == MagickSignature);
4630 if (image->debug != MagickFalse)
4631 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4632 assert(exception != (ExceptionInfo *) NULL);
4633 unsharp_image=BlurImageChannel(image,channel,radius,sigma,exception);
4634 if (unsharp_image == (Image *) NULL)
4635 return((Image *) NULL);
4636 quantum_threshold=(MagickRealType) QuantumRange*threshold;
4642 GetMagickPixelPacket(image,&bias);
4643 image_view=AcquireCacheView(image);
4644 unsharp_view=AcquireCacheView(unsharp_image);
4645 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4646 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4648 for (y=0; y < (long) image->rows; y++)
4653 register const IndexPacket
4656 register const PixelPacket
4659 register IndexPacket
4660 *restrict unsharp_indexes;
4665 register PixelPacket
4668 if (status == MagickFalse)
4670 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4671 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
4673 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4678 indexes=GetCacheViewVirtualIndexQueue(image_view);
4679 unsharp_indexes=GetCacheViewAuthenticIndexQueue(unsharp_view);
4681 for (x=0; x < (long) image->columns; x++)
4683 if ((channel & RedChannel) != 0)
4685 pixel.red=p->red-(MagickRealType) q->red;
4686 if (fabs(2.0*pixel.red) < quantum_threshold)
4687 pixel.red=(MagickRealType) p->red;
4689 pixel.red=(MagickRealType) p->red+(pixel.red*amount);
4690 q->red=RoundToQuantum(pixel.red);
4692 if ((channel & GreenChannel) != 0)
4694 pixel.green=p->green-(MagickRealType) q->green;
4695 if (fabs(2.0*pixel.green) < quantum_threshold)
4696 pixel.green=(MagickRealType) p->green;
4698 pixel.green=(MagickRealType) p->green+(pixel.green*amount);
4699 q->green=RoundToQuantum(pixel.green);
4701 if ((channel & BlueChannel) != 0)
4703 pixel.blue=p->blue-(MagickRealType) q->blue;
4704 if (fabs(2.0*pixel.blue) < quantum_threshold)
4705 pixel.blue=(MagickRealType) p->blue;
4707 pixel.blue=(MagickRealType) p->blue+(pixel.blue*amount);
4708 q->blue=RoundToQuantum(pixel.blue);
4710 if ((channel & OpacityChannel) != 0)
4712 pixel.opacity=p->opacity-(MagickRealType) q->opacity;
4713 if (fabs(2.0*pixel.opacity) < quantum_threshold)
4714 pixel.opacity=(MagickRealType) p->opacity;
4716 pixel.opacity=p->opacity+(pixel.opacity*amount);
4717 q->opacity=RoundToQuantum(pixel.opacity);
4719 if (((channel & IndexChannel) != 0) &&
4720 (image->colorspace == CMYKColorspace))
4722 pixel.index=unsharp_indexes[x]-(MagickRealType) indexes[x];
4723 if (fabs(2.0*pixel.index) < quantum_threshold)
4724 pixel.index=(MagickRealType) unsharp_indexes[x];
4726 pixel.index=(MagickRealType) unsharp_indexes[x]+(pixel.index*
4728 unsharp_indexes[x]=RoundToQuantum(pixel.index);
4733 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4735 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4740 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4741 #pragma omp critical (MagickCore_UnsharpMaskImageChannel)
4743 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
4744 if (proceed == MagickFalse)
4748 unsharp_image->type=image->type;
4749 unsharp_view=DestroyCacheView(unsharp_view);
4750 image_view=DestroyCacheView(image_view);
4751 if (status == MagickFalse)
4752 unsharp_image=DestroyImage(unsharp_image);
4753 return(unsharp_image);