]> granicus.if.org Git - imagemagick/blob - MagickCore/effect.c
998e6bcd56ed74f63e5cbfb2f43969eda53b8245
[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=BlurImage(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=BlurImage(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=MorphologyImage(image,ConvolveMorphology,1,kernel_info,
1323     exception);
1324   kernel_info=DestroyKernelInfo(kernel_info);
1325   if (emboss_image != (Image *) NULL)
1326     (void) EqualizeImage(emboss_image,exception);
1327   return(emboss_image);
1328 }
1329 \f
1330 /*
1331 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1332 %                                                                             %
1333 %                                                                             %
1334 %                                                                             %
1335 %     G a u s s i a n B l u r I m a g e                                       %
1336 %                                                                             %
1337 %                                                                             %
1338 %                                                                             %
1339 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1340 %
1341 %  GaussianBlurImage() blurs an image.  We convolve the image with a
1342 %  Gaussian operator of the given radius and standard deviation (sigma).
1343 %  For reasonable results, the radius should be larger than sigma.  Use a
1344 %  radius of 0 and GaussianBlurImage() selects a suitable radius for you
1345 %
1346 %  The format of the GaussianBlurImage method is:
1347 %
1348 %      Image *GaussianBlurImage(const Image *image,onst double radius,
1349 %        const double sigma,ExceptionInfo *exception)
1350 %
1351 %  A description of each parameter follows:
1352 %
1353 %    o image: the image.
1354 %
1355 %    o radius: the radius of the Gaussian, in pixels, not counting the center
1356 %      pixel.
1357 %
1358 %    o sigma: the standard deviation of the Gaussian, in pixels.
1359 %
1360 %    o exception: return any errors or warnings in this structure.
1361 %
1362 */
1363 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
1364   const double sigma,ExceptionInfo *exception)
1365 {
1366   char
1367     geometry[MaxTextExtent];
1368
1369   KernelInfo
1370     *kernel_info;
1371
1372   Image
1373     *blur_image;
1374
1375   assert(image != (const Image *) NULL);
1376   assert(image->signature == MagickSignature);
1377   if (image->debug != MagickFalse)
1378     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1379   assert(exception != (ExceptionInfo *) NULL);
1380   assert(exception->signature == MagickSignature);
1381   (void) FormatLocaleString(geometry,MaxTextExtent,"gaussian:%.20gx%.20g",
1382     radius,sigma);
1383   kernel_info=AcquireKernelInfo(geometry);
1384   if (kernel_info == (KernelInfo *) NULL)
1385     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1386   blur_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,exception);
1387   kernel_info=DestroyKernelInfo(kernel_info);
1388   return(blur_image);
1389 }
1390 \f
1391 /*
1392 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1393 %                                                                             %
1394 %                                                                             %
1395 %                                                                             %
1396 %     M o t i o n B l u r I m a g e                                           %
1397 %                                                                             %
1398 %                                                                             %
1399 %                                                                             %
1400 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1401 %
1402 %  MotionBlurImage() simulates motion blur.  We convolve the image with a
1403 %  Gaussian operator of the given radius and standard deviation (sigma).
1404 %  For reasonable results, radius should be larger than sigma.  Use a
1405 %  radius of 0 and MotionBlurImage() selects a suitable radius for you.
1406 %  Angle gives the angle of the blurring motion.
1407 %
1408 %  Andrew Protano contributed this effect.
1409 %
1410 %  The format of the MotionBlurImage method is:
1411 %
1412 %    Image *MotionBlurImage(const Image *image,const double radius,
1413 %      const double sigma,const double angle,ExceptionInfo *exception)
1414 %
1415 %  A description of each parameter follows:
1416 %
1417 %    o image: the image.
1418 %
1419 %    o radius: the radius of the Gaussian, in pixels, not counting
1420 %      the center pixel.
1421 %
1422 %    o sigma: the standard deviation of the Gaussian, in pixels.
1423 %
1424 %    o angle: Apply the effect along this angle.
1425 %
1426 %    o exception: return any errors or warnings in this structure.
1427 %
1428 */
1429
1430 static MagickRealType *GetMotionBlurKernel(const size_t width,
1431   const double sigma)
1432 {
1433   MagickRealType
1434     *kernel,
1435     normalize;
1436
1437   register ssize_t
1438     i;
1439
1440   /*
1441    Generate a 1-D convolution kernel.
1442   */
1443   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1444   kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
1445     width,sizeof(*kernel)));
1446   if (kernel == (MagickRealType *) NULL)
1447     return(kernel);
1448   normalize=0.0;
1449   for (i=0; i < (ssize_t) width; i++)
1450   {
1451     kernel[i]=(MagickRealType) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
1452       MagickSigma)))/(MagickSQ2PI*MagickSigma));
1453     normalize+=kernel[i];
1454   }
1455   for (i=0; i < (ssize_t) width; i++)
1456     kernel[i]/=normalize;
1457   return(kernel);
1458 }
1459
1460 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
1461   const double sigma,const double angle,ExceptionInfo *exception)
1462 {
1463 #define BlurImageTag  "Blur/Image"
1464
1465   CacheView
1466     *blur_view,
1467     *image_view,
1468     *motion_view;
1469
1470   Image
1471     *blur_image;
1472
1473   MagickBooleanType
1474     status;
1475
1476   MagickOffsetType
1477     progress;
1478
1479   MagickRealType
1480     *kernel;
1481
1482   OffsetInfo
1483     *offset;
1484
1485   PointInfo
1486     point;
1487
1488   register ssize_t
1489     i;
1490
1491   size_t
1492     width;
1493
1494   ssize_t
1495     y;
1496
1497   assert(image != (Image *) NULL);
1498   assert(image->signature == MagickSignature);
1499   if (image->debug != MagickFalse)
1500     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1501   assert(exception != (ExceptionInfo *) NULL);
1502   width=GetOptimalKernelWidth1D(radius,sigma);
1503   kernel=GetMotionBlurKernel(width,sigma);
1504   if (kernel == (MagickRealType *) NULL)
1505     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1506   offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
1507   if (offset == (OffsetInfo *) NULL)
1508     {
1509       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
1510       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1511     }
1512   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1513   if (blur_image == (Image *) NULL)
1514     {
1515       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
1516       offset=(OffsetInfo *) RelinquishMagickMemory(offset);
1517       return((Image *) NULL);
1518     }
1519   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
1520     {
1521       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
1522       offset=(OffsetInfo *) RelinquishMagickMemory(offset);
1523       blur_image=DestroyImage(blur_image);
1524       return((Image *) NULL);
1525     }
1526   point.x=(double) width*sin(DegreesToRadians(angle));
1527   point.y=(double) width*cos(DegreesToRadians(angle));
1528   for (i=0; i < (ssize_t) width; i++)
1529   {
1530     offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
1531     offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
1532   }
1533   /*
1534     Motion blur image.
1535   */
1536   status=MagickTrue;
1537   progress=0;
1538   image_view=AcquireVirtualCacheView(image,exception);
1539   motion_view=AcquireVirtualCacheView(image,exception);
1540   blur_view=AcquireAuthenticCacheView(blur_image,exception);
1541 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1542   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1543     magick_threads(image,blur_image,image->rows,1)
1544 #endif
1545   for (y=0; y < (ssize_t) image->rows; y++)
1546   {
1547     register const Quantum
1548       *restrict p;
1549
1550     register Quantum
1551       *restrict q;
1552
1553     register ssize_t
1554       x;
1555
1556     if (status == MagickFalse)
1557       continue;
1558     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1559     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
1560       exception);
1561     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1562       {
1563         status=MagickFalse;
1564         continue;
1565       }
1566     for (x=0; x < (ssize_t) image->columns; x++)
1567     {
1568       register ssize_t
1569         i;
1570
1571       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1572       {
1573         double
1574           alpha,
1575           gamma,
1576           pixel;
1577
1578         PixelChannel
1579           channel;
1580
1581         PixelTrait
1582           blur_traits,
1583           traits;
1584
1585         register const Quantum
1586           *restrict r;
1587
1588         register MagickRealType
1589           *restrict k;
1590
1591         register ssize_t
1592           j;
1593
1594         channel=GetPixelChannelChannel(image,i);
1595         traits=GetPixelChannelTraits(image,channel);
1596         blur_traits=GetPixelChannelTraits(blur_image,channel);
1597         if ((traits == UndefinedPixelTrait) ||
1598             (blur_traits == UndefinedPixelTrait))
1599           continue;
1600         if (((blur_traits & CopyPixelTrait) != 0) ||
1601             (GetPixelMask(image,p) == 0))
1602           {
1603             SetPixelChannel(blur_image,channel,p[i],q);
1604             continue;
1605           }
1606         k=kernel;
1607         pixel=0.0;
1608         if ((blur_traits & BlendPixelTrait) == 0)
1609           {
1610             for (j=0; j < (ssize_t) width; j++)
1611             {
1612               r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+
1613                 offset[j].y,1,1,exception);
1614               if (r == (const Quantum *) NULL)
1615                 {
1616                   status=MagickFalse;
1617                   continue;
1618                 }
1619               pixel+=(*k)*r[i];
1620               k++;
1621             }
1622             SetPixelChannel(blur_image,channel,ClampToQuantum(pixel),q);
1623             continue;
1624           }
1625         alpha=0.0;
1626         gamma=0.0;
1627         for (j=0; j < (ssize_t) width; j++)
1628         {
1629           r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+offset[j].y,1,
1630             1,exception);
1631           if (r == (const Quantum *) NULL)
1632             {
1633               status=MagickFalse;
1634               continue;
1635             }
1636           alpha=(double) (QuantumScale*GetPixelAlpha(image,r));
1637           pixel+=(*k)*alpha*r[i];
1638           gamma+=(*k)*alpha;
1639           k++;
1640         }
1641         gamma=PerceptibleReciprocal(gamma);
1642         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
1643       }
1644       p+=GetPixelChannels(image);
1645       q+=GetPixelChannels(blur_image);
1646     }
1647     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1648       status=MagickFalse;
1649     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1650       {
1651         MagickBooleanType
1652           proceed;
1653
1654 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1655         #pragma omp critical (MagickCore_MotionBlurImage)
1656 #endif
1657         proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
1658         if (proceed == MagickFalse)
1659           status=MagickFalse;
1660       }
1661   }
1662   blur_view=DestroyCacheView(blur_view);
1663   motion_view=DestroyCacheView(motion_view);
1664   image_view=DestroyCacheView(image_view);
1665   kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
1666   offset=(OffsetInfo *) RelinquishMagickMemory(offset);
1667   if (status == MagickFalse)
1668     blur_image=DestroyImage(blur_image);
1669   return(blur_image);
1670 }
1671 \f
1672 /*
1673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1674 %                                                                             %
1675 %                                                                             %
1676 %                                                                             %
1677 %     P r e v i e w I m a g e                                                 %
1678 %                                                                             %
1679 %                                                                             %
1680 %                                                                             %
1681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1682 %
1683 %  PreviewImage() tiles 9 thumbnails of the specified image with an image
1684 %  processing operation applied with varying parameters.  This may be helpful
1685 %  pin-pointing an appropriate parameter for a particular image processing
1686 %  operation.
1687 %
1688 %  The format of the PreviewImages method is:
1689 %
1690 %      Image *PreviewImages(const Image *image,const PreviewType preview,
1691 %        ExceptionInfo *exception)
1692 %
1693 %  A description of each parameter follows:
1694 %
1695 %    o image: the image.
1696 %
1697 %    o preview: the image processing operation.
1698 %
1699 %    o exception: return any errors or warnings in this structure.
1700 %
1701 */
1702 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
1703   ExceptionInfo *exception)
1704 {
1705 #define NumberTiles  9
1706 #define PreviewImageTag  "Preview/Image"
1707 #define DefaultPreviewGeometry  "204x204+10+10"
1708
1709   char
1710     factor[MaxTextExtent],
1711     label[MaxTextExtent];
1712
1713   double
1714     degrees,
1715     gamma,
1716     percentage,
1717     radius,
1718     sigma,
1719     threshold;
1720
1721   extern const char
1722     DefaultTileFrame[];
1723
1724   Image
1725     *images,
1726     *montage_image,
1727     *preview_image,
1728     *thumbnail;
1729
1730   ImageInfo
1731     *preview_info;
1732
1733   MagickBooleanType
1734     proceed;
1735
1736   MontageInfo
1737     *montage_info;
1738
1739   QuantizeInfo
1740     quantize_info;
1741
1742   RectangleInfo
1743     geometry;
1744
1745   register ssize_t
1746     i,
1747     x;
1748
1749   size_t
1750     colors;
1751
1752   ssize_t
1753     y;
1754
1755   /*
1756     Open output image file.
1757   */
1758   assert(image != (Image *) NULL);
1759   assert(image->signature == MagickSignature);
1760   if (image->debug != MagickFalse)
1761     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1762   colors=2;
1763   degrees=0.0;
1764   gamma=(-0.2f);
1765   preview_info=AcquireImageInfo();
1766   SetGeometry(image,&geometry);
1767   (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
1768     &geometry.width,&geometry.height);
1769   images=NewImageList();
1770   percentage=12.5;
1771   GetQuantizeInfo(&quantize_info);
1772   radius=0.0;
1773   sigma=1.0;
1774   threshold=0.0;
1775   x=0;
1776   y=0;
1777   for (i=0; i < NumberTiles; i++)
1778   {
1779     thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
1780     if (thumbnail == (Image *) NULL)
1781       break;
1782     (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
1783       (void *) NULL);
1784     (void) SetImageProperty(thumbnail,"label",DefaultTileLabel,exception);
1785     if (i == (NumberTiles/2))
1786       {
1787         (void) QueryColorCompliance("#dfdfdf",AllCompliance,
1788           &thumbnail->matte_color,exception);
1789         AppendImageToList(&images,thumbnail);
1790         continue;
1791       }
1792     switch (preview)
1793     {
1794       case RotatePreview:
1795       {
1796         degrees+=45.0;
1797         preview_image=RotateImage(thumbnail,degrees,exception);
1798         (void) FormatLocaleString(label,MaxTextExtent,"rotate %g",degrees);
1799         break;
1800       }
1801       case ShearPreview:
1802       {
1803         degrees+=5.0;
1804         preview_image=ShearImage(thumbnail,degrees,degrees,exception);
1805         (void) FormatLocaleString(label,MaxTextExtent,"shear %gx%g",degrees,
1806           2.0*degrees);
1807         break;
1808       }
1809       case RollPreview:
1810       {
1811         x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
1812         y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
1813         preview_image=RollImage(thumbnail,x,y,exception);
1814         (void) FormatLocaleString(label,MaxTextExtent,"roll %+.20gx%+.20g",
1815           (double) x,(double) y);
1816         break;
1817       }
1818       case HuePreview:
1819       {
1820         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1821         if (preview_image == (Image *) NULL)
1822           break;
1823         (void) FormatLocaleString(factor,MaxTextExtent,"100,100,%g",2.0*
1824           percentage);
1825         (void) ModulateImage(preview_image,factor,exception);
1826         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
1827         break;
1828       }
1829       case SaturationPreview:
1830       {
1831         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1832         if (preview_image == (Image *) NULL)
1833           break;
1834         (void) FormatLocaleString(factor,MaxTextExtent,"100,%g",2.0*percentage);
1835         (void) ModulateImage(preview_image,factor,exception);
1836         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
1837         break;
1838       }
1839       case BrightnessPreview:
1840       {
1841         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1842         if (preview_image == (Image *) NULL)
1843           break;
1844         (void) FormatLocaleString(factor,MaxTextExtent,"%g",2.0*percentage);
1845         (void) ModulateImage(preview_image,factor,exception);
1846         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
1847         break;
1848       }
1849       case GammaPreview:
1850       default:
1851       {
1852         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1853         if (preview_image == (Image *) NULL)
1854           break;
1855         gamma+=0.4f;
1856         (void) GammaImage(preview_image,gamma,exception);
1857         (void) FormatLocaleString(label,MaxTextExtent,"gamma %g",gamma);
1858         break;
1859       }
1860       case SpiffPreview:
1861       {
1862         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1863         if (preview_image != (Image *) NULL)
1864           for (x=0; x < i; x++)
1865             (void) ContrastImage(preview_image,MagickTrue,exception);
1866         (void) FormatLocaleString(label,MaxTextExtent,"contrast (%.20g)",
1867           (double) i+1);
1868         break;
1869       }
1870       case DullPreview:
1871       {
1872         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1873         if (preview_image == (Image *) NULL)
1874           break;
1875         for (x=0; x < i; x++)
1876           (void) ContrastImage(preview_image,MagickFalse,exception);
1877         (void) FormatLocaleString(label,MaxTextExtent,"+contrast (%.20g)",
1878           (double) i+1);
1879         break;
1880       }
1881       case GrayscalePreview:
1882       {
1883         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1884         if (preview_image == (Image *) NULL)
1885           break;
1886         colors<<=1;
1887         quantize_info.number_colors=colors;
1888         quantize_info.colorspace=GRAYColorspace;
1889         (void) QuantizeImage(&quantize_info,preview_image,exception);
1890         (void) FormatLocaleString(label,MaxTextExtent,
1891           "-colorspace gray -colors %.20g",(double) colors);
1892         break;
1893       }
1894       case QuantizePreview:
1895       {
1896         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1897         if (preview_image == (Image *) NULL)
1898           break;
1899         colors<<=1;
1900         quantize_info.number_colors=colors;
1901         (void) QuantizeImage(&quantize_info,preview_image,exception);
1902         (void) FormatLocaleString(label,MaxTextExtent,"colors %.20g",(double)
1903           colors);
1904         break;
1905       }
1906       case DespecklePreview:
1907       {
1908         for (x=0; x < (i-1); x++)
1909         {
1910           preview_image=DespeckleImage(thumbnail,exception);
1911           if (preview_image == (Image *) NULL)
1912             break;
1913           thumbnail=DestroyImage(thumbnail);
1914           thumbnail=preview_image;
1915         }
1916         preview_image=DespeckleImage(thumbnail,exception);
1917         if (preview_image == (Image *) NULL)
1918           break;
1919         (void) FormatLocaleString(label,MaxTextExtent,"despeckle (%.20g)",
1920           (double) i+1);
1921         break;
1922       }
1923       case ReduceNoisePreview:
1924       {
1925         preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
1926           (size_t) radius,exception);
1927         (void) FormatLocaleString(label,MaxTextExtent,"noise %g",radius);
1928         break;
1929       }
1930       case AddNoisePreview:
1931       {
1932         switch ((int) i)
1933         {
1934           case 0:
1935           {
1936             (void) CopyMagickString(factor,"uniform",MaxTextExtent);
1937             break;
1938           }
1939           case 1:
1940           {
1941             (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
1942             break;
1943           }
1944           case 2:
1945           {
1946             (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
1947             break;
1948           }
1949           case 3:
1950           {
1951             (void) CopyMagickString(factor,"impulse",MaxTextExtent);
1952             break;
1953           }
1954           case 4:
1955           {
1956             (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
1957             break;
1958           }
1959           case 5:
1960           {
1961             (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
1962             break;
1963           }
1964           default:
1965           {
1966             (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
1967             break;
1968           }
1969         }
1970         preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
1971           (size_t) i,exception);
1972         (void) FormatLocaleString(label,MaxTextExtent,"+noise %s",factor);
1973         break;
1974       }
1975       case SharpenPreview:
1976       {
1977         preview_image=SharpenImage(thumbnail,radius,sigma,exception);
1978         (void) FormatLocaleString(label,MaxTextExtent,"sharpen %gx%g",radius,
1979           sigma);
1980         break;
1981       }
1982       case BlurPreview:
1983       {
1984         preview_image=BlurImage(thumbnail,radius,sigma,exception);
1985         (void) FormatLocaleString(label,MaxTextExtent,"blur %gx%g",radius,
1986           sigma);
1987         break;
1988       }
1989       case ThresholdPreview:
1990       {
1991         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
1992         if (preview_image == (Image *) NULL)
1993           break;
1994         (void) BilevelImage(thumbnail,(double) (percentage*((double)
1995           QuantumRange+1.0))/100.0,exception);
1996         (void) FormatLocaleString(label,MaxTextExtent,"threshold %g",(double)
1997           (percentage*((double) QuantumRange+1.0))/100.0);
1998         break;
1999       }
2000       case EdgeDetectPreview:
2001       {
2002         preview_image=EdgeImage(thumbnail,radius,exception);
2003         (void) FormatLocaleString(label,MaxTextExtent,"edge %g",radius);
2004         break;
2005       }
2006       case SpreadPreview:
2007       {
2008         preview_image=SpreadImage(thumbnail,radius,thumbnail->interpolate,
2009           exception);
2010         (void) FormatLocaleString(label,MaxTextExtent,"spread %g",radius+0.5);
2011         break;
2012       }
2013       case SolarizePreview:
2014       {
2015         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2016         if (preview_image == (Image *) NULL)
2017           break;
2018         (void) SolarizeImage(preview_image,(double) QuantumRange*percentage/
2019           100.0,exception);
2020         (void) FormatLocaleString(label,MaxTextExtent,"solarize %g",
2021           (QuantumRange*percentage)/100.0);
2022         break;
2023       }
2024       case ShadePreview:
2025       {
2026         degrees+=10.0;
2027         preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2028           exception);
2029         (void) FormatLocaleString(label,MaxTextExtent,"shade %gx%g",degrees,
2030           degrees);
2031         break;
2032       }
2033       case RaisePreview:
2034       {
2035         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2036         if (preview_image == (Image *) NULL)
2037           break;
2038         geometry.width=(size_t) (2*i+2);
2039         geometry.height=(size_t) (2*i+2);
2040         geometry.x=(i-1)/2;
2041         geometry.y=(i-1)/2;
2042         (void) RaiseImage(preview_image,&geometry,MagickTrue,exception);
2043         (void) FormatLocaleString(label,MaxTextExtent,
2044           "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
2045           geometry.height,(double) geometry.x,(double) geometry.y);
2046         break;
2047       }
2048       case SegmentPreview:
2049       {
2050         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2051         if (preview_image == (Image *) NULL)
2052           break;
2053         threshold+=0.4f;
2054         (void) SegmentImage(preview_image,sRGBColorspace,MagickFalse,threshold,
2055           threshold,exception);
2056         (void) FormatLocaleString(label,MaxTextExtent,"segment %gx%g",
2057           threshold,threshold);
2058         break;
2059       }
2060       case SwirlPreview:
2061       {
2062         preview_image=SwirlImage(thumbnail,degrees,image->interpolate,
2063           exception);
2064         (void) FormatLocaleString(label,MaxTextExtent,"swirl %g",degrees);
2065         degrees+=45.0;
2066         break;
2067       }
2068       case ImplodePreview:
2069       {
2070         degrees+=0.1f;
2071         preview_image=ImplodeImage(thumbnail,degrees,image->interpolate,
2072           exception);
2073         (void) FormatLocaleString(label,MaxTextExtent,"implode %g",degrees);
2074         break;
2075       }
2076       case WavePreview:
2077       {
2078         degrees+=5.0f;
2079         preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,
2080           image->interpolate,exception);
2081         (void) FormatLocaleString(label,MaxTextExtent,"wave %gx%g",0.5*degrees,
2082           2.0*degrees);
2083         break;
2084       }
2085       case OilPaintPreview:
2086       {
2087         preview_image=OilPaintImage(thumbnail,(double) radius,(double) sigma,
2088           exception);
2089         (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",radius,
2090           sigma);
2091         break;
2092       }
2093       case CharcoalDrawingPreview:
2094       {
2095         preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2096           exception);
2097         (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",radius,
2098           sigma);
2099         break;
2100       }
2101       case JPEGPreview:
2102       {
2103         char
2104           filename[MaxTextExtent];
2105
2106         int
2107           file;
2108
2109         MagickBooleanType
2110           status;
2111
2112         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2113         if (preview_image == (Image *) NULL)
2114           break;
2115         preview_info->quality=(size_t) percentage;
2116         (void) FormatLocaleString(factor,MaxTextExtent,"%.20g",(double)
2117           preview_info->quality);
2118         file=AcquireUniqueFileResource(filename);
2119         if (file != -1)
2120           file=close(file)-1;
2121         (void) FormatLocaleString(preview_image->filename,MaxTextExtent,
2122           "jpeg:%s",filename);
2123         status=WriteImage(preview_info,preview_image,exception);
2124         if (status != MagickFalse)
2125           {
2126             Image
2127               *quality_image;
2128
2129             (void) CopyMagickString(preview_info->filename,
2130               preview_image->filename,MaxTextExtent);
2131             quality_image=ReadImage(preview_info,exception);
2132             if (quality_image != (Image *) NULL)
2133               {
2134                 preview_image=DestroyImage(preview_image);
2135                 preview_image=quality_image;
2136               }
2137           }
2138         (void) RelinquishUniqueFileResource(preview_image->filename);
2139         if ((GetBlobSize(preview_image)/1024) >= 1024)
2140           (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%gmb ",
2141             factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2142             1024.0/1024.0);
2143         else
2144           if (GetBlobSize(preview_image) >= 1024)
2145             (void) FormatLocaleString(label,MaxTextExtent,
2146               "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
2147               GetBlobSize(preview_image))/1024.0);
2148           else
2149             (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%.20gb ",
2150               factor,(double) ((MagickOffsetType) GetBlobSize(thumbnail)));
2151         break;
2152       }
2153     }
2154     thumbnail=DestroyImage(thumbnail);
2155     percentage+=12.5;
2156     radius+=0.5;
2157     sigma+=0.25;
2158     if (preview_image == (Image *) NULL)
2159       break;
2160     (void) DeleteImageProperty(preview_image,"label");
2161     (void) SetImageProperty(preview_image,"label",label,exception);
2162     AppendImageToList(&images,preview_image);
2163     proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2164       NumberTiles);
2165     if (proceed == MagickFalse)
2166       break;
2167   }
2168   if (images == (Image *) NULL)
2169     {
2170       preview_info=DestroyImageInfo(preview_info);
2171       return((Image *) NULL);
2172     }
2173   /*
2174     Create the montage.
2175   */
2176   montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2177   (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
2178   montage_info->shadow=MagickTrue;
2179   (void) CloneString(&montage_info->tile,"3x3");
2180   (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2181   (void) CloneString(&montage_info->frame,DefaultTileFrame);
2182   montage_image=MontageImages(images,montage_info,exception);
2183   montage_info=DestroyMontageInfo(montage_info);
2184   images=DestroyImageList(images);
2185   if (montage_image == (Image *) NULL)
2186     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2187   if (montage_image->montage != (char *) NULL)
2188     {
2189       /*
2190         Free image directory.
2191       */
2192       montage_image->montage=(char *) RelinquishMagickMemory(
2193         montage_image->montage);
2194       if (image->directory != (char *) NULL)
2195         montage_image->directory=(char *) RelinquishMagickMemory(
2196           montage_image->directory);
2197     }
2198   preview_info=DestroyImageInfo(preview_info);
2199   return(montage_image);
2200 }
2201 \f
2202 /*
2203 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2204 %                                                                             %
2205 %                                                                             %
2206 %                                                                             %
2207 %     R a d i a l B l u r I m a g e                                           %
2208 %                                                                             %
2209 %                                                                             %
2210 %                                                                             %
2211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2212 %
2213 %  RadialBlurImage() applies a radial blur to the image.
2214 %
2215 %  Andrew Protano contributed this effect.
2216 %
2217 %  The format of the RadialBlurImage method is:
2218 %
2219 %    Image *RadialBlurImage(const Image *image,const double angle,
2220 %      ExceptionInfo *exception)
2221 %
2222 %  A description of each parameter follows:
2223 %
2224 %    o image: the image.
2225 %
2226 %    o angle: the angle of the radial blur.
2227 %
2228 %    o blur: the blur.
2229 %
2230 %    o exception: return any errors or warnings in this structure.
2231 %
2232 */
2233 MagickExport Image *RadialBlurImage(const Image *image,const double angle,
2234   ExceptionInfo *exception)
2235 {
2236   CacheView
2237     *blur_view,
2238     *image_view,
2239     *radial_view;
2240
2241   Image
2242     *blur_image;
2243
2244   MagickBooleanType
2245     status;
2246
2247   MagickOffsetType
2248     progress;
2249
2250   double
2251     blur_radius,
2252     *cos_theta,
2253     offset,
2254     *sin_theta,
2255     theta;
2256
2257   PointInfo
2258     blur_center;
2259
2260   register ssize_t
2261     i;
2262
2263   size_t
2264     n;
2265
2266   ssize_t
2267     y;
2268
2269   /*
2270     Allocate blur image.
2271   */
2272   assert(image != (Image *) NULL);
2273   assert(image->signature == MagickSignature);
2274   if (image->debug != MagickFalse)
2275     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2276   assert(exception != (ExceptionInfo *) NULL);
2277   assert(exception->signature == MagickSignature);
2278   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2279   if (blur_image == (Image *) NULL)
2280     return((Image *) NULL);
2281   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2282     {
2283       blur_image=DestroyImage(blur_image);
2284       return((Image *) NULL);
2285     }
2286   blur_center.x=(double) (image->columns-1)/2.0;
2287   blur_center.y=(double) (image->rows-1)/2.0;
2288   blur_radius=hypot(blur_center.x,blur_center.y);
2289   n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
2290   theta=DegreesToRadians(angle)/(double) (n-1);
2291   cos_theta=(double *) AcquireQuantumMemory((size_t) n,
2292     sizeof(*cos_theta));
2293   sin_theta=(double *) AcquireQuantumMemory((size_t) n,
2294     sizeof(*sin_theta));
2295   if ((cos_theta == (double *) NULL) ||
2296       (sin_theta == (double *) NULL))
2297     {
2298       blur_image=DestroyImage(blur_image);
2299       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2300     }
2301   offset=theta*(double) (n-1)/2.0;
2302   for (i=0; i < (ssize_t) n; i++)
2303   {
2304     cos_theta[i]=cos((double) (theta*i-offset));
2305     sin_theta[i]=sin((double) (theta*i-offset));
2306   }
2307   /*
2308     Radial blur image.
2309   */
2310   status=MagickTrue;
2311   progress=0;
2312   image_view=AcquireVirtualCacheView(image,exception);
2313   radial_view=AcquireVirtualCacheView(image,exception);
2314   blur_view=AcquireAuthenticCacheView(blur_image,exception);
2315 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2316   #pragma omp parallel for schedule(static,4) shared(progress,status) \
2317     magick_threads(image,blur_image,image->rows,1)
2318 #endif
2319   for (y=0; y < (ssize_t) image->rows; y++)
2320   {
2321     register const Quantum
2322       *restrict p;
2323
2324     register Quantum
2325       *restrict q;
2326
2327     register ssize_t
2328       x;
2329
2330     if (status == MagickFalse)
2331       continue;
2332     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2333     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2334       exception);
2335     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2336       {
2337         status=MagickFalse;
2338         continue;
2339       }
2340     for (x=0; x < (ssize_t) image->columns; x++)
2341     {
2342       double
2343         radius;
2344
2345       PointInfo
2346         center;
2347
2348       register ssize_t
2349         i;
2350
2351       size_t
2352         step;
2353
2354       center.x=(double) x-blur_center.x;
2355       center.y=(double) y-blur_center.y;
2356       radius=hypot((double) center.x,center.y);
2357       if (radius == 0)
2358         step=1;
2359       else
2360         {
2361           step=(size_t) (blur_radius/radius);
2362           if (step == 0)
2363             step=1;
2364           else
2365             if (step >= n)
2366               step=n-1;
2367         }
2368       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2369       {
2370         double
2371           gamma,
2372           pixel;
2373
2374         PixelChannel
2375           channel;
2376
2377         PixelTrait
2378           blur_traits,
2379           traits;
2380
2381         register const Quantum
2382           *restrict r;
2383
2384         register ssize_t
2385           j;
2386
2387         channel=GetPixelChannelChannel(image,i);
2388         traits=GetPixelChannelTraits(image,channel);
2389         blur_traits=GetPixelChannelTraits(blur_image,channel);
2390         if ((traits == UndefinedPixelTrait) ||
2391             (blur_traits == UndefinedPixelTrait))
2392           continue;
2393         if (((blur_traits & CopyPixelTrait) != 0) ||
2394             (GetPixelMask(image,p) == 0))
2395           {
2396             SetPixelChannel(blur_image,channel,p[i],q);
2397             continue;
2398           }
2399         gamma=0.0;
2400         pixel=0.0;
2401         if ((blur_traits & BlendPixelTrait) == 0)
2402           {
2403             for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2404             {
2405               r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
2406                 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2407                 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2408                 1,1,exception);
2409               if (r == (const Quantum *) NULL)
2410                 {
2411                   status=MagickFalse;
2412                   continue;
2413                 }
2414               pixel+=r[i];
2415               gamma++;
2416             }
2417             gamma=PerceptibleReciprocal(gamma);
2418             SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2419             continue;
2420           }
2421         for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2422         {
2423           r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
2424             center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2425             (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2426             1,1,exception);
2427           if (r == (const Quantum *) NULL)
2428             {
2429               status=MagickFalse;
2430               continue;
2431             }
2432           pixel+=GetPixelAlpha(image,r)*r[i];
2433           gamma+=GetPixelAlpha(image,r);
2434         }
2435         gamma=PerceptibleReciprocal(gamma);
2436         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2437       }
2438       p+=GetPixelChannels(image);
2439       q+=GetPixelChannels(blur_image);
2440     }
2441     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2442       status=MagickFalse;
2443     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2444       {
2445         MagickBooleanType
2446           proceed;
2447
2448 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2449         #pragma omp critical (MagickCore_RadialBlurImage)
2450 #endif
2451         proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2452         if (proceed == MagickFalse)
2453           status=MagickFalse;
2454       }
2455   }
2456   blur_view=DestroyCacheView(blur_view);
2457   radial_view=DestroyCacheView(radial_view);
2458   image_view=DestroyCacheView(image_view);
2459   cos_theta=(double *) RelinquishMagickMemory(cos_theta);
2460   sin_theta=(double *) RelinquishMagickMemory(sin_theta);
2461   if (status == MagickFalse)
2462     blur_image=DestroyImage(blur_image);
2463   return(blur_image);
2464 }
2465 \f
2466 /*
2467 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2468 %                                                                             %
2469 %                                                                             %
2470 %                                                                             %
2471 %     S e l e c t i v e B l u r I m a g e                                     %
2472 %                                                                             %
2473 %                                                                             %
2474 %                                                                             %
2475 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2476 %
2477 %  SelectiveBlurImage() selectively blur pixels within a contrast threshold.
2478 %  It is similar to the unsharpen mask that sharpens everything with contrast
2479 %  above a certain threshold.
2480 %
2481 %  The format of the SelectiveBlurImage method is:
2482 %
2483 %      Image *SelectiveBlurImage(const Image *image,const double radius,
2484 %        const double sigma,const double threshold,ExceptionInfo *exception)
2485 %
2486 %  A description of each parameter follows:
2487 %
2488 %    o image: the image.
2489 %
2490 %    o radius: the radius of the Gaussian, in pixels, not counting the center
2491 %      pixel.
2492 %
2493 %    o sigma: the standard deviation of the Gaussian, in pixels.
2494 %
2495 %    o threshold: only pixels within this contrast threshold are included
2496 %      in the blur operation.
2497 %
2498 %    o exception: return any errors or warnings in this structure.
2499 %
2500 */
2501 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
2502   const double sigma,const double threshold,ExceptionInfo *exception)
2503 {
2504 #define SelectiveBlurImageTag  "SelectiveBlur/Image"
2505
2506   CacheView
2507     *blur_view,
2508     *image_view,
2509     *luminance_view;
2510
2511   Image
2512     *blur_image,
2513     *luminance_image;
2514
2515   MagickBooleanType
2516     status;
2517
2518   MagickOffsetType
2519     progress;
2520
2521   MagickRealType
2522     *kernel;
2523
2524   register ssize_t
2525     i;
2526
2527   size_t
2528     width;
2529
2530   ssize_t
2531     center,
2532     j,
2533     u,
2534     v,
2535     y;
2536
2537   /*
2538     Initialize blur image attributes.
2539   */
2540   assert(image != (Image *) NULL);
2541   assert(image->signature == MagickSignature);
2542   if (image->debug != MagickFalse)
2543     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2544   assert(exception != (ExceptionInfo *) NULL);
2545   assert(exception->signature == MagickSignature);
2546   width=GetOptimalKernelWidth1D(radius,sigma);
2547   kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
2548     width,width*sizeof(*kernel)));
2549   if (kernel == (MagickRealType *) NULL)
2550     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2551   j=(ssize_t) (width-1)/2;
2552   i=0;
2553   for (v=(-j); v <= j; v++)
2554   {
2555     for (u=(-j); u <= j; u++)
2556       kernel[i++]=(MagickRealType) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
2557         MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
2558   }
2559   if (image->debug != MagickFalse)
2560     {
2561       char
2562         format[MaxTextExtent],
2563         *message;
2564
2565       register const MagickRealType
2566         *k;
2567
2568       ssize_t
2569         u,
2570         v;
2571
2572       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2573         "  SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
2574         width);
2575       message=AcquireString("");
2576       k=kernel;
2577       for (v=0; v < (ssize_t) width; v++)
2578       {
2579         *message='\0';
2580         (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
2581         (void) ConcatenateString(&message,format);
2582         for (u=0; u < (ssize_t) width; u++)
2583         {
2584           (void) FormatLocaleString(format,MaxTextExtent,"%+f ",(double) *k++);
2585           (void) ConcatenateString(&message,format);
2586         }
2587         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
2588       }
2589       message=DestroyString(message);
2590     }
2591   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2592   if (blur_image == (Image *) NULL)
2593     return((Image *) NULL);
2594   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2595     {
2596       blur_image=DestroyImage(blur_image);
2597       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2598       return((Image *) NULL);
2599     }
2600   luminance_image=CloneImage(image,0,0,MagickTrue,exception);
2601   if (luminance_image == (Image *) NULL)
2602     {
2603       blur_image=DestroyImage(blur_image);
2604       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2605       return((Image *) NULL);
2606     }
2607   status=TransformImageColorspace(luminance_image,GRAYColorspace,exception);
2608   if (status == MagickFalse)
2609     {
2610       luminance_image=DestroyImage(luminance_image);
2611       blur_image=DestroyImage(blur_image);
2612       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2613       return((Image *) NULL);
2614     }
2615   /*
2616     Threshold blur image.
2617   */
2618   status=MagickTrue;
2619   progress=0;
2620   center=(ssize_t) (GetPixelChannels(image)*(image->columns+width)*
2621     ((width-1)/2L)+GetPixelChannels(image)*((width-1)/2L));
2622   image_view=AcquireVirtualCacheView(image,exception);
2623   luminance_view=AcquireVirtualCacheView(luminance_image,exception);
2624   blur_view=AcquireAuthenticCacheView(blur_image,exception);
2625 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2626   #pragma omp parallel for schedule(static,4) shared(progress,status) \
2627     magick_threads(image,blur_image,image->rows,1)
2628 #endif
2629   for (y=0; y < (ssize_t) image->rows; y++)
2630   {
2631     double
2632       contrast;
2633
2634     MagickBooleanType
2635       sync;
2636
2637     register const Quantum
2638       *restrict l,
2639       *restrict p;
2640
2641     register Quantum
2642       *restrict q;
2643
2644     register ssize_t
2645       x;
2646
2647     if (status == MagickFalse)
2648       continue;
2649     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) (width-1)/2L),y-(ssize_t)
2650       ((width-1)/2L),image->columns+width,width,exception);
2651     l=GetCacheViewVirtualPixels(luminance_view,-((ssize_t) (width-1)/2L),y-
2652       (ssize_t) ((width-1)/2L),luminance_image->columns+width,width,exception);
2653     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2654       exception);
2655     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2656       {
2657         status=MagickFalse;
2658         continue;
2659       }
2660     for (x=0; x < (ssize_t) image->columns; x++)
2661     {
2662       double
2663         intensity;
2664
2665       register ssize_t
2666         i;
2667
2668       intensity=GetPixelIntensity(image,p+center);
2669       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2670       {
2671         double
2672           alpha,
2673           gamma,
2674           pixel;
2675
2676         PixelChannel
2677           channel;
2678
2679         PixelTrait
2680           blur_traits,
2681           traits;
2682
2683         register const MagickRealType
2684           *restrict k;
2685
2686         register const Quantum
2687           *restrict luminance_pixels,
2688           *restrict pixels;
2689
2690         register ssize_t
2691           u;
2692
2693         ssize_t
2694           v;
2695
2696         channel=GetPixelChannelChannel(image,i);
2697         traits=GetPixelChannelTraits(image,channel);
2698         blur_traits=GetPixelChannelTraits(blur_image,channel);
2699         if ((traits == UndefinedPixelTrait) ||
2700             (blur_traits == UndefinedPixelTrait))
2701           continue;
2702         if (((blur_traits & CopyPixelTrait) != 0) ||
2703             (GetPixelMask(image,p+center) == 0))
2704           {
2705             SetPixelChannel(blur_image,channel,p[center+i],q);
2706             continue;
2707           }
2708         k=kernel;
2709         pixel=0.0;
2710         pixels=p;
2711         luminance_pixels=l;
2712         gamma=0.0;
2713         if ((blur_traits & BlendPixelTrait) == 0)
2714           {
2715             for (v=0; v < (ssize_t) width; v++)
2716             {
2717               for (u=0; u < (ssize_t) width; u++)
2718               {
2719                 contrast=GetPixelIntensity(luminance_image,luminance_pixels)-
2720                   intensity;
2721                 if (fabs(contrast) < threshold)
2722                   {
2723                     pixel+=(*k)*pixels[i];
2724                     gamma+=(*k);
2725                   }
2726                 k++;
2727                 pixels+=GetPixelChannels(image);
2728                 luminance_pixels+=GetPixelChannels(luminance_image);
2729               }
2730               pixels+=(image->columns-1)*GetPixelChannels(image);
2731               luminance_pixels+=luminance_image->columns*
2732                 GetPixelChannels(luminance_image);
2733             }
2734             if (fabs((double) gamma) < MagickEpsilon)
2735               {
2736                 SetPixelChannel(blur_image,channel,p[center+i],q);
2737                 continue;
2738               }
2739             gamma=PerceptibleReciprocal(gamma);
2740             SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2741             continue;
2742           }
2743         for (v=0; v < (ssize_t) width; v++)
2744         {
2745           for (u=0; u < (ssize_t) width; u++)
2746           {
2747             contrast=GetPixelIntensity(image,pixels)-intensity;
2748             if (fabs(contrast) < threshold)
2749               {
2750                 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
2751                 pixel+=(*k)*alpha*pixels[i];
2752                 gamma+=(*k)*alpha;
2753               }
2754             k++;
2755             pixels+=GetPixelChannels(image);
2756             luminance_pixels+=GetPixelChannels(luminance_image);
2757           }
2758           pixels+=(image->columns-1)*GetPixelChannels(image);
2759           luminance_pixels+=luminance_image->columns*
2760             GetPixelChannels(luminance_image);
2761         }
2762         if (fabs((double) gamma) < MagickEpsilon)
2763           {
2764             SetPixelChannel(blur_image,channel,p[center+i],q);
2765             continue;
2766           }
2767         gamma=PerceptibleReciprocal(gamma);
2768         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2769       }
2770       p+=GetPixelChannels(image);
2771       l+=GetPixelChannels(luminance_image);
2772       q+=GetPixelChannels(blur_image);
2773     }
2774     sync=SyncCacheViewAuthenticPixels(blur_view,exception);
2775     if (sync == MagickFalse)
2776       status=MagickFalse;
2777     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2778       {
2779         MagickBooleanType
2780           proceed;
2781
2782 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2783         #pragma omp critical (MagickCore_SelectiveBlurImage)
2784 #endif
2785         proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
2786           image->rows);
2787         if (proceed == MagickFalse)
2788           status=MagickFalse;
2789       }
2790   }
2791   blur_image->type=image->type;
2792   blur_view=DestroyCacheView(blur_view);
2793   image_view=DestroyCacheView(image_view);
2794   luminance_image=DestroyImage(luminance_image);
2795   kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2796   if (status == MagickFalse)
2797     blur_image=DestroyImage(blur_image);
2798   return(blur_image);
2799 }
2800 \f
2801 /*
2802 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2803 %                                                                             %
2804 %                                                                             %
2805 %                                                                             %
2806 %     S h a d e I m a g e                                                     %
2807 %                                                                             %
2808 %                                                                             %
2809 %                                                                             %
2810 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2811 %
2812 %  ShadeImage() shines a distant light on an image to create a
2813 %  three-dimensional effect. You control the positioning of the light with
2814 %  azimuth and elevation; azimuth is measured in degrees off the x axis
2815 %  and elevation is measured in pixels above the Z axis.
2816 %
2817 %  The format of the ShadeImage method is:
2818 %
2819 %      Image *ShadeImage(const Image *image,const MagickBooleanType gray,
2820 %        const double azimuth,const double elevation,ExceptionInfo *exception)
2821 %
2822 %  A description of each parameter follows:
2823 %
2824 %    o image: the image.
2825 %
2826 %    o gray: A value other than zero shades the intensity of each pixel.
2827 %
2828 %    o azimuth, elevation:  Define the light source direction.
2829 %
2830 %    o exception: return any errors or warnings in this structure.
2831 %
2832 */
2833 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
2834   const double azimuth,const double elevation,ExceptionInfo *exception)
2835 {
2836 #define ShadeImageTag  "Shade/Image"
2837
2838   CacheView
2839     *image_view,
2840     *shade_view;
2841
2842   Image
2843     *linear_image,
2844     *shade_image;
2845
2846   MagickBooleanType
2847     status;
2848
2849   MagickOffsetType
2850     progress;
2851
2852   PrimaryInfo
2853     light;
2854
2855   ssize_t
2856     y;
2857
2858   /*
2859     Initialize shaded image attributes.
2860   */
2861   assert(image != (const Image *) NULL);
2862   assert(image->signature == MagickSignature);
2863   if (image->debug != MagickFalse)
2864     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2865   assert(exception != (ExceptionInfo *) NULL);
2866   assert(exception->signature == MagickSignature);
2867   linear_image=CloneImage(image,0,0,MagickTrue,exception);
2868   shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2869   if ((linear_image == (Image *) NULL) || (shade_image == (Image *) NULL))
2870     {
2871       if (linear_image != (Image *) NULL)
2872         linear_image=DestroyImage(linear_image);
2873       if (shade_image != (Image *) NULL)
2874         shade_image=DestroyImage(shade_image);
2875       return((Image *) NULL);
2876     }
2877   if (SetImageStorageClass(shade_image,DirectClass,exception) == MagickFalse)
2878     {
2879       linear_image=DestroyImage(linear_image);
2880       shade_image=DestroyImage(shade_image);
2881       return((Image *) NULL);
2882     }
2883   /*
2884     Compute the light vector.
2885   */
2886   light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
2887     cos(DegreesToRadians(elevation));
2888   light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
2889     cos(DegreesToRadians(elevation));
2890   light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
2891   /*
2892     Shade image.
2893   */
2894   status=MagickTrue;
2895   progress=0;
2896   image_view=AcquireVirtualCacheView(linear_image,exception);
2897   shade_view=AcquireAuthenticCacheView(shade_image,exception);
2898 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2899   #pragma omp parallel for schedule(static,4) shared(progress,status) \
2900     magick_threads(linear_image,shade_image,linear_image->rows,1)
2901 #endif
2902   for (y=0; y < (ssize_t) linear_image->rows; y++)
2903   {
2904     double
2905       distance,
2906       normal_distance,
2907       shade;
2908
2909     PrimaryInfo
2910       normal;
2911
2912     register const Quantum
2913       *restrict center,
2914       *restrict p,
2915       *restrict post,
2916       *restrict pre;
2917
2918     register Quantum
2919       *restrict q;
2920
2921     register ssize_t
2922       x;
2923
2924     if (status == MagickFalse)
2925       continue;
2926     p=GetCacheViewVirtualPixels(image_view,-1,y-1,linear_image->columns+2,3,
2927       exception);
2928     q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
2929       exception);
2930     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2931       {
2932         status=MagickFalse;
2933         continue;
2934       }
2935     /*
2936       Shade this row of pixels.
2937     */
2938     normal.z=2.0*(double) QuantumRange;  /* constant Z of surface normal */
2939     pre=p+GetPixelChannels(linear_image);
2940     center=pre+(linear_image->columns+2)*GetPixelChannels(linear_image);
2941     post=center+(linear_image->columns+2)*GetPixelChannels(linear_image);
2942     for (x=0; x < (ssize_t) linear_image->columns; x++)
2943     {
2944       register ssize_t
2945         i;
2946
2947       /*
2948         Determine the surface normal and compute shading.
2949       */
2950       normal.x=(double) (
2951         GetPixelIntensity(linear_image,pre-GetPixelChannels(linear_image))+
2952         GetPixelIntensity(linear_image,center-GetPixelChannels(linear_image))+
2953         GetPixelIntensity(linear_image,post-GetPixelChannels(linear_image))-
2954         GetPixelIntensity(linear_image,pre+GetPixelChannels(linear_image))-
2955         GetPixelIntensity(linear_image,center+GetPixelChannels(linear_image))-
2956         GetPixelIntensity(linear_image,post+GetPixelChannels(linear_image)));
2957       normal.y=(double) (
2958         GetPixelIntensity(linear_image,post-GetPixelChannels(linear_image))+
2959         GetPixelIntensity(linear_image,post)+
2960         GetPixelIntensity(linear_image,post+GetPixelChannels(linear_image))-
2961         GetPixelIntensity(linear_image,pre-GetPixelChannels(linear_image))-
2962         GetPixelIntensity(linear_image,pre)-
2963         GetPixelIntensity(linear_image,pre+GetPixelChannels(linear_image)));
2964       if ((normal.x == 0.0) && (normal.y == 0.0))
2965         shade=light.z;
2966       else
2967         {
2968           shade=0.0;
2969           distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
2970           if (distance > MagickEpsilon)
2971             {
2972               normal_distance=normal.x*normal.x+normal.y*normal.y+
2973                 normal.z*normal.z;
2974               if (normal_distance > (MagickEpsilon*MagickEpsilon))
2975                 shade=distance/sqrt((double) normal_distance);
2976             }
2977         }
2978       for (i=0; i < (ssize_t) GetPixelChannels(linear_image); i++)
2979       {
2980         PixelChannel
2981           channel;
2982
2983         PixelTrait
2984           shade_traits,
2985           traits;
2986
2987         channel=GetPixelChannelChannel(linear_image,i);
2988         traits=GetPixelChannelTraits(linear_image,channel);
2989         shade_traits=GetPixelChannelTraits(shade_image,channel);
2990         if ((traits == UndefinedPixelTrait) ||
2991             (shade_traits == UndefinedPixelTrait))
2992           continue;
2993         if (((shade_traits & CopyPixelTrait) != 0) ||
2994             (GetPixelMask(linear_image,center) == 0))
2995           {
2996             SetPixelChannel(shade_image,channel,center[i],q);
2997             continue;
2998           }
2999         if (gray != MagickFalse)
3000           {
3001             SetPixelChannel(shade_image,channel,ClampToQuantum(shade),q);
3002             continue;
3003           }
3004         SetPixelChannel(shade_image,channel,ClampToQuantum(QuantumScale*shade*
3005           center[i]),q);
3006       }
3007       pre+=GetPixelChannels(linear_image);
3008       center+=GetPixelChannels(linear_image);
3009       post+=GetPixelChannels(linear_image);
3010       q+=GetPixelChannels(shade_image);
3011     }
3012     if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3013       status=MagickFalse;
3014     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3015       {
3016         MagickBooleanType
3017           proceed;
3018
3019 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3020         #pragma omp critical (MagickCore_ShadeImage)
3021 #endif
3022         proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
3023         if (proceed == MagickFalse)
3024           status=MagickFalse;
3025       }
3026   }
3027   shade_view=DestroyCacheView(shade_view);
3028   image_view=DestroyCacheView(image_view);
3029   linear_image=DestroyImage(linear_image);
3030   if (status == MagickFalse)
3031     shade_image=DestroyImage(shade_image);
3032   return(shade_image);
3033 }
3034 \f
3035 /*
3036 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3037 %                                                                             %
3038 %                                                                             %
3039 %                                                                             %
3040 %     S h a r p e n I m a g e                                                 %
3041 %                                                                             %
3042 %                                                                             %
3043 %                                                                             %
3044 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3045 %
3046 %  SharpenImage() sharpens the image.  We convolve the image with a Gaussian
3047 %  operator of the given radius and standard deviation (sigma).  For
3048 %  reasonable results, radius should be larger than sigma.  Use a radius of 0
3049 %  and SharpenImage() selects a suitable radius for you.
3050 %
3051 %  Using a separable kernel would be faster, but the negative weights cancel
3052 %  out on the corners of the kernel producing often undesirable ringing in the
3053 %  filtered result; this can be avoided by using a 2D gaussian shaped image
3054 %  sharpening kernel instead.
3055 %
3056 %  The format of the SharpenImage method is:
3057 %
3058 %    Image *SharpenImage(const Image *image,const double radius,
3059 %      const double sigma,ExceptionInfo *exception)
3060 %
3061 %  A description of each parameter follows:
3062 %
3063 %    o image: the image.
3064 %
3065 %    o radius: the radius of the Gaussian, in pixels, not counting the center
3066 %      pixel.
3067 %
3068 %    o sigma: the standard deviation of the Laplacian, in pixels.
3069 %
3070 %    o exception: return any errors or warnings in this structure.
3071 %
3072 */
3073 MagickExport Image *SharpenImage(const Image *image,const double radius,
3074   const double sigma,ExceptionInfo *exception)
3075 {
3076   char
3077     geometry[MaxTextExtent];
3078
3079   KernelInfo
3080     *kernel_info;
3081
3082   Image
3083     *sharp_image;
3084
3085   assert(image != (const Image *) NULL);
3086   assert(image->signature == MagickSignature);
3087   if (image->debug != MagickFalse)
3088     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3089   assert(exception != (ExceptionInfo *) NULL);
3090   assert(exception->signature == MagickSignature);
3091   (void) FormatLocaleString(geometry,MaxTextExtent,"LoG:%.20gx%.20g",
3092     radius,sigma);
3093   kernel_info=AcquireKernelInfo(geometry);
3094   if (kernel_info == (KernelInfo *) NULL)
3095     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3096   ScaleGeometryKernelInfo(kernel_info,"56!,100%");
3097   sharp_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,
3098     exception);
3099   kernel_info=DestroyKernelInfo(kernel_info);
3100   return(sharp_image);
3101 }
3102 \f
3103 /*
3104 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3105 %                                                                             %
3106 %                                                                             %
3107 %                                                                             %
3108 %     S p r e a d I m a g e                                                   %
3109 %                                                                             %
3110 %                                                                             %
3111 %                                                                             %
3112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3113 %
3114 %  SpreadImage() is a special effects method that randomly displaces each
3115 %  pixel in a block defined by the radius parameter.
3116 %
3117 %  The format of the SpreadImage method is:
3118 %
3119 %      Image *SpreadImage(const Image *image,const double radius,
3120 %        const PixelInterpolateMethod method,ExceptionInfo *exception)
3121 %
3122 %  A description of each parameter follows:
3123 %
3124 %    o image: the image.
3125 %
3126 %    o radius:  choose a random pixel in a neighborhood of this extent.
3127 %
3128 %    o method:  the pixel interpolation method.
3129 %
3130 %    o exception: return any errors or warnings in this structure.
3131 %
3132 */
3133 MagickExport Image *SpreadImage(const Image *image,const double radius,
3134   const PixelInterpolateMethod method,ExceptionInfo *exception)
3135 {
3136 #define SpreadImageTag  "Spread/Image"
3137
3138   CacheView
3139     *image_view,
3140     *spread_view;
3141
3142   Image
3143     *spread_image;
3144
3145   MagickBooleanType
3146     status;
3147
3148   MagickOffsetType
3149     progress;
3150
3151   RandomInfo
3152     **restrict random_info;
3153
3154   size_t
3155     width;
3156
3157   ssize_t
3158     y;
3159
3160 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3161   unsigned long
3162     key;
3163 #endif
3164
3165   /*
3166     Initialize spread image attributes.
3167   */
3168   assert(image != (Image *) NULL);
3169   assert(image->signature == MagickSignature);
3170   if (image->debug != MagickFalse)
3171     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3172   assert(exception != (ExceptionInfo *) NULL);
3173   assert(exception->signature == MagickSignature);
3174   spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3175     exception);
3176   if (spread_image == (Image *) NULL)
3177     return((Image *) NULL);
3178   if (SetImageStorageClass(spread_image,DirectClass,exception) == MagickFalse)
3179     {
3180       spread_image=DestroyImage(spread_image);
3181       return((Image *) NULL);
3182     }
3183   /*
3184     Spread image.
3185   */
3186   status=MagickTrue;
3187   progress=0;
3188   width=GetOptimalKernelWidth1D(radius,0.5);
3189   random_info=AcquireRandomInfoThreadSet();
3190   image_view=AcquireVirtualCacheView(image,exception);
3191   spread_view=AcquireAuthenticCacheView(spread_image,exception);
3192 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3193   key=GetRandomSecretKey(random_info[0]);
3194   #pragma omp parallel for schedule(static,4) shared(progress,status) \
3195     magick_threads(image,spread_image,image->rows,key == ~0UL)
3196 #endif
3197   for (y=0; y < (ssize_t) image->rows; y++)
3198   {
3199     const int
3200       id = GetOpenMPThreadId();
3201
3202     register const Quantum
3203       *restrict p;
3204
3205     register Quantum
3206       *restrict q;
3207
3208     register ssize_t
3209       x;
3210
3211     if (status == MagickFalse)
3212       continue;
3213     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3214     q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
3215       exception);
3216     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3217       {
3218         status=MagickFalse;
3219         continue;
3220       }
3221     for (x=0; x < (ssize_t) image->columns; x++)
3222     {
3223       PointInfo
3224         point;
3225
3226       point.x=GetPseudoRandomValue(random_info[id]);
3227       point.y=GetPseudoRandomValue(random_info[id]);
3228       status=InterpolatePixelChannels(image,image_view,spread_image,method,
3229         (double) x+width*point.x-0.5,(double) y+width*point.y-0.5,q,exception);
3230       q+=GetPixelChannels(spread_image);
3231     }
3232     if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
3233       status=MagickFalse;
3234     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3235       {
3236         MagickBooleanType
3237           proceed;
3238
3239 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3240         #pragma omp critical (MagickCore_SpreadImage)
3241 #endif
3242         proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3243         if (proceed == MagickFalse)
3244           status=MagickFalse;
3245       }
3246   }
3247   spread_view=DestroyCacheView(spread_view);
3248   image_view=DestroyCacheView(image_view);
3249   random_info=DestroyRandomInfoThreadSet(random_info);
3250   if (status == MagickFalse)
3251     spread_image=DestroyImage(spread_image);
3252   return(spread_image);
3253 }
3254 \f
3255 /*
3256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3257 %                                                                             %
3258 %                                                                             %
3259 %                                                                             %
3260 %     U n s h a r p M a s k I m a g e                                         %
3261 %                                                                             %
3262 %                                                                             %
3263 %                                                                             %
3264 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3265 %
3266 %  UnsharpMaskImage() sharpens one or more image channels.  We convolve the
3267 %  image with a Gaussian operator of the given radius and standard deviation
3268 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
3269 %  radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
3270 %
3271 %  The format of the UnsharpMaskImage method is:
3272 %
3273 %    Image *UnsharpMaskImage(const Image *image,const double radius,
3274 %      const double sigma,const double gain,ExceptionInfo *exception)
3275 %
3276 %  A description of each parameter follows:
3277 %
3278 %    o image: the image.
3279 %
3280 %    o radius: the radius of the Gaussian, in pixels, not counting the center
3281 %      pixel.
3282 %
3283 %    o sigma: the standard deviation of the Gaussian, in pixels.
3284 %
3285 %    o gain: the percentage of the difference between the original and the
3286 %      blur image that is added back into the original.
3287 %
3288 %    o exception: return any errors or warnings in this structure.
3289 %
3290 */
3291 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
3292   const double sigma,const double gain,ExceptionInfo *exception)
3293 {
3294   char
3295     geometry[MaxTextExtent];
3296
3297   KernelInfo
3298     *kernel_info;
3299
3300   Image
3301     *unsharp_image;
3302
3303   assert(image != (const Image *) NULL);
3304   assert(image->signature == MagickSignature);
3305   if (image->debug != MagickFalse)
3306     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3307   assert(exception != (ExceptionInfo *) NULL);
3308   assert(exception->signature == MagickSignature);
3309   (void) FormatLocaleString(geometry,MaxTextExtent,"Blur:%.20gx%.20g>",
3310     radius,sigma);
3311   kernel_info=AcquireKernelInfo(geometry);
3312   if (kernel_info == (KernelInfo *) NULL)
3313     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3314   (void) FormatLocaleString(geometry,MaxTextExtent,"%.20g,%.20g%%",
3315     -100.0+gain*100.0,200.0-gain*100.0);
3316   ScaleGeometryKernelInfo(kernel_info,geometry);
3317   unsharp_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,
3318     exception);
3319   kernel_info=DestroyKernelInfo(kernel_info);
3320   return(unsharp_image);
3321 }