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-2011 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "MagickCore/studio.h"
44 #include "MagickCore/accelerate.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/cache-view.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/colorspace.h"
50 #include "MagickCore/constitute.h"
51 #include "MagickCore/decorate.h"
52 #include "MagickCore/draw.h"
53 #include "MagickCore/enhance.h"
54 #include "MagickCore/exception.h"
55 #include "MagickCore/exception-private.h"
56 #include "MagickCore/effect.h"
57 #include "MagickCore/fx.h"
58 #include "MagickCore/gem.h"
59 #include "MagickCore/geometry.h"
60 #include "MagickCore/image-private.h"
61 #include "MagickCore/list.h"
62 #include "MagickCore/log.h"
63 #include "MagickCore/memory_.h"
64 #include "MagickCore/monitor.h"
65 #include "MagickCore/monitor-private.h"
66 #include "MagickCore/montage.h"
67 #include "MagickCore/morphology.h"
68 #include "MagickCore/paint.h"
69 #include "MagickCore/pixel-accessor.h"
70 #include "MagickCore/property.h"
71 #include "MagickCore/quantize.h"
72 #include "MagickCore/quantum.h"
73 #include "MagickCore/quantum-private.h"
74 #include "MagickCore/random_.h"
75 #include "MagickCore/random-private.h"
76 #include "MagickCore/resample.h"
77 #include "MagickCore/resample-private.h"
78 #include "MagickCore/resize.h"
79 #include "MagickCore/resource_.h"
80 #include "MagickCore/segment.h"
81 #include "MagickCore/shear.h"
82 #include "MagickCore/signature-private.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/thread-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/threshold.h"
89 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
93 % A d a p t i v e B l u r I m a g e %
97 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
99 % AdaptiveBlurImage() adaptively blurs the image by blurring less
100 % intensely near image edges and more intensely far from edges. We blur the
101 % image with a Gaussian operator of the given radius and standard deviation
102 % (sigma). For reasonable results, radius should be larger than sigma. Use a
103 % radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
105 % The format of the AdaptiveBlurImage method is:
107 % Image *AdaptiveBlurImage(const Image *image,const double radius,
108 % const double sigma,ExceptionInfo *exception)
110 % A description of each parameter follows:
112 % o image: the image.
114 % o radius: the radius of the Gaussian, in pixels, not counting the center
117 % o sigma: the standard deviation of the Laplacian, in pixels.
119 % o exception: return any errors or warnings in this structure.
123 MagickExport MagickBooleanType AdaptiveLevelImage(Image *image,
146 if (levels == (char *) NULL)
148 exception=(&image->exception);
149 flags=ParseGeometry(levels,&geometry_info);
150 black_point=geometry_info.rho;
151 white_point=(double) QuantumRange;
152 if ((flags & SigmaValue) != 0)
153 white_point=geometry_info.sigma;
155 if ((flags & XiValue) != 0)
156 gamma=geometry_info.xi;
157 if ((flags & PercentValue) != 0)
159 black_point*=(double) image->columns*image->rows/100.0;
160 white_point*=(double) image->columns*image->rows/100.0;
162 if ((flags & SigmaValue) == 0)
163 white_point=(double) QuantumRange-black_point;
164 if ((flags & AspectValue ) == 0)
165 status=LevelImage(image,black_point,white_point,gamma,exception);
167 status=LevelizeImage(image,black_point,white_point,gamma,exception);
171 MagickExport Image *AdaptiveBlurImage(const Image *image,
172 const double radius,const double sigma,ExceptionInfo *exception)
174 #define AdaptiveBlurImageTag "Convolve/Image"
175 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
213 assert(image != (const Image *) NULL);
214 assert(image->signature == MagickSignature);
215 if (image->debug != MagickFalse)
216 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
217 assert(exception != (ExceptionInfo *) NULL);
218 assert(exception->signature == MagickSignature);
219 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
220 if (blur_image == (Image *) NULL)
221 return((Image *) NULL);
222 if (fabs(sigma) <= MagickEpsilon)
224 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
226 blur_image=DestroyImage(blur_image);
227 return((Image *) NULL);
230 Edge detect the image brighness channel, level, blur, and level again.
232 edge_image=EdgeImage(image,radius,exception);
233 if (edge_image == (Image *) NULL)
235 blur_image=DestroyImage(blur_image);
236 return((Image *) NULL);
238 (void) AdaptiveLevelImage(edge_image,"20%,95%");
239 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
240 if (gaussian_image != (Image *) NULL)
242 edge_image=DestroyImage(edge_image);
243 edge_image=gaussian_image;
245 (void) AdaptiveLevelImage(edge_image,"10%,95%");
247 Create a set of kernels from maximum (radius,sigma) to minimum.
249 width=GetOptimalKernelWidth2D(radius,sigma);
250 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
251 if (kernel == (double **) NULL)
253 edge_image=DestroyImage(edge_image);
254 blur_image=DestroyImage(blur_image);
255 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
257 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
258 for (i=0; i < (ssize_t) width; i+=2)
260 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
262 if (kernel[i] == (double *) NULL)
265 j=(ssize_t) (width-i)/2;
267 for (v=(-j); v <= j; v++)
269 for (u=(-j); u <= j; u++)
271 kernel[i][k]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
272 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
273 normalize+=kernel[i][k];
277 if (fabs(normalize) <= MagickEpsilon)
279 normalize=1.0/normalize;
280 for (k=0; k < (j*j); k++)
281 kernel[i][k]=normalize*kernel[i][k];
283 if (i < (ssize_t) width)
285 for (i-=2; i >= 0; i-=2)
286 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
287 kernel=(double **) RelinquishMagickMemory(kernel);
288 edge_image=DestroyImage(edge_image);
289 blur_image=DestroyImage(blur_image);
290 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
293 Adaptively blur image.
297 GetPixelInfo(image,&bias);
298 SetPixelInfoBias(image,&bias);
299 image_view=AcquireCacheView(image);
300 edge_view=AcquireCacheView(edge_image);
301 blur_view=AcquireCacheView(blur_image);
302 #if defined(MAGICKCORE_OPENMP_SUPPORT)
303 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
305 for (y=0; y < (ssize_t) blur_image->rows; y++)
307 register const Quantum
317 if (status == MagickFalse)
319 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
320 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
322 if ((r == (const Quantum *) NULL) || (q == (const Quantum *) NULL))
327 for (x=0; x < (ssize_t) blur_image->columns; x++)
336 register const double
345 i=(ssize_t) ceil((double) width*QuantumScale*
346 GetPixelIntensity(edge_image,r)-0.5);
350 if (i > (ssize_t) width)
354 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-i)/2L),y-
355 (ssize_t) ((width-i)/2L),width-i,width-i,exception);
356 if (p == (const Quantum *) NULL)
360 for (v=0; v < (ssize_t) (width-i); v++)
362 for (u=0; u < (ssize_t) (width-i); u++)
365 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
366 (image->matte != MagickFalse))
367 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,p));
368 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
369 pixel.red+=(*k)*alpha*GetPixelRed(image,p);
370 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
371 pixel.green+=(*k)*alpha*GetPixelGreen(image,p);
372 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
373 pixel.blue+=(*k)*alpha*GetPixelBlue(image,p);
374 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
375 (image->colorspace == CMYKColorspace))
376 pixel.black+=(*k)*alpha*GetPixelBlack(image,p);
377 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
378 pixel.alpha+=(*k)*GetPixelAlpha(image,p);
381 p+=GetPixelChannels(image);
384 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
385 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
386 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
387 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
388 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
389 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
390 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
391 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
392 (image->colorspace == CMYKColorspace))
393 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
394 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
395 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
396 q+=GetPixelChannels(blur_image);
397 r+=GetPixelChannels(edge_image);
399 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
401 if (image->progress_monitor != (MagickProgressMonitor) NULL)
406 #if defined(MAGICKCORE_OPENMP_SUPPORT)
407 #pragma omp critical (MagickCore_AdaptiveSharpenImage)
409 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
411 if (proceed == MagickFalse)
415 blur_image->type=image->type;
416 blur_view=DestroyCacheView(blur_view);
417 edge_view=DestroyCacheView(edge_view);
418 image_view=DestroyCacheView(image_view);
419 edge_image=DestroyImage(edge_image);
420 for (i=0; i < (ssize_t) width; i+=2)
421 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
422 kernel=(double **) RelinquishMagickMemory(kernel);
423 if (status == MagickFalse)
424 blur_image=DestroyImage(blur_image);
429 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
433 % A d a p t i v e S h a r p e n I m a g e %
437 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
439 % AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
440 % intensely near image edges and less intensely far from edges. We sharpen the
441 % image with a Gaussian operator of the given radius and standard deviation
442 % (sigma). For reasonable results, radius should be larger than sigma. Use a
443 % radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
445 % The format of the AdaptiveSharpenImage method is:
447 % Image *AdaptiveSharpenImage(const Image *image,const double radius,
448 % const double sigma,ExceptionInfo *exception)
450 % A description of each parameter follows:
452 % o image: the image.
454 % o radius: the radius of the Gaussian, in pixels, not counting the center
457 % o sigma: the standard deviation of the Laplacian, in pixels.
459 % o exception: return any errors or warnings in this structure.
462 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
463 const double sigma,ExceptionInfo *exception)
465 #define AdaptiveSharpenImageTag "Convolve/Image"
466 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
504 assert(image != (const Image *) NULL);
505 assert(image->signature == MagickSignature);
506 if (image->debug != MagickFalse)
507 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
508 assert(exception != (ExceptionInfo *) NULL);
509 assert(exception->signature == MagickSignature);
510 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
511 if (sharp_image == (Image *) NULL)
512 return((Image *) NULL);
513 if (fabs(sigma) <= MagickEpsilon)
515 if (SetImageStorageClass(sharp_image,DirectClass,exception) == MagickFalse)
517 sharp_image=DestroyImage(sharp_image);
518 return((Image *) NULL);
521 Edge detect the image brighness channel, level, sharp, and level again.
523 edge_image=EdgeImage(image,radius,exception);
524 if (edge_image == (Image *) NULL)
526 sharp_image=DestroyImage(sharp_image);
527 return((Image *) NULL);
529 (void) AdaptiveLevelImage(edge_image,"20%,95%");
530 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
531 if (gaussian_image != (Image *) NULL)
533 edge_image=DestroyImage(edge_image);
534 edge_image=gaussian_image;
536 (void) AdaptiveLevelImage(edge_image,"10%,95%");
538 Create a set of kernels from maximum (radius,sigma) to minimum.
540 width=GetOptimalKernelWidth2D(radius,sigma);
541 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
542 if (kernel == (double **) NULL)
544 edge_image=DestroyImage(edge_image);
545 sharp_image=DestroyImage(sharp_image);
546 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
548 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
549 for (i=0; i < (ssize_t) width; i+=2)
551 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
553 if (kernel[i] == (double *) NULL)
556 j=(ssize_t) (width-i)/2;
558 for (v=(-j); v <= j; v++)
560 for (u=(-j); u <= j; u++)
562 kernel[i][k]=(double) (-exp(-((double) u*u+v*v)/(2.0*MagickSigma*
563 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
564 normalize+=kernel[i][k];
568 if (fabs(normalize) <= MagickEpsilon)
570 normalize=1.0/normalize;
571 for (k=0; k < (j*j); k++)
572 kernel[i][k]=normalize*kernel[i][k];
574 if (i < (ssize_t) width)
576 for (i-=2; i >= 0; i-=2)
577 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
578 kernel=(double **) RelinquishMagickMemory(kernel);
579 edge_image=DestroyImage(edge_image);
580 sharp_image=DestroyImage(sharp_image);
581 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
584 Adaptively sharpen image.
588 GetPixelInfo(image,&bias);
589 SetPixelInfoBias(image,&bias);
590 image_view=AcquireCacheView(image);
591 edge_view=AcquireCacheView(edge_image);
592 sharp_view=AcquireCacheView(sharp_image);
593 #if defined(MAGICKCORE_OPENMP_SUPPORT)
594 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
596 for (y=0; y < (ssize_t) sharp_image->rows; y++)
598 register const Quantum
608 if (status == MagickFalse)
610 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
611 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
613 if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
618 for (x=0; x < (ssize_t) sharp_image->columns; x++)
627 register const double
636 i=(ssize_t) ceil((double) width*QuantumScale*
637 GetPixelIntensity(edge_image,r)-0.5);
641 if (i > (ssize_t) width)
645 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-i)/2L),y-
646 (ssize_t) ((width-i)/2L),width-i,width-i,exception);
647 if (p == (const Quantum *) NULL)
651 for (v=0; v < (ssize_t) (width-i); v++)
653 for (u=0; u < (ssize_t) (width-i); u++)
656 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
657 (image->matte != MagickFalse))
658 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,p));
659 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
660 pixel.red+=(*k)*alpha*GetPixelRed(image,p);
661 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
662 pixel.green+=(*k)*alpha*GetPixelGreen(image,p);
663 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
664 pixel.blue+=(*k)*alpha*GetPixelBlue(image,p);
665 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
666 (image->colorspace == CMYKColorspace))
667 pixel.black+=(*k)*alpha*GetPixelBlack(image,p);
668 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
669 pixel.alpha+=(*k)*GetPixelAlpha(image,p);
672 p+=GetPixelChannels(image);
675 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
676 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
677 SetPixelRed(sharp_image,ClampToQuantum(gamma*pixel.red),q);
678 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
679 SetPixelGreen(sharp_image,ClampToQuantum(gamma*pixel.green),q);
680 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
681 SetPixelBlue(sharp_image,ClampToQuantum(gamma*pixel.blue),q);
682 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
683 (image->colorspace == CMYKColorspace))
684 SetPixelBlack(sharp_image,ClampToQuantum(gamma*pixel.black),q);
685 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
686 SetPixelAlpha(sharp_image,ClampToQuantum(pixel.alpha),q);
687 q+=GetPixelChannels(sharp_image);
688 r+=GetPixelChannels(edge_image);
690 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
692 if (image->progress_monitor != (MagickProgressMonitor) NULL)
697 #if defined(MAGICKCORE_OPENMP_SUPPORT)
698 #pragma omp critical (MagickCore_AdaptiveSharpenImage)
700 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
702 if (proceed == MagickFalse)
706 sharp_image->type=image->type;
707 sharp_view=DestroyCacheView(sharp_view);
708 edge_view=DestroyCacheView(edge_view);
709 image_view=DestroyCacheView(image_view);
710 edge_image=DestroyImage(edge_image);
711 for (i=0; i < (ssize_t) width; i+=2)
712 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
713 kernel=(double **) RelinquishMagickMemory(kernel);
714 if (status == MagickFalse)
715 sharp_image=DestroyImage(sharp_image);
720 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
724 % B l u r I m a g e %
728 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
730 % BlurImage() blurs an image. We convolve the image with a Gaussian operator
731 % of the given radius and standard deviation (sigma). For reasonable results,
732 % the radius should be larger than sigma. Use a radius of 0 and BlurImage()
733 % selects a suitable radius for you.
735 % BlurImage() differs from GaussianBlurImage() in that it uses a separable
736 % kernel which is faster but mathematically equivalent to the non-separable
739 % The format of the BlurImage method is:
741 % Image *BlurImage(const Image *image,const double radius,
742 % const double sigma,ExceptionInfo *exception)
744 % A description of each parameter follows:
746 % o image: the image.
748 % o radius: the radius of the Gaussian, in pixels, not counting the center
751 % o sigma: the standard deviation of the Gaussian, in pixels.
753 % o exception: return any errors or warnings in this structure.
757 static double *GetBlurKernel(const size_t width,const double sigma)
771 Generate a 1-D convolution kernel.
773 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
774 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
775 if (kernel == (double *) NULL)
780 for (k=(-j); k <= j; k++)
782 kernel[i]=(double) (exp(-((double) k*k)/(2.0*MagickSigma*MagickSigma))/
783 (MagickSQ2PI*MagickSigma));
784 normalize+=kernel[i];
787 for (i=0; i < (ssize_t) width; i++)
788 kernel[i]/=normalize;
792 MagickExport Image *BlurImage(const Image *image,const double radius,
793 const double sigma,ExceptionInfo *exception)
795 #define BlurImageTag "Blur/Image"
827 Initialize blur image attributes.
829 assert(image != (Image *) NULL);
830 assert(image->signature == MagickSignature);
831 if (image->debug != MagickFalse)
832 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
833 assert(exception != (ExceptionInfo *) NULL);
834 assert(exception->signature == MagickSignature);
835 blur_image=CloneImage(image,0,0,MagickTrue,exception);
836 if (blur_image == (Image *) NULL)
837 return((Image *) NULL);
838 if (fabs(sigma) <= MagickEpsilon)
840 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
842 blur_image=DestroyImage(blur_image);
843 return((Image *) NULL);
845 width=GetOptimalKernelWidth1D(radius,sigma);
846 kernel=GetBlurKernel(width,sigma);
847 if (kernel == (double *) NULL)
849 blur_image=DestroyImage(blur_image);
850 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
852 if (image->debug != MagickFalse)
855 format[MaxTextExtent],
858 register const double
861 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
862 " BlurImage with %.20g kernel:",(double) width);
863 message=AcquireString("");
865 for (i=0; i < (ssize_t) width; i++)
868 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) i);
869 (void) ConcatenateString(&message,format);
870 (void) FormatLocaleString(format,MaxTextExtent,"%g ",*k++);
871 (void) ConcatenateString(&message,format);
872 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
874 message=DestroyString(message);
881 GetPixelInfo(image,&bias);
882 SetPixelInfoBias(image,&bias);
883 image_view=AcquireCacheView(image);
884 blur_view=AcquireCacheView(blur_image);
885 #if defined(MAGICKCORE_OPENMP_SUPPORT)
886 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
888 for (y=0; y < (ssize_t) blur_image->rows; y++)
890 register const Quantum
899 if (status == MagickFalse)
901 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y,
902 image->columns+width,1,exception);
903 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
905 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
910 for (x=0; x < (ssize_t) blur_image->columns; x++)
915 register const double
918 register const Quantum
919 *restrict kernel_pixels;
927 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) ||
928 (image->matte == MagickFalse))
930 for (i=0; i < (ssize_t) width; i++)
932 pixel.red+=(*k)*GetPixelRed(image,kernel_pixels);
933 pixel.green+=(*k)*GetPixelGreen(image,kernel_pixels);
934 pixel.blue+=(*k)*GetPixelBlue(image,kernel_pixels);
935 if (image->colorspace == CMYKColorspace)
936 pixel.black+=(*k)*GetPixelBlack(image,kernel_pixels);
938 kernel_pixels+=GetPixelChannels(image);
940 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
941 SetPixelRed(blur_image,ClampToQuantum(pixel.red),q);
942 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
943 SetPixelGreen(blur_image,ClampToQuantum(pixel.green),q);
944 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
945 SetPixelBlue(blur_image,ClampToQuantum(pixel.blue),q);
946 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
947 (blur_image->colorspace == CMYKColorspace))
948 SetPixelBlack(blur_image,ClampToQuantum(pixel.black),q);
949 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
953 for (i=0; i < (ssize_t) width; i++)
955 pixel.alpha+=(*k)*GetPixelAlpha(image,kernel_pixels);
957 kernel_pixels+=GetPixelChannels(image);
959 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
969 for (i=0; i < (ssize_t) width; i++)
971 alpha=(MagickRealType) (QuantumScale*
972 GetPixelAlpha(image,kernel_pixels));
973 pixel.red+=(*k)*alpha*GetPixelRed(image,kernel_pixels);
974 pixel.green+=(*k)*alpha*GetPixelGreen(image,kernel_pixels);
975 pixel.blue+=(*k)*alpha*GetPixelBlue(image,kernel_pixels);
976 if (image->colorspace == CMYKColorspace)
977 pixel.black+=(*k)*alpha*GetPixelBlack(image,kernel_pixels);
980 kernel_pixels+=GetPixelChannels(image);
982 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
983 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
984 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
985 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
986 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
987 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
988 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
989 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
990 (blur_image->colorspace == CMYKColorspace))
991 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
992 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
996 for (i=0; i < (ssize_t) width; i++)
998 pixel.alpha+=(*k)*GetPixelAlpha(image,kernel_pixels);
1000 kernel_pixels+=GetPixelChannels(image);
1002 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
1005 p+=GetPixelChannels(image);
1006 q+=GetPixelChannels(blur_image);
1008 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1010 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1015 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1016 #pragma omp critical (MagickCore_BlurImage)
1018 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1019 blur_image->columns);
1020 if (proceed == MagickFalse)
1024 blur_view=DestroyCacheView(blur_view);
1025 image_view=DestroyCacheView(image_view);
1029 image_view=AcquireCacheView(blur_image);
1030 blur_view=AcquireCacheView(blur_image);
1031 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1032 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1034 for (x=0; x < (ssize_t) blur_image->columns; x++)
1036 register const Quantum
1045 if (status == MagickFalse)
1047 p=GetCacheViewVirtualPixels(image_view,x,-((ssize_t) width/2L),1,
1048 image->rows+width,exception);
1049 q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
1050 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1055 for (y=0; y < (ssize_t) blur_image->rows; y++)
1060 register const double
1063 register const Quantum
1064 *restrict kernel_pixels;
1072 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) ||
1073 (blur_image->matte == MagickFalse))
1075 for (i=0; i < (ssize_t) width; i++)
1077 pixel.red+=(*k)*GetPixelRed(blur_image,kernel_pixels);
1078 pixel.green+=(*k)*GetPixelGreen(blur_image,kernel_pixels);
1079 pixel.blue+=(*k)*GetPixelBlue(blur_image,kernel_pixels);
1080 if (blur_image->colorspace == CMYKColorspace)
1081 pixel.black+=(*k)*GetPixelBlack(blur_image,kernel_pixels);
1083 kernel_pixels+=GetPixelChannels(blur_image);
1085 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1086 SetPixelRed(blur_image,ClampToQuantum(pixel.red),q);
1087 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1088 SetPixelGreen(blur_image,ClampToQuantum(pixel.green),q);
1089 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1090 SetPixelBlue(blur_image,ClampToQuantum(pixel.blue),q);
1091 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1092 (blur_image->colorspace == CMYKColorspace))
1093 SetPixelBlack(blur_image,ClampToQuantum(pixel.black),q);
1094 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1098 for (i=0; i < (ssize_t) width; i++)
1100 pixel.alpha+=(*k)*GetPixelAlpha(blur_image,kernel_pixels);
1102 kernel_pixels+=GetPixelChannels(blur_image);
1104 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
1114 for (i=0; i < (ssize_t) width; i++)
1116 alpha=(MagickRealType) (QuantumScale*
1117 GetPixelAlpha(blur_image,kernel_pixels));
1118 pixel.red+=(*k)*alpha*GetPixelRed(blur_image,kernel_pixels);
1119 pixel.green+=(*k)*alpha*GetPixelGreen(blur_image,kernel_pixels);
1120 pixel.blue+=(*k)*alpha*GetPixelBlue(blur_image,kernel_pixels);
1121 if (blur_image->colorspace == CMYKColorspace)
1122 pixel.black+=(*k)*alpha*GetPixelBlack(blur_image,kernel_pixels);
1125 kernel_pixels+=GetPixelChannels(blur_image);
1127 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1128 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1129 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
1130 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1131 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
1132 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1133 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
1134 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1135 (blur_image->colorspace == CMYKColorspace))
1136 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
1137 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1141 for (i=0; i < (ssize_t) width; i++)
1143 pixel.alpha+=(*k)*GetPixelAlpha(blur_image,kernel_pixels);
1145 kernel_pixels+=GetPixelChannels(blur_image);
1147 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
1150 p+=GetPixelChannels(blur_image);
1151 q+=GetPixelChannels(blur_image);
1153 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1155 if (blur_image->progress_monitor != (MagickProgressMonitor) NULL)
1160 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1161 #pragma omp critical (MagickCore_BlurImage)
1163 proceed=SetImageProgress(blur_image,BlurImageTag,progress++,
1164 blur_image->rows+blur_image->columns);
1165 if (proceed == MagickFalse)
1169 blur_view=DestroyCacheView(blur_view);
1170 image_view=DestroyCacheView(image_view);
1171 kernel=(double *) RelinquishMagickMemory(kernel);
1172 if (status == MagickFalse)
1173 blur_image=DestroyImage(blur_image);
1174 blur_image->type=image->type;
1179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1183 % C o n v o l v e I m a g e %
1187 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1189 % ConvolveImage() applies a custom convolution kernel to the image.
1191 % The format of the ConvolveImage method is:
1193 % Image *ConvolveImage(const Image *image,const KernelInfo *kernel,
1194 % ExceptionInfo *exception)
1196 % A description of each parameter follows:
1198 % o image: the image.
1200 % o kernel: the filtering kernel.
1202 % o exception: return any errors or warnings in this structure.
1205 MagickExport Image *ConvolveImage(const Image *image,
1206 const KernelInfo *kernel_info,ExceptionInfo *exception)
1208 #define ConvolveImageTag "Convolve/Image"
1228 Initialize convolve image attributes.
1230 assert(image != (Image *) NULL);
1231 assert(image->signature == MagickSignature);
1232 if (image->debug != MagickFalse)
1233 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1234 assert(exception != (ExceptionInfo *) NULL);
1235 assert(exception->signature == MagickSignature);
1236 if ((kernel_info->width % 2) == 0)
1237 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
1238 convolve_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1240 if (convolve_image == (Image *) NULL)
1241 return((Image *) NULL);
1242 if (SetImageStorageClass(convolve_image,DirectClass,exception) == MagickFalse)
1244 convolve_image=DestroyImage(convolve_image);
1245 return((Image *) NULL);
1247 if (image->debug != MagickFalse)
1250 format[MaxTextExtent],
1253 register const double
1262 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1263 " ConvolveImage with %.20gx%.20g kernel:",(double) kernel_info->width,
1264 (double) kernel_info->height);
1265 message=AcquireString("");
1266 k=kernel_info->values;
1267 for (v=0; v < (ssize_t) kernel_info->width; v++)
1270 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
1271 (void) ConcatenateString(&message,format);
1272 for (u=0; u < (ssize_t) kernel_info->height; u++)
1274 (void) FormatLocaleString(format,MaxTextExtent,"%g ",*k++);
1275 (void) ConcatenateString(&message,format);
1277 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
1279 message=DestroyString(message);
1284 center=(ssize_t) GetPixelChannels(image)*(image->columns+kernel_info->width)*
1285 (kernel_info->height/2L)+GetPixelChannels(image)*(kernel_info->width/2);
1288 image_view=AcquireCacheView(image);
1289 convolve_view=AcquireCacheView(convolve_image);
1290 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1291 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1293 for (y=0; y < (ssize_t) image->rows; y++)
1295 register const Quantum
1304 if (status == MagickFalse)
1306 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel_info->width/2L),y-
1307 (ssize_t) (kernel_info->height/2L),image->columns+kernel_info->width,
1308 kernel_info->height,exception);
1309 q=QueueCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
1311 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1316 for (x=0; x < (ssize_t) image->columns; x++)
1321 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1335 register const double
1338 register const Quantum
1347 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1348 if (traits == UndefinedPixelTrait)
1350 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
1351 convolve_traits=GetPixelChannelMapTraits(convolve_image,channel);
1352 if (convolve_traits == UndefinedPixelTrait)
1354 if ((convolve_traits & CopyPixelTrait) != 0)
1356 q[channel]=p[center+i];
1359 k=kernel_info->values;
1361 pixel=kernel_info->bias;
1362 if ((convolve_traits & BlendPixelTrait) == 0)
1367 for (v=0; v < (ssize_t) kernel_info->height; v++)
1369 for (u=0; u < (ssize_t) kernel_info->width; u++)
1371 pixel+=(*k)*pixels[i];
1373 pixels+=GetPixelChannels(image);
1375 pixels+=image->columns*GetPixelChannels(image);
1377 q[channel]=ClampToQuantum(pixel);
1384 for (v=0; v < (ssize_t) kernel_info->height; v++)
1386 for (u=0; u < (ssize_t) kernel_info->width; u++)
1388 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
1389 pixel+=(*k)*alpha*pixels[i];
1392 pixels+=GetPixelChannels(image);
1394 pixels+=image->columns*GetPixelChannels(image);
1396 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1397 q[channel]=ClampToQuantum(gamma*pixel);
1399 p+=GetPixelChannels(image);
1400 q+=GetPixelChannels(convolve_image);
1402 if (SyncCacheViewAuthenticPixels(convolve_view,exception) == MagickFalse)
1404 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1409 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1410 #pragma omp critical (MagickCore_ConvolveImage)
1412 proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1413 if (proceed == MagickFalse)
1417 convolve_image->type=image->type;
1418 convolve_view=DestroyCacheView(convolve_view);
1419 image_view=DestroyCacheView(image_view);
1420 if (status == MagickFalse)
1421 convolve_image=DestroyImage(convolve_image);
1422 return(convolve_image);
1426 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1430 % D e s p e c k l e I m a g e %
1434 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1436 % DespeckleImage() reduces the speckle noise in an image while perserving the
1437 % edges of the original image.
1439 % The format of the DespeckleImage method is:
1441 % Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1443 % A description of each parameter follows:
1445 % o image: the image.
1447 % o exception: return any errors or warnings in this structure.
1451 static void Hull(const ssize_t x_offset,const ssize_t y_offset,
1452 const size_t columns,const size_t rows,Quantum *f,Quantum *g,
1470 assert(f != (Quantum *) NULL);
1471 assert(g != (Quantum *) NULL);
1474 r=p+(y_offset*((ssize_t) columns+2)+x_offset);
1475 for (y=0; y < (ssize_t) rows; y++)
1481 for (x=(ssize_t) columns; x != 0; x--)
1483 v=(MagickRealType) (*p);
1484 if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1485 v+=ScaleCharToQuantum(1);
1492 for (x=(ssize_t) columns; x != 0; x--)
1494 v=(MagickRealType) (*p);
1495 if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
1496 v-=(ssize_t) ScaleCharToQuantum(1);
1508 r=q+(y_offset*((ssize_t) columns+2)+x_offset);
1509 s=q-(y_offset*((ssize_t) columns+2)+x_offset);
1510 for (y=0; y < (ssize_t) rows; y++)
1517 for (x=(ssize_t) columns; x != 0; x--)
1519 v=(MagickRealType) (*q);
1520 if (((MagickRealType) *s >=
1521 (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1522 ((MagickRealType) *r > v))
1523 v+=ScaleCharToQuantum(1);
1531 for (x=(ssize_t) columns; x != 0; x--)
1533 v=(MagickRealType) (*q);
1534 if (((MagickRealType) *s <=
1535 (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1536 ((MagickRealType) *r < v))
1537 v-=(MagickRealType) ScaleCharToQuantum(1);
1551 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1553 #define DespeckleImageTag "Despeckle/Image"
1576 static const ssize_t
1577 X[4] = {0, 1, 1,-1},
1578 Y[4] = {1, 0, 1, 1};
1581 Allocate despeckled image.
1583 assert(image != (const Image *) NULL);
1584 assert(image->signature == MagickSignature);
1585 if (image->debug != MagickFalse)
1586 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1587 assert(exception != (ExceptionInfo *) NULL);
1588 assert(exception->signature == MagickSignature);
1589 despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1591 if (despeckle_image == (Image *) NULL)
1592 return((Image *) NULL);
1593 if (SetImageStorageClass(despeckle_image,DirectClass,exception) == MagickFalse)
1595 despeckle_image=DestroyImage(despeckle_image);
1596 return((Image *) NULL);
1599 Allocate image buffers.
1601 length=(size_t) ((image->columns+2)*(image->rows+2));
1602 pixels=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1603 buffers=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1604 if ((pixels == (Quantum *) NULL) || (buffers == (Quantum *) NULL))
1606 if (buffers != (Quantum *) NULL)
1607 buffers=(Quantum *) RelinquishMagickMemory(buffers);
1608 if (pixels != (Quantum *) NULL)
1609 pixels=(Quantum *) RelinquishMagickMemory(pixels);
1610 despeckle_image=DestroyImage(despeckle_image);
1611 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1614 Reduce speckle in the image.
1617 number_channels=(size_t) (image->colorspace == CMYKColorspace ? 5 : 4);
1618 image_view=AcquireCacheView(image);
1619 despeckle_view=AcquireCacheView(despeckle_image);
1620 for (i=0; i < (ssize_t) number_channels; i++)
1634 if (status == MagickFalse)
1637 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
1639 j=(ssize_t) image->columns+2;
1640 for (y=0; y < (ssize_t) image->rows; y++)
1642 register const Quantum
1645 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1646 if (p == (const Quantum *) NULL)
1649 for (x=0; x < (ssize_t) image->columns; x++)
1653 case 0: pixel[j]=GetPixelRed(image,p); break;
1654 case 1: pixel[j]=GetPixelGreen(image,p); break;
1655 case 2: pixel[j]=GetPixelBlue(image,p); break;
1656 case 3: pixel[j]=GetPixelAlpha(image,p); break;
1657 case 4: pixel[j]=GetPixelBlack(image,p); break;
1660 p+=GetPixelChannels(image);
1665 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1666 for (k=0; k < 4; k++)
1668 Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,1);
1669 Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,1);
1670 Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,-1);
1671 Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,-1);
1673 j=(ssize_t) image->columns+2;
1674 for (y=0; y < (ssize_t) image->rows; y++)
1682 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1684 if (q == (const Quantum *) NULL)
1687 for (x=0; x < (ssize_t) image->columns; x++)
1691 case 0: SetPixelRed(despeckle_image,pixel[j],q); break;
1692 case 1: SetPixelGreen(despeckle_image,pixel[j],q); break;
1693 case 2: SetPixelBlue(despeckle_image,pixel[j],q); break;
1694 case 3: SetPixelAlpha(despeckle_image,pixel[j],q); break;
1695 case 4: SetPixelBlack(despeckle_image,pixel[j],q); break;
1698 q+=GetPixelChannels(despeckle_image);
1701 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1702 if (sync == MagickFalse)
1709 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1714 proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1716 if (proceed == MagickFalse)
1720 despeckle_view=DestroyCacheView(despeckle_view);
1721 image_view=DestroyCacheView(image_view);
1722 buffers=(Quantum *) RelinquishMagickMemory(buffers);
1723 pixels=(Quantum *) RelinquishMagickMemory(pixels);
1724 despeckle_image->type=image->type;
1725 if (status == MagickFalse)
1726 despeckle_image=DestroyImage(despeckle_image);
1727 return(despeckle_image);
1731 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1735 % E d g e I m a g e %
1739 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1741 % EdgeImage() finds edges in an image. Radius defines the radius of the
1742 % convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1745 % The format of the EdgeImage method is:
1747 % Image *EdgeImage(const Image *image,const double radius,
1748 % ExceptionInfo *exception)
1750 % A description of each parameter follows:
1752 % o image: the image.
1754 % o radius: the radius of the pixel neighborhood.
1756 % o exception: return any errors or warnings in this structure.
1759 MagickExport Image *EdgeImage(const Image *image,const double radius,
1760 ExceptionInfo *exception)
1779 assert(image != (const Image *) NULL);
1780 assert(image->signature == MagickSignature);
1781 if (image->debug != MagickFalse)
1782 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1783 assert(exception != (ExceptionInfo *) NULL);
1784 assert(exception->signature == MagickSignature);
1785 width=GetOptimalKernelWidth1D(radius,0.5);
1786 kernel_info=AcquireKernelInfo((const char *) NULL);
1787 if (kernel_info == (KernelInfo *) NULL)
1788 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1789 kernel_info->width=width;
1790 kernel_info->height=width;
1791 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1792 kernel_info->width*sizeof(*kernel_info->values));
1793 if (kernel_info->values == (double *) NULL)
1795 kernel_info=DestroyKernelInfo(kernel_info);
1796 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1798 j=(ssize_t) kernel_info->width/2;
1800 for (v=(-j); v <= j; v++)
1802 for (u=(-j); u <= j; u++)
1804 kernel_info->values[i]=(-1.0);
1808 kernel_info->values[i/2]=(double) (width*width-1.0);
1809 kernel_info->bias=image->bias;
1810 edge_image=ConvolveImage(image,kernel_info,exception);
1811 kernel_info=DestroyKernelInfo(kernel_info);
1816 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1820 % E m b o s s I m a g e %
1824 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1826 % EmbossImage() returns a grayscale image with a three-dimensional effect.
1827 % We convolve the image with a Gaussian operator of the given radius and
1828 % standard deviation (sigma). For reasonable results, radius should be
1829 % larger than sigma. Use a radius of 0 and Emboss() selects a suitable
1832 % The format of the EmbossImage method is:
1834 % Image *EmbossImage(const Image *image,const double radius,
1835 % const double sigma,ExceptionInfo *exception)
1837 % A description of each parameter follows:
1839 % o image: the image.
1841 % o radius: the radius of the pixel neighborhood.
1843 % o sigma: the standard deviation of the Gaussian, in pixels.
1845 % o exception: return any errors or warnings in this structure.
1848 MagickExport Image *EmbossImage(const Image *image,const double radius,
1849 const double sigma,ExceptionInfo *exception)
1869 assert(image != (const Image *) NULL);
1870 assert(image->signature == MagickSignature);
1871 if (image->debug != MagickFalse)
1872 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1873 assert(exception != (ExceptionInfo *) NULL);
1874 assert(exception->signature == MagickSignature);
1875 width=GetOptimalKernelWidth2D(radius,sigma);
1876 kernel_info=AcquireKernelInfo((const char *) NULL);
1877 if (kernel_info == (KernelInfo *) NULL)
1878 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1879 kernel_info->width=width;
1880 kernel_info->height=width;
1881 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1882 kernel_info->width*sizeof(*kernel_info->values));
1883 if (kernel_info->values == (double *) NULL)
1885 kernel_info=DestroyKernelInfo(kernel_info);
1886 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1888 j=(ssize_t) kernel_info->width/2;
1891 for (v=(-j); v <= j; v++)
1893 for (u=(-j); u <= j; u++)
1895 kernel_info->values[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*
1896 exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
1897 (2.0*MagickPI*MagickSigma*MagickSigma));
1899 kernel_info->values[i]=0.0;
1904 kernel_info->bias=image->bias;
1905 emboss_image=ConvolveImage(image,kernel_info,exception);
1906 kernel_info=DestroyKernelInfo(kernel_info);
1907 if (emboss_image != (Image *) NULL)
1908 (void) EqualizeImage(emboss_image,exception);
1909 return(emboss_image);
1913 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1917 % G a u s s i a n B l u r I m a g e %
1921 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1923 % GaussianBlurImage() blurs an image. We convolve the image with a
1924 % Gaussian operator of the given radius and standard deviation (sigma).
1925 % For reasonable results, the radius should be larger than sigma. Use a
1926 % radius of 0 and GaussianBlurImage() selects a suitable radius for you
1928 % The format of the GaussianBlurImage method is:
1930 % Image *GaussianBlurImage(const Image *image,onst double radius,
1931 % const double sigma,ExceptionInfo *exception)
1933 % A description of each parameter follows:
1935 % o image: the image.
1937 % o radius: the radius of the Gaussian, in pixels, not counting the center
1940 % o sigma: the standard deviation of the Gaussian, in pixels.
1942 % o exception: return any errors or warnings in this structure.
1945 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
1946 const double sigma,ExceptionInfo *exception)
1965 assert(image != (const Image *) NULL);
1966 assert(image->signature == MagickSignature);
1967 if (image->debug != MagickFalse)
1968 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1969 assert(exception != (ExceptionInfo *) NULL);
1970 assert(exception->signature == MagickSignature);
1971 width=GetOptimalKernelWidth2D(radius,sigma);
1972 kernel_info=AcquireKernelInfo((const char *) NULL);
1973 if (kernel_info == (KernelInfo *) NULL)
1974 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1975 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
1976 kernel_info->width=width;
1977 kernel_info->height=width;
1978 kernel_info->signature=MagickSignature;
1979 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1980 kernel_info->width*sizeof(*kernel_info->values));
1981 if (kernel_info->values == (double *) NULL)
1983 kernel_info=DestroyKernelInfo(kernel_info);
1984 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1986 j=(ssize_t) kernel_info->width/2;
1988 for (v=(-j); v <= j; v++)
1990 for (u=(-j); u <= j; u++)
1992 kernel_info->values[i]=(double) (exp(-((double) u*u+v*v)/(2.0*
1993 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
1997 kernel_info->bias=image->bias;
1998 blur_image=ConvolveImage(image,kernel_info,exception);
1999 kernel_info=DestroyKernelInfo(kernel_info);
2004 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2008 % M o t i o n B l u r I m a g e %
2012 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2014 % MotionBlurImage() simulates motion blur. We convolve the image with a
2015 % Gaussian operator of the given radius and standard deviation (sigma).
2016 % For reasonable results, radius should be larger than sigma. Use a
2017 % radius of 0 and MotionBlurImage() selects a suitable radius for you.
2018 % Angle gives the angle of the blurring motion.
2020 % Andrew Protano contributed this effect.
2022 % The format of the MotionBlurImage method is:
2024 % Image *MotionBlurImage(const Image *image,const double radius,
2025 % const double sigma,const double angle,ExceptionInfo *exception)
2027 % A description of each parameter follows:
2029 % o image: the image.
2031 % o radius: the radius of the Gaussian, in pixels, not counting
2034 % o sigma: the standard deviation of the Gaussian, in pixels.
2036 % o angle: Apply the effect along this angle.
2038 % o exception: return any errors or warnings in this structure.
2042 static double *GetMotionBlurKernel(const size_t width,const double sigma)
2052 Generate a 1-D convolution kernel.
2054 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2055 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
2056 if (kernel == (double *) NULL)
2059 for (i=0; i < (ssize_t) width; i++)
2061 kernel[i]=(double) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
2062 MagickSigma)))/(MagickSQ2PI*MagickSigma));
2063 normalize+=kernel[i];
2065 for (i=0; i < (ssize_t) width; i++)
2066 kernel[i]/=normalize;
2070 MagickExport Image *MotionBlurImage(const Image *image,
2071 const double radius,const double sigma,const double angle,
2072 ExceptionInfo *exception)
2108 assert(image != (Image *) NULL);
2109 assert(image->signature == MagickSignature);
2110 if (image->debug != MagickFalse)
2111 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2112 assert(exception != (ExceptionInfo *) NULL);
2113 width=GetOptimalKernelWidth1D(radius,sigma);
2114 kernel=GetMotionBlurKernel(width,sigma);
2115 if (kernel == (double *) NULL)
2116 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2117 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2118 if (offset == (OffsetInfo *) NULL)
2120 kernel=(double *) RelinquishMagickMemory(kernel);
2121 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2123 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2124 if (blur_image == (Image *) NULL)
2126 kernel=(double *) RelinquishMagickMemory(kernel);
2127 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2128 return((Image *) NULL);
2130 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2132 kernel=(double *) RelinquishMagickMemory(kernel);
2133 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2134 blur_image=DestroyImage(blur_image);
2135 return((Image *) NULL);
2137 point.x=(double) width*sin(DegreesToRadians(angle));
2138 point.y=(double) width*cos(DegreesToRadians(angle));
2139 for (i=0; i < (ssize_t) width; i++)
2141 offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
2142 offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
2149 GetPixelInfo(image,&bias);
2150 image_view=AcquireCacheView(image);
2151 blur_view=AcquireCacheView(blur_image);
2152 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2153 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
2155 for (y=0; y < (ssize_t) image->rows; y++)
2163 if (status == MagickFalse)
2165 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2167 if (q == (const Quantum *) NULL)
2172 for (x=0; x < (ssize_t) image->columns; x++)
2188 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
2190 for (i=0; i < (ssize_t) width; i++)
2192 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2193 offset[i].y,&pixel,exception);
2194 qixel.red+=(*k)*pixel.red;
2195 qixel.green+=(*k)*pixel.green;
2196 qixel.blue+=(*k)*pixel.blue;
2197 qixel.alpha+=(*k)*pixel.alpha;
2198 if (image->colorspace == CMYKColorspace)
2199 qixel.black+=(*k)*pixel.black;
2202 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2203 SetPixelRed(blur_image,
2204 ClampToQuantum(qixel.red),q);
2205 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2206 SetPixelGreen(blur_image,
2207 ClampToQuantum(qixel.green),q);
2208 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2209 SetPixelBlue(blur_image,
2210 ClampToQuantum(qixel.blue),q);
2211 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2212 (image->colorspace == CMYKColorspace))
2213 SetPixelBlack(blur_image,
2214 ClampToQuantum(qixel.black),q);
2215 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2216 SetPixelAlpha(blur_image,
2217 ClampToQuantum(qixel.alpha),q);
2227 for (i=0; i < (ssize_t) width; i++)
2229 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2230 offset[i].y,&pixel,exception);
2231 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
2232 qixel.red+=(*k)*alpha*pixel.red;
2233 qixel.green+=(*k)*alpha*pixel.green;
2234 qixel.blue+=(*k)*alpha*pixel.blue;
2235 qixel.alpha+=(*k)*pixel.alpha;
2236 if (image->colorspace == CMYKColorspace)
2237 qixel.black+=(*k)*alpha*pixel.black;
2241 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2242 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2243 SetPixelRed(blur_image,
2244 ClampToQuantum(gamma*qixel.red),q);
2245 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2246 SetPixelGreen(blur_image,
2247 ClampToQuantum(gamma*qixel.green),q);
2248 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2249 SetPixelBlue(blur_image,
2250 ClampToQuantum(gamma*qixel.blue),q);
2251 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2252 (image->colorspace == CMYKColorspace))
2253 SetPixelBlack(blur_image,
2254 ClampToQuantum(gamma*qixel.black),q);
2255 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2256 SetPixelAlpha(blur_image,
2257 ClampToQuantum(qixel.alpha),q);
2259 q+=GetPixelChannels(blur_image);
2261 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2263 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2268 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2269 #pragma omp critical (MagickCore_MotionBlurImage)
2271 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2272 if (proceed == MagickFalse)
2276 blur_view=DestroyCacheView(blur_view);
2277 image_view=DestroyCacheView(image_view);
2278 kernel=(double *) RelinquishMagickMemory(kernel);
2279 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2280 if (status == MagickFalse)
2281 blur_image=DestroyImage(blur_image);
2286 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2290 % P r e v i e w I m a g e %
2294 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2296 % PreviewImage() tiles 9 thumbnails of the specified image with an image
2297 % processing operation applied with varying parameters. This may be helpful
2298 % pin-pointing an appropriate parameter for a particular image processing
2301 % The format of the PreviewImages method is:
2303 % Image *PreviewImages(const Image *image,const PreviewType preview,
2304 % ExceptionInfo *exception)
2306 % A description of each parameter follows:
2308 % o image: the image.
2310 % o preview: the image processing operation.
2312 % o exception: return any errors or warnings in this structure.
2315 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
2316 ExceptionInfo *exception)
2318 #define NumberTiles 9
2319 #define PreviewImageTag "Preview/Image"
2320 #define DefaultPreviewGeometry "204x204+10+10"
2323 factor[MaxTextExtent],
2324 label[MaxTextExtent];
2366 Open output image file.
2368 assert(image != (Image *) NULL);
2369 assert(image->signature == MagickSignature);
2370 if (image->debug != MagickFalse)
2371 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2375 preview_info=AcquireImageInfo();
2376 SetGeometry(image,&geometry);
2377 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2378 &geometry.width,&geometry.height);
2379 images=NewImageList();
2381 GetQuantizeInfo(&quantize_info);
2387 for (i=0; i < NumberTiles; i++)
2389 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2390 if (thumbnail == (Image *) NULL)
2392 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2394 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
2395 if (i == (NumberTiles/2))
2397 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
2398 AppendImageToList(&images,thumbnail);
2406 preview_image=RotateImage(thumbnail,degrees,exception);
2407 (void) FormatLocaleString(label,MaxTextExtent,"rotate %g",degrees);
2413 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
2414 (void) FormatLocaleString(label,MaxTextExtent,"shear %gx%g",
2415 degrees,2.0*degrees);
2420 x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
2421 y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
2422 preview_image=RollImage(thumbnail,x,y,exception);
2423 (void) FormatLocaleString(label,MaxTextExtent,"roll %+.20gx%+.20g",
2424 (double) x,(double) y);
2429 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2430 if (preview_image == (Image *) NULL)
2432 (void) FormatLocaleString(factor,MaxTextExtent,"100,100,%g",
2434 (void) ModulateImage(preview_image,factor,exception);
2435 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
2438 case SaturationPreview:
2440 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2441 if (preview_image == (Image *) NULL)
2443 (void) FormatLocaleString(factor,MaxTextExtent,"100,%g",
2445 (void) ModulateImage(preview_image,factor,exception);
2446 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
2449 case BrightnessPreview:
2451 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2452 if (preview_image == (Image *) NULL)
2454 (void) FormatLocaleString(factor,MaxTextExtent,"%g",2.0*percentage);
2455 (void) ModulateImage(preview_image,factor,exception);
2456 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
2462 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2463 if (preview_image == (Image *) NULL)
2466 (void) GammaImage(preview_image,gamma,exception);
2467 (void) FormatLocaleString(label,MaxTextExtent,"gamma %g",gamma);
2472 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2473 if (preview_image != (Image *) NULL)
2474 for (x=0; x < i; x++)
2475 (void) ContrastImage(preview_image,MagickTrue,exception);
2476 (void) FormatLocaleString(label,MaxTextExtent,"contrast (%.20g)",
2482 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2483 if (preview_image == (Image *) NULL)
2485 for (x=0; x < i; x++)
2486 (void) ContrastImage(preview_image,MagickFalse,exception);
2487 (void) FormatLocaleString(label,MaxTextExtent,"+contrast (%.20g)",
2491 case GrayscalePreview:
2493 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2494 if (preview_image == (Image *) NULL)
2497 quantize_info.number_colors=colors;
2498 quantize_info.colorspace=GRAYColorspace;
2499 (void) QuantizeImage(&quantize_info,preview_image);
2500 (void) FormatLocaleString(label,MaxTextExtent,
2501 "-colorspace gray -colors %.20g",(double) colors);
2504 case QuantizePreview:
2506 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2507 if (preview_image == (Image *) NULL)
2510 quantize_info.number_colors=colors;
2511 (void) QuantizeImage(&quantize_info,preview_image);
2512 (void) FormatLocaleString(label,MaxTextExtent,"colors %.20g",(double)
2516 case DespecklePreview:
2518 for (x=0; x < (i-1); x++)
2520 preview_image=DespeckleImage(thumbnail,exception);
2521 if (preview_image == (Image *) NULL)
2523 thumbnail=DestroyImage(thumbnail);
2524 thumbnail=preview_image;
2526 preview_image=DespeckleImage(thumbnail,exception);
2527 if (preview_image == (Image *) NULL)
2529 (void) FormatLocaleString(label,MaxTextExtent,"despeckle (%.20g)",
2533 case ReduceNoisePreview:
2535 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
2536 (size_t) radius,exception);
2537 (void) FormatLocaleString(label,MaxTextExtent,"noise %g",radius);
2540 case AddNoisePreview:
2546 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
2551 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
2556 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
2561 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
2566 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
2571 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
2576 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
2580 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
2581 (size_t) i,exception);
2582 (void) FormatLocaleString(label,MaxTextExtent,"+noise %s",factor);
2585 case SharpenPreview:
2587 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
2588 (void) FormatLocaleString(label,MaxTextExtent,"sharpen %gx%g",
2594 preview_image=BlurImage(thumbnail,radius,sigma,exception);
2595 (void) FormatLocaleString(label,MaxTextExtent,"blur %gx%g",radius,
2599 case ThresholdPreview:
2601 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2602 if (preview_image == (Image *) NULL)
2604 (void) BilevelImage(thumbnail,
2605 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2606 (void) FormatLocaleString(label,MaxTextExtent,"threshold %g",
2607 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2610 case EdgeDetectPreview:
2612 preview_image=EdgeImage(thumbnail,radius,exception);
2613 (void) FormatLocaleString(label,MaxTextExtent,"edge %g",radius);
2618 preview_image=SpreadImage(thumbnail,radius,exception);
2619 (void) FormatLocaleString(label,MaxTextExtent,"spread %g",
2623 case SolarizePreview:
2625 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2626 if (preview_image == (Image *) NULL)
2628 (void) SolarizeImage(preview_image,(double) QuantumRange*
2630 (void) FormatLocaleString(label,MaxTextExtent,"solarize %g",
2631 (QuantumRange*percentage)/100.0);
2637 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2639 (void) FormatLocaleString(label,MaxTextExtent,"shade %gx%g",
2645 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2646 if (preview_image == (Image *) NULL)
2648 geometry.width=(size_t) (2*i+2);
2649 geometry.height=(size_t) (2*i+2);
2652 (void) RaiseImage(preview_image,&geometry,MagickTrue,exception);
2653 (void) FormatLocaleString(label,MaxTextExtent,
2654 "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
2655 geometry.height,(double) geometry.x,(double) geometry.y);
2658 case SegmentPreview:
2660 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2661 if (preview_image == (Image *) NULL)
2664 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
2666 (void) FormatLocaleString(label,MaxTextExtent,"segment %gx%g",
2667 threshold,threshold);
2672 preview_image=SwirlImage(thumbnail,degrees,exception);
2673 (void) FormatLocaleString(label,MaxTextExtent,"swirl %g",degrees);
2677 case ImplodePreview:
2680 preview_image=ImplodeImage(thumbnail,degrees,exception);
2681 (void) FormatLocaleString(label,MaxTextExtent,"implode %g",degrees);
2687 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
2688 (void) FormatLocaleString(label,MaxTextExtent,"wave %gx%g",
2689 0.5*degrees,2.0*degrees);
2692 case OilPaintPreview:
2694 preview_image=OilPaintImage(thumbnail,(double) radius,(double) sigma,
2696 (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
2700 case CharcoalDrawingPreview:
2702 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2704 (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
2711 filename[MaxTextExtent];
2719 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2720 if (preview_image == (Image *) NULL)
2722 preview_info->quality=(size_t) percentage;
2723 (void) FormatLocaleString(factor,MaxTextExtent,"%.20g",(double)
2724 preview_info->quality);
2725 file=AcquireUniqueFileResource(filename);
2728 (void) FormatLocaleString(preview_image->filename,MaxTextExtent,
2729 "jpeg:%s",filename);
2730 status=WriteImage(preview_info,preview_image);
2731 if (status != MagickFalse)
2736 (void) CopyMagickString(preview_info->filename,
2737 preview_image->filename,MaxTextExtent);
2738 quality_image=ReadImage(preview_info,exception);
2739 if (quality_image != (Image *) NULL)
2741 preview_image=DestroyImage(preview_image);
2742 preview_image=quality_image;
2745 (void) RelinquishUniqueFileResource(preview_image->filename);
2746 if ((GetBlobSize(preview_image)/1024) >= 1024)
2747 (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%gmb ",
2748 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2751 if (GetBlobSize(preview_image) >= 1024)
2752 (void) FormatLocaleString(label,MaxTextExtent,
2753 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
2754 GetBlobSize(preview_image))/1024.0);
2756 (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%.20gb ",
2757 factor,(double) ((MagickOffsetType) GetBlobSize(thumbnail)));
2761 thumbnail=DestroyImage(thumbnail);
2765 if (preview_image == (Image *) NULL)
2767 (void) DeleteImageProperty(preview_image,"label");
2768 (void) SetImageProperty(preview_image,"label",label);
2769 AppendImageToList(&images,preview_image);
2770 proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2772 if (proceed == MagickFalse)
2775 if (images == (Image *) NULL)
2777 preview_info=DestroyImageInfo(preview_info);
2778 return((Image *) NULL);
2783 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2784 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
2785 montage_info->shadow=MagickTrue;
2786 (void) CloneString(&montage_info->tile,"3x3");
2787 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2788 (void) CloneString(&montage_info->frame,DefaultTileFrame);
2789 montage_image=MontageImages(images,montage_info,exception);
2790 montage_info=DestroyMontageInfo(montage_info);
2791 images=DestroyImageList(images);
2792 if (montage_image == (Image *) NULL)
2793 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2794 if (montage_image->montage != (char *) NULL)
2797 Free image directory.
2799 montage_image->montage=(char *) RelinquishMagickMemory(
2800 montage_image->montage);
2801 if (image->directory != (char *) NULL)
2802 montage_image->directory=(char *) RelinquishMagickMemory(
2803 montage_image->directory);
2805 preview_info=DestroyImageInfo(preview_info);
2806 return(montage_image);
2810 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2814 % R a d i a l B l u r I m a g e %
2818 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2820 % RadialBlurImage() applies a radial blur to the image.
2822 % Andrew Protano contributed this effect.
2824 % The format of the RadialBlurImage method is:
2826 % Image *RadialBlurImage(const Image *image,const double angle,
2827 % ExceptionInfo *exception)
2829 % A description of each parameter follows:
2831 % o image: the image.
2833 % o angle: the angle of the radial blur.
2835 % o exception: return any errors or warnings in this structure.
2838 MagickExport Image *RadialBlurImage(const Image *image,
2839 const double angle,ExceptionInfo *exception)
2877 Allocate blur image.
2879 assert(image != (Image *) NULL);
2880 assert(image->signature == MagickSignature);
2881 if (image->debug != MagickFalse)
2882 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2883 assert(exception != (ExceptionInfo *) NULL);
2884 assert(exception->signature == MagickSignature);
2885 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2886 if (blur_image == (Image *) NULL)
2887 return((Image *) NULL);
2888 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2890 blur_image=DestroyImage(blur_image);
2891 return((Image *) NULL);
2893 blur_center.x=(double) image->columns/2.0;
2894 blur_center.y=(double) image->rows/2.0;
2895 blur_radius=hypot(blur_center.x,blur_center.y);
2896 n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
2897 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
2898 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2899 sizeof(*cos_theta));
2900 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2901 sizeof(*sin_theta));
2902 if ((cos_theta == (MagickRealType *) NULL) ||
2903 (sin_theta == (MagickRealType *) NULL))
2905 blur_image=DestroyImage(blur_image);
2906 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2908 offset=theta*(MagickRealType) (n-1)/2.0;
2909 for (i=0; i < (ssize_t) n; i++)
2911 cos_theta[i]=cos((double) (theta*i-offset));
2912 sin_theta[i]=sin((double) (theta*i-offset));
2919 GetPixelInfo(image,&bias);
2920 image_view=AcquireCacheView(image);
2921 blur_view=AcquireCacheView(blur_image);
2922 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2923 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2925 for (y=0; y < (ssize_t) blur_image->rows; y++)
2933 if (status == MagickFalse)
2935 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2937 if (q == (const Quantum *) NULL)
2942 for (x=0; x < (ssize_t) blur_image->columns; x++)
2963 center.x=(double) x-blur_center.x;
2964 center.y=(double) y-blur_center.y;
2965 radius=hypot((double) center.x,center.y);
2970 step=(size_t) (blur_radius/radius);
2979 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
2981 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
2983 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
2984 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
2985 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
2986 cos_theta[i]+0.5),&pixel,exception);
2987 qixel.red+=pixel.red;
2988 qixel.green+=pixel.green;
2989 qixel.blue+=pixel.blue;
2990 if (image->colorspace == CMYKColorspace)
2991 qixel.black+=pixel.black;
2992 qixel.alpha+=pixel.alpha;
2995 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
2997 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2998 SetPixelRed(blur_image,
2999 ClampToQuantum(normalize*qixel.red),q);
3000 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3001 SetPixelGreen(blur_image,
3002 ClampToQuantum(normalize*qixel.green),q);
3003 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3004 SetPixelBlue(blur_image,
3005 ClampToQuantum(normalize*qixel.blue),q);
3006 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3007 (image->colorspace == CMYKColorspace))
3008 SetPixelBlack(blur_image,
3009 ClampToQuantum(normalize*qixel.black),q);
3010 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3011 SetPixelAlpha(blur_image,
3012 ClampToQuantum(normalize*qixel.alpha),q);
3022 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
3024 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3025 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
3026 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
3027 cos_theta[i]+0.5),&pixel,exception);
3028 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
3029 qixel.red+=alpha*pixel.red;
3030 qixel.green+=alpha*pixel.green;
3031 qixel.blue+=alpha*pixel.blue;
3032 qixel.alpha+=pixel.alpha;
3033 if (image->colorspace == CMYKColorspace)
3034 qixel.black+=alpha*pixel.black;
3038 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3039 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3041 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3042 SetPixelRed(blur_image,
3043 ClampToQuantum(gamma*qixel.red),q);
3044 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3045 SetPixelGreen(blur_image,
3046 ClampToQuantum(gamma*qixel.green),q);
3047 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3048 SetPixelBlue(blur_image,
3049 ClampToQuantum(gamma*qixel.blue),q);
3050 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3051 (image->colorspace == CMYKColorspace))
3052 SetPixelBlack(blur_image,
3053 ClampToQuantum(gamma*qixel.black),q);
3054 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3055 SetPixelAlpha(blur_image,
3056 ClampToQuantum(normalize*qixel.alpha),q);
3058 q+=GetPixelChannels(blur_image);
3060 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3062 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3067 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3068 #pragma omp critical (MagickCore_RadialBlurImage)
3070 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3071 if (proceed == MagickFalse)
3075 blur_view=DestroyCacheView(blur_view);
3076 image_view=DestroyCacheView(image_view);
3077 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
3078 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
3079 if (status == MagickFalse)
3080 blur_image=DestroyImage(blur_image);
3085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3089 % S e l e c t i v e B l u r I m a g e %
3093 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3095 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3096 % It is similar to the unsharpen mask that sharpens everything with contrast
3097 % above a certain threshold.
3099 % The format of the SelectiveBlurImage method is:
3101 % Image *SelectiveBlurImage(const Image *image,const double radius,
3102 % const double sigma,const double threshold,ExceptionInfo *exception)
3104 % A description of each parameter follows:
3106 % o image: the image.
3108 % o radius: the radius of the Gaussian, in pixels, not counting the center
3111 % o sigma: the standard deviation of the Gaussian, in pixels.
3113 % o threshold: only pixels within this contrast threshold are included
3114 % in the blur operation.
3116 % o exception: return any errors or warnings in this structure.
3119 MagickExport Image *SelectiveBlurImage(const Image *image,
3120 const double radius,const double sigma,const double threshold,
3121 ExceptionInfo *exception)
3123 #define SelectiveBlurImageTag "SelectiveBlur/Image"
3157 Initialize blur image attributes.
3159 assert(image != (Image *) NULL);
3160 assert(image->signature == MagickSignature);
3161 if (image->debug != MagickFalse)
3162 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3163 assert(exception != (ExceptionInfo *) NULL);
3164 assert(exception->signature == MagickSignature);
3165 width=GetOptimalKernelWidth1D(radius,sigma);
3166 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
3167 if (kernel == (double *) NULL)
3168 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3169 j=(ssize_t) width/2;
3171 for (v=(-j); v <= j; v++)
3173 for (u=(-j); u <= j; u++)
3174 kernel[i++]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
3175 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3177 if (image->debug != MagickFalse)
3180 format[MaxTextExtent],
3183 register const double
3190 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
3191 " SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
3193 message=AcquireString("");
3195 for (v=0; v < (ssize_t) width; v++)
3198 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
3199 (void) ConcatenateString(&message,format);
3200 for (u=0; u < (ssize_t) width; u++)
3202 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",*k++);
3203 (void) ConcatenateString(&message,format);
3205 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3207 message=DestroyString(message);
3209 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3210 if (blur_image == (Image *) NULL)
3211 return((Image *) NULL);
3212 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
3214 blur_image=DestroyImage(blur_image);
3215 return((Image *) NULL);
3218 Threshold blur image.
3222 GetPixelInfo(image,&bias);
3223 SetPixelInfoBias(image,&bias);
3224 image_view=AcquireCacheView(image);
3225 blur_view=AcquireCacheView(blur_image);
3226 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3227 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3229 for (y=0; y < (ssize_t) image->rows; y++)
3240 register const Quantum
3249 if (status == MagickFalse)
3251 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
3252 (width/2L),image->columns+width,width,exception);
3253 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3255 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3260 for (x=0; x < (ssize_t) image->columns; x++)
3265 register const double
3279 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
3281 for (v=0; v < (ssize_t) width; v++)
3283 for (u=0; u < (ssize_t) width; u++)
3285 contrast=GetPixelIntensity(image,p+(u+j)*GetPixelChannels(image))-
3286 (double) GetPixelIntensity(blur_image,q);
3287 if (fabs(contrast) < threshold)
3290 GetPixelRed(image,p+(u+j)*GetPixelChannels(image));
3292 GetPixelGreen(image,p+(u+j)*GetPixelChannels(image));
3294 GetPixelBlue(image,p+(u+j)*GetPixelChannels(image));
3295 if (image->colorspace == CMYKColorspace)
3297 GetPixelBlack(image,p+(u+j)*GetPixelChannels(image));
3302 j+=(ssize_t) (image->columns+width);
3306 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3307 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3308 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
3309 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3310 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
3311 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3312 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
3313 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3314 (image->colorspace == CMYKColorspace))
3315 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
3317 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3321 for (v=0; v < (ssize_t) width; v++)
3323 for (u=0; u < (ssize_t) width; u++)
3325 contrast=GetPixelIntensity(image,p+(u+j)*
3326 GetPixelChannels(image))-(double)
3327 GetPixelIntensity(blur_image,q);
3328 if (fabs(contrast) < threshold)
3331 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image));
3336 j+=(ssize_t) (image->columns+width);
3340 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3342 SetPixelAlpha(blur_image,ClampToQuantum(gamma*pixel.alpha),q);
3351 for (v=0; v < (ssize_t) width; v++)
3353 for (u=0; u < (ssize_t) width; u++)
3355 contrast=GetPixelIntensity(image,p+(u+j)*
3356 GetPixelChannels(image))-(double)
3357 GetPixelIntensity(blur_image,q);
3358 if (fabs(contrast) < threshold)
3360 alpha=(MagickRealType) (QuantumScale*
3361 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image)));
3362 pixel.red+=(*k)*alpha*
3363 GetPixelRed(image,p+(u+j)*GetPixelChannels(image));
3364 pixel.green+=(*k)*alpha*GetPixelGreen(image,p+(u+j)*
3365 GetPixelChannels(image));
3366 pixel.blue+=(*k)*alpha*GetPixelBlue(image,p+(u+j)*
3367 GetPixelChannels(image));
3368 pixel.alpha+=(*k)*GetPixelAlpha(image,p+(u+j)*
3369 GetPixelChannels(image));
3370 if (image->colorspace == CMYKColorspace)
3371 pixel.black+=(*k)*GetPixelBlack(image,p+(u+j)*
3372 GetPixelChannels(image));
3377 j+=(ssize_t) (image->columns+width);
3381 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3382 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3383 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
3384 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3385 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
3386 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3387 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
3388 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3389 (image->colorspace == CMYKColorspace))
3390 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
3392 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3396 for (v=0; v < (ssize_t) width; v++)
3398 for (u=0; u < (ssize_t) width; u++)
3400 contrast=GetPixelIntensity(image,p+(u+j)*
3401 GetPixelChannels(image))-(double)
3402 GetPixelIntensity(blur_image,q);
3403 if (fabs(contrast) < threshold)
3406 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image));
3411 j+=(ssize_t) (image->columns+width);
3415 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3417 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
3421 p+=GetPixelChannels(image);
3422 q+=GetPixelChannels(blur_image);
3424 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3425 if (sync == MagickFalse)
3427 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3432 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3433 #pragma omp critical (MagickCore_SelectiveBlurImage)
3435 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
3437 if (proceed == MagickFalse)
3441 blur_image->type=image->type;
3442 blur_view=DestroyCacheView(blur_view);
3443 image_view=DestroyCacheView(image_view);
3444 kernel=(double *) RelinquishMagickMemory(kernel);
3445 if (status == MagickFalse)
3446 blur_image=DestroyImage(blur_image);
3451 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3455 % S h a d e I m a g e %
3459 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3461 % ShadeImage() shines a distant light on an image to create a
3462 % three-dimensional effect. You control the positioning of the light with
3463 % azimuth and elevation; azimuth is measured in degrees off the x axis
3464 % and elevation is measured in pixels above the Z axis.
3466 % The format of the ShadeImage method is:
3468 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3469 % const double azimuth,const double elevation,ExceptionInfo *exception)
3471 % A description of each parameter follows:
3473 % o image: the image.
3475 % o gray: A value other than zero shades the intensity of each pixel.
3477 % o azimuth, elevation: Define the light source direction.
3479 % o exception: return any errors or warnings in this structure.
3482 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3483 const double azimuth,const double elevation,ExceptionInfo *exception)
3485 #define ShadeImageTag "Shade/Image"
3507 Initialize shaded image attributes.
3509 assert(image != (const Image *) NULL);
3510 assert(image->signature == MagickSignature);
3511 if (image->debug != MagickFalse)
3512 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3513 assert(exception != (ExceptionInfo *) NULL);
3514 assert(exception->signature == MagickSignature);
3515 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3516 if (shade_image == (Image *) NULL)
3517 return((Image *) NULL);
3518 if (SetImageStorageClass(shade_image,DirectClass,exception) == MagickFalse)
3520 shade_image=DestroyImage(shade_image);
3521 return((Image *) NULL);
3524 Compute the light vector.
3526 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3527 cos(DegreesToRadians(elevation));
3528 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3529 cos(DegreesToRadians(elevation));
3530 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3536 image_view=AcquireCacheView(image);
3537 shade_view=AcquireCacheView(shade_image);
3538 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3539 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3541 for (y=0; y < (ssize_t) image->rows; y++)
3551 register const Quantum
3563 if (status == MagickFalse)
3565 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
3566 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3568 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3574 Shade this row of pixels.
3576 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
3577 s0=p+GetPixelChannels(image);
3578 s1=s0+(image->columns+2)*GetPixelChannels(image);
3579 s2=s1+(image->columns+2)*GetPixelChannels(image);
3580 for (x=0; x < (ssize_t) image->columns; x++)
3583 Determine the surface normal and compute shading.
3585 normal.x=(double) (GetPixelIntensity(image,s0-GetPixelChannels(image))+
3586 GetPixelIntensity(image,s1-GetPixelChannels(image))+
3587 GetPixelIntensity(image,s2-GetPixelChannels(image))-
3588 GetPixelIntensity(image,s0+GetPixelChannels(image))-
3589 GetPixelIntensity(image,s1+GetPixelChannels(image))-
3590 GetPixelIntensity(image,s2+GetPixelChannels(image)));
3591 normal.y=(double) (GetPixelIntensity(image,s2-GetPixelChannels(image))+
3592 GetPixelIntensity(image,s2)+
3593 GetPixelIntensity(image,s2+GetPixelChannels(image))-
3594 GetPixelIntensity(image,s0-GetPixelChannels(image))-
3595 GetPixelIntensity(image,s0)-
3596 GetPixelIntensity(image,s0+GetPixelChannels(image)));
3597 if ((normal.x == 0.0) && (normal.y == 0.0))
3602 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3603 if (distance > MagickEpsilon)
3606 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
3607 if (normal_distance > (MagickEpsilon*MagickEpsilon))
3608 shade=distance/sqrt((double) normal_distance);
3611 if (gray != MagickFalse)
3613 SetPixelRed(shade_image,ClampToQuantum(shade),q);
3614 SetPixelGreen(shade_image,ClampToQuantum(shade),q);
3615 SetPixelBlue(shade_image,ClampToQuantum(shade),q);
3619 SetPixelRed(shade_image,ClampToQuantum(QuantumScale*shade*
3620 GetPixelRed(image,s1)),q);
3621 SetPixelGreen(shade_image,ClampToQuantum(QuantumScale*shade*
3622 GetPixelGreen(image,s1)),q);
3623 SetPixelBlue(shade_image,ClampToQuantum(QuantumScale*shade*
3624 GetPixelBlue(image,s1)),q);
3626 SetPixelAlpha(shade_image,GetPixelAlpha(image,s1),q);
3627 s0+=GetPixelChannels(image);
3628 s1+=GetPixelChannels(image);
3629 s2+=GetPixelChannels(image);
3630 q+=GetPixelChannels(shade_image);
3632 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3634 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3639 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3640 #pragma omp critical (MagickCore_ShadeImage)
3642 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
3643 if (proceed == MagickFalse)
3647 shade_view=DestroyCacheView(shade_view);
3648 image_view=DestroyCacheView(image_view);
3649 if (status == MagickFalse)
3650 shade_image=DestroyImage(shade_image);
3651 return(shade_image);
3655 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3659 % S h a r p e n I m a g e %
3663 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3665 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
3666 % operator of the given radius and standard deviation (sigma). For
3667 % reasonable results, radius should be larger than sigma. Use a radius of 0
3668 % and SharpenImage() selects a suitable radius for you.
3670 % Using a separable kernel would be faster, but the negative weights cancel
3671 % out on the corners of the kernel producing often undesirable ringing in the
3672 % filtered result; this can be avoided by using a 2D gaussian shaped image
3673 % sharpening kernel instead.
3675 % The format of the SharpenImage method is:
3677 % Image *SharpenImage(const Image *image,const double radius,
3678 % const double sigma,ExceptionInfo *exception)
3680 % A description of each parameter follows:
3682 % o image: the image.
3684 % o radius: the radius of the Gaussian, in pixels, not counting the center
3687 % o sigma: the standard deviation of the Laplacian, in pixels.
3689 % o exception: return any errors or warnings in this structure.
3692 MagickExport Image *SharpenImage(const Image *image,const double radius,
3693 const double sigma,ExceptionInfo *exception)
3715 assert(image != (const Image *) NULL);
3716 assert(image->signature == MagickSignature);
3717 if (image->debug != MagickFalse)
3718 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3719 assert(exception != (ExceptionInfo *) NULL);
3720 assert(exception->signature == MagickSignature);
3721 width=GetOptimalKernelWidth2D(radius,sigma);
3722 kernel_info=AcquireKernelInfo((const char *) NULL);
3723 if (kernel_info == (KernelInfo *) NULL)
3724 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3725 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
3726 kernel_info->width=width;
3727 kernel_info->height=width;
3728 kernel_info->signature=MagickSignature;
3729 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
3730 kernel_info->width*sizeof(*kernel_info->values));
3731 if (kernel_info->values == (double *) NULL)
3733 kernel_info=DestroyKernelInfo(kernel_info);
3734 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3737 j=(ssize_t) kernel_info->width/2;
3739 for (v=(-j); v <= j; v++)
3741 for (u=(-j); u <= j; u++)
3743 kernel_info->values[i]=(double) (-exp(-((double) u*u+v*v)/(2.0*
3744 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3745 normalize+=kernel_info->values[i];
3749 kernel_info->values[i/2]=(double) ((-2.0)*normalize);
3750 kernel_info->bias=image->bias;
3751 sharp_image=ConvolveImage(image,kernel_info,exception);
3752 kernel_info=DestroyKernelInfo(kernel_info);
3753 return(sharp_image);
3757 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3761 % S p r e a d I m a g e %
3765 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3767 % SpreadImage() is a special effects method that randomly displaces each
3768 % pixel in a block defined by the radius parameter.
3770 % The format of the SpreadImage method is:
3772 % Image *SpreadImage(const Image *image,const double radius,
3773 % ExceptionInfo *exception)
3775 % A description of each parameter follows:
3777 % o image: the image.
3779 % o radius: Choose a random pixel in a neighborhood of this extent.
3781 % o exception: return any errors or warnings in this structure.
3784 MagickExport Image *SpreadImage(const Image *image,const double radius,
3785 ExceptionInfo *exception)
3787 #define SpreadImageTag "Spread/Image"
3806 **restrict random_info;
3815 Initialize spread image attributes.
3817 assert(image != (Image *) NULL);
3818 assert(image->signature == MagickSignature);
3819 if (image->debug != MagickFalse)
3820 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3821 assert(exception != (ExceptionInfo *) NULL);
3822 assert(exception->signature == MagickSignature);
3823 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3825 if (spread_image == (Image *) NULL)
3826 return((Image *) NULL);
3827 if (SetImageStorageClass(spread_image,DirectClass,exception) == MagickFalse)
3829 spread_image=DestroyImage(spread_image);
3830 return((Image *) NULL);
3837 GetPixelInfo(spread_image,&bias);
3838 width=GetOptimalKernelWidth1D(radius,0.5);
3839 random_info=AcquireRandomInfoThreadSet();
3840 image_view=AcquireCacheView(image);
3841 spread_view=AcquireCacheView(spread_image);
3842 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3843 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
3845 for (y=0; y < (ssize_t) spread_image->rows; y++)
3848 id = GetOpenMPThreadId();
3859 if (status == MagickFalse)
3861 q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
3863 if (q == (const Quantum *) NULL)
3869 for (x=0; x < (ssize_t) spread_image->columns; x++)
3871 (void) InterpolatePixelInfo(image,image_view,
3872 UndefinedInterpolatePixel,(double) x+width*(GetPseudoRandomValue(
3873 random_info[id])-0.5),(double) y+width*(GetPseudoRandomValue(
3874 random_info[id])-0.5),&pixel,exception);
3875 SetPixelPixelInfo(spread_image,&pixel,q);
3876 q+=GetPixelChannels(spread_image);
3878 if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
3880 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3885 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3886 #pragma omp critical (MagickCore_SpreadImage)
3888 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3889 if (proceed == MagickFalse)
3893 spread_view=DestroyCacheView(spread_view);
3894 image_view=DestroyCacheView(image_view);
3895 random_info=DestroyRandomInfoThreadSet(random_info);
3896 return(spread_image);
3900 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3904 % S t a t i s t i c I m a g e %
3908 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3910 % StatisticImage() makes each pixel the min / max / median / mode / etc. of
3911 % the neighborhood of the specified width and height.
3913 % The format of the StatisticImage method is:
3915 % Image *StatisticImage(const Image *image,const StatisticType type,
3916 % const size_t width,const size_t height,ExceptionInfo *exception)
3918 % A description of each parameter follows:
3920 % o image: the image.
3922 % o type: the statistic type (median, mode, etc.).
3924 % o width: the width of the pixel neighborhood.
3926 % o height: the height of the pixel neighborhood.
3928 % o exception: return any errors or warnings in this structure.
3932 #define ListChannels 5
3934 typedef struct _ListNode
3942 typedef struct _SkipList
3951 typedef struct _PixelList
3959 lists[ListChannels];
3962 static PixelList *DestroyPixelList(PixelList *pixel_list)
3967 if (pixel_list == (PixelList *) NULL)
3968 return((PixelList *) NULL);
3969 for (i=0; i < ListChannels; i++)
3970 if (pixel_list->lists[i].nodes != (ListNode *) NULL)
3971 pixel_list->lists[i].nodes=(ListNode *) RelinquishMagickMemory(
3972 pixel_list->lists[i].nodes);
3973 pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
3977 static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
3982 assert(pixel_list != (PixelList **) NULL);
3983 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
3984 if (pixel_list[i] != (PixelList *) NULL)
3985 pixel_list[i]=DestroyPixelList(pixel_list[i]);
3986 pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
3990 static PixelList *AcquirePixelList(const size_t width,const size_t height)
3998 pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
3999 if (pixel_list == (PixelList *) NULL)
4001 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
4002 pixel_list->length=width*height;
4003 for (i=0; i < ListChannels; i++)
4005 pixel_list->lists[i].nodes=(ListNode *) AcquireQuantumMemory(65537UL,
4006 sizeof(*pixel_list->lists[i].nodes));
4007 if (pixel_list->lists[i].nodes == (ListNode *) NULL)
4008 return(DestroyPixelList(pixel_list));
4009 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
4010 sizeof(*pixel_list->lists[i].nodes));
4012 pixel_list->signature=MagickSignature;
4016 static PixelList **AcquirePixelListThreadSet(const size_t width,
4017 const size_t height)
4028 number_threads=GetOpenMPMaximumThreads();
4029 pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
4030 sizeof(*pixel_list));
4031 if (pixel_list == (PixelList **) NULL)
4032 return((PixelList **) NULL);
4033 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
4034 for (i=0; i < (ssize_t) number_threads; i++)
4036 pixel_list[i]=AcquirePixelList(width,height);
4037 if (pixel_list[i] == (PixelList *) NULL)
4038 return(DestroyPixelListThreadSet(pixel_list));
4043 static void AddNodePixelList(PixelList *pixel_list,const ssize_t channel,
4057 Initialize the node.
4059 list=pixel_list->lists+channel;
4060 list->nodes[color].signature=pixel_list->signature;
4061 list->nodes[color].count=1;
4063 Determine where it belongs in the list.
4066 for (level=list->level; level >= 0; level--)
4068 while (list->nodes[search].next[level] < color)
4069 search=list->nodes[search].next[level];
4070 update[level]=search;
4073 Generate a pseudo-random level for this node.
4075 for (level=0; ; level++)
4077 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
4078 if ((pixel_list->seed & 0x300) != 0x300)
4083 if (level > (list->level+2))
4084 level=list->level+2;
4086 If we're raising the list's level, link back to the root node.
4088 while (level > list->level)
4091 update[list->level]=65536UL;
4094 Link the node into the skip-list.
4098 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
4099 list->nodes[update[level]].next[level]=color;
4100 } while (level-- > 0);
4103 static PixelInfo GetMaximumPixelList(PixelList *pixel_list)
4122 channels[ListChannels];
4125 Find the maximum value for each of the color.
4127 for (channel=0; channel < 5; channel++)
4129 list=pixel_list->lists+channel;
4132 maximum=list->nodes[color].next[0];
4135 color=list->nodes[color].next[0];
4136 if (color > maximum)
4138 count+=list->nodes[color].count;
4139 } while (count < (ssize_t) pixel_list->length);
4140 channels[channel]=(unsigned short) maximum;
4142 GetPixelInfo((const Image *) NULL,&pixel);
4143 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4144 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4145 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
4146 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4147 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4151 static PixelInfo GetMeanPixelList(PixelList *pixel_list)
4172 channels[ListChannels];
4175 Find the mean value for each of the color.
4177 for (channel=0; channel < 5; channel++)
4179 list=pixel_list->lists+channel;
4185 color=list->nodes[color].next[0];
4186 sum+=(MagickRealType) list->nodes[color].count*color;
4187 count+=list->nodes[color].count;
4188 } while (count < (ssize_t) pixel_list->length);
4189 sum/=pixel_list->length;
4190 channels[channel]=(unsigned short) sum;
4192 GetPixelInfo((const Image *) NULL,&pixel);
4193 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4194 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4195 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
4196 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4197 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4201 static PixelInfo GetMedianPixelList(PixelList *pixel_list)
4219 channels[ListChannels];
4222 Find the median value for each of the color.
4224 for (channel=0; channel < 5; channel++)
4226 list=pixel_list->lists+channel;
4231 color=list->nodes[color].next[0];
4232 count+=list->nodes[color].count;
4233 } while (count <= (ssize_t) (pixel_list->length >> 1));
4234 channels[channel]=(unsigned short) color;
4236 GetPixelInfo((const Image *) NULL,&pixel);
4237 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4238 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4239 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
4240 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4241 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4245 static PixelInfo GetMinimumPixelList(PixelList *pixel_list)
4264 channels[ListChannels];
4267 Find the minimum value for each of the color.
4269 for (channel=0; channel < 5; channel++)
4271 list=pixel_list->lists+channel;
4274 minimum=list->nodes[color].next[0];
4277 color=list->nodes[color].next[0];
4278 if (color < minimum)
4280 count+=list->nodes[color].count;
4281 } while (count < (ssize_t) pixel_list->length);
4282 channels[channel]=(unsigned short) minimum;
4284 GetPixelInfo((const Image *) NULL,&pixel);
4285 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4286 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4287 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
4288 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4289 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4293 static PixelInfo GetModePixelList(PixelList *pixel_list)
4316 Make each pixel the 'predominant color' of the specified neighborhood.
4318 for (channel=0; channel < 5; channel++)
4320 list=pixel_list->lists+channel;
4323 max_count=list->nodes[mode].count;
4327 color=list->nodes[color].next[0];
4328 if (list->nodes[color].count > max_count)
4331 max_count=list->nodes[mode].count;
4333 count+=list->nodes[color].count;
4334 } while (count < (ssize_t) pixel_list->length);
4335 channels[channel]=(unsigned short) mode;
4337 GetPixelInfo((const Image *) NULL,&pixel);
4338 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4339 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4340 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
4341 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4342 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4346 static PixelInfo GetNonpeakPixelList(PixelList *pixel_list)
4369 Finds the non peak value for each of the colors.
4371 for (channel=0; channel < 5; channel++)
4373 list=pixel_list->lists+channel;
4375 next=list->nodes[color].next[0];
4381 next=list->nodes[color].next[0];
4382 count+=list->nodes[color].count;
4383 } while (count <= (ssize_t) (pixel_list->length >> 1));
4384 if ((previous == 65536UL) && (next != 65536UL))
4387 if ((previous != 65536UL) && (next == 65536UL))
4389 channels[channel]=(unsigned short) color;
4391 GetPixelInfo((const Image *) NULL,&pixel);
4392 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4393 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4394 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
4395 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4396 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4400 static PixelInfo GetStandardDeviationPixelList(PixelList *pixel_list)
4422 channels[ListChannels];
4425 Find the standard-deviation value for each of the color.
4427 for (channel=0; channel < 5; channel++)
4429 list=pixel_list->lists+channel;
4439 color=list->nodes[color].next[0];
4440 sum+=(MagickRealType) list->nodes[color].count*color;
4441 for (i=0; i < (ssize_t) list->nodes[color].count; i++)
4442 sum_squared+=((MagickRealType) color)*((MagickRealType) color);
4443 count+=list->nodes[color].count;
4444 } while (count < (ssize_t) pixel_list->length);
4445 sum/=pixel_list->length;
4446 sum_squared/=pixel_list->length;
4447 channels[channel]=(unsigned short) sqrt(sum_squared-(sum*sum));
4449 GetPixelInfo((const Image *) NULL,&pixel);
4450 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4451 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4452 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
4453 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4454 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4458 static inline void InsertPixelList(const Image *image,const Quantum *pixel,
4459 PixelList *pixel_list)
4467 index=ScaleQuantumToShort(GetPixelRed(image,pixel));
4468 signature=pixel_list->lists[0].nodes[index].signature;
4469 if (signature == pixel_list->signature)
4470 pixel_list->lists[0].nodes[index].count++;
4472 AddNodePixelList(pixel_list,0,index);
4473 index=ScaleQuantumToShort(GetPixelGreen(image,pixel));
4474 signature=pixel_list->lists[1].nodes[index].signature;
4475 if (signature == pixel_list->signature)
4476 pixel_list->lists[1].nodes[index].count++;
4478 AddNodePixelList(pixel_list,1,index);
4479 index=ScaleQuantumToShort(GetPixelBlue(image,pixel));
4480 signature=pixel_list->lists[2].nodes[index].signature;
4481 if (signature == pixel_list->signature)
4482 pixel_list->lists[2].nodes[index].count++;
4484 AddNodePixelList(pixel_list,2,index);
4485 index=ScaleQuantumToShort(GetPixelAlpha(image,pixel));
4486 signature=pixel_list->lists[3].nodes[index].signature;
4487 if (signature == pixel_list->signature)
4488 pixel_list->lists[3].nodes[index].count++;
4490 AddNodePixelList(pixel_list,3,index);
4491 if (image->colorspace == CMYKColorspace)
4492 index=ScaleQuantumToShort(GetPixelBlack(image,pixel));
4493 signature=pixel_list->lists[4].nodes[index].signature;
4494 if (signature == pixel_list->signature)
4495 pixel_list->lists[4].nodes[index].count++;
4497 AddNodePixelList(pixel_list,4,index);
4500 static inline MagickRealType MagickAbsoluteValue(const MagickRealType x)
4507 static void ResetPixelList(PixelList *pixel_list)
4522 Reset the skip-list.
4524 for (channel=0; channel < 5; channel++)
4526 list=pixel_list->lists+channel;
4527 root=list->nodes+65536UL;
4529 for (level=0; level < 9; level++)
4530 root->next[level]=65536UL;
4532 pixel_list->seed=pixel_list->signature++;
4535 MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
4536 const size_t width,const size_t height,ExceptionInfo *exception)
4538 #define StatisticWidth \
4539 (width == 0 ? GetOptimalKernelWidth2D((double) width,0.5) : width)
4540 #define StatisticHeight \
4541 (height == 0 ? GetOptimalKernelWidth2D((double) height,0.5) : height)
4542 #define StatisticImageTag "Statistic/Image"
4558 **restrict pixel_list;
4564 Initialize statistics image attributes.
4566 assert(image != (Image *) NULL);
4567 assert(image->signature == MagickSignature);
4568 if (image->debug != MagickFalse)
4569 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4570 assert(exception != (ExceptionInfo *) NULL);
4571 assert(exception->signature == MagickSignature);
4572 statistic_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4574 if (statistic_image == (Image *) NULL)
4575 return((Image *) NULL);
4576 if (SetImageStorageClass(statistic_image,DirectClass,exception) == MagickFalse)
4578 statistic_image=DestroyImage(statistic_image);
4579 return((Image *) NULL);
4581 pixel_list=AcquirePixelListThreadSet(StatisticWidth,StatisticHeight);
4582 if (pixel_list == (PixelList **) NULL)
4584 statistic_image=DestroyImage(statistic_image);
4585 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4588 Make each pixel the min / max / median / mode / etc. of the neighborhood.
4592 image_view=AcquireCacheView(image);
4593 statistic_view=AcquireCacheView(statistic_image);
4594 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4595 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4597 for (y=0; y < (ssize_t) statistic_image->rows; y++)
4600 id = GetOpenMPThreadId();
4602 register const Quantum
4611 if (status == MagickFalse)
4613 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) StatisticWidth/2L),y-
4614 (ssize_t) (StatisticHeight/2L),image->columns+StatisticWidth,
4615 StatisticHeight,exception);
4616 q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns, 1,exception);
4617 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
4622 for (x=0; x < (ssize_t) statistic_image->columns; x++)
4627 register const Quantum
4635 ResetPixelList(pixel_list[id]);
4636 for (v=0; v < (ssize_t) StatisticHeight; v++)
4638 for (u=0; u < (ssize_t) StatisticWidth; u++)
4639 InsertPixelList(image,r+u*GetPixelChannels(image),pixel_list[id]);
4640 r+=(image->columns+StatisticWidth)*GetPixelChannels(image);
4642 GetPixelInfo(image,&pixel);
4643 SetPixelInfo(image,p+(StatisticWidth*StatisticHeight/2)*
4644 GetPixelChannels(image),&pixel);
4647 case GradientStatistic:
4653 minimum=GetMinimumPixelList(pixel_list[id]);
4654 maximum=GetMaximumPixelList(pixel_list[id]);
4655 pixel.red=MagickAbsoluteValue(maximum.red-minimum.red);
4656 pixel.green=MagickAbsoluteValue(maximum.green-minimum.green);
4657 pixel.blue=MagickAbsoluteValue(maximum.blue-minimum.blue);
4658 pixel.alpha=MagickAbsoluteValue(maximum.alpha-minimum.alpha);
4659 if (image->colorspace == CMYKColorspace)
4660 pixel.black=MagickAbsoluteValue(maximum.black-minimum.black);
4663 case MaximumStatistic:
4665 pixel=GetMaximumPixelList(pixel_list[id]);
4670 pixel=GetMeanPixelList(pixel_list[id]);
4673 case MedianStatistic:
4676 pixel=GetMedianPixelList(pixel_list[id]);
4679 case MinimumStatistic:
4681 pixel=GetMinimumPixelList(pixel_list[id]);
4686 pixel=GetModePixelList(pixel_list[id]);
4689 case NonpeakStatistic:
4691 pixel=GetNonpeakPixelList(pixel_list[id]);
4694 case StandardDeviationStatistic:
4696 pixel=GetStandardDeviationPixelList(pixel_list[id]);
4700 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
4701 SetPixelRed(statistic_image,ClampToQuantum(pixel.red),q);
4702 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
4703 SetPixelGreen(statistic_image,ClampToQuantum(pixel.green),q);
4704 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
4705 SetPixelBlue(statistic_image,ClampToQuantum(pixel.blue),q);
4706 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
4707 (image->colorspace == CMYKColorspace))
4708 SetPixelBlack(statistic_image,ClampToQuantum(pixel.black),q);
4709 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
4710 (image->matte != MagickFalse))
4711 SetPixelAlpha(statistic_image,ClampToQuantum(pixel.alpha),q);
4712 p+=GetPixelChannels(image);
4713 q+=GetPixelChannels(statistic_image);
4715 if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
4717 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4722 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4723 #pragma omp critical (MagickCore_StatisticImage)
4725 proceed=SetImageProgress(image,StatisticImageTag,progress++,
4727 if (proceed == MagickFalse)
4731 statistic_view=DestroyCacheView(statistic_view);
4732 image_view=DestroyCacheView(image_view);
4733 pixel_list=DestroyPixelListThreadSet(pixel_list);
4734 return(statistic_image);
4738 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4742 % U n s h a r p M a s k I m a g e %
4746 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4748 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
4749 % image with a Gaussian operator of the given radius and standard deviation
4750 % (sigma). For reasonable results, radius should be larger than sigma. Use a
4751 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
4753 % The format of the UnsharpMaskImage method is:
4755 % Image *UnsharpMaskImage(const Image *image,const double radius,
4756 % const double sigma,const double amount,const double threshold,
4757 % ExceptionInfo *exception)
4759 % A description of each parameter follows:
4761 % o image: the image.
4763 % o radius: the radius of the Gaussian, in pixels, not counting the center
4766 % o sigma: the standard deviation of the Gaussian, in pixels.
4768 % o amount: the percentage of the difference between the original and the
4769 % blur image that is added back into the original.
4771 % o threshold: the threshold in pixels needed to apply the diffence amount.
4773 % o exception: return any errors or warnings in this structure.
4776 MagickExport Image *UnsharpMaskImage(const Image *image,
4777 const double radius,const double sigma,const double amount,
4778 const double threshold,ExceptionInfo *exception)
4780 #define SharpenImageTag "Sharpen/Image"
4804 assert(image != (const Image *) NULL);
4805 assert(image->signature == MagickSignature);
4806 if (image->debug != MagickFalse)
4807 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4808 assert(exception != (ExceptionInfo *) NULL);
4809 unsharp_image=BlurImage(image,radius,sigma,exception);
4810 if (unsharp_image == (Image *) NULL)
4811 return((Image *) NULL);
4812 quantum_threshold=(MagickRealType) QuantumRange*threshold;
4818 GetPixelInfo(image,&bias);
4819 image_view=AcquireCacheView(image);
4820 unsharp_view=AcquireCacheView(unsharp_image);
4821 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4822 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4824 for (y=0; y < (ssize_t) image->rows; y++)
4829 register const Quantum
4838 if (status == MagickFalse)
4840 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4841 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
4843 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
4849 for (x=0; x < (ssize_t) image->columns; x++)
4851 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
4853 pixel.red=GetPixelRed(image,p)-(MagickRealType) GetPixelRed(image,q);
4854 if (fabs(2.0*pixel.red) < quantum_threshold)
4855 pixel.red=(MagickRealType) GetPixelRed(image,p);
4857 pixel.red=(MagickRealType) GetPixelRed(image,p)+(pixel.red*amount);
4858 SetPixelRed(unsharp_image,ClampToQuantum(pixel.red),q);
4860 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
4862 pixel.green=GetPixelGreen(image,p)-
4863 (MagickRealType) GetPixelGreen(image,q);
4864 if (fabs(2.0*pixel.green) < quantum_threshold)
4865 pixel.green=(MagickRealType)
4866 GetPixelGreen(image,p);
4868 pixel.green=(MagickRealType)
4869 GetPixelGreen(image,p)+
4870 (pixel.green*amount);
4871 SetPixelGreen(unsharp_image,
4872 ClampToQuantum(pixel.green),q);
4874 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
4876 pixel.blue=GetPixelBlue(image,p)-
4877 (MagickRealType) GetPixelBlue(image,q);
4878 if (fabs(2.0*pixel.blue) < quantum_threshold)
4879 pixel.blue=(MagickRealType)
4880 GetPixelBlue(image,p);
4882 pixel.blue=(MagickRealType)
4883 GetPixelBlue(image,p)+(pixel.blue*amount);
4884 SetPixelBlue(unsharp_image,
4885 ClampToQuantum(pixel.blue),q);
4887 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
4888 (image->colorspace == CMYKColorspace))
4890 pixel.black=GetPixelBlack(image,p)-
4891 (MagickRealType) GetPixelBlack(image,q);
4892 if (fabs(2.0*pixel.black) < quantum_threshold)
4893 pixel.black=(MagickRealType)
4894 GetPixelBlack(image,p);
4896 pixel.black=(MagickRealType)
4897 GetPixelBlack(image,p)+(pixel.black*
4899 SetPixelBlack(unsharp_image,
4900 ClampToQuantum(pixel.black),q);
4902 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
4904 pixel.alpha=GetPixelAlpha(image,p)-
4905 (MagickRealType) GetPixelAlpha(image,q);
4906 if (fabs(2.0*pixel.alpha) < quantum_threshold)
4907 pixel.alpha=(MagickRealType)
4908 GetPixelAlpha(image,p);
4910 pixel.alpha=GetPixelAlpha(image,p)+
4911 (pixel.alpha*amount);
4912 SetPixelAlpha(unsharp_image,
4913 ClampToQuantum(pixel.alpha),q);
4915 p+=GetPixelChannels(image);
4916 q+=GetPixelChannels(unsharp_image);
4918 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4920 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4925 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4926 #pragma omp critical (MagickCore_UnsharpMaskImage)
4928 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
4929 if (proceed == MagickFalse)
4933 unsharp_image->type=image->type;
4934 unsharp_view=DestroyCacheView(unsharp_view);
4935 image_view=DestroyCacheView(image_view);
4936 if (status == MagickFalse)
4937 unsharp_image=DestroyImage(unsharp_image);
4938 return(unsharp_image);