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-2018 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 % https://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-private.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)
168 assert(image != (const Image *) NULL);
169 assert(image->signature == MagickCoreSignature);
170 if (image->debug != MagickFalse)
171 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
172 assert(exception != (ExceptionInfo *) NULL);
173 assert(exception->signature == MagickCoreSignature);
174 blur_image=CloneImage(image,0,0,MagickTrue,exception);
175 if (blur_image == (Image *) NULL)
176 return((Image *) NULL);
177 if (fabs(sigma) < MagickEpsilon)
179 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
181 blur_image=DestroyImage(blur_image);
182 return((Image *) NULL);
185 Edge detect the image brightness channel, level, blur, and level again.
187 edge_image=EdgeImage(image,radius,exception);
188 if (edge_image == (Image *) NULL)
190 blur_image=DestroyImage(blur_image);
191 return((Image *) NULL);
193 (void) AutoLevelImage(edge_image,exception);
194 gaussian_image=BlurImage(edge_image,radius,sigma,exception);
195 if (gaussian_image != (Image *) NULL)
197 edge_image=DestroyImage(edge_image);
198 edge_image=gaussian_image;
200 (void) AutoLevelImage(edge_image,exception);
202 Create a set of kernels from maximum (radius,sigma) to minimum.
204 width=GetOptimalKernelWidth2D(radius,sigma);
205 kernel=(double **) MagickAssumeAligned(AcquireAlignedMemory((size_t) width,
207 if (kernel == (double **) NULL)
209 edge_image=DestroyImage(edge_image);
210 blur_image=DestroyImage(blur_image);
211 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
213 (void) memset(kernel,0,(size_t) width*sizeof(*kernel));
214 for (i=0; i < (ssize_t) width; i+=2)
216 kernel[i]=(double *) MagickAssumeAligned(AcquireAlignedMemory(
217 (size_t) (width-i),(width-i)*sizeof(**kernel)));
218 if (kernel[i] == (double *) NULL)
221 j=(ssize_t) (width-i-1)/2;
223 for (v=(-j); v <= j; v++)
225 for (u=(-j); u <= j; u++)
227 kernel[i][k]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
228 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
229 normalize+=kernel[i][k];
233 kernel[i][(k-1)/2]+=(double) (1.0-normalize);
234 if (sigma < MagickEpsilon)
235 kernel[i][(k-1)/2]=1.0;
237 if (i < (ssize_t) width)
239 for (i-=2; i >= 0; i-=2)
240 kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
241 kernel=(double **) RelinquishAlignedMemory(kernel);
242 edge_image=DestroyImage(edge_image);
243 blur_image=DestroyImage(blur_image);
244 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
247 Adaptively blur image.
251 image_view=AcquireVirtualCacheView(image,exception);
252 edge_view=AcquireVirtualCacheView(edge_image,exception);
253 blur_view=AcquireAuthenticCacheView(blur_image,exception);
254 #if defined(MAGICKCORE_OPENMP_SUPPORT)
255 #pragma omp parallel for schedule(static) shared(progress,status) \
256 magick_number_threads(image,blur_image,blur_image->rows,1)
258 for (y=0; y < (ssize_t) blur_image->rows; y++)
260 register const Quantum
269 if (status == MagickFalse)
271 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
272 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
274 if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
279 for (x=0; x < (ssize_t) blur_image->columns; x++)
281 register const Quantum
291 j=(ssize_t) ceil((double) width*(1.0-QuantumScale*
292 GetPixelIntensity(edge_image,r))-0.5);
296 if (j > (ssize_t) width)
300 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
301 (ssize_t) ((width-j)/2L),width-j,width-j,exception);
302 if (p == (const Quantum *) NULL)
304 center=(ssize_t) GetPixelChannels(image)*(width-j)*((width-j)/2L)+
305 GetPixelChannels(image)*((width-j)/2);
306 for (i=0; i < (ssize_t) GetPixelChannels(blur_image); i++)
320 register const double
323 register const Quantum
324 *magick_restrict pixels;
332 channel=GetPixelChannelChannel(image,i);
333 traits=GetPixelChannelTraits(image,channel);
334 blur_traits=GetPixelChannelTraits(blur_image,channel);
335 if ((traits == UndefinedPixelTrait) ||
336 (blur_traits == UndefinedPixelTrait))
338 if ((blur_traits & CopyPixelTrait) != 0)
340 SetPixelChannel(blur_image,channel,p[center+i],q);
347 if ((blur_traits & BlendPixelTrait) == 0)
352 for (v=0; v < (ssize_t) (width-j); v++)
354 for (u=0; u < (ssize_t) (width-j); u++)
356 pixel+=(*k)*pixels[i];
359 pixels+=GetPixelChannels(image);
362 gamma=PerceptibleReciprocal(gamma);
363 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
369 for (v=0; v < (ssize_t) (width-j); v++)
371 for (u=0; u < (ssize_t) (width-j); u++)
373 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
374 pixel+=(*k)*alpha*pixels[i];
377 pixels+=GetPixelChannels(image);
380 gamma=PerceptibleReciprocal(gamma);
381 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
383 q+=GetPixelChannels(blur_image);
384 r+=GetPixelChannels(edge_image);
386 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
388 if (image->progress_monitor != (MagickProgressMonitor) NULL)
393 #if defined(MAGICKCORE_OPENMP_SUPPORT)
394 #pragma omp critical (MagickCore_AdaptiveBlurImage)
396 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
398 if (proceed == MagickFalse)
402 blur_image->type=image->type;
403 blur_view=DestroyCacheView(blur_view);
404 edge_view=DestroyCacheView(edge_view);
405 image_view=DestroyCacheView(image_view);
406 edge_image=DestroyImage(edge_image);
407 for (i=0; i < (ssize_t) width; i+=2)
408 kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
409 kernel=(double **) RelinquishAlignedMemory(kernel);
410 if (status == MagickFalse)
411 blur_image=DestroyImage(blur_image);
416 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
420 % A d a p t i v e S h a r p e n I m a g e %
424 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
426 % AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
427 % intensely near image edges and less intensely far from edges. We sharpen the
428 % image with a Gaussian operator of the given radius and standard deviation
429 % (sigma). For reasonable results, radius should be larger than sigma. Use a
430 % radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
432 % The format of the AdaptiveSharpenImage method is:
434 % Image *AdaptiveSharpenImage(const Image *image,const double radius,
435 % const double sigma,ExceptionInfo *exception)
437 % A description of each parameter follows:
439 % o image: the image.
441 % o radius: the radius of the Gaussian, in pixels, not counting the center
444 % o sigma: the standard deviation of the Laplacian, in pixels.
446 % o exception: return any errors or warnings in this structure.
449 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
450 const double sigma,ExceptionInfo *exception)
452 #define AdaptiveSharpenImageTag "Convolve/Image"
453 #define MagickSigma (fabs(sigma) < MagickEpsilon ? MagickEpsilon : sigma)
488 assert(image != (const Image *) NULL);
489 assert(image->signature == MagickCoreSignature);
490 if (image->debug != MagickFalse)
491 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
492 assert(exception != (ExceptionInfo *) NULL);
493 assert(exception->signature == MagickCoreSignature);
494 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
495 if (sharp_image == (Image *) NULL)
496 return((Image *) NULL);
497 if (fabs(sigma) < MagickEpsilon)
499 if (SetImageStorageClass(sharp_image,DirectClass,exception) == MagickFalse)
501 sharp_image=DestroyImage(sharp_image);
502 return((Image *) NULL);
505 Edge detect the image brightness channel, level, sharp, and level again.
507 edge_image=EdgeImage(image,radius,exception);
508 if (edge_image == (Image *) NULL)
510 sharp_image=DestroyImage(sharp_image);
511 return((Image *) NULL);
513 (void) AutoLevelImage(edge_image,exception);
514 gaussian_image=BlurImage(edge_image,radius,sigma,exception);
515 if (gaussian_image != (Image *) NULL)
517 edge_image=DestroyImage(edge_image);
518 edge_image=gaussian_image;
520 (void) AutoLevelImage(edge_image,exception);
522 Create a set of kernels from maximum (radius,sigma) to minimum.
524 width=GetOptimalKernelWidth2D(radius,sigma);
525 kernel=(double **) MagickAssumeAligned(AcquireAlignedMemory((size_t)
526 width,sizeof(*kernel)));
527 if (kernel == (double **) NULL)
529 edge_image=DestroyImage(edge_image);
530 sharp_image=DestroyImage(sharp_image);
531 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
533 (void) memset(kernel,0,(size_t) width*sizeof(*kernel));
534 for (i=0; i < (ssize_t) width; i+=2)
536 kernel[i]=(double *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
537 (width-i),(width-i)*sizeof(**kernel)));
538 if (kernel[i] == (double *) NULL)
541 j=(ssize_t) (width-i-1)/2;
543 for (v=(-j); v <= j; v++)
545 for (u=(-j); u <= j; u++)
547 kernel[i][k]=(double) (-exp(-((double) u*u+v*v)/(2.0*MagickSigma*
548 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
549 normalize+=kernel[i][k];
553 kernel[i][(k-1)/2]=(double) ((-2.0)*normalize);
554 if (sigma < MagickEpsilon)
555 kernel[i][(k-1)/2]=1.0;
557 if (i < (ssize_t) width)
559 for (i-=2; i >= 0; i-=2)
560 kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
561 kernel=(double **) RelinquishAlignedMemory(kernel);
562 edge_image=DestroyImage(edge_image);
563 sharp_image=DestroyImage(sharp_image);
564 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
567 Adaptively sharpen image.
571 image_view=AcquireVirtualCacheView(image,exception);
572 edge_view=AcquireVirtualCacheView(edge_image,exception);
573 sharp_view=AcquireAuthenticCacheView(sharp_image,exception);
574 #if defined(MAGICKCORE_OPENMP_SUPPORT)
575 #pragma omp parallel for schedule(static) shared(progress,status) \
576 magick_number_threads(image,sharp_image,sharp_image->rows,1)
578 for (y=0; y < (ssize_t) sharp_image->rows; y++)
580 register const Quantum
589 if (status == MagickFalse)
591 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
592 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
594 if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
599 for (x=0; x < (ssize_t) sharp_image->columns; x++)
601 register const Quantum
611 j=(ssize_t) ceil((double) width*(1.0-QuantumScale*
612 GetPixelIntensity(edge_image,r))-0.5);
616 if (j > (ssize_t) width)
620 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
621 (ssize_t) ((width-j)/2L),width-j,width-j,exception);
622 if (p == (const Quantum *) NULL)
624 center=(ssize_t) GetPixelChannels(image)*(width-j)*((width-j)/2L)+
625 GetPixelChannels(image)*((width-j)/2);
626 for (i=0; i < (ssize_t) GetPixelChannels(sharp_image); i++)
640 register const double
643 register const Quantum
644 *magick_restrict pixels;
652 channel=GetPixelChannelChannel(image,i);
653 traits=GetPixelChannelTraits(image,channel);
654 sharp_traits=GetPixelChannelTraits(sharp_image,channel);
655 if ((traits == UndefinedPixelTrait) ||
656 (sharp_traits == UndefinedPixelTrait))
658 if ((sharp_traits & CopyPixelTrait) != 0)
660 SetPixelChannel(sharp_image,channel,p[center+i],q);
667 if ((sharp_traits & BlendPixelTrait) == 0)
672 for (v=0; v < (ssize_t) (width-j); v++)
674 for (u=0; u < (ssize_t) (width-j); u++)
676 pixel+=(*k)*pixels[i];
679 pixels+=GetPixelChannels(image);
682 gamma=PerceptibleReciprocal(gamma);
683 SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
689 for (v=0; v < (ssize_t) (width-j); v++)
691 for (u=0; u < (ssize_t) (width-j); u++)
693 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
694 pixel+=(*k)*alpha*pixels[i];
697 pixels+=GetPixelChannels(image);
700 gamma=PerceptibleReciprocal(gamma);
701 SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
703 q+=GetPixelChannels(sharp_image);
704 r+=GetPixelChannels(edge_image);
706 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
708 if (image->progress_monitor != (MagickProgressMonitor) NULL)
713 #if defined(MAGICKCORE_OPENMP_SUPPORT)
714 #pragma omp critical (MagickCore_AdaptiveSharpenImage)
716 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
718 if (proceed == MagickFalse)
722 sharp_image->type=image->type;
723 sharp_view=DestroyCacheView(sharp_view);
724 edge_view=DestroyCacheView(edge_view);
725 image_view=DestroyCacheView(image_view);
726 edge_image=DestroyImage(edge_image);
727 for (i=0; i < (ssize_t) width; i+=2)
728 kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
729 kernel=(double **) RelinquishAlignedMemory(kernel);
730 if (status == MagickFalse)
731 sharp_image=DestroyImage(sharp_image);
736 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
740 % B l u r I m a g e %
744 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
746 % BlurImage() blurs an image. We convolve the image with a Gaussian operator
747 % of the given radius and standard deviation (sigma). For reasonable results,
748 % the radius should be larger than sigma. Use a radius of 0 and BlurImage()
749 % selects a suitable radius for you.
751 % The format of the BlurImage method is:
753 % Image *BlurImage(const Image *image,const double radius,
754 % const double sigma,ExceptionInfo *exception)
756 % A description of each parameter follows:
758 % o image: the image.
760 % o radius: the radius of the Gaussian, in pixels, not counting the center
763 % o sigma: the standard deviation of the Gaussian, in pixels.
765 % o exception: return any errors or warnings in this structure.
768 MagickExport Image *BlurImage(const Image *image,const double radius,
769 const double sigma,ExceptionInfo *exception)
772 geometry[MagickPathExtent];
780 assert(image != (const Image *) NULL);
781 assert(image->signature == MagickCoreSignature);
782 if (image->debug != MagickFalse)
783 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
784 assert(exception != (ExceptionInfo *) NULL);
785 assert(exception->signature == MagickCoreSignature);
786 #if defined(MAGICKCORE_OPENCL_SUPPORT)
787 blur_image=AccelerateBlurImage(image,radius,sigma,exception);
788 if (blur_image != (Image *) NULL)
791 (void) FormatLocaleString(geometry,MagickPathExtent,
792 "blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
793 kernel_info=AcquireKernelInfo(geometry,exception);
794 if (kernel_info == (KernelInfo *) NULL)
795 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
796 blur_image=ConvolveImage(image,kernel_info,exception);
797 kernel_info=DestroyKernelInfo(kernel_info);
802 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
806 % C o n v o l v e I m a g e %
810 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
812 % ConvolveImage() applies a custom convolution kernel to the image.
814 % The format of the ConvolveImage method is:
816 % Image *ConvolveImage(const Image *image,const KernelInfo *kernel,
817 % ExceptionInfo *exception)
819 % A description of each parameter follows:
821 % o image: the image.
823 % o kernel: the filtering kernel.
825 % o exception: return any errors or warnings in this structure.
828 MagickExport Image *ConvolveImage(const Image *image,
829 const KernelInfo *kernel_info,ExceptionInfo *exception)
834 #if defined(MAGICKCORE_OPENCL_SUPPORT)
835 convolve_image=AccelerateConvolveImage(image,kernel_info,exception);
836 if (convolve_image != (Image *) NULL)
837 return(convolve_image);
840 convolve_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,
842 return(convolve_image);
846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
850 % D e s p e c k l e I m a g e %
854 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
856 % DespeckleImage() reduces the speckle noise in an image while perserving the
857 % edges of the original image. A speckle removing filter uses a complementary
858 % hulling technique (raising pixels that are darker than their surrounding
859 % neighbors, then complementarily lowering pixels that are brighter than their
860 % surrounding neighbors) to reduce the speckle index of that image (reference
861 % Crimmins speckle removal).
863 % The format of the DespeckleImage method is:
865 % Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
867 % A description of each parameter follows:
869 % o image: the image.
871 % o exception: return any errors or warnings in this structure.
875 static void Hull(const Image *image,const ssize_t x_offset,
876 const ssize_t y_offset,const size_t columns,const size_t rows,
877 const int polarity,Quantum *magick_restrict f,Quantum *magick_restrict g)
888 assert(image != (const Image *) NULL);
889 assert(image->signature == MagickCoreSignature);
890 if (image->debug != MagickFalse)
891 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
892 assert(f != (Quantum *) NULL);
893 assert(g != (Quantum *) NULL);
896 r=p+(y_offset*(columns+2)+x_offset);
897 #if defined(MAGICKCORE_OPENMP_SUPPORT)
898 #pragma omp parallel for schedule(static) \
899 magick_number_threads(image,image,rows,1)
901 for (y=0; y < (ssize_t) rows; y++)
912 for (x=0; x < (ssize_t) columns; x++)
914 v=(MagickRealType) p[i];
915 if ((MagickRealType) r[i] >= (v+ScaleCharToQuantum(2)))
916 v+=ScaleCharToQuantum(1);
921 for (x=0; x < (ssize_t) columns; x++)
923 v=(MagickRealType) p[i];
924 if ((MagickRealType) r[i] <= (v-ScaleCharToQuantum(2)))
925 v-=ScaleCharToQuantum(1);
932 r=q+(y_offset*(columns+2)+x_offset);
933 s=q-(y_offset*(columns+2)+x_offset);
934 #if defined(MAGICKCORE_OPENMP_SUPPORT)
935 #pragma omp parallel for schedule(static) \
936 magick_number_threads(image,image,rows,1)
938 for (y=0; y < (ssize_t) rows; y++)
949 for (x=0; x < (ssize_t) columns; x++)
951 v=(MagickRealType) q[i];
952 if (((MagickRealType) s[i] >= (v+ScaleCharToQuantum(2))) &&
953 ((MagickRealType) r[i] > v))
954 v+=ScaleCharToQuantum(1);
959 for (x=0; x < (ssize_t) columns; x++)
961 v=(MagickRealType) q[i];
962 if (((MagickRealType) s[i] <= (v-ScaleCharToQuantum(2))) &&
963 ((MagickRealType) r[i] < v))
964 v-=ScaleCharToQuantum(1);
971 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
973 #define DespeckleImageTag "Despeckle/Image"
990 *magick_restrict buffer,
991 *magick_restrict pixels;
1000 X[4] = {0, 1, 1,-1},
1001 Y[4] = {1, 0, 1, 1};
1004 Allocate despeckled image.
1006 assert(image != (const Image *) NULL);
1007 assert(image->signature == MagickCoreSignature);
1008 if (image->debug != MagickFalse)
1009 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1010 assert(exception != (ExceptionInfo *) NULL);
1011 assert(exception->signature == MagickCoreSignature);
1012 #if defined(MAGICKCORE_OPENCL_SUPPORT)
1013 despeckle_image=AccelerateDespeckleImage(image,exception);
1014 if (despeckle_image != (Image *) NULL)
1015 return(despeckle_image);
1017 despeckle_image=CloneImage(image,0,0,MagickTrue,exception);
1018 if (despeckle_image == (Image *) NULL)
1019 return((Image *) NULL);
1020 status=SetImageStorageClass(despeckle_image,DirectClass,exception);
1021 if (status == MagickFalse)
1023 despeckle_image=DestroyImage(despeckle_image);
1024 return((Image *) NULL);
1027 Allocate image buffer.
1029 length=(size_t) ((image->columns+2)*(image->rows+2));
1030 pixel_info=AcquireVirtualMemory(length,sizeof(*pixels));
1031 buffer_info=AcquireVirtualMemory(length,sizeof(*buffer));
1032 if ((pixel_info == (MemoryInfo *) NULL) ||
1033 (buffer_info == (MemoryInfo *) NULL))
1035 if (buffer_info != (MemoryInfo *) NULL)
1036 buffer_info=RelinquishVirtualMemory(buffer_info);
1037 if (pixel_info != (MemoryInfo *) NULL)
1038 pixel_info=RelinquishVirtualMemory(pixel_info);
1039 despeckle_image=DestroyImage(despeckle_image);
1040 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1042 pixels=(Quantum *) GetVirtualMemoryBlob(pixel_info);
1043 buffer=(Quantum *) GetVirtualMemoryBlob(buffer_info);
1045 Reduce speckle in the image.
1048 image_view=AcquireVirtualCacheView(image,exception);
1049 despeckle_view=AcquireAuthenticCacheView(despeckle_image,exception);
1050 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1067 if (status == MagickFalse)
1069 channel=GetPixelChannelChannel(image,i);
1070 traits=GetPixelChannelTraits(image,channel);
1071 despeckle_traits=GetPixelChannelTraits(despeckle_image,channel);
1072 if ((traits == UndefinedPixelTrait) ||
1073 (despeckle_traits == UndefinedPixelTrait))
1075 if ((despeckle_traits & CopyPixelTrait) != 0)
1077 (void) memset(pixels,0,length*sizeof(*pixels));
1078 j=(ssize_t) image->columns+2;
1079 for (y=0; y < (ssize_t) image->rows; y++)
1081 register const Quantum
1084 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1085 if (p == (const Quantum *) NULL)
1091 for (x=0; x < (ssize_t) image->columns; x++)
1094 p+=GetPixelChannels(image);
1098 (void) memset(buffer,0,length*sizeof(*buffer));
1099 for (k=0; k < 4; k++)
1101 Hull(image,X[k],Y[k],image->columns,image->rows,1,pixels,buffer);
1102 Hull(image,-X[k],-Y[k],image->columns,image->rows,1,pixels,buffer);
1103 Hull(image,-X[k],-Y[k],image->columns,image->rows,-1,pixels,buffer);
1104 Hull(image,X[k],Y[k],image->columns,image->rows,-1,pixels,buffer);
1106 j=(ssize_t) image->columns+2;
1107 for (y=0; y < (ssize_t) image->rows; y++)
1115 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1117 if (q == (Quantum *) NULL)
1123 for (x=0; x < (ssize_t) image->columns; x++)
1125 SetPixelChannel(despeckle_image,channel,pixels[j++],q);
1126 q+=GetPixelChannels(despeckle_image);
1128 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1129 if (sync == MagickFalse)
1133 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1138 proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1139 GetPixelChannels(image));
1140 if (proceed == MagickFalse)
1144 despeckle_view=DestroyCacheView(despeckle_view);
1145 image_view=DestroyCacheView(image_view);
1146 buffer_info=RelinquishVirtualMemory(buffer_info);
1147 pixel_info=RelinquishVirtualMemory(pixel_info);
1148 despeckle_image->type=image->type;
1149 if (status == MagickFalse)
1150 despeckle_image=DestroyImage(despeckle_image);
1151 return(despeckle_image);
1155 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1159 % E d g e I m a g e %
1163 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1165 % EdgeImage() finds edges in an image. Radius defines the radius of the
1166 % convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1169 % The format of the EdgeImage method is:
1171 % Image *EdgeImage(const Image *image,const double radius,
1172 % ExceptionInfo *exception)
1174 % A description of each parameter follows:
1176 % o image: the image.
1178 % o radius: the radius of the pixel neighborhood.
1180 % o exception: return any errors or warnings in this structure.
1183 MagickExport Image *EdgeImage(const Image *image,const double radius,
1184 ExceptionInfo *exception)
1198 assert(image != (const Image *) NULL);
1199 assert(image->signature == MagickCoreSignature);
1200 if (image->debug != MagickFalse)
1201 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1202 assert(exception != (ExceptionInfo *) NULL);
1203 assert(exception->signature == MagickCoreSignature);
1204 width=GetOptimalKernelWidth1D(radius,0.5);
1205 kernel_info=AcquireKernelInfo((const char *) NULL,exception);
1206 if (kernel_info == (KernelInfo *) NULL)
1207 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1208 (void) memset(kernel_info,0,sizeof(*kernel_info));
1209 kernel_info->width=width;
1210 kernel_info->height=width;
1211 kernel_info->x=(ssize_t) (kernel_info->width-1)/2;
1212 kernel_info->y=(ssize_t) (kernel_info->height-1)/2;
1213 kernel_info->signature=MagickCoreSignature;
1214 kernel_info->values=(MagickRealType *) MagickAssumeAligned(
1215 AcquireAlignedMemory(kernel_info->width,kernel_info->height*
1216 sizeof(*kernel_info->values)));
1217 if (kernel_info->values == (MagickRealType *) NULL)
1219 kernel_info=DestroyKernelInfo(kernel_info);
1220 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1222 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
1223 kernel_info->values[i]=(-1.0);
1224 kernel_info->values[i/2]=(double) kernel_info->width*kernel_info->height-1.0;
1225 edge_image=ConvolveImage(image,kernel_info,exception);
1226 kernel_info=DestroyKernelInfo(kernel_info);
1231 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1235 % E m b o s s I m a g e %
1239 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1241 % EmbossImage() returns a grayscale image with a three-dimensional effect.
1242 % We convolve the image with a Gaussian operator of the given radius and
1243 % standard deviation (sigma). For reasonable results, radius should be
1244 % larger than sigma. Use a radius of 0 and Emboss() selects a suitable
1247 % The format of the EmbossImage method is:
1249 % Image *EmbossImage(const Image *image,const double radius,
1250 % const double sigma,ExceptionInfo *exception)
1252 % A description of each parameter follows:
1254 % o image: the image.
1256 % o radius: the radius of the pixel neighborhood.
1258 % o sigma: the standard deviation of the Gaussian, in pixels.
1260 % o exception: return any errors or warnings in this structure.
1263 MagickExport Image *EmbossImage(const Image *image,const double radius,
1264 const double sigma,ExceptionInfo *exception)
1288 assert(image != (const Image *) NULL);
1289 assert(image->signature == MagickCoreSignature);
1290 if (image->debug != MagickFalse)
1291 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1292 assert(exception != (ExceptionInfo *) NULL);
1293 assert(exception->signature == MagickCoreSignature);
1294 width=GetOptimalKernelWidth1D(radius,sigma);
1295 kernel_info=AcquireKernelInfo((const char *) NULL,exception);
1296 if (kernel_info == (KernelInfo *) NULL)
1297 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1298 kernel_info->width=width;
1299 kernel_info->height=width;
1300 kernel_info->x=(ssize_t) (width-1)/2;
1301 kernel_info->y=(ssize_t) (width-1)/2;
1302 kernel_info->values=(MagickRealType *) MagickAssumeAligned(
1303 AcquireAlignedMemory(kernel_info->width,kernel_info->width*
1304 sizeof(*kernel_info->values)));
1305 if (kernel_info->values == (MagickRealType *) NULL)
1307 kernel_info=DestroyKernelInfo(kernel_info);
1308 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1310 j=(ssize_t) (kernel_info->width-1)/2;
1313 for (v=(-j); v <= j; v++)
1315 for (u=(-j); u <= j; u++)
1317 kernel_info->values[i]=(MagickRealType) (((u < 0) || (v < 0) ? -8.0 :
1318 8.0)*exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
1319 (2.0*MagickPI*MagickSigma*MagickSigma));
1321 kernel_info->values[i]=0.0;
1327 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
1328 normalize+=kernel_info->values[i];
1329 gamma=PerceptibleReciprocal(normalize);
1330 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
1331 kernel_info->values[i]*=gamma;
1332 emboss_image=ConvolveImage(image,kernel_info,exception);
1333 kernel_info=DestroyKernelInfo(kernel_info);
1334 if (emboss_image != (Image *) NULL)
1335 (void) EqualizeImage(emboss_image,exception);
1336 return(emboss_image);
1340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1344 % G a u s s i a n B l u r I m a g e %
1348 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1350 % GaussianBlurImage() blurs an image. We convolve the image with a
1351 % Gaussian operator of the given radius and standard deviation (sigma).
1352 % For reasonable results, the radius should be larger than sigma. Use a
1353 % radius of 0 and GaussianBlurImage() selects a suitable radius for you
1355 % The format of the GaussianBlurImage method is:
1357 % Image *GaussianBlurImage(const Image *image,onst double radius,
1358 % const double sigma,ExceptionInfo *exception)
1360 % A description of each parameter follows:
1362 % o image: the image.
1364 % o radius: the radius of the Gaussian, in pixels, not counting the center
1367 % o sigma: the standard deviation of the Gaussian, in pixels.
1369 % o exception: return any errors or warnings in this structure.
1372 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
1373 const double sigma,ExceptionInfo *exception)
1376 geometry[MagickPathExtent];
1384 assert(image != (const Image *) NULL);
1385 assert(image->signature == MagickCoreSignature);
1386 if (image->debug != MagickFalse)
1387 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1388 assert(exception != (ExceptionInfo *) NULL);
1389 assert(exception->signature == MagickCoreSignature);
1390 (void) FormatLocaleString(geometry,MagickPathExtent,"gaussian:%.20gx%.20g",
1392 kernel_info=AcquireKernelInfo(geometry,exception);
1393 if (kernel_info == (KernelInfo *) NULL)
1394 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1395 blur_image=ConvolveImage(image,kernel_info,exception);
1396 kernel_info=DestroyKernelInfo(kernel_info);
1401 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1405 % K u w a h a r a I m a g e %
1409 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1411 % KuwaharaImage() is an edge preserving noise reduction filter.
1413 % The format of the KuwaharaImage method is:
1415 % Image *KuwaharaImage(const Image *image,const double radius,
1416 % const double sigma,ExceptionInfo *exception)
1418 % A description of each parameter follows:
1420 % o image: the image.
1422 % o radius: the square window radius.
1424 % o sigma: the standard deviation of the Gaussian, in pixels.
1426 % o exception: return any errors or warnings in this structure.
1430 static inline MagickRealType GetMeanLuma(const Image *magick_restrict image,
1431 const double *magick_restrict pixel)
1433 return(0.212656f*pixel[image->channel_map[RedPixelChannel].offset]+
1434 0.715158f*pixel[image->channel_map[GreenPixelChannel].offset]+
1435 0.072186f*pixel[image->channel_map[BluePixelChannel].offset]); /* Rec709 */
1438 MagickExport Image *KuwaharaImage(const Image *image,const double radius,
1439 const double sigma,ExceptionInfo *exception)
1441 #define KuwaharaImageTag "Kuwahara/Image"
1464 Initialize Kuwahara image attributes.
1466 assert(image != (Image *) NULL);
1467 assert(image->signature == MagickCoreSignature);
1468 if (image->debug != MagickFalse)
1469 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1470 assert(exception != (ExceptionInfo *) NULL);
1471 assert(exception->signature == MagickCoreSignature);
1472 width=(size_t) radius+1;
1473 gaussian_image=BlurImage(image,radius,sigma,exception);
1474 if (gaussian_image == (Image *) NULL)
1475 return((Image *) NULL);
1476 kuwahara_image=CloneImage(image,0,0,MagickTrue,exception);
1477 if (kuwahara_image == (Image *) NULL)
1479 gaussian_image=DestroyImage(gaussian_image);
1480 return((Image *) NULL);
1482 if (SetImageStorageClass(kuwahara_image,DirectClass,exception) == MagickFalse)
1484 gaussian_image=DestroyImage(gaussian_image);
1485 kuwahara_image=DestroyImage(kuwahara_image);
1486 return((Image *) NULL);
1489 Edge preserving noise reduction filter.
1493 image_view=AcquireVirtualCacheView(gaussian_image,exception);
1494 kuwahara_view=AcquireAuthenticCacheView(kuwahara_image,exception);
1495 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1496 #pragma omp parallel for schedule(static) shared(progress,status) \
1497 magick_number_threads(image,kuwahara_image,gaussian_image->rows,1)
1499 for (y=0; y < (ssize_t) gaussian_image->rows; y++)
1507 if (status == MagickFalse)
1509 q=QueueCacheViewAuthenticPixels(kuwahara_view,0,y,kuwahara_image->columns,1,
1511 if (q == (Quantum *) NULL)
1516 for (x=0; x < (ssize_t) gaussian_image->columns; x++)
1531 min_variance=MagickMaximumValue;
1532 SetGeometry(gaussian_image,&target);
1533 quadrant.width=width;
1534 quadrant.height=width;
1535 for (i=0; i < 4; i++)
1541 mean[MaxPixelChannels],
1556 quadrant.x=x-(ssize_t) (width-1);
1557 quadrant.y=y-(ssize_t) (width-1);
1562 quadrant.y=y-(ssize_t) (width-1);
1567 quadrant.x=x-(ssize_t) (width-1);
1574 p=GetCacheViewVirtualPixels(image_view,quadrant.x,quadrant.y,
1575 quadrant.width,quadrant.height,exception);
1576 if (p == (const Quantum *) NULL)
1578 for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
1581 for (n=0; n < (ssize_t) (width*width); n++)
1583 for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
1584 mean[j]+=(double) k[j];
1585 k+=GetPixelChannels(gaussian_image);
1587 for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
1588 mean[j]/=(double) (width*width);
1591 for (n=0; n < (ssize_t) (width*width); n++)
1596 luma=GetPixelLuma(gaussian_image,k);
1597 variance+=(luma-GetMeanLuma(gaussian_image,mean))*
1598 (luma-GetMeanLuma(gaussian_image,mean));
1599 k+=GetPixelChannels(gaussian_image);
1601 if (variance < min_variance)
1603 min_variance=variance;
1612 status=InterpolatePixelChannels(gaussian_image,image_view,kuwahara_image,
1613 UndefinedInterpolatePixel,(double) target.x+target.width/2.0,(double)
1614 target.y+target.height/2.0,q,exception);
1615 if (status == MagickFalse)
1617 q+=GetPixelChannels(kuwahara_image);
1619 if (SyncCacheViewAuthenticPixels(kuwahara_view,exception) == MagickFalse)
1621 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1626 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1627 #pragma omp critical (MagickCore_KuwaharaImage)
1629 proceed=SetImageProgress(image,KuwaharaImageTag,progress++,image->rows);
1630 if (proceed == MagickFalse)
1634 kuwahara_view=DestroyCacheView(kuwahara_view);
1635 image_view=DestroyCacheView(image_view);
1636 gaussian_image=DestroyImage(gaussian_image);
1637 if (status == MagickFalse)
1638 kuwahara_image=DestroyImage(kuwahara_image);
1639 return(kuwahara_image);
1643 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1647 % L o c a l C o n t r a s t I m a g e %
1651 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1653 % LocalContrastImage() attempts to increase the appearance of large-scale
1654 % light-dark transitions. Local contrast enhancement works similarly to
1655 % sharpening with an unsharp mask, however the mask is instead created using
1656 % an image with a greater blur distance.
1658 % The format of the LocalContrastImage method is:
1660 % Image *LocalContrastImage(const Image *image, const double radius,
1661 % const double strength,ExceptionInfo *exception)
1663 % A description of each parameter follows:
1665 % o image: the image.
1667 % o radius: the radius of the Gaussian blur, in percentage with 100%
1668 % resulting in a blur radius of 20% of largest dimension.
1670 % o strength: the strength of the blur mask in percentage.
1672 % o exception: return any errors or warnings in this structure.
1675 MagickExport Image *LocalContrastImage(const Image *image,const double radius,
1676 const double strength,ExceptionInfo *exception)
1678 #define LocalContrastImageTag "LocalContrast/Image"
1696 *scanLinePixels_info,
1704 Initialize contrast image attributes.
1706 assert(image != (const Image *) NULL);
1707 assert(image->signature == MagickCoreSignature);
1708 if (image->debug != MagickFalse)
1709 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1710 assert(exception != (ExceptionInfo *) NULL);
1711 assert(exception->signature == MagickCoreSignature);
1712 #if defined(MAGICKCORE_OPENCL_SUPPORT)
1713 contrast_image=AccelerateLocalContrastImage(image,radius,strength,exception);
1714 if (contrast_image != (Image *) NULL)
1715 return(contrast_image);
1717 contrast_image=CloneImage(image,0,0,MagickTrue,exception);
1718 if (contrast_image == (Image *) NULL)
1719 return((Image *) NULL);
1720 if (SetImageStorageClass(contrast_image,DirectClass,exception) == MagickFalse)
1722 contrast_image=DestroyImage(contrast_image);
1723 return((Image *) NULL);
1725 image_view=AcquireVirtualCacheView(image,exception);
1726 contrast_view=AcquireAuthenticCacheView(contrast_image,exception);
1727 scanLineSize=(ssize_t) MagickMax(image->columns,image->rows);
1728 width=(ssize_t) scanLineSize*0.002f*fabs(radius);
1729 scanLineSize+=(2*width);
1730 scanLinePixels_info=AcquireVirtualMemory((size_t) GetOpenMPMaximumThreads()*
1731 scanLineSize,sizeof(*scanLinePixels));
1732 if (scanLinePixels_info == (MemoryInfo *) NULL)
1734 contrast_view=DestroyCacheView(contrast_view);
1735 image_view=DestroyCacheView(image_view);
1736 contrast_image=DestroyImage(contrast_image);
1737 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1739 scanLinePixels=(float *) GetVirtualMemoryBlob(scanLinePixels_info);
1741 Create intermediate buffer.
1743 interImage_info=AcquireVirtualMemory(image->rows*(image->columns+(2*width)),
1744 sizeof(*interImage));
1745 if (interImage_info == (MemoryInfo *) NULL)
1747 scanLinePixels_info=RelinquishVirtualMemory(scanLinePixels_info);
1748 contrast_view=DestroyCacheView(contrast_view);
1749 image_view=DestroyCacheView(image_view);
1750 contrast_image=DestroyImage(contrast_image);
1751 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1753 interImage=(float *) GetVirtualMemoryBlob(interImage_info);
1754 totalWeight=(float) ((width+1)*(width+1));
1763 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1764 #pragma omp parallel for schedule(static) \
1765 magick_number_threads(image,image,image->columns,1)
1767 for (x=0; x < (ssize_t) image->columns; x++)
1770 id = GetOpenMPThreadId();
1786 if (status == MagickFalse)
1788 pixels=scanLinePixels;
1789 pixels+=id*scanLineSize;
1791 p=GetCacheViewVirtualPixels(image_view,x,-width,1,image->rows+(2*width),
1793 if (p == (const Quantum *) NULL)
1798 for (y=0; y < (ssize_t) image->rows+(2*width); y++)
1800 *pix++=(float)GetPixelLuma(image,p);
1801 p+=image->number_channels;
1803 out=interImage+x+width;
1804 for (y=0; y < (ssize_t) image->rows; y++)
1813 for (i=0; i < width; i++)
1815 sum+=weight*(*pix++);
1818 for (i=width+1; i < (2*width); i++)
1820 sum+=weight*(*pix++);
1823 /* write to output */
1824 *out=sum/totalWeight;
1825 /* mirror into padding */
1826 if (x <= width && x != 0)
1828 if ((x > (ssize_t) image->columns-width-2) &&
1829 (x != (ssize_t) image->columns-1))
1830 *(out+((image->columns-x-1)*2))=*out;
1831 out+=image->columns+(width*2);
1842 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1843 #pragma omp parallel for schedule(static) \
1844 magick_number_threads(image,image,image->rows,1)
1846 for (y=0; y < (ssize_t) image->rows; y++)
1849 id = GetOpenMPThreadId();
1867 if (status == MagickFalse)
1869 pixels=scanLinePixels;
1870 pixels+=id*scanLineSize;
1871 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1872 q=GetCacheViewAuthenticPixels(contrast_view,0,y,image->columns,1,
1874 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1879 memcpy(pixels,interImage+(y*(image->columns+(2*width))),(image->columns+
1880 (2*width))*sizeof(float));
1881 for (x=0; x < (ssize_t) image->columns; x++)
1895 for (i=0; i < width; i++)
1897 sum+=weight*(*pix++);
1900 for (i=width+1; i < (2*width); i++)
1902 sum+=weight*(*pix++);
1905 /* Apply and write */
1906 srcVal=(float) GetPixelLuma(image,p);
1907 mult=(srcVal-(sum/totalWeight))*(strength/100.0f);
1908 mult=(srcVal+mult)/srcVal;
1909 traits=GetPixelChannelTraits(image,RedPixelChannel);
1910 if ((traits & UpdatePixelTrait) != 0)
1911 SetPixelRed(contrast_image,ClampToQuantum(GetPixelRed(image,p)*mult),
1913 traits=GetPixelChannelTraits(image,GreenPixelChannel);
1914 if ((traits & UpdatePixelTrait) != 0)
1915 SetPixelGreen(contrast_image,ClampToQuantum(GetPixelGreen(image,p)*
1917 traits=GetPixelChannelTraits(image,BluePixelChannel);
1918 if ((traits & UpdatePixelTrait) != 0)
1919 SetPixelBlue(contrast_image,ClampToQuantum(GetPixelBlue(image,p)*
1921 p+=image->number_channels;
1922 q+=contrast_image->number_channels;
1924 if (SyncCacheViewAuthenticPixels(contrast_view,exception) == MagickFalse)
1928 scanLinePixels_info=RelinquishVirtualMemory(scanLinePixels_info);
1929 interImage_info=RelinquishVirtualMemory(interImage_info);
1930 contrast_view=DestroyCacheView(contrast_view);
1931 image_view=DestroyCacheView(image_view);
1932 if (status == MagickFalse)
1933 contrast_image=DestroyImage(contrast_image);
1934 return(contrast_image);
1938 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1942 % M o t i o n B l u r I m a g e %
1946 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1948 % MotionBlurImage() simulates motion blur. We convolve the image with a
1949 % Gaussian operator of the given radius and standard deviation (sigma).
1950 % For reasonable results, radius should be larger than sigma. Use a
1951 % radius of 0 and MotionBlurImage() selects a suitable radius for you.
1952 % Angle gives the angle of the blurring motion.
1954 % Andrew Protano contributed this effect.
1956 % The format of the MotionBlurImage method is:
1958 % Image *MotionBlurImage(const Image *image,const double radius,
1959 % const double sigma,const double angle,ExceptionInfo *exception)
1961 % A description of each parameter follows:
1963 % o image: the image.
1965 % o radius: the radius of the Gaussian, in pixels, not counting
1968 % o sigma: the standard deviation of the Gaussian, in pixels.
1970 % o angle: Apply the effect along this angle.
1972 % o exception: return any errors or warnings in this structure.
1976 static MagickRealType *GetMotionBlurKernel(const size_t width,
1987 Generate a 1-D convolution kernel.
1989 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1990 kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
1991 width,sizeof(*kernel)));
1992 if (kernel == (MagickRealType *) NULL)
1995 for (i=0; i < (ssize_t) width; i++)
1997 kernel[i]=(MagickRealType) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
1998 MagickSigma)))/(MagickSQ2PI*MagickSigma));
1999 normalize+=kernel[i];
2001 for (i=0; i < (ssize_t) width; i++)
2002 kernel[i]/=normalize;
2006 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
2007 const double sigma,const double angle,ExceptionInfo *exception)
2009 #define BlurImageTag "Blur/Image"
2043 assert(image != (Image *) NULL);
2044 assert(image->signature == MagickCoreSignature);
2045 if (image->debug != MagickFalse)
2046 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2047 assert(exception != (ExceptionInfo *) NULL);
2048 width=GetOptimalKernelWidth1D(radius,sigma);
2049 kernel=GetMotionBlurKernel(width,sigma);
2050 if (kernel == (MagickRealType *) NULL)
2051 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2052 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2053 if (offset == (OffsetInfo *) NULL)
2055 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2056 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2058 point.x=(double) width*sin(DegreesToRadians(angle));
2059 point.y=(double) width*cos(DegreesToRadians(angle));
2060 for (i=0; i < (ssize_t) width; i++)
2062 offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
2063 offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
2068 #if defined(MAGICKCORE_OPENCL_SUPPORT)
2069 blur_image=AccelerateMotionBlurImage(image,kernel,width,offset,exception);
2070 if (blur_image != (Image *) NULL)
2072 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2073 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2077 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2078 if (blur_image == (Image *) NULL)
2080 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2081 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2082 return((Image *) NULL);
2084 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2086 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2087 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2088 blur_image=DestroyImage(blur_image);
2089 return((Image *) NULL);
2093 image_view=AcquireVirtualCacheView(image,exception);
2094 motion_view=AcquireVirtualCacheView(image,exception);
2095 blur_view=AcquireAuthenticCacheView(blur_image,exception);
2096 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2097 #pragma omp parallel for schedule(static) shared(progress,status) \
2098 magick_number_threads(image,blur_image,image->rows,1)
2100 for (y=0; y < (ssize_t) image->rows; y++)
2102 register const Quantum
2111 if (status == MagickFalse)
2113 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2114 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2116 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2121 for (x=0; x < (ssize_t) image->columns; x++)
2126 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2140 register const Quantum
2143 register MagickRealType
2149 channel=GetPixelChannelChannel(image,i);
2150 traits=GetPixelChannelTraits(image,channel);
2151 blur_traits=GetPixelChannelTraits(blur_image,channel);
2152 if ((traits == UndefinedPixelTrait) ||
2153 (blur_traits == UndefinedPixelTrait))
2155 if ((blur_traits & CopyPixelTrait) != 0)
2157 SetPixelChannel(blur_image,channel,p[i],q);
2162 if ((blur_traits & BlendPixelTrait) == 0)
2164 for (j=0; j < (ssize_t) width; j++)
2166 r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+
2167 offset[j].y,1,1,exception);
2168 if (r == (const Quantum *) NULL)
2176 SetPixelChannel(blur_image,channel,ClampToQuantum(pixel),q);
2181 for (j=0; j < (ssize_t) width; j++)
2183 r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+offset[j].y,1,
2185 if (r == (const Quantum *) NULL)
2190 alpha=(double) (QuantumScale*GetPixelAlpha(image,r));
2191 pixel+=(*k)*alpha*r[i];
2195 gamma=PerceptibleReciprocal(gamma);
2196 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2198 p+=GetPixelChannels(image);
2199 q+=GetPixelChannels(blur_image);
2201 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2203 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2208 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2209 #pragma omp critical (MagickCore_MotionBlurImage)
2211 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2212 if (proceed == MagickFalse)
2216 blur_view=DestroyCacheView(blur_view);
2217 motion_view=DestroyCacheView(motion_view);
2218 image_view=DestroyCacheView(image_view);
2219 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2220 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2221 if (status == MagickFalse)
2222 blur_image=DestroyImage(blur_image);
2227 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2231 % P r e v i e w I m a g e %
2235 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2237 % PreviewImage() tiles 9 thumbnails of the specified image with an image
2238 % processing operation applied with varying parameters. This may be helpful
2239 % pin-pointing an appropriate parameter for a particular image processing
2242 % The format of the PreviewImages method is:
2244 % Image *PreviewImages(const Image *image,const PreviewType preview,
2245 % ExceptionInfo *exception)
2247 % A description of each parameter follows:
2249 % o image: the image.
2251 % o preview: the image processing operation.
2253 % o exception: return any errors or warnings in this structure.
2256 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
2257 ExceptionInfo *exception)
2259 #define NumberTiles 9
2260 #define PreviewImageTag "Preview/Image"
2261 #define DefaultPreviewGeometry "204x204+10+10"
2264 factor[MagickPathExtent],
2265 label[MagickPathExtent];
2310 Open output image file.
2312 assert(image != (Image *) NULL);
2313 assert(image->signature == MagickCoreSignature);
2314 if (image->debug != MagickFalse)
2315 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2319 preview_info=AcquireImageInfo();
2320 SetGeometry(image,&geometry);
2321 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2322 &geometry.width,&geometry.height);
2323 images=NewImageList();
2325 GetQuantizeInfo(&quantize_info);
2331 for (i=0; i < NumberTiles; i++)
2333 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2334 if (thumbnail == (Image *) NULL)
2336 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2338 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel,exception);
2339 if (i == (NumberTiles/2))
2341 (void) QueryColorCompliance("#dfdfdf",AllCompliance,
2342 &thumbnail->matte_color,exception);
2343 AppendImageToList(&images,thumbnail);
2351 preview_image=RotateImage(thumbnail,degrees,exception);
2352 (void) FormatLocaleString(label,MagickPathExtent,"rotate %g",degrees);
2358 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
2359 (void) FormatLocaleString(label,MagickPathExtent,"shear %gx%g",degrees,
2365 x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
2366 y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
2367 preview_image=RollImage(thumbnail,x,y,exception);
2368 (void) FormatLocaleString(label,MagickPathExtent,"roll %+.20gx%+.20g",
2369 (double) x,(double) y);
2374 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2375 if (preview_image == (Image *) NULL)
2377 (void) FormatLocaleString(factor,MagickPathExtent,"100,100,%g",2.0*
2379 (void) ModulateImage(preview_image,factor,exception);
2380 (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
2383 case SaturationPreview:
2385 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2386 if (preview_image == (Image *) NULL)
2388 (void) FormatLocaleString(factor,MagickPathExtent,"100,%g",2.0*
2390 (void) ModulateImage(preview_image,factor,exception);
2391 (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
2394 case BrightnessPreview:
2396 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2397 if (preview_image == (Image *) NULL)
2399 (void) FormatLocaleString(factor,MagickPathExtent,"%g",2.0*percentage);
2400 (void) ModulateImage(preview_image,factor,exception);
2401 (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
2407 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2408 if (preview_image == (Image *) NULL)
2411 (void) GammaImage(preview_image,gamma,exception);
2412 (void) FormatLocaleString(label,MagickPathExtent,"gamma %g",gamma);
2417 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2418 if (preview_image != (Image *) NULL)
2419 for (x=0; x < i; x++)
2420 (void) ContrastImage(preview_image,MagickTrue,exception);
2421 (void) FormatLocaleString(label,MagickPathExtent,"contrast (%.20g)",
2427 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2428 if (preview_image == (Image *) NULL)
2430 for (x=0; x < i; x++)
2431 (void) ContrastImage(preview_image,MagickFalse,exception);
2432 (void) FormatLocaleString(label,MagickPathExtent,"+contrast (%.20g)",
2436 case GrayscalePreview:
2438 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2439 if (preview_image == (Image *) NULL)
2442 quantize_info.number_colors=colors;
2443 quantize_info.colorspace=GRAYColorspace;
2444 (void) QuantizeImage(&quantize_info,preview_image,exception);
2445 (void) FormatLocaleString(label,MagickPathExtent,
2446 "-colorspace gray -colors %.20g",(double) colors);
2449 case QuantizePreview:
2451 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2452 if (preview_image == (Image *) NULL)
2455 quantize_info.number_colors=colors;
2456 (void) QuantizeImage(&quantize_info,preview_image,exception);
2457 (void) FormatLocaleString(label,MagickPathExtent,"colors %.20g",
2461 case DespecklePreview:
2463 for (x=0; x < (i-1); x++)
2465 preview_image=DespeckleImage(thumbnail,exception);
2466 if (preview_image == (Image *) NULL)
2468 thumbnail=DestroyImage(thumbnail);
2469 thumbnail=preview_image;
2471 preview_image=DespeckleImage(thumbnail,exception);
2472 if (preview_image == (Image *) NULL)
2474 (void) FormatLocaleString(label,MagickPathExtent,"despeckle (%.20g)",
2478 case ReduceNoisePreview:
2480 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t)
2481 radius,(size_t) radius,exception);
2482 (void) FormatLocaleString(label,MagickPathExtent,"noise %g",radius);
2485 case AddNoisePreview:
2491 (void) CopyMagickString(factor,"uniform",MagickPathExtent);
2496 (void) CopyMagickString(factor,"gaussian",MagickPathExtent);
2501 (void) CopyMagickString(factor,"multiplicative",MagickPathExtent);
2506 (void) CopyMagickString(factor,"impulse",MagickPathExtent);
2511 (void) CopyMagickString(factor,"laplacian",MagickPathExtent);
2516 (void) CopyMagickString(factor,"Poisson",MagickPathExtent);
2521 (void) CopyMagickString(thumbnail->magick,"NULL",MagickPathExtent);
2525 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
2526 (size_t) i,exception);
2527 (void) FormatLocaleString(label,MagickPathExtent,"+noise %s",factor);
2530 case SharpenPreview:
2532 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
2533 (void) FormatLocaleString(label,MagickPathExtent,"sharpen %gx%g",
2539 preview_image=BlurImage(thumbnail,radius,sigma,exception);
2540 (void) FormatLocaleString(label,MagickPathExtent,"blur %gx%g",radius,
2544 case ThresholdPreview:
2546 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2547 if (preview_image == (Image *) NULL)
2549 (void) BilevelImage(thumbnail,(double) (percentage*((double)
2550 QuantumRange+1.0))/100.0,exception);
2551 (void) FormatLocaleString(label,MagickPathExtent,"threshold %g",
2552 (double) (percentage*((double) QuantumRange+1.0))/100.0);
2555 case EdgeDetectPreview:
2557 preview_image=EdgeImage(thumbnail,radius,exception);
2558 (void) FormatLocaleString(label,MagickPathExtent,"edge %g",radius);
2563 preview_image=SpreadImage(thumbnail,image->interpolate,radius,
2565 (void) FormatLocaleString(label,MagickPathExtent,"spread %g",
2569 case SolarizePreview:
2571 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2572 if (preview_image == (Image *) NULL)
2574 (void) SolarizeImage(preview_image,(double) QuantumRange*percentage/
2576 (void) FormatLocaleString(label,MagickPathExtent,"solarize %g",
2577 (QuantumRange*percentage)/100.0);
2583 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2585 (void) FormatLocaleString(label,MagickPathExtent,"shade %gx%g",degrees,
2591 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2592 if (preview_image == (Image *) NULL)
2594 geometry.width=(size_t) (2*i+2);
2595 geometry.height=(size_t) (2*i+2);
2598 (void) RaiseImage(preview_image,&geometry,MagickTrue,exception);
2599 (void) FormatLocaleString(label,MagickPathExtent,
2600 "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
2601 geometry.height,(double) geometry.x,(double) geometry.y);
2604 case SegmentPreview:
2606 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2607 if (preview_image == (Image *) NULL)
2610 (void) SegmentImage(preview_image,sRGBColorspace,MagickFalse,threshold,
2611 threshold,exception);
2612 (void) FormatLocaleString(label,MagickPathExtent,"segment %gx%g",
2613 threshold,threshold);
2618 preview_image=SwirlImage(thumbnail,degrees,image->interpolate,
2620 (void) FormatLocaleString(label,MagickPathExtent,"swirl %g",degrees);
2624 case ImplodePreview:
2627 preview_image=ImplodeImage(thumbnail,degrees,image->interpolate,
2629 (void) FormatLocaleString(label,MagickPathExtent,"implode %g",degrees);
2635 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,
2636 image->interpolate,exception);
2637 (void) FormatLocaleString(label,MagickPathExtent,"wave %gx%g",0.5*
2638 degrees,2.0*degrees);
2641 case OilPaintPreview:
2643 preview_image=OilPaintImage(thumbnail,(double) radius,(double) sigma,
2645 (void) FormatLocaleString(label,MagickPathExtent,"charcoal %gx%g",
2649 case CharcoalDrawingPreview:
2651 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2653 (void) FormatLocaleString(label,MagickPathExtent,"charcoal %gx%g",
2660 filename[MagickPathExtent];
2668 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2669 if (preview_image == (Image *) NULL)
2671 preview_info->quality=(size_t) percentage;
2672 (void) FormatLocaleString(factor,MagickPathExtent,"%.20g",(double)
2673 preview_info->quality);
2674 file=AcquireUniqueFileResource(filename);
2677 (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
2678 "jpeg:%s",filename);
2679 status=WriteImage(preview_info,preview_image,exception);
2680 if (status != MagickFalse)
2685 (void) CopyMagickString(preview_info->filename,
2686 preview_image->filename,MagickPathExtent);
2687 quality_image=ReadImage(preview_info,exception);
2688 if (quality_image != (Image *) NULL)
2690 preview_image=DestroyImage(preview_image);
2691 preview_image=quality_image;
2694 (void) RelinquishUniqueFileResource(preview_image->filename);
2695 if ((GetBlobSize(preview_image)/1024) >= 1024)
2696 (void) FormatLocaleString(label,MagickPathExtent,"quality %s\n%gmb ",
2697 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2700 if (GetBlobSize(preview_image) >= 1024)
2701 (void) FormatLocaleString(label,MagickPathExtent,
2702 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
2703 GetBlobSize(preview_image))/1024.0);
2705 (void) FormatLocaleString(label,MagickPathExtent,
2706 "quality %s\n%.20gb ",factor,(double) ((MagickOffsetType)
2707 GetBlobSize(thumbnail)));
2711 thumbnail=DestroyImage(thumbnail);
2715 if (preview_image == (Image *) NULL)
2717 (void) DeleteImageProperty(preview_image,"label");
2718 (void) SetImageProperty(preview_image,"label",label,exception);
2719 AppendImageToList(&images,preview_image);
2720 proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2722 if (proceed == MagickFalse)
2725 if (images == (Image *) NULL)
2727 preview_info=DestroyImageInfo(preview_info);
2728 return((Image *) NULL);
2733 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2734 (void) CopyMagickString(montage_info->filename,image->filename,
2736 montage_info->shadow=MagickTrue;
2737 (void) CloneString(&montage_info->tile,"3x3");
2738 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2739 (void) CloneString(&montage_info->frame,DefaultTileFrame);
2740 montage_image=MontageImages(images,montage_info,exception);
2741 montage_info=DestroyMontageInfo(montage_info);
2742 images=DestroyImageList(images);
2743 if (montage_image == (Image *) NULL)
2744 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2745 if (montage_image->montage != (char *) NULL)
2748 Free image directory.
2750 montage_image->montage=(char *) RelinquishMagickMemory(
2751 montage_image->montage);
2752 if (image->directory != (char *) NULL)
2753 montage_image->directory=(char *) RelinquishMagickMemory(
2754 montage_image->directory);
2756 preview_info=DestroyImageInfo(preview_info);
2757 return(montage_image);
2761 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2765 % R o t a t i o n a l B l u r I m a g e %
2769 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2771 % RotationalBlurImage() applies a radial blur to the image.
2773 % Andrew Protano contributed this effect.
2775 % The format of the RotationalBlurImage method is:
2777 % Image *RotationalBlurImage(const Image *image,const double angle,
2778 % ExceptionInfo *exception)
2780 % A description of each parameter follows:
2782 % o image: the image.
2784 % o angle: the angle of the radial blur.
2788 % o exception: return any errors or warnings in this structure.
2791 MagickExport Image *RotationalBlurImage(const Image *image,const double angle,
2792 ExceptionInfo *exception)
2828 Allocate blur image.
2830 assert(image != (Image *) NULL);
2831 assert(image->signature == MagickCoreSignature);
2832 if (image->debug != MagickFalse)
2833 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2834 assert(exception != (ExceptionInfo *) NULL);
2835 assert(exception->signature == MagickCoreSignature);
2836 #if defined(MAGICKCORE_OPENCL_SUPPORT)
2837 blur_image=AccelerateRotationalBlurImage(image,angle,exception);
2838 if (blur_image != (Image *) NULL)
2841 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2842 if (blur_image == (Image *) NULL)
2843 return((Image *) NULL);
2844 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2846 blur_image=DestroyImage(blur_image);
2847 return((Image *) NULL);
2849 blur_center.x=(double) (image->columns-1)/2.0;
2850 blur_center.y=(double) (image->rows-1)/2.0;
2851 blur_radius=hypot(blur_center.x,blur_center.y);
2852 n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
2853 theta=DegreesToRadians(angle)/(double) (n-1);
2854 cos_theta=(double *) AcquireQuantumMemory((size_t) n,
2855 sizeof(*cos_theta));
2856 sin_theta=(double *) AcquireQuantumMemory((size_t) n,
2857 sizeof(*sin_theta));
2858 if ((cos_theta == (double *) NULL) ||
2859 (sin_theta == (double *) NULL))
2861 if (cos_theta != (double *) NULL)
2862 cos_theta=(double *) RelinquishMagickMemory(cos_theta);
2863 if (sin_theta != (double *) NULL)
2864 sin_theta=(double *) RelinquishMagickMemory(sin_theta);
2865 blur_image=DestroyImage(blur_image);
2866 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2868 offset=theta*(double) (n-1)/2.0;
2869 for (i=0; i < (ssize_t) n; i++)
2871 cos_theta[i]=cos((double) (theta*i-offset));
2872 sin_theta[i]=sin((double) (theta*i-offset));
2879 image_view=AcquireVirtualCacheView(image,exception);
2880 radial_view=AcquireVirtualCacheView(image,exception);
2881 blur_view=AcquireAuthenticCacheView(blur_image,exception);
2882 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2883 #pragma omp parallel for schedule(static) shared(progress,status) \
2884 magick_number_threads(image,blur_image,image->rows,1)
2886 for (y=0; y < (ssize_t) image->rows; y++)
2888 register const Quantum
2897 if (status == MagickFalse)
2899 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2900 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2902 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2907 for (x=0; x < (ssize_t) image->columns; x++)
2921 center.x=(double) x-blur_center.x;
2922 center.y=(double) y-blur_center.y;
2923 radius=hypot((double) center.x,center.y);
2928 step=(size_t) (blur_radius/radius);
2935 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2948 register const Quantum
2954 channel=GetPixelChannelChannel(image,i);
2955 traits=GetPixelChannelTraits(image,channel);
2956 blur_traits=GetPixelChannelTraits(blur_image,channel);
2957 if ((traits == UndefinedPixelTrait) ||
2958 (blur_traits == UndefinedPixelTrait))
2960 if ((blur_traits & CopyPixelTrait) != 0)
2962 SetPixelChannel(blur_image,channel,p[i],q);
2967 if ((GetPixelChannelTraits(image,AlphaPixelChannel) == UndefinedPixelTrait) ||
2968 (channel == AlphaPixelChannel))
2970 for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2972 r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
2973 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2974 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2976 if (r == (const Quantum *) NULL)
2984 gamma=PerceptibleReciprocal(gamma);
2985 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2988 for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2993 r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
2994 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2995 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2997 if (r == (const Quantum *) NULL)
3002 alpha=(double) QuantumScale*GetPixelAlpha(image,r);
3006 gamma=PerceptibleReciprocal(gamma);
3007 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3009 p+=GetPixelChannels(image);
3010 q+=GetPixelChannels(blur_image);
3012 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3014 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3019 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3020 #pragma omp critical (MagickCore_RotationalBlurImage)
3022 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3023 if (proceed == MagickFalse)
3027 blur_view=DestroyCacheView(blur_view);
3028 radial_view=DestroyCacheView(radial_view);
3029 image_view=DestroyCacheView(image_view);
3030 cos_theta=(double *) RelinquishMagickMemory(cos_theta);
3031 sin_theta=(double *) RelinquishMagickMemory(sin_theta);
3032 if (status == MagickFalse)
3033 blur_image=DestroyImage(blur_image);
3038 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3042 % S e l e c t i v e B l u r I m a g e %
3046 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3048 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3049 % It is similar to the unsharpen mask that sharpens everything with contrast
3050 % above a certain threshold.
3052 % The format of the SelectiveBlurImage method is:
3054 % Image *SelectiveBlurImage(const Image *image,const double radius,
3055 % const double sigma,const double threshold,ExceptionInfo *exception)
3057 % A description of each parameter follows:
3059 % o image: the image.
3061 % o radius: the radius of the Gaussian, in pixels, not counting the center
3064 % o sigma: the standard deviation of the Gaussian, in pixels.
3066 % o threshold: only pixels within this contrast threshold are included
3067 % in the blur operation.
3069 % o exception: return any errors or warnings in this structure.
3072 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
3073 const double sigma,const double threshold,ExceptionInfo *exception)
3075 #define SelectiveBlurImageTag "SelectiveBlur/Image"
3109 Initialize blur image attributes.
3111 assert(image != (Image *) NULL);
3112 assert(image->signature == MagickCoreSignature);
3113 if (image->debug != MagickFalse)
3114 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3115 assert(exception != (ExceptionInfo *) NULL);
3116 assert(exception->signature == MagickCoreSignature);
3117 width=GetOptimalKernelWidth1D(radius,sigma);
3118 kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
3119 width,width*sizeof(*kernel)));
3120 if (kernel == (MagickRealType *) NULL)
3121 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3122 j=(ssize_t) (width-1)/2;
3124 for (v=(-j); v <= j; v++)
3126 for (u=(-j); u <= j; u++)
3127 kernel[i++]=(MagickRealType) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
3128 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3130 if (image->debug != MagickFalse)
3133 format[MagickPathExtent],
3136 register const MagickRealType
3143 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
3144 " SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
3146 message=AcquireString("");
3148 for (v=0; v < (ssize_t) width; v++)
3151 (void) FormatLocaleString(format,MagickPathExtent,"%.20g: ",(double) v);
3152 (void) ConcatenateString(&message,format);
3153 for (u=0; u < (ssize_t) width; u++)
3155 (void) FormatLocaleString(format,MagickPathExtent,"%+f ",(double)
3157 (void) ConcatenateString(&message,format);
3159 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3161 message=DestroyString(message);
3163 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3164 if (blur_image == (Image *) NULL)
3165 return((Image *) NULL);
3166 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
3168 blur_image=DestroyImage(blur_image);
3169 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3170 return((Image *) NULL);
3172 luminance_image=CloneImage(image,0,0,MagickTrue,exception);
3173 if (luminance_image == (Image *) NULL)
3175 blur_image=DestroyImage(blur_image);
3176 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3177 return((Image *) NULL);
3179 status=TransformImageColorspace(luminance_image,GRAYColorspace,exception);
3180 if (status == MagickFalse)
3182 luminance_image=DestroyImage(luminance_image);
3183 blur_image=DestroyImage(blur_image);
3184 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3185 return((Image *) NULL);
3188 Threshold blur image.
3192 center=(ssize_t) (GetPixelChannels(image)*(image->columns+width)*
3193 ((width-1)/2L)+GetPixelChannels(image)*((width-1)/2L));
3194 image_view=AcquireVirtualCacheView(image,exception);
3195 luminance_view=AcquireVirtualCacheView(luminance_image,exception);
3196 blur_view=AcquireAuthenticCacheView(blur_image,exception);
3197 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3198 #pragma omp parallel for schedule(static) shared(progress,status) \
3199 magick_number_threads(image,blur_image,image->rows,1)
3201 for (y=0; y < (ssize_t) image->rows; y++)
3209 register const Quantum
3219 if (status == MagickFalse)
3221 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) (width-1)/2L),y-(ssize_t)
3222 ((width-1)/2L),image->columns+width,width,exception);
3223 l=GetCacheViewVirtualPixels(luminance_view,-((ssize_t) (width-1)/2L),y-
3224 (ssize_t) ((width-1)/2L),luminance_image->columns+width,width,exception);
3225 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3227 if ((p == (const Quantum *) NULL) || (l == (const Quantum *) NULL) ||
3228 (q == (Quantum *) NULL))
3233 for (x=0; x < (ssize_t) image->columns; x++)
3241 intensity=GetPixelIntensity(image,p+center);
3242 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3256 register const MagickRealType
3259 register const Quantum
3260 *magick_restrict luminance_pixels,
3261 *magick_restrict pixels;
3269 channel=GetPixelChannelChannel(image,i);
3270 traits=GetPixelChannelTraits(image,channel);
3271 blur_traits=GetPixelChannelTraits(blur_image,channel);
3272 if ((traits == UndefinedPixelTrait) ||
3273 (blur_traits == UndefinedPixelTrait))
3275 if ((blur_traits & CopyPixelTrait) != 0)
3277 SetPixelChannel(blur_image,channel,p[center+i],q);
3285 if ((blur_traits & BlendPixelTrait) == 0)
3287 for (v=0; v < (ssize_t) width; v++)
3289 for (u=0; u < (ssize_t) width; u++)
3291 contrast=GetPixelIntensity(luminance_image,luminance_pixels)-
3293 if (fabs(contrast) < threshold)
3295 pixel+=(*k)*pixels[i];
3299 pixels+=GetPixelChannels(image);
3300 luminance_pixels+=GetPixelChannels(luminance_image);
3302 pixels+=GetPixelChannels(image)*image->columns;
3303 luminance_pixels+=GetPixelChannels(luminance_image)*
3304 luminance_image->columns;
3306 if (fabs((double) gamma) < MagickEpsilon)
3308 SetPixelChannel(blur_image,channel,p[center+i],q);
3311 gamma=PerceptibleReciprocal(gamma);
3312 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3315 for (v=0; v < (ssize_t) width; v++)
3317 for (u=0; u < (ssize_t) width; u++)
3319 contrast=GetPixelIntensity(image,pixels)-intensity;
3320 if (fabs(contrast) < threshold)
3322 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
3323 pixel+=(*k)*alpha*pixels[i];
3327 pixels+=GetPixelChannels(image);
3328 luminance_pixels+=GetPixelChannels(luminance_image);
3330 pixels+=GetPixelChannels(image)*image->columns;
3331 luminance_pixels+=GetPixelChannels(luminance_image)*
3332 luminance_image->columns;
3334 if (fabs((double) gamma) < MagickEpsilon)
3336 SetPixelChannel(blur_image,channel,p[center+i],q);
3339 gamma=PerceptibleReciprocal(gamma);
3340 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3342 p+=GetPixelChannels(image);
3343 l+=GetPixelChannels(luminance_image);
3344 q+=GetPixelChannels(blur_image);
3346 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3347 if (sync == MagickFalse)
3349 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3354 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3355 #pragma omp critical (MagickCore_SelectiveBlurImage)
3357 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
3359 if (proceed == MagickFalse)
3363 blur_image->type=image->type;
3364 blur_view=DestroyCacheView(blur_view);
3365 image_view=DestroyCacheView(image_view);
3366 luminance_image=DestroyImage(luminance_image);
3367 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3368 if (status == MagickFalse)
3369 blur_image=DestroyImage(blur_image);
3374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3378 % S h a d e I m a g e %
3382 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3384 % ShadeImage() shines a distant light on an image to create a
3385 % three-dimensional effect. You control the positioning of the light with
3386 % azimuth and elevation; azimuth is measured in degrees off the x axis
3387 % and elevation is measured in pixels above the Z axis.
3389 % The format of the ShadeImage method is:
3391 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3392 % const double azimuth,const double elevation,ExceptionInfo *exception)
3394 % A description of each parameter follows:
3396 % o image: the image.
3398 % o gray: A value other than zero shades the intensity of each pixel.
3400 % o azimuth, elevation: Define the light source direction.
3402 % o exception: return any errors or warnings in this structure.
3405 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3406 const double azimuth,const double elevation,ExceptionInfo *exception)
3408 #define ShadeImageTag "Shade/Image"
3431 Initialize shaded image attributes.
3433 assert(image != (const Image *) NULL);
3434 assert(image->signature == MagickCoreSignature);
3435 if (image->debug != MagickFalse)
3436 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3437 assert(exception != (ExceptionInfo *) NULL);
3438 assert(exception->signature == MagickCoreSignature);
3439 linear_image=CloneImage(image,0,0,MagickTrue,exception);
3440 shade_image=CloneImage(image,0,0,MagickTrue,exception);
3441 if ((linear_image == (Image *) NULL) || (shade_image == (Image *) NULL))
3443 if (linear_image != (Image *) NULL)
3444 linear_image=DestroyImage(linear_image);
3445 if (shade_image != (Image *) NULL)
3446 shade_image=DestroyImage(shade_image);
3447 return((Image *) NULL);
3449 if (SetImageStorageClass(shade_image,DirectClass,exception) == MagickFalse)
3451 linear_image=DestroyImage(linear_image);
3452 shade_image=DestroyImage(shade_image);
3453 return((Image *) NULL);
3456 Compute the light vector.
3458 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3459 cos(DegreesToRadians(elevation));
3460 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3461 cos(DegreesToRadians(elevation));
3462 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3468 image_view=AcquireVirtualCacheView(linear_image,exception);
3469 shade_view=AcquireAuthenticCacheView(shade_image,exception);
3470 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3471 #pragma omp parallel for schedule(static) shared(progress,status) \
3472 magick_number_threads(linear_image,shade_image,linear_image->rows,1)
3474 for (y=0; y < (ssize_t) linear_image->rows; y++)
3484 register const Quantum
3485 *magick_restrict center,
3487 *magick_restrict post,
3488 *magick_restrict pre;
3496 if (status == MagickFalse)
3498 p=GetCacheViewVirtualPixels(image_view,-1,y-1,linear_image->columns+2,3,
3500 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3502 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3508 Shade this row of pixels.
3510 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
3511 for (x=0; x < (ssize_t) linear_image->columns; x++)
3517 Determine the surface normal and compute shading.
3519 pre=p+GetPixelChannels(linear_image);
3520 center=pre+(linear_image->columns+2)*GetPixelChannels(linear_image);
3521 post=center+(linear_image->columns+2)*GetPixelChannels(linear_image);
3523 GetPixelIntensity(linear_image,pre-GetPixelChannels(linear_image))+
3524 GetPixelIntensity(linear_image,center-GetPixelChannels(linear_image))+
3525 GetPixelIntensity(linear_image,post-GetPixelChannels(linear_image))-
3526 GetPixelIntensity(linear_image,pre+GetPixelChannels(linear_image))-
3527 GetPixelIntensity(linear_image,center+GetPixelChannels(linear_image))-
3528 GetPixelIntensity(linear_image,post+GetPixelChannels(linear_image)));
3530 GetPixelIntensity(linear_image,post-GetPixelChannels(linear_image))+
3531 GetPixelIntensity(linear_image,post)+
3532 GetPixelIntensity(linear_image,post+GetPixelChannels(linear_image))-
3533 GetPixelIntensity(linear_image,pre-GetPixelChannels(linear_image))-
3534 GetPixelIntensity(linear_image,pre)-
3535 GetPixelIntensity(linear_image,pre+GetPixelChannels(linear_image)));
3536 if ((fabs(normal.x) <= MagickEpsilon) &&
3537 (fabs(normal.y) <= MagickEpsilon))
3542 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3543 if (distance > MagickEpsilon)
3545 normal_distance=normal.x*normal.x+normal.y*normal.y+
3547 if (normal_distance > (MagickEpsilon*MagickEpsilon))
3548 shade=distance/sqrt((double) normal_distance);
3551 for (i=0; i < (ssize_t) GetPixelChannels(linear_image); i++)
3560 channel=GetPixelChannelChannel(linear_image,i);
3561 traits=GetPixelChannelTraits(linear_image,channel);
3562 shade_traits=GetPixelChannelTraits(shade_image,channel);
3563 if ((traits == UndefinedPixelTrait) ||
3564 (shade_traits == UndefinedPixelTrait))
3566 if ((shade_traits & CopyPixelTrait) != 0)
3568 SetPixelChannel(shade_image,channel,center[i],q);
3571 if ((traits & UpdatePixelTrait) == 0)
3573 SetPixelChannel(shade_image,channel,center[i],q);
3576 if (gray != MagickFalse)
3578 SetPixelChannel(shade_image,channel,ClampToQuantum(shade),q);
3581 SetPixelChannel(shade_image,channel,ClampToQuantum(QuantumScale*shade*
3584 p+=GetPixelChannels(linear_image);
3585 q+=GetPixelChannels(shade_image);
3587 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3589 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3594 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3595 #pragma omp critical (MagickCore_ShadeImage)
3597 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
3598 if (proceed == MagickFalse)
3602 shade_view=DestroyCacheView(shade_view);
3603 image_view=DestroyCacheView(image_view);
3604 linear_image=DestroyImage(linear_image);
3605 if (status == MagickFalse)
3606 shade_image=DestroyImage(shade_image);
3607 return(shade_image);
3611 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3615 % S h a r p e n I m a g e %
3619 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3621 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
3622 % operator of the given radius and standard deviation (sigma). For
3623 % reasonable results, radius should be larger than sigma. Use a radius of 0
3624 % and SharpenImage() selects a suitable radius for you.
3626 % Using a separable kernel would be faster, but the negative weights cancel
3627 % out on the corners of the kernel producing often undesirable ringing in the
3628 % filtered result; this can be avoided by using a 2D gaussian shaped image
3629 % sharpening kernel instead.
3631 % The format of the SharpenImage method is:
3633 % Image *SharpenImage(const Image *image,const double radius,
3634 % const double sigma,ExceptionInfo *exception)
3636 % A description of each parameter follows:
3638 % o image: the image.
3640 % o radius: the radius of the Gaussian, in pixels, not counting the center
3643 % o sigma: the standard deviation of the Laplacian, in pixels.
3645 % o exception: return any errors or warnings in this structure.
3648 MagickExport Image *SharpenImage(const Image *image,const double radius,
3649 const double sigma,ExceptionInfo *exception)
3672 assert(image != (const Image *) NULL);
3673 assert(image->signature == MagickCoreSignature);
3674 if (image->debug != MagickFalse)
3675 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3676 assert(exception != (ExceptionInfo *) NULL);
3677 assert(exception->signature == MagickCoreSignature);
3678 width=GetOptimalKernelWidth2D(radius,sigma);
3679 kernel_info=AcquireKernelInfo((const char *) NULL,exception);
3680 if (kernel_info == (KernelInfo *) NULL)
3681 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3682 (void) memset(kernel_info,0,sizeof(*kernel_info));
3683 kernel_info->width=width;
3684 kernel_info->height=width;
3685 kernel_info->x=(ssize_t) (width-1)/2;
3686 kernel_info->y=(ssize_t) (width-1)/2;
3687 kernel_info->signature=MagickCoreSignature;
3688 kernel_info->values=(MagickRealType *) MagickAssumeAligned(
3689 AcquireAlignedMemory(kernel_info->width,kernel_info->height*
3690 sizeof(*kernel_info->values)));
3691 if (kernel_info->values == (MagickRealType *) NULL)
3693 kernel_info=DestroyKernelInfo(kernel_info);
3694 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3697 j=(ssize_t) (kernel_info->width-1)/2;
3699 for (v=(-j); v <= j; v++)
3701 for (u=(-j); u <= j; u++)
3703 kernel_info->values[i]=(MagickRealType) (-exp(-((double) u*u+v*v)/(2.0*
3704 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3705 normalize+=kernel_info->values[i];
3709 kernel_info->values[i/2]=(double) ((-2.0)*normalize);
3711 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
3712 normalize+=kernel_info->values[i];
3713 gamma=PerceptibleReciprocal(normalize);
3714 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
3715 kernel_info->values[i]*=gamma;
3716 sharp_image=ConvolveImage(image,kernel_info,exception);
3717 kernel_info=DestroyKernelInfo(kernel_info);
3718 return(sharp_image);
3722 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3726 % S p r e a d I m a g e %
3730 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3732 % SpreadImage() is a special effects method that randomly displaces each
3733 % pixel in a square area defined by the radius parameter.
3735 % The format of the SpreadImage method is:
3737 % Image *SpreadImage(const Image *image,
3738 % const PixelInterpolateMethod method,const double radius,
3739 % ExceptionInfo *exception)
3741 % A description of each parameter follows:
3743 % o image: the image.
3745 % o method: intepolation method.
3747 % o radius: choose a random pixel in a neighborhood of this extent.
3749 % o exception: return any errors or warnings in this structure.
3752 MagickExport Image *SpreadImage(const Image *image,
3753 const PixelInterpolateMethod method,const double radius,
3754 ExceptionInfo *exception)
3756 #define SpreadImageTag "Spread/Image"
3772 **magick_restrict random_info;
3780 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3786 Initialize spread image attributes.
3788 assert(image != (Image *) NULL);
3789 assert(image->signature == MagickCoreSignature);
3790 if (image->debug != MagickFalse)
3791 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3792 assert(exception != (ExceptionInfo *) NULL);
3793 assert(exception->signature == MagickCoreSignature);
3794 spread_image=CloneImage(image,0,0,MagickTrue,exception);
3795 if (spread_image == (Image *) NULL)
3796 return((Image *) NULL);
3797 if (SetImageStorageClass(spread_image,DirectClass,exception) == MagickFalse)
3799 spread_image=DestroyImage(spread_image);
3800 return((Image *) NULL);
3807 width=GetOptimalKernelWidth1D(radius,0.5);
3808 random_info=AcquireRandomInfoThreadSet();
3809 image_view=AcquireVirtualCacheView(image,exception);
3810 spread_view=AcquireAuthenticCacheView(spread_image,exception);
3811 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3812 key=GetRandomSecretKey(random_info[0]);
3813 #pragma omp parallel for schedule(static) shared(progress,status) \
3814 magick_number_threads(image,spread_image,image->rows,key == ~0UL)
3816 for (y=0; y < (ssize_t) image->rows; y++)
3819 id = GetOpenMPThreadId();
3827 if (status == MagickFalse)
3829 q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
3831 if (q == (Quantum *) NULL)
3836 for (x=0; x < (ssize_t) image->columns; x++)
3841 point.x=GetPseudoRandomValue(random_info[id]);
3842 point.y=GetPseudoRandomValue(random_info[id]);
3843 status=InterpolatePixelChannels(image,image_view,spread_image,method,
3844 (double) x+width*(point.x-0.5),(double) y+width*(point.y-0.5),q,
3846 if (status == MagickFalse)
3848 q+=GetPixelChannels(spread_image);
3850 if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
3852 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3857 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3858 #pragma omp critical (MagickCore_SpreadImage)
3860 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3861 if (proceed == MagickFalse)
3865 spread_view=DestroyCacheView(spread_view);
3866 image_view=DestroyCacheView(image_view);
3867 random_info=DestroyRandomInfoThreadSet(random_info);
3868 if (status == MagickFalse)
3869 spread_image=DestroyImage(spread_image);
3870 return(spread_image);
3874 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3878 % U n s h a r p M a s k I m a g e %
3882 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3884 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
3885 % image with a Gaussian operator of the given radius and standard deviation
3886 % (sigma). For reasonable results, radius should be larger than sigma. Use a
3887 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
3889 % The format of the UnsharpMaskImage method is:
3891 % Image *UnsharpMaskImage(const Image *image,const double radius,
3892 % const double sigma,const double amount,const double threshold,
3893 % ExceptionInfo *exception)
3895 % A description of each parameter follows:
3897 % o image: the image.
3899 % o radius: the radius of the Gaussian, in pixels, not counting the center
3902 % o sigma: the standard deviation of the Gaussian, in pixels.
3904 % o gain: the percentage of the difference between the original and the
3905 % blur image that is added back into the original.
3907 % o threshold: the threshold in pixels needed to apply the diffence gain.
3909 % o exception: return any errors or warnings in this structure.
3912 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
3913 const double sigma,const double gain,const double threshold,
3914 ExceptionInfo *exception)
3916 #define SharpenImageTag "Sharpen/Image"
3937 assert(image != (const Image *) NULL);
3938 assert(image->signature == MagickCoreSignature);
3939 if (image->debug != MagickFalse)
3940 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3941 assert(exception != (ExceptionInfo *) NULL);
3942 #if defined(MAGICKCORE_OPENCL_SUPPORT)
3943 unsharp_image=AccelerateUnsharpMaskImage(image,radius,sigma,gain,threshold,
3945 if (unsharp_image != (Image *) NULL)
3946 return(unsharp_image);
3948 unsharp_image=BlurImage(image,radius,sigma,exception);
3949 if (unsharp_image == (Image *) NULL)
3950 return((Image *) NULL);
3951 quantum_threshold=(double) QuantumRange*threshold;
3957 image_view=AcquireVirtualCacheView(image,exception);
3958 unsharp_view=AcquireAuthenticCacheView(unsharp_image,exception);
3959 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3960 #pragma omp parallel for schedule(static) shared(progress,status) \
3961 magick_number_threads(image,unsharp_image,image->rows,1)
3963 for (y=0; y < (ssize_t) image->rows; y++)
3965 register const Quantum
3974 if (status == MagickFalse)
3976 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3977 q=QueueCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
3979 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3984 for (x=0; x < (ssize_t) image->columns; x++)
3989 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4001 channel=GetPixelChannelChannel(image,i);
4002 traits=GetPixelChannelTraits(image,channel);
4003 unsharp_traits=GetPixelChannelTraits(unsharp_image,channel);
4004 if ((traits == UndefinedPixelTrait) ||
4005 (unsharp_traits == UndefinedPixelTrait))
4007 if ((unsharp_traits & CopyPixelTrait) != 0)
4009 SetPixelChannel(unsharp_image,channel,p[i],q);
4012 pixel=p[i]-(double) GetPixelChannel(unsharp_image,channel,q);
4013 if (fabs(2.0*pixel) < quantum_threshold)
4014 pixel=(double) p[i];
4016 pixel=(double) p[i]+gain*pixel;
4017 SetPixelChannel(unsharp_image,channel,ClampToQuantum(pixel),q);
4019 p+=GetPixelChannels(image);
4020 q+=GetPixelChannels(unsharp_image);
4022 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4024 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4029 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4030 #pragma omp critical (MagickCore_UnsharpMaskImage)
4032 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
4033 if (proceed == MagickFalse)
4037 unsharp_image->type=image->type;
4038 unsharp_view=DestroyCacheView(unsharp_view);
4039 image_view=DestroyCacheView(image_view);
4040 if (status == MagickFalse)
4041 unsharp_image=DestroyImage(unsharp_image);
4042 return(unsharp_image);