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-2015 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 "MagickCore/studio.h"
44 #include "MagickCore/accelerate.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/cache-view.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/colorspace.h"
50 #include "MagickCore/constitute.h"
51 #include "MagickCore/decorate.h"
52 #include "MagickCore/distort.h"
53 #include "MagickCore/draw.h"
54 #include "MagickCore/enhance.h"
55 #include "MagickCore/exception.h"
56 #include "MagickCore/exception-private.h"
57 #include "MagickCore/effect.h"
58 #include "MagickCore/fx.h"
59 #include "MagickCore/gem.h"
60 #include "MagickCore/gem-private.h"
61 #include "MagickCore/geometry.h"
62 #include "MagickCore/image-private.h"
63 #include "MagickCore/list.h"
64 #include "MagickCore/log.h"
65 #include "MagickCore/matrix.h"
66 #include "MagickCore/memory_.h"
67 #include "MagickCore/memory-private.h"
68 #include "MagickCore/monitor.h"
69 #include "MagickCore/monitor-private.h"
70 #include "MagickCore/montage.h"
71 #include "MagickCore/morphology.h"
72 #include "MagickCore/morphology-private.h"
73 #include "MagickCore/paint.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/pixel-private.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantize.h"
78 #include "MagickCore/quantum.h"
79 #include "MagickCore/quantum-private.h"
80 #include "MagickCore/random_.h"
81 #include "MagickCore/random-private.h"
82 #include "MagickCore/resample.h"
83 #include "MagickCore/resample-private.h"
84 #include "MagickCore/resize.h"
85 #include "MagickCore/resource_.h"
86 #include "MagickCore/segment.h"
87 #include "MagickCore/shear.h"
88 #include "MagickCore/signature-private.h"
89 #include "MagickCore/statistic.h"
90 #include "MagickCore/string_.h"
91 #include "MagickCore/thread-private.h"
92 #include "MagickCore/transform.h"
93 #include "MagickCore/threshold.h"
96 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
100 % A d a p t i v e B l u r I m a g e %
104 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106 % AdaptiveBlurImage() adaptively blurs the image by blurring less
107 % intensely near image edges and more intensely far from edges. We blur the
108 % image with a Gaussian operator of the given radius and standard deviation
109 % (sigma). For reasonable results, radius should be larger than sigma. Use a
110 % radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
112 % The format of the AdaptiveBlurImage method is:
114 % Image *AdaptiveBlurImage(const Image *image,const double radius,
115 % const double sigma,ExceptionInfo *exception)
117 % A description of each parameter follows:
119 % o image: the image.
121 % o radius: the radius of the Gaussian, in pixels, not counting the center
124 % o sigma: the standard deviation of the Laplacian, in pixels.
126 % o exception: return any errors or warnings in this structure.
129 MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
130 const double sigma,ExceptionInfo *exception)
132 #define AdaptiveBlurImageTag "Convolve/Image"
133 #define MagickSigma (fabs(sigma) < MagickEpsilon ? MagickEpsilon : sigma)
170 assert(image != (const Image *) NULL);
171 assert(image->signature == MagickSignature);
172 if (image->debug != MagickFalse)
173 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
174 assert(exception != (ExceptionInfo *) NULL);
175 assert(exception->signature == MagickSignature);
176 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
177 if (blur_image == (Image *) NULL)
178 return((Image *) NULL);
179 if (fabs(sigma) < MagickEpsilon)
181 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
183 blur_image=DestroyImage(blur_image);
184 return((Image *) NULL);
187 Edge detect the image brighness channel, level, blur, and level again.
189 edge_image=EdgeImage(image,radius,exception);
190 if (edge_image == (Image *) NULL)
192 blur_image=DestroyImage(blur_image);
193 return((Image *) NULL);
195 (void) AutoLevelImage(edge_image,exception);
196 gaussian_image=BlurImage(edge_image,radius,sigma,exception);
197 if (gaussian_image != (Image *) NULL)
199 edge_image=DestroyImage(edge_image);
200 edge_image=gaussian_image;
202 (void) AutoLevelImage(edge_image,exception);
204 Create a set of kernels from maximum (radius,sigma) to minimum.
206 width=GetOptimalKernelWidth2D(radius,sigma);
207 kernel=(MagickRealType **) MagickAssumeAligned(AcquireAlignedMemory((size_t)
208 width,sizeof(*kernel)));
209 if (kernel == (MagickRealType **) NULL)
211 edge_image=DestroyImage(edge_image);
212 blur_image=DestroyImage(blur_image);
213 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
215 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
216 for (i=0; i < (ssize_t) width; i+=2)
218 kernel[i]=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory(
219 (size_t) (width-i),(width-i)*sizeof(**kernel)));
220 if (kernel[i] == (MagickRealType *) NULL)
223 j=(ssize_t) (width-i-1)/2;
225 for (v=(-j); v <= j; v++)
227 for (u=(-j); u <= j; u++)
229 kernel[i][k]=(MagickRealType) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
230 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
231 normalize+=kernel[i][k];
235 kernel[i][(j-1)/2]+=(1.0-normalize);
236 if (sigma < MagickEpsilon)
237 kernel[i][(j-1)/2]=1.0;
239 if (i < (ssize_t) width)
241 for (i-=2; i >= 0; i-=2)
242 kernel[i]=(MagickRealType *) RelinquishAlignedMemory(kernel[i]);
243 kernel=(MagickRealType **) RelinquishAlignedMemory(kernel);
244 edge_image=DestroyImage(edge_image);
245 blur_image=DestroyImage(blur_image);
246 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
249 Adaptively blur image.
253 image_view=AcquireVirtualCacheView(image,exception);
254 edge_view=AcquireVirtualCacheView(edge_image,exception);
255 blur_view=AcquireAuthenticCacheView(blur_image,exception);
256 #if defined(MAGICKCORE_OPENMP_SUPPORT)
257 #pragma omp parallel for schedule(static,4) shared(progress,status) \
258 magick_threads(image,blur_image,blur_image->rows,1)
260 for (y=0; y < (ssize_t) blur_image->rows; y++)
262 register const Quantum
271 if (status == MagickFalse)
273 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
274 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
276 if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
281 for (x=0; x < (ssize_t) blur_image->columns; x++)
283 register const Quantum
293 j=(ssize_t) ceil((double) width*QuantumScale*
294 GetPixelIntensity(edge_image,r)-0.5);
298 if (j > (ssize_t) width)
302 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
303 (ssize_t) ((width-j)/2L),width-j,width-j,exception);
304 if (p == (const Quantum *) NULL)
306 center=(ssize_t) GetPixelChannels(image)*(width-j)*((width-j)/2L)+
307 GetPixelChannels(image)*((width-j)/2L);
308 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
322 register const MagickRealType
325 register const Quantum
334 channel=GetPixelChannelChannel(image,i);
335 traits=GetPixelChannelTraits(image,channel);
336 blur_traits=GetPixelChannelTraits(blur_image,channel);
337 if ((traits == UndefinedPixelTrait) ||
338 (blur_traits == UndefinedPixelTrait))
340 if (((blur_traits & CopyPixelTrait) != 0) ||
341 (GetPixelReadMask(image,p+center) == 0))
343 SetPixelChannel(blur_image,channel,p[center+i],q);
350 if ((blur_traits & BlendPixelTrait) == 0)
355 for (v=0; v < (ssize_t) (width-j); v++)
357 for (u=0; u < (ssize_t) (width-j); u++)
359 pixel+=(*k)*pixels[i];
362 pixels+=GetPixelChannels(image);
365 gamma=PerceptibleReciprocal(gamma);
366 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
372 for (v=0; v < (ssize_t) (width-j); v++)
374 for (u=0; u < (ssize_t) (width-j); u++)
376 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
377 pixel+=(*k)*alpha*pixels[i];
380 pixels+=GetPixelChannels(image);
383 gamma=PerceptibleReciprocal(gamma);
384 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
386 q+=GetPixelChannels(blur_image);
387 r+=GetPixelChannels(edge_image);
389 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
391 if (image->progress_monitor != (MagickProgressMonitor) NULL)
396 #if defined(MAGICKCORE_OPENMP_SUPPORT)
397 #pragma omp critical (MagickCore_AdaptiveBlurImage)
399 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
401 if (proceed == MagickFalse)
405 blur_image->type=image->type;
406 blur_view=DestroyCacheView(blur_view);
407 edge_view=DestroyCacheView(edge_view);
408 image_view=DestroyCacheView(image_view);
409 edge_image=DestroyImage(edge_image);
410 for (i=0; i < (ssize_t) width; i+=2)
411 kernel[i]=(MagickRealType *) RelinquishAlignedMemory(kernel[i]);
412 kernel=(MagickRealType **) RelinquishAlignedMemory(kernel);
413 if (status == MagickFalse)
414 blur_image=DestroyImage(blur_image);
419 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
423 % A d a p t i v e S h a r p e n I m a g e %
427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
429 % AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
430 % intensely near image edges and less intensely far from edges. We sharpen the
431 % image with a Gaussian operator of the given radius and standard deviation
432 % (sigma). For reasonable results, radius should be larger than sigma. Use a
433 % radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
435 % The format of the AdaptiveSharpenImage method is:
437 % Image *AdaptiveSharpenImage(const Image *image,const double radius,
438 % const double sigma,ExceptionInfo *exception)
440 % A description of each parameter follows:
442 % o image: the image.
444 % o radius: the radius of the Gaussian, in pixels, not counting the center
447 % o sigma: the standard deviation of the Laplacian, in pixels.
449 % o exception: return any errors or warnings in this structure.
452 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
453 const double sigma,ExceptionInfo *exception)
455 #define AdaptiveSharpenImageTag "Convolve/Image"
456 #define MagickSigma (fabs(sigma) < MagickEpsilon ? MagickEpsilon : sigma)
493 assert(image != (const Image *) NULL);
494 assert(image->signature == MagickSignature);
495 if (image->debug != MagickFalse)
496 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
497 assert(exception != (ExceptionInfo *) NULL);
498 assert(exception->signature == MagickSignature);
499 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
500 if (sharp_image == (Image *) NULL)
501 return((Image *) NULL);
502 if (fabs(sigma) < MagickEpsilon)
504 if (SetImageStorageClass(sharp_image,DirectClass,exception) == MagickFalse)
506 sharp_image=DestroyImage(sharp_image);
507 return((Image *) NULL);
510 Edge detect the image brightness channel, level, sharp, and level again.
512 edge_image=EdgeImage(image,radius,exception);
513 if (edge_image == (Image *) NULL)
515 sharp_image=DestroyImage(sharp_image);
516 return((Image *) NULL);
518 (void) AutoLevelImage(edge_image,exception);
519 gaussian_image=BlurImage(edge_image,radius,sigma,exception);
520 if (gaussian_image != (Image *) NULL)
522 edge_image=DestroyImage(edge_image);
523 edge_image=gaussian_image;
525 (void) AutoLevelImage(edge_image,exception);
527 Create a set of kernels from maximum (radius,sigma) to minimum.
529 width=GetOptimalKernelWidth2D(radius,sigma);
530 kernel=(MagickRealType **) MagickAssumeAligned(AcquireAlignedMemory((size_t)
531 width,sizeof(*kernel)));
532 if (kernel == (MagickRealType **) NULL)
534 edge_image=DestroyImage(edge_image);
535 sharp_image=DestroyImage(sharp_image);
536 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
538 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
539 for (i=0; i < (ssize_t) width; i+=2)
541 kernel[i]=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory(
542 (size_t) (width-i),(width-i)*sizeof(**kernel)));
543 if (kernel[i] == (MagickRealType *) NULL)
546 j=(ssize_t) (width-i)/2;
548 for (v=(-j); v <= j; v++)
550 for (u=(-j); u <= j; u++)
552 kernel[i][k]=(MagickRealType) (-exp(-((double) u*u+v*v)/(2.0*
553 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
554 normalize+=kernel[i][k];
558 kernel[i][(k-1)/2]=(double) ((-2.0)*normalize);
559 if (sigma < MagickEpsilon)
560 kernel[i][(k-1)/2]=1.0;
562 if (i < (ssize_t) width)
564 for (i-=2; i >= 0; i-=2)
565 kernel[i]=(MagickRealType *) RelinquishAlignedMemory(kernel[i]);
566 kernel=(MagickRealType **) RelinquishAlignedMemory(kernel);
567 edge_image=DestroyImage(edge_image);
568 sharp_image=DestroyImage(sharp_image);
569 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
572 Adaptively sharpen image.
576 image_view=AcquireVirtualCacheView(image,exception);
577 edge_view=AcquireVirtualCacheView(edge_image,exception);
578 sharp_view=AcquireAuthenticCacheView(sharp_image,exception);
579 #if defined(MAGICKCORE_OPENMP_SUPPORT)
580 #pragma omp parallel for schedule(static,4) shared(progress,status) \
581 magick_threads(image,sharp_image,sharp_image->rows,1)
583 for (y=0; y < (ssize_t) sharp_image->rows; y++)
585 register const Quantum
594 if (status == MagickFalse)
596 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
597 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
599 if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
604 for (x=0; x < (ssize_t) sharp_image->columns; x++)
606 register const Quantum
616 j=(ssize_t) ceil((double) width*(1.0-QuantumScale*
617 GetPixelIntensity(edge_image,r))-0.5);
621 if (j > (ssize_t) width)
625 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
626 (ssize_t) ((width-j)/2L),width-j,width-j,exception);
627 if (p == (const Quantum *) NULL)
629 center=(ssize_t) GetPixelChannels(image)*(width-j)*((width-j)/2L)+
630 GetPixelChannels(image)*((width-j)/2);
631 for (i=0; i < (ssize_t) GetPixelChannels(sharp_image); i++)
645 register const MagickRealType
648 register const Quantum
657 channel=GetPixelChannelChannel(image,i);
658 traits=GetPixelChannelTraits(image,channel);
659 sharp_traits=GetPixelChannelTraits(sharp_image,channel);
660 if ((traits == UndefinedPixelTrait) ||
661 (sharp_traits == UndefinedPixelTrait))
663 if (((sharp_traits & CopyPixelTrait) != 0) ||
664 (GetPixelReadMask(image,p+center) == 0))
666 SetPixelChannel(sharp_image,channel,p[center+i],q);
673 if ((sharp_traits & BlendPixelTrait) == 0)
678 for (v=0; v < (ssize_t) (width-j); v++)
680 for (u=0; u < (ssize_t) (width-j); u++)
682 pixel+=(*k)*pixels[i];
685 pixels+=GetPixelChannels(image);
688 gamma=PerceptibleReciprocal(gamma);
689 SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
695 for (v=0; v < (ssize_t) (width-j); v++)
697 for (u=0; u < (ssize_t) (width-j); u++)
699 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
700 pixel+=(*k)*alpha*pixels[i];
703 pixels+=GetPixelChannels(image);
706 gamma=PerceptibleReciprocal(gamma);
707 SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
709 q+=GetPixelChannels(sharp_image);
710 r+=GetPixelChannels(edge_image);
712 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
714 if (image->progress_monitor != (MagickProgressMonitor) NULL)
719 #if defined(MAGICKCORE_OPENMP_SUPPORT)
720 #pragma omp critical (MagickCore_AdaptiveSharpenImage)
722 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
724 if (proceed == MagickFalse)
728 sharp_image->type=image->type;
729 sharp_view=DestroyCacheView(sharp_view);
730 edge_view=DestroyCacheView(edge_view);
731 image_view=DestroyCacheView(image_view);
732 edge_image=DestroyImage(edge_image);
733 for (i=0; i < (ssize_t) width; i+=2)
734 kernel[i]=(MagickRealType *) RelinquishAlignedMemory(kernel[i]);
735 kernel=(MagickRealType **) RelinquishAlignedMemory(kernel);
736 if (status == MagickFalse)
737 sharp_image=DestroyImage(sharp_image);
742 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
746 % B l u r I m a g e %
750 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
752 % BlurImage() blurs an image. We convolve the image with a Gaussian operator
753 % of the given radius and standard deviation (sigma). For reasonable results,
754 % the radius should be larger than sigma. Use a radius of 0 and BlurImage()
755 % selects a suitable radius for you.
757 % The format of the BlurImage method is:
759 % Image *BlurImage(const Image *image,const double radius,
760 % const double sigma,ExceptionInfo *exception)
762 % A description of each parameter follows:
764 % o image: the image.
766 % o radius: the radius of the Gaussian, in pixels, not counting the center
769 % o sigma: the standard deviation of the Gaussian, in pixels.
771 % o exception: return any errors or warnings in this structure.
774 MagickExport Image *BlurImage(const Image *image,const double radius,
775 const double sigma,ExceptionInfo *exception)
778 geometry[MagickPathExtent];
786 assert(image != (const Image *) NULL);
787 assert(image->signature == MagickSignature);
788 if (image->debug != MagickFalse)
789 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
790 assert(exception != (ExceptionInfo *) NULL);
791 assert(exception->signature == MagickSignature);
792 (void) FormatLocaleString(geometry,MagickPathExtent,
793 "blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
794 kernel_info=AcquireKernelInfo(geometry,exception);
795 if (kernel_info == (KernelInfo *) NULL)
796 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
797 blur_image=MorphologyApply(image,ConvolveMorphology,1,kernel_info,
798 UndefinedCompositeOp,0.0,exception);
799 kernel_info=DestroyKernelInfo(kernel_info);
804 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
808 % C o n v o l v e I m a g e %
812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
814 % ConvolveImage() applies a custom convolution kernel to the image.
816 % The format of the ConvolveImage method is:
818 % Image *ConvolveImage(const Image *image,const KernelInfo *kernel,
819 % ExceptionInfo *exception)
821 % A description of each parameter follows:
823 % o image: the image.
825 % o kernel: the filtering kernel.
827 % o exception: return any errors or warnings in this structure.
830 MagickExport Image *ConvolveImage(const Image *image,
831 const KernelInfo *kernel_info,ExceptionInfo *exception)
836 convolve_image=MorphologyApply(image,ConvolveMorphology,1,kernel_info,
837 UndefinedCompositeOp,0.0,exception);
838 return(convolve_image);
842 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
846 % D e s p e c k l e I m a g e %
850 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
852 % DespeckleImage() reduces the speckle noise in an image while perserving the
853 % edges of the original image. A speckle removing filter uses a complementary % hulling technique (raising pixels that are darker than their surrounding
854 % neighbors, then complementarily lowering pixels that are brighter than their
855 % surrounding neighbors) to reduce the speckle index of that image (reference
856 % Crimmins speckle removal).
858 % The format of the DespeckleImage method is:
860 % Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
862 % A description of each parameter follows:
864 % o image: the image.
866 % o exception: return any errors or warnings in this structure.
870 static void Hull(const Image *image,const ssize_t x_offset,
871 const ssize_t y_offset,const size_t columns,const size_t rows,
872 const int polarity,Quantum *restrict f,Quantum *restrict g)
883 assert(image != (const Image *) NULL);
884 assert(image->signature == MagickSignature);
885 if (image->debug != MagickFalse)
886 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
887 assert(f != (Quantum *) NULL);
888 assert(g != (Quantum *) NULL);
891 r=p+(y_offset*(columns+2)+x_offset);
892 #if defined(MAGICKCORE_OPENMP_SUPPORT)
893 #pragma omp parallel for schedule(static,4) \
894 magick_threads(image,image,1,1)
896 for (y=0; y < (ssize_t) rows; y++)
907 for (x=0; x < (ssize_t) columns; x++)
909 v=(MagickRealType) p[i];
910 if ((MagickRealType) r[i] >= (v+ScaleCharToQuantum(2)))
911 v+=ScaleCharToQuantum(1);
916 for (x=0; x < (ssize_t) columns; x++)
918 v=(MagickRealType) p[i];
919 if ((MagickRealType) r[i] <= (v-ScaleCharToQuantum(2)))
920 v-=ScaleCharToQuantum(1);
927 r=q+(y_offset*(columns+2)+x_offset);
928 s=q-(y_offset*(columns+2)+x_offset);
929 #if defined(MAGICKCORE_OPENMP_SUPPORT)
930 #pragma omp parallel for schedule(static,4) \
931 magick_threads(image,image,1,1)
933 for (y=0; y < (ssize_t) rows; y++)
944 for (x=0; x < (ssize_t) columns; x++)
946 v=(MagickRealType) q[i];
947 if (((MagickRealType) s[i] >= (v+ScaleCharToQuantum(2))) &&
948 ((MagickRealType) r[i] > v))
949 v+=ScaleCharToQuantum(1);
954 for (x=0; x < (ssize_t) columns; x++)
956 v=(MagickRealType) q[i];
957 if (((MagickRealType) s[i] <= (v-ScaleCharToQuantum(2))) &&
958 ((MagickRealType) r[i] < v))
959 v-=ScaleCharToQuantum(1);
966 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
968 #define DespeckleImageTag "Despeckle/Image"
999 Allocate despeckled image.
1001 assert(image != (const Image *) NULL);
1002 assert(image->signature == MagickSignature);
1003 if (image->debug != MagickFalse)
1004 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1005 assert(exception != (ExceptionInfo *) NULL);
1006 assert(exception->signature == MagickSignature);
1007 despeckle_image=CloneImage(image,0,0,MagickTrue,exception);
1008 if (despeckle_image == (Image *) NULL)
1009 return((Image *) NULL);
1010 status=SetImageStorageClass(despeckle_image,DirectClass,exception);
1011 if (status == MagickFalse)
1013 despeckle_image=DestroyImage(despeckle_image);
1014 return((Image *) NULL);
1017 Allocate image buffer.
1019 length=(size_t) ((image->columns+2)*(image->rows+2));
1020 pixel_info=AcquireVirtualMemory(length,sizeof(*pixels));
1021 buffer_info=AcquireVirtualMemory(length,sizeof(*buffer));
1022 if ((pixel_info == (MemoryInfo *) NULL) ||
1023 (buffer_info == (MemoryInfo *) NULL))
1025 if (buffer_info != (MemoryInfo *) NULL)
1026 buffer_info=RelinquishVirtualMemory(buffer_info);
1027 if (pixel_info != (MemoryInfo *) NULL)
1028 pixel_info=RelinquishVirtualMemory(pixel_info);
1029 despeckle_image=DestroyImage(despeckle_image);
1030 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1032 pixels=(Quantum *) GetVirtualMemoryBlob(pixel_info);
1033 buffer=(Quantum *) GetVirtualMemoryBlob(buffer_info);
1035 Reduce speckle in the image.
1038 image_view=AcquireVirtualCacheView(image,exception);
1039 despeckle_view=AcquireAuthenticCacheView(despeckle_image,exception);
1040 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1057 if (status == MagickFalse)
1059 channel=GetPixelChannelChannel(image,i);
1060 traits=GetPixelChannelTraits(image,channel);
1061 despeckle_traits=GetPixelChannelTraits(despeckle_image,channel);
1062 if ((traits == UndefinedPixelTrait) ||
1063 (despeckle_traits == UndefinedPixelTrait))
1065 if ((despeckle_traits & CopyPixelTrait) != 0)
1067 (void) ResetMagickMemory(pixels,0,length*sizeof(*pixels));
1068 j=(ssize_t) image->columns+2;
1069 for (y=0; y < (ssize_t) image->rows; y++)
1071 register const Quantum
1074 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1075 if (p == (const Quantum *) NULL)
1081 for (x=0; x < (ssize_t) image->columns; x++)
1084 p+=GetPixelChannels(image);
1088 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1089 for (k=0; k < 4; k++)
1091 Hull(image,X[k],Y[k],image->columns,image->rows,1,pixels,buffer);
1092 Hull(image,-X[k],-Y[k],image->columns,image->rows,1,pixels,buffer);
1093 Hull(image,-X[k],-Y[k],image->columns,image->rows,-1,pixels,buffer);
1094 Hull(image,X[k],Y[k],image->columns,image->rows,-1,pixels,buffer);
1096 j=(ssize_t) image->columns+2;
1097 for (y=0; y < (ssize_t) image->rows; y++)
1105 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1107 if (q == (Quantum *) NULL)
1113 for (x=0; x < (ssize_t) image->columns; x++)
1115 SetPixelChannel(despeckle_image,channel,pixels[j++],q);
1116 q+=GetPixelChannels(despeckle_image);
1118 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1119 if (sync == MagickFalse)
1123 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1128 proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1129 GetPixelChannels(image));
1130 if (proceed == MagickFalse)
1134 despeckle_view=DestroyCacheView(despeckle_view);
1135 image_view=DestroyCacheView(image_view);
1136 buffer_info=RelinquishVirtualMemory(buffer_info);
1137 pixel_info=RelinquishVirtualMemory(pixel_info);
1138 despeckle_image->type=image->type;
1139 if (status == MagickFalse)
1140 despeckle_image=DestroyImage(despeckle_image);
1141 return(despeckle_image);
1145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1149 % E d g e I m a g e %
1153 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1155 % EdgeImage() finds edges in an image. Radius defines the radius of the
1156 % convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1159 % The format of the EdgeImage method is:
1161 % Image *EdgeImage(const Image *image,const double radius,
1162 % ExceptionInfo *exception)
1164 % A description of each parameter follows:
1166 % o image: the image.
1168 % o radius: the radius of the pixel neighborhood.
1170 % o exception: return any errors or warnings in this structure.
1173 MagickExport Image *EdgeImage(const Image *image,const double radius,
1174 ExceptionInfo *exception)
1188 assert(image != (const Image *) NULL);
1189 assert(image->signature == MagickSignature);
1190 if (image->debug != MagickFalse)
1191 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1192 assert(exception != (ExceptionInfo *) NULL);
1193 assert(exception->signature == MagickSignature);
1194 width=GetOptimalKernelWidth1D(radius,0.5);
1195 kernel_info=AcquireKernelInfo((const char *) NULL,exception);
1196 if (kernel_info == (KernelInfo *) NULL)
1197 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1198 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
1199 kernel_info->width=width;
1200 kernel_info->height=width;
1201 kernel_info->x=(ssize_t) (kernel_info->width-1)/2;
1202 kernel_info->y=(ssize_t) (kernel_info->height-1)/2;
1203 kernel_info->signature=MagickSignature;
1204 kernel_info->values=(MagickRealType *) MagickAssumeAligned(
1205 AcquireAlignedMemory(kernel_info->width,kernel_info->height*
1206 sizeof(*kernel_info->values)));
1207 if (kernel_info->values == (MagickRealType *) NULL)
1209 kernel_info=DestroyKernelInfo(kernel_info);
1210 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1212 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
1213 kernel_info->values[i]=(-1.0);
1214 kernel_info->values[i/2]=(double) kernel_info->width*kernel_info->height-1.0;
1215 edge_image=MorphologyApply(image,ConvolveMorphology,1,kernel_info,
1216 UndefinedCompositeOp,0.0,exception);
1217 kernel_info=DestroyKernelInfo(kernel_info);
1222 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1226 % E m b o s s I m a g e %
1230 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1232 % EmbossImage() returns a grayscale image with a three-dimensional effect.
1233 % We convolve the image with a Gaussian operator of the given radius and
1234 % standard deviation (sigma). For reasonable results, radius should be
1235 % larger than sigma. Use a radius of 0 and Emboss() selects a suitable
1238 % The format of the EmbossImage method is:
1240 % Image *EmbossImage(const Image *image,const double radius,
1241 % const double sigma,ExceptionInfo *exception)
1243 % A description of each parameter follows:
1245 % o image: the image.
1247 % o radius: the radius of the pixel neighborhood.
1249 % o sigma: the standard deviation of the Gaussian, in pixels.
1251 % o exception: return any errors or warnings in this structure.
1254 MagickExport Image *EmbossImage(const Image *image,const double radius,
1255 const double sigma,ExceptionInfo *exception)
1279 assert(image != (const Image *) NULL);
1280 assert(image->signature == MagickSignature);
1281 if (image->debug != MagickFalse)
1282 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1283 assert(exception != (ExceptionInfo *) NULL);
1284 assert(exception->signature == MagickSignature);
1285 width=GetOptimalKernelWidth1D(radius,sigma);
1286 kernel_info=AcquireKernelInfo((const char *) NULL,exception);
1287 if (kernel_info == (KernelInfo *) NULL)
1288 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1289 kernel_info->width=width;
1290 kernel_info->height=width;
1291 kernel_info->x=(ssize_t) (width-1)/2;
1292 kernel_info->y=(ssize_t) (width-1)/2;
1293 kernel_info->values=(MagickRealType *) MagickAssumeAligned(
1294 AcquireAlignedMemory(kernel_info->width,kernel_info->width*
1295 sizeof(*kernel_info->values)));
1296 if (kernel_info->values == (MagickRealType *) NULL)
1298 kernel_info=DestroyKernelInfo(kernel_info);
1299 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1301 j=(ssize_t) (kernel_info->width-1)/2;
1304 for (v=(-j); v <= j; v++)
1306 for (u=(-j); u <= j; u++)
1308 kernel_info->values[i]=(MagickRealType) (((u < 0) || (v < 0) ? -8.0 :
1309 8.0)*exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
1310 (2.0*MagickPI*MagickSigma*MagickSigma));
1312 kernel_info->values[i]=0.0;
1318 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
1319 normalize+=kernel_info->values[i];
1320 gamma=PerceptibleReciprocal(normalize);
1321 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
1322 kernel_info->values[i]*=gamma;
1323 emboss_image=MorphologyApply(image,ConvolveMorphology,1,kernel_info,
1324 UndefinedCompositeOp,0.0,exception);
1325 kernel_info=DestroyKernelInfo(kernel_info);
1326 if (emboss_image != (Image *) NULL)
1327 (void) EqualizeImage(emboss_image,exception);
1328 return(emboss_image);
1332 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1336 % G a u s s i a n B l u r I m a g e %
1340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1342 % GaussianBlurImage() blurs an image. We convolve the image with a
1343 % Gaussian operator of the given radius and standard deviation (sigma).
1344 % For reasonable results, the radius should be larger than sigma. Use a
1345 % radius of 0 and GaussianBlurImage() selects a suitable radius for you
1347 % The format of the GaussianBlurImage method is:
1349 % Image *GaussianBlurImage(const Image *image,onst double radius,
1350 % const double sigma,ExceptionInfo *exception)
1352 % A description of each parameter follows:
1354 % o image: the image.
1356 % o radius: the radius of the Gaussian, in pixels, not counting the center
1359 % o sigma: the standard deviation of the Gaussian, in pixels.
1361 % o exception: return any errors or warnings in this structure.
1364 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
1365 const double sigma,ExceptionInfo *exception)
1368 geometry[MagickPathExtent];
1376 assert(image != (const Image *) NULL);
1377 assert(image->signature == MagickSignature);
1378 if (image->debug != MagickFalse)
1379 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1380 assert(exception != (ExceptionInfo *) NULL);
1381 assert(exception->signature == MagickSignature);
1382 (void) FormatLocaleString(geometry,MagickPathExtent,"gaussian:%.20gx%.20g",
1384 kernel_info=AcquireKernelInfo(geometry,exception);
1385 if (kernel_info == (KernelInfo *) NULL)
1386 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1387 blur_image=MorphologyApply(image,ConvolveMorphology,1,kernel_info,
1388 UndefinedCompositeOp,0.0,exception);
1389 kernel_info=DestroyKernelInfo(kernel_info);
1394 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1398 % K u w a h a r a I m a g e %
1402 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1404 % KuwaharaImage() is an edge preserving noise reduction filter.
1406 % The format of the KuwaharaImage method is:
1408 % Image *KuwaharaImage(const Image *image,const double radius,
1409 % const double sigma,ExceptionInfo *exception)
1411 % A description of each parameter follows:
1413 % o image: the image.
1415 % o radius: the square window radius.
1417 % o sigma: the standard deviation of the Gaussian, in pixels.
1419 % o exception: return any errors or warnings in this structure.
1423 static inline MagickRealType GetMeanLuma(const Image *restrict image,
1424 const double *restrict pixel)
1426 if (image->colorspace == GRAYColorspace)
1427 return((MagickRealType) pixel[image->channel_map[GrayPixelChannel].offset]);
1428 return(0.212656f*pixel[image->channel_map[RedPixelChannel].offset]+
1429 0.715158f*pixel[image->channel_map[GreenPixelChannel].offset]+
1430 0.072186f*pixel[image->channel_map[BluePixelChannel].offset]); /* Rec709 */
1433 MagickExport Image *KuwaharaImage(const Image *image,const double radius,
1434 const double sigma,ExceptionInfo *exception)
1436 #define KuwaharaImageTag "Kuwahara/Image"
1459 Initialize Kuwahara image attributes.
1461 assert(image != (Image *) NULL);
1462 assert(image->signature == MagickSignature);
1463 if (image->debug != MagickFalse)
1464 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1465 assert(exception != (ExceptionInfo *) NULL);
1466 assert(exception->signature == MagickSignature);
1467 width=(size_t) radius+1;
1468 gaussian_image=BlurImage(image,radius,sigma,exception);
1469 if (gaussian_image == (Image *) NULL)
1470 return((Image *) NULL);
1471 kuwahara_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1473 if (kuwahara_image == (Image *) NULL)
1475 gaussian_image=DestroyImage(gaussian_image);
1476 return((Image *) NULL);
1478 if (SetImageStorageClass(kuwahara_image,DirectClass,exception) == MagickFalse)
1480 gaussian_image=DestroyImage(gaussian_image);
1481 kuwahara_image=DestroyImage(kuwahara_image);
1482 return((Image *) NULL);
1485 Edge preserving noise reduction filter.
1489 image_view=AcquireVirtualCacheView(gaussian_image,exception);
1490 kuwahara_view=AcquireAuthenticCacheView(kuwahara_image,exception);
1491 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1492 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1493 magick_threads(image,kuwahara_image,image->rows,1)
1495 for (y=0; y < (ssize_t) image->rows; y++)
1503 if (status == MagickFalse)
1505 q=QueueCacheViewAuthenticPixels(kuwahara_view,0,y,kuwahara_image->columns,1,
1507 if (q == (Quantum *) NULL)
1512 for (x=0; x < (ssize_t) image->columns; x++)
1527 min_variance=MagickMaximumValue;
1528 SetGeometry(gaussian_image,&target);
1529 quadrant.width=width;
1530 quadrant.height=width;
1531 for (i=0; i < 4; i++)
1537 mean[MaxPixelChannels],
1552 quadrant.x=x-(ssize_t) (width-1);
1553 quadrant.y=y-(ssize_t) (width-1);
1558 quadrant.y=y-(ssize_t) (width-1);
1563 quadrant.x=x-(ssize_t) (width-1);
1570 p=GetCacheViewVirtualPixels(image_view,quadrant.x,quadrant.y,
1571 quadrant.width,quadrant.height,exception);
1572 if (p == (const Quantum *) NULL)
1574 for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
1577 for (n=0; n < (ssize_t) (width*width); n++)
1579 for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
1580 mean[j]+=(double) k[j];
1581 k+=GetPixelChannels(image);
1583 for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
1584 mean[j]/=(double) (width*width);
1587 for (n=0; n < (ssize_t) (width*width); n++)
1592 luma=GetPixelLuma(gaussian_image,k);
1593 variance+=(luma-GetMeanLuma(gaussian_image,mean))*
1594 (luma-GetMeanLuma(gaussian_image,mean));
1595 k+=GetPixelChannels(image);
1597 if (variance < min_variance)
1599 min_variance=variance;
1608 status=InterpolatePixelChannels(gaussian_image,image_view,kuwahara_image,
1609 UndefinedInterpolatePixel,(double) target.x+target.width/2.0,(double)
1610 target.y+target.height/2.0,q,exception);
1611 q+=GetPixelChannels(kuwahara_image);
1613 if (SyncCacheViewAuthenticPixels(kuwahara_view,exception) == MagickFalse)
1615 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1620 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1621 #pragma omp critical (MagickCore_KuwaharaImage)
1623 proceed=SetImageProgress(image,KuwaharaImageTag,progress++,image->rows);
1624 if (proceed == MagickFalse)
1628 kuwahara_view=DestroyCacheView(kuwahara_view);
1629 image_view=DestroyCacheView(image_view);
1630 gaussian_image=DestroyImage(gaussian_image);
1631 if (status == MagickFalse)
1632 kuwahara_image=DestroyImage(kuwahara_image);
1633 return(kuwahara_image);
1637 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1641 % M o t i o n B l u r I m a g e %
1645 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1647 % MotionBlurImage() simulates motion blur. We convolve the image with a
1648 % Gaussian operator of the given radius and standard deviation (sigma).
1649 % For reasonable results, radius should be larger than sigma. Use a
1650 % radius of 0 and MotionBlurImage() selects a suitable radius for you.
1651 % Angle gives the angle of the blurring motion.
1653 % Andrew Protano contributed this effect.
1655 % The format of the MotionBlurImage method is:
1657 % Image *MotionBlurImage(const Image *image,const double radius,
1658 % const double sigma,const double angle,ExceptionInfo *exception)
1660 % A description of each parameter follows:
1662 % o image: the image.
1664 % o radius: the radius of the Gaussian, in pixels, not counting
1667 % o sigma: the standard deviation of the Gaussian, in pixels.
1669 % o angle: Apply the effect along this angle.
1671 % o exception: return any errors or warnings in this structure.
1675 static MagickRealType *GetMotionBlurKernel(const size_t width,
1686 Generate a 1-D convolution kernel.
1688 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1689 kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
1690 width,sizeof(*kernel)));
1691 if (kernel == (MagickRealType *) NULL)
1694 for (i=0; i < (ssize_t) width; i++)
1696 kernel[i]=(MagickRealType) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
1697 MagickSigma)))/(MagickSQ2PI*MagickSigma));
1698 normalize+=kernel[i];
1700 for (i=0; i < (ssize_t) width; i++)
1701 kernel[i]/=normalize;
1705 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
1706 const double sigma,const double angle,ExceptionInfo *exception)
1708 #define BlurImageTag "Blur/Image"
1742 assert(image != (Image *) NULL);
1743 assert(image->signature == MagickSignature);
1744 if (image->debug != MagickFalse)
1745 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1746 assert(exception != (ExceptionInfo *) NULL);
1747 width=GetOptimalKernelWidth1D(radius,sigma);
1748 kernel=GetMotionBlurKernel(width,sigma);
1749 if (kernel == (MagickRealType *) NULL)
1750 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1751 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
1752 if (offset == (OffsetInfo *) NULL)
1754 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
1755 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1757 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1758 if (blur_image == (Image *) NULL)
1760 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
1761 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
1762 return((Image *) NULL);
1764 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
1766 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
1767 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
1768 blur_image=DestroyImage(blur_image);
1769 return((Image *) NULL);
1771 point.x=(double) width*sin(DegreesToRadians(angle));
1772 point.y=(double) width*cos(DegreesToRadians(angle));
1773 for (i=0; i < (ssize_t) width; i++)
1775 offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
1776 offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
1783 image_view=AcquireVirtualCacheView(image,exception);
1784 motion_view=AcquireVirtualCacheView(image,exception);
1785 blur_view=AcquireAuthenticCacheView(blur_image,exception);
1786 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1787 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1788 magick_threads(image,blur_image,image->rows,1)
1790 for (y=0; y < (ssize_t) image->rows; y++)
1792 register const Quantum
1801 if (status == MagickFalse)
1803 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1804 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
1806 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1811 for (x=0; x < (ssize_t) image->columns; x++)
1816 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1830 register const Quantum
1833 register MagickRealType
1839 channel=GetPixelChannelChannel(image,i);
1840 traits=GetPixelChannelTraits(image,channel);
1841 blur_traits=GetPixelChannelTraits(blur_image,channel);
1842 if ((traits == UndefinedPixelTrait) ||
1843 (blur_traits == UndefinedPixelTrait))
1845 if (((blur_traits & CopyPixelTrait) != 0) ||
1846 (GetPixelReadMask(image,p) == 0))
1848 SetPixelChannel(blur_image,channel,p[i],q);
1853 if ((blur_traits & BlendPixelTrait) == 0)
1855 for (j=0; j < (ssize_t) width; j++)
1857 r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+
1858 offset[j].y,1,1,exception);
1859 if (r == (const Quantum *) NULL)
1867 SetPixelChannel(blur_image,channel,ClampToQuantum(pixel),q);
1872 for (j=0; j < (ssize_t) width; j++)
1874 r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+offset[j].y,1,
1876 if (r == (const Quantum *) NULL)
1881 alpha=(double) (QuantumScale*GetPixelAlpha(image,r));
1882 pixel+=(*k)*alpha*r[i];
1886 gamma=PerceptibleReciprocal(gamma);
1887 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
1889 p+=GetPixelChannels(image);
1890 q+=GetPixelChannels(blur_image);
1892 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1894 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1899 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1900 #pragma omp critical (MagickCore_MotionBlurImage)
1902 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
1903 if (proceed == MagickFalse)
1907 blur_view=DestroyCacheView(blur_view);
1908 motion_view=DestroyCacheView(motion_view);
1909 image_view=DestroyCacheView(image_view);
1910 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
1911 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
1912 if (status == MagickFalse)
1913 blur_image=DestroyImage(blur_image);
1918 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1922 % P r e v i e w I m a g e %
1926 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1928 % PreviewImage() tiles 9 thumbnails of the specified image with an image
1929 % processing operation applied with varying parameters. This may be helpful
1930 % pin-pointing an appropriate parameter for a particular image processing
1933 % The format of the PreviewImages method is:
1935 % Image *PreviewImages(const Image *image,const PreviewType preview,
1936 % ExceptionInfo *exception)
1938 % A description of each parameter follows:
1940 % o image: the image.
1942 % o preview: the image processing operation.
1944 % o exception: return any errors or warnings in this structure.
1947 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
1948 ExceptionInfo *exception)
1950 #define NumberTiles 9
1951 #define PreviewImageTag "Preview/Image"
1952 #define DefaultPreviewGeometry "204x204+10+10"
1955 factor[MagickPathExtent],
1956 label[MagickPathExtent];
2001 Open output image file.
2003 assert(image != (Image *) NULL);
2004 assert(image->signature == MagickSignature);
2005 if (image->debug != MagickFalse)
2006 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2010 preview_info=AcquireImageInfo();
2011 SetGeometry(image,&geometry);
2012 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2013 &geometry.width,&geometry.height);
2014 images=NewImageList();
2016 GetQuantizeInfo(&quantize_info);
2022 for (i=0; i < NumberTiles; i++)
2024 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2025 if (thumbnail == (Image *) NULL)
2027 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2029 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel,exception);
2030 if (i == (NumberTiles/2))
2032 (void) QueryColorCompliance("#dfdfdf",AllCompliance,
2033 &thumbnail->matte_color,exception);
2034 AppendImageToList(&images,thumbnail);
2042 preview_image=RotateImage(thumbnail,degrees,exception);
2043 (void) FormatLocaleString(label,MagickPathExtent,"rotate %g",degrees);
2049 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
2050 (void) FormatLocaleString(label,MagickPathExtent,"shear %gx%g",degrees,
2056 x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
2057 y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
2058 preview_image=RollImage(thumbnail,x,y,exception);
2059 (void) FormatLocaleString(label,MagickPathExtent,"roll %+.20gx%+.20g",
2060 (double) x,(double) y);
2065 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2066 if (preview_image == (Image *) NULL)
2068 (void) FormatLocaleString(factor,MagickPathExtent,"100,100,%g",2.0*
2070 (void) ModulateImage(preview_image,factor,exception);
2071 (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
2074 case SaturationPreview:
2076 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2077 if (preview_image == (Image *) NULL)
2079 (void) FormatLocaleString(factor,MagickPathExtent,"100,%g",2.0*percentage);
2080 (void) ModulateImage(preview_image,factor,exception);
2081 (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
2084 case BrightnessPreview:
2086 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2087 if (preview_image == (Image *) NULL)
2089 (void) FormatLocaleString(factor,MagickPathExtent,"%g",2.0*percentage);
2090 (void) ModulateImage(preview_image,factor,exception);
2091 (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
2097 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2098 if (preview_image == (Image *) NULL)
2101 (void) GammaImage(preview_image,gamma,exception);
2102 (void) FormatLocaleString(label,MagickPathExtent,"gamma %g",gamma);
2107 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2108 if (preview_image != (Image *) NULL)
2109 for (x=0; x < i; x++)
2110 (void) ContrastImage(preview_image,MagickTrue,exception);
2111 (void) FormatLocaleString(label,MagickPathExtent,"contrast (%.20g)",
2117 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2118 if (preview_image == (Image *) NULL)
2120 for (x=0; x < i; x++)
2121 (void) ContrastImage(preview_image,MagickFalse,exception);
2122 (void) FormatLocaleString(label,MagickPathExtent,"+contrast (%.20g)",
2126 case GrayscalePreview:
2128 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2129 if (preview_image == (Image *) NULL)
2132 quantize_info.number_colors=colors;
2133 quantize_info.colorspace=GRAYColorspace;
2134 (void) QuantizeImage(&quantize_info,preview_image,exception);
2135 (void) FormatLocaleString(label,MagickPathExtent,
2136 "-colorspace gray -colors %.20g",(double) colors);
2139 case QuantizePreview:
2141 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2142 if (preview_image == (Image *) NULL)
2145 quantize_info.number_colors=colors;
2146 (void) QuantizeImage(&quantize_info,preview_image,exception);
2147 (void) FormatLocaleString(label,MagickPathExtent,"colors %.20g",(double)
2151 case DespecklePreview:
2153 for (x=0; x < (i-1); x++)
2155 preview_image=DespeckleImage(thumbnail,exception);
2156 if (preview_image == (Image *) NULL)
2158 thumbnail=DestroyImage(thumbnail);
2159 thumbnail=preview_image;
2161 preview_image=DespeckleImage(thumbnail,exception);
2162 if (preview_image == (Image *) NULL)
2164 (void) FormatLocaleString(label,MagickPathExtent,"despeckle (%.20g)",
2168 case ReduceNoisePreview:
2170 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
2171 (size_t) radius,exception);
2172 (void) FormatLocaleString(label,MagickPathExtent,"noise %g",radius);
2175 case AddNoisePreview:
2181 (void) CopyMagickString(factor,"uniform",MagickPathExtent);
2186 (void) CopyMagickString(factor,"gaussian",MagickPathExtent);
2191 (void) CopyMagickString(factor,"multiplicative",MagickPathExtent);
2196 (void) CopyMagickString(factor,"impulse",MagickPathExtent);
2201 (void) CopyMagickString(factor,"laplacian",MagickPathExtent);
2206 (void) CopyMagickString(factor,"Poisson",MagickPathExtent);
2211 (void) CopyMagickString(thumbnail->magick,"NULL",MagickPathExtent);
2215 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
2216 (size_t) i,exception);
2217 (void) FormatLocaleString(label,MagickPathExtent,"+noise %s",factor);
2220 case SharpenPreview:
2222 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
2223 (void) FormatLocaleString(label,MagickPathExtent,"sharpen %gx%g",radius,
2229 preview_image=BlurImage(thumbnail,radius,sigma,exception);
2230 (void) FormatLocaleString(label,MagickPathExtent,"blur %gx%g",radius,
2234 case ThresholdPreview:
2236 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2237 if (preview_image == (Image *) NULL)
2239 (void) BilevelImage(thumbnail,(double) (percentage*((double)
2240 QuantumRange+1.0))/100.0,exception);
2241 (void) FormatLocaleString(label,MagickPathExtent,"threshold %g",(double)
2242 (percentage*((double) QuantumRange+1.0))/100.0);
2245 case EdgeDetectPreview:
2247 preview_image=EdgeImage(thumbnail,radius,exception);
2248 (void) FormatLocaleString(label,MagickPathExtent,"edge %g",radius);
2253 preview_image=SpreadImage(thumbnail,radius,thumbnail->interpolate,
2255 (void) FormatLocaleString(label,MagickPathExtent,"spread %g",radius+0.5);
2258 case SolarizePreview:
2260 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2261 if (preview_image == (Image *) NULL)
2263 (void) SolarizeImage(preview_image,(double) QuantumRange*percentage/
2265 (void) FormatLocaleString(label,MagickPathExtent,"solarize %g",
2266 (QuantumRange*percentage)/100.0);
2272 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2274 (void) FormatLocaleString(label,MagickPathExtent,"shade %gx%g",degrees,
2280 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2281 if (preview_image == (Image *) NULL)
2283 geometry.width=(size_t) (2*i+2);
2284 geometry.height=(size_t) (2*i+2);
2287 (void) RaiseImage(preview_image,&geometry,MagickTrue,exception);
2288 (void) FormatLocaleString(label,MagickPathExtent,
2289 "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
2290 geometry.height,(double) geometry.x,(double) geometry.y);
2293 case SegmentPreview:
2295 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2296 if (preview_image == (Image *) NULL)
2299 (void) SegmentImage(preview_image,sRGBColorspace,MagickFalse,threshold,
2300 threshold,exception);
2301 (void) FormatLocaleString(label,MagickPathExtent,"segment %gx%g",
2302 threshold,threshold);
2307 preview_image=SwirlImage(thumbnail,degrees,image->interpolate,
2309 (void) FormatLocaleString(label,MagickPathExtent,"swirl %g",degrees);
2313 case ImplodePreview:
2316 preview_image=ImplodeImage(thumbnail,degrees,image->interpolate,
2318 (void) FormatLocaleString(label,MagickPathExtent,"implode %g",degrees);
2324 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,
2325 image->interpolate,exception);
2326 (void) FormatLocaleString(label,MagickPathExtent,"wave %gx%g",0.5*degrees,
2330 case OilPaintPreview:
2332 preview_image=OilPaintImage(thumbnail,(double) radius,(double) sigma,
2334 (void) FormatLocaleString(label,MagickPathExtent,"charcoal %gx%g",radius,
2338 case CharcoalDrawingPreview:
2340 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2342 (void) FormatLocaleString(label,MagickPathExtent,"charcoal %gx%g",radius,
2349 filename[MagickPathExtent];
2357 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2358 if (preview_image == (Image *) NULL)
2360 preview_info->quality=(size_t) percentage;
2361 (void) FormatLocaleString(factor,MagickPathExtent,"%.20g",(double)
2362 preview_info->quality);
2363 file=AcquireUniqueFileResource(filename);
2366 (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
2367 "jpeg:%s",filename);
2368 status=WriteImage(preview_info,preview_image,exception);
2369 if (status != MagickFalse)
2374 (void) CopyMagickString(preview_info->filename,
2375 preview_image->filename,MagickPathExtent);
2376 quality_image=ReadImage(preview_info,exception);
2377 if (quality_image != (Image *) NULL)
2379 preview_image=DestroyImage(preview_image);
2380 preview_image=quality_image;
2383 (void) RelinquishUniqueFileResource(preview_image->filename);
2384 if ((GetBlobSize(preview_image)/1024) >= 1024)
2385 (void) FormatLocaleString(label,MagickPathExtent,"quality %s\n%gmb ",
2386 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2389 if (GetBlobSize(preview_image) >= 1024)
2390 (void) FormatLocaleString(label,MagickPathExtent,
2391 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
2392 GetBlobSize(preview_image))/1024.0);
2394 (void) FormatLocaleString(label,MagickPathExtent,"quality %s\n%.20gb ",
2395 factor,(double) ((MagickOffsetType) GetBlobSize(thumbnail)));
2399 thumbnail=DestroyImage(thumbnail);
2403 if (preview_image == (Image *) NULL)
2405 (void) DeleteImageProperty(preview_image,"label");
2406 (void) SetImageProperty(preview_image,"label",label,exception);
2407 AppendImageToList(&images,preview_image);
2408 proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2410 if (proceed == MagickFalse)
2413 if (images == (Image *) NULL)
2415 preview_info=DestroyImageInfo(preview_info);
2416 return((Image *) NULL);
2421 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2422 (void) CopyMagickString(montage_info->filename,image->filename,MagickPathExtent);
2423 montage_info->shadow=MagickTrue;
2424 (void) CloneString(&montage_info->tile,"3x3");
2425 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2426 (void) CloneString(&montage_info->frame,DefaultTileFrame);
2427 montage_image=MontageImages(images,montage_info,exception);
2428 montage_info=DestroyMontageInfo(montage_info);
2429 images=DestroyImageList(images);
2430 if (montage_image == (Image *) NULL)
2431 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2432 if (montage_image->montage != (char *) NULL)
2435 Free image directory.
2437 montage_image->montage=(char *) RelinquishMagickMemory(
2438 montage_image->montage);
2439 if (image->directory != (char *) NULL)
2440 montage_image->directory=(char *) RelinquishMagickMemory(
2441 montage_image->directory);
2443 preview_info=DestroyImageInfo(preview_info);
2444 return(montage_image);
2448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2452 % R o t a t i o n a l B l u r I m a g e %
2456 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2458 % RotationalBlurImage() applies a radial blur to the image.
2460 % Andrew Protano contributed this effect.
2462 % The format of the RotationalBlurImage method is:
2464 % Image *RotationalBlurImage(const Image *image,const double angle,
2465 % ExceptionInfo *exception)
2467 % A description of each parameter follows:
2469 % o image: the image.
2471 % o angle: the angle of the radial blur.
2475 % o exception: return any errors or warnings in this structure.
2478 MagickExport Image *RotationalBlurImage(const Image *image,const double angle,
2479 ExceptionInfo *exception)
2515 Allocate blur image.
2517 assert(image != (Image *) NULL);
2518 assert(image->signature == MagickSignature);
2519 if (image->debug != MagickFalse)
2520 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2521 assert(exception != (ExceptionInfo *) NULL);
2522 assert(exception->signature == MagickSignature);
2523 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2524 if (blur_image == (Image *) NULL)
2525 return((Image *) NULL);
2526 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2528 blur_image=DestroyImage(blur_image);
2529 return((Image *) NULL);
2531 blur_center.x=(double) (image->columns-1)/2.0;
2532 blur_center.y=(double) (image->rows-1)/2.0;
2533 blur_radius=hypot(blur_center.x,blur_center.y);
2534 n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
2535 theta=DegreesToRadians(angle)/(double) (n-1);
2536 cos_theta=(double *) AcquireQuantumMemory((size_t) n,
2537 sizeof(*cos_theta));
2538 sin_theta=(double *) AcquireQuantumMemory((size_t) n,
2539 sizeof(*sin_theta));
2540 if ((cos_theta == (double *) NULL) ||
2541 (sin_theta == (double *) NULL))
2543 blur_image=DestroyImage(blur_image);
2544 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2546 offset=theta*(double) (n-1)/2.0;
2547 for (i=0; i < (ssize_t) n; i++)
2549 cos_theta[i]=cos((double) (theta*i-offset));
2550 sin_theta[i]=sin((double) (theta*i-offset));
2557 image_view=AcquireVirtualCacheView(image,exception);
2558 radial_view=AcquireVirtualCacheView(image,exception);
2559 blur_view=AcquireAuthenticCacheView(blur_image,exception);
2560 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2561 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2562 magick_threads(image,blur_image,image->rows,1)
2564 for (y=0; y < (ssize_t) image->rows; y++)
2566 register const Quantum
2575 if (status == MagickFalse)
2577 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2578 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2580 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2585 for (x=0; x < (ssize_t) image->columns; x++)
2599 center.x=(double) x-blur_center.x;
2600 center.y=(double) y-blur_center.y;
2601 radius=hypot((double) center.x,center.y);
2606 step=(size_t) (blur_radius/radius);
2613 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2626 register const Quantum
2632 channel=GetPixelChannelChannel(image,i);
2633 traits=GetPixelChannelTraits(image,channel);
2634 blur_traits=GetPixelChannelTraits(blur_image,channel);
2635 if ((traits == UndefinedPixelTrait) ||
2636 (blur_traits == UndefinedPixelTrait))
2638 if (((blur_traits & CopyPixelTrait) != 0) ||
2639 (GetPixelReadMask(image,p) == 0))
2641 SetPixelChannel(blur_image,channel,p[i],q);
2646 if ((blur_traits & BlendPixelTrait) == 0)
2648 for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2650 r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
2651 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2652 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2654 if (r == (const Quantum *) NULL)
2662 gamma=PerceptibleReciprocal(gamma);
2663 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2666 for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2668 r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
2669 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2670 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2672 if (r == (const Quantum *) NULL)
2677 pixel+=GetPixelAlpha(image,r)*r[i];
2678 gamma+=GetPixelAlpha(image,r);
2680 gamma=PerceptibleReciprocal(gamma);
2681 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2683 p+=GetPixelChannels(image);
2684 q+=GetPixelChannels(blur_image);
2686 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2688 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2693 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2694 #pragma omp critical (MagickCore_RotationalBlurImage)
2696 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2697 if (proceed == MagickFalse)
2701 blur_view=DestroyCacheView(blur_view);
2702 radial_view=DestroyCacheView(radial_view);
2703 image_view=DestroyCacheView(image_view);
2704 cos_theta=(double *) RelinquishMagickMemory(cos_theta);
2705 sin_theta=(double *) RelinquishMagickMemory(sin_theta);
2706 if (status == MagickFalse)
2707 blur_image=DestroyImage(blur_image);
2712 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2716 % S e l e c t i v e B l u r I m a g e %
2720 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2722 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
2723 % It is similar to the unsharpen mask that sharpens everything with contrast
2724 % above a certain threshold.
2726 % The format of the SelectiveBlurImage method is:
2728 % Image *SelectiveBlurImage(const Image *image,const double radius,
2729 % const double sigma,const double threshold,ExceptionInfo *exception)
2731 % A description of each parameter follows:
2733 % o image: the image.
2735 % o radius: the radius of the Gaussian, in pixels, not counting the center
2738 % o sigma: the standard deviation of the Gaussian, in pixels.
2740 % o threshold: only pixels within this contrast threshold are included
2741 % in the blur operation.
2743 % o exception: return any errors or warnings in this structure.
2746 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
2747 const double sigma,const double threshold,ExceptionInfo *exception)
2749 #define SelectiveBlurImageTag "SelectiveBlur/Image"
2783 Initialize blur image attributes.
2785 assert(image != (Image *) NULL);
2786 assert(image->signature == MagickSignature);
2787 if (image->debug != MagickFalse)
2788 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2789 assert(exception != (ExceptionInfo *) NULL);
2790 assert(exception->signature == MagickSignature);
2791 width=GetOptimalKernelWidth1D(radius,sigma);
2792 kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
2793 width,width*sizeof(*kernel)));
2794 if (kernel == (MagickRealType *) NULL)
2795 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2796 j=(ssize_t) (width-1)/2;
2798 for (v=(-j); v <= j; v++)
2800 for (u=(-j); u <= j; u++)
2801 kernel[i++]=(MagickRealType) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
2802 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
2804 if (image->debug != MagickFalse)
2807 format[MagickPathExtent],
2810 register const MagickRealType
2817 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2818 " SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
2820 message=AcquireString("");
2822 for (v=0; v < (ssize_t) width; v++)
2825 (void) FormatLocaleString(format,MagickPathExtent,"%.20g: ",(double) v);
2826 (void) ConcatenateString(&message,format);
2827 for (u=0; u < (ssize_t) width; u++)
2829 (void) FormatLocaleString(format,MagickPathExtent,"%+f ",(double) *k++);
2830 (void) ConcatenateString(&message,format);
2832 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
2834 message=DestroyString(message);
2836 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2837 if (blur_image == (Image *) NULL)
2838 return((Image *) NULL);
2839 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2841 blur_image=DestroyImage(blur_image);
2842 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2843 return((Image *) NULL);
2845 luminance_image=CloneImage(image,0,0,MagickTrue,exception);
2846 if (luminance_image == (Image *) NULL)
2848 blur_image=DestroyImage(blur_image);
2849 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2850 return((Image *) NULL);
2852 status=TransformImageColorspace(luminance_image,GRAYColorspace,exception);
2853 if (status == MagickFalse)
2855 luminance_image=DestroyImage(luminance_image);
2856 blur_image=DestroyImage(blur_image);
2857 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2858 return((Image *) NULL);
2861 Threshold blur image.
2865 center=(ssize_t) (GetPixelChannels(image)*(image->columns+width)*
2866 ((width-1)/2L)+GetPixelChannels(image)*((width-1)/2L));
2867 image_view=AcquireVirtualCacheView(image,exception);
2868 luminance_view=AcquireVirtualCacheView(luminance_image,exception);
2869 blur_view=AcquireAuthenticCacheView(blur_image,exception);
2870 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2871 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2872 magick_threads(image,blur_image,image->rows,1)
2874 for (y=0; y < (ssize_t) image->rows; y++)
2882 register const Quantum
2892 if (status == MagickFalse)
2894 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) (width-1)/2L),y-(ssize_t)
2895 ((width-1)/2L),image->columns+width,width,exception);
2896 l=GetCacheViewVirtualPixels(luminance_view,-((ssize_t) (width-1)/2L),y-
2897 (ssize_t) ((width-1)/2L),luminance_image->columns+width,width,exception);
2898 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2900 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2905 for (x=0; x < (ssize_t) image->columns; x++)
2913 intensity=GetPixelIntensity(image,p+center);
2914 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2928 register const MagickRealType
2931 register const Quantum
2932 *restrict luminance_pixels,
2941 channel=GetPixelChannelChannel(image,i);
2942 traits=GetPixelChannelTraits(image,channel);
2943 blur_traits=GetPixelChannelTraits(blur_image,channel);
2944 if ((traits == UndefinedPixelTrait) ||
2945 (blur_traits == UndefinedPixelTrait))
2947 if (((blur_traits & CopyPixelTrait) != 0) ||
2948 (GetPixelReadMask(image,p+center) == 0))
2950 SetPixelChannel(blur_image,channel,p[center+i],q);
2958 if ((blur_traits & BlendPixelTrait) == 0)
2960 for (v=0; v < (ssize_t) width; v++)
2962 for (u=0; u < (ssize_t) width; u++)
2964 contrast=GetPixelIntensity(luminance_image,luminance_pixels)-
2966 if (fabs(contrast) < threshold)
2968 pixel+=(*k)*pixels[i];
2972 pixels+=GetPixelChannels(image);
2973 luminance_pixels+=GetPixelChannels(luminance_image);
2975 pixels+=GetPixelChannels(image)*image->columns;
2976 luminance_pixels+=GetPixelChannels(luminance_image)*
2977 luminance_image->columns;
2979 if (fabs((double) gamma) < MagickEpsilon)
2981 SetPixelChannel(blur_image,channel,p[center+i],q);
2984 gamma=PerceptibleReciprocal(gamma);
2985 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2988 for (v=0; v < (ssize_t) width; v++)
2990 for (u=0; u < (ssize_t) width; u++)
2992 contrast=GetPixelIntensity(image,pixels)-intensity;
2993 if (fabs(contrast) < threshold)
2995 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
2996 pixel+=(*k)*alpha*pixels[i];
3000 pixels+=GetPixelChannels(image);
3001 luminance_pixels+=GetPixelChannels(luminance_image);
3003 pixels+=GetPixelChannels(image)*image->columns;
3004 luminance_pixels+=GetPixelChannels(luminance_image)*
3005 luminance_image->columns;
3007 if (fabs((double) gamma) < MagickEpsilon)
3009 SetPixelChannel(blur_image,channel,p[center+i],q);
3012 gamma=PerceptibleReciprocal(gamma);
3013 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3015 p+=GetPixelChannels(image);
3016 l+=GetPixelChannels(luminance_image);
3017 q+=GetPixelChannels(blur_image);
3019 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3020 if (sync == MagickFalse)
3022 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3027 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3028 #pragma omp critical (MagickCore_SelectiveBlurImage)
3030 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
3032 if (proceed == MagickFalse)
3036 blur_image->type=image->type;
3037 blur_view=DestroyCacheView(blur_view);
3038 image_view=DestroyCacheView(image_view);
3039 luminance_image=DestroyImage(luminance_image);
3040 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3041 if (status == MagickFalse)
3042 blur_image=DestroyImage(blur_image);
3047 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3051 % S h a d e I m a g e %
3055 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3057 % ShadeImage() shines a distant light on an image to create a
3058 % three-dimensional effect. You control the positioning of the light with
3059 % azimuth and elevation; azimuth is measured in degrees off the x axis
3060 % and elevation is measured in pixels above the Z axis.
3062 % The format of the ShadeImage method is:
3064 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3065 % const double azimuth,const double elevation,ExceptionInfo *exception)
3067 % A description of each parameter follows:
3069 % o image: the image.
3071 % o gray: A value other than zero shades the intensity of each pixel.
3073 % o azimuth, elevation: Define the light source direction.
3075 % o exception: return any errors or warnings in this structure.
3078 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3079 const double azimuth,const double elevation,ExceptionInfo *exception)
3081 #define ShadeImageTag "Shade/Image"
3104 Initialize shaded image attributes.
3106 assert(image != (const Image *) NULL);
3107 assert(image->signature == MagickSignature);
3108 if (image->debug != MagickFalse)
3109 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3110 assert(exception != (ExceptionInfo *) NULL);
3111 assert(exception->signature == MagickSignature);
3112 linear_image=CloneImage(image,0,0,MagickTrue,exception);
3113 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3114 if ((linear_image == (Image *) NULL) || (shade_image == (Image *) NULL))
3116 if (linear_image != (Image *) NULL)
3117 linear_image=DestroyImage(linear_image);
3118 if (shade_image != (Image *) NULL)
3119 shade_image=DestroyImage(shade_image);
3120 return((Image *) NULL);
3122 if (SetImageStorageClass(shade_image,DirectClass,exception) == MagickFalse)
3124 linear_image=DestroyImage(linear_image);
3125 shade_image=DestroyImage(shade_image);
3126 return((Image *) NULL);
3129 Compute the light vector.
3131 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3132 cos(DegreesToRadians(elevation));
3133 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3134 cos(DegreesToRadians(elevation));
3135 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3141 image_view=AcquireVirtualCacheView(linear_image,exception);
3142 shade_view=AcquireAuthenticCacheView(shade_image,exception);
3143 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3144 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3145 magick_threads(linear_image,shade_image,linear_image->rows,1)
3147 for (y=0; y < (ssize_t) linear_image->rows; y++)
3157 register const Quantum
3169 if (status == MagickFalse)
3171 p=GetCacheViewVirtualPixels(image_view,-1,y-1,linear_image->columns+2,3,
3173 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3175 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3181 Shade this row of pixels.
3183 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
3184 pre=p+GetPixelChannels(linear_image);
3185 center=pre+(linear_image->columns+2)*GetPixelChannels(linear_image);
3186 post=center+(linear_image->columns+2)*GetPixelChannels(linear_image);
3187 for (x=0; x < (ssize_t) linear_image->columns; x++)
3193 Determine the surface normal and compute shading.
3196 GetPixelIntensity(linear_image,pre-GetPixelChannels(linear_image))+
3197 GetPixelIntensity(linear_image,center-GetPixelChannels(linear_image))+
3198 GetPixelIntensity(linear_image,post-GetPixelChannels(linear_image))-
3199 GetPixelIntensity(linear_image,pre+GetPixelChannels(linear_image))-
3200 GetPixelIntensity(linear_image,center+GetPixelChannels(linear_image))-
3201 GetPixelIntensity(linear_image,post+GetPixelChannels(linear_image)));
3203 GetPixelIntensity(linear_image,post-GetPixelChannels(linear_image))+
3204 GetPixelIntensity(linear_image,post)+
3205 GetPixelIntensity(linear_image,post+GetPixelChannels(linear_image))-
3206 GetPixelIntensity(linear_image,pre-GetPixelChannels(linear_image))-
3207 GetPixelIntensity(linear_image,pre)-
3208 GetPixelIntensity(linear_image,pre+GetPixelChannels(linear_image)));
3209 if ((normal.x == 0.0) && (normal.y == 0.0))
3214 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3215 if (distance > MagickEpsilon)
3217 normal_distance=normal.x*normal.x+normal.y*normal.y+
3219 if (normal_distance > (MagickEpsilon*MagickEpsilon))
3220 shade=distance/sqrt((double) normal_distance);
3223 for (i=0; i < (ssize_t) GetPixelChannels(linear_image); i++)
3232 channel=GetPixelChannelChannel(linear_image,i);
3233 traits=GetPixelChannelTraits(linear_image,channel);
3234 shade_traits=GetPixelChannelTraits(shade_image,channel);
3235 if ((traits == UndefinedPixelTrait) ||
3236 (shade_traits == UndefinedPixelTrait))
3238 if (((shade_traits & CopyPixelTrait) != 0) ||
3239 (GetPixelReadMask(linear_image,center) == 0))
3241 SetPixelChannel(shade_image,channel,center[i],q);
3244 if ((traits & UpdatePixelTrait) == 0)
3246 SetPixelChannel(shade_image,channel,center[i],q);
3249 if (gray != MagickFalse)
3251 SetPixelChannel(shade_image,channel,ClampToQuantum(shade),q);
3254 SetPixelChannel(shade_image,channel,ClampToQuantum(QuantumScale*shade*
3257 pre+=GetPixelChannels(linear_image);
3258 center+=GetPixelChannels(linear_image);
3259 post+=GetPixelChannels(linear_image);
3260 q+=GetPixelChannels(shade_image);
3262 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3264 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3269 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3270 #pragma omp critical (MagickCore_ShadeImage)
3272 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
3273 if (proceed == MagickFalse)
3277 shade_view=DestroyCacheView(shade_view);
3278 image_view=DestroyCacheView(image_view);
3279 linear_image=DestroyImage(linear_image);
3280 if (status == MagickFalse)
3281 shade_image=DestroyImage(shade_image);
3282 return(shade_image);
3286 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3290 % S h a r p e n I m a g e %
3294 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3296 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
3297 % operator of the given radius and standard deviation (sigma). For
3298 % reasonable results, radius should be larger than sigma. Use a radius of 0
3299 % and SharpenImage() selects a suitable radius for you.
3301 % Using a separable kernel would be faster, but the negative weights cancel
3302 % out on the corners of the kernel producing often undesirable ringing in the
3303 % filtered result; this can be avoided by using a 2D gaussian shaped image
3304 % sharpening kernel instead.
3306 % The format of the SharpenImage method is:
3308 % Image *SharpenImage(const Image *image,const double radius,
3309 % const double sigma,ExceptionInfo *exception)
3311 % A description of each parameter follows:
3313 % o image: the image.
3315 % o radius: the radius of the Gaussian, in pixels, not counting the center
3318 % o sigma: the standard deviation of the Laplacian, in pixels.
3320 % o exception: return any errors or warnings in this structure.
3323 MagickExport Image *SharpenImage(const Image *image,const double radius,
3324 const double sigma,ExceptionInfo *exception)
3347 assert(image != (const Image *) NULL);
3348 assert(image->signature == MagickSignature);
3349 if (image->debug != MagickFalse)
3350 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3351 assert(exception != (ExceptionInfo *) NULL);
3352 assert(exception->signature == MagickSignature);
3353 width=GetOptimalKernelWidth2D(radius,sigma);
3354 kernel_info=AcquireKernelInfo((const char *) NULL,exception);
3355 if (kernel_info == (KernelInfo *) NULL)
3356 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3357 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
3358 kernel_info->width=width;
3359 kernel_info->height=width;
3360 kernel_info->x=(ssize_t) (width-1)/2;
3361 kernel_info->y=(ssize_t) (width-1)/2;
3362 kernel_info->signature=MagickSignature;
3363 kernel_info->values=(MagickRealType *) MagickAssumeAligned(
3364 AcquireAlignedMemory(kernel_info->width,kernel_info->height*
3365 sizeof(*kernel_info->values)));
3366 if (kernel_info->values == (MagickRealType *) NULL)
3368 kernel_info=DestroyKernelInfo(kernel_info);
3369 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3372 j=(ssize_t) (kernel_info->width-1)/2;
3374 for (v=(-j); v <= j; v++)
3376 for (u=(-j); u <= j; u++)
3378 kernel_info->values[i]=(MagickRealType) (-exp(-((double) u*u+v*v)/(2.0*
3379 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3380 normalize+=kernel_info->values[i];
3384 kernel_info->values[i/2]=(double) ((-2.0)*normalize);
3386 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
3387 normalize+=kernel_info->values[i];
3388 gamma=PerceptibleReciprocal(normalize);
3389 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
3390 kernel_info->values[i]*=gamma;
3391 sharp_image=MorphologyApply(image,ConvolveMorphology,1,kernel_info,
3392 UndefinedCompositeOp,0.0,exception);
3393 kernel_info=DestroyKernelInfo(kernel_info);
3394 return(sharp_image);
3398 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3402 % S p r e a d I m a g e %
3406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3408 % SpreadImage() is a special effects method that randomly displaces each
3409 % pixel in a block defined by the radius parameter.
3411 % The format of the SpreadImage method is:
3413 % Image *SpreadImage(const Image *image,const double radius,
3414 % const PixelInterpolateMethod method,ExceptionInfo *exception)
3416 % A description of each parameter follows:
3418 % o image: the image.
3420 % o radius: choose a random pixel in a neighborhood of this extent.
3422 % o method: the pixel interpolation method.
3424 % o exception: return any errors or warnings in this structure.
3427 MagickExport Image *SpreadImage(const Image *image,const double radius,
3428 const PixelInterpolateMethod method,ExceptionInfo *exception)
3430 #define SpreadImageTag "Spread/Image"
3446 **restrict random_info;
3454 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3460 Initialize spread image attributes.
3462 assert(image != (Image *) NULL);
3463 assert(image->signature == MagickSignature);
3464 if (image->debug != MagickFalse)
3465 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3466 assert(exception != (ExceptionInfo *) NULL);
3467 assert(exception->signature == MagickSignature);
3468 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3470 if (spread_image == (Image *) NULL)
3471 return((Image *) NULL);
3472 if (SetImageStorageClass(spread_image,DirectClass,exception) == MagickFalse)
3474 spread_image=DestroyImage(spread_image);
3475 return((Image *) NULL);
3482 width=GetOptimalKernelWidth1D(radius,0.5);
3483 random_info=AcquireRandomInfoThreadSet();
3484 image_view=AcquireVirtualCacheView(image,exception);
3485 spread_view=AcquireAuthenticCacheView(spread_image,exception);
3486 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3487 key=GetRandomSecretKey(random_info[0]);
3488 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3489 magick_threads(image,spread_image,image->rows,key == ~0UL)
3491 for (y=0; y < (ssize_t) image->rows; y++)
3494 id = GetOpenMPThreadId();
3502 if (status == MagickFalse)
3504 q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
3506 if (q == (Quantum *) NULL)
3511 for (x=0; x < (ssize_t) image->columns; x++)
3516 point.x=GetPseudoRandomValue(random_info[id]);
3517 point.y=GetPseudoRandomValue(random_info[id]);
3518 status=InterpolatePixelChannels(image,image_view,spread_image,method,
3519 (double) x+width*(point.x-0.5),(double) y+width*(point.y-0.5),q,
3521 q+=GetPixelChannels(spread_image);
3523 if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
3525 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3530 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3531 #pragma omp critical (MagickCore_SpreadImage)
3533 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3534 if (proceed == MagickFalse)
3538 spread_view=DestroyCacheView(spread_view);
3539 image_view=DestroyCacheView(image_view);
3540 random_info=DestroyRandomInfoThreadSet(random_info);
3541 if (status == MagickFalse)
3542 spread_image=DestroyImage(spread_image);
3543 return(spread_image);
3547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3551 % U n s h a r p M a s k I m a g e %
3555 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3557 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
3558 % image with a Gaussian operator of the given radius and standard deviation
3559 % (sigma). For reasonable results, radius should be larger than sigma. Use a
3560 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
3562 % The format of the UnsharpMaskImage method is:
3564 % Image *UnsharpMaskImage(const Image *image,const double radius,
3565 % const double sigma,const double amount,const double threshold,
3566 % ExceptionInfo *exception)
3568 % A description of each parameter follows:
3570 % o image: the image.
3572 % o radius: the radius of the Gaussian, in pixels, not counting the center
3575 % o sigma: the standard deviation of the Gaussian, in pixels.
3577 % o gain: the percentage of the difference between the original and the
3578 % blur image that is added back into the original.
3580 % o threshold: the threshold in pixels needed to apply the diffence gain.
3582 % o exception: return any errors or warnings in this structure.
3585 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
3586 const double sigma,const double gain,const double threshold,
3587 ExceptionInfo *exception)
3589 #define SharpenImageTag "Sharpen/Image"
3610 assert(image != (const Image *) NULL);
3611 assert(image->signature == MagickSignature);
3612 if (image->debug != MagickFalse)
3613 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3614 assert(exception != (ExceptionInfo *) NULL);
3615 unsharp_image=BlurImage(image,radius,sigma,exception);
3616 if (unsharp_image == (Image *) NULL)
3617 return((Image *) NULL);
3618 quantum_threshold=(double) QuantumRange*threshold;
3624 image_view=AcquireVirtualCacheView(image,exception);
3625 unsharp_view=AcquireAuthenticCacheView(unsharp_image,exception);
3626 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3627 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3628 magick_threads(image,unsharp_image,image->rows,1)
3630 for (y=0; y < (ssize_t) image->rows; y++)
3632 register const Quantum
3641 if (status == MagickFalse)
3643 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3644 q=QueueCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
3646 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3651 for (x=0; x < (ssize_t) image->columns; x++)
3656 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3668 channel=GetPixelChannelChannel(image,i);
3669 traits=GetPixelChannelTraits(image,channel);
3670 unsharp_traits=GetPixelChannelTraits(unsharp_image,channel);
3671 if ((traits == UndefinedPixelTrait) ||
3672 (unsharp_traits == UndefinedPixelTrait))
3674 if (((unsharp_traits & CopyPixelTrait) != 0) ||
3675 (GetPixelReadMask(image,p) == 0))
3677 SetPixelChannel(unsharp_image,channel,p[i],q);
3680 pixel=p[i]-(double) GetPixelChannel(unsharp_image,channel,q);
3681 if (fabs(2.0*pixel) < quantum_threshold)
3682 pixel=(double) p[i];
3684 pixel=(double) p[i]+gain*pixel;
3685 SetPixelChannel(unsharp_image,channel,ClampToQuantum(pixel),q);
3687 p+=GetPixelChannels(image);
3688 q+=GetPixelChannels(unsharp_image);
3690 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
3692 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3697 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3698 #pragma omp critical (MagickCore_UnsharpMaskImage)
3700 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
3701 if (proceed == MagickFalse)
3705 unsharp_image->type=image->type;
3706 unsharp_view=DestroyCacheView(unsharp_view);
3707 image_view=DestroyCacheView(image_view);
3708 if (status == MagickFalse)
3709 unsharp_image=DestroyImage(unsharp_image);
3710 return(unsharp_image);