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-2017 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,image->columns,image->rows,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) ResetMagickMemory(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,4) shared(progress,status) \
256 magick_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) ||
339 (GetPixelWriteMask(image,p+center) == 0))
341 SetPixelChannel(blur_image,channel,p[center+i],q);
348 if ((blur_traits & BlendPixelTrait) == 0)
353 for (v=0; v < (ssize_t) (width-j); v++)
355 for (u=0; u < (ssize_t) (width-j); u++)
357 pixel+=(*k)*pixels[i];
360 pixels+=GetPixelChannels(image);
363 gamma=PerceptibleReciprocal(gamma);
364 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
370 for (v=0; v < (ssize_t) (width-j); v++)
372 for (u=0; u < (ssize_t) (width-j); u++)
374 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
375 pixel+=(*k)*alpha*pixels[i];
378 pixels+=GetPixelChannels(image);
381 gamma=PerceptibleReciprocal(gamma);
382 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
384 q+=GetPixelChannels(blur_image);
385 r+=GetPixelChannels(edge_image);
387 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
389 if (image->progress_monitor != (MagickProgressMonitor) NULL)
394 #if defined(MAGICKCORE_OPENMP_SUPPORT)
395 #pragma omp critical (MagickCore_AdaptiveBlurImage)
397 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
399 if (proceed == MagickFalse)
403 blur_image->type=image->type;
404 blur_view=DestroyCacheView(blur_view);
405 edge_view=DestroyCacheView(edge_view);
406 image_view=DestroyCacheView(image_view);
407 edge_image=DestroyImage(edge_image);
408 for (i=0; i < (ssize_t) width; i+=2)
409 kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
410 kernel=(double **) RelinquishAlignedMemory(kernel);
411 if (status == MagickFalse)
412 blur_image=DestroyImage(blur_image);
417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
421 % A d a p t i v e S h a r p e n I m a g e %
425 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
427 % AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
428 % intensely near image edges and less intensely far from edges. We sharpen the
429 % image with a Gaussian operator of the given radius and standard deviation
430 % (sigma). For reasonable results, radius should be larger than sigma. Use a
431 % radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
433 % The format of the AdaptiveSharpenImage method is:
435 % Image *AdaptiveSharpenImage(const Image *image,const double radius,
436 % const double sigma,ExceptionInfo *exception)
438 % A description of each parameter follows:
440 % o image: the image.
442 % o radius: the radius of the Gaussian, in pixels, not counting the center
445 % o sigma: the standard deviation of the Laplacian, in pixels.
447 % o exception: return any errors or warnings in this structure.
450 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
451 const double sigma,ExceptionInfo *exception)
453 #define AdaptiveSharpenImageTag "Convolve/Image"
454 #define MagickSigma (fabs(sigma) < MagickEpsilon ? MagickEpsilon : sigma)
489 assert(image != (const Image *) NULL);
490 assert(image->signature == MagickCoreSignature);
491 if (image->debug != MagickFalse)
492 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
493 assert(exception != (ExceptionInfo *) NULL);
494 assert(exception->signature == MagickCoreSignature);
495 sharp_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
496 if (sharp_image == (Image *) NULL)
497 return((Image *) NULL);
498 if (fabs(sigma) < MagickEpsilon)
500 if (SetImageStorageClass(sharp_image,DirectClass,exception) == MagickFalse)
502 sharp_image=DestroyImage(sharp_image);
503 return((Image *) NULL);
506 Edge detect the image brightness channel, level, sharp, and level again.
508 edge_image=EdgeImage(image,radius,exception);
509 if (edge_image == (Image *) NULL)
511 sharp_image=DestroyImage(sharp_image);
512 return((Image *) NULL);
514 (void) AutoLevelImage(edge_image,exception);
515 gaussian_image=BlurImage(edge_image,radius,sigma,exception);
516 if (gaussian_image != (Image *) NULL)
518 edge_image=DestroyImage(edge_image);
519 edge_image=gaussian_image;
521 (void) AutoLevelImage(edge_image,exception);
523 Create a set of kernels from maximum (radius,sigma) to minimum.
525 width=GetOptimalKernelWidth2D(radius,sigma);
526 kernel=(double **) MagickAssumeAligned(AcquireAlignedMemory((size_t)
527 width,sizeof(*kernel)));
528 if (kernel == (double **) NULL)
530 edge_image=DestroyImage(edge_image);
531 sharp_image=DestroyImage(sharp_image);
532 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
534 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
535 for (i=0; i < (ssize_t) width; i+=2)
537 kernel[i]=(double *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
538 (width-i),(width-i)*sizeof(**kernel)));
539 if (kernel[i] == (double *) NULL)
542 j=(ssize_t) (width-i-1)/2;
544 for (v=(-j); v <= j; v++)
546 for (u=(-j); u <= j; u++)
548 kernel[i][k]=(double) (-exp(-((double) u*u+v*v)/(2.0*MagickSigma*
549 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
550 normalize+=kernel[i][k];
554 kernel[i][(k-1)/2]=(double) ((-2.0)*normalize);
555 if (sigma < MagickEpsilon)
556 kernel[i][(k-1)/2]=1.0;
558 if (i < (ssize_t) width)
560 for (i-=2; i >= 0; i-=2)
561 kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
562 kernel=(double **) RelinquishAlignedMemory(kernel);
563 edge_image=DestroyImage(edge_image);
564 sharp_image=DestroyImage(sharp_image);
565 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
568 Adaptively sharpen image.
572 image_view=AcquireVirtualCacheView(image,exception);
573 edge_view=AcquireVirtualCacheView(edge_image,exception);
574 sharp_view=AcquireAuthenticCacheView(sharp_image,exception);
575 #if defined(MAGICKCORE_OPENMP_SUPPORT)
576 #pragma omp parallel for schedule(static,4) shared(progress,status) \
577 magick_threads(image,sharp_image,sharp_image->rows,1)
579 for (y=0; y < (ssize_t) sharp_image->rows; y++)
581 register const Quantum
590 if (status == MagickFalse)
592 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
593 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
595 if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
600 for (x=0; x < (ssize_t) sharp_image->columns; x++)
602 register const Quantum
612 j=(ssize_t) ceil((double) width*(1.0-QuantumScale*
613 GetPixelIntensity(edge_image,r))-0.5);
617 if (j > (ssize_t) width)
621 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
622 (ssize_t) ((width-j)/2L),width-j,width-j,exception);
623 if (p == (const Quantum *) NULL)
625 center=(ssize_t) GetPixelChannels(image)*(width-j)*((width-j)/2L)+
626 GetPixelChannels(image)*((width-j)/2);
627 for (i=0; i < (ssize_t) GetPixelChannels(sharp_image); i++)
641 register const double
644 register const Quantum
645 *magick_restrict pixels;
653 channel=GetPixelChannelChannel(image,i);
654 traits=GetPixelChannelTraits(image,channel);
655 sharp_traits=GetPixelChannelTraits(sharp_image,channel);
656 if ((traits == UndefinedPixelTrait) ||
657 (sharp_traits == UndefinedPixelTrait))
659 if (((sharp_traits & CopyPixelTrait) != 0) ||
660 (GetPixelWriteMask(image,p+center) == 0))
662 SetPixelChannel(sharp_image,channel,p[center+i],q);
669 if ((sharp_traits & BlendPixelTrait) == 0)
674 for (v=0; v < (ssize_t) (width-j); v++)
676 for (u=0; u < (ssize_t) (width-j); u++)
678 pixel+=(*k)*pixels[i];
681 pixels+=GetPixelChannels(image);
684 gamma=PerceptibleReciprocal(gamma);
685 SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
691 for (v=0; v < (ssize_t) (width-j); v++)
693 for (u=0; u < (ssize_t) (width-j); u++)
695 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
696 pixel+=(*k)*alpha*pixels[i];
699 pixels+=GetPixelChannels(image);
702 gamma=PerceptibleReciprocal(gamma);
703 SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
705 q+=GetPixelChannels(sharp_image);
706 r+=GetPixelChannels(edge_image);
708 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
710 if (image->progress_monitor != (MagickProgressMonitor) NULL)
715 #if defined(MAGICKCORE_OPENMP_SUPPORT)
716 #pragma omp critical (MagickCore_AdaptiveSharpenImage)
718 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
720 if (proceed == MagickFalse)
724 sharp_image->type=image->type;
725 sharp_view=DestroyCacheView(sharp_view);
726 edge_view=DestroyCacheView(edge_view);
727 image_view=DestroyCacheView(image_view);
728 edge_image=DestroyImage(edge_image);
729 for (i=0; i < (ssize_t) width; i+=2)
730 kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
731 kernel=(double **) RelinquishAlignedMemory(kernel);
732 if (status == MagickFalse)
733 sharp_image=DestroyImage(sharp_image);
738 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
742 % B l u r I m a g e %
746 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
748 % BlurImage() blurs an image. We convolve the image with a Gaussian operator
749 % of the given radius and standard deviation (sigma). For reasonable results,
750 % the radius should be larger than sigma. Use a radius of 0 and BlurImage()
751 % selects a suitable radius for you.
753 % The format of the BlurImage method is:
755 % Image *BlurImage(const Image *image,const double radius,
756 % const double sigma,ExceptionInfo *exception)
758 % A description of each parameter follows:
760 % o image: the image.
762 % o radius: the radius of the Gaussian, in pixels, not counting the center
765 % o sigma: the standard deviation of the Gaussian, in pixels.
767 % o exception: return any errors or warnings in this structure.
770 MagickExport Image *BlurImage(const Image *image,const double radius,
771 const double sigma,ExceptionInfo *exception)
774 geometry[MagickPathExtent];
782 assert(image != (const Image *) NULL);
783 assert(image->signature == MagickCoreSignature);
784 if (image->debug != MagickFalse)
785 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
786 assert(exception != (ExceptionInfo *) NULL);
787 assert(exception->signature == MagickCoreSignature);
788 #if defined(MAGICKCORE_OPENCL_SUPPORT)
789 blur_image=AccelerateBlurImage(image,radius,sigma,exception);
790 if (blur_image != (Image *) NULL)
793 (void) FormatLocaleString(geometry,MagickPathExtent,
794 "blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
795 kernel_info=AcquireKernelInfo(geometry,exception);
796 if (kernel_info == (KernelInfo *) NULL)
797 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
798 blur_image=ConvolveImage(image,kernel_info,exception);
799 kernel_info=DestroyKernelInfo(kernel_info);
804 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
808 % C o n v o l v e I m a g e %
812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
814 % ConvolveImage() applies a custom convolution kernel to the image.
816 % The format of the ConvolveImage method is:
818 % Image *ConvolveImage(const Image *image,const KernelInfo *kernel,
819 % ExceptionInfo *exception)
821 % A description of each parameter follows:
823 % o image: the image.
825 % o kernel: the filtering kernel.
827 % o exception: return any errors or warnings in this structure.
830 MagickExport Image *ConvolveImage(const Image *image,
831 const KernelInfo *kernel_info,ExceptionInfo *exception)
836 #if defined(MAGICKCORE_OPENCL_SUPPORT)
837 convolve_image=AccelerateConvolveImage(image,kernel_info,exception);
838 if (convolve_image != (Image *) NULL)
839 return(convolve_image);
842 convolve_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,
844 return(convolve_image);
848 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
852 % D e s p e c k l e I m a g e %
856 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
858 % DespeckleImage() reduces the speckle noise in an image while perserving the
859 % edges of the original image. A speckle removing filter uses a complementary
860 % hulling technique (raising pixels that are darker than their surrounding
861 % neighbors, then complementarily lowering pixels that are brighter than their
862 % surrounding neighbors) to reduce the speckle index of that image (reference
863 % Crimmins speckle removal).
865 % The format of the DespeckleImage method is:
867 % Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
869 % A description of each parameter follows:
871 % o image: the image.
873 % o exception: return any errors or warnings in this structure.
877 static void Hull(const Image *image,const ssize_t x_offset,
878 const ssize_t y_offset,const size_t columns,const size_t rows,
879 const int polarity,Quantum *magick_restrict f,Quantum *magick_restrict g)
890 assert(image != (const Image *) NULL);
891 assert(image->signature == MagickCoreSignature);
892 if (image->debug != MagickFalse)
893 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
894 assert(f != (Quantum *) NULL);
895 assert(g != (Quantum *) NULL);
898 r=p+(y_offset*(columns+2)+x_offset);
899 #if defined(MAGICKCORE_OPENMP_SUPPORT)
900 #pragma omp parallel for schedule(static,4) \
901 magick_threads(image,image,1,1)
903 for (y=0; y < (ssize_t) rows; y++)
914 for (x=0; x < (ssize_t) columns; x++)
916 v=(MagickRealType) p[i];
917 if ((MagickRealType) r[i] >= (v+ScaleCharToQuantum(2)))
918 v+=ScaleCharToQuantum(1);
923 for (x=0; x < (ssize_t) columns; x++)
925 v=(MagickRealType) p[i];
926 if ((MagickRealType) r[i] <= (v-ScaleCharToQuantum(2)))
927 v-=ScaleCharToQuantum(1);
934 r=q+(y_offset*(columns+2)+x_offset);
935 s=q-(y_offset*(columns+2)+x_offset);
936 #if defined(MAGICKCORE_OPENMP_SUPPORT)
937 #pragma omp parallel for schedule(static,4) \
938 magick_threads(image,image,1,1)
940 for (y=0; y < (ssize_t) rows; y++)
951 for (x=0; x < (ssize_t) columns; x++)
953 v=(MagickRealType) q[i];
954 if (((MagickRealType) s[i] >= (v+ScaleCharToQuantum(2))) &&
955 ((MagickRealType) r[i] > v))
956 v+=ScaleCharToQuantum(1);
961 for (x=0; x < (ssize_t) columns; x++)
963 v=(MagickRealType) q[i];
964 if (((MagickRealType) s[i] <= (v-ScaleCharToQuantum(2))) &&
965 ((MagickRealType) r[i] < v))
966 v-=ScaleCharToQuantum(1);
973 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
975 #define DespeckleImageTag "Despeckle/Image"
992 *magick_restrict buffer,
993 *magick_restrict pixels;
1001 static const ssize_t
1002 X[4] = {0, 1, 1,-1},
1003 Y[4] = {1, 0, 1, 1};
1006 Allocate despeckled image.
1008 assert(image != (const Image *) NULL);
1009 assert(image->signature == MagickCoreSignature);
1010 if (image->debug != MagickFalse)
1011 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1012 assert(exception != (ExceptionInfo *) NULL);
1013 assert(exception->signature == MagickCoreSignature);
1014 #if defined(MAGICKCORE_OPENCL_SUPPORT)
1015 despeckle_image=AccelerateDespeckleImage(image,exception);
1016 if (despeckle_image != (Image *) NULL)
1017 return(despeckle_image);
1019 despeckle_image=CloneImage(image,0,0,MagickTrue,exception);
1020 if (despeckle_image == (Image *) NULL)
1021 return((Image *) NULL);
1022 status=SetImageStorageClass(despeckle_image,DirectClass,exception);
1023 if (status == MagickFalse)
1025 despeckle_image=DestroyImage(despeckle_image);
1026 return((Image *) NULL);
1029 Allocate image buffer.
1031 length=(size_t) ((image->columns+2)*(image->rows+2));
1032 pixel_info=AcquireVirtualMemory(length,sizeof(*pixels));
1033 buffer_info=AcquireVirtualMemory(length,sizeof(*buffer));
1034 if ((pixel_info == (MemoryInfo *) NULL) ||
1035 (buffer_info == (MemoryInfo *) NULL))
1037 if (buffer_info != (MemoryInfo *) NULL)
1038 buffer_info=RelinquishVirtualMemory(buffer_info);
1039 if (pixel_info != (MemoryInfo *) NULL)
1040 pixel_info=RelinquishVirtualMemory(pixel_info);
1041 despeckle_image=DestroyImage(despeckle_image);
1042 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1044 pixels=(Quantum *) GetVirtualMemoryBlob(pixel_info);
1045 buffer=(Quantum *) GetVirtualMemoryBlob(buffer_info);
1047 Reduce speckle in the image.
1050 image_view=AcquireVirtualCacheView(image,exception);
1051 despeckle_view=AcquireAuthenticCacheView(despeckle_image,exception);
1052 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1069 if (status == MagickFalse)
1071 channel=GetPixelChannelChannel(image,i);
1072 traits=GetPixelChannelTraits(image,channel);
1073 despeckle_traits=GetPixelChannelTraits(despeckle_image,channel);
1074 if ((traits == UndefinedPixelTrait) ||
1075 (despeckle_traits == UndefinedPixelTrait))
1077 if ((despeckle_traits & CopyPixelTrait) != 0)
1079 (void) ResetMagickMemory(pixels,0,length*sizeof(*pixels));
1080 j=(ssize_t) image->columns+2;
1081 for (y=0; y < (ssize_t) image->rows; y++)
1083 register const Quantum
1086 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1087 if (p == (const Quantum *) NULL)
1093 for (x=0; x < (ssize_t) image->columns; x++)
1096 p+=GetPixelChannels(image);
1100 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1101 for (k=0; k < 4; k++)
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);
1105 Hull(image,-X[k],-Y[k],image->columns,image->rows,-1,pixels,buffer);
1106 Hull(image,X[k],Y[k],image->columns,image->rows,-1,pixels,buffer);
1108 j=(ssize_t) image->columns+2;
1109 for (y=0; y < (ssize_t) image->rows; y++)
1117 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1119 if (q == (Quantum *) NULL)
1125 for (x=0; x < (ssize_t) image->columns; x++)
1127 SetPixelChannel(despeckle_image,channel,pixels[j++],q);
1128 q+=GetPixelChannels(despeckle_image);
1130 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1131 if (sync == MagickFalse)
1135 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1140 proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1141 GetPixelChannels(image));
1142 if (proceed == MagickFalse)
1146 despeckle_view=DestroyCacheView(despeckle_view);
1147 image_view=DestroyCacheView(image_view);
1148 buffer_info=RelinquishVirtualMemory(buffer_info);
1149 pixel_info=RelinquishVirtualMemory(pixel_info);
1150 despeckle_image->type=image->type;
1151 if (status == MagickFalse)
1152 despeckle_image=DestroyImage(despeckle_image);
1153 return(despeckle_image);
1157 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1161 % E d g e I m a g e %
1165 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1167 % EdgeImage() finds edges in an image. Radius defines the radius of the
1168 % convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1171 % The format of the EdgeImage method is:
1173 % Image *EdgeImage(const Image *image,const double radius,
1174 % ExceptionInfo *exception)
1176 % A description of each parameter follows:
1178 % o image: the image.
1180 % o radius: the radius of the pixel neighborhood.
1182 % o exception: return any errors or warnings in this structure.
1185 MagickExport Image *EdgeImage(const Image *image,const double radius,
1186 ExceptionInfo *exception)
1200 assert(image != (const Image *) NULL);
1201 assert(image->signature == MagickCoreSignature);
1202 if (image->debug != MagickFalse)
1203 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1204 assert(exception != (ExceptionInfo *) NULL);
1205 assert(exception->signature == MagickCoreSignature);
1206 width=GetOptimalKernelWidth1D(radius,0.5);
1207 kernel_info=AcquireKernelInfo((const char *) NULL,exception);
1208 if (kernel_info == (KernelInfo *) NULL)
1209 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1210 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
1211 kernel_info->width=width;
1212 kernel_info->height=width;
1213 kernel_info->x=(ssize_t) (kernel_info->width-1)/2;
1214 kernel_info->y=(ssize_t) (kernel_info->height-1)/2;
1215 kernel_info->signature=MagickCoreSignature;
1216 kernel_info->values=(MagickRealType *) MagickAssumeAligned(
1217 AcquireAlignedMemory(kernel_info->width,kernel_info->height*
1218 sizeof(*kernel_info->values)));
1219 if (kernel_info->values == (MagickRealType *) NULL)
1221 kernel_info=DestroyKernelInfo(kernel_info);
1222 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1224 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
1225 kernel_info->values[i]=(-1.0);
1226 kernel_info->values[i/2]=(double) kernel_info->width*kernel_info->height-1.0;
1227 edge_image=ConvolveImage(image,kernel_info,exception);
1228 kernel_info=DestroyKernelInfo(kernel_info);
1233 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1237 % E m b o s s I m a g e %
1241 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1243 % EmbossImage() returns a grayscale image with a three-dimensional effect.
1244 % We convolve the image with a Gaussian operator of the given radius and
1245 % standard deviation (sigma). For reasonable results, radius should be
1246 % larger than sigma. Use a radius of 0 and Emboss() selects a suitable
1249 % The format of the EmbossImage method is:
1251 % Image *EmbossImage(const Image *image,const double radius,
1252 % const double sigma,ExceptionInfo *exception)
1254 % A description of each parameter follows:
1256 % o image: the image.
1258 % o radius: the radius of the pixel neighborhood.
1260 % o sigma: the standard deviation of the Gaussian, in pixels.
1262 % o exception: return any errors or warnings in this structure.
1265 MagickExport Image *EmbossImage(const Image *image,const double radius,
1266 const double sigma,ExceptionInfo *exception)
1290 assert(image != (const Image *) NULL);
1291 assert(image->signature == MagickCoreSignature);
1292 if (image->debug != MagickFalse)
1293 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1294 assert(exception != (ExceptionInfo *) NULL);
1295 assert(exception->signature == MagickCoreSignature);
1296 width=GetOptimalKernelWidth1D(radius,sigma);
1297 kernel_info=AcquireKernelInfo((const char *) NULL,exception);
1298 if (kernel_info == (KernelInfo *) NULL)
1299 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1300 kernel_info->width=width;
1301 kernel_info->height=width;
1302 kernel_info->x=(ssize_t) (width-1)/2;
1303 kernel_info->y=(ssize_t) (width-1)/2;
1304 kernel_info->values=(MagickRealType *) MagickAssumeAligned(
1305 AcquireAlignedMemory(kernel_info->width,kernel_info->width*
1306 sizeof(*kernel_info->values)));
1307 if (kernel_info->values == (MagickRealType *) NULL)
1309 kernel_info=DestroyKernelInfo(kernel_info);
1310 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1312 j=(ssize_t) (kernel_info->width-1)/2;
1315 for (v=(-j); v <= j; v++)
1317 for (u=(-j); u <= j; u++)
1319 kernel_info->values[i]=(MagickRealType) (((u < 0) || (v < 0) ? -8.0 :
1320 8.0)*exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
1321 (2.0*MagickPI*MagickSigma*MagickSigma));
1323 kernel_info->values[i]=0.0;
1329 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
1330 normalize+=kernel_info->values[i];
1331 gamma=PerceptibleReciprocal(normalize);
1332 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
1333 kernel_info->values[i]*=gamma;
1334 emboss_image=ConvolveImage(image,kernel_info,exception);
1335 kernel_info=DestroyKernelInfo(kernel_info);
1336 if (emboss_image != (Image *) NULL)
1337 (void) EqualizeImage(emboss_image,exception);
1338 return(emboss_image);
1342 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1346 % G a u s s i a n B l u r I m a g e %
1350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1352 % GaussianBlurImage() blurs an image. We convolve the image with a
1353 % Gaussian operator of the given radius and standard deviation (sigma).
1354 % For reasonable results, the radius should be larger than sigma. Use a
1355 % radius of 0 and GaussianBlurImage() selects a suitable radius for you
1357 % The format of the GaussianBlurImage method is:
1359 % Image *GaussianBlurImage(const Image *image,onst double radius,
1360 % const double sigma,ExceptionInfo *exception)
1362 % A description of each parameter follows:
1364 % o image: the image.
1366 % o radius: the radius of the Gaussian, in pixels, not counting the center
1369 % o sigma: the standard deviation of the Gaussian, in pixels.
1371 % o exception: return any errors or warnings in this structure.
1374 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
1375 const double sigma,ExceptionInfo *exception)
1378 geometry[MagickPathExtent];
1386 assert(image != (const Image *) NULL);
1387 assert(image->signature == MagickCoreSignature);
1388 if (image->debug != MagickFalse)
1389 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1390 assert(exception != (ExceptionInfo *) NULL);
1391 assert(exception->signature == MagickCoreSignature);
1392 (void) FormatLocaleString(geometry,MagickPathExtent,"gaussian:%.20gx%.20g",
1394 kernel_info=AcquireKernelInfo(geometry,exception);
1395 if (kernel_info == (KernelInfo *) NULL)
1396 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1397 blur_image=ConvolveImage(image,kernel_info,exception);
1398 kernel_info=DestroyKernelInfo(kernel_info);
1403 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1407 % K u w a h a r a I m a g e %
1411 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1413 % KuwaharaImage() is an edge preserving noise reduction filter.
1415 % The format of the KuwaharaImage method is:
1417 % Image *KuwaharaImage(const Image *image,const double radius,
1418 % const double sigma,ExceptionInfo *exception)
1420 % A description of each parameter follows:
1422 % o image: the image.
1424 % o radius: the square window radius.
1426 % o sigma: the standard deviation of the Gaussian, in pixels.
1428 % o exception: return any errors or warnings in this structure.
1432 static inline MagickRealType GetMeanLuma(const Image *magick_restrict image,
1433 const double *magick_restrict pixel)
1435 return(0.212656f*pixel[image->channel_map[RedPixelChannel].offset]+
1436 0.715158f*pixel[image->channel_map[GreenPixelChannel].offset]+
1437 0.072186f*pixel[image->channel_map[BluePixelChannel].offset]); /* Rec709 */
1440 MagickExport Image *KuwaharaImage(const Image *image,const double radius,
1441 const double sigma,ExceptionInfo *exception)
1443 #define KuwaharaImageTag "Kuwahara/Image"
1466 Initialize Kuwahara image attributes.
1468 assert(image != (Image *) NULL);
1469 assert(image->signature == MagickCoreSignature);
1470 if (image->debug != MagickFalse)
1471 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1472 assert(exception != (ExceptionInfo *) NULL);
1473 assert(exception->signature == MagickCoreSignature);
1474 width=(size_t) radius+1;
1475 gaussian_image=BlurImage(image,radius,sigma,exception);
1476 if (gaussian_image == (Image *) NULL)
1477 return((Image *) NULL);
1478 kuwahara_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1480 if (kuwahara_image == (Image *) NULL)
1482 gaussian_image=DestroyImage(gaussian_image);
1483 return((Image *) NULL);
1485 if (SetImageStorageClass(kuwahara_image,DirectClass,exception) == MagickFalse)
1487 gaussian_image=DestroyImage(gaussian_image);
1488 kuwahara_image=DestroyImage(kuwahara_image);
1489 return((Image *) NULL);
1492 Edge preserving noise reduction filter.
1496 image_view=AcquireVirtualCacheView(gaussian_image,exception);
1497 kuwahara_view=AcquireAuthenticCacheView(kuwahara_image,exception);
1498 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1499 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1500 magick_threads(image,kuwahara_image,image->rows,1)
1502 for (y=0; y < (ssize_t) gaussian_image->rows; y++)
1510 if (status == MagickFalse)
1512 q=QueueCacheViewAuthenticPixels(kuwahara_view,0,y,kuwahara_image->columns,1,
1514 if (q == (Quantum *) NULL)
1519 for (x=0; x < (ssize_t) gaussian_image->columns; x++)
1534 min_variance=MagickMaximumValue;
1535 SetGeometry(gaussian_image,&target);
1536 quadrant.width=width;
1537 quadrant.height=width;
1538 for (i=0; i < 4; i++)
1544 mean[MaxPixelChannels],
1559 quadrant.x=x-(ssize_t) (width-1);
1560 quadrant.y=y-(ssize_t) (width-1);
1565 quadrant.y=y-(ssize_t) (width-1);
1570 quadrant.x=x-(ssize_t) (width-1);
1577 p=GetCacheViewVirtualPixels(image_view,quadrant.x,quadrant.y,
1578 quadrant.width,quadrant.height,exception);
1579 if (p == (const Quantum *) NULL)
1581 for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
1584 for (n=0; n < (ssize_t) (width*width); n++)
1586 for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
1587 mean[j]+=(double) k[j];
1588 k+=GetPixelChannels(gaussian_image);
1590 for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
1591 mean[j]/=(double) (width*width);
1594 for (n=0; n < (ssize_t) (width*width); n++)
1599 luma=GetPixelLuma(gaussian_image,k);
1600 variance+=(luma-GetMeanLuma(gaussian_image,mean))*
1601 (luma-GetMeanLuma(gaussian_image,mean));
1602 k+=GetPixelChannels(gaussian_image);
1604 if (variance < min_variance)
1606 min_variance=variance;
1615 status=InterpolatePixelChannels(gaussian_image,image_view,kuwahara_image,
1616 UndefinedInterpolatePixel,(double) target.x+target.width/2.0,(double)
1617 target.y+target.height/2.0,q,exception);
1618 q+=GetPixelChannels(kuwahara_image);
1620 if (SyncCacheViewAuthenticPixels(kuwahara_view,exception) == MagickFalse)
1622 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1627 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1628 #pragma omp critical (MagickCore_KuwaharaImage)
1630 proceed=SetImageProgress(image,KuwaharaImageTag,progress++,image->rows);
1631 if (proceed == MagickFalse)
1635 kuwahara_view=DestroyCacheView(kuwahara_view);
1636 image_view=DestroyCacheView(image_view);
1637 gaussian_image=DestroyImage(gaussian_image);
1638 if (status == MagickFalse)
1639 kuwahara_image=DestroyImage(kuwahara_image);
1640 return(kuwahara_image);
1644 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1648 % L o c a l C o n t r a s t I m a g e %
1652 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1654 % LocalContrastImage() attempts to increase the appearance of large-scale
1655 % light-dark transitions. Local contrast enhancement works similarly to
1656 % sharpening with an unsharp mask, however the mask is instead created using
1657 % an image with a greater blur distance.
1659 % The format of the LocalContrastImage method is:
1661 % Image *LocalContrastImage(const Image *image, const double radius,
1662 % const double strength,ExceptionInfo *exception)
1664 % A description of each parameter follows:
1666 % o image: the image.
1668 % o radius: the radius of the Gaussian blur, in percentage with 100%
1669 % resulting in a blur radius of 20% of largest dimension.
1671 % o strength: the strength of the blur mask in percentage.
1673 % o exception: return any errors or warnings in this structure.
1676 MagickExport Image *LocalContrastImage(const Image *image,const double radius,
1677 const double strength,ExceptionInfo *exception)
1679 #define LocalContrastImageTag "LocalContrast/Image"
1697 *scanLinePixels_info,
1705 Initialize contrast image attributes.
1707 assert(image != (const Image *) NULL);
1708 assert(image->signature == MagickCoreSignature);
1709 if (image->debug != MagickFalse)
1710 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1711 assert(exception != (ExceptionInfo *) NULL);
1712 assert(exception->signature == MagickCoreSignature);
1713 #if defined(MAGICKCORE_OPENCL_SUPPORT)
1714 contrast_image=AccelerateLocalContrastImage(image,radius,strength,exception);
1715 if (contrast_image != (Image *) NULL)
1716 return(contrast_image);
1718 contrast_image=CloneImage(image,0,0,MagickTrue,exception);
1719 if (contrast_image == (Image *) NULL)
1720 return((Image *) NULL);
1721 if (SetImageStorageClass(contrast_image,DirectClass,exception) == MagickFalse)
1723 contrast_image=DestroyImage(contrast_image);
1724 return((Image *) NULL);
1726 image_view=AcquireVirtualCacheView(image,exception);
1727 contrast_view=AcquireAuthenticCacheView(contrast_image,exception);
1728 scanLineSize=(ssize_t) MagickMax(image->columns,image->rows);
1729 width=(ssize_t) scanLineSize*0.002f*fabs(radius);
1730 scanLineSize+=(2*width);
1731 scanLinePixels_info=AcquireVirtualMemory((size_t) GetOpenMPMaximumThreads()*
1732 scanLineSize,sizeof(*scanLinePixels));
1733 if (scanLinePixels_info == (MemoryInfo *) NULL)
1735 contrast_view=DestroyCacheView(contrast_view);
1736 image_view=DestroyCacheView(image_view);
1737 contrast_image=DestroyImage(contrast_image);
1738 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1740 scanLinePixels=(float *) GetVirtualMemoryBlob(scanLinePixels_info);
1742 Create intermediate buffer.
1744 interImage_info=AcquireVirtualMemory(image->rows*(image->columns+(2*width)),
1745 sizeof(*interImage));
1746 if (interImage_info == (MemoryInfo *) NULL)
1748 scanLinePixels_info=RelinquishVirtualMemory(scanLinePixels_info);
1749 contrast_view=DestroyCacheView(contrast_view);
1750 image_view=DestroyCacheView(image_view);
1751 contrast_image=DestroyImage(contrast_image);
1752 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1754 interImage=(float *) GetVirtualMemoryBlob(interImage_info);
1755 totalWeight=(float) ((width+1)*(width+1));
1764 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1765 #pragma omp parallel for schedule(static,4) \
1766 magick_threads(image,image,image->columns,1)
1768 for (x=0; x < (ssize_t) image->columns; x++)
1771 id = GetOpenMPThreadId();
1787 if (status == MagickFalse)
1789 pixels=scanLinePixels;
1790 pixels+=id*scanLineSize;
1792 p=GetCacheViewVirtualPixels(image_view,x,-width,1,image->rows+(2*width),
1794 if (p == (const Quantum *) NULL)
1799 for (y=0; y < (ssize_t) image->rows+(2*width); y++)
1801 *pix++=(float)GetPixelLuma(image,p);
1802 p+=image->number_channels;
1804 out=interImage+x+width;
1805 for (y=0; y < (ssize_t) image->rows; y++)
1814 for (i=0; i < width; i++)
1816 sum+=weight*(*pix++);
1819 for (i=width+1; i < (2*width); i++)
1821 sum+=weight*(*pix++);
1824 /* write to output */
1825 *out=sum/totalWeight;
1826 /* mirror into padding */
1827 if (x <= width && x != 0)
1829 if ((x > (ssize_t) image->columns-width-2) &&
1830 (x != (ssize_t) image->columns-1))
1831 *(out+((image->columns-x-1)*2))=*out;
1832 out+=image->columns+(width*2);
1843 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1844 #pragma omp parallel for schedule(static,4) \
1845 magick_threads(image,image,image->rows,1)
1847 for (y=0; y < (ssize_t) image->rows; y++)
1850 id = GetOpenMPThreadId();
1868 if (status == MagickFalse)
1870 pixels=scanLinePixels;
1871 pixels+=id*scanLineSize;
1872 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1873 q=GetCacheViewAuthenticPixels(contrast_view,0,y,image->columns,1,
1875 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1880 memcpy(pixels,interImage+(y*(image->columns+(2*width))),(image->columns+
1881 (2*width))*sizeof(float));
1882 for (x=0; x < (ssize_t) image->columns; x++)
1893 for (i=0; i < width; i++)
1895 sum+=weight*(*pix++);
1898 for (i=width+1; i < (2*width); i++)
1900 sum+=weight*(*pix++);
1903 /* Apply and write */
1904 srcVal=(float) GetPixelLuma(image,p);
1905 mult=(srcVal-(sum/totalWeight))*(strength/100.0f);
1906 mult=(srcVal+mult)/srcVal;
1907 SetPixelRed(contrast_image,ClampToQuantum(GetPixelRed(image,p)*mult),
1909 SetPixelGreen(contrast_image,ClampToQuantum(GetPixelGreen(image,p)*
1911 SetPixelBlue(contrast_image,ClampToQuantum(GetPixelBlue(image,p)*mult),
1913 p+=image->number_channels;
1914 q+=contrast_image->number_channels;
1916 if (SyncCacheViewAuthenticPixels(contrast_view,exception) == MagickFalse)
1920 scanLinePixels_info=RelinquishVirtualMemory(scanLinePixels_info);
1921 interImage_info=RelinquishVirtualMemory(interImage_info);
1922 contrast_view=DestroyCacheView(contrast_view);
1923 image_view=DestroyCacheView(image_view);
1924 if (status == MagickFalse)
1925 contrast_image=DestroyImage(contrast_image);
1926 return(contrast_image);
1930 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1934 % M o t i o n B l u r I m a g e %
1938 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1940 % MotionBlurImage() simulates motion blur. We convolve the image with a
1941 % Gaussian operator of the given radius and standard deviation (sigma).
1942 % For reasonable results, radius should be larger than sigma. Use a
1943 % radius of 0 and MotionBlurImage() selects a suitable radius for you.
1944 % Angle gives the angle of the blurring motion.
1946 % Andrew Protano contributed this effect.
1948 % The format of the MotionBlurImage method is:
1950 % Image *MotionBlurImage(const Image *image,const double radius,
1951 % const double sigma,const double angle,ExceptionInfo *exception)
1953 % A description of each parameter follows:
1955 % o image: the image.
1957 % o radius: the radius of the Gaussian, in pixels, not counting
1960 % o sigma: the standard deviation of the Gaussian, in pixels.
1962 % o angle: Apply the effect along this angle.
1964 % o exception: return any errors or warnings in this structure.
1968 static MagickRealType *GetMotionBlurKernel(const size_t width,
1979 Generate a 1-D convolution kernel.
1981 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1982 kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
1983 width,sizeof(*kernel)));
1984 if (kernel == (MagickRealType *) NULL)
1987 for (i=0; i < (ssize_t) width; i++)
1989 kernel[i]=(MagickRealType) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
1990 MagickSigma)))/(MagickSQ2PI*MagickSigma));
1991 normalize+=kernel[i];
1993 for (i=0; i < (ssize_t) width; i++)
1994 kernel[i]/=normalize;
1998 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
1999 const double sigma,const double angle,ExceptionInfo *exception)
2001 #define BlurImageTag "Blur/Image"
2035 assert(image != (Image *) NULL);
2036 assert(image->signature == MagickCoreSignature);
2037 if (image->debug != MagickFalse)
2038 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2039 assert(exception != (ExceptionInfo *) NULL);
2040 width=GetOptimalKernelWidth1D(radius,sigma);
2041 kernel=GetMotionBlurKernel(width,sigma);
2042 if (kernel == (MagickRealType *) NULL)
2043 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2044 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2045 if (offset == (OffsetInfo *) NULL)
2047 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2048 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2050 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2051 if (blur_image == (Image *) NULL)
2053 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2054 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2055 return((Image *) NULL);
2057 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2059 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2060 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2061 blur_image=DestroyImage(blur_image);
2062 return((Image *) NULL);
2064 point.x=(double) width*sin(DegreesToRadians(angle));
2065 point.y=(double) width*cos(DegreesToRadians(angle));
2066 for (i=0; i < (ssize_t) width; i++)
2068 offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
2069 offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
2076 image_view=AcquireVirtualCacheView(image,exception);
2077 motion_view=AcquireVirtualCacheView(image,exception);
2078 blur_view=AcquireAuthenticCacheView(blur_image,exception);
2079 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2080 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2081 magick_threads(image,blur_image,image->rows,1)
2083 for (y=0; y < (ssize_t) image->rows; y++)
2085 register const Quantum
2094 if (status == MagickFalse)
2096 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2097 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2099 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2104 for (x=0; x < (ssize_t) image->columns; x++)
2109 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2123 register const Quantum
2126 register MagickRealType
2132 channel=GetPixelChannelChannel(image,i);
2133 traits=GetPixelChannelTraits(image,channel);
2134 blur_traits=GetPixelChannelTraits(blur_image,channel);
2135 if ((traits == UndefinedPixelTrait) ||
2136 (blur_traits == UndefinedPixelTrait))
2138 if (((blur_traits & CopyPixelTrait) != 0) ||
2139 (GetPixelWriteMask(image,p) == 0))
2141 SetPixelChannel(blur_image,channel,p[i],q);
2146 if ((blur_traits & BlendPixelTrait) == 0)
2148 for (j=0; j < (ssize_t) width; j++)
2150 r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+
2151 offset[j].y,1,1,exception);
2152 if (r == (const Quantum *) NULL)
2160 SetPixelChannel(blur_image,channel,ClampToQuantum(pixel),q);
2165 for (j=0; j < (ssize_t) width; j++)
2167 r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+offset[j].y,1,
2169 if (r == (const Quantum *) NULL)
2174 alpha=(double) (QuantumScale*GetPixelAlpha(image,r));
2175 pixel+=(*k)*alpha*r[i];
2179 gamma=PerceptibleReciprocal(gamma);
2180 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2182 p+=GetPixelChannels(image);
2183 q+=GetPixelChannels(blur_image);
2185 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2187 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2192 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2193 #pragma omp critical (MagickCore_MotionBlurImage)
2195 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2196 if (proceed == MagickFalse)
2200 blur_view=DestroyCacheView(blur_view);
2201 motion_view=DestroyCacheView(motion_view);
2202 image_view=DestroyCacheView(image_view);
2203 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2204 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2205 if (status == MagickFalse)
2206 blur_image=DestroyImage(blur_image);
2211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2215 % P r e v i e w I m a g e %
2219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2221 % PreviewImage() tiles 9 thumbnails of the specified image with an image
2222 % processing operation applied with varying parameters. This may be helpful
2223 % pin-pointing an appropriate parameter for a particular image processing
2226 % The format of the PreviewImages method is:
2228 % Image *PreviewImages(const Image *image,const PreviewType preview,
2229 % ExceptionInfo *exception)
2231 % A description of each parameter follows:
2233 % o image: the image.
2235 % o preview: the image processing operation.
2237 % o exception: return any errors or warnings in this structure.
2240 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
2241 ExceptionInfo *exception)
2243 #define NumberTiles 9
2244 #define PreviewImageTag "Preview/Image"
2245 #define DefaultPreviewGeometry "204x204+10+10"
2248 factor[MagickPathExtent],
2249 label[MagickPathExtent];
2294 Open output image file.
2296 assert(image != (Image *) NULL);
2297 assert(image->signature == MagickCoreSignature);
2298 if (image->debug != MagickFalse)
2299 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2303 preview_info=AcquireImageInfo();
2304 SetGeometry(image,&geometry);
2305 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2306 &geometry.width,&geometry.height);
2307 images=NewImageList();
2309 GetQuantizeInfo(&quantize_info);
2315 for (i=0; i < NumberTiles; i++)
2317 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2318 if (thumbnail == (Image *) NULL)
2320 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2322 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel,exception);
2323 if (i == (NumberTiles/2))
2325 (void) QueryColorCompliance("#dfdfdf",AllCompliance,
2326 &thumbnail->matte_color,exception);
2327 AppendImageToList(&images,thumbnail);
2335 preview_image=RotateImage(thumbnail,degrees,exception);
2336 (void) FormatLocaleString(label,MagickPathExtent,"rotate %g",degrees);
2342 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
2343 (void) FormatLocaleString(label,MagickPathExtent,"shear %gx%g",degrees,
2349 x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
2350 y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
2351 preview_image=RollImage(thumbnail,x,y,exception);
2352 (void) FormatLocaleString(label,MagickPathExtent,"roll %+.20gx%+.20g",
2353 (double) x,(double) y);
2358 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2359 if (preview_image == (Image *) NULL)
2361 (void) FormatLocaleString(factor,MagickPathExtent,"100,100,%g",2.0*
2363 (void) ModulateImage(preview_image,factor,exception);
2364 (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
2367 case SaturationPreview:
2369 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2370 if (preview_image == (Image *) NULL)
2372 (void) FormatLocaleString(factor,MagickPathExtent,"100,%g",2.0*percentage);
2373 (void) ModulateImage(preview_image,factor,exception);
2374 (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
2377 case BrightnessPreview:
2379 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2380 if (preview_image == (Image *) NULL)
2382 (void) FormatLocaleString(factor,MagickPathExtent,"%g",2.0*percentage);
2383 (void) ModulateImage(preview_image,factor,exception);
2384 (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
2390 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2391 if (preview_image == (Image *) NULL)
2394 (void) GammaImage(preview_image,gamma,exception);
2395 (void) FormatLocaleString(label,MagickPathExtent,"gamma %g",gamma);
2400 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2401 if (preview_image != (Image *) NULL)
2402 for (x=0; x < i; x++)
2403 (void) ContrastImage(preview_image,MagickTrue,exception);
2404 (void) FormatLocaleString(label,MagickPathExtent,"contrast (%.20g)",
2410 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2411 if (preview_image == (Image *) NULL)
2413 for (x=0; x < i; x++)
2414 (void) ContrastImage(preview_image,MagickFalse,exception);
2415 (void) FormatLocaleString(label,MagickPathExtent,"+contrast (%.20g)",
2419 case GrayscalePreview:
2421 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2422 if (preview_image == (Image *) NULL)
2425 quantize_info.number_colors=colors;
2426 quantize_info.colorspace=GRAYColorspace;
2427 (void) QuantizeImage(&quantize_info,preview_image,exception);
2428 (void) FormatLocaleString(label,MagickPathExtent,
2429 "-colorspace gray -colors %.20g",(double) colors);
2432 case QuantizePreview:
2434 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2435 if (preview_image == (Image *) NULL)
2438 quantize_info.number_colors=colors;
2439 (void) QuantizeImage(&quantize_info,preview_image,exception);
2440 (void) FormatLocaleString(label,MagickPathExtent,"colors %.20g",(double)
2444 case DespecklePreview:
2446 for (x=0; x < (i-1); x++)
2448 preview_image=DespeckleImage(thumbnail,exception);
2449 if (preview_image == (Image *) NULL)
2451 thumbnail=DestroyImage(thumbnail);
2452 thumbnail=preview_image;
2454 preview_image=DespeckleImage(thumbnail,exception);
2455 if (preview_image == (Image *) NULL)
2457 (void) FormatLocaleString(label,MagickPathExtent,"despeckle (%.20g)",
2461 case ReduceNoisePreview:
2463 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
2464 (size_t) radius,exception);
2465 (void) FormatLocaleString(label,MagickPathExtent,"noise %g",radius);
2468 case AddNoisePreview:
2474 (void) CopyMagickString(factor,"uniform",MagickPathExtent);
2479 (void) CopyMagickString(factor,"gaussian",MagickPathExtent);
2484 (void) CopyMagickString(factor,"multiplicative",MagickPathExtent);
2489 (void) CopyMagickString(factor,"impulse",MagickPathExtent);
2494 (void) CopyMagickString(factor,"laplacian",MagickPathExtent);
2499 (void) CopyMagickString(factor,"Poisson",MagickPathExtent);
2504 (void) CopyMagickString(thumbnail->magick,"NULL",MagickPathExtent);
2508 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
2509 (size_t) i,exception);
2510 (void) FormatLocaleString(label,MagickPathExtent,"+noise %s",factor);
2513 case SharpenPreview:
2515 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
2516 (void) FormatLocaleString(label,MagickPathExtent,"sharpen %gx%g",radius,
2522 preview_image=BlurImage(thumbnail,radius,sigma,exception);
2523 (void) FormatLocaleString(label,MagickPathExtent,"blur %gx%g",radius,
2527 case ThresholdPreview:
2529 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2530 if (preview_image == (Image *) NULL)
2532 (void) BilevelImage(thumbnail,(double) (percentage*((double)
2533 QuantumRange+1.0))/100.0,exception);
2534 (void) FormatLocaleString(label,MagickPathExtent,"threshold %g",(double)
2535 (percentage*((double) QuantumRange+1.0))/100.0);
2538 case EdgeDetectPreview:
2540 preview_image=EdgeImage(thumbnail,radius,exception);
2541 (void) FormatLocaleString(label,MagickPathExtent,"edge %g",radius);
2546 preview_image=SpreadImage(thumbnail,image->interpolate,radius,
2548 (void) FormatLocaleString(label,MagickPathExtent,"spread %g",
2552 case SolarizePreview:
2554 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2555 if (preview_image == (Image *) NULL)
2557 (void) SolarizeImage(preview_image,(double) QuantumRange*percentage/
2559 (void) FormatLocaleString(label,MagickPathExtent,"solarize %g",
2560 (QuantumRange*percentage)/100.0);
2566 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2568 (void) FormatLocaleString(label,MagickPathExtent,"shade %gx%g",degrees,
2574 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2575 if (preview_image == (Image *) NULL)
2577 geometry.width=(size_t) (2*i+2);
2578 geometry.height=(size_t) (2*i+2);
2581 (void) RaiseImage(preview_image,&geometry,MagickTrue,exception);
2582 (void) FormatLocaleString(label,MagickPathExtent,
2583 "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
2584 geometry.height,(double) geometry.x,(double) geometry.y);
2587 case SegmentPreview:
2589 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2590 if (preview_image == (Image *) NULL)
2593 (void) SegmentImage(preview_image,sRGBColorspace,MagickFalse,threshold,
2594 threshold,exception);
2595 (void) FormatLocaleString(label,MagickPathExtent,"segment %gx%g",
2596 threshold,threshold);
2601 preview_image=SwirlImage(thumbnail,degrees,image->interpolate,
2603 (void) FormatLocaleString(label,MagickPathExtent,"swirl %g",degrees);
2607 case ImplodePreview:
2610 preview_image=ImplodeImage(thumbnail,degrees,image->interpolate,
2612 (void) FormatLocaleString(label,MagickPathExtent,"implode %g",degrees);
2618 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,
2619 image->interpolate,exception);
2620 (void) FormatLocaleString(label,MagickPathExtent,"wave %gx%g",0.5*degrees,
2624 case OilPaintPreview:
2626 preview_image=OilPaintImage(thumbnail,(double) radius,(double) sigma,
2628 (void) FormatLocaleString(label,MagickPathExtent,"charcoal %gx%g",radius,
2632 case CharcoalDrawingPreview:
2634 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2636 (void) FormatLocaleString(label,MagickPathExtent,"charcoal %gx%g",radius,
2643 filename[MagickPathExtent];
2651 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2652 if (preview_image == (Image *) NULL)
2654 preview_info->quality=(size_t) percentage;
2655 (void) FormatLocaleString(factor,MagickPathExtent,"%.20g",(double)
2656 preview_info->quality);
2657 file=AcquireUniqueFileResource(filename);
2660 (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
2661 "jpeg:%s",filename);
2662 status=WriteImage(preview_info,preview_image,exception);
2663 if (status != MagickFalse)
2668 (void) CopyMagickString(preview_info->filename,
2669 preview_image->filename,MagickPathExtent);
2670 quality_image=ReadImage(preview_info,exception);
2671 if (quality_image != (Image *) NULL)
2673 preview_image=DestroyImage(preview_image);
2674 preview_image=quality_image;
2677 (void) RelinquishUniqueFileResource(preview_image->filename);
2678 if ((GetBlobSize(preview_image)/1024) >= 1024)
2679 (void) FormatLocaleString(label,MagickPathExtent,"quality %s\n%gmb ",
2680 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2683 if (GetBlobSize(preview_image) >= 1024)
2684 (void) FormatLocaleString(label,MagickPathExtent,
2685 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
2686 GetBlobSize(preview_image))/1024.0);
2688 (void) FormatLocaleString(label,MagickPathExtent,"quality %s\n%.20gb ",
2689 factor,(double) ((MagickOffsetType) GetBlobSize(thumbnail)));
2693 thumbnail=DestroyImage(thumbnail);
2697 if (preview_image == (Image *) NULL)
2699 (void) DeleteImageProperty(preview_image,"label");
2700 (void) SetImageProperty(preview_image,"label",label,exception);
2701 AppendImageToList(&images,preview_image);
2702 proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2704 if (proceed == MagickFalse)
2707 if (images == (Image *) NULL)
2709 preview_info=DestroyImageInfo(preview_info);
2710 return((Image *) NULL);
2715 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2716 (void) CopyMagickString(montage_info->filename,image->filename,MagickPathExtent);
2717 montage_info->shadow=MagickTrue;
2718 (void) CloneString(&montage_info->tile,"3x3");
2719 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2720 (void) CloneString(&montage_info->frame,DefaultTileFrame);
2721 montage_image=MontageImages(images,montage_info,exception);
2722 montage_info=DestroyMontageInfo(montage_info);
2723 images=DestroyImageList(images);
2724 if (montage_image == (Image *) NULL)
2725 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2726 if (montage_image->montage != (char *) NULL)
2729 Free image directory.
2731 montage_image->montage=(char *) RelinquishMagickMemory(
2732 montage_image->montage);
2733 if (image->directory != (char *) NULL)
2734 montage_image->directory=(char *) RelinquishMagickMemory(
2735 montage_image->directory);
2737 preview_info=DestroyImageInfo(preview_info);
2738 return(montage_image);
2742 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2746 % R o t a t i o n a l B l u r I m a g e %
2750 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2752 % RotationalBlurImage() applies a radial blur to the image.
2754 % Andrew Protano contributed this effect.
2756 % The format of the RotationalBlurImage method is:
2758 % Image *RotationalBlurImage(const Image *image,const double angle,
2759 % ExceptionInfo *exception)
2761 % A description of each parameter follows:
2763 % o image: the image.
2765 % o angle: the angle of the radial blur.
2769 % o exception: return any errors or warnings in this structure.
2772 MagickExport Image *RotationalBlurImage(const Image *image,const double angle,
2773 ExceptionInfo *exception)
2809 Allocate blur image.
2811 assert(image != (Image *) NULL);
2812 assert(image->signature == MagickCoreSignature);
2813 if (image->debug != MagickFalse)
2814 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2815 assert(exception != (ExceptionInfo *) NULL);
2816 assert(exception->signature == MagickCoreSignature);
2817 #if defined(MAGICKCORE_OPENCL_SUPPORT)
2818 blur_image=AccelerateRotationalBlurImage(image,angle,exception);
2819 if (blur_image != (Image *) NULL)
2822 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2823 if (blur_image == (Image *) NULL)
2824 return((Image *) NULL);
2825 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2827 blur_image=DestroyImage(blur_image);
2828 return((Image *) NULL);
2830 blur_center.x=(double) (image->columns-1)/2.0;
2831 blur_center.y=(double) (image->rows-1)/2.0;
2832 blur_radius=hypot(blur_center.x,blur_center.y);
2833 n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
2834 theta=DegreesToRadians(angle)/(double) (n-1);
2835 cos_theta=(double *) AcquireQuantumMemory((size_t) n,
2836 sizeof(*cos_theta));
2837 sin_theta=(double *) AcquireQuantumMemory((size_t) n,
2838 sizeof(*sin_theta));
2839 if ((cos_theta == (double *) NULL) ||
2840 (sin_theta == (double *) NULL))
2842 blur_image=DestroyImage(blur_image);
2843 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2845 offset=theta*(double) (n-1)/2.0;
2846 for (i=0; i < (ssize_t) n; i++)
2848 cos_theta[i]=cos((double) (theta*i-offset));
2849 sin_theta[i]=sin((double) (theta*i-offset));
2856 image_view=AcquireVirtualCacheView(image,exception);
2857 radial_view=AcquireVirtualCacheView(image,exception);
2858 blur_view=AcquireAuthenticCacheView(blur_image,exception);
2859 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2860 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2861 magick_threads(image,blur_image,image->rows,1)
2863 for (y=0; y < (ssize_t) image->rows; y++)
2865 register const Quantum
2874 if (status == MagickFalse)
2876 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2877 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2879 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2884 for (x=0; x < (ssize_t) image->columns; x++)
2898 center.x=(double) x-blur_center.x;
2899 center.y=(double) y-blur_center.y;
2900 radius=hypot((double) center.x,center.y);
2905 step=(size_t) (blur_radius/radius);
2912 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2925 register const Quantum
2931 channel=GetPixelChannelChannel(image,i);
2932 traits=GetPixelChannelTraits(image,channel);
2933 blur_traits=GetPixelChannelTraits(blur_image,channel);
2934 if ((traits == UndefinedPixelTrait) ||
2935 (blur_traits == UndefinedPixelTrait))
2937 if (((blur_traits & CopyPixelTrait) != 0) ||
2938 (GetPixelWriteMask(image,p) == 0))
2940 SetPixelChannel(blur_image,channel,p[i],q);
2945 if ((GetPixelChannelTraits(image,AlphaPixelChannel) == UndefinedPixelTrait) ||
2946 (channel == AlphaPixelChannel))
2948 for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2950 r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
2951 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2952 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2954 if (r == (const Quantum *) NULL)
2962 gamma=PerceptibleReciprocal(gamma);
2963 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2966 for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2971 r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
2972 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2973 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2975 if (r == (const Quantum *) NULL)
2980 alpha=(double) QuantumScale*GetPixelAlpha(image,r);
2984 gamma=PerceptibleReciprocal(gamma);
2985 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2987 p+=GetPixelChannels(image);
2988 q+=GetPixelChannels(blur_image);
2990 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2992 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2997 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2998 #pragma omp critical (MagickCore_RotationalBlurImage)
3000 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3001 if (proceed == MagickFalse)
3005 blur_view=DestroyCacheView(blur_view);
3006 radial_view=DestroyCacheView(radial_view);
3007 image_view=DestroyCacheView(image_view);
3008 cos_theta=(double *) RelinquishMagickMemory(cos_theta);
3009 sin_theta=(double *) RelinquishMagickMemory(sin_theta);
3010 if (status == MagickFalse)
3011 blur_image=DestroyImage(blur_image);
3016 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3020 % S e l e c t i v e B l u r I m a g e %
3024 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3026 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3027 % It is similar to the unsharpen mask that sharpens everything with contrast
3028 % above a certain threshold.
3030 % The format of the SelectiveBlurImage method is:
3032 % Image *SelectiveBlurImage(const Image *image,const double radius,
3033 % const double sigma,const double threshold,ExceptionInfo *exception)
3035 % A description of each parameter follows:
3037 % o image: the image.
3039 % o radius: the radius of the Gaussian, in pixels, not counting the center
3042 % o sigma: the standard deviation of the Gaussian, in pixels.
3044 % o threshold: only pixels within this contrast threshold are included
3045 % in the blur operation.
3047 % o exception: return any errors or warnings in this structure.
3050 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
3051 const double sigma,const double threshold,ExceptionInfo *exception)
3053 #define SelectiveBlurImageTag "SelectiveBlur/Image"
3087 Initialize blur image attributes.
3089 assert(image != (Image *) NULL);
3090 assert(image->signature == MagickCoreSignature);
3091 if (image->debug != MagickFalse)
3092 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3093 assert(exception != (ExceptionInfo *) NULL);
3094 assert(exception->signature == MagickCoreSignature);
3095 width=GetOptimalKernelWidth1D(radius,sigma);
3096 kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
3097 width,width*sizeof(*kernel)));
3098 if (kernel == (MagickRealType *) NULL)
3099 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3100 j=(ssize_t) (width-1)/2;
3102 for (v=(-j); v <= j; v++)
3104 for (u=(-j); u <= j; u++)
3105 kernel[i++]=(MagickRealType) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
3106 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3108 if (image->debug != MagickFalse)
3111 format[MagickPathExtent],
3114 register const MagickRealType
3121 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
3122 " SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
3124 message=AcquireString("");
3126 for (v=0; v < (ssize_t) width; v++)
3129 (void) FormatLocaleString(format,MagickPathExtent,"%.20g: ",(double) v);
3130 (void) ConcatenateString(&message,format);
3131 for (u=0; u < (ssize_t) width; u++)
3133 (void) FormatLocaleString(format,MagickPathExtent,"%+f ",(double) *k++);
3134 (void) ConcatenateString(&message,format);
3136 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3138 message=DestroyString(message);
3140 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3141 if (blur_image == (Image *) NULL)
3142 return((Image *) NULL);
3143 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
3145 blur_image=DestroyImage(blur_image);
3146 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3147 return((Image *) NULL);
3149 luminance_image=CloneImage(image,0,0,MagickTrue,exception);
3150 if (luminance_image == (Image *) NULL)
3152 blur_image=DestroyImage(blur_image);
3153 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3154 return((Image *) NULL);
3156 status=TransformImageColorspace(luminance_image,GRAYColorspace,exception);
3157 if (status == MagickFalse)
3159 luminance_image=DestroyImage(luminance_image);
3160 blur_image=DestroyImage(blur_image);
3161 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3162 return((Image *) NULL);
3165 Threshold blur image.
3169 center=(ssize_t) (GetPixelChannels(image)*(image->columns+width)*
3170 ((width-1)/2L)+GetPixelChannels(image)*((width-1)/2L));
3171 image_view=AcquireVirtualCacheView(image,exception);
3172 luminance_view=AcquireVirtualCacheView(luminance_image,exception);
3173 blur_view=AcquireAuthenticCacheView(blur_image,exception);
3174 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3175 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3176 magick_threads(image,blur_image,image->rows,1)
3178 for (y=0; y < (ssize_t) image->rows; y++)
3186 register const Quantum
3196 if (status == MagickFalse)
3198 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) (width-1)/2L),y-(ssize_t)
3199 ((width-1)/2L),image->columns+width,width,exception);
3200 l=GetCacheViewVirtualPixels(luminance_view,-((ssize_t) (width-1)/2L),y-
3201 (ssize_t) ((width-1)/2L),luminance_image->columns+width,width,exception);
3202 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3204 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3209 for (x=0; x < (ssize_t) image->columns; x++)
3217 intensity=GetPixelIntensity(image,p+center);
3218 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3232 register const MagickRealType
3235 register const Quantum
3236 *magick_restrict luminance_pixels,
3237 *magick_restrict pixels;
3245 channel=GetPixelChannelChannel(image,i);
3246 traits=GetPixelChannelTraits(image,channel);
3247 blur_traits=GetPixelChannelTraits(blur_image,channel);
3248 if ((traits == UndefinedPixelTrait) ||
3249 (blur_traits == UndefinedPixelTrait))
3251 if (((blur_traits & CopyPixelTrait) != 0) ||
3252 (GetPixelWriteMask(image,p+center) == 0))
3254 SetPixelChannel(blur_image,channel,p[center+i],q);
3262 if ((blur_traits & BlendPixelTrait) == 0)
3264 for (v=0; v < (ssize_t) width; v++)
3266 for (u=0; u < (ssize_t) width; u++)
3268 contrast=GetPixelIntensity(luminance_image,luminance_pixels)-
3270 if (fabs(contrast) < threshold)
3272 pixel+=(*k)*pixels[i];
3276 pixels+=GetPixelChannels(image);
3277 luminance_pixels+=GetPixelChannels(luminance_image);
3279 pixels+=GetPixelChannels(image)*image->columns;
3280 luminance_pixels+=GetPixelChannels(luminance_image)*
3281 luminance_image->columns;
3283 if (fabs((double) gamma) < MagickEpsilon)
3285 SetPixelChannel(blur_image,channel,p[center+i],q);
3288 gamma=PerceptibleReciprocal(gamma);
3289 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3292 for (v=0; v < (ssize_t) width; v++)
3294 for (u=0; u < (ssize_t) width; u++)
3296 contrast=GetPixelIntensity(image,pixels)-intensity;
3297 if (fabs(contrast) < threshold)
3299 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
3300 pixel+=(*k)*alpha*pixels[i];
3304 pixels+=GetPixelChannels(image);
3305 luminance_pixels+=GetPixelChannels(luminance_image);
3307 pixels+=GetPixelChannels(image)*image->columns;
3308 luminance_pixels+=GetPixelChannels(luminance_image)*
3309 luminance_image->columns;
3311 if (fabs((double) gamma) < MagickEpsilon)
3313 SetPixelChannel(blur_image,channel,p[center+i],q);
3316 gamma=PerceptibleReciprocal(gamma);
3317 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3319 p+=GetPixelChannels(image);
3320 l+=GetPixelChannels(luminance_image);
3321 q+=GetPixelChannels(blur_image);
3323 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3324 if (sync == MagickFalse)
3326 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3331 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3332 #pragma omp critical (MagickCore_SelectiveBlurImage)
3334 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
3336 if (proceed == MagickFalse)
3340 blur_image->type=image->type;
3341 blur_view=DestroyCacheView(blur_view);
3342 image_view=DestroyCacheView(image_view);
3343 luminance_image=DestroyImage(luminance_image);
3344 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3345 if (status == MagickFalse)
3346 blur_image=DestroyImage(blur_image);
3351 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3355 % S h a d e I m a g e %
3359 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3361 % ShadeImage() shines a distant light on an image to create a
3362 % three-dimensional effect. You control the positioning of the light with
3363 % azimuth and elevation; azimuth is measured in degrees off the x axis
3364 % and elevation is measured in pixels above the Z axis.
3366 % The format of the ShadeImage method is:
3368 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3369 % const double azimuth,const double elevation,ExceptionInfo *exception)
3371 % A description of each parameter follows:
3373 % o image: the image.
3375 % o gray: A value other than zero shades the intensity of each pixel.
3377 % o azimuth, elevation: Define the light source direction.
3379 % o exception: return any errors or warnings in this structure.
3382 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3383 const double azimuth,const double elevation,ExceptionInfo *exception)
3385 #define ShadeImageTag "Shade/Image"
3408 Initialize shaded image attributes.
3410 assert(image != (const Image *) NULL);
3411 assert(image->signature == MagickCoreSignature);
3412 if (image->debug != MagickFalse)
3413 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3414 assert(exception != (ExceptionInfo *) NULL);
3415 assert(exception->signature == MagickCoreSignature);
3416 linear_image=CloneImage(image,0,0,MagickTrue,exception);
3417 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3418 if ((linear_image == (Image *) NULL) || (shade_image == (Image *) NULL))
3420 if (linear_image != (Image *) NULL)
3421 linear_image=DestroyImage(linear_image);
3422 if (shade_image != (Image *) NULL)
3423 shade_image=DestroyImage(shade_image);
3424 return((Image *) NULL);
3426 if (SetImageStorageClass(shade_image,DirectClass,exception) == MagickFalse)
3428 linear_image=DestroyImage(linear_image);
3429 shade_image=DestroyImage(shade_image);
3430 return((Image *) NULL);
3433 Compute the light vector.
3435 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3436 cos(DegreesToRadians(elevation));
3437 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3438 cos(DegreesToRadians(elevation));
3439 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3445 image_view=AcquireVirtualCacheView(linear_image,exception);
3446 shade_view=AcquireAuthenticCacheView(shade_image,exception);
3447 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3448 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3449 magick_threads(linear_image,shade_image,linear_image->rows,1)
3451 for (y=0; y < (ssize_t) linear_image->rows; y++)
3461 register const Quantum
3462 *magick_restrict center,
3464 *magick_restrict post,
3465 *magick_restrict pre;
3473 if (status == MagickFalse)
3475 p=GetCacheViewVirtualPixels(image_view,-1,y-1,linear_image->columns+2,3,
3477 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3479 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3485 Shade this row of pixels.
3487 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
3488 for (x=0; x < (ssize_t) linear_image->columns; x++)
3494 Determine the surface normal and compute shading.
3496 pre=p+GetPixelChannels(linear_image);
3497 center=pre+(linear_image->columns+2)*GetPixelChannels(linear_image);
3498 post=center+(linear_image->columns+2)*GetPixelChannels(linear_image);
3500 GetPixelIntensity(linear_image,pre-GetPixelChannels(linear_image))+
3501 GetPixelIntensity(linear_image,center-GetPixelChannels(linear_image))+
3502 GetPixelIntensity(linear_image,post-GetPixelChannels(linear_image))-
3503 GetPixelIntensity(linear_image,pre+GetPixelChannels(linear_image))-
3504 GetPixelIntensity(linear_image,center+GetPixelChannels(linear_image))-
3505 GetPixelIntensity(linear_image,post+GetPixelChannels(linear_image)));
3507 GetPixelIntensity(linear_image,post-GetPixelChannels(linear_image))+
3508 GetPixelIntensity(linear_image,post)+
3509 GetPixelIntensity(linear_image,post+GetPixelChannels(linear_image))-
3510 GetPixelIntensity(linear_image,pre-GetPixelChannels(linear_image))-
3511 GetPixelIntensity(linear_image,pre)-
3512 GetPixelIntensity(linear_image,pre+GetPixelChannels(linear_image)));
3513 if ((fabs(normal.x) <= MagickEpsilon) &&
3514 (fabs(normal.y) <= MagickEpsilon))
3519 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3520 if (distance > MagickEpsilon)
3522 normal_distance=normal.x*normal.x+normal.y*normal.y+
3524 if (normal_distance > (MagickEpsilon*MagickEpsilon))
3525 shade=distance/sqrt((double) normal_distance);
3528 for (i=0; i < (ssize_t) GetPixelChannels(linear_image); i++)
3537 channel=GetPixelChannelChannel(linear_image,i);
3538 traits=GetPixelChannelTraits(linear_image,channel);
3539 shade_traits=GetPixelChannelTraits(shade_image,channel);
3540 if ((traits == UndefinedPixelTrait) ||
3541 (shade_traits == UndefinedPixelTrait))
3543 if (((shade_traits & CopyPixelTrait) != 0) ||
3544 (GetPixelWriteMask(linear_image,center) == 0))
3546 SetPixelChannel(shade_image,channel,center[i],q);
3549 if ((traits & UpdatePixelTrait) == 0)
3551 SetPixelChannel(shade_image,channel,center[i],q);
3554 if (gray != MagickFalse)
3556 SetPixelChannel(shade_image,channel,ClampToQuantum(shade),q);
3559 SetPixelChannel(shade_image,channel,ClampToQuantum(QuantumScale*shade*
3562 p+=GetPixelChannels(linear_image);
3563 q+=GetPixelChannels(shade_image);
3565 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3567 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3572 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3573 #pragma omp critical (MagickCore_ShadeImage)
3575 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
3576 if (proceed == MagickFalse)
3580 shade_view=DestroyCacheView(shade_view);
3581 image_view=DestroyCacheView(image_view);
3582 linear_image=DestroyImage(linear_image);
3583 if (status == MagickFalse)
3584 shade_image=DestroyImage(shade_image);
3585 return(shade_image);
3589 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3593 % S h a r p e n I m a g e %
3597 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3599 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
3600 % operator of the given radius and standard deviation (sigma). For
3601 % reasonable results, radius should be larger than sigma. Use a radius of 0
3602 % and SharpenImage() selects a suitable radius for you.
3604 % Using a separable kernel would be faster, but the negative weights cancel
3605 % out on the corners of the kernel producing often undesirable ringing in the
3606 % filtered result; this can be avoided by using a 2D gaussian shaped image
3607 % sharpening kernel instead.
3609 % The format of the SharpenImage method is:
3611 % Image *SharpenImage(const Image *image,const double radius,
3612 % const double sigma,ExceptionInfo *exception)
3614 % A description of each parameter follows:
3616 % o image: the image.
3618 % o radius: the radius of the Gaussian, in pixels, not counting the center
3621 % o sigma: the standard deviation of the Laplacian, in pixels.
3623 % o exception: return any errors or warnings in this structure.
3626 MagickExport Image *SharpenImage(const Image *image,const double radius,
3627 const double sigma,ExceptionInfo *exception)
3650 assert(image != (const Image *) NULL);
3651 assert(image->signature == MagickCoreSignature);
3652 if (image->debug != MagickFalse)
3653 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3654 assert(exception != (ExceptionInfo *) NULL);
3655 assert(exception->signature == MagickCoreSignature);
3656 width=GetOptimalKernelWidth2D(radius,sigma);
3657 kernel_info=AcquireKernelInfo((const char *) NULL,exception);
3658 if (kernel_info == (KernelInfo *) NULL)
3659 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3660 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
3661 kernel_info->width=width;
3662 kernel_info->height=width;
3663 kernel_info->x=(ssize_t) (width-1)/2;
3664 kernel_info->y=(ssize_t) (width-1)/2;
3665 kernel_info->signature=MagickCoreSignature;
3666 kernel_info->values=(MagickRealType *) MagickAssumeAligned(
3667 AcquireAlignedMemory(kernel_info->width,kernel_info->height*
3668 sizeof(*kernel_info->values)));
3669 if (kernel_info->values == (MagickRealType *) NULL)
3671 kernel_info=DestroyKernelInfo(kernel_info);
3672 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3675 j=(ssize_t) (kernel_info->width-1)/2;
3677 for (v=(-j); v <= j; v++)
3679 for (u=(-j); u <= j; u++)
3681 kernel_info->values[i]=(MagickRealType) (-exp(-((double) u*u+v*v)/(2.0*
3682 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3683 normalize+=kernel_info->values[i];
3687 kernel_info->values[i/2]=(double) ((-2.0)*normalize);
3689 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
3690 normalize+=kernel_info->values[i];
3691 gamma=PerceptibleReciprocal(normalize);
3692 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
3693 kernel_info->values[i]*=gamma;
3694 sharp_image=ConvolveImage(image,kernel_info,exception);
3695 kernel_info=DestroyKernelInfo(kernel_info);
3696 return(sharp_image);
3700 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3704 % S p r e a d I m a g e %
3708 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3710 % SpreadImage() is a special effects method that randomly displaces each
3711 % pixel in a square area defined by the radius parameter.
3713 % The format of the SpreadImage method is:
3715 % Image *SpreadImage(const Image *image,
3716 % const PixelInterpolateMethod method,const double radius,
3717 % ExceptionInfo *exception)
3719 % A description of each parameter follows:
3721 % o image: the image.
3723 % o method: intepolation method.
3725 % o radius: choose a random pixel in a neighborhood of this extent.
3727 % o exception: return any errors or warnings in this structure.
3730 MagickExport Image *SpreadImage(const Image *image,
3731 const PixelInterpolateMethod method,const double radius,
3732 ExceptionInfo *exception)
3734 #define SpreadImageTag "Spread/Image"
3750 **magick_restrict random_info;
3758 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3764 Initialize spread image attributes.
3766 assert(image != (Image *) NULL);
3767 assert(image->signature == MagickCoreSignature);
3768 if (image->debug != MagickFalse)
3769 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3770 assert(exception != (ExceptionInfo *) NULL);
3771 assert(exception->signature == MagickCoreSignature);
3772 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3774 if (spread_image == (Image *) NULL)
3775 return((Image *) NULL);
3776 if (SetImageStorageClass(spread_image,DirectClass,exception) == MagickFalse)
3778 spread_image=DestroyImage(spread_image);
3779 return((Image *) NULL);
3786 width=GetOptimalKernelWidth1D(radius,0.5);
3787 random_info=AcquireRandomInfoThreadSet();
3788 image_view=AcquireVirtualCacheView(image,exception);
3789 spread_view=AcquireAuthenticCacheView(spread_image,exception);
3790 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3791 key=GetRandomSecretKey(random_info[0]);
3792 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3793 magick_threads(image,spread_image,image->rows,key == ~0UL)
3795 for (y=0; y < (ssize_t) image->rows; y++)
3798 id = GetOpenMPThreadId();
3806 if (status == MagickFalse)
3808 q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
3810 if (q == (Quantum *) NULL)
3815 for (x=0; x < (ssize_t) image->columns; x++)
3820 point.x=GetPseudoRandomValue(random_info[id]);
3821 point.y=GetPseudoRandomValue(random_info[id]);
3822 status=InterpolatePixelChannels(image,image_view,spread_image,method,
3823 (double) x+width*(point.x-0.5),(double) y+width*(point.y-0.5),q,
3825 q+=GetPixelChannels(spread_image);
3827 if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
3829 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3834 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3835 #pragma omp critical (MagickCore_SpreadImage)
3837 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3838 if (proceed == MagickFalse)
3842 spread_view=DestroyCacheView(spread_view);
3843 image_view=DestroyCacheView(image_view);
3844 random_info=DestroyRandomInfoThreadSet(random_info);
3845 if (status == MagickFalse)
3846 spread_image=DestroyImage(spread_image);
3847 return(spread_image);
3851 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3855 % U n s h a r p M a s k I m a g e %
3859 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3861 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
3862 % image with a Gaussian operator of the given radius and standard deviation
3863 % (sigma). For reasonable results, radius should be larger than sigma. Use a
3864 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
3866 % The format of the UnsharpMaskImage method is:
3868 % Image *UnsharpMaskImage(const Image *image,const double radius,
3869 % const double sigma,const double amount,const double threshold,
3870 % ExceptionInfo *exception)
3872 % A description of each parameter follows:
3874 % o image: the image.
3876 % o radius: the radius of the Gaussian, in pixels, not counting the center
3879 % o sigma: the standard deviation of the Gaussian, in pixels.
3881 % o gain: the percentage of the difference between the original and the
3882 % blur image that is added back into the original.
3884 % o threshold: the threshold in pixels needed to apply the diffence gain.
3886 % o exception: return any errors or warnings in this structure.
3889 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
3890 const double sigma,const double gain,const double threshold,
3891 ExceptionInfo *exception)
3893 #define SharpenImageTag "Sharpen/Image"
3914 assert(image != (const Image *) NULL);
3915 assert(image->signature == MagickCoreSignature);
3916 if (image->debug != MagickFalse)
3917 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3918 assert(exception != (ExceptionInfo *) NULL);
3919 #if defined(MAGICKCORE_OPENCL_SUPPORT)
3920 unsharp_image=AccelerateUnsharpMaskImage(image,radius,sigma,gain,threshold,
3922 if (unsharp_image != (Image *) NULL)
3923 return(unsharp_image);
3925 unsharp_image=BlurImage(image,radius,sigma,exception);
3926 if (unsharp_image == (Image *) NULL)
3927 return((Image *) NULL);
3928 quantum_threshold=(double) QuantumRange*threshold;
3934 image_view=AcquireVirtualCacheView(image,exception);
3935 unsharp_view=AcquireAuthenticCacheView(unsharp_image,exception);
3936 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3937 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3938 magick_threads(image,unsharp_image,image->rows,1)
3940 for (y=0; y < (ssize_t) image->rows; y++)
3942 register const Quantum
3951 if (status == MagickFalse)
3953 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3954 q=QueueCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
3956 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3961 for (x=0; x < (ssize_t) image->columns; x++)
3966 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3978 channel=GetPixelChannelChannel(image,i);
3979 traits=GetPixelChannelTraits(image,channel);
3980 unsharp_traits=GetPixelChannelTraits(unsharp_image,channel);
3981 if ((traits == UndefinedPixelTrait) ||
3982 (unsharp_traits == UndefinedPixelTrait))
3984 if (((unsharp_traits & CopyPixelTrait) != 0) ||
3985 (GetPixelWriteMask(image,p) == 0))
3987 SetPixelChannel(unsharp_image,channel,p[i],q);
3990 pixel=p[i]-(double) GetPixelChannel(unsharp_image,channel,q);
3991 if (fabs(2.0*pixel) < quantum_threshold)
3992 pixel=(double) p[i];
3994 pixel=(double) p[i]+gain*pixel;
3995 SetPixelChannel(unsharp_image,channel,ClampToQuantum(pixel),q);
3997 p+=GetPixelChannels(image);
3998 q+=GetPixelChannels(unsharp_image);
4000 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4002 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4007 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4008 #pragma omp critical (MagickCore_UnsharpMaskImage)
4010 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
4011 if (proceed == MagickFalse)
4015 unsharp_image->type=image->type;
4016 unsharp_view=DestroyCacheView(unsharp_view);
4017 image_view=DestroyCacheView(image_view);
4018 if (status == MagickFalse)
4019 unsharp_image=DestroyImage(unsharp_image);
4020 return(unsharp_image);