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/accelerate.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/morphology.h"
68 #include "magick/paint.h"
69 #include "magick/pixel-private.h"
70 #include "magick/property.h"
71 #include "magick/quantize.h"
72 #include "magick/quantum.h"
73 #include "magick/random_.h"
74 #include "magick/random-private.h"
75 #include "magick/resample.h"
76 #include "magick/resample-private.h"
77 #include "magick/resize.h"
78 #include "magick/resource_.h"
79 #include "magick/segment.h"
80 #include "magick/shear.h"
81 #include "magick/signature-private.h"
82 #include "magick/string_.h"
83 #include "magick/thread-private.h"
84 #include "magick/transform.h"
85 #include "magick/threshold.h"
88 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92 % A d a p t i v e B l u r I m a g e %
96 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
98 % AdaptiveBlurImage() adaptively blurs the image by blurring less
99 % intensely near image edges and more intensely far from edges. We blur the
100 % image with a Gaussian operator of the given radius and standard deviation
101 % (sigma). For reasonable results, radius should be larger than sigma. Use a
102 % radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
104 % The format of the AdaptiveBlurImage method is:
106 % Image *AdaptiveBlurImage(const Image *image,const double radius,
107 % const double sigma,ExceptionInfo *exception)
108 % Image *AdaptiveBlurImageChannel(const Image *image,
109 % const ChannelType channel,double radius,const double sigma,
110 % ExceptionInfo *exception)
112 % A description of each parameter follows:
114 % o image: the image.
116 % o channel: the channel type.
118 % o radius: the radius of the Gaussian, in pixels, not counting the center
121 % o sigma: the standard deviation of the Laplacian, in pixels.
123 % o exception: return any errors or warnings in this structure.
127 MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
128 const double sigma,ExceptionInfo *exception)
133 blur_image=AdaptiveBlurImageChannel(image,DefaultChannels,radius,sigma,
138 MagickExport Image *AdaptiveBlurImageChannel(const Image *image,
139 const ChannelType channel,const double radius,const double sigma,
140 ExceptionInfo *exception)
142 #define AdaptiveBlurImageTag "Convolve/Image"
143 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
179 assert(image != (const Image *) NULL);
180 assert(image->signature == MagickSignature);
181 if (image->debug != MagickFalse)
182 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
183 assert(exception != (ExceptionInfo *) NULL);
184 assert(exception->signature == MagickSignature);
185 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
186 if (blur_image == (Image *) NULL)
187 return((Image *) NULL);
188 if (fabs(sigma) <= MagickEpsilon)
190 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
192 InheritException(exception,&blur_image->exception);
193 blur_image=DestroyImage(blur_image);
194 return((Image *) NULL);
197 Edge detect the image brighness channel, level, blur, and level again.
199 edge_image=EdgeImage(image,radius,exception);
200 if (edge_image == (Image *) NULL)
202 blur_image=DestroyImage(blur_image);
203 return((Image *) NULL);
205 (void) LevelImage(edge_image,"20%,95%");
206 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
207 if (gaussian_image != (Image *) NULL)
209 edge_image=DestroyImage(edge_image);
210 edge_image=gaussian_image;
212 (void) LevelImage(edge_image,"10%,95%");
214 Create a set of kernels from maximum (radius,sigma) to minimum.
216 width=GetOptimalKernelWidth2D(radius,sigma);
217 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
218 if (kernel == (double **) NULL)
220 edge_image=DestroyImage(edge_image);
221 blur_image=DestroyImage(blur_image);
222 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
224 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
225 for (i=0; i < (long) width; i+=2)
227 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
229 if (kernel[i] == (double *) NULL)
232 j=(long) (width-i)/2;
234 for (v=(-j); v <= j; v++)
236 for (u=(-j); u <= j; u++)
238 kernel[i][k]=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
239 (2.0*MagickPI*MagickSigma*MagickSigma);
240 normalize+=kernel[i][k];
244 if (fabs(normalize) <= MagickEpsilon)
246 normalize=1.0/normalize;
247 for (k=0; k < (j*j); k++)
248 kernel[i][k]=normalize*kernel[i][k];
250 if (i < (long) width)
252 for (i-=2; i >= 0; i-=2)
253 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
254 kernel=(double **) RelinquishMagickMemory(kernel);
255 edge_image=DestroyImage(edge_image);
256 blur_image=DestroyImage(blur_image);
257 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
260 Adaptively blur image.
264 GetMagickPixelPacket(image,&bias);
265 SetMagickPixelPacketBias(image,&bias);
266 image_view=AcquireCacheView(image);
267 edge_view=AcquireCacheView(edge_image);
268 blur_view=AcquireCacheView(blur_image);
269 #if defined(MAGICKCORE_OPENMP_SUPPORT)
270 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
272 for (y=0; y < (long) blur_image->rows; y++)
274 register const IndexPacket
277 register const PixelPacket
282 *restrict blur_indexes;
290 if (status == MagickFalse)
292 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
293 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
295 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
300 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
301 for (x=0; x < (long) blur_image->columns; x++)
310 register const double
319 i=(long) ceil(width*QuantumScale*PixelIntensity(r)-0.5);
323 if (i > (long) width)
327 p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
328 ((width-i)/2L),width-i,width-i,exception);
329 if (p == (const PixelPacket *) NULL)
331 indexes=GetCacheViewVirtualIndexQueue(image_view);
334 for (v=0; v < (long) (width-i); v++)
336 for (u=0; u < (long) (width-i); u++)
339 if (((channel & OpacityChannel) != 0) &&
340 (image->matte != MagickFalse))
341 alpha=(MagickRealType) (QuantumScale*GetAlphaPixelComponent(p));
342 if ((channel & RedChannel) != 0)
343 pixel.red+=(*k)*alpha*GetRedPixelComponent(p);
344 if ((channel & GreenChannel) != 0)
345 pixel.green+=(*k)*alpha*GetGreenPixelComponent(p);
346 if ((channel & BlueChannel) != 0)
347 pixel.blue+=(*k)*alpha*GetBluePixelComponent(p);
348 if ((channel & OpacityChannel) != 0)
349 pixel.opacity+=(*k)*GetOpacityPixelComponent(p);
350 if (((channel & IndexChannel) != 0) &&
351 (image->colorspace == CMYKColorspace))
352 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
358 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
359 if ((channel & RedChannel) != 0)
360 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
361 if ((channel & GreenChannel) != 0)
362 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
363 if ((channel & BlueChannel) != 0)
364 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
365 if ((channel & OpacityChannel) != 0)
366 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
367 if (((channel & IndexChannel) != 0) &&
368 (image->colorspace == CMYKColorspace))
369 blur_indexes[x]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
373 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
375 if (image->progress_monitor != (MagickProgressMonitor) NULL)
380 #if defined(MAGICKCORE_OPENMP_SUPPORT)
381 #pragma omp critical (MagickCore_AdaptiveBlurImageChannel)
383 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
385 if (proceed == MagickFalse)
389 blur_image->type=image->type;
390 blur_view=DestroyCacheView(blur_view);
391 edge_view=DestroyCacheView(edge_view);
392 image_view=DestroyCacheView(image_view);
393 edge_image=DestroyImage(edge_image);
394 for (i=0; i < (long) width; i+=2)
395 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
396 kernel=(double **) RelinquishMagickMemory(kernel);
397 if (status == MagickFalse)
398 blur_image=DestroyImage(blur_image);
403 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
407 % A d a p t i v e S h a r p e n I m a g e %
411 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
413 % AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
414 % intensely near image edges and less intensely far from edges. We sharpen the
415 % image with a Gaussian operator of the given radius and standard deviation
416 % (sigma). For reasonable results, radius should be larger than sigma. Use a
417 % radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
419 % The format of the AdaptiveSharpenImage method is:
421 % Image *AdaptiveSharpenImage(const Image *image,const double radius,
422 % const double sigma,ExceptionInfo *exception)
423 % Image *AdaptiveSharpenImageChannel(const Image *image,
424 % const ChannelType channel,double radius,const double sigma,
425 % ExceptionInfo *exception)
427 % A description of each parameter follows:
429 % o image: the image.
431 % o channel: the channel type.
433 % o radius: the radius of the Gaussian, in pixels, not counting the center
436 % o sigma: the standard deviation of the Laplacian, in pixels.
438 % o exception: return any errors or warnings in this structure.
442 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
443 const double sigma,ExceptionInfo *exception)
448 sharp_image=AdaptiveSharpenImageChannel(image,DefaultChannels,radius,sigma,
453 MagickExport Image *AdaptiveSharpenImageChannel(const Image *image,
454 const ChannelType channel,const double radius,const double sigma,
455 ExceptionInfo *exception)
457 #define AdaptiveSharpenImageTag "Convolve/Image"
458 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
494 assert(image != (const Image *) NULL);
495 assert(image->signature == MagickSignature);
496 if (image->debug != MagickFalse)
497 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
498 assert(exception != (ExceptionInfo *) NULL);
499 assert(exception->signature == MagickSignature);
500 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
501 if (sharp_image == (Image *) NULL)
502 return((Image *) NULL);
503 if (fabs(sigma) <= MagickEpsilon)
505 if (SetImageStorageClass(sharp_image,DirectClass) == MagickFalse)
507 InheritException(exception,&sharp_image->exception);
508 sharp_image=DestroyImage(sharp_image);
509 return((Image *) NULL);
512 Edge detect the image brighness channel, level, sharp, and level again.
514 edge_image=EdgeImage(image,radius,exception);
515 if (edge_image == (Image *) NULL)
517 sharp_image=DestroyImage(sharp_image);
518 return((Image *) NULL);
520 (void) LevelImage(edge_image,"20%,95%");
521 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
522 if (gaussian_image != (Image *) NULL)
524 edge_image=DestroyImage(edge_image);
525 edge_image=gaussian_image;
527 (void) LevelImage(edge_image,"10%,95%");
529 Create a set of kernels from maximum (radius,sigma) to minimum.
531 width=GetOptimalKernelWidth2D(radius,sigma);
532 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
533 if (kernel == (double **) NULL)
535 edge_image=DestroyImage(edge_image);
536 sharp_image=DestroyImage(sharp_image);
537 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
539 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
540 for (i=0; i < (long) width; i+=2)
542 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
544 if (kernel[i] == (double *) NULL)
547 j=(long) (width-i)/2;
549 for (v=(-j); v <= j; v++)
551 for (u=(-j); u <= j; u++)
553 kernel[i][k]=(-exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
554 (2.0*MagickPI*MagickSigma*MagickSigma));
555 normalize+=kernel[i][k];
559 if (fabs(normalize) <= MagickEpsilon)
561 normalize=1.0/normalize;
562 for (k=0; k < (j*j); k++)
563 kernel[i][k]=normalize*kernel[i][k];
565 if (i < (long) width)
567 for (i-=2; i >= 0; i-=2)
568 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
569 kernel=(double **) RelinquishMagickMemory(kernel);
570 edge_image=DestroyImage(edge_image);
571 sharp_image=DestroyImage(sharp_image);
572 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
575 Adaptively sharpen image.
579 GetMagickPixelPacket(image,&bias);
580 SetMagickPixelPacketBias(image,&bias);
581 image_view=AcquireCacheView(image);
582 edge_view=AcquireCacheView(edge_image);
583 sharp_view=AcquireCacheView(sharp_image);
584 #if defined(MAGICKCORE_OPENMP_SUPPORT)
585 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
587 for (y=0; y < (long) sharp_image->rows; y++)
589 register const IndexPacket
592 register const PixelPacket
597 *restrict sharp_indexes;
605 if (status == MagickFalse)
607 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
608 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
610 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
615 sharp_indexes=GetCacheViewAuthenticIndexQueue(sharp_view);
616 for (x=0; x < (long) sharp_image->columns; x++)
625 register const double
634 i=(long) ceil(width*(QuantumRange-QuantumScale*PixelIntensity(r))-0.5);
638 if (i > (long) width)
642 p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
643 ((width-i)/2L),width-i,width-i,exception);
644 if (p == (const PixelPacket *) NULL)
646 indexes=GetCacheViewVirtualIndexQueue(image_view);
649 for (v=0; v < (long) (width-i); v++)
651 for (u=0; u < (long) (width-i); u++)
654 if (((channel & OpacityChannel) != 0) &&
655 (image->matte != MagickFalse))
656 alpha=(MagickRealType) (QuantumScale*GetAlphaPixelComponent(p));
657 if ((channel & RedChannel) != 0)
658 pixel.red+=(*k)*alpha*GetRedPixelComponent(p);
659 if ((channel & GreenChannel) != 0)
660 pixel.green+=(*k)*alpha*GetGreenPixelComponent(p);
661 if ((channel & BlueChannel) != 0)
662 pixel.blue+=(*k)*alpha*GetBluePixelComponent(p);
663 if ((channel & OpacityChannel) != 0)
664 pixel.opacity+=(*k)*GetOpacityPixelComponent(p);
665 if (((channel & IndexChannel) != 0) &&
666 (image->colorspace == CMYKColorspace))
667 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
673 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
674 if ((channel & RedChannel) != 0)
675 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
676 if ((channel & GreenChannel) != 0)
677 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
678 if ((channel & BlueChannel) != 0)
679 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
680 if ((channel & OpacityChannel) != 0)
681 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
682 if (((channel & IndexChannel) != 0) &&
683 (image->colorspace == CMYKColorspace))
684 sharp_indexes[x]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
688 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
690 if (image->progress_monitor != (MagickProgressMonitor) NULL)
695 #if defined(MAGICKCORE_OPENMP_SUPPORT)
696 #pragma omp critical (MagickCore_AdaptiveSharpenImageChannel)
698 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
700 if (proceed == MagickFalse)
704 sharp_image->type=image->type;
705 sharp_view=DestroyCacheView(sharp_view);
706 edge_view=DestroyCacheView(edge_view);
707 image_view=DestroyCacheView(image_view);
708 edge_image=DestroyImage(edge_image);
709 for (i=0; i < (long) width; i+=2)
710 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
711 kernel=(double **) RelinquishMagickMemory(kernel);
712 if (status == MagickFalse)
713 sharp_image=DestroyImage(sharp_image);
718 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
722 % B l u r I m a g e %
726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
728 % BlurImage() blurs an image. We convolve the image with a Gaussian operator
729 % of the given radius and standard deviation (sigma). For reasonable results,
730 % the radius should be larger than sigma. Use a radius of 0 and BlurImage()
731 % selects a suitable radius for you.
733 % BlurImage() differs from GaussianBlurImage() in that it uses a separable
734 % kernel which is faster but mathematically equivalent to the non-separable
737 % The format of the BlurImage method is:
739 % Image *BlurImage(const Image *image,const double radius,
740 % const double sigma,ExceptionInfo *exception)
741 % Image *BlurImageChannel(const Image *image,const ChannelType channel,
742 % const double radius,const double sigma,ExceptionInfo *exception)
744 % A description of each parameter follows:
746 % o image: the image.
748 % o channel: the channel type.
750 % o radius: the radius of the Gaussian, in pixels, not counting the center
753 % o sigma: the standard deviation of the Gaussian, in pixels.
755 % o exception: return any errors or warnings in this structure.
759 MagickExport Image *BlurImage(const Image *image,const double radius,
760 const double sigma,ExceptionInfo *exception)
765 blur_image=BlurImageChannel(image,DefaultChannels,radius,sigma,exception);
769 static double *GetBlurKernel(const unsigned long width,const double sigma)
783 Generate a 1-D convolution kernel.
785 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
786 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
787 if (kernel == (double *) NULL)
792 for (k=(-j); k <= j; k++)
794 kernel[i]=exp(-((double) k*k)/(2.0*MagickSigma*MagickSigma))/
795 (MagickSQ2PI*MagickSigma);
796 normalize+=kernel[i];
799 for (i=0; i < (long) width; i++)
800 kernel[i]/=normalize;
804 MagickExport Image *BlurImageChannel(const Image *image,
805 const ChannelType channel,const double radius,const double sigma,
806 ExceptionInfo *exception)
808 #define BlurImageTag "Blur/Image"
838 Initialize blur image attributes.
840 assert(image != (Image *) NULL);
841 assert(image->signature == MagickSignature);
842 if (image->debug != MagickFalse)
843 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
844 assert(exception != (ExceptionInfo *) NULL);
845 assert(exception->signature == MagickSignature);
846 blur_image=CloneImage(image,0,0,MagickTrue,exception);
847 if (blur_image == (Image *) NULL)
848 return((Image *) NULL);
849 if (fabs(sigma) <= MagickEpsilon)
851 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
853 InheritException(exception,&blur_image->exception);
854 blur_image=DestroyImage(blur_image);
855 return((Image *) NULL);
857 width=GetOptimalKernelWidth1D(radius,sigma);
858 kernel=GetBlurKernel(width,sigma);
859 if (kernel == (double *) NULL)
861 blur_image=DestroyImage(blur_image);
862 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
864 if (image->debug != MagickFalse)
867 format[MaxTextExtent],
870 register const double
873 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
874 " BlurImage with %ld kernel:",width);
875 message=AcquireString("");
877 for (i=0; i < (long) width; i++)
880 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",i);
881 (void) ConcatenateString(&message,format);
882 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
883 (void) ConcatenateString(&message,format);
884 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
886 message=DestroyString(message);
893 GetMagickPixelPacket(image,&bias);
894 SetMagickPixelPacketBias(image,&bias);
895 image_view=AcquireCacheView(image);
896 blur_view=AcquireCacheView(blur_image);
897 #if defined(MAGICKCORE_OPENMP_SUPPORT)
898 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
900 for (y=0; y < (long) blur_image->rows; y++)
902 register const IndexPacket
905 register const PixelPacket
909 *restrict blur_indexes;
917 if (status == MagickFalse)
919 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y,image->columns+
921 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
923 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
928 indexes=GetCacheViewVirtualIndexQueue(image_view);
929 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
930 for (x=0; x < (long) blur_image->columns; x++)
935 register const double
938 register const PixelPacket
939 *restrict kernel_pixels;
947 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
949 for (i=0; i < (long) width; i++)
951 pixel.red+=(*k)*kernel_pixels->red;
952 pixel.green+=(*k)*kernel_pixels->green;
953 pixel.blue+=(*k)*kernel_pixels->blue;
957 if ((channel & RedChannel) != 0)
958 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
959 if ((channel & GreenChannel) != 0)
960 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
961 if ((channel & BlueChannel) != 0)
962 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
963 if ((channel & OpacityChannel) != 0)
967 for (i=0; i < (long) width; i++)
969 pixel.opacity+=(*k)*kernel_pixels->opacity;
973 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
975 if (((channel & IndexChannel) != 0) &&
976 (image->colorspace == CMYKColorspace))
978 register const IndexPacket
979 *restrict kernel_indexes;
982 kernel_indexes=indexes;
983 for (i=0; i < (long) width; i++)
985 pixel.index+=(*k)*(*kernel_indexes);
989 blur_indexes[x]=ClampToQuantum(pixel.index);
999 for (i=0; i < (long) width; i++)
1001 alpha=(MagickRealType) (QuantumScale*
1002 GetAlphaPixelComponent(kernel_pixels));
1003 pixel.red+=(*k)*alpha*kernel_pixels->red;
1004 pixel.green+=(*k)*alpha*kernel_pixels->green;
1005 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
1010 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1011 if ((channel & RedChannel) != 0)
1012 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
1013 if ((channel & GreenChannel) != 0)
1014 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
1015 if ((channel & BlueChannel) != 0)
1016 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
1017 if ((channel & OpacityChannel) != 0)
1021 for (i=0; i < (long) width; i++)
1023 pixel.opacity+=(*k)*kernel_pixels->opacity;
1027 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1029 if (((channel & IndexChannel) != 0) &&
1030 (image->colorspace == CMYKColorspace))
1032 register const IndexPacket
1033 *restrict kernel_indexes;
1037 kernel_indexes=indexes;
1038 for (i=0; i < (long) width; i++)
1040 alpha=(MagickRealType) (QuantumScale*
1041 GetAlphaPixelComponent(kernel_pixels));
1042 pixel.index+=(*k)*alpha*(*kernel_indexes);
1047 blur_indexes[x]=ClampToQuantum(gamma*
1048 GetIndexPixelComponent(&pixel));
1054 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1056 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1061 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1062 #pragma omp critical (MagickCore_BlurImageChannel)
1064 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1065 blur_image->columns);
1066 if (proceed == MagickFalse)
1070 blur_view=DestroyCacheView(blur_view);
1071 image_view=DestroyCacheView(image_view);
1075 image_view=AcquireCacheView(blur_image);
1076 blur_view=AcquireCacheView(blur_image);
1077 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1078 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1080 for (x=0; x < (long) blur_image->columns; x++)
1082 register const IndexPacket
1085 register const PixelPacket
1088 register IndexPacket
1089 *restrict blur_indexes;
1094 register PixelPacket
1097 if (status == MagickFalse)
1099 p=GetCacheViewVirtualPixels(image_view,x,-((long) width/2L),1,image->rows+
1101 q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
1102 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1107 indexes=GetCacheViewVirtualIndexQueue(image_view);
1108 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
1109 for (y=0; y < (long) blur_image->rows; y++)
1114 register const double
1117 register const PixelPacket
1118 *restrict kernel_pixels;
1126 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
1128 for (i=0; i < (long) width; i++)
1130 pixel.red+=(*k)*kernel_pixels->red;
1131 pixel.green+=(*k)*kernel_pixels->green;
1132 pixel.blue+=(*k)*kernel_pixels->blue;
1136 if ((channel & RedChannel) != 0)
1137 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
1138 if ((channel & GreenChannel) != 0)
1139 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
1140 if ((channel & BlueChannel) != 0)
1141 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
1142 if ((channel & OpacityChannel) != 0)
1146 for (i=0; i < (long) width; i++)
1148 pixel.opacity+=(*k)*kernel_pixels->opacity;
1152 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1154 if (((channel & IndexChannel) != 0) &&
1155 (image->colorspace == CMYKColorspace))
1157 register const IndexPacket
1158 *restrict kernel_indexes;
1161 kernel_indexes=indexes;
1162 for (i=0; i < (long) width; i++)
1164 pixel.index+=(*k)*(*kernel_indexes);
1168 blur_indexes[y]=ClampToQuantum(pixel.index);
1178 for (i=0; i < (long) width; i++)
1180 alpha=(MagickRealType) (QuantumScale*
1181 GetAlphaPixelComponent(kernel_pixels));
1182 pixel.red+=(*k)*alpha*kernel_pixels->red;
1183 pixel.green+=(*k)*alpha*kernel_pixels->green;
1184 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
1189 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1190 if ((channel & RedChannel) != 0)
1191 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
1192 if ((channel & GreenChannel) != 0)
1193 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
1194 if ((channel & BlueChannel) != 0)
1195 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
1196 if ((channel & OpacityChannel) != 0)
1200 for (i=0; i < (long) width; i++)
1202 pixel.opacity+=(*k)*kernel_pixels->opacity;
1206 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1208 if (((channel & IndexChannel) != 0) &&
1209 (image->colorspace == CMYKColorspace))
1211 register const IndexPacket
1212 *restrict kernel_indexes;
1216 kernel_indexes=indexes;
1217 for (i=0; i < (long) width; i++)
1219 alpha=(MagickRealType) (QuantumScale*
1220 GetAlphaPixelComponent(kernel_pixels));
1221 pixel.index+=(*k)*alpha*(*kernel_indexes);
1226 blur_indexes[y]=ClampToQuantum(gamma*
1227 GetIndexPixelComponent(&pixel));
1233 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1235 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1240 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1241 #pragma omp critical (MagickCore_BlurImageChannel)
1243 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1244 blur_image->columns);
1245 if (proceed == MagickFalse)
1249 blur_view=DestroyCacheView(blur_view);
1250 image_view=DestroyCacheView(image_view);
1251 kernel=(double *) RelinquishMagickMemory(kernel);
1252 if (status == MagickFalse)
1253 blur_image=DestroyImage(blur_image);
1254 blur_image->type=image->type;
1259 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1263 % C o n v o l v e I m a g e %
1267 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1269 % ConvolveImage() applies a custom convolution kernel to the image.
1271 % The format of the ConvolveImage method is:
1273 % Image *ConvolveImage(const Image *image,const unsigned long order,
1274 % const double *kernel,ExceptionInfo *exception)
1275 % Image *ConvolveImageChannel(const Image *image,const ChannelType channel,
1276 % const unsigned long order,const double *kernel,
1277 % ExceptionInfo *exception)
1279 % A description of each parameter follows:
1281 % o image: the image.
1283 % o channel: the channel type.
1285 % o order: the number of columns and rows in the filter kernel.
1287 % o kernel: An array of double representing the convolution kernel.
1289 % o exception: return any errors or warnings in this structure.
1293 MagickExport Image *ConvolveImage(const Image *image,const unsigned long order,
1294 const double *kernel,ExceptionInfo *exception)
1299 convolve_image=ConvolveImageChannel(image,DefaultChannels,order,kernel,
1301 return(convolve_image);
1304 MagickExport Image *ConvolveImageChannel(const Image *image,
1305 const ChannelType channel,const unsigned long order,const double *kernel,
1306 ExceptionInfo *exception)
1308 #define ConvolveImageTag "Convolve/Image"
1340 Initialize convolve image attributes.
1342 assert(image != (Image *) NULL);
1343 assert(image->signature == MagickSignature);
1344 if (image->debug != MagickFalse)
1345 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1346 assert(exception != (ExceptionInfo *) NULL);
1347 assert(exception->signature == MagickSignature);
1349 if ((width % 2) == 0)
1350 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
1351 convolve_image=CloneImage(image,0,0,MagickTrue,exception);
1352 if (convolve_image == (Image *) NULL)
1353 return((Image *) NULL);
1354 if (SetImageStorageClass(convolve_image,DirectClass) == MagickFalse)
1356 InheritException(exception,&convolve_image->exception);
1357 convolve_image=DestroyImage(convolve_image);
1358 return((Image *) NULL);
1360 if (image->debug != MagickFalse)
1363 format[MaxTextExtent],
1370 register const double
1373 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1374 " ConvolveImage with %ldx%ld kernel:",width,width);
1375 message=AcquireString("");
1377 for (v=0; v < (long) width; v++)
1380 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
1381 (void) ConcatenateString(&message,format);
1382 for (u=0; u < (long) width; u++)
1384 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
1385 (void) ConcatenateString(&message,format);
1387 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
1389 message=DestroyString(message);
1394 normal_kernel=(double *) AcquireQuantumMemory(width*width,
1395 sizeof(*normal_kernel));
1396 if (normal_kernel == (double *) NULL)
1398 convolve_image=DestroyImage(convolve_image);
1399 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1402 for (i=0; i < (long) (width*width); i++)
1404 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1405 for (i=0; i < (long) (width*width); i++)
1406 normal_kernel[i]=gamma*kernel[i];
1412 GetMagickPixelPacket(image,&bias);
1413 SetMagickPixelPacketBias(image,&bias);
1414 image_view=AcquireCacheView(image);
1415 convolve_view=AcquireCacheView(convolve_image);
1416 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1417 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1419 for (y=0; y < (long) image->rows; y++)
1424 register const IndexPacket
1427 register const PixelPacket
1430 register IndexPacket
1431 *restrict convolve_indexes;
1436 register PixelPacket
1439 if (status == MagickFalse)
1441 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
1442 2L),image->columns+width,width,exception);
1443 q=GetCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
1445 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1450 indexes=GetCacheViewVirtualIndexQueue(image_view);
1451 convolve_indexes=GetCacheViewAuthenticIndexQueue(convolve_view);
1452 for (x=0; x < (long) image->columns; x++)
1460 register const double
1463 register const PixelPacket
1464 *restrict kernel_pixels;
1472 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
1474 for (v=0; v < (long) width; v++)
1476 for (u=0; u < (long) width; u++)
1478 pixel.red+=(*k)*kernel_pixels[u].red;
1479 pixel.green+=(*k)*kernel_pixels[u].green;
1480 pixel.blue+=(*k)*kernel_pixels[u].blue;
1483 kernel_pixels+=image->columns+width;
1485 if ((channel & RedChannel) != 0)
1486 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
1487 if ((channel & GreenChannel) != 0)
1488 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
1489 if ((channel & BlueChannel) != 0)
1490 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
1491 if ((channel & OpacityChannel) != 0)
1495 for (v=0; v < (long) width; v++)
1497 for (u=0; u < (long) width; u++)
1499 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1502 kernel_pixels+=image->columns+width;
1504 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1506 if (((channel & IndexChannel) != 0) &&
1507 (image->colorspace == CMYKColorspace))
1509 register const IndexPacket
1510 *restrict kernel_indexes;
1513 kernel_indexes=indexes;
1514 for (v=0; v < (long) width; v++)
1516 for (u=0; u < (long) width; u++)
1518 pixel.index+=(*k)*kernel_indexes[u];
1521 kernel_indexes+=image->columns+width;
1523 convolve_indexes[x]=ClampToQuantum(pixel.index);
1533 for (v=0; v < (long) width; v++)
1535 for (u=0; u < (long) width; u++)
1537 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1538 kernel_pixels[u].opacity));
1539 pixel.red+=(*k)*alpha*kernel_pixels[u].red;
1540 pixel.green+=(*k)*alpha*kernel_pixels[u].green;
1541 pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
1545 kernel_pixels+=image->columns+width;
1547 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1548 if ((channel & RedChannel) != 0)
1549 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
1550 if ((channel & GreenChannel) != 0)
1551 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
1552 if ((channel & BlueChannel) != 0)
1553 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
1554 if ((channel & OpacityChannel) != 0)
1558 for (v=0; v < (long) width; v++)
1560 for (u=0; u < (long) width; u++)
1562 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1565 kernel_pixels+=image->columns+width;
1567 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1569 if (((channel & IndexChannel) != 0) &&
1570 (image->colorspace == CMYKColorspace))
1572 register const IndexPacket
1573 *restrict kernel_indexes;
1577 kernel_indexes=indexes;
1578 for (v=0; v < (long) width; v++)
1580 for (u=0; u < (long) width; u++)
1582 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1583 kernel_pixels[u].opacity));
1584 pixel.index+=(*k)*alpha*kernel_indexes[u];
1587 kernel_pixels+=image->columns+width;
1588 kernel_indexes+=image->columns+width;
1590 convolve_indexes[x]=ClampToQuantum(gamma*
1591 GetIndexPixelComponent(&pixel));
1597 sync=SyncCacheViewAuthenticPixels(convolve_view,exception);
1598 if (sync == MagickFalse)
1600 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1605 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1606 #pragma omp critical (MagickCore_ConvolveImageChannel)
1608 proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1609 if (proceed == MagickFalse)
1613 convolve_image->type=image->type;
1614 convolve_view=DestroyCacheView(convolve_view);
1615 image_view=DestroyCacheView(image_view);
1616 normal_kernel=(double *) RelinquishMagickMemory(normal_kernel);
1617 if (status == MagickFalse)
1618 convolve_image=DestroyImage(convolve_image);
1619 return(convolve_image);
1623 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1627 % D e s p e c k l e I m a g e %
1631 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1633 % DespeckleImage() reduces the speckle noise in an image while perserving the
1634 % edges of the original image.
1636 % The format of the DespeckleImage method is:
1638 % Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1640 % A description of each parameter follows:
1642 % o image: the image.
1644 % o exception: return any errors or warnings in this structure.
1648 static Quantum **DestroyPixelThreadSet(Quantum **pixels)
1653 assert(pixels != (Quantum **) NULL);
1654 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
1655 if (pixels[i] != (Quantum *) NULL)
1656 pixels[i]=(Quantum *) RelinquishMagickMemory(pixels[i]);
1657 pixels=(Quantum **) RelinquishAlignedMemory(pixels);
1661 static Quantum **AcquirePixelThreadSet(const size_t count)
1672 number_threads=GetOpenMPMaximumThreads();
1673 pixels=(Quantum **) AcquireAlignedMemory(number_threads,sizeof(*pixels));
1674 if (pixels == (Quantum **) NULL)
1675 return((Quantum **) NULL);
1676 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
1677 for (i=0; i < (long) number_threads; i++)
1679 pixels[i]=(Quantum *) AcquireQuantumMemory(count,sizeof(**pixels));
1680 if (pixels[i] == (Quantum *) NULL)
1681 return(DestroyPixelThreadSet(pixels));
1686 static void Hull(const long x_offset,const long y_offset,
1687 const unsigned long columns,const unsigned long rows,Quantum *f,Quantum *g,
1705 assert(f != (Quantum *) NULL);
1706 assert(g != (Quantum *) NULL);
1709 r=p+(y_offset*((long) columns+2)+x_offset);
1710 for (y=0; y < (long) rows; y++)
1716 for (x=(long) columns; x != 0; x--)
1718 v=(MagickRealType) (*p);
1719 if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1720 v+=ScaleCharToQuantum(1);
1727 for (x=(long) columns; x != 0; x--)
1729 v=(MagickRealType) (*p);
1730 if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
1731 v-=(long) ScaleCharToQuantum(1);
1743 r=q+(y_offset*((long) columns+2)+x_offset);
1744 s=q-(y_offset*((long) columns+2)+x_offset);
1745 for (y=0; y < (long) rows; y++)
1752 for (x=(long) columns; x != 0; x--)
1754 v=(MagickRealType) (*q);
1755 if (((MagickRealType) *s >=
1756 (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1757 ((MagickRealType) *r > v))
1758 v+=ScaleCharToQuantum(1);
1766 for (x=(long) columns; x != 0; x--)
1768 v=(MagickRealType) (*q);
1769 if (((MagickRealType) *s <=
1770 (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1771 ((MagickRealType) *r < v))
1772 v-=(MagickRealType) ScaleCharToQuantum(1);
1786 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1788 #define DespeckleImageTag "Despeckle/Image"
1811 X[4] = {0, 1, 1,-1},
1812 Y[4] = {1, 0, 1, 1};
1815 Allocate despeckled image.
1817 assert(image != (const Image *) NULL);
1818 assert(image->signature == MagickSignature);
1819 if (image->debug != MagickFalse)
1820 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1821 assert(exception != (ExceptionInfo *) NULL);
1822 assert(exception->signature == MagickSignature);
1823 despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1825 if (despeckle_image == (Image *) NULL)
1826 return((Image *) NULL);
1827 if (SetImageStorageClass(despeckle_image,DirectClass) == MagickFalse)
1829 InheritException(exception,&despeckle_image->exception);
1830 despeckle_image=DestroyImage(despeckle_image);
1831 return((Image *) NULL);
1834 Allocate image buffers.
1836 length=(size_t) ((image->columns+2)*(image->rows+2));
1837 pixels=AcquirePixelThreadSet(length);
1838 buffers=AcquirePixelThreadSet(length);
1839 if ((pixels == (Quantum **) NULL) || (buffers == (Quantum **) NULL))
1841 if (buffers != (Quantum **) NULL)
1842 buffers=DestroyPixelThreadSet(buffers);
1843 if (pixels != (Quantum **) NULL)
1844 pixels=DestroyPixelThreadSet(pixels);
1845 despeckle_image=DestroyImage(despeckle_image);
1846 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1849 Reduce speckle in the image.
1852 image_view=AcquireCacheView(image);
1853 despeckle_view=AcquireCacheView(despeckle_image);
1854 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1855 #pragma omp parallel for schedule(dynamic,4) shared(status)
1857 for (channel=0; channel <= 3; channel++)
1872 if (status == MagickFalse)
1874 id=GetOpenMPThreadId();
1876 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
1878 j=(long) image->columns+2;
1879 for (y=0; y < (long) image->rows; y++)
1881 register const PixelPacket
1884 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1885 if (p == (const PixelPacket *) NULL)
1888 for (x=0; x < (long) image->columns; x++)
1892 case 0: pixel[j]=GetRedPixelComponent(p); break;
1893 case 1: pixel[j]=GetGreenPixelComponent(p); break;
1894 case 2: pixel[j]=GetBluePixelComponent(p); break;
1895 case 3: pixel[j]=GetOpacityPixelComponent(p); break;
1903 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1904 for (i=0; i < 4; i++)
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);
1909 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,-1);
1911 j=(long) image->columns+2;
1912 for (y=0; y < (long) image->rows; y++)
1917 register PixelPacket
1920 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1922 if (q == (PixelPacket *) NULL)
1925 for (x=0; x < (long) image->columns; x++)
1929 case 0: q->red=pixel[j]; break;
1930 case 1: q->green=pixel[j]; break;
1931 case 2: q->blue=pixel[j]; break;
1932 case 3: q->opacity=pixel[j]; break;
1938 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1939 if (sync == MagickFalse)
1946 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1951 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1952 #pragma omp critical (MagickCore_DespeckleImage)
1954 proceed=SetImageProgress(image,DespeckleImageTag,channel,3);
1955 if (proceed == MagickFalse)
1959 despeckle_view=DestroyCacheView(despeckle_view);
1960 image_view=DestroyCacheView(image_view);
1961 buffers=DestroyPixelThreadSet(buffers);
1962 pixels=DestroyPixelThreadSet(pixels);
1963 despeckle_image->type=image->type;
1964 if (status == MagickFalse)
1965 despeckle_image=DestroyImage(despeckle_image);
1966 return(despeckle_image);
1970 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1974 % E d g e I m a g e %
1978 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1980 % EdgeImage() finds edges in an image. Radius defines the radius of the
1981 % convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1984 % The format of the EdgeImage method is:
1986 % Image *EdgeImage(const Image *image,const double radius,
1987 % ExceptionInfo *exception)
1989 % A description of each parameter follows:
1991 % o image: the image.
1993 % o radius: the radius of the pixel neighborhood.
1995 % o exception: return any errors or warnings in this structure.
1998 MagickExport Image *EdgeImage(const Image *image,const double radius,
1999 ExceptionInfo *exception)
2013 assert(image != (const Image *) NULL);
2014 assert(image->signature == MagickSignature);
2015 if (image->debug != MagickFalse)
2016 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2017 assert(exception != (ExceptionInfo *) NULL);
2018 assert(exception->signature == MagickSignature);
2019 width=GetOptimalKernelWidth1D(radius,0.5);
2020 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2021 if (kernel == (double *) NULL)
2022 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2023 for (i=0; i < (long) (width*width); i++)
2025 kernel[i/2]=(double) (width*width-1.0);
2026 edge_image=ConvolveImage(image,width,kernel,exception);
2027 kernel=(double *) RelinquishMagickMemory(kernel);
2032 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2036 % E m b o s s I m a g e %
2040 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2042 % EmbossImage() returns a grayscale image with a three-dimensional effect.
2043 % We convolve the image with a Gaussian operator of the given radius and
2044 % standard deviation (sigma). For reasonable results, radius should be
2045 % larger than sigma. Use a radius of 0 and Emboss() selects a suitable
2048 % The format of the EmbossImage method is:
2050 % Image *EmbossImage(const Image *image,const double radius,
2051 % const double sigma,ExceptionInfo *exception)
2053 % A description of each parameter follows:
2055 % o image: the image.
2057 % o radius: the radius of the pixel neighborhood.
2059 % o sigma: the standard deviation of the Gaussian, in pixels.
2061 % o exception: return any errors or warnings in this structure.
2064 MagickExport Image *EmbossImage(const Image *image,const double radius,
2065 const double sigma,ExceptionInfo *exception)
2085 assert(image != (Image *) NULL);
2086 assert(image->signature == MagickSignature);
2087 if (image->debug != MagickFalse)
2088 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2089 assert(exception != (ExceptionInfo *) NULL);
2090 assert(exception->signature == MagickSignature);
2091 width=GetOptimalKernelWidth2D(radius,sigma);
2092 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2093 if (kernel == (double *) NULL)
2094 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2098 for (v=(-j); v <= j; v++)
2100 for (u=(-j); u <= j; u++)
2102 kernel[i]=((u < 0) || (v < 0) ? -8.0 : 8.0)*
2103 exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
2104 (2.0*MagickPI*MagickSigma*MagickSigma);
2111 emboss_image=ConvolveImage(image,width,kernel,exception);
2112 if (emboss_image != (Image *) NULL)
2113 (void) EqualizeImage(emboss_image);
2114 kernel=(double *) RelinquishMagickMemory(kernel);
2115 return(emboss_image);
2119 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2123 % F i l t e r I m a g e %
2127 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2129 % FilterImage() applies a custom convolution kernel to the image.
2131 % The format of the FilterImage method is:
2133 % Image *FilterImage(const Image *image,const KernelInfo *kernel,
2134 % ExceptionInfo *exception)
2135 % Image *FilterImageChannel(const Image *image,const ChannelType channel,
2136 % const KernelInfo *kernel,ExceptionInfo *exception)
2138 % A description of each parameter follows:
2140 % o image: the image.
2142 % o channel: the channel type.
2144 % o kernel: the filtering kernel.
2146 % o exception: return any errors or warnings in this structure.
2150 MagickExport Image *FilterImage(const Image *image,const KernelInfo *kernel,
2151 ExceptionInfo *exception)
2156 filter_image=FilterImageChannel(image,DefaultChannels,kernel,exception);
2157 return(filter_image);
2160 MagickExport Image *FilterImageChannel(const Image *image,
2161 const ChannelType channel,const KernelInfo *kernel,ExceptionInfo *exception)
2163 #define FilterImageTag "Filter/Image"
2183 Initialize filter image attributes.
2185 assert(image != (Image *) NULL);
2186 assert(image->signature == MagickSignature);
2187 if (image->debug != MagickFalse)
2188 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2189 assert(exception != (ExceptionInfo *) NULL);
2190 assert(exception->signature == MagickSignature);
2191 if ((kernel->width % 2) == 0)
2192 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
2193 filter_image=CloneImage(image,0,0,MagickTrue,exception);
2194 if (filter_image == (Image *) NULL)
2195 return((Image *) NULL);
2196 if (SetImageStorageClass(filter_image,DirectClass) == MagickFalse)
2198 InheritException(exception,&filter_image->exception);
2199 filter_image=DestroyImage(filter_image);
2200 return((Image *) NULL);
2202 if (image->debug != MagickFalse)
2205 format[MaxTextExtent],
2212 register const double
2215 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2216 " FilterImage with %ldx%ld kernel:",kernel->width,kernel->height);
2217 message=AcquireString("");
2219 for (v=0; v < (long) kernel->height; v++)
2222 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
2223 (void) ConcatenateString(&message,format);
2224 for (u=0; u < (long) kernel->width; u++)
2226 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
2227 (void) ConcatenateString(&message,format);
2229 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
2231 message=DestroyString(message);
2233 status=AccelerateConvolveImage(image,kernel,filter_image,exception);
2234 if (status == MagickTrue)
2235 return(filter_image);
2241 GetMagickPixelPacket(image,&bias);
2242 SetMagickPixelPacketBias(image,&bias);
2243 image_view=AcquireCacheView(image);
2244 filter_view=AcquireCacheView(filter_image);
2245 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2246 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2248 for (y=0; y < (long) image->rows; y++)
2253 register const IndexPacket
2256 register const PixelPacket
2259 register IndexPacket
2260 *restrict filter_indexes;
2265 register PixelPacket
2268 if (status == MagickFalse)
2270 p=GetCacheViewVirtualPixels(image_view,-((long) kernel->width/2L),
2271 y-(long) (kernel->height/2L),image->columns+kernel->width,kernel->height,
2273 q=GetCacheViewAuthenticPixels(filter_view,0,y,filter_image->columns,1,
2275 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2280 indexes=GetCacheViewVirtualIndexQueue(image_view);
2281 filter_indexes=GetCacheViewAuthenticIndexQueue(filter_view);
2282 for (x=0; x < (long) image->columns; x++)
2290 register const double
2293 register const PixelPacket
2294 *restrict kernel_pixels;
2302 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
2304 for (v=0; v < (long) kernel->width; v++)
2306 for (u=0; u < (long) kernel->height; u++)
2308 pixel.red+=(*k)*kernel_pixels[u].red;
2309 pixel.green+=(*k)*kernel_pixels[u].green;
2310 pixel.blue+=(*k)*kernel_pixels[u].blue;
2313 kernel_pixels+=image->columns+kernel->width;
2315 if ((channel & RedChannel) != 0)
2316 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2317 if ((channel & GreenChannel) != 0)
2318 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2319 if ((channel & BlueChannel) != 0)
2320 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2321 if ((channel & OpacityChannel) != 0)
2325 for (v=0; v < (long) kernel->width; v++)
2327 for (u=0; u < (long) kernel->height; u++)
2329 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
2332 kernel_pixels+=image->columns+kernel->width;
2334 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2336 if (((channel & IndexChannel) != 0) &&
2337 (image->colorspace == CMYKColorspace))
2339 register const IndexPacket
2340 *restrict kernel_indexes;
2343 kernel_indexes=indexes;
2344 for (v=0; v < (long) kernel->width; v++)
2346 for (u=0; u < (long) kernel->height; u++)
2348 pixel.index+=(*k)*kernel_indexes[u];
2351 kernel_indexes+=image->columns+kernel->width;
2353 filter_indexes[x]=ClampToQuantum(pixel.index);
2363 for (v=0; v < (long) kernel->width; v++)
2365 for (u=0; u < (long) kernel->height; u++)
2367 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
2368 kernel_pixels[u].opacity));
2369 pixel.red+=(*k)*alpha*kernel_pixels[u].red;
2370 pixel.green+=(*k)*alpha*kernel_pixels[u].green;
2371 pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
2375 kernel_pixels+=image->columns+kernel->width;
2377 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2378 if ((channel & RedChannel) != 0)
2379 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2380 if ((channel & GreenChannel) != 0)
2381 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2382 if ((channel & BlueChannel) != 0)
2383 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2384 if ((channel & OpacityChannel) != 0)
2388 for (v=0; v < (long) kernel->width; v++)
2390 for (u=0; u < (long) kernel->height; u++)
2392 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
2395 kernel_pixels+=image->columns+kernel->width;
2397 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2399 if (((channel & IndexChannel) != 0) &&
2400 (image->colorspace == CMYKColorspace))
2402 register const IndexPacket
2403 *restrict kernel_indexes;
2407 kernel_indexes=indexes;
2408 for (v=0; v < (long) kernel->width; v++)
2410 for (u=0; u < (long) kernel->height; u++)
2412 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
2413 kernel_pixels[u].opacity));
2414 pixel.index+=(*k)*alpha*kernel_indexes[u];
2417 kernel_pixels+=image->columns+kernel->width;
2418 kernel_indexes+=image->columns+kernel->width;
2420 filter_indexes[x]=ClampToQuantum(gamma*
2421 GetIndexPixelComponent(&pixel));
2427 sync=SyncCacheViewAuthenticPixels(filter_view,exception);
2428 if (sync == MagickFalse)
2430 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2435 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2436 #pragma omp critical (MagickCore_FilterImageChannel)
2438 proceed=SetImageProgress(image,FilterImageTag,progress++,image->rows);
2439 if (proceed == MagickFalse)
2443 filter_image->type=image->type;
2444 filter_view=DestroyCacheView(filter_view);
2445 image_view=DestroyCacheView(image_view);
2446 if (status == MagickFalse)
2447 filter_image=DestroyImage(filter_image);
2448 return(filter_image);
2452 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2456 % G a u s s i a n B l u r I m a g e %
2460 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2462 % GaussianBlurImage() blurs an image. We convolve the image with a
2463 % Gaussian operator of the given radius and standard deviation (sigma).
2464 % For reasonable results, the radius should be larger than sigma. Use a
2465 % radius of 0 and GaussianBlurImage() selects a suitable radius for you
2467 % The format of the GaussianBlurImage method is:
2469 % Image *GaussianBlurImage(const Image *image,onst double radius,
2470 % const double sigma,ExceptionInfo *exception)
2471 % Image *GaussianBlurImageChannel(const Image *image,
2472 % const ChannelType channel,const double radius,const double sigma,
2473 % ExceptionInfo *exception)
2475 % A description of each parameter follows:
2477 % o image: the image.
2479 % o channel: the channel type.
2481 % o radius: the radius of the Gaussian, in pixels, not counting the center
2484 % o sigma: the standard deviation of the Gaussian, in pixels.
2486 % o exception: return any errors or warnings in this structure.
2490 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
2491 const double sigma,ExceptionInfo *exception)
2496 blur_image=GaussianBlurImageChannel(image,DefaultChannels,radius,sigma,
2501 MagickExport Image *GaussianBlurImageChannel(const Image *image,
2502 const ChannelType channel,const double radius,const double sigma,
2503 ExceptionInfo *exception)
2522 assert(image != (const Image *) NULL);
2523 assert(image->signature == MagickSignature);
2524 if (image->debug != MagickFalse)
2525 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2526 assert(exception != (ExceptionInfo *) NULL);
2527 assert(exception->signature == MagickSignature);
2528 width=GetOptimalKernelWidth2D(radius,sigma);
2529 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2530 if (kernel == (double *) NULL)
2531 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2534 for (v=(-j); v <= j; v++)
2536 for (u=(-j); u <= j; u++)
2537 kernel[i++]=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
2538 (2.0*MagickPI*MagickSigma*MagickSigma);
2540 blur_image=ConvolveImageChannel(image,channel,width,kernel,exception);
2541 kernel=(double *) RelinquishMagickMemory(kernel);
2546 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2550 % M e d i a n F i l t e r I m a g e %
2554 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2556 % MedianFilterImage() applies a digital filter that improves the quality
2557 % of a noisy image. Each pixel is replaced by the median in a set of
2558 % neighboring pixels as defined by radius.
2560 % The algorithm was contributed by Mike Edmonds and implements an insertion
2561 % sort for selecting median color-channel values. For more on this algorithm
2562 % see "Skip Lists: A probabilistic Alternative to Balanced Trees" by William
2563 % Pugh in the June 1990 of Communications of the ACM.
2565 % The format of the MedianFilterImage method is:
2567 % Image *MedianFilterImage(const Image *image,const double radius,
2568 % ExceptionInfo *exception)
2570 % A description of each parameter follows:
2572 % o image: the image.
2574 % o radius: the radius of the pixel neighborhood.
2576 % o exception: return any errors or warnings in this structure.
2580 #define MedianListChannels 5
2582 typedef struct _MedianListNode
2590 typedef struct _MedianSkipList
2599 typedef struct _MedianPixelList
2607 lists[MedianListChannels];
2610 static MedianPixelList *DestroyMedianPixelList(MedianPixelList *pixel_list)
2615 if (pixel_list == (MedianPixelList *) NULL)
2616 return((MedianPixelList *) NULL);
2617 for (i=0; i < MedianListChannels; i++)
2618 if (pixel_list->lists[i].nodes != (MedianListNode *) NULL)
2619 pixel_list->lists[i].nodes=(MedianListNode *) RelinquishMagickMemory(
2620 pixel_list->lists[i].nodes);
2621 pixel_list=(MedianPixelList *) RelinquishAlignedMemory(pixel_list);
2625 static MedianPixelList **DestroyMedianPixelListThreadSet(
2626 MedianPixelList **pixel_list)
2631 assert(pixel_list != (MedianPixelList **) NULL);
2632 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
2633 if (pixel_list[i] != (MedianPixelList *) NULL)
2634 pixel_list[i]=DestroyMedianPixelList(pixel_list[i]);
2635 pixel_list=(MedianPixelList **) RelinquishAlignedMemory(pixel_list);
2639 static MedianPixelList *AcquireMedianPixelList(const unsigned long width)
2647 pixel_list=(MedianPixelList *) AcquireAlignedMemory(1,sizeof(*pixel_list));
2648 if (pixel_list == (MedianPixelList *) NULL)
2650 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
2651 pixel_list->center=width*width/2;
2652 for (i=0; i < MedianListChannels; i++)
2654 pixel_list->lists[i].nodes=(MedianListNode *) AcquireQuantumMemory(65537UL,
2655 sizeof(*pixel_list->lists[i].nodes));
2656 if (pixel_list->lists[i].nodes == (MedianListNode *) NULL)
2657 return(DestroyMedianPixelList(pixel_list));
2658 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
2659 sizeof(*pixel_list->lists[i].nodes));
2661 pixel_list->signature=MagickSignature;
2665 static MedianPixelList **AcquireMedianPixelListThreadSet(
2666 const unsigned long width)
2677 number_threads=GetOpenMPMaximumThreads();
2678 pixel_list=(MedianPixelList **) AcquireAlignedMemory(number_threads,
2679 sizeof(*pixel_list));
2680 if (pixel_list == (MedianPixelList **) NULL)
2681 return((MedianPixelList **) NULL);
2682 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
2683 for (i=0; i < (long) number_threads; i++)
2685 pixel_list[i]=AcquireMedianPixelList(width);
2686 if (pixel_list[i] == (MedianPixelList *) NULL)
2687 return(DestroyMedianPixelListThreadSet(pixel_list));
2692 static void AddNodeMedianPixelList(MedianPixelList *pixel_list,
2693 const long channel,const unsigned long color)
2698 register MedianSkipList
2706 Initialize the node.
2708 list=pixel_list->lists+channel;
2709 list->nodes[color].signature=pixel_list->signature;
2710 list->nodes[color].count=1;
2712 Determine where it belongs in the list.
2715 for (level=list->level; level >= 0; level--)
2717 while (list->nodes[search].next[level] < color)
2718 search=list->nodes[search].next[level];
2719 update[level]=search;
2722 Generate a pseudo-random level for this node.
2724 for (level=0; ; level++)
2726 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
2727 if ((pixel_list->seed & 0x300) != 0x300)
2732 if (level > (list->level+2))
2733 level=list->level+2;
2735 If we're raising the list's level, link back to the root node.
2737 while (level > list->level)
2740 update[list->level]=65536UL;
2743 Link the node into the skip-list.
2747 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
2748 list->nodes[update[level]].next[level]=color;
2750 while (level-- > 0);
2753 static MagickPixelPacket GetMedianPixelList(MedianPixelList *pixel_list)
2761 register MedianSkipList
2770 channels[MedianListChannels];
2773 Find the median value for each of the color.
2775 center=pixel_list->center;
2776 for (channel=0; channel < 5; channel++)
2778 list=pixel_list->lists+channel;
2783 color=list->nodes[color].next[0];
2784 count+=list->nodes[color].count;
2786 while (count <= center);
2787 channels[channel]=(unsigned short) color;
2789 GetMagickPixelPacket((const Image *) NULL,&pixel);
2790 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
2791 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
2792 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
2793 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
2794 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
2798 static inline void InsertMedianPixelList(const Image *image,
2799 const PixelPacket *pixel,const IndexPacket *indexes,
2800 MedianPixelList *pixel_list)
2808 index=ScaleQuantumToShort(pixel->red);
2809 signature=pixel_list->lists[0].nodes[index].signature;
2810 if (signature == pixel_list->signature)
2811 pixel_list->lists[0].nodes[index].count++;
2813 AddNodeMedianPixelList(pixel_list,0,index);
2814 index=ScaleQuantumToShort(pixel->green);
2815 signature=pixel_list->lists[1].nodes[index].signature;
2816 if (signature == pixel_list->signature)
2817 pixel_list->lists[1].nodes[index].count++;
2819 AddNodeMedianPixelList(pixel_list,1,index);
2820 index=ScaleQuantumToShort(pixel->blue);
2821 signature=pixel_list->lists[2].nodes[index].signature;
2822 if (signature == pixel_list->signature)
2823 pixel_list->lists[2].nodes[index].count++;
2825 AddNodeMedianPixelList(pixel_list,2,index);
2826 index=ScaleQuantumToShort(pixel->opacity);
2827 signature=pixel_list->lists[3].nodes[index].signature;
2828 if (signature == pixel_list->signature)
2829 pixel_list->lists[3].nodes[index].count++;
2831 AddNodeMedianPixelList(pixel_list,3,index);
2832 if (image->colorspace == CMYKColorspace)
2833 index=ScaleQuantumToShort(*indexes);
2834 signature=pixel_list->lists[4].nodes[index].signature;
2835 if (signature == pixel_list->signature)
2836 pixel_list->lists[4].nodes[index].count++;
2838 AddNodeMedianPixelList(pixel_list,4,index);
2841 static void ResetMedianPixelList(MedianPixelList *pixel_list)
2849 register MedianListNode
2852 register MedianSkipList
2856 Reset the skip-list.
2858 for (channel=0; channel < 5; channel++)
2860 list=pixel_list->lists+channel;
2861 root=list->nodes+65536UL;
2863 for (level=0; level < 9; level++)
2864 root->next[level]=65536UL;
2866 pixel_list->seed=pixel_list->signature++;
2869 MagickExport Image *MedianFilterImage(const Image *image,const double radius,
2870 ExceptionInfo *exception)
2872 #define MedianFilterImageTag "MedianFilter/Image"
2889 **restrict pixel_list;
2895 Initialize median image attributes.
2897 assert(image != (Image *) NULL);
2898 assert(image->signature == MagickSignature);
2899 if (image->debug != MagickFalse)
2900 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2901 assert(exception != (ExceptionInfo *) NULL);
2902 assert(exception->signature == MagickSignature);
2903 width=GetOptimalKernelWidth2D(radius,0.5);
2904 if ((image->columns < width) || (image->rows < width))
2905 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
2906 median_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2908 if (median_image == (Image *) NULL)
2909 return((Image *) NULL);
2910 if (SetImageStorageClass(median_image,DirectClass) == MagickFalse)
2912 InheritException(exception,&median_image->exception);
2913 median_image=DestroyImage(median_image);
2914 return((Image *) NULL);
2916 pixel_list=AcquireMedianPixelListThreadSet(width);
2917 if (pixel_list == (MedianPixelList **) NULL)
2919 median_image=DestroyImage(median_image);
2920 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2923 Median filter each image row.
2927 image_view=AcquireCacheView(image);
2928 median_view=AcquireCacheView(median_image);
2929 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2930 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2932 for (y=0; y < (long) median_image->rows; y++)
2934 register const IndexPacket
2937 register const PixelPacket
2940 register IndexPacket
2941 *restrict median_indexes;
2947 register PixelPacket
2950 if (status == MagickFalse)
2952 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
2953 2L),image->columns+width,width,exception);
2954 q=QueueCacheViewAuthenticPixels(median_view,0,y,median_image->columns,1,
2956 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2961 indexes=GetCacheViewVirtualIndexQueue(image_view);
2962 median_indexes=GetCacheViewAuthenticIndexQueue(median_view);
2963 id=GetOpenMPThreadId();
2964 for (x=0; x < (long) median_image->columns; x++)
2969 register const PixelPacket
2972 register const IndexPacket
2981 ResetMedianPixelList(pixel_list[id]);
2982 for (v=0; v < (long) width; v++)
2984 for (u=0; u < (long) width; u++)
2985 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
2986 r+=image->columns+width;
2987 s+=image->columns+width;
2989 pixel=GetMedianPixelList(pixel_list[id]);
2990 SetPixelPacket(median_image,&pixel,q,median_indexes+x);
2994 if (SyncCacheViewAuthenticPixels(median_view,exception) == MagickFalse)
2996 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3001 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3002 #pragma omp critical (MagickCore_MedianFilterImage)
3004 proceed=SetImageProgress(image,MedianFilterImageTag,progress++,
3006 if (proceed == MagickFalse)
3010 median_view=DestroyCacheView(median_view);
3011 image_view=DestroyCacheView(image_view);
3012 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
3013 return(median_image);
3017 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3021 % M o t i o n B l u r I m a g e %
3025 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3027 % MotionBlurImage() simulates motion blur. We convolve the image with a
3028 % Gaussian operator of the given radius and standard deviation (sigma).
3029 % For reasonable results, radius should be larger than sigma. Use a
3030 % radius of 0 and MotionBlurImage() selects a suitable radius for you.
3031 % Angle gives the angle of the blurring motion.
3033 % Andrew Protano contributed this effect.
3035 % The format of the MotionBlurImage method is:
3037 % Image *MotionBlurImage(const Image *image,const double radius,
3038 % const double sigma,const double angle,ExceptionInfo *exception)
3039 % Image *MotionBlurImageChannel(const Image *image,const ChannelType channel,
3040 % const double radius,const double sigma,const double angle,
3041 % ExceptionInfo *exception)
3043 % A description of each parameter follows:
3045 % o image: the image.
3047 % o channel: the channel type.
3049 % o radius: the radius of the Gaussian, in pixels, not counting the center
3050 % o radius: the radius of the Gaussian, in pixels, not counting
3053 % o sigma: the standard deviation of the Gaussian, in pixels.
3055 % o angle: Apply the effect along this angle.
3057 % o exception: return any errors or warnings in this structure.
3061 static double *GetMotionBlurKernel(const unsigned long width,const double sigma)
3071 Generate a 1-D convolution kernel.
3073 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3074 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
3075 if (kernel == (double *) NULL)
3078 for (i=0; i < (long) width; i++)
3080 kernel[i]=exp((-((double) i*i)/(double) (2.0*MagickSigma*MagickSigma)))/
3081 (MagickSQ2PI*MagickSigma);
3082 normalize+=kernel[i];
3084 for (i=0; i < (long) width; i++)
3085 kernel[i]/=normalize;
3089 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
3090 const double sigma,const double angle,ExceptionInfo *exception)
3095 motion_blur=MotionBlurImageChannel(image,DefaultChannels,radius,sigma,angle,
3097 return(motion_blur);
3100 MagickExport Image *MotionBlurImageChannel(const Image *image,
3101 const ChannelType channel,const double radius,const double sigma,
3102 const double angle,ExceptionInfo *exception)
3136 assert(image != (Image *) NULL);
3137 assert(image->signature == MagickSignature);
3138 if (image->debug != MagickFalse)
3139 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3140 assert(exception != (ExceptionInfo *) NULL);
3141 width=GetOptimalKernelWidth1D(radius,sigma);
3142 kernel=GetMotionBlurKernel(width,sigma);
3143 if (kernel == (double *) NULL)
3144 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3145 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
3146 if (offset == (OffsetInfo *) NULL)
3148 kernel=(double *) RelinquishMagickMemory(kernel);
3149 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3151 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3152 if (blur_image == (Image *) NULL)
3154 kernel=(double *) RelinquishMagickMemory(kernel);
3155 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3156 return((Image *) NULL);
3158 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3160 kernel=(double *) RelinquishMagickMemory(kernel);
3161 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3162 InheritException(exception,&blur_image->exception);
3163 blur_image=DestroyImage(blur_image);
3164 return((Image *) NULL);
3166 point.x=(double) width*sin(DegreesToRadians(angle));
3167 point.y=(double) width*cos(DegreesToRadians(angle));
3168 for (i=0; i < (long) width; i++)
3170 offset[i].x=(long) ceil((i*point.y)/hypot(point.x,point.y)-0.5);
3171 offset[i].y=(long) ceil((i*point.x)/hypot(point.x,point.y)-0.5);
3178 GetMagickPixelPacket(image,&bias);
3179 image_view=AcquireCacheView(image);
3180 blur_view=AcquireCacheView(blur_image);
3181 #if defined(MAGICKCORE_OPENMP_SUPPORT) && defined(MAGICKCORE_FUTURE)
3182 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3184 for (y=0; y < (long) image->rows; y++)
3186 register IndexPacket
3187 *restrict blur_indexes;
3192 register PixelPacket
3195 if (status == MagickFalse)
3197 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3199 if (q == (PixelPacket *) NULL)
3204 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
3205 for (x=0; x < (long) image->columns; x++)
3219 register const IndexPacket
3224 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
3226 for (i=0; i < (long) width; i++)
3228 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
3229 offset[i].y,&pixel,exception);
3230 qixel.red+=(*k)*pixel.red;
3231 qixel.green+=(*k)*pixel.green;
3232 qixel.blue+=(*k)*pixel.blue;
3233 qixel.opacity+=(*k)*pixel.opacity;
3234 if (image->colorspace == CMYKColorspace)
3236 indexes=GetCacheViewVirtualIndexQueue(image_view);
3237 qixel.index+=(*k)*(*indexes);
3241 if ((channel & RedChannel) != 0)
3242 q->red=ClampToQuantum(qixel.red);
3243 if ((channel & GreenChannel) != 0)
3244 q->green=ClampToQuantum(qixel.green);
3245 if ((channel & BlueChannel) != 0)
3246 q->blue=ClampToQuantum(qixel.blue);
3247 if ((channel & OpacityChannel) != 0)
3248 q->opacity=ClampToQuantum(qixel.opacity);
3249 if (((channel & IndexChannel) != 0) &&
3250 (image->colorspace == CMYKColorspace))
3251 blur_indexes[x]=(IndexPacket) ClampToQuantum(qixel.index);
3261 for (i=0; i < (long) width; i++)
3263 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
3264 offset[i].y,&pixel,exception);
3265 alpha=(MagickRealType) (QuantumScale*
3266 GetAlphaPixelComponent(&pixel));
3267 qixel.red+=(*k)*alpha*pixel.red;
3268 qixel.green+=(*k)*alpha*pixel.green;
3269 qixel.blue+=(*k)*alpha*pixel.blue;
3270 qixel.opacity+=(*k)*pixel.opacity;
3271 if (image->colorspace == CMYKColorspace)
3273 indexes=GetCacheViewVirtualIndexQueue(image_view);
3274 qixel.index+=(*k)*alpha*(*indexes);
3279 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3280 if ((channel & RedChannel) != 0)
3281 q->red=ClampToQuantum(gamma*qixel.red);
3282 if ((channel & GreenChannel) != 0)
3283 q->green=ClampToQuantum(gamma*qixel.green);
3284 if ((channel & BlueChannel) != 0)
3285 q->blue=ClampToQuantum(gamma*qixel.blue);
3286 if ((channel & OpacityChannel) != 0)
3287 q->opacity=ClampToQuantum(qixel.opacity);
3288 if (((channel & IndexChannel) != 0) &&
3289 (image->colorspace == CMYKColorspace))
3290 blur_indexes[x]=(IndexPacket) ClampToQuantum(gamma*qixel.index);
3294 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3296 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3301 #if defined(MAGICKCORE_OPENMP_SUPPORT) && defined(MAGICKCORE_FUTURE)
3302 #pragma omp critical (MagickCore_MotionBlurImageChannel)
3304 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3305 if (proceed == MagickFalse)
3309 blur_view=DestroyCacheView(blur_view);
3310 image_view=DestroyCacheView(image_view);
3311 kernel=(double *) RelinquishMagickMemory(kernel);
3312 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3313 if (status == MagickFalse)
3314 blur_image=DestroyImage(blur_image);
3319 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3323 % P r e v i e w I m a g e %
3327 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3329 % PreviewImage() tiles 9 thumbnails of the specified image with an image
3330 % processing operation applied with varying parameters. This may be helpful
3331 % pin-pointing an appropriate parameter for a particular image processing
3334 % The format of the PreviewImages method is:
3336 % Image *PreviewImages(const Image *image,const PreviewType preview,
3337 % ExceptionInfo *exception)
3339 % A description of each parameter follows:
3341 % o image: the image.
3343 % o preview: the image processing operation.
3345 % o exception: return any errors or warnings in this structure.
3348 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
3349 ExceptionInfo *exception)
3351 #define NumberTiles 9
3352 #define PreviewImageTag "Preview/Image"
3353 #define DefaultPreviewGeometry "204x204+10+10"
3356 factor[MaxTextExtent],
3357 label[MaxTextExtent];
3399 Open output image file.
3401 assert(image != (Image *) NULL);
3402 assert(image->signature == MagickSignature);
3403 if (image->debug != MagickFalse)
3404 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3408 preview_info=AcquireImageInfo();
3409 SetGeometry(image,&geometry);
3410 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
3411 &geometry.width,&geometry.height);
3412 images=NewImageList();
3414 GetQuantizeInfo(&quantize_info);
3420 for (i=0; i < NumberTiles; i++)
3422 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
3423 if (thumbnail == (Image *) NULL)
3425 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
3427 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
3428 if (i == (NumberTiles/2))
3430 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
3431 AppendImageToList(&images,thumbnail);
3439 preview_image=RotateImage(thumbnail,degrees,exception);
3440 (void) FormatMagickString(label,MaxTextExtent,"rotate %g",degrees);
3446 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
3447 (void) FormatMagickString(label,MaxTextExtent,"shear %gx%g",
3448 degrees,2.0*degrees);
3453 x=(long) ((i+1)*thumbnail->columns)/NumberTiles;
3454 y=(long) ((i+1)*thumbnail->rows)/NumberTiles;
3455 preview_image=RollImage(thumbnail,x,y,exception);
3456 (void) FormatMagickString(label,MaxTextExtent,"roll %ldx%ld",x,y);
3461 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3462 if (preview_image == (Image *) NULL)
3464 (void) FormatMagickString(factor,MaxTextExtent,"100,100,%g",
3466 (void) ModulateImage(preview_image,factor);
3467 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3470 case SaturationPreview:
3472 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3473 if (preview_image == (Image *) NULL)
3475 (void) FormatMagickString(factor,MaxTextExtent,"100,%g",
3477 (void) ModulateImage(preview_image,factor);
3478 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3481 case BrightnessPreview:
3483 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3484 if (preview_image == (Image *) NULL)
3486 (void) FormatMagickString(factor,MaxTextExtent,"%g",2.0*percentage);
3487 (void) ModulateImage(preview_image,factor);
3488 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3494 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3495 if (preview_image == (Image *) NULL)
3498 (void) GammaImageChannel(preview_image,DefaultChannels,gamma);
3499 (void) FormatMagickString(label,MaxTextExtent,"gamma %g",gamma);
3504 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3505 if (preview_image != (Image *) NULL)
3506 for (x=0; x < i; x++)
3507 (void) ContrastImage(preview_image,MagickTrue);
3508 (void) FormatMagickString(label,MaxTextExtent,"contrast (%ld)",i+1);
3513 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3514 if (preview_image == (Image *) NULL)
3516 for (x=0; x < i; x++)
3517 (void) ContrastImage(preview_image,MagickFalse);
3518 (void) FormatMagickString(label,MaxTextExtent,"+contrast (%ld)",i+1);
3521 case GrayscalePreview:
3523 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3524 if (preview_image == (Image *) NULL)
3527 quantize_info.number_colors=colors;
3528 quantize_info.colorspace=GRAYColorspace;
3529 (void) QuantizeImage(&quantize_info,preview_image);
3530 (void) FormatMagickString(label,MaxTextExtent,
3531 "-colorspace gray -colors %ld",colors);
3534 case QuantizePreview:
3536 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3537 if (preview_image == (Image *) NULL)
3540 quantize_info.number_colors=colors;
3541 (void) QuantizeImage(&quantize_info,preview_image);
3542 (void) FormatMagickString(label,MaxTextExtent,"colors %ld",colors);
3545 case DespecklePreview:
3547 for (x=0; x < (i-1); x++)
3549 preview_image=DespeckleImage(thumbnail,exception);
3550 if (preview_image == (Image *) NULL)
3552 thumbnail=DestroyImage(thumbnail);
3553 thumbnail=preview_image;
3555 preview_image=DespeckleImage(thumbnail,exception);
3556 if (preview_image == (Image *) NULL)
3558 (void) FormatMagickString(label,MaxTextExtent,"despeckle (%ld)",i+1);
3561 case ReduceNoisePreview:
3563 preview_image=ReduceNoiseImage(thumbnail,radius,exception);
3564 (void) FormatMagickString(label,MaxTextExtent,"noise %g",radius);
3567 case AddNoisePreview:
3573 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
3578 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
3583 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
3588 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
3593 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
3598 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
3603 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
3607 preview_image=ReduceNoiseImage(thumbnail,(double) i,exception);
3608 (void) FormatMagickString(label,MaxTextExtent,"+noise %s",factor);
3611 case SharpenPreview:
3613 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
3614 (void) FormatMagickString(label,MaxTextExtent,"sharpen %gx%g",
3620 preview_image=BlurImage(thumbnail,radius,sigma,exception);
3621 (void) FormatMagickString(label,MaxTextExtent,"blur %gx%g",radius,
3625 case ThresholdPreview:
3627 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3628 if (preview_image == (Image *) NULL)
3630 (void) BilevelImage(thumbnail,
3631 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3632 (void) FormatMagickString(label,MaxTextExtent,"threshold %g",
3633 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3636 case EdgeDetectPreview:
3638 preview_image=EdgeImage(thumbnail,radius,exception);
3639 (void) FormatMagickString(label,MaxTextExtent,"edge %g",radius);
3644 preview_image=SpreadImage(thumbnail,radius,exception);
3645 (void) FormatMagickString(label,MaxTextExtent,"spread %g",
3649 case SolarizePreview:
3651 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3652 if (preview_image == (Image *) NULL)
3654 (void) SolarizeImage(preview_image,(double) QuantumRange*
3656 (void) FormatMagickString(label,MaxTextExtent,"solarize %g",
3657 (QuantumRange*percentage)/100.0);
3663 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
3665 (void) FormatMagickString(label,MaxTextExtent,"shade %gx%g",
3671 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3672 if (preview_image == (Image *) NULL)
3674 geometry.width=(unsigned long) (2*i+2);
3675 geometry.height=(unsigned long) (2*i+2);
3678 (void) RaiseImage(preview_image,&geometry,MagickTrue);
3679 (void) FormatMagickString(label,MaxTextExtent,"raise %lux%lu%+ld%+ld",
3680 geometry.width,geometry.height,geometry.x,geometry.y);
3683 case SegmentPreview:
3685 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3686 if (preview_image == (Image *) NULL)
3689 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
3691 (void) FormatMagickString(label,MaxTextExtent,"segment %gx%g",
3692 threshold,threshold);
3697 preview_image=SwirlImage(thumbnail,degrees,exception);
3698 (void) FormatMagickString(label,MaxTextExtent,"swirl %g",degrees);
3702 case ImplodePreview:
3705 preview_image=ImplodeImage(thumbnail,degrees,exception);
3706 (void) FormatMagickString(label,MaxTextExtent,"implode %g",degrees);
3712 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
3713 (void) FormatMagickString(label,MaxTextExtent,"wave %gx%g",
3714 0.5*degrees,2.0*degrees);
3717 case OilPaintPreview:
3719 preview_image=OilPaintImage(thumbnail,(double) radius,exception);
3720 (void) FormatMagickString(label,MaxTextExtent,"paint %g",radius);
3723 case CharcoalDrawingPreview:
3725 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
3727 (void) FormatMagickString(label,MaxTextExtent,"charcoal %gx%g",
3734 filename[MaxTextExtent];
3742 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3743 if (preview_image == (Image *) NULL)
3745 preview_info->quality=(unsigned long) percentage;
3746 (void) FormatMagickString(factor,MaxTextExtent,"%lu",
3747 preview_info->quality);
3748 file=AcquireUniqueFileResource(filename);
3751 (void) FormatMagickString(preview_image->filename,MaxTextExtent,
3752 "jpeg:%s",filename);
3753 status=WriteImage(preview_info,preview_image);
3754 if (status != MagickFalse)
3759 (void) CopyMagickString(preview_info->filename,
3760 preview_image->filename,MaxTextExtent);
3761 quality_image=ReadImage(preview_info,exception);
3762 if (quality_image != (Image *) NULL)
3764 preview_image=DestroyImage(preview_image);
3765 preview_image=quality_image;
3768 (void) RelinquishUniqueFileResource(preview_image->filename);
3769 if ((GetBlobSize(preview_image)/1024) >= 1024)
3770 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gmb ",
3771 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
3774 if (GetBlobSize(preview_image) >= 1024)
3775 (void) FormatMagickString(label,MaxTextExtent,
3776 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
3777 GetBlobSize(preview_image))/1024.0);
3779 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%lub ",
3780 factor,(unsigned long) GetBlobSize(thumbnail));
3784 thumbnail=DestroyImage(thumbnail);
3788 if (preview_image == (Image *) NULL)
3790 (void) DeleteImageProperty(preview_image,"label");
3791 (void) SetImageProperty(preview_image,"label",label);
3792 AppendImageToList(&images,preview_image);
3793 proceed=SetImageProgress(image,PreviewImageTag,i,NumberTiles);
3794 if (proceed == MagickFalse)
3797 if (images == (Image *) NULL)
3799 preview_info=DestroyImageInfo(preview_info);
3800 return((Image *) NULL);
3805 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
3806 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
3807 montage_info->shadow=MagickTrue;
3808 (void) CloneString(&montage_info->tile,"3x3");
3809 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
3810 (void) CloneString(&montage_info->frame,DefaultTileFrame);
3811 montage_image=MontageImages(images,montage_info,exception);
3812 montage_info=DestroyMontageInfo(montage_info);
3813 images=DestroyImageList(images);
3814 if (montage_image == (Image *) NULL)
3815 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3816 if (montage_image->montage != (char *) NULL)
3819 Free image directory.
3821 montage_image->montage=(char *) RelinquishMagickMemory(
3822 montage_image->montage);
3823 if (image->directory != (char *) NULL)
3824 montage_image->directory=(char *) RelinquishMagickMemory(
3825 montage_image->directory);
3827 preview_info=DestroyImageInfo(preview_info);
3828 return(montage_image);
3832 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3836 % R a d i a l B l u r I m a g e %
3840 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3842 % RadialBlurImage() applies a radial blur to the image.
3844 % Andrew Protano contributed this effect.
3846 % The format of the RadialBlurImage method is:
3848 % Image *RadialBlurImage(const Image *image,const double angle,
3849 % ExceptionInfo *exception)
3850 % Image *RadialBlurImageChannel(const Image *image,const ChannelType channel,
3851 % const double angle,ExceptionInfo *exception)
3853 % A description of each parameter follows:
3855 % o image: the image.
3857 % o channel: the channel type.
3859 % o angle: the angle of the radial blur.
3861 % o exception: return any errors or warnings in this structure.
3865 MagickExport Image *RadialBlurImage(const Image *image,const double angle,
3866 ExceptionInfo *exception)
3871 blur_image=RadialBlurImageChannel(image,DefaultChannels,angle,exception);
3875 MagickExport Image *RadialBlurImageChannel(const Image *image,
3876 const ChannelType channel,const double angle,ExceptionInfo *exception)
3912 Allocate blur image.
3914 assert(image != (Image *) NULL);
3915 assert(image->signature == MagickSignature);
3916 if (image->debug != MagickFalse)
3917 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3918 assert(exception != (ExceptionInfo *) NULL);
3919 assert(exception->signature == MagickSignature);
3920 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3921 if (blur_image == (Image *) NULL)
3922 return((Image *) NULL);
3923 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3925 InheritException(exception,&blur_image->exception);
3926 blur_image=DestroyImage(blur_image);
3927 return((Image *) NULL);
3929 blur_center.x=(double) image->columns/2.0;
3930 blur_center.y=(double) image->rows/2.0;
3931 blur_radius=hypot(blur_center.x,blur_center.y);
3932 n=(unsigned long) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+
3934 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
3935 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3936 sizeof(*cos_theta));
3937 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3938 sizeof(*sin_theta));
3939 if ((cos_theta == (MagickRealType *) NULL) ||
3940 (sin_theta == (MagickRealType *) NULL))
3942 blur_image=DestroyImage(blur_image);
3943 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3945 offset=theta*(MagickRealType) (n-1)/2.0;
3946 for (i=0; i < (long) n; i++)
3948 cos_theta[i]=cos((double) (theta*i-offset));
3949 sin_theta[i]=sin((double) (theta*i-offset));
3956 GetMagickPixelPacket(image,&bias);
3957 image_view=AcquireCacheView(image);
3958 blur_view=AcquireCacheView(blur_image);
3959 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3960 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3962 for (y=0; y < (long) blur_image->rows; y++)
3964 register const IndexPacket
3967 register IndexPacket
3968 *restrict blur_indexes;
3973 register PixelPacket
3976 if (status == MagickFalse)
3978 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3980 if (q == (PixelPacket *) NULL)
3985 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
3986 for (x=0; x < (long) blur_image->columns; x++)
4007 center.x=(double) x-blur_center.x;
4008 center.y=(double) y-blur_center.y;
4009 radius=hypot((double) center.x,center.y);
4014 step=(unsigned long) (blur_radius/radius);
4023 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
4025 for (i=0; i < (long) n; i+=step)
4027 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
4028 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
4029 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
4031 qixel.red+=pixel.red;
4032 qixel.green+=pixel.green;
4033 qixel.blue+=pixel.blue;
4034 qixel.opacity+=pixel.opacity;
4035 if (image->colorspace == CMYKColorspace)
4037 indexes=GetCacheViewVirtualIndexQueue(image_view);
4038 qixel.index+=(*indexes);
4042 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
4044 if ((channel & RedChannel) != 0)
4045 q->red=ClampToQuantum(normalize*qixel.red);
4046 if ((channel & GreenChannel) != 0)
4047 q->green=ClampToQuantum(normalize*qixel.green);
4048 if ((channel & BlueChannel) != 0)
4049 q->blue=ClampToQuantum(normalize*qixel.blue);
4050 if ((channel & OpacityChannel) != 0)
4051 q->opacity=ClampToQuantum(normalize*qixel.opacity);
4052 if (((channel & IndexChannel) != 0) &&
4053 (image->colorspace == CMYKColorspace))
4054 blur_indexes[x]=(IndexPacket) ClampToQuantum(normalize*qixel.index);
4064 for (i=0; i < (long) n; i+=step)
4066 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
4067 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
4068 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
4070 alpha=(MagickRealType) (QuantumScale*
4071 GetAlphaPixelComponent(&pixel));
4072 qixel.red+=alpha*pixel.red;
4073 qixel.green+=alpha*pixel.green;
4074 qixel.blue+=alpha*pixel.blue;
4075 qixel.opacity+=pixel.opacity;
4076 if (image->colorspace == CMYKColorspace)
4078 indexes=GetCacheViewVirtualIndexQueue(image_view);
4079 qixel.index+=alpha*(*indexes);
4084 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4085 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
4087 if ((channel & RedChannel) != 0)
4088 q->red=ClampToQuantum(gamma*qixel.red);
4089 if ((channel & GreenChannel) != 0)
4090 q->green=ClampToQuantum(gamma*qixel.green);
4091 if ((channel & BlueChannel) != 0)
4092 q->blue=ClampToQuantum(gamma*qixel.blue);
4093 if ((channel & OpacityChannel) != 0)
4094 q->opacity=ClampToQuantum(normalize*qixel.opacity);
4095 if (((channel & IndexChannel) != 0) &&
4096 (image->colorspace == CMYKColorspace))
4097 blur_indexes[x]=(IndexPacket) ClampToQuantum(gamma*qixel.index);
4101 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
4103 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4108 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4109 #pragma omp critical (MagickCore_RadialBlurImageChannel)
4111 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
4112 if (proceed == MagickFalse)
4116 blur_view=DestroyCacheView(blur_view);
4117 image_view=DestroyCacheView(image_view);
4118 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
4119 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
4120 if (status == MagickFalse)
4121 blur_image=DestroyImage(blur_image);
4126 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4130 % R e d u c e N o i s e I m a g e %
4134 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4136 % ReduceNoiseImage() smooths the contours of an image while still preserving
4137 % edge information. The algorithm works by replacing each pixel with its
4138 % neighbor closest in value. A neighbor is defined by radius. Use a radius
4139 % of 0 and ReduceNoise() selects a suitable radius for you.
4141 % The format of the ReduceNoiseImage method is:
4143 % Image *ReduceNoiseImage(const Image *image,const double radius,
4144 % ExceptionInfo *exception)
4146 % A description of each parameter follows:
4148 % o image: the image.
4150 % o radius: the radius of the pixel neighborhood.
4152 % o exception: return any errors or warnings in this structure.
4156 static MagickPixelPacket GetNonpeakMedianPixelList(MedianPixelList *pixel_list)
4164 register MedianSkipList
4178 Finds the median value for each of the color.
4180 center=pixel_list->center;
4181 for (channel=0; channel < 5; channel++)
4183 list=pixel_list->lists+channel;
4185 next=list->nodes[color].next[0];
4191 next=list->nodes[color].next[0];
4192 count+=list->nodes[color].count;
4194 while (count <= center);
4195 if ((previous == 65536UL) && (next != 65536UL))
4198 if ((previous != 65536UL) && (next == 65536UL))
4200 channels[channel]=(unsigned short) color;
4202 GetMagickPixelPacket((const Image *) NULL,&pixel);
4203 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4204 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4205 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
4206 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
4207 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
4211 MagickExport Image *ReduceNoiseImage(const Image *image,const double radius,
4212 ExceptionInfo *exception)
4214 #define ReduceNoiseImageTag "ReduceNoise/Image"
4231 **restrict pixel_list;
4237 Initialize noise image attributes.
4239 assert(image != (Image *) NULL);
4240 assert(image->signature == MagickSignature);
4241 if (image->debug != MagickFalse)
4242 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4243 assert(exception != (ExceptionInfo *) NULL);
4244 assert(exception->signature == MagickSignature);
4245 width=GetOptimalKernelWidth2D(radius,0.5);
4246 if ((image->columns < width) || (image->rows < width))
4247 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
4248 noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4250 if (noise_image == (Image *) NULL)
4251 return((Image *) NULL);
4252 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
4254 InheritException(exception,&noise_image->exception);
4255 noise_image=DestroyImage(noise_image);
4256 return((Image *) NULL);
4258 pixel_list=AcquireMedianPixelListThreadSet(width);
4259 if (pixel_list == (MedianPixelList **) NULL)
4261 noise_image=DestroyImage(noise_image);
4262 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4269 image_view=AcquireCacheView(image);
4270 noise_view=AcquireCacheView(noise_image);
4271 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4272 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4274 for (y=0; y < (long) noise_image->rows; y++)
4276 register const IndexPacket
4279 register const PixelPacket
4282 register IndexPacket
4283 *restrict noise_indexes;
4289 register PixelPacket
4292 if (status == MagickFalse)
4294 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
4295 2L),image->columns+width,width,exception);
4296 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
4298 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4303 indexes=GetCacheViewVirtualIndexQueue(image_view);
4304 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
4305 id=GetOpenMPThreadId();
4306 for (x=0; x < (long) noise_image->columns; x++)
4311 register const PixelPacket
4314 register const IndexPacket
4323 ResetMedianPixelList(pixel_list[id]);
4324 for (v=0; v < (long) width; v++)
4326 for (u=0; u < (long) width; u++)
4327 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
4328 r+=image->columns+width;
4329 s+=image->columns+width;
4331 pixel=GetNonpeakMedianPixelList(pixel_list[id]);
4332 SetPixelPacket(noise_image,&pixel,q,noise_indexes+x);
4336 if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
4338 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4343 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4344 #pragma omp critical (MagickCore_ReduceNoiseImage)
4346 proceed=SetImageProgress(image,ReduceNoiseImageTag,progress++,
4348 if (proceed == MagickFalse)
4352 noise_view=DestroyCacheView(noise_view);
4353 image_view=DestroyCacheView(image_view);
4354 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
4355 return(noise_image);
4359 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4363 % S e l e c t i v e B l u r I m a g e %
4367 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4369 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
4370 % It is similar to the unsharpen mask that sharpens everything with contrast
4371 % above a certain threshold.
4373 % The format of the SelectiveBlurImage method is:
4375 % Image *SelectiveBlurImage(const Image *image,const double radius,
4376 % const double sigma,const double threshold,ExceptionInfo *exception)
4377 % Image *SelectiveBlurImageChannel(const Image *image,
4378 % const ChannelType channel,const double radius,const double sigma,
4379 % const double threshold,ExceptionInfo *exception)
4381 % A description of each parameter follows:
4383 % o image: the image.
4385 % o channel: the channel type.
4387 % o radius: the radius of the Gaussian, in pixels, not counting the center
4390 % o sigma: the standard deviation of the Gaussian, in pixels.
4392 % o threshold: only pixels within this contrast threshold are included
4393 % in the blur operation.
4395 % o exception: return any errors or warnings in this structure.
4399 static inline MagickBooleanType SelectiveContrast(const PixelPacket *p,
4400 const PixelPacket *q,const double threshold)
4402 if (fabs(PixelIntensity(p)-PixelIntensity(q)) < threshold)
4404 return(MagickFalse);
4407 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
4408 const double sigma,const double threshold,ExceptionInfo *exception)
4413 blur_image=SelectiveBlurImageChannel(image,DefaultChannels,radius,sigma,
4414 threshold,exception);
4418 MagickExport Image *SelectiveBlurImageChannel(const Image *image,
4419 const ChannelType channel,const double radius,const double sigma,
4420 const double threshold,ExceptionInfo *exception)
4422 #define SelectiveBlurImageTag "SelectiveBlur/Image"
4454 Initialize blur image attributes.
4456 assert(image != (Image *) NULL);
4457 assert(image->signature == MagickSignature);
4458 if (image->debug != MagickFalse)
4459 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4460 assert(exception != (ExceptionInfo *) NULL);
4461 assert(exception->signature == MagickSignature);
4462 width=GetOptimalKernelWidth1D(radius,sigma);
4463 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
4464 if (kernel == (double *) NULL)
4465 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4468 for (v=(-j); v <= j; v++)
4470 for (u=(-j); u <= j; u++)
4471 kernel[i++]=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
4472 (2.0*MagickPI*MagickSigma*MagickSigma);
4474 if (image->debug != MagickFalse)
4477 format[MaxTextExtent],
4484 register const double
4487 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
4488 " SelectiveBlurImage with %ldx%ld kernel:",width,width);
4489 message=AcquireString("");
4491 for (v=0; v < (long) width; v++)
4494 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
4495 (void) ConcatenateString(&message,format);
4496 for (u=0; u < (long) width; u++)
4498 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
4499 (void) ConcatenateString(&message,format);
4501 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
4503 message=DestroyString(message);
4505 blur_image=CloneImage(image,0,0,MagickTrue,exception);
4506 if (blur_image == (Image *) NULL)
4507 return((Image *) NULL);
4508 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
4510 InheritException(exception,&blur_image->exception);
4511 blur_image=DestroyImage(blur_image);
4512 return((Image *) NULL);
4515 Threshold blur image.
4519 GetMagickPixelPacket(image,&bias);
4520 SetMagickPixelPacketBias(image,&bias);
4521 image_view=AcquireCacheView(image);
4522 blur_view=AcquireCacheView(blur_image);
4523 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4524 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4526 for (y=0; y < (long) image->rows; y++)
4534 register const IndexPacket
4537 register const PixelPacket
4540 register IndexPacket
4541 *restrict blur_indexes;
4546 register PixelPacket
4549 if (status == MagickFalse)
4551 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
4552 2L),image->columns+width,width,exception);
4553 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
4555 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4560 indexes=GetCacheViewVirtualIndexQueue(image_view);
4561 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
4562 for (x=0; x < (long) image->columns; x++)
4571 register const double
4581 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
4583 for (v=0; v < (long) width; v++)
4585 for (u=0; u < (long) width; u++)
4587 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4589 pixel.red+=(*k)*(p+u+j)->red;
4590 pixel.green+=(*k)*(p+u+j)->green;
4591 pixel.blue+=(*k)*(p+u+j)->blue;
4596 j+=image->columns+width;
4600 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4601 if ((channel & RedChannel) != 0)
4602 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
4603 if ((channel & GreenChannel) != 0)
4604 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
4605 if ((channel & BlueChannel) != 0)
4606 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
4608 if ((channel & OpacityChannel) != 0)
4612 for (v=0; v < (long) width; v++)
4614 for (u=0; u < (long) width; u++)
4616 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4618 pixel.opacity+=(*k)*(p+u+j)->opacity;
4623 j+=image->columns+width;
4627 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4629 SetOpacityPixelComponent(q,ClampToQuantum(gamma*
4630 GetOpacityPixelComponent(&pixel)));
4633 if (((channel & IndexChannel) != 0) &&
4634 (image->colorspace == CMYKColorspace))
4638 for (v=0; v < (long) width; v++)
4640 for (u=0; u < (long) width; u++)
4642 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4644 pixel.index+=(*k)*indexes[x+u+j];
4649 j+=image->columns+width;
4653 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4655 blur_indexes[x]=ClampToQuantum(gamma*
4656 GetIndexPixelComponent(&pixel));
4665 for (v=0; v < (long) width; v++)
4667 for (u=0; u < (long) width; u++)
4669 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4671 alpha=(MagickRealType) (QuantumScale*
4672 GetAlphaPixelComponent(p+u+j));
4673 pixel.red+=(*k)*alpha*(p+u+j)->red;
4674 pixel.green+=(*k)*alpha*(p+u+j)->green;
4675 pixel.blue+=(*k)*alpha*(p+u+j)->blue;
4676 pixel.opacity+=(*k)*(p+u+j)->opacity;
4681 j+=image->columns+width;
4685 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4686 if ((channel & RedChannel) != 0)
4687 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
4688 if ((channel & GreenChannel) != 0)
4689 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
4690 if ((channel & BlueChannel) != 0)
4691 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
4693 if ((channel & OpacityChannel) != 0)
4697 for (v=0; v < (long) width; v++)
4699 for (u=0; u < (long) width; u++)
4701 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4703 pixel.opacity+=(*k)*(p+u+j)->opacity;
4708 j+=image->columns+width;
4712 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4714 SetOpacityPixelComponent(q,
4715 ClampOpacityPixelComponent(&pixel));
4718 if (((channel & IndexChannel) != 0) &&
4719 (image->colorspace == CMYKColorspace))
4723 for (v=0; v < (long) width; v++)
4725 for (u=0; u < (long) width; u++)
4727 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4729 alpha=(MagickRealType) (QuantumScale*
4730 GetAlphaPixelComponent(p+u+j));
4731 pixel.index+=(*k)*alpha*indexes[x+u+j];
4736 j+=image->columns+width;
4740 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4742 blur_indexes[x]=ClampToQuantum(gamma*
4743 GetIndexPixelComponent(&pixel));
4750 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
4751 if (sync == MagickFalse)
4753 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4758 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4759 #pragma omp critical (MagickCore_SelectiveBlurImageChannel)
4761 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
4763 if (proceed == MagickFalse)
4767 blur_image->type=image->type;
4768 blur_view=DestroyCacheView(blur_view);
4769 image_view=DestroyCacheView(image_view);
4770 kernel=(double *) RelinquishMagickMemory(kernel);
4771 if (status == MagickFalse)
4772 blur_image=DestroyImage(blur_image);
4777 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4781 % S h a d e I m a g e %
4785 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4787 % ShadeImage() shines a distant light on an image to create a
4788 % three-dimensional effect. You control the positioning of the light with
4789 % azimuth and elevation; azimuth is measured in degrees off the x axis
4790 % and elevation is measured in pixels above the Z axis.
4792 % The format of the ShadeImage method is:
4794 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4795 % const double azimuth,const double elevation,ExceptionInfo *exception)
4797 % A description of each parameter follows:
4799 % o image: the image.
4801 % o gray: A value other than zero shades the intensity of each pixel.
4803 % o azimuth, elevation: Define the light source direction.
4805 % o exception: return any errors or warnings in this structure.
4808 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4809 const double azimuth,const double elevation,ExceptionInfo *exception)
4811 #define ShadeImageTag "Shade/Image"
4831 Initialize shaded image attributes.
4833 assert(image != (const Image *) NULL);
4834 assert(image->signature == MagickSignature);
4835 if (image->debug != MagickFalse)
4836 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4837 assert(exception != (ExceptionInfo *) NULL);
4838 assert(exception->signature == MagickSignature);
4839 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4840 if (shade_image == (Image *) NULL)
4841 return((Image *) NULL);
4842 if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
4844 InheritException(exception,&shade_image->exception);
4845 shade_image=DestroyImage(shade_image);
4846 return((Image *) NULL);
4849 Compute the light vector.
4851 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
4852 cos(DegreesToRadians(elevation));
4853 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
4854 cos(DegreesToRadians(elevation));
4855 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
4861 image_view=AcquireCacheView(image);
4862 shade_view=AcquireCacheView(shade_image);
4863 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4864 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4866 for (y=0; y < (long) image->rows; y++)
4876 register const PixelPacket
4885 register PixelPacket
4888 if (status == MagickFalse)
4890 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
4891 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
4893 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4899 Shade this row of pixels.
4901 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
4903 s1=s0+image->columns+2;
4904 s2=s1+image->columns+2;
4905 for (x=0; x < (long) image->columns; x++)
4908 Determine the surface normal and compute shading.
4910 normal.x=(double) (PixelIntensity(s0-1)+PixelIntensity(s1-1)+
4911 PixelIntensity(s2-1)-PixelIntensity(s0+1)-PixelIntensity(s1+1)-
4912 PixelIntensity(s2+1));
4913 normal.y=(double) (PixelIntensity(s2-1)+PixelIntensity(s2)+
4914 PixelIntensity(s2+1)-PixelIntensity(s0-1)-PixelIntensity(s0)-
4915 PixelIntensity(s0+1));
4916 if ((normal.x == 0.0) && (normal.y == 0.0))
4921 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
4922 if (distance > MagickEpsilon)
4925 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
4926 if (normal_distance > (MagickEpsilon*MagickEpsilon))
4927 shade=distance/sqrt((double) normal_distance);
4930 if (gray != MagickFalse)
4932 q->red=(Quantum) shade;
4933 q->green=(Quantum) shade;
4934 q->blue=(Quantum) shade;
4938 q->red=ClampToQuantum(QuantumScale*shade*s1->red);
4939 q->green=ClampToQuantum(QuantumScale*shade*s1->green);
4940 q->blue=ClampToQuantum(QuantumScale*shade*s1->blue);
4942 q->opacity=s1->opacity;
4948 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
4950 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4955 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4956 #pragma omp critical (MagickCore_ShadeImage)
4958 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
4959 if (proceed == MagickFalse)
4963 shade_view=DestroyCacheView(shade_view);
4964 image_view=DestroyCacheView(image_view);
4965 if (status == MagickFalse)
4966 shade_image=DestroyImage(shade_image);
4967 return(shade_image);
4971 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4975 % S h a r p e n I m a g e %
4979 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4981 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
4982 % operator of the given radius and standard deviation (sigma). For
4983 % reasonable results, radius should be larger than sigma. Use a radius of 0
4984 % and SharpenImage() selects a suitable radius for you.
4986 % Using a separable kernel would be faster, but the negative weights cancel
4987 % out on the corners of the kernel producing often undesirable ringing in the
4988 % filtered result; this can be avoided by using a 2D gaussian shaped image
4989 % sharpening kernel instead.
4991 % The format of the SharpenImage method is:
4993 % Image *SharpenImage(const Image *image,const double radius,
4994 % const double sigma,ExceptionInfo *exception)
4995 % Image *SharpenImageChannel(const Image *image,const ChannelType channel,
4996 % const double radius,const double sigma,ExceptionInfo *exception)
4998 % A description of each parameter follows:
5000 % o image: the image.
5002 % o channel: the channel type.
5004 % o radius: the radius of the Gaussian, in pixels, not counting the center
5007 % o sigma: the standard deviation of the Laplacian, in pixels.
5009 % o exception: return any errors or warnings in this structure.
5013 MagickExport Image *SharpenImage(const Image *image,const double radius,
5014 const double sigma,ExceptionInfo *exception)
5019 sharp_image=SharpenImageChannel(image,DefaultChannels,radius,sigma,exception);
5020 return(sharp_image);
5023 MagickExport Image *SharpenImageChannel(const Image *image,
5024 const ChannelType channel,const double radius,const double sigma,
5025 ExceptionInfo *exception)
5045 assert(image != (const Image *) NULL);
5046 assert(image->signature == MagickSignature);
5047 if (image->debug != MagickFalse)
5048 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5049 assert(exception != (ExceptionInfo *) NULL);
5050 assert(exception->signature == MagickSignature);
5051 width=GetOptimalKernelWidth2D(radius,sigma);
5052 kernel=(double *) AcquireQuantumMemory((size_t) width*width,sizeof(*kernel));
5053 if (kernel == (double *) NULL)
5054 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5058 for (v=(-j); v <= j; v++)
5060 for (u=(-j); u <= j; u++)
5062 kernel[i]=(-exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
5063 (2.0*MagickPI*MagickSigma*MagickSigma));
5064 normalize+=kernel[i];
5068 kernel[i/2]=(double) ((-2.0)*normalize);
5069 sharp_image=ConvolveImageChannel(image,channel,width,kernel,exception);
5070 kernel=(double *) RelinquishMagickMemory(kernel);
5071 return(sharp_image);
5075 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5079 % S p r e a d I m a g e %
5083 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5085 % SpreadImage() is a special effects method that randomly displaces each
5086 % pixel in a block defined by the radius parameter.
5088 % The format of the SpreadImage method is:
5090 % Image *SpreadImage(const Image *image,const double radius,
5091 % ExceptionInfo *exception)
5093 % A description of each parameter follows:
5095 % o image: the image.
5097 % o radius: Choose a random pixel in a neighborhood of this extent.
5099 % o exception: return any errors or warnings in this structure.
5102 MagickExport Image *SpreadImage(const Image *image,const double radius,
5103 ExceptionInfo *exception)
5105 #define SpreadImageTag "Spread/Image"
5124 **restrict random_info;
5127 **restrict resample_filter;
5133 Initialize spread image attributes.
5135 assert(image != (Image *) NULL);
5136 assert(image->signature == MagickSignature);
5137 if (image->debug != MagickFalse)
5138 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5139 assert(exception != (ExceptionInfo *) NULL);
5140 assert(exception->signature == MagickSignature);
5141 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
5143 if (spread_image == (Image *) NULL)
5144 return((Image *) NULL);
5145 if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
5147 InheritException(exception,&spread_image->exception);
5148 spread_image=DestroyImage(spread_image);
5149 return((Image *) NULL);
5156 GetMagickPixelPacket(spread_image,&bias);
5157 width=GetOptimalKernelWidth1D(radius,0.5);
5158 resample_filter=AcquireResampleFilterThreadSet(image,
5159 UndefinedVirtualPixelMethod,MagickTrue,exception);
5160 random_info=AcquireRandomInfoThreadSet();
5161 image_view=AcquireCacheView(spread_image);
5162 #if defined(MAGICKCORE_OPENMP_SUPPORT) && defined(MAGICKCORE_FUTURE)
5163 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5165 for (y=0; y < (long) spread_image->rows; y++)
5170 register IndexPacket
5177 register PixelPacket
5180 if (status == MagickFalse)
5182 q=QueueCacheViewAuthenticPixels(image_view,0,y,spread_image->columns,1,
5184 if (q == (PixelPacket *) NULL)
5189 indexes=GetCacheViewAuthenticIndexQueue(image_view);
5191 id=GetOpenMPThreadId();
5192 for (x=0; x < (long) spread_image->columns; x++)
5194 (void) ResamplePixelColor(resample_filter[id],(double) x+width*
5195 (GetPseudoRandomValue(random_info[id])-0.5),(double) y+width*
5196 (GetPseudoRandomValue(random_info[id])-0.5),&pixel);
5197 SetPixelPacket(spread_image,&pixel,q,indexes+x);
5200 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5202 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5207 #if defined(MAGICKCORE_OPENMP_SUPPORT) && defined(MAGICKCORE_FUTURE)
5208 #pragma omp critical (MagickCore_SpreadImage)
5210 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
5211 if (proceed == MagickFalse)
5215 image_view=DestroyCacheView(image_view);
5216 random_info=DestroyRandomInfoThreadSet(random_info);
5217 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5218 return(spread_image);
5222 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5226 % U n s h a r p M a s k I m a g e %
5230 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5232 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
5233 % image with a Gaussian operator of the given radius and standard deviation
5234 % (sigma). For reasonable results, radius should be larger than sigma. Use a
5235 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
5237 % The format of the UnsharpMaskImage method is:
5239 % Image *UnsharpMaskImage(const Image *image,const double radius,
5240 % const double sigma,const double amount,const double threshold,
5241 % ExceptionInfo *exception)
5242 % Image *UnsharpMaskImageChannel(const Image *image,
5243 % const ChannelType channel,const double radius,const double sigma,
5244 % const double amount,const double threshold,ExceptionInfo *exception)
5246 % A description of each parameter follows:
5248 % o image: the image.
5250 % o channel: the channel type.
5252 % o radius: the radius of the Gaussian, in pixels, not counting the center
5255 % o sigma: the standard deviation of the Gaussian, in pixels.
5257 % o amount: the percentage of the difference between the original and the
5258 % blur image that is added back into the original.
5260 % o threshold: the threshold in pixels needed to apply the diffence amount.
5262 % o exception: return any errors or warnings in this structure.
5266 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
5267 const double sigma,const double amount,const double threshold,
5268 ExceptionInfo *exception)
5273 sharp_image=UnsharpMaskImageChannel(image,DefaultChannels,radius,sigma,amount,
5274 threshold,exception);
5275 return(sharp_image);
5278 MagickExport Image *UnsharpMaskImageChannel(const Image *image,
5279 const ChannelType channel,const double radius,const double sigma,
5280 const double amount,const double threshold,ExceptionInfo *exception)
5282 #define SharpenImageTag "Sharpen/Image"
5304 assert(image != (const Image *) NULL);
5305 assert(image->signature == MagickSignature);
5306 if (image->debug != MagickFalse)
5307 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5308 assert(exception != (ExceptionInfo *) NULL);
5309 unsharp_image=BlurImageChannel(image,channel,radius,sigma,exception);
5310 if (unsharp_image == (Image *) NULL)
5311 return((Image *) NULL);
5312 quantum_threshold=(MagickRealType) QuantumRange*threshold;
5318 GetMagickPixelPacket(image,&bias);
5319 image_view=AcquireCacheView(image);
5320 unsharp_view=AcquireCacheView(unsharp_image);
5321 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5322 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5324 for (y=0; y < (long) image->rows; y++)
5329 register const IndexPacket
5332 register const PixelPacket
5335 register IndexPacket
5336 *restrict unsharp_indexes;
5341 register PixelPacket
5344 if (status == MagickFalse)
5346 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5347 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
5349 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5354 indexes=GetCacheViewVirtualIndexQueue(image_view);
5355 unsharp_indexes=GetCacheViewAuthenticIndexQueue(unsharp_view);
5357 for (x=0; x < (long) image->columns; x++)
5359 if ((channel & RedChannel) != 0)
5361 pixel.red=p->red-(MagickRealType) q->red;
5362 if (fabs(2.0*pixel.red) < quantum_threshold)
5363 pixel.red=(MagickRealType) GetRedPixelComponent(p);
5365 pixel.red=(MagickRealType) p->red+(pixel.red*amount);
5366 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
5368 if ((channel & GreenChannel) != 0)
5370 pixel.green=p->green-(MagickRealType) q->green;
5371 if (fabs(2.0*pixel.green) < quantum_threshold)
5372 pixel.green=(MagickRealType) GetGreenPixelComponent(p);
5374 pixel.green=(MagickRealType) p->green+(pixel.green*amount);
5375 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
5377 if ((channel & BlueChannel) != 0)
5379 pixel.blue=p->blue-(MagickRealType) q->blue;
5380 if (fabs(2.0*pixel.blue) < quantum_threshold)
5381 pixel.blue=(MagickRealType) GetBluePixelComponent(p);
5383 pixel.blue=(MagickRealType) p->blue+(pixel.blue*amount);
5384 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
5386 if ((channel & OpacityChannel) != 0)
5388 pixel.opacity=p->opacity-(MagickRealType) q->opacity;
5389 if (fabs(2.0*pixel.opacity) < quantum_threshold)
5390 pixel.opacity=(MagickRealType) GetOpacityPixelComponent(p);
5392 pixel.opacity=p->opacity+(pixel.opacity*amount);
5393 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
5395 if (((channel & IndexChannel) != 0) &&
5396 (image->colorspace == CMYKColorspace))
5398 pixel.index=unsharp_indexes[x]-(MagickRealType) indexes[x];
5399 if (fabs(2.0*pixel.index) < quantum_threshold)
5400 pixel.index=(MagickRealType) unsharp_indexes[x];
5402 pixel.index=(MagickRealType) unsharp_indexes[x]+(pixel.index*
5404 unsharp_indexes[x]=ClampToQuantum(pixel.index);
5409 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
5411 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5416 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5417 #pragma omp critical (MagickCore_UnsharpMaskImageChannel)
5419 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
5420 if (proceed == MagickFalse)
5424 unsharp_image->type=image->type;
5425 unsharp_view=DestroyCacheView(unsharp_view);
5426 image_view=DestroyCacheView(image_view);
5427 if (status == MagickFalse)
5428 unsharp_image=DestroyImage(unsharp_image);
5429 return(unsharp_image);