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"
1458 X[4] = {0, 1, 1,-1},
1459 Y[4] = {1, 0, 1, 1};
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++)
1519 if (status == MagickFalse)
1521 id=GetOpenMPThreadId();
1523 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
1525 j=(long) image->columns+2;
1526 for (y=0; y < (long) image->rows; y++)
1528 register const PixelPacket
1531 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1532 if (p == (const PixelPacket *) NULL)
1535 for (x=0; x < (long) image->columns; x++)
1539 case 0: pixel[j]=p->red; break;
1540 case 1: pixel[j]=p->green; break;
1541 case 2: pixel[j]=p->blue; break;
1542 case 3: pixel[j]=p->opacity; break;
1550 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1551 for (i=0; i < 4; i++)
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);
1555 Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,-1);
1556 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,-1);
1558 j=(long) image->columns+2;
1559 for (y=0; y < (long) image->rows; y++)
1564 register PixelPacket
1567 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1569 if (q == (PixelPacket *) NULL)
1572 for (x=0; x < (long) image->columns; x++)
1576 case 0: q->red=pixel[j]; break;
1577 case 1: q->green=pixel[j]; break;
1578 case 2: q->blue=pixel[j]; break;
1579 case 3: q->opacity=pixel[j]; break;
1585 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1586 if (sync == MagickFalse)
1593 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1598 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1599 #pragma omp critical (MagickCore_DespeckleImage)
1601 proceed=SetImageProgress(image,DespeckleImageTag,channel,3);
1602 if (proceed == MagickFalse)
1606 despeckle_view=DestroyCacheView(despeckle_view);
1607 image_view=DestroyCacheView(image_view);
1608 buffers=DestroyPixelThreadSet(buffers);
1609 pixels=DestroyPixelThreadSet(pixels);
1610 despeckle_image->type=image->type;
1611 if (status == MagickFalse)
1612 despeckle_image=DestroyImage(despeckle_image);
1613 return(despeckle_image);
1617 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1621 % E d g e I m a g e %
1625 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1627 % EdgeImage() finds edges in an image. Radius defines the radius of the
1628 % convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1631 % The format of the EdgeImage method is:
1633 % Image *EdgeImage(const Image *image,const double radius,
1634 % ExceptionInfo *exception)
1636 % A description of each parameter follows:
1638 % o image: the image.
1640 % o radius: the radius of the pixel neighborhood.
1642 % o exception: return any errors or warnings in this structure.
1645 MagickExport Image *EdgeImage(const Image *image,const double radius,
1646 ExceptionInfo *exception)
1660 assert(image != (const Image *) NULL);
1661 assert(image->signature == MagickSignature);
1662 if (image->debug != MagickFalse)
1663 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1664 assert(exception != (ExceptionInfo *) NULL);
1665 assert(exception->signature == MagickSignature);
1666 width=GetOptimalKernelWidth1D(radius,0.5);
1667 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
1668 if (kernel == (double *) NULL)
1669 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1670 for (i=0; i < (long) (width*width); i++)
1672 kernel[i/2]=(double) (width*width-1.0);
1673 edge_image=ConvolveImage(image,width,kernel,exception);
1674 kernel=(double *) RelinquishMagickMemory(kernel);
1679 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1683 % E m b o s s I m a g e %
1687 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1689 % EmbossImage() returns a grayscale image with a three-dimensional effect.
1690 % We convolve the image with a Gaussian operator of the given radius and
1691 % standard deviation (sigma). For reasonable results, radius should be
1692 % larger than sigma. Use a radius of 0 and Emboss() selects a suitable
1695 % The format of the EmbossImage method is:
1697 % Image *EmbossImage(const Image *image,const double radius,
1698 % const double sigma,ExceptionInfo *exception)
1700 % A description of each parameter follows:
1702 % o image: the image.
1704 % o radius: the radius of the pixel neighborhood.
1706 % o sigma: the standard deviation of the Gaussian, in pixels.
1708 % o exception: return any errors or warnings in this structure.
1711 MagickExport Image *EmbossImage(const Image *image,const double radius,
1712 const double sigma,ExceptionInfo *exception)
1734 assert(image != (Image *) NULL);
1735 assert(image->signature == MagickSignature);
1736 if (image->debug != MagickFalse)
1737 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1738 assert(exception != (ExceptionInfo *) NULL);
1739 assert(exception->signature == MagickSignature);
1740 width=GetOptimalKernelWidth2D(radius,sigma);
1741 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
1742 if (kernel == (double *) NULL)
1743 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1746 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
1748 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
1750 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
1751 kernel[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*alpha/
1752 (2.0*MagickPI*MagickSigma*MagickSigma));
1759 emboss_image=ConvolveImage(image,width,kernel,exception);
1760 if (emboss_image != (Image *) NULL)
1761 (void) EqualizeImage(emboss_image);
1762 kernel=(double *) RelinquishMagickMemory(kernel);
1763 return(emboss_image);
1767 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1771 % G a u s s i a n B l u r I m a g e %
1775 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1777 % GaussianBlurImage() blurs an image. We convolve the image with a
1778 % Gaussian operator of the given radius and standard deviation (sigma).
1779 % For reasonable results, the radius should be larger than sigma. Use a
1780 % radius of 0 and GaussianBlurImage() selects a suitable radius for you
1782 % The format of the GaussianBlurImage method is:
1784 % Image *GaussianBlurImage(const Image *image,onst double radius,
1785 % const double sigma,ExceptionInfo *exception)
1786 % Image *GaussianBlurImageChannel(const Image *image,
1787 % const ChannelType channel,const double radius,const double sigma,
1788 % ExceptionInfo *exception)
1790 % A description of each parameter follows:
1792 % o image: the image.
1794 % o channel: the channel type.
1796 % o radius: the radius of the Gaussian, in pixels, not counting the center
1799 % o sigma: the standard deviation of the Gaussian, in pixels.
1801 % o exception: return any errors or warnings in this structure.
1805 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
1806 const double sigma,ExceptionInfo *exception)
1811 blur_image=GaussianBlurImageChannel(image,DefaultChannels,radius,sigma,
1816 MagickExport Image *GaussianBlurImageChannel(const Image *image,
1817 const ChannelType channel,const double radius,const double sigma,
1818 ExceptionInfo *exception)
1837 assert(image != (const Image *) NULL);
1838 assert(image->signature == MagickSignature);
1839 if (image->debug != MagickFalse)
1840 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1841 assert(exception != (ExceptionInfo *) NULL);
1842 assert(exception->signature == MagickSignature);
1843 width=GetOptimalKernelWidth2D(radius,sigma);
1844 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
1845 if (kernel == (double *) NULL)
1846 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1848 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
1850 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
1852 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
1853 kernel[i]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
1857 blur_image=ConvolveImageChannel(image,channel,width,kernel,exception);
1858 kernel=(double *) RelinquishMagickMemory(kernel);
1863 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1867 % M e d i a n F i l t e r I m a g e %
1871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1873 % MedianFilterImage() applies a digital filter that improves the quality
1874 % of a noisy image. Each pixel is replaced by the median in a set of
1875 % neighboring pixels as defined by radius.
1877 % The algorithm was contributed by Mike Edmonds and implements an insertion
1878 % sort for selecting median color-channel values. For more on this algorithm
1879 % see "Skip Lists: A probabilistic Alternative to Balanced Trees" by William
1880 % Pugh in the June 1990 of Communications of the ACM.
1882 % The format of the MedianFilterImage method is:
1884 % Image *MedianFilterImage(const Image *image,const double radius,
1885 % ExceptionInfo *exception)
1887 % A description of each parameter follows:
1889 % o image: the image.
1891 % o radius: the radius of the pixel neighborhood.
1893 % o exception: return any errors or warnings in this structure.
1897 #define MedianListChannels 5
1899 typedef struct _MedianListNode
1907 typedef struct _MedianSkipList
1916 typedef struct _MedianPixelList
1924 lists[MedianListChannels];
1927 static MedianPixelList *DestroyMedianPixelList(MedianPixelList *pixel_list)
1932 if (pixel_list == (MedianPixelList *) NULL)
1933 return((MedianPixelList *) NULL);
1934 for (i=0; i < MedianListChannels; i++)
1935 if (pixel_list->lists[i].nodes != (MedianListNode *) NULL)
1936 pixel_list->lists[i].nodes=(MedianListNode *) RelinquishMagickMemory(
1937 pixel_list->lists[i].nodes);
1938 pixel_list=(MedianPixelList *) RelinquishAlignedMemory(pixel_list);
1942 static MedianPixelList **DestroyMedianPixelListThreadSet(
1943 MedianPixelList **pixel_list)
1948 assert(pixel_list != (MedianPixelList **) NULL);
1949 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
1950 if (pixel_list[i] != (MedianPixelList *) NULL)
1951 pixel_list[i]=DestroyMedianPixelList(pixel_list[i]);
1952 pixel_list=(MedianPixelList **) RelinquishAlignedMemory(pixel_list);
1956 static MedianPixelList *AcquireMedianPixelList(const unsigned long width)
1964 pixel_list=(MedianPixelList *) AcquireAlignedMemory(1,sizeof(*pixel_list));
1965 if (pixel_list == (MedianPixelList *) NULL)
1967 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
1968 pixel_list->center=width*width/2;
1969 for (i=0; i < MedianListChannels; i++)
1971 pixel_list->lists[i].nodes=(MedianListNode *) AcquireQuantumMemory(65537UL,
1972 sizeof(*pixel_list->lists[i].nodes));
1973 if (pixel_list->lists[i].nodes == (MedianListNode *) NULL)
1974 return(DestroyMedianPixelList(pixel_list));
1975 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
1976 sizeof(*pixel_list->lists[i].nodes));
1978 pixel_list->signature=MagickSignature;
1982 static MedianPixelList **AcquireMedianPixelListThreadSet(
1983 const unsigned long width)
1994 number_threads=GetOpenMPMaximumThreads();
1995 pixel_list=(MedianPixelList **) AcquireAlignedMemory(number_threads,
1996 sizeof(*pixel_list));
1997 if (pixel_list == (MedianPixelList **) NULL)
1998 return((MedianPixelList **) NULL);
1999 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
2000 for (i=0; i < (long) number_threads; i++)
2002 pixel_list[i]=AcquireMedianPixelList(width);
2003 if (pixel_list[i] == (MedianPixelList *) NULL)
2004 return(DestroyMedianPixelListThreadSet(pixel_list));
2009 static void AddNodeMedianPixelList(MedianPixelList *pixel_list,
2010 const long channel,const unsigned long color)
2015 register MedianSkipList
2023 Initialize the node.
2025 list=pixel_list->lists+channel;
2026 list->nodes[color].signature=pixel_list->signature;
2027 list->nodes[color].count=1;
2029 Determine where it belongs in the list.
2032 for (level=list->level; level >= 0; level--)
2034 while (list->nodes[search].next[level] < color)
2035 search=list->nodes[search].next[level];
2036 update[level]=search;
2039 Generate a pseudo-random level for this node.
2041 for (level=0; ; level++)
2043 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
2044 if ((pixel_list->seed & 0x300) != 0x300)
2049 if (level > (list->level+2))
2050 level=list->level+2;
2052 If we're raising the list's level, link back to the root node.
2054 while (level > list->level)
2057 update[list->level]=65536UL;
2060 Link the node into the skip-list.
2064 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
2065 list->nodes[update[level]].next[level]=color;
2067 while (level-- > 0);
2070 static MagickPixelPacket GetMedianPixelList(MedianPixelList *pixel_list)
2078 register MedianSkipList
2087 channels[MedianListChannels];
2090 Find the median value for each of the color.
2092 center=pixel_list->center;
2093 for (channel=0; channel < 5; channel++)
2095 list=pixel_list->lists+channel;
2100 color=list->nodes[color].next[0];
2101 count+=list->nodes[color].count;
2103 while (count <= center);
2104 channels[channel]=(unsigned short) color;
2106 GetMagickPixelPacket((const Image *) NULL,&pixel);
2107 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
2108 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
2109 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
2110 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
2111 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
2115 static inline void InsertMedianPixelList(const Image *image,
2116 const PixelPacket *pixel,const IndexPacket *indexes,
2117 MedianPixelList *pixel_list)
2125 index=ScaleQuantumToShort(pixel->red);
2126 signature=pixel_list->lists[0].nodes[index].signature;
2127 if (signature == pixel_list->signature)
2128 pixel_list->lists[0].nodes[index].count++;
2130 AddNodeMedianPixelList(pixel_list,0,index);
2131 index=ScaleQuantumToShort(pixel->green);
2132 signature=pixel_list->lists[1].nodes[index].signature;
2133 if (signature == pixel_list->signature)
2134 pixel_list->lists[1].nodes[index].count++;
2136 AddNodeMedianPixelList(pixel_list,1,index);
2137 index=ScaleQuantumToShort(pixel->blue);
2138 signature=pixel_list->lists[2].nodes[index].signature;
2139 if (signature == pixel_list->signature)
2140 pixel_list->lists[2].nodes[index].count++;
2142 AddNodeMedianPixelList(pixel_list,2,index);
2143 index=ScaleQuantumToShort(pixel->opacity);
2144 signature=pixel_list->lists[3].nodes[index].signature;
2145 if (signature == pixel_list->signature)
2146 pixel_list->lists[3].nodes[index].count++;
2148 AddNodeMedianPixelList(pixel_list,3,index);
2149 if (image->colorspace == CMYKColorspace)
2150 index=ScaleQuantumToShort(*indexes);
2151 signature=pixel_list->lists[4].nodes[index].signature;
2152 if (signature == pixel_list->signature)
2153 pixel_list->lists[4].nodes[index].count++;
2155 AddNodeMedianPixelList(pixel_list,4,index);
2158 static void ResetMedianPixelList(MedianPixelList *pixel_list)
2166 register MedianListNode
2169 register MedianSkipList
2173 Reset the skip-list.
2175 for (channel=0; channel < 5; channel++)
2177 list=pixel_list->lists+channel;
2178 root=list->nodes+65536UL;
2180 for (level=0; level < 9; level++)
2181 root->next[level]=65536UL;
2183 pixel_list->seed=pixel_list->signature++;
2186 MagickExport Image *MedianFilterImage(const Image *image,const double radius,
2187 ExceptionInfo *exception)
2189 #define MedianFilterImageTag "MedianFilter/Image"
2212 Initialize median image attributes.
2214 assert(image != (Image *) NULL);
2215 assert(image->signature == MagickSignature);
2216 if (image->debug != MagickFalse)
2217 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2218 assert(exception != (ExceptionInfo *) NULL);
2219 assert(exception->signature == MagickSignature);
2220 width=GetOptimalKernelWidth2D(radius,0.5);
2221 if ((image->columns < width) || (image->rows < width))
2222 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
2223 median_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2225 if (median_image == (Image *) NULL)
2226 return((Image *) NULL);
2227 if (SetImageStorageClass(median_image,DirectClass) == MagickFalse)
2229 InheritException(exception,&median_image->exception);
2230 median_image=DestroyImage(median_image);
2231 return((Image *) NULL);
2233 pixel_list=AcquireMedianPixelListThreadSet(width);
2234 if (pixel_list == (MedianPixelList **) NULL)
2236 median_image=DestroyImage(median_image);
2237 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2240 Median filter each image row.
2244 image_view=AcquireCacheView(image);
2245 median_view=AcquireCacheView(median_image);
2246 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2247 #pragma omp parallel for schedule(dynamic) shared(progress,status)
2249 for (y=0; y < (long) median_image->rows; y++)
2251 register const IndexPacket
2252 *__restrict indexes;
2254 register const PixelPacket
2257 register IndexPacket
2258 *__restrict median_indexes;
2264 register PixelPacket
2267 if (status == MagickFalse)
2269 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
2270 2L),image->columns+width,width,exception);
2271 q=QueueCacheViewAuthenticPixels(median_view,0,y,median_image->columns,1,
2273 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2278 indexes=GetCacheViewVirtualIndexQueue(image_view);
2279 median_indexes=GetCacheViewAuthenticIndexQueue(median_view);
2280 id=GetOpenMPThreadId();
2281 for (x=0; x < (long) median_image->columns; x++)
2286 register const PixelPacket
2289 register const IndexPacket
2298 ResetMedianPixelList(pixel_list[id]);
2299 for (v=0; v < (long) width; v++)
2301 for (u=0; u < (long) width; u++)
2302 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
2303 r+=image->columns+width;
2304 s+=image->columns+width;
2306 pixel=GetMedianPixelList(pixel_list[id]);
2307 SetPixelPacket(median_image,&pixel,q,median_indexes+x);
2311 if (SyncCacheViewAuthenticPixels(median_view,exception) == MagickFalse)
2313 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2318 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2319 #pragma omp critical (MagickCore_MedianFilterImage)
2321 proceed=SetImageProgress(image,MedianFilterImageTag,progress++,
2323 if (proceed == MagickFalse)
2327 median_view=DestroyCacheView(median_view);
2328 image_view=DestroyCacheView(image_view);
2329 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
2330 return(median_image);
2334 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2338 % M o t i o n B l u r I m a g e %
2342 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2344 % MotionBlurImage() simulates motion blur. We convolve the image with a
2345 % Gaussian operator of the given radius and standard deviation (sigma).
2346 % For reasonable results, radius should be larger than sigma. Use a
2347 % radius of 0 and MotionBlurImage() selects a suitable radius for you.
2348 % Angle gives the angle of the blurring motion.
2350 % Andrew Protano contributed this effect.
2352 % The format of the MotionBlurImage method is:
2354 % Image *MotionBlurImage(const Image *image,const double radius,
2355 % const double sigma,const double angle,ExceptionInfo *exception)
2356 % Image *MotionBlurImageChannel(const Image *image,const ChannelType channel,
2357 % const double radius,const double sigma,const double angle,
2358 % ExceptionInfo *exception)
2360 % A description of each parameter follows:
2362 % o image: the image.
2364 % o channel: the channel type.
2366 % o radius: the radius of the Gaussian, in pixels, not counting the center
2367 % o radius: the radius of the Gaussian, in pixels, not counting
2370 % o sigma: the standard deviation of the Gaussian, in pixels.
2372 % o angle: Apply the effect along this angle.
2374 % o exception: return any errors or warnings in this structure.
2378 static double *GetMotionBlurKernel(unsigned long width,
2379 const MagickRealType sigma)
2381 #define KernelRank 3
2397 Generate a 1-D convolution kernel.
2399 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2400 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
2401 if (kernel == (double *) NULL)
2403 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
2404 bias=(long) (KernelRank*width);
2405 for (i=0; i < (long) bias; i++)
2407 alpha=exp((-((double) (i*i))/(double) (2.0*KernelRank*KernelRank*
2408 MagickSigma*MagickSigma)));
2409 kernel[i/KernelRank]+=(double) alpha/(MagickSQ2PI*sigma);
2412 for (i=0; i < (long) width; i++)
2413 normalize+=kernel[i];
2414 for (i=0; i < (long) width; i++)
2415 kernel[i]/=normalize;
2419 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
2420 const double sigma,const double angle,ExceptionInfo *exception)
2425 motion_blur=MotionBlurImageChannel(image,DefaultChannels,radius,sigma,angle,
2427 return(motion_blur);
2430 MagickExport Image *MotionBlurImageChannel(const Image *image,
2431 const ChannelType channel,const double radius,const double sigma,
2432 const double angle,ExceptionInfo *exception)
2434 typedef struct _OffsetInfo
2473 assert(image != (Image *) NULL);
2474 assert(image->signature == MagickSignature);
2475 if (image->debug != MagickFalse)
2476 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2477 assert(exception != (ExceptionInfo *) NULL);
2478 width=GetOptimalKernelWidth1D(radius,sigma);
2479 kernel=GetMotionBlurKernel(width,sigma);
2480 if (kernel == (double *) NULL)
2481 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2482 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2483 if (offset == (OffsetInfo *) NULL)
2485 kernel=(double *) RelinquishMagickMemory(kernel);
2486 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2488 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2489 if (blur_image == (Image *) NULL)
2491 kernel=(double *) RelinquishMagickMemory(kernel);
2492 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2493 return((Image *) NULL);
2495 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
2497 kernel=(double *) RelinquishMagickMemory(kernel);
2498 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2499 InheritException(exception,&blur_image->exception);
2500 blur_image=DestroyImage(blur_image);
2501 return((Image *) NULL);
2503 point.x=(double) width*sin(DegreesToRadians(angle));
2504 point.y=(double) width*cos(DegreesToRadians(angle));
2505 for (i=0; i < (long) width; i++)
2507 offset[i].x=(long) ((i*point.y)/hypot(point.x,point.y)+0.5);
2508 offset[i].y=(long) ((i*point.x)/hypot(point.x,point.y)+0.5);
2515 GetMagickPixelPacket(image,&zero);
2516 image_view=AcquireCacheView(image);
2517 blur_view=AcquireCacheView(blur_image);
2518 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2519 #pragma omp parallel for schedule(dynamic) shared(progress,status)
2521 for (y=0; y < (long) image->rows; y++)
2523 register IndexPacket
2524 *__restrict blur_indexes;
2529 register PixelPacket
2532 if (status == MagickFalse)
2534 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2536 if (q == (PixelPacket *) NULL)
2541 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
2542 for (x=0; x < (long) image->columns; x++)
2556 register const IndexPacket
2557 *__restrict indexes;
2561 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
2563 for (i=0; i < (long) width; i++)
2565 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2566 offset[i].y,&pixel,exception);
2567 qixel.red+=(*k)*pixel.red;
2568 qixel.green+=(*k)*pixel.green;
2569 qixel.blue+=(*k)*pixel.blue;
2570 qixel.opacity+=(*k)*pixel.opacity;
2571 if (image->colorspace == CMYKColorspace)
2573 indexes=GetCacheViewVirtualIndexQueue(image_view);
2574 qixel.index+=(*k)*(*indexes);
2578 if ((channel & RedChannel) != 0)
2579 q->red=RoundToQuantum(qixel.red);
2580 if ((channel & GreenChannel) != 0)
2581 q->green=RoundToQuantum(qixel.green);
2582 if ((channel & BlueChannel) != 0)
2583 q->blue=RoundToQuantum(qixel.blue);
2584 if ((channel & OpacityChannel) != 0)
2585 q->opacity=RoundToQuantum(qixel.opacity);
2586 if (((channel & IndexChannel) != 0) &&
2587 (image->colorspace == CMYKColorspace))
2588 blur_indexes[x]=(IndexPacket) RoundToQuantum(qixel.index);
2598 for (i=0; i < (long) width; i++)
2600 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2601 offset[i].y,&pixel,exception);
2602 alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
2603 qixel.red+=(*k)*alpha*pixel.red;
2604 qixel.green+=(*k)*alpha*pixel.green;
2605 qixel.blue+=(*k)*alpha*pixel.blue;
2606 qixel.opacity+=(*k)*pixel.opacity;
2607 if (image->colorspace == CMYKColorspace)
2609 indexes=GetCacheViewVirtualIndexQueue(image_view);
2610 qixel.index+=(*k)*alpha*(*indexes);
2615 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2616 if ((channel & RedChannel) != 0)
2617 q->red=RoundToQuantum(gamma*qixel.red);
2618 if ((channel & GreenChannel) != 0)
2619 q->green=RoundToQuantum(gamma*qixel.green);
2620 if ((channel & BlueChannel) != 0)
2621 q->blue=RoundToQuantum(gamma*qixel.blue);
2622 if ((channel & OpacityChannel) != 0)
2623 q->opacity=RoundToQuantum(qixel.opacity);
2624 if (((channel & IndexChannel) != 0) &&
2625 (image->colorspace == CMYKColorspace))
2626 blur_indexes[x]=(IndexPacket) RoundToQuantum(gamma*qixel.index);
2630 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2632 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2637 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2638 #pragma omp critical (MagickCore_MotionBlurImageChannel)
2640 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2641 if (proceed == MagickFalse)
2645 blur_view=DestroyCacheView(blur_view);
2646 image_view=DestroyCacheView(image_view);
2647 kernel=(double *) RelinquishMagickMemory(kernel);
2648 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2649 if (status == MagickFalse)
2650 blur_image=DestroyImage(blur_image);
2655 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2659 % P r e v i e w I m a g e %
2663 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2665 % PreviewImage() tiles 9 thumbnails of the specified image with an image
2666 % processing operation applied with varying parameters. This may be helpful
2667 % pin-pointing an appropriate parameter for a particular image processing
2670 % The format of the PreviewImages method is:
2672 % Image *PreviewImages(const Image *image,const PreviewType preview,
2673 % ExceptionInfo *exception)
2675 % A description of each parameter follows:
2677 % o image: the image.
2679 % o preview: the image processing operation.
2681 % o exception: return any errors or warnings in this structure.
2684 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
2685 ExceptionInfo *exception)
2687 #define NumberTiles 9
2688 #define PreviewImageTag "Preview/Image"
2689 #define DefaultPreviewGeometry "204x204+10+10"
2692 factor[MaxTextExtent],
2693 label[MaxTextExtent];
2735 Open output image file.
2737 assert(image != (Image *) NULL);
2738 assert(image->signature == MagickSignature);
2739 if (image->debug != MagickFalse)
2740 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2744 preview_info=AcquireImageInfo();
2745 SetGeometry(image,&geometry);
2746 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2747 &geometry.width,&geometry.height);
2748 images=NewImageList();
2750 GetQuantizeInfo(&quantize_info);
2756 for (i=0; i < NumberTiles; i++)
2758 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2759 if (thumbnail == (Image *) NULL)
2761 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2763 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
2764 if (i == (NumberTiles/2))
2766 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
2767 AppendImageToList(&images,thumbnail);
2775 preview_image=RotateImage(thumbnail,degrees,exception);
2776 (void) FormatMagickString(label,MaxTextExtent,"rotate %g",degrees);
2782 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
2783 (void) FormatMagickString(label,MaxTextExtent,"shear %gx%g",
2784 degrees,2.0*degrees);
2789 x=(long) ((i+1)*thumbnail->columns)/NumberTiles;
2790 y=(long) ((i+1)*thumbnail->rows)/NumberTiles;
2791 preview_image=RollImage(thumbnail,x,y,exception);
2792 (void) FormatMagickString(label,MaxTextExtent,"roll %ldx%ld",x,y);
2797 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2798 if (preview_image == (Image *) NULL)
2800 (void) FormatMagickString(factor,MaxTextExtent,"100,100,%g",
2802 (void) ModulateImage(preview_image,factor);
2803 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
2806 case SaturationPreview:
2808 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2809 if (preview_image == (Image *) NULL)
2811 (void) FormatMagickString(factor,MaxTextExtent,"100,%g",2.0*percentage);
2812 (void) ModulateImage(preview_image,factor);
2813 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
2816 case BrightnessPreview:
2818 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2819 if (preview_image == (Image *) NULL)
2821 (void) FormatMagickString(factor,MaxTextExtent,"%g",2.0*percentage);
2822 (void) ModulateImage(preview_image,factor);
2823 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
2829 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2830 if (preview_image == (Image *) NULL)
2833 (void) GammaImageChannel(preview_image,DefaultChannels,gamma);
2834 (void) FormatMagickString(label,MaxTextExtent,"gamma %g",gamma);
2839 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2840 if (preview_image != (Image *) NULL)
2841 for (x=0; x < i; x++)
2842 (void) ContrastImage(preview_image,MagickTrue);
2843 (void) FormatMagickString(label,MaxTextExtent,"contrast (%ld)",i+1);
2848 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2849 if (preview_image == (Image *) NULL)
2851 for (x=0; x < i; x++)
2852 (void) ContrastImage(preview_image,MagickFalse);
2853 (void) FormatMagickString(label,MaxTextExtent,"+contrast (%ld)",i+1);
2856 case GrayscalePreview:
2858 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2859 if (preview_image == (Image *) NULL)
2862 quantize_info.number_colors=colors;
2863 quantize_info.colorspace=GRAYColorspace;
2864 (void) QuantizeImage(&quantize_info,preview_image);
2865 (void) FormatMagickString(label,MaxTextExtent,
2866 "-colorspace gray -colors %ld",colors);
2869 case QuantizePreview:
2871 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2872 if (preview_image == (Image *) NULL)
2875 quantize_info.number_colors=colors;
2876 (void) QuantizeImage(&quantize_info,preview_image);
2877 (void) FormatMagickString(label,MaxTextExtent,"colors %ld",colors);
2880 case DespecklePreview:
2882 for (x=0; x < (i-1); x++)
2884 preview_image=DespeckleImage(thumbnail,exception);
2885 if (preview_image == (Image *) NULL)
2887 thumbnail=DestroyImage(thumbnail);
2888 thumbnail=preview_image;
2890 preview_image=DespeckleImage(thumbnail,exception);
2891 if (preview_image == (Image *) NULL)
2893 (void) FormatMagickString(label,MaxTextExtent,"despeckle (%ld)",i+1);
2896 case ReduceNoisePreview:
2898 preview_image=ReduceNoiseImage(thumbnail,radius,exception);
2899 (void) FormatMagickString(label,MaxTextExtent,"noise %g",radius);
2902 case AddNoisePreview:
2908 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
2913 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
2918 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
2923 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
2928 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
2933 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
2938 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
2942 preview_image=ReduceNoiseImage(thumbnail,(double) i,exception);
2943 (void) FormatMagickString(label,MaxTextExtent,"+noise %s",factor);
2946 case SharpenPreview:
2948 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
2949 (void) FormatMagickString(label,MaxTextExtent,"sharpen %gx%g",radius,
2955 preview_image=BlurImage(thumbnail,radius,sigma,exception);
2956 (void) FormatMagickString(label,MaxTextExtent,"blur %gx%g",radius,
2960 case ThresholdPreview:
2962 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2963 if (preview_image == (Image *) NULL)
2965 (void) BilevelImage(thumbnail,
2966 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2967 (void) FormatMagickString(label,MaxTextExtent,"threshold %g",
2968 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2971 case EdgeDetectPreview:
2973 preview_image=EdgeImage(thumbnail,radius,exception);
2974 (void) FormatMagickString(label,MaxTextExtent,"edge %g",radius);
2979 preview_image=SpreadImage(thumbnail,radius,exception);
2980 (void) FormatMagickString(label,MaxTextExtent,"spread %g",radius+0.5);
2983 case SolarizePreview:
2985 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2986 if (preview_image == (Image *) NULL)
2988 (void) SolarizeImage(preview_image,(double) QuantumRange*
2990 (void) FormatMagickString(label,MaxTextExtent,"solarize %g",
2991 (QuantumRange*percentage)/100.0);
2997 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2999 (void) FormatMagickString(label,MaxTextExtent,"shade %gx%g",degrees,
3005 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3006 if (preview_image == (Image *) NULL)
3008 geometry.width=(unsigned long) (2*i+2);
3009 geometry.height=(unsigned long) (2*i+2);
3012 (void) RaiseImage(preview_image,&geometry,MagickTrue);
3013 (void) FormatMagickString(label,MaxTextExtent,"raise %lux%lu%+ld%+ld",
3014 geometry.width,geometry.height,geometry.x,geometry.y);
3017 case SegmentPreview:
3019 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3020 if (preview_image == (Image *) NULL)
3023 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
3025 (void) FormatMagickString(label,MaxTextExtent,"segment %gx%g",
3026 threshold,threshold);
3031 preview_image=SwirlImage(thumbnail,degrees,exception);
3032 (void) FormatMagickString(label,MaxTextExtent,"swirl %g",degrees);
3036 case ImplodePreview:
3039 preview_image=ImplodeImage(thumbnail,degrees,exception);
3040 (void) FormatMagickString(label,MaxTextExtent,"implode %g",degrees);
3046 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
3047 (void) FormatMagickString(label,MaxTextExtent,"wave %gx%g",0.5*degrees,
3051 case OilPaintPreview:
3053 preview_image=OilPaintImage(thumbnail,(double) radius,exception);
3054 (void) FormatMagickString(label,MaxTextExtent,"paint %g",radius);
3057 case CharcoalDrawingPreview:
3059 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
3061 (void) FormatMagickString(label,MaxTextExtent,"charcoal %gx%g",radius,
3068 filename[MaxTextExtent];
3076 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3077 if (preview_image == (Image *) NULL)
3079 preview_info->quality=(unsigned long) percentage;
3080 (void) FormatMagickString(factor,MaxTextExtent,"%lu",
3081 preview_info->quality);
3082 file=AcquireUniqueFileResource(filename);
3085 (void) FormatMagickString(preview_image->filename,MaxTextExtent,
3086 "jpeg:%s",filename);
3087 status=WriteImage(preview_info,preview_image);
3088 if (status != MagickFalse)
3093 (void) CopyMagickString(preview_info->filename,
3094 preview_image->filename,MaxTextExtent);
3095 quality_image=ReadImage(preview_info,exception);
3096 if (quality_image != (Image *) NULL)
3098 preview_image=DestroyImage(preview_image);
3099 preview_image=quality_image;
3102 (void) RelinquishUniqueFileResource(preview_image->filename);
3103 if ((GetBlobSize(preview_image)/1024) >= 1024)
3104 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gmb ",
3105 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
3108 if (GetBlobSize(preview_image) >= 1024)
3109 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gkb ",
3110 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
3113 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%lub ",
3114 factor,(unsigned long) GetBlobSize(thumbnail));
3118 thumbnail=DestroyImage(thumbnail);
3122 if (preview_image == (Image *) NULL)
3124 (void) DeleteImageProperty(preview_image,"label");
3125 (void) SetImageProperty(preview_image,"label",label);
3126 AppendImageToList(&images,preview_image);
3127 proceed=SetImageProgress(image,PreviewImageTag,i,NumberTiles);
3128 if (proceed == MagickFalse)
3131 if (images == (Image *) NULL)
3133 preview_info=DestroyImageInfo(preview_info);
3134 return((Image *) NULL);
3139 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
3140 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
3141 montage_info->shadow=MagickTrue;
3142 (void) CloneString(&montage_info->tile,"3x3");
3143 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
3144 (void) CloneString(&montage_info->frame,DefaultTileFrame);
3145 montage_image=MontageImages(images,montage_info,exception);
3146 montage_info=DestroyMontageInfo(montage_info);
3147 images=DestroyImageList(images);
3148 if (montage_image == (Image *) NULL)
3149 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3150 if (montage_image->montage != (char *) NULL)
3153 Free image directory.
3155 montage_image->montage=(char *) RelinquishMagickMemory(
3156 montage_image->montage);
3157 if (image->directory != (char *) NULL)
3158 montage_image->directory=(char *) RelinquishMagickMemory(
3159 montage_image->directory);
3161 preview_info=DestroyImageInfo(preview_info);
3162 return(montage_image);
3166 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3170 % R a d i a l B l u r I m a g e %
3174 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3176 % RadialBlurImage() applies a radial blur to the image.
3178 % Andrew Protano contributed this effect.
3180 % The format of the RadialBlurImage method is:
3182 % Image *RadialBlurImage(const Image *image,const double angle,
3183 % ExceptionInfo *exception)
3184 % Image *RadialBlurImageChannel(const Image *image,const ChannelType channel,
3185 % const double angle,ExceptionInfo *exception)
3187 % A description of each parameter follows:
3189 % o image: the image.
3191 % o channel: the channel type.
3193 % o angle: the angle of the radial blur.
3195 % o exception: return any errors or warnings in this structure.
3199 MagickExport Image *RadialBlurImage(const Image *image,const double angle,
3200 ExceptionInfo *exception)
3205 blur_image=RadialBlurImageChannel(image,DefaultChannels,angle,exception);
3209 MagickExport Image *RadialBlurImageChannel(const Image *image,
3210 const ChannelType channel,const double angle,ExceptionInfo *exception)
3246 Allocate blur image.
3248 assert(image != (Image *) NULL);
3249 assert(image->signature == MagickSignature);
3250 if (image->debug != MagickFalse)
3251 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3252 assert(exception != (ExceptionInfo *) NULL);
3253 assert(exception->signature == MagickSignature);
3254 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3255 if (blur_image == (Image *) NULL)
3256 return((Image *) NULL);
3257 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3259 InheritException(exception,&blur_image->exception);
3260 blur_image=DestroyImage(blur_image);
3261 return((Image *) NULL);
3263 blur_center.x=(double) image->columns/2.0;
3264 blur_center.y=(double) image->rows/2.0;
3265 blur_radius=hypot(blur_center.x,blur_center.y);
3266 n=(unsigned long) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+
3268 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
3269 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3270 sizeof(*cos_theta));
3271 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3272 sizeof(*sin_theta));
3273 if ((cos_theta == (MagickRealType *) NULL) ||
3274 (sin_theta == (MagickRealType *) NULL))
3276 blur_image=DestroyImage(blur_image);
3277 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3279 offset=theta*(MagickRealType) (n-1)/2.0;
3280 for (i=0; i < (long) n; i++)
3282 cos_theta[i]=cos((double) (theta*i-offset));
3283 sin_theta[i]=sin((double) (theta*i-offset));
3290 GetMagickPixelPacket(image,&zero);
3291 image_view=AcquireCacheView(image);
3292 blur_view=AcquireCacheView(blur_image);
3293 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3294 #pragma omp parallel for schedule(dynamic) shared(progress,status)
3296 for (y=0; y < (long) blur_image->rows; y++)
3298 register const IndexPacket
3299 *__restrict indexes;
3301 register IndexPacket
3302 *__restrict blur_indexes;
3307 register PixelPacket
3310 if (status == MagickFalse)
3312 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3314 if (q == (PixelPacket *) NULL)
3319 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
3320 for (x=0; x < (long) blur_image->columns; x++)
3341 center.x=(double) x-blur_center.x;
3342 center.y=(double) y-blur_center.y;
3343 radius=hypot((double) center.x,center.y);
3348 step=(unsigned long) (blur_radius/radius);
3357 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
3359 for (i=0; i < (long) n; i+=step)
3361 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
3362 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
3363 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
3365 qixel.red+=pixel.red;
3366 qixel.green+=pixel.green;
3367 qixel.blue+=pixel.blue;
3368 qixel.opacity+=pixel.opacity;
3369 if (image->colorspace == CMYKColorspace)
3371 indexes=GetCacheViewVirtualIndexQueue(image_view);
3372 qixel.index+=(*indexes);
3376 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3378 if ((channel & RedChannel) != 0)
3379 q->red=RoundToQuantum(normalize*qixel.red);
3380 if ((channel & GreenChannel) != 0)
3381 q->green=RoundToQuantum(normalize*qixel.green);
3382 if ((channel & BlueChannel) != 0)
3383 q->blue=RoundToQuantum(normalize*qixel.blue);
3384 if ((channel & OpacityChannel) != 0)
3385 q->opacity=RoundToQuantum(normalize*qixel.opacity);
3386 if (((channel & IndexChannel) != 0) &&
3387 (image->colorspace == CMYKColorspace))
3388 blur_indexes[x]=(IndexPacket) RoundToQuantum(normalize*qixel.index);
3398 for (i=0; i < (long) n; i+=step)
3400 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
3401 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
3402 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
3404 alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
3405 qixel.red+=alpha*pixel.red;
3406 qixel.green+=alpha*pixel.green;
3407 qixel.blue+=alpha*pixel.blue;
3408 qixel.opacity+=pixel.opacity;
3409 if (image->colorspace == CMYKColorspace)
3411 indexes=GetCacheViewVirtualIndexQueue(image_view);
3412 qixel.index+=alpha*(*indexes);
3417 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3418 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3420 if ((channel & RedChannel) != 0)
3421 q->red=RoundToQuantum(gamma*qixel.red);
3422 if ((channel & GreenChannel) != 0)
3423 q->green=RoundToQuantum(gamma*qixel.green);
3424 if ((channel & BlueChannel) != 0)
3425 q->blue=RoundToQuantum(gamma*qixel.blue);
3426 if ((channel & OpacityChannel) != 0)
3427 q->opacity=RoundToQuantum(normalize*qixel.opacity);
3428 if (((channel & IndexChannel) != 0) &&
3429 (image->colorspace == CMYKColorspace))
3430 blur_indexes[x]=(IndexPacket) RoundToQuantum(gamma*qixel.index);
3434 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3436 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3441 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3442 #pragma omp critical (MagickCore_RadialBlurImageChannel)
3444 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3445 if (proceed == MagickFalse)
3449 blur_view=DestroyCacheView(blur_view);
3450 image_view=DestroyCacheView(image_view);
3451 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
3452 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
3453 if (status == MagickFalse)
3454 blur_image=DestroyImage(blur_image);
3459 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3463 % R e d u c e N o i s e I m a g e %
3467 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3469 % ReduceNoiseImage() smooths the contours of an image while still preserving
3470 % edge information. The algorithm works by replacing each pixel with its
3471 % neighbor closest in value. A neighbor is defined by radius. Use a radius
3472 % of 0 and ReduceNoise() selects a suitable radius for you.
3474 % The format of the ReduceNoiseImage method is:
3476 % Image *ReduceNoiseImage(const Image *image,const double radius,
3477 % ExceptionInfo *exception)
3479 % A description of each parameter follows:
3481 % o image: the image.
3483 % o radius: the radius of the pixel neighborhood.
3485 % o exception: return any errors or warnings in this structure.
3489 static MagickPixelPacket GetNonpeakMedianPixelList(MedianPixelList *pixel_list)
3497 register MedianSkipList
3511 Finds the median value for each of the color.
3513 center=pixel_list->center;
3514 for (channel=0; channel < 5; channel++)
3516 list=pixel_list->lists+channel;
3518 next=list->nodes[color].next[0];
3524 next=list->nodes[color].next[0];
3525 count+=list->nodes[color].count;
3527 while (count <= center);
3528 if ((previous == 65536UL) && (next != 65536UL))
3531 if ((previous != 65536UL) && (next == 65536UL))
3533 channels[channel]=(unsigned short) color;
3535 GetMagickPixelPacket((const Image *) NULL,&pixel);
3536 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
3537 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
3538 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
3539 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
3540 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
3544 MagickExport Image *ReduceNoiseImage(const Image *image,const double radius,
3545 ExceptionInfo *exception)
3547 #define ReduceNoiseImageTag "ReduceNoise/Image"
3570 Initialize noise image attributes.
3572 assert(image != (Image *) NULL);
3573 assert(image->signature == MagickSignature);
3574 if (image->debug != MagickFalse)
3575 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3576 assert(exception != (ExceptionInfo *) NULL);
3577 assert(exception->signature == MagickSignature);
3578 width=GetOptimalKernelWidth2D(radius,0.5);
3579 if ((image->columns < width) || (image->rows < width))
3580 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
3581 noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3583 if (noise_image == (Image *) NULL)
3584 return((Image *) NULL);
3585 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
3587 InheritException(exception,&noise_image->exception);
3588 noise_image=DestroyImage(noise_image);
3589 return((Image *) NULL);
3591 pixel_list=AcquireMedianPixelListThreadSet(width);
3592 if (pixel_list == (MedianPixelList **) NULL)
3594 noise_image=DestroyImage(noise_image);
3595 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3602 image_view=AcquireCacheView(image);
3603 noise_view=AcquireCacheView(noise_image);
3604 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3605 #pragma omp parallel for schedule(dynamic) shared(progress,status)
3607 for (y=0; y < (long) noise_image->rows; y++)
3609 register const IndexPacket
3610 *__restrict indexes;
3612 register const PixelPacket
3615 register IndexPacket
3616 *__restrict noise_indexes;
3622 register PixelPacket
3625 if (status == MagickFalse)
3627 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
3628 2L),image->columns+width,width,exception);
3629 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
3631 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3636 indexes=GetCacheViewVirtualIndexQueue(image_view);
3637 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
3638 id=GetOpenMPThreadId();
3639 for (x=0; x < (long) noise_image->columns; x++)
3644 register const PixelPacket
3647 register const IndexPacket
3656 ResetMedianPixelList(pixel_list[id]);
3657 for (v=0; v < (long) width; v++)
3659 for (u=0; u < (long) width; u++)
3660 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
3661 r+=image->columns+width;
3662 s+=image->columns+width;
3664 pixel=GetNonpeakMedianPixelList(pixel_list[id]);
3665 SetPixelPacket(noise_image,&pixel,q,noise_indexes+x);
3669 if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
3671 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3676 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3677 #pragma omp critical (MagickCore_ReduceNoiseImage)
3679 proceed=SetImageProgress(image,ReduceNoiseImageTag,progress++,
3681 if (proceed == MagickFalse)
3685 noise_view=DestroyCacheView(noise_view);
3686 image_view=DestroyCacheView(image_view);
3687 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
3688 return(noise_image);
3692 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3696 % S e l e c t i v e B l u r I m a g e %
3700 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3702 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3703 % It is similar to the unsharpen mask that sharpens everything with contrast
3704 % above a certain threshold.
3706 % The format of the SelectiveBlurImage method is:
3708 % Image *SelectiveBlurImage(const Image *image,const double radius,
3709 % const double sigma,const double threshold,ExceptionInfo *exception)
3710 % Image *SelectiveBlurImageChannel(const Image *image,
3711 % const ChannelType channel,const double radius,const double sigma,
3712 % const double threshold,ExceptionInfo *exception)
3714 % A description of each parameter follows:
3716 % o image: the image.
3718 % o channel: the channel type.
3720 % o radius: the radius of the Gaussian, in pixels, not counting the center
3723 % o sigma: the standard deviation of the Gaussian, in pixels.
3725 % o threshold: only pixels within this contrast threshold are included
3726 % in the blur operation.
3728 % o exception: return any errors or warnings in this structure.
3732 static inline MagickBooleanType SelectiveContrast(const PixelPacket *p,
3733 const PixelPacket *q,const double threshold)
3735 if (fabs(PixelIntensity(p)-PixelIntensity(q)) < threshold)
3737 return(MagickFalse);
3740 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
3741 const double sigma,const double threshold,ExceptionInfo *exception)
3746 blur_image=SelectiveBlurImageChannel(image,DefaultChannels,radius,sigma,
3747 threshold,exception);
3751 MagickExport Image *SelectiveBlurImageChannel(const Image *image,
3752 const ChannelType channel,const double radius,const double sigma,
3753 const double threshold,ExceptionInfo *exception)
3755 #define SelectiveBlurImageTag "SelectiveBlur/Image"
3790 Initialize blur image attributes.
3792 assert(image != (Image *) NULL);
3793 assert(image->signature == MagickSignature);
3794 if (image->debug != MagickFalse)
3795 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3796 assert(exception != (ExceptionInfo *) NULL);
3797 assert(exception->signature == MagickSignature);
3798 width=GetOptimalKernelWidth1D(radius,sigma);
3799 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
3800 if (kernel == (double *) NULL)
3801 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3803 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
3805 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
3807 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
3808 kernel[i]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
3812 if (image->debug != MagickFalse)
3815 format[MaxTextExtent],
3822 register const double
3825 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
3826 " SelectiveBlurImage with %ldx%ld kernel:",width,width);
3827 message=AcquireString("");
3829 for (v=0; v < (long) width; v++)
3832 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
3833 (void) ConcatenateString(&message,format);
3834 for (u=0; u < (long) width; u++)
3836 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
3837 (void) ConcatenateString(&message,format);
3839 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3841 message=DestroyString(message);
3843 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3844 if (blur_image == (Image *) NULL)
3845 return((Image *) NULL);
3846 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3848 InheritException(exception,&blur_image->exception);
3849 blur_image=DestroyImage(blur_image);
3850 return((Image *) NULL);
3853 Threshold blur image.
3857 GetMagickPixelPacket(image,&zero);
3859 image_view=AcquireCacheView(image);
3860 blur_view=AcquireCacheView(blur_image);
3861 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3862 #pragma omp parallel for schedule(dynamic) shared(progress,status)
3864 for (y=0; y < (long) image->rows; y++)
3872 register const IndexPacket
3873 *__restrict indexes;
3875 register const PixelPacket
3878 register IndexPacket
3879 *__restrict blur_indexes;
3884 register PixelPacket
3887 if (status == MagickFalse)
3889 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
3890 2L),image->columns+width,width,exception);
3891 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3893 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3898 indexes=GetCacheViewVirtualIndexQueue(image_view);
3899 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
3900 for (x=0; x < (long) image->columns; x++)
3909 register const double
3919 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
3921 for (v=0; v < (long) width; v++)
3923 for (u=0; u < (long) width; u++)
3925 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
3927 pixel.red+=(*k)*(p+u+j)->red;
3928 pixel.green+=(*k)*(p+u+j)->green;
3929 pixel.blue+=(*k)*(p+u+j)->blue;
3934 j+=image->columns+width;
3938 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3939 if ((channel & RedChannel) != 0)
3940 q->red=RoundToQuantum(gamma*pixel.red+bias);
3941 if ((channel & GreenChannel) != 0)
3942 q->green=RoundToQuantum(gamma*pixel.green+bias);
3943 if ((channel & BlueChannel) != 0)
3944 q->blue=RoundToQuantum(gamma*pixel.blue+bias);
3946 if ((channel & OpacityChannel) != 0)
3950 for (v=0; v < (long) width; v++)
3952 for (u=0; u < (long) width; u++)
3954 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
3956 pixel.opacity+=(*k)*(p+u+j)->opacity;
3961 j+=image->columns+width;
3965 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3967 q->opacity=RoundToQuantum(gamma*pixel.opacity+bias);
3970 if (((channel & IndexChannel) != 0) &&
3971 (image->colorspace == CMYKColorspace))
3975 for (v=0; v < (long) width; v++)
3977 for (u=0; u < (long) width; u++)
3979 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
3981 pixel.index+=(*k)*indexes[x+u+j];
3986 j+=image->columns+width;
3990 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3992 blur_indexes[x]=RoundToQuantum(gamma*pixel.index+bias);
4001 for (v=0; v < (long) width; v++)
4003 for (u=0; u < (long) width; u++)
4005 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4007 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
4009 pixel.red+=(*k)*alpha*(p+u+j)->red;
4010 pixel.green+=(*k)*alpha*(p+u+j)->green;
4011 pixel.blue+=(*k)*alpha*(p+u+j)->blue;
4012 pixel.opacity+=(*k)*(p+u+j)->opacity;
4017 j+=image->columns+width;
4021 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4022 if ((channel & RedChannel) != 0)
4023 q->red=RoundToQuantum(gamma*pixel.red+bias);
4024 if ((channel & GreenChannel) != 0)
4025 q->green=RoundToQuantum(gamma*pixel.green+bias);
4026 if ((channel & BlueChannel) != 0)
4027 q->blue=RoundToQuantum(gamma*pixel.blue+bias);
4029 if ((channel & OpacityChannel) != 0)
4033 for (v=0; v < (long) width; v++)
4035 for (u=0; u < (long) width; u++)
4037 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4039 pixel.opacity+=(*k)*(p+u+j)->opacity;
4044 j+=image->columns+width;
4048 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4050 q->opacity=RoundToQuantum(pixel.opacity+bias);
4053 if (((channel & IndexChannel) != 0) &&
4054 (image->colorspace == CMYKColorspace))
4058 for (v=0; v < (long) width; v++)
4060 for (u=0; u < (long) width; u++)
4062 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4064 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
4066 pixel.index+=(*k)*alpha*indexes[x+u+j];
4071 j+=image->columns+width;
4075 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4077 blur_indexes[x]=RoundToQuantum(gamma*pixel.index+bias);
4084 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
4085 if (sync == MagickFalse)
4087 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4092 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4093 #pragma omp critical (MagickCore_SelectiveBlurImageChannel)
4095 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
4097 if (proceed == MagickFalse)
4101 blur_image->type=image->type;
4102 blur_view=DestroyCacheView(blur_view);
4103 image_view=DestroyCacheView(image_view);
4104 kernel=(double *) RelinquishMagickMemory(kernel);
4105 if (status == MagickFalse)
4106 blur_image=DestroyImage(blur_image);
4111 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4115 % S h a d e I m a g e %
4119 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4121 % ShadeImage() shines a distant light on an image to create a
4122 % three-dimensional effect. You control the positioning of the light with
4123 % azimuth and elevation; azimuth is measured in degrees off the x axis
4124 % and elevation is measured in pixels above the Z axis.
4126 % The format of the ShadeImage method is:
4128 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4129 % const double azimuth,const double elevation,ExceptionInfo *exception)
4131 % A description of each parameter follows:
4133 % o image: the image.
4135 % o gray: A value other than zero shades the intensity of each pixel.
4137 % o azimuth, elevation: Define the light source direction.
4139 % o exception: return any errors or warnings in this structure.
4142 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4143 const double azimuth,const double elevation,ExceptionInfo *exception)
4145 #define ShadeImageTag "Shade/Image"
4165 Initialize shaded image attributes.
4167 assert(image != (const Image *) NULL);
4168 assert(image->signature == MagickSignature);
4169 if (image->debug != MagickFalse)
4170 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4171 assert(exception != (ExceptionInfo *) NULL);
4172 assert(exception->signature == MagickSignature);
4173 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4174 if (shade_image == (Image *) NULL)
4175 return((Image *) NULL);
4176 if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
4178 InheritException(exception,&shade_image->exception);
4179 shade_image=DestroyImage(shade_image);
4180 return((Image *) NULL);
4183 Compute the light vector.
4185 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
4186 cos(DegreesToRadians(elevation));
4187 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
4188 cos(DegreesToRadians(elevation));
4189 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
4195 image_view=AcquireCacheView(image);
4196 shade_view=AcquireCacheView(shade_image);
4197 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4198 #pragma omp parallel for schedule(dynamic) shared(progress,status)
4200 for (y=0; y < (long) image->rows; y++)
4210 register const PixelPacket
4219 register PixelPacket
4222 if (status == MagickFalse)
4224 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
4225 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
4227 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4233 Shade this row of pixels.
4235 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
4237 s1=s0+image->columns+2;
4238 s2=s1+image->columns+2;
4239 for (x=0; x < (long) image->columns; x++)
4242 Determine the surface normal and compute shading.
4244 normal.x=(double) (PixelIntensity(s0-1)+PixelIntensity(s1-1)+
4245 PixelIntensity(s2-1)-PixelIntensity(s0+1)-PixelIntensity(s1+1)-
4246 PixelIntensity(s2+1));
4247 normal.y=(double) (PixelIntensity(s2-1)+PixelIntensity(s2)+
4248 PixelIntensity(s2+1)-PixelIntensity(s0-1)-PixelIntensity(s0)-
4249 PixelIntensity(s0+1));
4250 if ((normal.x == 0.0) && (normal.y == 0.0))
4255 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
4256 if (distance > MagickEpsilon)
4259 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
4260 if (normal_distance > (MagickEpsilon*MagickEpsilon))
4261 shade=distance/sqrt((double) normal_distance);
4264 if (gray != MagickFalse)
4266 q->red=(Quantum) shade;
4267 q->green=(Quantum) shade;
4268 q->blue=(Quantum) shade;
4272 q->red=RoundToQuantum(QuantumScale*shade*s1->red);
4273 q->green=RoundToQuantum(QuantumScale*shade*s1->green);
4274 q->blue=RoundToQuantum(QuantumScale*shade*s1->blue);
4276 q->opacity=s1->opacity;
4282 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
4284 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4289 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4290 #pragma omp critical (MagickCore_ShadeImage)
4292 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
4293 if (proceed == MagickFalse)
4297 shade_view=DestroyCacheView(shade_view);
4298 image_view=DestroyCacheView(image_view);
4299 if (status == MagickFalse)
4300 shade_image=DestroyImage(shade_image);
4301 return(shade_image);
4305 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4309 % S h a r p e n I m a g e %
4313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4315 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
4316 % operator of the given radius and standard deviation (sigma). For
4317 % reasonable results, radius should be larger than sigma. Use a radius of 0
4318 % and SharpenImage() selects a suitable radius for you.
4320 % Using a separable kernel would be faster, but the negative weights cancel
4321 % out on the corners of the kernel producing often undesirable ringing in the
4322 % filtered result; this can be avoided by using a 2D gaussian shaped image
4323 % sharpening kernel instead.
4325 % The format of the SharpenImage method is:
4327 % Image *SharpenImage(const Image *image,const double radius,
4328 % const double sigma,ExceptionInfo *exception)
4329 % Image *SharpenImageChannel(const Image *image,const ChannelType channel,
4330 % const double radius,const double sigma,ExceptionInfo *exception)
4332 % A description of each parameter follows:
4334 % o image: the image.
4336 % o channel: the channel type.
4338 % o radius: the radius of the Gaussian, in pixels, not counting the center
4341 % o sigma: the standard deviation of the Laplacian, in pixels.
4343 % o exception: return any errors or warnings in this structure.
4347 MagickExport Image *SharpenImage(const Image *image,const double radius,
4348 const double sigma,ExceptionInfo *exception)
4353 sharp_image=SharpenImageChannel(image,DefaultChannels,radius,sigma,exception);
4354 return(sharp_image);
4357 MagickExport Image *SharpenImageChannel(const Image *image,
4358 const ChannelType channel,const double radius,const double sigma,
4359 ExceptionInfo *exception)
4379 assert(image != (const Image *) NULL);
4380 assert(image->signature == MagickSignature);
4381 if (image->debug != MagickFalse)
4382 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4383 assert(exception != (ExceptionInfo *) NULL);
4384 assert(exception->signature == MagickSignature);
4385 width=GetOptimalKernelWidth2D(radius,sigma);
4386 kernel=(double *) AcquireQuantumMemory((size_t) width*width,sizeof(*kernel));
4387 if (kernel == (double *) NULL)
4388 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4391 for (v=(-((long) width/2)); v <= (long) (width/2); v++)
4393 for (u=(-((long) width/2)); u <= (long) (width/2); u++)
4395 alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
4396 kernel[i]=(double) (-alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
4397 normalize+=kernel[i];
4401 kernel[i/2]=(double) ((-2.0)*normalize);
4402 sharp_image=ConvolveImageChannel(image,channel,width,kernel,exception);
4403 kernel=(double *) RelinquishMagickMemory(kernel);
4404 return(sharp_image);
4408 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4412 % S p r e a d I m a g e %
4416 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4418 % SpreadImage() is a special effects method that randomly displaces each
4419 % pixel in a block defined by the radius parameter.
4421 % The format of the SpreadImage method is:
4423 % Image *SpreadImage(const Image *image,const double radius,
4424 % ExceptionInfo *exception)
4426 % A description of each parameter follows:
4428 % o image: the image.
4430 % o radius: Choose a random pixel in a neighborhood of this extent.
4432 % o exception: return any errors or warnings in this structure.
4435 MagickExport Image *SpreadImage(const Image *image,const double radius,
4436 ExceptionInfo *exception)
4438 #define SpreadImageTag "Spread/Image"
4466 Initialize spread image attributes.
4468 assert(image != (Image *) NULL);
4469 assert(image->signature == MagickSignature);
4470 if (image->debug != MagickFalse)
4471 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4472 assert(exception != (ExceptionInfo *) NULL);
4473 assert(exception->signature == MagickSignature);
4474 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4476 if (spread_image == (Image *) NULL)
4477 return((Image *) NULL);
4478 if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
4480 InheritException(exception,&spread_image->exception);
4481 spread_image=DestroyImage(spread_image);
4482 return((Image *) NULL);
4489 GetMagickPixelPacket(spread_image,&zero);
4490 width=GetOptimalKernelWidth1D(radius,0.5);
4491 resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
4492 random_info=AcquireRandomInfoThreadSet();
4493 image_view=AcquireCacheView(spread_image);
4494 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4495 #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
4497 for (y=0; y < (long) spread_image->rows; y++)
4502 register IndexPacket
4503 *__restrict indexes;
4509 register PixelPacket
4512 if (status == MagickFalse)
4514 q=QueueCacheViewAuthenticPixels(image_view,0,y,spread_image->columns,1,
4516 if (q == (PixelPacket *) NULL)
4521 indexes=GetCacheViewAuthenticIndexQueue(image_view);
4523 id=GetOpenMPThreadId();
4524 for (x=0; x < (long) spread_image->columns; x++)
4526 (void) ResamplePixelColor(resample_filter[id],(double) x+width*
4527 (GetPseudoRandomValue(random_info[id])-0.5),(double) y+width*
4528 (GetPseudoRandomValue(random_info[id])-0.5),&pixel);
4529 SetPixelPacket(spread_image,&pixel,q,indexes+x);
4532 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4534 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4539 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4540 #pragma omp critical (MagickCore_SpreadImage)
4542 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
4543 if (proceed == MagickFalse)
4547 image_view=DestroyCacheView(image_view);
4548 random_info=DestroyRandomInfoThreadSet(random_info);
4549 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
4550 return(spread_image);
4554 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4558 % U n s h a r p M a s k I m a g e %
4562 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4564 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
4565 % image with a Gaussian operator of the given radius and standard deviation
4566 % (sigma). For reasonable results, radius should be larger than sigma. Use a
4567 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
4569 % The format of the UnsharpMaskImage method is:
4571 % Image *UnsharpMaskImage(const Image *image,const double radius,
4572 % const double sigma,const double amount,const double threshold,
4573 % ExceptionInfo *exception)
4574 % Image *UnsharpMaskImageChannel(const Image *image,
4575 % const ChannelType channel,const double radius,const double sigma,
4576 % const double amount,const double threshold,ExceptionInfo *exception)
4578 % A description of each parameter follows:
4580 % o image: the image.
4582 % o channel: the channel type.
4584 % o radius: the radius of the Gaussian, in pixels, not counting the center
4587 % o sigma: the standard deviation of the Gaussian, in pixels.
4589 % o amount: the percentage of the difference between the original and the
4590 % blur image that is added back into the original.
4592 % o threshold: the threshold in pixels needed to apply the diffence amount.
4594 % o exception: return any errors or warnings in this structure.
4598 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
4599 const double sigma,const double amount,const double threshold,
4600 ExceptionInfo *exception)
4605 sharp_image=UnsharpMaskImageChannel(image,DefaultChannels,radius,sigma,amount,
4606 threshold,exception);
4607 return(sharp_image);
4610 MagickExport Image *UnsharpMaskImageChannel(const Image *image,
4611 const ChannelType channel,const double radius,const double sigma,
4612 const double amount,const double threshold,ExceptionInfo *exception)
4614 #define SharpenImageTag "Sharpen/Image"
4636 assert(image != (const Image *) NULL);
4637 assert(image->signature == MagickSignature);
4638 if (image->debug != MagickFalse)
4639 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4640 assert(exception != (ExceptionInfo *) NULL);
4641 unsharp_image=BlurImageChannel(image,channel,radius,sigma,exception);
4642 if (unsharp_image == (Image *) NULL)
4643 return((Image *) NULL);
4644 quantum_threshold=(MagickRealType) QuantumRange*threshold;
4650 GetMagickPixelPacket(image,&zero);
4651 image_view=AcquireCacheView(image);
4652 unsharp_view=AcquireCacheView(unsharp_image);
4653 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4654 #pragma omp parallel for schedule(dynamic) shared(progress,status)
4656 for (y=0; y < (long) image->rows; y++)
4661 register const IndexPacket
4662 *__restrict indexes;
4664 register const PixelPacket
4667 register IndexPacket
4668 *__restrict unsharp_indexes;
4673 register PixelPacket
4676 if (status == MagickFalse)
4678 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4679 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
4681 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4686 indexes=GetCacheViewVirtualIndexQueue(image_view);
4687 unsharp_indexes=GetCacheViewAuthenticIndexQueue(unsharp_view);
4689 for (x=0; x < (long) image->columns; x++)
4691 if ((channel & RedChannel) != 0)
4693 pixel.red=p->red-(MagickRealType) q->red;
4694 if (fabs(2.0*pixel.red) < quantum_threshold)
4695 pixel.red=(MagickRealType) p->red;
4697 pixel.red=(MagickRealType) p->red+(pixel.red*amount);
4698 q->red=RoundToQuantum(pixel.red);
4700 if ((channel & GreenChannel) != 0)
4702 pixel.green=p->green-(MagickRealType) q->green;
4703 if (fabs(2.0*pixel.green) < quantum_threshold)
4704 pixel.green=(MagickRealType) p->green;
4706 pixel.green=(MagickRealType) p->green+(pixel.green*amount);
4707 q->green=RoundToQuantum(pixel.green);
4709 if ((channel & BlueChannel) != 0)
4711 pixel.blue=p->blue-(MagickRealType) q->blue;
4712 if (fabs(2.0*pixel.blue) < quantum_threshold)
4713 pixel.blue=(MagickRealType) p->blue;
4715 pixel.blue=(MagickRealType) p->blue+(pixel.blue*amount);
4716 q->blue=RoundToQuantum(pixel.blue);
4718 if ((channel & OpacityChannel) != 0)
4720 pixel.opacity=p->opacity-(MagickRealType) q->opacity;
4721 if (fabs(2.0*pixel.opacity) < quantum_threshold)
4722 pixel.opacity=(MagickRealType) p->opacity;
4724 pixel.opacity=p->opacity+(pixel.opacity*amount);
4725 q->opacity=RoundToQuantum(pixel.opacity);
4727 if (((channel & IndexChannel) != 0) &&
4728 (image->colorspace == CMYKColorspace))
4730 pixel.index=unsharp_indexes[x]-(MagickRealType) indexes[x];
4731 if (fabs(2.0*pixel.index) < quantum_threshold)
4732 pixel.index=(MagickRealType) unsharp_indexes[x];
4734 pixel.index=(MagickRealType) unsharp_indexes[x]+(pixel.index*
4736 unsharp_indexes[x]=RoundToQuantum(pixel.index);
4741 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4743 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4748 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4749 #pragma omp critical (MagickCore_UnsharpMaskImageChannel)
4751 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
4752 if (proceed == MagickFalse)
4756 unsharp_image->type=image->type;
4757 unsharp_view=DestroyCacheView(unsharp_view);
4758 image_view=DestroyCacheView(image_view);
4759 if (status == MagickFalse)
4760 unsharp_image=DestroyImage(unsharp_image);
4761 return(unsharp_image);