]> granicus.if.org Git - imagemagick/blob - MagickCore/effect.c
(no commit message)
[imagemagick] / MagickCore / effect.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                   EEEEE  FFFFF  FFFFF  EEEEE  CCCC  TTTTT                   %
7 %                   E      F      F      E     C        T                     %
8 %                   EEE    FFF    FFF    EEE   C        T                     %
9 %                   E      F      F      E     C        T                     %
10 %                   EEEEE  F      F      EEEEE  CCCC    T                     %
11 %                                                                             %
12 %                                                                             %
13 %                       MagickCore Image Effects Methods                      %
14 %                                                                             %
15 %                               Software Design                               %
16 %                                 John Cristy                                 %
17 %                                 October 1996                                %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 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/draw.h"
53 #include "MagickCore/enhance.h"
54 #include "MagickCore/exception.h"
55 #include "MagickCore/exception-private.h"
56 #include "MagickCore/effect.h"
57 #include "MagickCore/fx.h"
58 #include "MagickCore/gem.h"
59 #include "MagickCore/gem-private.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/image-private.h"
62 #include "MagickCore/list.h"
63 #include "MagickCore/log.h"
64 #include "MagickCore/memory_.h"
65 #include "MagickCore/monitor.h"
66 #include "MagickCore/monitor-private.h"
67 #include "MagickCore/montage.h"
68 #include "MagickCore/morphology.h"
69 #include "MagickCore/paint.h"
70 #include "MagickCore/pixel-accessor.h"
71 #include "MagickCore/property.h"
72 #include "MagickCore/quantize.h"
73 #include "MagickCore/quantum.h"
74 #include "MagickCore/quantum-private.h"
75 #include "MagickCore/random_.h"
76 #include "MagickCore/random-private.h"
77 #include "MagickCore/resample.h"
78 #include "MagickCore/resample-private.h"
79 #include "MagickCore/resize.h"
80 #include "MagickCore/resource_.h"
81 #include "MagickCore/segment.h"
82 #include "MagickCore/shear.h"
83 #include "MagickCore/signature-private.h"
84 #include "MagickCore/string_.h"
85 #include "MagickCore/thread-private.h"
86 #include "MagickCore/transform.h"
87 #include "MagickCore/threshold.h"
88 \f
89 /*
90 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91 %                                                                             %
92 %                                                                             %
93 %                                                                             %
94 %     A d a p t i v e B l u r I m a g e                                       %
95 %                                                                             %
96 %                                                                             %
97 %                                                                             %
98 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
99 %
100 %  AdaptiveBlurImage() adaptively blurs the image by blurring less
101 %  intensely near image edges and more intensely far from edges.  We blur the
102 %  image with a Gaussian operator of the given radius and standard deviation
103 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
104 %  radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
105 %
106 %  The format of the AdaptiveBlurImage method is:
107 %
108 %      Image *AdaptiveBlurImage(const Image *image,const double radius,
109 %        const double sigma,const double bias,ExceptionInfo *exception)
110 %
111 %  A description of each parameter follows:
112 %
113 %    o image: the image.
114 %
115 %    o radius: the radius of the Gaussian, in pixels, not counting the center
116 %      pixel.
117 %
118 %    o sigma: the standard deviation of the Laplacian, in pixels.
119 %
120 %    o bias: the bias.
121 %
122 %    o exception: return any errors or warnings in this structure.
123 %
124 */
125
126 MagickExport MagickBooleanType AdaptiveLevelImage(Image *image,
127   const char *levels,ExceptionInfo *exception)
128 {
129   double
130     black_point,
131     gamma,
132     white_point;
133
134   GeometryInfo
135     geometry_info;
136
137   MagickBooleanType
138     status;
139
140   MagickStatusType
141     flags;
142
143   /*
144     Parse levels.
145   */
146   if (levels == (char *) NULL)
147     return(MagickFalse);
148   flags=ParseGeometry(levels,&geometry_info);
149   black_point=geometry_info.rho;
150   white_point=(double) QuantumRange;
151   if ((flags & SigmaValue) != 0)
152     white_point=geometry_info.sigma;
153   gamma=1.0;
154   if ((flags & XiValue) != 0)
155     gamma=geometry_info.xi;
156   if ((flags & PercentValue) != 0)
157     {
158       black_point*=(double) image->columns*image->rows/100.0;
159       white_point*=(double) image->columns*image->rows/100.0;
160     }
161   if ((flags & SigmaValue) == 0)
162     white_point=(double) QuantumRange-black_point;
163   if ((flags & AspectValue ) == 0)
164     status=LevelImage(image,black_point,white_point,gamma,exception);
165   else
166     status=LevelizeImage(image,black_point,white_point,gamma,exception);
167   return(status);
168 }
169
170 MagickExport Image *AdaptiveBlurImage(const Image *image,
171   const double radius,const double sigma,const double bias,
172   ExceptionInfo *exception)
173 {
174 #define AdaptiveBlurImageTag  "Convolve/Image"
175 #define MagickSigma  (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
176
177   CacheView
178     *blur_view,
179     *edge_view,
180     *image_view;
181
182   double
183     **kernel,
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   register ssize_t
198     i;
199
200   size_t
201     width;
202
203   ssize_t
204     j,
205     k,
206     u,
207     v,
208     y;
209
210   assert(image != (const Image *) NULL);
211   assert(image->signature == MagickSignature);
212   if (image->debug != MagickFalse)
213     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
214   assert(exception != (ExceptionInfo *) NULL);
215   assert(exception->signature == MagickSignature);
216   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
217   if (blur_image == (Image *) NULL)
218     return((Image *) NULL);
219   if (fabs(sigma) <= MagickEpsilon)
220     return(blur_image);
221   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
222     {
223       blur_image=DestroyImage(blur_image);
224       return((Image *) NULL);
225     }
226   /*
227     Edge detect the image brighness channel, level, blur, and level again.
228   */
229   edge_image=EdgeImage(image,radius,sigma,exception);
230   if (edge_image == (Image *) NULL)
231     {
232       blur_image=DestroyImage(blur_image);
233       return((Image *) NULL);
234     }
235   (void) AdaptiveLevelImage(edge_image,"20%,95%",exception);
236   gaussian_image=GaussianBlurImage(edge_image,radius,sigma,bias,exception);
237   if (gaussian_image != (Image *) NULL)
238     {
239       edge_image=DestroyImage(edge_image);
240       edge_image=gaussian_image;
241     }
242   (void) AdaptiveLevelImage(edge_image,"10%,95%",exception);
243   /*
244     Create a set of kernels from maximum (radius,sigma) to minimum.
245   */
246   width=GetOptimalKernelWidth2D(radius,sigma);
247   kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
248   if (kernel == (double **) NULL)
249     {
250       edge_image=DestroyImage(edge_image);
251       blur_image=DestroyImage(blur_image);
252       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
253     }
254   (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
255   for (i=0; i < (ssize_t) width; i+=2)
256   {
257     kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
258       sizeof(**kernel));
259     if (kernel[i] == (double *) NULL)
260       break;
261     normalize=0.0;
262     j=(ssize_t) (width-i)/2;
263     k=0;
264     for (v=(-j); v <= j; v++)
265     {
266       for (u=(-j); u <= j; u++)
267       {
268         kernel[i][k]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
269           MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
270         normalize+=kernel[i][k];
271         k++;
272       }
273     }
274     if (fabs(normalize) <= MagickEpsilon)
275       normalize=1.0;
276     normalize=1.0/normalize;
277     for (k=0; k < (j*j); k++)
278       kernel[i][k]=normalize*kernel[i][k];
279   }
280   if (i < (ssize_t) width)
281     {
282       for (i-=2; i >= 0; i-=2)
283         kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
284       kernel=(double **) RelinquishMagickMemory(kernel);
285       edge_image=DestroyImage(edge_image);
286       blur_image=DestroyImage(blur_image);
287       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
288     }
289   /*
290     Adaptively blur image.
291   */
292   status=MagickTrue;
293   progress=0;
294   image_view=AcquireCacheView(image);
295   edge_view=AcquireCacheView(edge_image);
296   blur_view=AcquireCacheView(blur_image);
297 #if defined(MAGICKCORE_OPENMP_SUPPORT)
298   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
299 #endif
300   for (y=0; y < (ssize_t) blur_image->rows; y++)
301   {
302     register const Quantum
303       *restrict r;
304
305     register Quantum
306       *restrict q;
307
308     register ssize_t
309       x;
310
311     if (status == MagickFalse)
312       continue;
313     r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
314     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
315       exception);
316     if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
317       {
318         status=MagickFalse;
319         continue;
320       }
321     for (x=0; x < (ssize_t) blur_image->columns; x++)
322     {
323       register const Quantum
324         *restrict p;
325
326       register ssize_t
327         i;
328
329       ssize_t
330         center,
331         j;
332
333       j=(ssize_t) ceil((double) width*QuantumScale*
334         GetPixelIntensity(edge_image,r)-0.5);
335       if (j < 0)
336         j=0;
337       else
338         if (j > (ssize_t) width)
339           j=(ssize_t) width;
340       if ((j & 0x01) != 0)
341         j--;
342       p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
343         (ssize_t) ((width-j)/2L),width-j,width-j,exception);
344       if (p == (const Quantum *) NULL)
345         break;
346       center=(ssize_t) GetPixelChannels(image)*(width-j)*
347         ((width-j)/2L)+GetPixelChannels(image)*((width-j)/2L);
348       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
349       {
350         MagickRealType
351           alpha,
352           gamma,
353           pixel;
354
355         PixelChannel
356           channel;
357
358         PixelTrait
359           blur_traits,
360           traits;
361
362         register const double
363           *restrict k;
364
365         register const Quantum
366           *restrict pixels;
367
368         register ssize_t
369           u;
370
371         ssize_t
372           v;
373
374         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
375         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
376         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
377         if ((traits == UndefinedPixelTrait) ||
378             (blur_traits == UndefinedPixelTrait))
379           continue;
380         if ((blur_traits & CopyPixelTrait) != 0)
381           {
382             SetPixelChannel(blur_image,channel,p[center+i],q);
383             continue;
384           }
385         k=kernel[j];
386         pixels=p;
387         pixel=bias;
388         gamma=0.0;
389         if ((blur_traits & BlendPixelTrait) == 0)
390           {
391             /*
392               No alpha blending.
393             */
394             for (v=0; v < (ssize_t) (width-j); v++)
395             {
396               for (u=0; u < (ssize_t) (width-j); u++)
397               {
398                 pixel+=(*k)*pixels[i];
399                 gamma+=(*k);
400                 k++;
401                 pixels+=GetPixelChannels(image);
402               }
403             }
404             gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
405             SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
406             continue;
407           }
408         /*
409           Alpha blending.
410         */
411         for (v=0; v < (ssize_t) (width-j); v++)
412         {
413           for (u=0; u < (ssize_t) (width-j); u++)
414           {
415             alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
416             pixel+=(*k)*alpha*pixels[i];
417             gamma+=(*k)*alpha;
418             k++;
419             pixels+=GetPixelChannels(image);
420           }
421         }
422         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
423         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
424       }
425       q+=GetPixelChannels(blur_image);
426       r+=GetPixelChannels(edge_image);
427     }
428     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
429       status=MagickFalse;
430     if (image->progress_monitor != (MagickProgressMonitor) NULL)
431       {
432         MagickBooleanType
433           proceed;
434
435 #if defined(MAGICKCORE_OPENMP_SUPPORT)
436   #pragma omp critical (MagickCore_AdaptiveBlurImage)
437 #endif
438         proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
439           image->rows);
440         if (proceed == MagickFalse)
441           status=MagickFalse;
442       }
443   }
444   blur_image->type=image->type;
445   blur_view=DestroyCacheView(blur_view);
446   edge_view=DestroyCacheView(edge_view);
447   image_view=DestroyCacheView(image_view);
448   edge_image=DestroyImage(edge_image);
449   for (i=0; i < (ssize_t) width;  i+=2)
450     kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
451   kernel=(double **) RelinquishMagickMemory(kernel);
452   if (status == MagickFalse)
453     blur_image=DestroyImage(blur_image);
454   return(blur_image);
455 }
456 \f
457 /*
458 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
459 %                                                                             %
460 %                                                                             %
461 %                                                                             %
462 %     A d a p t i v e S h a r p e n I m a g e                                 %
463 %                                                                             %
464 %                                                                             %
465 %                                                                             %
466 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
467 %
468 %  AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
469 %  intensely near image edges and less intensely far from edges. We sharpen the
470 %  image with a Gaussian operator of the given radius and standard deviation
471 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
472 %  radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
473 %
474 %  The format of the AdaptiveSharpenImage method is:
475 %
476 %      Image *AdaptiveSharpenImage(const Image *image,const double radius,
477 %        const double sigma,const double bias,ExceptionInfo *exception)
478 %
479 %  A description of each parameter follows:
480 %
481 %    o image: the image.
482 %
483 %    o radius: the radius of the Gaussian, in pixels, not counting the center
484 %      pixel.
485 %
486 %    o sigma: the standard deviation of the Laplacian, in pixels.
487 %
488 %    o bias: the bias.
489 %
490 %    o exception: return any errors or warnings in this structure.
491 %
492 */
493 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
494   const double sigma,const double bias,ExceptionInfo *exception)
495 {
496 #define AdaptiveSharpenImageTag  "Convolve/Image"
497 #define MagickSigma  (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
498
499   CacheView
500     *sharp_view,
501     *edge_view,
502     *image_view;
503
504   double
505     **kernel,
506     normalize;
507
508   Image
509     *sharp_image,
510     *edge_image,
511     *gaussian_image;
512
513   MagickBooleanType
514     status;
515
516   MagickOffsetType
517     progress;
518
519   register ssize_t
520     i;
521
522   size_t
523     width;
524
525   ssize_t
526     j,
527     k,
528     u,
529     v,
530     y;
531
532   assert(image != (const Image *) NULL);
533   assert(image->signature == MagickSignature);
534   if (image->debug != MagickFalse)
535     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
536   assert(exception != (ExceptionInfo *) NULL);
537   assert(exception->signature == MagickSignature);
538   sharp_image=CloneImage(image,0,0,MagickTrue,exception);
539   if (sharp_image == (Image *) NULL)
540     return((Image *) NULL);
541   if (fabs(sigma) <= MagickEpsilon)
542     return(sharp_image);
543   if (SetImageStorageClass(sharp_image,DirectClass,exception) == MagickFalse)
544     {
545       sharp_image=DestroyImage(sharp_image);
546       return((Image *) NULL);
547     }
548   /*
549     Edge detect the image brighness channel, level, sharp, and level again.
550   */
551   edge_image=EdgeImage(image,radius,sigma,exception);
552   if (edge_image == (Image *) NULL)
553     {
554       sharp_image=DestroyImage(sharp_image);
555       return((Image *) NULL);
556     }
557   (void) AdaptiveLevelImage(edge_image,"20%,95%",exception);
558   gaussian_image=GaussianBlurImage(edge_image,radius,sigma,bias,exception);
559   if (gaussian_image != (Image *) NULL)
560     {
561       edge_image=DestroyImage(edge_image);
562       edge_image=gaussian_image;
563     }
564   (void) AdaptiveLevelImage(edge_image,"10%,95%",exception);
565   /*
566     Create a set of kernels from maximum (radius,sigma) to minimum.
567   */
568   width=GetOptimalKernelWidth2D(radius,sigma);
569   kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
570   if (kernel == (double **) NULL)
571     {
572       edge_image=DestroyImage(edge_image);
573       sharp_image=DestroyImage(sharp_image);
574       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
575     }
576   (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
577   for (i=0; i < (ssize_t) width; i+=2)
578   {
579     kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
580       sizeof(**kernel));
581     if (kernel[i] == (double *) NULL)
582       break;
583     normalize=0.0;
584     j=(ssize_t) (width-i)/2;
585     k=0;
586     for (v=(-j); v <= j; v++)
587     {
588       for (u=(-j); u <= j; u++)
589       {
590         kernel[i][k]=(double) (-exp(-((double) u*u+v*v)/(2.0*MagickSigma*
591           MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
592         normalize+=kernel[i][k];
593         k++;
594       }
595     }
596     if (fabs(normalize) <= MagickEpsilon)
597       normalize=1.0;
598     normalize=1.0/normalize;
599     for (k=0; k < (j*j); k++)
600       kernel[i][k]=normalize*kernel[i][k];
601   }
602   if (i < (ssize_t) width)
603     {
604       for (i-=2; i >= 0; i-=2)
605         kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
606       kernel=(double **) RelinquishMagickMemory(kernel);
607       edge_image=DestroyImage(edge_image);
608       sharp_image=DestroyImage(sharp_image);
609       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
610     }
611   /*
612     Adaptively sharpen image.
613   */
614   status=MagickTrue;
615   progress=0;
616   image_view=AcquireCacheView(image);
617   edge_view=AcquireCacheView(edge_image);
618   sharp_view=AcquireCacheView(sharp_image);
619 #if defined(MAGICKCORE_OPENMP_SUPPORT)
620   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
621 #endif
622   for (y=0; y < (ssize_t) sharp_image->rows; y++)
623   {
624     register const Quantum
625       *restrict r;
626
627     register Quantum
628       *restrict q;
629
630     register ssize_t
631       x;
632
633     if (status == MagickFalse)
634       continue;
635     r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
636     q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
637       exception);
638     if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
639       {
640         status=MagickFalse;
641         continue;
642       }
643     for (x=0; x < (ssize_t) sharp_image->columns; x++)
644     {
645       register const Quantum
646         *restrict p;
647
648       register ssize_t
649         i;
650
651       ssize_t
652         center,
653         j;
654
655       j=(ssize_t) ceil((double) width*QuantumScale*
656         GetPixelIntensity(edge_image,r)-0.5);
657       if (j < 0)
658         j=0;
659       else
660         if (j > (ssize_t) width)
661           j=(ssize_t) width;
662       if ((j & 0x01) != 0)
663         j--;
664       p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
665         (ssize_t) ((width-j)/2L),width-j,width-j,exception);
666       if (p == (const Quantum *) NULL)
667         break;
668       center=(ssize_t) GetPixelChannels(image)*(width-j)*
669         ((width-j)/2L)+GetPixelChannels(image)*((width-j)/2);
670       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
671       {
672         MagickRealType
673           alpha,
674           gamma,
675           pixel;
676
677         PixelChannel
678           channel;
679
680         PixelTrait
681           sharp_traits,
682           traits;
683
684         register const double
685           *restrict k;
686
687         register const Quantum
688           *restrict pixels;
689
690         register ssize_t
691           u;
692
693         ssize_t
694           v;
695
696         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
697         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
698         sharp_traits=GetPixelChannelMapTraits(sharp_image,channel);
699         if ((traits == UndefinedPixelTrait) ||
700             (sharp_traits == UndefinedPixelTrait))
701           continue;
702         if ((sharp_traits & CopyPixelTrait) != 0)
703           {
704             SetPixelChannel(sharp_image,channel,p[center+i],q);
705             continue;
706           }
707         k=kernel[j];
708         pixels=p;
709         pixel=bias;
710         gamma=0.0;
711         if ((sharp_traits & BlendPixelTrait) == 0)
712           {
713             /*
714               No alpha blending.
715             */
716             for (v=0; v < (ssize_t) (width-j); v++)
717             {
718               for (u=0; u < (ssize_t) (width-j); u++)
719               {
720                 pixel+=(*k)*pixels[i];
721                 gamma+=(*k);
722                 k++;
723                 pixels+=GetPixelChannels(image);
724               }
725             }
726             gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
727             SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
728             continue;
729           }
730         /*
731           Alpha blending.
732         */
733         for (v=0; v < (ssize_t) (width-j); v++)
734         {
735           for (u=0; u < (ssize_t) (width-j); u++)
736           {
737             alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
738             pixel+=(*k)*alpha*pixels[i];
739             gamma+=(*k)*alpha;
740             k++;
741             pixels+=GetPixelChannels(image);
742           }
743         }
744         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
745         SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
746       }
747       q+=GetPixelChannels(sharp_image);
748       r+=GetPixelChannels(edge_image);
749     }
750     if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
751       status=MagickFalse;
752     if (image->progress_monitor != (MagickProgressMonitor) NULL)
753       {
754         MagickBooleanType
755           proceed;
756
757 #if defined(MAGICKCORE_OPENMP_SUPPORT)
758   #pragma omp critical (MagickCore_AdaptiveSharpenImage)
759 #endif
760         proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
761           image->rows);
762         if (proceed == MagickFalse)
763           status=MagickFalse;
764       }
765   }
766   sharp_image->type=image->type;
767   sharp_view=DestroyCacheView(sharp_view);
768   edge_view=DestroyCacheView(edge_view);
769   image_view=DestroyCacheView(image_view);
770   edge_image=DestroyImage(edge_image);
771   for (i=0; i < (ssize_t) width;  i+=2)
772     kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
773   kernel=(double **) RelinquishMagickMemory(kernel);
774   if (status == MagickFalse)
775     sharp_image=DestroyImage(sharp_image);
776   return(sharp_image);
777 }
778 \f
779 /*
780 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
781 %                                                                             %
782 %                                                                             %
783 %                                                                             %
784 %     B l u r I m a g e                                                       %
785 %                                                                             %
786 %                                                                             %
787 %                                                                             %
788 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
789 %
790 %  BlurImage() blurs an image.  We convolve the image with a Gaussian operator
791 %  of the given radius and standard deviation (sigma).  For reasonable results,
792 %  the radius should be larger than sigma.  Use a radius of 0 and BlurImage()
793 %  selects a suitable radius for you.
794 %
795 %  BlurImage() differs from GaussianBlurImage() in that it uses a separable
796 %  kernel which is faster but mathematically equivalent to the non-separable
797 %  kernel.
798 %
799 %  The format of the BlurImage method is:
800 %
801 %      Image *BlurImage(const Image *image,const double radius,
802 %        const double sigma,const double bias,ExceptionInfo *exception)
803 %
804 %  A description of each parameter follows:
805 %
806 %    o image: the image.
807 %
808 %    o radius: the radius of the Gaussian, in pixels, not counting the center
809 %      pixel.
810 %
811 %    o sigma: the standard deviation of the Gaussian, in pixels.
812 %
813 %    o bias: the bias.
814 %
815 %    o exception: return any errors or warnings in this structure.
816 %
817 */
818
819 static double *GetBlurKernel(const size_t width,const double sigma)
820 {
821   double
822     *kernel,
823     normalize;
824
825   register ssize_t
826     i;
827
828   ssize_t
829     j,
830     k;
831
832   /*
833     Generate a 1-D convolution kernel.
834   */
835   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
836   kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
837   if (kernel == (double *) NULL)
838     return(0);
839   normalize=0.0;
840   j=(ssize_t) width/2;
841   i=0;
842   for (k=(-j); k <= j; k++)
843   {
844     kernel[i]=(double) (exp(-((double) k*k)/(2.0*MagickSigma*MagickSigma))/
845       (MagickSQ2PI*MagickSigma));
846     normalize+=kernel[i];
847     i++;
848   }
849   for (i=0; i < (ssize_t) width; i++)
850     kernel[i]/=normalize;
851   return(kernel);
852 }
853
854 MagickExport Image *BlurImage(const Image *image,const double radius,
855   const double sigma,const double bias,ExceptionInfo *exception)
856 {
857 #define BlurImageTag  "Blur/Image"
858
859   CacheView
860     *blur_view,
861     *image_view;
862
863   double
864     *kernel;
865
866   Image
867     *blur_image;
868
869   MagickBooleanType
870     status;
871
872   MagickOffsetType
873     progress;
874
875   register ssize_t
876     i;
877
878   size_t
879     width;
880
881   ssize_t
882     center,
883     x,
884     y;
885
886   /*
887     Initialize blur image attributes.
888   */
889   assert(image != (Image *) NULL);
890   assert(image->signature == MagickSignature);
891   if (image->debug != MagickFalse)
892     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
893   assert(exception != (ExceptionInfo *) NULL);
894   assert(exception->signature == MagickSignature);
895   blur_image=CloneImage(image,0,0,MagickTrue,exception);
896   if (blur_image == (Image *) NULL)
897     return((Image *) NULL);
898   if (fabs(sigma) <= MagickEpsilon)
899     return(blur_image);
900   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
901     {
902       blur_image=DestroyImage(blur_image);
903       return((Image *) NULL);
904     }
905   width=GetOptimalKernelWidth1D(radius,sigma);
906   kernel=GetBlurKernel(width,sigma);
907   if (kernel == (double *) NULL)
908     {
909       blur_image=DestroyImage(blur_image);
910       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
911     }
912   if (image->debug != MagickFalse)
913     {
914       char
915         format[MaxTextExtent],
916         *message;
917
918       register const double
919         *k;
920
921       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
922         "  BlurImage with %.20g kernel:",(double) width);
923       message=AcquireString("");
924       k=kernel;
925       for (i=0; i < (ssize_t) width; i++)
926       {
927         *message='\0';
928         (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) i);
929         (void) ConcatenateString(&message,format);
930         (void) FormatLocaleString(format,MaxTextExtent,"%g ",*k++);
931         (void) ConcatenateString(&message,format);
932         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
933       }
934       message=DestroyString(message);
935     }
936   /*
937     Blur rows.
938   */
939   status=MagickTrue;
940   progress=0;
941   center=(ssize_t) GetPixelChannels(image)*(width/2L);
942   image_view=AcquireCacheView(image);
943   blur_view=AcquireCacheView(blur_image);
944 #if defined(MAGICKCORE_OPENMP_SUPPORT)
945   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
946 #endif
947   for (y=0; y < (ssize_t) image->rows; y++)
948   {
949     register const Quantum
950       *restrict p;
951
952     register Quantum
953       *restrict q;
954
955     register ssize_t
956       x;
957
958     if (status == MagickFalse)
959       continue;
960     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y,
961       image->columns+width,1,exception);
962     q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
963       exception);
964     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
965       {
966         status=MagickFalse;
967         continue;
968       }
969     for (x=0; x < (ssize_t) image->columns; x++)
970     {
971       register ssize_t
972         i;
973
974       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
975       {
976         MagickRealType
977           alpha,
978           gamma,
979           pixel;
980
981         PixelChannel
982           channel;
983
984         PixelTrait
985           blur_traits,
986           traits;
987
988         register const double
989           *restrict k;
990
991         register const Quantum
992           *restrict pixels;
993
994         register ssize_t
995           u;
996
997         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
998         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
999         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
1000         if ((traits == UndefinedPixelTrait) ||
1001             (blur_traits == UndefinedPixelTrait))
1002           continue;
1003         if ((blur_traits & CopyPixelTrait) != 0)
1004           {
1005             SetPixelChannel(blur_image,channel,p[center+i],q);
1006             continue;
1007           }
1008         k=kernel;
1009         pixels=p;
1010         pixel=0.0;
1011         if ((blur_traits & BlendPixelTrait) == 0)
1012           {
1013             /*
1014               No alpha blending.
1015             */
1016             for (u=0; u < (ssize_t) width; u++)
1017             {
1018               pixel+=(*k)*pixels[i];
1019               k++;
1020               pixels+=GetPixelChannels(image);
1021             }
1022             SetPixelChannel(blur_image,channel,ClampToQuantum(pixel),q);
1023             continue;
1024           }
1025         /*
1026           Alpha blending.
1027         */
1028         gamma=0.0;
1029         for (u=0; u < (ssize_t) width; u++)
1030         {
1031           alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
1032           pixel+=(*k)*alpha*pixels[i];
1033           gamma+=(*k)*alpha;
1034           k++;
1035           pixels+=GetPixelChannels(image);
1036         }
1037         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1038         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
1039       }
1040       p+=GetPixelChannels(image);
1041       q+=GetPixelChannels(blur_image);
1042     }
1043     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1044       status=MagickFalse;
1045     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1046       {
1047         MagickBooleanType
1048           proceed;
1049
1050 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1051   #pragma omp critical (MagickCore_BlurImage)
1052 #endif
1053         proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1054           blur_image->columns);
1055         if (proceed == MagickFalse)
1056           status=MagickFalse;
1057       }
1058   }
1059   blur_view=DestroyCacheView(blur_view);
1060   image_view=DestroyCacheView(image_view);
1061   /*
1062     Blur columns.
1063   */
1064   image_view=AcquireCacheView(blur_image);
1065   blur_view=AcquireCacheView(blur_image);
1066 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1067   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1068 #endif
1069   for (x=0; x < (ssize_t) image->columns; x++)
1070   {
1071     register const Quantum
1072       *restrict p;
1073
1074     register Quantum
1075       *restrict q;
1076
1077     register ssize_t
1078       y;
1079
1080     if (status == MagickFalse)
1081       continue;
1082     p=GetCacheViewVirtualPixels(image_view,x,-((ssize_t) width/2L),1,
1083       image->rows+width,exception);
1084     q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
1085     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1086       {
1087         status=MagickFalse;
1088         continue;
1089       }
1090     for (y=0; y < (ssize_t) image->rows; y++)
1091     {
1092       register ssize_t
1093         i;
1094
1095       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1096       {
1097         MagickRealType
1098           alpha,
1099           gamma,
1100           pixel;
1101
1102         PixelChannel
1103           channel;
1104
1105         PixelTrait
1106           blur_traits,
1107           traits;
1108
1109         register const double
1110           *restrict k;
1111
1112         register const Quantum
1113           *restrict pixels;
1114
1115         register ssize_t
1116           u;
1117
1118         traits=GetPixelChannelMapTraits(blur_image,(PixelChannel) i);
1119         channel=GetPixelChannelMapChannel(blur_image,(PixelChannel) i);
1120         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
1121         if ((traits == UndefinedPixelTrait) ||
1122             (blur_traits == UndefinedPixelTrait))
1123           continue;
1124         if ((blur_traits & CopyPixelTrait) != 0)
1125           {
1126             SetPixelChannel(blur_image,channel,p[center+i],q);
1127             continue;
1128           }
1129         k=kernel;
1130         pixels=p;
1131         pixel=0.0;
1132         if ((blur_traits & BlendPixelTrait) == 0)
1133           {
1134             /*
1135               No alpha blending.
1136             */
1137             for (u=0; u < (ssize_t) width; u++)
1138             {
1139               pixel+=(*k)*pixels[i];
1140               k++;
1141               pixels+=GetPixelChannels(blur_image);
1142             }
1143             SetPixelChannel(blur_image,channel,ClampToQuantum(pixel),q);
1144             continue;
1145           }
1146         /*
1147           Alpha blending.
1148         */
1149         gamma=0.0;
1150         for (u=0; u < (ssize_t) width; u++)
1151         {
1152           alpha=(MagickRealType) (QuantumScale*
1153             GetPixelAlpha(blur_image,pixels));
1154           pixel+=(*k)*alpha*pixels[i];
1155           gamma+=(*k)*alpha;
1156           k++;
1157           pixels+=GetPixelChannels(blur_image);
1158         }
1159         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1160         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
1161       }
1162       p+=GetPixelChannels(blur_image);
1163       q+=GetPixelChannels(blur_image);
1164     }
1165     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1166       status=MagickFalse;
1167     if (blur_image->progress_monitor != (MagickProgressMonitor) NULL)
1168       {
1169         MagickBooleanType
1170           proceed;
1171
1172 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1173   #pragma omp critical (MagickCore_BlurImage)
1174 #endif
1175         proceed=SetImageProgress(blur_image,BlurImageTag,progress++,
1176           blur_image->rows+blur_image->columns);
1177         if (proceed == MagickFalse)
1178           status=MagickFalse;
1179       }
1180   }
1181   blur_view=DestroyCacheView(blur_view);
1182   image_view=DestroyCacheView(image_view);
1183   kernel=(double *) RelinquishMagickMemory(kernel);
1184   if (status == MagickFalse)
1185     blur_image=DestroyImage(blur_image);
1186   blur_image->type=image->type;
1187   return(blur_image);
1188 }
1189 \f
1190 /*
1191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1192 %                                                                             %
1193 %                                                                             %
1194 %                                                                             %
1195 %     C o n v o l v e I m a g e                                               %
1196 %                                                                             %
1197 %                                                                             %
1198 %                                                                             %
1199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1200 %
1201 %  ConvolveImage() applies a custom convolution kernel to the image.
1202 %
1203 %  The format of the ConvolveImage method is:
1204 %
1205 %      Image *ConvolveImage(const Image *image,const KernelInfo *kernel,
1206 %        ExceptionInfo *exception)
1207 %
1208 %  A description of each parameter follows:
1209 %
1210 %    o image: the image.
1211 %
1212 %    o kernel: the filtering kernel.
1213 %
1214 %    o exception: return any errors or warnings in this structure.
1215 %
1216 */
1217 MagickExport Image *ConvolveImage(const Image *image,
1218   const KernelInfo *kernel_info,ExceptionInfo *exception)
1219 {
1220 #define ConvolveImageTag  "Convolve/Image"
1221
1222   CacheView
1223     *convolve_view,
1224     *image_view;
1225
1226   Image
1227     *convolve_image;
1228
1229   MagickBooleanType
1230     status;
1231
1232   MagickOffsetType
1233     progress;
1234
1235   ssize_t
1236     center,
1237     y;
1238
1239   /*
1240     Initialize convolve image attributes.
1241   */
1242   assert(image != (Image *) NULL);
1243   assert(image->signature == MagickSignature);
1244   if (image->debug != MagickFalse)
1245     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1246   assert(exception != (ExceptionInfo *) NULL);
1247   assert(exception->signature == MagickSignature);
1248   if ((kernel_info->width % 2) == 0)
1249     ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
1250   convolve_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1251     exception);
1252   if (convolve_image == (Image *) NULL)
1253     return((Image *) NULL);
1254   if (SetImageStorageClass(convolve_image,DirectClass,exception) == MagickFalse)
1255     {
1256       convolve_image=DestroyImage(convolve_image);
1257       return((Image *) NULL);
1258     }
1259   if (image->debug != MagickFalse)
1260     {
1261       char
1262         format[MaxTextExtent],
1263         *message;
1264
1265       register const double
1266         *k;
1267
1268       register ssize_t
1269         u;
1270
1271       ssize_t
1272         v;
1273
1274       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1275         "  ConvolveImage with %.20gx%.20g kernel:",(double) kernel_info->width,
1276         (double) kernel_info->height);
1277       message=AcquireString("");
1278       k=kernel_info->values;
1279       for (v=0; v < (ssize_t) kernel_info->width; v++)
1280       {
1281         *message='\0';
1282         (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
1283         (void) ConcatenateString(&message,format);
1284         for (u=0; u < (ssize_t) kernel_info->height; u++)
1285         {
1286           (void) FormatLocaleString(format,MaxTextExtent,"%g ",*k++);
1287           (void) ConcatenateString(&message,format);
1288         }
1289         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
1290       }
1291       message=DestroyString(message);
1292     }
1293   status=AccelerateConvolveImage(image,kernel_info,convolve_image,exception);
1294   if (status == MagickTrue)
1295     return(convolve_image);
1296   /*
1297     Convolve image.
1298   */
1299   center=(ssize_t) GetPixelChannels(image)*(image->columns+kernel_info->width)*
1300     (kernel_info->height/2L)+GetPixelChannels(image)*(kernel_info->width/2L);
1301   status=MagickTrue;
1302   progress=0;
1303   image_view=AcquireCacheView(image);
1304   convolve_view=AcquireCacheView(convolve_image);
1305 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1306   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1307 #endif
1308   for (y=0; y < (ssize_t) image->rows; y++)
1309   {
1310     register const Quantum
1311       *restrict p;
1312
1313     register Quantum
1314       *restrict q;
1315
1316     register ssize_t
1317       x;
1318
1319     if (status == MagickFalse)
1320       continue;
1321     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel_info->width/2L),y-
1322       (ssize_t) (kernel_info->height/2L),image->columns+kernel_info->width,
1323       kernel_info->height,exception);
1324     q=QueueCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
1325       exception);
1326     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1327       {
1328         status=MagickFalse;
1329         continue;
1330       }
1331     for (x=0; x < (ssize_t) image->columns; x++)
1332     {
1333       register ssize_t
1334         i;
1335
1336       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1337       {
1338         MagickRealType
1339           alpha,
1340           gamma,
1341           pixel;
1342
1343         PixelChannel
1344           channel;
1345
1346         PixelTrait
1347           convolve_traits,
1348           traits;
1349
1350         register const double
1351           *restrict k;
1352
1353         register const Quantum
1354           *restrict pixels;
1355
1356         register ssize_t
1357           u;
1358
1359         ssize_t
1360           v;
1361
1362         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1363         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
1364         convolve_traits=GetPixelChannelMapTraits(convolve_image,channel);
1365         if ((traits == UndefinedPixelTrait) ||
1366             (convolve_traits == UndefinedPixelTrait))
1367           continue;
1368         if ((convolve_traits & CopyPixelTrait) != 0)
1369           {
1370             SetPixelChannel(convolve_image,channel,p[center+i],q);
1371             continue;
1372           }
1373         k=kernel_info->values;
1374         pixels=p;
1375         pixel=kernel_info->bias;
1376         if ((convolve_traits & BlendPixelTrait) == 0)
1377           {
1378             /*
1379               No alpha blending.
1380             */
1381             for (v=0; v < (ssize_t) kernel_info->height; v++)
1382             {
1383               for (u=0; u < (ssize_t) kernel_info->width; u++)
1384               {
1385                 pixel+=(*k)*pixels[i];
1386                 k++;
1387                 pixels+=GetPixelChannels(image);
1388               }
1389               pixels+=image->columns*GetPixelChannels(image);
1390             }
1391             SetPixelChannel(convolve_image,channel,ClampToQuantum(pixel),q);
1392             continue;
1393           }
1394         /*
1395           Alpha blending.
1396         */
1397         gamma=0.0;
1398         for (v=0; v < (ssize_t) kernel_info->height; v++)
1399         {
1400           for (u=0; u < (ssize_t) kernel_info->width; u++)
1401           {
1402             alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
1403             pixel+=(*k)*alpha*pixels[i];
1404             gamma+=(*k)*alpha;
1405             k++;
1406             pixels+=GetPixelChannels(image);
1407           }
1408           pixels+=image->columns*GetPixelChannels(image);
1409         }
1410         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1411         SetPixelChannel(convolve_image,channel,ClampToQuantum(gamma*pixel),q);
1412       }
1413       p+=GetPixelChannels(image);
1414       q+=GetPixelChannels(convolve_image);
1415     }
1416     if (SyncCacheViewAuthenticPixels(convolve_view,exception) == MagickFalse)
1417       status=MagickFalse;
1418     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1419       {
1420         MagickBooleanType
1421           proceed;
1422
1423 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1424   #pragma omp critical (MagickCore_ConvolveImage)
1425 #endif
1426         proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1427         if (proceed == MagickFalse)
1428           status=MagickFalse;
1429       }
1430   }
1431   convolve_image->type=image->type;
1432   convolve_view=DestroyCacheView(convolve_view);
1433   image_view=DestroyCacheView(image_view);
1434   if (status == MagickFalse)
1435     convolve_image=DestroyImage(convolve_image);
1436   return(convolve_image);
1437 }
1438 \f
1439 /*
1440 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1441 %                                                                             %
1442 %                                                                             %
1443 %                                                                             %
1444 %     D e s p e c k l e I m a g e                                             %
1445 %                                                                             %
1446 %                                                                             %
1447 %                                                                             %
1448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1449 %
1450 %  DespeckleImage() reduces the speckle noise in an image while perserving the
1451 %  edges of the original image.
1452 %
1453 %  The format of the DespeckleImage method is:
1454 %
1455 %      Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1456 %
1457 %  A description of each parameter follows:
1458 %
1459 %    o image: the image.
1460 %
1461 %    o exception: return any errors or warnings in this structure.
1462 %
1463 */
1464
1465 static void Hull(const ssize_t x_offset,const ssize_t y_offset,
1466   const size_t columns,const size_t rows,Quantum *f,Quantum *g,
1467   const int polarity)
1468 {
1469   MagickRealType
1470     v;
1471
1472   register Quantum
1473     *p,
1474     *q,
1475     *r,
1476     *s;
1477
1478   register ssize_t
1479     x;
1480
1481   ssize_t
1482     y;
1483
1484   assert(f != (Quantum *) NULL);
1485   assert(g != (Quantum *) NULL);
1486   p=f+(columns+2);
1487   q=g+(columns+2);
1488   r=p+(y_offset*((ssize_t) columns+2)+x_offset);
1489   for (y=0; y < (ssize_t) rows; y++)
1490   {
1491     p++;
1492     q++;
1493     r++;
1494     if (polarity > 0)
1495       for (x=(ssize_t) columns; x != 0; x--)
1496       {
1497         v=(MagickRealType) (*p);
1498         if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1499           v+=ScaleCharToQuantum(1);
1500         *q=(Quantum) v;
1501         p++;
1502         q++;
1503         r++;
1504       }
1505     else
1506       for (x=(ssize_t) columns; x != 0; x--)
1507       {
1508         v=(MagickRealType) (*p);
1509         if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
1510           v-=(ssize_t) ScaleCharToQuantum(1);
1511         *q=(Quantum) v;
1512         p++;
1513         q++;
1514         r++;
1515       }
1516     p++;
1517     q++;
1518     r++;
1519   }
1520   p=f+(columns+2);
1521   q=g+(columns+2);
1522   r=q+(y_offset*((ssize_t) columns+2)+x_offset);
1523   s=q-(y_offset*((ssize_t) columns+2)+x_offset);
1524   for (y=0; y < (ssize_t) rows; y++)
1525   {
1526     p++;
1527     q++;
1528     r++;
1529     s++;
1530     if (polarity > 0)
1531       for (x=(ssize_t) columns; x != 0; x--)
1532       {
1533         v=(MagickRealType) (*q);
1534         if (((MagickRealType) *s >=
1535              (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1536             ((MagickRealType) *r > v))
1537           v+=ScaleCharToQuantum(1);
1538         *p=(Quantum) v;
1539         p++;
1540         q++;
1541         r++;
1542         s++;
1543       }
1544     else
1545       for (x=(ssize_t) columns; x != 0; x--)
1546       {
1547         v=(MagickRealType) (*q);
1548         if (((MagickRealType) *s <=
1549              (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1550             ((MagickRealType) *r < v))
1551           v-=(MagickRealType) ScaleCharToQuantum(1);
1552         *p=(Quantum) v;
1553         p++;
1554         q++;
1555         r++;
1556         s++;
1557       }
1558     p++;
1559     q++;
1560     r++;
1561     s++;
1562   }
1563 }
1564
1565 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1566 {
1567 #define DespeckleImageTag  "Despeckle/Image"
1568
1569   CacheView
1570     *despeckle_view,
1571     *image_view;
1572
1573   Image
1574     *despeckle_image;
1575
1576   MagickBooleanType
1577     status;
1578
1579   Quantum
1580     *restrict buffers,
1581     *restrict pixels;
1582
1583   register ssize_t
1584     i;
1585
1586   size_t
1587     length;
1588
1589   static const ssize_t
1590     X[4] = {0, 1, 1,-1},
1591     Y[4] = {1, 0, 1, 1};
1592
1593   /*
1594     Allocate despeckled image.
1595   */
1596   assert(image != (const Image *) NULL);
1597   assert(image->signature == MagickSignature);
1598   if (image->debug != MagickFalse)
1599     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1600   assert(exception != (ExceptionInfo *) NULL);
1601   assert(exception->signature == MagickSignature);
1602   despeckle_image=CloneImage(image,0,0,MagickTrue,exception);
1603   if (despeckle_image == (Image *) NULL)
1604     return((Image *) NULL);
1605   status=SetImageStorageClass(despeckle_image,DirectClass,exception);
1606   if (status == MagickFalse)
1607     {
1608       despeckle_image=DestroyImage(despeckle_image);
1609       return((Image *) NULL);
1610     }
1611   /*
1612     Allocate image buffers.
1613   */
1614   length=(size_t) ((image->columns+2)*(image->rows+2));
1615   pixels=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1616   buffers=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1617   if ((pixels == (Quantum *) NULL) || (buffers == (Quantum *) NULL))
1618     {
1619       if (buffers != (Quantum *) NULL)
1620         buffers=(Quantum *) RelinquishMagickMemory(buffers);
1621       if (pixels != (Quantum *) NULL)
1622         pixels=(Quantum *) RelinquishMagickMemory(pixels);
1623       despeckle_image=DestroyImage(despeckle_image);
1624       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1625     }
1626   /*
1627     Reduce speckle in the image.
1628   */
1629   status=MagickTrue;
1630   image_view=AcquireCacheView(image);
1631   despeckle_view=AcquireCacheView(despeckle_image);
1632   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1633   {
1634     PixelChannel
1635        channel;
1636
1637     PixelTrait
1638       despeckle_traits,
1639       traits;
1640
1641     register Quantum
1642       *buffer,
1643       *pixel;
1644
1645     register ssize_t
1646       k,
1647       x;
1648
1649     ssize_t
1650       j,
1651       y;
1652
1653     if (status == MagickFalse)
1654       continue;
1655     traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1656     channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
1657     despeckle_traits=GetPixelChannelMapTraits(despeckle_image,channel);
1658     if ((traits == UndefinedPixelTrait) ||
1659         (despeckle_traits == UndefinedPixelTrait))
1660       continue;
1661     if ((despeckle_traits & CopyPixelTrait) != 0)
1662       continue;
1663     pixel=pixels;
1664     (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
1665     buffer=buffers;
1666     j=(ssize_t) image->columns+2;
1667     for (y=0; y < (ssize_t) image->rows; y++)
1668     {
1669       register const Quantum
1670         *restrict p;
1671
1672       p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1673       if (p == (const Quantum *) NULL)
1674         {
1675           status=MagickFalse;
1676           continue;
1677         }
1678       j++;
1679       for (x=0; x < (ssize_t) image->columns; x++)
1680       {
1681         pixel[j++]=p[i];
1682         p+=GetPixelChannels(image);
1683       }
1684       j++;
1685     }
1686     (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1687     for (k=0; k < 4; k++)
1688     {
1689       Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,1);
1690       Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,1);
1691       Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,-1);
1692       Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,-1);
1693     }
1694     j=(ssize_t) image->columns+2;
1695     for (y=0; y < (ssize_t) image->rows; y++)
1696     {
1697       MagickBooleanType
1698         sync;
1699
1700       register Quantum
1701         *restrict q;
1702
1703       q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1704         1,exception);
1705       if (q == (Quantum *) NULL)
1706         {
1707           status=MagickFalse;
1708           continue;
1709         }
1710       j++;
1711       for (x=0; x < (ssize_t) image->columns; x++)
1712       {
1713         SetPixelChannel(despeckle_image,channel,pixel[j++],q);
1714         q+=GetPixelChannels(despeckle_image);
1715       }
1716       sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1717       if (sync == MagickFalse)
1718         status=MagickFalse;
1719       j++;
1720     }
1721     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1722       {
1723         MagickBooleanType
1724           proceed;
1725
1726         proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1727           GetPixelChannels(image));
1728         if (proceed == MagickFalse)
1729           status=MagickFalse;
1730       }
1731   }
1732   despeckle_view=DestroyCacheView(despeckle_view);
1733   image_view=DestroyCacheView(image_view);
1734   buffers=(Quantum *) RelinquishMagickMemory(buffers);
1735   pixels=(Quantum *) RelinquishMagickMemory(pixels);
1736   despeckle_image->type=image->type;
1737   if (status == MagickFalse)
1738     despeckle_image=DestroyImage(despeckle_image);
1739   return(despeckle_image);
1740 }
1741 \f
1742 /*
1743 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1744 %                                                                             %
1745 %                                                                             %
1746 %                                                                             %
1747 %     E d g e I m a g e                                                       %
1748 %                                                                             %
1749 %                                                                             %
1750 %                                                                             %
1751 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1752 %
1753 %  EdgeImage() finds edges in an image.  Radius defines the radius of the
1754 %  convolution filter.  Use a radius of 0 and EdgeImage() selects a suitable
1755 %  radius for you.
1756 %
1757 %  The format of the EdgeImage method is:
1758 %
1759 %      Image *EdgeImage(const Image *image,const double radius,
1760 %        const double sigma,ExceptionInfo *exception)
1761 %
1762 %  A description of each parameter follows:
1763 %
1764 %    o image: the image.
1765 %
1766 %    o radius: the radius of the pixel neighborhood.
1767 %
1768 %    o sigma: the standard deviation of the Gaussian, in pixels.
1769 %
1770 %    o exception: return any errors or warnings in this structure.
1771 %
1772 */
1773 MagickExport Image *EdgeImage(const Image *image,const double radius,
1774   const double sigma,ExceptionInfo *exception)
1775 {
1776   Image
1777     *edge_image;
1778
1779   KernelInfo
1780     *kernel_info;
1781
1782   register ssize_t
1783     i;
1784
1785   size_t
1786     width;
1787
1788   ssize_t
1789     j,
1790     u,
1791     v;
1792
1793   assert(image != (const Image *) NULL);
1794   assert(image->signature == MagickSignature);
1795   if (image->debug != MagickFalse)
1796     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1797   assert(exception != (ExceptionInfo *) NULL);
1798   assert(exception->signature == MagickSignature);
1799   width=GetOptimalKernelWidth2D(radius,sigma);
1800   kernel_info=AcquireKernelInfo((const char *) NULL);
1801   if (kernel_info == (KernelInfo *) NULL)
1802     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1803   kernel_info->width=width;
1804   kernel_info->height=width;
1805   kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1806     kernel_info->width*sizeof(*kernel_info->values));
1807   if (kernel_info->values == (double *) NULL)
1808     {
1809       kernel_info=DestroyKernelInfo(kernel_info);
1810       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1811     }
1812   j=(ssize_t) kernel_info->width/2;
1813   i=0;
1814   for (v=(-j); v <= j; v++)
1815   {
1816     for (u=(-j); u <= j; u++)
1817     {
1818       kernel_info->values[i]=(-1.0);
1819       i++;
1820     }
1821   }
1822   kernel_info->values[i/2]=(double) (width*width-1.0);
1823   kernel_info->bias=image->bias;
1824   edge_image=ConvolveImage(image,kernel_info,exception);
1825   kernel_info=DestroyKernelInfo(kernel_info);
1826   return(edge_image);
1827 }
1828 \f
1829 /*
1830 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1831 %                                                                             %
1832 %                                                                             %
1833 %                                                                             %
1834 %     E m b o s s I m a g e                                                   %
1835 %                                                                             %
1836 %                                                                             %
1837 %                                                                             %
1838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1839 %
1840 %  EmbossImage() returns a grayscale image with a three-dimensional effect.
1841 %  We convolve the image with a Gaussian operator of the given radius and
1842 %  standard deviation (sigma).  For reasonable results, radius should be
1843 %  larger than sigma.  Use a radius of 0 and Emboss() selects a suitable
1844 %  radius for you.
1845 %
1846 %  The format of the EmbossImage method is:
1847 %
1848 %      Image *EmbossImage(const Image *image,const double radius,
1849 %        const double sigma,ExceptionInfo *exception)
1850 %
1851 %  A description of each parameter follows:
1852 %
1853 %    o image: the image.
1854 %
1855 %    o radius: the radius of the pixel neighborhood.
1856 %
1857 %    o sigma: the standard deviation of the Gaussian, in pixels.
1858 %
1859 %    o exception: return any errors or warnings in this structure.
1860 %
1861 */
1862 MagickExport Image *EmbossImage(const Image *image,const double radius,
1863   const double sigma,ExceptionInfo *exception)
1864 {
1865   Image
1866     *emboss_image;
1867
1868   KernelInfo
1869     *kernel_info;
1870
1871   register ssize_t
1872     i;
1873
1874   size_t
1875     width;
1876
1877   ssize_t
1878     j,
1879     k,
1880     u,
1881     v;
1882
1883   assert(image != (const Image *) NULL);
1884   assert(image->signature == MagickSignature);
1885   if (image->debug != MagickFalse)
1886     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1887   assert(exception != (ExceptionInfo *) NULL);
1888   assert(exception->signature == MagickSignature);
1889   width=GetOptimalKernelWidth2D(radius,sigma);
1890   kernel_info=AcquireKernelInfo((const char *) NULL);
1891   if (kernel_info == (KernelInfo *) NULL)
1892     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1893   kernel_info->width=width;
1894   kernel_info->height=width;
1895   kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1896     kernel_info->width*sizeof(*kernel_info->values));
1897   if (kernel_info->values == (double *) NULL)
1898     {
1899       kernel_info=DestroyKernelInfo(kernel_info);
1900       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1901     }
1902   j=(ssize_t) kernel_info->width/2;
1903   k=j;
1904   i=0;
1905   for (v=(-j); v <= j; v++)
1906   {
1907     for (u=(-j); u <= j; u++)
1908     {
1909       kernel_info->values[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*
1910         exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
1911         (2.0*MagickPI*MagickSigma*MagickSigma));
1912       if (u != k)
1913         kernel_info->values[i]=0.0;
1914       i++;
1915     }
1916     k--;
1917   }
1918   kernel_info->bias=image->bias;
1919   emboss_image=ConvolveImage(image,kernel_info,exception);
1920   kernel_info=DestroyKernelInfo(kernel_info);
1921   if (emboss_image != (Image *) NULL)
1922     (void) EqualizeImage(emboss_image,exception);
1923   return(emboss_image);
1924 }
1925 \f
1926 /*
1927 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1928 %                                                                             %
1929 %                                                                             %
1930 %                                                                             %
1931 %     G a u s s i a n B l u r I m a g e                                       %
1932 %                                                                             %
1933 %                                                                             %
1934 %                                                                             %
1935 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1936 %
1937 %  GaussianBlurImage() blurs an image.  We convolve the image with a
1938 %  Gaussian operator of the given radius and standard deviation (sigma).
1939 %  For reasonable results, the radius should be larger than sigma.  Use a
1940 %  radius of 0 and GaussianBlurImage() selects a suitable radius for you
1941 %
1942 %  The format of the GaussianBlurImage method is:
1943 %
1944 %      Image *GaussianBlurImage(const Image *image,onst double radius,
1945 %        const double sigma,const double bias,ExceptionInfo *exception)
1946 %
1947 %  A description of each parameter follows:
1948 %
1949 %    o image: the image.
1950 %
1951 %    o radius: the radius of the Gaussian, in pixels, not counting the center
1952 %      pixel.
1953 %
1954 %    o sigma: the standard deviation of the Gaussian, in pixels.
1955 %
1956 %    o bias: the bias.
1957 %
1958 %    o exception: return any errors or warnings in this structure.
1959 %
1960 */
1961 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
1962   const double sigma,const double bias,ExceptionInfo *exception)
1963 {
1964   Image
1965     *blur_image;
1966
1967   KernelInfo
1968     *kernel_info;
1969
1970   register ssize_t
1971     i;
1972
1973   size_t
1974     width;
1975
1976   ssize_t
1977     j,
1978     u,
1979     v;
1980
1981   assert(image != (const Image *) NULL);
1982   assert(image->signature == MagickSignature);
1983   if (image->debug != MagickFalse)
1984     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1985   assert(exception != (ExceptionInfo *) NULL);
1986   assert(exception->signature == MagickSignature);
1987   width=GetOptimalKernelWidth2D(radius,sigma);
1988   kernel_info=AcquireKernelInfo((const char *) NULL);
1989   if (kernel_info == (KernelInfo *) NULL)
1990     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1991   (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
1992   kernel_info->width=width;
1993   kernel_info->height=width;
1994   kernel_info->bias=bias;
1995   kernel_info->signature=MagickSignature;
1996   kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1997     kernel_info->width*sizeof(*kernel_info->values));
1998   if (kernel_info->values == (double *) NULL)
1999     {
2000       kernel_info=DestroyKernelInfo(kernel_info);
2001       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2002     }
2003   j=(ssize_t) kernel_info->width/2;
2004   i=0;
2005   for (v=(-j); v <= j; v++)
2006   {
2007     for (u=(-j); u <= j; u++)
2008     {
2009       kernel_info->values[i]=(double) (exp(-((double) u*u+v*v)/(2.0*
2010         MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
2011       i++;
2012     }
2013   }
2014   blur_image=ConvolveImage(image,kernel_info,exception);
2015   kernel_info=DestroyKernelInfo(kernel_info);
2016   return(blur_image);
2017 }
2018 \f
2019 /*
2020 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2021 %                                                                             %
2022 %                                                                             %
2023 %                                                                             %
2024 %     M o t i o n B l u r I m a g e                                           %
2025 %                                                                             %
2026 %                                                                             %
2027 %                                                                             %
2028 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2029 %
2030 %  MotionBlurImage() simulates motion blur.  We convolve the image with a
2031 %  Gaussian operator of the given radius and standard deviation (sigma).
2032 %  For reasonable results, radius should be larger than sigma.  Use a
2033 %  radius of 0 and MotionBlurImage() selects a suitable radius for you.
2034 %  Angle gives the angle of the blurring motion.
2035 %
2036 %  Andrew Protano contributed this effect.
2037 %
2038 %  The format of the MotionBlurImage method is:
2039 %
2040 %    Image *MotionBlurImage(const Image *image,const double radius,
2041 %      const double sigma,const double angle,const double bias,
2042 %      ExceptionInfo *exception)
2043 %
2044 %  A description of each parameter follows:
2045 %
2046 %    o image: the image.
2047 %
2048 %    o radius: the radius of the Gaussian, in pixels, not counting
2049 %      the center pixel.
2050 %
2051 %    o sigma: the standard deviation of the Gaussian, in pixels.
2052 %
2053 %    o angle: Apply the effect along this angle.
2054 %
2055 %    o bias: the bias.
2056 %
2057 %    o exception: return any errors or warnings in this structure.
2058 %
2059 */
2060
2061 static double *GetMotionBlurKernel(const size_t width,const double sigma)
2062 {
2063   double
2064     *kernel,
2065     normalize;
2066
2067   register ssize_t
2068     i;
2069
2070   /*
2071    Generate a 1-D convolution kernel.
2072   */
2073   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2074   kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
2075   if (kernel == (double *) NULL)
2076     return(kernel);
2077   normalize=0.0;
2078   for (i=0; i < (ssize_t) width; i++)
2079   {
2080     kernel[i]=(double) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
2081       MagickSigma)))/(MagickSQ2PI*MagickSigma));
2082     normalize+=kernel[i];
2083   }
2084   for (i=0; i < (ssize_t) width; i++)
2085     kernel[i]/=normalize;
2086   return(kernel);
2087 }
2088
2089 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
2090   const double sigma,const double angle,const double bias,
2091   ExceptionInfo *exception)
2092 {
2093   CacheView
2094     *blur_view,
2095     *image_view;
2096
2097   double
2098     *kernel;
2099
2100   Image
2101     *blur_image;
2102
2103   MagickBooleanType
2104     status;
2105
2106   MagickOffsetType
2107     progress;
2108
2109   OffsetInfo
2110     *offset;
2111
2112   PointInfo
2113     point;
2114
2115   register ssize_t
2116     i;
2117
2118   size_t
2119     width;
2120
2121   ssize_t
2122     y;
2123
2124   assert(image != (Image *) NULL);
2125   assert(image->signature == MagickSignature);
2126   if (image->debug != MagickFalse)
2127     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2128   assert(exception != (ExceptionInfo *) NULL);
2129   width=GetOptimalKernelWidth1D(radius,sigma);
2130   kernel=GetMotionBlurKernel(width,sigma);
2131   if (kernel == (double *) NULL)
2132     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2133   offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2134   if (offset == (OffsetInfo *) NULL)
2135     {
2136       kernel=(double *) RelinquishMagickMemory(kernel);
2137       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2138     }
2139   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2140   if (blur_image == (Image *) NULL)
2141     {
2142       kernel=(double *) RelinquishMagickMemory(kernel);
2143       offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2144       return((Image *) NULL);
2145     }
2146   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2147     {
2148       kernel=(double *) RelinquishMagickMemory(kernel);
2149       offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2150       blur_image=DestroyImage(blur_image);
2151       return((Image *) NULL);
2152     }
2153   point.x=(double) width*sin(DegreesToRadians(angle));
2154   point.y=(double) width*cos(DegreesToRadians(angle));
2155   for (i=0; i < (ssize_t) width; i++)
2156   {
2157     offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
2158     offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
2159   }
2160   /*
2161     Motion blur image.
2162   */
2163   status=MagickTrue;
2164   progress=0;
2165   image_view=AcquireCacheView(image);
2166   blur_view=AcquireCacheView(blur_image);
2167 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2168   #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
2169 #endif
2170   for (y=0; y < (ssize_t) image->rows; y++)
2171   {
2172     register const Quantum
2173       *restrict p;
2174
2175     register Quantum
2176       *restrict q;
2177
2178     register ssize_t
2179       x;
2180
2181     if (status == MagickFalse)
2182       continue;
2183     p=GetCacheViewVirtualPixels(blur_view,0,y,image->columns,1,exception);
2184     q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2185       exception);
2186     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2187       {
2188         status=MagickFalse;
2189         continue;
2190       }
2191     for (x=0; x < (ssize_t) image->columns; x++)
2192     {
2193       register ssize_t
2194         i;
2195
2196       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2197       {
2198         MagickRealType
2199           alpha,
2200           gamma,
2201           pixel;
2202
2203         PixelChannel
2204           channel;
2205
2206         PixelTrait
2207           blur_traits,
2208           traits;
2209
2210         register const Quantum
2211           *restrict r;
2212
2213         register double
2214           *restrict k;
2215
2216         register ssize_t
2217           j;
2218
2219         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2220         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
2221         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
2222         if ((traits == UndefinedPixelTrait) ||
2223             (blur_traits == UndefinedPixelTrait))
2224           continue;
2225         if ((blur_traits & CopyPixelTrait) != 0)
2226           {
2227             SetPixelChannel(blur_image,channel,p[i],q);
2228             continue;
2229           }
2230         k=kernel;
2231         pixel=bias;
2232         if ((blur_traits & BlendPixelTrait) == 0)
2233           {
2234             for (j=0; j < (ssize_t) width; j++)
2235             {
2236               r=GetCacheViewVirtualPixels(image_view,x+offset[j].x,y+
2237                 offset[j].y,1,1,exception);
2238               if (r == (const Quantum *) NULL)
2239                 {
2240                   status=MagickFalse;
2241                   continue;
2242                 }
2243               pixel+=(*k)*r[i];
2244               k++;
2245             }
2246             SetPixelChannel(blur_image,channel,ClampToQuantum(pixel),q);
2247             continue;
2248           }
2249         alpha=0.0;
2250         gamma=0.0;
2251         for (j=0; j < (ssize_t) width; j++)
2252         {
2253           r=GetCacheViewVirtualPixels(image_view,x+offset[j].x,y+offset[j].y,1,
2254             1,exception);
2255           if (r == (const Quantum *) NULL)
2256             {
2257               status=MagickFalse;
2258               continue;
2259             }
2260           alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,r));
2261           pixel+=(*k)*alpha*r[i];
2262           gamma+=(*k)*alpha;
2263           k++;
2264         }
2265         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2266         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2267       }
2268       p+=GetPixelChannels(image);
2269       q+=GetPixelChannels(blur_image);
2270     }
2271     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2272       status=MagickFalse;
2273     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2274       {
2275         MagickBooleanType
2276           proceed;
2277
2278 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2279   #pragma omp critical (MagickCore_MotionBlurImage)
2280 #endif
2281         proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2282         if (proceed == MagickFalse)
2283           status=MagickFalse;
2284       }
2285   }
2286   blur_view=DestroyCacheView(blur_view);
2287   image_view=DestroyCacheView(image_view);
2288   kernel=(double *) RelinquishMagickMemory(kernel);
2289   offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2290   if (status == MagickFalse)
2291     blur_image=DestroyImage(blur_image);
2292   return(blur_image);
2293 }
2294 \f
2295 /*
2296 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2297 %                                                                             %
2298 %                                                                             %
2299 %                                                                             %
2300 %     P r e v i e w I m a g e                                                 %
2301 %                                                                             %
2302 %                                                                             %
2303 %                                                                             %
2304 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2305 %
2306 %  PreviewImage() tiles 9 thumbnails of the specified image with an image
2307 %  processing operation applied with varying parameters.  This may be helpful
2308 %  pin-pointing an appropriate parameter for a particular image processing
2309 %  operation.
2310 %
2311 %  The format of the PreviewImages method is:
2312 %
2313 %      Image *PreviewImages(const Image *image,const PreviewType preview,
2314 %        ExceptionInfo *exception)
2315 %
2316 %  A description of each parameter follows:
2317 %
2318 %    o image: the image.
2319 %
2320 %    o preview: the image processing operation.
2321 %
2322 %    o exception: return any errors or warnings in this structure.
2323 %
2324 */
2325 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
2326   ExceptionInfo *exception)
2327 {
2328 #define NumberTiles  9
2329 #define PreviewImageTag  "Preview/Image"
2330 #define DefaultPreviewGeometry  "204x204+10+10"
2331
2332   char
2333     factor[MaxTextExtent],
2334     label[MaxTextExtent];
2335
2336   double
2337     degrees,
2338     gamma,
2339     percentage,
2340     radius,
2341     sigma,
2342     threshold;
2343
2344   Image
2345     *images,
2346     *montage_image,
2347     *preview_image,
2348     *thumbnail;
2349
2350   ImageInfo
2351     *preview_info;
2352
2353   MagickBooleanType
2354     proceed;
2355
2356   MontageInfo
2357     *montage_info;
2358
2359   QuantizeInfo
2360     quantize_info;
2361
2362   RectangleInfo
2363     geometry;
2364
2365   register ssize_t
2366     i,
2367     x;
2368
2369   size_t
2370     colors;
2371
2372   ssize_t
2373     y;
2374
2375   /*
2376     Open output image file.
2377   */
2378   assert(image != (Image *) NULL);
2379   assert(image->signature == MagickSignature);
2380   if (image->debug != MagickFalse)
2381     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2382   colors=2;
2383   degrees=0.0;
2384   gamma=(-0.2f);
2385   preview_info=AcquireImageInfo();
2386   SetGeometry(image,&geometry);
2387   (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2388     &geometry.width,&geometry.height);
2389   images=NewImageList();
2390   percentage=12.5;
2391   GetQuantizeInfo(&quantize_info);
2392   radius=0.0;
2393   sigma=1.0;
2394   threshold=0.0;
2395   x=0;
2396   y=0;
2397   for (i=0; i < NumberTiles; i++)
2398   {
2399     thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2400     if (thumbnail == (Image *) NULL)
2401       break;
2402     (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2403       (void *) NULL);
2404     (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
2405     if (i == (NumberTiles/2))
2406       {
2407         (void) QueryColorCompliance("#dfdfdf",AllCompliance,
2408           &thumbnail->matte_color,exception);
2409         AppendImageToList(&images,thumbnail);
2410         continue;
2411       }
2412     switch (preview)
2413     {
2414       case RotatePreview:
2415       {
2416         degrees+=45.0;
2417         preview_image=RotateImage(thumbnail,degrees,exception);
2418         (void) FormatLocaleString(label,MaxTextExtent,"rotate %g",degrees);
2419         break;
2420       }
2421       case ShearPreview:
2422       {
2423         degrees+=5.0;
2424         preview_image=ShearImage(thumbnail,degrees,degrees,exception);
2425         (void) FormatLocaleString(label,MaxTextExtent,"shear %gx%g",
2426           degrees,2.0*degrees);
2427         break;
2428       }
2429       case RollPreview:
2430       {
2431         x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
2432         y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
2433         preview_image=RollImage(thumbnail,x,y,exception);
2434         (void) FormatLocaleString(label,MaxTextExtent,"roll %+.20gx%+.20g",
2435           (double) x,(double) y);
2436         break;
2437       }
2438       case HuePreview:
2439       {
2440         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2441         if (preview_image == (Image *) NULL)
2442           break;
2443         (void) FormatLocaleString(factor,MaxTextExtent,"100,100,%g",
2444           2.0*percentage);
2445         (void) ModulateImage(preview_image,factor,exception);
2446         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
2447         break;
2448       }
2449       case SaturationPreview:
2450       {
2451         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2452         if (preview_image == (Image *) NULL)
2453           break;
2454         (void) FormatLocaleString(factor,MaxTextExtent,"100,%g",
2455           2.0*percentage);
2456         (void) ModulateImage(preview_image,factor,exception);
2457         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
2458         break;
2459       }
2460       case BrightnessPreview:
2461       {
2462         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2463         if (preview_image == (Image *) NULL)
2464           break;
2465         (void) FormatLocaleString(factor,MaxTextExtent,"%g",2.0*percentage);
2466         (void) ModulateImage(preview_image,factor,exception);
2467         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
2468         break;
2469       }
2470       case GammaPreview:
2471       default:
2472       {
2473         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2474         if (preview_image == (Image *) NULL)
2475           break;
2476         gamma+=0.4f;
2477         (void) GammaImage(preview_image,gamma,exception);
2478         (void) FormatLocaleString(label,MaxTextExtent,"gamma %g",gamma);
2479         break;
2480       }
2481       case SpiffPreview:
2482       {
2483         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2484         if (preview_image != (Image *) NULL)
2485           for (x=0; x < i; x++)
2486             (void) ContrastImage(preview_image,MagickTrue,exception);
2487         (void) FormatLocaleString(label,MaxTextExtent,"contrast (%.20g)",
2488           (double) i+1);
2489         break;
2490       }
2491       case DullPreview:
2492       {
2493         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2494         if (preview_image == (Image *) NULL)
2495           break;
2496         for (x=0; x < i; x++)
2497           (void) ContrastImage(preview_image,MagickFalse,exception);
2498         (void) FormatLocaleString(label,MaxTextExtent,"+contrast (%.20g)",
2499           (double) i+1);
2500         break;
2501       }
2502       case GrayscalePreview:
2503       {
2504         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2505         if (preview_image == (Image *) NULL)
2506           break;
2507         colors<<=1;
2508         quantize_info.number_colors=colors;
2509         quantize_info.colorspace=GRAYColorspace;
2510         (void) QuantizeImage(&quantize_info,preview_image,exception);
2511         (void) FormatLocaleString(label,MaxTextExtent,
2512           "-colorspace gray -colors %.20g",(double) colors);
2513         break;
2514       }
2515       case QuantizePreview:
2516       {
2517         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2518         if (preview_image == (Image *) NULL)
2519           break;
2520         colors<<=1;
2521         quantize_info.number_colors=colors;
2522         (void) QuantizeImage(&quantize_info,preview_image,exception);
2523         (void) FormatLocaleString(label,MaxTextExtent,"colors %.20g",(double)
2524           colors);
2525         break;
2526       }
2527       case DespecklePreview:
2528       {
2529         for (x=0; x < (i-1); x++)
2530         {
2531           preview_image=DespeckleImage(thumbnail,exception);
2532           if (preview_image == (Image *) NULL)
2533             break;
2534           thumbnail=DestroyImage(thumbnail);
2535           thumbnail=preview_image;
2536         }
2537         preview_image=DespeckleImage(thumbnail,exception);
2538         if (preview_image == (Image *) NULL)
2539           break;
2540         (void) FormatLocaleString(label,MaxTextExtent,"despeckle (%.20g)",
2541           (double) i+1);
2542         break;
2543       }
2544       case ReduceNoisePreview:
2545       {
2546         preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
2547           (size_t) radius,exception);
2548         (void) FormatLocaleString(label,MaxTextExtent,"noise %g",radius);
2549         break;
2550       }
2551       case AddNoisePreview:
2552       {
2553         switch ((int) i)
2554         {
2555           case 0:
2556           {
2557             (void) CopyMagickString(factor,"uniform",MaxTextExtent);
2558             break;
2559           }
2560           case 1:
2561           {
2562             (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
2563             break;
2564           }
2565           case 2:
2566           {
2567             (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
2568             break;
2569           }
2570           case 3:
2571           {
2572             (void) CopyMagickString(factor,"impulse",MaxTextExtent);
2573             break;
2574           }
2575           case 4:
2576           {
2577             (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
2578             break;
2579           }
2580           case 5:
2581           {
2582             (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
2583             break;
2584           }
2585           default:
2586           {
2587             (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
2588             break;
2589           }
2590         }
2591         preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
2592           (size_t) i,exception);
2593         (void) FormatLocaleString(label,MaxTextExtent,"+noise %s",factor);
2594         break;
2595       }
2596       case SharpenPreview:
2597       {
2598         preview_image=SharpenImage(thumbnail,radius,sigma,image->bias,
2599           exception);
2600         (void) FormatLocaleString(label,MaxTextExtent,"sharpen %gx%g",
2601           radius,sigma);
2602         break;
2603       }
2604       case BlurPreview:
2605       {
2606         preview_image=BlurImage(thumbnail,radius,sigma,image->bias,exception);
2607         (void) FormatLocaleString(label,MaxTextExtent,"blur %gx%g",radius,
2608           sigma);
2609         break;
2610       }
2611       case ThresholdPreview:
2612       {
2613         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2614         if (preview_image == (Image *) NULL)
2615           break;
2616         (void) BilevelImage(thumbnail,
2617           (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2618         (void) FormatLocaleString(label,MaxTextExtent,"threshold %g",
2619           (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2620         break;
2621       }
2622       case EdgeDetectPreview:
2623       {
2624         preview_image=EdgeImage(thumbnail,radius,sigma,exception);
2625         (void) FormatLocaleString(label,MaxTextExtent,"edge %g",radius);
2626         break;
2627       }
2628       case SpreadPreview:
2629       {
2630         preview_image=SpreadImage(thumbnail,radius,thumbnail->interpolate,
2631           exception);
2632         (void) FormatLocaleString(label,MaxTextExtent,"spread %g",
2633           radius+0.5);
2634         break;
2635       }
2636       case SolarizePreview:
2637       {
2638         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2639         if (preview_image == (Image *) NULL)
2640           break;
2641         (void) SolarizeImage(preview_image,(double) QuantumRange*
2642           percentage/100.0,exception);
2643         (void) FormatLocaleString(label,MaxTextExtent,"solarize %g",
2644           (QuantumRange*percentage)/100.0);
2645         break;
2646       }
2647       case ShadePreview:
2648       {
2649         degrees+=10.0;
2650         preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2651           exception);
2652         (void) FormatLocaleString(label,MaxTextExtent,"shade %gx%g",
2653           degrees,degrees);
2654         break;
2655       }
2656       case RaisePreview:
2657       {
2658         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2659         if (preview_image == (Image *) NULL)
2660           break;
2661         geometry.width=(size_t) (2*i+2);
2662         geometry.height=(size_t) (2*i+2);
2663         geometry.x=i/2;
2664         geometry.y=i/2;
2665         (void) RaiseImage(preview_image,&geometry,MagickTrue,exception);
2666         (void) FormatLocaleString(label,MaxTextExtent,
2667           "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
2668           geometry.height,(double) geometry.x,(double) geometry.y);
2669         break;
2670       }
2671       case SegmentPreview:
2672       {
2673         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2674         if (preview_image == (Image *) NULL)
2675           break;
2676         threshold+=0.4f;
2677         (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
2678           threshold,exception);
2679         (void) FormatLocaleString(label,MaxTextExtent,"segment %gx%g",
2680           threshold,threshold);
2681         break;
2682       }
2683       case SwirlPreview:
2684       {
2685         preview_image=SwirlImage(thumbnail,degrees,image->interpolate,
2686           exception);
2687         (void) FormatLocaleString(label,MaxTextExtent,"swirl %g",degrees);
2688         degrees+=45.0;
2689         break;
2690       }
2691       case ImplodePreview:
2692       {
2693         degrees+=0.1f;
2694         preview_image=ImplodeImage(thumbnail,degrees,image->interpolate,
2695           exception);
2696         (void) FormatLocaleString(label,MaxTextExtent,"implode %g",degrees);
2697         break;
2698       }
2699       case WavePreview:
2700       {
2701         degrees+=5.0f;
2702         preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,
2703           image->interpolate,exception);
2704         (void) FormatLocaleString(label,MaxTextExtent,"wave %gx%g",
2705           0.5*degrees,2.0*degrees);
2706         break;
2707       }
2708       case OilPaintPreview:
2709       {
2710         preview_image=OilPaintImage(thumbnail,(double) radius,(double) sigma,
2711           exception);
2712         (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
2713           radius,sigma);
2714         break;
2715       }
2716       case CharcoalDrawingPreview:
2717       {
2718         preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2719           image->bias,exception);
2720         (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
2721           radius,sigma);
2722         break;
2723       }
2724       case JPEGPreview:
2725       {
2726         char
2727           filename[MaxTextExtent];
2728
2729         int
2730           file;
2731
2732         MagickBooleanType
2733           status;
2734
2735         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2736         if (preview_image == (Image *) NULL)
2737           break;
2738         preview_info->quality=(size_t) percentage;
2739         (void) FormatLocaleString(factor,MaxTextExtent,"%.20g",(double)
2740           preview_info->quality);
2741         file=AcquireUniqueFileResource(filename);
2742         if (file != -1)
2743           file=close(file)-1;
2744         (void) FormatLocaleString(preview_image->filename,MaxTextExtent,
2745           "jpeg:%s",filename);
2746         status=WriteImage(preview_info,preview_image,exception);
2747         if (status != MagickFalse)
2748           {
2749             Image
2750               *quality_image;
2751
2752             (void) CopyMagickString(preview_info->filename,
2753               preview_image->filename,MaxTextExtent);
2754             quality_image=ReadImage(preview_info,exception);
2755             if (quality_image != (Image *) NULL)
2756               {
2757                 preview_image=DestroyImage(preview_image);
2758                 preview_image=quality_image;
2759               }
2760           }
2761         (void) RelinquishUniqueFileResource(preview_image->filename);
2762         if ((GetBlobSize(preview_image)/1024) >= 1024)
2763           (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%gmb ",
2764             factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2765             1024.0/1024.0);
2766         else
2767           if (GetBlobSize(preview_image) >= 1024)
2768             (void) FormatLocaleString(label,MaxTextExtent,
2769               "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
2770               GetBlobSize(preview_image))/1024.0);
2771           else
2772             (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%.20gb ",
2773               factor,(double) ((MagickOffsetType) GetBlobSize(thumbnail)));
2774         break;
2775       }
2776     }
2777     thumbnail=DestroyImage(thumbnail);
2778     percentage+=12.5;
2779     radius+=0.5;
2780     sigma+=0.25;
2781     if (preview_image == (Image *) NULL)
2782       break;
2783     (void) DeleteImageProperty(preview_image,"label");
2784     (void) SetImageProperty(preview_image,"label",label);
2785     AppendImageToList(&images,preview_image);
2786     proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2787       NumberTiles);
2788     if (proceed == MagickFalse)
2789       break;
2790   }
2791   if (images == (Image *) NULL)
2792     {
2793       preview_info=DestroyImageInfo(preview_info);
2794       return((Image *) NULL);
2795     }
2796   /*
2797     Create the montage.
2798   */
2799   montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2800   (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
2801   montage_info->shadow=MagickTrue;
2802   (void) CloneString(&montage_info->tile,"3x3");
2803   (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2804   (void) CloneString(&montage_info->frame,DefaultTileFrame);
2805   montage_image=MontageImages(images,montage_info,exception);
2806   montage_info=DestroyMontageInfo(montage_info);
2807   images=DestroyImageList(images);
2808   if (montage_image == (Image *) NULL)
2809     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2810   if (montage_image->montage != (char *) NULL)
2811     {
2812       /*
2813         Free image directory.
2814       */
2815       montage_image->montage=(char *) RelinquishMagickMemory(
2816         montage_image->montage);
2817       if (image->directory != (char *) NULL)
2818         montage_image->directory=(char *) RelinquishMagickMemory(
2819           montage_image->directory);
2820     }
2821   preview_info=DestroyImageInfo(preview_info);
2822   return(montage_image);
2823 }
2824 \f
2825 /*
2826 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2827 %                                                                             %
2828 %                                                                             %
2829 %                                                                             %
2830 %     R a d i a l B l u r I m a g e                                           %
2831 %                                                                             %
2832 %                                                                             %
2833 %                                                                             %
2834 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2835 %
2836 %  RadialBlurImage() applies a radial blur to the image.
2837 %
2838 %  Andrew Protano contributed this effect.
2839 %
2840 %  The format of the RadialBlurImage method is:
2841 %
2842 %    Image *RadialBlurImage(const Image *image,const double angle,
2843 %      const double blur,ExceptionInfo *exception)
2844 %
2845 %  A description of each parameter follows:
2846 %
2847 %    o image: the image.
2848 %
2849 %    o angle: the angle of the radial blur.
2850 %
2851 %    o blur: the blur.
2852 %
2853 %    o exception: return any errors or warnings in this structure.
2854 %
2855 */
2856 MagickExport Image *RadialBlurImage(const Image *image,
2857   const double angle,const double bias,ExceptionInfo *exception)
2858 {
2859   CacheView
2860     *blur_view,
2861     *image_view;
2862
2863   Image
2864     *blur_image;
2865
2866   MagickBooleanType
2867     status;
2868
2869   MagickOffsetType
2870     progress;
2871
2872   MagickRealType
2873     blur_radius,
2874     *cos_theta,
2875     offset,
2876     *sin_theta,
2877     theta;
2878
2879   PointInfo
2880     blur_center;
2881
2882   register ssize_t
2883     i;
2884
2885   size_t
2886     n;
2887
2888   ssize_t
2889     y;
2890
2891   /*
2892     Allocate blur image.
2893   */
2894   assert(image != (Image *) NULL);
2895   assert(image->signature == MagickSignature);
2896   if (image->debug != MagickFalse)
2897     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2898   assert(exception != (ExceptionInfo *) NULL);
2899   assert(exception->signature == MagickSignature);
2900   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2901   if (blur_image == (Image *) NULL)
2902     return((Image *) NULL);
2903   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2904     {
2905       blur_image=DestroyImage(blur_image);
2906       return((Image *) NULL);
2907     }
2908   blur_center.x=(double) image->columns/2.0;
2909   blur_center.y=(double) image->rows/2.0;
2910   blur_radius=hypot(blur_center.x,blur_center.y);
2911   n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
2912   theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
2913   cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2914     sizeof(*cos_theta));
2915   sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2916     sizeof(*sin_theta));
2917   if ((cos_theta == (MagickRealType *) NULL) ||
2918       (sin_theta == (MagickRealType *) NULL))
2919     {
2920       blur_image=DestroyImage(blur_image);
2921       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2922     }
2923   offset=theta*(MagickRealType) (n-1)/2.0;
2924   for (i=0; i < (ssize_t) n; i++)
2925   {
2926     cos_theta[i]=cos((double) (theta*i-offset));
2927     sin_theta[i]=sin((double) (theta*i-offset));
2928   }
2929   /*
2930     Radial blur image.
2931   */
2932   status=MagickTrue;
2933   progress=0;
2934   image_view=AcquireCacheView(image);
2935   blur_view=AcquireCacheView(blur_image);
2936 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2937   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2938 #endif
2939   for (y=0; y < (ssize_t) image->rows; y++)
2940   {
2941     register const Quantum
2942       *restrict p;
2943
2944     register Quantum
2945       *restrict q;
2946
2947     register ssize_t
2948       x;
2949
2950     if (status == MagickFalse)
2951       continue;
2952     p=GetCacheViewVirtualPixels(blur_view,0,y,image->columns,1,exception);
2953     q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2954       exception);
2955     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2956       {
2957         status=MagickFalse;
2958         continue;
2959       }
2960     for (x=0; x < (ssize_t) image->columns; x++)
2961     {
2962       MagickRealType
2963         radius;
2964
2965       PointInfo
2966         center;
2967
2968       register ssize_t
2969         i;
2970
2971       size_t
2972         step;
2973
2974       center.x=(double) x-blur_center.x;
2975       center.y=(double) y-blur_center.y;
2976       radius=hypot((double) center.x,center.y);
2977       if (radius == 0)
2978         step=1;
2979       else
2980         {
2981           step=(size_t) (blur_radius/radius);
2982           if (step == 0)
2983             step=1;
2984           else
2985             if (step >= n)
2986               step=n-1;
2987         }
2988       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2989       {
2990         MagickRealType
2991           gamma,
2992           pixel;
2993
2994         PixelChannel
2995           channel;
2996
2997         PixelTrait
2998           blur_traits,
2999           traits;
3000
3001         register const Quantum
3002           *restrict r;
3003
3004         register ssize_t
3005           j;
3006
3007         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3008         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3009         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
3010         if ((traits == UndefinedPixelTrait) ||
3011             (blur_traits == UndefinedPixelTrait))
3012           continue;
3013         if ((blur_traits & CopyPixelTrait) != 0)
3014           {
3015             SetPixelChannel(blur_image,channel,p[i],q);
3016             continue;
3017           }
3018         gamma=0.0;
3019         pixel=bias;
3020         if ((blur_traits & BlendPixelTrait) == 0)
3021           {
3022             for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
3023             {
3024               r=GetCacheViewVirtualPixels(image_view, (ssize_t) (blur_center.x+
3025                 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
3026                 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
3027                 1,1,exception);
3028               if (r == (const Quantum *) NULL)
3029                 {
3030                   status=MagickFalse;
3031                   continue;
3032                 }
3033               pixel+=r[i];
3034               gamma++;
3035             }
3036             gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3037             SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3038             continue;
3039           }
3040         for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
3041         {
3042           r=GetCacheViewVirtualPixels(image_view, (ssize_t) (blur_center.x+
3043             center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
3044             (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
3045             1,1,exception);
3046           if (r == (const Quantum *) NULL)
3047             {
3048               status=MagickFalse;
3049               continue;
3050             }
3051           pixel+=GetPixelAlpha(image,r)*r[i];
3052           gamma+=GetPixelAlpha(image,r);
3053         }
3054         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3055         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3056       }
3057       p+=GetPixelChannels(image);
3058       q+=GetPixelChannels(blur_image);
3059     }
3060     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3061       status=MagickFalse;
3062     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3063       {
3064         MagickBooleanType
3065           proceed;
3066
3067 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3068   #pragma omp critical (MagickCore_RadialBlurImage)
3069 #endif
3070         proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3071         if (proceed == MagickFalse)
3072           status=MagickFalse;
3073       }
3074   }
3075   blur_view=DestroyCacheView(blur_view);
3076   image_view=DestroyCacheView(image_view);
3077   cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
3078   sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
3079   if (status == MagickFalse)
3080     blur_image=DestroyImage(blur_image);
3081   return(blur_image);
3082 }
3083 \f
3084 /*
3085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3086 %                                                                             %
3087 %                                                                             %
3088 %                                                                             %
3089 %     S e l e c t i v e B l u r I m a g e                                     %
3090 %                                                                             %
3091 %                                                                             %
3092 %                                                                             %
3093 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3094 %
3095 %  SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3096 %  It is similar to the unsharpen mask that sharpens everything with contrast
3097 %  above a certain threshold.
3098 %
3099 %  The format of the SelectiveBlurImage method is:
3100 %
3101 %      Image *SelectiveBlurImage(const Image *image,const double radius,
3102 %        const double sigma,const double threshold,const double bias,
3103 %        ExceptionInfo *exception)
3104 %
3105 %  A description of each parameter follows:
3106 %
3107 %    o image: the image.
3108 %
3109 %    o radius: the radius of the Gaussian, in pixels, not counting the center
3110 %      pixel.
3111 %
3112 %    o sigma: the standard deviation of the Gaussian, in pixels.
3113 %
3114 %    o threshold: only pixels within this contrast threshold are included
3115 %      in the blur operation.
3116 %
3117 %    o bias: the bias.
3118 %
3119 %    o exception: return any errors or warnings in this structure.
3120 %
3121 */
3122 MagickExport Image *SelectiveBlurImage(const Image *image,
3123   const double radius,const double sigma,const double threshold,
3124   const double bias,ExceptionInfo *exception)
3125 {
3126 #define SelectiveBlurImageTag  "SelectiveBlur/Image"
3127
3128   CacheView
3129     *blur_view,
3130     *image_view;
3131
3132   double
3133     *kernel;
3134
3135   Image
3136     *blur_image;
3137
3138   MagickBooleanType
3139     status;
3140
3141   MagickOffsetType
3142     progress;
3143
3144   register ssize_t
3145     i;
3146
3147   size_t
3148     width;
3149
3150   ssize_t
3151     center,
3152     j,
3153     u,
3154     v,
3155     y;
3156
3157   /*
3158     Initialize blur image attributes.
3159   */
3160   assert(image != (Image *) NULL);
3161   assert(image->signature == MagickSignature);
3162   if (image->debug != MagickFalse)
3163     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3164   assert(exception != (ExceptionInfo *) NULL);
3165   assert(exception->signature == MagickSignature);
3166   width=GetOptimalKernelWidth1D(radius,sigma);
3167   kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
3168   if (kernel == (double *) NULL)
3169     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3170   j=(ssize_t) width/2;
3171   i=0;
3172   for (v=(-j); v <= j; v++)
3173   {
3174     for (u=(-j); u <= j; u++)
3175       kernel[i++]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
3176         MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3177   }
3178   if (image->debug != MagickFalse)
3179     {
3180       char
3181         format[MaxTextExtent],
3182         *message;
3183
3184       register const double
3185         *k;
3186
3187       ssize_t
3188         u,
3189         v;
3190
3191       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
3192         "  SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
3193         width);
3194       message=AcquireString("");
3195       k=kernel;
3196       for (v=0; v < (ssize_t) width; v++)
3197       {
3198         *message='\0';
3199         (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
3200         (void) ConcatenateString(&message,format);
3201         for (u=0; u < (ssize_t) width; u++)
3202         {
3203           (void) FormatLocaleString(format,MaxTextExtent,"%+f ",*k++);
3204           (void) ConcatenateString(&message,format);
3205         }
3206         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3207       }
3208       message=DestroyString(message);
3209     }
3210   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3211   if (blur_image == (Image *) NULL)
3212     return((Image *) NULL);
3213   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
3214     {
3215       blur_image=DestroyImage(blur_image);
3216       return((Image *) NULL);
3217     }
3218   /*
3219     Threshold blur image.
3220   */
3221   status=MagickTrue;
3222   progress=0;
3223   center=(ssize_t) (GetPixelChannels(image)*(image->columns+width)*(width/2L)+
3224     GetPixelChannels(image)*(width/2L));
3225   image_view=AcquireCacheView(image);
3226   blur_view=AcquireCacheView(blur_image);
3227 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3228   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3229 #endif
3230   for (y=0; y < (ssize_t) image->rows; y++)
3231   {
3232     double
3233       contrast;
3234
3235     MagickBooleanType
3236       sync;
3237
3238     register const Quantum
3239       *restrict p;
3240
3241     register Quantum
3242       *restrict q;
3243
3244     register ssize_t
3245       x;
3246
3247     if (status == MagickFalse)
3248       continue;
3249     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
3250       (width/2L),image->columns+width,width,exception);
3251     q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3252       exception);
3253     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3254       {
3255         status=MagickFalse;
3256         continue;
3257       }
3258     for (x=0; x < (ssize_t) image->columns; x++)
3259     {
3260       register ssize_t
3261         i;
3262
3263       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3264       {
3265         MagickRealType
3266           alpha,
3267           gamma,
3268           intensity,
3269           pixel;
3270
3271         PixelChannel
3272           channel;
3273
3274         PixelTrait
3275           blur_traits,
3276           traits;
3277
3278         register const double
3279           *restrict k;
3280
3281         register const Quantum
3282           *restrict pixels;
3283
3284         register ssize_t
3285           u;
3286
3287         ssize_t
3288           v;
3289
3290         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3291         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3292         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
3293         if ((traits == UndefinedPixelTrait) ||
3294             (blur_traits == UndefinedPixelTrait))
3295           continue;
3296         if ((blur_traits & CopyPixelTrait) != 0)
3297           {
3298             SetPixelChannel(blur_image,channel,p[center+i],q);
3299             continue;
3300           }
3301         k=kernel;
3302         pixel=bias;
3303         pixels=p;
3304         intensity=(MagickRealType) GetPixelIntensity(image,p+center);
3305         gamma=0.0;
3306         if ((blur_traits & BlendPixelTrait) == 0)
3307           {
3308             for (v=0; v < (ssize_t) width; v++)
3309             {
3310               for (u=0; u < (ssize_t) width; u++)
3311               {
3312                 contrast=GetPixelIntensity(image,pixels)-intensity;
3313                 if (fabs(contrast) < threshold)
3314                   {
3315                     pixel+=(*k)*pixels[i];
3316                     gamma+=(*k);
3317                   }
3318                 k++;
3319                 pixels+=GetPixelChannels(image);
3320               }
3321               pixels+=image->columns*GetPixelChannels(image);
3322             }
3323             if (fabs((double) gamma) < MagickEpsilon)
3324               {
3325                 SetPixelChannel(blur_image,channel,p[center+i],q);
3326                 continue;
3327               }
3328             gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3329             SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3330             continue;
3331           }
3332         for (v=0; v < (ssize_t) width; v++)
3333         {
3334           for (u=0; u < (ssize_t) width; u++)
3335           {
3336             contrast=GetPixelIntensity(image,pixels)-intensity;
3337             if (fabs(contrast) < threshold)
3338               {
3339                 alpha=(MagickRealType) (QuantumScale*
3340                   GetPixelAlpha(image,pixels));
3341                 pixel+=(*k)*alpha*pixels[i];
3342                 gamma+=(*k)*alpha;
3343               }
3344             k++;
3345             pixels+=GetPixelChannels(image);
3346           }
3347           pixels+=image->columns*GetPixelChannels(image);
3348         }
3349         if (fabs((double) gamma) < MagickEpsilon)
3350           {
3351             SetPixelChannel(blur_image,channel,p[center+i],q);
3352             continue;
3353           }
3354         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3355         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3356       }
3357       p+=GetPixelChannels(image);
3358       q+=GetPixelChannels(blur_image);
3359     }
3360     sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3361     if (sync == MagickFalse)
3362       status=MagickFalse;
3363     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3364       {
3365         MagickBooleanType
3366           proceed;
3367
3368 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3369   #pragma omp critical (MagickCore_SelectiveBlurImage)
3370 #endif
3371         proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
3372           image->rows);
3373         if (proceed == MagickFalse)
3374           status=MagickFalse;
3375       }
3376   }
3377   blur_image->type=image->type;
3378   blur_view=DestroyCacheView(blur_view);
3379   image_view=DestroyCacheView(image_view);
3380   kernel=(double *) RelinquishMagickMemory(kernel);
3381   if (status == MagickFalse)
3382     blur_image=DestroyImage(blur_image);
3383   return(blur_image);
3384 }
3385 \f
3386 /*
3387 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3388 %                                                                             %
3389 %                                                                             %
3390 %                                                                             %
3391 %     S h a d e I m a g e                                                     %
3392 %                                                                             %
3393 %                                                                             %
3394 %                                                                             %
3395 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3396 %
3397 %  ShadeImage() shines a distant light on an image to create a
3398 %  three-dimensional effect. You control the positioning of the light with
3399 %  azimuth and elevation; azimuth is measured in degrees off the x axis
3400 %  and elevation is measured in pixels above the Z axis.
3401 %
3402 %  The format of the ShadeImage method is:
3403 %
3404 %      Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3405 %        const double azimuth,const double elevation,ExceptionInfo *exception)
3406 %
3407 %  A description of each parameter follows:
3408 %
3409 %    o image: the image.
3410 %
3411 %    o gray: A value other than zero shades the intensity of each pixel.
3412 %
3413 %    o azimuth, elevation:  Define the light source direction.
3414 %
3415 %    o exception: return any errors or warnings in this structure.
3416 %
3417 */
3418 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3419   const double azimuth,const double elevation,ExceptionInfo *exception)
3420 {
3421 #define ShadeImageTag  "Shade/Image"
3422
3423   CacheView
3424     *image_view,
3425     *shade_view;
3426
3427   Image
3428     *shade_image;
3429
3430   MagickBooleanType
3431     status;
3432
3433   MagickOffsetType
3434     progress;
3435
3436   PrimaryInfo
3437     light;
3438
3439   ssize_t
3440     y;
3441
3442   /*
3443     Initialize shaded image attributes.
3444   */
3445   assert(image != (const Image *) NULL);
3446   assert(image->signature == MagickSignature);
3447   if (image->debug != MagickFalse)
3448     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3449   assert(exception != (ExceptionInfo *) NULL);
3450   assert(exception->signature == MagickSignature);
3451   shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3452   if (shade_image == (Image *) NULL)
3453     return((Image *) NULL);
3454   if (SetImageStorageClass(shade_image,DirectClass,exception) == MagickFalse)
3455     {
3456       shade_image=DestroyImage(shade_image);
3457       return((Image *) NULL);
3458     }
3459   /*
3460     Compute the light vector.
3461   */
3462   light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3463     cos(DegreesToRadians(elevation));
3464   light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3465     cos(DegreesToRadians(elevation));
3466   light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3467   /*
3468     Shade image.
3469   */
3470   status=MagickTrue;
3471   progress=0;
3472   image_view=AcquireCacheView(image);
3473   shade_view=AcquireCacheView(shade_image);
3474 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3475   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3476 #endif
3477   for (y=0; y < (ssize_t) image->rows; y++)
3478   {
3479     MagickRealType
3480       distance,
3481       normal_distance,
3482       shade;
3483
3484     PrimaryInfo
3485       normal;
3486
3487     register const Quantum
3488       *restrict center,
3489       *restrict p,
3490       *restrict post,
3491       *restrict pre;
3492
3493     register Quantum
3494       *restrict q;
3495
3496     register ssize_t
3497       x;
3498
3499     if (status == MagickFalse)
3500       continue;
3501     p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
3502     q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3503       exception);
3504     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3505       {
3506         status=MagickFalse;
3507         continue;
3508       }
3509     /*
3510       Shade this row of pixels.
3511     */
3512     normal.z=2.0*(double) QuantumRange;  /* constant Z of surface normal */
3513     pre=p+GetPixelChannels(image);
3514     center=pre+(image->columns+2)*GetPixelChannels(image);
3515     post=center+(image->columns+2)*GetPixelChannels(image);
3516     for (x=0; x < (ssize_t) image->columns; x++)
3517     {
3518       register ssize_t
3519         i;
3520
3521       /*
3522         Determine the surface normal and compute shading.
3523       */
3524       normal.x=(double) (GetPixelIntensity(image,pre-GetPixelChannels(image))+
3525         GetPixelIntensity(image,center-GetPixelChannels(image))+
3526         GetPixelIntensity(image,post-GetPixelChannels(image))-
3527         GetPixelIntensity(image,pre+GetPixelChannels(image))-
3528         GetPixelIntensity(image,center+GetPixelChannels(image))-
3529         GetPixelIntensity(image,post+GetPixelChannels(image)));
3530       normal.y=(double) (GetPixelIntensity(image,post-GetPixelChannels(image))+
3531         GetPixelIntensity(image,post)+GetPixelIntensity(image,post+
3532         GetPixelChannels(image))-GetPixelIntensity(image,pre-
3533         GetPixelChannels(image))-GetPixelIntensity(image,pre)-
3534         GetPixelIntensity(image,pre+GetPixelChannels(image)));
3535       if ((normal.x == 0.0) && (normal.y == 0.0))
3536         shade=light.z;
3537       else
3538         {
3539           shade=0.0;
3540           distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3541           if (distance > MagickEpsilon)
3542             {
3543               normal_distance=
3544                 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
3545               if (normal_distance > (MagickEpsilon*MagickEpsilon))
3546                 shade=distance/sqrt((double) normal_distance);
3547             }
3548         }
3549       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3550       {
3551         PixelChannel
3552           channel;
3553
3554         PixelTrait
3555           shade_traits,
3556           traits;
3557
3558         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3559         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3560         shade_traits=GetPixelChannelMapTraits(shade_image,channel);
3561         if ((traits == UndefinedPixelTrait) ||
3562             (shade_traits == UndefinedPixelTrait))
3563           continue;
3564         if ((shade_traits & CopyPixelTrait) != 0)
3565           {
3566             SetPixelChannel(shade_image,channel,center[i],q);
3567             continue;
3568           }
3569         if (gray != MagickFalse)
3570           {
3571             SetPixelChannel(shade_image,channel,ClampToQuantum(shade),q);
3572             continue;
3573           }
3574         SetPixelChannel(shade_image,channel,ClampToQuantum(QuantumScale*shade*
3575           center[i]),q);
3576       }
3577       pre+=GetPixelChannels(image);
3578       center+=GetPixelChannels(image);
3579       post+=GetPixelChannels(image);
3580       q+=GetPixelChannels(shade_image);
3581     }
3582     if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3583       status=MagickFalse;
3584     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3585       {
3586         MagickBooleanType
3587           proceed;
3588
3589 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3590   #pragma omp critical (MagickCore_ShadeImage)
3591 #endif
3592         proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
3593         if (proceed == MagickFalse)
3594           status=MagickFalse;
3595       }
3596   }
3597   shade_view=DestroyCacheView(shade_view);
3598   image_view=DestroyCacheView(image_view);
3599   if (status == MagickFalse)
3600     shade_image=DestroyImage(shade_image);
3601   return(shade_image);
3602 }
3603 \f
3604 /*
3605 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3606 %                                                                             %
3607 %                                                                             %
3608 %                                                                             %
3609 %     S h a r p e n I m a g e                                                 %
3610 %                                                                             %
3611 %                                                                             %
3612 %                                                                             %
3613 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3614 %
3615 %  SharpenImage() sharpens the image.  We convolve the image with a Gaussian
3616 %  operator of the given radius and standard deviation (sigma).  For
3617 %  reasonable results, radius should be larger than sigma.  Use a radius of 0
3618 %  and SharpenImage() selects a suitable radius for you.
3619 %
3620 %  Using a separable kernel would be faster, but the negative weights cancel
3621 %  out on the corners of the kernel producing often undesirable ringing in the
3622 %  filtered result; this can be avoided by using a 2D gaussian shaped image
3623 %  sharpening kernel instead.
3624 %
3625 %  The format of the SharpenImage method is:
3626 %
3627 %    Image *SharpenImage(const Image *image,const double radius,
3628 %      const double sigma,const double bias,ExceptionInfo *exception)
3629 %
3630 %  A description of each parameter follows:
3631 %
3632 %    o image: the image.
3633 %
3634 %    o radius: the radius of the Gaussian, in pixels, not counting the center
3635 %      pixel.
3636 %
3637 %    o sigma: the standard deviation of the Laplacian, in pixels.
3638 %
3639 %    o bias: bias.
3640 %
3641 %    o exception: return any errors or warnings in this structure.
3642 %
3643 */
3644 MagickExport Image *SharpenImage(const Image *image,const double radius,
3645   const double sigma,const double bias,ExceptionInfo *exception)
3646 {
3647   double
3648     normalize;
3649
3650   Image
3651     *sharp_image;
3652
3653   KernelInfo
3654     *kernel_info;
3655
3656   register ssize_t
3657     i;
3658
3659   size_t
3660     width;
3661
3662   ssize_t
3663     j,
3664     u,
3665     v;
3666
3667   assert(image != (const Image *) NULL);
3668   assert(image->signature == MagickSignature);
3669   if (image->debug != MagickFalse)
3670     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3671   assert(exception != (ExceptionInfo *) NULL);
3672   assert(exception->signature == MagickSignature);
3673   width=GetOptimalKernelWidth2D(radius,sigma);
3674   kernel_info=AcquireKernelInfo((const char *) NULL);
3675   if (kernel_info == (KernelInfo *) NULL)
3676     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3677   (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
3678   kernel_info->width=width;
3679   kernel_info->height=width;
3680   kernel_info->bias=bias;
3681   kernel_info->signature=MagickSignature;
3682   kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
3683     kernel_info->width*sizeof(*kernel_info->values));
3684   if (kernel_info->values == (double *) NULL)
3685     {
3686       kernel_info=DestroyKernelInfo(kernel_info);
3687       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3688     }
3689   normalize=0.0;
3690   j=(ssize_t) kernel_info->width/2;
3691   i=0;
3692   for (v=(-j); v <= j; v++)
3693   {
3694     for (u=(-j); u <= j; u++)
3695     {
3696       kernel_info->values[i]=(double) (-exp(-((double) u*u+v*v)/(2.0*
3697         MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3698       normalize+=kernel_info->values[i];
3699       i++;
3700     }
3701   }
3702   kernel_info->values[i/2]=(double) ((-2.0)*normalize);
3703   sharp_image=ConvolveImage(image,kernel_info,exception);
3704   kernel_info=DestroyKernelInfo(kernel_info);
3705   return(sharp_image);
3706 }
3707 \f
3708 /*
3709 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3710 %                                                                             %
3711 %                                                                             %
3712 %                                                                             %
3713 %     S p r e a d I m a g e                                                   %
3714 %                                                                             %
3715 %                                                                             %
3716 %                                                                             %
3717 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3718 %
3719 %  SpreadImage() is a special effects method that randomly displaces each
3720 %  pixel in a block defined by the radius parameter.
3721 %
3722 %  The format of the SpreadImage method is:
3723 %
3724 %      Image *SpreadImage(const Image *image,const double radius,
3725 %        const PixelInterpolateMethod method,ExceptionInfo *exception)
3726 %
3727 %  A description of each parameter follows:
3728 %
3729 %    o image: the image.
3730 %
3731 %    o radius:  choose a random pixel in a neighborhood of this extent.
3732 %
3733 %    o method:  the pixel interpolation method.
3734 %
3735 %    o exception: return any errors or warnings in this structure.
3736 %
3737 */
3738 MagickExport Image *SpreadImage(const Image *image,const double radius,
3739   const PixelInterpolateMethod method,ExceptionInfo *exception)
3740 {
3741 #define SpreadImageTag  "Spread/Image"
3742
3743   CacheView
3744     *image_view,
3745     *spread_view;
3746
3747   Image
3748     *spread_image;
3749
3750   MagickBooleanType
3751     status;
3752
3753   MagickOffsetType
3754     progress;
3755
3756   RandomInfo
3757     **restrict random_info;
3758
3759   size_t
3760     width;
3761
3762   ssize_t
3763     y;
3764
3765   /*
3766     Initialize spread image attributes.
3767   */
3768   assert(image != (Image *) NULL);
3769   assert(image->signature == MagickSignature);
3770   if (image->debug != MagickFalse)
3771     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3772   assert(exception != (ExceptionInfo *) NULL);
3773   assert(exception->signature == MagickSignature);
3774   spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3775     exception);
3776   if (spread_image == (Image *) NULL)
3777     return((Image *) NULL);
3778   if (SetImageStorageClass(spread_image,DirectClass,exception) == MagickFalse)
3779     {
3780       spread_image=DestroyImage(spread_image);
3781       return((Image *) NULL);
3782     }
3783   /*
3784     Spread image.
3785   */
3786   status=MagickTrue;
3787   progress=0;
3788   width=GetOptimalKernelWidth1D(radius,0.5);
3789   random_info=AcquireRandomInfoThreadSet();
3790   image_view=AcquireCacheView(image);
3791   spread_view=AcquireCacheView(spread_image);
3792 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3793   #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
3794 #endif
3795   for (y=0; y < (ssize_t) image->rows; y++)
3796   {
3797     const int
3798       id = GetOpenMPThreadId();
3799
3800     register const Quantum
3801       *restrict p;
3802
3803     register Quantum
3804       *restrict q;
3805
3806     register ssize_t
3807       x;
3808
3809     if (status == MagickFalse)
3810       continue;
3811     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3812     q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
3813       exception);
3814     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3815       {
3816         status=MagickFalse;
3817         continue;
3818       }
3819     for (x=0; x < (ssize_t) image->columns; x++)
3820     {
3821       PointInfo
3822         point;
3823
3824       point.x=GetPseudoRandomValue(random_info[id]);
3825       point.y=GetPseudoRandomValue(random_info[id]);
3826       status=InterpolatePixelChannels(image,image_view,spread_image,method,
3827         (double) x+width*point.x-0.5,(double) y+width*point.y-0.5,q,exception);
3828       q+=GetPixelChannels(spread_image);
3829     }
3830     if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
3831       status=MagickFalse;
3832     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3833       {
3834         MagickBooleanType
3835           proceed;
3836
3837 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3838   #pragma omp critical (MagickCore_SpreadImage)
3839 #endif
3840         proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3841         if (proceed == MagickFalse)
3842           status=MagickFalse;
3843       }
3844   }
3845   spread_view=DestroyCacheView(spread_view);
3846   image_view=DestroyCacheView(image_view);
3847   random_info=DestroyRandomInfoThreadSet(random_info);
3848   return(spread_image);
3849 }
3850 \f
3851 /*
3852 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3853 %                                                                             %
3854 %                                                                             %
3855 %                                                                             %
3856 %     S t a t i s t i c I m a g e                                             %
3857 %                                                                             %
3858 %                                                                             %
3859 %                                                                             %
3860 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3861 %
3862 %  StatisticImage() makes each pixel the min / max / median / mode / etc. of
3863 %  the neighborhood of the specified width and height.
3864 %
3865 %  The format of the StatisticImage method is:
3866 %
3867 %      Image *StatisticImage(const Image *image,const StatisticType type,
3868 %        const size_t width,const size_t height,ExceptionInfo *exception)
3869 %
3870 %  A description of each parameter follows:
3871 %
3872 %    o image: the image.
3873 %
3874 %    o type: the statistic type (median, mode, etc.).
3875 %
3876 %    o width: the width of the pixel neighborhood.
3877 %
3878 %    o height: the height of the pixel neighborhood.
3879 %
3880 %    o exception: return any errors or warnings in this structure.
3881 %
3882 */
3883
3884 typedef struct _SkipNode
3885 {
3886   size_t
3887     next[9],
3888     count,
3889     signature;
3890 } SkipNode;
3891
3892 typedef struct _SkipList
3893 {
3894   ssize_t
3895     level;
3896
3897   SkipNode
3898     *nodes;
3899 } SkipList;
3900
3901 typedef struct _PixelList
3902 {
3903   size_t
3904     length,
3905     seed;
3906
3907   SkipList
3908     skip_list;
3909
3910   size_t
3911     signature;
3912 } PixelList;
3913
3914 static PixelList *DestroyPixelList(PixelList *pixel_list)
3915 {
3916   if (pixel_list == (PixelList *) NULL)
3917     return((PixelList *) NULL);
3918   if (pixel_list->skip_list.nodes != (SkipNode *) NULL)
3919     pixel_list->skip_list.nodes=(SkipNode *) RelinquishMagickMemory(
3920       pixel_list->skip_list.nodes);
3921   pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
3922   return(pixel_list);
3923 }
3924
3925 static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
3926 {
3927   register ssize_t
3928     i;
3929
3930   assert(pixel_list != (PixelList **) NULL);
3931   for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
3932     if (pixel_list[i] != (PixelList *) NULL)
3933       pixel_list[i]=DestroyPixelList(pixel_list[i]);
3934   pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
3935   return(pixel_list);
3936 }
3937
3938 static PixelList *AcquirePixelList(const size_t width,const size_t height)
3939 {
3940   PixelList
3941     *pixel_list;
3942
3943   pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
3944   if (pixel_list == (PixelList *) NULL)
3945     return(pixel_list);
3946   (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
3947   pixel_list->length=width*height;
3948   pixel_list->skip_list.nodes=(SkipNode *) AcquireQuantumMemory(65537UL,
3949     sizeof(*pixel_list->skip_list.nodes));
3950   if (pixel_list->skip_list.nodes == (SkipNode *) NULL)
3951     return(DestroyPixelList(pixel_list));
3952   (void) ResetMagickMemory(pixel_list->skip_list.nodes,0,65537UL*
3953     sizeof(*pixel_list->skip_list.nodes));
3954   pixel_list->signature=MagickSignature;
3955   return(pixel_list);
3956 }
3957
3958 static PixelList **AcquirePixelListThreadSet(const size_t width,
3959   const size_t height)
3960 {
3961   PixelList
3962     **pixel_list;
3963
3964   register ssize_t
3965     i;
3966
3967   size_t
3968     number_threads;
3969
3970   number_threads=GetOpenMPMaximumThreads();
3971   pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
3972     sizeof(*pixel_list));
3973   if (pixel_list == (PixelList **) NULL)
3974     return((PixelList **) NULL);
3975   (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
3976   for (i=0; i < (ssize_t) number_threads; i++)
3977   {
3978     pixel_list[i]=AcquirePixelList(width,height);
3979     if (pixel_list[i] == (PixelList *) NULL)
3980       return(DestroyPixelListThreadSet(pixel_list));
3981   }
3982   return(pixel_list);
3983 }
3984
3985 static void AddNodePixelList(PixelList *pixel_list,const size_t color)
3986 {
3987   register SkipList
3988     *p;
3989
3990   register ssize_t
3991     level;
3992
3993   size_t
3994     search,
3995     update[9];
3996
3997   /*
3998     Initialize the node.
3999   */
4000   p=(&pixel_list->skip_list);
4001   p->nodes[color].signature=pixel_list->signature;
4002   p->nodes[color].count=1;
4003   /*
4004     Determine where it belongs in the list.
4005   */
4006   search=65536UL;
4007   for (level=p->level; level >= 0; level--)
4008   {
4009     while (p->nodes[search].next[level] < color)
4010       search=p->nodes[search].next[level];
4011     update[level]=search;
4012   }
4013   /*
4014     Generate a pseudo-random level for this node.
4015   */
4016   for (level=0; ; level++)
4017   {
4018     pixel_list->seed=(pixel_list->seed*42893621L)+1L;
4019     if ((pixel_list->seed & 0x300) != 0x300)
4020       break;
4021   }
4022   if (level > 8)
4023     level=8;
4024   if (level > (p->level+2))
4025     level=p->level+2;
4026   /*
4027     If we're raising the list's level, link back to the root node.
4028   */
4029   while (level > p->level)
4030   {
4031     p->level++;
4032     update[p->level]=65536UL;
4033   }
4034   /*
4035     Link the node into the skip-list.
4036   */
4037   do
4038   {
4039     p->nodes[color].next[level]=p->nodes[update[level]].next[level];
4040     p->nodes[update[level]].next[level]=color;
4041   } while (level-- > 0);
4042 }
4043
4044 static Quantum GetMaximumPixelList(PixelList *pixel_list)
4045 {
4046   register SkipList
4047     *p;
4048
4049   size_t
4050     color,
4051     maximum;
4052
4053   ssize_t
4054     count;
4055
4056   /*
4057     Find the maximum value for each of the color.
4058   */
4059   p=(&pixel_list->skip_list);
4060   color=65536L;
4061   count=0;
4062   maximum=p->nodes[color].next[0];
4063   do
4064   {
4065     color=p->nodes[color].next[0];
4066     if (color > maximum)
4067       maximum=color;
4068     count+=p->nodes[color].count;
4069   } while (count < (ssize_t) pixel_list->length);
4070   return(ScaleShortToQuantum((unsigned short) maximum));
4071 }
4072
4073 static Quantum GetMeanPixelList(PixelList *pixel_list)
4074 {
4075   MagickRealType
4076     sum;
4077
4078   register SkipList
4079     *p;
4080
4081   size_t
4082     color;
4083
4084   ssize_t
4085     count;
4086
4087   /*
4088     Find the mean value for each of the color.
4089   */
4090   p=(&pixel_list->skip_list);
4091   color=65536L;
4092   count=0;
4093   sum=0.0;
4094   do
4095   {
4096     color=p->nodes[color].next[0];
4097     sum+=(MagickRealType) p->nodes[color].count*color;
4098     count+=p->nodes[color].count;
4099   } while (count < (ssize_t) pixel_list->length);
4100   sum/=pixel_list->length;
4101   return(ScaleShortToQuantum((unsigned short) sum));
4102 }
4103
4104 static Quantum GetMedianPixelList(PixelList *pixel_list)
4105 {
4106   register SkipList
4107     *p;
4108
4109   size_t
4110     color;
4111
4112   ssize_t
4113     count;
4114
4115   /*
4116     Find the median value for each of the color.
4117   */
4118   p=(&pixel_list->skip_list);
4119   color=65536L;
4120   count=0;
4121   do
4122   {
4123     color=p->nodes[color].next[0];
4124     count+=p->nodes[color].count;
4125   } while (count <= (ssize_t) (pixel_list->length >> 1));
4126   return(ScaleShortToQuantum((unsigned short) color));
4127 }
4128
4129 static Quantum GetMinimumPixelList(PixelList *pixel_list)
4130 {
4131   register SkipList
4132     *p;
4133
4134   size_t
4135     color,
4136     minimum;
4137
4138   ssize_t
4139     count;
4140
4141   /*
4142     Find the minimum value for each of the color.
4143   */
4144   p=(&pixel_list->skip_list);
4145   count=0;
4146   color=65536UL;
4147   minimum=p->nodes[color].next[0];
4148   do
4149   {
4150     color=p->nodes[color].next[0];
4151     if (color < minimum)
4152       minimum=color;
4153     count+=p->nodes[color].count;
4154   } while (count < (ssize_t) pixel_list->length);
4155   return(ScaleShortToQuantum((unsigned short) minimum));
4156 }
4157
4158 static Quantum GetModePixelList(PixelList *pixel_list)
4159 {
4160   register SkipList
4161     *p;
4162
4163   size_t
4164     color,
4165     max_count,
4166     mode;
4167
4168   ssize_t
4169     count;
4170
4171   /*
4172     Make each pixel the 'predominant color' of the specified neighborhood.
4173   */
4174   p=(&pixel_list->skip_list);
4175   color=65536L;
4176   mode=color;
4177   max_count=p->nodes[mode].count;
4178   count=0;
4179   do
4180   {
4181     color=p->nodes[color].next[0];
4182     if (p->nodes[color].count > max_count)
4183       {
4184         mode=color;
4185         max_count=p->nodes[mode].count;
4186       }
4187     count+=p->nodes[color].count;
4188   } while (count < (ssize_t) pixel_list->length);
4189   return(ScaleShortToQuantum((unsigned short) mode));
4190 }
4191
4192 static Quantum GetNonpeakPixelList(PixelList *pixel_list)
4193 {
4194   register SkipList
4195     *p;
4196
4197   size_t
4198     color,
4199     next,
4200     previous;
4201
4202   ssize_t
4203     count;
4204
4205   /*
4206     Finds the non peak value for each of the colors.
4207   */
4208   p=(&pixel_list->skip_list);
4209   color=65536L;
4210   next=p->nodes[color].next[0];
4211   count=0;
4212   do
4213   {
4214     previous=color;
4215     color=next;
4216     next=p->nodes[color].next[0];
4217     count+=p->nodes[color].count;
4218   } while (count <= (ssize_t) (pixel_list->length >> 1));
4219   if ((previous == 65536UL) && (next != 65536UL))
4220     color=next;
4221   else
4222     if ((previous != 65536UL) && (next == 65536UL))
4223       color=previous;
4224   return(ScaleShortToQuantum((unsigned short) color));
4225 }
4226
4227 static Quantum GetStandardDeviationPixelList(PixelList *pixel_list)
4228 {
4229   MagickRealType
4230     sum,
4231     sum_squared;
4232
4233   register SkipList
4234     *p;
4235
4236   size_t
4237     color;
4238
4239   ssize_t
4240     count;
4241
4242   /*
4243     Find the standard-deviation value for each of the color.
4244   */
4245   p=(&pixel_list->skip_list);
4246   color=65536L;
4247   count=0;
4248   sum=0.0;
4249   sum_squared=0.0;
4250   do
4251   {
4252     register ssize_t
4253       i;
4254
4255     color=p->nodes[color].next[0];
4256     sum+=(MagickRealType) p->nodes[color].count*color;
4257     for (i=0; i < (ssize_t) p->nodes[color].count; i++)
4258       sum_squared+=((MagickRealType) color)*((MagickRealType) color);
4259     count+=p->nodes[color].count;
4260   } while (count < (ssize_t) pixel_list->length);
4261   sum/=pixel_list->length;
4262   sum_squared/=pixel_list->length;
4263   return(ScaleShortToQuantum((unsigned short) sqrt(sum_squared-(sum*sum))));
4264 }
4265
4266 static inline void InsertPixelList(const Image *image,const Quantum pixel,
4267   PixelList *pixel_list)
4268 {
4269   size_t
4270     signature;
4271
4272   unsigned short
4273     index;
4274
4275   index=ScaleQuantumToShort(pixel);
4276   signature=pixel_list->skip_list.nodes[index].signature;
4277   if (signature == pixel_list->signature)
4278     {
4279       pixel_list->skip_list.nodes[index].count++;
4280       return;
4281     }
4282   AddNodePixelList(pixel_list,index);
4283 }
4284
4285 static inline MagickRealType MagickAbsoluteValue(const MagickRealType x)
4286 {
4287   if (x < 0)
4288     return(-x);
4289   return(x);
4290 }
4291
4292 static inline size_t MagickMax(const size_t x,const size_t y)
4293 {
4294   if (x > y)
4295     return(x);
4296   return(y);
4297 }
4298
4299 static void ResetPixelList(PixelList *pixel_list)
4300 {
4301   int
4302     level;
4303
4304   register SkipNode
4305     *root;
4306
4307   register SkipList
4308     *p;
4309
4310   /*
4311     Reset the skip-list.
4312   */
4313   p=(&pixel_list->skip_list);
4314   root=p->nodes+65536UL;
4315   p->level=0;
4316   for (level=0; level < 9; level++)
4317     root->next[level]=65536UL;
4318   pixel_list->seed=pixel_list->signature++;
4319 }
4320
4321 MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
4322   const size_t width,const size_t height,ExceptionInfo *exception)
4323 {
4324 #define StatisticImageTag  "Statistic/Image"
4325
4326   CacheView
4327     *image_view,
4328     *statistic_view;
4329
4330   Image
4331     *statistic_image;
4332
4333   MagickBooleanType
4334     status;
4335
4336   MagickOffsetType
4337     progress;
4338
4339   PixelList
4340     **restrict pixel_list;
4341
4342   ssize_t
4343     center,
4344     y;
4345
4346   /*
4347     Initialize statistics image attributes.
4348   */
4349   assert(image != (Image *) NULL);
4350   assert(image->signature == MagickSignature);
4351   if (image->debug != MagickFalse)
4352     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4353   assert(exception != (ExceptionInfo *) NULL);
4354   assert(exception->signature == MagickSignature);
4355   statistic_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4356     exception);
4357   if (statistic_image == (Image *) NULL)
4358     return((Image *) NULL);
4359   status=SetImageStorageClass(statistic_image,DirectClass,exception);
4360   if (status == MagickFalse)
4361     {
4362       statistic_image=DestroyImage(statistic_image);
4363       return((Image *) NULL);
4364     }
4365   pixel_list=AcquirePixelListThreadSet(MagickMax(width,1),MagickMax(height,1));
4366   if (pixel_list == (PixelList **) NULL)
4367     {
4368       statistic_image=DestroyImage(statistic_image);
4369       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4370     }
4371   /*
4372     Make each pixel the min / max / median / mode / etc. of the neighborhood.
4373   */
4374   center=(ssize_t) GetPixelChannels(image)*(image->columns+MagickMax(width,1))*
4375     (MagickMax(height,1)/2L)+GetPixelChannels(image)*(MagickMax(width,1)/2L);
4376   status=MagickTrue;
4377   progress=0;
4378   image_view=AcquireCacheView(image);
4379   statistic_view=AcquireCacheView(statistic_image);
4380 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4381   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4382 #endif
4383   for (y=0; y < (ssize_t) statistic_image->rows; y++)
4384   {
4385     const int
4386       id = GetOpenMPThreadId();
4387
4388     register const Quantum
4389       *restrict p;
4390
4391     register Quantum
4392       *restrict q;
4393
4394     register ssize_t
4395       x;
4396
4397     if (status == MagickFalse)
4398       continue;
4399     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) MagickMax(width,1)/2L),y-
4400       (ssize_t) (MagickMax(height,1)/2L),image->columns+MagickMax(width,1),
4401       MagickMax(height,1),exception);
4402     q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns,      1,exception);
4403     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
4404       {
4405         status=MagickFalse;
4406         continue;
4407       }
4408     for (x=0; x < (ssize_t) statistic_image->columns; x++)
4409     {
4410       register ssize_t
4411         i;
4412
4413       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4414       {
4415         PixelChannel
4416           channel;
4417
4418         PixelTrait
4419           statistic_traits,
4420           traits;
4421
4422         Quantum
4423           pixel;
4424
4425         register const Quantum
4426           *restrict pixels;
4427
4428         register ssize_t
4429           u;
4430
4431         ssize_t
4432           v;
4433
4434         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
4435         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
4436         statistic_traits=GetPixelChannelMapTraits(statistic_image,channel);
4437         if ((traits == UndefinedPixelTrait) ||
4438             (statistic_traits == UndefinedPixelTrait))
4439           continue;
4440         if ((statistic_traits & CopyPixelTrait) != 0)
4441           {
4442             SetPixelChannel(statistic_image,channel,p[center+i],q);
4443             continue;
4444           }
4445         pixels=p;
4446         ResetPixelList(pixel_list[id]);
4447         for (v=0; v < (ssize_t) MagickMax(height,1); v++)
4448         {
4449           for (u=0; u < (ssize_t) MagickMax(width,1); u++)
4450           {
4451             InsertPixelList(image,pixels[i],pixel_list[id]);
4452             pixels+=GetPixelChannels(image);
4453           }
4454           pixels+=image->columns*GetPixelChannels(image);
4455         }
4456         switch (type)
4457         {
4458           case GradientStatistic:
4459           {
4460             MagickRealType
4461               maximum,
4462               minimum;
4463
4464             minimum=(MagickRealType) GetMinimumPixelList(pixel_list[id]);
4465             maximum=(MagickRealType) GetMaximumPixelList(pixel_list[id]);
4466             pixel=ClampToQuantum(MagickAbsoluteValue(maximum-minimum));
4467             break;
4468           }
4469           case MaximumStatistic:
4470           {
4471             pixel=GetMaximumPixelList(pixel_list[id]);
4472             break;
4473           }
4474           case MeanStatistic:
4475           {
4476             pixel=GetMeanPixelList(pixel_list[id]);
4477             break;
4478           }
4479           case MedianStatistic:
4480           default:
4481           {
4482             pixel=GetMedianPixelList(pixel_list[id]);
4483             break;
4484           }
4485           case MinimumStatistic:
4486           {
4487             pixel=GetMinimumPixelList(pixel_list[id]);
4488             break;
4489           }
4490           case ModeStatistic:
4491           {
4492             pixel=GetModePixelList(pixel_list[id]);
4493             break;
4494           }
4495           case NonpeakStatistic:
4496           {
4497             pixel=GetNonpeakPixelList(pixel_list[id]);
4498             break;
4499           }
4500           case StandardDeviationStatistic:
4501           {
4502             pixel=GetStandardDeviationPixelList(pixel_list[id]);
4503             break;
4504           }
4505         }
4506         SetPixelChannel(statistic_image,channel,pixel,q);
4507       }
4508       p+=GetPixelChannels(image);
4509       q+=GetPixelChannels(statistic_image);
4510     }
4511     if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
4512       status=MagickFalse;
4513     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4514       {
4515         MagickBooleanType
4516           proceed;
4517
4518 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4519   #pragma omp critical (MagickCore_StatisticImage)
4520 #endif
4521         proceed=SetImageProgress(image,StatisticImageTag,progress++,
4522           image->rows);
4523         if (proceed == MagickFalse)
4524           status=MagickFalse;
4525       }
4526   }
4527   statistic_view=DestroyCacheView(statistic_view);
4528   image_view=DestroyCacheView(image_view);
4529   pixel_list=DestroyPixelListThreadSet(pixel_list);
4530   return(statistic_image);
4531 }
4532 \f
4533 /*
4534 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4535 %                                                                             %
4536 %                                                                             %
4537 %                                                                             %
4538 %     U n s h a r p M a s k I m a g e                                         %
4539 %                                                                             %
4540 %                                                                             %
4541 %                                                                             %
4542 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4543 %
4544 %  UnsharpMaskImage() sharpens one or more image channels.  We convolve the
4545 %  image with a Gaussian operator of the given radius and standard deviation
4546 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
4547 %  radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
4548 %
4549 %  The format of the UnsharpMaskImage method is:
4550 %
4551 %    Image *UnsharpMaskImage(const Image *image,const double radius,
4552 %      const double sigma,const double amount,const double threshold,
4553 %      ExceptionInfo *exception)
4554 %
4555 %  A description of each parameter follows:
4556 %
4557 %    o image: the image.
4558 %
4559 %    o radius: the radius of the Gaussian, in pixels, not counting the center
4560 %      pixel.
4561 %
4562 %    o sigma: the standard deviation of the Gaussian, in pixels.
4563 %
4564 %    o amount: the percentage of the difference between the original and the
4565 %      blur image that is added back into the original.
4566 %
4567 %    o threshold: the threshold in pixels needed to apply the diffence amount.
4568 %
4569 %    o exception: return any errors or warnings in this structure.
4570 %
4571 */
4572 MagickExport Image *UnsharpMaskImage(const Image *image,
4573   const double radius,const double sigma,const double amount,
4574   const double threshold,ExceptionInfo *exception)
4575 {
4576 #define SharpenImageTag  "Sharpen/Image"
4577
4578   CacheView
4579     *image_view,
4580     *unsharp_view;
4581
4582   Image
4583     *unsharp_image;
4584
4585   MagickBooleanType
4586     status;
4587
4588   MagickOffsetType
4589     progress;
4590
4591   MagickRealType
4592     quantum_threshold;
4593
4594   ssize_t
4595     y;
4596
4597   assert(image != (const Image *) NULL);
4598   assert(image->signature == MagickSignature);
4599   if (image->debug != MagickFalse)
4600     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4601   assert(exception != (ExceptionInfo *) NULL);
4602   unsharp_image=BlurImage(image,radius,sigma,image->bias,exception);
4603   if (unsharp_image == (Image *) NULL)
4604     return((Image *) NULL);
4605   quantum_threshold=(MagickRealType) QuantumRange*threshold;
4606   /*
4607     Unsharp-mask image.
4608   */
4609   status=MagickTrue;
4610   progress=0;
4611   image_view=AcquireCacheView(image);
4612   unsharp_view=AcquireCacheView(unsharp_image);
4613 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4614   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4615 #endif
4616   for (y=0; y < (ssize_t) image->rows; y++)
4617   {
4618     register const Quantum
4619       *restrict p;
4620
4621     register Quantum
4622       *restrict q;
4623
4624     register ssize_t
4625       x;
4626
4627     if (status == MagickFalse)
4628       continue;
4629     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4630     q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
4631       exception);
4632     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
4633       {
4634         status=MagickFalse;
4635         continue;
4636       }
4637     for (x=0; x < (ssize_t) image->columns; x++)
4638     {
4639       register ssize_t
4640         i;
4641
4642       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4643       {
4644         MagickRealType
4645           pixel;
4646
4647         PixelChannel
4648           channel;
4649
4650         PixelTrait
4651           traits,
4652           unsharp_traits;
4653
4654         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
4655         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
4656         unsharp_traits=GetPixelChannelMapTraits(unsharp_image,channel);
4657         if ((traits == UndefinedPixelTrait) ||
4658             (unsharp_traits == UndefinedPixelTrait))
4659           continue;
4660         if ((unsharp_traits & CopyPixelTrait) != 0)
4661           {
4662             SetPixelChannel(unsharp_image,channel,p[i],q);
4663             continue;
4664           }
4665         pixel=p[i]-(MagickRealType) GetPixelChannel(unsharp_image,channel,q);
4666         if (fabs(2.0*pixel) < quantum_threshold)
4667           pixel=(MagickRealType) p[i];
4668         else
4669           pixel=(MagickRealType) p[i]+amount*pixel;
4670         SetPixelChannel(unsharp_image,channel,ClampToQuantum(pixel),q);
4671       }
4672       p+=GetPixelChannels(image);
4673       q+=GetPixelChannels(unsharp_image);
4674     }
4675     if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4676       status=MagickFalse;
4677     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4678       {
4679         MagickBooleanType
4680           proceed;
4681
4682 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4683   #pragma omp critical (MagickCore_UnsharpMaskImage)
4684 #endif
4685         proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
4686         if (proceed == MagickFalse)
4687           status=MagickFalse;
4688       }
4689   }
4690   unsharp_image->type=image->type;
4691   unsharp_view=DestroyCacheView(unsharp_view);
4692   image_view=DestroyCacheView(image_view);
4693   if (status == MagickFalse)
4694     unsharp_image=DestroyImage(unsharp_image);
4695   return(unsharp_image);
4696 }