2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % EEEEE FFFFF FFFFF EEEEE CCCC TTTTT %
8 % EEE FFF FFF EEE C T %
10 % EEEEE F F EEEEE CCCC T %
13 % MagickCore Image Effects Methods %
20 % Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "magick/studio.h"
44 #include "magick/property.h"
45 #include "magick/blob.h"
46 #include "magick/cache-view.h"
47 #include "magick/color.h"
48 #include "magick/color-private.h"
49 #include "magick/colorspace.h"
50 #include "magick/constitute.h"
51 #include "magick/decorate.h"
52 #include "magick/draw.h"
53 #include "magick/enhance.h"
54 #include "magick/exception.h"
55 #include "magick/exception-private.h"
56 #include "magick/effect.h"
57 #include "magick/fx.h"
58 #include "magick/gem.h"
59 #include "magick/geometry.h"
60 #include "magick/image-private.h"
61 #include "magick/list.h"
62 #include "magick/log.h"
63 #include "magick/memory_.h"
64 #include "magick/monitor.h"
65 #include "magick/monitor-private.h"
66 #include "magick/montage.h"
67 #include "magick/paint.h"
68 #include "magick/pixel-private.h"
69 #include "magick/property.h"
70 #include "magick/quantize.h"
71 #include "magick/quantum.h"
72 #include "magick/random_.h"
73 #include "magick/random-private.h"
74 #include "magick/resample.h"
75 #include "magick/resample-private.h"
76 #include "magick/resize.h"
77 #include "magick/resource_.h"
78 #include "magick/segment.h"
79 #include "magick/shear.h"
80 #include "magick/signature-private.h"
81 #include "magick/string_.h"
82 #include "magick/thread-private.h"
83 #include "magick/transform.h"
84 #include "magick/threshold.h"
87 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91 % A d a p t i v e B l u r I m a g e %
95 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
97 % AdaptiveBlurImage() adaptively blurs the image by blurring less
98 % intensely near image edges and more intensely far from edges. We blur the
99 % image with a Gaussian operator of the given radius and standard deviation
100 % (sigma). For reasonable results, radius should be larger than sigma. Use a
101 % radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
103 % The format of the AdaptiveBlurImage method is:
105 % Image *AdaptiveBlurImage(const Image *image,const double radius,
106 % const double sigma,ExceptionInfo *exception)
107 % Image *AdaptiveBlurImageChannel(const Image *image,
108 % const ChannelType channel,double radius,const double sigma,
109 % ExceptionInfo *exception)
111 % A description of each parameter follows:
113 % o image: the image.
115 % o channel: the channel type.
117 % o radius: the radius of the Gaussian, in pixels, not counting the center
120 % o sigma: the standard deviation of the Laplacian, in pixels.
122 % o exception: return any errors or warnings in this structure.
126 MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
127 const double sigma,ExceptionInfo *exception)
132 blur_image=AdaptiveBlurImageChannel(image,DefaultChannels,radius,sigma,
137 MagickExport Image *AdaptiveBlurImageChannel(const Image *image,
138 const ChannelType channel,const double radius,const double sigma,
139 ExceptionInfo *exception)
141 #define AdaptiveBlurImageTag "Convolve/Image"
142 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
178 assert(image != (const Image *) NULL);
179 assert(image->signature == MagickSignature);
180 if (image->debug != MagickFalse)
181 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
182 assert(exception != (ExceptionInfo *) NULL);
183 assert(exception->signature == MagickSignature);
184 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
185 if (blur_image == (Image *) NULL)
186 return((Image *) NULL);
187 if (fabs(sigma) <= MagickEpsilon)
189 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
191 InheritException(exception,&blur_image->exception);
192 blur_image=DestroyImage(blur_image);
193 return((Image *) NULL);
196 Edge detect the image brighness channel, level, blur, and level again.
198 edge_image=EdgeImage(image,radius,exception);
199 if (edge_image == (Image *) NULL)
201 blur_image=DestroyImage(blur_image);
202 return((Image *) NULL);
204 (void) LevelImage(edge_image,"20%,95%");
205 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
206 if (gaussian_image != (Image *) NULL)
208 edge_image=DestroyImage(edge_image);
209 edge_image=gaussian_image;
211 (void) LevelImage(edge_image,"10%,95%");
213 Create a set of kernels from maximum (radius,sigma) to minimum.
215 width=GetOptimalKernelWidth2D(radius,sigma);
216 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
217 if (kernel == (double **) NULL)
219 edge_image=DestroyImage(edge_image);
220 blur_image=DestroyImage(blur_image);
221 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
223 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
224 for (i=0; i < (long) width; i+=2)
226 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
228 if (kernel[i] == (double *) NULL)
231 j=(long) (width-i)/2;
233 for (v=(-j); v <= j; v++)
235 for (u=(-j); u <= j; u++)
237 kernel[i][k]=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
238 (2.0*MagickPI*MagickSigma*MagickSigma);
239 normalize+=kernel[i][k];
243 if (fabs(normalize) <= MagickEpsilon)
245 normalize=1.0/normalize;
246 for (k=0; k < (j*j); k++)
247 kernel[i][k]=normalize*kernel[i][k];
249 if (i < (long) width)
251 for (i-=2; i >= 0; i-=2)
252 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
253 kernel=(double **) RelinquishMagickMemory(kernel);
254 edge_image=DestroyImage(edge_image);
255 blur_image=DestroyImage(blur_image);
256 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
259 Adaptively blur image.
263 GetMagickPixelPacket(image,&bias);
264 SetMagickPixelPacketBias(image,&bias);
265 image_view=AcquireCacheView(image);
266 edge_view=AcquireCacheView(edge_image);
267 blur_view=AcquireCacheView(blur_image);
268 #if defined(MAGICKCORE_OPENMP_SUPPORT)
269 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
271 for (y=0; y < (long) blur_image->rows; y++)
273 register const IndexPacket
276 register const PixelPacket
281 *restrict blur_indexes;
289 if (status == MagickFalse)
291 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
292 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
294 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
299 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
300 for (x=0; x < (long) blur_image->columns; x++)
309 register const double
318 i=(long) (width*QuantumScale*PixelIntensity(r)+0.5);
322 if (i > (long) width)
326 p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
327 ((width-i)/2L),width-i,width-i,exception);
328 if (p == (const PixelPacket *) NULL)
330 indexes=GetCacheViewVirtualIndexQueue(image_view);
333 for (v=0; v < (long) (width-i); v++)
335 for (u=0; u < (long) (width-i); u++)
338 if (((channel & OpacityChannel) != 0) &&
339 (image->matte != MagickFalse))
340 alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
341 if ((channel & RedChannel) != 0)
342 pixel.red+=(*k)*alpha*p->red;
343 if ((channel & GreenChannel) != 0)
344 pixel.green+=(*k)*alpha*p->green;
345 if ((channel & BlueChannel) != 0)
346 pixel.blue+=(*k)*alpha*p->blue;
347 if ((channel & OpacityChannel) != 0)
348 pixel.opacity+=(*k)*p->opacity;
349 if (((channel & IndexChannel) != 0) &&
350 (image->colorspace == CMYKColorspace))
351 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
357 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
358 if ((channel & RedChannel) != 0)
359 q->red=RoundToQuantum(gamma*pixel.red);
360 if ((channel & GreenChannel) != 0)
361 q->green=RoundToQuantum(gamma*pixel.green);
362 if ((channel & BlueChannel) != 0)
363 q->blue=RoundToQuantum(gamma*pixel.blue);
364 if ((channel & OpacityChannel) != 0)
365 q->opacity=RoundToQuantum(pixel.opacity);
366 if (((channel & IndexChannel) != 0) &&
367 (image->colorspace == CMYKColorspace))
368 blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
372 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
374 if (image->progress_monitor != (MagickProgressMonitor) NULL)
379 #if defined(MAGICKCORE_OPENMP_SUPPORT)
380 #pragma omp critical (MagickCore_AdaptiveBlurImageChannel)
382 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
384 if (proceed == MagickFalse)
388 blur_image->type=image->type;
389 blur_view=DestroyCacheView(blur_view);
390 edge_view=DestroyCacheView(edge_view);
391 image_view=DestroyCacheView(image_view);
392 edge_image=DestroyImage(edge_image);
393 for (i=0; i < (long) width; i+=2)
394 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
395 kernel=(double **) RelinquishMagickMemory(kernel);
396 if (status == MagickFalse)
397 blur_image=DestroyImage(blur_image);
402 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
406 % A d a p t i v e S h a r p e n I m a g e %
410 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
412 % AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
413 % intensely near image edges and less intensely far from edges. We sharpen the
414 % image with a Gaussian operator of the given radius and standard deviation
415 % (sigma). For reasonable results, radius should be larger than sigma. Use a
416 % radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
418 % The format of the AdaptiveSharpenImage method is:
420 % Image *AdaptiveSharpenImage(const Image *image,const double radius,
421 % const double sigma,ExceptionInfo *exception)
422 % Image *AdaptiveSharpenImageChannel(const Image *image,
423 % const ChannelType channel,double radius,const double sigma,
424 % ExceptionInfo *exception)
426 % A description of each parameter follows:
428 % o image: the image.
430 % o channel: the channel type.
432 % o radius: the radius of the Gaussian, in pixels, not counting the center
435 % o sigma: the standard deviation of the Laplacian, in pixels.
437 % o exception: return any errors or warnings in this structure.
441 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
442 const double sigma,ExceptionInfo *exception)
447 sharp_image=AdaptiveSharpenImageChannel(image,DefaultChannels,radius,sigma,
452 MagickExport Image *AdaptiveSharpenImageChannel(const Image *image,
453 const ChannelType channel,const double radius,const double sigma,
454 ExceptionInfo *exception)
456 #define AdaptiveSharpenImageTag "Convolve/Image"
457 #define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
493 assert(image != (const Image *) NULL);
494 assert(image->signature == MagickSignature);
495 if (image->debug != MagickFalse)
496 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
497 assert(exception != (ExceptionInfo *) NULL);
498 assert(exception->signature == MagickSignature);
499 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
500 if (sharp_image == (Image *) NULL)
501 return((Image *) NULL);
502 if (fabs(sigma) <= MagickEpsilon)
504 if (SetImageStorageClass(sharp_image,DirectClass) == MagickFalse)
506 InheritException(exception,&sharp_image->exception);
507 sharp_image=DestroyImage(sharp_image);
508 return((Image *) NULL);
511 Edge detect the image brighness channel, level, sharp, and level again.
513 edge_image=EdgeImage(image,radius,exception);
514 if (edge_image == (Image *) NULL)
516 sharp_image=DestroyImage(sharp_image);
517 return((Image *) NULL);
519 (void) LevelImage(edge_image,"20%,95%");
520 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
521 if (gaussian_image != (Image *) NULL)
523 edge_image=DestroyImage(edge_image);
524 edge_image=gaussian_image;
526 (void) LevelImage(edge_image,"10%,95%");
528 Create a set of kernels from maximum (radius,sigma) to minimum.
530 width=GetOptimalKernelWidth2D(radius,sigma);
531 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
532 if (kernel == (double **) NULL)
534 edge_image=DestroyImage(edge_image);
535 sharp_image=DestroyImage(sharp_image);
536 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
538 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
539 for (i=0; i < (long) width; i+=2)
541 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
543 if (kernel[i] == (double *) NULL)
546 j=(long) (width-i)/2;
548 for (v=(-j); v <= j; v++)
550 for (u=(-j); u <= j; u++)
552 kernel[i][k]=(-exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
553 (2.0*MagickPI*MagickSigma*MagickSigma));
554 normalize+=kernel[i][k];
558 if (fabs(normalize) <= MagickEpsilon)
560 normalize=1.0/normalize;
561 for (k=0; k < (j*j); k++)
562 kernel[i][k]=normalize*kernel[i][k];
564 if (i < (long) width)
566 for (i-=2; i >= 0; i-=2)
567 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
568 kernel=(double **) RelinquishMagickMemory(kernel);
569 edge_image=DestroyImage(edge_image);
570 sharp_image=DestroyImage(sharp_image);
571 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
574 Adaptively sharpen image.
578 GetMagickPixelPacket(image,&bias);
579 SetMagickPixelPacketBias(image,&bias);
580 image_view=AcquireCacheView(image);
581 edge_view=AcquireCacheView(edge_image);
582 sharp_view=AcquireCacheView(sharp_image);
583 #if defined(MAGICKCORE_OPENMP_SUPPORT)
584 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
586 for (y=0; y < (long) sharp_image->rows; y++)
588 register const IndexPacket
591 register const PixelPacket
596 *restrict sharp_indexes;
604 if (status == MagickFalse)
606 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
607 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
609 if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
614 sharp_indexes=GetCacheViewAuthenticIndexQueue(sharp_view);
615 for (x=0; x < (long) sharp_image->columns; x++)
624 register const double
633 i=(long) (width*(QuantumRange-QuantumScale*PixelIntensity(r))+0.5);
637 if (i > (long) width)
641 p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
642 ((width-i)/2L),width-i,width-i,exception);
643 if (p == (const PixelPacket *) NULL)
645 indexes=GetCacheViewVirtualIndexQueue(image_view);
648 for (v=0; v < (long) (width-i); v++)
650 for (u=0; u < (long) (width-i); u++)
653 if (((channel & OpacityChannel) != 0) &&
654 (image->matte != MagickFalse))
655 alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
656 if ((channel & RedChannel) != 0)
657 pixel.red+=(*k)*alpha*p->red;
658 if ((channel & GreenChannel) != 0)
659 pixel.green+=(*k)*alpha*p->green;
660 if ((channel & BlueChannel) != 0)
661 pixel.blue+=(*k)*alpha*p->blue;
662 if ((channel & OpacityChannel) != 0)
663 pixel.opacity+=(*k)*p->opacity;
664 if (((channel & IndexChannel) != 0) &&
665 (image->colorspace == CMYKColorspace))
666 pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
672 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
673 if ((channel & RedChannel) != 0)
674 q->red=RoundToQuantum(gamma*pixel.red);
675 if ((channel & GreenChannel) != 0)
676 q->green=RoundToQuantum(gamma*pixel.green);
677 if ((channel & BlueChannel) != 0)
678 q->blue=RoundToQuantum(gamma*pixel.blue);
679 if ((channel & OpacityChannel) != 0)
680 q->opacity=RoundToQuantum(pixel.opacity);
681 if (((channel & IndexChannel) != 0) &&
682 (image->colorspace == CMYKColorspace))
683 sharp_indexes[x]=RoundToQuantum(gamma*pixel.index);
687 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
689 if (image->progress_monitor != (MagickProgressMonitor) NULL)
694 #if defined(MAGICKCORE_OPENMP_SUPPORT)
695 #pragma omp critical (MagickCore_AdaptiveSharpenImageChannel)
697 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
699 if (proceed == MagickFalse)
703 sharp_image->type=image->type;
704 sharp_view=DestroyCacheView(sharp_view);
705 edge_view=DestroyCacheView(edge_view);
706 image_view=DestroyCacheView(image_view);
707 edge_image=DestroyImage(edge_image);
708 for (i=0; i < (long) width; i+=2)
709 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
710 kernel=(double **) RelinquishMagickMemory(kernel);
711 if (status == MagickFalse)
712 sharp_image=DestroyImage(sharp_image);
717 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
721 % B l u r I m a g e %
725 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727 % BlurImage() blurs an image. We convolve the image with a Gaussian operator
728 % of the given radius and standard deviation (sigma). For reasonable results,
729 % the radius should be larger than sigma. Use a radius of 0 and BlurImage()
730 % selects a suitable radius for you.
732 % BlurImage() differs from GaussianBlurImage() in that it uses a separable
733 % kernel which is faster but mathematically equivalent to the non-separable
736 % The format of the BlurImage method is:
738 % Image *BlurImage(const Image *image,const double radius,
739 % const double sigma,ExceptionInfo *exception)
740 % Image *BlurImageChannel(const Image *image,const ChannelType channel,
741 % const double radius,const double sigma,ExceptionInfo *exception)
743 % A description of each parameter follows:
745 % o image: the image.
747 % o channel: the channel type.
749 % o radius: the radius of the Gaussian, in pixels, not counting the center
752 % o sigma: the standard deviation of the Gaussian, in pixels.
754 % o exception: return any errors or warnings in this structure.
758 MagickExport Image *BlurImage(const Image *image,const double radius,
759 const double sigma,ExceptionInfo *exception)
764 blur_image=BlurImageChannel(image,DefaultChannels,radius,sigma,exception);
768 static double *GetBlurKernel(const unsigned long width,const double sigma)
782 Generate a 1-D convolution kernel.
784 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
785 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
786 if (kernel == (double *) NULL)
791 for (k=(-j); k <= j; k++)
793 kernel[i]=exp(-((double) k*k)/(2.0*MagickSigma*MagickSigma))/
794 (MagickSQ2PI*MagickSigma);
795 normalize+=kernel[i];
798 for (i=0; i < (long) width; i++)
799 kernel[i]/=normalize;
803 MagickExport Image *BlurImageChannel(const Image *image,
804 const ChannelType channel,const double radius,const double sigma,
805 ExceptionInfo *exception)
807 #define BlurImageTag "Blur/Image"
837 Initialize blur image attributes.
839 assert(image != (Image *) NULL);
840 assert(image->signature == MagickSignature);
841 if (image->debug != MagickFalse)
842 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
843 assert(exception != (ExceptionInfo *) NULL);
844 assert(exception->signature == MagickSignature);
845 blur_image=CloneImage(image,0,0,MagickTrue,exception);
846 if (blur_image == (Image *) NULL)
847 return((Image *) NULL);
848 if (fabs(sigma) <= MagickEpsilon)
850 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
852 InheritException(exception,&blur_image->exception);
853 blur_image=DestroyImage(blur_image);
854 return((Image *) NULL);
856 width=GetOptimalKernelWidth1D(radius,sigma);
857 kernel=GetBlurKernel(width,sigma);
858 if (kernel == (double *) NULL)
860 blur_image=DestroyImage(blur_image);
861 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
863 if (image->debug != MagickFalse)
866 format[MaxTextExtent],
869 register const double
872 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
873 " BlurImage with %ld kernel:",width);
874 message=AcquireString("");
876 for (i=0; i < (long) width; i++)
879 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",i);
880 (void) ConcatenateString(&message,format);
881 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
882 (void) ConcatenateString(&message,format);
883 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
885 message=DestroyString(message);
892 GetMagickPixelPacket(image,&bias);
893 SetMagickPixelPacketBias(image,&bias);
894 image_view=AcquireCacheView(image);
895 blur_view=AcquireCacheView(blur_image);
896 #if defined(MAGICKCORE_OPENMP_SUPPORT)
897 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
899 for (y=0; y < (long) blur_image->rows; y++)
901 register const IndexPacket
904 register const PixelPacket
908 *restrict blur_indexes;
916 if (status == MagickFalse)
918 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y,image->columns+
920 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
922 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
927 indexes=GetCacheViewVirtualIndexQueue(image_view);
928 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
929 for (x=0; x < (long) blur_image->columns; x++)
934 register const double
937 register const PixelPacket
938 *restrict kernel_pixels;
946 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
948 for (i=0; i < (long) width; i++)
950 pixel.red+=(*k)*kernel_pixels->red;
951 pixel.green+=(*k)*kernel_pixels->green;
952 pixel.blue+=(*k)*kernel_pixels->blue;
956 if ((channel & RedChannel) != 0)
957 q->red=RoundToQuantum(pixel.red);
958 if ((channel & GreenChannel) != 0)
959 q->green=RoundToQuantum(pixel.green);
960 if ((channel & BlueChannel) != 0)
961 q->blue=RoundToQuantum(pixel.blue);
962 if ((channel & OpacityChannel) != 0)
966 for (i=0; i < (long) width; i++)
968 pixel.opacity+=(*k)*kernel_pixels->opacity;
972 q->opacity=RoundToQuantum(pixel.opacity);
974 if (((channel & IndexChannel) != 0) &&
975 (image->colorspace == CMYKColorspace))
977 register const IndexPacket
978 *restrict kernel_indexes;
981 kernel_indexes=indexes;
982 for (i=0; i < (long) width; i++)
984 pixel.index+=(*k)*(*kernel_indexes);
988 blur_indexes[x]=RoundToQuantum(pixel.index);
998 for (i=0; i < (long) width; i++)
1000 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1001 kernel_pixels->opacity));
1002 pixel.red+=(*k)*alpha*kernel_pixels->red;
1003 pixel.green+=(*k)*alpha*kernel_pixels->green;
1004 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
1009 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1010 if ((channel & RedChannel) != 0)
1011 q->red=RoundToQuantum(gamma*pixel.red);
1012 if ((channel & GreenChannel) != 0)
1013 q->green=RoundToQuantum(gamma*pixel.green);
1014 if ((channel & BlueChannel) != 0)
1015 q->blue=RoundToQuantum(gamma*pixel.blue);
1016 if ((channel & OpacityChannel) != 0)
1020 for (i=0; i < (long) width; i++)
1022 pixel.opacity+=(*k)*kernel_pixels->opacity;
1026 q->opacity=RoundToQuantum(pixel.opacity);
1028 if (((channel & IndexChannel) != 0) &&
1029 (image->colorspace == CMYKColorspace))
1031 register const IndexPacket
1032 *restrict kernel_indexes;
1036 kernel_indexes=indexes;
1037 for (i=0; i < (long) width; i++)
1039 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1040 kernel_pixels->opacity));
1041 pixel.index+=(*k)*alpha*(*kernel_indexes);
1046 blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
1052 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1054 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1059 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1060 #pragma omp critical (MagickCore_BlurImageChannel)
1062 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1063 blur_image->columns);
1064 if (proceed == MagickFalse)
1068 blur_view=DestroyCacheView(blur_view);
1069 image_view=DestroyCacheView(image_view);
1073 image_view=AcquireCacheView(blur_image);
1074 blur_view=AcquireCacheView(blur_image);
1075 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1076 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1078 for (x=0; x < (long) blur_image->columns; x++)
1080 register const IndexPacket
1083 register const PixelPacket
1086 register IndexPacket
1087 *restrict blur_indexes;
1092 register PixelPacket
1095 if (status == MagickFalse)
1097 p=GetCacheViewVirtualPixels(image_view,x,-((long) width/2L),1,image->rows+
1099 q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
1100 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1105 indexes=GetCacheViewVirtualIndexQueue(image_view);
1106 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
1107 for (y=0; y < (long) blur_image->rows; y++)
1112 register const double
1115 register const PixelPacket
1116 *restrict kernel_pixels;
1124 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
1126 for (i=0; i < (long) width; i++)
1128 pixel.red+=(*k)*kernel_pixels->red;
1129 pixel.green+=(*k)*kernel_pixels->green;
1130 pixel.blue+=(*k)*kernel_pixels->blue;
1134 if ((channel & RedChannel) != 0)
1135 q->red=RoundToQuantum(pixel.red);
1136 if ((channel & GreenChannel) != 0)
1137 q->green=RoundToQuantum(pixel.green);
1138 if ((channel & BlueChannel) != 0)
1139 q->blue=RoundToQuantum(pixel.blue);
1140 if ((channel & OpacityChannel) != 0)
1144 for (i=0; i < (long) width; i++)
1146 pixel.opacity+=(*k)*kernel_pixels->opacity;
1150 q->opacity=RoundToQuantum(pixel.opacity);
1152 if (((channel & IndexChannel) != 0) &&
1153 (image->colorspace == CMYKColorspace))
1155 register const IndexPacket
1156 *restrict kernel_indexes;
1159 kernel_indexes=indexes;
1160 for (i=0; i < (long) width; i++)
1162 pixel.index+=(*k)*(*kernel_indexes);
1166 blur_indexes[y]=RoundToQuantum(pixel.index);
1176 for (i=0; i < (long) width; i++)
1178 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1179 kernel_pixels->opacity));
1180 pixel.red+=(*k)*alpha*kernel_pixels->red;
1181 pixel.green+=(*k)*alpha*kernel_pixels->green;
1182 pixel.blue+=(*k)*alpha*kernel_pixels->blue;
1187 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1188 if ((channel & RedChannel) != 0)
1189 q->red=RoundToQuantum(gamma*pixel.red);
1190 if ((channel & GreenChannel) != 0)
1191 q->green=RoundToQuantum(gamma*pixel.green);
1192 if ((channel & BlueChannel) != 0)
1193 q->blue=RoundToQuantum(gamma*pixel.blue);
1194 if ((channel & OpacityChannel) != 0)
1198 for (i=0; i < (long) width; i++)
1200 pixel.opacity+=(*k)*kernel_pixels->opacity;
1204 q->opacity=RoundToQuantum(pixel.opacity);
1206 if (((channel & IndexChannel) != 0) &&
1207 (image->colorspace == CMYKColorspace))
1209 register const IndexPacket
1210 *restrict kernel_indexes;
1214 kernel_indexes=indexes;
1215 for (i=0; i < (long) width; i++)
1217 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1218 kernel_pixels->opacity));
1219 pixel.index+=(*k)*alpha*(*kernel_indexes);
1224 blur_indexes[y]=RoundToQuantum(gamma*pixel.index);
1230 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1232 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1237 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1238 #pragma omp critical (MagickCore_BlurImageChannel)
1240 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1241 blur_image->columns);
1242 if (proceed == MagickFalse)
1246 blur_view=DestroyCacheView(blur_view);
1247 image_view=DestroyCacheView(image_view);
1248 kernel=(double *) RelinquishMagickMemory(kernel);
1249 if (status == MagickFalse)
1250 blur_image=DestroyImage(blur_image);
1251 blur_image->type=image->type;
1256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1260 % C o n v o l v e I m a g e %
1264 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1266 % ConvolveImage() applies a custom convolution kernel to the image.
1268 % The format of the ConvolveImage method is:
1270 % Image *ConvolveImage(const Image *image,const unsigned long order,
1271 % const double *kernel,ExceptionInfo *exception)
1272 % Image *ConvolveImageChannel(const Image *image,const ChannelType channel,
1273 % const unsigned long order,const double *kernel,
1274 % ExceptionInfo *exception)
1276 % A description of each parameter follows:
1278 % o image: the image.
1280 % o channel: the channel type.
1282 % o order: the number of columns and rows in the filter kernel.
1284 % o kernel: An array of double representing the convolution kernel.
1286 % o exception: return any errors or warnings in this structure.
1290 MagickExport Image *ConvolveImage(const Image *image,const unsigned long order,
1291 const double *kernel,ExceptionInfo *exception)
1296 convolve_image=ConvolveImageChannel(image,DefaultChannels,order,kernel,
1298 return(convolve_image);
1301 MagickExport Image *ConvolveImageChannel(const Image *image,
1302 const ChannelType channel,const unsigned long order,const double *kernel,
1303 ExceptionInfo *exception)
1305 #define ConvolveImageTag "Convolve/Image"
1337 Initialize convolve image attributes.
1339 assert(image != (Image *) NULL);
1340 assert(image->signature == MagickSignature);
1341 if (image->debug != MagickFalse)
1342 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1343 assert(exception != (ExceptionInfo *) NULL);
1344 assert(exception->signature == MagickSignature);
1346 if ((width % 2) == 0)
1347 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
1348 convolve_image=CloneImage(image,0,0,MagickTrue,exception);
1349 if (convolve_image == (Image *) NULL)
1350 return((Image *) NULL);
1351 if (SetImageStorageClass(convolve_image,DirectClass) == MagickFalse)
1353 InheritException(exception,&convolve_image->exception);
1354 convolve_image=DestroyImage(convolve_image);
1355 return((Image *) NULL);
1357 if (image->debug != MagickFalse)
1360 format[MaxTextExtent],
1367 register const double
1370 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1371 " ConvolveImage with %ldx%ld kernel:",width,width);
1372 message=AcquireString("");
1374 for (v=0; v < (long) width; v++)
1377 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
1378 (void) ConcatenateString(&message,format);
1379 for (u=0; u < (long) width; u++)
1381 (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
1382 (void) ConcatenateString(&message,format);
1384 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
1386 message=DestroyString(message);
1391 normal_kernel=(double *) AcquireQuantumMemory(width*width,
1392 sizeof(*normal_kernel));
1393 if (normal_kernel == (double *) NULL)
1395 convolve_image=DestroyImage(convolve_image);
1396 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1399 for (i=0; i < (long) (width*width); i++)
1401 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1402 for (i=0; i < (long) (width*width); i++)
1403 normal_kernel[i]=gamma*kernel[i];
1409 GetMagickPixelPacket(image,&bias);
1410 SetMagickPixelPacketBias(image,&bias);
1411 image_view=AcquireCacheView(image);
1412 convolve_view=AcquireCacheView(convolve_image);
1413 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1414 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1416 for (y=0; y < (long) image->rows; y++)
1421 register const IndexPacket
1424 register const PixelPacket
1427 register IndexPacket
1428 *restrict convolve_indexes;
1433 register PixelPacket
1436 if (status == MagickFalse)
1438 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
1439 2L),image->columns+width,width,exception);
1440 q=GetCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
1442 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1447 indexes=GetCacheViewVirtualIndexQueue(image_view);
1448 convolve_indexes=GetCacheViewAuthenticIndexQueue(convolve_view);
1449 for (x=0; x < (long) image->columns; x++)
1457 register const double
1460 register const PixelPacket
1461 *restrict kernel_pixels;
1469 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
1471 for (v=0; v < (long) width; v++)
1473 for (u=0; u < (long) width; u++)
1475 pixel.red+=(*k)*kernel_pixels[u].red;
1476 pixel.green+=(*k)*kernel_pixels[u].green;
1477 pixel.blue+=(*k)*kernel_pixels[u].blue;
1480 kernel_pixels+=image->columns+width;
1482 if ((channel & RedChannel) != 0)
1483 q->red=RoundToQuantum(pixel.red);
1484 if ((channel & GreenChannel) != 0)
1485 q->green=RoundToQuantum(pixel.green);
1486 if ((channel & BlueChannel) != 0)
1487 q->blue=RoundToQuantum(pixel.blue);
1488 if ((channel & OpacityChannel) != 0)
1492 for (v=0; v < (long) width; v++)
1494 for (u=0; u < (long) width; u++)
1496 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1499 kernel_pixels+=image->columns+width;
1501 q->opacity=RoundToQuantum(pixel.opacity);
1503 if (((channel & IndexChannel) != 0) &&
1504 (image->colorspace == CMYKColorspace))
1506 register const IndexPacket
1507 *restrict kernel_indexes;
1510 kernel_indexes=indexes;
1511 for (v=0; v < (long) width; v++)
1513 for (u=0; u < (long) width; u++)
1515 pixel.index+=(*k)*kernel_indexes[u];
1518 kernel_indexes+=image->columns+width;
1520 convolve_indexes[x]=RoundToQuantum(pixel.index);
1530 for (v=0; v < (long) width; v++)
1532 for (u=0; u < (long) width; u++)
1534 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1535 kernel_pixels[u].opacity));
1536 pixel.red+=(*k)*alpha*kernel_pixels[u].red;
1537 pixel.green+=(*k)*alpha*kernel_pixels[u].green;
1538 pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
1539 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1543 kernel_pixels+=image->columns+width;
1545 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1546 if ((channel & RedChannel) != 0)
1547 q->red=RoundToQuantum(gamma*pixel.red);
1548 if ((channel & GreenChannel) != 0)
1549 q->green=RoundToQuantum(gamma*pixel.green);
1550 if ((channel & BlueChannel) != 0)
1551 q->blue=RoundToQuantum(gamma*pixel.blue);
1552 if ((channel & OpacityChannel) != 0)
1556 for (v=0; v < (long) width; v++)
1558 for (u=0; u < (long) width; u++)
1560 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1563 kernel_pixels+=image->columns+width;
1565 q->opacity=RoundToQuantum(pixel.opacity);
1567 if (((channel & IndexChannel) != 0) &&
1568 (image->colorspace == CMYKColorspace))
1570 register const IndexPacket
1571 *restrict kernel_indexes;
1575 kernel_indexes=indexes;
1576 for (v=0; v < (long) width; v++)
1578 for (u=0; u < (long) width; u++)
1580 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1581 kernel_pixels[u].opacity));
1582 pixel.index+=(*k)*alpha*kernel_indexes[u];
1585 kernel_pixels+=image->columns+width;
1586 kernel_indexes+=image->columns+width;
1588 convolve_indexes[x]=RoundToQuantum(gamma*pixel.index);
1594 sync=SyncCacheViewAuthenticPixels(convolve_view,exception);
1595 if (sync == MagickFalse)
1597 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1602 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1603 #pragma omp critical (MagickCore_ConvolveImageChannel)
1605 proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1606 if (proceed == MagickFalse)
1610 convolve_image->type=image->type;
1611 convolve_view=DestroyCacheView(convolve_view);
1612 image_view=DestroyCacheView(image_view);
1613 normal_kernel=(double *) RelinquishMagickMemory(normal_kernel);
1614 if (status == MagickFalse)
1615 convolve_image=DestroyImage(convolve_image);
1616 return(convolve_image);
1620 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1624 % D e s p e c k l e I m a g e %
1628 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1630 % DespeckleImage() reduces the speckle noise in an image while perserving the
1631 % edges of the original image.
1633 % The format of the DespeckleImage method is:
1635 % Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1637 % A description of each parameter follows:
1639 % o image: the image.
1641 % o exception: return any errors or warnings in this structure.
1645 static Quantum **DestroyPixelThreadSet(Quantum **pixels)
1650 assert(pixels != (Quantum **) NULL);
1651 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
1652 if (pixels[i] != (Quantum *) NULL)
1653 pixels[i]=(Quantum *) RelinquishMagickMemory(pixels[i]);
1654 pixels=(Quantum **) RelinquishAlignedMemory(pixels);
1658 static Quantum **AcquirePixelThreadSet(const size_t count)
1669 number_threads=GetOpenMPMaximumThreads();
1670 pixels=(Quantum **) AcquireAlignedMemory(number_threads,sizeof(*pixels));
1671 if (pixels == (Quantum **) NULL)
1672 return((Quantum **) NULL);
1673 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
1674 for (i=0; i < (long) number_threads; i++)
1676 pixels[i]=(Quantum *) AcquireQuantumMemory(count,sizeof(**pixels));
1677 if (pixels[i] == (Quantum *) NULL)
1678 return(DestroyPixelThreadSet(pixels));
1683 static void Hull(const long x_offset,const long y_offset,
1684 const unsigned long columns,const unsigned long rows,Quantum *f,Quantum *g,
1702 assert(f != (Quantum *) NULL);
1703 assert(g != (Quantum *) NULL);
1706 r=p+(y_offset*((long) columns+2)+x_offset);
1707 for (y=0; y < (long) rows; y++)
1713 for (x=(long) columns; x != 0; x--)
1715 v=(MagickRealType) (*p);
1716 if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1717 v+=ScaleCharToQuantum(1);
1724 for (x=(long) columns; x != 0; x--)
1726 v=(MagickRealType) (*p);
1727 if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
1728 v-=(long) ScaleCharToQuantum(1);
1740 r=q+(y_offset*((long) columns+2)+x_offset);
1741 s=q-(y_offset*((long) columns+2)+x_offset);
1742 for (y=0; y < (long) rows; y++)
1749 for (x=(long) columns; x != 0; x--)
1751 v=(MagickRealType) (*q);
1752 if (((MagickRealType) *s >=
1753 (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1754 ((MagickRealType) *r > v))
1755 v+=ScaleCharToQuantum(1);
1763 for (x=(long) columns; x != 0; x--)
1765 v=(MagickRealType) (*q);
1766 if (((MagickRealType) *s <=
1767 (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1768 ((MagickRealType) *r < v))
1769 v-=(MagickRealType) ScaleCharToQuantum(1);
1783 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1785 #define DespeckleImageTag "Despeckle/Image"
1808 X[4] = {0, 1, 1,-1},
1809 Y[4] = {1, 0, 1, 1};
1812 Allocate despeckled image.
1814 assert(image != (const Image *) NULL);
1815 assert(image->signature == MagickSignature);
1816 if (image->debug != MagickFalse)
1817 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1818 assert(exception != (ExceptionInfo *) NULL);
1819 assert(exception->signature == MagickSignature);
1820 despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1822 if (despeckle_image == (Image *) NULL)
1823 return((Image *) NULL);
1824 if (SetImageStorageClass(despeckle_image,DirectClass) == MagickFalse)
1826 InheritException(exception,&despeckle_image->exception);
1827 despeckle_image=DestroyImage(despeckle_image);
1828 return((Image *) NULL);
1831 Allocate image buffers.
1833 length=(size_t) ((image->columns+2)*(image->rows+2));
1834 pixels=AcquirePixelThreadSet(length);
1835 buffers=AcquirePixelThreadSet(length);
1836 if ((pixels == (Quantum **) NULL) || (buffers == (Quantum **) NULL))
1838 if (buffers != (Quantum **) NULL)
1839 buffers=DestroyPixelThreadSet(buffers);
1840 if (pixels != (Quantum **) NULL)
1841 pixels=DestroyPixelThreadSet(pixels);
1842 despeckle_image=DestroyImage(despeckle_image);
1843 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1846 Reduce speckle in the image.
1849 image_view=AcquireCacheView(image);
1850 despeckle_view=AcquireCacheView(despeckle_image);
1851 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1852 #pragma omp parallel for schedule(dynamic,4) shared(status)
1854 for (channel=0; channel <= 3; channel++)
1869 if (status == MagickFalse)
1871 id=GetOpenMPThreadId();
1873 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
1875 j=(long) image->columns+2;
1876 for (y=0; y < (long) image->rows; y++)
1878 register const PixelPacket
1881 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1882 if (p == (const PixelPacket *) NULL)
1885 for (x=0; x < (long) image->columns; x++)
1889 case 0: pixel[j]=p->red; break;
1890 case 1: pixel[j]=p->green; break;
1891 case 2: pixel[j]=p->blue; break;
1892 case 3: pixel[j]=p->opacity; break;
1900 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1901 for (i=0; i < 4; i++)
1903 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,1);
1904 Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,1);
1905 Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,-1);
1906 Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,-1);
1908 j=(long) image->columns+2;
1909 for (y=0; y < (long) image->rows; y++)
1914 register PixelPacket
1917 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1919 if (q == (PixelPacket *) NULL)
1922 for (x=0; x < (long) image->columns; x++)
1926 case 0: q->red=pixel[j]; break;
1927 case 1: q->green=pixel[j]; break;
1928 case 2: q->blue=pixel[j]; break;
1929 case 3: q->opacity=pixel[j]; break;
1935 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1936 if (sync == MagickFalse)
1943 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1948 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1949 #pragma omp critical (MagickCore_DespeckleImage)
1951 proceed=SetImageProgress(image,DespeckleImageTag,channel,3);
1952 if (proceed == MagickFalse)
1956 despeckle_view=DestroyCacheView(despeckle_view);
1957 image_view=DestroyCacheView(image_view);
1958 buffers=DestroyPixelThreadSet(buffers);
1959 pixels=DestroyPixelThreadSet(pixels);
1960 despeckle_image->type=image->type;
1961 if (status == MagickFalse)
1962 despeckle_image=DestroyImage(despeckle_image);
1963 return(despeckle_image);
1967 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1971 % E d g e I m a g e %
1975 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1977 % EdgeImage() finds edges in an image. Radius defines the radius of the
1978 % convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1981 % The format of the EdgeImage method is:
1983 % Image *EdgeImage(const Image *image,const double radius,
1984 % ExceptionInfo *exception)
1986 % A description of each parameter follows:
1988 % o image: the image.
1990 % o radius: the radius of the pixel neighborhood.
1992 % o exception: return any errors or warnings in this structure.
1995 MagickExport Image *EdgeImage(const Image *image,const double radius,
1996 ExceptionInfo *exception)
2010 assert(image != (const Image *) NULL);
2011 assert(image->signature == MagickSignature);
2012 if (image->debug != MagickFalse)
2013 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2014 assert(exception != (ExceptionInfo *) NULL);
2015 assert(exception->signature == MagickSignature);
2016 width=GetOptimalKernelWidth1D(radius,0.5);
2017 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2018 if (kernel == (double *) NULL)
2019 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2020 for (i=0; i < (long) (width*width); i++)
2022 kernel[i/2]=(double) (width*width-1.0);
2023 edge_image=ConvolveImage(image,width,kernel,exception);
2024 kernel=(double *) RelinquishMagickMemory(kernel);
2029 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2033 % E m b o s s I m a g e %
2037 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2039 % EmbossImage() returns a grayscale image with a three-dimensional effect.
2040 % We convolve the image with a Gaussian operator of the given radius and
2041 % standard deviation (sigma). For reasonable results, radius should be
2042 % larger than sigma. Use a radius of 0 and Emboss() selects a suitable
2045 % The format of the EmbossImage method is:
2047 % Image *EmbossImage(const Image *image,const double radius,
2048 % const double sigma,ExceptionInfo *exception)
2050 % A description of each parameter follows:
2052 % o image: the image.
2054 % o radius: the radius of the pixel neighborhood.
2056 % o sigma: the standard deviation of the Gaussian, in pixels.
2058 % o exception: return any errors or warnings in this structure.
2061 MagickExport Image *EmbossImage(const Image *image,const double radius,
2062 const double sigma,ExceptionInfo *exception)
2082 assert(image != (Image *) NULL);
2083 assert(image->signature == MagickSignature);
2084 if (image->debug != MagickFalse)
2085 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2086 assert(exception != (ExceptionInfo *) NULL);
2087 assert(exception->signature == MagickSignature);
2088 width=GetOptimalKernelWidth2D(radius,sigma);
2089 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2090 if (kernel == (double *) NULL)
2091 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2095 for (v=(-j); v <= j; v++)
2097 for (u=(-j); u <= j; u++)
2099 kernel[i]=((u < 0) || (v < 0) ? -8.0 : 8.0)*
2100 exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
2101 (2.0*MagickPI*MagickSigma*MagickSigma);
2108 emboss_image=ConvolveImage(image,width,kernel,exception);
2109 if (emboss_image != (Image *) NULL)
2110 (void) EqualizeImage(emboss_image);
2111 kernel=(double *) RelinquishMagickMemory(kernel);
2112 return(emboss_image);
2116 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2120 % G a u s s i a n B l u r I m a g e %
2124 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2126 % GaussianBlurImage() blurs an image. We convolve the image with a
2127 % Gaussian operator of the given radius and standard deviation (sigma).
2128 % For reasonable results, the radius should be larger than sigma. Use a
2129 % radius of 0 and GaussianBlurImage() selects a suitable radius for you
2131 % The format of the GaussianBlurImage method is:
2133 % Image *GaussianBlurImage(const Image *image,onst double radius,
2134 % const double sigma,ExceptionInfo *exception)
2135 % Image *GaussianBlurImageChannel(const Image *image,
2136 % const ChannelType channel,const double radius,const double sigma,
2137 % ExceptionInfo *exception)
2139 % A description of each parameter follows:
2141 % o image: the image.
2143 % o channel: the channel type.
2145 % o radius: the radius of the Gaussian, in pixels, not counting the center
2148 % o sigma: the standard deviation of the Gaussian, in pixels.
2150 % o exception: return any errors or warnings in this structure.
2154 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
2155 const double sigma,ExceptionInfo *exception)
2160 blur_image=GaussianBlurImageChannel(image,DefaultChannels,radius,sigma,
2165 MagickExport Image *GaussianBlurImageChannel(const Image *image,
2166 const ChannelType channel,const double radius,const double sigma,
2167 ExceptionInfo *exception)
2186 assert(image != (const Image *) NULL);
2187 assert(image->signature == MagickSignature);
2188 if (image->debug != MagickFalse)
2189 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2190 assert(exception != (ExceptionInfo *) NULL);
2191 assert(exception->signature == MagickSignature);
2192 width=GetOptimalKernelWidth2D(radius,sigma);
2193 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
2194 if (kernel == (double *) NULL)
2195 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2198 for (v=(-j); v <= j; v++)
2200 for (u=(-j); u <= j; u++)
2201 kernel[i++]=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
2202 (2.0*MagickPI*MagickSigma*MagickSigma);
2204 blur_image=ConvolveImageChannel(image,channel,width,kernel,exception);
2205 kernel=(double *) RelinquishMagickMemory(kernel);
2210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2214 % M e d i a n F i l t e r I m a g e %
2218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2220 % MedianFilterImage() applies a digital filter that improves the quality
2221 % of a noisy image. Each pixel is replaced by the median in a set of
2222 % neighboring pixels as defined by radius.
2224 % The algorithm was contributed by Mike Edmonds and implements an insertion
2225 % sort for selecting median color-channel values. For more on this algorithm
2226 % see "Skip Lists: A probabilistic Alternative to Balanced Trees" by William
2227 % Pugh in the June 1990 of Communications of the ACM.
2229 % The format of the MedianFilterImage method is:
2231 % Image *MedianFilterImage(const Image *image,const double radius,
2232 % ExceptionInfo *exception)
2234 % A description of each parameter follows:
2236 % o image: the image.
2238 % o radius: the radius of the pixel neighborhood.
2240 % o exception: return any errors or warnings in this structure.
2244 #define MedianListChannels 5
2246 typedef struct _MedianListNode
2254 typedef struct _MedianSkipList
2263 typedef struct _MedianPixelList
2271 lists[MedianListChannels];
2274 static MedianPixelList *DestroyMedianPixelList(MedianPixelList *pixel_list)
2279 if (pixel_list == (MedianPixelList *) NULL)
2280 return((MedianPixelList *) NULL);
2281 for (i=0; i < MedianListChannels; i++)
2282 if (pixel_list->lists[i].nodes != (MedianListNode *) NULL)
2283 pixel_list->lists[i].nodes=(MedianListNode *) RelinquishMagickMemory(
2284 pixel_list->lists[i].nodes);
2285 pixel_list=(MedianPixelList *) RelinquishAlignedMemory(pixel_list);
2289 static MedianPixelList **DestroyMedianPixelListThreadSet(
2290 MedianPixelList **pixel_list)
2295 assert(pixel_list != (MedianPixelList **) NULL);
2296 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
2297 if (pixel_list[i] != (MedianPixelList *) NULL)
2298 pixel_list[i]=DestroyMedianPixelList(pixel_list[i]);
2299 pixel_list=(MedianPixelList **) RelinquishAlignedMemory(pixel_list);
2303 static MedianPixelList *AcquireMedianPixelList(const unsigned long width)
2311 pixel_list=(MedianPixelList *) AcquireAlignedMemory(1,sizeof(*pixel_list));
2312 if (pixel_list == (MedianPixelList *) NULL)
2314 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
2315 pixel_list->center=width*width/2;
2316 for (i=0; i < MedianListChannels; i++)
2318 pixel_list->lists[i].nodes=(MedianListNode *) AcquireQuantumMemory(65537UL,
2319 sizeof(*pixel_list->lists[i].nodes));
2320 if (pixel_list->lists[i].nodes == (MedianListNode *) NULL)
2321 return(DestroyMedianPixelList(pixel_list));
2322 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
2323 sizeof(*pixel_list->lists[i].nodes));
2325 pixel_list->signature=MagickSignature;
2329 static MedianPixelList **AcquireMedianPixelListThreadSet(
2330 const unsigned long width)
2341 number_threads=GetOpenMPMaximumThreads();
2342 pixel_list=(MedianPixelList **) AcquireAlignedMemory(number_threads,
2343 sizeof(*pixel_list));
2344 if (pixel_list == (MedianPixelList **) NULL)
2345 return((MedianPixelList **) NULL);
2346 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
2347 for (i=0; i < (long) number_threads; i++)
2349 pixel_list[i]=AcquireMedianPixelList(width);
2350 if (pixel_list[i] == (MedianPixelList *) NULL)
2351 return(DestroyMedianPixelListThreadSet(pixel_list));
2356 static void AddNodeMedianPixelList(MedianPixelList *pixel_list,
2357 const long channel,const unsigned long color)
2362 register MedianSkipList
2370 Initialize the node.
2372 list=pixel_list->lists+channel;
2373 list->nodes[color].signature=pixel_list->signature;
2374 list->nodes[color].count=1;
2376 Determine where it belongs in the list.
2379 for (level=list->level; level >= 0; level--)
2381 while (list->nodes[search].next[level] < color)
2382 search=list->nodes[search].next[level];
2383 update[level]=search;
2386 Generate a pseudo-random level for this node.
2388 for (level=0; ; level++)
2390 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
2391 if ((pixel_list->seed & 0x300) != 0x300)
2396 if (level > (list->level+2))
2397 level=list->level+2;
2399 If we're raising the list's level, link back to the root node.
2401 while (level > list->level)
2404 update[list->level]=65536UL;
2407 Link the node into the skip-list.
2411 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
2412 list->nodes[update[level]].next[level]=color;
2414 while (level-- > 0);
2417 static MagickPixelPacket GetMedianPixelList(MedianPixelList *pixel_list)
2425 register MedianSkipList
2434 channels[MedianListChannels];
2437 Find the median value for each of the color.
2439 center=pixel_list->center;
2440 for (channel=0; channel < 5; channel++)
2442 list=pixel_list->lists+channel;
2447 color=list->nodes[color].next[0];
2448 count+=list->nodes[color].count;
2450 while (count <= center);
2451 channels[channel]=(unsigned short) color;
2453 GetMagickPixelPacket((const Image *) NULL,&pixel);
2454 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
2455 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
2456 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
2457 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
2458 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
2462 static inline void InsertMedianPixelList(const Image *image,
2463 const PixelPacket *pixel,const IndexPacket *indexes,
2464 MedianPixelList *pixel_list)
2472 index=ScaleQuantumToShort(pixel->red);
2473 signature=pixel_list->lists[0].nodes[index].signature;
2474 if (signature == pixel_list->signature)
2475 pixel_list->lists[0].nodes[index].count++;
2477 AddNodeMedianPixelList(pixel_list,0,index);
2478 index=ScaleQuantumToShort(pixel->green);
2479 signature=pixel_list->lists[1].nodes[index].signature;
2480 if (signature == pixel_list->signature)
2481 pixel_list->lists[1].nodes[index].count++;
2483 AddNodeMedianPixelList(pixel_list,1,index);
2484 index=ScaleQuantumToShort(pixel->blue);
2485 signature=pixel_list->lists[2].nodes[index].signature;
2486 if (signature == pixel_list->signature)
2487 pixel_list->lists[2].nodes[index].count++;
2489 AddNodeMedianPixelList(pixel_list,2,index);
2490 index=ScaleQuantumToShort(pixel->opacity);
2491 signature=pixel_list->lists[3].nodes[index].signature;
2492 if (signature == pixel_list->signature)
2493 pixel_list->lists[3].nodes[index].count++;
2495 AddNodeMedianPixelList(pixel_list,3,index);
2496 if (image->colorspace == CMYKColorspace)
2497 index=ScaleQuantumToShort(*indexes);
2498 signature=pixel_list->lists[4].nodes[index].signature;
2499 if (signature == pixel_list->signature)
2500 pixel_list->lists[4].nodes[index].count++;
2502 AddNodeMedianPixelList(pixel_list,4,index);
2505 static void ResetMedianPixelList(MedianPixelList *pixel_list)
2513 register MedianListNode
2516 register MedianSkipList
2520 Reset the skip-list.
2522 for (channel=0; channel < 5; channel++)
2524 list=pixel_list->lists+channel;
2525 root=list->nodes+65536UL;
2527 for (level=0; level < 9; level++)
2528 root->next[level]=65536UL;
2530 pixel_list->seed=pixel_list->signature++;
2533 MagickExport Image *MedianFilterImage(const Image *image,const double radius,
2534 ExceptionInfo *exception)
2536 #define MedianFilterImageTag "MedianFilter/Image"
2559 Initialize median image attributes.
2561 assert(image != (Image *) NULL);
2562 assert(image->signature == MagickSignature);
2563 if (image->debug != MagickFalse)
2564 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2565 assert(exception != (ExceptionInfo *) NULL);
2566 assert(exception->signature == MagickSignature);
2567 width=GetOptimalKernelWidth2D(radius,0.5);
2568 if ((image->columns < width) || (image->rows < width))
2569 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
2570 median_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2572 if (median_image == (Image *) NULL)
2573 return((Image *) NULL);
2574 if (SetImageStorageClass(median_image,DirectClass) == MagickFalse)
2576 InheritException(exception,&median_image->exception);
2577 median_image=DestroyImage(median_image);
2578 return((Image *) NULL);
2580 pixel_list=AcquireMedianPixelListThreadSet(width);
2581 if (pixel_list == (MedianPixelList **) NULL)
2583 median_image=DestroyImage(median_image);
2584 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2587 Median filter each image row.
2591 image_view=AcquireCacheView(image);
2592 median_view=AcquireCacheView(median_image);
2593 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2594 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2596 for (y=0; y < (long) median_image->rows; y++)
2598 register const IndexPacket
2601 register const PixelPacket
2604 register IndexPacket
2605 *restrict median_indexes;
2611 register PixelPacket
2614 if (status == MagickFalse)
2616 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
2617 2L),image->columns+width,width,exception);
2618 q=QueueCacheViewAuthenticPixels(median_view,0,y,median_image->columns,1,
2620 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2625 indexes=GetCacheViewVirtualIndexQueue(image_view);
2626 median_indexes=GetCacheViewAuthenticIndexQueue(median_view);
2627 id=GetOpenMPThreadId();
2628 for (x=0; x < (long) median_image->columns; x++)
2633 register const PixelPacket
2636 register const IndexPacket
2645 ResetMedianPixelList(pixel_list[id]);
2646 for (v=0; v < (long) width; v++)
2648 for (u=0; u < (long) width; u++)
2649 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
2650 r+=image->columns+width;
2651 s+=image->columns+width;
2653 pixel=GetMedianPixelList(pixel_list[id]);
2654 SetPixelPacket(median_image,&pixel,q,median_indexes+x);
2658 if (SyncCacheViewAuthenticPixels(median_view,exception) == MagickFalse)
2660 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2665 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2666 #pragma omp critical (MagickCore_MedianFilterImage)
2668 proceed=SetImageProgress(image,MedianFilterImageTag,progress++,
2670 if (proceed == MagickFalse)
2674 median_view=DestroyCacheView(median_view);
2675 image_view=DestroyCacheView(image_view);
2676 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
2677 return(median_image);
2681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2685 % M o t i o n B l u r I m a g e %
2689 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2691 % MotionBlurImage() simulates motion blur. We convolve the image with a
2692 % Gaussian operator of the given radius and standard deviation (sigma).
2693 % For reasonable results, radius should be larger than sigma. Use a
2694 % radius of 0 and MotionBlurImage() selects a suitable radius for you.
2695 % Angle gives the angle of the blurring motion.
2697 % Andrew Protano contributed this effect.
2699 % The format of the MotionBlurImage method is:
2701 % Image *MotionBlurImage(const Image *image,const double radius,
2702 % const double sigma,const double angle,ExceptionInfo *exception)
2703 % Image *MotionBlurImageChannel(const Image *image,const ChannelType channel,
2704 % const double radius,const double sigma,const double angle,
2705 % ExceptionInfo *exception)
2707 % A description of each parameter follows:
2709 % o image: the image.
2711 % o channel: the channel type.
2713 % o radius: the radius of the Gaussian, in pixels, not counting the center
2714 % o radius: the radius of the Gaussian, in pixels, not counting
2717 % o sigma: the standard deviation of the Gaussian, in pixels.
2719 % o angle: Apply the effect along this angle.
2721 % o exception: return any errors or warnings in this structure.
2725 static double *GetMotionBlurKernel(const unsigned long width,const double sigma)
2735 Generate a 1-D convolution kernel.
2737 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2738 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
2739 if (kernel == (double *) NULL)
2742 for (i=0; i < (long) width; i++)
2744 kernel[i]=exp((-((double) i*i)/(double) (2.0*MagickSigma*MagickSigma)))/
2745 (MagickSQ2PI*MagickSigma);
2746 normalize+=kernel[i];
2748 for (i=0; i < (long) width; i++)
2749 kernel[i]/=normalize;
2753 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
2754 const double sigma,const double angle,ExceptionInfo *exception)
2759 motion_blur=MotionBlurImageChannel(image,DefaultChannels,radius,sigma,angle,
2761 return(motion_blur);
2764 MagickExport Image *MotionBlurImageChannel(const Image *image,
2765 const ChannelType channel,const double radius,const double sigma,
2766 const double angle,ExceptionInfo *exception)
2768 typedef struct _OffsetInfo
2807 assert(image != (Image *) NULL);
2808 assert(image->signature == MagickSignature);
2809 if (image->debug != MagickFalse)
2810 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2811 assert(exception != (ExceptionInfo *) NULL);
2812 width=GetOptimalKernelWidth1D(radius,sigma);
2813 kernel=GetMotionBlurKernel(width,sigma);
2814 if (kernel == (double *) NULL)
2815 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2816 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2817 if (offset == (OffsetInfo *) NULL)
2819 kernel=(double *) RelinquishMagickMemory(kernel);
2820 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2822 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2823 if (blur_image == (Image *) NULL)
2825 kernel=(double *) RelinquishMagickMemory(kernel);
2826 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2827 return((Image *) NULL);
2829 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
2831 kernel=(double *) RelinquishMagickMemory(kernel);
2832 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2833 InheritException(exception,&blur_image->exception);
2834 blur_image=DestroyImage(blur_image);
2835 return((Image *) NULL);
2837 point.x=(double) width*sin(DegreesToRadians(angle));
2838 point.y=(double) width*cos(DegreesToRadians(angle));
2839 for (i=0; i < (long) width; i++)
2841 offset[i].x=(long) ((i*point.y)/hypot(point.x,point.y)+0.5);
2842 offset[i].y=(long) ((i*point.x)/hypot(point.x,point.y)+0.5);
2849 GetMagickPixelPacket(image,&bias);
2850 image_view=AcquireCacheView(image);
2851 blur_view=AcquireCacheView(blur_image);
2852 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2853 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2855 for (y=0; y < (long) image->rows; y++)
2857 register IndexPacket
2858 *restrict blur_indexes;
2863 register PixelPacket
2866 if (status == MagickFalse)
2868 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2870 if (q == (PixelPacket *) NULL)
2875 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
2876 for (x=0; x < (long) image->columns; x++)
2890 register const IndexPacket
2895 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
2897 for (i=0; i < (long) width; i++)
2899 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2900 offset[i].y,&pixel,exception);
2901 qixel.red+=(*k)*pixel.red;
2902 qixel.green+=(*k)*pixel.green;
2903 qixel.blue+=(*k)*pixel.blue;
2904 qixel.opacity+=(*k)*pixel.opacity;
2905 if (image->colorspace == CMYKColorspace)
2907 indexes=GetCacheViewVirtualIndexQueue(image_view);
2908 qixel.index+=(*k)*(*indexes);
2912 if ((channel & RedChannel) != 0)
2913 q->red=RoundToQuantum(qixel.red);
2914 if ((channel & GreenChannel) != 0)
2915 q->green=RoundToQuantum(qixel.green);
2916 if ((channel & BlueChannel) != 0)
2917 q->blue=RoundToQuantum(qixel.blue);
2918 if ((channel & OpacityChannel) != 0)
2919 q->opacity=RoundToQuantum(qixel.opacity);
2920 if (((channel & IndexChannel) != 0) &&
2921 (image->colorspace == CMYKColorspace))
2922 blur_indexes[x]=(IndexPacket) RoundToQuantum(qixel.index);
2932 for (i=0; i < (long) width; i++)
2934 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2935 offset[i].y,&pixel,exception);
2936 alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
2937 qixel.red+=(*k)*alpha*pixel.red;
2938 qixel.green+=(*k)*alpha*pixel.green;
2939 qixel.blue+=(*k)*alpha*pixel.blue;
2940 qixel.opacity+=(*k)*pixel.opacity;
2941 if (image->colorspace == CMYKColorspace)
2943 indexes=GetCacheViewVirtualIndexQueue(image_view);
2944 qixel.index+=(*k)*alpha*(*indexes);
2949 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2950 if ((channel & RedChannel) != 0)
2951 q->red=RoundToQuantum(gamma*qixel.red);
2952 if ((channel & GreenChannel) != 0)
2953 q->green=RoundToQuantum(gamma*qixel.green);
2954 if ((channel & BlueChannel) != 0)
2955 q->blue=RoundToQuantum(gamma*qixel.blue);
2956 if ((channel & OpacityChannel) != 0)
2957 q->opacity=RoundToQuantum(qixel.opacity);
2958 if (((channel & IndexChannel) != 0) &&
2959 (image->colorspace == CMYKColorspace))
2960 blur_indexes[x]=(IndexPacket) RoundToQuantum(gamma*qixel.index);
2964 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2966 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2971 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2972 #pragma omp critical (MagickCore_MotionBlurImageChannel)
2974 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2975 if (proceed == MagickFalse)
2979 blur_view=DestroyCacheView(blur_view);
2980 image_view=DestroyCacheView(image_view);
2981 kernel=(double *) RelinquishMagickMemory(kernel);
2982 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2983 if (status == MagickFalse)
2984 blur_image=DestroyImage(blur_image);
2989 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2993 % P r e v i e w I m a g e %
2997 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2999 % PreviewImage() tiles 9 thumbnails of the specified image with an image
3000 % processing operation applied with varying parameters. This may be helpful
3001 % pin-pointing an appropriate parameter for a particular image processing
3004 % The format of the PreviewImages method is:
3006 % Image *PreviewImages(const Image *image,const PreviewType preview,
3007 % ExceptionInfo *exception)
3009 % A description of each parameter follows:
3011 % o image: the image.
3013 % o preview: the image processing operation.
3015 % o exception: return any errors or warnings in this structure.
3018 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
3019 ExceptionInfo *exception)
3021 #define NumberTiles 9
3022 #define PreviewImageTag "Preview/Image"
3023 #define DefaultPreviewGeometry "204x204+10+10"
3026 factor[MaxTextExtent],
3027 label[MaxTextExtent];
3069 Open output image file.
3071 assert(image != (Image *) NULL);
3072 assert(image->signature == MagickSignature);
3073 if (image->debug != MagickFalse)
3074 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3078 preview_info=AcquireImageInfo();
3079 SetGeometry(image,&geometry);
3080 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
3081 &geometry.width,&geometry.height);
3082 images=NewImageList();
3084 GetQuantizeInfo(&quantize_info);
3090 for (i=0; i < NumberTiles; i++)
3092 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
3093 if (thumbnail == (Image *) NULL)
3095 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
3097 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
3098 if (i == (NumberTiles/2))
3100 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
3101 AppendImageToList(&images,thumbnail);
3109 preview_image=RotateImage(thumbnail,degrees,exception);
3110 (void) FormatMagickString(label,MaxTextExtent,"rotate %g",degrees);
3116 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
3117 (void) FormatMagickString(label,MaxTextExtent,"shear %gx%g",
3118 degrees,2.0*degrees);
3123 x=(long) ((i+1)*thumbnail->columns)/NumberTiles;
3124 y=(long) ((i+1)*thumbnail->rows)/NumberTiles;
3125 preview_image=RollImage(thumbnail,x,y,exception);
3126 (void) FormatMagickString(label,MaxTextExtent,"roll %ldx%ld",x,y);
3131 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3132 if (preview_image == (Image *) NULL)
3134 (void) FormatMagickString(factor,MaxTextExtent,"100,100,%g",
3136 (void) ModulateImage(preview_image,factor);
3137 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3140 case SaturationPreview:
3142 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3143 if (preview_image == (Image *) NULL)
3145 (void) FormatMagickString(factor,MaxTextExtent,"100,%g",2.0*percentage);
3146 (void) ModulateImage(preview_image,factor);
3147 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3150 case BrightnessPreview:
3152 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3153 if (preview_image == (Image *) NULL)
3155 (void) FormatMagickString(factor,MaxTextExtent,"%g",2.0*percentage);
3156 (void) ModulateImage(preview_image,factor);
3157 (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
3163 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3164 if (preview_image == (Image *) NULL)
3167 (void) GammaImageChannel(preview_image,DefaultChannels,gamma);
3168 (void) FormatMagickString(label,MaxTextExtent,"gamma %g",gamma);
3173 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3174 if (preview_image != (Image *) NULL)
3175 for (x=0; x < i; x++)
3176 (void) ContrastImage(preview_image,MagickTrue);
3177 (void) FormatMagickString(label,MaxTextExtent,"contrast (%ld)",i+1);
3182 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3183 if (preview_image == (Image *) NULL)
3185 for (x=0; x < i; x++)
3186 (void) ContrastImage(preview_image,MagickFalse);
3187 (void) FormatMagickString(label,MaxTextExtent,"+contrast (%ld)",i+1);
3190 case GrayscalePreview:
3192 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3193 if (preview_image == (Image *) NULL)
3196 quantize_info.number_colors=colors;
3197 quantize_info.colorspace=GRAYColorspace;
3198 (void) QuantizeImage(&quantize_info,preview_image);
3199 (void) FormatMagickString(label,MaxTextExtent,
3200 "-colorspace gray -colors %ld",colors);
3203 case QuantizePreview:
3205 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3206 if (preview_image == (Image *) NULL)
3209 quantize_info.number_colors=colors;
3210 (void) QuantizeImage(&quantize_info,preview_image);
3211 (void) FormatMagickString(label,MaxTextExtent,"colors %ld",colors);
3214 case DespecklePreview:
3216 for (x=0; x < (i-1); x++)
3218 preview_image=DespeckleImage(thumbnail,exception);
3219 if (preview_image == (Image *) NULL)
3221 thumbnail=DestroyImage(thumbnail);
3222 thumbnail=preview_image;
3224 preview_image=DespeckleImage(thumbnail,exception);
3225 if (preview_image == (Image *) NULL)
3227 (void) FormatMagickString(label,MaxTextExtent,"despeckle (%ld)",i+1);
3230 case ReduceNoisePreview:
3232 preview_image=ReduceNoiseImage(thumbnail,radius,exception);
3233 (void) FormatMagickString(label,MaxTextExtent,"noise %g",radius);
3236 case AddNoisePreview:
3242 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
3247 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
3252 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
3257 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
3262 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
3267 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
3272 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
3276 preview_image=ReduceNoiseImage(thumbnail,(double) i,exception);
3277 (void) FormatMagickString(label,MaxTextExtent,"+noise %s",factor);
3280 case SharpenPreview:
3282 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
3283 (void) FormatMagickString(label,MaxTextExtent,"sharpen %gx%g",radius,
3289 preview_image=BlurImage(thumbnail,radius,sigma,exception);
3290 (void) FormatMagickString(label,MaxTextExtent,"blur %gx%g",radius,
3294 case ThresholdPreview:
3296 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3297 if (preview_image == (Image *) NULL)
3299 (void) BilevelImage(thumbnail,
3300 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3301 (void) FormatMagickString(label,MaxTextExtent,"threshold %g",
3302 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
3305 case EdgeDetectPreview:
3307 preview_image=EdgeImage(thumbnail,radius,exception);
3308 (void) FormatMagickString(label,MaxTextExtent,"edge %g",radius);
3313 preview_image=SpreadImage(thumbnail,radius,exception);
3314 (void) FormatMagickString(label,MaxTextExtent,"spread %g",radius+0.5);
3317 case SolarizePreview:
3319 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3320 if (preview_image == (Image *) NULL)
3322 (void) SolarizeImage(preview_image,(double) QuantumRange*
3324 (void) FormatMagickString(label,MaxTextExtent,"solarize %g",
3325 (QuantumRange*percentage)/100.0);
3331 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
3333 (void) FormatMagickString(label,MaxTextExtent,"shade %gx%g",degrees,
3339 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3340 if (preview_image == (Image *) NULL)
3342 geometry.width=(unsigned long) (2*i+2);
3343 geometry.height=(unsigned long) (2*i+2);
3346 (void) RaiseImage(preview_image,&geometry,MagickTrue);
3347 (void) FormatMagickString(label,MaxTextExtent,"raise %lux%lu%+ld%+ld",
3348 geometry.width,geometry.height,geometry.x,geometry.y);
3351 case SegmentPreview:
3353 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3354 if (preview_image == (Image *) NULL)
3357 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
3359 (void) FormatMagickString(label,MaxTextExtent,"segment %gx%g",
3360 threshold,threshold);
3365 preview_image=SwirlImage(thumbnail,degrees,exception);
3366 (void) FormatMagickString(label,MaxTextExtent,"swirl %g",degrees);
3370 case ImplodePreview:
3373 preview_image=ImplodeImage(thumbnail,degrees,exception);
3374 (void) FormatMagickString(label,MaxTextExtent,"implode %g",degrees);
3380 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
3381 (void) FormatMagickString(label,MaxTextExtent,"wave %gx%g",0.5*degrees,
3385 case OilPaintPreview:
3387 preview_image=OilPaintImage(thumbnail,(double) radius,exception);
3388 (void) FormatMagickString(label,MaxTextExtent,"paint %g",radius);
3391 case CharcoalDrawingPreview:
3393 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
3395 (void) FormatMagickString(label,MaxTextExtent,"charcoal %gx%g",radius,
3402 filename[MaxTextExtent];
3410 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
3411 if (preview_image == (Image *) NULL)
3413 preview_info->quality=(unsigned long) percentage;
3414 (void) FormatMagickString(factor,MaxTextExtent,"%lu",
3415 preview_info->quality);
3416 file=AcquireUniqueFileResource(filename);
3419 (void) FormatMagickString(preview_image->filename,MaxTextExtent,
3420 "jpeg:%s",filename);
3421 status=WriteImage(preview_info,preview_image);
3422 if (status != MagickFalse)
3427 (void) CopyMagickString(preview_info->filename,
3428 preview_image->filename,MaxTextExtent);
3429 quality_image=ReadImage(preview_info,exception);
3430 if (quality_image != (Image *) NULL)
3432 preview_image=DestroyImage(preview_image);
3433 preview_image=quality_image;
3436 (void) RelinquishUniqueFileResource(preview_image->filename);
3437 if ((GetBlobSize(preview_image)/1024) >= 1024)
3438 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gmb ",
3439 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
3442 if (GetBlobSize(preview_image) >= 1024)
3443 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gkb ",
3444 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
3447 (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%lub ",
3448 factor,(unsigned long) GetBlobSize(thumbnail));
3452 thumbnail=DestroyImage(thumbnail);
3456 if (preview_image == (Image *) NULL)
3458 (void) DeleteImageProperty(preview_image,"label");
3459 (void) SetImageProperty(preview_image,"label",label);
3460 AppendImageToList(&images,preview_image);
3461 proceed=SetImageProgress(image,PreviewImageTag,i,NumberTiles);
3462 if (proceed == MagickFalse)
3465 if (images == (Image *) NULL)
3467 preview_info=DestroyImageInfo(preview_info);
3468 return((Image *) NULL);
3473 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
3474 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
3475 montage_info->shadow=MagickTrue;
3476 (void) CloneString(&montage_info->tile,"3x3");
3477 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
3478 (void) CloneString(&montage_info->frame,DefaultTileFrame);
3479 montage_image=MontageImages(images,montage_info,exception);
3480 montage_info=DestroyMontageInfo(montage_info);
3481 images=DestroyImageList(images);
3482 if (montage_image == (Image *) NULL)
3483 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3484 if (montage_image->montage != (char *) NULL)
3487 Free image directory.
3489 montage_image->montage=(char *) RelinquishMagickMemory(
3490 montage_image->montage);
3491 if (image->directory != (char *) NULL)
3492 montage_image->directory=(char *) RelinquishMagickMemory(
3493 montage_image->directory);
3495 preview_info=DestroyImageInfo(preview_info);
3496 return(montage_image);
3500 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3504 % R a d i a l B l u r I m a g e %
3508 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3510 % RadialBlurImage() applies a radial blur to the image.
3512 % Andrew Protano contributed this effect.
3514 % The format of the RadialBlurImage method is:
3516 % Image *RadialBlurImage(const Image *image,const double angle,
3517 % ExceptionInfo *exception)
3518 % Image *RadialBlurImageChannel(const Image *image,const ChannelType channel,
3519 % const double angle,ExceptionInfo *exception)
3521 % A description of each parameter follows:
3523 % o image: the image.
3525 % o channel: the channel type.
3527 % o angle: the angle of the radial blur.
3529 % o exception: return any errors or warnings in this structure.
3533 MagickExport Image *RadialBlurImage(const Image *image,const double angle,
3534 ExceptionInfo *exception)
3539 blur_image=RadialBlurImageChannel(image,DefaultChannels,angle,exception);
3543 MagickExport Image *RadialBlurImageChannel(const Image *image,
3544 const ChannelType channel,const double angle,ExceptionInfo *exception)
3580 Allocate blur image.
3582 assert(image != (Image *) NULL);
3583 assert(image->signature == MagickSignature);
3584 if (image->debug != MagickFalse)
3585 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3586 assert(exception != (ExceptionInfo *) NULL);
3587 assert(exception->signature == MagickSignature);
3588 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3589 if (blur_image == (Image *) NULL)
3590 return((Image *) NULL);
3591 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3593 InheritException(exception,&blur_image->exception);
3594 blur_image=DestroyImage(blur_image);
3595 return((Image *) NULL);
3597 blur_center.x=(double) image->columns/2.0;
3598 blur_center.y=(double) image->rows/2.0;
3599 blur_radius=hypot(blur_center.x,blur_center.y);
3600 n=(unsigned long) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+
3602 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
3603 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3604 sizeof(*cos_theta));
3605 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
3606 sizeof(*sin_theta));
3607 if ((cos_theta == (MagickRealType *) NULL) ||
3608 (sin_theta == (MagickRealType *) NULL))
3610 blur_image=DestroyImage(blur_image);
3611 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3613 offset=theta*(MagickRealType) (n-1)/2.0;
3614 for (i=0; i < (long) n; i++)
3616 cos_theta[i]=cos((double) (theta*i-offset));
3617 sin_theta[i]=sin((double) (theta*i-offset));
3624 GetMagickPixelPacket(image,&bias);
3625 image_view=AcquireCacheView(image);
3626 blur_view=AcquireCacheView(blur_image);
3627 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3628 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3630 for (y=0; y < (long) blur_image->rows; y++)
3632 register const IndexPacket
3635 register IndexPacket
3636 *restrict blur_indexes;
3641 register PixelPacket
3644 if (status == MagickFalse)
3646 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3648 if (q == (PixelPacket *) NULL)
3653 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
3654 for (x=0; x < (long) blur_image->columns; x++)
3675 center.x=(double) x-blur_center.x;
3676 center.y=(double) y-blur_center.y;
3677 radius=hypot((double) center.x,center.y);
3682 step=(unsigned long) (blur_radius/radius);
3691 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
3693 for (i=0; i < (long) n; i+=step)
3695 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
3696 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
3697 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
3699 qixel.red+=pixel.red;
3700 qixel.green+=pixel.green;
3701 qixel.blue+=pixel.blue;
3702 qixel.opacity+=pixel.opacity;
3703 if (image->colorspace == CMYKColorspace)
3705 indexes=GetCacheViewVirtualIndexQueue(image_view);
3706 qixel.index+=(*indexes);
3710 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3712 if ((channel & RedChannel) != 0)
3713 q->red=RoundToQuantum(normalize*qixel.red);
3714 if ((channel & GreenChannel) != 0)
3715 q->green=RoundToQuantum(normalize*qixel.green);
3716 if ((channel & BlueChannel) != 0)
3717 q->blue=RoundToQuantum(normalize*qixel.blue);
3718 if ((channel & OpacityChannel) != 0)
3719 q->opacity=RoundToQuantum(normalize*qixel.opacity);
3720 if (((channel & IndexChannel) != 0) &&
3721 (image->colorspace == CMYKColorspace))
3722 blur_indexes[x]=(IndexPacket) RoundToQuantum(normalize*qixel.index);
3732 for (i=0; i < (long) n; i+=step)
3734 (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
3735 center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
3736 blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
3738 alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
3739 qixel.red+=alpha*pixel.red;
3740 qixel.green+=alpha*pixel.green;
3741 qixel.blue+=alpha*pixel.blue;
3742 qixel.opacity+=pixel.opacity;
3743 if (image->colorspace == CMYKColorspace)
3745 indexes=GetCacheViewVirtualIndexQueue(image_view);
3746 qixel.index+=alpha*(*indexes);
3751 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3752 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3754 if ((channel & RedChannel) != 0)
3755 q->red=RoundToQuantum(gamma*qixel.red);
3756 if ((channel & GreenChannel) != 0)
3757 q->green=RoundToQuantum(gamma*qixel.green);
3758 if ((channel & BlueChannel) != 0)
3759 q->blue=RoundToQuantum(gamma*qixel.blue);
3760 if ((channel & OpacityChannel) != 0)
3761 q->opacity=RoundToQuantum(normalize*qixel.opacity);
3762 if (((channel & IndexChannel) != 0) &&
3763 (image->colorspace == CMYKColorspace))
3764 blur_indexes[x]=(IndexPacket) RoundToQuantum(gamma*qixel.index);
3768 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3770 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3775 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3776 #pragma omp critical (MagickCore_RadialBlurImageChannel)
3778 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3779 if (proceed == MagickFalse)
3783 blur_view=DestroyCacheView(blur_view);
3784 image_view=DestroyCacheView(image_view);
3785 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
3786 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
3787 if (status == MagickFalse)
3788 blur_image=DestroyImage(blur_image);
3793 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3797 % R e d u c e N o i s e I m a g e %
3801 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3803 % ReduceNoiseImage() smooths the contours of an image while still preserving
3804 % edge information. The algorithm works by replacing each pixel with its
3805 % neighbor closest in value. A neighbor is defined by radius. Use a radius
3806 % of 0 and ReduceNoise() selects a suitable radius for you.
3808 % The format of the ReduceNoiseImage method is:
3810 % Image *ReduceNoiseImage(const Image *image,const double radius,
3811 % ExceptionInfo *exception)
3813 % A description of each parameter follows:
3815 % o image: the image.
3817 % o radius: the radius of the pixel neighborhood.
3819 % o exception: return any errors or warnings in this structure.
3823 static MagickPixelPacket GetNonpeakMedianPixelList(MedianPixelList *pixel_list)
3831 register MedianSkipList
3845 Finds the median value for each of the color.
3847 center=pixel_list->center;
3848 for (channel=0; channel < 5; channel++)
3850 list=pixel_list->lists+channel;
3852 next=list->nodes[color].next[0];
3858 next=list->nodes[color].next[0];
3859 count+=list->nodes[color].count;
3861 while (count <= center);
3862 if ((previous == 65536UL) && (next != 65536UL))
3865 if ((previous != 65536UL) && (next == 65536UL))
3867 channels[channel]=(unsigned short) color;
3869 GetMagickPixelPacket((const Image *) NULL,&pixel);
3870 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
3871 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
3872 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
3873 pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
3874 pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
3878 MagickExport Image *ReduceNoiseImage(const Image *image,const double radius,
3879 ExceptionInfo *exception)
3881 #define ReduceNoiseImageTag "ReduceNoise/Image"
3904 Initialize noise image attributes.
3906 assert(image != (Image *) NULL);
3907 assert(image->signature == MagickSignature);
3908 if (image->debug != MagickFalse)
3909 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3910 assert(exception != (ExceptionInfo *) NULL);
3911 assert(exception->signature == MagickSignature);
3912 width=GetOptimalKernelWidth2D(radius,0.5);
3913 if ((image->columns < width) || (image->rows < width))
3914 ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
3915 noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3917 if (noise_image == (Image *) NULL)
3918 return((Image *) NULL);
3919 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
3921 InheritException(exception,&noise_image->exception);
3922 noise_image=DestroyImage(noise_image);
3923 return((Image *) NULL);
3925 pixel_list=AcquireMedianPixelListThreadSet(width);
3926 if (pixel_list == (MedianPixelList **) NULL)
3928 noise_image=DestroyImage(noise_image);
3929 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3936 image_view=AcquireCacheView(image);
3937 noise_view=AcquireCacheView(noise_image);
3938 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3939 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3941 for (y=0; y < (long) noise_image->rows; y++)
3943 register const IndexPacket
3946 register const PixelPacket
3949 register IndexPacket
3950 *restrict noise_indexes;
3956 register PixelPacket
3959 if (status == MagickFalse)
3961 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
3962 2L),image->columns+width,width,exception);
3963 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
3965 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3970 indexes=GetCacheViewVirtualIndexQueue(image_view);
3971 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
3972 id=GetOpenMPThreadId();
3973 for (x=0; x < (long) noise_image->columns; x++)
3978 register const PixelPacket
3981 register const IndexPacket
3990 ResetMedianPixelList(pixel_list[id]);
3991 for (v=0; v < (long) width; v++)
3993 for (u=0; u < (long) width; u++)
3994 InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
3995 r+=image->columns+width;
3996 s+=image->columns+width;
3998 pixel=GetNonpeakMedianPixelList(pixel_list[id]);
3999 SetPixelPacket(noise_image,&pixel,q,noise_indexes+x);
4003 if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
4005 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4010 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4011 #pragma omp critical (MagickCore_ReduceNoiseImage)
4013 proceed=SetImageProgress(image,ReduceNoiseImageTag,progress++,
4015 if (proceed == MagickFalse)
4019 noise_view=DestroyCacheView(noise_view);
4020 image_view=DestroyCacheView(image_view);
4021 pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
4022 return(noise_image);
4026 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4030 % S e l e c t i v e B l u r I m a g e %
4034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4036 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
4037 % It is similar to the unsharpen mask that sharpens everything with contrast
4038 % above a certain threshold.
4040 % The format of the SelectiveBlurImage method is:
4042 % Image *SelectiveBlurImage(const Image *image,const double radius,
4043 % const double sigma,const double threshold,ExceptionInfo *exception)
4044 % Image *SelectiveBlurImageChannel(const Image *image,
4045 % const ChannelType channel,const double radius,const double sigma,
4046 % const double threshold,ExceptionInfo *exception)
4048 % A description of each parameter follows:
4050 % o image: the image.
4052 % o channel: the channel type.
4054 % o radius: the radius of the Gaussian, in pixels, not counting the center
4057 % o sigma: the standard deviation of the Gaussian, in pixels.
4059 % o threshold: only pixels within this contrast threshold are included
4060 % in the blur operation.
4062 % o exception: return any errors or warnings in this structure.
4066 static inline MagickBooleanType SelectiveContrast(const PixelPacket *p,
4067 const PixelPacket *q,const double threshold)
4069 if (fabs(PixelIntensity(p)-PixelIntensity(q)) < threshold)
4071 return(MagickFalse);
4074 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
4075 const double sigma,const double threshold,ExceptionInfo *exception)
4080 blur_image=SelectiveBlurImageChannel(image,DefaultChannels,radius,sigma,
4081 threshold,exception);
4085 MagickExport Image *SelectiveBlurImageChannel(const Image *image,
4086 const ChannelType channel,const double radius,const double sigma,
4087 const double threshold,ExceptionInfo *exception)
4089 #define SelectiveBlurImageTag "SelectiveBlur/Image"
4121 Initialize blur image attributes.
4123 assert(image != (Image *) NULL);
4124 assert(image->signature == MagickSignature);
4125 if (image->debug != MagickFalse)
4126 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4127 assert(exception != (ExceptionInfo *) NULL);
4128 assert(exception->signature == MagickSignature);
4129 width=GetOptimalKernelWidth1D(radius,sigma);
4130 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
4131 if (kernel == (double *) NULL)
4132 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4135 for (v=(-j); v <= j; v++)
4137 for (u=(-j); u <= j; u++)
4138 kernel[i++]=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
4139 (2.0*MagickPI*MagickSigma*MagickSigma);
4141 if (image->debug != MagickFalse)
4144 format[MaxTextExtent],
4151 register const double
4154 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
4155 " SelectiveBlurImage with %ldx%ld kernel:",width,width);
4156 message=AcquireString("");
4158 for (v=0; v < (long) width; v++)
4161 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
4162 (void) ConcatenateString(&message,format);
4163 for (u=0; u < (long) width; u++)
4165 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
4166 (void) ConcatenateString(&message,format);
4168 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
4170 message=DestroyString(message);
4172 blur_image=CloneImage(image,0,0,MagickTrue,exception);
4173 if (blur_image == (Image *) NULL)
4174 return((Image *) NULL);
4175 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
4177 InheritException(exception,&blur_image->exception);
4178 blur_image=DestroyImage(blur_image);
4179 return((Image *) NULL);
4182 Threshold blur image.
4186 GetMagickPixelPacket(image,&bias);
4187 SetMagickPixelPacketBias(image,&bias);
4188 image_view=AcquireCacheView(image);
4189 blur_view=AcquireCacheView(blur_image);
4190 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4191 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4193 for (y=0; y < (long) image->rows; y++)
4201 register const IndexPacket
4204 register const PixelPacket
4207 register IndexPacket
4208 *restrict blur_indexes;
4213 register PixelPacket
4216 if (status == MagickFalse)
4218 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
4219 2L),image->columns+width,width,exception);
4220 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
4222 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4227 indexes=GetCacheViewVirtualIndexQueue(image_view);
4228 blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
4229 for (x=0; x < (long) image->columns; x++)
4238 register const double
4248 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
4250 for (v=0; v < (long) width; v++)
4252 for (u=0; u < (long) width; u++)
4254 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4256 pixel.red+=(*k)*(p+u+j)->red;
4257 pixel.green+=(*k)*(p+u+j)->green;
4258 pixel.blue+=(*k)*(p+u+j)->blue;
4263 j+=image->columns+width;
4267 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4268 if ((channel & RedChannel) != 0)
4269 q->red=RoundToQuantum(gamma*pixel.red);
4270 if ((channel & GreenChannel) != 0)
4271 q->green=RoundToQuantum(gamma*pixel.green);
4272 if ((channel & BlueChannel) != 0)
4273 q->blue=RoundToQuantum(gamma*pixel.blue);
4275 if ((channel & OpacityChannel) != 0)
4279 for (v=0; v < (long) width; v++)
4281 for (u=0; u < (long) width; u++)
4283 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4285 pixel.opacity+=(*k)*(p+u+j)->opacity;
4290 j+=image->columns+width;
4294 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4296 q->opacity=RoundToQuantum(gamma*pixel.opacity);
4299 if (((channel & IndexChannel) != 0) &&
4300 (image->colorspace == CMYKColorspace))
4304 for (v=0; v < (long) width; v++)
4306 for (u=0; u < (long) width; u++)
4308 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4310 pixel.index+=(*k)*indexes[x+u+j];
4315 j+=image->columns+width;
4319 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4321 blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
4330 for (v=0; v < (long) width; v++)
4332 for (u=0; u < (long) width; u++)
4334 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4336 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
4338 pixel.red+=(*k)*alpha*(p+u+j)->red;
4339 pixel.green+=(*k)*alpha*(p+u+j)->green;
4340 pixel.blue+=(*k)*alpha*(p+u+j)->blue;
4341 pixel.opacity+=(*k)*(p+u+j)->opacity;
4346 j+=image->columns+width;
4350 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
4351 if ((channel & RedChannel) != 0)
4352 q->red=RoundToQuantum(gamma*pixel.red);
4353 if ((channel & GreenChannel) != 0)
4354 q->green=RoundToQuantum(gamma*pixel.green);
4355 if ((channel & BlueChannel) != 0)
4356 q->blue=RoundToQuantum(gamma*pixel.blue);
4358 if ((channel & OpacityChannel) != 0)
4362 for (v=0; v < (long) width; v++)
4364 for (u=0; u < (long) width; u++)
4366 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4368 pixel.opacity+=(*k)*(p+u+j)->opacity;
4373 j+=image->columns+width;
4377 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4379 q->opacity=RoundToQuantum(pixel.opacity);
4382 if (((channel & IndexChannel) != 0) &&
4383 (image->colorspace == CMYKColorspace))
4387 for (v=0; v < (long) width; v++)
4389 for (u=0; u < (long) width; u++)
4391 if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
4393 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
4395 pixel.index+=(*k)*alpha*indexes[x+u+j];
4400 j+=image->columns+width;
4404 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
4406 blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
4413 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
4414 if (sync == MagickFalse)
4416 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4421 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4422 #pragma omp critical (MagickCore_SelectiveBlurImageChannel)
4424 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
4426 if (proceed == MagickFalse)
4430 blur_image->type=image->type;
4431 blur_view=DestroyCacheView(blur_view);
4432 image_view=DestroyCacheView(image_view);
4433 kernel=(double *) RelinquishMagickMemory(kernel);
4434 if (status == MagickFalse)
4435 blur_image=DestroyImage(blur_image);
4440 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4444 % S h a d e I m a g e %
4448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4450 % ShadeImage() shines a distant light on an image to create a
4451 % three-dimensional effect. You control the positioning of the light with
4452 % azimuth and elevation; azimuth is measured in degrees off the x axis
4453 % and elevation is measured in pixels above the Z axis.
4455 % The format of the ShadeImage method is:
4457 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4458 % const double azimuth,const double elevation,ExceptionInfo *exception)
4460 % A description of each parameter follows:
4462 % o image: the image.
4464 % o gray: A value other than zero shades the intensity of each pixel.
4466 % o azimuth, elevation: Define the light source direction.
4468 % o exception: return any errors or warnings in this structure.
4471 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
4472 const double azimuth,const double elevation,ExceptionInfo *exception)
4474 #define ShadeImageTag "Shade/Image"
4494 Initialize shaded image attributes.
4496 assert(image != (const Image *) NULL);
4497 assert(image->signature == MagickSignature);
4498 if (image->debug != MagickFalse)
4499 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4500 assert(exception != (ExceptionInfo *) NULL);
4501 assert(exception->signature == MagickSignature);
4502 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4503 if (shade_image == (Image *) NULL)
4504 return((Image *) NULL);
4505 if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
4507 InheritException(exception,&shade_image->exception);
4508 shade_image=DestroyImage(shade_image);
4509 return((Image *) NULL);
4512 Compute the light vector.
4514 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
4515 cos(DegreesToRadians(elevation));
4516 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
4517 cos(DegreesToRadians(elevation));
4518 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
4524 image_view=AcquireCacheView(image);
4525 shade_view=AcquireCacheView(shade_image);
4526 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4527 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4529 for (y=0; y < (long) image->rows; y++)
4539 register const PixelPacket
4548 register PixelPacket
4551 if (status == MagickFalse)
4553 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
4554 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
4556 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4562 Shade this row of pixels.
4564 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
4566 s1=s0+image->columns+2;
4567 s2=s1+image->columns+2;
4568 for (x=0; x < (long) image->columns; x++)
4571 Determine the surface normal and compute shading.
4573 normal.x=(double) (PixelIntensity(s0-1)+PixelIntensity(s1-1)+
4574 PixelIntensity(s2-1)-PixelIntensity(s0+1)-PixelIntensity(s1+1)-
4575 PixelIntensity(s2+1));
4576 normal.y=(double) (PixelIntensity(s2-1)+PixelIntensity(s2)+
4577 PixelIntensity(s2+1)-PixelIntensity(s0-1)-PixelIntensity(s0)-
4578 PixelIntensity(s0+1));
4579 if ((normal.x == 0.0) && (normal.y == 0.0))
4584 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
4585 if (distance > MagickEpsilon)
4588 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
4589 if (normal_distance > (MagickEpsilon*MagickEpsilon))
4590 shade=distance/sqrt((double) normal_distance);
4593 if (gray != MagickFalse)
4595 q->red=(Quantum) shade;
4596 q->green=(Quantum) shade;
4597 q->blue=(Quantum) shade;
4601 q->red=RoundToQuantum(QuantumScale*shade*s1->red);
4602 q->green=RoundToQuantum(QuantumScale*shade*s1->green);
4603 q->blue=RoundToQuantum(QuantumScale*shade*s1->blue);
4605 q->opacity=s1->opacity;
4611 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
4613 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4618 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4619 #pragma omp critical (MagickCore_ShadeImage)
4621 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
4622 if (proceed == MagickFalse)
4626 shade_view=DestroyCacheView(shade_view);
4627 image_view=DestroyCacheView(image_view);
4628 if (status == MagickFalse)
4629 shade_image=DestroyImage(shade_image);
4630 return(shade_image);
4634 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4638 % S h a r p e n I m a g e %
4642 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4644 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
4645 % operator of the given radius and standard deviation (sigma). For
4646 % reasonable results, radius should be larger than sigma. Use a radius of 0
4647 % and SharpenImage() selects a suitable radius for you.
4649 % Using a separable kernel would be faster, but the negative weights cancel
4650 % out on the corners of the kernel producing often undesirable ringing in the
4651 % filtered result; this can be avoided by using a 2D gaussian shaped image
4652 % sharpening kernel instead.
4654 % The format of the SharpenImage method is:
4656 % Image *SharpenImage(const Image *image,const double radius,
4657 % const double sigma,ExceptionInfo *exception)
4658 % Image *SharpenImageChannel(const Image *image,const ChannelType channel,
4659 % const double radius,const double sigma,ExceptionInfo *exception)
4661 % A description of each parameter follows:
4663 % o image: the image.
4665 % o channel: the channel type.
4667 % o radius: the radius of the Gaussian, in pixels, not counting the center
4670 % o sigma: the standard deviation of the Laplacian, in pixels.
4672 % o exception: return any errors or warnings in this structure.
4676 MagickExport Image *SharpenImage(const Image *image,const double radius,
4677 const double sigma,ExceptionInfo *exception)
4682 sharp_image=SharpenImageChannel(image,DefaultChannels,radius,sigma,exception);
4683 return(sharp_image);
4686 MagickExport Image *SharpenImageChannel(const Image *image,
4687 const ChannelType channel,const double radius,const double sigma,
4688 ExceptionInfo *exception)
4708 assert(image != (const Image *) NULL);
4709 assert(image->signature == MagickSignature);
4710 if (image->debug != MagickFalse)
4711 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4712 assert(exception != (ExceptionInfo *) NULL);
4713 assert(exception->signature == MagickSignature);
4714 width=GetOptimalKernelWidth2D(radius,sigma);
4715 kernel=(double *) AcquireQuantumMemory((size_t) width*width,sizeof(*kernel));
4716 if (kernel == (double *) NULL)
4717 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4721 for (v=(-j); v <= j; v++)
4723 for (u=(-j); u <= j; u++)
4725 kernel[i]=(-exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
4726 (2.0*MagickPI*MagickSigma*MagickSigma));
4727 normalize+=kernel[i];
4731 kernel[i/2]=(double) ((-2.0)*normalize);
4732 sharp_image=ConvolveImageChannel(image,channel,width,kernel,exception);
4733 kernel=(double *) RelinquishMagickMemory(kernel);
4734 return(sharp_image);
4738 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4742 % S p r e a d I m a g e %
4746 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4748 % SpreadImage() is a special effects method that randomly displaces each
4749 % pixel in a block defined by the radius parameter.
4751 % The format of the SpreadImage method is:
4753 % Image *SpreadImage(const Image *image,const double radius,
4754 % ExceptionInfo *exception)
4756 % A description of each parameter follows:
4758 % o image: the image.
4760 % o radius: Choose a random pixel in a neighborhood of this extent.
4762 % o exception: return any errors or warnings in this structure.
4765 MagickExport Image *SpreadImage(const Image *image,const double radius,
4766 ExceptionInfo *exception)
4768 #define SpreadImageTag "Spread/Image"
4796 Initialize spread image attributes.
4798 assert(image != (Image *) NULL);
4799 assert(image->signature == MagickSignature);
4800 if (image->debug != MagickFalse)
4801 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4802 assert(exception != (ExceptionInfo *) NULL);
4803 assert(exception->signature == MagickSignature);
4804 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4806 if (spread_image == (Image *) NULL)
4807 return((Image *) NULL);
4808 if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
4810 InheritException(exception,&spread_image->exception);
4811 spread_image=DestroyImage(spread_image);
4812 return((Image *) NULL);
4819 GetMagickPixelPacket(spread_image,&bias);
4820 width=GetOptimalKernelWidth1D(radius,0.5);
4821 resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
4822 random_info=AcquireRandomInfoThreadSet();
4823 image_view=AcquireCacheView(spread_image);
4824 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4825 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4827 for (y=0; y < (long) spread_image->rows; y++)
4832 register IndexPacket
4839 register PixelPacket
4842 if (status == MagickFalse)
4844 q=QueueCacheViewAuthenticPixels(image_view,0,y,spread_image->columns,1,
4846 if (q == (PixelPacket *) NULL)
4851 indexes=GetCacheViewAuthenticIndexQueue(image_view);
4853 id=GetOpenMPThreadId();
4854 for (x=0; x < (long) spread_image->columns; x++)
4856 (void) ResamplePixelColor(resample_filter[id],(double) x+width*
4857 (GetPseudoRandomValue(random_info[id])-0.5),(double) y+width*
4858 (GetPseudoRandomValue(random_info[id])-0.5),&pixel);
4859 SetPixelPacket(spread_image,&pixel,q,indexes+x);
4862 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4864 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4869 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4870 #pragma omp critical (MagickCore_SpreadImage)
4872 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
4873 if (proceed == MagickFalse)
4877 image_view=DestroyCacheView(image_view);
4878 random_info=DestroyRandomInfoThreadSet(random_info);
4879 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
4880 return(spread_image);
4884 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4888 % U n s h a r p M a s k I m a g e %
4892 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4894 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
4895 % image with a Gaussian operator of the given radius and standard deviation
4896 % (sigma). For reasonable results, radius should be larger than sigma. Use a
4897 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
4899 % The format of the UnsharpMaskImage method is:
4901 % Image *UnsharpMaskImage(const Image *image,const double radius,
4902 % const double sigma,const double amount,const double threshold,
4903 % ExceptionInfo *exception)
4904 % Image *UnsharpMaskImageChannel(const Image *image,
4905 % const ChannelType channel,const double radius,const double sigma,
4906 % const double amount,const double threshold,ExceptionInfo *exception)
4908 % A description of each parameter follows:
4910 % o image: the image.
4912 % o channel: the channel type.
4914 % o radius: the radius of the Gaussian, in pixels, not counting the center
4917 % o sigma: the standard deviation of the Gaussian, in pixels.
4919 % o amount: the percentage of the difference between the original and the
4920 % blur image that is added back into the original.
4922 % o threshold: the threshold in pixels needed to apply the diffence amount.
4924 % o exception: return any errors or warnings in this structure.
4928 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
4929 const double sigma,const double amount,const double threshold,
4930 ExceptionInfo *exception)
4935 sharp_image=UnsharpMaskImageChannel(image,DefaultChannels,radius,sigma,amount,
4936 threshold,exception);
4937 return(sharp_image);
4940 MagickExport Image *UnsharpMaskImageChannel(const Image *image,
4941 const ChannelType channel,const double radius,const double sigma,
4942 const double amount,const double threshold,ExceptionInfo *exception)
4944 #define SharpenImageTag "Sharpen/Image"
4966 assert(image != (const Image *) NULL);
4967 assert(image->signature == MagickSignature);
4968 if (image->debug != MagickFalse)
4969 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4970 assert(exception != (ExceptionInfo *) NULL);
4971 unsharp_image=BlurImageChannel(image,channel,radius,sigma,exception);
4972 if (unsharp_image == (Image *) NULL)
4973 return((Image *) NULL);
4974 quantum_threshold=(MagickRealType) QuantumRange*threshold;
4980 GetMagickPixelPacket(image,&bias);
4981 image_view=AcquireCacheView(image);
4982 unsharp_view=AcquireCacheView(unsharp_image);
4983 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4984 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4986 for (y=0; y < (long) image->rows; y++)
4991 register const IndexPacket
4994 register const PixelPacket
4997 register IndexPacket
4998 *restrict unsharp_indexes;
5003 register PixelPacket
5006 if (status == MagickFalse)
5008 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5009 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
5011 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5016 indexes=GetCacheViewVirtualIndexQueue(image_view);
5017 unsharp_indexes=GetCacheViewAuthenticIndexQueue(unsharp_view);
5019 for (x=0; x < (long) image->columns; x++)
5021 if ((channel & RedChannel) != 0)
5023 pixel.red=p->red-(MagickRealType) q->red;
5024 if (fabs(2.0*pixel.red) < quantum_threshold)
5025 pixel.red=(MagickRealType) p->red;
5027 pixel.red=(MagickRealType) p->red+(pixel.red*amount);
5028 q->red=RoundToQuantum(pixel.red);
5030 if ((channel & GreenChannel) != 0)
5032 pixel.green=p->green-(MagickRealType) q->green;
5033 if (fabs(2.0*pixel.green) < quantum_threshold)
5034 pixel.green=(MagickRealType) p->green;
5036 pixel.green=(MagickRealType) p->green+(pixel.green*amount);
5037 q->green=RoundToQuantum(pixel.green);
5039 if ((channel & BlueChannel) != 0)
5041 pixel.blue=p->blue-(MagickRealType) q->blue;
5042 if (fabs(2.0*pixel.blue) < quantum_threshold)
5043 pixel.blue=(MagickRealType) p->blue;
5045 pixel.blue=(MagickRealType) p->blue+(pixel.blue*amount);
5046 q->blue=RoundToQuantum(pixel.blue);
5048 if ((channel & OpacityChannel) != 0)
5050 pixel.opacity=p->opacity-(MagickRealType) q->opacity;
5051 if (fabs(2.0*pixel.opacity) < quantum_threshold)
5052 pixel.opacity=(MagickRealType) p->opacity;
5054 pixel.opacity=p->opacity+(pixel.opacity*amount);
5055 q->opacity=RoundToQuantum(pixel.opacity);
5057 if (((channel & IndexChannel) != 0) &&
5058 (image->colorspace == CMYKColorspace))
5060 pixel.index=unsharp_indexes[x]-(MagickRealType) indexes[x];
5061 if (fabs(2.0*pixel.index) < quantum_threshold)
5062 pixel.index=(MagickRealType) unsharp_indexes[x];
5064 pixel.index=(MagickRealType) unsharp_indexes[x]+(pixel.index*
5066 unsharp_indexes[x]=RoundToQuantum(pixel.index);
5071 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
5073 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5078 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5079 #pragma omp critical (MagickCore_UnsharpMaskImageChannel)
5081 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
5082 if (proceed == MagickFalse)
5086 unsharp_image->type=image->type;
5087 unsharp_view=DestroyCacheView(unsharp_view);
5088 image_view=DestroyCacheView(image_view);
5089 if (status == MagickFalse)
5090 unsharp_image=DestroyImage(unsharp_image);
5091 return(unsharp_image);