2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % EEEEE FFFFF FFFFF EEEEE CCCC TTTTT %
8 % EEE FFF FFF EEE C T %
10 % EEEEE F F EEEEE CCCC T %
13 % MagickCore Image Effects Methods %
20 % Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "magick/studio.h"
44 #include "magick/property.h"
45 #include "magick/blob.h"
46 #include "magick/cache-view.h"
47 #include "magick/color.h"
48 #include "magick/color-private.h"
49 #include "magick/colorspace.h"
50 #include "magick/constitute.h"
51 #include "magick/decorate.h"
52 #include "magick/draw.h"
53 #include "magick/enhance.h"
54 #include "magick/exception.h"
55 #include "magick/exception-private.h"
56 #include "magick/effect.h"
57 #include "magick/fx.h"
58 #include "magick/gem.h"
59 #include "magick/geometry.h"
60 #include "magick/image-private.h"
61 #include "magick/list.h"
62 #include "magick/log.h"
63 #include "magick/memory_.h"
64 #include "magick/monitor.h"
65 #include "magick/monitor-private.h"
66 #include "magick/montage.h"
67 #include "magick/paint.h"
68 #include "magick/pixel-private.h"
69 #include "magick/property.h"
70 #include "magick/quantize.h"
71 #include "magick/quantum.h"
72 #include "magick/random_.h"
73 #include "magick/random-private.h"
74 #include "magick/resample.h"
75 #include "magick/resample-private.h"
76 #include "magick/resize.h"
77 #include "magick/resource_.h"
78 #include "magick/segment.h"
79 #include "magick/shear.h"
80 #include "magick/signature-private.h"
81 #include "magick/string_.h"
82 #include "magick/thread-private.h"
83 #include "magick/transform.h"
84 #include "magick/threshold.h"
87 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91 % A d a p t i v e B l u r I m a g e %
95 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
97 % AdaptiveBlurImage() adaptively blurs the image by blurring less
98 % intensely near image edges and more intensely far from edges. We blur the
99 % image with a Gaussian operator of the given radius and standard deviation
100 % (sigma). For reasonable results, radius should be larger than sigma. Use a
101 % radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
103 % The format of the AdaptiveBlurImage method is:
105 % Image *AdaptiveBlurImage(const Image *image,const double radius,
106 % const double sigma,ExceptionInfo *exception)
107 % Image *AdaptiveBlurImageChannel(const Image *image,
108 % const ChannelType channel,double radius,const double sigma,
109 % ExceptionInfo *exception)
111 % A description of each parameter follows:
113 % o image: the image.
115 % o channel: the channel type.
117 % o radius: the radius of the Gaussian, in pixels, not counting the center
120 % o sigma: the standard deviation of the Laplacian, in pixels.
122 % o exception: return any errors or warnings in this structure.
126 MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
127 const double sigma,ExceptionInfo *exception)
132 blur_image=AdaptiveBlurImageChannel(image,DefaultChannels,radius,sigma,
137 MagickExport Image *AdaptiveBlurImageChannel(const Image *image,
138 const ChannelType channel,const double radius,const double sigma,
139 ExceptionInfo *exception)
141 #define AdaptiveBlurImageTag "Convolve/Image"
142 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
181 assert(image != (const Image *) NULL);
182 assert(image->signature == MagickSignature);
183 if (image->debug != MagickFalse)
184 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
185 assert(exception != (ExceptionInfo *) NULL);
186 assert(exception->signature == MagickSignature);
187 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
188 if (blur_image == (Image *) NULL)
189 return((Image *) NULL);
190 if (fabs(sigma) <= MagickEpsilon)
192 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
194 InheritException(exception,&blur_image->exception);
195 blur_image=DestroyImage(blur_image);
196 return((Image *) NULL);
199 Edge detect the image brighness channel, level, blur, and level again.
201 edge_image=EdgeImage(image,radius,exception);
202 if (edge_image == (Image *) NULL)
204 blur_image=DestroyImage(blur_image);
205 return((Image *) NULL);
207 (void) LevelImage(edge_image,"20%,95%");
208 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
209 if (gaussian_image != (Image *) NULL)
211 edge_image=DestroyImage(edge_image);
212 edge_image=gaussian_image;
214 (void) LevelImage(edge_image,"10%,95%");
216 Create a set of kernels from maximum (radius,sigma) to minimum.
218 width=GetOptimalKernelWidth2D(radius,sigma);
219 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
220 if (kernel == (double **) NULL)
222 edge_image=DestroyImage(edge_image);
223 blur_image=DestroyImage(blur_image);
224 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
226 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
227 for (i=0; i < (long) width; i+=2)
229 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
231 if (kernel[i] == (double *) NULL)
234 for (v=(-((long) (width-i)/2)); v <= (long) ((width-i)/2); v++)
236 for (u=(-((long) (width-i)/2)); u <= (long) ((width-i)/2); u++)
238 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
239 kernel[i][j]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
244 for (j=0; j < (long) ((width-i)*(width-i)); j++)
245 normalize+=kernel[i][j];
246 if (fabs(normalize) <= MagickEpsilon)
248 normalize=1.0/normalize;
249 for (j=0; j < (long) ((width-i)*(width-i)); j++)
250 kernel[i][j]=(double) (normalize*kernel[i][j]);
252 if (i < (long) width)
254 for (i-=2; i >= 0; i-=2)
255 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
256 kernel=(double **) RelinquishMagickMemory(kernel);
257 edge_image=DestroyImage(edge_image);
258 blur_image=DestroyImage(blur_image);
259 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
262 Adaptively blur image.
267 GetMagickPixelPacket(image,&zero);
268 image_view=AcquireCacheView(image);
269 edge_view=AcquireCacheView(edge_image);
270 blur_view=AcquireCacheView(blur_image);
271 #if defined(MAGICKCORE_OPENMP_SUPPORT)
272 #pragma omp parallel for schedule(dynamic) shared(progress,status)
274 for (y=0; y < (long) blur_image->rows; y++)
276 register const IndexPacket
279 register const PixelPacket
284 *__restrict blur_indexes;
292 if (status == MagickFalse)
294 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
295 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
297 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
302 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
303 for (x=0; x < (long) blur_image->columns; x++)
312 register const double
321 i=(long) (width*QuantumScale*PixelIntensity(r)+0.5);
325 if (i > (long) width)
329 p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
330 ((width-i)/2L),width-i,width-i,exception);
331 if (p == (const PixelPacket *) NULL)
333 indexes=GetCacheViewVirtualIndexQueue(image_view);
336 for (v=0; v < (long) (width-i); v++)
338 for (u=0; u < (long) (width-i); u++)
341 if (((channel & OpacityChannel) != 0) &&
342 (image->matte != MagickFalse))
343 alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
344 if ((channel & RedChannel) != 0)
345 pixel.red+=(*k)*alpha*p->red;
346 if ((channel & GreenChannel) != 0)
347 pixel.green+=(*k)*alpha*p->green;
348 if ((channel & BlueChannel) != 0)
349 pixel.blue+=(*k)*alpha*p->blue;
350 if ((channel & OpacityChannel) != 0)
351 pixel.opacity+=(*k)*p->opacity;
352 if (((channel & IndexChannel) != 0) &&
353 (image->colorspace == CMYKColorspace))
354 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
360 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
361 if ((channel & RedChannel) != 0)
362 q->red=RoundToQuantum(gamma*pixel.red+bias);
363 if ((channel & GreenChannel) != 0)
364 q->green=RoundToQuantum(gamma*pixel.green+bias);
365 if ((channel & BlueChannel) != 0)
366 q->blue=RoundToQuantum(gamma*pixel.blue+bias);
367 if ((channel & OpacityChannel) != 0)
368 q->opacity=RoundToQuantum(pixel.opacity+bias);
369 if (((channel & IndexChannel) != 0) &&
370 (image->colorspace == CMYKColorspace))
371 blur_indexes[x]=RoundToQuantum(gamma*pixel.index+bias);
375 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
377 if (image->progress_monitor != (MagickProgressMonitor) NULL)
382 #if defined(MAGICKCORE_OPENMP_SUPPORT)
383 #pragma omp critical (MagickCore_AdaptiveBlurImageChannel)
385 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
387 if (proceed == MagickFalse)
391 blur_image->type=image->type;
392 blur_view=DestroyCacheView(blur_view);
393 edge_view=DestroyCacheView(edge_view);
394 image_view=DestroyCacheView(image_view);
395 edge_image=DestroyImage(edge_image);
396 for (i=0; i < (long) width; i+=2)
397 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
398 kernel=(double **) RelinquishMagickMemory(kernel);
399 if (status == MagickFalse)
400 blur_image=DestroyImage(blur_image);
405 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
409 % A d a p t i v e S h a r p e n I m a g e %
413 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
415 % AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
416 % intensely near image edges and less intensely far from edges. We sharpen the
417 % image with a Gaussian operator of the given radius and standard deviation
418 % (sigma). For reasonable results, radius should be larger than sigma. Use a
419 % radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
421 % The format of the AdaptiveSharpenImage method is:
423 % Image *AdaptiveSharpenImage(const Image *image,const double radius,
424 % const double sigma,ExceptionInfo *exception)
425 % Image *AdaptiveSharpenImageChannel(const Image *image,
426 % const ChannelType channel,double radius,const double sigma,
427 % ExceptionInfo *exception)
429 % A description of each parameter follows:
431 % o image: the image.
433 % o channel: the channel type.
435 % o radius: the radius of the Gaussian, in pixels, not counting the center
438 % o sigma: the standard deviation of the Laplacian, in pixels.
440 % o exception: return any errors or warnings in this structure.
444 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
445 const double sigma,ExceptionInfo *exception)
450 sharp_image=AdaptiveSharpenImageChannel(image,DefaultChannels,radius,sigma,
455 MagickExport Image *AdaptiveSharpenImageChannel(const Image *image,
456 const ChannelType channel,const double radius,const double sigma,
457 ExceptionInfo *exception)
459 #define AdaptiveSharpenImageTag "Convolve/Image"
460 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
499 assert(image != (const Image *) NULL);
500 assert(image->signature == MagickSignature);
501 if (image->debug != MagickFalse)
502 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
503 assert(exception != (ExceptionInfo *) NULL);
504 assert(exception->signature == MagickSignature);
505 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
506 if (sharp_image == (Image *) NULL)
507 return((Image *) NULL);
508 if (fabs(sigma) <= MagickEpsilon)
510 if (SetImageStorageClass(sharp_image,DirectClass) == MagickFalse)
512 InheritException(exception,&sharp_image->exception);
513 sharp_image=DestroyImage(sharp_image);
514 return((Image *) NULL);
517 Edge detect the image brighness channel, level, sharp, and level again.
519 edge_image=EdgeImage(image,radius,exception);
520 if (edge_image == (Image *) NULL)
522 sharp_image=DestroyImage(sharp_image);
523 return((Image *) NULL);
525 (void) LevelImage(edge_image,"20%,95%");
526 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
527 if (gaussian_image != (Image *) NULL)
529 edge_image=DestroyImage(edge_image);
530 edge_image=gaussian_image;
532 (void) LevelImage(edge_image,"10%,95%");
534 Create a set of kernels from maximum (radius,sigma) to minimum.
536 width=GetOptimalKernelWidth2D(radius,sigma);
537 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
538 if (kernel == (double **) NULL)
540 edge_image=DestroyImage(edge_image);
541 sharp_image=DestroyImage(sharp_image);
542 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
544 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
545 for (i=0; i < (long) width; i+=2)
547 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
549 if (kernel[i] == (double *) NULL)
552 for (v=(-((long) (width-i)/2)); v <= (long) ((width-i)/2); v++)
554 for (u=(-((long) (width-i)/2)); u <= (long) ((width-i)/2); u++)
556 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
557 kernel[i][j]=(double) (-alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
562 for (j=0; j < (long) ((width-i)*(width-i)); j++)
563 normalize+=kernel[i][j];
564 if (fabs(normalize) <= MagickEpsilon)
566 normalize=1.0/normalize;
567 for (j=0; j < (long) ((width-i)*(width-i)); j++)
568 kernel[i][j]=(double) (normalize*kernel[i][j]);
570 if (i < (long) width)
572 for (i-=2; i >= 0; i-=2)
573 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
574 kernel=(double **) RelinquishMagickMemory(kernel);
575 edge_image=DestroyImage(edge_image);
576 sharp_image=DestroyImage(sharp_image);
577 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
580 Adaptively sharpen image.
585 GetMagickPixelPacket(image,&zero);
586 image_view=AcquireCacheView(image);
587 edge_view=AcquireCacheView(edge_image);
588 sharp_view=AcquireCacheView(sharp_image);
589 #if defined(MAGICKCORE_OPENMP_SUPPORT)
590 #pragma omp parallel for schedule(dynamic) shared(progress,status)
592 for (y=0; y < (long) sharp_image->rows; y++)
594 register const IndexPacket
597 register const PixelPacket
602 *__restrict sharp_indexes;
610 if (status == MagickFalse)
612 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
613 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
615 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
620 sharp_indexes=GetCacheViewAuthenticIndexQueue(sharp_view);
621 for (x=0; x < (long) sharp_image->columns; x++)
630 register const double
639 i=(long) (width*(QuantumRange-QuantumScale*PixelIntensity(r))+0.5);
643 if (i > (long) width)
647 p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
648 ((width-i)/2L),width-i,width-i,exception);
649 if (p == (const PixelPacket *) NULL)
651 indexes=GetCacheViewVirtualIndexQueue(image_view);
654 for (v=0; v < (long) (width-i); v++)
656 for (u=0; u < (long) (width-i); u++)
659 if (((channel & OpacityChannel) != 0) &&
660 (image->matte != MagickFalse))
661 alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
662 if ((channel & RedChannel) != 0)
663 pixel.red+=(*k)*alpha*p->red;
664 if ((channel & GreenChannel) != 0)
665 pixel.green+=(*k)*alpha*p->green;
666 if ((channel & BlueChannel) != 0)
667 pixel.blue+=(*k)*alpha*p->blue;
668 if ((channel & OpacityChannel) != 0)
669 pixel.opacity+=(*k)*p->opacity;
670 if (((channel & IndexChannel) != 0) &&
671 (image->colorspace == CMYKColorspace))
672 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
678 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
679 if ((channel & RedChannel) != 0)
680 q->red=RoundToQuantum(gamma*pixel.red+bias);
681 if ((channel & GreenChannel) != 0)
682 q->green=RoundToQuantum(gamma*pixel.green+bias);
683 if ((channel & BlueChannel) != 0)
684 q->blue=RoundToQuantum(gamma*pixel.blue+bias);
685 if ((channel & OpacityChannel) != 0)
686 q->opacity=RoundToQuantum(pixel.opacity+bias);
687 if (((channel & IndexChannel) != 0) &&
688 (image->colorspace == CMYKColorspace))
689 sharp_indexes[x]=RoundToQuantum(gamma*pixel.index+bias);
693 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
695 if (image->progress_monitor != (MagickProgressMonitor) NULL)
700 #if defined(MAGICKCORE_OPENMP_SUPPORT)
701 #pragma omp critical (MagickCore_AdaptiveSharpenImageChannel)
703 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
705 if (proceed == MagickFalse)
709 sharp_image->type=image->type;
710 sharp_view=DestroyCacheView(sharp_view);
711 edge_view=DestroyCacheView(edge_view);
712 image_view=DestroyCacheView(image_view);
713 edge_image=DestroyImage(edge_image);
714 for (i=0; i < (long) width; i+=2)
715 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
716 kernel=(double **) RelinquishMagickMemory(kernel);
717 if (status == MagickFalse)
718 sharp_image=DestroyImage(sharp_image);
723 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727 % B l u r I m a g e %
731 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
733 % BlurImage() blurs an image. We convolve the image with a Gaussian operator
734 % of the given radius and standard deviation (sigma). For reasonable results,
735 % the radius should be larger than sigma. Use a radius of 0 and BlurImage()
736 % selects a suitable radius for you.
738 % BlurImage() differs from GaussianBlurImage() in that it uses a separable
739 % kernel which is faster but mathematically equivalent to the non-separable
742 % The format of the BlurImage method is:
744 % Image *BlurImage(const Image *image,const double radius,
745 % const double sigma,ExceptionInfo *exception)
746 % Image *BlurImageChannel(const Image *image,const ChannelType channel,
747 % const double radius,const double sigma,ExceptionInfo *exception)
749 % A description of each parameter follows:
751 % o image: the image.
753 % o channel: the channel type.
755 % o radius: the radius of the Gaussian, in pixels, not counting the center
758 % o sigma: the standard deviation of the Gaussian, in pixels.
760 % o exception: return any errors or warnings in this structure.
764 MagickExport Image *BlurImage(const Image *image,const double radius,
765 const double sigma,ExceptionInfo *exception)
770 blur_image=BlurImageChannel(image,DefaultChannels,radius,sigma,exception);
774 static double *GetBlurKernel(unsigned long width,const MagickRealType sigma)
792 Generate a 1-D convolution kernel.
794 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
795 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
796 if (kernel == (double *) NULL)
798 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
799 bias=KernelRank*(long) width/2;
800 for (i=(-bias); i <= bias; i++)
802 alpha=exp((-((double) (i*i))/(double) (2.0*KernelRank*KernelRank*
803 MagickSigma*MagickSigma)));
804 kernel[(i+bias)/KernelRank]+=(double) (alpha/(MagickSQ2PI*sigma));
807 for (i=0; i < (long) width; i++)
808 normalize+=kernel[i];
809 for (i=0; i < (long) width; i++)
810 kernel[i]/=normalize;
814 MagickExport Image *BlurImageChannel(const Image *image,
815 const ChannelType channel,const double radius,const double sigma,
816 ExceptionInfo *exception)
818 #define BlurImageTag "Blur/Image"
851 Initialize blur image attributes.
853 assert(image != (Image *) NULL);
854 assert(image->signature == MagickSignature);
855 if (image->debug != MagickFalse)
856 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
857 assert(exception != (ExceptionInfo *) NULL);
858 assert(exception->signature == MagickSignature);
859 blur_image=CloneImage(image,0,0,MagickTrue,exception);
860 if (blur_image == (Image *) NULL)
861 return((Image *) NULL);
862 if (fabs(sigma) <= MagickEpsilon)
864 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
866 InheritException(exception,&blur_image->exception);
867 blur_image=DestroyImage(blur_image);
868 return((Image *) NULL);
870 width=GetOptimalKernelWidth1D(radius,sigma);
871 kernel=GetBlurKernel(width,sigma);
872 if (kernel == (double *) NULL)
874 blur_image=DestroyImage(blur_image);
875 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
877 if (image->debug != MagickFalse)
880 format[MaxTextExtent],
883 register const double
886 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
887 " BlurImage with %ld kernel:",width);
888 message=AcquireString("");
890 for (i=0; i < (long) width; i++)
893 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",i);
894 (void) ConcatenateString(&message,format);
895 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
896 (void) ConcatenateString(&message,format);
897 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
899 message=DestroyString(message);
906 GetMagickPixelPacket(image,&zero);
908 image_view=AcquireCacheView(image);
909 blur_view=AcquireCacheView(blur_image);
910 #if defined(MAGICKCORE_OPENMP_SUPPORT)
911 #pragma omp parallel for schedule(dynamic) shared(progress,status)
913 for (y=0; y < (long) blur_image->rows; y++)
915 register const IndexPacket
918 register const PixelPacket
922 *__restrict blur_indexes;
930 if (status == MagickFalse)
932 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y,image->columns+
934 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
936 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
941 indexes=GetCacheViewVirtualIndexQueue(image_view);
942 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
943 for (x=0; x < (long) blur_image->columns; x++)
948 register const double
951 register const PixelPacket
952 *__restrict kernel_pixels;
960 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
962 for (i=0; i < (long) width; i++)
964 pixel.red+=(*k)*kernel_pixels->red;
965 pixel.green+=(*k)*kernel_pixels->green;
966 pixel.blue+=(*k)*kernel_pixels->blue;
970 if ((channel & RedChannel) != 0)
971 q->red=RoundToQuantum(pixel.red+bias);
972 if ((channel & GreenChannel) != 0)
973 q->green=RoundToQuantum(pixel.green+bias);
974 if ((channel & BlueChannel) != 0)
975 q->blue=RoundToQuantum(pixel.blue+bias);
976 if ((channel & OpacityChannel) != 0)
980 for (i=0; i < (long) width; i++)
982 pixel.opacity+=(*k)*kernel_pixels->opacity;
986 q->opacity=RoundToQuantum(pixel.opacity+bias);
988 if (((channel & IndexChannel) != 0) &&
989 (image->colorspace == CMYKColorspace))
991 register const IndexPacket
992 *__restrict kernel_indexes;
995 kernel_indexes=indexes;
996 for (i=0; i < (long) width; i++)
998 pixel.index+=(*k)*(*kernel_indexes);
1002 blur_indexes[x]=RoundToQuantum(pixel.index+bias);
1012 for (i=0; i < (long) width; i++)
1014 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1015 kernel_pixels->opacity));
1016 pixel.red+=(*k)*alpha*kernel_pixels->red;
1017 pixel.green+=(*k)*alpha*kernel_pixels->green;
1018 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
1023 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1024 if ((channel & RedChannel) != 0)
1025 q->red=RoundToQuantum(gamma*pixel.red+bias);
1026 if ((channel & GreenChannel) != 0)
1027 q->green=RoundToQuantum(gamma*pixel.green+bias);
1028 if ((channel & BlueChannel) != 0)
1029 q->blue=RoundToQuantum(gamma*pixel.blue+bias);
1030 if ((channel & OpacityChannel) != 0)
1034 for (i=0; i < (long) width; i++)
1036 pixel.opacity+=(*k)*kernel_pixels->opacity;
1040 q->opacity=RoundToQuantum(pixel.opacity+bias);
1042 if (((channel & IndexChannel) != 0) &&
1043 (image->colorspace == CMYKColorspace))
1045 register const IndexPacket
1046 *__restrict kernel_indexes;
1050 kernel_indexes=indexes;
1051 for (i=0; i < (long) width; i++)
1053 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1054 kernel_pixels->opacity));
1055 pixel.index+=(*k)*alpha*(*kernel_indexes);
1060 blur_indexes[x]=RoundToQuantum(gamma*pixel.index+bias);
1066 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1068 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1073 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1074 #pragma omp critical (MagickCore_BlurImageChannel)
1076 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1077 blur_image->columns);
1078 if (proceed == MagickFalse)
1082 blur_view=DestroyCacheView(blur_view);
1083 image_view=DestroyCacheView(image_view);
1087 image_view=AcquireCacheView(blur_image);
1088 blur_view=AcquireCacheView(blur_image);
1089 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1090 #pragma omp parallel for shared(progress,status)
1092 for (x=0; x < (long) blur_image->columns; x++)
1094 register const IndexPacket
1095 *__restrict indexes;
1097 register const PixelPacket
1100 register IndexPacket
1101 *__restrict blur_indexes;
1106 register PixelPacket
1109 if (status == MagickFalse)
1111 p=GetCacheViewVirtualPixels(image_view,x,-((long) width/2L),1,image->rows+
1113 q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
1114 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1119 indexes=GetCacheViewVirtualIndexQueue(image_view);
1120 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
1121 for (y=0; y < (long) blur_image->rows; y++)
1126 register const double
1129 register const PixelPacket
1130 *__restrict kernel_pixels;
1138 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
1140 for (i=0; i < (long) width; i++)
1142 pixel.red+=(*k)*kernel_pixels->red;
1143 pixel.green+=(*k)*kernel_pixels->green;
1144 pixel.blue+=(*k)*kernel_pixels->blue;
1148 if ((channel & RedChannel) != 0)
1149 q->red=RoundToQuantum(pixel.red+bias);
1150 if ((channel & GreenChannel) != 0)
1151 q->green=RoundToQuantum(pixel.green+bias);
1152 if ((channel & BlueChannel) != 0)
1153 q->blue=RoundToQuantum(pixel.blue+bias);
1154 if ((channel & OpacityChannel) != 0)
1158 for (i=0; i < (long) width; i++)
1160 pixel.opacity+=(*k)*kernel_pixels->opacity;
1164 q->opacity=RoundToQuantum(pixel.opacity+bias);
1166 if (((channel & IndexChannel) != 0) &&
1167 (image->colorspace == CMYKColorspace))
1169 register const IndexPacket
1170 *__restrict kernel_indexes;
1173 kernel_indexes=indexes;
1174 for (i=0; i < (long) width; i++)
1176 pixel.index+=(*k)*(*kernel_indexes);
1180 blur_indexes[y]=RoundToQuantum(pixel.index+bias);
1190 for (i=0; i < (long) width; i++)
1192 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1193 kernel_pixels->opacity));
1194 pixel.red+=(*k)*alpha*kernel_pixels->red;
1195 pixel.green+=(*k)*alpha*kernel_pixels->green;
1196 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
1201 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1202 if ((channel & RedChannel) != 0)
1203 q->red=RoundToQuantum(gamma*pixel.red+bias);
1204 if ((channel & GreenChannel) != 0)
1205 q->green=RoundToQuantum(gamma*pixel.green+bias);
1206 if ((channel & BlueChannel) != 0)
1207 q->blue=RoundToQuantum(gamma*pixel.blue+bias);
1208 if ((channel & OpacityChannel) != 0)
1212 for (i=0; i < (long) width; i++)
1214 pixel.opacity+=(*k)*kernel_pixels->opacity;
1218 q->opacity=RoundToQuantum(pixel.opacity+bias);
1220 if (((channel & IndexChannel) != 0) &&
1221 (image->colorspace == CMYKColorspace))
1223 register const IndexPacket
1224 *__restrict kernel_indexes;
1228 kernel_indexes=indexes;
1229 for (i=0; i < (long) width; i++)
1231 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1232 kernel_pixels->opacity));
1233 pixel.index+=(*k)*alpha*(*kernel_indexes);
1238 blur_indexes[y]=RoundToQuantum(gamma*pixel.index+bias);
1244 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1246 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1251 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1252 #pragma omp critical (MagickCore_BlurImageChannel)
1254 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1255 blur_image->columns);
1256 if (proceed == MagickFalse)
1260 blur_view=DestroyCacheView(blur_view);
1261 image_view=DestroyCacheView(image_view);
1262 kernel=(double *) RelinquishMagickMemory(kernel);
1263 if (status == MagickFalse)
1264 blur_image=DestroyImage(blur_image);
1265 blur_image->type=image->type;
1270 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1274 % D e s p e c k l e I m a g e %
1278 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1280 % DespeckleImage() reduces the speckle noise in an image while perserving the
1281 % edges of the original image.
1283 % The format of the DespeckleImage method is:
1285 % Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1287 % A description of each parameter follows:
1289 % o image: the image.
1291 % o exception: return any errors or warnings in this structure.
1295 static Quantum **DestroyPixelThreadSet(Quantum **pixels)
1300 assert(pixels != (Quantum **) NULL);
1301 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
1302 if (pixels[i] != (Quantum *) NULL)
1303 pixels[i]=(Quantum *) RelinquishMagickMemory(pixels[i]);
1304 pixels=(Quantum **) RelinquishAlignedMemory(pixels);
1308 static Quantum **AcquirePixelThreadSet(const size_t count)
1319 number_threads=GetOpenMPMaximumThreads();
1320 pixels=(Quantum **) AcquireAlignedMemory(number_threads,sizeof(*pixels));
1321 if (pixels == (Quantum **) NULL)
1322 return((Quantum **) NULL);
1323 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
1324 for (i=0; i < (long) number_threads; i++)
1326 pixels[i]=(Quantum *) AcquireQuantumMemory(count,sizeof(**pixels));
1327 if (pixels[i] == (Quantum *) NULL)
1328 return(DestroyPixelThreadSet(pixels));
1333 static void Hull(const long x_offset,const long y_offset,
1334 const unsigned long columns,const unsigned long rows,Quantum *f,Quantum *g,
1352 assert(f != (Quantum *) NULL);
1353 assert(g != (Quantum *) NULL);
1356 r=p+(y_offset*((long) columns+2)+x_offset);
1357 for (y=0; y < (long) rows; y++)
1363 for (x=(long) columns; x != 0; x--)
1365 v=(MagickRealType) (*p);
1366 if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1367 v+=ScaleCharToQuantum(1);
1374 for (x=(long) columns; x != 0; x--)
1376 v=(MagickRealType) (*p);
1377 if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
1378 v-=(long) ScaleCharToQuantum(1);
1390 r=q+(y_offset*((long) columns+2)+x_offset);
1391 s=q-(y_offset*((long) columns+2)+x_offset);
1392 for (y=0; y < (long) rows; y++)
1399 for (x=(long) columns; x != 0; x--)
1401 v=(MagickRealType) (*q);
1402 if (((MagickRealType) *s >=
1403 (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1404 ((MagickRealType) *r > v))
1405 v+=ScaleCharToQuantum(1);
1413 for (x=(long) columns; x != 0; x--)
1415 v=(MagickRealType) (*q);
1416 if (((MagickRealType) *s <=
1417 (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1418 ((MagickRealType) *r < v))
1419 v-=(MagickRealType) ScaleCharToQuantum(1);
1433 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1435 #define DespeckleImageTag "Despeckle/Image"
1462 Allocate despeckled image.
1464 assert(image != (const Image *) NULL);
1465 assert(image->signature == MagickSignature);
1466 if (image->debug != MagickFalse)
1467 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1468 assert(exception != (ExceptionInfo *) NULL);
1469 assert(exception->signature == MagickSignature);
1470 despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1472 if (despeckle_image == (Image *) NULL)
1473 return((Image *) NULL);
1474 if (SetImageStorageClass(despeckle_image,DirectClass) == MagickFalse)
1476 InheritException(exception,&despeckle_image->exception);
1477 despeckle_image=DestroyImage(despeckle_image);
1478 return((Image *) NULL);
1481 Allocate image buffers.
1483 length=(size_t) ((image->columns+2)*(image->rows+2));
1484 pixels=AcquirePixelThreadSet(length);
1485 buffers=AcquirePixelThreadSet(length);
1486 if ((pixels == (Quantum **) NULL) || (buffers == (Quantum **) NULL))
1488 if (buffers != (Quantum **) NULL)
1489 buffers=DestroyPixelThreadSet(buffers);
1490 if (pixels != (Quantum **) NULL)
1491 pixels=DestroyPixelThreadSet(pixels);
1492 despeckle_image=DestroyImage(despeckle_image);
1493 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1496 Reduce speckle in the image.
1499 image_view=AcquireCacheView(image);
1500 despeckle_view=AcquireCacheView(despeckle_image);
1501 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1502 #pragma omp parallel for schedule(dynamic,1) shared(status)
1504 for (channel=0; channel <= 3; channel++)
1518 if (status == MagickFalse)
1520 pixel=pixels[GetOpenMPThreadId()];
1521 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
1522 j=(long) image->columns+2;
1523 for (y=0; y < (long) image->rows; y++)
1525 register const PixelPacket
1528 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1529 if (p == (const PixelPacket *) NULL)
1532 for (x=0; x < (long) image->columns; x++)
1536 case 0: pixel[j]=p->red; break;
1537 case 1: pixel[j]=p->green; break;
1538 case 2: pixel[j]=p->blue; break;
1539 case 3: pixel[j]=p->opacity; break;
1547 buffer=buffers[GetOpenMPThreadId()];
1548 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1549 for (i=0; i < 4; i++)
1551 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,1);
1552 Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,1);
1553 Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,-1);
1554 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,-1);
1556 j=(long) image->columns+2;
1557 for (y=0; y < (long) image->rows; y++)
1562 register PixelPacket
1565 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1567 if (q == (PixelPacket *) NULL)
1570 for (x=0; x < (long) image->columns; x++)
1574 case 0: q->red=pixel[j]; break;
1575 case 1: q->green=pixel[j]; break;
1576 case 2: q->blue=pixel[j]; break;
1577 case 3: q->opacity=pixel[j]; break;
1583 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1584 if (sync == MagickFalse)
1591 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1596 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1597 #pragma omp critical (MagickCore_DespeckleImage)
1599 proceed=SetImageProgress(image,DespeckleImageTag,channel,3);
1600 if (proceed == MagickFalse)
1604 despeckle_view=DestroyCacheView(despeckle_view);
1605 image_view=DestroyCacheView(image_view);
1606 buffers=DestroyPixelThreadSet(buffers);
1607 pixels=DestroyPixelThreadSet(pixels);
1608 despeckle_image->type=image->type;
1609 if (status == MagickFalse)
1610 despeckle_image=DestroyImage(despeckle_image);
1611 return(despeckle_image);
1615 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1619 % E d g e I m a g e %
1623 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1625 % EdgeImage() finds edges in an image. Radius defines the radius of the
1626 % convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1629 % The format of the EdgeImage method is:
1631 % Image *EdgeImage(const Image *image,const double radius,
1632 % ExceptionInfo *exception)
1634 % A description of each parameter follows:
1636 % o image: the image.
1638 % o radius: the radius of the pixel neighborhood.
1640 % o exception: return any errors or warnings in this structure.
1643 MagickExport Image *EdgeImage(const Image *image,const double radius,
1644 ExceptionInfo *exception)
1658 assert(image != (const Image *) NULL);
1659 assert(image->signature == MagickSignature);
1660 if (image->debug != MagickFalse)
1661 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1662 assert(exception != (ExceptionInfo *) NULL);
1663 assert(exception->signature == MagickSignature);
1664 width=GetOptimalKernelWidth1D(radius,0.5);
1665 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
1666 if (kernel == (double *) NULL)
1667 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1668 for (i=0; i < (long) (width*width); i++)
1670 kernel[i/2]=(double) (width*width-1.0);
1671 edge_image=ConvolveImage(image,width,kernel,exception);
1672 kernel=(double *) RelinquishMagickMemory(kernel);
1677 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1681 % E m b o s s I m a g e %
1685 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1687 % EmbossImage() returns a grayscale image with a three-dimensional effect.
1688 % We convolve the image with a Gaussian operator of the given radius and
1689 % standard deviation (sigma). For reasonable results, radius should be
1690 % larger than sigma. Use a radius of 0 and Emboss() selects a suitable
1693 % The format of the EmbossImage method is:
1695 % Image *EmbossImage(const Image *image,const double radius,
1696 % const double sigma,ExceptionInfo *exception)
1698 % A description of each parameter follows:
1700 % o image: the image.
1702 % o radius: the radius of the pixel neighborhood.
1704 % o sigma: the standard deviation of the Gaussian, in pixels.
1706 % o exception: return any errors or warnings in this structure.
1709 MagickExport Image *EmbossImage(const Image *image,const double radius,
1710 const double sigma,ExceptionInfo *exception)
1732 assert(image != (Image *) NULL);
1733 assert(image->signature == MagickSignature);
1734 if (image->debug != MagickFalse)
1735 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1736 assert(exception != (ExceptionInfo *) NULL);
1737 assert(exception->signature == MagickSignature);
1738 width=GetOptimalKernelWidth2D(radius,sigma);
1739 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
1740 if (kernel == (double *) NULL)
1741 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1744 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
1746 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
1748 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
1749 kernel[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*alpha/
1750 (2.0*MagickPI*MagickSigma*MagickSigma));
1757 emboss_image=ConvolveImage(image,width,kernel,exception);
1758 if (emboss_image != (Image *) NULL)
1759 (void) EqualizeImage(emboss_image);
1760 kernel=(double *) RelinquishMagickMemory(kernel);
1761 return(emboss_image);
1765 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1769 % G a u s s i a n B l u r I m a g e %
1773 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1775 % GaussianBlurImage() blurs an image. We convolve the image with a
1776 % Gaussian operator of the given radius and standard deviation (sigma).
1777 % For reasonable results, the radius should be larger than sigma. Use a
1778 % radius of 0 and GaussianBlurImage() selects a suitable radius for you
1780 % The format of the GaussianBlurImage method is:
1782 % Image *GaussianBlurImage(const Image *image,onst double radius,
1783 % const double sigma,ExceptionInfo *exception)
1784 % Image *GaussianBlurImageChannel(const Image *image,
1785 % const ChannelType channel,const double radius,const double sigma,
1786 % ExceptionInfo *exception)
1788 % A description of each parameter follows:
1790 % o image: the image.
1792 % o channel: the channel type.
1794 % o radius: the radius of the Gaussian, in pixels, not counting the center
1797 % o sigma: the standard deviation of the Gaussian, in pixels.
1799 % o exception: return any errors or warnings in this structure.
1803 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
1804 const double sigma,ExceptionInfo *exception)
1809 blur_image=GaussianBlurImageChannel(image,DefaultChannels,radius,sigma,
1814 MagickExport Image *GaussianBlurImageChannel(const Image *image,
1815 const ChannelType channel,const double radius,const double sigma,
1816 ExceptionInfo *exception)
1835 assert(image != (const Image *) NULL);
1836 assert(image->signature == MagickSignature);
1837 if (image->debug != MagickFalse)
1838 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1839 assert(exception != (ExceptionInfo *) NULL);
1840 assert(exception->signature == MagickSignature);
1841 width=GetOptimalKernelWidth2D(radius,sigma);
1842 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
1843 if (kernel == (double *) NULL)
1844 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1846 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
1848 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
1850 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
1851 kernel[i]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
1855 blur_image=ConvolveImageChannel(image,channel,width,kernel,exception);
1856 kernel=(double *) RelinquishMagickMemory(kernel);
1861 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1865 % M e d i a n F i l t e r I m a g e %
1869 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1871 % MedianFilterImage() applies a digital filter that improves the quality
1872 % of a noisy image. Each pixel is replaced by the median in a set of
1873 % neighboring pixels as defined by radius.
1875 % The algorithm was contributed by Mike Edmonds and implements an insertion
1876 % sort for selecting median color-channel values. For more on this algorithm
1877 % see "Skip Lists: A probabilistic Alternative to Balanced Trees" by William
1878 % Pugh in the June 1990 of Communications of the ACM.
1880 % The format of the MedianFilterImage method is:
1882 % Image *MedianFilterImage(const Image *image,const double radius,
1883 % ExceptionInfo *exception)
1885 % A description of each parameter follows:
1887 % o image: the image.
1889 % o radius: the radius of the pixel neighborhood.
1891 % o exception: return any errors or warnings in this structure.
1895 #define MedianListChannels 5
1897 typedef struct _MedianListNode
1905 typedef struct _MedianSkipList
1914 typedef struct _MedianPixelList
1922 lists[MedianListChannels];
1925 static MedianPixelList *DestroyMedianPixelList(MedianPixelList *pixel_list)
1930 if (pixel_list == (MedianPixelList *) NULL)
1931 return((MedianPixelList *) NULL);
1932 for (i=0; i < MedianListChannels; i++)
1933 if (pixel_list->lists[i].nodes != (MedianListNode *) NULL)
1934 pixel_list->lists[i].nodes=(MedianListNode *) RelinquishMagickMemory(
1935 pixel_list->lists[i].nodes);
1936 pixel_list=(MedianPixelList *) RelinquishAlignedMemory(pixel_list);
1940 static MedianPixelList **DestroyMedianPixelListThreadSet(
1941 MedianPixelList **pixel_list)
1946 assert(pixel_list != (MedianPixelList **) NULL);
1947 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
1948 if (pixel_list[i] != (MedianPixelList *) NULL)
1949 pixel_list[i]=DestroyMedianPixelList(pixel_list[i]);
1950 pixel_list=(MedianPixelList **) RelinquishAlignedMemory(pixel_list);
1954 static MedianPixelList *AcquireMedianPixelList(const unsigned long width)
1962 pixel_list=(MedianPixelList *) AcquireAlignedMemory(1,sizeof(*pixel_list));
1963 if (pixel_list == (MedianPixelList *) NULL)
1965 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
1966 pixel_list->center=width*width/2;
1967 for (i=0; i < MedianListChannels; i++)
1969 pixel_list->lists[i].nodes=(MedianListNode *) AcquireQuantumMemory(65537UL,
1970 sizeof(*pixel_list->lists[i].nodes));
1971 if (pixel_list->lists[i].nodes == (MedianListNode *) NULL)
1972 return(DestroyMedianPixelList(pixel_list));
1973 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
1974 sizeof(*pixel_list->lists[i].nodes));
1976 pixel_list->signature=MagickSignature;
1980 static MedianPixelList **AcquireMedianPixelListThreadSet(
1981 const unsigned long width)
1992 number_threads=GetOpenMPMaximumThreads();
1993 pixel_list=(MedianPixelList **) AcquireAlignedMemory(number_threads,
1994 sizeof(*pixel_list));
1995 if (pixel_list == (MedianPixelList **) NULL)
1996 return((MedianPixelList **) NULL);
1997 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
1998 for (i=0; i < (long) number_threads; i++)
2000 pixel_list[i]=AcquireMedianPixelList(width);
2001 if (pixel_list[i] == (MedianPixelList *) NULL)
2002 return(DestroyMedianPixelListThreadSet(pixel_list));
2007 static void AddNodeMedianPixelList(MedianPixelList *pixel_list,
2008 const long channel,const unsigned long color)
2013 register MedianSkipList
2021 Initialize the node.
2023 list=pixel_list->lists+channel;
2024 list->nodes[color].signature=pixel_list->signature;
2025 list->nodes[color].count=1;
2027 Determine where it belongs in the list.
2030 for (level=list->level; level >= 0; level--)
2032 while (list->nodes[search].next[level] < color)
2033 search=list->nodes[search].next[level];
2034 update[level]=search;
2037 Generate a pseudo-random level for this node.
2039 for (level=0; ; level++)
2041 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
2042 if ((pixel_list->seed & 0x300) != 0x300)
2047 if (level > (list->level+2))
2048 level=list->level+2;
2050 If we're raising the list's level, link back to the root node.
2052 while (level > list->level)
2055 update[list->level]=65536UL;
2058 Link the node into the skip-list.
2062 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
2063 list->nodes[update[level]].next[level]=color;
2065 while (level-- > 0);
2068 static MagickPixelPacket GetMedianPixelList(MedianPixelList *pixel_list)
2076 register MedianSkipList
2085 channels[MedianListChannels];
2088 Find the median value for each of the color.
2090 center=pixel_list->center;
2091 for (channel=0; channel < 5; channel++)
2093 list=pixel_list->lists+channel;
2098 color=list->nodes[color].next[0];
2099 count+=list->nodes[color].count;
2101 while (count <= center);
2102 channels[channel]=(unsigned short) color;
2104 GetMagickPixelPacket((const Image *) NULL,&pixel);
2105 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
2106 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
2107 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
2108 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
2109 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
2113 static inline void InsertMedianPixelList(const Image *image,
2114 const PixelPacket *pixel,const IndexPacket *indexes,
2115 MedianPixelList *pixel_list)
2123 index=ScaleQuantumToShort(pixel->red);
2124 signature=pixel_list->lists[0].nodes[index].signature;
2125 if (signature == pixel_list->signature)
2126 pixel_list->lists[0].nodes[index].count++;
2128 AddNodeMedianPixelList(pixel_list,0,index);
2129 index=ScaleQuantumToShort(pixel->green);
2130 signature=pixel_list->lists[1].nodes[index].signature;
2131 if (signature == pixel_list->signature)
2132 pixel_list->lists[1].nodes[index].count++;
2134 AddNodeMedianPixelList(pixel_list,1,index);
2135 index=ScaleQuantumToShort(pixel->blue);
2136 signature=pixel_list->lists[2].nodes[index].signature;
2137 if (signature == pixel_list->signature)
2138 pixel_list->lists[2].nodes[index].count++;
2140 AddNodeMedianPixelList(pixel_list,2,index);
2141 index=ScaleQuantumToShort(pixel->opacity);
2142 signature=pixel_list->lists[3].nodes[index].signature;
2143 if (signature == pixel_list->signature)
2144 pixel_list->lists[3].nodes[index].count++;
2146 AddNodeMedianPixelList(pixel_list,3,index);
2147 if (image->colorspace == CMYKColorspace)
2148 index=ScaleQuantumToShort(*indexes);
2149 signature=pixel_list->lists[4].nodes[index].signature;
2150 if (signature == pixel_list->signature)
2151 pixel_list->lists[4].nodes[index].count++;
2153 AddNodeMedianPixelList(pixel_list,4,index);
2156 static void ResetMedianPixelList(MedianPixelList *pixel_list)
2164 register MedianListNode
2167 register MedianSkipList
2171 Reset the skip-list.
2173 for (channel=0; channel < 5; channel++)
2175 list=pixel_list->lists+channel;
2176 root=list->nodes+65536UL;
2178 for (level=0; level < 9; level++)
2179 root->next[level]=65536UL;
2181 pixel_list->seed=pixel_list->signature++;
2184 MagickExport Image *MedianFilterImage(const Image *image,const double radius,
2185 ExceptionInfo *exception)
2187 #define MedianFilterImageTag "MedianFilter/Image"
2210 Initialize median image attributes.
2212 assert(image != (Image *) NULL);
2213 assert(image->signature == MagickSignature);
2214 if (image->debug != MagickFalse)
2215 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2216 assert(exception != (ExceptionInfo *) NULL);
2217 assert(exception->signature == MagickSignature);
2218 width=GetOptimalKernelWidth2D(radius,0.5);
2219 if ((image->columns < width) || (image->rows < width))
2220 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
2221 median_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2223 if (median_image == (Image *) NULL)
2224 return((Image *) NULL);
2225 if (SetImageStorageClass(median_image,DirectClass) == MagickFalse)
2227 InheritException(exception,&median_image->exception);
2228 median_image=DestroyImage(median_image);
2229 return((Image *) NULL);
2231 pixel_list=AcquireMedianPixelListThreadSet(width);
2232 if (pixel_list == (MedianPixelList **) NULL)
2234 median_image=DestroyImage(median_image);
2235 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2238 Median filter each image row.
2242 image_view=AcquireCacheView(image);
2243 median_view=AcquireCacheView(median_image);
2244 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2245 #pragma omp parallel for schedule(dynamic) shared(progress,status)
2247 for (y=0; y < (long) median_image->rows; y++)
2249 register const IndexPacket
2250 *__restrict indexes;
2252 register const PixelPacket
2255 register IndexPacket
2256 *__restrict median_indexes;
2262 register PixelPacket
2265 if (status == MagickFalse)
2267 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
2268 2L),image->columns+width,width,exception);
2269 q=QueueCacheViewAuthenticPixels(median_view,0,y,median_image->columns,1,
2271 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2276 indexes=GetCacheViewVirtualIndexQueue(image_view);
2277 median_indexes=GetCacheViewAuthenticIndexQueue(median_view);
2278 id=GetOpenMPThreadId();
2279 for (x=0; x < (long) median_image->columns; x++)
2284 register const PixelPacket
2287 register const IndexPacket
2296 ResetMedianPixelList(pixel_list[id]);
2297 for (v=0; v < (long) width; v++)
2299 for (u=0; u < (long) width; u++)
2300 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
2301 r+=image->columns+width;
2302 s+=image->columns+width;
2304 pixel=GetMedianPixelList(pixel_list[id]);
2305 SetPixelPacket(median_image,&pixel,q,median_indexes+x);
2309 if (SyncCacheViewAuthenticPixels(median_view,exception) == MagickFalse)
2311 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2316 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2317 #pragma omp critical (MagickCore_MedianFilterImage)
2319 proceed=SetImageProgress(image,MedianFilterImageTag,progress++,
2321 if (proceed == MagickFalse)
2325 median_view=DestroyCacheView(median_view);
2326 image_view=DestroyCacheView(image_view);
2327 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
2328 return(median_image);
2332 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2336 % M o t i o n B l u r I m a g e %
2340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2342 % MotionBlurImage() simulates motion blur. We convolve the image with a
2343 % Gaussian operator of the given radius and standard deviation (sigma).
2344 % For reasonable results, radius should be larger than sigma. Use a
2345 % radius of 0 and MotionBlurImage() selects a suitable radius for you.
2346 % Angle gives the angle of the blurring motion.
2348 % Andrew Protano contributed this effect.
2350 % The format of the MotionBlurImage method is:
2352 % Image *MotionBlurImage(const Image *image,const double radius,
2353 % const double sigma,const double angle,ExceptionInfo *exception)
2354 % Image *MotionBlurImageChannel(const Image *image,const ChannelType channel,
2355 % const double radius,const double sigma,const double angle,
2356 % ExceptionInfo *exception)
2358 % A description of each parameter follows:
2360 % o image: the image.
2362 % o channel: the channel type.
2364 % o radius: the radius of the Gaussian, in pixels, not counting the center
2365 % o radius: the radius of the Gaussian, in pixels, not counting
2368 % o sigma: the standard deviation of the Gaussian, in pixels.
2370 % o angle: Apply the effect along this angle.
2372 % o exception: return any errors or warnings in this structure.
2376 static double *GetMotionBlurKernel(unsigned long width,
2377 const MagickRealType sigma)
2379 #define KernelRank 3
2395 Generate a 1-D convolution kernel.
2397 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2398 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
2399 if (kernel == (double *) NULL)
2401 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
2402 bias=(long) (KernelRank*width);
2403 for (i=0; i < (long) bias; i++)
2405 alpha=exp((-((double) (i*i))/(double) (2.0*KernelRank*KernelRank*
2406 MagickSigma*MagickSigma)));
2407 kernel[i/KernelRank]+=(double) alpha/(MagickSQ2PI*sigma);
2410 for (i=0; i < (long) width; i++)
2411 normalize+=kernel[i];
2412 for (i=0; i < (long) width; i++)
2413 kernel[i]/=normalize;
2417 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
2418 const double sigma,const double angle,ExceptionInfo *exception)
2423 motion_blur=MotionBlurImageChannel(image,DefaultChannels,radius,sigma,angle,
2425 return(motion_blur);
2428 MagickExport Image *MotionBlurImageChannel(const Image *image,
2429 const ChannelType channel,const double radius,const double sigma,
2430 const double angle,ExceptionInfo *exception)
2432 typedef struct _OffsetInfo
2471 assert(image != (Image *) NULL);
2472 assert(image->signature == MagickSignature);
2473 if (image->debug != MagickFalse)
2474 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2475 assert(exception != (ExceptionInfo *) NULL);
2476 width=GetOptimalKernelWidth1D(radius,sigma);
2477 kernel=GetMotionBlurKernel(width,sigma);
2478 if (kernel == (double *) NULL)
2479 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2480 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2481 if (offset == (OffsetInfo *) NULL)
2483 kernel=(double *) RelinquishMagickMemory(kernel);
2484 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2486 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2487 if (blur_image == (Image *) NULL)
2489 kernel=(double *) RelinquishMagickMemory(kernel);
2490 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2491 return((Image *) NULL);
2493 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
2495 kernel=(double *) RelinquishMagickMemory(kernel);
2496 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2497 InheritException(exception,&blur_image->exception);
2498 blur_image=DestroyImage(blur_image);
2499 return((Image *) NULL);
2501 point.x=(double) width*sin(DegreesToRadians(angle));
2502 point.y=(double) width*cos(DegreesToRadians(angle));
2503 for (i=0; i < (long) width; i++)
2505 offset[i].x=(long) ((i*point.y)/hypot(point.x,point.y)+0.5);
2506 offset[i].y=(long) ((i*point.x)/hypot(point.x,point.y)+0.5);
2513 GetMagickPixelPacket(image,&zero);
2514 image_view=AcquireCacheView(image);
2515 blur_view=AcquireCacheView(blur_image);
2516 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2517 #pragma omp parallel for schedule(dynamic) shared(progress,status)
2519 for (y=0; y < (long) image->rows; y++)
2521 register IndexPacket
2522 *__restrict blur_indexes;
2527 register PixelPacket
2530 if (status == MagickFalse)
2532 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2534 if (q == (PixelPacket *) NULL)
2539 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
2540 for (x=0; x < (long) image->columns; x++)
2554 register const IndexPacket
2555 *__restrict indexes;
2559 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
2561 for (i=0; i < (long) width; i++)
2563 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2564 offset[i].y,&pixel,exception);
2565 qixel.red+=(*k)*pixel.red;
2566 qixel.green+=(*k)*pixel.green;
2567 qixel.blue+=(*k)*pixel.blue;
2568 qixel.opacity+=(*k)*pixel.opacity;
2569 if (image->colorspace == CMYKColorspace)
2571 indexes=GetCacheViewVirtualIndexQueue(image_view);
2572 qixel.index+=(*k)*(*indexes);
2576 if ((channel & RedChannel) != 0)
2577 q->red=RoundToQuantum(qixel.red);
2578 if ((channel & GreenChannel) != 0)
2579 q->green=RoundToQuantum(qixel.green);
2580 if ((channel & BlueChannel) != 0)
2581 q->blue=RoundToQuantum(qixel.blue);
2582 if ((channel & OpacityChannel) != 0)
2583 q->opacity=RoundToQuantum(qixel.opacity);
2584 if (((channel & IndexChannel) != 0) &&
2585 (image->colorspace == CMYKColorspace))
2586 blur_indexes[x]=(IndexPacket) RoundToQuantum(qixel.index);
2596 for (i=0; i < (long) width; i++)
2598 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2599 offset[i].y,&pixel,exception);
2600 alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
2601 qixel.red+=(*k)*alpha*pixel.red;
2602 qixel.green+=(*k)*alpha*pixel.green;
2603 qixel.blue+=(*k)*alpha*pixel.blue;
2604 qixel.opacity+=(*k)*pixel.opacity;
2605 if (image->colorspace == CMYKColorspace)
2607 indexes=GetCacheViewVirtualIndexQueue(image_view);
2608 qixel.index+=(*k)*alpha*(*indexes);
2613 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2614 if ((channel & RedChannel) != 0)
2615 q->red=RoundToQuantum(gamma*qixel.red);
2616 if ((channel & GreenChannel) != 0)
2617 q->green=RoundToQuantum(gamma*qixel.green);
2618 if ((channel & BlueChannel) != 0)
2619 q->blue=RoundToQuantum(gamma*qixel.blue);
2620 if ((channel & OpacityChannel) != 0)
2621 q->opacity=RoundToQuantum(qixel.opacity);
2622 if (((channel & IndexChannel) != 0) &&
2623 (image->colorspace == CMYKColorspace))
2624 blur_indexes[x]=(IndexPacket) RoundToQuantum(gamma*qixel.index);
2628 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2630 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2635 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2636 #pragma omp critical (MagickCore_MotionBlurImageChannel)
2638 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2639 if (proceed == MagickFalse)
2643 blur_view=DestroyCacheView(blur_view);
2644 image_view=DestroyCacheView(image_view);
2645 kernel=(double *) RelinquishMagickMemory(kernel);
2646 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2647 if (status == MagickFalse)
2648 blur_image=DestroyImage(blur_image);
2653 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2657 % P r e v i e w I m a g e %
2661 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2663 % PreviewImage() tiles 9 thumbnails of the specified image with an image
2664 % processing operation applied with varying parameters. This may be helpful
2665 % pin-pointing an appropriate parameter for a particular image processing
2668 % The format of the PreviewImages method is:
2670 % Image *PreviewImages(const Image *image,const PreviewType preview,
2671 % ExceptionInfo *exception)
2673 % A description of each parameter follows:
2675 % o image: the image.
2677 % o preview: the image processing operation.
2679 % o exception: return any errors or warnings in this structure.
2682 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
2683 ExceptionInfo *exception)
2685 #define NumberTiles 9
2686 #define PreviewImageTag "Preview/Image"
2687 #define DefaultPreviewGeometry "204x204+10+10"
2690 factor[MaxTextExtent],
2691 label[MaxTextExtent];
2733 Open output image file.
2735 assert(image != (Image *) NULL);
2736 assert(image->signature == MagickSignature);
2737 if (image->debug != MagickFalse)
2738 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2742 preview_info=AcquireImageInfo();
2743 SetGeometry(image,&geometry);
2744 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2745 &geometry.width,&geometry.height);
2746 images=NewImageList();
2748 GetQuantizeInfo(&quantize_info);
2754 for (i=0; i < NumberTiles; i++)
2756 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2757 if (thumbnail == (Image *) NULL)
2759 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2761 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
2762 if (i == (NumberTiles/2))
2764 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
2765 AppendImageToList(&images,thumbnail);
2773 preview_image=RotateImage(thumbnail,degrees,exception);
2774 (void) FormatMagickString(label,MaxTextExtent,"rotate %g",degrees);
2780 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
2781 (void) FormatMagickString(label,MaxTextExtent,"shear %gx%g",
2782 degrees,2.0*degrees);
2787 x=(long) ((i+1)*thumbnail->columns)/NumberTiles;
2788 y=(long) ((i+1)*thumbnail->rows)/NumberTiles;
2789 preview_image=RollImage(thumbnail,x,y,exception);
2790 (void) FormatMagickString(label,MaxTextExtent,"roll %ldx%ld",x,y);
2795 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2796 if (preview_image == (Image *) NULL)
2798 (void) FormatMagickString(factor,MaxTextExtent,"100,100,%g",
2800 (void) ModulateImage(preview_image,factor);
2801 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
2804 case SaturationPreview:
2806 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2807 if (preview_image == (Image *) NULL)
2809 (void) FormatMagickString(factor,MaxTextExtent,"100,%g",2.0*percentage);
2810 (void) ModulateImage(preview_image,factor);
2811 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
2814 case BrightnessPreview:
2816 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2817 if (preview_image == (Image *) NULL)
2819 (void) FormatMagickString(factor,MaxTextExtent,"%g",2.0*percentage);
2820 (void) ModulateImage(preview_image,factor);
2821 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
2827 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2828 if (preview_image == (Image *) NULL)
2831 (void) GammaImageChannel(preview_image,DefaultChannels,gamma);
2832 (void) FormatMagickString(label,MaxTextExtent,"gamma %g",gamma);
2837 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2838 if (preview_image != (Image *) NULL)
2839 for (x=0; x < i; x++)
2840 (void) ContrastImage(preview_image,MagickTrue);
2841 (void) FormatMagickString(label,MaxTextExtent,"contrast (%ld)",i+1);
2846 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2847 if (preview_image == (Image *) NULL)
2849 for (x=0; x < i; x++)
2850 (void) ContrastImage(preview_image,MagickFalse);
2851 (void) FormatMagickString(label,MaxTextExtent,"+contrast (%ld)",i+1);
2854 case GrayscalePreview:
2856 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2857 if (preview_image == (Image *) NULL)
2860 quantize_info.number_colors=colors;
2861 quantize_info.colorspace=GRAYColorspace;
2862 (void) QuantizeImage(&quantize_info,preview_image);
2863 (void) FormatMagickString(label,MaxTextExtent,
2864 "-colorspace gray -colors %ld",colors);
2867 case QuantizePreview:
2869 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2870 if (preview_image == (Image *) NULL)
2873 quantize_info.number_colors=colors;
2874 (void) QuantizeImage(&quantize_info,preview_image);
2875 (void) FormatMagickString(label,MaxTextExtent,"colors %ld",colors);
2878 case DespecklePreview:
2880 for (x=0; x < (i-1); x++)
2882 preview_image=DespeckleImage(thumbnail,exception);
2883 if (preview_image == (Image *) NULL)
2885 thumbnail=DestroyImage(thumbnail);
2886 thumbnail=preview_image;
2888 preview_image=DespeckleImage(thumbnail,exception);
2889 if (preview_image == (Image *) NULL)
2891 (void) FormatMagickString(label,MaxTextExtent,"despeckle (%ld)",i+1);
2894 case ReduceNoisePreview:
2896 preview_image=ReduceNoiseImage(thumbnail,radius,exception);
2897 (void) FormatMagickString(label,MaxTextExtent,"noise %g",radius);
2900 case AddNoisePreview:
2906 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
2911 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
2916 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
2921 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
2926 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
2931 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
2936 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
2940 preview_image=ReduceNoiseImage(thumbnail,(double) i,exception);
2941 (void) FormatMagickString(label,MaxTextExtent,"+noise %s",factor);
2944 case SharpenPreview:
2946 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
2947 (void) FormatMagickString(label,MaxTextExtent,"sharpen %gx%g",radius,
2953 preview_image=BlurImage(thumbnail,radius,sigma,exception);
2954 (void) FormatMagickString(label,MaxTextExtent,"blur %gx%g",radius,
2958 case ThresholdPreview:
2960 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2961 if (preview_image == (Image *) NULL)
2963 (void) BilevelImage(thumbnail,
2964 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2965 (void) FormatMagickString(label,MaxTextExtent,"threshold %g",
2966 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2969 case EdgeDetectPreview:
2971 preview_image=EdgeImage(thumbnail,radius,exception);
2972 (void) FormatMagickString(label,MaxTextExtent,"edge %g",radius);
2977 preview_image=SpreadImage(thumbnail,radius,exception);
2978 (void) FormatMagickString(label,MaxTextExtent,"spread %g",radius+0.5);
2981 case SolarizePreview:
2983 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2984 if (preview_image == (Image *) NULL)
2986 (void) SolarizeImage(preview_image,(double) QuantumRange*
2988 (void) FormatMagickString(label,MaxTextExtent,"solarize %g",
2989 (QuantumRange*percentage)/100.0);
2995 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2997 (void) FormatMagickString(label,MaxTextExtent,"shade %gx%g",degrees,
3003 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3004 if (preview_image == (Image *) NULL)
3006 geometry.width=(unsigned long) (2*i+2);
3007 geometry.height=(unsigned long) (2*i+2);
3010 (void) RaiseImage(preview_image,&geometry,MagickTrue);
3011 (void) FormatMagickString(label,MaxTextExtent,"raise %lux%lu%+ld%+ld",
3012 geometry.width,geometry.height,geometry.x,geometry.y);
3015 case SegmentPreview:
3017 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3018 if (preview_image == (Image *) NULL)
3021 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
3023 (void) FormatMagickString(label,MaxTextExtent,"segment %gx%g",
3024 threshold,threshold);
3029 preview_image=SwirlImage(thumbnail,degrees,exception);
3030 (void) FormatMagickString(label,MaxTextExtent,"swirl %g",degrees);
3034 case ImplodePreview:
3037 preview_image=ImplodeImage(thumbnail,degrees,exception);
3038 (void) FormatMagickString(label,MaxTextExtent,"implode %g",degrees);
3044 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
3045 (void) FormatMagickString(label,MaxTextExtent,"wave %gx%g",0.5*degrees,
3049 case OilPaintPreview:
3051 preview_image=OilPaintImage(thumbnail,(double) radius,exception);
3052 (void) FormatMagickString(label,MaxTextExtent,"paint %g",radius);
3055 case CharcoalDrawingPreview:
3057 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
3059 (void) FormatMagickString(label,MaxTextExtent,"charcoal %gx%g",radius,
3066 filename[MaxTextExtent];
3074 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3075 if (preview_image == (Image *) NULL)
3077 preview_info->quality=(unsigned long) percentage;
3078 (void) FormatMagickString(factor,MaxTextExtent,"%lu",
3079 preview_info->quality);
3080 file=AcquireUniqueFileResource(filename);
3083 (void) FormatMagickString(preview_image->filename,MaxTextExtent,
3084 "jpeg:%s",filename);
3085 status=WriteImage(preview_info,preview_image);
3086 if (status != MagickFalse)
3091 (void) CopyMagickString(preview_info->filename,
3092 preview_image->filename,MaxTextExtent);
3093 quality_image=ReadImage(preview_info,exception);
3094 if (quality_image != (Image *) NULL)
3096 preview_image=DestroyImage(preview_image);
3097 preview_image=quality_image;
3100 (void) RelinquishUniqueFileResource(preview_image->filename);
3101 if ((GetBlobSize(preview_image)/1024) >= 1024)
3102 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gmb ",
3103 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
3106 if (GetBlobSize(preview_image) >= 1024)
3107 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gkb ",
3108 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
3111 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%lub ",
3112 factor,(unsigned long) GetBlobSize(thumbnail));
3116 thumbnail=DestroyImage(thumbnail);
3120 if (preview_image == (Image *) NULL)
3122 (void) DeleteImageProperty(preview_image,"label");
3123 (void) SetImageProperty(preview_image,"label",label);
3124 AppendImageToList(&images,preview_image);
3125 proceed=SetImageProgress(image,PreviewImageTag,i,NumberTiles);
3126 if (proceed == MagickFalse)
3129 if (images == (Image *) NULL)
3131 preview_info=DestroyImageInfo(preview_info);
3132 return((Image *) NULL);
3137 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
3138 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
3139 montage_info->shadow=MagickTrue;
3140 (void) CloneString(&montage_info->tile,"3x3");
3141 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
3142 (void) CloneString(&montage_info->frame,DefaultTileFrame);
3143 montage_image=MontageImages(images,montage_info,exception);
3144 montage_info=DestroyMontageInfo(montage_info);
3145 images=DestroyImageList(images);
3146 if (montage_image == (Image *) NULL)
3147 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3148 if (montage_image->montage != (char *) NULL)
3151 Free image directory.
3153 montage_image->montage=(char *) RelinquishMagickMemory(
3154 montage_image->montage);
3155 if (image->directory != (char *) NULL)
3156 montage_image->directory=(char *) RelinquishMagickMemory(
3157 montage_image->directory);
3159 preview_info=DestroyImageInfo(preview_info);
3160 return(montage_image);
3164 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3168 % R a d i a l B l u r I m a g e %
3172 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3174 % RadialBlurImage() applies a radial blur to the image.
3176 % Andrew Protano contributed this effect.
3178 % The format of the RadialBlurImage method is:
3180 % Image *RadialBlurImage(const Image *image,const double angle,
3181 % ExceptionInfo *exception)
3182 % Image *RadialBlurImageChannel(const Image *image,const ChannelType channel,
3183 % const double angle,ExceptionInfo *exception)
3185 % A description of each parameter follows:
3187 % o image: the image.
3189 % o channel: the channel type.
3191 % o angle: the angle of the radial blur.
3193 % o exception: return any errors or warnings in this structure.
3197 MagickExport Image *RadialBlurImage(const Image *image,const double angle,
3198 ExceptionInfo *exception)
3203 blur_image=RadialBlurImageChannel(image,DefaultChannels,angle,exception);
3207 MagickExport Image *RadialBlurImageChannel(const Image *image,
3208 const ChannelType channel,const double angle,ExceptionInfo *exception)
3244 Allocate blur image.
3246 assert(image != (Image *) NULL);
3247 assert(image->signature == MagickSignature);
3248 if (image->debug != MagickFalse)
3249 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3250 assert(exception != (ExceptionInfo *) NULL);
3251 assert(exception->signature == MagickSignature);
3252 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3253 if (blur_image == (Image *) NULL)
3254 return((Image *) NULL);
3255 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3257 InheritException(exception,&blur_image->exception);
3258 blur_image=DestroyImage(blur_image);
3259 return((Image *) NULL);
3261 blur_center.x=(double) image->columns/2.0;
3262 blur_center.y=(double) image->rows/2.0;
3263 blur_radius=hypot(blur_center.x,blur_center.y);
3264 n=(unsigned long) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+
3266 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
3267 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3268 sizeof(*cos_theta));
3269 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3270 sizeof(*sin_theta));
3271 if ((cos_theta == (MagickRealType *) NULL) ||
3272 (sin_theta == (MagickRealType *) NULL))
3274 blur_image=DestroyImage(blur_image);
3275 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3277 offset=theta*(MagickRealType) (n-1)/2.0;
3278 for (i=0; i < (long) n; i++)
3280 cos_theta[i]=cos((double) (theta*i-offset));
3281 sin_theta[i]=sin((double) (theta*i-offset));
3288 GetMagickPixelPacket(image,&zero);
3289 image_view=AcquireCacheView(image);
3290 blur_view=AcquireCacheView(blur_image);
3291 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3292 #pragma omp parallel for schedule(dynamic) shared(progress,status)
3294 for (y=0; y < (long) blur_image->rows; y++)
3296 register const IndexPacket
3297 *__restrict indexes;
3299 register IndexPacket
3300 *__restrict blur_indexes;
3305 register PixelPacket
3308 if (status == MagickFalse)
3310 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3312 if (q == (PixelPacket *) NULL)
3317 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
3318 for (x=0; x < (long) blur_image->columns; x++)
3339 center.x=(double) x-blur_center.x;
3340 center.y=(double) y-blur_center.y;
3341 radius=hypot((double) center.x,center.y);
3346 step=(unsigned long) (blur_radius/radius);
3355 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
3357 for (i=0; i < (long) n; i+=step)
3359 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
3360 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
3361 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
3363 qixel.red+=pixel.red;
3364 qixel.green+=pixel.green;
3365 qixel.blue+=pixel.blue;
3366 qixel.opacity+=pixel.opacity;
3367 if (image->colorspace == CMYKColorspace)
3369 indexes=GetCacheViewVirtualIndexQueue(image_view);
3370 qixel.index+=(*indexes);
3374 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3376 if ((channel & RedChannel) != 0)
3377 q->red=RoundToQuantum(normalize*qixel.red);
3378 if ((channel & GreenChannel) != 0)
3379 q->green=RoundToQuantum(normalize*qixel.green);
3380 if ((channel & BlueChannel) != 0)
3381 q->blue=RoundToQuantum(normalize*qixel.blue);
3382 if ((channel & OpacityChannel) != 0)
3383 q->opacity=RoundToQuantum(normalize*qixel.opacity);
3384 if (((channel & IndexChannel) != 0) &&
3385 (image->colorspace == CMYKColorspace))
3386 blur_indexes[x]=(IndexPacket) RoundToQuantum(normalize*qixel.index);
3396 for (i=0; i < (long) n; i+=step)
3398 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
3399 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
3400 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
3402 alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
3403 qixel.red+=alpha*pixel.red;
3404 qixel.green+=alpha*pixel.green;
3405 qixel.blue+=alpha*pixel.blue;
3406 qixel.opacity+=pixel.opacity;
3407 if (image->colorspace == CMYKColorspace)
3409 indexes=GetCacheViewVirtualIndexQueue(image_view);
3410 qixel.index+=alpha*(*indexes);
3415 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3416 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3418 if ((channel & RedChannel) != 0)
3419 q->red=RoundToQuantum(gamma*qixel.red);
3420 if ((channel & GreenChannel) != 0)
3421 q->green=RoundToQuantum(gamma*qixel.green);
3422 if ((channel & BlueChannel) != 0)
3423 q->blue=RoundToQuantum(gamma*qixel.blue);
3424 if ((channel & OpacityChannel) != 0)
3425 q->opacity=RoundToQuantum(normalize*qixel.opacity);
3426 if (((channel & IndexChannel) != 0) &&
3427 (image->colorspace == CMYKColorspace))
3428 blur_indexes[x]=(IndexPacket) RoundToQuantum(gamma*qixel.index);
3432 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3434 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3439 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3440 #pragma omp critical (MagickCore_RadialBlurImageChannel)
3442 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3443 if (proceed == MagickFalse)
3447 blur_view=DestroyCacheView(blur_view);
3448 image_view=DestroyCacheView(image_view);
3449 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
3450 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
3451 if (status == MagickFalse)
3452 blur_image=DestroyImage(blur_image);
3457 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3461 % R e d u c e N o i s e I m a g e %
3465 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3467 % ReduceNoiseImage() smooths the contours of an image while still preserving
3468 % edge information. The algorithm works by replacing each pixel with its
3469 % neighbor closest in value. A neighbor is defined by radius. Use a radius
3470 % of 0 and ReduceNoise() selects a suitable radius for you.
3472 % The format of the ReduceNoiseImage method is:
3474 % Image *ReduceNoiseImage(const Image *image,const double radius,
3475 % ExceptionInfo *exception)
3477 % A description of each parameter follows:
3479 % o image: the image.
3481 % o radius: the radius of the pixel neighborhood.
3483 % o exception: return any errors or warnings in this structure.
3487 static MagickPixelPacket GetNonpeakMedianPixelList(MedianPixelList *pixel_list)
3495 register MedianSkipList
3509 Finds the median value for each of the color.
3511 center=pixel_list->center;
3512 for (channel=0; channel < 5; channel++)
3514 list=pixel_list->lists+channel;
3516 next=list->nodes[color].next[0];
3522 next=list->nodes[color].next[0];
3523 count+=list->nodes[color].count;
3525 while (count <= center);
3526 if ((previous == 65536UL) && (next != 65536UL))
3529 if ((previous != 65536UL) && (next == 65536UL))
3531 channels[channel]=(unsigned short) color;
3533 GetMagickPixelPacket((const Image *) NULL,&pixel);
3534 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
3535 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
3536 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
3537 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
3538 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
3542 MagickExport Image *ReduceNoiseImage(const Image *image,const double radius,
3543 ExceptionInfo *exception)
3545 #define ReduceNoiseImageTag "ReduceNoise/Image"
3568 Initialize noise image attributes.
3570 assert(image != (Image *) NULL);
3571 assert(image->signature == MagickSignature);
3572 if (image->debug != MagickFalse)
3573 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3574 assert(exception != (ExceptionInfo *) NULL);
3575 assert(exception->signature == MagickSignature);
3576 width=GetOptimalKernelWidth2D(radius,0.5);
3577 if ((image->columns < width) || (image->rows < width))
3578 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
3579 noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3581 if (noise_image == (Image *) NULL)
3582 return((Image *) NULL);
3583 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
3585 InheritException(exception,&noise_image->exception);
3586 noise_image=DestroyImage(noise_image);
3587 return((Image *) NULL);
3589 pixel_list=AcquireMedianPixelListThreadSet(width);
3590 if (pixel_list == (MedianPixelList **) NULL)
3592 noise_image=DestroyImage(noise_image);
3593 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3600 image_view=AcquireCacheView(image);
3601 noise_view=AcquireCacheView(noise_image);
3602 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3603 #pragma omp parallel for schedule(dynamic) shared(progress,status)
3605 for (y=0; y < (long) noise_image->rows; y++)
3607 register const IndexPacket
3608 *__restrict indexes;
3610 register const PixelPacket
3613 register IndexPacket
3614 *__restrict noise_indexes;
3620 register PixelPacket
3623 if (status == MagickFalse)
3625 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
3626 2L),image->columns+width,width,exception);
3627 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
3629 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3634 indexes=GetCacheViewVirtualIndexQueue(image_view);
3635 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
3636 id=GetOpenMPThreadId();
3637 for (x=0; x < (long) noise_image->columns; x++)
3642 register const PixelPacket
3645 register const IndexPacket
3654 ResetMedianPixelList(pixel_list[id]);
3655 for (v=0; v < (long) width; v++)
3657 for (u=0; u < (long) width; u++)
3658 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
3659 r+=image->columns+width;
3660 s+=image->columns+width;
3662 pixel=GetNonpeakMedianPixelList(pixel_list[id]);
3663 SetPixelPacket(noise_image,&pixel,q,noise_indexes+x);
3667 if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
3669 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3674 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3675 #pragma omp critical (MagickCore_ReduceNoiseImage)
3677 proceed=SetImageProgress(image,ReduceNoiseImageTag,progress++,
3679 if (proceed == MagickFalse)
3683 noise_view=DestroyCacheView(noise_view);
3684 image_view=DestroyCacheView(image_view);
3685 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
3686 return(noise_image);
3690 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3694 % S e l e c t i v e B l u r I m a g e %
3698 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3700 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3701 % It is similar to the unsharpen mask that sharpens everything with contrast
3702 % above a certain threshold.
3704 % The format of the SelectiveBlurImage method is:
3706 % Image *SelectiveBlurImage(const Image *image,const double radius,
3707 % const double sigma,const double threshold,ExceptionInfo *exception)
3708 % Image *SelectiveBlurImageChannel(const Image *image,
3709 % const ChannelType channel,const double radius,const double sigma,
3710 % const double threshold,ExceptionInfo *exception)
3712 % A description of each parameter follows:
3714 % o image: the image.
3716 % o channel: the channel type.
3718 % o radius: the radius of the Gaussian, in pixels, not counting the center
3721 % o sigma: the standard deviation of the Gaussian, in pixels.
3723 % o threshold: only pixels within this contrast threshold are included
3724 % in the blur operation.
3726 % o exception: return any errors or warnings in this structure.
3730 static inline MagickBooleanType SelectiveContrast(const PixelPacket *p,
3731 const PixelPacket *q,const double threshold)
3733 if (fabs(PixelIntensity(p)-PixelIntensity(q)) < threshold)
3735 return(MagickFalse);
3738 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
3739 const double sigma,const double threshold,ExceptionInfo *exception)
3744 blur_image=SelectiveBlurImageChannel(image,DefaultChannels,radius,sigma,
3745 threshold,exception);
3749 MagickExport Image *SelectiveBlurImageChannel(const Image *image,
3750 const ChannelType channel,const double radius,const double sigma,
3751 const double threshold,ExceptionInfo *exception)
3753 #define SelectiveBlurImageTag "SelectiveBlur/Image"
3788 Initialize blur image attributes.
3790 assert(image != (Image *) NULL);
3791 assert(image->signature == MagickSignature);
3792 if (image->debug != MagickFalse)
3793 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3794 assert(exception != (ExceptionInfo *) NULL);
3795 assert(exception->signature == MagickSignature);
3796 width=GetOptimalKernelWidth1D(radius,sigma);
3797 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
3798 if (kernel == (double *) NULL)
3799 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3801 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
3803 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
3805 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
3806 kernel[i]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
3810 if (image->debug != MagickFalse)
3813 format[MaxTextExtent],
3820 register const double
3823 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
3824 " SelectiveBlurImage with %ldx%ld kernel:",width,width);
3825 message=AcquireString("");
3827 for (v=0; v < (long) width; v++)
3830 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
3831 (void) ConcatenateString(&message,format);
3832 for (u=0; u < (long) width; u++)
3834 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
3835 (void) ConcatenateString(&message,format);
3837 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3839 message=DestroyString(message);
3841 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3842 if (blur_image == (Image *) NULL)
3843 return((Image *) NULL);
3844 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3846 InheritException(exception,&blur_image->exception);
3847 blur_image=DestroyImage(blur_image);
3848 return((Image *) NULL);
3851 Threshold blur image.
3855 GetMagickPixelPacket(image,&zero);
3857 image_view=AcquireCacheView(image);
3858 blur_view=AcquireCacheView(blur_image);
3859 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3860 #pragma omp parallel for schedule(dynamic) shared(progress,status)
3862 for (y=0; y < (long) image->rows; y++)
3870 register const IndexPacket
3871 *__restrict indexes;
3873 register const PixelPacket
3876 register IndexPacket
3877 *__restrict blur_indexes;
3882 register PixelPacket
3885 if (status == MagickFalse)
3887 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
3888 2L),image->columns+width,width,exception);
3889 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3891 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3896 indexes=GetCacheViewVirtualIndexQueue(image_view);
3897 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
3898 for (x=0; x < (long) image->columns; x++)
3907 register const double
3917 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
3919 for (v=0; v < (long) width; v++)
3921 for (u=0; u < (long) width; u++)
3923 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
3925 pixel.red+=(*k)*(p+u+j)->red;
3926 pixel.green+=(*k)*(p+u+j)->green;
3927 pixel.blue+=(*k)*(p+u+j)->blue;
3932 j+=image->columns+width;
3936 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3937 if ((channel & RedChannel) != 0)
3938 q->red=RoundToQuantum(gamma*pixel.red+bias);
3939 if ((channel & GreenChannel) != 0)
3940 q->green=RoundToQuantum(gamma*pixel.green+bias);
3941 if ((channel & BlueChannel) != 0)
3942 q->blue=RoundToQuantum(gamma*pixel.blue+bias);
3944 if ((channel & OpacityChannel) != 0)
3948 for (v=0; v < (long) width; v++)
3950 for (u=0; u < (long) width; u++)
3952 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
3954 pixel.opacity+=(*k)*(p+u+j)->opacity;
3959 j+=image->columns+width;
3963 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3965 q->opacity=RoundToQuantum(gamma*pixel.opacity+bias);
3968 if (((channel & IndexChannel) != 0) &&
3969 (image->colorspace == CMYKColorspace))
3973 for (v=0; v < (long) width; v++)
3975 for (u=0; u < (long) width; u++)
3977 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
3979 pixel.index+=(*k)*indexes[x+u+j];
3984 j+=image->columns+width;
3988 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3990 blur_indexes[x]=RoundToQuantum(gamma*pixel.index+bias);
3999 for (v=0; v < (long) width; v++)
4001 for (u=0; u < (long) width; u++)
4003 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4005 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
4007 pixel.red+=(*k)*alpha*(p+u+j)->red;
4008 pixel.green+=(*k)*alpha*(p+u+j)->green;
4009 pixel.blue+=(*k)*alpha*(p+u+j)->blue;
4010 pixel.opacity+=(*k)*(p+u+j)->opacity;
4015 j+=image->columns+width;
4019 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4020 if ((channel & RedChannel) != 0)
4021 q->red=RoundToQuantum(gamma*pixel.red+bias);
4022 if ((channel & GreenChannel) != 0)
4023 q->green=RoundToQuantum(gamma*pixel.green+bias);
4024 if ((channel & BlueChannel) != 0)
4025 q->blue=RoundToQuantum(gamma*pixel.blue+bias);
4027 if ((channel & OpacityChannel) != 0)
4031 for (v=0; v < (long) width; v++)
4033 for (u=0; u < (long) width; u++)
4035 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4037 pixel.opacity+=(*k)*(p+u+j)->opacity;
4042 j+=image->columns+width;
4046 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4048 q->opacity=RoundToQuantum(pixel.opacity+bias);
4051 if (((channel & IndexChannel) != 0) &&
4052 (image->colorspace == CMYKColorspace))
4056 for (v=0; v < (long) width; v++)
4058 for (u=0; u < (long) width; u++)
4060 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4062 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
4064 pixel.index+=(*k)*alpha*indexes[x+u+j];
4069 j+=image->columns+width;
4073 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4075 blur_indexes[x]=RoundToQuantum(gamma*pixel.index+bias);
4082 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
4083 if (sync == MagickFalse)
4085 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4090 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4091 #pragma omp critical (MagickCore_SelectiveBlurImageChannel)
4093 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
4095 if (proceed == MagickFalse)
4099 blur_image->type=image->type;
4100 blur_view=DestroyCacheView(blur_view);
4101 image_view=DestroyCacheView(image_view);
4102 kernel=(double *) RelinquishMagickMemory(kernel);
4103 if (status == MagickFalse)
4104 blur_image=DestroyImage(blur_image);
4109 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4113 % S h a d e I m a g e %
4117 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4119 % ShadeImage() shines a distant light on an image to create a
4120 % three-dimensional effect. You control the positioning of the light with
4121 % azimuth and elevation; azimuth is measured in degrees off the x axis
4122 % and elevation is measured in pixels above the Z axis.
4124 % The format of the ShadeImage method is:
4126 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4127 % const double azimuth,const double elevation,ExceptionInfo *exception)
4129 % A description of each parameter follows:
4131 % o image: the image.
4133 % o gray: A value other than zero shades the intensity of each pixel.
4135 % o azimuth, elevation: Define the light source direction.
4137 % o exception: return any errors or warnings in this structure.
4140 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4141 const double azimuth,const double elevation,ExceptionInfo *exception)
4143 #define ShadeImageTag "Shade/Image"
4163 Initialize shaded image attributes.
4165 assert(image != (const Image *) NULL);
4166 assert(image->signature == MagickSignature);
4167 if (image->debug != MagickFalse)
4168 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4169 assert(exception != (ExceptionInfo *) NULL);
4170 assert(exception->signature == MagickSignature);
4171 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4172 if (shade_image == (Image *) NULL)
4173 return((Image *) NULL);
4174 if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
4176 InheritException(exception,&shade_image->exception);
4177 shade_image=DestroyImage(shade_image);
4178 return((Image *) NULL);
4181 Compute the light vector.
4183 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
4184 cos(DegreesToRadians(elevation));
4185 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
4186 cos(DegreesToRadians(elevation));
4187 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
4193 image_view=AcquireCacheView(image);
4194 shade_view=AcquireCacheView(shade_image);
4195 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4196 #pragma omp parallel for schedule(dynamic) shared(progress,status)
4198 for (y=0; y < (long) image->rows; y++)
4208 register const PixelPacket
4217 register PixelPacket
4220 if (status == MagickFalse)
4222 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
4223 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
4225 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4231 Shade this row of pixels.
4233 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
4235 s1=s0+image->columns+2;
4236 s2=s1+image->columns+2;
4237 for (x=0; x < (long) image->columns; x++)
4240 Determine the surface normal and compute shading.
4242 normal.x=(double) (PixelIntensity(s0-1)+PixelIntensity(s1-1)+
4243 PixelIntensity(s2-1)-PixelIntensity(s0+1)-PixelIntensity(s1+1)-
4244 PixelIntensity(s2+1));
4245 normal.y=(double) (PixelIntensity(s2-1)+PixelIntensity(s2)+
4246 PixelIntensity(s2+1)-PixelIntensity(s0-1)-PixelIntensity(s0)-
4247 PixelIntensity(s0+1));
4248 if ((normal.x == 0.0) && (normal.y == 0.0))
4253 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
4254 if (distance > MagickEpsilon)
4257 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
4258 if (normal_distance > (MagickEpsilon*MagickEpsilon))
4259 shade=distance/sqrt((double) normal_distance);
4262 if (gray != MagickFalse)
4264 q->red=(Quantum) shade;
4265 q->green=(Quantum) shade;
4266 q->blue=(Quantum) shade;
4270 q->red=RoundToQuantum(QuantumScale*shade*s1->red);
4271 q->green=RoundToQuantum(QuantumScale*shade*s1->green);
4272 q->blue=RoundToQuantum(QuantumScale*shade*s1->blue);
4274 q->opacity=s1->opacity;
4280 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
4282 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4287 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4288 #pragma omp critical (MagickCore_ShadeImage)
4290 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
4291 if (proceed == MagickFalse)
4295 shade_view=DestroyCacheView(shade_view);
4296 image_view=DestroyCacheView(image_view);
4297 if (status == MagickFalse)
4298 shade_image=DestroyImage(shade_image);
4299 return(shade_image);
4303 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4307 % S h a r p e n I m a g e %
4311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4313 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
4314 % operator of the given radius and standard deviation (sigma). For
4315 % reasonable results, radius should be larger than sigma. Use a radius of 0
4316 % and SharpenImage() selects a suitable radius for you.
4318 % Using a separable kernel would be faster, but the negative weights cancel
4319 % out on the corners of the kernel producing often undesirable ringing in the
4320 % filtered result; this can be avoided by using a 2D gaussian shaped image
4321 % sharpening kernel instead.
4323 % The format of the SharpenImage method is:
4325 % Image *SharpenImage(const Image *image,const double radius,
4326 % const double sigma,ExceptionInfo *exception)
4327 % Image *SharpenImageChannel(const Image *image,const ChannelType channel,
4328 % const double radius,const double sigma,ExceptionInfo *exception)
4330 % A description of each parameter follows:
4332 % o image: the image.
4334 % o channel: the channel type.
4336 % o radius: the radius of the Gaussian, in pixels, not counting the center
4339 % o sigma: the standard deviation of the Laplacian, in pixels.
4341 % o exception: return any errors or warnings in this structure.
4345 MagickExport Image *SharpenImage(const Image *image,const double radius,
4346 const double sigma,ExceptionInfo *exception)
4351 sharp_image=SharpenImageChannel(image,DefaultChannels,radius,sigma,exception);
4352 return(sharp_image);
4355 MagickExport Image *SharpenImageChannel(const Image *image,
4356 const ChannelType channel,const double radius,const double sigma,
4357 ExceptionInfo *exception)
4377 assert(image != (const Image *) NULL);
4378 assert(image->signature == MagickSignature);
4379 if (image->debug != MagickFalse)
4380 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4381 assert(exception != (ExceptionInfo *) NULL);
4382 assert(exception->signature == MagickSignature);
4383 width=GetOptimalKernelWidth2D(radius,sigma);
4384 kernel=(double *) AcquireQuantumMemory((size_t) width*width,sizeof(*kernel));
4385 if (kernel == (double *) NULL)
4386 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4389 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
4391 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
4393 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
4394 kernel[i]=(double) (-alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
4395 normalize+=kernel[i];
4399 kernel[i/2]=(double) ((-2.0)*normalize);
4400 sharp_image=ConvolveImageChannel(image,channel,width,kernel,exception);
4401 kernel=(double *) RelinquishMagickMemory(kernel);
4402 return(sharp_image);
4406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4410 % S p r e a d I m a g e %
4414 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4416 % SpreadImage() is a special effects method that randomly displaces each
4417 % pixel in a block defined by the radius parameter.
4419 % The format of the SpreadImage method is:
4421 % Image *SpreadImage(const Image *image,const double radius,
4422 % ExceptionInfo *exception)
4424 % A description of each parameter follows:
4426 % o image: the image.
4428 % o radius: Choose a random pixel in a neighborhood of this extent.
4430 % o exception: return any errors or warnings in this structure.
4433 MagickExport Image *SpreadImage(const Image *image,const double radius,
4434 ExceptionInfo *exception)
4436 #define SpreadImageTag "Spread/Image"
4464 Initialize spread image attributes.
4466 assert(image != (Image *) NULL);
4467 assert(image->signature == MagickSignature);
4468 if (image->debug != MagickFalse)
4469 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4470 assert(exception != (ExceptionInfo *) NULL);
4471 assert(exception->signature == MagickSignature);
4472 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4474 if (spread_image == (Image *) NULL)
4475 return((Image *) NULL);
4476 if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
4478 InheritException(exception,&spread_image->exception);
4479 spread_image=DestroyImage(spread_image);
4480 return((Image *) NULL);
4487 GetMagickPixelPacket(spread_image,&zero);
4488 width=GetOptimalKernelWidth1D(radius,0.5);
4489 resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
4490 random_info=AcquireRandomInfoThreadSet();
4491 image_view=AcquireCacheView(spread_image);
4492 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4493 #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
4495 for (y=0; y < (long) spread_image->rows; y++)
4500 register IndexPacket
4501 *__restrict indexes;
4507 register PixelPacket
4510 if (status == MagickFalse)
4512 q=QueueCacheViewAuthenticPixels(image_view,0,y,spread_image->columns,1,
4514 if (q == (PixelPacket *) NULL)
4519 indexes=GetCacheViewAuthenticIndexQueue(image_view);
4521 id=GetOpenMPThreadId();
4522 for (x=0; x < (long) spread_image->columns; x++)
4524 (void) ResamplePixelColor(resample_filter[id],(double) x+width*
4525 (GetPseudoRandomValue(random_info[id])-0.5),(double) y+width*
4526 (GetPseudoRandomValue(random_info[id])-0.5),&pixel);
4527 SetPixelPacket(spread_image,&pixel,q,indexes+x);
4530 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4532 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4537 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4538 #pragma omp critical (MagickCore_SpreadImage)
4540 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
4541 if (proceed == MagickFalse)
4545 image_view=DestroyCacheView(image_view);
4546 random_info=DestroyRandomInfoThreadSet(random_info);
4547 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
4548 return(spread_image);
4552 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4556 % U n s h a r p M a s k I m a g e %
4560 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4562 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
4563 % image with a Gaussian operator of the given radius and standard deviation
4564 % (sigma). For reasonable results, radius should be larger than sigma. Use a
4565 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
4567 % The format of the UnsharpMaskImage method is:
4569 % Image *UnsharpMaskImage(const Image *image,const double radius,
4570 % const double sigma,const double amount,const double threshold,
4571 % ExceptionInfo *exception)
4572 % Image *UnsharpMaskImageChannel(const Image *image,
4573 % const ChannelType channel,const double radius,const double sigma,
4574 % const double amount,const double threshold,ExceptionInfo *exception)
4576 % A description of each parameter follows:
4578 % o image: the image.
4580 % o channel: the channel type.
4582 % o radius: the radius of the Gaussian, in pixels, not counting the center
4585 % o sigma: the standard deviation of the Gaussian, in pixels.
4587 % o amount: the percentage of the difference between the original and the
4588 % blur image that is added back into the original.
4590 % o threshold: the threshold in pixels needed to apply the diffence amount.
4592 % o exception: return any errors or warnings in this structure.
4596 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
4597 const double sigma,const double amount,const double threshold,
4598 ExceptionInfo *exception)
4603 sharp_image=UnsharpMaskImageChannel(image,DefaultChannels,radius,sigma,amount,
4604 threshold,exception);
4605 return(sharp_image);
4608 MagickExport Image *UnsharpMaskImageChannel(const Image *image,
4609 const ChannelType channel,const double radius,const double sigma,
4610 const double amount,const double threshold,ExceptionInfo *exception)
4612 #define SharpenImageTag "Sharpen/Image"
4634 assert(image != (const Image *) NULL);
4635 assert(image->signature == MagickSignature);
4636 if (image->debug != MagickFalse)
4637 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4638 assert(exception != (ExceptionInfo *) NULL);
4639 unsharp_image=BlurImageChannel(image,channel,radius,sigma,exception);
4640 if (unsharp_image == (Image *) NULL)
4641 return((Image *) NULL);
4642 quantum_threshold=(MagickRealType) QuantumRange*threshold;
4648 GetMagickPixelPacket(image,&zero);
4649 image_view=AcquireCacheView(image);
4650 unsharp_view=AcquireCacheView(unsharp_image);
4651 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4652 #pragma omp parallel for schedule(dynamic) shared(progress,status)
4654 for (y=0; y < (long) image->rows; y++)
4659 register const IndexPacket
4660 *__restrict indexes;
4662 register const PixelPacket
4665 register IndexPacket
4666 *__restrict unsharp_indexes;
4671 register PixelPacket
4674 if (status == MagickFalse)
4676 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4677 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
4679 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4684 indexes=GetCacheViewVirtualIndexQueue(image_view);
4685 unsharp_indexes=GetCacheViewAuthenticIndexQueue(unsharp_view);
4687 for (x=0; x < (long) image->columns; x++)
4689 if ((channel & RedChannel) != 0)
4691 pixel.red=p->red-(MagickRealType) q->red;
4692 if (fabs(2.0*pixel.red) < quantum_threshold)
4693 pixel.red=(MagickRealType) p->red;
4695 pixel.red=(MagickRealType) p->red+(pixel.red*amount);
4696 q->red=RoundToQuantum(pixel.red);
4698 if ((channel & GreenChannel) != 0)
4700 pixel.green=p->green-(MagickRealType) q->green;
4701 if (fabs(2.0*pixel.green) < quantum_threshold)
4702 pixel.green=(MagickRealType) p->green;
4704 pixel.green=(MagickRealType) p->green+(pixel.green*amount);
4705 q->green=RoundToQuantum(pixel.green);
4707 if ((channel & BlueChannel) != 0)
4709 pixel.blue=p->blue-(MagickRealType) q->blue;
4710 if (fabs(2.0*pixel.blue) < quantum_threshold)
4711 pixel.blue=(MagickRealType) p->blue;
4713 pixel.blue=(MagickRealType) p->blue+(pixel.blue*amount);
4714 q->blue=RoundToQuantum(pixel.blue);
4716 if ((channel & OpacityChannel) != 0)
4718 pixel.opacity=p->opacity-(MagickRealType) q->opacity;
4719 if (fabs(2.0*pixel.opacity) < quantum_threshold)
4720 pixel.opacity=(MagickRealType) p->opacity;
4722 pixel.opacity=p->opacity+(pixel.opacity*amount);
4723 q->opacity=RoundToQuantum(pixel.opacity);
4725 if (((channel & IndexChannel) != 0) &&
4726 (image->colorspace == CMYKColorspace))
4728 pixel.index=unsharp_indexes[x]-(MagickRealType) indexes[x];
4729 if (fabs(2.0*pixel.index) < quantum_threshold)
4730 pixel.index=(MagickRealType) unsharp_indexes[x];
4732 pixel.index=(MagickRealType) unsharp_indexes[x]+(pixel.index*
4734 unsharp_indexes[x]=RoundToQuantum(pixel.index);
4739 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4741 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4746 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4747 #pragma omp critical (MagickCore_UnsharpMaskImageChannel)
4749 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
4750 if (proceed == MagickFalse)
4754 unsharp_image->type=image->type;
4755 unsharp_view=DestroyCacheView(unsharp_view);
4756 image_view=DestroyCacheView(image_view);
4757 if (status == MagickFalse)
4758 unsharp_image=DestroyImage(unsharp_image);
4759 return(unsharp_image);