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)
181 assert(image != (const Image *) NULL);
182 assert(image->signature == MagickSignature);
183 if (image->debug != MagickFalse)
184 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
185 assert(exception != (ExceptionInfo *) NULL);
186 assert(exception->signature == MagickSignature);
187 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
188 if (blur_image == (Image *) NULL)
189 return((Image *) NULL);
190 if (fabs(sigma) <= MagickEpsilon)
192 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
194 InheritException(exception,&blur_image->exception);
195 blur_image=DestroyImage(blur_image);
196 return((Image *) NULL);
199 Edge detect the image brighness channel, level, blur, and level again.
201 edge_image=EdgeImage(image,radius,exception);
202 if (edge_image == (Image *) NULL)
204 blur_image=DestroyImage(blur_image);
205 return((Image *) NULL);
207 (void) LevelImage(edge_image,"20%,95%");
208 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
209 if (gaussian_image != (Image *) NULL)
211 edge_image=DestroyImage(edge_image);
212 edge_image=gaussian_image;
214 (void) LevelImage(edge_image,"10%,95%");
216 Create a set of kernels from maximum (radius,sigma) to minimum.
218 width=GetOptimalKernelWidth2D(radius,sigma);
219 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
220 if (kernel == (double **) NULL)
222 edge_image=DestroyImage(edge_image);
223 blur_image=DestroyImage(blur_image);
224 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
226 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
227 for (i=0; i < (ssize_t) width; i+=2)
229 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
231 if (kernel[i] == (double *) NULL)
234 j=(ssize_t) (width-i)/2;
236 for (v=(-j); v <= j; v++)
238 for (u=(-j); u <= j; u++)
240 kernel[i][k]=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
241 (2.0*MagickPI*MagickSigma*MagickSigma);
242 normalize+=kernel[i][k];
246 if (fabs(normalize) <= MagickEpsilon)
248 normalize=1.0/normalize;
249 for (k=0; k < (j*j); k++)
250 kernel[i][k]=normalize*kernel[i][k];
252 if (i < (ssize_t) width)
254 for (i-=2; i >= 0; i-=2)
255 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
256 kernel=(double **) RelinquishMagickMemory(kernel);
257 edge_image=DestroyImage(edge_image);
258 blur_image=DestroyImage(blur_image);
259 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
262 Adaptively blur image.
266 GetMagickPixelPacket(image,&bias);
267 SetMagickPixelPacketBias(image,&bias);
268 image_view=AcquireCacheView(image);
269 edge_view=AcquireCacheView(edge_image);
270 blur_view=AcquireCacheView(blur_image);
271 #if defined(MAGICKCORE_OPENMP_SUPPORT)
272 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
274 for (y=0; y < (ssize_t) blur_image->rows; y++)
276 register const IndexPacket
279 register const PixelPacket
284 *restrict blur_indexes;
292 if (status == MagickFalse)
294 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
295 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
297 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
302 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
303 for (x=0; x < (ssize_t) blur_image->columns; x++)
312 register const double
321 i=(ssize_t) ceil((double) width*QuantumScale*PixelIntensity(r)-0.5);
325 if (i > (ssize_t) width)
329 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-i)/2L),y-(ssize_t)
330 ((width-i)/2L),width-i,width-i,exception);
331 if (p == (const PixelPacket *) NULL)
333 indexes=GetCacheViewVirtualIndexQueue(image_view);
336 for (v=0; v < (ssize_t) (width-i); v++)
338 for (u=0; u < (ssize_t) (width-i); u++)
341 if (((channel & OpacityChannel) != 0) &&
342 (image->matte != MagickFalse))
343 alpha=(MagickRealType) (QuantumScale*GetAlphaPixelComponent(p));
344 if ((channel & RedChannel) != 0)
345 pixel.red+=(*k)*alpha*GetRedPixelComponent(p);
346 if ((channel & GreenChannel) != 0)
347 pixel.green+=(*k)*alpha*GetGreenPixelComponent(p);
348 if ((channel & BlueChannel) != 0)
349 pixel.blue+=(*k)*alpha*GetBluePixelComponent(p);
350 if ((channel & OpacityChannel) != 0)
351 pixel.opacity+=(*k)*GetOpacityPixelComponent(p);
352 if (((channel & IndexChannel) != 0) &&
353 (image->colorspace == CMYKColorspace))
354 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
360 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
361 if ((channel & RedChannel) != 0)
362 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
363 if ((channel & GreenChannel) != 0)
364 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
365 if ((channel & BlueChannel) != 0)
366 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
367 if ((channel & OpacityChannel) != 0)
368 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
369 if (((channel & IndexChannel) != 0) &&
370 (image->colorspace == CMYKColorspace))
371 blur_indexes[x]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
375 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
377 if (image->progress_monitor != (MagickProgressMonitor) NULL)
382 #if defined(MAGICKCORE_OPENMP_SUPPORT)
383 #pragma omp critical (MagickCore_AdaptiveBlurImageChannel)
385 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
387 if (proceed == MagickFalse)
391 blur_image->type=image->type;
392 blur_view=DestroyCacheView(blur_view);
393 edge_view=DestroyCacheView(edge_view);
394 image_view=DestroyCacheView(image_view);
395 edge_image=DestroyImage(edge_image);
396 for (i=0; i < (ssize_t) width; i+=2)
397 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
398 kernel=(double **) RelinquishMagickMemory(kernel);
399 if (status == MagickFalse)
400 blur_image=DestroyImage(blur_image);
405 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
409 % A d a p t i v e S h a r p e n I m a g e %
413 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
415 % AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
416 % intensely near image edges and less intensely far from edges. We sharpen the
417 % image with a Gaussian operator of the given radius and standard deviation
418 % (sigma). For reasonable results, radius should be larger than sigma. Use a
419 % radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
421 % The format of the AdaptiveSharpenImage method is:
423 % Image *AdaptiveSharpenImage(const Image *image,const double radius,
424 % const double sigma,ExceptionInfo *exception)
425 % Image *AdaptiveSharpenImageChannel(const Image *image,
426 % const ChannelType channel,double radius,const double sigma,
427 % ExceptionInfo *exception)
429 % A description of each parameter follows:
431 % o image: the image.
433 % o channel: the channel type.
435 % o radius: the radius of the Gaussian, in pixels, not counting the center
438 % o sigma: the standard deviation of the Laplacian, in pixels.
440 % o exception: return any errors or warnings in this structure.
444 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
445 const double sigma,ExceptionInfo *exception)
450 sharp_image=AdaptiveSharpenImageChannel(image,DefaultChannels,radius,sigma,
455 MagickExport Image *AdaptiveSharpenImageChannel(const Image *image,
456 const ChannelType channel,const double radius,const double sigma,
457 ExceptionInfo *exception)
459 #define AdaptiveSharpenImageTag "Convolve/Image"
460 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
498 assert(image != (const Image *) NULL);
499 assert(image->signature == MagickSignature);
500 if (image->debug != MagickFalse)
501 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
502 assert(exception != (ExceptionInfo *) NULL);
503 assert(exception->signature == MagickSignature);
504 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
505 if (sharp_image == (Image *) NULL)
506 return((Image *) NULL);
507 if (fabs(sigma) <= MagickEpsilon)
509 if (SetImageStorageClass(sharp_image,DirectClass) == MagickFalse)
511 InheritException(exception,&sharp_image->exception);
512 sharp_image=DestroyImage(sharp_image);
513 return((Image *) NULL);
516 Edge detect the image brighness channel, level, sharp, and level again.
518 edge_image=EdgeImage(image,radius,exception);
519 if (edge_image == (Image *) NULL)
521 sharp_image=DestroyImage(sharp_image);
522 return((Image *) NULL);
524 (void) LevelImage(edge_image,"20%,95%");
525 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
526 if (gaussian_image != (Image *) NULL)
528 edge_image=DestroyImage(edge_image);
529 edge_image=gaussian_image;
531 (void) LevelImage(edge_image,"10%,95%");
533 Create a set of kernels from maximum (radius,sigma) to minimum.
535 width=GetOptimalKernelWidth2D(radius,sigma);
536 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
537 if (kernel == (double **) NULL)
539 edge_image=DestroyImage(edge_image);
540 sharp_image=DestroyImage(sharp_image);
541 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
543 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
544 for (i=0; i < (ssize_t) width; i+=2)
546 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
548 if (kernel[i] == (double *) NULL)
551 j=(ssize_t) (width-i)/2;
553 for (v=(-j); v <= j; v++)
555 for (u=(-j); u <= j; u++)
557 kernel[i][k]=(-exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
558 (2.0*MagickPI*MagickSigma*MagickSigma));
559 normalize+=kernel[i][k];
563 if (fabs(normalize) <= MagickEpsilon)
565 normalize=1.0/normalize;
566 for (k=0; k < (j*j); k++)
567 kernel[i][k]=normalize*kernel[i][k];
569 if (i < (ssize_t) width)
571 for (i-=2; i >= 0; i-=2)
572 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
573 kernel=(double **) RelinquishMagickMemory(kernel);
574 edge_image=DestroyImage(edge_image);
575 sharp_image=DestroyImage(sharp_image);
576 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
579 Adaptively sharpen image.
583 GetMagickPixelPacket(image,&bias);
584 SetMagickPixelPacketBias(image,&bias);
585 image_view=AcquireCacheView(image);
586 edge_view=AcquireCacheView(edge_image);
587 sharp_view=AcquireCacheView(sharp_image);
588 #if defined(MAGICKCORE_OPENMP_SUPPORT)
589 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
591 for (y=0; y < (ssize_t) sharp_image->rows; y++)
593 register const IndexPacket
596 register const PixelPacket
601 *restrict sharp_indexes;
609 if (status == MagickFalse)
611 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
612 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
614 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
619 sharp_indexes=GetCacheViewAuthenticIndexQueue(sharp_view);
620 for (x=0; x < (ssize_t) sharp_image->columns; x++)
629 register const double
638 i=(ssize_t) ceil((double) width*(QuantumRange-QuantumScale*
639 PixelIntensity(r))-0.5);
643 if (i > (ssize_t) width)
647 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-i)/2L),y-(ssize_t)
648 ((width-i)/2L),width-i,width-i,exception);
649 if (p == (const PixelPacket *) NULL)
651 indexes=GetCacheViewVirtualIndexQueue(image_view);
654 for (v=0; v < (ssize_t) (width-i); v++)
656 for (u=0; u < (ssize_t) (width-i); u++)
659 if (((channel & OpacityChannel) != 0) &&
660 (image->matte != MagickFalse))
661 alpha=(MagickRealType) (QuantumScale*GetAlphaPixelComponent(p));
662 if ((channel & RedChannel) != 0)
663 pixel.red+=(*k)*alpha*GetRedPixelComponent(p);
664 if ((channel & GreenChannel) != 0)
665 pixel.green+=(*k)*alpha*GetGreenPixelComponent(p);
666 if ((channel & BlueChannel) != 0)
667 pixel.blue+=(*k)*alpha*GetBluePixelComponent(p);
668 if ((channel & OpacityChannel) != 0)
669 pixel.opacity+=(*k)*GetOpacityPixelComponent(p);
670 if (((channel & IndexChannel) != 0) &&
671 (image->colorspace == CMYKColorspace))
672 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
678 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
679 if ((channel & RedChannel) != 0)
680 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
681 if ((channel & GreenChannel) != 0)
682 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
683 if ((channel & BlueChannel) != 0)
684 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
685 if ((channel & OpacityChannel) != 0)
686 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
687 if (((channel & IndexChannel) != 0) &&
688 (image->colorspace == CMYKColorspace))
689 sharp_indexes[x]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
693 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
695 if (image->progress_monitor != (MagickProgressMonitor) NULL)
700 #if defined(MAGICKCORE_OPENMP_SUPPORT)
701 #pragma omp critical (MagickCore_AdaptiveSharpenImageChannel)
703 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
705 if (proceed == MagickFalse)
709 sharp_image->type=image->type;
710 sharp_view=DestroyCacheView(sharp_view);
711 edge_view=DestroyCacheView(edge_view);
712 image_view=DestroyCacheView(image_view);
713 edge_image=DestroyImage(edge_image);
714 for (i=0; i < (ssize_t) width; i+=2)
715 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
716 kernel=(double **) RelinquishMagickMemory(kernel);
717 if (status == MagickFalse)
718 sharp_image=DestroyImage(sharp_image);
723 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727 % B l u r I m a g e %
731 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
733 % BlurImage() blurs an image. We convolve the image with a Gaussian operator
734 % of the given radius and standard deviation (sigma). For reasonable results,
735 % the radius should be larger than sigma. Use a radius of 0 and BlurImage()
736 % selects a suitable radius for you.
738 % BlurImage() differs from GaussianBlurImage() in that it uses a separable
739 % kernel which is faster but mathematically equivalent to the non-separable
742 % The format of the BlurImage method is:
744 % Image *BlurImage(const Image *image,const double radius,
745 % const double sigma,ExceptionInfo *exception)
746 % Image *BlurImageChannel(const Image *image,const ChannelType channel,
747 % const double radius,const double sigma,ExceptionInfo *exception)
749 % A description of each parameter follows:
751 % o image: the image.
753 % o channel: the channel type.
755 % o radius: the radius of the Gaussian, in pixels, not counting the center
758 % o sigma: the standard deviation of the Gaussian, in pixels.
760 % o exception: return any errors or warnings in this structure.
764 MagickExport Image *BlurImage(const Image *image,const double radius,
765 const double sigma,ExceptionInfo *exception)
770 blur_image=BlurImageChannel(image,DefaultChannels,radius,sigma,exception);
774 static double *GetBlurKernel(const size_t width,const double sigma)
788 Generate a 1-D convolution kernel.
790 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
791 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
792 if (kernel == (double *) NULL)
797 for (k=(-j); k <= j; k++)
799 kernel[i]=exp(-((double) k*k)/(2.0*MagickSigma*MagickSigma))/
800 (MagickSQ2PI*MagickSigma);
801 normalize+=kernel[i];
804 for (i=0; i < (ssize_t) width; i++)
805 kernel[i]/=normalize;
809 MagickExport Image *BlurImageChannel(const Image *image,
810 const ChannelType channel,const double radius,const double sigma,
811 ExceptionInfo *exception)
813 #define BlurImageTag "Blur/Image"
845 Initialize blur image attributes.
847 assert(image != (Image *) NULL);
848 assert(image->signature == MagickSignature);
849 if (image->debug != MagickFalse)
850 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
851 assert(exception != (ExceptionInfo *) NULL);
852 assert(exception->signature == MagickSignature);
853 blur_image=CloneImage(image,0,0,MagickTrue,exception);
854 if (blur_image == (Image *) NULL)
855 return((Image *) NULL);
856 if (fabs(sigma) <= MagickEpsilon)
858 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
860 InheritException(exception,&blur_image->exception);
861 blur_image=DestroyImage(blur_image);
862 return((Image *) NULL);
864 width=GetOptimalKernelWidth1D(radius,sigma);
865 kernel=GetBlurKernel(width,sigma);
866 if (kernel == (double *) NULL)
868 blur_image=DestroyImage(blur_image);
869 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
871 if (image->debug != MagickFalse)
874 format[MaxTextExtent],
877 register const double
880 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
881 " BlurImage with %lu kernel:",(unsigned long) width);
882 message=AcquireString("");
884 for (i=0; i < (ssize_t) width; i++)
887 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",(long) i);
888 (void) ConcatenateString(&message,format);
889 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
890 (void) ConcatenateString(&message,format);
891 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
893 message=DestroyString(message);
900 GetMagickPixelPacket(image,&bias);
901 SetMagickPixelPacketBias(image,&bias);
902 image_view=AcquireCacheView(image);
903 blur_view=AcquireCacheView(blur_image);
904 #if defined(MAGICKCORE_OPENMP_SUPPORT)
905 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
907 for (y=0; y < (ssize_t) blur_image->rows; y++)
909 register const IndexPacket
912 register const PixelPacket
916 *restrict blur_indexes;
924 if (status == MagickFalse)
926 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y,image->columns+
928 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
930 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
935 indexes=GetCacheViewVirtualIndexQueue(image_view);
936 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
937 for (x=0; x < (ssize_t) blur_image->columns; x++)
942 register const double
945 register const PixelPacket
946 *restrict kernel_pixels;
954 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
956 for (i=0; i < (ssize_t) width; i++)
958 pixel.red+=(*k)*kernel_pixels->red;
959 pixel.green+=(*k)*kernel_pixels->green;
960 pixel.blue+=(*k)*kernel_pixels->blue;
964 if ((channel & RedChannel) != 0)
965 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
966 if ((channel & GreenChannel) != 0)
967 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
968 if ((channel & BlueChannel) != 0)
969 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
970 if ((channel & OpacityChannel) != 0)
974 for (i=0; i < (ssize_t) width; i++)
976 pixel.opacity+=(*k)*kernel_pixels->opacity;
980 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
982 if (((channel & IndexChannel) != 0) &&
983 (image->colorspace == CMYKColorspace))
985 register const IndexPacket
986 *restrict kernel_indexes;
989 kernel_indexes=indexes;
990 for (i=0; i < (ssize_t) width; i++)
992 pixel.index+=(*k)*(*kernel_indexes);
996 blur_indexes[x]=ClampToQuantum(pixel.index);
1006 for (i=0; i < (ssize_t) width; i++)
1008 alpha=(MagickRealType) (QuantumScale*
1009 GetAlphaPixelComponent(kernel_pixels));
1010 pixel.red+=(*k)*alpha*kernel_pixels->red;
1011 pixel.green+=(*k)*alpha*kernel_pixels->green;
1012 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
1017 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1018 if ((channel & RedChannel) != 0)
1019 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
1020 if ((channel & GreenChannel) != 0)
1021 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
1022 if ((channel & BlueChannel) != 0)
1023 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
1024 if ((channel & OpacityChannel) != 0)
1028 for (i=0; i < (ssize_t) width; i++)
1030 pixel.opacity+=(*k)*kernel_pixels->opacity;
1034 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1036 if (((channel & IndexChannel) != 0) &&
1037 (image->colorspace == CMYKColorspace))
1039 register const IndexPacket
1040 *restrict kernel_indexes;
1044 kernel_indexes=indexes;
1045 for (i=0; i < (ssize_t) width; i++)
1047 alpha=(MagickRealType) (QuantumScale*
1048 GetAlphaPixelComponent(kernel_pixels));
1049 pixel.index+=(*k)*alpha*(*kernel_indexes);
1054 blur_indexes[x]=ClampToQuantum(gamma*
1055 GetIndexPixelComponent(&pixel));
1061 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1063 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1068 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1069 #pragma omp critical (MagickCore_BlurImageChannel)
1071 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1072 blur_image->columns);
1073 if (proceed == MagickFalse)
1077 blur_view=DestroyCacheView(blur_view);
1078 image_view=DestroyCacheView(image_view);
1082 image_view=AcquireCacheView(blur_image);
1083 blur_view=AcquireCacheView(blur_image);
1084 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1085 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1087 for (x=0; x < (ssize_t) blur_image->columns; x++)
1089 register const IndexPacket
1092 register const PixelPacket
1095 register IndexPacket
1096 *restrict blur_indexes;
1101 register PixelPacket
1104 if (status == MagickFalse)
1106 p=GetCacheViewVirtualPixels(image_view,x,-((ssize_t) width/2L),1,image->rows+
1108 q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
1109 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1114 indexes=GetCacheViewVirtualIndexQueue(image_view);
1115 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
1116 for (y=0; y < (ssize_t) blur_image->rows; y++)
1121 register const double
1124 register const PixelPacket
1125 *restrict kernel_pixels;
1133 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
1135 for (i=0; i < (ssize_t) width; i++)
1137 pixel.red+=(*k)*kernel_pixels->red;
1138 pixel.green+=(*k)*kernel_pixels->green;
1139 pixel.blue+=(*k)*kernel_pixels->blue;
1143 if ((channel & RedChannel) != 0)
1144 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
1145 if ((channel & GreenChannel) != 0)
1146 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
1147 if ((channel & BlueChannel) != 0)
1148 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
1149 if ((channel & OpacityChannel) != 0)
1153 for (i=0; i < (ssize_t) width; i++)
1155 pixel.opacity+=(*k)*kernel_pixels->opacity;
1159 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1161 if (((channel & IndexChannel) != 0) &&
1162 (image->colorspace == CMYKColorspace))
1164 register const IndexPacket
1165 *restrict kernel_indexes;
1168 kernel_indexes=indexes;
1169 for (i=0; i < (ssize_t) width; i++)
1171 pixel.index+=(*k)*(*kernel_indexes);
1175 blur_indexes[y]=ClampToQuantum(pixel.index);
1185 for (i=0; i < (ssize_t) width; i++)
1187 alpha=(MagickRealType) (QuantumScale*
1188 GetAlphaPixelComponent(kernel_pixels));
1189 pixel.red+=(*k)*alpha*kernel_pixels->red;
1190 pixel.green+=(*k)*alpha*kernel_pixels->green;
1191 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
1196 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1197 if ((channel & RedChannel) != 0)
1198 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
1199 if ((channel & GreenChannel) != 0)
1200 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
1201 if ((channel & BlueChannel) != 0)
1202 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
1203 if ((channel & OpacityChannel) != 0)
1207 for (i=0; i < (ssize_t) width; i++)
1209 pixel.opacity+=(*k)*kernel_pixels->opacity;
1213 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1215 if (((channel & IndexChannel) != 0) &&
1216 (image->colorspace == CMYKColorspace))
1218 register const IndexPacket
1219 *restrict kernel_indexes;
1223 kernel_indexes=indexes;
1224 for (i=0; i < (ssize_t) width; i++)
1226 alpha=(MagickRealType) (QuantumScale*
1227 GetAlphaPixelComponent(kernel_pixels));
1228 pixel.index+=(*k)*alpha*(*kernel_indexes);
1233 blur_indexes[y]=ClampToQuantum(gamma*
1234 GetIndexPixelComponent(&pixel));
1240 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1242 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1247 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1248 #pragma omp critical (MagickCore_BlurImageChannel)
1250 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1251 blur_image->columns);
1252 if (proceed == MagickFalse)
1256 blur_view=DestroyCacheView(blur_view);
1257 image_view=DestroyCacheView(image_view);
1258 kernel=(double *) RelinquishMagickMemory(kernel);
1259 if (status == MagickFalse)
1260 blur_image=DestroyImage(blur_image);
1261 blur_image->type=image->type;
1266 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1270 % C o n v o l v e I m a g e %
1274 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1276 % ConvolveImage() applies a custom convolution kernel to the image.
1278 % The format of the ConvolveImage method is:
1280 % Image *ConvolveImage(const Image *image,const size_t order,
1281 % const double *kernel,ExceptionInfo *exception)
1282 % Image *ConvolveImageChannel(const Image *image,const ChannelType channel,
1283 % const size_t order,const double *kernel,
1284 % ExceptionInfo *exception)
1286 % A description of each parameter follows:
1288 % o image: the image.
1290 % o channel: the channel type.
1292 % o order: the number of columns and rows in the filter kernel.
1294 % o kernel: An array of double representing the convolution kernel.
1296 % o exception: return any errors or warnings in this structure.
1300 MagickExport Image *ConvolveImage(const Image *image,const size_t order,
1301 const double *kernel,ExceptionInfo *exception)
1306 convolve_image=ConvolveImageChannel(image,DefaultChannels,order,kernel,
1308 return(convolve_image);
1311 MagickExport Image *ConvolveImageChannel(const Image *image,
1312 const ChannelType channel,const size_t order,const double *kernel,
1313 ExceptionInfo *exception)
1315 #define ConvolveImageTag "Convolve/Image"
1349 Initialize convolve image attributes.
1351 assert(image != (Image *) NULL);
1352 assert(image->signature == MagickSignature);
1353 if (image->debug != MagickFalse)
1354 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1355 assert(exception != (ExceptionInfo *) NULL);
1356 assert(exception->signature == MagickSignature);
1358 if ((width % 2) == 0)
1359 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
1360 convolve_image=CloneImage(image,0,0,MagickTrue,exception);
1361 if (convolve_image == (Image *) NULL)
1362 return((Image *) NULL);
1363 if (SetImageStorageClass(convolve_image,DirectClass) == MagickFalse)
1365 InheritException(exception,&convolve_image->exception);
1366 convolve_image=DestroyImage(convolve_image);
1367 return((Image *) NULL);
1369 if (image->debug != MagickFalse)
1372 format[MaxTextExtent],
1379 register const double
1382 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1383 " ConvolveImage with %lux%lu kernel:",(unsigned long) width,
1384 (unsigned long) width);
1385 message=AcquireString("");
1387 for (v=0; v < (ssize_t) width; v++)
1390 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",(long) v);
1391 (void) ConcatenateString(&message,format);
1392 for (u=0; u < (ssize_t) width; u++)
1394 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
1395 (void) ConcatenateString(&message,format);
1397 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
1399 message=DestroyString(message);
1404 normal_kernel=(double *) AcquireQuantumMemory(width*width,
1405 sizeof(*normal_kernel));
1406 if (normal_kernel == (double *) NULL)
1408 convolve_image=DestroyImage(convolve_image);
1409 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1412 for (i=0; i < (ssize_t) (width*width); i++)
1414 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1415 for (i=0; i < (ssize_t) (width*width); i++)
1416 normal_kernel[i]=gamma*kernel[i];
1422 GetMagickPixelPacket(image,&bias);
1423 SetMagickPixelPacketBias(image,&bias);
1424 image_view=AcquireCacheView(image);
1425 convolve_view=AcquireCacheView(convolve_image);
1426 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1427 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1429 for (y=0; y < (ssize_t) image->rows; y++)
1434 register const IndexPacket
1437 register const PixelPacket
1440 register IndexPacket
1441 *restrict convolve_indexes;
1446 register PixelPacket
1449 if (status == MagickFalse)
1451 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t) (width/
1452 2L),image->columns+width,width,exception);
1453 q=GetCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
1455 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1460 indexes=GetCacheViewVirtualIndexQueue(image_view);
1461 convolve_indexes=GetCacheViewAuthenticIndexQueue(convolve_view);
1462 for (x=0; x < (ssize_t) image->columns; x++)
1470 register const double
1473 register const PixelPacket
1474 *restrict kernel_pixels;
1482 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
1484 for (v=0; v < (ssize_t) width; v++)
1486 for (u=0; u < (ssize_t) width; u++)
1488 pixel.red+=(*k)*kernel_pixels[u].red;
1489 pixel.green+=(*k)*kernel_pixels[u].green;
1490 pixel.blue+=(*k)*kernel_pixels[u].blue;
1493 kernel_pixels+=image->columns+width;
1495 if ((channel & RedChannel) != 0)
1496 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
1497 if ((channel & GreenChannel) != 0)
1498 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
1499 if ((channel & BlueChannel) != 0)
1500 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
1501 if ((channel & OpacityChannel) != 0)
1505 for (v=0; v < (ssize_t) width; v++)
1507 for (u=0; u < (ssize_t) width; u++)
1509 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1512 kernel_pixels+=image->columns+width;
1514 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1516 if (((channel & IndexChannel) != 0) &&
1517 (image->colorspace == CMYKColorspace))
1519 register const IndexPacket
1520 *restrict kernel_indexes;
1523 kernel_indexes=indexes;
1524 for (v=0; v < (ssize_t) width; v++)
1526 for (u=0; u < (ssize_t) width; u++)
1528 pixel.index+=(*k)*kernel_indexes[u];
1531 kernel_indexes+=image->columns+width;
1533 convolve_indexes[x]=ClampToQuantum(pixel.index);
1543 for (v=0; v < (ssize_t) width; v++)
1545 for (u=0; u < (ssize_t) width; u++)
1547 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1548 kernel_pixels[u].opacity));
1549 pixel.red+=(*k)*alpha*kernel_pixels[u].red;
1550 pixel.green+=(*k)*alpha*kernel_pixels[u].green;
1551 pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
1555 kernel_pixels+=image->columns+width;
1557 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1558 if ((channel & RedChannel) != 0)
1559 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
1560 if ((channel & GreenChannel) != 0)
1561 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
1562 if ((channel & BlueChannel) != 0)
1563 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
1564 if ((channel & OpacityChannel) != 0)
1568 for (v=0; v < (ssize_t) width; v++)
1570 for (u=0; u < (ssize_t) width; u++)
1572 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1575 kernel_pixels+=image->columns+width;
1577 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
1579 if (((channel & IndexChannel) != 0) &&
1580 (image->colorspace == CMYKColorspace))
1582 register const IndexPacket
1583 *restrict kernel_indexes;
1587 kernel_indexes=indexes;
1588 for (v=0; v < (ssize_t) width; v++)
1590 for (u=0; u < (ssize_t) width; u++)
1592 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1593 kernel_pixels[u].opacity));
1594 pixel.index+=(*k)*alpha*kernel_indexes[u];
1597 kernel_pixels+=image->columns+width;
1598 kernel_indexes+=image->columns+width;
1600 convolve_indexes[x]=ClampToQuantum(gamma*
1601 GetIndexPixelComponent(&pixel));
1607 sync=SyncCacheViewAuthenticPixels(convolve_view,exception);
1608 if (sync == MagickFalse)
1610 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1615 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1616 #pragma omp critical (MagickCore_ConvolveImageChannel)
1618 proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1619 if (proceed == MagickFalse)
1623 convolve_image->type=image->type;
1624 convolve_view=DestroyCacheView(convolve_view);
1625 image_view=DestroyCacheView(image_view);
1626 normal_kernel=(double *) RelinquishMagickMemory(normal_kernel);
1627 if (status == MagickFalse)
1628 convolve_image=DestroyImage(convolve_image);
1629 return(convolve_image);
1633 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1637 % D e s p e c k l e I m a g e %
1641 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1643 % DespeckleImage() reduces the speckle noise in an image while perserving the
1644 % edges of the original image.
1646 % The format of the DespeckleImage method is:
1648 % Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1650 % A description of each parameter follows:
1652 % o image: the image.
1654 % o exception: return any errors or warnings in this structure.
1658 static Quantum **DestroyPixelThreadSet(Quantum **pixels)
1663 assert(pixels != (Quantum **) NULL);
1664 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
1665 if (pixels[i] != (Quantum *) NULL)
1666 pixels[i]=(Quantum *) RelinquishMagickMemory(pixels[i]);
1667 pixels=(Quantum **) RelinquishAlignedMemory(pixels);
1671 static Quantum **AcquirePixelThreadSet(const size_t count)
1682 number_threads=GetOpenMPMaximumThreads();
1683 pixels=(Quantum **) AcquireAlignedMemory(number_threads,sizeof(*pixels));
1684 if (pixels == (Quantum **) NULL)
1685 return((Quantum **) NULL);
1686 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
1687 for (i=0; i < (ssize_t) number_threads; i++)
1689 pixels[i]=(Quantum *) AcquireQuantumMemory(count,sizeof(**pixels));
1690 if (pixels[i] == (Quantum *) NULL)
1691 return(DestroyPixelThreadSet(pixels));
1696 static void Hull(const ssize_t x_offset,const ssize_t y_offset,
1697 const size_t columns,const size_t rows,Quantum *f,Quantum *g,
1715 assert(f != (Quantum *) NULL);
1716 assert(g != (Quantum *) NULL);
1719 r=p+(y_offset*((ssize_t) columns+2)+x_offset);
1720 for (y=0; y < (ssize_t) rows; y++)
1726 for (x=(ssize_t) columns; x != 0; x--)
1728 v=(MagickRealType) (*p);
1729 if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1730 v+=ScaleCharToQuantum(1);
1737 for (x=(ssize_t) columns; x != 0; x--)
1739 v=(MagickRealType) (*p);
1740 if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
1741 v-=(ssize_t) ScaleCharToQuantum(1);
1753 r=q+(y_offset*((ssize_t) columns+2)+x_offset);
1754 s=q-(y_offset*((ssize_t) columns+2)+x_offset);
1755 for (y=0; y < (ssize_t) rows; y++)
1762 for (x=(ssize_t) columns; x != 0; x--)
1764 v=(MagickRealType) (*q);
1765 if (((MagickRealType) *s >=
1766 (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1767 ((MagickRealType) *r > v))
1768 v+=ScaleCharToQuantum(1);
1776 for (x=(ssize_t) columns; x != 0; x--)
1778 v=(MagickRealType) (*q);
1779 if (((MagickRealType) *s <=
1780 (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1781 ((MagickRealType) *r < v))
1782 v-=(MagickRealType) ScaleCharToQuantum(1);
1796 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1798 #define DespeckleImageTag "Despeckle/Image"
1820 static const ssize_t
1821 X[4] = {0, 1, 1,-1},
1822 Y[4] = {1, 0, 1, 1};
1825 Allocate despeckled image.
1827 assert(image != (const Image *) NULL);
1828 assert(image->signature == MagickSignature);
1829 if (image->debug != MagickFalse)
1830 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1831 assert(exception != (ExceptionInfo *) NULL);
1832 assert(exception->signature == MagickSignature);
1833 despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1835 if (despeckle_image == (Image *) NULL)
1836 return((Image *) NULL);
1837 if (SetImageStorageClass(despeckle_image,DirectClass) == MagickFalse)
1839 InheritException(exception,&despeckle_image->exception);
1840 despeckle_image=DestroyImage(despeckle_image);
1841 return((Image *) NULL);
1844 Allocate image buffers.
1846 length=(size_t) ((image->columns+2)*(image->rows+2));
1847 pixels=AcquirePixelThreadSet(length);
1848 buffers=AcquirePixelThreadSet(length);
1849 if ((pixels == (Quantum **) NULL) || (buffers == (Quantum **) NULL))
1851 if (buffers != (Quantum **) NULL)
1852 buffers=DestroyPixelThreadSet(buffers);
1853 if (pixels != (Quantum **) NULL)
1854 pixels=DestroyPixelThreadSet(pixels);
1855 despeckle_image=DestroyImage(despeckle_image);
1856 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1859 Reduce speckle in the image.
1862 image_view=AcquireCacheView(image);
1863 despeckle_view=AcquireCacheView(despeckle_image);
1864 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1865 #pragma omp parallel for schedule(dynamic,4) shared(status)
1867 for (channel=0; channel <= 3; channel++)
1882 if (status == MagickFalse)
1884 id=GetOpenMPThreadId();
1886 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
1888 j=(ssize_t) image->columns+2;
1889 for (y=0; y < (ssize_t) image->rows; y++)
1891 register const PixelPacket
1894 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1895 if (p == (const PixelPacket *) NULL)
1898 for (x=0; x < (ssize_t) image->columns; x++)
1902 case 0: pixel[j]=GetRedPixelComponent(p); break;
1903 case 1: pixel[j]=GetGreenPixelComponent(p); break;
1904 case 2: pixel[j]=GetBluePixelComponent(p); break;
1905 case 3: pixel[j]=GetOpacityPixelComponent(p); break;
1913 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1914 for (i=0; i < 4; i++)
1916 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,1);
1917 Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,1);
1918 Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,-1);
1919 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,-1);
1921 j=(ssize_t) image->columns+2;
1922 for (y=0; y < (ssize_t) image->rows; y++)
1927 register PixelPacket
1930 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1932 if (q == (PixelPacket *) NULL)
1935 for (x=0; x < (ssize_t) image->columns; x++)
1939 case 0: q->red=pixel[j]; break;
1940 case 1: q->green=pixel[j]; break;
1941 case 2: q->blue=pixel[j]; break;
1942 case 3: q->opacity=pixel[j]; break;
1948 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1949 if (sync == MagickFalse)
1956 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1961 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1962 #pragma omp critical (MagickCore_DespeckleImage)
1964 proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType)
1966 if (proceed == MagickFalse)
1970 despeckle_view=DestroyCacheView(despeckle_view);
1971 image_view=DestroyCacheView(image_view);
1972 buffers=DestroyPixelThreadSet(buffers);
1973 pixels=DestroyPixelThreadSet(pixels);
1974 despeckle_image->type=image->type;
1975 if (status == MagickFalse)
1976 despeckle_image=DestroyImage(despeckle_image);
1977 return(despeckle_image);
1981 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1985 % E d g e I m a g e %
1989 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1991 % EdgeImage() finds edges in an image. Radius defines the radius of the
1992 % convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1995 % The format of the EdgeImage method is:
1997 % Image *EdgeImage(const Image *image,const double radius,
1998 % ExceptionInfo *exception)
2000 % A description of each parameter follows:
2002 % o image: the image.
2004 % o radius: the radius of the pixel neighborhood.
2006 % o exception: return any errors or warnings in this structure.
2009 MagickExport Image *EdgeImage(const Image *image,const double radius,
2010 ExceptionInfo *exception)
2024 assert(image != (const Image *) NULL);
2025 assert(image->signature == MagickSignature);
2026 if (image->debug != MagickFalse)
2027 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2028 assert(exception != (ExceptionInfo *) NULL);
2029 assert(exception->signature == MagickSignature);
2030 width=GetOptimalKernelWidth1D(radius,0.5);
2031 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2032 if (kernel == (double *) NULL)
2033 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2034 for (i=0; i < (ssize_t) (width*width); i++)
2036 kernel[i/2]=(double) (width*width-1.0);
2037 edge_image=ConvolveImage(image,width,kernel,exception);
2038 kernel=(double *) RelinquishMagickMemory(kernel);
2043 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2047 % E m b o s s I m a g e %
2051 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2053 % EmbossImage() returns a grayscale image with a three-dimensional effect.
2054 % We convolve the image with a Gaussian operator of the given radius and
2055 % standard deviation (sigma). For reasonable results, radius should be
2056 % larger than sigma. Use a radius of 0 and Emboss() selects a suitable
2059 % The format of the EmbossImage method is:
2061 % Image *EmbossImage(const Image *image,const double radius,
2062 % const double sigma,ExceptionInfo *exception)
2064 % A description of each parameter follows:
2066 % o image: the image.
2068 % o radius: the radius of the pixel neighborhood.
2070 % o sigma: the standard deviation of the Gaussian, in pixels.
2072 % o exception: return any errors or warnings in this structure.
2075 MagickExport Image *EmbossImage(const Image *image,const double radius,
2076 const double sigma,ExceptionInfo *exception)
2096 assert(image != (Image *) NULL);
2097 assert(image->signature == MagickSignature);
2098 if (image->debug != MagickFalse)
2099 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2100 assert(exception != (ExceptionInfo *) NULL);
2101 assert(exception->signature == MagickSignature);
2102 width=GetOptimalKernelWidth2D(radius,sigma);
2103 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2104 if (kernel == (double *) NULL)
2105 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2106 j=(ssize_t) width/2;
2109 for (v=(-j); v <= j; v++)
2111 for (u=(-j); u <= j; u++)
2113 kernel[i]=((u < 0) || (v < 0) ? -8.0 : 8.0)*
2114 exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
2115 (2.0*MagickPI*MagickSigma*MagickSigma);
2122 emboss_image=ConvolveImage(image,width,kernel,exception);
2123 if (emboss_image != (Image *) NULL)
2124 (void) EqualizeImage(emboss_image);
2125 kernel=(double *) RelinquishMagickMemory(kernel);
2126 return(emboss_image);
2130 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2134 % F i l t e r I m a g e %
2138 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2140 % FilterImage() applies a custom convolution kernel to the image.
2142 % The format of the FilterImage method is:
2144 % Image *FilterImage(const Image *image,const KernelInfo *kernel,
2145 % ExceptionInfo *exception)
2146 % Image *FilterImageChannel(const Image *image,const ChannelType channel,
2147 % const KernelInfo *kernel,ExceptionInfo *exception)
2149 % A description of each parameter follows:
2151 % o image: the image.
2153 % o channel: the channel type.
2155 % o kernel: the filtering kernel.
2157 % o exception: return any errors or warnings in this structure.
2161 MagickExport Image *FilterImage(const Image *image,const KernelInfo *kernel,
2162 ExceptionInfo *exception)
2167 filter_image=FilterImageChannel(image,DefaultChannels,kernel,exception);
2168 return(filter_image);
2171 MagickExport Image *FilterImageChannel(const Image *image,
2172 const ChannelType channel,const KernelInfo *kernel,ExceptionInfo *exception)
2174 #define FilterImageTag "Filter/Image"
2196 Initialize filter image attributes.
2198 assert(image != (Image *) NULL);
2199 assert(image->signature == MagickSignature);
2200 if (image->debug != MagickFalse)
2201 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2202 assert(exception != (ExceptionInfo *) NULL);
2203 assert(exception->signature == MagickSignature);
2204 if ((kernel->width % 2) == 0)
2205 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
2206 filter_image=CloneImage(image,0,0,MagickTrue,exception);
2207 if (filter_image == (Image *) NULL)
2208 return((Image *) NULL);
2209 if (SetImageStorageClass(filter_image,DirectClass) == MagickFalse)
2211 InheritException(exception,&filter_image->exception);
2212 filter_image=DestroyImage(filter_image);
2213 return((Image *) NULL);
2215 if (image->debug != MagickFalse)
2218 format[MaxTextExtent],
2225 register const double
2228 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2229 " FilterImage with %ldx%ld kernel:",(unsigned long) kernel->width,
2230 (unsigned long) kernel->height);
2231 message=AcquireString("");
2233 for (v=0; v < (ssize_t) kernel->height; v++)
2236 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",(long) v);
2237 (void) ConcatenateString(&message,format);
2238 for (u=0; u < (ssize_t) kernel->width; u++)
2240 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
2241 (void) ConcatenateString(&message,format);
2243 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
2245 message=DestroyString(message);
2247 status=AccelerateConvolveImage(image,kernel,filter_image,exception);
2248 if (status == MagickTrue)
2249 return(filter_image);
2255 GetMagickPixelPacket(image,&bias);
2256 SetMagickPixelPacketBias(image,&bias);
2257 image_view=AcquireCacheView(image);
2258 filter_view=AcquireCacheView(filter_image);
2259 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2260 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2262 for (y=0; y < (ssize_t) image->rows; y++)
2267 register const IndexPacket
2270 register const PixelPacket
2273 register IndexPacket
2274 *restrict filter_indexes;
2279 register PixelPacket
2282 if (status == MagickFalse)
2284 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel->width/2L),
2285 y-(ssize_t) (kernel->height/2L),image->columns+kernel->width,kernel->height,
2287 q=GetCacheViewAuthenticPixels(filter_view,0,y,filter_image->columns,1,
2289 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2294 indexes=GetCacheViewVirtualIndexQueue(image_view);
2295 filter_indexes=GetCacheViewAuthenticIndexQueue(filter_view);
2296 for (x=0; x < (ssize_t) image->columns; x++)
2304 register const double
2307 register const PixelPacket
2308 *restrict kernel_pixels;
2316 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
2318 for (v=0; v < (ssize_t) kernel->width; v++)
2320 for (u=0; u < (ssize_t) kernel->height; u++)
2322 pixel.red+=(*k)*kernel_pixels[u].red;
2323 pixel.green+=(*k)*kernel_pixels[u].green;
2324 pixel.blue+=(*k)*kernel_pixels[u].blue;
2327 kernel_pixels+=image->columns+kernel->width;
2329 if ((channel & RedChannel) != 0)
2330 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2331 if ((channel & GreenChannel) != 0)
2332 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2333 if ((channel & BlueChannel) != 0)
2334 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2335 if ((channel & OpacityChannel) != 0)
2339 for (v=0; v < (ssize_t) kernel->width; v++)
2341 for (u=0; u < (ssize_t) kernel->height; u++)
2343 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
2346 kernel_pixels+=image->columns+kernel->width;
2348 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2350 if (((channel & IndexChannel) != 0) &&
2351 (image->colorspace == CMYKColorspace))
2353 register const IndexPacket
2354 *restrict kernel_indexes;
2357 kernel_indexes=indexes;
2358 for (v=0; v < (ssize_t) kernel->width; v++)
2360 for (u=0; u < (ssize_t) kernel->height; u++)
2362 pixel.index+=(*k)*kernel_indexes[u];
2365 kernel_indexes+=image->columns+kernel->width;
2367 filter_indexes[x]=ClampToQuantum(pixel.index);
2377 for (v=0; v < (ssize_t) kernel->width; v++)
2379 for (u=0; u < (ssize_t) kernel->height; u++)
2381 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
2382 kernel_pixels[u].opacity));
2383 pixel.red+=(*k)*alpha*kernel_pixels[u].red;
2384 pixel.green+=(*k)*alpha*kernel_pixels[u].green;
2385 pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
2389 kernel_pixels+=image->columns+kernel->width;
2391 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2392 if ((channel & RedChannel) != 0)
2393 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2394 if ((channel & GreenChannel) != 0)
2395 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2396 if ((channel & BlueChannel) != 0)
2397 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2398 if ((channel & OpacityChannel) != 0)
2402 for (v=0; v < (ssize_t) kernel->width; v++)
2404 for (u=0; u < (ssize_t) kernel->height; u++)
2406 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
2409 kernel_pixels+=image->columns+kernel->width;
2411 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2413 if (((channel & IndexChannel) != 0) &&
2414 (image->colorspace == CMYKColorspace))
2416 register const IndexPacket
2417 *restrict kernel_indexes;
2421 kernel_indexes=indexes;
2422 for (v=0; v < (ssize_t) kernel->width; v++)
2424 for (u=0; u < (ssize_t) kernel->height; u++)
2426 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
2427 kernel_pixels[u].opacity));
2428 pixel.index+=(*k)*alpha*kernel_indexes[u];
2431 kernel_pixels+=image->columns+kernel->width;
2432 kernel_indexes+=image->columns+kernel->width;
2434 filter_indexes[x]=ClampToQuantum(gamma*
2435 GetIndexPixelComponent(&pixel));
2441 sync=SyncCacheViewAuthenticPixels(filter_view,exception);
2442 if (sync == MagickFalse)
2444 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2449 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2450 #pragma omp critical (MagickCore_FilterImageChannel)
2452 proceed=SetImageProgress(image,FilterImageTag,progress++,image->rows);
2453 if (proceed == MagickFalse)
2457 filter_image->type=image->type;
2458 filter_view=DestroyCacheView(filter_view);
2459 image_view=DestroyCacheView(image_view);
2460 if (status == MagickFalse)
2461 filter_image=DestroyImage(filter_image);
2462 return(filter_image);
2466 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2470 % G a u s s i a n B l u r I m a g e %
2474 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2476 % GaussianBlurImage() blurs an image. We convolve the image with a
2477 % Gaussian operator of the given radius and standard deviation (sigma).
2478 % For reasonable results, the radius should be larger than sigma. Use a
2479 % radius of 0 and GaussianBlurImage() selects a suitable radius for you
2481 % The format of the GaussianBlurImage method is:
2483 % Image *GaussianBlurImage(const Image *image,onst double radius,
2484 % const double sigma,ExceptionInfo *exception)
2485 % Image *GaussianBlurImageChannel(const Image *image,
2486 % const ChannelType channel,const double radius,const double sigma,
2487 % ExceptionInfo *exception)
2489 % A description of each parameter follows:
2491 % o image: the image.
2493 % o channel: the channel type.
2495 % o radius: the radius of the Gaussian, in pixels, not counting the center
2498 % o sigma: the standard deviation of the Gaussian, in pixels.
2500 % o exception: return any errors or warnings in this structure.
2504 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
2505 const double sigma,ExceptionInfo *exception)
2510 blur_image=GaussianBlurImageChannel(image,DefaultChannels,radius,sigma,
2515 MagickExport Image *GaussianBlurImageChannel(const Image *image,
2516 const ChannelType channel,const double radius,const double sigma,
2517 ExceptionInfo *exception)
2536 assert(image != (const Image *) NULL);
2537 assert(image->signature == MagickSignature);
2538 if (image->debug != MagickFalse)
2539 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2540 assert(exception != (ExceptionInfo *) NULL);
2541 assert(exception->signature == MagickSignature);
2542 width=GetOptimalKernelWidth2D(radius,sigma);
2543 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2544 if (kernel == (double *) NULL)
2545 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2546 j=(ssize_t) width/2;
2548 for (v=(-j); v <= j; v++)
2550 for (u=(-j); u <= j; u++)
2551 kernel[i++]=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
2552 (2.0*MagickPI*MagickSigma*MagickSigma);
2554 blur_image=ConvolveImageChannel(image,channel,width,kernel,exception);
2555 kernel=(double *) RelinquishMagickMemory(kernel);
2560 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2564 % M e d i a n F i l t e r I m a g e %
2568 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2570 % MedianFilterImage() applies a digital filter that improves the quality
2571 % of a noisy image. Each pixel is replaced by the median in a set of
2572 % neighboring pixels as defined by radius.
2574 % The algorithm was contributed by Mike Edmonds and implements an insertion
2575 % sort for selecting median color-channel values. For more on this algorithm
2576 % see "Skip Lists: A probabilistic Alternative to Balanced Trees" by William
2577 % Pugh in the June 1990 of Communications of the ACM.
2579 % The format of the MedianFilterImage method is:
2581 % Image *MedianFilterImage(const Image *image,const double radius,
2582 % ExceptionInfo *exception)
2584 % A description of each parameter follows:
2586 % o image: the image.
2588 % o radius: the radius of the pixel neighborhood.
2590 % o exception: return any errors or warnings in this structure.
2594 #define MedianListChannels 5
2596 typedef struct _MedianListNode
2604 typedef struct _MedianSkipList
2613 typedef struct _MedianPixelList
2621 lists[MedianListChannels];
2624 static MedianPixelList *DestroyMedianPixelList(MedianPixelList *pixel_list)
2629 if (pixel_list == (MedianPixelList *) NULL)
2630 return((MedianPixelList *) NULL);
2631 for (i=0; i < MedianListChannels; i++)
2632 if (pixel_list->lists[i].nodes != (MedianListNode *) NULL)
2633 pixel_list->lists[i].nodes=(MedianListNode *) RelinquishMagickMemory(
2634 pixel_list->lists[i].nodes);
2635 pixel_list=(MedianPixelList *) RelinquishAlignedMemory(pixel_list);
2639 static MedianPixelList **DestroyMedianPixelListThreadSet(
2640 MedianPixelList **pixel_list)
2645 assert(pixel_list != (MedianPixelList **) NULL);
2646 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
2647 if (pixel_list[i] != (MedianPixelList *) NULL)
2648 pixel_list[i]=DestroyMedianPixelList(pixel_list[i]);
2649 pixel_list=(MedianPixelList **) RelinquishAlignedMemory(pixel_list);
2653 static MedianPixelList *AcquireMedianPixelList(const size_t width)
2661 pixel_list=(MedianPixelList *) AcquireAlignedMemory(1,sizeof(*pixel_list));
2662 if (pixel_list == (MedianPixelList *) NULL)
2664 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
2665 pixel_list->center=width*width/2;
2666 for (i=0; i < MedianListChannels; i++)
2668 pixel_list->lists[i].nodes=(MedianListNode *) AcquireQuantumMemory(65537UL,
2669 sizeof(*pixel_list->lists[i].nodes));
2670 if (pixel_list->lists[i].nodes == (MedianListNode *) NULL)
2671 return(DestroyMedianPixelList(pixel_list));
2672 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
2673 sizeof(*pixel_list->lists[i].nodes));
2675 pixel_list->signature=MagickSignature;
2679 static MedianPixelList **AcquireMedianPixelListThreadSet(
2691 number_threads=GetOpenMPMaximumThreads();
2692 pixel_list=(MedianPixelList **) AcquireAlignedMemory(number_threads,
2693 sizeof(*pixel_list));
2694 if (pixel_list == (MedianPixelList **) NULL)
2695 return((MedianPixelList **) NULL);
2696 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
2697 for (i=0; i < (ssize_t) number_threads; i++)
2699 pixel_list[i]=AcquireMedianPixelList(width);
2700 if (pixel_list[i] == (MedianPixelList *) NULL)
2701 return(DestroyMedianPixelListThreadSet(pixel_list));
2706 static void AddNodeMedianPixelList(MedianPixelList *pixel_list,
2707 const ssize_t channel,const size_t color)
2712 register MedianSkipList
2720 Initialize the node.
2722 list=pixel_list->lists+channel;
2723 list->nodes[color].signature=pixel_list->signature;
2724 list->nodes[color].count=1;
2726 Determine where it bessize_ts in the list.
2729 for (level=list->level; level >= 0; level--)
2731 while (list->nodes[search].next[level] < color)
2732 search=list->nodes[search].next[level];
2733 update[level]=search;
2736 Generate a pseudo-random level for this node.
2738 for (level=0; ; level++)
2740 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
2741 if ((pixel_list->seed & 0x300) != 0x300)
2746 if (level > (list->level+2))
2747 level=list->level+2;
2749 If we're raising the list's level, link back to the root node.
2751 while (level > list->level)
2754 update[list->level]=65536UL;
2757 Link the node into the skip-list.
2761 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
2762 list->nodes[update[level]].next[level]=color;
2764 while (level-- > 0);
2767 static MagickPixelPacket GetMedianPixelList(MedianPixelList *pixel_list)
2775 register MedianSkipList
2784 channels[MedianListChannels];
2787 Find the median value for each of the color.
2789 center=pixel_list->center;
2790 for (channel=0; channel < 5; channel++)
2792 list=pixel_list->lists+channel;
2797 color=list->nodes[color].next[0];
2798 count+=list->nodes[color].count;
2800 while (count <= center);
2801 channels[channel]=(unsigned short) color;
2803 GetMagickPixelPacket((const Image *) NULL,&pixel);
2804 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
2805 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
2806 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
2807 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
2808 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
2812 static inline void InsertMedianPixelList(const Image *image,
2813 const PixelPacket *pixel,const IndexPacket *indexes,
2814 MedianPixelList *pixel_list)
2822 index=ScaleQuantumToShort(pixel->red);
2823 signature=pixel_list->lists[0].nodes[index].signature;
2824 if (signature == pixel_list->signature)
2825 pixel_list->lists[0].nodes[index].count++;
2827 AddNodeMedianPixelList(pixel_list,0,index);
2828 index=ScaleQuantumToShort(pixel->green);
2829 signature=pixel_list->lists[1].nodes[index].signature;
2830 if (signature == pixel_list->signature)
2831 pixel_list->lists[1].nodes[index].count++;
2833 AddNodeMedianPixelList(pixel_list,1,index);
2834 index=ScaleQuantumToShort(pixel->blue);
2835 signature=pixel_list->lists[2].nodes[index].signature;
2836 if (signature == pixel_list->signature)
2837 pixel_list->lists[2].nodes[index].count++;
2839 AddNodeMedianPixelList(pixel_list,2,index);
2840 index=ScaleQuantumToShort(pixel->opacity);
2841 signature=pixel_list->lists[3].nodes[index].signature;
2842 if (signature == pixel_list->signature)
2843 pixel_list->lists[3].nodes[index].count++;
2845 AddNodeMedianPixelList(pixel_list,3,index);
2846 if (image->colorspace == CMYKColorspace)
2847 index=ScaleQuantumToShort(*indexes);
2848 signature=pixel_list->lists[4].nodes[index].signature;
2849 if (signature == pixel_list->signature)
2850 pixel_list->lists[4].nodes[index].count++;
2852 AddNodeMedianPixelList(pixel_list,4,index);
2855 static void ResetMedianPixelList(MedianPixelList *pixel_list)
2863 register MedianListNode
2866 register MedianSkipList
2870 Reset the skip-list.
2872 for (channel=0; channel < 5; channel++)
2874 list=pixel_list->lists+channel;
2875 root=list->nodes+65536UL;
2877 for (level=0; level < 9; level++)
2878 root->next[level]=65536UL;
2880 pixel_list->seed=pixel_list->signature++;
2883 MagickExport Image *MedianFilterImage(const Image *image,const double radius,
2884 ExceptionInfo *exception)
2886 #define MedianFilterImageTag "MedianFilter/Image"
2902 **restrict pixel_list;
2911 Initialize median image attributes.
2913 assert(image != (Image *) NULL);
2914 assert(image->signature == MagickSignature);
2915 if (image->debug != MagickFalse)
2916 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2917 assert(exception != (ExceptionInfo *) NULL);
2918 assert(exception->signature == MagickSignature);
2919 width=GetOptimalKernelWidth2D(radius,0.5);
2920 if ((image->columns < width) || (image->rows < width))
2921 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
2922 median_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2924 if (median_image == (Image *) NULL)
2925 return((Image *) NULL);
2926 if (SetImageStorageClass(median_image,DirectClass) == MagickFalse)
2928 InheritException(exception,&median_image->exception);
2929 median_image=DestroyImage(median_image);
2930 return((Image *) NULL);
2932 pixel_list=AcquireMedianPixelListThreadSet(width);
2933 if (pixel_list == (MedianPixelList **) NULL)
2935 median_image=DestroyImage(median_image);
2936 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2939 Median filter each image row.
2943 image_view=AcquireCacheView(image);
2944 median_view=AcquireCacheView(median_image);
2945 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2946 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2948 for (y=0; y < (ssize_t) median_image->rows; y++)
2950 register const IndexPacket
2953 register const PixelPacket
2956 register IndexPacket
2957 *restrict median_indexes;
2963 register PixelPacket
2966 if (status == MagickFalse)
2968 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t) (width/
2969 2L),image->columns+width,width,exception);
2970 q=QueueCacheViewAuthenticPixels(median_view,0,y,median_image->columns,1,
2972 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2977 indexes=GetCacheViewVirtualIndexQueue(image_view);
2978 median_indexes=GetCacheViewAuthenticIndexQueue(median_view);
2979 id=GetOpenMPThreadId();
2980 for (x=0; x < (ssize_t) median_image->columns; x++)
2985 register const PixelPacket
2988 register const IndexPacket
2997 ResetMedianPixelList(pixel_list[id]);
2998 for (v=0; v < (ssize_t) width; v++)
3000 for (u=0; u < (ssize_t) width; u++)
3001 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
3002 r+=image->columns+width;
3003 s+=image->columns+width;
3005 pixel=GetMedianPixelList(pixel_list[id]);
3006 SetPixelPacket(median_image,&pixel,q,median_indexes+x);
3010 if (SyncCacheViewAuthenticPixels(median_view,exception) == MagickFalse)
3012 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3017 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3018 #pragma omp critical (MagickCore_MedianFilterImage)
3020 proceed=SetImageProgress(image,MedianFilterImageTag,progress++,
3022 if (proceed == MagickFalse)
3026 median_view=DestroyCacheView(median_view);
3027 image_view=DestroyCacheView(image_view);
3028 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
3029 return(median_image);
3033 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3037 % M o t i o n B l u r I m a g e %
3041 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3043 % MotionBlurImage() simulates motion blur. We convolve the image with a
3044 % Gaussian operator of the given radius and standard deviation (sigma).
3045 % For reasonable results, radius should be larger than sigma. Use a
3046 % radius of 0 and MotionBlurImage() selects a suitable radius for you.
3047 % Angle gives the angle of the blurring motion.
3049 % Andrew Protano contributed this effect.
3051 % The format of the MotionBlurImage method is:
3053 % Image *MotionBlurImage(const Image *image,const double radius,
3054 % const double sigma,const double angle,ExceptionInfo *exception)
3055 % Image *MotionBlurImageChannel(const Image *image,const ChannelType channel,
3056 % const double radius,const double sigma,const double angle,
3057 % ExceptionInfo *exception)
3059 % A description of each parameter follows:
3061 % o image: the image.
3063 % o channel: the channel type.
3065 % o radius: the radius of the Gaussian, in pixels, not counting the center
3066 % o radius: the radius of the Gaussian, in pixels, not counting
3069 % o sigma: the standard deviation of the Gaussian, in pixels.
3071 % o angle: Apply the effect along this angle.
3073 % o exception: return any errors or warnings in this structure.
3077 static double *GetMotionBlurKernel(const size_t width,const double sigma)
3087 Generate a 1-D convolution kernel.
3089 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3090 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
3091 if (kernel == (double *) NULL)
3094 for (i=0; i < (ssize_t) width; i++)
3096 kernel[i]=exp((-((double) i*i)/(double) (2.0*MagickSigma*MagickSigma)))/
3097 (MagickSQ2PI*MagickSigma);
3098 normalize+=kernel[i];
3100 for (i=0; i < (ssize_t) width; i++)
3101 kernel[i]/=normalize;
3105 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
3106 const double sigma,const double angle,ExceptionInfo *exception)
3111 motion_blur=MotionBlurImageChannel(image,DefaultChannels,radius,sigma,angle,
3113 return(motion_blur);
3116 MagickExport Image *MotionBlurImageChannel(const Image *image,
3117 const ChannelType channel,const double radius,const double sigma,
3118 const double angle,ExceptionInfo *exception)
3154 assert(image != (Image *) NULL);
3155 assert(image->signature == MagickSignature);
3156 if (image->debug != MagickFalse)
3157 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3158 assert(exception != (ExceptionInfo *) NULL);
3159 width=GetOptimalKernelWidth1D(radius,sigma);
3160 kernel=GetMotionBlurKernel(width,sigma);
3161 if (kernel == (double *) NULL)
3162 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3163 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
3164 if (offset == (OffsetInfo *) NULL)
3166 kernel=(double *) RelinquishMagickMemory(kernel);
3167 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3169 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3170 if (blur_image == (Image *) NULL)
3172 kernel=(double *) RelinquishMagickMemory(kernel);
3173 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3174 return((Image *) NULL);
3176 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3178 kernel=(double *) RelinquishMagickMemory(kernel);
3179 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3180 InheritException(exception,&blur_image->exception);
3181 blur_image=DestroyImage(blur_image);
3182 return((Image *) NULL);
3184 point.x=(double) width*sin(DegreesToRadians(angle));
3185 point.y=(double) width*cos(DegreesToRadians(angle));
3186 for (i=0; i < (ssize_t) width; i++)
3188 offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
3189 offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
3196 GetMagickPixelPacket(image,&bias);
3197 image_view=AcquireCacheView(image);
3198 blur_view=AcquireCacheView(blur_image);
3199 #if defined(MAGICKCORE_OPENMP_SUPPORT) && defined(MAGICKCORE_FUTURE)
3200 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3202 for (y=0; y < (ssize_t) image->rows; y++)
3204 register IndexPacket
3205 *restrict blur_indexes;
3210 register PixelPacket
3213 if (status == MagickFalse)
3215 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3217 if (q == (PixelPacket *) NULL)
3222 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
3223 for (x=0; x < (ssize_t) image->columns; x++)
3237 register const IndexPacket
3242 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
3244 for (i=0; i < (ssize_t) width; i++)
3246 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
3247 offset[i].y,&pixel,exception);
3248 qixel.red+=(*k)*pixel.red;
3249 qixel.green+=(*k)*pixel.green;
3250 qixel.blue+=(*k)*pixel.blue;
3251 qixel.opacity+=(*k)*pixel.opacity;
3252 if (image->colorspace == CMYKColorspace)
3254 indexes=GetCacheViewVirtualIndexQueue(image_view);
3255 qixel.index+=(*k)*(*indexes);
3259 if ((channel & RedChannel) != 0)
3260 q->red=ClampToQuantum(qixel.red);
3261 if ((channel & GreenChannel) != 0)
3262 q->green=ClampToQuantum(qixel.green);
3263 if ((channel & BlueChannel) != 0)
3264 q->blue=ClampToQuantum(qixel.blue);
3265 if ((channel & OpacityChannel) != 0)
3266 q->opacity=ClampToQuantum(qixel.opacity);
3267 if (((channel & IndexChannel) != 0) &&
3268 (image->colorspace == CMYKColorspace))
3269 blur_indexes[x]=(IndexPacket) ClampToQuantum(qixel.index);
3279 for (i=0; i < (ssize_t) width; i++)
3281 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
3282 offset[i].y,&pixel,exception);
3283 alpha=(MagickRealType) (QuantumScale*
3284 GetAlphaPixelComponent(&pixel));
3285 qixel.red+=(*k)*alpha*pixel.red;
3286 qixel.green+=(*k)*alpha*pixel.green;
3287 qixel.blue+=(*k)*alpha*pixel.blue;
3288 qixel.opacity+=(*k)*pixel.opacity;
3289 if (image->colorspace == CMYKColorspace)
3291 indexes=GetCacheViewVirtualIndexQueue(image_view);
3292 qixel.index+=(*k)*alpha*(*indexes);
3297 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3298 if ((channel & RedChannel) != 0)
3299 q->red=ClampToQuantum(gamma*qixel.red);
3300 if ((channel & GreenChannel) != 0)
3301 q->green=ClampToQuantum(gamma*qixel.green);
3302 if ((channel & BlueChannel) != 0)
3303 q->blue=ClampToQuantum(gamma*qixel.blue);
3304 if ((channel & OpacityChannel) != 0)
3305 q->opacity=ClampToQuantum(qixel.opacity);
3306 if (((channel & IndexChannel) != 0) &&
3307 (image->colorspace == CMYKColorspace))
3308 blur_indexes[x]=(IndexPacket) ClampToQuantum(gamma*qixel.index);
3312 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3314 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3319 #if defined(MAGICKCORE_OPENMP_SUPPORT) && defined(MAGICKCORE_FUTURE)
3320 #pragma omp critical (MagickCore_MotionBlurImageChannel)
3322 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3323 if (proceed == MagickFalse)
3327 blur_view=DestroyCacheView(blur_view);
3328 image_view=DestroyCacheView(image_view);
3329 kernel=(double *) RelinquishMagickMemory(kernel);
3330 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
3331 if (status == MagickFalse)
3332 blur_image=DestroyImage(blur_image);
3337 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3341 % P r e v i e w I m a g e %
3345 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3347 % PreviewImage() tiles 9 thumbnails of the specified image with an image
3348 % processing operation applied with varying parameters. This may be helpful
3349 % pin-pointing an appropriate parameter for a particular image processing
3352 % The format of the PreviewImages method is:
3354 % Image *PreviewImages(const Image *image,const PreviewType preview,
3355 % ExceptionInfo *exception)
3357 % A description of each parameter follows:
3359 % o image: the image.
3361 % o preview: the image processing operation.
3363 % o exception: return any errors or warnings in this structure.
3366 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
3367 ExceptionInfo *exception)
3369 #define NumberTiles 9
3370 #define PreviewImageTag "Preview/Image"
3371 #define DefaultPreviewGeometry "204x204+10+10"
3374 factor[MaxTextExtent],
3375 label[MaxTextExtent];
3417 Open output image file.
3419 assert(image != (Image *) NULL);
3420 assert(image->signature == MagickSignature);
3421 if (image->debug != MagickFalse)
3422 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3426 preview_info=AcquireImageInfo();
3427 SetGeometry(image,&geometry);
3428 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
3429 &geometry.width,&geometry.height);
3430 images=NewImageList();
3432 GetQuantizeInfo(&quantize_info);
3438 for (i=0; i < NumberTiles; i++)
3440 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
3441 if (thumbnail == (Image *) NULL)
3443 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
3445 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
3446 if (i == (NumberTiles/2))
3448 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
3449 AppendImageToList(&images,thumbnail);
3457 preview_image=RotateImage(thumbnail,degrees,exception);
3458 (void) FormatMagickString(label,MaxTextExtent,"rotate %g",degrees);
3464 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
3465 (void) FormatMagickString(label,MaxTextExtent,"shear %gx%g",
3466 degrees,2.0*degrees);
3471 x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
3472 y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
3473 preview_image=RollImage(thumbnail,x,y,exception);
3474 (void) FormatMagickString(label,MaxTextExtent,"roll %ldx%ld",(long) x,
3480 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3481 if (preview_image == (Image *) NULL)
3483 (void) FormatMagickString(factor,MaxTextExtent,"100,100,%g",
3485 (void) ModulateImage(preview_image,factor);
3486 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3489 case SaturationPreview:
3491 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3492 if (preview_image == (Image *) NULL)
3494 (void) FormatMagickString(factor,MaxTextExtent,"100,%g",
3496 (void) ModulateImage(preview_image,factor);
3497 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3500 case BrightnessPreview:
3502 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3503 if (preview_image == (Image *) NULL)
3505 (void) FormatMagickString(factor,MaxTextExtent,"%g",2.0*percentage);
3506 (void) ModulateImage(preview_image,factor);
3507 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3513 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3514 if (preview_image == (Image *) NULL)
3517 (void) GammaImageChannel(preview_image,DefaultChannels,gamma);
3518 (void) FormatMagickString(label,MaxTextExtent,"gamma %g",gamma);
3523 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3524 if (preview_image != (Image *) NULL)
3525 for (x=0; x < i; x++)
3526 (void) ContrastImage(preview_image,MagickTrue);
3527 (void) FormatMagickString(label,MaxTextExtent,"contrast (%ld)",(long)
3533 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3534 if (preview_image == (Image *) NULL)
3536 for (x=0; x < i; x++)
3537 (void) ContrastImage(preview_image,MagickFalse);
3538 (void) FormatMagickString(label,MaxTextExtent,"+contrast (%ld)",
3542 case GrayscalePreview:
3544 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3545 if (preview_image == (Image *) NULL)
3548 quantize_info.number_colors=colors;
3549 quantize_info.colorspace=GRAYColorspace;
3550 (void) QuantizeImage(&quantize_info,preview_image);
3551 (void) FormatMagickString(label,MaxTextExtent,
3552 "-colorspace gray -colors %lu",(unsigned long) colors);
3555 case QuantizePreview:
3557 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3558 if (preview_image == (Image *) NULL)
3561 quantize_info.number_colors=colors;
3562 (void) QuantizeImage(&quantize_info,preview_image);
3563 (void) FormatMagickString(label,MaxTextExtent,"colors %lu",
3564 (unsigned long) colors);
3567 case DespecklePreview:
3569 for (x=0; x < (i-1); x++)
3571 preview_image=DespeckleImage(thumbnail,exception);
3572 if (preview_image == (Image *) NULL)
3574 thumbnail=DestroyImage(thumbnail);
3575 thumbnail=preview_image;
3577 preview_image=DespeckleImage(thumbnail,exception);
3578 if (preview_image == (Image *) NULL)
3580 (void) FormatMagickString(label,MaxTextExtent,"despeckle (%ld)",(long)
3584 case ReduceNoisePreview:
3586 preview_image=ReduceNoiseImage(thumbnail,radius,exception);
3587 (void) FormatMagickString(label,MaxTextExtent,"noise %g",radius);
3590 case AddNoisePreview:
3596 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
3601 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
3606 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
3611 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
3616 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
3621 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
3626 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
3630 preview_image=ReduceNoiseImage(thumbnail,(double) i,exception);
3631 (void) FormatMagickString(label,MaxTextExtent,"+noise %s",factor);
3634 case SharpenPreview:
3636 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
3637 (void) FormatMagickString(label,MaxTextExtent,"sharpen %gx%g",
3643 preview_image=BlurImage(thumbnail,radius,sigma,exception);
3644 (void) FormatMagickString(label,MaxTextExtent,"blur %gx%g",radius,
3648 case ThresholdPreview:
3650 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3651 if (preview_image == (Image *) NULL)
3653 (void) BilevelImage(thumbnail,
3654 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3655 (void) FormatMagickString(label,MaxTextExtent,"threshold %g",
3656 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3659 case EdgeDetectPreview:
3661 preview_image=EdgeImage(thumbnail,radius,exception);
3662 (void) FormatMagickString(label,MaxTextExtent,"edge %g",radius);
3667 preview_image=SpreadImage(thumbnail,radius,exception);
3668 (void) FormatMagickString(label,MaxTextExtent,"spread %g",
3672 case SolarizePreview:
3674 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3675 if (preview_image == (Image *) NULL)
3677 (void) SolarizeImage(preview_image,(double) QuantumRange*
3679 (void) FormatMagickString(label,MaxTextExtent,"solarize %g",
3680 (QuantumRange*percentage)/100.0);
3686 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
3688 (void) FormatMagickString(label,MaxTextExtent,"shade %gx%g",
3694 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3695 if (preview_image == (Image *) NULL)
3697 geometry.width=(size_t) (2*i+2);
3698 geometry.height=(size_t) (2*i+2);
3701 (void) RaiseImage(preview_image,&geometry,MagickTrue);
3702 (void) FormatMagickString(label,MaxTextExtent,"raise %lux%lu%+ld%+ld",
3703 (unsigned long) geometry.width,(unsigned long) geometry.height,
3704 (long) geometry.x,(long) geometry.y);
3707 case SegmentPreview:
3709 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3710 if (preview_image == (Image *) NULL)
3713 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
3715 (void) FormatMagickString(label,MaxTextExtent,"segment %gx%g",
3716 threshold,threshold);
3721 preview_image=SwirlImage(thumbnail,degrees,exception);
3722 (void) FormatMagickString(label,MaxTextExtent,"swirl %g",degrees);
3726 case ImplodePreview:
3729 preview_image=ImplodeImage(thumbnail,degrees,exception);
3730 (void) FormatMagickString(label,MaxTextExtent,"implode %g",degrees);
3736 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
3737 (void) FormatMagickString(label,MaxTextExtent,"wave %gx%g",
3738 0.5*degrees,2.0*degrees);
3741 case OilPaintPreview:
3743 preview_image=OilPaintImage(thumbnail,(double) radius,exception);
3744 (void) FormatMagickString(label,MaxTextExtent,"paint %g",radius);
3747 case CharcoalDrawingPreview:
3749 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
3751 (void) FormatMagickString(label,MaxTextExtent,"charcoal %gx%g",
3758 filename[MaxTextExtent];
3766 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3767 if (preview_image == (Image *) NULL)
3769 preview_info->quality=(size_t) percentage;
3770 (void) FormatMagickString(factor,MaxTextExtent,"%lu",
3771 (unsigned long) preview_info->quality);
3772 file=AcquireUniqueFileResource(filename);
3775 (void) FormatMagickString(preview_image->filename,MaxTextExtent,
3776 "jpeg:%s",filename);
3777 status=WriteImage(preview_info,preview_image);
3778 if (status != MagickFalse)
3783 (void) CopyMagickString(preview_info->filename,
3784 preview_image->filename,MaxTextExtent);
3785 quality_image=ReadImage(preview_info,exception);
3786 if (quality_image != (Image *) NULL)
3788 preview_image=DestroyImage(preview_image);
3789 preview_image=quality_image;
3792 (void) RelinquishUniqueFileResource(preview_image->filename);
3793 if ((GetBlobSize(preview_image)/1024) >= 1024)
3794 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gmb ",
3795 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
3798 if (GetBlobSize(preview_image) >= 1024)
3799 (void) FormatMagickString(label,MaxTextExtent,
3800 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
3801 GetBlobSize(preview_image))/1024.0);
3803 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%lub ",
3804 factor,(unsigned long) GetBlobSize(thumbnail));
3808 thumbnail=DestroyImage(thumbnail);
3812 if (preview_image == (Image *) NULL)
3814 (void) DeleteImageProperty(preview_image,"label");
3815 (void) SetImageProperty(preview_image,"label",label);
3816 AppendImageToList(&images,preview_image);
3817 proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
3819 if (proceed == MagickFalse)
3822 if (images == (Image *) NULL)
3824 preview_info=DestroyImageInfo(preview_info);
3825 return((Image *) NULL);
3830 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
3831 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
3832 montage_info->shadow=MagickTrue;
3833 (void) CloneString(&montage_info->tile,"3x3");
3834 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
3835 (void) CloneString(&montage_info->frame,DefaultTileFrame);
3836 montage_image=MontageImages(images,montage_info,exception);
3837 montage_info=DestroyMontageInfo(montage_info);
3838 images=DestroyImageList(images);
3839 if (montage_image == (Image *) NULL)
3840 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3841 if (montage_image->montage != (char *) NULL)
3844 Free image directory.
3846 montage_image->montage=(char *) RelinquishMagickMemory(
3847 montage_image->montage);
3848 if (image->directory != (char *) NULL)
3849 montage_image->directory=(char *) RelinquishMagickMemory(
3850 montage_image->directory);
3852 preview_info=DestroyImageInfo(preview_info);
3853 return(montage_image);
3857 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3861 % R a d i a l B l u r I m a g e %
3865 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3867 % RadialBlurImage() applies a radial blur to the image.
3869 % Andrew Protano contributed this effect.
3871 % The format of the RadialBlurImage method is:
3873 % Image *RadialBlurImage(const Image *image,const double angle,
3874 % ExceptionInfo *exception)
3875 % Image *RadialBlurImageChannel(const Image *image,const ChannelType channel,
3876 % const double angle,ExceptionInfo *exception)
3878 % A description of each parameter follows:
3880 % o image: the image.
3882 % o channel: the channel type.
3884 % o angle: the angle of the radial blur.
3886 % o exception: return any errors or warnings in this structure.
3890 MagickExport Image *RadialBlurImage(const Image *image,const double angle,
3891 ExceptionInfo *exception)
3896 blur_image=RadialBlurImageChannel(image,DefaultChannels,angle,exception);
3900 MagickExport Image *RadialBlurImageChannel(const Image *image,
3901 const ChannelType channel,const double angle,ExceptionInfo *exception)
3939 Allocate blur image.
3941 assert(image != (Image *) NULL);
3942 assert(image->signature == MagickSignature);
3943 if (image->debug != MagickFalse)
3944 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3945 assert(exception != (ExceptionInfo *) NULL);
3946 assert(exception->signature == MagickSignature);
3947 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3948 if (blur_image == (Image *) NULL)
3949 return((Image *) NULL);
3950 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3952 InheritException(exception,&blur_image->exception);
3953 blur_image=DestroyImage(blur_image);
3954 return((Image *) NULL);
3956 blur_center.x=(double) image->columns/2.0;
3957 blur_center.y=(double) image->rows/2.0;
3958 blur_radius=hypot(blur_center.x,blur_center.y);
3959 n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+
3961 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
3962 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3963 sizeof(*cos_theta));
3964 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3965 sizeof(*sin_theta));
3966 if ((cos_theta == (MagickRealType *) NULL) ||
3967 (sin_theta == (MagickRealType *) NULL))
3969 blur_image=DestroyImage(blur_image);
3970 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3972 offset=theta*(MagickRealType) (n-1)/2.0;
3973 for (i=0; i < (ssize_t) n; i++)
3975 cos_theta[i]=cos((double) (theta*i-offset));
3976 sin_theta[i]=sin((double) (theta*i-offset));
3983 GetMagickPixelPacket(image,&bias);
3984 image_view=AcquireCacheView(image);
3985 blur_view=AcquireCacheView(blur_image);
3986 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3987 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3989 for (y=0; y < (ssize_t) blur_image->rows; y++)
3991 register const IndexPacket
3994 register IndexPacket
3995 *restrict blur_indexes;
4000 register PixelPacket
4003 if (status == MagickFalse)
4005 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
4007 if (q == (PixelPacket *) NULL)
4012 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
4013 for (x=0; x < (ssize_t) blur_image->columns; x++)
4034 center.x=(double) x-blur_center.x;
4035 center.y=(double) y-blur_center.y;
4036 radius=hypot((double) center.x,center.y);
4041 step=(size_t) (blur_radius/radius);
4050 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
4052 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
4054 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
4055 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
4056 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
4057 cos_theta[i]+0.5),&pixel,exception);
4058 qixel.red+=pixel.red;
4059 qixel.green+=pixel.green;
4060 qixel.blue+=pixel.blue;
4061 qixel.opacity+=pixel.opacity;
4062 if (image->colorspace == CMYKColorspace)
4064 indexes=GetCacheViewVirtualIndexQueue(image_view);
4065 qixel.index+=(*indexes);
4069 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
4071 if ((channel & RedChannel) != 0)
4072 q->red=ClampToQuantum(normalize*qixel.red);
4073 if ((channel & GreenChannel) != 0)
4074 q->green=ClampToQuantum(normalize*qixel.green);
4075 if ((channel & BlueChannel) != 0)
4076 q->blue=ClampToQuantum(normalize*qixel.blue);
4077 if ((channel & OpacityChannel) != 0)
4078 q->opacity=ClampToQuantum(normalize*qixel.opacity);
4079 if (((channel & IndexChannel) != 0) &&
4080 (image->colorspace == CMYKColorspace))
4081 blur_indexes[x]=(IndexPacket) ClampToQuantum(normalize*qixel.index);
4091 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
4093 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
4094 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
4095 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
4096 cos_theta[i]+0.5),&pixel,exception);
4097 alpha=(MagickRealType) (QuantumScale*
4098 GetAlphaPixelComponent(&pixel));
4099 qixel.red+=alpha*pixel.red;
4100 qixel.green+=alpha*pixel.green;
4101 qixel.blue+=alpha*pixel.blue;
4102 qixel.opacity+=pixel.opacity;
4103 if (image->colorspace == CMYKColorspace)
4105 indexes=GetCacheViewVirtualIndexQueue(image_view);
4106 qixel.index+=alpha*(*indexes);
4111 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4112 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
4114 if ((channel & RedChannel) != 0)
4115 q->red=ClampToQuantum(gamma*qixel.red);
4116 if ((channel & GreenChannel) != 0)
4117 q->green=ClampToQuantum(gamma*qixel.green);
4118 if ((channel & BlueChannel) != 0)
4119 q->blue=ClampToQuantum(gamma*qixel.blue);
4120 if ((channel & OpacityChannel) != 0)
4121 q->opacity=ClampToQuantum(normalize*qixel.opacity);
4122 if (((channel & IndexChannel) != 0) &&
4123 (image->colorspace == CMYKColorspace))
4124 blur_indexes[x]=(IndexPacket) ClampToQuantum(gamma*qixel.index);
4128 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
4130 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4135 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4136 #pragma omp critical (MagickCore_RadialBlurImageChannel)
4138 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
4139 if (proceed == MagickFalse)
4143 blur_view=DestroyCacheView(blur_view);
4144 image_view=DestroyCacheView(image_view);
4145 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
4146 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
4147 if (status == MagickFalse)
4148 blur_image=DestroyImage(blur_image);
4153 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4157 % R e d u c e N o i s e I m a g e %
4161 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4163 % ReduceNoiseImage() smooths the contours of an image while still preserving
4164 % edge information. The algorithm works by replacing each pixel with its
4165 % neighbor closest in value. A neighbor is defined by radius. Use a radius
4166 % of 0 and ReduceNoise() selects a suitable radius for you.
4168 % The format of the ReduceNoiseImage method is:
4170 % Image *ReduceNoiseImage(const Image *image,const double radius,
4171 % ExceptionInfo *exception)
4173 % A description of each parameter follows:
4175 % o image: the image.
4177 % o radius: the radius of the pixel neighborhood.
4179 % o exception: return any errors or warnings in this structure.
4183 static MagickPixelPacket GetNonpeakMedianPixelList(MedianPixelList *pixel_list)
4191 register MedianSkipList
4205 Finds the median value for each of the color.
4207 center=pixel_list->center;
4208 for (channel=0; channel < 5; channel++)
4210 list=pixel_list->lists+channel;
4212 next=list->nodes[color].next[0];
4218 next=list->nodes[color].next[0];
4219 count+=list->nodes[color].count;
4221 while (count <= center);
4222 if ((previous == 65536UL) && (next != 65536UL))
4225 if ((previous != 65536UL) && (next == 65536UL))
4227 channels[channel]=(unsigned short) color;
4229 GetMagickPixelPacket((const Image *) NULL,&pixel);
4230 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4231 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4232 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
4233 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
4234 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
4238 MagickExport Image *ReduceNoiseImage(const Image *image,const double radius,
4239 ExceptionInfo *exception)
4241 #define ReduceNoiseImageTag "ReduceNoise/Image"
4257 **restrict pixel_list;
4266 Initialize noise image attributes.
4268 assert(image != (Image *) NULL);
4269 assert(image->signature == MagickSignature);
4270 if (image->debug != MagickFalse)
4271 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4272 assert(exception != (ExceptionInfo *) NULL);
4273 assert(exception->signature == MagickSignature);
4274 width=GetOptimalKernelWidth2D(radius,0.5);
4275 if ((image->columns < width) || (image->rows < width))
4276 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
4277 noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4279 if (noise_image == (Image *) NULL)
4280 return((Image *) NULL);
4281 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
4283 InheritException(exception,&noise_image->exception);
4284 noise_image=DestroyImage(noise_image);
4285 return((Image *) NULL);
4287 pixel_list=AcquireMedianPixelListThreadSet(width);
4288 if (pixel_list == (MedianPixelList **) NULL)
4290 noise_image=DestroyImage(noise_image);
4291 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4298 image_view=AcquireCacheView(image);
4299 noise_view=AcquireCacheView(noise_image);
4300 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4301 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4303 for (y=0; y < (ssize_t) noise_image->rows; y++)
4305 register const IndexPacket
4308 register const PixelPacket
4311 register IndexPacket
4312 *restrict noise_indexes;
4318 register PixelPacket
4321 if (status == MagickFalse)
4323 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t) (width/
4324 2L),image->columns+width,width,exception);
4325 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
4327 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4332 indexes=GetCacheViewVirtualIndexQueue(image_view);
4333 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
4334 id=GetOpenMPThreadId();
4335 for (x=0; x < (ssize_t) noise_image->columns; x++)
4340 register const PixelPacket
4343 register const IndexPacket
4352 ResetMedianPixelList(pixel_list[id]);
4353 for (v=0; v < (ssize_t) width; v++)
4355 for (u=0; u < (ssize_t) width; u++)
4356 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
4357 r+=image->columns+width;
4358 s+=image->columns+width;
4360 pixel=GetNonpeakMedianPixelList(pixel_list[id]);
4361 SetPixelPacket(noise_image,&pixel,q,noise_indexes+x);
4365 if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
4367 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4372 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4373 #pragma omp critical (MagickCore_ReduceNoiseImage)
4375 proceed=SetImageProgress(image,ReduceNoiseImageTag,progress++,
4377 if (proceed == MagickFalse)
4381 noise_view=DestroyCacheView(noise_view);
4382 image_view=DestroyCacheView(image_view);
4383 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
4384 return(noise_image);
4388 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4392 % S e l e c t i v e B l u r I m a g e %
4396 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4398 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
4399 % It is similar to the unsharpen mask that sharpens everything with contrast
4400 % above a certain threshold.
4402 % The format of the SelectiveBlurImage method is:
4404 % Image *SelectiveBlurImage(const Image *image,const double radius,
4405 % const double sigma,const double threshold,ExceptionInfo *exception)
4406 % Image *SelectiveBlurImageChannel(const Image *image,
4407 % const ChannelType channel,const double radius,const double sigma,
4408 % const double threshold,ExceptionInfo *exception)
4410 % A description of each parameter follows:
4412 % o image: the image.
4414 % o channel: the channel type.
4416 % o radius: the radius of the Gaussian, in pixels, not counting the center
4419 % o sigma: the standard deviation of the Gaussian, in pixels.
4421 % o threshold: only pixels within this contrast threshold are included
4422 % in the blur operation.
4424 % o exception: return any errors or warnings in this structure.
4428 static inline MagickBooleanType SelectiveContrast(const PixelPacket *p,
4429 const PixelPacket *q,const double threshold)
4431 if (fabs(PixelIntensity(p)-PixelIntensity(q)) < threshold)
4433 return(MagickFalse);
4436 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
4437 const double sigma,const double threshold,ExceptionInfo *exception)
4442 blur_image=SelectiveBlurImageChannel(image,DefaultChannels,radius,sigma,
4443 threshold,exception);
4447 MagickExport Image *SelectiveBlurImageChannel(const Image *image,
4448 const ChannelType channel,const double radius,const double sigma,
4449 const double threshold,ExceptionInfo *exception)
4451 #define SelectiveBlurImageTag "SelectiveBlur/Image"
4485 Initialize blur image attributes.
4487 assert(image != (Image *) NULL);
4488 assert(image->signature == MagickSignature);
4489 if (image->debug != MagickFalse)
4490 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4491 assert(exception != (ExceptionInfo *) NULL);
4492 assert(exception->signature == MagickSignature);
4493 width=GetOptimalKernelWidth1D(radius,sigma);
4494 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
4495 if (kernel == (double *) NULL)
4496 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4497 j=(ssize_t) width/2;
4499 for (v=(-j); v <= j; v++)
4501 for (u=(-j); u <= j; u++)
4502 kernel[i++]=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
4503 (2.0*MagickPI*MagickSigma*MagickSigma);
4505 if (image->debug != MagickFalse)
4508 format[MaxTextExtent],
4515 register const double
4518 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
4519 " SelectiveBlurImage with %lux%lu kernel:",(unsigned long) width,
4520 (unsigned long) width);
4521 message=AcquireString("");
4523 for (v=0; v < (ssize_t) width; v++)
4526 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",(long) v);
4527 (void) ConcatenateString(&message,format);
4528 for (u=0; u < (ssize_t) width; u++)
4530 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
4531 (void) ConcatenateString(&message,format);
4533 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
4535 message=DestroyString(message);
4537 blur_image=CloneImage(image,0,0,MagickTrue,exception);
4538 if (blur_image == (Image *) NULL)
4539 return((Image *) NULL);
4540 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
4542 InheritException(exception,&blur_image->exception);
4543 blur_image=DestroyImage(blur_image);
4544 return((Image *) NULL);
4547 Threshold blur image.
4551 GetMagickPixelPacket(image,&bias);
4552 SetMagickPixelPacketBias(image,&bias);
4553 image_view=AcquireCacheView(image);
4554 blur_view=AcquireCacheView(blur_image);
4555 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4556 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4558 for (y=0; y < (ssize_t) image->rows; y++)
4566 register const IndexPacket
4569 register const PixelPacket
4572 register IndexPacket
4573 *restrict blur_indexes;
4578 register PixelPacket
4581 if (status == MagickFalse)
4583 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t) (width/
4584 2L),image->columns+width,width,exception);
4585 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
4587 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4592 indexes=GetCacheViewVirtualIndexQueue(image_view);
4593 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
4594 for (x=0; x < (ssize_t) image->columns; x++)
4603 register const double
4613 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
4615 for (v=0; v < (ssize_t) width; v++)
4617 for (u=0; u < (ssize_t) width; u++)
4619 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4621 pixel.red+=(*k)*(p+u+j)->red;
4622 pixel.green+=(*k)*(p+u+j)->green;
4623 pixel.blue+=(*k)*(p+u+j)->blue;
4628 j+=image->columns+width;
4632 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4633 if ((channel & RedChannel) != 0)
4634 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
4635 if ((channel & GreenChannel) != 0)
4636 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
4637 if ((channel & BlueChannel) != 0)
4638 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
4640 if ((channel & OpacityChannel) != 0)
4644 for (v=0; v < (ssize_t) width; v++)
4646 for (u=0; u < (ssize_t) width; u++)
4648 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4650 pixel.opacity+=(*k)*(p+u+j)->opacity;
4655 j+=(ssize_t) (image->columns+width);
4659 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4661 SetOpacityPixelComponent(q,ClampToQuantum(gamma*
4662 GetOpacityPixelComponent(&pixel)));
4665 if (((channel & IndexChannel) != 0) &&
4666 (image->colorspace == CMYKColorspace))
4670 for (v=0; v < (ssize_t) width; v++)
4672 for (u=0; u < (ssize_t) width; u++)
4674 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4676 pixel.index+=(*k)*indexes[x+u+j];
4681 j+=(ssize_t) (image->columns+width);
4685 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4687 blur_indexes[x]=ClampToQuantum(gamma*
4688 GetIndexPixelComponent(&pixel));
4697 for (v=0; v < (ssize_t) width; v++)
4699 for (u=0; u < (ssize_t) width; u++)
4701 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4703 alpha=(MagickRealType) (QuantumScale*
4704 GetAlphaPixelComponent(p+u+j));
4705 pixel.red+=(*k)*alpha*(p+u+j)->red;
4706 pixel.green+=(*k)*alpha*(p+u+j)->green;
4707 pixel.blue+=(*k)*alpha*(p+u+j)->blue;
4708 pixel.opacity+=(*k)*(p+u+j)->opacity;
4713 j+=(ssize_t) (image->columns+width);
4717 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4718 if ((channel & RedChannel) != 0)
4719 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
4720 if ((channel & GreenChannel) != 0)
4721 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
4722 if ((channel & BlueChannel) != 0)
4723 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
4725 if ((channel & OpacityChannel) != 0)
4729 for (v=0; v < (ssize_t) width; v++)
4731 for (u=0; u < (ssize_t) width; u++)
4733 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4735 pixel.opacity+=(*k)*(p+u+j)->opacity;
4740 j+=(ssize_t) (image->columns+width);
4744 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4746 SetOpacityPixelComponent(q,
4747 ClampOpacityPixelComponent(&pixel));
4750 if (((channel & IndexChannel) != 0) &&
4751 (image->colorspace == CMYKColorspace))
4755 for (v=0; v < (ssize_t) width; v++)
4757 for (u=0; u < (ssize_t) width; u++)
4759 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4761 alpha=(MagickRealType) (QuantumScale*
4762 GetAlphaPixelComponent(p+u+j));
4763 pixel.index+=(*k)*alpha*indexes[x+u+j];
4768 j+=(ssize_t) (image->columns+width);
4772 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4774 blur_indexes[x]=ClampToQuantum(gamma*
4775 GetIndexPixelComponent(&pixel));
4782 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
4783 if (sync == MagickFalse)
4785 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4790 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4791 #pragma omp critical (MagickCore_SelectiveBlurImageChannel)
4793 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
4795 if (proceed == MagickFalse)
4799 blur_image->type=image->type;
4800 blur_view=DestroyCacheView(blur_view);
4801 image_view=DestroyCacheView(image_view);
4802 kernel=(double *) RelinquishMagickMemory(kernel);
4803 if (status == MagickFalse)
4804 blur_image=DestroyImage(blur_image);
4809 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4813 % S h a d e I m a g e %
4817 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4819 % ShadeImage() shines a distant light on an image to create a
4820 % three-dimensional effect. You control the positioning of the light with
4821 % azimuth and elevation; azimuth is measured in degrees off the x axis
4822 % and elevation is measured in pixels above the Z axis.
4824 % The format of the ShadeImage method is:
4826 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4827 % const double azimuth,const double elevation,ExceptionInfo *exception)
4829 % A description of each parameter follows:
4831 % o image: the image.
4833 % o gray: A value other than zero shades the intensity of each pixel.
4835 % o azimuth, elevation: Define the light source direction.
4837 % o exception: return any errors or warnings in this structure.
4840 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4841 const double azimuth,const double elevation,ExceptionInfo *exception)
4843 #define ShadeImageTag "Shade/Image"
4865 Initialize shaded image attributes.
4867 assert(image != (const Image *) NULL);
4868 assert(image->signature == MagickSignature);
4869 if (image->debug != MagickFalse)
4870 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4871 assert(exception != (ExceptionInfo *) NULL);
4872 assert(exception->signature == MagickSignature);
4873 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4874 if (shade_image == (Image *) NULL)
4875 return((Image *) NULL);
4876 if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
4878 InheritException(exception,&shade_image->exception);
4879 shade_image=DestroyImage(shade_image);
4880 return((Image *) NULL);
4883 Compute the light vector.
4885 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
4886 cos(DegreesToRadians(elevation));
4887 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
4888 cos(DegreesToRadians(elevation));
4889 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
4895 image_view=AcquireCacheView(image);
4896 shade_view=AcquireCacheView(shade_image);
4897 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4898 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4900 for (y=0; y < (ssize_t) image->rows; y++)
4910 register const PixelPacket
4919 register PixelPacket
4922 if (status == MagickFalse)
4924 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
4925 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
4927 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4933 Shade this row of pixels.
4935 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
4937 s1=s0+image->columns+2;
4938 s2=s1+image->columns+2;
4939 for (x=0; x < (ssize_t) image->columns; x++)
4942 Determine the surface normal and compute shading.
4944 normal.x=(double) (PixelIntensity(s0-1)+PixelIntensity(s1-1)+
4945 PixelIntensity(s2-1)-PixelIntensity(s0+1)-PixelIntensity(s1+1)-
4946 PixelIntensity(s2+1));
4947 normal.y=(double) (PixelIntensity(s2-1)+PixelIntensity(s2)+
4948 PixelIntensity(s2+1)-PixelIntensity(s0-1)-PixelIntensity(s0)-
4949 PixelIntensity(s0+1));
4950 if ((normal.x == 0.0) && (normal.y == 0.0))
4955 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
4956 if (distance > MagickEpsilon)
4959 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
4960 if (normal_distance > (MagickEpsilon*MagickEpsilon))
4961 shade=distance/sqrt((double) normal_distance);
4964 if (gray != MagickFalse)
4966 q->red=(Quantum) shade;
4967 q->green=(Quantum) shade;
4968 q->blue=(Quantum) shade;
4972 q->red=ClampToQuantum(QuantumScale*shade*s1->red);
4973 q->green=ClampToQuantum(QuantumScale*shade*s1->green);
4974 q->blue=ClampToQuantum(QuantumScale*shade*s1->blue);
4976 q->opacity=s1->opacity;
4982 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
4984 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4989 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4990 #pragma omp critical (MagickCore_ShadeImage)
4992 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
4993 if (proceed == MagickFalse)
4997 shade_view=DestroyCacheView(shade_view);
4998 image_view=DestroyCacheView(image_view);
4999 if (status == MagickFalse)
5000 shade_image=DestroyImage(shade_image);
5001 return(shade_image);
5005 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5009 % S h a r p e n I m a g e %
5013 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5015 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
5016 % operator of the given radius and standard deviation (sigma). For
5017 % reasonable results, radius should be larger than sigma. Use a radius of 0
5018 % and SharpenImage() selects a suitable radius for you.
5020 % Using a separable kernel would be faster, but the negative weights cancel
5021 % out on the corners of the kernel producing often undesirable ringing in the
5022 % filtered result; this can be avoided by using a 2D gaussian shaped image
5023 % sharpening kernel instead.
5025 % The format of the SharpenImage method is:
5027 % Image *SharpenImage(const Image *image,const double radius,
5028 % const double sigma,ExceptionInfo *exception)
5029 % Image *SharpenImageChannel(const Image *image,const ChannelType channel,
5030 % const double radius,const double sigma,ExceptionInfo *exception)
5032 % A description of each parameter follows:
5034 % o image: the image.
5036 % o channel: the channel type.
5038 % o radius: the radius of the Gaussian, in pixels, not counting the center
5041 % o sigma: the standard deviation of the Laplacian, in pixels.
5043 % o exception: return any errors or warnings in this structure.
5047 MagickExport Image *SharpenImage(const Image *image,const double radius,
5048 const double sigma,ExceptionInfo *exception)
5053 sharp_image=SharpenImageChannel(image,DefaultChannels,radius,sigma,exception);
5054 return(sharp_image);
5057 MagickExport Image *SharpenImageChannel(const Image *image,
5058 const ChannelType channel,const double radius,const double sigma,
5059 ExceptionInfo *exception)
5079 assert(image != (const Image *) NULL);
5080 assert(image->signature == MagickSignature);
5081 if (image->debug != MagickFalse)
5082 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5083 assert(exception != (ExceptionInfo *) NULL);
5084 assert(exception->signature == MagickSignature);
5085 width=GetOptimalKernelWidth2D(radius,sigma);
5086 kernel=(double *) AcquireQuantumMemory((size_t) width*width,sizeof(*kernel));
5087 if (kernel == (double *) NULL)
5088 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5090 j=(ssize_t) width/2;
5092 for (v=(-j); v <= j; v++)
5094 for (u=(-j); u <= j; u++)
5096 kernel[i]=(-exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
5097 (2.0*MagickPI*MagickSigma*MagickSigma));
5098 normalize+=kernel[i];
5102 kernel[i/2]=(double) ((-2.0)*normalize);
5103 sharp_image=ConvolveImageChannel(image,channel,width,kernel,exception);
5104 kernel=(double *) RelinquishMagickMemory(kernel);
5105 return(sharp_image);
5109 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5113 % S p r e a d I m a g e %
5117 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5119 % SpreadImage() is a special effects method that randomly displaces each
5120 % pixel in a block defined by the radius parameter.
5122 % The format of the SpreadImage method is:
5124 % Image *SpreadImage(const Image *image,const double radius,
5125 % ExceptionInfo *exception)
5127 % A description of each parameter follows:
5129 % o image: the image.
5131 % o radius: Choose a random pixel in a neighborhood of this extent.
5133 % o exception: return any errors or warnings in this structure.
5136 MagickExport Image *SpreadImage(const Image *image,const double radius,
5137 ExceptionInfo *exception)
5139 #define SpreadImageTag "Spread/Image"
5157 **restrict random_info;
5160 **restrict resample_filter;
5169 Initialize spread image attributes.
5171 assert(image != (Image *) NULL);
5172 assert(image->signature == MagickSignature);
5173 if (image->debug != MagickFalse)
5174 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5175 assert(exception != (ExceptionInfo *) NULL);
5176 assert(exception->signature == MagickSignature);
5177 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
5179 if (spread_image == (Image *) NULL)
5180 return((Image *) NULL);
5181 if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
5183 InheritException(exception,&spread_image->exception);
5184 spread_image=DestroyImage(spread_image);
5185 return((Image *) NULL);
5192 GetMagickPixelPacket(spread_image,&bias);
5193 width=GetOptimalKernelWidth1D(radius,0.5);
5194 resample_filter=AcquireResampleFilterThreadSet(image,
5195 UndefinedVirtualPixelMethod,MagickTrue,exception);
5196 random_info=AcquireRandomInfoThreadSet();
5197 image_view=AcquireCacheView(spread_image);
5198 #if defined(MAGICKCORE_OPENMP_SUPPORT) && defined(MAGICKCORE_FUTURE)
5199 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5201 for (y=0; y < (ssize_t) spread_image->rows; y++)
5206 register IndexPacket
5213 register PixelPacket
5216 if (status == MagickFalse)
5218 q=QueueCacheViewAuthenticPixels(image_view,0,y,spread_image->columns,1,
5220 if (q == (PixelPacket *) NULL)
5225 indexes=GetCacheViewAuthenticIndexQueue(image_view);
5227 id=GetOpenMPThreadId();
5228 for (x=0; x < (ssize_t) spread_image->columns; x++)
5230 (void) ResamplePixelColor(resample_filter[id],(double) x+width*
5231 (GetPseudoRandomValue(random_info[id])-0.5),(double) y+width*
5232 (GetPseudoRandomValue(random_info[id])-0.5),&pixel);
5233 SetPixelPacket(spread_image,&pixel,q,indexes+x);
5236 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5238 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5243 #if defined(MAGICKCORE_OPENMP_SUPPORT) && defined(MAGICKCORE_FUTURE)
5244 #pragma omp critical (MagickCore_SpreadImage)
5246 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
5247 if (proceed == MagickFalse)
5251 image_view=DestroyCacheView(image_view);
5252 random_info=DestroyRandomInfoThreadSet(random_info);
5253 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5254 return(spread_image);
5258 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5262 % U n s h a r p M a s k I m a g e %
5266 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5268 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
5269 % image with a Gaussian operator of the given radius and standard deviation
5270 % (sigma). For reasonable results, radius should be larger than sigma. Use a
5271 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
5273 % The format of the UnsharpMaskImage method is:
5275 % Image *UnsharpMaskImage(const Image *image,const double radius,
5276 % const double sigma,const double amount,const double threshold,
5277 % ExceptionInfo *exception)
5278 % Image *UnsharpMaskImageChannel(const Image *image,
5279 % const ChannelType channel,const double radius,const double sigma,
5280 % const double amount,const double threshold,ExceptionInfo *exception)
5282 % A description of each parameter follows:
5284 % o image: the image.
5286 % o channel: the channel type.
5288 % o radius: the radius of the Gaussian, in pixels, not counting the center
5291 % o sigma: the standard deviation of the Gaussian, in pixels.
5293 % o amount: the percentage of the difference between the original and the
5294 % blur image that is added back into the original.
5296 % o threshold: the threshold in pixels needed to apply the diffence amount.
5298 % o exception: return any errors or warnings in this structure.
5302 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
5303 const double sigma,const double amount,const double threshold,
5304 ExceptionInfo *exception)
5309 sharp_image=UnsharpMaskImageChannel(image,DefaultChannels,radius,sigma,amount,
5310 threshold,exception);
5311 return(sharp_image);
5314 MagickExport Image *UnsharpMaskImageChannel(const Image *image,
5315 const ChannelType channel,const double radius,const double sigma,
5316 const double amount,const double threshold,ExceptionInfo *exception)
5318 #define SharpenImageTag "Sharpen/Image"
5342 assert(image != (const Image *) NULL);
5343 assert(image->signature == MagickSignature);
5344 if (image->debug != MagickFalse)
5345 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5346 assert(exception != (ExceptionInfo *) NULL);
5347 unsharp_image=BlurImageChannel(image,channel,radius,sigma,exception);
5348 if (unsharp_image == (Image *) NULL)
5349 return((Image *) NULL);
5350 quantum_threshold=(MagickRealType) QuantumRange*threshold;
5356 GetMagickPixelPacket(image,&bias);
5357 image_view=AcquireCacheView(image);
5358 unsharp_view=AcquireCacheView(unsharp_image);
5359 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5360 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5362 for (y=0; y < (ssize_t) image->rows; y++)
5367 register const IndexPacket
5370 register const PixelPacket
5373 register IndexPacket
5374 *restrict unsharp_indexes;
5379 register PixelPacket
5382 if (status == MagickFalse)
5384 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5385 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
5387 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5392 indexes=GetCacheViewVirtualIndexQueue(image_view);
5393 unsharp_indexes=GetCacheViewAuthenticIndexQueue(unsharp_view);
5395 for (x=0; x < (ssize_t) image->columns; x++)
5397 if ((channel & RedChannel) != 0)
5399 pixel.red=p->red-(MagickRealType) q->red;
5400 if (fabs(2.0*pixel.red) < quantum_threshold)
5401 pixel.red=(MagickRealType) GetRedPixelComponent(p);
5403 pixel.red=(MagickRealType) p->red+(pixel.red*amount);
5404 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
5406 if ((channel & GreenChannel) != 0)
5408 pixel.green=p->green-(MagickRealType) q->green;
5409 if (fabs(2.0*pixel.green) < quantum_threshold)
5410 pixel.green=(MagickRealType) GetGreenPixelComponent(p);
5412 pixel.green=(MagickRealType) p->green+(pixel.green*amount);
5413 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
5415 if ((channel & BlueChannel) != 0)
5417 pixel.blue=p->blue-(MagickRealType) q->blue;
5418 if (fabs(2.0*pixel.blue) < quantum_threshold)
5419 pixel.blue=(MagickRealType) GetBluePixelComponent(p);
5421 pixel.blue=(MagickRealType) p->blue+(pixel.blue*amount);
5422 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
5424 if ((channel & OpacityChannel) != 0)
5426 pixel.opacity=p->opacity-(MagickRealType) q->opacity;
5427 if (fabs(2.0*pixel.opacity) < quantum_threshold)
5428 pixel.opacity=(MagickRealType) GetOpacityPixelComponent(p);
5430 pixel.opacity=p->opacity+(pixel.opacity*amount);
5431 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
5433 if (((channel & IndexChannel) != 0) &&
5434 (image->colorspace == CMYKColorspace))
5436 pixel.index=unsharp_indexes[x]-(MagickRealType) indexes[x];
5437 if (fabs(2.0*pixel.index) < quantum_threshold)
5438 pixel.index=(MagickRealType) unsharp_indexes[x];
5440 pixel.index=(MagickRealType) unsharp_indexes[x]+(pixel.index*
5442 unsharp_indexes[x]=ClampToQuantum(pixel.index);
5447 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
5449 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5454 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5455 #pragma omp critical (MagickCore_UnsharpMaskImageChannel)
5457 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
5458 if (proceed == MagickFalse)
5462 unsharp_image->type=image->type;
5463 unsharp_view=DestroyCacheView(unsharp_view);
5464 image_view=DestroyCacheView(image_view);
5465 if (status == MagickFalse)
5466 unsharp_image=DestroyImage(unsharp_image);
5467 return(unsharp_image);