]> granicus.if.org Git - imagemagick/blob - MagickCore/effect.c
(no commit message)
[imagemagick] / MagickCore / effect.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                   EEEEE  FFFFF  FFFFF  EEEEE  CCCC  TTTTT                   %
7 %                   E      F      F      E     C        T                     %
8 %                   EEE    FFF    FFF    EEE   C        T                     %
9 %                   E      F      F      E     C        T                     %
10 %                   EEEEE  F      F      EEEEE  CCCC    T                     %
11 %                                                                             %
12 %                                                                             %
13 %                       MagickCore Image Effects Methods                      %
14 %                                                                             %
15 %                               Software Design                               %
16 %                                 John Cristy                                 %
17 %                                 October 1996                                %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
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.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 %
38 */
39 \f
40 /*
41   Include declarations.
42 */
43 #include "MagickCore/studio.h"
44 #include "MagickCore/accelerate.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/cache-view.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/colorspace.h"
50 #include "MagickCore/constitute.h"
51 #include "MagickCore/decorate.h"
52 #include "MagickCore/distort.h"
53 #include "MagickCore/draw.h"
54 #include "MagickCore/enhance.h"
55 #include "MagickCore/exception.h"
56 #include "MagickCore/exception-private.h"
57 #include "MagickCore/effect.h"
58 #include "MagickCore/fx.h"
59 #include "MagickCore/gem.h"
60 #include "MagickCore/gem-private.h"
61 #include "MagickCore/geometry.h"
62 #include "MagickCore/image-private.h"
63 #include "MagickCore/list.h"
64 #include "MagickCore/log.h"
65 #include "MagickCore/memory_.h"
66 #include "MagickCore/memory-private.h"
67 #include "MagickCore/monitor.h"
68 #include "MagickCore/monitor-private.h"
69 #include "MagickCore/montage.h"
70 #include "MagickCore/morphology.h"
71 #include "MagickCore/paint.h"
72 #include "MagickCore/pixel-accessor.h"
73 #include "MagickCore/pixel-private.h"
74 #include "MagickCore/property.h"
75 #include "MagickCore/quantize.h"
76 #include "MagickCore/quantum.h"
77 #include "MagickCore/quantum-private.h"
78 #include "MagickCore/random_.h"
79 #include "MagickCore/random-private.h"
80 #include "MagickCore/resample.h"
81 #include "MagickCore/resample-private.h"
82 #include "MagickCore/resize.h"
83 #include "MagickCore/resource_.h"
84 #include "MagickCore/segment.h"
85 #include "MagickCore/shear.h"
86 #include "MagickCore/signature-private.h"
87 #include "MagickCore/statistic.h"
88 #include "MagickCore/string_.h"
89 #include "MagickCore/thread-private.h"
90 #include "MagickCore/transform.h"
91 #include "MagickCore/threshold.h"
92 \f
93 /*
94 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
95 %                                                                             %
96 %                                                                             %
97 %                                                                             %
98 %     A d a p t i v e B l u r I m a g e                                       %
99 %                                                                             %
100 %                                                                             %
101 %                                                                             %
102 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
103 %
104 %  AdaptiveBlurImage() adaptively blurs the image by blurring less
105 %  intensely near image edges and more intensely far from edges.  We blur the
106 %  image with a Gaussian operator of the given radius and standard deviation
107 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
108 %  radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
109 %
110 %  The format of the AdaptiveBlurImage method is:
111 %
112 %      Image *AdaptiveBlurImage(const Image *image,const double radius,
113 %        const double sigma,ExceptionInfo *exception)
114 %
115 %  A description of each parameter follows:
116 %
117 %    o image: the image.
118 %
119 %    o radius: the radius of the Gaussian, in pixels, not counting the center
120 %      pixel.
121 %
122 %    o sigma: the standard deviation of the Laplacian, in pixels.
123 %
124 %    o exception: return any errors or warnings in this structure.
125 %
126 */
127
128 MagickExport MagickBooleanType AdaptiveLevelImage(Image *image,
129   const char *levels,ExceptionInfo *exception)
130 {
131   double
132     black_point,
133     gamma,
134     white_point;
135
136   GeometryInfo
137     geometry_info;
138
139   MagickBooleanType
140     status;
141
142   MagickStatusType
143     flags;
144
145   /*
146     Parse levels.
147   */
148   if (levels == (char *) NULL)
149     return(MagickFalse);
150   flags=ParseGeometry(levels,&geometry_info);
151   black_point=geometry_info.rho;
152   white_point=(double) QuantumRange;
153   if ((flags & SigmaValue) != 0)
154     white_point=geometry_info.sigma;
155   gamma=1.0;
156   if ((flags & XiValue) != 0)
157     gamma=geometry_info.xi;
158   if ((flags & PercentValue) != 0)
159     {
160       black_point*=(double) image->columns*image->rows/100.0;
161       white_point*=(double) image->columns*image->rows/100.0;
162     }
163   if ((flags & SigmaValue) == 0)
164     white_point=(double) QuantumRange-black_point;
165   if ((flags & AspectValue ) == 0)
166     status=LevelImage(image,black_point,white_point,gamma,exception);
167   else
168     status=LevelizeImage(image,black_point,white_point,gamma,exception);
169   return(status);
170 }
171
172 MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
173   const double sigma,ExceptionInfo *exception)
174 {
175 #define AdaptiveBlurImageTag  "Convolve/Image"
176 #define MagickSigma  (fabs(sigma) < MagickEpsilon ? MagickEpsilon : sigma)
177
178   CacheView
179     *blur_view,
180     *edge_view,
181     *image_view;
182
183   double
184     normalize;
185
186   Image
187     *blur_image,
188     *edge_image,
189     *gaussian_image;
190
191   MagickBooleanType
192     status;
193
194   MagickOffsetType
195     progress;
196
197   MagickRealType
198     **kernel;
199
200   register ssize_t
201     i;
202
203   size_t
204     width;
205
206   ssize_t
207     j,
208     k,
209     u,
210     v,
211     y;
212
213   assert(image != (const Image *) NULL);
214   assert(image->signature == MagickSignature);
215   if (image->debug != MagickFalse)
216     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
217   assert(exception != (ExceptionInfo *) NULL);
218   assert(exception->signature == MagickSignature);
219   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
220   if (blur_image == (Image *) NULL)
221     return((Image *) NULL);
222   if (fabs(sigma) < MagickEpsilon)
223     return(blur_image);
224   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
225     {
226       blur_image=DestroyImage(blur_image);
227       return((Image *) NULL);
228     }
229   /*
230     Edge detect the image brighness channel, level, blur, and level again.
231   */
232   edge_image=EdgeImage(image,radius,exception);
233   if (edge_image == (Image *) NULL)
234     {
235       blur_image=DestroyImage(blur_image);
236       return((Image *) NULL);
237     }
238   (void) AdaptiveLevelImage(edge_image,"20%,95%",exception);
239   gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
240   if (gaussian_image != (Image *) NULL)
241     {
242       edge_image=DestroyImage(edge_image);
243       edge_image=gaussian_image;
244     }
245   (void) AdaptiveLevelImage(edge_image,"10%,95%",exception);
246   /*
247     Create a set of kernels from maximum (radius,sigma) to minimum.
248   */
249   width=GetOptimalKernelWidth2D(radius,sigma);
250   kernel=(MagickRealType **) MagickAssumeAligned(AcquireAlignedMemory((size_t)
251     width,sizeof(*kernel)));
252   if (kernel == (MagickRealType  **) NULL)
253     {
254       edge_image=DestroyImage(edge_image);
255       blur_image=DestroyImage(blur_image);
256       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
257     }
258   (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
259   for (i=0; i < (ssize_t) width; i+=2)
260   {
261     kernel[i]=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory(
262       (size_t) (width-i),(width-i)*sizeof(**kernel)));
263     if (kernel[i] == (MagickRealType *) NULL)
264       break;
265     normalize=0.0;
266     j=(ssize_t) (width-i)/2;
267     k=0;
268     for (v=(-j); v <= j; v++)
269     {
270       for (u=(-j); u <= j; u++)
271       {
272         kernel[i][k]=(MagickRealType) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
273           MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
274         normalize+=kernel[i][k];
275         k++;
276       }
277     }
278     if (fabs(normalize) < MagickEpsilon)
279       normalize=MagickEpsilon;
280     normalize=PerceptibleReciprocal(normalize);
281     for (k=0; k < (j*j); k++)
282       kernel[i][k]=normalize*kernel[i][k];
283   }
284   if (i < (ssize_t) width)
285     {
286       for (i-=2; i >= 0; i-=2)
287         kernel[i]=(MagickRealType *) RelinquishAlignedMemory(kernel[i]);
288       kernel=(MagickRealType **) RelinquishAlignedMemory(kernel);
289       edge_image=DestroyImage(edge_image);
290       blur_image=DestroyImage(blur_image);
291       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
292     }
293   /*
294     Adaptively blur image.
295   */
296   status=MagickTrue;
297   progress=0;
298   image_view=AcquireVirtualCacheView(image,exception);
299   edge_view=AcquireVirtualCacheView(edge_image,exception);
300   blur_view=AcquireAuthenticCacheView(blur_image,exception);
301 #if defined(MAGICKCORE_OPENMP_SUPPORT)
302   #pragma omp parallel for schedule(static,4) shared(progress,status) \
303     magick_threads(image,blur_image,blur_image->rows,1)
304 #endif
305   for (y=0; y < (ssize_t) blur_image->rows; y++)
306   {
307     register const Quantum
308       *restrict r;
309
310     register Quantum
311       *restrict q;
312
313     register ssize_t
314       x;
315
316     if (status == MagickFalse)
317       continue;
318     r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
319     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
320       exception);
321     if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
322       {
323         status=MagickFalse;
324         continue;
325       }
326     for (x=0; x < (ssize_t) blur_image->columns; x++)
327     {
328       register const Quantum
329         *restrict p;
330
331       register ssize_t
332         i;
333
334       ssize_t
335         center,
336         j;
337
338       j=(ssize_t) ceil((double) width*QuantumScale*
339         GetPixelIntensity(edge_image,r)-0.5);
340       if (j < 0)
341         j=0;
342       else
343         if (j > (ssize_t) width)
344           j=(ssize_t) width;
345       if ((j & 0x01) != 0)
346         j--;
347       p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
348         (ssize_t) ((width-j)/2L),width-j,width-j,exception);
349       if (p == (const Quantum *) NULL)
350         break;
351       center=(ssize_t) GetPixelChannels(image)*(width-j)*((width-j)/2L)+
352         GetPixelChannels(image)*((width-j)/2L);
353       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
354       {
355         double
356           alpha,
357           gamma,
358           pixel;
359
360         PixelChannel
361           channel;
362
363         PixelTrait
364           blur_traits,
365           traits;
366
367         register const MagickRealType
368           *restrict k;
369
370         register const Quantum
371           *restrict pixels;
372
373         register ssize_t
374           u;
375
376         ssize_t
377           v;
378
379         channel=GetPixelChannelChannel(image,i);
380         traits=GetPixelChannelTraits(image,channel);
381         blur_traits=GetPixelChannelTraits(blur_image,channel);
382         if ((traits == UndefinedPixelTrait) ||
383             (blur_traits == UndefinedPixelTrait))
384           continue;
385         if (((blur_traits & CopyPixelTrait) != 0) ||
386             (GetPixelMask(image,p+center) == 0))
387           {
388             SetPixelChannel(blur_image,channel,p[center+i],q);
389             continue;
390           }
391         k=kernel[j];
392         pixels=p;
393         pixel=0.0;
394         gamma=0.0;
395         if ((blur_traits & BlendPixelTrait) == 0)
396           {
397             /*
398               No alpha blending.
399             */
400             for (v=0; v < (ssize_t) (width-j); v++)
401             {
402               for (u=0; u < (ssize_t) (width-j); u++)
403               {
404                 pixel+=(*k)*pixels[i];
405                 gamma+=(*k);
406                 k++;
407                 pixels+=GetPixelChannels(image);
408               }
409             }
410             gamma=PerceptibleReciprocal(gamma);
411             SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
412             continue;
413           }
414         /*
415           Alpha blending.
416         */
417         for (v=0; v < (ssize_t) (width-j); v++)
418         {
419           for (u=0; u < (ssize_t) (width-j); u++)
420           {
421             alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
422             pixel+=(*k)*alpha*pixels[i];
423             gamma+=(*k)*alpha;
424             k++;
425             pixels+=GetPixelChannels(image);
426           }
427         }
428         gamma=PerceptibleReciprocal(gamma);
429         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
430       }
431       q+=GetPixelChannels(blur_image);
432       r+=GetPixelChannels(edge_image);
433     }
434     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
435       status=MagickFalse;
436     if (image->progress_monitor != (MagickProgressMonitor) NULL)
437       {
438         MagickBooleanType
439           proceed;
440
441 #if defined(MAGICKCORE_OPENMP_SUPPORT)
442         #pragma omp critical (MagickCore_AdaptiveBlurImage)
443 #endif
444         proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
445           image->rows);
446         if (proceed == MagickFalse)
447           status=MagickFalse;
448       }
449   }
450   blur_image->type=image->type;
451   blur_view=DestroyCacheView(blur_view);
452   edge_view=DestroyCacheView(edge_view);
453   image_view=DestroyCacheView(image_view);
454   edge_image=DestroyImage(edge_image);
455   for (i=0; i < (ssize_t) width;  i+=2)
456     kernel[i]=(MagickRealType *) RelinquishAlignedMemory(kernel[i]);
457   kernel=(MagickRealType **) RelinquishAlignedMemory(kernel);
458   if (status == MagickFalse)
459     blur_image=DestroyImage(blur_image);
460   return(blur_image);
461 }
462 \f
463 /*
464 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
465 %                                                                             %
466 %                                                                             %
467 %                                                                             %
468 %     A d a p t i v e S h a r p e n I m a g e                                 %
469 %                                                                             %
470 %                                                                             %
471 %                                                                             %
472 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
473 %
474 %  AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
475 %  intensely near image edges and less intensely far from edges. We sharpen the
476 %  image with a Gaussian operator of the given radius and standard deviation
477 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
478 %  radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
479 %
480 %  The format of the AdaptiveSharpenImage method is:
481 %
482 %      Image *AdaptiveSharpenImage(const Image *image,const double radius,
483 %        const double sigma,ExceptionInfo *exception)
484 %
485 %  A description of each parameter follows:
486 %
487 %    o image: the image.
488 %
489 %    o radius: the radius of the Gaussian, in pixels, not counting the center
490 %      pixel.
491 %
492 %    o sigma: the standard deviation of the Laplacian, in pixels.
493 %
494 %    o exception: return any errors or warnings in this structure.
495 %
496 */
497 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
498   const double sigma,ExceptionInfo *exception)
499 {
500 #define AdaptiveSharpenImageTag  "Convolve/Image"
501 #define MagickSigma  (fabs(sigma) < MagickEpsilon ? MagickEpsilon : sigma)
502
503   CacheView
504     *sharp_view,
505     *edge_view,
506     *image_view;
507
508   double
509     normalize;
510
511   Image
512     *sharp_image,
513     *edge_image,
514     *gaussian_image;
515
516   MagickBooleanType
517     status;
518
519   MagickOffsetType
520     progress;
521
522   MagickRealType
523     **kernel;
524
525   register ssize_t
526     i;
527
528   size_t
529     width;
530
531   ssize_t
532     j,
533     k,
534     u,
535     v,
536     y;
537
538   assert(image != (const Image *) NULL);
539   assert(image->signature == MagickSignature);
540   if (image->debug != MagickFalse)
541     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
542   assert(exception != (ExceptionInfo *) NULL);
543   assert(exception->signature == MagickSignature);
544   sharp_image=CloneImage(image,0,0,MagickTrue,exception);
545   if (sharp_image == (Image *) NULL)
546     return((Image *) NULL);
547   if (fabs(sigma) < MagickEpsilon)
548     return(sharp_image);
549   if (SetImageStorageClass(sharp_image,DirectClass,exception) == MagickFalse)
550     {
551       sharp_image=DestroyImage(sharp_image);
552       return((Image *) NULL);
553     }
554   /*
555     Edge detect the image brighness channel, level, sharp, and level again.
556   */
557   edge_image=EdgeImage(image,radius,exception);
558   if (edge_image == (Image *) NULL)
559     {
560       sharp_image=DestroyImage(sharp_image);
561       return((Image *) NULL);
562     }
563   (void) AdaptiveLevelImage(edge_image,"20%,95%",exception);
564   gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
565   if (gaussian_image != (Image *) NULL)
566     {
567       edge_image=DestroyImage(edge_image);
568       edge_image=gaussian_image;
569     }
570   (void) AdaptiveLevelImage(edge_image,"10%,95%",exception);
571   /*
572     Create a set of kernels from maximum (radius,sigma) to minimum.
573   */
574   width=GetOptimalKernelWidth2D(radius,sigma);
575   kernel=(MagickRealType **) MagickAssumeAligned(AcquireAlignedMemory((size_t)
576     width,sizeof(*kernel)));
577   if (kernel == (MagickRealType **) NULL)
578     {
579       edge_image=DestroyImage(edge_image);
580       sharp_image=DestroyImage(sharp_image);
581       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
582     }
583   (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
584   for (i=0; i < (ssize_t) width; i+=2)
585   {
586     kernel[i]=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory(
587       (size_t) (width-i),(width-i)*sizeof(**kernel)));
588     if (kernel[i] == (MagickRealType *) NULL)
589       break;
590     normalize=0.0;
591     j=(ssize_t) (width-i)/2;
592     k=0;
593     for (v=(-j); v <= j; v++)
594     {
595       for (u=(-j); u <= j; u++)
596       {
597         kernel[i][k]=(MagickRealType) (-exp(-((double) u*u+v*v)/(2.0*
598           MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
599         normalize+=kernel[i][k];
600         k++;
601       }
602     }
603     if (fabs(normalize) < MagickEpsilon)
604       normalize=MagickEpsilon;
605     normalize=PerceptibleReciprocal(normalize);
606     for (k=0; k < (j*j); k++)
607       kernel[i][k]=normalize*kernel[i][k];
608   }
609   if (i < (ssize_t) width)
610     {
611       for (i-=2; i >= 0; i-=2)
612         kernel[i]=(MagickRealType *) RelinquishAlignedMemory(kernel[i]);
613       kernel=(MagickRealType **) RelinquishAlignedMemory(kernel);
614       edge_image=DestroyImage(edge_image);
615       sharp_image=DestroyImage(sharp_image);
616       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
617     }
618   /*
619     Adaptively sharpen image.
620   */
621   status=MagickTrue;
622   progress=0;
623   image_view=AcquireVirtualCacheView(image,exception);
624   edge_view=AcquireVirtualCacheView(edge_image,exception);
625   sharp_view=AcquireAuthenticCacheView(sharp_image,exception);
626 #if defined(MAGICKCORE_OPENMP_SUPPORT)
627   #pragma omp parallel for schedule(static,4) shared(progress,status) \
628     magick_threads(image,sharp_image,sharp_image->rows,1)
629 #endif
630   for (y=0; y < (ssize_t) sharp_image->rows; y++)
631   {
632     register const Quantum
633       *restrict r;
634
635     register Quantum
636       *restrict q;
637
638     register ssize_t
639       x;
640
641     if (status == MagickFalse)
642       continue;
643     r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
644     q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
645       exception);
646     if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
647       {
648         status=MagickFalse;
649         continue;
650       }
651     for (x=0; x < (ssize_t) sharp_image->columns; x++)
652     {
653       register const Quantum
654         *restrict p;
655
656       register ssize_t
657         i;
658
659       ssize_t
660         center,
661         j;
662
663       j=(ssize_t) ceil((double) width*QuantumScale*
664         GetPixelIntensity(edge_image,r)-0.5);
665       if (j < 0)
666         j=0;
667       else
668         if (j > (ssize_t) width)
669           j=(ssize_t) width;
670       if ((j & 0x01) != 0)
671         j--;
672       p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
673         (ssize_t) ((width-j)/2L),width-j,width-j,exception);
674       if (p == (const Quantum *) NULL)
675         break;
676       center=(ssize_t) GetPixelChannels(image)*(width-j)*((width-j)/2L)+
677         GetPixelChannels(image)*((width-j)/2);
678       for (i=0; i < (ssize_t) GetPixelChannels(sharp_image); i++)
679       {
680         double
681           alpha,
682           gamma,
683           pixel;
684
685         PixelChannel
686           channel;
687
688         PixelTrait
689           sharp_traits,
690           traits;
691
692         register const MagickRealType
693           *restrict k;
694
695         register const Quantum
696           *restrict pixels;
697
698         register ssize_t
699           u;
700
701         ssize_t
702           v;
703
704         channel=GetPixelChannelChannel(image,i);
705         traits=GetPixelChannelTraits(image,channel);
706         sharp_traits=GetPixelChannelTraits(sharp_image,channel);
707         if ((traits == UndefinedPixelTrait) ||
708             (sharp_traits == UndefinedPixelTrait))
709           continue;
710         if (((sharp_traits & CopyPixelTrait) != 0) ||
711             (GetPixelMask(image,p+center) == 0))
712           {
713             SetPixelChannel(sharp_image,channel,p[center+i],q);
714             continue;
715           }
716         k=kernel[j];
717         pixels=p;
718         pixel=0.0;
719         gamma=0.0;
720         if ((sharp_traits & BlendPixelTrait) == 0)
721           {
722             /*
723               No alpha blending.
724             */
725             for (v=0; v < (ssize_t) (width-j); v++)
726             {
727               for (u=0; u < (ssize_t) (width-j); u++)
728               {
729                 pixel+=(*k)*pixels[i];
730                 gamma+=(*k);
731                 k++;
732                 pixels+=GetPixelChannels(image);
733               }
734             }
735             gamma=PerceptibleReciprocal(gamma);
736             SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
737             continue;
738           }
739         /*
740           Alpha blending.
741         */
742         for (v=0; v < (ssize_t) (width-j); v++)
743         {
744           for (u=0; u < (ssize_t) (width-j); u++)
745           {
746             alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
747             pixel+=(*k)*alpha*pixels[i];
748             gamma+=(*k)*alpha;
749             k++;
750             pixels+=GetPixelChannels(image);
751           }
752         }
753         gamma=PerceptibleReciprocal(gamma);
754         SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
755       }
756       q+=GetPixelChannels(sharp_image);
757       r+=GetPixelChannels(edge_image);
758     }
759     if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
760       status=MagickFalse;
761     if (image->progress_monitor != (MagickProgressMonitor) NULL)
762       {
763         MagickBooleanType
764           proceed;
765
766 #if defined(MAGICKCORE_OPENMP_SUPPORT)
767         #pragma omp critical (MagickCore_AdaptiveSharpenImage)
768 #endif
769         proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
770           image->rows);
771         if (proceed == MagickFalse)
772           status=MagickFalse;
773       }
774   }
775   sharp_image->type=image->type;
776   sharp_view=DestroyCacheView(sharp_view);
777   edge_view=DestroyCacheView(edge_view);
778   image_view=DestroyCacheView(image_view);
779   edge_image=DestroyImage(edge_image);
780   for (i=0; i < (ssize_t) width;  i+=2)
781     kernel[i]=(MagickRealType *) RelinquishAlignedMemory(kernel[i]);
782   kernel=(MagickRealType **) RelinquishAlignedMemory(kernel);
783   if (status == MagickFalse)
784     sharp_image=DestroyImage(sharp_image);
785   return(sharp_image);
786 }
787 \f
788 /*
789 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
790 %                                                                             %
791 %                                                                             %
792 %                                                                             %
793 %     B l u r I m a g e                                                       %
794 %                                                                             %
795 %                                                                             %
796 %                                                                             %
797 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
798 %
799 %  BlurImage() blurs an image.  We convolve the image with a Gaussian operator
800 %  of the given radius and standard deviation (sigma).  For reasonable results,
801 %  the radius should be larger than sigma.  Use a radius of 0 and BlurImage()
802 %  selects a suitable radius for you.
803 %
804 %  The format of the BlurImage method is:
805 %
806 %      Image *BlurImage(const Image *image,const double radius,
807 %        const double sigma,ExceptionInfo *exception)
808 %
809 %  A description of each parameter follows:
810 %
811 %    o image: the image.
812 %
813 %    o radius: the radius of the Gaussian, in pixels, not counting the center
814 %      pixel.
815 %
816 %    o sigma: the standard deviation of the Gaussian, in pixels.
817 %
818 %    o exception: return any errors or warnings in this structure.
819 %
820 */
821 MagickExport Image *BlurImage(const Image *image,const double radius,
822   const double sigma,ExceptionInfo *exception)
823 {
824   char
825     geometry[MaxTextExtent];
826
827   KernelInfo
828     *kernel_info;
829
830   Image
831     *blur_image;
832
833   assert(image != (const Image *) NULL);
834   assert(image->signature == MagickSignature);
835   if (image->debug != MagickFalse)
836     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
837   assert(exception != (ExceptionInfo *) NULL);
838   assert(exception->signature == MagickSignature);
839   (void) FormatLocaleString(geometry,MaxTextExtent,
840     "blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
841   kernel_info=AcquireKernelInfo(geometry);
842   if (kernel_info == (KernelInfo *) NULL)
843     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
844   blur_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,exception);
845   kernel_info=DestroyKernelInfo(kernel_info);
846   return(blur_image);
847 }
848 \f
849 /*
850 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
851 %                                                                             %
852 %                                                                             %
853 %                                                                             %
854 %     C o n v o l v e I m a g e                                               %
855 %                                                                             %
856 %                                                                             %
857 %                                                                             %
858 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
859 %
860 %  ConvolveImage() applies a custom convolution kernel to the image.
861 %
862 %  The format of the ConvolveImage method is:
863 %
864 %      Image *ConvolveImage(const Image *image,const KernelInfo *kernel,
865 %        ExceptionInfo *exception)
866 %
867 %  A description of each parameter follows:
868 %
869 %    o image: the image.
870 %
871 %    o kernel: the filtering kernel.
872 %
873 %    o exception: return any errors or warnings in this structure.
874 %
875 */
876 MagickExport Image *ConvolveImage(const Image *image,
877   const KernelInfo *kernel_info,ExceptionInfo *exception)
878 {
879   return(MorphologyImage(image,ConvolveMorphology,1,kernel_info,exception));
880 }
881 \f
882 /*
883 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
884 %                                                                             %
885 %                                                                             %
886 %                                                                             %
887 %     D e s p e c k l e I m a g e                                             %
888 %                                                                             %
889 %                                                                             %
890 %                                                                             %
891 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
892 %
893 %  DespeckleImage() reduces the speckle noise in an image while perserving the
894 %  edges of the original image.  A speckle removing filter uses a complementary %  hulling technique (raising pixels that are darker than their surrounding
895 %  neighbors, then complementarily lowering pixels that are brighter than their
896 %  surrounding neighbors) to reduce the speckle index of that image (reference
897 %  Crimmins speckle removal).
898 %
899 %  The format of the DespeckleImage method is:
900 %
901 %      Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
902 %
903 %  A description of each parameter follows:
904 %
905 %    o image: the image.
906 %
907 %    o exception: return any errors or warnings in this structure.
908 %
909 */
910
911 static void Hull(const Image *image,const ssize_t x_offset,
912   const ssize_t y_offset,const size_t columns,const size_t rows,
913   const int polarity,Quantum *restrict f,Quantum *restrict g)
914 {
915   register Quantum
916     *p,
917     *q,
918     *r,
919     *s;
920
921   ssize_t
922     y;
923
924   assert(f != (Quantum *) NULL);
925   assert(g != (Quantum *) NULL);
926   p=f+(columns+2);
927   q=g+(columns+2);
928   r=p+(y_offset*(columns+2)+x_offset);
929 #if defined(MAGICKCORE_OPENMP_SUPPORT)
930   #pragma omp parallel for schedule(static,4) \
931     magick_threads(image,image,1,1)
932 #endif
933   for (y=0; y < (ssize_t) rows; y++)
934   {
935     MagickRealType
936       v;
937
938     register ssize_t
939       i,
940       x;
941
942     i=(2*y+1)+y*columns;
943     if (polarity > 0)
944       for (x=0; x < (ssize_t) columns; x++)
945       {
946         v=(MagickRealType) p[i];
947         if ((MagickRealType) r[i] >= (v+ScaleCharToQuantum(2)))
948           v+=ScaleCharToQuantum(1);
949         q[i]=(Quantum) v;
950         i++;
951       }
952     else
953       for (x=0; x < (ssize_t) columns; x++)
954       {
955         v=(MagickRealType) p[i];
956         if ((MagickRealType) r[i] <= (v-ScaleCharToQuantum(2)))
957           v-=ScaleCharToQuantum(1);
958         q[i]=(Quantum) v;
959         i++;
960       }
961   }
962   p=f+(columns+2);
963   q=g+(columns+2);
964   r=q+(y_offset*(columns+2)+x_offset);
965   s=q-(y_offset*(columns+2)+x_offset);
966 #if defined(MAGICKCORE_OPENMP_SUPPORT)
967   #pragma omp parallel for schedule(static,4) \
968     magick_threads(image,image,1,1)
969 #endif
970   for (y=0; y < (ssize_t) rows; y++)
971   {
972     register ssize_t
973       i,
974       x;
975
976     MagickRealType
977       v;
978
979     i=(2*y+1)+y*columns;
980     if (polarity > 0)
981       for (x=0; x < (ssize_t) columns; x++)
982       {
983         v=(MagickRealType) q[i];
984         if (((MagickRealType) s[i] >= (v+ScaleCharToQuantum(2))) &&
985             ((MagickRealType) r[i] > v))
986           v+=ScaleCharToQuantum(1);
987         p[i]=(Quantum) v;
988         i++;
989       }
990     else
991       for (x=0; x < (ssize_t) columns; x++)
992       {
993         v=(MagickRealType) q[i];
994         if (((MagickRealType) s[i] <= (v-ScaleCharToQuantum(2))) &&
995             ((MagickRealType) r[i] < v))
996           v-=ScaleCharToQuantum(1);
997         p[i]=(Quantum) v;
998         i++;
999       }
1000   }
1001 }
1002
1003 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1004 {
1005 #define DespeckleImageTag  "Despeckle/Image"
1006
1007   CacheView
1008     *despeckle_view,
1009     *image_view;
1010
1011   Image
1012     *despeckle_image;
1013
1014   MagickBooleanType
1015     status;
1016
1017   Quantum
1018     *restrict buffer,
1019     *restrict pixels;
1020
1021   register ssize_t
1022     i;
1023
1024   size_t
1025     length;
1026
1027   static const ssize_t
1028     X[4] = {0, 1, 1,-1},
1029     Y[4] = {1, 0, 1, 1};
1030
1031   /*
1032     Allocate despeckled image.
1033   */
1034   assert(image != (const Image *) NULL);
1035   assert(image->signature == MagickSignature);
1036   if (image->debug != MagickFalse)
1037     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1038   assert(exception != (ExceptionInfo *) NULL);
1039   assert(exception->signature == MagickSignature);
1040   despeckle_image=CloneImage(image,0,0,MagickTrue,exception);
1041   if (despeckle_image == (Image *) NULL)
1042     return((Image *) NULL);
1043   status=SetImageStorageClass(despeckle_image,DirectClass,exception);
1044   if (status == MagickFalse)
1045     {
1046       despeckle_image=DestroyImage(despeckle_image);
1047       return((Image *) NULL);
1048     }
1049   /*
1050     Allocate image buffer.
1051   */
1052   length=(size_t) ((image->columns+2)*(image->rows+2));
1053   pixels=(Quantum *) AcquireQuantumMemory(length,sizeof(*pixels));
1054   buffer=(Quantum *) AcquireQuantumMemory(length,sizeof(*buffer));
1055   if ((pixels == (Quantum *) NULL) || (buffer == (Quantum *) NULL))
1056     {
1057       if (buffer != (Quantum *) NULL)
1058         buffer=(Quantum *) RelinquishMagickMemory(buffer);
1059       if (pixels != (Quantum *) NULL)
1060         pixels=(Quantum *) RelinquishMagickMemory(pixels);
1061       despeckle_image=DestroyImage(despeckle_image);
1062       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1063     }
1064   /*
1065     Reduce speckle in the image.
1066   */
1067   status=MagickTrue;
1068   image_view=AcquireVirtualCacheView(image,exception);
1069   despeckle_view=AcquireAuthenticCacheView(despeckle_image,exception);
1070   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1071   {
1072     PixelChannel
1073        channel;
1074
1075     PixelTrait
1076       despeckle_traits,
1077       traits;
1078
1079     register ssize_t
1080       k,
1081       x;
1082
1083     ssize_t
1084       j,
1085       y;
1086
1087     if (status == MagickFalse)
1088       continue;
1089     channel=GetPixelChannelChannel(image,i);
1090     traits=GetPixelChannelTraits(image,channel);
1091     despeckle_traits=GetPixelChannelTraits(despeckle_image,channel);
1092     if ((traits == UndefinedPixelTrait) ||
1093         (despeckle_traits == UndefinedPixelTrait))
1094       continue;
1095     if ((despeckle_traits & CopyPixelTrait) != 0)
1096       continue;
1097     (void) ResetMagickMemory(pixels,0,length*sizeof(*pixels));
1098     j=(ssize_t) image->columns+2;
1099     for (y=0; y < (ssize_t) image->rows; y++)
1100     {
1101       register const Quantum
1102         *restrict p;
1103
1104       p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1105       if (p == (const Quantum *) NULL)
1106         {
1107           status=MagickFalse;
1108           continue;
1109         }
1110       j++;
1111       for (x=0; x < (ssize_t) image->columns; x++)
1112       {
1113         pixels[j++]=p[i];
1114         p+=GetPixelChannels(image);
1115       }
1116       j++;
1117     }
1118     (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1119     for (k=0; k < 4; k++)
1120     {
1121       Hull(image,X[k],Y[k],image->columns,image->rows,1,pixels,buffer);
1122       Hull(image,-X[k],-Y[k],image->columns,image->rows,1,pixels,buffer);
1123       Hull(image,-X[k],-Y[k],image->columns,image->rows,-1,pixels,buffer);
1124       Hull(image,X[k],Y[k],image->columns,image->rows,-1,pixels,buffer);
1125     }
1126     j=(ssize_t) image->columns+2;
1127     for (y=0; y < (ssize_t) image->rows; y++)
1128     {
1129       MagickBooleanType
1130         sync;
1131
1132       register Quantum
1133         *restrict q;
1134
1135       q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1136         1,exception);
1137       if (q == (Quantum *) NULL)
1138         {
1139           status=MagickFalse;
1140           continue;
1141         }
1142       j++;
1143       for (x=0; x < (ssize_t) image->columns; x++)
1144       {
1145         SetPixelChannel(despeckle_image,channel,pixels[j++],q);
1146         q+=GetPixelChannels(despeckle_image);
1147       }
1148       sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1149       if (sync == MagickFalse)
1150         status=MagickFalse;
1151       j++;
1152     }
1153     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1154       {
1155         MagickBooleanType
1156           proceed;
1157
1158         proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1159           GetPixelChannels(image));
1160         if (proceed == MagickFalse)
1161           status=MagickFalse;
1162       }
1163   }
1164   despeckle_view=DestroyCacheView(despeckle_view);
1165   image_view=DestroyCacheView(image_view);
1166   buffer=(Quantum *) RelinquishMagickMemory(buffer);
1167   pixels=(Quantum *) RelinquishMagickMemory(pixels);
1168   despeckle_image->type=image->type;
1169   if (status == MagickFalse)
1170     despeckle_image=DestroyImage(despeckle_image);
1171   return(despeckle_image);
1172 }
1173 \f
1174 /*
1175 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1176 %                                                                             %
1177 %                                                                             %
1178 %                                                                             %
1179 %     E d g e I m a g e                                                       %
1180 %                                                                             %
1181 %                                                                             %
1182 %                                                                             %
1183 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1184 %
1185 %  EdgeImage() finds edges in an image.  Radius defines the radius of the
1186 %  convolution filter.  Use a radius of 0 and EdgeImage() selects a suitable
1187 %  radius for you.
1188 %
1189 %  The format of the EdgeImage method is:
1190 %
1191 %      Image *EdgeImage(const Image *image,const double radius,
1192 %        ExceptionInfo *exception)
1193 %
1194 %  A description of each parameter follows:
1195 %
1196 %    o image: the image.
1197 %
1198 %    o radius: the radius of the pixel neighborhood.
1199 %
1200 %    o exception: return any errors or warnings in this structure.
1201 %
1202 */
1203 MagickExport Image *EdgeImage(const Image *image,const double radius,
1204   ExceptionInfo *exception)
1205 {
1206   char
1207     geometry[MaxTextExtent];
1208
1209   KernelInfo
1210     *kernel_info;
1211
1212   Image
1213     *edge_image;
1214
1215   assert(image != (const Image *) NULL);
1216   assert(image->signature == MagickSignature);
1217   if (image->debug != MagickFalse)
1218     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1219   assert(exception != (ExceptionInfo *) NULL);
1220   assert(exception->signature == MagickSignature);
1221   (void) FormatLocaleString(geometry,MaxTextExtent,"laplacian:%.20g",radius);
1222   kernel_info=AcquireKernelInfo(geometry);
1223   if (kernel_info == (KernelInfo *) NULL)
1224     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1225   edge_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,exception);
1226   kernel_info=DestroyKernelInfo(kernel_info);
1227   return(edge_image);
1228 }
1229 \f
1230 /*
1231 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1232 %                                                                             %
1233 %                                                                             %
1234 %                                                                             %
1235 %     E m b o s s I m a g e                                                   %
1236 %                                                                             %
1237 %                                                                             %
1238 %                                                                             %
1239 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1240 %
1241 %  EmbossImage() returns a grayscale image with a three-dimensional effect.
1242 %  We convolve the image with a Gaussian operator of the given radius and
1243 %  standard deviation (sigma).  For reasonable results, radius should be
1244 %  larger than sigma.  Use a radius of 0 and Emboss() selects a suitable
1245 %  radius for you.
1246 %
1247 %  The format of the EmbossImage method is:
1248 %
1249 %      Image *EmbossImage(const Image *image,const double radius,
1250 %        const double sigma,ExceptionInfo *exception)
1251 %
1252 %  A description of each parameter follows:
1253 %
1254 %    o image: the image.
1255 %
1256 %    o radius: the radius of the pixel neighborhood.
1257 %
1258 %    o sigma: the standard deviation of the Gaussian, in pixels.
1259 %
1260 %    o exception: return any errors or warnings in this structure.
1261 %
1262 */
1263 MagickExport Image *EmbossImage(const Image *image,const double radius,
1264   const double sigma,ExceptionInfo *exception)
1265 {
1266   Image
1267     *emboss_image;
1268
1269   KernelInfo
1270     *kernel_info;
1271
1272   register ssize_t
1273     i;
1274
1275   size_t
1276     width;
1277
1278   ssize_t
1279     j,
1280     k,
1281     u,
1282     v;
1283
1284   assert(image != (const Image *) NULL);
1285   assert(image->signature == MagickSignature);
1286   if (image->debug != MagickFalse)
1287     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1288   assert(exception != (ExceptionInfo *) NULL);
1289   assert(exception->signature == MagickSignature);
1290   width=GetOptimalKernelWidth1D(radius,sigma);
1291   kernel_info=AcquireKernelInfo((const char *) NULL);
1292   if (kernel_info == (KernelInfo *) NULL)
1293     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1294   kernel_info->width=width;
1295   kernel_info->height=width;
1296   kernel_info->x=(ssize_t) (width-1)/2;
1297   kernel_info->y=(ssize_t) (width-1)/2;
1298   kernel_info->values=(MagickRealType *) MagickAssumeAligned(
1299     AcquireAlignedMemory(kernel_info->width,kernel_info->width*
1300     sizeof(*kernel_info->values)));
1301   if (kernel_info->values == (MagickRealType *) NULL)
1302     {
1303       kernel_info=DestroyKernelInfo(kernel_info);
1304       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1305     }
1306   j=(ssize_t) (kernel_info->width-1)/2;
1307   k=j;
1308   i=0;
1309   for (v=(-j); v <= j; v++)
1310   {
1311     for (u=(-j); u <= j; u++)
1312     {
1313       kernel_info->values[i]=(MagickRealType) (((u < 0) || (v < 0) ? -8.0 :
1314         8.0)*exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
1315         (2.0*MagickPI*MagickSigma*MagickSigma));
1316       if (u == k)
1317         kernel_info->values[i]=v == k ? 1.0 : 0.0;
1318       i++;
1319     }
1320     k--;
1321   }
1322   emboss_image=ConvolveImage(image,kernel_info,exception);
1323   kernel_info=DestroyKernelInfo(kernel_info);
1324   if (emboss_image != (Image *) NULL)
1325     (void) EqualizeImage(emboss_image,exception);
1326   return(emboss_image);
1327 }
1328 \f
1329 /*
1330 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1331 %                                                                             %
1332 %                                                                             %
1333 %                                                                             %
1334 %     G a u s s i a n B l u r I m a g e                                       %
1335 %                                                                             %
1336 %                                                                             %
1337 %                                                                             %
1338 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1339 %
1340 %  GaussianBlurImage() blurs an image.  We convolve the image with a
1341 %  Gaussian operator of the given radius and standard deviation (sigma).
1342 %  For reasonable results, the radius should be larger than sigma.  Use a
1343 %  radius of 0 and GaussianBlurImage() selects a suitable radius for you
1344 %
1345 %  The format of the GaussianBlurImage method is:
1346 %
1347 %      Image *GaussianBlurImage(const Image *image,onst double radius,
1348 %        const double sigma,ExceptionInfo *exception)
1349 %
1350 %  A description of each parameter follows:
1351 %
1352 %    o image: the image.
1353 %
1354 %    o radius: the radius of the Gaussian, in pixels, not counting the center
1355 %      pixel.
1356 %
1357 %    o sigma: the standard deviation of the Gaussian, in pixels.
1358 %
1359 %    o exception: return any errors or warnings in this structure.
1360 %
1361 */
1362 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
1363   const double sigma,ExceptionInfo *exception)
1364 {
1365   char
1366     geometry[MaxTextExtent];
1367
1368   KernelInfo
1369     *kernel_info;
1370
1371   Image
1372     *blur_image;
1373
1374   assert(image != (const Image *) NULL);
1375   assert(image->signature == MagickSignature);
1376   if (image->debug != MagickFalse)
1377     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1378   assert(exception != (ExceptionInfo *) NULL);
1379   assert(exception->signature == MagickSignature);
1380   (void) FormatLocaleString(geometry,MaxTextExtent,"gaussian:%.20gx%.20g",
1381     radius,sigma);
1382   kernel_info=AcquireKernelInfo(geometry);
1383   if (kernel_info == (KernelInfo *) NULL)
1384     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1385   blur_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,exception);
1386   kernel_info=DestroyKernelInfo(kernel_info);
1387   return(blur_image);
1388 }
1389 \f
1390 /*
1391 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1392 %                                                                             %
1393 %                                                                             %
1394 %                                                                             %
1395 %     M o t i o n B l u r I m a g e                                           %
1396 %                                                                             %
1397 %                                                                             %
1398 %                                                                             %
1399 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1400 %
1401 %  MotionBlurImage() simulates motion blur.  We convolve the image with a
1402 %  Gaussian operator of the given radius and standard deviation (sigma).
1403 %  For reasonable results, radius should be larger than sigma.  Use a
1404 %  radius of 0 and MotionBlurImage() selects a suitable radius for you.
1405 %  Angle gives the angle of the blurring motion.
1406 %
1407 %  Andrew Protano contributed this effect.
1408 %
1409 %  The format of the MotionBlurImage method is:
1410 %
1411 %    Image *MotionBlurImage(const Image *image,const double radius,
1412 %      const double sigma,const double angle,ExceptionInfo *exception)
1413 %
1414 %  A description of each parameter follows:
1415 %
1416 %    o image: the image.
1417 %
1418 %    o radius: the radius of the Gaussian, in pixels, not counting
1419 %      the center pixel.
1420 %
1421 %    o sigma: the standard deviation of the Gaussian, in pixels.
1422 %
1423 %    o angle: Apply the effect along this angle.
1424 %
1425 %    o exception: return any errors or warnings in this structure.
1426 %
1427 */
1428
1429 static MagickRealType *GetMotionBlurKernel(const size_t width,
1430   const double sigma)
1431 {
1432   MagickRealType
1433     *kernel,
1434     normalize;
1435
1436   register ssize_t
1437     i;
1438
1439   /*
1440    Generate a 1-D convolution kernel.
1441   */
1442   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1443   kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
1444     width,sizeof(*kernel)));
1445   if (kernel == (MagickRealType *) NULL)
1446     return(kernel);
1447   normalize=0.0;
1448   for (i=0; i < (ssize_t) width; i++)
1449   {
1450     kernel[i]=(MagickRealType) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
1451       MagickSigma)))/(MagickSQ2PI*MagickSigma));
1452     normalize+=kernel[i];
1453   }
1454   for (i=0; i < (ssize_t) width; i++)
1455     kernel[i]/=normalize;
1456   return(kernel);
1457 }
1458
1459 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
1460   const double sigma,const double angle,ExceptionInfo *exception)
1461 {
1462 #define BlurImageTag  "Blur/Image"
1463
1464   CacheView
1465     *blur_view,
1466     *image_view,
1467     *motion_view;
1468
1469   Image
1470     *blur_image;
1471
1472   MagickBooleanType
1473     status;
1474
1475   MagickOffsetType
1476     progress;
1477
1478   MagickRealType
1479     *kernel;
1480
1481   OffsetInfo
1482     *offset;
1483
1484   PointInfo
1485     point;
1486
1487   register ssize_t
1488     i;
1489
1490   size_t
1491     width;
1492
1493   ssize_t
1494     y;
1495
1496   assert(image != (Image *) NULL);
1497   assert(image->signature == MagickSignature);
1498   if (image->debug != MagickFalse)
1499     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1500   assert(exception != (ExceptionInfo *) NULL);
1501   width=GetOptimalKernelWidth1D(radius,sigma);
1502   kernel=GetMotionBlurKernel(width,sigma);
1503   if (kernel == (MagickRealType *) NULL)
1504     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1505   offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
1506   if (offset == (OffsetInfo *) NULL)
1507     {
1508       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
1509       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1510     }
1511   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1512   if (blur_image == (Image *) NULL)
1513     {
1514       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
1515       offset=(OffsetInfo *) RelinquishMagickMemory(offset);
1516       return((Image *) NULL);
1517     }
1518   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
1519     {
1520       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
1521       offset=(OffsetInfo *) RelinquishMagickMemory(offset);
1522       blur_image=DestroyImage(blur_image);
1523       return((Image *) NULL);
1524     }
1525   point.x=(double) width*sin(DegreesToRadians(angle));
1526   point.y=(double) width*cos(DegreesToRadians(angle));
1527   for (i=0; i < (ssize_t) width; i++)
1528   {
1529     offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
1530     offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
1531   }
1532   /*
1533     Motion blur image.
1534   */
1535   status=MagickTrue;
1536   progress=0;
1537   image_view=AcquireVirtualCacheView(image,exception);
1538   motion_view=AcquireVirtualCacheView(image,exception);
1539   blur_view=AcquireAuthenticCacheView(blur_image,exception);
1540 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1541   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1542     magick_threads(image,blur_image,image->rows,1)
1543 #endif
1544   for (y=0; y < (ssize_t) image->rows; y++)
1545   {
1546     register const Quantum
1547       *restrict p;
1548
1549     register Quantum
1550       *restrict q;
1551
1552     register ssize_t
1553       x;
1554
1555     if (status == MagickFalse)
1556       continue;
1557     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1558     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
1559       exception);
1560     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1561       {
1562         status=MagickFalse;
1563         continue;
1564       }
1565     for (x=0; x < (ssize_t) image->columns; x++)
1566     {
1567       register ssize_t
1568         i;
1569
1570       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1571       {
1572         double
1573           alpha,
1574           gamma,
1575           pixel;
1576
1577         PixelChannel
1578           channel;
1579
1580         PixelTrait
1581           blur_traits,
1582           traits;
1583
1584         register const Quantum
1585           *restrict r;
1586
1587         register MagickRealType
1588           *restrict k;
1589
1590         register ssize_t
1591           j;
1592
1593         channel=GetPixelChannelChannel(image,i);
1594         traits=GetPixelChannelTraits(image,channel);
1595         blur_traits=GetPixelChannelTraits(blur_image,channel);
1596         if ((traits == UndefinedPixelTrait) ||
1597             (blur_traits == UndefinedPixelTrait))
1598           continue;
1599         if (((blur_traits & CopyPixelTrait) != 0) ||
1600             (GetPixelMask(image,p) == 0))
1601           {
1602             SetPixelChannel(blur_image,channel,p[i],q);
1603             continue;
1604           }
1605         k=kernel;
1606         pixel=0.0;
1607         if ((blur_traits & BlendPixelTrait) == 0)
1608           {
1609             for (j=0; j < (ssize_t) width; j++)
1610             {
1611               r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+
1612                 offset[j].y,1,1,exception);
1613               if (r == (const Quantum *) NULL)
1614                 {
1615                   status=MagickFalse;
1616                   continue;
1617                 }
1618               pixel+=(*k)*r[i];
1619               k++;
1620             }
1621             SetPixelChannel(blur_image,channel,ClampToQuantum(pixel),q);
1622             continue;
1623           }
1624         alpha=0.0;
1625         gamma=0.0;
1626         for (j=0; j < (ssize_t) width; j++)
1627         {
1628           r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+offset[j].y,1,
1629             1,exception);
1630           if (r == (const Quantum *) NULL)
1631             {
1632               status=MagickFalse;
1633               continue;
1634             }
1635           alpha=(double) (QuantumScale*GetPixelAlpha(image,r));
1636           pixel+=(*k)*alpha*r[i];
1637           gamma+=(*k)*alpha;
1638           k++;
1639         }
1640         gamma=PerceptibleReciprocal(gamma);
1641         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
1642       }
1643       p+=GetPixelChannels(image);
1644       q+=GetPixelChannels(blur_image);
1645     }
1646     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1647       status=MagickFalse;
1648     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1649       {
1650         MagickBooleanType
1651           proceed;
1652
1653 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1654         #pragma omp critical (MagickCore_MotionBlurImage)
1655 #endif
1656         proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
1657         if (proceed == MagickFalse)
1658           status=MagickFalse;
1659       }
1660   }
1661   blur_view=DestroyCacheView(blur_view);
1662   motion_view=DestroyCacheView(motion_view);
1663   image_view=DestroyCacheView(image_view);
1664   kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
1665   offset=(OffsetInfo *) RelinquishMagickMemory(offset);
1666   if (status == MagickFalse)
1667     blur_image=DestroyImage(blur_image);
1668   return(blur_image);
1669 }
1670 \f
1671 /*
1672 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1673 %                                                                             %
1674 %                                                                             %
1675 %                                                                             %
1676 %     P r e v i e w I m a g e                                                 %
1677 %                                                                             %
1678 %                                                                             %
1679 %                                                                             %
1680 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1681 %
1682 %  PreviewImage() tiles 9 thumbnails of the specified image with an image
1683 %  processing operation applied with varying parameters.  This may be helpful
1684 %  pin-pointing an appropriate parameter for a particular image processing
1685 %  operation.
1686 %
1687 %  The format of the PreviewImages method is:
1688 %
1689 %      Image *PreviewImages(const Image *image,const PreviewType preview,
1690 %        ExceptionInfo *exception)
1691 %
1692 %  A description of each parameter follows:
1693 %
1694 %    o image: the image.
1695 %
1696 %    o preview: the image processing operation.
1697 %
1698 %    o exception: return any errors or warnings in this structure.
1699 %
1700 */
1701 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
1702   ExceptionInfo *exception)
1703 {
1704 #define NumberTiles  9
1705 #define PreviewImageTag  "Preview/Image"
1706 #define DefaultPreviewGeometry  "204x204+10+10"
1707
1708   char
1709     factor[MaxTextExtent],
1710     label[MaxTextExtent];
1711
1712   double
1713     degrees,
1714     gamma,
1715     percentage,
1716     radius,
1717     sigma,
1718     threshold;
1719
1720   extern const char
1721     DefaultTileFrame[];
1722
1723   Image
1724     *images,
1725     *montage_image,
1726     *preview_image,
1727     *thumbnail;
1728
1729   ImageInfo
1730     *preview_info;
1731
1732   MagickBooleanType
1733     proceed;
1734
1735   MontageInfo
1736     *montage_info;
1737
1738   QuantizeInfo
1739     quantize_info;
1740
1741   RectangleInfo
1742     geometry;
1743
1744   register ssize_t
1745     i,
1746     x;
1747
1748   size_t
1749     colors;
1750
1751   ssize_t
1752     y;
1753
1754   /*
1755     Open output image file.
1756   */
1757   assert(image != (Image *) NULL);
1758   assert(image->signature == MagickSignature);
1759   if (image->debug != MagickFalse)
1760     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1761   colors=2;
1762   degrees=0.0;
1763   gamma=(-0.2f);
1764   preview_info=AcquireImageInfo();
1765   SetGeometry(image,&geometry);
1766   (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
1767     &geometry.width,&geometry.height);
1768   images=NewImageList();
1769   percentage=12.5;
1770   GetQuantizeInfo(&quantize_info);
1771   radius=0.0;
1772   sigma=1.0;
1773   threshold=0.0;
1774   x=0;
1775   y=0;
1776   for (i=0; i < NumberTiles; i++)
1777   {
1778     thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
1779     if (thumbnail == (Image *) NULL)
1780       break;
1781     (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
1782       (void *) NULL);
1783     (void) SetImageProperty(thumbnail,"label",DefaultTileLabel,exception);
1784     if (i == (NumberTiles/2))
1785       {
1786         (void) QueryColorCompliance("#dfdfdf",AllCompliance,
1787           &thumbnail->matte_color,exception);
1788         AppendImageToList(&images,thumbnail);
1789         continue;
1790       }
1791     switch (preview)
1792     {
1793       case RotatePreview:
1794       {
1795         degrees+=45.0;
1796         preview_image=RotateImage(thumbnail,degrees,exception);
1797         (void) FormatLocaleString(label,MaxTextExtent,"rotate %g",degrees);
1798         break;
1799       }
1800       case ShearPreview:
1801       {
1802         degrees+=5.0;
1803         preview_image=ShearImage(thumbnail,degrees,degrees,exception);
1804         (void) FormatLocaleString(label,MaxTextExtent,"shear %gx%g",degrees,
1805           2.0*degrees);
1806         break;
1807       }
1808       case RollPreview:
1809       {
1810         x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
1811         y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
1812         preview_image=RollImage(thumbnail,x,y,exception);
1813         (void) FormatLocaleString(label,MaxTextExtent,"roll %+.20gx%+.20g",
1814           (double) x,(double) y);
1815         break;
1816       }
1817       case HuePreview:
1818       {
1819         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1820         if (preview_image == (Image *) NULL)
1821           break;
1822         (void) FormatLocaleString(factor,MaxTextExtent,"100,100,%g",2.0*
1823           percentage);
1824         (void) ModulateImage(preview_image,factor,exception);
1825         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
1826         break;
1827       }
1828       case SaturationPreview:
1829       {
1830         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1831         if (preview_image == (Image *) NULL)
1832           break;
1833         (void) FormatLocaleString(factor,MaxTextExtent,"100,%g",2.0*percentage);
1834         (void) ModulateImage(preview_image,factor,exception);
1835         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
1836         break;
1837       }
1838       case BrightnessPreview:
1839       {
1840         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1841         if (preview_image == (Image *) NULL)
1842           break;
1843         (void) FormatLocaleString(factor,MaxTextExtent,"%g",2.0*percentage);
1844         (void) ModulateImage(preview_image,factor,exception);
1845         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
1846         break;
1847       }
1848       case GammaPreview:
1849       default:
1850       {
1851         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1852         if (preview_image == (Image *) NULL)
1853           break;
1854         gamma+=0.4f;
1855         (void) GammaImage(preview_image,gamma,exception);
1856         (void) FormatLocaleString(label,MaxTextExtent,"gamma %g",gamma);
1857         break;
1858       }
1859       case SpiffPreview:
1860       {
1861         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1862         if (preview_image != (Image *) NULL)
1863           for (x=0; x < i; x++)
1864             (void) ContrastImage(preview_image,MagickTrue,exception);
1865         (void) FormatLocaleString(label,MaxTextExtent,"contrast (%.20g)",
1866           (double) i+1);
1867         break;
1868       }
1869       case DullPreview:
1870       {
1871         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1872         if (preview_image == (Image *) NULL)
1873           break;
1874         for (x=0; x < i; x++)
1875           (void) ContrastImage(preview_image,MagickFalse,exception);
1876         (void) FormatLocaleString(label,MaxTextExtent,"+contrast (%.20g)",
1877           (double) i+1);
1878         break;
1879       }
1880       case GrayscalePreview:
1881       {
1882         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1883         if (preview_image == (Image *) NULL)
1884           break;
1885         colors<<=1;
1886         quantize_info.number_colors=colors;
1887         quantize_info.colorspace=GRAYColorspace;
1888         (void) QuantizeImage(&quantize_info,preview_image,exception);
1889         (void) FormatLocaleString(label,MaxTextExtent,
1890           "-colorspace gray -colors %.20g",(double) colors);
1891         break;
1892       }
1893       case QuantizePreview:
1894       {
1895         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1896         if (preview_image == (Image *) NULL)
1897           break;
1898         colors<<=1;
1899         quantize_info.number_colors=colors;
1900         (void) QuantizeImage(&quantize_info,preview_image,exception);
1901         (void) FormatLocaleString(label,MaxTextExtent,"colors %.20g",(double)
1902           colors);
1903         break;
1904       }
1905       case DespecklePreview:
1906       {
1907         for (x=0; x < (i-1); x++)
1908         {
1909           preview_image=DespeckleImage(thumbnail,exception);
1910           if (preview_image == (Image *) NULL)
1911             break;
1912           thumbnail=DestroyImage(thumbnail);
1913           thumbnail=preview_image;
1914         }
1915         preview_image=DespeckleImage(thumbnail,exception);
1916         if (preview_image == (Image *) NULL)
1917           break;
1918         (void) FormatLocaleString(label,MaxTextExtent,"despeckle (%.20g)",
1919           (double) i+1);
1920         break;
1921       }
1922       case ReduceNoisePreview:
1923       {
1924         preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
1925           (size_t) radius,exception);
1926         (void) FormatLocaleString(label,MaxTextExtent,"noise %g",radius);
1927         break;
1928       }
1929       case AddNoisePreview:
1930       {
1931         switch ((int) i)
1932         {
1933           case 0:
1934           {
1935             (void) CopyMagickString(factor,"uniform",MaxTextExtent);
1936             break;
1937           }
1938           case 1:
1939           {
1940             (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
1941             break;
1942           }
1943           case 2:
1944           {
1945             (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
1946             break;
1947           }
1948           case 3:
1949           {
1950             (void) CopyMagickString(factor,"impulse",MaxTextExtent);
1951             break;
1952           }
1953           case 4:
1954           {
1955             (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
1956             break;
1957           }
1958           case 5:
1959           {
1960             (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
1961             break;
1962           }
1963           default:
1964           {
1965             (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
1966             break;
1967           }
1968         }
1969         preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
1970           (size_t) i,exception);
1971         (void) FormatLocaleString(label,MaxTextExtent,"+noise %s",factor);
1972         break;
1973       }
1974       case SharpenPreview:
1975       {
1976         preview_image=SharpenImage(thumbnail,radius,sigma,exception);
1977         (void) FormatLocaleString(label,MaxTextExtent,"sharpen %gx%g",radius,
1978           sigma);
1979         break;
1980       }
1981       case BlurPreview:
1982       {
1983         preview_image=BlurImage(thumbnail,radius,sigma,exception);
1984         (void) FormatLocaleString(label,MaxTextExtent,"blur %gx%g",radius,
1985           sigma);
1986         break;
1987       }
1988       case ThresholdPreview:
1989       {
1990         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1991         if (preview_image == (Image *) NULL)
1992           break;
1993         (void) BilevelImage(thumbnail,(double) (percentage*((double)
1994           QuantumRange+1.0))/100.0,exception);
1995         (void) FormatLocaleString(label,MaxTextExtent,"threshold %g",(double)
1996           (percentage*((double) QuantumRange+1.0))/100.0);
1997         break;
1998       }
1999       case EdgeDetectPreview:
2000       {
2001         preview_image=EdgeImage(thumbnail,radius,exception);
2002         (void) FormatLocaleString(label,MaxTextExtent,"edge %g",radius);
2003         break;
2004       }
2005       case SpreadPreview:
2006       {
2007         preview_image=SpreadImage(thumbnail,radius,thumbnail->interpolate,
2008           exception);
2009         (void) FormatLocaleString(label,MaxTextExtent,"spread %g",radius+0.5);
2010         break;
2011       }
2012       case SolarizePreview:
2013       {
2014         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2015         if (preview_image == (Image *) NULL)
2016           break;
2017         (void) SolarizeImage(preview_image,(double) QuantumRange*percentage/
2018           100.0,exception);
2019         (void) FormatLocaleString(label,MaxTextExtent,"solarize %g",
2020           (QuantumRange*percentage)/100.0);
2021         break;
2022       }
2023       case ShadePreview:
2024       {
2025         degrees+=10.0;
2026         preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2027           exception);
2028         (void) FormatLocaleString(label,MaxTextExtent,"shade %gx%g",degrees,
2029           degrees);
2030         break;
2031       }
2032       case RaisePreview:
2033       {
2034         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2035         if (preview_image == (Image *) NULL)
2036           break;
2037         geometry.width=(size_t) (2*i+2);
2038         geometry.height=(size_t) (2*i+2);
2039         geometry.x=(i-1)/2;
2040         geometry.y=(i-1)/2;
2041         (void) RaiseImage(preview_image,&geometry,MagickTrue,exception);
2042         (void) FormatLocaleString(label,MaxTextExtent,
2043           "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
2044           geometry.height,(double) geometry.x,(double) geometry.y);
2045         break;
2046       }
2047       case SegmentPreview:
2048       {
2049         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2050         if (preview_image == (Image *) NULL)
2051           break;
2052         threshold+=0.4f;
2053         (void) SegmentImage(preview_image,sRGBColorspace,MagickFalse,threshold,
2054           threshold,exception);
2055         (void) FormatLocaleString(label,MaxTextExtent,"segment %gx%g",
2056           threshold,threshold);
2057         break;
2058       }
2059       case SwirlPreview:
2060       {
2061         preview_image=SwirlImage(thumbnail,degrees,image->interpolate,
2062           exception);
2063         (void) FormatLocaleString(label,MaxTextExtent,"swirl %g",degrees);
2064         degrees+=45.0;
2065         break;
2066       }
2067       case ImplodePreview:
2068       {
2069         degrees+=0.1f;
2070         preview_image=ImplodeImage(thumbnail,degrees,image->interpolate,
2071           exception);
2072         (void) FormatLocaleString(label,MaxTextExtent,"implode %g",degrees);
2073         break;
2074       }
2075       case WavePreview:
2076       {
2077         degrees+=5.0f;
2078         preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,
2079           image->interpolate,exception);
2080         (void) FormatLocaleString(label,MaxTextExtent,"wave %gx%g",0.5*degrees,
2081           2.0*degrees);
2082         break;
2083       }
2084       case OilPaintPreview:
2085       {
2086         preview_image=OilPaintImage(thumbnail,(double) radius,(double) sigma,
2087           exception);
2088         (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",radius,
2089           sigma);
2090         break;
2091       }
2092       case CharcoalDrawingPreview:
2093       {
2094         preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2095           exception);
2096         (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",radius,
2097           sigma);
2098         break;
2099       }
2100       case JPEGPreview:
2101       {
2102         char
2103           filename[MaxTextExtent];
2104
2105         int
2106           file;
2107
2108         MagickBooleanType
2109           status;
2110
2111         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2112         if (preview_image == (Image *) NULL)
2113           break;
2114         preview_info->quality=(size_t) percentage;
2115         (void) FormatLocaleString(factor,MaxTextExtent,"%.20g",(double)
2116           preview_info->quality);
2117         file=AcquireUniqueFileResource(filename);
2118         if (file != -1)
2119           file=close(file)-1;
2120         (void) FormatLocaleString(preview_image->filename,MaxTextExtent,
2121           "jpeg:%s",filename);
2122         status=WriteImage(preview_info,preview_image,exception);
2123         if (status != MagickFalse)
2124           {
2125             Image
2126               *quality_image;
2127
2128             (void) CopyMagickString(preview_info->filename,
2129               preview_image->filename,MaxTextExtent);
2130             quality_image=ReadImage(preview_info,exception);
2131             if (quality_image != (Image *) NULL)
2132               {
2133                 preview_image=DestroyImage(preview_image);
2134                 preview_image=quality_image;
2135               }
2136           }
2137         (void) RelinquishUniqueFileResource(preview_image->filename);
2138         if ((GetBlobSize(preview_image)/1024) >= 1024)
2139           (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%gmb ",
2140             factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2141             1024.0/1024.0);
2142         else
2143           if (GetBlobSize(preview_image) >= 1024)
2144             (void) FormatLocaleString(label,MaxTextExtent,
2145               "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
2146               GetBlobSize(preview_image))/1024.0);
2147           else
2148             (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%.20gb ",
2149               factor,(double) ((MagickOffsetType) GetBlobSize(thumbnail)));
2150         break;
2151       }
2152     }
2153     thumbnail=DestroyImage(thumbnail);
2154     percentage+=12.5;
2155     radius+=0.5;
2156     sigma+=0.25;
2157     if (preview_image == (Image *) NULL)
2158       break;
2159     (void) DeleteImageProperty(preview_image,"label");
2160     (void) SetImageProperty(preview_image,"label",label,exception);
2161     AppendImageToList(&images,preview_image);
2162     proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2163       NumberTiles);
2164     if (proceed == MagickFalse)
2165       break;
2166   }
2167   if (images == (Image *) NULL)
2168     {
2169       preview_info=DestroyImageInfo(preview_info);
2170       return((Image *) NULL);
2171     }
2172   /*
2173     Create the montage.
2174   */
2175   montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2176   (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
2177   montage_info->shadow=MagickTrue;
2178   (void) CloneString(&montage_info->tile,"3x3");
2179   (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2180   (void) CloneString(&montage_info->frame,DefaultTileFrame);
2181   montage_image=MontageImages(images,montage_info,exception);
2182   montage_info=DestroyMontageInfo(montage_info);
2183   images=DestroyImageList(images);
2184   if (montage_image == (Image *) NULL)
2185     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2186   if (montage_image->montage != (char *) NULL)
2187     {
2188       /*
2189         Free image directory.
2190       */
2191       montage_image->montage=(char *) RelinquishMagickMemory(
2192         montage_image->montage);
2193       if (image->directory != (char *) NULL)
2194         montage_image->directory=(char *) RelinquishMagickMemory(
2195           montage_image->directory);
2196     }
2197   preview_info=DestroyImageInfo(preview_info);
2198   return(montage_image);
2199 }
2200 \f
2201 /*
2202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2203 %                                                                             %
2204 %                                                                             %
2205 %                                                                             %
2206 %     R a d i a l B l u r I m a g e                                           %
2207 %                                                                             %
2208 %                                                                             %
2209 %                                                                             %
2210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2211 %
2212 %  RadialBlurImage() applies a radial blur to the image.
2213 %
2214 %  Andrew Protano contributed this effect.
2215 %
2216 %  The format of the RadialBlurImage method is:
2217 %
2218 %    Image *RadialBlurImage(const Image *image,const double angle,
2219 %      ExceptionInfo *exception)
2220 %
2221 %  A description of each parameter follows:
2222 %
2223 %    o image: the image.
2224 %
2225 %    o angle: the angle of the radial blur.
2226 %
2227 %    o blur: the blur.
2228 %
2229 %    o exception: return any errors or warnings in this structure.
2230 %
2231 */
2232 MagickExport Image *RadialBlurImage(const Image *image,const double angle,
2233   ExceptionInfo *exception)
2234 {
2235   CacheView
2236     *blur_view,
2237     *image_view,
2238     *radial_view;
2239
2240   Image
2241     *blur_image;
2242
2243   MagickBooleanType
2244     status;
2245
2246   MagickOffsetType
2247     progress;
2248
2249   double
2250     blur_radius,
2251     *cos_theta,
2252     offset,
2253     *sin_theta,
2254     theta;
2255
2256   PointInfo
2257     blur_center;
2258
2259   register ssize_t
2260     i;
2261
2262   size_t
2263     n;
2264
2265   ssize_t
2266     y;
2267
2268   /*
2269     Allocate blur image.
2270   */
2271   assert(image != (Image *) NULL);
2272   assert(image->signature == MagickSignature);
2273   if (image->debug != MagickFalse)
2274     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2275   assert(exception != (ExceptionInfo *) NULL);
2276   assert(exception->signature == MagickSignature);
2277   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2278   if (blur_image == (Image *) NULL)
2279     return((Image *) NULL);
2280   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2281     {
2282       blur_image=DestroyImage(blur_image);
2283       return((Image *) NULL);
2284     }
2285   blur_center.x=(double) (image->columns-1)/2.0;
2286   blur_center.y=(double) (image->rows-1)/2.0;
2287   blur_radius=hypot(blur_center.x,blur_center.y);
2288   n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
2289   theta=DegreesToRadians(angle)/(double) (n-1);
2290   cos_theta=(double *) AcquireQuantumMemory((size_t) n,
2291     sizeof(*cos_theta));
2292   sin_theta=(double *) AcquireQuantumMemory((size_t) n,
2293     sizeof(*sin_theta));
2294   if ((cos_theta == (double *) NULL) ||
2295       (sin_theta == (double *) NULL))
2296     {
2297       blur_image=DestroyImage(blur_image);
2298       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2299     }
2300   offset=theta*(double) (n-1)/2.0;
2301   for (i=0; i < (ssize_t) n; i++)
2302   {
2303     cos_theta[i]=cos((double) (theta*i-offset));
2304     sin_theta[i]=sin((double) (theta*i-offset));
2305   }
2306   /*
2307     Radial blur image.
2308   */
2309   status=MagickTrue;
2310   progress=0;
2311   image_view=AcquireVirtualCacheView(image,exception);
2312   radial_view=AcquireVirtualCacheView(image,exception);
2313   blur_view=AcquireAuthenticCacheView(blur_image,exception);
2314 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2315   #pragma omp parallel for schedule(static,4) shared(progress,status) \
2316     magick_threads(image,blur_image,image->rows,1)
2317 #endif
2318   for (y=0; y < (ssize_t) image->rows; y++)
2319   {
2320     register const Quantum
2321       *restrict p;
2322
2323     register Quantum
2324       *restrict q;
2325
2326     register ssize_t
2327       x;
2328
2329     if (status == MagickFalse)
2330       continue;
2331     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2332     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2333       exception);
2334     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2335       {
2336         status=MagickFalse;
2337         continue;
2338       }
2339     for (x=0; x < (ssize_t) image->columns; x++)
2340     {
2341       double
2342         radius;
2343
2344       PointInfo
2345         center;
2346
2347       register ssize_t
2348         i;
2349
2350       size_t
2351         step;
2352
2353       center.x=(double) x-blur_center.x;
2354       center.y=(double) y-blur_center.y;
2355       radius=hypot((double) center.x,center.y);
2356       if (radius == 0)
2357         step=1;
2358       else
2359         {
2360           step=(size_t) (blur_radius/radius);
2361           if (step == 0)
2362             step=1;
2363           else
2364             if (step >= n)
2365               step=n-1;
2366         }
2367       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2368       {
2369         double
2370           gamma,
2371           pixel;
2372
2373         PixelChannel
2374           channel;
2375
2376         PixelTrait
2377           blur_traits,
2378           traits;
2379
2380         register const Quantum
2381           *restrict r;
2382
2383         register ssize_t
2384           j;
2385
2386         channel=GetPixelChannelChannel(image,i);
2387         traits=GetPixelChannelTraits(image,channel);
2388         blur_traits=GetPixelChannelTraits(blur_image,channel);
2389         if ((traits == UndefinedPixelTrait) ||
2390             (blur_traits == UndefinedPixelTrait))
2391           continue;
2392         if (((blur_traits & CopyPixelTrait) != 0) ||
2393             (GetPixelMask(image,p) == 0))
2394           {
2395             SetPixelChannel(blur_image,channel,p[i],q);
2396             continue;
2397           }
2398         gamma=0.0;
2399         pixel=0.0;
2400         if ((blur_traits & BlendPixelTrait) == 0)
2401           {
2402             for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2403             {
2404               r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
2405                 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2406                 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2407                 1,1,exception);
2408               if (r == (const Quantum *) NULL)
2409                 {
2410                   status=MagickFalse;
2411                   continue;
2412                 }
2413               pixel+=r[i];
2414               gamma++;
2415             }
2416             gamma=PerceptibleReciprocal(gamma);
2417             SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2418             continue;
2419           }
2420         for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2421         {
2422           r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
2423             center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2424             (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2425             1,1,exception);
2426           if (r == (const Quantum *) NULL)
2427             {
2428               status=MagickFalse;
2429               continue;
2430             }
2431           pixel+=GetPixelAlpha(image,r)*r[i];
2432           gamma+=GetPixelAlpha(image,r);
2433         }
2434         gamma=PerceptibleReciprocal(gamma);
2435         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2436       }
2437       p+=GetPixelChannels(image);
2438       q+=GetPixelChannels(blur_image);
2439     }
2440     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2441       status=MagickFalse;
2442     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2443       {
2444         MagickBooleanType
2445           proceed;
2446
2447 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2448         #pragma omp critical (MagickCore_RadialBlurImage)
2449 #endif
2450         proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2451         if (proceed == MagickFalse)
2452           status=MagickFalse;
2453       }
2454   }
2455   blur_view=DestroyCacheView(blur_view);
2456   radial_view=DestroyCacheView(radial_view);
2457   image_view=DestroyCacheView(image_view);
2458   cos_theta=(double *) RelinquishMagickMemory(cos_theta);
2459   sin_theta=(double *) RelinquishMagickMemory(sin_theta);
2460   if (status == MagickFalse)
2461     blur_image=DestroyImage(blur_image);
2462   return(blur_image);
2463 }
2464 \f
2465 /*
2466 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2467 %                                                                             %
2468 %                                                                             %
2469 %                                                                             %
2470 %     S e l e c t i v e B l u r I m a g e                                     %
2471 %                                                                             %
2472 %                                                                             %
2473 %                                                                             %
2474 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2475 %
2476 %  SelectiveBlurImage() selectively blur pixels within a contrast threshold.
2477 %  It is similar to the unsharpen mask that sharpens everything with contrast
2478 %  above a certain threshold.
2479 %
2480 %  The format of the SelectiveBlurImage method is:
2481 %
2482 %      Image *SelectiveBlurImage(const Image *image,const double radius,
2483 %        const double sigma,const double threshold,ExceptionInfo *exception)
2484 %
2485 %  A description of each parameter follows:
2486 %
2487 %    o image: the image.
2488 %
2489 %    o radius: the radius of the Gaussian, in pixels, not counting the center
2490 %      pixel.
2491 %
2492 %    o sigma: the standard deviation of the Gaussian, in pixels.
2493 %
2494 %    o threshold: only pixels within this contrast threshold are included
2495 %      in the blur operation.
2496 %
2497 %    o exception: return any errors or warnings in this structure.
2498 %
2499 */
2500 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
2501   const double sigma,const double threshold,ExceptionInfo *exception)
2502 {
2503 #define SelectiveBlurImageTag  "SelectiveBlur/Image"
2504
2505   CacheView
2506     *blur_view,
2507     *image_view,
2508     *luminance_view;
2509
2510   Image
2511     *blur_image,
2512     *luminance_image;
2513
2514   MagickBooleanType
2515     status;
2516
2517   MagickOffsetType
2518     progress;
2519
2520   MagickRealType
2521     *kernel;
2522
2523   register ssize_t
2524     i;
2525
2526   size_t
2527     width;
2528
2529   ssize_t
2530     center,
2531     j,
2532     u,
2533     v,
2534     y;
2535
2536   /*
2537     Initialize blur image attributes.
2538   */
2539   assert(image != (Image *) NULL);
2540   assert(image->signature == MagickSignature);
2541   if (image->debug != MagickFalse)
2542     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2543   assert(exception != (ExceptionInfo *) NULL);
2544   assert(exception->signature == MagickSignature);
2545   width=GetOptimalKernelWidth1D(radius,sigma);
2546   kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
2547     width,width*sizeof(*kernel)));
2548   if (kernel == (MagickRealType *) NULL)
2549     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2550   j=(ssize_t) (width-1)/2;
2551   i=0;
2552   for (v=(-j); v <= j; v++)
2553   {
2554     for (u=(-j); u <= j; u++)
2555       kernel[i++]=(MagickRealType) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
2556         MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
2557   }
2558   if (image->debug != MagickFalse)
2559     {
2560       char
2561         format[MaxTextExtent],
2562         *message;
2563
2564       register const MagickRealType
2565         *k;
2566
2567       ssize_t
2568         u,
2569         v;
2570
2571       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2572         "  SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
2573         width);
2574       message=AcquireString("");
2575       k=kernel;
2576       for (v=0; v < (ssize_t) width; v++)
2577       {
2578         *message='\0';
2579         (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
2580         (void) ConcatenateString(&message,format);
2581         for (u=0; u < (ssize_t) width; u++)
2582         {
2583           (void) FormatLocaleString(format,MaxTextExtent,"%+f ",(double) *k++);
2584           (void) ConcatenateString(&message,format);
2585         }
2586         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
2587       }
2588       message=DestroyString(message);
2589     }
2590   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2591   if (blur_image == (Image *) NULL)
2592     return((Image *) NULL);
2593   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2594     {
2595       blur_image=DestroyImage(blur_image);
2596       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2597       return((Image *) NULL);
2598     }
2599   luminance_image=CloneImage(image,0,0,MagickTrue,exception);
2600   if (luminance_image == (Image *) NULL)
2601     {
2602       blur_image=DestroyImage(blur_image);
2603       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2604       return((Image *) NULL);
2605     }
2606   status=TransformImageColorspace(luminance_image,GRAYColorspace,exception);
2607   if (status == MagickFalse)
2608     {
2609       luminance_image=DestroyImage(luminance_image);
2610       blur_image=DestroyImage(blur_image);
2611       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2612       return((Image *) NULL);
2613     }
2614   /*
2615     Threshold blur image.
2616   */
2617   status=MagickTrue;
2618   progress=0;
2619   center=(ssize_t) (GetPixelChannels(image)*(image->columns+width)*
2620     ((width-1)/2L)+GetPixelChannels(image)*((width-1)/2L));
2621   image_view=AcquireVirtualCacheView(image,exception);
2622   luminance_view=AcquireVirtualCacheView(luminance_image,exception);
2623   blur_view=AcquireAuthenticCacheView(blur_image,exception);
2624 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2625   #pragma omp parallel for schedule(static,4) shared(progress,status) \
2626     magick_threads(image,blur_image,image->rows,1)
2627 #endif
2628   for (y=0; y < (ssize_t) image->rows; y++)
2629   {
2630     double
2631       contrast;
2632
2633     MagickBooleanType
2634       sync;
2635
2636     register const Quantum
2637       *restrict l,
2638       *restrict p;
2639
2640     register Quantum
2641       *restrict q;
2642
2643     register ssize_t
2644       x;
2645
2646     if (status == MagickFalse)
2647       continue;
2648     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) (width-1)/2L),y-(ssize_t)
2649       ((width-1)/2L),image->columns+width,width,exception);
2650     l=GetCacheViewVirtualPixels(luminance_view,-((ssize_t) (width-1)/2L),y-
2651       (ssize_t) ((width-1)/2L),luminance_image->columns+width,width,exception);
2652     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2653       exception);
2654     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2655       {
2656         status=MagickFalse;
2657         continue;
2658       }
2659     for (x=0; x < (ssize_t) image->columns; x++)
2660     {
2661       double
2662         intensity;
2663
2664       register ssize_t
2665         i;
2666
2667       intensity=GetPixelIntensity(image,p+center);
2668       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2669       {
2670         double
2671           alpha,
2672           gamma,
2673           pixel;
2674
2675         PixelChannel
2676           channel;
2677
2678         PixelTrait
2679           blur_traits,
2680           traits;
2681
2682         register const MagickRealType
2683           *restrict k;
2684
2685         register const Quantum
2686           *restrict luminance_pixels,
2687           *restrict pixels;
2688
2689         register ssize_t
2690           u;
2691
2692         ssize_t
2693           v;
2694
2695         channel=GetPixelChannelChannel(image,i);
2696         traits=GetPixelChannelTraits(image,channel);
2697         blur_traits=GetPixelChannelTraits(blur_image,channel);
2698         if ((traits == UndefinedPixelTrait) ||
2699             (blur_traits == UndefinedPixelTrait))
2700           continue;
2701         if (((blur_traits & CopyPixelTrait) != 0) ||
2702             (GetPixelMask(image,p+center) == 0))
2703           {
2704             SetPixelChannel(blur_image,channel,p[center+i],q);
2705             continue;
2706           }
2707         k=kernel;
2708         pixel=0.0;
2709         pixels=p;
2710         luminance_pixels=l;
2711         gamma=0.0;
2712         if ((blur_traits & BlendPixelTrait) == 0)
2713           {
2714             for (v=0; v < (ssize_t) width; v++)
2715             {
2716               for (u=0; u < (ssize_t) width; u++)
2717               {
2718                 contrast=GetPixelIntensity(luminance_image,luminance_pixels)-
2719                   intensity;
2720                 if (fabs(contrast) < threshold)
2721                   {
2722                     pixel+=(*k)*pixels[i];
2723                     gamma+=(*k);
2724                   }
2725                 k++;
2726                 pixels+=GetPixelChannels(image);
2727                 luminance_pixels+=GetPixelChannels(luminance_image);
2728               }
2729               pixels+=(image->columns-1)*GetPixelChannels(image);
2730               luminance_pixels+=luminance_image->columns*
2731                 GetPixelChannels(luminance_image);
2732             }
2733             if (fabs((double) gamma) < MagickEpsilon)
2734               {
2735                 SetPixelChannel(blur_image,channel,p[center+i],q);
2736                 continue;
2737               }
2738             gamma=PerceptibleReciprocal(gamma);
2739             SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2740             continue;
2741           }
2742         for (v=0; v < (ssize_t) width; v++)
2743         {
2744           for (u=0; u < (ssize_t) width; u++)
2745           {
2746             contrast=GetPixelIntensity(image,pixels)-intensity;
2747             if (fabs(contrast) < threshold)
2748               {
2749                 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
2750                 pixel+=(*k)*alpha*pixels[i];
2751                 gamma+=(*k)*alpha;
2752               }
2753             k++;
2754             pixels+=GetPixelChannels(image);
2755             luminance_pixels+=GetPixelChannels(luminance_image);
2756           }
2757           pixels+=(image->columns-1)*GetPixelChannels(image);
2758           luminance_pixels+=luminance_image->columns*
2759             GetPixelChannels(luminance_image);
2760         }
2761         if (fabs((double) gamma) < MagickEpsilon)
2762           {
2763             SetPixelChannel(blur_image,channel,p[center+i],q);
2764             continue;
2765           }
2766         gamma=PerceptibleReciprocal(gamma);
2767         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2768       }
2769       p+=GetPixelChannels(image);
2770       l+=GetPixelChannels(luminance_image);
2771       q+=GetPixelChannels(blur_image);
2772     }
2773     sync=SyncCacheViewAuthenticPixels(blur_view,exception);
2774     if (sync == MagickFalse)
2775       status=MagickFalse;
2776     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2777       {
2778         MagickBooleanType
2779           proceed;
2780
2781 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2782         #pragma omp critical (MagickCore_SelectiveBlurImage)
2783 #endif
2784         proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
2785           image->rows);
2786         if (proceed == MagickFalse)
2787           status=MagickFalse;
2788       }
2789   }
2790   blur_image->type=image->type;
2791   blur_view=DestroyCacheView(blur_view);
2792   image_view=DestroyCacheView(image_view);
2793   luminance_image=DestroyImage(luminance_image);
2794   kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2795   if (status == MagickFalse)
2796     blur_image=DestroyImage(blur_image);
2797   return(blur_image);
2798 }
2799 \f
2800 /*
2801 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2802 %                                                                             %
2803 %                                                                             %
2804 %                                                                             %
2805 %     S h a d e I m a g e                                                     %
2806 %                                                                             %
2807 %                                                                             %
2808 %                                                                             %
2809 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2810 %
2811 %  ShadeImage() shines a distant light on an image to create a
2812 %  three-dimensional effect. You control the positioning of the light with
2813 %  azimuth and elevation; azimuth is measured in degrees off the x axis
2814 %  and elevation is measured in pixels above the Z axis.
2815 %
2816 %  The format of the ShadeImage method is:
2817 %
2818 %      Image *ShadeImage(const Image *image,const MagickBooleanType gray,
2819 %        const double azimuth,const double elevation,ExceptionInfo *exception)
2820 %
2821 %  A description of each parameter follows:
2822 %
2823 %    o image: the image.
2824 %
2825 %    o gray: A value other than zero shades the intensity of each pixel.
2826 %
2827 %    o azimuth, elevation:  Define the light source direction.
2828 %
2829 %    o exception: return any errors or warnings in this structure.
2830 %
2831 */
2832 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
2833   const double azimuth,const double elevation,ExceptionInfo *exception)
2834 {
2835 #define ShadeImageTag  "Shade/Image"
2836
2837   CacheView
2838     *image_view,
2839     *shade_view;
2840
2841   Image
2842     *linear_image,
2843     *shade_image;
2844
2845   MagickBooleanType
2846     status;
2847
2848   MagickOffsetType
2849     progress;
2850
2851   PrimaryInfo
2852     light;
2853
2854   ssize_t
2855     y;
2856
2857   /*
2858     Initialize shaded image attributes.
2859   */
2860   assert(image != (const Image *) NULL);
2861   assert(image->signature == MagickSignature);
2862   if (image->debug != MagickFalse)
2863     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2864   assert(exception != (ExceptionInfo *) NULL);
2865   assert(exception->signature == MagickSignature);
2866   linear_image=CloneImage(image,0,0,MagickTrue,exception);
2867   shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2868   if ((linear_image == (Image *) NULL) || (shade_image == (Image *) NULL))
2869     {
2870       if (linear_image != (Image *) NULL)
2871         linear_image=DestroyImage(linear_image);
2872       if (shade_image != (Image *) NULL)
2873         shade_image=DestroyImage(shade_image);
2874       return((Image *) NULL);
2875     }
2876   if (SetImageStorageClass(shade_image,DirectClass,exception) == MagickFalse)
2877     {
2878       linear_image=DestroyImage(linear_image);
2879       shade_image=DestroyImage(shade_image);
2880       return((Image *) NULL);
2881     }
2882   /*
2883     Compute the light vector.
2884   */
2885   light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
2886     cos(DegreesToRadians(elevation));
2887   light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
2888     cos(DegreesToRadians(elevation));
2889   light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
2890   /*
2891     Shade image.
2892   */
2893   status=MagickTrue;
2894   progress=0;
2895   image_view=AcquireVirtualCacheView(linear_image,exception);
2896   shade_view=AcquireAuthenticCacheView(shade_image,exception);
2897 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2898   #pragma omp parallel for schedule(static,4) shared(progress,status) \
2899     magick_threads(linear_image,shade_image,linear_image->rows,1)
2900 #endif
2901   for (y=0; y < (ssize_t) linear_image->rows; y++)
2902   {
2903     double
2904       distance,
2905       normal_distance,
2906       shade;
2907
2908     PrimaryInfo
2909       normal;
2910
2911     register const Quantum
2912       *restrict center,
2913       *restrict p,
2914       *restrict post,
2915       *restrict pre;
2916
2917     register Quantum
2918       *restrict q;
2919
2920     register ssize_t
2921       x;
2922
2923     if (status == MagickFalse)
2924       continue;
2925     p=GetCacheViewVirtualPixels(image_view,-1,y-1,linear_image->columns+2,3,
2926       exception);
2927     q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
2928       exception);
2929     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2930       {
2931         status=MagickFalse;
2932         continue;
2933       }
2934     /*
2935       Shade this row of pixels.
2936     */
2937     normal.z=2.0*(double) QuantumRange;  /* constant Z of surface normal */
2938     pre=p+GetPixelChannels(linear_image);
2939     center=pre+(linear_image->columns+2)*GetPixelChannels(linear_image);
2940     post=center+(linear_image->columns+2)*GetPixelChannels(linear_image);
2941     for (x=0; x < (ssize_t) linear_image->columns; x++)
2942     {
2943       register ssize_t
2944         i;
2945
2946       /*
2947         Determine the surface normal and compute shading.
2948       */
2949       normal.x=(double) (
2950         GetPixelIntensity(linear_image,pre-GetPixelChannels(linear_image))+
2951         GetPixelIntensity(linear_image,center-GetPixelChannels(linear_image))+
2952         GetPixelIntensity(linear_image,post-GetPixelChannels(linear_image))-
2953         GetPixelIntensity(linear_image,pre+GetPixelChannels(linear_image))-
2954         GetPixelIntensity(linear_image,center+GetPixelChannels(linear_image))-
2955         GetPixelIntensity(linear_image,post+GetPixelChannels(linear_image)));
2956       normal.y=(double) (
2957         GetPixelIntensity(linear_image,post-GetPixelChannels(linear_image))+
2958         GetPixelIntensity(linear_image,post)+
2959         GetPixelIntensity(linear_image,post+GetPixelChannels(linear_image))-
2960         GetPixelIntensity(linear_image,pre-GetPixelChannels(linear_image))-
2961         GetPixelIntensity(linear_image,pre)-
2962         GetPixelIntensity(linear_image,pre+GetPixelChannels(linear_image)));
2963       if ((normal.x == 0.0) && (normal.y == 0.0))
2964         shade=light.z;
2965       else
2966         {
2967           shade=0.0;
2968           distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
2969           if (distance > MagickEpsilon)
2970             {
2971               normal_distance=normal.x*normal.x+normal.y*normal.y+
2972                 normal.z*normal.z;
2973               if (normal_distance > (MagickEpsilon*MagickEpsilon))
2974                 shade=distance/sqrt((double) normal_distance);
2975             }
2976         }
2977       for (i=0; i < (ssize_t) GetPixelChannels(linear_image); i++)
2978       {
2979         PixelChannel
2980           channel;
2981
2982         PixelTrait
2983           shade_traits,
2984           traits;
2985
2986         channel=GetPixelChannelChannel(linear_image,i);
2987         traits=GetPixelChannelTraits(linear_image,channel);
2988         shade_traits=GetPixelChannelTraits(shade_image,channel);
2989         if ((traits == UndefinedPixelTrait) ||
2990             (shade_traits == UndefinedPixelTrait))
2991           continue;
2992         if (((shade_traits & CopyPixelTrait) != 0) ||
2993             (GetPixelMask(linear_image,center) == 0))
2994           {
2995             SetPixelChannel(shade_image,channel,center[i],q);
2996             continue;
2997           }
2998         if (gray != MagickFalse)
2999           {
3000             SetPixelChannel(shade_image,channel,ClampToQuantum(shade),q);
3001             continue;
3002           }
3003         SetPixelChannel(shade_image,channel,ClampToQuantum(QuantumScale*shade*
3004           center[i]),q);
3005       }
3006       pre+=GetPixelChannels(linear_image);
3007       center+=GetPixelChannels(linear_image);
3008       post+=GetPixelChannels(linear_image);
3009       q+=GetPixelChannels(shade_image);
3010     }
3011     if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3012       status=MagickFalse;
3013     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3014       {
3015         MagickBooleanType
3016           proceed;
3017
3018 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3019         #pragma omp critical (MagickCore_ShadeImage)
3020 #endif
3021         proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
3022         if (proceed == MagickFalse)
3023           status=MagickFalse;
3024       }
3025   }
3026   shade_view=DestroyCacheView(shade_view);
3027   image_view=DestroyCacheView(image_view);
3028   linear_image=DestroyImage(linear_image);
3029   if (status == MagickFalse)
3030     shade_image=DestroyImage(shade_image);
3031   return(shade_image);
3032 }
3033 \f
3034 /*
3035 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3036 %                                                                             %
3037 %                                                                             %
3038 %                                                                             %
3039 %     S h a r p e n I m a g e                                                 %
3040 %                                                                             %
3041 %                                                                             %
3042 %                                                                             %
3043 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3044 %
3045 %  SharpenImage() sharpens the image.  We convolve the image with a Gaussian
3046 %  operator of the given radius and standard deviation (sigma).  For
3047 %  reasonable results, radius should be larger than sigma.  Use a radius of 0
3048 %  and SharpenImage() selects a suitable radius for you.
3049 %
3050 %  Using a separable kernel would be faster, but the negative weights cancel
3051 %  out on the corners of the kernel producing often undesirable ringing in the
3052 %  filtered result; this can be avoided by using a 2D gaussian shaped image
3053 %  sharpening kernel instead.
3054 %
3055 %  The format of the SharpenImage method is:
3056 %
3057 %    Image *SharpenImage(const Image *image,const double radius,
3058 %      const double sigma,ExceptionInfo *exception)
3059 %
3060 %  A description of each parameter follows:
3061 %
3062 %    o image: the image.
3063 %
3064 %    o radius: the radius of the Gaussian, in pixels, not counting the center
3065 %      pixel.
3066 %
3067 %    o sigma: the standard deviation of the Laplacian, in pixels.
3068 %
3069 %    o exception: return any errors or warnings in this structure.
3070 %
3071 */
3072 MagickExport Image *SharpenImage(const Image *image,const double radius,
3073   const double sigma,ExceptionInfo *exception)
3074 {
3075   char
3076     geometry[MaxTextExtent];
3077
3078   KernelInfo
3079     *kernel_info;
3080
3081   Image
3082     *sharp_image;
3083
3084   assert(image != (const Image *) NULL);
3085   assert(image->signature == MagickSignature);
3086   if (image->debug != MagickFalse)
3087     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3088   assert(exception != (ExceptionInfo *) NULL);
3089   assert(exception->signature == MagickSignature);
3090   (void) FormatLocaleString(geometry,MaxTextExtent,"LoG:%.20gx%.20g",
3091     radius,sigma);
3092   kernel_info=AcquireKernelInfo(geometry);
3093   if (kernel_info == (KernelInfo *) NULL)
3094     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3095   ScaleGeometryKernelInfo(kernel_info,"56!,100%");
3096   sharp_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,
3097     exception);
3098   kernel_info=DestroyKernelInfo(kernel_info);
3099   return(sharp_image);
3100 }
3101 \f
3102 /*
3103 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3104 %                                                                             %
3105 %                                                                             %
3106 %                                                                             %
3107 %     S p r e a d I m a g e                                                   %
3108 %                                                                             %
3109 %                                                                             %
3110 %                                                                             %
3111 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3112 %
3113 %  SpreadImage() is a special effects method that randomly displaces each
3114 %  pixel in a block defined by the radius parameter.
3115 %
3116 %  The format of the SpreadImage method is:
3117 %
3118 %      Image *SpreadImage(const Image *image,const double radius,
3119 %        const PixelInterpolateMethod method,ExceptionInfo *exception)
3120 %
3121 %  A description of each parameter follows:
3122 %
3123 %    o image: the image.
3124 %
3125 %    o radius:  choose a random pixel in a neighborhood of this extent.
3126 %
3127 %    o method:  the pixel interpolation method.
3128 %
3129 %    o exception: return any errors or warnings in this structure.
3130 %
3131 */
3132 MagickExport Image *SpreadImage(const Image *image,const double radius,
3133   const PixelInterpolateMethod method,ExceptionInfo *exception)
3134 {
3135 #define SpreadImageTag  "Spread/Image"
3136
3137   CacheView
3138     *image_view,
3139     *spread_view;
3140
3141   Image
3142     *spread_image;
3143
3144   MagickBooleanType
3145     status;
3146
3147   MagickOffsetType
3148     progress;
3149
3150   RandomInfo
3151     **restrict random_info;
3152
3153   size_t
3154     width;
3155
3156   ssize_t
3157     y;
3158
3159 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3160   unsigned long
3161     key;
3162 #endif
3163
3164   /*
3165     Initialize spread image attributes.
3166   */
3167   assert(image != (Image *) NULL);
3168   assert(image->signature == MagickSignature);
3169   if (image->debug != MagickFalse)
3170     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3171   assert(exception != (ExceptionInfo *) NULL);
3172   assert(exception->signature == MagickSignature);
3173   spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3174     exception);
3175   if (spread_image == (Image *) NULL)
3176     return((Image *) NULL);
3177   if (SetImageStorageClass(spread_image,DirectClass,exception) == MagickFalse)
3178     {
3179       spread_image=DestroyImage(spread_image);
3180       return((Image *) NULL);
3181     }
3182   /*
3183     Spread image.
3184   */
3185   status=MagickTrue;
3186   progress=0;
3187   width=GetOptimalKernelWidth1D(radius,0.5);
3188   random_info=AcquireRandomInfoThreadSet();
3189   image_view=AcquireVirtualCacheView(image,exception);
3190   spread_view=AcquireAuthenticCacheView(spread_image,exception);
3191 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3192   key=GetRandomSecretKey(random_info[0]);
3193   #pragma omp parallel for schedule(static,4) shared(progress,status) \
3194     magick_threads(image,spread_image,image->rows,key == ~0UL)
3195 #endif
3196   for (y=0; y < (ssize_t) image->rows; y++)
3197   {
3198     const int
3199       id = GetOpenMPThreadId();
3200
3201     register const Quantum
3202       *restrict p;
3203
3204     register Quantum
3205       *restrict q;
3206
3207     register ssize_t
3208       x;
3209
3210     if (status == MagickFalse)
3211       continue;
3212     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3213     q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
3214       exception);
3215     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3216       {
3217         status=MagickFalse;
3218         continue;
3219       }
3220     for (x=0; x < (ssize_t) image->columns; x++)
3221     {
3222       PointInfo
3223         point;
3224
3225       point.x=GetPseudoRandomValue(random_info[id]);
3226       point.y=GetPseudoRandomValue(random_info[id]);
3227       status=InterpolatePixelChannels(image,image_view,spread_image,method,
3228         (double) x+width*point.x-0.5,(double) y+width*point.y-0.5,q,exception);
3229       q+=GetPixelChannels(spread_image);
3230     }
3231     if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
3232       status=MagickFalse;
3233     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3234       {
3235         MagickBooleanType
3236           proceed;
3237
3238 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3239         #pragma omp critical (MagickCore_SpreadImage)
3240 #endif
3241         proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3242         if (proceed == MagickFalse)
3243           status=MagickFalse;
3244       }
3245   }
3246   spread_view=DestroyCacheView(spread_view);
3247   image_view=DestroyCacheView(image_view);
3248   random_info=DestroyRandomInfoThreadSet(random_info);
3249   if (status == MagickFalse)
3250     spread_image=DestroyImage(spread_image);
3251   return(spread_image);
3252 }
3253 \f
3254 /*
3255 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3256 %                                                                             %
3257 %                                                                             %
3258 %                                                                             %
3259 %     U n s h a r p M a s k I m a g e                                         %
3260 %                                                                             %
3261 %                                                                             %
3262 %                                                                             %
3263 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3264 %
3265 %  UnsharpMaskImage() sharpens one or more image channels.  We convolve the
3266 %  image with a Gaussian operator of the given radius and standard deviation
3267 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
3268 %  radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
3269 %
3270 %  The format of the UnsharpMaskImage method is:
3271 %
3272 %    Image *UnsharpMaskImage(const Image *image,const double radius,
3273 %      const double sigma,const double gain,ExceptionInfo *exception)
3274 %
3275 %  A description of each parameter follows:
3276 %
3277 %    o image: the image.
3278 %
3279 %    o radius: the radius of the Gaussian, in pixels, not counting the center
3280 %      pixel.
3281 %
3282 %    o sigma: the standard deviation of the Gaussian, in pixels.
3283 %
3284 %    o gain: the percentage of the difference between the original and the
3285 %      blur image that is added back into the original.
3286 %
3287 %    o exception: return any errors or warnings in this structure.
3288 %
3289 */
3290 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
3291   const double sigma,const double gain,ExceptionInfo *exception)
3292 {
3293   char
3294     geometry[MaxTextExtent];
3295
3296   KernelInfo
3297     *kernel_info;
3298
3299   Image
3300     *unsharp_image;
3301
3302   assert(image != (const Image *) NULL);
3303   assert(image->signature == MagickSignature);
3304   if (image->debug != MagickFalse)
3305     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3306   assert(exception != (ExceptionInfo *) NULL);
3307   assert(exception->signature == MagickSignature);
3308   (void) FormatLocaleString(geometry,MaxTextExtent,"Blur:%.20gx%.20g>",
3309     radius,sigma);
3310   kernel_info=AcquireKernelInfo(geometry);
3311   if (kernel_info == (KernelInfo *) NULL)
3312     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3313   (void) FormatLocaleString(geometry,MaxTextExtent,"%.20g,%.20g%%",
3314     -100.0+gain*100.0,200.0-gain*100.0);
3315   ScaleGeometryKernelInfo(kernel_info,geometry);
3316   unsharp_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,
3317     exception);
3318   kernel_info=DestroyKernelInfo(kernel_info);
3319   return(unsharp_image);
3320 }