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-2010 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)
178 assert(image != (const Image *) NULL);
179 assert(image->signature == MagickSignature);
180 if (image->debug != MagickFalse)
181 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
182 assert(exception != (ExceptionInfo *) NULL);
183 assert(exception->signature == MagickSignature);
184 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
185 if (blur_image == (Image *) NULL)
186 return((Image *) NULL);
187 if (fabs(sigma) <= MagickEpsilon)
189 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
191 InheritException(exception,&blur_image->exception);
192 blur_image=DestroyImage(blur_image);
193 return((Image *) NULL);
196 Edge detect the image brighness channel, level, blur, and level again.
198 edge_image=EdgeImage(image,radius,exception);
199 if (edge_image == (Image *) NULL)
201 blur_image=DestroyImage(blur_image);
202 return((Image *) NULL);
204 (void) LevelImage(edge_image,"20%,95%");
205 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
206 if (gaussian_image != (Image *) NULL)
208 edge_image=DestroyImage(edge_image);
209 edge_image=gaussian_image;
211 (void) LevelImage(edge_image,"10%,95%");
213 Create a set of kernels from maximum (radius,sigma) to minimum.
215 width=GetOptimalKernelWidth2D(radius,sigma);
216 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
217 if (kernel == (double **) NULL)
219 edge_image=DestroyImage(edge_image);
220 blur_image=DestroyImage(blur_image);
221 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
223 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
224 for (i=0; i < (long) width; i+=2)
226 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
228 if (kernel[i] == (double *) NULL)
231 j=(long) (width-i)/2;
233 for (v=(-j); v <= j; v++)
235 for (u=(-j); u <= j; u++)
237 kernel[i][k]=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
238 (2.0*MagickPI*MagickSigma*MagickSigma);
239 normalize+=kernel[i][k];
243 if (fabs(normalize) <= MagickEpsilon)
245 normalize=1.0/normalize;
246 for (k=0; k < (j*j); k++)
247 kernel[i][k]=normalize*kernel[i][k];
249 if (i < (long) width)
251 for (i-=2; i >= 0; i-=2)
252 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
253 kernel=(double **) RelinquishMagickMemory(kernel);
254 edge_image=DestroyImage(edge_image);
255 blur_image=DestroyImage(blur_image);
256 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
259 Adaptively blur image.
263 GetMagickPixelPacket(image,&bias);
264 SetMagickPixelPacketBias(image,&bias);
265 image_view=AcquireCacheView(image);
266 edge_view=AcquireCacheView(edge_image);
267 blur_view=AcquireCacheView(blur_image);
268 #if defined(MAGICKCORE_OPENMP_SUPPORT)
269 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
271 for (y=0; y < (long) blur_image->rows; y++)
273 register const IndexPacket
276 register const PixelPacket
281 *restrict blur_indexes;
289 if (status == MagickFalse)
291 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
292 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
294 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
299 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
300 for (x=0; x < (long) blur_image->columns; x++)
309 register const double
318 i=(long) (width*QuantumScale*PixelIntensity(r)+0.5);
322 if (i > (long) width)
326 p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
327 ((width-i)/2L),width-i,width-i,exception);
328 if (p == (const PixelPacket *) NULL)
330 indexes=GetCacheViewVirtualIndexQueue(image_view);
333 for (v=0; v < (long) (width-i); v++)
335 for (u=0; u < (long) (width-i); u++)
338 if (((channel & OpacityChannel) != 0) &&
339 (image->matte != MagickFalse))
340 alpha=(MagickRealType) (QuantumScale*GetAlphaPixelComponent(p));
341 if ((channel & RedChannel) != 0)
342 pixel.red+=(*k)*alpha*GetRedPixelComponent(p);
343 if ((channel & GreenChannel) != 0)
344 pixel.green+=(*k)*alpha*GetGreenPixelComponent(p);
345 if ((channel & BlueChannel) != 0)
346 pixel.blue+=(*k)*alpha*GetBluePixelComponent(p);
347 if ((channel & OpacityChannel) != 0)
348 pixel.opacity+=(*k)*GetOpacityPixelComponent(p);
349 if (((channel & IndexChannel) != 0) &&
350 (image->colorspace == CMYKColorspace))
351 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
357 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
358 if ((channel & RedChannel) != 0)
359 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
360 if ((channel & GreenChannel) != 0)
361 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
362 if ((channel & BlueChannel) != 0)
363 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
364 if ((channel & OpacityChannel) != 0)
365 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
366 if (((channel & IndexChannel) != 0) &&
367 (image->colorspace == CMYKColorspace))
368 blur_indexes[x]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
372 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
374 if (image->progress_monitor != (MagickProgressMonitor) NULL)
379 #if defined(MAGICKCORE_OPENMP_SUPPORT)
380 #pragma omp critical (MagickCore_AdaptiveBlurImageChannel)
382 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
384 if (proceed == MagickFalse)
388 blur_image->type=image->type;
389 blur_view=DestroyCacheView(blur_view);
390 edge_view=DestroyCacheView(edge_view);
391 image_view=DestroyCacheView(image_view);
392 edge_image=DestroyImage(edge_image);
393 for (i=0; i < (long) width; i+=2)
394 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
395 kernel=(double **) RelinquishMagickMemory(kernel);
396 if (status == MagickFalse)
397 blur_image=DestroyImage(blur_image);
402 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
406 % A d a p t i v e S h a r p e n I m a g e %
410 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
412 % AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
413 % intensely near image edges and less intensely far from edges. We sharpen the
414 % image with a Gaussian operator of the given radius and standard deviation
415 % (sigma). For reasonable results, radius should be larger than sigma. Use a
416 % radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
418 % The format of the AdaptiveSharpenImage method is:
420 % Image *AdaptiveSharpenImage(const Image *image,const double radius,
421 % const double sigma,ExceptionInfo *exception)
422 % Image *AdaptiveSharpenImageChannel(const Image *image,
423 % const ChannelType channel,double radius,const double sigma,
424 % ExceptionInfo *exception)
426 % A description of each parameter follows:
428 % o image: the image.
430 % o channel: the channel type.
432 % o radius: the radius of the Gaussian, in pixels, not counting the center
435 % o sigma: the standard deviation of the Laplacian, in pixels.
437 % o exception: return any errors or warnings in this structure.
441 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
442 const double sigma,ExceptionInfo *exception)
447 sharp_image=AdaptiveSharpenImageChannel(image,DefaultChannels,radius,sigma,
452 MagickExport Image *AdaptiveSharpenImageChannel(const Image *image,
453 const ChannelType channel,const double radius,const double sigma,
454 ExceptionInfo *exception)
456 #define AdaptiveSharpenImageTag "Convolve/Image"
457 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
493 assert(image != (const Image *) NULL);
494 assert(image->signature == MagickSignature);
495 if (image->debug != MagickFalse)
496 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
497 assert(exception != (ExceptionInfo *) NULL);
498 assert(exception->signature == MagickSignature);
499 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
500 if (sharp_image == (Image *) NULL)
501 return((Image *) NULL);
502 if (fabs(sigma) <= MagickEpsilon)
504 if (SetImageStorageClass(sharp_image,DirectClass) == MagickFalse)
506 InheritException(exception,&sharp_image->exception);
507 sharp_image=DestroyImage(sharp_image);
508 return((Image *) NULL);
511 Edge detect the image brighness channel, level, sharp, and level again.
513 edge_image=EdgeImage(image,radius,exception);
514 if (edge_image == (Image *) NULL)
516 sharp_image=DestroyImage(sharp_image);
517 return((Image *) NULL);
519 (void) LevelImage(edge_image,"20%,95%");
520 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
521 if (gaussian_image != (Image *) NULL)
523 edge_image=DestroyImage(edge_image);
524 edge_image=gaussian_image;
526 (void) LevelImage(edge_image,"10%,95%");
528 Create a set of kernels from maximum (radius,sigma) to minimum.
530 width=GetOptimalKernelWidth2D(radius,sigma);
531 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
532 if (kernel == (double **) NULL)
534 edge_image=DestroyImage(edge_image);
535 sharp_image=DestroyImage(sharp_image);
536 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
538 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
539 for (i=0; i < (long) width; i+=2)
541 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
543 if (kernel[i] == (double *) NULL)
546 j=(long) (width-i)/2;
548 for (v=(-j); v <= j; v++)
550 for (u=(-j); u <= j; u++)
552 kernel[i][k]=(-exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
553 (2.0*MagickPI*MagickSigma*MagickSigma));
554 normalize+=kernel[i][k];
558 if (fabs(normalize) <= MagickEpsilon)
560 normalize=1.0/normalize;
561 for (k=0; k < (j*j); k++)
562 kernel[i][k]=normalize*kernel[i][k];
564 if (i < (long) width)
566 for (i-=2; i >= 0; i-=2)
567 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
568 kernel=(double **) RelinquishMagickMemory(kernel);
569 edge_image=DestroyImage(edge_image);
570 sharp_image=DestroyImage(sharp_image);
571 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
574 Adaptively sharpen image.
578 GetMagickPixelPacket(image,&bias);
579 SetMagickPixelPacketBias(image,&bias);
580 image_view=AcquireCacheView(image);
581 edge_view=AcquireCacheView(edge_image);
582 sharp_view=AcquireCacheView(sharp_image);
583 #if defined(MAGICKCORE_OPENMP_SUPPORT)
584 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
586 for (y=0; y < (long) sharp_image->rows; y++)
588 register const IndexPacket
591 register const PixelPacket
596 *restrict sharp_indexes;
604 if (status == MagickFalse)
606 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
607 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
609 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
614 sharp_indexes=GetCacheViewAuthenticIndexQueue(sharp_view);
615 for (x=0; x < (long) sharp_image->columns; x++)
624 register const double
633 i=(long) (width*(QuantumRange-QuantumScale*PixelIntensity(r))+0.5);
637 if (i > (long) width)
641 p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
642 ((width-i)/2L),width-i,width-i,exception);
643 if (p == (const PixelPacket *) NULL)
645 indexes=GetCacheViewVirtualIndexQueue(image_view);
648 for (v=0; v < (long) (width-i); v++)
650 for (u=0; u < (long) (width-i); u++)
653 if (((channel & OpacityChannel) != 0) &&
654 (image->matte != MagickFalse))
655 alpha=(MagickRealType) (QuantumScale*GetAlphaPixelComponent(p));
656 if ((channel & RedChannel) != 0)
657 pixel.red+=(*k)*alpha*GetRedPixelComponent(p);
658 if ((channel & GreenChannel) != 0)
659 pixel.green+=(*k)*alpha*GetGreenPixelComponent(p);
660 if ((channel & BlueChannel) != 0)
661 pixel.blue+=(*k)*alpha*GetBluePixelComponent(p);
662 if ((channel & OpacityChannel) != 0)
663 pixel.opacity+=(*k)*GetOpacityPixelComponent(p);
664 if (((channel & IndexChannel) != 0) &&
665 (image->colorspace == CMYKColorspace))
666 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
672 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
673 if ((channel & RedChannel) != 0)
674 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
675 if ((channel & GreenChannel) != 0)
676 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
677 if ((channel & BlueChannel) != 0)
678 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
679 if ((channel & OpacityChannel) != 0)
680 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
681 if (((channel & IndexChannel) != 0) &&
682 (image->colorspace == CMYKColorspace))
683 sharp_indexes[x]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
687 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
689 if (image->progress_monitor != (MagickProgressMonitor) NULL)
694 #if defined(MAGICKCORE_OPENMP_SUPPORT)
695 #pragma omp critical (MagickCore_AdaptiveSharpenImageChannel)
697 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
699 if (proceed == MagickFalse)
703 sharp_image->type=image->type;
704 sharp_view=DestroyCacheView(sharp_view);
705 edge_view=DestroyCacheView(edge_view);
706 image_view=DestroyCacheView(image_view);
707 edge_image=DestroyImage(edge_image);
708 for (i=0; i < (long) width; i+=2)
709 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
710 kernel=(double **) RelinquishMagickMemory(kernel);
711 if (status == MagickFalse)
712 sharp_image=DestroyImage(sharp_image);
717 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
721 % B l u r I m a g e %
725 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727 % BlurImage() blurs an image. We convolve the image with a Gaussian operator
728 % of the given radius and standard deviation (sigma). For reasonable results,
729 % the radius should be larger than sigma. Use a radius of 0 and BlurImage()
730 % selects a suitable radius for you.
732 % BlurImage() differs from GaussianBlurImage() in that it uses a separable
733 % kernel which is faster but mathematically equivalent to the non-separable
736 % The format of the BlurImage method is:
738 % Image *BlurImage(const Image *image,const double radius,
739 % const double sigma,ExceptionInfo *exception)
740 % Image *BlurImageChannel(const Image *image,const ChannelType channel,
741 % const double radius,const double sigma,ExceptionInfo *exception)
743 % A description of each parameter follows:
745 % o image: the image.
747 % o channel: the channel type.
749 % o radius: the radius of the Gaussian, in pixels, not counting the center
752 % o sigma: the standard deviation of the Gaussian, in pixels.
754 % o exception: return any errors or warnings in this structure.
758 MagickExport Image *BlurImage(const Image *image,const double radius,
759 const double sigma,ExceptionInfo *exception)
764 blur_image=BlurImageChannel(image,DefaultChannels,radius,sigma,exception);
768 static double *GetBlurKernel(const unsigned long width,const double sigma)
782 Generate a 1-D convolution kernel.
784 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
785 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
786 if (kernel == (double *) NULL)
791 for (k=(-j); k <= j; k++)
793 kernel[i]=exp(-((double) k*k)/(2.0*MagickSigma*MagickSigma))/
794 (MagickSQ2PI*MagickSigma);
795 normalize+=kernel[i];
798 for (i=0; i < (long) width; i++)
799 kernel[i]/=normalize;
803 MagickExport Image *BlurImageChannel(const Image *image,
804 const ChannelType channel,const double radius,const double sigma,
805 ExceptionInfo *exception)
807 #define BlurImageTag "Blur/Image"
837 Initialize blur image attributes.
839 assert(image != (Image *) NULL);
840 assert(image->signature == MagickSignature);
841 if (image->debug != MagickFalse)
842 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
843 assert(exception != (ExceptionInfo *) NULL);
844 assert(exception->signature == MagickSignature);
845 blur_image=CloneImage(image,0,0,MagickTrue,exception);
846 if (blur_image == (Image *) NULL)
847 return((Image *) NULL);
848 if (fabs(sigma) <= MagickEpsilon)
850 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
852 InheritException(exception,&blur_image->exception);
853 blur_image=DestroyImage(blur_image);
854 return((Image *) NULL);
856 width=GetOptimalKernelWidth1D(radius,sigma);
857 kernel=GetBlurKernel(width,sigma);
858 if (kernel == (double *) NULL)
860 blur_image=DestroyImage(blur_image);
861 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
863 if (image->debug != MagickFalse)
866 format[MaxTextExtent],
869 register const double
872 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
873 " BlurImage with %ld kernel:",width);
874 message=AcquireString("");
876 for (i=0; i < (long) width; i++)
879 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",i);
880 (void) ConcatenateString(&message,format);
881 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
882 (void) ConcatenateString(&message,format);
883 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
885 message=DestroyString(message);
892 GetMagickPixelPacket(image,&bias);
893 SetMagickPixelPacketBias(image,&bias);
894 image_view=AcquireCacheView(image);
895 blur_view=AcquireCacheView(blur_image);
896 #if defined(MAGICKCORE_OPENMP_SUPPORT)
897 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
899 for (y=0; y < (long) blur_image->rows; y++)
901 register const IndexPacket
904 register const PixelPacket
908 *restrict blur_indexes;
916 if (status == MagickFalse)
918 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y,image->columns+
920 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
922 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
927 indexes=GetCacheViewVirtualIndexQueue(image_view);
928 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
929 for (x=0; x < (long) blur_image->columns; x++)
934 register const double
937 register const PixelPacket
938 *restrict kernel_pixels;
946 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
948 for (i=0; i < (long) width; i++)
950 pixel.red+=(*k)*kernel_pixels->red;
951 pixel.green+=(*k)*kernel_pixels->green;
952 pixel.blue+=(*k)*kernel_pixels->blue;
956 if ((channel & RedChannel) != 0)
957 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
958 if ((channel & GreenChannel) != 0)
959 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
960 if ((channel & BlueChannel) != 0)
961 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
962 if ((channel & OpacityChannel) != 0)
966 for (i=0; i < (long) width; i++)
968 pixel.opacity+=(*k)*kernel_pixels->opacity;
972 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
974 if (((channel & IndexChannel) != 0) &&
975 (image->colorspace == CMYKColorspace))
977 register const IndexPacket
978 *restrict kernel_indexes;
981 kernel_indexes=indexes;
982 for (i=0; i < (long) width; i++)
984 pixel.index+=(*k)*(*kernel_indexes);
988 blur_indexes[x]=ClampToQuantum(pixel.index);
998 for (i=0; i < (long) width; i++)
1000 alpha=(MagickRealType) (QuantumScale*
1001 GetAlphaPixelComponent(kernel_pixels));
1002 pixel.red+=(*k)*alpha*kernel_pixels->red;
1003 pixel.green+=(*k)*alpha*kernel_pixels->green;
1004 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
1009 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1010 if ((channel & RedChannel) != 0)
1011 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
1012 if ((channel & GreenChannel) != 0)
1013 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
1014 if ((channel & BlueChannel) != 0)
1015 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
1016 if ((channel & OpacityChannel) != 0)
1020 for (i=0; i < (long) width; i++)
1022 pixel.opacity+=(*k)*kernel_pixels->opacity;
1026 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1028 if (((channel & IndexChannel) != 0) &&
1029 (image->colorspace == CMYKColorspace))
1031 register const IndexPacket
1032 *restrict kernel_indexes;
1036 kernel_indexes=indexes;
1037 for (i=0; i < (long) width; i++)
1039 alpha=(MagickRealType) (QuantumScale*
1040 GetAlphaPixelComponent(kernel_pixels));
1041 pixel.index+=(*k)*alpha*(*kernel_indexes);
1046 blur_indexes[x]=ClampToQuantum(gamma*
1047 GetIndexPixelComponent(&pixel));
1053 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1055 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1060 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1061 #pragma omp critical (MagickCore_BlurImageChannel)
1063 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1064 blur_image->columns);
1065 if (proceed == MagickFalse)
1069 blur_view=DestroyCacheView(blur_view);
1070 image_view=DestroyCacheView(image_view);
1074 image_view=AcquireCacheView(blur_image);
1075 blur_view=AcquireCacheView(blur_image);
1076 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1077 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1079 for (x=0; x < (long) blur_image->columns; x++)
1081 register const IndexPacket
1084 register const PixelPacket
1087 register IndexPacket
1088 *restrict blur_indexes;
1093 register PixelPacket
1096 if (status == MagickFalse)
1098 p=GetCacheViewVirtualPixels(image_view,x,-((long) width/2L),1,image->rows+
1100 q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
1101 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1106 indexes=GetCacheViewVirtualIndexQueue(image_view);
1107 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
1108 for (y=0; y < (long) blur_image->rows; y++)
1113 register const double
1116 register const PixelPacket
1117 *restrict kernel_pixels;
1125 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
1127 for (i=0; i < (long) width; i++)
1129 pixel.red+=(*k)*kernel_pixels->red;
1130 pixel.green+=(*k)*kernel_pixels->green;
1131 pixel.blue+=(*k)*kernel_pixels->blue;
1135 if ((channel & RedChannel) != 0)
1136 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
1137 if ((channel & GreenChannel) != 0)
1138 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
1139 if ((channel & BlueChannel) != 0)
1140 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
1141 if ((channel & OpacityChannel) != 0)
1145 for (i=0; i < (long) width; i++)
1147 pixel.opacity+=(*k)*kernel_pixels->opacity;
1151 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1153 if (((channel & IndexChannel) != 0) &&
1154 (image->colorspace == CMYKColorspace))
1156 register const IndexPacket
1157 *restrict kernel_indexes;
1160 kernel_indexes=indexes;
1161 for (i=0; i < (long) width; i++)
1163 pixel.index+=(*k)*(*kernel_indexes);
1167 blur_indexes[y]=ClampToQuantum(pixel.index);
1177 for (i=0; i < (long) width; i++)
1179 alpha=(MagickRealType) (QuantumScale*
1180 GetAlphaPixelComponent(kernel_pixels));
1181 pixel.red+=(*k)*alpha*kernel_pixels->red;
1182 pixel.green+=(*k)*alpha*kernel_pixels->green;
1183 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
1188 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1189 if ((channel & RedChannel) != 0)
1190 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
1191 if ((channel & GreenChannel) != 0)
1192 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
1193 if ((channel & BlueChannel) != 0)
1194 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
1195 if ((channel & OpacityChannel) != 0)
1199 for (i=0; i < (long) width; i++)
1201 pixel.opacity+=(*k)*kernel_pixels->opacity;
1205 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1207 if (((channel & IndexChannel) != 0) &&
1208 (image->colorspace == CMYKColorspace))
1210 register const IndexPacket
1211 *restrict kernel_indexes;
1215 kernel_indexes=indexes;
1216 for (i=0; i < (long) width; i++)
1218 alpha=(MagickRealType) (QuantumScale*
1219 GetAlphaPixelComponent(kernel_pixels));
1220 pixel.index+=(*k)*alpha*(*kernel_indexes);
1225 blur_indexes[y]=ClampToQuantum(gamma*
1226 GetIndexPixelComponent(&pixel));
1232 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1234 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1239 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1240 #pragma omp critical (MagickCore_BlurImageChannel)
1242 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1243 blur_image->columns);
1244 if (proceed == MagickFalse)
1248 blur_view=DestroyCacheView(blur_view);
1249 image_view=DestroyCacheView(image_view);
1250 kernel=(double *) RelinquishMagickMemory(kernel);
1251 if (status == MagickFalse)
1252 blur_image=DestroyImage(blur_image);
1253 blur_image->type=image->type;
1258 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1262 % C o n v o l v e I m a g e %
1266 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1268 % ConvolveImage() applies a custom convolution kernel to the image.
1270 % The format of the ConvolveImage method is:
1272 % Image *ConvolveImage(const Image *image,const unsigned long order,
1273 % const double *kernel,ExceptionInfo *exception)
1274 % Image *ConvolveImageChannel(const Image *image,const ChannelType channel,
1275 % const unsigned long order,const double *kernel,
1276 % ExceptionInfo *exception)
1278 % A description of each parameter follows:
1280 % o image: the image.
1282 % o channel: the channel type.
1284 % o order: the number of columns and rows in the filter kernel.
1286 % o kernel: An array of double representing the convolution kernel.
1288 % o exception: return any errors or warnings in this structure.
1292 MagickExport Image *ConvolveImage(const Image *image,const unsigned long order,
1293 const double *kernel,ExceptionInfo *exception)
1298 convolve_image=ConvolveImageChannel(image,DefaultChannels,order,kernel,
1300 return(convolve_image);
1303 MagickExport Image *ConvolveImageChannel(const Image *image,
1304 const ChannelType channel,const unsigned long order,const double *kernel,
1305 ExceptionInfo *exception)
1307 #define ConvolveImageTag "Convolve/Image"
1339 Initialize convolve image attributes.
1341 assert(image != (Image *) NULL);
1342 assert(image->signature == MagickSignature);
1343 if (image->debug != MagickFalse)
1344 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1345 assert(exception != (ExceptionInfo *) NULL);
1346 assert(exception->signature == MagickSignature);
1348 if ((width % 2) == 0)
1349 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
1350 convolve_image=CloneImage(image,0,0,MagickTrue,exception);
1351 if (convolve_image == (Image *) NULL)
1352 return((Image *) NULL);
1353 if (SetImageStorageClass(convolve_image,DirectClass) == MagickFalse)
1355 InheritException(exception,&convolve_image->exception);
1356 convolve_image=DestroyImage(convolve_image);
1357 return((Image *) NULL);
1359 if (image->debug != MagickFalse)
1362 format[MaxTextExtent],
1369 register const double
1372 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1373 " ConvolveImage with %ldx%ld kernel:",width,width);
1374 message=AcquireString("");
1376 for (v=0; v < (long) width; v++)
1379 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
1380 (void) ConcatenateString(&message,format);
1381 for (u=0; u < (long) width; u++)
1383 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
1384 (void) ConcatenateString(&message,format);
1386 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
1388 message=DestroyString(message);
1393 normal_kernel=(double *) AcquireQuantumMemory(width*width,
1394 sizeof(*normal_kernel));
1395 if (normal_kernel == (double *) NULL)
1397 convolve_image=DestroyImage(convolve_image);
1398 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1401 for (i=0; i < (long) (width*width); i++)
1403 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1404 for (i=0; i < (long) (width*width); i++)
1405 normal_kernel[i]=gamma*kernel[i];
1411 GetMagickPixelPacket(image,&bias);
1412 SetMagickPixelPacketBias(image,&bias);
1413 image_view=AcquireCacheView(image);
1414 convolve_view=AcquireCacheView(convolve_image);
1415 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1416 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1418 for (y=0; y < (long) image->rows; y++)
1423 register const IndexPacket
1426 register const PixelPacket
1429 register IndexPacket
1430 *restrict convolve_indexes;
1435 register PixelPacket
1438 if (status == MagickFalse)
1440 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
1441 2L),image->columns+width,width,exception);
1442 q=GetCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
1444 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1449 indexes=GetCacheViewVirtualIndexQueue(image_view);
1450 convolve_indexes=GetCacheViewAuthenticIndexQueue(convolve_view);
1451 for (x=0; x < (long) image->columns; x++)
1459 register const double
1462 register const PixelPacket
1463 *restrict kernel_pixels;
1471 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
1473 for (v=0; v < (long) width; v++)
1475 for (u=0; u < (long) width; u++)
1477 pixel.red+=(*k)*kernel_pixels[u].red;
1478 pixel.green+=(*k)*kernel_pixels[u].green;
1479 pixel.blue+=(*k)*kernel_pixels[u].blue;
1482 kernel_pixels+=image->columns+width;
1484 if ((channel & RedChannel) != 0)
1485 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
1486 if ((channel & GreenChannel) != 0)
1487 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
1488 if ((channel & BlueChannel) != 0)
1489 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
1490 if ((channel & OpacityChannel) != 0)
1494 for (v=0; v < (long) width; v++)
1496 for (u=0; u < (long) width; u++)
1498 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1501 kernel_pixels+=image->columns+width;
1503 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1505 if (((channel & IndexChannel) != 0) &&
1506 (image->colorspace == CMYKColorspace))
1508 register const IndexPacket
1509 *restrict kernel_indexes;
1512 kernel_indexes=indexes;
1513 for (v=0; v < (long) width; v++)
1515 for (u=0; u < (long) width; u++)
1517 pixel.index+=(*k)*kernel_indexes[u];
1520 kernel_indexes+=image->columns+width;
1522 convolve_indexes[x]=ClampToQuantum(pixel.index);
1532 for (v=0; v < (long) width; v++)
1534 for (u=0; u < (long) width; u++)
1536 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1537 kernel_pixels[u].opacity));
1538 pixel.red+=(*k)*alpha*kernel_pixels[u].red;
1539 pixel.green+=(*k)*alpha*kernel_pixels[u].green;
1540 pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
1544 kernel_pixels+=image->columns+width;
1546 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1547 if ((channel & RedChannel) != 0)
1548 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
1549 if ((channel & GreenChannel) != 0)
1550 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
1551 if ((channel & BlueChannel) != 0)
1552 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
1553 if ((channel & OpacityChannel) != 0)
1557 for (v=0; v < (long) width; v++)
1559 for (u=0; u < (long) width; u++)
1561 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1564 kernel_pixels+=image->columns+width;
1566 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1568 if (((channel & IndexChannel) != 0) &&
1569 (image->colorspace == CMYKColorspace))
1571 register const IndexPacket
1572 *restrict kernel_indexes;
1576 kernel_indexes=indexes;
1577 for (v=0; v < (long) width; v++)
1579 for (u=0; u < (long) width; u++)
1581 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1582 kernel_pixels[u].opacity));
1583 pixel.index+=(*k)*alpha*kernel_indexes[u];
1586 kernel_pixels+=image->columns+width;
1587 kernel_indexes+=image->columns+width;
1589 convolve_indexes[x]=ClampToQuantum(gamma*
1590 GetIndexPixelComponent(&pixel));
1596 sync=SyncCacheViewAuthenticPixels(convolve_view,exception);
1597 if (sync == MagickFalse)
1599 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1604 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1605 #pragma omp critical (MagickCore_ConvolveImageChannel)
1607 proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1608 if (proceed == MagickFalse)
1612 convolve_image->type=image->type;
1613 convolve_view=DestroyCacheView(convolve_view);
1614 image_view=DestroyCacheView(image_view);
1615 normal_kernel=(double *) RelinquishMagickMemory(normal_kernel);
1616 if (status == MagickFalse)
1617 convolve_image=DestroyImage(convolve_image);
1618 return(convolve_image);
1622 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1626 % D e s p e c k l e I m a g e %
1630 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1632 % DespeckleImage() reduces the speckle noise in an image while perserving the
1633 % edges of the original image.
1635 % The format of the DespeckleImage method is:
1637 % Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1639 % A description of each parameter follows:
1641 % o image: the image.
1643 % o exception: return any errors or warnings in this structure.
1647 static Quantum **DestroyPixelThreadSet(Quantum **pixels)
1652 assert(pixels != (Quantum **) NULL);
1653 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
1654 if (pixels[i] != (Quantum *) NULL)
1655 pixels[i]=(Quantum *) RelinquishMagickMemory(pixels[i]);
1656 pixels=(Quantum **) RelinquishAlignedMemory(pixels);
1660 static Quantum **AcquirePixelThreadSet(const size_t count)
1671 number_threads=GetOpenMPMaximumThreads();
1672 pixels=(Quantum **) AcquireAlignedMemory(number_threads,sizeof(*pixels));
1673 if (pixels == (Quantum **) NULL)
1674 return((Quantum **) NULL);
1675 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
1676 for (i=0; i < (long) number_threads; i++)
1678 pixels[i]=(Quantum *) AcquireQuantumMemory(count,sizeof(**pixels));
1679 if (pixels[i] == (Quantum *) NULL)
1680 return(DestroyPixelThreadSet(pixels));
1685 static void Hull(const long x_offset,const long y_offset,
1686 const unsigned long columns,const unsigned long rows,Quantum *f,Quantum *g,
1704 assert(f != (Quantum *) NULL);
1705 assert(g != (Quantum *) NULL);
1708 r=p+(y_offset*((long) columns+2)+x_offset);
1709 for (y=0; y < (long) rows; y++)
1715 for (x=(long) columns; x != 0; x--)
1717 v=(MagickRealType) (*p);
1718 if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1719 v+=ScaleCharToQuantum(1);
1726 for (x=(long) columns; x != 0; x--)
1728 v=(MagickRealType) (*p);
1729 if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
1730 v-=(long) ScaleCharToQuantum(1);
1742 r=q+(y_offset*((long) columns+2)+x_offset);
1743 s=q-(y_offset*((long) columns+2)+x_offset);
1744 for (y=0; y < (long) rows; y++)
1751 for (x=(long) columns; x != 0; x--)
1753 v=(MagickRealType) (*q);
1754 if (((MagickRealType) *s >=
1755 (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1756 ((MagickRealType) *r > v))
1757 v+=ScaleCharToQuantum(1);
1765 for (x=(long) columns; x != 0; x--)
1767 v=(MagickRealType) (*q);
1768 if (((MagickRealType) *s <=
1769 (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1770 ((MagickRealType) *r < v))
1771 v-=(MagickRealType) ScaleCharToQuantum(1);
1785 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1787 #define DespeckleImageTag "Despeckle/Image"
1810 X[4] = {0, 1, 1,-1},
1811 Y[4] = {1, 0, 1, 1};
1814 Allocate despeckled image.
1816 assert(image != (const Image *) NULL);
1817 assert(image->signature == MagickSignature);
1818 if (image->debug != MagickFalse)
1819 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1820 assert(exception != (ExceptionInfo *) NULL);
1821 assert(exception->signature == MagickSignature);
1822 despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1824 if (despeckle_image == (Image *) NULL)
1825 return((Image *) NULL);
1826 if (SetImageStorageClass(despeckle_image,DirectClass) == MagickFalse)
1828 InheritException(exception,&despeckle_image->exception);
1829 despeckle_image=DestroyImage(despeckle_image);
1830 return((Image *) NULL);
1833 Allocate image buffers.
1835 length=(size_t) ((image->columns+2)*(image->rows+2));
1836 pixels=AcquirePixelThreadSet(length);
1837 buffers=AcquirePixelThreadSet(length);
1838 if ((pixels == (Quantum **) NULL) || (buffers == (Quantum **) NULL))
1840 if (buffers != (Quantum **) NULL)
1841 buffers=DestroyPixelThreadSet(buffers);
1842 if (pixels != (Quantum **) NULL)
1843 pixels=DestroyPixelThreadSet(pixels);
1844 despeckle_image=DestroyImage(despeckle_image);
1845 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1848 Reduce speckle in the image.
1851 image_view=AcquireCacheView(image);
1852 despeckle_view=AcquireCacheView(despeckle_image);
1853 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1854 #pragma omp parallel for schedule(dynamic,4) shared(status)
1856 for (channel=0; channel <= 3; channel++)
1871 if (status == MagickFalse)
1873 id=GetOpenMPThreadId();
1875 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
1877 j=(long) image->columns+2;
1878 for (y=0; y < (long) image->rows; y++)
1880 register const PixelPacket
1883 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1884 if (p == (const PixelPacket *) NULL)
1887 for (x=0; x < (long) image->columns; x++)
1891 case 0: pixel[j]=GetRedPixelComponent(p); break;
1892 case 1: pixel[j]=GetGreenPixelComponent(p); break;
1893 case 2: pixel[j]=GetBluePixelComponent(p); break;
1894 case 3: pixel[j]=GetOpacityPixelComponent(p); break;
1902 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1903 for (i=0; i < 4; i++)
1905 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,1);
1906 Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,1);
1907 Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,-1);
1908 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,-1);
1910 j=(long) image->columns+2;
1911 for (y=0; y < (long) image->rows; y++)
1916 register PixelPacket
1919 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1921 if (q == (PixelPacket *) NULL)
1924 for (x=0; x < (long) image->columns; x++)
1928 case 0: q->red=pixel[j]; break;
1929 case 1: q->green=pixel[j]; break;
1930 case 2: q->blue=pixel[j]; break;
1931 case 3: q->opacity=pixel[j]; break;
1937 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1938 if (sync == MagickFalse)
1945 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1950 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1951 #pragma omp critical (MagickCore_DespeckleImage)
1953 proceed=SetImageProgress(image,DespeckleImageTag,channel,3);
1954 if (proceed == MagickFalse)
1958 despeckle_view=DestroyCacheView(despeckle_view);
1959 image_view=DestroyCacheView(image_view);
1960 buffers=DestroyPixelThreadSet(buffers);
1961 pixels=DestroyPixelThreadSet(pixels);
1962 despeckle_image->type=image->type;
1963 if (status == MagickFalse)
1964 despeckle_image=DestroyImage(despeckle_image);
1965 return(despeckle_image);
1969 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1973 % E d g e I m a g e %
1977 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1979 % EdgeImage() finds edges in an image. Radius defines the radius of the
1980 % convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1983 % The format of the EdgeImage method is:
1985 % Image *EdgeImage(const Image *image,const double radius,
1986 % ExceptionInfo *exception)
1988 % A description of each parameter follows:
1990 % o image: the image.
1992 % o radius: the radius of the pixel neighborhood.
1994 % o exception: return any errors or warnings in this structure.
1997 MagickExport Image *EdgeImage(const Image *image,const double radius,
1998 ExceptionInfo *exception)
2012 assert(image != (const Image *) NULL);
2013 assert(image->signature == MagickSignature);
2014 if (image->debug != MagickFalse)
2015 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2016 assert(exception != (ExceptionInfo *) NULL);
2017 assert(exception->signature == MagickSignature);
2018 width=GetOptimalKernelWidth1D(radius,0.5);
2019 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2020 if (kernel == (double *) NULL)
2021 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2022 for (i=0; i < (long) (width*width); i++)
2024 kernel[i/2]=(double) (width*width-1.0);
2025 edge_image=ConvolveImage(image,width,kernel,exception);
2026 kernel=(double *) RelinquishMagickMemory(kernel);
2031 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2035 % E m b o s s I m a g e %
2039 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2041 % EmbossImage() returns a grayscale image with a three-dimensional effect.
2042 % We convolve the image with a Gaussian operator of the given radius and
2043 % standard deviation (sigma). For reasonable results, radius should be
2044 % larger than sigma. Use a radius of 0 and Emboss() selects a suitable
2047 % The format of the EmbossImage method is:
2049 % Image *EmbossImage(const Image *image,const double radius,
2050 % const double sigma,ExceptionInfo *exception)
2052 % A description of each parameter follows:
2054 % o image: the image.
2056 % o radius: the radius of the pixel neighborhood.
2058 % o sigma: the standard deviation of the Gaussian, in pixels.
2060 % o exception: return any errors or warnings in this structure.
2063 MagickExport Image *EmbossImage(const Image *image,const double radius,
2064 const double sigma,ExceptionInfo *exception)
2084 assert(image != (Image *) NULL);
2085 assert(image->signature == MagickSignature);
2086 if (image->debug != MagickFalse)
2087 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2088 assert(exception != (ExceptionInfo *) NULL);
2089 assert(exception->signature == MagickSignature);
2090 width=GetOptimalKernelWidth2D(radius,sigma);
2091 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2092 if (kernel == (double *) NULL)
2093 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2097 for (v=(-j); v <= j; v++)
2099 for (u=(-j); u <= j; u++)
2101 kernel[i]=((u < 0) || (v < 0) ? -8.0 : 8.0)*
2102 exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
2103 (2.0*MagickPI*MagickSigma*MagickSigma);
2110 emboss_image=ConvolveImage(image,width,kernel,exception);
2111 if (emboss_image != (Image *) NULL)
2112 (void) EqualizeImage(emboss_image);
2113 kernel=(double *) RelinquishMagickMemory(kernel);
2114 return(emboss_image);
2118 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2122 % F i l t e r I m a g e %
2126 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2128 % FilterImage() applies a custom convolution kernel to the image.
2130 % The format of the FilterImage method is:
2132 % Image *FilterImage(const Image *image,const MagickKernel *kernel,
2133 % ExceptionInfo *exception)
2134 % Image *FilterImageChannel(const Image *image,const ChannelType channel,
2135 % const MagickKernel *kernel,ExceptionInfo *exception)
2137 % A description of each parameter follows:
2139 % o image: the image.
2141 % o channel: the channel type.
2143 % o kernel: the filtering kernel.
2145 % o exception: return any errors or warnings in this structure.
2149 MagickExport Image *FilterImage(const Image *image,const MagickKernel *kernel,
2150 ExceptionInfo *exception)
2155 filter_image=FilterImageChannel(image,DefaultChannels,kernel,exception);
2156 return(filter_image);
2159 MagickExport Image *FilterImageChannel(const Image *image,
2160 const ChannelType channel,const MagickKernel *kernel,ExceptionInfo *exception)
2162 #define FilterImageTag "Filter/Image"
2191 Initialize filter image attributes.
2193 assert(image != (Image *) NULL);
2194 assert(image->signature == MagickSignature);
2195 if (image->debug != MagickFalse)
2196 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2197 assert(exception != (ExceptionInfo *) NULL);
2198 assert(exception->signature == MagickSignature);
2199 if ((kernel->width % 2) == 0)
2200 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
2201 filter_image=CloneImage(image,0,0,MagickTrue,exception);
2202 if (filter_image == (Image *) NULL)
2203 return((Image *) NULL);
2204 if (SetImageStorageClass(filter_image,DirectClass) == MagickFalse)
2206 InheritException(exception,&filter_image->exception);
2207 filter_image=DestroyImage(filter_image);
2208 return((Image *) NULL);
2210 if (image->debug != MagickFalse)
2213 format[MaxTextExtent],
2220 register const double
2223 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2224 " FilterImage with %ldx%ld kernel:",kernel->width,kernel->height);
2225 message=AcquireString("");
2227 for (v=0; v < (long) kernel->height; v++)
2230 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
2231 (void) ConcatenateString(&message,format);
2232 for (u=0; u < (long) kernel->width; u++)
2234 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
2235 (void) ConcatenateString(&message,format);
2237 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
2239 message=DestroyString(message);
2244 normal_kernel=(double *) AcquireQuantumMemory(kernel->width*kernel->height,
2245 sizeof(*normal_kernel));
2246 if (normal_kernel == (double *) NULL)
2248 filter_image=DestroyImage(filter_image);
2249 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2252 for (i=0; i < (long) (kernel->width*kernel->height); i++)
2253 gamma+=kernel->values[i];
2254 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2255 for (i=0; i < (long) (kernel->width*kernel->height); i++)
2256 normal_kernel[i]=gamma*kernel->values[i];
2262 GetMagickPixelPacket(image,&bias);
2263 SetMagickPixelPacketBias(image,&bias);
2264 image_view=AcquireCacheView(image);
2265 filter_view=AcquireCacheView(filter_image);
2266 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2267 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2269 for (y=0; y < (long) image->rows; y++)
2274 register const IndexPacket
2277 register const PixelPacket
2280 register IndexPacket
2281 *restrict filter_indexes;
2286 register PixelPacket
2289 if (status == MagickFalse)
2291 p=GetCacheViewVirtualPixels(image_view,-((long) kernel->width/2L),
2292 y-(long) (kernel->height/2L),image->columns+kernel->width,kernel->height,
2294 q=GetCacheViewAuthenticPixels(filter_view,0,y,filter_image->columns,1,
2296 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2301 indexes=GetCacheViewVirtualIndexQueue(image_view);
2302 filter_indexes=GetCacheViewAuthenticIndexQueue(filter_view);
2303 for (x=0; x < (long) image->columns; x++)
2311 register const double
2314 register const PixelPacket
2315 *restrict kernel_pixels;
2323 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
2325 for (v=0; v < (long) kernel->width; v++)
2327 for (u=0; u < (long) kernel->height; u++)
2329 pixel.red+=(*k)*kernel_pixels[u].red;
2330 pixel.green+=(*k)*kernel_pixels[u].green;
2331 pixel.blue+=(*k)*kernel_pixels[u].blue;
2334 kernel_pixels+=image->columns+kernel->width;
2336 if ((channel & RedChannel) != 0)
2337 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2338 if ((channel & GreenChannel) != 0)
2339 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2340 if ((channel & BlueChannel) != 0)
2341 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2342 if ((channel & OpacityChannel) != 0)
2346 for (v=0; v < (long) kernel->width; v++)
2348 for (u=0; u < (long) kernel->height; u++)
2350 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
2353 kernel_pixels+=image->columns+kernel->width;
2355 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2357 if (((channel & IndexChannel) != 0) &&
2358 (image->colorspace == CMYKColorspace))
2360 register const IndexPacket
2361 *restrict kernel_indexes;
2364 kernel_indexes=indexes;
2365 for (v=0; v < (long) kernel->width; v++)
2367 for (u=0; u < (long) kernel->height; u++)
2369 pixel.index+=(*k)*kernel_indexes[u];
2372 kernel_indexes+=image->columns+kernel->width;
2374 filter_indexes[x]=ClampToQuantum(pixel.index);
2384 for (v=0; v < (long) kernel->width; v++)
2386 for (u=0; u < (long) kernel->height; u++)
2388 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
2389 kernel_pixels[u].opacity));
2390 pixel.red+=(*k)*alpha*kernel_pixels[u].red;
2391 pixel.green+=(*k)*alpha*kernel_pixels[u].green;
2392 pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
2396 kernel_pixels+=image->columns+kernel->width;
2398 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2399 if ((channel & RedChannel) != 0)
2400 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2401 if ((channel & GreenChannel) != 0)
2402 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2403 if ((channel & BlueChannel) != 0)
2404 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2405 if ((channel & OpacityChannel) != 0)
2409 for (v=0; v < (long) kernel->width; v++)
2411 for (u=0; u < (long) kernel->height; u++)
2413 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
2416 kernel_pixels+=image->columns+kernel->width;
2418 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2420 if (((channel & IndexChannel) != 0) &&
2421 (image->colorspace == CMYKColorspace))
2423 register const IndexPacket
2424 *restrict kernel_indexes;
2428 kernel_indexes=indexes;
2429 for (v=0; v < (long) kernel->width; v++)
2431 for (u=0; u < (long) kernel->height; u++)
2433 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
2434 kernel_pixels[u].opacity));
2435 pixel.index+=(*k)*alpha*kernel_indexes[u];
2438 kernel_pixels+=image->columns+kernel->width;
2439 kernel_indexes+=image->columns+kernel->width;
2441 filter_indexes[x]=ClampToQuantum(gamma*
2442 GetIndexPixelComponent(&pixel));
2448 sync=SyncCacheViewAuthenticPixels(filter_view,exception);
2449 if (sync == MagickFalse)
2451 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2456 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2457 #pragma omp critical (MagickCore_FilterImageChannel)
2459 proceed=SetImageProgress(image,FilterImageTag,progress++,image->rows);
2460 if (proceed == MagickFalse)
2464 filter_image->type=image->type;
2465 filter_view=DestroyCacheView(filter_view);
2466 image_view=DestroyCacheView(image_view);
2467 normal_kernel=(double *) RelinquishMagickMemory(normal_kernel);
2468 if (status == MagickFalse)
2469 filter_image=DestroyImage(filter_image);
2470 return(filter_image);
2474 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2478 % G a u s s i a n B l u r I m a g e %
2482 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2484 % GaussianBlurImage() blurs an image. We convolve the image with a
2485 % Gaussian operator of the given radius and standard deviation (sigma).
2486 % For reasonable results, the radius should be larger than sigma. Use a
2487 % radius of 0 and GaussianBlurImage() selects a suitable radius for you
2489 % The format of the GaussianBlurImage method is:
2491 % Image *GaussianBlurImage(const Image *image,onst double radius,
2492 % const double sigma,ExceptionInfo *exception)
2493 % Image *GaussianBlurImageChannel(const Image *image,
2494 % const ChannelType channel,const double radius,const double sigma,
2495 % ExceptionInfo *exception)
2497 % A description of each parameter follows:
2499 % o image: the image.
2501 % o channel: the channel type.
2503 % o radius: the radius of the Gaussian, in pixels, not counting the center
2506 % o sigma: the standard deviation of the Gaussian, in pixels.
2508 % o exception: return any errors or warnings in this structure.
2512 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
2513 const double sigma,ExceptionInfo *exception)
2518 blur_image=GaussianBlurImageChannel(image,DefaultChannels,radius,sigma,
2523 MagickExport Image *GaussianBlurImageChannel(const Image *image,
2524 const ChannelType channel,const double radius,const double sigma,
2525 ExceptionInfo *exception)
2544 assert(image != (const Image *) NULL);
2545 assert(image->signature == MagickSignature);
2546 if (image->debug != MagickFalse)
2547 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2548 assert(exception != (ExceptionInfo *) NULL);
2549 assert(exception->signature == MagickSignature);
2550 width=GetOptimalKernelWidth2D(radius,sigma);
2551 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2552 if (kernel == (double *) NULL)
2553 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2556 for (v=(-j); v <= j; v++)
2558 for (u=(-j); u <= j; u++)
2559 kernel[i++]=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
2560 (2.0*MagickPI*MagickSigma*MagickSigma);
2562 blur_image=ConvolveImageChannel(image,channel,width,kernel,exception);
2563 kernel=(double *) RelinquishMagickMemory(kernel);
2568 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2572 % M e d i a n F i l t e r I m a g e %
2576 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2578 % MedianFilterImage() applies a digital filter that improves the quality
2579 % of a noisy image. Each pixel is replaced by the median in a set of
2580 % neighboring pixels as defined by radius.
2582 % The algorithm was contributed by Mike Edmonds and implements an insertion
2583 % sort for selecting median color-channel values. For more on this algorithm
2584 % see "Skip Lists: A probabilistic Alternative to Balanced Trees" by William
2585 % Pugh in the June 1990 of Communications of the ACM.
2587 % The format of the MedianFilterImage method is:
2589 % Image *MedianFilterImage(const Image *image,const double radius,
2590 % ExceptionInfo *exception)
2592 % A description of each parameter follows:
2594 % o image: the image.
2596 % o radius: the radius of the pixel neighborhood.
2598 % o exception: return any errors or warnings in this structure.
2602 #define MedianListChannels 5
2604 typedef struct _MedianListNode
2612 typedef struct _MedianSkipList
2621 typedef struct _MedianPixelList
2629 lists[MedianListChannels];
2632 static MedianPixelList *DestroyMedianPixelList(MedianPixelList *pixel_list)
2637 if (pixel_list == (MedianPixelList *) NULL)
2638 return((MedianPixelList *) NULL);
2639 for (i=0; i < MedianListChannels; i++)
2640 if (pixel_list->lists[i].nodes != (MedianListNode *) NULL)
2641 pixel_list->lists[i].nodes=(MedianListNode *) RelinquishMagickMemory(
2642 pixel_list->lists[i].nodes);
2643 pixel_list=(MedianPixelList *) RelinquishAlignedMemory(pixel_list);
2647 static MedianPixelList **DestroyMedianPixelListThreadSet(
2648 MedianPixelList **pixel_list)
2653 assert(pixel_list != (MedianPixelList **) NULL);
2654 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
2655 if (pixel_list[i] != (MedianPixelList *) NULL)
2656 pixel_list[i]=DestroyMedianPixelList(pixel_list[i]);
2657 pixel_list=(MedianPixelList **) RelinquishAlignedMemory(pixel_list);
2661 static MedianPixelList *AcquireMedianPixelList(const unsigned long width)
2669 pixel_list=(MedianPixelList *) AcquireAlignedMemory(1,sizeof(*pixel_list));
2670 if (pixel_list == (MedianPixelList *) NULL)
2672 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
2673 pixel_list->center=width*width/2;
2674 for (i=0; i < MedianListChannels; i++)
2676 pixel_list->lists[i].nodes=(MedianListNode *) AcquireQuantumMemory(65537UL,
2677 sizeof(*pixel_list->lists[i].nodes));
2678 if (pixel_list->lists[i].nodes == (MedianListNode *) NULL)
2679 return(DestroyMedianPixelList(pixel_list));
2680 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
2681 sizeof(*pixel_list->lists[i].nodes));
2683 pixel_list->signature=MagickSignature;
2687 static MedianPixelList **AcquireMedianPixelListThreadSet(
2688 const unsigned long width)
2699 number_threads=GetOpenMPMaximumThreads();
2700 pixel_list=(MedianPixelList **) AcquireAlignedMemory(number_threads,
2701 sizeof(*pixel_list));
2702 if (pixel_list == (MedianPixelList **) NULL)
2703 return((MedianPixelList **) NULL);
2704 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
2705 for (i=0; i < (long) number_threads; i++)
2707 pixel_list[i]=AcquireMedianPixelList(width);
2708 if (pixel_list[i] == (MedianPixelList *) NULL)
2709 return(DestroyMedianPixelListThreadSet(pixel_list));
2714 static void AddNodeMedianPixelList(MedianPixelList *pixel_list,
2715 const long channel,const unsigned long color)
2720 register MedianSkipList
2728 Initialize the node.
2730 list=pixel_list->lists+channel;
2731 list->nodes[color].signature=pixel_list->signature;
2732 list->nodes[color].count=1;
2734 Determine where it belongs in the list.
2737 for (level=list->level; level >= 0; level--)
2739 while (list->nodes[search].next[level] < color)
2740 search=list->nodes[search].next[level];
2741 update[level]=search;
2744 Generate a pseudo-random level for this node.
2746 for (level=0; ; level++)
2748 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
2749 if ((pixel_list->seed & 0x300) != 0x300)
2754 if (level > (list->level+2))
2755 level=list->level+2;
2757 If we're raising the list's level, link back to the root node.
2759 while (level > list->level)
2762 update[list->level]=65536UL;
2765 Link the node into the skip-list.
2769 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
2770 list->nodes[update[level]].next[level]=color;
2772 while (level-- > 0);
2775 static MagickPixelPacket GetMedianPixelList(MedianPixelList *pixel_list)
2783 register MedianSkipList
2792 channels[MedianListChannels];
2795 Find the median value for each of the color.
2797 center=pixel_list->center;
2798 for (channel=0; channel < 5; channel++)
2800 list=pixel_list->lists+channel;
2805 color=list->nodes[color].next[0];
2806 count+=list->nodes[color].count;
2808 while (count <= center);
2809 channels[channel]=(unsigned short) color;
2811 GetMagickPixelPacket((const Image *) NULL,&pixel);
2812 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
2813 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
2814 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
2815 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
2816 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
2820 static inline void InsertMedianPixelList(const Image *image,
2821 const PixelPacket *pixel,const IndexPacket *indexes,
2822 MedianPixelList *pixel_list)
2830 index=ScaleQuantumToShort(pixel->red);
2831 signature=pixel_list->lists[0].nodes[index].signature;
2832 if (signature == pixel_list->signature)
2833 pixel_list->lists[0].nodes[index].count++;
2835 AddNodeMedianPixelList(pixel_list,0,index);
2836 index=ScaleQuantumToShort(pixel->green);
2837 signature=pixel_list->lists[1].nodes[index].signature;
2838 if (signature == pixel_list->signature)
2839 pixel_list->lists[1].nodes[index].count++;
2841 AddNodeMedianPixelList(pixel_list,1,index);
2842 index=ScaleQuantumToShort(pixel->blue);
2843 signature=pixel_list->lists[2].nodes[index].signature;
2844 if (signature == pixel_list->signature)
2845 pixel_list->lists[2].nodes[index].count++;
2847 AddNodeMedianPixelList(pixel_list,2,index);
2848 index=ScaleQuantumToShort(pixel->opacity);
2849 signature=pixel_list->lists[3].nodes[index].signature;
2850 if (signature == pixel_list->signature)
2851 pixel_list->lists[3].nodes[index].count++;
2853 AddNodeMedianPixelList(pixel_list,3,index);
2854 if (image->colorspace == CMYKColorspace)
2855 index=ScaleQuantumToShort(*indexes);
2856 signature=pixel_list->lists[4].nodes[index].signature;
2857 if (signature == pixel_list->signature)
2858 pixel_list->lists[4].nodes[index].count++;
2860 AddNodeMedianPixelList(pixel_list,4,index);
2863 static void ResetMedianPixelList(MedianPixelList *pixel_list)
2871 register MedianListNode
2874 register MedianSkipList
2878 Reset the skip-list.
2880 for (channel=0; channel < 5; channel++)
2882 list=pixel_list->lists+channel;
2883 root=list->nodes+65536UL;
2885 for (level=0; level < 9; level++)
2886 root->next[level]=65536UL;
2888 pixel_list->seed=pixel_list->signature++;
2891 MagickExport Image *MedianFilterImage(const Image *image,const double radius,
2892 ExceptionInfo *exception)
2894 #define MedianFilterImageTag "MedianFilter/Image"
2911 **restrict pixel_list;
2917 Initialize median image attributes.
2919 assert(image != (Image *) NULL);
2920 assert(image->signature == MagickSignature);
2921 if (image->debug != MagickFalse)
2922 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2923 assert(exception != (ExceptionInfo *) NULL);
2924 assert(exception->signature == MagickSignature);
2925 width=GetOptimalKernelWidth2D(radius,0.5);
2926 if ((image->columns < width) || (image->rows < width))
2927 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
2928 median_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2930 if (median_image == (Image *) NULL)
2931 return((Image *) NULL);
2932 if (SetImageStorageClass(median_image,DirectClass) == MagickFalse)
2934 InheritException(exception,&median_image->exception);
2935 median_image=DestroyImage(median_image);
2936 return((Image *) NULL);
2938 pixel_list=AcquireMedianPixelListThreadSet(width);
2939 if (pixel_list == (MedianPixelList **) NULL)
2941 median_image=DestroyImage(median_image);
2942 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2945 Median filter each image row.
2949 image_view=AcquireCacheView(image);
2950 median_view=AcquireCacheView(median_image);
2951 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2952 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2954 for (y=0; y < (long) median_image->rows; y++)
2956 register const IndexPacket
2959 register const PixelPacket
2962 register IndexPacket
2963 *restrict median_indexes;
2969 register PixelPacket
2972 if (status == MagickFalse)
2974 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
2975 2L),image->columns+width,width,exception);
2976 q=QueueCacheViewAuthenticPixels(median_view,0,y,median_image->columns,1,
2978 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2983 indexes=GetCacheViewVirtualIndexQueue(image_view);
2984 median_indexes=GetCacheViewAuthenticIndexQueue(median_view);
2985 id=GetOpenMPThreadId();
2986 for (x=0; x < (long) median_image->columns; x++)
2991 register const PixelPacket
2994 register const IndexPacket
3003 ResetMedianPixelList(pixel_list[id]);
3004 for (v=0; v < (long) width; v++)
3006 for (u=0; u < (long) width; u++)
3007 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
3008 r+=image->columns+width;
3009 s+=image->columns+width;
3011 pixel=GetMedianPixelList(pixel_list[id]);
3012 SetPixelPacket(median_image,&pixel,q,median_indexes+x);
3016 if (SyncCacheViewAuthenticPixels(median_view,exception) == MagickFalse)
3018 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3023 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3024 #pragma omp critical (MagickCore_MedianFilterImage)
3026 proceed=SetImageProgress(image,MedianFilterImageTag,progress++,
3028 if (proceed == MagickFalse)
3032 median_view=DestroyCacheView(median_view);
3033 image_view=DestroyCacheView(image_view);
3034 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
3035 return(median_image);
3039 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3043 % M o t i o n B l u r I m a g e %
3047 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3049 % MotionBlurImage() simulates motion blur. We convolve the image with a
3050 % Gaussian operator of the given radius and standard deviation (sigma).
3051 % For reasonable results, radius should be larger than sigma. Use a
3052 % radius of 0 and MotionBlurImage() selects a suitable radius for you.
3053 % Angle gives the angle of the blurring motion.
3055 % Andrew Protano contributed this effect.
3057 % The format of the MotionBlurImage method is:
3059 % Image *MotionBlurImage(const Image *image,const double radius,
3060 % const double sigma,const double angle,ExceptionInfo *exception)
3061 % Image *MotionBlurImageChannel(const Image *image,const ChannelType channel,
3062 % const double radius,const double sigma,const double angle,
3063 % ExceptionInfo *exception)
3065 % A description of each parameter follows:
3067 % o image: the image.
3069 % o channel: the channel type.
3071 % o radius: the radius of the Gaussian, in pixels, not counting the center
3072 % o radius: the radius of the Gaussian, in pixels, not counting
3075 % o sigma: the standard deviation of the Gaussian, in pixels.
3077 % o angle: Apply the effect along this angle.
3079 % o exception: return any errors or warnings in this structure.
3083 static double *GetMotionBlurKernel(const unsigned long width,const double sigma)
3093 Generate a 1-D convolution kernel.
3095 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3096 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
3097 if (kernel == (double *) NULL)
3100 for (i=0; i < (long) width; i++)
3102 kernel[i]=exp((-((double) i*i)/(double) (2.0*MagickSigma*MagickSigma)))/
3103 (MagickSQ2PI*MagickSigma);
3104 normalize+=kernel[i];
3106 for (i=0; i < (long) width; i++)
3107 kernel[i]/=normalize;
3111 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
3112 const double sigma,const double angle,ExceptionInfo *exception)
3117 motion_blur=MotionBlurImageChannel(image,DefaultChannels,radius,sigma,angle,
3119 return(motion_blur);
3122 MagickExport Image *MotionBlurImageChannel(const Image *image,
3123 const ChannelType channel,const double radius,const double sigma,
3124 const double angle,ExceptionInfo *exception)
3126 typedef struct _OffsetInfo
3165 assert(image != (Image *) NULL);
3166 assert(image->signature == MagickSignature);
3167 if (image->debug != MagickFalse)
3168 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3169 assert(exception != (ExceptionInfo *) NULL);
3170 width=GetOptimalKernelWidth1D(radius,sigma);
3171 kernel=GetMotionBlurKernel(width,sigma);
3172 if (kernel == (double *) NULL)
3173 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3174 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
3175 if (offset == (OffsetInfo *) NULL)
3177 kernel=(double *) RelinquishMagickMemory(kernel);
3178 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3180 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3181 if (blur_image == (Image *) NULL)
3183 kernel=(double *) RelinquishMagickMemory(kernel);
3184 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3185 return((Image *) NULL);
3187 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3189 kernel=(double *) RelinquishMagickMemory(kernel);
3190 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3191 InheritException(exception,&blur_image->exception);
3192 blur_image=DestroyImage(blur_image);
3193 return((Image *) NULL);
3195 point.x=(double) width*sin(DegreesToRadians(angle));
3196 point.y=(double) width*cos(DegreesToRadians(angle));
3197 for (i=0; i < (long) width; i++)
3199 offset[i].x=(long) ((i*point.y)/hypot(point.x,point.y)+0.5);
3200 offset[i].y=(long) ((i*point.x)/hypot(point.x,point.y)+0.5);
3207 GetMagickPixelPacket(image,&bias);
3208 image_view=AcquireCacheView(image);
3209 blur_view=AcquireCacheView(blur_image);
3210 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3211 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3213 for (y=0; y < (long) image->rows; y++)
3215 register IndexPacket
3216 *restrict blur_indexes;
3221 register PixelPacket
3224 if (status == MagickFalse)
3226 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3228 if (q == (PixelPacket *) NULL)
3233 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
3234 for (x=0; x < (long) image->columns; x++)
3248 register const IndexPacket
3253 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
3255 for (i=0; i < (long) width; i++)
3257 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
3258 offset[i].y,&pixel,exception);
3259 qixel.red+=(*k)*pixel.red;
3260 qixel.green+=(*k)*pixel.green;
3261 qixel.blue+=(*k)*pixel.blue;
3262 qixel.opacity+=(*k)*pixel.opacity;
3263 if (image->colorspace == CMYKColorspace)
3265 indexes=GetCacheViewVirtualIndexQueue(image_view);
3266 qixel.index+=(*k)*(*indexes);
3270 if ((channel & RedChannel) != 0)
3271 q->red=ClampToQuantum(qixel.red);
3272 if ((channel & GreenChannel) != 0)
3273 q->green=ClampToQuantum(qixel.green);
3274 if ((channel & BlueChannel) != 0)
3275 q->blue=ClampToQuantum(qixel.blue);
3276 if ((channel & OpacityChannel) != 0)
3277 q->opacity=ClampToQuantum(qixel.opacity);
3278 if (((channel & IndexChannel) != 0) &&
3279 (image->colorspace == CMYKColorspace))
3280 blur_indexes[x]=(IndexPacket) ClampToQuantum(qixel.index);
3290 for (i=0; i < (long) width; i++)
3292 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
3293 offset[i].y,&pixel,exception);
3294 alpha=(MagickRealType) (QuantumScale*GetAlphaPixelComponent(&pixel));
3295 qixel.red+=(*k)*alpha*pixel.red;
3296 qixel.green+=(*k)*alpha*pixel.green;
3297 qixel.blue+=(*k)*alpha*pixel.blue;
3298 qixel.opacity+=(*k)*pixel.opacity;
3299 if (image->colorspace == CMYKColorspace)
3301 indexes=GetCacheViewVirtualIndexQueue(image_view);
3302 qixel.index+=(*k)*alpha*(*indexes);
3307 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3308 if ((channel & RedChannel) != 0)
3309 q->red=ClampToQuantum(gamma*qixel.red);
3310 if ((channel & GreenChannel) != 0)
3311 q->green=ClampToQuantum(gamma*qixel.green);
3312 if ((channel & BlueChannel) != 0)
3313 q->blue=ClampToQuantum(gamma*qixel.blue);
3314 if ((channel & OpacityChannel) != 0)
3315 q->opacity=ClampToQuantum(qixel.opacity);
3316 if (((channel & IndexChannel) != 0) &&
3317 (image->colorspace == CMYKColorspace))
3318 blur_indexes[x]=(IndexPacket) ClampToQuantum(gamma*qixel.index);
3322 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3324 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3329 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3330 #pragma omp critical (MagickCore_MotionBlurImageChannel)
3332 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3333 if (proceed == MagickFalse)
3337 blur_view=DestroyCacheView(blur_view);
3338 image_view=DestroyCacheView(image_view);
3339 kernel=(double *) RelinquishMagickMemory(kernel);
3340 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3341 if (status == MagickFalse)
3342 blur_image=DestroyImage(blur_image);
3347 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3351 % P r e v i e w I m a g e %
3355 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3357 % PreviewImage() tiles 9 thumbnails of the specified image with an image
3358 % processing operation applied with varying parameters. This may be helpful
3359 % pin-pointing an appropriate parameter for a particular image processing
3362 % The format of the PreviewImages method is:
3364 % Image *PreviewImages(const Image *image,const PreviewType preview,
3365 % ExceptionInfo *exception)
3367 % A description of each parameter follows:
3369 % o image: the image.
3371 % o preview: the image processing operation.
3373 % o exception: return any errors or warnings in this structure.
3376 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
3377 ExceptionInfo *exception)
3379 #define NumberTiles 9
3380 #define PreviewImageTag "Preview/Image"
3381 #define DefaultPreviewGeometry "204x204+10+10"
3384 factor[MaxTextExtent],
3385 label[MaxTextExtent];
3427 Open output image file.
3429 assert(image != (Image *) NULL);
3430 assert(image->signature == MagickSignature);
3431 if (image->debug != MagickFalse)
3432 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3436 preview_info=AcquireImageInfo();
3437 SetGeometry(image,&geometry);
3438 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
3439 &geometry.width,&geometry.height);
3440 images=NewImageList();
3442 GetQuantizeInfo(&quantize_info);
3448 for (i=0; i < NumberTiles; i++)
3450 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
3451 if (thumbnail == (Image *) NULL)
3453 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
3455 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
3456 if (i == (NumberTiles/2))
3458 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
3459 AppendImageToList(&images,thumbnail);
3467 preview_image=RotateImage(thumbnail,degrees,exception);
3468 (void) FormatMagickString(label,MaxTextExtent,"rotate %g",degrees);
3474 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
3475 (void) FormatMagickString(label,MaxTextExtent,"shear %gx%g",
3476 degrees,2.0*degrees);
3481 x=(long) ((i+1)*thumbnail->columns)/NumberTiles;
3482 y=(long) ((i+1)*thumbnail->rows)/NumberTiles;
3483 preview_image=RollImage(thumbnail,x,y,exception);
3484 (void) FormatMagickString(label,MaxTextExtent,"roll %ldx%ld",x,y);
3489 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3490 if (preview_image == (Image *) NULL)
3492 (void) FormatMagickString(factor,MaxTextExtent,"100,100,%g",
3494 (void) ModulateImage(preview_image,factor);
3495 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3498 case SaturationPreview:
3500 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3501 if (preview_image == (Image *) NULL)
3503 (void) FormatMagickString(factor,MaxTextExtent,"100,%g",
3505 (void) ModulateImage(preview_image,factor);
3506 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3509 case BrightnessPreview:
3511 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3512 if (preview_image == (Image *) NULL)
3514 (void) FormatMagickString(factor,MaxTextExtent,"%g",2.0*percentage);
3515 (void) ModulateImage(preview_image,factor);
3516 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3522 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3523 if (preview_image == (Image *) NULL)
3526 (void) GammaImageChannel(preview_image,DefaultChannels,gamma);
3527 (void) FormatMagickString(label,MaxTextExtent,"gamma %g",gamma);
3532 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3533 if (preview_image != (Image *) NULL)
3534 for (x=0; x < i; x++)
3535 (void) ContrastImage(preview_image,MagickTrue);
3536 (void) FormatMagickString(label,MaxTextExtent,"contrast (%ld)",i+1);
3541 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3542 if (preview_image == (Image *) NULL)
3544 for (x=0; x < i; x++)
3545 (void) ContrastImage(preview_image,MagickFalse);
3546 (void) FormatMagickString(label,MaxTextExtent,"+contrast (%ld)",i+1);
3549 case GrayscalePreview:
3551 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3552 if (preview_image == (Image *) NULL)
3555 quantize_info.number_colors=colors;
3556 quantize_info.colorspace=GRAYColorspace;
3557 (void) QuantizeImage(&quantize_info,preview_image);
3558 (void) FormatMagickString(label,MaxTextExtent,
3559 "-colorspace gray -colors %ld",colors);
3562 case QuantizePreview:
3564 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3565 if (preview_image == (Image *) NULL)
3568 quantize_info.number_colors=colors;
3569 (void) QuantizeImage(&quantize_info,preview_image);
3570 (void) FormatMagickString(label,MaxTextExtent,"colors %ld",colors);
3573 case DespecklePreview:
3575 for (x=0; x < (i-1); x++)
3577 preview_image=DespeckleImage(thumbnail,exception);
3578 if (preview_image == (Image *) NULL)
3580 thumbnail=DestroyImage(thumbnail);
3581 thumbnail=preview_image;
3583 preview_image=DespeckleImage(thumbnail,exception);
3584 if (preview_image == (Image *) NULL)
3586 (void) FormatMagickString(label,MaxTextExtent,"despeckle (%ld)",i+1);
3589 case ReduceNoisePreview:
3591 preview_image=ReduceNoiseImage(thumbnail,radius,exception);
3592 (void) FormatMagickString(label,MaxTextExtent,"noise %g",radius);
3595 case AddNoisePreview:
3601 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
3606 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
3611 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
3616 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
3621 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
3626 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
3631 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
3635 preview_image=ReduceNoiseImage(thumbnail,(double) i,exception);
3636 (void) FormatMagickString(label,MaxTextExtent,"+noise %s",factor);
3639 case SharpenPreview:
3641 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
3642 (void) FormatMagickString(label,MaxTextExtent,"sharpen %gx%g",
3648 preview_image=BlurImage(thumbnail,radius,sigma,exception);
3649 (void) FormatMagickString(label,MaxTextExtent,"blur %gx%g",radius,
3653 case ThresholdPreview:
3655 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3656 if (preview_image == (Image *) NULL)
3658 (void) BilevelImage(thumbnail,
3659 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3660 (void) FormatMagickString(label,MaxTextExtent,"threshold %g",
3661 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3664 case EdgeDetectPreview:
3666 preview_image=EdgeImage(thumbnail,radius,exception);
3667 (void) FormatMagickString(label,MaxTextExtent,"edge %g",radius);
3672 preview_image=SpreadImage(thumbnail,radius,exception);
3673 (void) FormatMagickString(label,MaxTextExtent,"spread %g",
3677 case SolarizePreview:
3679 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3680 if (preview_image == (Image *) NULL)
3682 (void) SolarizeImage(preview_image,(double) QuantumRange*
3684 (void) FormatMagickString(label,MaxTextExtent,"solarize %g",
3685 (QuantumRange*percentage)/100.0);
3691 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
3693 (void) FormatMagickString(label,MaxTextExtent,"shade %gx%g",
3699 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3700 if (preview_image == (Image *) NULL)
3702 geometry.width=(unsigned long) (2*i+2);
3703 geometry.height=(unsigned long) (2*i+2);
3706 (void) RaiseImage(preview_image,&geometry,MagickTrue);
3707 (void) FormatMagickString(label,MaxTextExtent,"raise %lux%lu%+ld%+ld",
3708 geometry.width,geometry.height,geometry.x,geometry.y);
3711 case SegmentPreview:
3713 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3714 if (preview_image == (Image *) NULL)
3717 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
3719 (void) FormatMagickString(label,MaxTextExtent,"segment %gx%g",
3720 threshold,threshold);
3725 preview_image=SwirlImage(thumbnail,degrees,exception);
3726 (void) FormatMagickString(label,MaxTextExtent,"swirl %g",degrees);
3730 case ImplodePreview:
3733 preview_image=ImplodeImage(thumbnail,degrees,exception);
3734 (void) FormatMagickString(label,MaxTextExtent,"implode %g",degrees);
3740 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
3741 (void) FormatMagickString(label,MaxTextExtent,"wave %gx%g",
3742 0.5*degrees,2.0*degrees);
3745 case OilPaintPreview:
3747 preview_image=OilPaintImage(thumbnail,(double) radius,exception);
3748 (void) FormatMagickString(label,MaxTextExtent,"paint %g",radius);
3751 case CharcoalDrawingPreview:
3753 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
3755 (void) FormatMagickString(label,MaxTextExtent,"charcoal %gx%g",
3762 filename[MaxTextExtent];
3770 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3771 if (preview_image == (Image *) NULL)
3773 preview_info->quality=(unsigned long) percentage;
3774 (void) FormatMagickString(factor,MaxTextExtent,"%lu",
3775 preview_info->quality);
3776 file=AcquireUniqueFileResource(filename);
3779 (void) FormatMagickString(preview_image->filename,MaxTextExtent,
3780 "jpeg:%s",filename);
3781 status=WriteImage(preview_info,preview_image);
3782 if (status != MagickFalse)
3787 (void) CopyMagickString(preview_info->filename,
3788 preview_image->filename,MaxTextExtent);
3789 quality_image=ReadImage(preview_info,exception);
3790 if (quality_image != (Image *) NULL)
3792 preview_image=DestroyImage(preview_image);
3793 preview_image=quality_image;
3796 (void) RelinquishUniqueFileResource(preview_image->filename);
3797 if ((GetBlobSize(preview_image)/1024) >= 1024)
3798 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gmb ",
3799 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
3802 if (GetBlobSize(preview_image) >= 1024)
3803 (void) FormatMagickString(label,MaxTextExtent,
3804 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
3805 GetBlobSize(preview_image))/1024.0);
3807 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%lub ",
3808 factor,(unsigned long) GetBlobSize(thumbnail));
3812 thumbnail=DestroyImage(thumbnail);
3816 if (preview_image == (Image *) NULL)
3818 (void) DeleteImageProperty(preview_image,"label");
3819 (void) SetImageProperty(preview_image,"label",label);
3820 AppendImageToList(&images,preview_image);
3821 proceed=SetImageProgress(image,PreviewImageTag,i,NumberTiles);
3822 if (proceed == MagickFalse)
3825 if (images == (Image *) NULL)
3827 preview_info=DestroyImageInfo(preview_info);
3828 return((Image *) NULL);
3833 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
3834 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
3835 montage_info->shadow=MagickTrue;
3836 (void) CloneString(&montage_info->tile,"3x3");
3837 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
3838 (void) CloneString(&montage_info->frame,DefaultTileFrame);
3839 montage_image=MontageImages(images,montage_info,exception);
3840 montage_info=DestroyMontageInfo(montage_info);
3841 images=DestroyImageList(images);
3842 if (montage_image == (Image *) NULL)
3843 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3844 if (montage_image->montage != (char *) NULL)
3847 Free image directory.
3849 montage_image->montage=(char *) RelinquishMagickMemory(
3850 montage_image->montage);
3851 if (image->directory != (char *) NULL)
3852 montage_image->directory=(char *) RelinquishMagickMemory(
3853 montage_image->directory);
3855 preview_info=DestroyImageInfo(preview_info);
3856 return(montage_image);
3860 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3864 % R a d i a l B l u r I m a g e %
3868 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3870 % RadialBlurImage() applies a radial blur to the image.
3872 % Andrew Protano contributed this effect.
3874 % The format of the RadialBlurImage method is:
3876 % Image *RadialBlurImage(const Image *image,const double angle,
3877 % ExceptionInfo *exception)
3878 % Image *RadialBlurImageChannel(const Image *image,const ChannelType channel,
3879 % const double angle,ExceptionInfo *exception)
3881 % A description of each parameter follows:
3883 % o image: the image.
3885 % o channel: the channel type.
3887 % o angle: the angle of the radial blur.
3889 % o exception: return any errors or warnings in this structure.
3893 MagickExport Image *RadialBlurImage(const Image *image,const double angle,
3894 ExceptionInfo *exception)
3899 blur_image=RadialBlurImageChannel(image,DefaultChannels,angle,exception);
3903 MagickExport Image *RadialBlurImageChannel(const Image *image,
3904 const ChannelType channel,const double angle,ExceptionInfo *exception)
3940 Allocate blur image.
3942 assert(image != (Image *) NULL);
3943 assert(image->signature == MagickSignature);
3944 if (image->debug != MagickFalse)
3945 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3946 assert(exception != (ExceptionInfo *) NULL);
3947 assert(exception->signature == MagickSignature);
3948 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3949 if (blur_image == (Image *) NULL)
3950 return((Image *) NULL);
3951 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3953 InheritException(exception,&blur_image->exception);
3954 blur_image=DestroyImage(blur_image);
3955 return((Image *) NULL);
3957 blur_center.x=(double) image->columns/2.0;
3958 blur_center.y=(double) image->rows/2.0;
3959 blur_radius=hypot(blur_center.x,blur_center.y);
3960 n=(unsigned long) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+
3962 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
3963 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3964 sizeof(*cos_theta));
3965 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3966 sizeof(*sin_theta));
3967 if ((cos_theta == (MagickRealType *) NULL) ||
3968 (sin_theta == (MagickRealType *) NULL))
3970 blur_image=DestroyImage(blur_image);
3971 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3973 offset=theta*(MagickRealType) (n-1)/2.0;
3974 for (i=0; i < (long) n; i++)
3976 cos_theta[i]=cos((double) (theta*i-offset));
3977 sin_theta[i]=sin((double) (theta*i-offset));
3984 GetMagickPixelPacket(image,&bias);
3985 image_view=AcquireCacheView(image);
3986 blur_view=AcquireCacheView(blur_image);
3987 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3988 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3990 for (y=0; y < (long) blur_image->rows; y++)
3992 register const IndexPacket
3995 register IndexPacket
3996 *restrict blur_indexes;
4001 register PixelPacket
4004 if (status == MagickFalse)
4006 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
4008 if (q == (PixelPacket *) NULL)
4013 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
4014 for (x=0; x < (long) blur_image->columns; x++)
4035 center.x=(double) x-blur_center.x;
4036 center.y=(double) y-blur_center.y;
4037 radius=hypot((double) center.x,center.y);
4042 step=(unsigned long) (blur_radius/radius);
4051 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
4053 for (i=0; i < (long) n; i+=step)
4055 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
4056 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
4057 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
4059 qixel.red+=pixel.red;
4060 qixel.green+=pixel.green;
4061 qixel.blue+=pixel.blue;
4062 qixel.opacity+=pixel.opacity;
4063 if (image->colorspace == CMYKColorspace)
4065 indexes=GetCacheViewVirtualIndexQueue(image_view);
4066 qixel.index+=(*indexes);
4070 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
4072 if ((channel & RedChannel) != 0)
4073 q->red=ClampToQuantum(normalize*qixel.red);
4074 if ((channel & GreenChannel) != 0)
4075 q->green=ClampToQuantum(normalize*qixel.green);
4076 if ((channel & BlueChannel) != 0)
4077 q->blue=ClampToQuantum(normalize*qixel.blue);
4078 if ((channel & OpacityChannel) != 0)
4079 q->opacity=ClampToQuantum(normalize*qixel.opacity);
4080 if (((channel & IndexChannel) != 0) &&
4081 (image->colorspace == CMYKColorspace))
4082 blur_indexes[x]=(IndexPacket) ClampToQuantum(normalize*qixel.index);
4092 for (i=0; i < (long) n; i+=step)
4094 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
4095 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
4096 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
4098 alpha=(MagickRealType) (QuantumScale*
4099 GetAlphaPixelComponent(&pixel));
4100 qixel.red+=alpha*pixel.red;
4101 qixel.green+=alpha*pixel.green;
4102 qixel.blue+=alpha*pixel.blue;
4103 qixel.opacity+=pixel.opacity;
4104 if (image->colorspace == CMYKColorspace)
4106 indexes=GetCacheViewVirtualIndexQueue(image_view);
4107 qixel.index+=alpha*(*indexes);
4112 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4113 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
4115 if ((channel & RedChannel) != 0)
4116 q->red=ClampToQuantum(gamma*qixel.red);
4117 if ((channel & GreenChannel) != 0)
4118 q->green=ClampToQuantum(gamma*qixel.green);
4119 if ((channel & BlueChannel) != 0)
4120 q->blue=ClampToQuantum(gamma*qixel.blue);
4121 if ((channel & OpacityChannel) != 0)
4122 q->opacity=ClampToQuantum(normalize*qixel.opacity);
4123 if (((channel & IndexChannel) != 0) &&
4124 (image->colorspace == CMYKColorspace))
4125 blur_indexes[x]=(IndexPacket) ClampToQuantum(gamma*qixel.index);
4129 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
4131 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4136 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4137 #pragma omp critical (MagickCore_RadialBlurImageChannel)
4139 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
4140 if (proceed == MagickFalse)
4144 blur_view=DestroyCacheView(blur_view);
4145 image_view=DestroyCacheView(image_view);
4146 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
4147 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
4148 if (status == MagickFalse)
4149 blur_image=DestroyImage(blur_image);
4154 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4158 % R e d u c e N o i s e I m a g e %
4162 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4164 % ReduceNoiseImage() smooths the contours of an image while still preserving
4165 % edge information. The algorithm works by replacing each pixel with its
4166 % neighbor closest in value. A neighbor is defined by radius. Use a radius
4167 % of 0 and ReduceNoise() selects a suitable radius for you.
4169 % The format of the ReduceNoiseImage method is:
4171 % Image *ReduceNoiseImage(const Image *image,const double radius,
4172 % ExceptionInfo *exception)
4174 % A description of each parameter follows:
4176 % o image: the image.
4178 % o radius: the radius of the pixel neighborhood.
4180 % o exception: return any errors or warnings in this structure.
4184 static MagickPixelPacket GetNonpeakMedianPixelList(MedianPixelList *pixel_list)
4192 register MedianSkipList
4206 Finds the median value for each of the color.
4208 center=pixel_list->center;
4209 for (channel=0; channel < 5; channel++)
4211 list=pixel_list->lists+channel;
4213 next=list->nodes[color].next[0];
4219 next=list->nodes[color].next[0];
4220 count+=list->nodes[color].count;
4222 while (count <= center);
4223 if ((previous == 65536UL) && (next != 65536UL))
4226 if ((previous != 65536UL) && (next == 65536UL))
4228 channels[channel]=(unsigned short) color;
4230 GetMagickPixelPacket((const Image *) NULL,&pixel);
4231 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4232 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4233 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
4234 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
4235 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
4239 MagickExport Image *ReduceNoiseImage(const Image *image,const double radius,
4240 ExceptionInfo *exception)
4242 #define ReduceNoiseImageTag "ReduceNoise/Image"
4259 **restrict pixel_list;
4265 Initialize noise image attributes.
4267 assert(image != (Image *) NULL);
4268 assert(image->signature == MagickSignature);
4269 if (image->debug != MagickFalse)
4270 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4271 assert(exception != (ExceptionInfo *) NULL);
4272 assert(exception->signature == MagickSignature);
4273 width=GetOptimalKernelWidth2D(radius,0.5);
4274 if ((image->columns < width) || (image->rows < width))
4275 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
4276 noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4278 if (noise_image == (Image *) NULL)
4279 return((Image *) NULL);
4280 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
4282 InheritException(exception,&noise_image->exception);
4283 noise_image=DestroyImage(noise_image);
4284 return((Image *) NULL);
4286 pixel_list=AcquireMedianPixelListThreadSet(width);
4287 if (pixel_list == (MedianPixelList **) NULL)
4289 noise_image=DestroyImage(noise_image);
4290 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4297 image_view=AcquireCacheView(image);
4298 noise_view=AcquireCacheView(noise_image);
4299 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4300 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4302 for (y=0; y < (long) noise_image->rows; y++)
4304 register const IndexPacket
4307 register const PixelPacket
4310 register IndexPacket
4311 *restrict noise_indexes;
4317 register PixelPacket
4320 if (status == MagickFalse)
4322 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
4323 2L),image->columns+width,width,exception);
4324 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
4326 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4331 indexes=GetCacheViewVirtualIndexQueue(image_view);
4332 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
4333 id=GetOpenMPThreadId();
4334 for (x=0; x < (long) noise_image->columns; x++)
4339 register const PixelPacket
4342 register const IndexPacket
4351 ResetMedianPixelList(pixel_list[id]);
4352 for (v=0; v < (long) width; v++)
4354 for (u=0; u < (long) width; u++)
4355 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
4356 r+=image->columns+width;
4357 s+=image->columns+width;
4359 pixel=GetNonpeakMedianPixelList(pixel_list[id]);
4360 SetPixelPacket(noise_image,&pixel,q,noise_indexes+x);
4364 if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
4366 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4371 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4372 #pragma omp critical (MagickCore_ReduceNoiseImage)
4374 proceed=SetImageProgress(image,ReduceNoiseImageTag,progress++,
4376 if (proceed == MagickFalse)
4380 noise_view=DestroyCacheView(noise_view);
4381 image_view=DestroyCacheView(image_view);
4382 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
4383 return(noise_image);
4387 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4391 % S e l e c t i v e B l u r I m a g e %
4395 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4397 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
4398 % It is similar to the unsharpen mask that sharpens everything with contrast
4399 % above a certain threshold.
4401 % The format of the SelectiveBlurImage method is:
4403 % Image *SelectiveBlurImage(const Image *image,const double radius,
4404 % const double sigma,const double threshold,ExceptionInfo *exception)
4405 % Image *SelectiveBlurImageChannel(const Image *image,
4406 % const ChannelType channel,const double radius,const double sigma,
4407 % const double threshold,ExceptionInfo *exception)
4409 % A description of each parameter follows:
4411 % o image: the image.
4413 % o channel: the channel type.
4415 % o radius: the radius of the Gaussian, in pixels, not counting the center
4418 % o sigma: the standard deviation of the Gaussian, in pixels.
4420 % o threshold: only pixels within this contrast threshold are included
4421 % in the blur operation.
4423 % o exception: return any errors or warnings in this structure.
4427 static inline MagickBooleanType SelectiveContrast(const PixelPacket *p,
4428 const PixelPacket *q,const double threshold)
4430 if (fabs(PixelIntensity(p)-PixelIntensity(q)) < threshold)
4432 return(MagickFalse);
4435 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
4436 const double sigma,const double threshold,ExceptionInfo *exception)
4441 blur_image=SelectiveBlurImageChannel(image,DefaultChannels,radius,sigma,
4442 threshold,exception);
4446 MagickExport Image *SelectiveBlurImageChannel(const Image *image,
4447 const ChannelType channel,const double radius,const double sigma,
4448 const double threshold,ExceptionInfo *exception)
4450 #define SelectiveBlurImageTag "SelectiveBlur/Image"
4482 Initialize blur image attributes.
4484 assert(image != (Image *) NULL);
4485 assert(image->signature == MagickSignature);
4486 if (image->debug != MagickFalse)
4487 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4488 assert(exception != (ExceptionInfo *) NULL);
4489 assert(exception->signature == MagickSignature);
4490 width=GetOptimalKernelWidth1D(radius,sigma);
4491 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
4492 if (kernel == (double *) NULL)
4493 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4496 for (v=(-j); v <= j; v++)
4498 for (u=(-j); u <= j; u++)
4499 kernel[i++]=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
4500 (2.0*MagickPI*MagickSigma*MagickSigma);
4502 if (image->debug != MagickFalse)
4505 format[MaxTextExtent],
4512 register const double
4515 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
4516 " SelectiveBlurImage with %ldx%ld kernel:",width,width);
4517 message=AcquireString("");
4519 for (v=0; v < (long) width; v++)
4522 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
4523 (void) ConcatenateString(&message,format);
4524 for (u=0; u < (long) width; u++)
4526 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
4527 (void) ConcatenateString(&message,format);
4529 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
4531 message=DestroyString(message);
4533 blur_image=CloneImage(image,0,0,MagickTrue,exception);
4534 if (blur_image == (Image *) NULL)
4535 return((Image *) NULL);
4536 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
4538 InheritException(exception,&blur_image->exception);
4539 blur_image=DestroyImage(blur_image);
4540 return((Image *) NULL);
4543 Threshold blur image.
4547 GetMagickPixelPacket(image,&bias);
4548 SetMagickPixelPacketBias(image,&bias);
4549 image_view=AcquireCacheView(image);
4550 blur_view=AcquireCacheView(blur_image);
4551 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4552 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4554 for (y=0; y < (long) image->rows; y++)
4562 register const IndexPacket
4565 register const PixelPacket
4568 register IndexPacket
4569 *restrict blur_indexes;
4574 register PixelPacket
4577 if (status == MagickFalse)
4579 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
4580 2L),image->columns+width,width,exception);
4581 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
4583 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4588 indexes=GetCacheViewVirtualIndexQueue(image_view);
4589 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
4590 for (x=0; x < (long) image->columns; x++)
4599 register const double
4609 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
4611 for (v=0; v < (long) width; v++)
4613 for (u=0; u < (long) width; u++)
4615 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4617 pixel.red+=(*k)*(p+u+j)->red;
4618 pixel.green+=(*k)*(p+u+j)->green;
4619 pixel.blue+=(*k)*(p+u+j)->blue;
4624 j+=image->columns+width;
4628 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4629 if ((channel & RedChannel) != 0)
4630 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
4631 if ((channel & GreenChannel) != 0)
4632 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
4633 if ((channel & BlueChannel) != 0)
4634 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
4636 if ((channel & OpacityChannel) != 0)
4640 for (v=0; v < (long) width; v++)
4642 for (u=0; u < (long) width; u++)
4644 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4646 pixel.opacity+=(*k)*(p+u+j)->opacity;
4651 j+=image->columns+width;
4655 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4657 SetOpacityPixelComponent(q,ClampToQuantum(gamma*
4658 GetOpacityPixelComponent(&pixel)));
4661 if (((channel & IndexChannel) != 0) &&
4662 (image->colorspace == CMYKColorspace))
4666 for (v=0; v < (long) width; v++)
4668 for (u=0; u < (long) width; u++)
4670 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4672 pixel.index+=(*k)*indexes[x+u+j];
4677 j+=image->columns+width;
4681 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4683 blur_indexes[x]=ClampToQuantum(gamma*
4684 GetIndexPixelComponent(&pixel));
4693 for (v=0; v < (long) width; v++)
4695 for (u=0; u < (long) width; u++)
4697 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4699 alpha=(MagickRealType) (QuantumScale*
4700 GetAlphaPixelComponent(p+u+j));
4701 pixel.red+=(*k)*alpha*(p+u+j)->red;
4702 pixel.green+=(*k)*alpha*(p+u+j)->green;
4703 pixel.blue+=(*k)*alpha*(p+u+j)->blue;
4704 pixel.opacity+=(*k)*(p+u+j)->opacity;
4709 j+=image->columns+width;
4713 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4714 if ((channel & RedChannel) != 0)
4715 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
4716 if ((channel & GreenChannel) != 0)
4717 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
4718 if ((channel & BlueChannel) != 0)
4719 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
4721 if ((channel & OpacityChannel) != 0)
4725 for (v=0; v < (long) width; v++)
4727 for (u=0; u < (long) width; u++)
4729 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4731 pixel.opacity+=(*k)*(p+u+j)->opacity;
4736 j+=image->columns+width;
4740 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4742 SetOpacityPixelComponent(q,
4743 ClampOpacityPixelComponent(&pixel));
4746 if (((channel & IndexChannel) != 0) &&
4747 (image->colorspace == CMYKColorspace))
4751 for (v=0; v < (long) width; v++)
4753 for (u=0; u < (long) width; u++)
4755 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4757 alpha=(MagickRealType) (QuantumScale*
4758 GetAlphaPixelComponent(p+u+j));
4759 pixel.index+=(*k)*alpha*indexes[x+u+j];
4764 j+=image->columns+width;
4768 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4770 blur_indexes[x]=ClampToQuantum(gamma*
4771 GetIndexPixelComponent(&pixel));
4778 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
4779 if (sync == MagickFalse)
4781 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4786 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4787 #pragma omp critical (MagickCore_SelectiveBlurImageChannel)
4789 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
4791 if (proceed == MagickFalse)
4795 blur_image->type=image->type;
4796 blur_view=DestroyCacheView(blur_view);
4797 image_view=DestroyCacheView(image_view);
4798 kernel=(double *) RelinquishMagickMemory(kernel);
4799 if (status == MagickFalse)
4800 blur_image=DestroyImage(blur_image);
4805 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4809 % S h a d e I m a g e %
4813 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4815 % ShadeImage() shines a distant light on an image to create a
4816 % three-dimensional effect. You control the positioning of the light with
4817 % azimuth and elevation; azimuth is measured in degrees off the x axis
4818 % and elevation is measured in pixels above the Z axis.
4820 % The format of the ShadeImage method is:
4822 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4823 % const double azimuth,const double elevation,ExceptionInfo *exception)
4825 % A description of each parameter follows:
4827 % o image: the image.
4829 % o gray: A value other than zero shades the intensity of each pixel.
4831 % o azimuth, elevation: Define the light source direction.
4833 % o exception: return any errors or warnings in this structure.
4836 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4837 const double azimuth,const double elevation,ExceptionInfo *exception)
4839 #define ShadeImageTag "Shade/Image"
4859 Initialize shaded image attributes.
4861 assert(image != (const Image *) NULL);
4862 assert(image->signature == MagickSignature);
4863 if (image->debug != MagickFalse)
4864 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4865 assert(exception != (ExceptionInfo *) NULL);
4866 assert(exception->signature == MagickSignature);
4867 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4868 if (shade_image == (Image *) NULL)
4869 return((Image *) NULL);
4870 if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
4872 InheritException(exception,&shade_image->exception);
4873 shade_image=DestroyImage(shade_image);
4874 return((Image *) NULL);
4877 Compute the light vector.
4879 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
4880 cos(DegreesToRadians(elevation));
4881 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
4882 cos(DegreesToRadians(elevation));
4883 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
4889 image_view=AcquireCacheView(image);
4890 shade_view=AcquireCacheView(shade_image);
4891 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4892 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4894 for (y=0; y < (long) image->rows; y++)
4904 register const PixelPacket
4913 register PixelPacket
4916 if (status == MagickFalse)
4918 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
4919 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
4921 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4927 Shade this row of pixels.
4929 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
4931 s1=s0+image->columns+2;
4932 s2=s1+image->columns+2;
4933 for (x=0; x < (long) image->columns; x++)
4936 Determine the surface normal and compute shading.
4938 normal.x=(double) (PixelIntensity(s0-1)+PixelIntensity(s1-1)+
4939 PixelIntensity(s2-1)-PixelIntensity(s0+1)-PixelIntensity(s1+1)-
4940 PixelIntensity(s2+1));
4941 normal.y=(double) (PixelIntensity(s2-1)+PixelIntensity(s2)+
4942 PixelIntensity(s2+1)-PixelIntensity(s0-1)-PixelIntensity(s0)-
4943 PixelIntensity(s0+1));
4944 if ((normal.x == 0.0) && (normal.y == 0.0))
4949 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
4950 if (distance > MagickEpsilon)
4953 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
4954 if (normal_distance > (MagickEpsilon*MagickEpsilon))
4955 shade=distance/sqrt((double) normal_distance);
4958 if (gray != MagickFalse)
4960 q->red=(Quantum) shade;
4961 q->green=(Quantum) shade;
4962 q->blue=(Quantum) shade;
4966 q->red=ClampToQuantum(QuantumScale*shade*s1->red);
4967 q->green=ClampToQuantum(QuantumScale*shade*s1->green);
4968 q->blue=ClampToQuantum(QuantumScale*shade*s1->blue);
4970 q->opacity=s1->opacity;
4976 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
4978 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4983 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4984 #pragma omp critical (MagickCore_ShadeImage)
4986 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
4987 if (proceed == MagickFalse)
4991 shade_view=DestroyCacheView(shade_view);
4992 image_view=DestroyCacheView(image_view);
4993 if (status == MagickFalse)
4994 shade_image=DestroyImage(shade_image);
4995 return(shade_image);
4999 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5003 % S h a r p e n I m a g e %
5007 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5009 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
5010 % operator of the given radius and standard deviation (sigma). For
5011 % reasonable results, radius should be larger than sigma. Use a radius of 0
5012 % and SharpenImage() selects a suitable radius for you.
5014 % Using a separable kernel would be faster, but the negative weights cancel
5015 % out on the corners of the kernel producing often undesirable ringing in the
5016 % filtered result; this can be avoided by using a 2D gaussian shaped image
5017 % sharpening kernel instead.
5019 % The format of the SharpenImage method is:
5021 % Image *SharpenImage(const Image *image,const double radius,
5022 % const double sigma,ExceptionInfo *exception)
5023 % Image *SharpenImageChannel(const Image *image,const ChannelType channel,
5024 % const double radius,const double sigma,ExceptionInfo *exception)
5026 % A description of each parameter follows:
5028 % o image: the image.
5030 % o channel: the channel type.
5032 % o radius: the radius of the Gaussian, in pixels, not counting the center
5035 % o sigma: the standard deviation of the Laplacian, in pixels.
5037 % o exception: return any errors or warnings in this structure.
5041 MagickExport Image *SharpenImage(const Image *image,const double radius,
5042 const double sigma,ExceptionInfo *exception)
5047 sharp_image=SharpenImageChannel(image,DefaultChannels,radius,sigma,exception);
5048 return(sharp_image);
5051 MagickExport Image *SharpenImageChannel(const Image *image,
5052 const ChannelType channel,const double radius,const double sigma,
5053 ExceptionInfo *exception)
5073 assert(image != (const Image *) NULL);
5074 assert(image->signature == MagickSignature);
5075 if (image->debug != MagickFalse)
5076 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5077 assert(exception != (ExceptionInfo *) NULL);
5078 assert(exception->signature == MagickSignature);
5079 width=GetOptimalKernelWidth2D(radius,sigma);
5080 kernel=(double *) AcquireQuantumMemory((size_t) width*width,sizeof(*kernel));
5081 if (kernel == (double *) NULL)
5082 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5086 for (v=(-j); v <= j; v++)
5088 for (u=(-j); u <= j; u++)
5090 kernel[i]=(-exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
5091 (2.0*MagickPI*MagickSigma*MagickSigma));
5092 normalize+=kernel[i];
5096 kernel[i/2]=(double) ((-2.0)*normalize);
5097 sharp_image=ConvolveImageChannel(image,channel,width,kernel,exception);
5098 kernel=(double *) RelinquishMagickMemory(kernel);
5099 return(sharp_image);
5103 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5107 % S p r e a d I m a g e %
5111 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5113 % SpreadImage() is a special effects method that randomly displaces each
5114 % pixel in a block defined by the radius parameter.
5116 % The format of the SpreadImage method is:
5118 % Image *SpreadImage(const Image *image,const double radius,
5119 % ExceptionInfo *exception)
5121 % A description of each parameter follows:
5123 % o image: the image.
5125 % o radius: Choose a random pixel in a neighborhood of this extent.
5127 % o exception: return any errors or warnings in this structure.
5130 MagickExport Image *SpreadImage(const Image *image,const double radius,
5131 ExceptionInfo *exception)
5133 #define SpreadImageTag "Spread/Image"
5152 **restrict random_info;
5155 **restrict resample_filter;
5161 Initialize spread image attributes.
5163 assert(image != (Image *) NULL);
5164 assert(image->signature == MagickSignature);
5165 if (image->debug != MagickFalse)
5166 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5167 assert(exception != (ExceptionInfo *) NULL);
5168 assert(exception->signature == MagickSignature);
5169 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
5171 if (spread_image == (Image *) NULL)
5172 return((Image *) NULL);
5173 if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
5175 InheritException(exception,&spread_image->exception);
5176 spread_image=DestroyImage(spread_image);
5177 return((Image *) NULL);
5184 GetMagickPixelPacket(spread_image,&bias);
5185 width=GetOptimalKernelWidth1D(radius,0.5);
5186 resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
5187 random_info=AcquireRandomInfoThreadSet();
5188 image_view=AcquireCacheView(spread_image);
5189 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5190 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5192 for (y=0; y < (long) spread_image->rows; y++)
5197 register IndexPacket
5204 register PixelPacket
5207 if (status == MagickFalse)
5209 q=QueueCacheViewAuthenticPixels(image_view,0,y,spread_image->columns,1,
5211 if (q == (PixelPacket *) NULL)
5216 indexes=GetCacheViewAuthenticIndexQueue(image_view);
5218 id=GetOpenMPThreadId();
5219 for (x=0; x < (long) spread_image->columns; x++)
5221 (void) ResamplePixelColor(resample_filter[id],(double) x+width*
5222 (GetPseudoRandomValue(random_info[id])-0.5),(double) y+width*
5223 (GetPseudoRandomValue(random_info[id])-0.5),&pixel);
5224 SetPixelPacket(spread_image,&pixel,q,indexes+x);
5227 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5229 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5234 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5235 #pragma omp critical (MagickCore_SpreadImage)
5237 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
5238 if (proceed == MagickFalse)
5242 image_view=DestroyCacheView(image_view);
5243 random_info=DestroyRandomInfoThreadSet(random_info);
5244 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5245 return(spread_image);
5249 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5253 % U n s h a r p M a s k I m a g e %
5257 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5259 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
5260 % image with a Gaussian operator of the given radius and standard deviation
5261 % (sigma). For reasonable results, radius should be larger than sigma. Use a
5262 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
5264 % The format of the UnsharpMaskImage method is:
5266 % Image *UnsharpMaskImage(const Image *image,const double radius,
5267 % const double sigma,const double amount,const double threshold,
5268 % ExceptionInfo *exception)
5269 % Image *UnsharpMaskImageChannel(const Image *image,
5270 % const ChannelType channel,const double radius,const double sigma,
5271 % const double amount,const double threshold,ExceptionInfo *exception)
5273 % A description of each parameter follows:
5275 % o image: the image.
5277 % o channel: the channel type.
5279 % o radius: the radius of the Gaussian, in pixels, not counting the center
5282 % o sigma: the standard deviation of the Gaussian, in pixels.
5284 % o amount: the percentage of the difference between the original and the
5285 % blur image that is added back into the original.
5287 % o threshold: the threshold in pixels needed to apply the diffence amount.
5289 % o exception: return any errors or warnings in this structure.
5293 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
5294 const double sigma,const double amount,const double threshold,
5295 ExceptionInfo *exception)
5300 sharp_image=UnsharpMaskImageChannel(image,DefaultChannels,radius,sigma,amount,
5301 threshold,exception);
5302 return(sharp_image);
5305 MagickExport Image *UnsharpMaskImageChannel(const Image *image,
5306 const ChannelType channel,const double radius,const double sigma,
5307 const double amount,const double threshold,ExceptionInfo *exception)
5309 #define SharpenImageTag "Sharpen/Image"
5331 assert(image != (const Image *) NULL);
5332 assert(image->signature == MagickSignature);
5333 if (image->debug != MagickFalse)
5334 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5335 assert(exception != (ExceptionInfo *) NULL);
5336 unsharp_image=BlurImageChannel(image,channel,radius,sigma,exception);
5337 if (unsharp_image == (Image *) NULL)
5338 return((Image *) NULL);
5339 quantum_threshold=(MagickRealType) QuantumRange*threshold;
5345 GetMagickPixelPacket(image,&bias);
5346 image_view=AcquireCacheView(image);
5347 unsharp_view=AcquireCacheView(unsharp_image);
5348 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5349 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5351 for (y=0; y < (long) image->rows; y++)
5356 register const IndexPacket
5359 register const PixelPacket
5362 register IndexPacket
5363 *restrict unsharp_indexes;
5368 register PixelPacket
5371 if (status == MagickFalse)
5373 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5374 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
5376 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5381 indexes=GetCacheViewVirtualIndexQueue(image_view);
5382 unsharp_indexes=GetCacheViewAuthenticIndexQueue(unsharp_view);
5384 for (x=0; x < (long) image->columns; x++)
5386 if ((channel & RedChannel) != 0)
5388 pixel.red=p->red-(MagickRealType) q->red;
5389 if (fabs(2.0*pixel.red) < quantum_threshold)
5390 pixel.red=(MagickRealType) GetRedPixelComponent(p);
5392 pixel.red=(MagickRealType) p->red+(pixel.red*amount);
5393 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
5395 if ((channel & GreenChannel) != 0)
5397 pixel.green=p->green-(MagickRealType) q->green;
5398 if (fabs(2.0*pixel.green) < quantum_threshold)
5399 pixel.green=(MagickRealType) GetGreenPixelComponent(p);
5401 pixel.green=(MagickRealType) p->green+(pixel.green*amount);
5402 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
5404 if ((channel & BlueChannel) != 0)
5406 pixel.blue=p->blue-(MagickRealType) q->blue;
5407 if (fabs(2.0*pixel.blue) < quantum_threshold)
5408 pixel.blue=(MagickRealType) GetBluePixelComponent(p);
5410 pixel.blue=(MagickRealType) p->blue+(pixel.blue*amount);
5411 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
5413 if ((channel & OpacityChannel) != 0)
5415 pixel.opacity=p->opacity-(MagickRealType) q->opacity;
5416 if (fabs(2.0*pixel.opacity) < quantum_threshold)
5417 pixel.opacity=(MagickRealType) GetOpacityPixelComponent(p);
5419 pixel.opacity=p->opacity+(pixel.opacity*amount);
5420 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
5422 if (((channel & IndexChannel) != 0) &&
5423 (image->colorspace == CMYKColorspace))
5425 pixel.index=unsharp_indexes[x]-(MagickRealType) indexes[x];
5426 if (fabs(2.0*pixel.index) < quantum_threshold)
5427 pixel.index=(MagickRealType) unsharp_indexes[x];
5429 pixel.index=(MagickRealType) unsharp_indexes[x]+(pixel.index*
5431 unsharp_indexes[x]=ClampToQuantum(pixel.index);
5436 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
5438 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5443 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5444 #pragma omp critical (MagickCore_UnsharpMaskImageChannel)
5446 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
5447 if (proceed == MagickFalse)
5451 unsharp_image->type=image->type;
5452 unsharp_view=DestroyCacheView(unsharp_view);
5453 image_view=DestroyCacheView(image_view);
5454 if (status == MagickFalse)
5455 unsharp_image=DestroyImage(unsharp_image);
5456 return(unsharp_image);