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 % C o n v o l v e I m a g e %
1273 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1275 % ConvolveImage() applies a custom convolution kernel to the image.
1277 % The format of the ConvolveImage method is:
1279 % Image *ConvolveImage(const Image *image,const unsigned long order,
1280 % const double *kernel,ExceptionInfo *exception)
1281 % Image *ConvolveImageChannel(const Image *image,const ChannelType channel,
1282 % const unsigned long order,const double *kernel,
1283 % ExceptionInfo *exception)
1285 % A description of each parameter follows:
1287 % o image: the image.
1289 % o channel: the channel type.
1291 % o order: the number of columns and rows in the filter kernel.
1293 % o kernel: An array of double representing the convolution kernel.
1295 % o exception: return any errors or warnings in this structure.
1299 MagickExport Image *ConvolveImage(const Image *image,const unsigned long order,
1300 const double *kernel,ExceptionInfo *exception)
1305 convolve_image=ConvolveImageChannel(image,DefaultChannels,order,kernel,
1307 return(convolve_image);
1310 MagickExport Image *ConvolveImageChannel(const Image *image,
1311 const ChannelType channel,const unsigned long order,const double *kernel,
1312 ExceptionInfo *exception)
1314 #define ConvolveImageTag "Convolve/Image"
1346 Initialize convolve image attributes.
1348 assert(image != (Image *) NULL);
1349 assert(image->signature == MagickSignature);
1350 if (image->debug != MagickFalse)
1351 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1352 assert(exception != (ExceptionInfo *) NULL);
1353 assert(exception->signature == MagickSignature);
1355 if ((width % 2) == 0)
1356 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
1357 convolve_image=CloneImage(image,0,0,MagickTrue,exception);
1358 if (convolve_image == (Image *) NULL)
1359 return((Image *) NULL);
1360 if (SetImageStorageClass(convolve_image,DirectClass) == MagickFalse)
1362 InheritException(exception,&convolve_image->exception);
1363 convolve_image=DestroyImage(convolve_image);
1364 return((Image *) NULL);
1366 if (image->debug != MagickFalse)
1369 format[MaxTextExtent],
1376 register const double
1379 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1380 " ConvolveImage with %ldx%ld kernel:",width,width);
1381 message=AcquireString("");
1383 for (v=0; v < (long) width; v++)
1386 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
1387 (void) ConcatenateString(&message,format);
1388 for (u=0; u < (long) width; u++)
1390 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
1391 (void) ConcatenateString(&message,format);
1393 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
1395 message=DestroyString(message);
1400 normal_kernel=(double *) AcquireQuantumMemory(width*width,
1401 sizeof(*normal_kernel));
1402 if (normal_kernel == (double *) NULL)
1404 convolve_image=DestroyImage(convolve_image);
1405 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1408 for (i=0; i < (long) (width*width); i++)
1410 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1411 for (i=0; i < (long) (width*width); i++)
1412 normal_kernel[i]=gamma*kernel[i];
1418 GetMagickPixelPacket(image,&bias);
1419 SetMagickPixelPacketBias(image,&bias);
1420 image_view=AcquireCacheView(image);
1421 convolve_view=AcquireCacheView(convolve_image);
1422 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1423 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1425 for (y=0; y < (long) image->rows; y++)
1430 register const IndexPacket
1433 register const PixelPacket
1436 register IndexPacket
1437 *restrict convolve_indexes;
1442 register PixelPacket
1445 if (status == MagickFalse)
1447 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
1448 2L),image->columns+width,width,exception);
1449 q=GetCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
1451 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1456 indexes=GetCacheViewVirtualIndexQueue(image_view);
1457 convolve_indexes=GetCacheViewAuthenticIndexQueue(convolve_view);
1458 for (x=0; x < (long) image->columns; x++)
1466 register const double
1469 register const PixelPacket
1470 *restrict kernel_pixels;
1478 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
1480 for (v=0; v < (long) width; v++)
1482 for (u=0; u < (long) width; u++)
1484 pixel.red+=(*k)*kernel_pixels[u].red;
1485 pixel.green+=(*k)*kernel_pixels[u].green;
1486 pixel.blue+=(*k)*kernel_pixels[u].blue;
1489 kernel_pixels+=image->columns+width;
1491 if ((channel & RedChannel) != 0)
1492 q->red=RoundToQuantum(pixel.red);
1493 if ((channel & GreenChannel) != 0)
1494 q->green=RoundToQuantum(pixel.green);
1495 if ((channel & BlueChannel) != 0)
1496 q->blue=RoundToQuantum(pixel.blue);
1497 if ((channel & OpacityChannel) != 0)
1501 for (v=0; v < (long) width; v++)
1503 for (u=0; u < (long) width; u++)
1505 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1508 kernel_pixels+=image->columns+width;
1510 q->opacity=RoundToQuantum(pixel.opacity);
1512 if (((channel & IndexChannel) != 0) &&
1513 (image->colorspace == CMYKColorspace))
1515 register const IndexPacket
1516 *restrict kernel_indexes;
1519 kernel_indexes=indexes;
1520 for (v=0; v < (long) width; v++)
1522 for (u=0; u < (long) width; u++)
1524 pixel.index+=(*k)*kernel_indexes[u];
1527 kernel_indexes+=image->columns+width;
1529 convolve_indexes[x]=RoundToQuantum(pixel.index);
1539 for (v=0; v < (long) width; v++)
1541 for (u=0; u < (long) width; u++)
1543 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1544 kernel_pixels[u].opacity));
1545 pixel.red+=(*k)*alpha*kernel_pixels[u].red;
1546 pixel.green+=(*k)*alpha*kernel_pixels[u].green;
1547 pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
1548 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1552 kernel_pixels+=image->columns+width;
1554 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1555 if ((channel & RedChannel) != 0)
1556 q->red=RoundToQuantum(gamma*pixel.red);
1557 if ((channel & GreenChannel) != 0)
1558 q->green=RoundToQuantum(gamma*pixel.green);
1559 if ((channel & BlueChannel) != 0)
1560 q->blue=RoundToQuantum(gamma*pixel.blue);
1561 if ((channel & OpacityChannel) != 0)
1565 for (v=0; v < (long) width; v++)
1567 for (u=0; u < (long) width; u++)
1569 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1572 kernel_pixels+=image->columns+width;
1574 q->opacity=RoundToQuantum(pixel.opacity);
1576 if (((channel & IndexChannel) != 0) &&
1577 (image->colorspace == CMYKColorspace))
1579 register const IndexPacket
1580 *restrict kernel_indexes;
1584 kernel_indexes=indexes;
1585 for (v=0; v < (long) width; v++)
1587 for (u=0; u < (long) width; u++)
1589 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1590 kernel_pixels[u].opacity));
1591 pixel.index+=(*k)*alpha*kernel_indexes[u];
1594 kernel_pixels+=image->columns+width;
1595 kernel_indexes+=image->columns+width;
1597 convolve_indexes[x]=RoundToQuantum(gamma*pixel.index);
1603 sync=SyncCacheViewAuthenticPixels(convolve_view,exception);
1604 if (sync == MagickFalse)
1606 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1611 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1612 #pragma omp critical (MagickCore_ConvolveImageChannel)
1614 proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1615 if (proceed == MagickFalse)
1619 convolve_image->type=image->type;
1620 convolve_view=DestroyCacheView(convolve_view);
1621 image_view=DestroyCacheView(image_view);
1622 normal_kernel=(double *) RelinquishMagickMemory(normal_kernel);
1623 if (status == MagickFalse)
1624 convolve_image=DestroyImage(convolve_image);
1625 return(convolve_image);
1629 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1633 % D e s p e c k l e I m a g e %
1637 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1639 % DespeckleImage() reduces the speckle noise in an image while perserving the
1640 % edges of the original image.
1642 % The format of the DespeckleImage method is:
1644 % Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1646 % A description of each parameter follows:
1648 % o image: the image.
1650 % o exception: return any errors or warnings in this structure.
1654 static Quantum **DestroyPixelThreadSet(Quantum **pixels)
1659 assert(pixels != (Quantum **) NULL);
1660 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
1661 if (pixels[i] != (Quantum *) NULL)
1662 pixels[i]=(Quantum *) RelinquishMagickMemory(pixels[i]);
1663 pixels=(Quantum **) RelinquishAlignedMemory(pixels);
1667 static Quantum **AcquirePixelThreadSet(const size_t count)
1678 number_threads=GetOpenMPMaximumThreads();
1679 pixels=(Quantum **) AcquireAlignedMemory(number_threads,sizeof(*pixels));
1680 if (pixels == (Quantum **) NULL)
1681 return((Quantum **) NULL);
1682 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
1683 for (i=0; i < (long) number_threads; i++)
1685 pixels[i]=(Quantum *) AcquireQuantumMemory(count,sizeof(**pixels));
1686 if (pixels[i] == (Quantum *) NULL)
1687 return(DestroyPixelThreadSet(pixels));
1692 static void Hull(const long x_offset,const long y_offset,
1693 const unsigned long columns,const unsigned long rows,Quantum *f,Quantum *g,
1711 assert(f != (Quantum *) NULL);
1712 assert(g != (Quantum *) NULL);
1715 r=p+(y_offset*((long) columns+2)+x_offset);
1716 for (y=0; y < (long) rows; y++)
1722 for (x=(long) columns; x != 0; x--)
1724 v=(MagickRealType) (*p);
1725 if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1726 v+=ScaleCharToQuantum(1);
1733 for (x=(long) columns; x != 0; x--)
1735 v=(MagickRealType) (*p);
1736 if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
1737 v-=(long) ScaleCharToQuantum(1);
1749 r=q+(y_offset*((long) columns+2)+x_offset);
1750 s=q-(y_offset*((long) columns+2)+x_offset);
1751 for (y=0; y < (long) rows; y++)
1758 for (x=(long) columns; x != 0; x--)
1760 v=(MagickRealType) (*q);
1761 if (((MagickRealType) *s >=
1762 (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1763 ((MagickRealType) *r > v))
1764 v+=ScaleCharToQuantum(1);
1772 for (x=(long) columns; x != 0; x--)
1774 v=(MagickRealType) (*q);
1775 if (((MagickRealType) *s <=
1776 (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1777 ((MagickRealType) *r < v))
1778 v-=(MagickRealType) ScaleCharToQuantum(1);
1792 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1794 #define DespeckleImageTag "Despeckle/Image"
1817 X[4] = {0, 1, 1,-1},
1818 Y[4] = {1, 0, 1, 1};
1821 Allocate despeckled image.
1823 assert(image != (const Image *) NULL);
1824 assert(image->signature == MagickSignature);
1825 if (image->debug != MagickFalse)
1826 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1827 assert(exception != (ExceptionInfo *) NULL);
1828 assert(exception->signature == MagickSignature);
1829 despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1831 if (despeckle_image == (Image *) NULL)
1832 return((Image *) NULL);
1833 if (SetImageStorageClass(despeckle_image,DirectClass) == MagickFalse)
1835 InheritException(exception,&despeckle_image->exception);
1836 despeckle_image=DestroyImage(despeckle_image);
1837 return((Image *) NULL);
1840 Allocate image buffers.
1842 length=(size_t) ((image->columns+2)*(image->rows+2));
1843 pixels=AcquirePixelThreadSet(length);
1844 buffers=AcquirePixelThreadSet(length);
1845 if ((pixels == (Quantum **) NULL) || (buffers == (Quantum **) NULL))
1847 if (buffers != (Quantum **) NULL)
1848 buffers=DestroyPixelThreadSet(buffers);
1849 if (pixels != (Quantum **) NULL)
1850 pixels=DestroyPixelThreadSet(pixels);
1851 despeckle_image=DestroyImage(despeckle_image);
1852 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1855 Reduce speckle in the image.
1858 image_view=AcquireCacheView(image);
1859 despeckle_view=AcquireCacheView(despeckle_image);
1860 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1861 #pragma omp parallel for schedule(dynamic,4) shared(status)
1863 for (channel=0; channel <= 3; channel++)
1878 if (status == MagickFalse)
1880 id=GetOpenMPThreadId();
1882 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
1884 j=(long) image->columns+2;
1885 for (y=0; y < (long) image->rows; y++)
1887 register const PixelPacket
1890 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1891 if (p == (const PixelPacket *) NULL)
1894 for (x=0; x < (long) image->columns; x++)
1898 case 0: pixel[j]=p->red; break;
1899 case 1: pixel[j]=p->green; break;
1900 case 2: pixel[j]=p->blue; break;
1901 case 3: pixel[j]=p->opacity; break;
1909 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1910 for (i=0; i < 4; i++)
1912 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,1);
1913 Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,1);
1914 Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,-1);
1915 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,-1);
1917 j=(long) image->columns+2;
1918 for (y=0; y < (long) image->rows; y++)
1923 register PixelPacket
1926 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1928 if (q == (PixelPacket *) NULL)
1931 for (x=0; x < (long) image->columns; x++)
1935 case 0: q->red=pixel[j]; break;
1936 case 1: q->green=pixel[j]; break;
1937 case 2: q->blue=pixel[j]; break;
1938 case 3: q->opacity=pixel[j]; break;
1944 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1945 if (sync == MagickFalse)
1952 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1957 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1958 #pragma omp critical (MagickCore_DespeckleImage)
1960 proceed=SetImageProgress(image,DespeckleImageTag,channel,3);
1961 if (proceed == MagickFalse)
1965 despeckle_view=DestroyCacheView(despeckle_view);
1966 image_view=DestroyCacheView(image_view);
1967 buffers=DestroyPixelThreadSet(buffers);
1968 pixels=DestroyPixelThreadSet(pixels);
1969 despeckle_image->type=image->type;
1970 if (status == MagickFalse)
1971 despeckle_image=DestroyImage(despeckle_image);
1972 return(despeckle_image);
1976 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1980 % E d g e I m a g e %
1984 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1986 % EdgeImage() finds edges in an image. Radius defines the radius of the
1987 % convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1990 % The format of the EdgeImage method is:
1992 % Image *EdgeImage(const Image *image,const double radius,
1993 % ExceptionInfo *exception)
1995 % A description of each parameter follows:
1997 % o image: the image.
1999 % o radius: the radius of the pixel neighborhood.
2001 % o exception: return any errors or warnings in this structure.
2004 MagickExport Image *EdgeImage(const Image *image,const double radius,
2005 ExceptionInfo *exception)
2019 assert(image != (const Image *) NULL);
2020 assert(image->signature == MagickSignature);
2021 if (image->debug != MagickFalse)
2022 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2023 assert(exception != (ExceptionInfo *) NULL);
2024 assert(exception->signature == MagickSignature);
2025 width=GetOptimalKernelWidth1D(radius,0.5);
2026 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2027 if (kernel == (double *) NULL)
2028 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2029 for (i=0; i < (long) (width*width); i++)
2031 kernel[i/2]=(double) (width*width-1.0);
2032 edge_image=ConvolveImage(image,width,kernel,exception);
2033 kernel=(double *) RelinquishMagickMemory(kernel);
2038 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2042 % E m b o s s I m a g e %
2046 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2048 % EmbossImage() returns a grayscale image with a three-dimensional effect.
2049 % We convolve the image with a Gaussian operator of the given radius and
2050 % standard deviation (sigma). For reasonable results, radius should be
2051 % larger than sigma. Use a radius of 0 and Emboss() selects a suitable
2054 % The format of the EmbossImage method is:
2056 % Image *EmbossImage(const Image *image,const double radius,
2057 % const double sigma,ExceptionInfo *exception)
2059 % A description of each parameter follows:
2061 % o image: the image.
2063 % o radius: the radius of the pixel neighborhood.
2065 % o sigma: the standard deviation of the Gaussian, in pixels.
2067 % o exception: return any errors or warnings in this structure.
2070 MagickExport Image *EmbossImage(const Image *image,const double radius,
2071 const double sigma,ExceptionInfo *exception)
2093 assert(image != (Image *) NULL);
2094 assert(image->signature == MagickSignature);
2095 if (image->debug != MagickFalse)
2096 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2097 assert(exception != (ExceptionInfo *) NULL);
2098 assert(exception->signature == MagickSignature);
2099 width=GetOptimalKernelWidth2D(radius,sigma);
2100 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2101 if (kernel == (double *) NULL)
2102 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2105 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
2107 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
2109 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
2110 kernel[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*alpha/
2111 (2.0*MagickPI*MagickSigma*MagickSigma));
2118 emboss_image=ConvolveImage(image,width,kernel,exception);
2119 if (emboss_image != (Image *) NULL)
2120 (void) EqualizeImage(emboss_image);
2121 kernel=(double *) RelinquishMagickMemory(kernel);
2122 return(emboss_image);
2126 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2130 % G a u s s i a n B l u r I m a g e %
2134 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2136 % GaussianBlurImage() blurs an image. We convolve the image with a
2137 % Gaussian operator of the given radius and standard deviation (sigma).
2138 % For reasonable results, the radius should be larger than sigma. Use a
2139 % radius of 0 and GaussianBlurImage() selects a suitable radius for you
2141 % The format of the GaussianBlurImage method is:
2143 % Image *GaussianBlurImage(const Image *image,onst double radius,
2144 % const double sigma,ExceptionInfo *exception)
2145 % Image *GaussianBlurImageChannel(const Image *image,
2146 % const ChannelType channel,const double radius,const double sigma,
2147 % ExceptionInfo *exception)
2149 % A description of each parameter follows:
2151 % o image: the image.
2153 % o channel: the channel type.
2155 % o radius: the radius of the Gaussian, in pixels, not counting the center
2158 % o sigma: the standard deviation of the Gaussian, in pixels.
2160 % o exception: return any errors or warnings in this structure.
2164 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
2165 const double sigma,ExceptionInfo *exception)
2170 blur_image=GaussianBlurImageChannel(image,DefaultChannels,radius,sigma,
2175 MagickExport Image *GaussianBlurImageChannel(const Image *image,
2176 const ChannelType channel,const double radius,const double sigma,
2177 ExceptionInfo *exception)
2196 assert(image != (const Image *) NULL);
2197 assert(image->signature == MagickSignature);
2198 if (image->debug != MagickFalse)
2199 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2200 assert(exception != (ExceptionInfo *) NULL);
2201 assert(exception->signature == MagickSignature);
2202 width=GetOptimalKernelWidth2D(radius,sigma);
2203 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2204 if (kernel == (double *) NULL)
2205 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2207 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
2209 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
2211 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
2212 kernel[i]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
2216 blur_image=ConvolveImageChannel(image,channel,width,kernel,exception);
2217 kernel=(double *) RelinquishMagickMemory(kernel);
2222 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2226 % M e d i a n F i l t e r I m a g e %
2230 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2232 % MedianFilterImage() applies a digital filter that improves the quality
2233 % of a noisy image. Each pixel is replaced by the median in a set of
2234 % neighboring pixels as defined by radius.
2236 % The algorithm was contributed by Mike Edmonds and implements an insertion
2237 % sort for selecting median color-channel values. For more on this algorithm
2238 % see "Skip Lists: A probabilistic Alternative to Balanced Trees" by William
2239 % Pugh in the June 1990 of Communications of the ACM.
2241 % The format of the MedianFilterImage method is:
2243 % Image *MedianFilterImage(const Image *image,const double radius,
2244 % ExceptionInfo *exception)
2246 % A description of each parameter follows:
2248 % o image: the image.
2250 % o radius: the radius of the pixel neighborhood.
2252 % o exception: return any errors or warnings in this structure.
2256 #define MedianListChannels 5
2258 typedef struct _MedianListNode
2266 typedef struct _MedianSkipList
2275 typedef struct _MedianPixelList
2283 lists[MedianListChannels];
2286 static MedianPixelList *DestroyMedianPixelList(MedianPixelList *pixel_list)
2291 if (pixel_list == (MedianPixelList *) NULL)
2292 return((MedianPixelList *) NULL);
2293 for (i=0; i < MedianListChannels; i++)
2294 if (pixel_list->lists[i].nodes != (MedianListNode *) NULL)
2295 pixel_list->lists[i].nodes=(MedianListNode *) RelinquishMagickMemory(
2296 pixel_list->lists[i].nodes);
2297 pixel_list=(MedianPixelList *) RelinquishAlignedMemory(pixel_list);
2301 static MedianPixelList **DestroyMedianPixelListThreadSet(
2302 MedianPixelList **pixel_list)
2307 assert(pixel_list != (MedianPixelList **) NULL);
2308 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
2309 if (pixel_list[i] != (MedianPixelList *) NULL)
2310 pixel_list[i]=DestroyMedianPixelList(pixel_list[i]);
2311 pixel_list=(MedianPixelList **) RelinquishAlignedMemory(pixel_list);
2315 static MedianPixelList *AcquireMedianPixelList(const unsigned long width)
2323 pixel_list=(MedianPixelList *) AcquireAlignedMemory(1,sizeof(*pixel_list));
2324 if (pixel_list == (MedianPixelList *) NULL)
2326 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
2327 pixel_list->center=width*width/2;
2328 for (i=0; i < MedianListChannels; i++)
2330 pixel_list->lists[i].nodes=(MedianListNode *) AcquireQuantumMemory(65537UL,
2331 sizeof(*pixel_list->lists[i].nodes));
2332 if (pixel_list->lists[i].nodes == (MedianListNode *) NULL)
2333 return(DestroyMedianPixelList(pixel_list));
2334 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
2335 sizeof(*pixel_list->lists[i].nodes));
2337 pixel_list->signature=MagickSignature;
2341 static MedianPixelList **AcquireMedianPixelListThreadSet(
2342 const unsigned long width)
2353 number_threads=GetOpenMPMaximumThreads();
2354 pixel_list=(MedianPixelList **) AcquireAlignedMemory(number_threads,
2355 sizeof(*pixel_list));
2356 if (pixel_list == (MedianPixelList **) NULL)
2357 return((MedianPixelList **) NULL);
2358 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
2359 for (i=0; i < (long) number_threads; i++)
2361 pixel_list[i]=AcquireMedianPixelList(width);
2362 if (pixel_list[i] == (MedianPixelList *) NULL)
2363 return(DestroyMedianPixelListThreadSet(pixel_list));
2368 static void AddNodeMedianPixelList(MedianPixelList *pixel_list,
2369 const long channel,const unsigned long color)
2374 register MedianSkipList
2382 Initialize the node.
2384 list=pixel_list->lists+channel;
2385 list->nodes[color].signature=pixel_list->signature;
2386 list->nodes[color].count=1;
2388 Determine where it belongs in the list.
2391 for (level=list->level; level >= 0; level--)
2393 while (list->nodes[search].next[level] < color)
2394 search=list->nodes[search].next[level];
2395 update[level]=search;
2398 Generate a pseudo-random level for this node.
2400 for (level=0; ; level++)
2402 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
2403 if ((pixel_list->seed & 0x300) != 0x300)
2408 if (level > (list->level+2))
2409 level=list->level+2;
2411 If we're raising the list's level, link back to the root node.
2413 while (level > list->level)
2416 update[list->level]=65536UL;
2419 Link the node into the skip-list.
2423 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
2424 list->nodes[update[level]].next[level]=color;
2426 while (level-- > 0);
2429 static MagickPixelPacket GetMedianPixelList(MedianPixelList *pixel_list)
2437 register MedianSkipList
2446 channels[MedianListChannels];
2449 Find the median value for each of the color.
2451 center=pixel_list->center;
2452 for (channel=0; channel < 5; channel++)
2454 list=pixel_list->lists+channel;
2459 color=list->nodes[color].next[0];
2460 count+=list->nodes[color].count;
2462 while (count <= center);
2463 channels[channel]=(unsigned short) color;
2465 GetMagickPixelPacket((const Image *) NULL,&pixel);
2466 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
2467 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
2468 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
2469 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
2470 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
2474 static inline void InsertMedianPixelList(const Image *image,
2475 const PixelPacket *pixel,const IndexPacket *indexes,
2476 MedianPixelList *pixel_list)
2484 index=ScaleQuantumToShort(pixel->red);
2485 signature=pixel_list->lists[0].nodes[index].signature;
2486 if (signature == pixel_list->signature)
2487 pixel_list->lists[0].nodes[index].count++;
2489 AddNodeMedianPixelList(pixel_list,0,index);
2490 index=ScaleQuantumToShort(pixel->green);
2491 signature=pixel_list->lists[1].nodes[index].signature;
2492 if (signature == pixel_list->signature)
2493 pixel_list->lists[1].nodes[index].count++;
2495 AddNodeMedianPixelList(pixel_list,1,index);
2496 index=ScaleQuantumToShort(pixel->blue);
2497 signature=pixel_list->lists[2].nodes[index].signature;
2498 if (signature == pixel_list->signature)
2499 pixel_list->lists[2].nodes[index].count++;
2501 AddNodeMedianPixelList(pixel_list,2,index);
2502 index=ScaleQuantumToShort(pixel->opacity);
2503 signature=pixel_list->lists[3].nodes[index].signature;
2504 if (signature == pixel_list->signature)
2505 pixel_list->lists[3].nodes[index].count++;
2507 AddNodeMedianPixelList(pixel_list,3,index);
2508 if (image->colorspace == CMYKColorspace)
2509 index=ScaleQuantumToShort(*indexes);
2510 signature=pixel_list->lists[4].nodes[index].signature;
2511 if (signature == pixel_list->signature)
2512 pixel_list->lists[4].nodes[index].count++;
2514 AddNodeMedianPixelList(pixel_list,4,index);
2517 static void ResetMedianPixelList(MedianPixelList *pixel_list)
2525 register MedianListNode
2528 register MedianSkipList
2532 Reset the skip-list.
2534 for (channel=0; channel < 5; channel++)
2536 list=pixel_list->lists+channel;
2537 root=list->nodes+65536UL;
2539 for (level=0; level < 9; level++)
2540 root->next[level]=65536UL;
2542 pixel_list->seed=pixel_list->signature++;
2545 MagickExport Image *MedianFilterImage(const Image *image,const double radius,
2546 ExceptionInfo *exception)
2548 #define MedianFilterImageTag "MedianFilter/Image"
2571 Initialize median image attributes.
2573 assert(image != (Image *) NULL);
2574 assert(image->signature == MagickSignature);
2575 if (image->debug != MagickFalse)
2576 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2577 assert(exception != (ExceptionInfo *) NULL);
2578 assert(exception->signature == MagickSignature);
2579 width=GetOptimalKernelWidth2D(radius,0.5);
2580 if ((image->columns < width) || (image->rows < width))
2581 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
2582 median_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2584 if (median_image == (Image *) NULL)
2585 return((Image *) NULL);
2586 if (SetImageStorageClass(median_image,DirectClass) == MagickFalse)
2588 InheritException(exception,&median_image->exception);
2589 median_image=DestroyImage(median_image);
2590 return((Image *) NULL);
2592 pixel_list=AcquireMedianPixelListThreadSet(width);
2593 if (pixel_list == (MedianPixelList **) NULL)
2595 median_image=DestroyImage(median_image);
2596 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2599 Median filter each image row.
2603 image_view=AcquireCacheView(image);
2604 median_view=AcquireCacheView(median_image);
2605 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2606 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2608 for (y=0; y < (long) median_image->rows; y++)
2610 register const IndexPacket
2613 register const PixelPacket
2616 register IndexPacket
2617 *restrict median_indexes;
2623 register PixelPacket
2626 if (status == MagickFalse)
2628 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
2629 2L),image->columns+width,width,exception);
2630 q=QueueCacheViewAuthenticPixels(median_view,0,y,median_image->columns,1,
2632 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2637 indexes=GetCacheViewVirtualIndexQueue(image_view);
2638 median_indexes=GetCacheViewAuthenticIndexQueue(median_view);
2639 id=GetOpenMPThreadId();
2640 for (x=0; x < (long) median_image->columns; x++)
2645 register const PixelPacket
2648 register const IndexPacket
2657 ResetMedianPixelList(pixel_list[id]);
2658 for (v=0; v < (long) width; v++)
2660 for (u=0; u < (long) width; u++)
2661 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
2662 r+=image->columns+width;
2663 s+=image->columns+width;
2665 pixel=GetMedianPixelList(pixel_list[id]);
2666 SetPixelPacket(median_image,&pixel,q,median_indexes+x);
2670 if (SyncCacheViewAuthenticPixels(median_view,exception) == MagickFalse)
2672 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2677 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2678 #pragma omp critical (MagickCore_MedianFilterImage)
2680 proceed=SetImageProgress(image,MedianFilterImageTag,progress++,
2682 if (proceed == MagickFalse)
2686 median_view=DestroyCacheView(median_view);
2687 image_view=DestroyCacheView(image_view);
2688 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
2689 return(median_image);
2693 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2697 % M o t i o n B l u r I m a g e %
2701 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2703 % MotionBlurImage() simulates motion blur. We convolve the image with a
2704 % Gaussian operator of the given radius and standard deviation (sigma).
2705 % For reasonable results, radius should be larger than sigma. Use a
2706 % radius of 0 and MotionBlurImage() selects a suitable radius for you.
2707 % Angle gives the angle of the blurring motion.
2709 % Andrew Protano contributed this effect.
2711 % The format of the MotionBlurImage method is:
2713 % Image *MotionBlurImage(const Image *image,const double radius,
2714 % const double sigma,const double angle,ExceptionInfo *exception)
2715 % Image *MotionBlurImageChannel(const Image *image,const ChannelType channel,
2716 % const double radius,const double sigma,const double angle,
2717 % ExceptionInfo *exception)
2719 % A description of each parameter follows:
2721 % o image: the image.
2723 % o channel: the channel type.
2725 % o radius: the radius of the Gaussian, in pixels, not counting the center
2726 % o radius: the radius of the Gaussian, in pixels, not counting
2729 % o sigma: the standard deviation of the Gaussian, in pixels.
2731 % o angle: Apply the effect along this angle.
2733 % o exception: return any errors or warnings in this structure.
2737 static double *GetMotionBlurKernel(unsigned long width,
2738 const MagickRealType sigma)
2740 #define KernelRank 3
2756 Generate a 1-D convolution kernel.
2758 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2759 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
2760 if (kernel == (double *) NULL)
2762 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
2763 bias=(long) (KernelRank*width);
2764 for (i=0; i < (long) bias; i++)
2766 alpha=exp((-((double) (i*i))/(double) (2.0*KernelRank*KernelRank*
2767 MagickSigma*MagickSigma)));
2768 kernel[i/KernelRank]+=(double) alpha/(MagickSQ2PI*sigma);
2771 for (i=0; i < (long) width; i++)
2772 normalize+=kernel[i];
2773 for (i=0; i < (long) width; i++)
2774 kernel[i]/=normalize;
2778 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
2779 const double sigma,const double angle,ExceptionInfo *exception)
2784 motion_blur=MotionBlurImageChannel(image,DefaultChannels,radius,sigma,angle,
2786 return(motion_blur);
2789 MagickExport Image *MotionBlurImageChannel(const Image *image,
2790 const ChannelType channel,const double radius,const double sigma,
2791 const double angle,ExceptionInfo *exception)
2793 typedef struct _OffsetInfo
2832 assert(image != (Image *) NULL);
2833 assert(image->signature == MagickSignature);
2834 if (image->debug != MagickFalse)
2835 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2836 assert(exception != (ExceptionInfo *) NULL);
2837 width=GetOptimalKernelWidth1D(radius,sigma);
2838 kernel=GetMotionBlurKernel(width,sigma);
2839 if (kernel == (double *) NULL)
2840 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2841 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2842 if (offset == (OffsetInfo *) NULL)
2844 kernel=(double *) RelinquishMagickMemory(kernel);
2845 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2847 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2848 if (blur_image == (Image *) NULL)
2850 kernel=(double *) RelinquishMagickMemory(kernel);
2851 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2852 return((Image *) NULL);
2854 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
2856 kernel=(double *) RelinquishMagickMemory(kernel);
2857 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2858 InheritException(exception,&blur_image->exception);
2859 blur_image=DestroyImage(blur_image);
2860 return((Image *) NULL);
2862 point.x=(double) width*sin(DegreesToRadians(angle));
2863 point.y=(double) width*cos(DegreesToRadians(angle));
2864 for (i=0; i < (long) width; i++)
2866 offset[i].x=(long) ((i*point.y)/hypot(point.x,point.y)+0.5);
2867 offset[i].y=(long) ((i*point.x)/hypot(point.x,point.y)+0.5);
2874 GetMagickPixelPacket(image,&bias);
2875 image_view=AcquireCacheView(image);
2876 blur_view=AcquireCacheView(blur_image);
2877 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2878 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2880 for (y=0; y < (long) image->rows; y++)
2882 register IndexPacket
2883 *restrict blur_indexes;
2888 register PixelPacket
2891 if (status == MagickFalse)
2893 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2895 if (q == (PixelPacket *) NULL)
2900 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
2901 for (x=0; x < (long) image->columns; x++)
2915 register const IndexPacket
2920 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
2922 for (i=0; i < (long) width; i++)
2924 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2925 offset[i].y,&pixel,exception);
2926 qixel.red+=(*k)*pixel.red;
2927 qixel.green+=(*k)*pixel.green;
2928 qixel.blue+=(*k)*pixel.blue;
2929 qixel.opacity+=(*k)*pixel.opacity;
2930 if (image->colorspace == CMYKColorspace)
2932 indexes=GetCacheViewVirtualIndexQueue(image_view);
2933 qixel.index+=(*k)*(*indexes);
2937 if ((channel & RedChannel) != 0)
2938 q->red=RoundToQuantum(qixel.red);
2939 if ((channel & GreenChannel) != 0)
2940 q->green=RoundToQuantum(qixel.green);
2941 if ((channel & BlueChannel) != 0)
2942 q->blue=RoundToQuantum(qixel.blue);
2943 if ((channel & OpacityChannel) != 0)
2944 q->opacity=RoundToQuantum(qixel.opacity);
2945 if (((channel & IndexChannel) != 0) &&
2946 (image->colorspace == CMYKColorspace))
2947 blur_indexes[x]=(IndexPacket) RoundToQuantum(qixel.index);
2957 for (i=0; i < (long) width; i++)
2959 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2960 offset[i].y,&pixel,exception);
2961 alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
2962 qixel.red+=(*k)*alpha*pixel.red;
2963 qixel.green+=(*k)*alpha*pixel.green;
2964 qixel.blue+=(*k)*alpha*pixel.blue;
2965 qixel.opacity+=(*k)*pixel.opacity;
2966 if (image->colorspace == CMYKColorspace)
2968 indexes=GetCacheViewVirtualIndexQueue(image_view);
2969 qixel.index+=(*k)*alpha*(*indexes);
2974 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2975 if ((channel & RedChannel) != 0)
2976 q->red=RoundToQuantum(gamma*qixel.red);
2977 if ((channel & GreenChannel) != 0)
2978 q->green=RoundToQuantum(gamma*qixel.green);
2979 if ((channel & BlueChannel) != 0)
2980 q->blue=RoundToQuantum(gamma*qixel.blue);
2981 if ((channel & OpacityChannel) != 0)
2982 q->opacity=RoundToQuantum(qixel.opacity);
2983 if (((channel & IndexChannel) != 0) &&
2984 (image->colorspace == CMYKColorspace))
2985 blur_indexes[x]=(IndexPacket) RoundToQuantum(gamma*qixel.index);
2989 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2991 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2996 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2997 #pragma omp critical (MagickCore_MotionBlurImageChannel)
2999 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3000 if (proceed == MagickFalse)
3004 blur_view=DestroyCacheView(blur_view);
3005 image_view=DestroyCacheView(image_view);
3006 kernel=(double *) RelinquishMagickMemory(kernel);
3007 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3008 if (status == MagickFalse)
3009 blur_image=DestroyImage(blur_image);
3014 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3018 % P r e v i e w I m a g e %
3022 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3024 % PreviewImage() tiles 9 thumbnails of the specified image with an image
3025 % processing operation applied with varying parameters. This may be helpful
3026 % pin-pointing an appropriate parameter for a particular image processing
3029 % The format of the PreviewImages method is:
3031 % Image *PreviewImages(const Image *image,const PreviewType preview,
3032 % ExceptionInfo *exception)
3034 % A description of each parameter follows:
3036 % o image: the image.
3038 % o preview: the image processing operation.
3040 % o exception: return any errors or warnings in this structure.
3043 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
3044 ExceptionInfo *exception)
3046 #define NumberTiles 9
3047 #define PreviewImageTag "Preview/Image"
3048 #define DefaultPreviewGeometry "204x204+10+10"
3051 factor[MaxTextExtent],
3052 label[MaxTextExtent];
3094 Open output image file.
3096 assert(image != (Image *) NULL);
3097 assert(image->signature == MagickSignature);
3098 if (image->debug != MagickFalse)
3099 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3103 preview_info=AcquireImageInfo();
3104 SetGeometry(image,&geometry);
3105 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
3106 &geometry.width,&geometry.height);
3107 images=NewImageList();
3109 GetQuantizeInfo(&quantize_info);
3115 for (i=0; i < NumberTiles; i++)
3117 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
3118 if (thumbnail == (Image *) NULL)
3120 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
3122 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
3123 if (i == (NumberTiles/2))
3125 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
3126 AppendImageToList(&images,thumbnail);
3134 preview_image=RotateImage(thumbnail,degrees,exception);
3135 (void) FormatMagickString(label,MaxTextExtent,"rotate %g",degrees);
3141 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
3142 (void) FormatMagickString(label,MaxTextExtent,"shear %gx%g",
3143 degrees,2.0*degrees);
3148 x=(long) ((i+1)*thumbnail->columns)/NumberTiles;
3149 y=(long) ((i+1)*thumbnail->rows)/NumberTiles;
3150 preview_image=RollImage(thumbnail,x,y,exception);
3151 (void) FormatMagickString(label,MaxTextExtent,"roll %ldx%ld",x,y);
3156 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3157 if (preview_image == (Image *) NULL)
3159 (void) FormatMagickString(factor,MaxTextExtent,"100,100,%g",
3161 (void) ModulateImage(preview_image,factor);
3162 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3165 case SaturationPreview:
3167 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3168 if (preview_image == (Image *) NULL)
3170 (void) FormatMagickString(factor,MaxTextExtent,"100,%g",2.0*percentage);
3171 (void) ModulateImage(preview_image,factor);
3172 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3175 case BrightnessPreview:
3177 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3178 if (preview_image == (Image *) NULL)
3180 (void) FormatMagickString(factor,MaxTextExtent,"%g",2.0*percentage);
3181 (void) ModulateImage(preview_image,factor);
3182 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3188 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3189 if (preview_image == (Image *) NULL)
3192 (void) GammaImageChannel(preview_image,DefaultChannels,gamma);
3193 (void) FormatMagickString(label,MaxTextExtent,"gamma %g",gamma);
3198 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3199 if (preview_image != (Image *) NULL)
3200 for (x=0; x < i; x++)
3201 (void) ContrastImage(preview_image,MagickTrue);
3202 (void) FormatMagickString(label,MaxTextExtent,"contrast (%ld)",i+1);
3207 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3208 if (preview_image == (Image *) NULL)
3210 for (x=0; x < i; x++)
3211 (void) ContrastImage(preview_image,MagickFalse);
3212 (void) FormatMagickString(label,MaxTextExtent,"+contrast (%ld)",i+1);
3215 case GrayscalePreview:
3217 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3218 if (preview_image == (Image *) NULL)
3221 quantize_info.number_colors=colors;
3222 quantize_info.colorspace=GRAYColorspace;
3223 (void) QuantizeImage(&quantize_info,preview_image);
3224 (void) FormatMagickString(label,MaxTextExtent,
3225 "-colorspace gray -colors %ld",colors);
3228 case QuantizePreview:
3230 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3231 if (preview_image == (Image *) NULL)
3234 quantize_info.number_colors=colors;
3235 (void) QuantizeImage(&quantize_info,preview_image);
3236 (void) FormatMagickString(label,MaxTextExtent,"colors %ld",colors);
3239 case DespecklePreview:
3241 for (x=0; x < (i-1); x++)
3243 preview_image=DespeckleImage(thumbnail,exception);
3244 if (preview_image == (Image *) NULL)
3246 thumbnail=DestroyImage(thumbnail);
3247 thumbnail=preview_image;
3249 preview_image=DespeckleImage(thumbnail,exception);
3250 if (preview_image == (Image *) NULL)
3252 (void) FormatMagickString(label,MaxTextExtent,"despeckle (%ld)",i+1);
3255 case ReduceNoisePreview:
3257 preview_image=ReduceNoiseImage(thumbnail,radius,exception);
3258 (void) FormatMagickString(label,MaxTextExtent,"noise %g",radius);
3261 case AddNoisePreview:
3267 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
3272 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
3277 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
3282 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
3287 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
3292 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
3297 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
3301 preview_image=ReduceNoiseImage(thumbnail,(double) i,exception);
3302 (void) FormatMagickString(label,MaxTextExtent,"+noise %s",factor);
3305 case SharpenPreview:
3307 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
3308 (void) FormatMagickString(label,MaxTextExtent,"sharpen %gx%g",radius,
3314 preview_image=BlurImage(thumbnail,radius,sigma,exception);
3315 (void) FormatMagickString(label,MaxTextExtent,"blur %gx%g",radius,
3319 case ThresholdPreview:
3321 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3322 if (preview_image == (Image *) NULL)
3324 (void) BilevelImage(thumbnail,
3325 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3326 (void) FormatMagickString(label,MaxTextExtent,"threshold %g",
3327 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3330 case EdgeDetectPreview:
3332 preview_image=EdgeImage(thumbnail,radius,exception);
3333 (void) FormatMagickString(label,MaxTextExtent,"edge %g",radius);
3338 preview_image=SpreadImage(thumbnail,radius,exception);
3339 (void) FormatMagickString(label,MaxTextExtent,"spread %g",radius+0.5);
3342 case SolarizePreview:
3344 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3345 if (preview_image == (Image *) NULL)
3347 (void) SolarizeImage(preview_image,(double) QuantumRange*
3349 (void) FormatMagickString(label,MaxTextExtent,"solarize %g",
3350 (QuantumRange*percentage)/100.0);
3356 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
3358 (void) FormatMagickString(label,MaxTextExtent,"shade %gx%g",degrees,
3364 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3365 if (preview_image == (Image *) NULL)
3367 geometry.width=(unsigned long) (2*i+2);
3368 geometry.height=(unsigned long) (2*i+2);
3371 (void) RaiseImage(preview_image,&geometry,MagickTrue);
3372 (void) FormatMagickString(label,MaxTextExtent,"raise %lux%lu%+ld%+ld",
3373 geometry.width,geometry.height,geometry.x,geometry.y);
3376 case SegmentPreview:
3378 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3379 if (preview_image == (Image *) NULL)
3382 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
3384 (void) FormatMagickString(label,MaxTextExtent,"segment %gx%g",
3385 threshold,threshold);
3390 preview_image=SwirlImage(thumbnail,degrees,exception);
3391 (void) FormatMagickString(label,MaxTextExtent,"swirl %g",degrees);
3395 case ImplodePreview:
3398 preview_image=ImplodeImage(thumbnail,degrees,exception);
3399 (void) FormatMagickString(label,MaxTextExtent,"implode %g",degrees);
3405 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
3406 (void) FormatMagickString(label,MaxTextExtent,"wave %gx%g",0.5*degrees,
3410 case OilPaintPreview:
3412 preview_image=OilPaintImage(thumbnail,(double) radius,exception);
3413 (void) FormatMagickString(label,MaxTextExtent,"paint %g",radius);
3416 case CharcoalDrawingPreview:
3418 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
3420 (void) FormatMagickString(label,MaxTextExtent,"charcoal %gx%g",radius,
3427 filename[MaxTextExtent];
3435 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3436 if (preview_image == (Image *) NULL)
3438 preview_info->quality=(unsigned long) percentage;
3439 (void) FormatMagickString(factor,MaxTextExtent,"%lu",
3440 preview_info->quality);
3441 file=AcquireUniqueFileResource(filename);
3444 (void) FormatMagickString(preview_image->filename,MaxTextExtent,
3445 "jpeg:%s",filename);
3446 status=WriteImage(preview_info,preview_image);
3447 if (status != MagickFalse)
3452 (void) CopyMagickString(preview_info->filename,
3453 preview_image->filename,MaxTextExtent);
3454 quality_image=ReadImage(preview_info,exception);
3455 if (quality_image != (Image *) NULL)
3457 preview_image=DestroyImage(preview_image);
3458 preview_image=quality_image;
3461 (void) RelinquishUniqueFileResource(preview_image->filename);
3462 if ((GetBlobSize(preview_image)/1024) >= 1024)
3463 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gmb ",
3464 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
3467 if (GetBlobSize(preview_image) >= 1024)
3468 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gkb ",
3469 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
3472 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%lub ",
3473 factor,(unsigned long) GetBlobSize(thumbnail));
3477 thumbnail=DestroyImage(thumbnail);
3481 if (preview_image == (Image *) NULL)
3483 (void) DeleteImageProperty(preview_image,"label");
3484 (void) SetImageProperty(preview_image,"label",label);
3485 AppendImageToList(&images,preview_image);
3486 proceed=SetImageProgress(image,PreviewImageTag,i,NumberTiles);
3487 if (proceed == MagickFalse)
3490 if (images == (Image *) NULL)
3492 preview_info=DestroyImageInfo(preview_info);
3493 return((Image *) NULL);
3498 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
3499 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
3500 montage_info->shadow=MagickTrue;
3501 (void) CloneString(&montage_info->tile,"3x3");
3502 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
3503 (void) CloneString(&montage_info->frame,DefaultTileFrame);
3504 montage_image=MontageImages(images,montage_info,exception);
3505 montage_info=DestroyMontageInfo(montage_info);
3506 images=DestroyImageList(images);
3507 if (montage_image == (Image *) NULL)
3508 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3509 if (montage_image->montage != (char *) NULL)
3512 Free image directory.
3514 montage_image->montage=(char *) RelinquishMagickMemory(
3515 montage_image->montage);
3516 if (image->directory != (char *) NULL)
3517 montage_image->directory=(char *) RelinquishMagickMemory(
3518 montage_image->directory);
3520 preview_info=DestroyImageInfo(preview_info);
3521 return(montage_image);
3525 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3529 % R a d i a l B l u r I m a g e %
3533 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3535 % RadialBlurImage() applies a radial blur to the image.
3537 % Andrew Protano contributed this effect.
3539 % The format of the RadialBlurImage method is:
3541 % Image *RadialBlurImage(const Image *image,const double angle,
3542 % ExceptionInfo *exception)
3543 % Image *RadialBlurImageChannel(const Image *image,const ChannelType channel,
3544 % const double angle,ExceptionInfo *exception)
3546 % A description of each parameter follows:
3548 % o image: the image.
3550 % o channel: the channel type.
3552 % o angle: the angle of the radial blur.
3554 % o exception: return any errors or warnings in this structure.
3558 MagickExport Image *RadialBlurImage(const Image *image,const double angle,
3559 ExceptionInfo *exception)
3564 blur_image=RadialBlurImageChannel(image,DefaultChannels,angle,exception);
3568 MagickExport Image *RadialBlurImageChannel(const Image *image,
3569 const ChannelType channel,const double angle,ExceptionInfo *exception)
3605 Allocate blur image.
3607 assert(image != (Image *) NULL);
3608 assert(image->signature == MagickSignature);
3609 if (image->debug != MagickFalse)
3610 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3611 assert(exception != (ExceptionInfo *) NULL);
3612 assert(exception->signature == MagickSignature);
3613 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3614 if (blur_image == (Image *) NULL)
3615 return((Image *) NULL);
3616 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3618 InheritException(exception,&blur_image->exception);
3619 blur_image=DestroyImage(blur_image);
3620 return((Image *) NULL);
3622 blur_center.x=(double) image->columns/2.0;
3623 blur_center.y=(double) image->rows/2.0;
3624 blur_radius=hypot(blur_center.x,blur_center.y);
3625 n=(unsigned long) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+
3627 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
3628 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3629 sizeof(*cos_theta));
3630 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3631 sizeof(*sin_theta));
3632 if ((cos_theta == (MagickRealType *) NULL) ||
3633 (sin_theta == (MagickRealType *) NULL))
3635 blur_image=DestroyImage(blur_image);
3636 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3638 offset=theta*(MagickRealType) (n-1)/2.0;
3639 for (i=0; i < (long) n; i++)
3641 cos_theta[i]=cos((double) (theta*i-offset));
3642 sin_theta[i]=sin((double) (theta*i-offset));
3649 GetMagickPixelPacket(image,&bias);
3650 image_view=AcquireCacheView(image);
3651 blur_view=AcquireCacheView(blur_image);
3652 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3653 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3655 for (y=0; y < (long) blur_image->rows; y++)
3657 register const IndexPacket
3660 register IndexPacket
3661 *restrict blur_indexes;
3666 register PixelPacket
3669 if (status == MagickFalse)
3671 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3673 if (q == (PixelPacket *) NULL)
3678 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
3679 for (x=0; x < (long) blur_image->columns; x++)
3700 center.x=(double) x-blur_center.x;
3701 center.y=(double) y-blur_center.y;
3702 radius=hypot((double) center.x,center.y);
3707 step=(unsigned long) (blur_radius/radius);
3716 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
3718 for (i=0; i < (long) n; i+=step)
3720 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
3721 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
3722 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
3724 qixel.red+=pixel.red;
3725 qixel.green+=pixel.green;
3726 qixel.blue+=pixel.blue;
3727 qixel.opacity+=pixel.opacity;
3728 if (image->colorspace == CMYKColorspace)
3730 indexes=GetCacheViewVirtualIndexQueue(image_view);
3731 qixel.index+=(*indexes);
3735 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3737 if ((channel & RedChannel) != 0)
3738 q->red=RoundToQuantum(normalize*qixel.red);
3739 if ((channel & GreenChannel) != 0)
3740 q->green=RoundToQuantum(normalize*qixel.green);
3741 if ((channel & BlueChannel) != 0)
3742 q->blue=RoundToQuantum(normalize*qixel.blue);
3743 if ((channel & OpacityChannel) != 0)
3744 q->opacity=RoundToQuantum(normalize*qixel.opacity);
3745 if (((channel & IndexChannel) != 0) &&
3746 (image->colorspace == CMYKColorspace))
3747 blur_indexes[x]=(IndexPacket) RoundToQuantum(normalize*qixel.index);
3757 for (i=0; i < (long) n; i+=step)
3759 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
3760 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
3761 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
3763 alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
3764 qixel.red+=alpha*pixel.red;
3765 qixel.green+=alpha*pixel.green;
3766 qixel.blue+=alpha*pixel.blue;
3767 qixel.opacity+=pixel.opacity;
3768 if (image->colorspace == CMYKColorspace)
3770 indexes=GetCacheViewVirtualIndexQueue(image_view);
3771 qixel.index+=alpha*(*indexes);
3776 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3777 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3779 if ((channel & RedChannel) != 0)
3780 q->red=RoundToQuantum(gamma*qixel.red);
3781 if ((channel & GreenChannel) != 0)
3782 q->green=RoundToQuantum(gamma*qixel.green);
3783 if ((channel & BlueChannel) != 0)
3784 q->blue=RoundToQuantum(gamma*qixel.blue);
3785 if ((channel & OpacityChannel) != 0)
3786 q->opacity=RoundToQuantum(normalize*qixel.opacity);
3787 if (((channel & IndexChannel) != 0) &&
3788 (image->colorspace == CMYKColorspace))
3789 blur_indexes[x]=(IndexPacket) RoundToQuantum(gamma*qixel.index);
3793 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3795 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3800 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3801 #pragma omp critical (MagickCore_RadialBlurImageChannel)
3803 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3804 if (proceed == MagickFalse)
3808 blur_view=DestroyCacheView(blur_view);
3809 image_view=DestroyCacheView(image_view);
3810 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
3811 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
3812 if (status == MagickFalse)
3813 blur_image=DestroyImage(blur_image);
3818 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3822 % R e d u c e N o i s e I m a g e %
3826 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3828 % ReduceNoiseImage() smooths the contours of an image while still preserving
3829 % edge information. The algorithm works by replacing each pixel with its
3830 % neighbor closest in value. A neighbor is defined by radius. Use a radius
3831 % of 0 and ReduceNoise() selects a suitable radius for you.
3833 % The format of the ReduceNoiseImage method is:
3835 % Image *ReduceNoiseImage(const Image *image,const double radius,
3836 % ExceptionInfo *exception)
3838 % A description of each parameter follows:
3840 % o image: the image.
3842 % o radius: the radius of the pixel neighborhood.
3844 % o exception: return any errors or warnings in this structure.
3848 static MagickPixelPacket GetNonpeakMedianPixelList(MedianPixelList *pixel_list)
3856 register MedianSkipList
3870 Finds the median value for each of the color.
3872 center=pixel_list->center;
3873 for (channel=0; channel < 5; channel++)
3875 list=pixel_list->lists+channel;
3877 next=list->nodes[color].next[0];
3883 next=list->nodes[color].next[0];
3884 count+=list->nodes[color].count;
3886 while (count <= center);
3887 if ((previous == 65536UL) && (next != 65536UL))
3890 if ((previous != 65536UL) && (next == 65536UL))
3892 channels[channel]=(unsigned short) color;
3894 GetMagickPixelPacket((const Image *) NULL,&pixel);
3895 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
3896 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
3897 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
3898 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
3899 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
3903 MagickExport Image *ReduceNoiseImage(const Image *image,const double radius,
3904 ExceptionInfo *exception)
3906 #define ReduceNoiseImageTag "ReduceNoise/Image"
3929 Initialize noise image attributes.
3931 assert(image != (Image *) NULL);
3932 assert(image->signature == MagickSignature);
3933 if (image->debug != MagickFalse)
3934 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3935 assert(exception != (ExceptionInfo *) NULL);
3936 assert(exception->signature == MagickSignature);
3937 width=GetOptimalKernelWidth2D(radius,0.5);
3938 if ((image->columns < width) || (image->rows < width))
3939 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
3940 noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3942 if (noise_image == (Image *) NULL)
3943 return((Image *) NULL);
3944 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
3946 InheritException(exception,&noise_image->exception);
3947 noise_image=DestroyImage(noise_image);
3948 return((Image *) NULL);
3950 pixel_list=AcquireMedianPixelListThreadSet(width);
3951 if (pixel_list == (MedianPixelList **) NULL)
3953 noise_image=DestroyImage(noise_image);
3954 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3961 image_view=AcquireCacheView(image);
3962 noise_view=AcquireCacheView(noise_image);
3963 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3964 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3966 for (y=0; y < (long) noise_image->rows; y++)
3968 register const IndexPacket
3971 register const PixelPacket
3974 register IndexPacket
3975 *restrict noise_indexes;
3981 register PixelPacket
3984 if (status == MagickFalse)
3986 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
3987 2L),image->columns+width,width,exception);
3988 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
3990 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3995 indexes=GetCacheViewVirtualIndexQueue(image_view);
3996 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
3997 id=GetOpenMPThreadId();
3998 for (x=0; x < (long) noise_image->columns; x++)
4003 register const PixelPacket
4006 register const IndexPacket
4015 ResetMedianPixelList(pixel_list[id]);
4016 for (v=0; v < (long) width; v++)
4018 for (u=0; u < (long) width; u++)
4019 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
4020 r+=image->columns+width;
4021 s+=image->columns+width;
4023 pixel=GetNonpeakMedianPixelList(pixel_list[id]);
4024 SetPixelPacket(noise_image,&pixel,q,noise_indexes+x);
4028 if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
4030 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4035 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4036 #pragma omp critical (MagickCore_ReduceNoiseImage)
4038 proceed=SetImageProgress(image,ReduceNoiseImageTag,progress++,
4040 if (proceed == MagickFalse)
4044 noise_view=DestroyCacheView(noise_view);
4045 image_view=DestroyCacheView(image_view);
4046 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
4047 return(noise_image);
4051 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4055 % S e l e c t i v e B l u r I m a g e %
4059 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4061 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
4062 % It is similar to the unsharpen mask that sharpens everything with contrast
4063 % above a certain threshold.
4065 % The format of the SelectiveBlurImage method is:
4067 % Image *SelectiveBlurImage(const Image *image,const double radius,
4068 % const double sigma,const double threshold,ExceptionInfo *exception)
4069 % Image *SelectiveBlurImageChannel(const Image *image,
4070 % const ChannelType channel,const double radius,const double sigma,
4071 % const double threshold,ExceptionInfo *exception)
4073 % A description of each parameter follows:
4075 % o image: the image.
4077 % o channel: the channel type.
4079 % o radius: the radius of the Gaussian, in pixels, not counting the center
4082 % o sigma: the standard deviation of the Gaussian, in pixels.
4084 % o threshold: only pixels within this contrast threshold are included
4085 % in the blur operation.
4087 % o exception: return any errors or warnings in this structure.
4091 static inline MagickBooleanType SelectiveContrast(const PixelPacket *p,
4092 const PixelPacket *q,const double threshold)
4094 if (fabs(PixelIntensity(p)-PixelIntensity(q)) < threshold)
4096 return(MagickFalse);
4099 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
4100 const double sigma,const double threshold,ExceptionInfo *exception)
4105 blur_image=SelectiveBlurImageChannel(image,DefaultChannels,radius,sigma,
4106 threshold,exception);
4110 MagickExport Image *SelectiveBlurImageChannel(const Image *image,
4111 const ChannelType channel,const double radius,const double sigma,
4112 const double threshold,ExceptionInfo *exception)
4114 #define SelectiveBlurImageTag "SelectiveBlur/Image"
4146 Initialize blur image attributes.
4148 assert(image != (Image *) NULL);
4149 assert(image->signature == MagickSignature);
4150 if (image->debug != MagickFalse)
4151 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4152 assert(exception != (ExceptionInfo *) NULL);
4153 assert(exception->signature == MagickSignature);
4154 width=GetOptimalKernelWidth1D(radius,sigma);
4155 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
4156 if (kernel == (double *) NULL)
4157 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4159 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
4161 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
4163 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
4164 kernel[i]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
4168 if (image->debug != MagickFalse)
4171 format[MaxTextExtent],
4178 register const double
4181 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
4182 " SelectiveBlurImage with %ldx%ld kernel:",width,width);
4183 message=AcquireString("");
4185 for (v=0; v < (long) width; v++)
4188 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
4189 (void) ConcatenateString(&message,format);
4190 for (u=0; u < (long) width; u++)
4192 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
4193 (void) ConcatenateString(&message,format);
4195 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
4197 message=DestroyString(message);
4199 blur_image=CloneImage(image,0,0,MagickTrue,exception);
4200 if (blur_image == (Image *) NULL)
4201 return((Image *) NULL);
4202 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
4204 InheritException(exception,&blur_image->exception);
4205 blur_image=DestroyImage(blur_image);
4206 return((Image *) NULL);
4209 Threshold blur image.
4213 GetMagickPixelPacket(image,&bias);
4214 SetMagickPixelPacketBias(image,&bias);
4215 image_view=AcquireCacheView(image);
4216 blur_view=AcquireCacheView(blur_image);
4217 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4218 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4220 for (y=0; y < (long) image->rows; y++)
4228 register const IndexPacket
4231 register const PixelPacket
4234 register IndexPacket
4235 *restrict blur_indexes;
4240 register PixelPacket
4243 if (status == MagickFalse)
4245 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
4246 2L),image->columns+width,width,exception);
4247 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
4249 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4254 indexes=GetCacheViewVirtualIndexQueue(image_view);
4255 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
4256 for (x=0; x < (long) image->columns; x++)
4265 register const double
4275 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
4277 for (v=0; v < (long) width; v++)
4279 for (u=0; u < (long) width; u++)
4281 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4283 pixel.red+=(*k)*(p+u+j)->red;
4284 pixel.green+=(*k)*(p+u+j)->green;
4285 pixel.blue+=(*k)*(p+u+j)->blue;
4290 j+=image->columns+width;
4294 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4295 if ((channel & RedChannel) != 0)
4296 q->red=RoundToQuantum(gamma*pixel.red);
4297 if ((channel & GreenChannel) != 0)
4298 q->green=RoundToQuantum(gamma*pixel.green);
4299 if ((channel & BlueChannel) != 0)
4300 q->blue=RoundToQuantum(gamma*pixel.blue);
4302 if ((channel & OpacityChannel) != 0)
4306 for (v=0; v < (long) width; v++)
4308 for (u=0; u < (long) width; u++)
4310 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4312 pixel.opacity+=(*k)*(p+u+j)->opacity;
4317 j+=image->columns+width;
4321 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4323 q->opacity=RoundToQuantum(gamma*pixel.opacity);
4326 if (((channel & IndexChannel) != 0) &&
4327 (image->colorspace == CMYKColorspace))
4331 for (v=0; v < (long) width; v++)
4333 for (u=0; u < (long) width; u++)
4335 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4337 pixel.index+=(*k)*indexes[x+u+j];
4342 j+=image->columns+width;
4346 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4348 blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
4357 for (v=0; v < (long) width; v++)
4359 for (u=0; u < (long) width; u++)
4361 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4363 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
4365 pixel.red+=(*k)*alpha*(p+u+j)->red;
4366 pixel.green+=(*k)*alpha*(p+u+j)->green;
4367 pixel.blue+=(*k)*alpha*(p+u+j)->blue;
4368 pixel.opacity+=(*k)*(p+u+j)->opacity;
4373 j+=image->columns+width;
4377 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4378 if ((channel & RedChannel) != 0)
4379 q->red=RoundToQuantum(gamma*pixel.red);
4380 if ((channel & GreenChannel) != 0)
4381 q->green=RoundToQuantum(gamma*pixel.green);
4382 if ((channel & BlueChannel) != 0)
4383 q->blue=RoundToQuantum(gamma*pixel.blue);
4385 if ((channel & OpacityChannel) != 0)
4389 for (v=0; v < (long) width; v++)
4391 for (u=0; u < (long) width; u++)
4393 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4395 pixel.opacity+=(*k)*(p+u+j)->opacity;
4400 j+=image->columns+width;
4404 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4406 q->opacity=RoundToQuantum(pixel.opacity);
4409 if (((channel & IndexChannel) != 0) &&
4410 (image->colorspace == CMYKColorspace))
4414 for (v=0; v < (long) width; v++)
4416 for (u=0; u < (long) width; u++)
4418 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4420 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
4422 pixel.index+=(*k)*alpha*indexes[x+u+j];
4427 j+=image->columns+width;
4431 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4433 blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
4440 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
4441 if (sync == MagickFalse)
4443 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4448 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4449 #pragma omp critical (MagickCore_SelectiveBlurImageChannel)
4451 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
4453 if (proceed == MagickFalse)
4457 blur_image->type=image->type;
4458 blur_view=DestroyCacheView(blur_view);
4459 image_view=DestroyCacheView(image_view);
4460 kernel=(double *) RelinquishMagickMemory(kernel);
4461 if (status == MagickFalse)
4462 blur_image=DestroyImage(blur_image);
4467 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4471 % S h a d e I m a g e %
4475 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4477 % ShadeImage() shines a distant light on an image to create a
4478 % three-dimensional effect. You control the positioning of the light with
4479 % azimuth and elevation; azimuth is measured in degrees off the x axis
4480 % and elevation is measured in pixels above the Z axis.
4482 % The format of the ShadeImage method is:
4484 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4485 % const double azimuth,const double elevation,ExceptionInfo *exception)
4487 % A description of each parameter follows:
4489 % o image: the image.
4491 % o gray: A value other than zero shades the intensity of each pixel.
4493 % o azimuth, elevation: Define the light source direction.
4495 % o exception: return any errors or warnings in this structure.
4498 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4499 const double azimuth,const double elevation,ExceptionInfo *exception)
4501 #define ShadeImageTag "Shade/Image"
4521 Initialize shaded image attributes.
4523 assert(image != (const Image *) NULL);
4524 assert(image->signature == MagickSignature);
4525 if (image->debug != MagickFalse)
4526 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4527 assert(exception != (ExceptionInfo *) NULL);
4528 assert(exception->signature == MagickSignature);
4529 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4530 if (shade_image == (Image *) NULL)
4531 return((Image *) NULL);
4532 if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
4534 InheritException(exception,&shade_image->exception);
4535 shade_image=DestroyImage(shade_image);
4536 return((Image *) NULL);
4539 Compute the light vector.
4541 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
4542 cos(DegreesToRadians(elevation));
4543 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
4544 cos(DegreesToRadians(elevation));
4545 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
4551 image_view=AcquireCacheView(image);
4552 shade_view=AcquireCacheView(shade_image);
4553 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4554 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4556 for (y=0; y < (long) image->rows; y++)
4566 register const PixelPacket
4575 register PixelPacket
4578 if (status == MagickFalse)
4580 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
4581 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
4583 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4589 Shade this row of pixels.
4591 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
4593 s1=s0+image->columns+2;
4594 s2=s1+image->columns+2;
4595 for (x=0; x < (long) image->columns; x++)
4598 Determine the surface normal and compute shading.
4600 normal.x=(double) (PixelIntensity(s0-1)+PixelIntensity(s1-1)+
4601 PixelIntensity(s2-1)-PixelIntensity(s0+1)-PixelIntensity(s1+1)-
4602 PixelIntensity(s2+1));
4603 normal.y=(double) (PixelIntensity(s2-1)+PixelIntensity(s2)+
4604 PixelIntensity(s2+1)-PixelIntensity(s0-1)-PixelIntensity(s0)-
4605 PixelIntensity(s0+1));
4606 if ((normal.x == 0.0) && (normal.y == 0.0))
4611 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
4612 if (distance > MagickEpsilon)
4615 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
4616 if (normal_distance > (MagickEpsilon*MagickEpsilon))
4617 shade=distance/sqrt((double) normal_distance);
4620 if (gray != MagickFalse)
4622 q->red=(Quantum) shade;
4623 q->green=(Quantum) shade;
4624 q->blue=(Quantum) shade;
4628 q->red=RoundToQuantum(QuantumScale*shade*s1->red);
4629 q->green=RoundToQuantum(QuantumScale*shade*s1->green);
4630 q->blue=RoundToQuantum(QuantumScale*shade*s1->blue);
4632 q->opacity=s1->opacity;
4638 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
4640 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4645 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4646 #pragma omp critical (MagickCore_ShadeImage)
4648 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
4649 if (proceed == MagickFalse)
4653 shade_view=DestroyCacheView(shade_view);
4654 image_view=DestroyCacheView(image_view);
4655 if (status == MagickFalse)
4656 shade_image=DestroyImage(shade_image);
4657 return(shade_image);
4661 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4665 % S h a r p e n I m a g e %
4669 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4671 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
4672 % operator of the given radius and standard deviation (sigma). For
4673 % reasonable results, radius should be larger than sigma. Use a radius of 0
4674 % and SharpenImage() selects a suitable radius for you.
4676 % Using a separable kernel would be faster, but the negative weights cancel
4677 % out on the corners of the kernel producing often undesirable ringing in the
4678 % filtered result; this can be avoided by using a 2D gaussian shaped image
4679 % sharpening kernel instead.
4681 % The format of the SharpenImage method is:
4683 % Image *SharpenImage(const Image *image,const double radius,
4684 % const double sigma,ExceptionInfo *exception)
4685 % Image *SharpenImageChannel(const Image *image,const ChannelType channel,
4686 % const double radius,const double sigma,ExceptionInfo *exception)
4688 % A description of each parameter follows:
4690 % o image: the image.
4692 % o channel: the channel type.
4694 % o radius: the radius of the Gaussian, in pixels, not counting the center
4697 % o sigma: the standard deviation of the Laplacian, in pixels.
4699 % o exception: return any errors or warnings in this structure.
4703 MagickExport Image *SharpenImage(const Image *image,const double radius,
4704 const double sigma,ExceptionInfo *exception)
4709 sharp_image=SharpenImageChannel(image,DefaultChannels,radius,sigma,exception);
4710 return(sharp_image);
4713 MagickExport Image *SharpenImageChannel(const Image *image,
4714 const ChannelType channel,const double radius,const double sigma,
4715 ExceptionInfo *exception)
4735 assert(image != (const Image *) NULL);
4736 assert(image->signature == MagickSignature);
4737 if (image->debug != MagickFalse)
4738 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4739 assert(exception != (ExceptionInfo *) NULL);
4740 assert(exception->signature == MagickSignature);
4741 width=GetOptimalKernelWidth2D(radius,sigma);
4742 kernel=(double *) AcquireQuantumMemory((size_t) width*width,sizeof(*kernel));
4743 if (kernel == (double *) NULL)
4744 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4747 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
4749 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
4751 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
4752 kernel[i]=(double) (-alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
4753 normalize+=kernel[i];
4757 kernel[i/2]=(double) ((-2.0)*normalize);
4758 sharp_image=ConvolveImageChannel(image,channel,width,kernel,exception);
4759 kernel=(double *) RelinquishMagickMemory(kernel);
4760 return(sharp_image);
4764 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4768 % S p r e a d I m a g e %
4772 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4774 % SpreadImage() is a special effects method that randomly displaces each
4775 % pixel in a block defined by the radius parameter.
4777 % The format of the SpreadImage method is:
4779 % Image *SpreadImage(const Image *image,const double radius,
4780 % ExceptionInfo *exception)
4782 % A description of each parameter follows:
4784 % o image: the image.
4786 % o radius: Choose a random pixel in a neighborhood of this extent.
4788 % o exception: return any errors or warnings in this structure.
4791 MagickExport Image *SpreadImage(const Image *image,const double radius,
4792 ExceptionInfo *exception)
4794 #define SpreadImageTag "Spread/Image"
4822 Initialize spread image attributes.
4824 assert(image != (Image *) NULL);
4825 assert(image->signature == MagickSignature);
4826 if (image->debug != MagickFalse)
4827 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4828 assert(exception != (ExceptionInfo *) NULL);
4829 assert(exception->signature == MagickSignature);
4830 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4832 if (spread_image == (Image *) NULL)
4833 return((Image *) NULL);
4834 if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
4836 InheritException(exception,&spread_image->exception);
4837 spread_image=DestroyImage(spread_image);
4838 return((Image *) NULL);
4845 GetMagickPixelPacket(spread_image,&bias);
4846 width=GetOptimalKernelWidth1D(radius,0.5);
4847 resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
4848 random_info=AcquireRandomInfoThreadSet();
4849 image_view=AcquireCacheView(spread_image);
4850 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4851 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4853 for (y=0; y < (long) spread_image->rows; y++)
4858 register IndexPacket
4865 register PixelPacket
4868 if (status == MagickFalse)
4870 q=QueueCacheViewAuthenticPixels(image_view,0,y,spread_image->columns,1,
4872 if (q == (PixelPacket *) NULL)
4877 indexes=GetCacheViewAuthenticIndexQueue(image_view);
4879 id=GetOpenMPThreadId();
4880 for (x=0; x < (long) spread_image->columns; x++)
4882 (void) ResamplePixelColor(resample_filter[id],(double) x+width*
4883 (GetPseudoRandomValue(random_info[id])-0.5),(double) y+width*
4884 (GetPseudoRandomValue(random_info[id])-0.5),&pixel);
4885 SetPixelPacket(spread_image,&pixel,q,indexes+x);
4888 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4890 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4895 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4896 #pragma omp critical (MagickCore_SpreadImage)
4898 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
4899 if (proceed == MagickFalse)
4903 image_view=DestroyCacheView(image_view);
4904 random_info=DestroyRandomInfoThreadSet(random_info);
4905 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
4906 return(spread_image);
4910 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4914 % U n s h a r p M a s k I m a g e %
4918 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4920 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
4921 % image with a Gaussian operator of the given radius and standard deviation
4922 % (sigma). For reasonable results, radius should be larger than sigma. Use a
4923 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
4925 % The format of the UnsharpMaskImage method is:
4927 % Image *UnsharpMaskImage(const Image *image,const double radius,
4928 % const double sigma,const double amount,const double threshold,
4929 % ExceptionInfo *exception)
4930 % Image *UnsharpMaskImageChannel(const Image *image,
4931 % const ChannelType channel,const double radius,const double sigma,
4932 % const double amount,const double threshold,ExceptionInfo *exception)
4934 % A description of each parameter follows:
4936 % o image: the image.
4938 % o channel: the channel type.
4940 % o radius: the radius of the Gaussian, in pixels, not counting the center
4943 % o sigma: the standard deviation of the Gaussian, in pixels.
4945 % o amount: the percentage of the difference between the original and the
4946 % blur image that is added back into the original.
4948 % o threshold: the threshold in pixels needed to apply the diffence amount.
4950 % o exception: return any errors or warnings in this structure.
4954 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
4955 const double sigma,const double amount,const double threshold,
4956 ExceptionInfo *exception)
4961 sharp_image=UnsharpMaskImageChannel(image,DefaultChannels,radius,sigma,amount,
4962 threshold,exception);
4963 return(sharp_image);
4966 MagickExport Image *UnsharpMaskImageChannel(const Image *image,
4967 const ChannelType channel,const double radius,const double sigma,
4968 const double amount,const double threshold,ExceptionInfo *exception)
4970 #define SharpenImageTag "Sharpen/Image"
4992 assert(image != (const Image *) NULL);
4993 assert(image->signature == MagickSignature);
4994 if (image->debug != MagickFalse)
4995 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4996 assert(exception != (ExceptionInfo *) NULL);
4997 unsharp_image=BlurImageChannel(image,channel,radius,sigma,exception);
4998 if (unsharp_image == (Image *) NULL)
4999 return((Image *) NULL);
5000 quantum_threshold=(MagickRealType) QuantumRange*threshold;
5006 GetMagickPixelPacket(image,&bias);
5007 image_view=AcquireCacheView(image);
5008 unsharp_view=AcquireCacheView(unsharp_image);
5009 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5010 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5012 for (y=0; y < (long) image->rows; y++)
5017 register const IndexPacket
5020 register const PixelPacket
5023 register IndexPacket
5024 *restrict unsharp_indexes;
5029 register PixelPacket
5032 if (status == MagickFalse)
5034 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5035 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
5037 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5042 indexes=GetCacheViewVirtualIndexQueue(image_view);
5043 unsharp_indexes=GetCacheViewAuthenticIndexQueue(unsharp_view);
5045 for (x=0; x < (long) image->columns; x++)
5047 if ((channel & RedChannel) != 0)
5049 pixel.red=p->red-(MagickRealType) q->red;
5050 if (fabs(2.0*pixel.red) < quantum_threshold)
5051 pixel.red=(MagickRealType) p->red;
5053 pixel.red=(MagickRealType) p->red+(pixel.red*amount);
5054 q->red=RoundToQuantum(pixel.red);
5056 if ((channel & GreenChannel) != 0)
5058 pixel.green=p->green-(MagickRealType) q->green;
5059 if (fabs(2.0*pixel.green) < quantum_threshold)
5060 pixel.green=(MagickRealType) p->green;
5062 pixel.green=(MagickRealType) p->green+(pixel.green*amount);
5063 q->green=RoundToQuantum(pixel.green);
5065 if ((channel & BlueChannel) != 0)
5067 pixel.blue=p->blue-(MagickRealType) q->blue;
5068 if (fabs(2.0*pixel.blue) < quantum_threshold)
5069 pixel.blue=(MagickRealType) p->blue;
5071 pixel.blue=(MagickRealType) p->blue+(pixel.blue*amount);
5072 q->blue=RoundToQuantum(pixel.blue);
5074 if ((channel & OpacityChannel) != 0)
5076 pixel.opacity=p->opacity-(MagickRealType) q->opacity;
5077 if (fabs(2.0*pixel.opacity) < quantum_threshold)
5078 pixel.opacity=(MagickRealType) p->opacity;
5080 pixel.opacity=p->opacity+(pixel.opacity*amount);
5081 q->opacity=RoundToQuantum(pixel.opacity);
5083 if (((channel & IndexChannel) != 0) &&
5084 (image->colorspace == CMYKColorspace))
5086 pixel.index=unsharp_indexes[x]-(MagickRealType) indexes[x];
5087 if (fabs(2.0*pixel.index) < quantum_threshold)
5088 pixel.index=(MagickRealType) unsharp_indexes[x];
5090 pixel.index=(MagickRealType) unsharp_indexes[x]+(pixel.index*
5092 unsharp_indexes[x]=RoundToQuantum(pixel.index);
5097 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
5099 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5104 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5105 #pragma omp critical (MagickCore_UnsharpMaskImageChannel)
5107 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
5108 if (proceed == MagickFalse)
5112 unsharp_image->type=image->type;
5113 unsharp_view=DestroyCacheView(unsharp_view);
5114 image_view=DestroyCacheView(image_view);
5115 if (status == MagickFalse)
5116 unsharp_image=DestroyImage(unsharp_image);
5117 return(unsharp_image);