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) (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) (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) ((i*point.y)/hypot(point.x,point.y)+0.5);
3171 offset[i].y=(long) ((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)
3182 #pragma omp parallel for schedule(static) 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*GetAlphaPixelComponent(&pixel));
3266 qixel.red+=(*k)*alpha*pixel.red;
3267 qixel.green+=(*k)*alpha*pixel.green;
3268 qixel.blue+=(*k)*alpha*pixel.blue;
3269 qixel.opacity+=(*k)*pixel.opacity;
3270 if (image->colorspace == CMYKColorspace)
3272 indexes=GetCacheViewVirtualIndexQueue(image_view);
3273 qixel.index+=(*k)*alpha*(*indexes);
3278 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3279 if ((channel & RedChannel) != 0)
3280 q->red=ClampToQuantum(gamma*qixel.red);
3281 if ((channel & GreenChannel) != 0)
3282 q->green=ClampToQuantum(gamma*qixel.green);
3283 if ((channel & BlueChannel) != 0)
3284 q->blue=ClampToQuantum(gamma*qixel.blue);
3285 if ((channel & OpacityChannel) != 0)
3286 q->opacity=ClampToQuantum(qixel.opacity);
3287 if (((channel & IndexChannel) != 0) &&
3288 (image->colorspace == CMYKColorspace))
3289 blur_indexes[x]=(IndexPacket) ClampToQuantum(gamma*qixel.index);
3293 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3295 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3300 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3301 #pragma omp critical (MagickCore_MotionBlurImageChannel)
3303 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3304 if (proceed == MagickFalse)
3308 blur_view=DestroyCacheView(blur_view);
3309 image_view=DestroyCacheView(image_view);
3310 kernel=(double *) RelinquishMagickMemory(kernel);
3311 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3312 if (status == MagickFalse)
3313 blur_image=DestroyImage(blur_image);
3318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3322 % P r e v i e w I m a g e %
3326 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3328 % PreviewImage() tiles 9 thumbnails of the specified image with an image
3329 % processing operation applied with varying parameters. This may be helpful
3330 % pin-pointing an appropriate parameter for a particular image processing
3333 % The format of the PreviewImages method is:
3335 % Image *PreviewImages(const Image *image,const PreviewType preview,
3336 % ExceptionInfo *exception)
3338 % A description of each parameter follows:
3340 % o image: the image.
3342 % o preview: the image processing operation.
3344 % o exception: return any errors or warnings in this structure.
3347 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
3348 ExceptionInfo *exception)
3350 #define NumberTiles 9
3351 #define PreviewImageTag "Preview/Image"
3352 #define DefaultPreviewGeometry "204x204+10+10"
3355 factor[MaxTextExtent],
3356 label[MaxTextExtent];
3398 Open output image file.
3400 assert(image != (Image *) NULL);
3401 assert(image->signature == MagickSignature);
3402 if (image->debug != MagickFalse)
3403 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3407 preview_info=AcquireImageInfo();
3408 SetGeometry(image,&geometry);
3409 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
3410 &geometry.width,&geometry.height);
3411 images=NewImageList();
3413 GetQuantizeInfo(&quantize_info);
3419 for (i=0; i < NumberTiles; i++)
3421 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
3422 if (thumbnail == (Image *) NULL)
3424 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
3426 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
3427 if (i == (NumberTiles/2))
3429 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
3430 AppendImageToList(&images,thumbnail);
3438 preview_image=RotateImage(thumbnail,degrees,exception);
3439 (void) FormatMagickString(label,MaxTextExtent,"rotate %g",degrees);
3445 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
3446 (void) FormatMagickString(label,MaxTextExtent,"shear %gx%g",
3447 degrees,2.0*degrees);
3452 x=(long) ((i+1)*thumbnail->columns)/NumberTiles;
3453 y=(long) ((i+1)*thumbnail->rows)/NumberTiles;
3454 preview_image=RollImage(thumbnail,x,y,exception);
3455 (void) FormatMagickString(label,MaxTextExtent,"roll %ldx%ld",x,y);
3460 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3461 if (preview_image == (Image *) NULL)
3463 (void) FormatMagickString(factor,MaxTextExtent,"100,100,%g",
3465 (void) ModulateImage(preview_image,factor);
3466 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3469 case SaturationPreview:
3471 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3472 if (preview_image == (Image *) NULL)
3474 (void) FormatMagickString(factor,MaxTextExtent,"100,%g",
3476 (void) ModulateImage(preview_image,factor);
3477 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3480 case BrightnessPreview:
3482 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3483 if (preview_image == (Image *) NULL)
3485 (void) FormatMagickString(factor,MaxTextExtent,"%g",2.0*percentage);
3486 (void) ModulateImage(preview_image,factor);
3487 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3493 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3494 if (preview_image == (Image *) NULL)
3497 (void) GammaImageChannel(preview_image,DefaultChannels,gamma);
3498 (void) FormatMagickString(label,MaxTextExtent,"gamma %g",gamma);
3503 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3504 if (preview_image != (Image *) NULL)
3505 for (x=0; x < i; x++)
3506 (void) ContrastImage(preview_image,MagickTrue);
3507 (void) FormatMagickString(label,MaxTextExtent,"contrast (%ld)",i+1);
3512 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3513 if (preview_image == (Image *) NULL)
3515 for (x=0; x < i; x++)
3516 (void) ContrastImage(preview_image,MagickFalse);
3517 (void) FormatMagickString(label,MaxTextExtent,"+contrast (%ld)",i+1);
3520 case GrayscalePreview:
3522 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3523 if (preview_image == (Image *) NULL)
3526 quantize_info.number_colors=colors;
3527 quantize_info.colorspace=GRAYColorspace;
3528 (void) QuantizeImage(&quantize_info,preview_image);
3529 (void) FormatMagickString(label,MaxTextExtent,
3530 "-colorspace gray -colors %ld",colors);
3533 case QuantizePreview:
3535 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3536 if (preview_image == (Image *) NULL)
3539 quantize_info.number_colors=colors;
3540 (void) QuantizeImage(&quantize_info,preview_image);
3541 (void) FormatMagickString(label,MaxTextExtent,"colors %ld",colors);
3544 case DespecklePreview:
3546 for (x=0; x < (i-1); x++)
3548 preview_image=DespeckleImage(thumbnail,exception);
3549 if (preview_image == (Image *) NULL)
3551 thumbnail=DestroyImage(thumbnail);
3552 thumbnail=preview_image;
3554 preview_image=DespeckleImage(thumbnail,exception);
3555 if (preview_image == (Image *) NULL)
3557 (void) FormatMagickString(label,MaxTextExtent,"despeckle (%ld)",i+1);
3560 case ReduceNoisePreview:
3562 preview_image=ReduceNoiseImage(thumbnail,radius,exception);
3563 (void) FormatMagickString(label,MaxTextExtent,"noise %g",radius);
3566 case AddNoisePreview:
3572 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
3577 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
3582 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
3587 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
3592 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
3597 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
3602 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
3606 preview_image=ReduceNoiseImage(thumbnail,(double) i,exception);
3607 (void) FormatMagickString(label,MaxTextExtent,"+noise %s",factor);
3610 case SharpenPreview:
3612 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
3613 (void) FormatMagickString(label,MaxTextExtent,"sharpen %gx%g",
3619 preview_image=BlurImage(thumbnail,radius,sigma,exception);
3620 (void) FormatMagickString(label,MaxTextExtent,"blur %gx%g",radius,
3624 case ThresholdPreview:
3626 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3627 if (preview_image == (Image *) NULL)
3629 (void) BilevelImage(thumbnail,
3630 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3631 (void) FormatMagickString(label,MaxTextExtent,"threshold %g",
3632 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3635 case EdgeDetectPreview:
3637 preview_image=EdgeImage(thumbnail,radius,exception);
3638 (void) FormatMagickString(label,MaxTextExtent,"edge %g",radius);
3643 preview_image=SpreadImage(thumbnail,radius,exception);
3644 (void) FormatMagickString(label,MaxTextExtent,"spread %g",
3648 case SolarizePreview:
3650 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3651 if (preview_image == (Image *) NULL)
3653 (void) SolarizeImage(preview_image,(double) QuantumRange*
3655 (void) FormatMagickString(label,MaxTextExtent,"solarize %g",
3656 (QuantumRange*percentage)/100.0);
3662 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
3664 (void) FormatMagickString(label,MaxTextExtent,"shade %gx%g",
3670 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3671 if (preview_image == (Image *) NULL)
3673 geometry.width=(unsigned long) (2*i+2);
3674 geometry.height=(unsigned long) (2*i+2);
3677 (void) RaiseImage(preview_image,&geometry,MagickTrue);
3678 (void) FormatMagickString(label,MaxTextExtent,"raise %lux%lu%+ld%+ld",
3679 geometry.width,geometry.height,geometry.x,geometry.y);
3682 case SegmentPreview:
3684 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3685 if (preview_image == (Image *) NULL)
3688 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
3690 (void) FormatMagickString(label,MaxTextExtent,"segment %gx%g",
3691 threshold,threshold);
3696 preview_image=SwirlImage(thumbnail,degrees,exception);
3697 (void) FormatMagickString(label,MaxTextExtent,"swirl %g",degrees);
3701 case ImplodePreview:
3704 preview_image=ImplodeImage(thumbnail,degrees,exception);
3705 (void) FormatMagickString(label,MaxTextExtent,"implode %g",degrees);
3711 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
3712 (void) FormatMagickString(label,MaxTextExtent,"wave %gx%g",
3713 0.5*degrees,2.0*degrees);
3716 case OilPaintPreview:
3718 preview_image=OilPaintImage(thumbnail,(double) radius,exception);
3719 (void) FormatMagickString(label,MaxTextExtent,"paint %g",radius);
3722 case CharcoalDrawingPreview:
3724 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
3726 (void) FormatMagickString(label,MaxTextExtent,"charcoal %gx%g",
3733 filename[MaxTextExtent];
3741 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3742 if (preview_image == (Image *) NULL)
3744 preview_info->quality=(unsigned long) percentage;
3745 (void) FormatMagickString(factor,MaxTextExtent,"%lu",
3746 preview_info->quality);
3747 file=AcquireUniqueFileResource(filename);
3750 (void) FormatMagickString(preview_image->filename,MaxTextExtent,
3751 "jpeg:%s",filename);
3752 status=WriteImage(preview_info,preview_image);
3753 if (status != MagickFalse)
3758 (void) CopyMagickString(preview_info->filename,
3759 preview_image->filename,MaxTextExtent);
3760 quality_image=ReadImage(preview_info,exception);
3761 if (quality_image != (Image *) NULL)
3763 preview_image=DestroyImage(preview_image);
3764 preview_image=quality_image;
3767 (void) RelinquishUniqueFileResource(preview_image->filename);
3768 if ((GetBlobSize(preview_image)/1024) >= 1024)
3769 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gmb ",
3770 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
3773 if (GetBlobSize(preview_image) >= 1024)
3774 (void) FormatMagickString(label,MaxTextExtent,
3775 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
3776 GetBlobSize(preview_image))/1024.0);
3778 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%lub ",
3779 factor,(unsigned long) GetBlobSize(thumbnail));
3783 thumbnail=DestroyImage(thumbnail);
3787 if (preview_image == (Image *) NULL)
3789 (void) DeleteImageProperty(preview_image,"label");
3790 (void) SetImageProperty(preview_image,"label",label);
3791 AppendImageToList(&images,preview_image);
3792 proceed=SetImageProgress(image,PreviewImageTag,i,NumberTiles);
3793 if (proceed == MagickFalse)
3796 if (images == (Image *) NULL)
3798 preview_info=DestroyImageInfo(preview_info);
3799 return((Image *) NULL);
3804 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
3805 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
3806 montage_info->shadow=MagickTrue;
3807 (void) CloneString(&montage_info->tile,"3x3");
3808 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
3809 (void) CloneString(&montage_info->frame,DefaultTileFrame);
3810 montage_image=MontageImages(images,montage_info,exception);
3811 montage_info=DestroyMontageInfo(montage_info);
3812 images=DestroyImageList(images);
3813 if (montage_image == (Image *) NULL)
3814 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3815 if (montage_image->montage != (char *) NULL)
3818 Free image directory.
3820 montage_image->montage=(char *) RelinquishMagickMemory(
3821 montage_image->montage);
3822 if (image->directory != (char *) NULL)
3823 montage_image->directory=(char *) RelinquishMagickMemory(
3824 montage_image->directory);
3826 preview_info=DestroyImageInfo(preview_info);
3827 return(montage_image);
3831 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3835 % R a d i a l B l u r I m a g e %
3839 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3841 % RadialBlurImage() applies a radial blur to the image.
3843 % Andrew Protano contributed this effect.
3845 % The format of the RadialBlurImage method is:
3847 % Image *RadialBlurImage(const Image *image,const double angle,
3848 % ExceptionInfo *exception)
3849 % Image *RadialBlurImageChannel(const Image *image,const ChannelType channel,
3850 % const double angle,ExceptionInfo *exception)
3852 % A description of each parameter follows:
3854 % o image: the image.
3856 % o channel: the channel type.
3858 % o angle: the angle of the radial blur.
3860 % o exception: return any errors or warnings in this structure.
3864 MagickExport Image *RadialBlurImage(const Image *image,const double angle,
3865 ExceptionInfo *exception)
3870 blur_image=RadialBlurImageChannel(image,DefaultChannels,angle,exception);
3874 MagickExport Image *RadialBlurImageChannel(const Image *image,
3875 const ChannelType channel,const double angle,ExceptionInfo *exception)
3911 Allocate blur image.
3913 assert(image != (Image *) NULL);
3914 assert(image->signature == MagickSignature);
3915 if (image->debug != MagickFalse)
3916 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3917 assert(exception != (ExceptionInfo *) NULL);
3918 assert(exception->signature == MagickSignature);
3919 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3920 if (blur_image == (Image *) NULL)
3921 return((Image *) NULL);
3922 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3924 InheritException(exception,&blur_image->exception);
3925 blur_image=DestroyImage(blur_image);
3926 return((Image *) NULL);
3928 blur_center.x=(double) image->columns/2.0;
3929 blur_center.y=(double) image->rows/2.0;
3930 blur_radius=hypot(blur_center.x,blur_center.y);
3931 n=(unsigned long) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+
3933 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
3934 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3935 sizeof(*cos_theta));
3936 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3937 sizeof(*sin_theta));
3938 if ((cos_theta == (MagickRealType *) NULL) ||
3939 (sin_theta == (MagickRealType *) NULL))
3941 blur_image=DestroyImage(blur_image);
3942 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3944 offset=theta*(MagickRealType) (n-1)/2.0;
3945 for (i=0; i < (long) n; i++)
3947 cos_theta[i]=cos((double) (theta*i-offset));
3948 sin_theta[i]=sin((double) (theta*i-offset));
3955 GetMagickPixelPacket(image,&bias);
3956 image_view=AcquireCacheView(image);
3957 blur_view=AcquireCacheView(blur_image);
3958 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3959 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3961 for (y=0; y < (long) blur_image->rows; y++)
3963 register const IndexPacket
3966 register IndexPacket
3967 *restrict blur_indexes;
3972 register PixelPacket
3975 if (status == MagickFalse)
3977 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3979 if (q == (PixelPacket *) NULL)
3984 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
3985 for (x=0; x < (long) blur_image->columns; x++)
4006 center.x=(double) x-blur_center.x;
4007 center.y=(double) y-blur_center.y;
4008 radius=hypot((double) center.x,center.y);
4013 step=(unsigned long) (blur_radius/radius);
4022 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
4024 for (i=0; i < (long) n; i+=step)
4026 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
4027 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
4028 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
4030 qixel.red+=pixel.red;
4031 qixel.green+=pixel.green;
4032 qixel.blue+=pixel.blue;
4033 qixel.opacity+=pixel.opacity;
4034 if (image->colorspace == CMYKColorspace)
4036 indexes=GetCacheViewVirtualIndexQueue(image_view);
4037 qixel.index+=(*indexes);
4041 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
4043 if ((channel & RedChannel) != 0)
4044 q->red=ClampToQuantum(normalize*qixel.red);
4045 if ((channel & GreenChannel) != 0)
4046 q->green=ClampToQuantum(normalize*qixel.green);
4047 if ((channel & BlueChannel) != 0)
4048 q->blue=ClampToQuantum(normalize*qixel.blue);
4049 if ((channel & OpacityChannel) != 0)
4050 q->opacity=ClampToQuantum(normalize*qixel.opacity);
4051 if (((channel & IndexChannel) != 0) &&
4052 (image->colorspace == CMYKColorspace))
4053 blur_indexes[x]=(IndexPacket) ClampToQuantum(normalize*qixel.index);
4063 for (i=0; i < (long) n; i+=step)
4065 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
4066 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
4067 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
4069 alpha=(MagickRealType) (QuantumScale*
4070 GetAlphaPixelComponent(&pixel));
4071 qixel.red+=alpha*pixel.red;
4072 qixel.green+=alpha*pixel.green;
4073 qixel.blue+=alpha*pixel.blue;
4074 qixel.opacity+=pixel.opacity;
4075 if (image->colorspace == CMYKColorspace)
4077 indexes=GetCacheViewVirtualIndexQueue(image_view);
4078 qixel.index+=alpha*(*indexes);
4083 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4084 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
4086 if ((channel & RedChannel) != 0)
4087 q->red=ClampToQuantum(gamma*qixel.red);
4088 if ((channel & GreenChannel) != 0)
4089 q->green=ClampToQuantum(gamma*qixel.green);
4090 if ((channel & BlueChannel) != 0)
4091 q->blue=ClampToQuantum(gamma*qixel.blue);
4092 if ((channel & OpacityChannel) != 0)
4093 q->opacity=ClampToQuantum(normalize*qixel.opacity);
4094 if (((channel & IndexChannel) != 0) &&
4095 (image->colorspace == CMYKColorspace))
4096 blur_indexes[x]=(IndexPacket) ClampToQuantum(gamma*qixel.index);
4100 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
4102 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4107 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4108 #pragma omp critical (MagickCore_RadialBlurImageChannel)
4110 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
4111 if (proceed == MagickFalse)
4115 blur_view=DestroyCacheView(blur_view);
4116 image_view=DestroyCacheView(image_view);
4117 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
4118 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
4119 if (status == MagickFalse)
4120 blur_image=DestroyImage(blur_image);
4125 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4129 % R e d u c e N o i s e I m a g e %
4133 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4135 % ReduceNoiseImage() smooths the contours of an image while still preserving
4136 % edge information. The algorithm works by replacing each pixel with its
4137 % neighbor closest in value. A neighbor is defined by radius. Use a radius
4138 % of 0 and ReduceNoise() selects a suitable radius for you.
4140 % The format of the ReduceNoiseImage method is:
4142 % Image *ReduceNoiseImage(const Image *image,const double radius,
4143 % ExceptionInfo *exception)
4145 % A description of each parameter follows:
4147 % o image: the image.
4149 % o radius: the radius of the pixel neighborhood.
4151 % o exception: return any errors or warnings in this structure.
4155 static MagickPixelPacket GetNonpeakMedianPixelList(MedianPixelList *pixel_list)
4163 register MedianSkipList
4177 Finds the median value for each of the color.
4179 center=pixel_list->center;
4180 for (channel=0; channel < 5; channel++)
4182 list=pixel_list->lists+channel;
4184 next=list->nodes[color].next[0];
4190 next=list->nodes[color].next[0];
4191 count+=list->nodes[color].count;
4193 while (count <= center);
4194 if ((previous == 65536UL) && (next != 65536UL))
4197 if ((previous != 65536UL) && (next == 65536UL))
4199 channels[channel]=(unsigned short) color;
4201 GetMagickPixelPacket((const Image *) NULL,&pixel);
4202 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4203 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4204 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
4205 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
4206 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
4210 MagickExport Image *ReduceNoiseImage(const Image *image,const double radius,
4211 ExceptionInfo *exception)
4213 #define ReduceNoiseImageTag "ReduceNoise/Image"
4230 **restrict pixel_list;
4236 Initialize noise image attributes.
4238 assert(image != (Image *) NULL);
4239 assert(image->signature == MagickSignature);
4240 if (image->debug != MagickFalse)
4241 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4242 assert(exception != (ExceptionInfo *) NULL);
4243 assert(exception->signature == MagickSignature);
4244 width=GetOptimalKernelWidth2D(radius,0.5);
4245 if ((image->columns < width) || (image->rows < width))
4246 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
4247 noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4249 if (noise_image == (Image *) NULL)
4250 return((Image *) NULL);
4251 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
4253 InheritException(exception,&noise_image->exception);
4254 noise_image=DestroyImage(noise_image);
4255 return((Image *) NULL);
4257 pixel_list=AcquireMedianPixelListThreadSet(width);
4258 if (pixel_list == (MedianPixelList **) NULL)
4260 noise_image=DestroyImage(noise_image);
4261 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4268 image_view=AcquireCacheView(image);
4269 noise_view=AcquireCacheView(noise_image);
4270 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4271 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4273 for (y=0; y < (long) noise_image->rows; y++)
4275 register const IndexPacket
4278 register const PixelPacket
4281 register IndexPacket
4282 *restrict noise_indexes;
4288 register PixelPacket
4291 if (status == MagickFalse)
4293 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
4294 2L),image->columns+width,width,exception);
4295 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
4297 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4302 indexes=GetCacheViewVirtualIndexQueue(image_view);
4303 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
4304 id=GetOpenMPThreadId();
4305 for (x=0; x < (long) noise_image->columns; x++)
4310 register const PixelPacket
4313 register const IndexPacket
4322 ResetMedianPixelList(pixel_list[id]);
4323 for (v=0; v < (long) width; v++)
4325 for (u=0; u < (long) width; u++)
4326 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
4327 r+=image->columns+width;
4328 s+=image->columns+width;
4330 pixel=GetNonpeakMedianPixelList(pixel_list[id]);
4331 SetPixelPacket(noise_image,&pixel,q,noise_indexes+x);
4335 if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
4337 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4342 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4343 #pragma omp critical (MagickCore_ReduceNoiseImage)
4345 proceed=SetImageProgress(image,ReduceNoiseImageTag,progress++,
4347 if (proceed == MagickFalse)
4351 noise_view=DestroyCacheView(noise_view);
4352 image_view=DestroyCacheView(image_view);
4353 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
4354 return(noise_image);
4358 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4362 % S e l e c t i v e B l u r I m a g e %
4366 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4368 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
4369 % It is similar to the unsharpen mask that sharpens everything with contrast
4370 % above a certain threshold.
4372 % The format of the SelectiveBlurImage method is:
4374 % Image *SelectiveBlurImage(const Image *image,const double radius,
4375 % const double sigma,const double threshold,ExceptionInfo *exception)
4376 % Image *SelectiveBlurImageChannel(const Image *image,
4377 % const ChannelType channel,const double radius,const double sigma,
4378 % const double threshold,ExceptionInfo *exception)
4380 % A description of each parameter follows:
4382 % o image: the image.
4384 % o channel: the channel type.
4386 % o radius: the radius of the Gaussian, in pixels, not counting the center
4389 % o sigma: the standard deviation of the Gaussian, in pixels.
4391 % o threshold: only pixels within this contrast threshold are included
4392 % in the blur operation.
4394 % o exception: return any errors or warnings in this structure.
4398 static inline MagickBooleanType SelectiveContrast(const PixelPacket *p,
4399 const PixelPacket *q,const double threshold)
4401 if (fabs(PixelIntensity(p)-PixelIntensity(q)) < threshold)
4403 return(MagickFalse);
4406 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
4407 const double sigma,const double threshold,ExceptionInfo *exception)
4412 blur_image=SelectiveBlurImageChannel(image,DefaultChannels,radius,sigma,
4413 threshold,exception);
4417 MagickExport Image *SelectiveBlurImageChannel(const Image *image,
4418 const ChannelType channel,const double radius,const double sigma,
4419 const double threshold,ExceptionInfo *exception)
4421 #define SelectiveBlurImageTag "SelectiveBlur/Image"
4453 Initialize blur image attributes.
4455 assert(image != (Image *) NULL);
4456 assert(image->signature == MagickSignature);
4457 if (image->debug != MagickFalse)
4458 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4459 assert(exception != (ExceptionInfo *) NULL);
4460 assert(exception->signature == MagickSignature);
4461 width=GetOptimalKernelWidth1D(radius,sigma);
4462 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
4463 if (kernel == (double *) NULL)
4464 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4467 for (v=(-j); v <= j; v++)
4469 for (u=(-j); u <= j; u++)
4470 kernel[i++]=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
4471 (2.0*MagickPI*MagickSigma*MagickSigma);
4473 if (image->debug != MagickFalse)
4476 format[MaxTextExtent],
4483 register const double
4486 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
4487 " SelectiveBlurImage with %ldx%ld kernel:",width,width);
4488 message=AcquireString("");
4490 for (v=0; v < (long) width; v++)
4493 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
4494 (void) ConcatenateString(&message,format);
4495 for (u=0; u < (long) width; u++)
4497 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
4498 (void) ConcatenateString(&message,format);
4500 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
4502 message=DestroyString(message);
4504 blur_image=CloneImage(image,0,0,MagickTrue,exception);
4505 if (blur_image == (Image *) NULL)
4506 return((Image *) NULL);
4507 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
4509 InheritException(exception,&blur_image->exception);
4510 blur_image=DestroyImage(blur_image);
4511 return((Image *) NULL);
4514 Threshold blur image.
4518 GetMagickPixelPacket(image,&bias);
4519 SetMagickPixelPacketBias(image,&bias);
4520 image_view=AcquireCacheView(image);
4521 blur_view=AcquireCacheView(blur_image);
4522 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4523 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4525 for (y=0; y < (long) image->rows; y++)
4533 register const IndexPacket
4536 register const PixelPacket
4539 register IndexPacket
4540 *restrict blur_indexes;
4545 register PixelPacket
4548 if (status == MagickFalse)
4550 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
4551 2L),image->columns+width,width,exception);
4552 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
4554 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4559 indexes=GetCacheViewVirtualIndexQueue(image_view);
4560 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
4561 for (x=0; x < (long) image->columns; x++)
4570 register const double
4580 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
4582 for (v=0; v < (long) width; v++)
4584 for (u=0; u < (long) width; u++)
4586 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4588 pixel.red+=(*k)*(p+u+j)->red;
4589 pixel.green+=(*k)*(p+u+j)->green;
4590 pixel.blue+=(*k)*(p+u+j)->blue;
4595 j+=image->columns+width;
4599 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4600 if ((channel & RedChannel) != 0)
4601 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
4602 if ((channel & GreenChannel) != 0)
4603 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
4604 if ((channel & BlueChannel) != 0)
4605 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
4607 if ((channel & OpacityChannel) != 0)
4611 for (v=0; v < (long) width; v++)
4613 for (u=0; u < (long) width; u++)
4615 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4617 pixel.opacity+=(*k)*(p+u+j)->opacity;
4622 j+=image->columns+width;
4626 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4628 SetOpacityPixelComponent(q,ClampToQuantum(gamma*
4629 GetOpacityPixelComponent(&pixel)));
4632 if (((channel & IndexChannel) != 0) &&
4633 (image->colorspace == CMYKColorspace))
4637 for (v=0; v < (long) width; v++)
4639 for (u=0; u < (long) width; u++)
4641 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4643 pixel.index+=(*k)*indexes[x+u+j];
4648 j+=image->columns+width;
4652 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4654 blur_indexes[x]=ClampToQuantum(gamma*
4655 GetIndexPixelComponent(&pixel));
4664 for (v=0; v < (long) width; v++)
4666 for (u=0; u < (long) width; u++)
4668 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4670 alpha=(MagickRealType) (QuantumScale*
4671 GetAlphaPixelComponent(p+u+j));
4672 pixel.red+=(*k)*alpha*(p+u+j)->red;
4673 pixel.green+=(*k)*alpha*(p+u+j)->green;
4674 pixel.blue+=(*k)*alpha*(p+u+j)->blue;
4675 pixel.opacity+=(*k)*(p+u+j)->opacity;
4680 j+=image->columns+width;
4684 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4685 if ((channel & RedChannel) != 0)
4686 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
4687 if ((channel & GreenChannel) != 0)
4688 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
4689 if ((channel & BlueChannel) != 0)
4690 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
4692 if ((channel & OpacityChannel) != 0)
4696 for (v=0; v < (long) width; v++)
4698 for (u=0; u < (long) width; u++)
4700 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4702 pixel.opacity+=(*k)*(p+u+j)->opacity;
4707 j+=image->columns+width;
4711 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4713 SetOpacityPixelComponent(q,
4714 ClampOpacityPixelComponent(&pixel));
4717 if (((channel & IndexChannel) != 0) &&
4718 (image->colorspace == CMYKColorspace))
4722 for (v=0; v < (long) width; v++)
4724 for (u=0; u < (long) width; u++)
4726 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4728 alpha=(MagickRealType) (QuantumScale*
4729 GetAlphaPixelComponent(p+u+j));
4730 pixel.index+=(*k)*alpha*indexes[x+u+j];
4735 j+=image->columns+width;
4739 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4741 blur_indexes[x]=ClampToQuantum(gamma*
4742 GetIndexPixelComponent(&pixel));
4749 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
4750 if (sync == MagickFalse)
4752 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4757 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4758 #pragma omp critical (MagickCore_SelectiveBlurImageChannel)
4760 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
4762 if (proceed == MagickFalse)
4766 blur_image->type=image->type;
4767 blur_view=DestroyCacheView(blur_view);
4768 image_view=DestroyCacheView(image_view);
4769 kernel=(double *) RelinquishMagickMemory(kernel);
4770 if (status == MagickFalse)
4771 blur_image=DestroyImage(blur_image);
4776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4780 % S h a d e I m a g e %
4784 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4786 % ShadeImage() shines a distant light on an image to create a
4787 % three-dimensional effect. You control the positioning of the light with
4788 % azimuth and elevation; azimuth is measured in degrees off the x axis
4789 % and elevation is measured in pixels above the Z axis.
4791 % The format of the ShadeImage method is:
4793 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4794 % const double azimuth,const double elevation,ExceptionInfo *exception)
4796 % A description of each parameter follows:
4798 % o image: the image.
4800 % o gray: A value other than zero shades the intensity of each pixel.
4802 % o azimuth, elevation: Define the light source direction.
4804 % o exception: return any errors or warnings in this structure.
4807 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4808 const double azimuth,const double elevation,ExceptionInfo *exception)
4810 #define ShadeImageTag "Shade/Image"
4830 Initialize shaded image attributes.
4832 assert(image != (const Image *) NULL);
4833 assert(image->signature == MagickSignature);
4834 if (image->debug != MagickFalse)
4835 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4836 assert(exception != (ExceptionInfo *) NULL);
4837 assert(exception->signature == MagickSignature);
4838 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4839 if (shade_image == (Image *) NULL)
4840 return((Image *) NULL);
4841 if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
4843 InheritException(exception,&shade_image->exception);
4844 shade_image=DestroyImage(shade_image);
4845 return((Image *) NULL);
4848 Compute the light vector.
4850 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
4851 cos(DegreesToRadians(elevation));
4852 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
4853 cos(DegreesToRadians(elevation));
4854 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
4860 image_view=AcquireCacheView(image);
4861 shade_view=AcquireCacheView(shade_image);
4862 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4863 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4865 for (y=0; y < (long) image->rows; y++)
4875 register const PixelPacket
4884 register PixelPacket
4887 if (status == MagickFalse)
4889 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
4890 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
4892 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4898 Shade this row of pixels.
4900 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
4902 s1=s0+image->columns+2;
4903 s2=s1+image->columns+2;
4904 for (x=0; x < (long) image->columns; x++)
4907 Determine the surface normal and compute shading.
4909 normal.x=(double) (PixelIntensity(s0-1)+PixelIntensity(s1-1)+
4910 PixelIntensity(s2-1)-PixelIntensity(s0+1)-PixelIntensity(s1+1)-
4911 PixelIntensity(s2+1));
4912 normal.y=(double) (PixelIntensity(s2-1)+PixelIntensity(s2)+
4913 PixelIntensity(s2+1)-PixelIntensity(s0-1)-PixelIntensity(s0)-
4914 PixelIntensity(s0+1));
4915 if ((normal.x == 0.0) && (normal.y == 0.0))
4920 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
4921 if (distance > MagickEpsilon)
4924 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
4925 if (normal_distance > (MagickEpsilon*MagickEpsilon))
4926 shade=distance/sqrt((double) normal_distance);
4929 if (gray != MagickFalse)
4931 q->red=(Quantum) shade;
4932 q->green=(Quantum) shade;
4933 q->blue=(Quantum) shade;
4937 q->red=ClampToQuantum(QuantumScale*shade*s1->red);
4938 q->green=ClampToQuantum(QuantumScale*shade*s1->green);
4939 q->blue=ClampToQuantum(QuantumScale*shade*s1->blue);
4941 q->opacity=s1->opacity;
4947 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
4949 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4954 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4955 #pragma omp critical (MagickCore_ShadeImage)
4957 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
4958 if (proceed == MagickFalse)
4962 shade_view=DestroyCacheView(shade_view);
4963 image_view=DestroyCacheView(image_view);
4964 if (status == MagickFalse)
4965 shade_image=DestroyImage(shade_image);
4966 return(shade_image);
4970 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4974 % S h a r p e n I m a g e %
4978 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4980 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
4981 % operator of the given radius and standard deviation (sigma). For
4982 % reasonable results, radius should be larger than sigma. Use a radius of 0
4983 % and SharpenImage() selects a suitable radius for you.
4985 % Using a separable kernel would be faster, but the negative weights cancel
4986 % out on the corners of the kernel producing often undesirable ringing in the
4987 % filtered result; this can be avoided by using a 2D gaussian shaped image
4988 % sharpening kernel instead.
4990 % The format of the SharpenImage method is:
4992 % Image *SharpenImage(const Image *image,const double radius,
4993 % const double sigma,ExceptionInfo *exception)
4994 % Image *SharpenImageChannel(const Image *image,const ChannelType channel,
4995 % const double radius,const double sigma,ExceptionInfo *exception)
4997 % A description of each parameter follows:
4999 % o image: the image.
5001 % o channel: the channel type.
5003 % o radius: the radius of the Gaussian, in pixels, not counting the center
5006 % o sigma: the standard deviation of the Laplacian, in pixels.
5008 % o exception: return any errors or warnings in this structure.
5012 MagickExport Image *SharpenImage(const Image *image,const double radius,
5013 const double sigma,ExceptionInfo *exception)
5018 sharp_image=SharpenImageChannel(image,DefaultChannels,radius,sigma,exception);
5019 return(sharp_image);
5022 MagickExport Image *SharpenImageChannel(const Image *image,
5023 const ChannelType channel,const double radius,const double sigma,
5024 ExceptionInfo *exception)
5044 assert(image != (const Image *) NULL);
5045 assert(image->signature == MagickSignature);
5046 if (image->debug != MagickFalse)
5047 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5048 assert(exception != (ExceptionInfo *) NULL);
5049 assert(exception->signature == MagickSignature);
5050 width=GetOptimalKernelWidth2D(radius,sigma);
5051 kernel=(double *) AcquireQuantumMemory((size_t) width*width,sizeof(*kernel));
5052 if (kernel == (double *) NULL)
5053 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5057 for (v=(-j); v <= j; v++)
5059 for (u=(-j); u <= j; u++)
5061 kernel[i]=(-exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
5062 (2.0*MagickPI*MagickSigma*MagickSigma));
5063 normalize+=kernel[i];
5067 kernel[i/2]=(double) ((-2.0)*normalize);
5068 sharp_image=ConvolveImageChannel(image,channel,width,kernel,exception);
5069 kernel=(double *) RelinquishMagickMemory(kernel);
5070 return(sharp_image);
5074 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5078 % S p r e a d I m a g e %
5082 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5084 % SpreadImage() is a special effects method that randomly displaces each
5085 % pixel in a block defined by the radius parameter.
5087 % The format of the SpreadImage method is:
5089 % Image *SpreadImage(const Image *image,const double radius,
5090 % ExceptionInfo *exception)
5092 % A description of each parameter follows:
5094 % o image: the image.
5096 % o radius: Choose a random pixel in a neighborhood of this extent.
5098 % o exception: return any errors or warnings in this structure.
5101 MagickExport Image *SpreadImage(const Image *image,const double radius,
5102 ExceptionInfo *exception)
5104 #define SpreadImageTag "Spread/Image"
5123 **restrict random_info;
5126 **restrict resample_filter;
5132 Initialize spread image attributes.
5134 assert(image != (Image *) NULL);
5135 assert(image->signature == MagickSignature);
5136 if (image->debug != MagickFalse)
5137 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5138 assert(exception != (ExceptionInfo *) NULL);
5139 assert(exception->signature == MagickSignature);
5140 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
5142 if (spread_image == (Image *) NULL)
5143 return((Image *) NULL);
5144 if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
5146 InheritException(exception,&spread_image->exception);
5147 spread_image=DestroyImage(spread_image);
5148 return((Image *) NULL);
5155 GetMagickPixelPacket(spread_image,&bias);
5156 width=GetOptimalKernelWidth1D(radius,0.5);
5157 resample_filter=AcquireResampleFilterThreadSet(image,
5158 UndefinedVirtualPixelMethod,MagickTrue,exception);
5159 random_info=AcquireRandomInfoThreadSet();
5160 image_view=AcquireCacheView(spread_image);
5161 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5162 #pragma omp parallel for schedule(static) shared(progress,status)
5164 for (y=0; y < (long) spread_image->rows; y++)
5169 register IndexPacket
5176 register PixelPacket
5179 if (status == MagickFalse)
5181 q=QueueCacheViewAuthenticPixels(image_view,0,y,spread_image->columns,1,
5183 if (q == (PixelPacket *) NULL)
5188 indexes=GetCacheViewAuthenticIndexQueue(image_view);
5190 id=GetOpenMPThreadId();
5191 for (x=0; x < (long) spread_image->columns; x++)
5193 (void) ResamplePixelColor(resample_filter[id],(double) x+width*
5194 (GetPseudoRandomValue(random_info[id])-0.5),(double) y+width*
5195 (GetPseudoRandomValue(random_info[id])-0.5),&pixel);
5196 SetPixelPacket(spread_image,&pixel,q,indexes+x);
5199 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5201 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5206 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5207 #pragma omp critical (MagickCore_SpreadImage)
5209 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
5210 if (proceed == MagickFalse)
5214 image_view=DestroyCacheView(image_view);
5215 random_info=DestroyRandomInfoThreadSet(random_info);
5216 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5217 return(spread_image);
5221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5225 % U n s h a r p M a s k I m a g e %
5229 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5231 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
5232 % image with a Gaussian operator of the given radius and standard deviation
5233 % (sigma). For reasonable results, radius should be larger than sigma. Use a
5234 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
5236 % The format of the UnsharpMaskImage method is:
5238 % Image *UnsharpMaskImage(const Image *image,const double radius,
5239 % const double sigma,const double amount,const double threshold,
5240 % ExceptionInfo *exception)
5241 % Image *UnsharpMaskImageChannel(const Image *image,
5242 % const ChannelType channel,const double radius,const double sigma,
5243 % const double amount,const double threshold,ExceptionInfo *exception)
5245 % A description of each parameter follows:
5247 % o image: the image.
5249 % o channel: the channel type.
5251 % o radius: the radius of the Gaussian, in pixels, not counting the center
5254 % o sigma: the standard deviation of the Gaussian, in pixels.
5256 % o amount: the percentage of the difference between the original and the
5257 % blur image that is added back into the original.
5259 % o threshold: the threshold in pixels needed to apply the diffence amount.
5261 % o exception: return any errors or warnings in this structure.
5265 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
5266 const double sigma,const double amount,const double threshold,
5267 ExceptionInfo *exception)
5272 sharp_image=UnsharpMaskImageChannel(image,DefaultChannels,radius,sigma,amount,
5273 threshold,exception);
5274 return(sharp_image);
5277 MagickExport Image *UnsharpMaskImageChannel(const Image *image,
5278 const ChannelType channel,const double radius,const double sigma,
5279 const double amount,const double threshold,ExceptionInfo *exception)
5281 #define SharpenImageTag "Sharpen/Image"
5303 assert(image != (const Image *) NULL);
5304 assert(image->signature == MagickSignature);
5305 if (image->debug != MagickFalse)
5306 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5307 assert(exception != (ExceptionInfo *) NULL);
5308 unsharp_image=BlurImageChannel(image,channel,radius,sigma,exception);
5309 if (unsharp_image == (Image *) NULL)
5310 return((Image *) NULL);
5311 quantum_threshold=(MagickRealType) QuantumRange*threshold;
5317 GetMagickPixelPacket(image,&bias);
5318 image_view=AcquireCacheView(image);
5319 unsharp_view=AcquireCacheView(unsharp_image);
5320 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5321 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5323 for (y=0; y < (long) image->rows; y++)
5328 register const IndexPacket
5331 register const PixelPacket
5334 register IndexPacket
5335 *restrict unsharp_indexes;
5340 register PixelPacket
5343 if (status == MagickFalse)
5345 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5346 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
5348 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5353 indexes=GetCacheViewVirtualIndexQueue(image_view);
5354 unsharp_indexes=GetCacheViewAuthenticIndexQueue(unsharp_view);
5356 for (x=0; x < (long) image->columns; x++)
5358 if ((channel & RedChannel) != 0)
5360 pixel.red=p->red-(MagickRealType) q->red;
5361 if (fabs(2.0*pixel.red) < quantum_threshold)
5362 pixel.red=(MagickRealType) GetRedPixelComponent(p);
5364 pixel.red=(MagickRealType) p->red+(pixel.red*amount);
5365 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
5367 if ((channel & GreenChannel) != 0)
5369 pixel.green=p->green-(MagickRealType) q->green;
5370 if (fabs(2.0*pixel.green) < quantum_threshold)
5371 pixel.green=(MagickRealType) GetGreenPixelComponent(p);
5373 pixel.green=(MagickRealType) p->green+(pixel.green*amount);
5374 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
5376 if ((channel & BlueChannel) != 0)
5378 pixel.blue=p->blue-(MagickRealType) q->blue;
5379 if (fabs(2.0*pixel.blue) < quantum_threshold)
5380 pixel.blue=(MagickRealType) GetBluePixelComponent(p);
5382 pixel.blue=(MagickRealType) p->blue+(pixel.blue*amount);
5383 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
5385 if ((channel & OpacityChannel) != 0)
5387 pixel.opacity=p->opacity-(MagickRealType) q->opacity;
5388 if (fabs(2.0*pixel.opacity) < quantum_threshold)
5389 pixel.opacity=(MagickRealType) GetOpacityPixelComponent(p);
5391 pixel.opacity=p->opacity+(pixel.opacity*amount);
5392 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
5394 if (((channel & IndexChannel) != 0) &&
5395 (image->colorspace == CMYKColorspace))
5397 pixel.index=unsharp_indexes[x]-(MagickRealType) indexes[x];
5398 if (fabs(2.0*pixel.index) < quantum_threshold)
5399 pixel.index=(MagickRealType) unsharp_indexes[x];
5401 pixel.index=(MagickRealType) unsharp_indexes[x]+(pixel.index*
5403 unsharp_indexes[x]=ClampToQuantum(pixel.index);
5408 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
5410 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5415 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5416 #pragma omp critical (MagickCore_UnsharpMaskImageChannel)
5418 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
5419 if (proceed == MagickFalse)
5423 unsharp_image->type=image->type;
5424 unsharp_view=DestroyCacheView(unsharp_view);
5425 image_view=DestroyCacheView(image_view);
5426 if (status == MagickFalse)
5427 unsharp_image=DestroyImage(unsharp_image);
5428 return(unsharp_image);