root / ImageMagick / trunk / magick / effect.c

Revision 12610, 125.5 kB (checked in by cristy, 5 days ago)
Line 
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%                      ImageMagick Image Effects Methods                      %
14%                                                                             %
15%                               Software Design                               %
16%                                 John Cristy                                 %
17%                                 October 1996                                %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2008 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
40/*
41  Include declarations.
42*/
43#include "magick/studio.h"
44#include "magick/property.h"
45#include "magick/blob.h"
46#include "magick/cache-view.h"
47#include "magick/color.h"
48#include "magick/color-private.h"
49#include "magick/colorspace.h"
50#include "magick/constitute.h"
51#include "magick/decorate.h"
52#include "magick/draw.h"
53#include "magick/enhance.h"
54#include "magick/exception.h"
55#include "magick/exception-private.h"
56#include "magick/effect.h"
57#include "magick/fx.h"
58#include "magick/gem.h"
59#include "magick/geometry.h"
60#include "magick/image-private.h"
61#include "magick/list.h"
62#include "magick/log.h"
63#include "magick/memory_.h"
64#include "magick/monitor.h"
65#include "magick/monitor-private.h"
66#include "magick/montage.h"
67#include "magick/paint.h"
68#include "magick/pixel-private.h"
69#include "magick/property.h"
70#include "magick/quantize.h"
71#include "magick/quantum.h"
72#include "magick/random_.h"
73#include "magick/resize.h"
74#include "magick/resource_.h"
75#include "magick/segment.h"
76#include "magick/shear.h"
77#include "magick/signature-private.h"
78#include "magick/string_.h"
79#include "magick/transform.h"
80#include "magick/threshold.h"
81
82/*
83%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84%                                                                             %
85%                                                                             %
86%                                                                             %
87%     A d a p t i v e B l u r I m a g e                                       %
88%                                                                             %
89%                                                                             %
90%                                                                             %
91%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92%
93%  AdaptiveBlurImage() adaptively blurs the image by blurring less
94%  intensely near image edges and more intensely far from edges.  We blur the
95%  image with a Gaussian operator of the given radius and standard deviation
96%  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
97%  radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
98%
99%  The format of the AdaptiveBlurImage method is:
100%
101%      Image *AdaptiveBlurImage(const Image *image,const double radius,
102%        const double sigma,ExceptionInfo *exception)
103%      Image *AdaptiveBlurImageChannel(const Image *image,
104%        const ChannelType channel,double radius,const double sigma,
105%        ExceptionInfo *exception)
106%
107%  A description of each parameter follows:
108%
109%    o image: the image.
110%
111%    o channel: the channel type.
112%
113%    o radius: the radius of the Gaussian, in pixels, not counting the center
114%      pixel.
115%
116%    o sigma: the standard deviation of the Laplacian, in pixels.
117%
118%    o exception: Return any errors or warnings in this structure.
119%
120*/
121
122MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
123  const double sigma,ExceptionInfo *exception)
124{
125  Image
126    *blur_image;
127
128  blur_image=AdaptiveBlurImageChannel(image,DefaultChannels,radius,sigma,
129    exception);
130  return(blur_image);
131}
132
133MagickExport Image *AdaptiveBlurImageChannel(const Image *image,
134  const ChannelType channel,const double radius,const double sigma,
135  ExceptionInfo *exception)
136{
137#define AdaptiveBlurImageTag  "Convolve/Image"
138#define MagickSigma  (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
139
140  double
141    **kernel;
142
143  Image
144    *blur_image,
145    *edge_image,
146    *gaussian_image;
147
148  long
149    j,
150    progress,
151    y;
152
153  MagickPixelPacket
154    zero;
155
156  MagickRealType
157    alpha,
158    normalize;
159
160  register long
161    i,
162    u,
163    v;
164
165  unsigned long
166    width;
167
168  ViewInfo
169    **blur_view,
170    **edge_view,
171    **image_view;
172
173  volatile MagickBooleanType
174    status;
175
176  assert(image != (const Image *) NULL);
177  assert(image->signature == MagickSignature);
178  if (image->debug != MagickFalse)
179    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
180  assert(exception != (ExceptionInfo *) NULL);
181  assert(exception->signature == MagickSignature);
182  blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
183  if (blur_image == (Image *) NULL)
184    return((Image *) NULL);
185  if (fabs(sigma) <= MagickEpsilon)
186    return(blur_image);
187  if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
188    {
189      InheritException(exception,&blur_image->exception);
190      blur_image=DestroyImage(blur_image);
191      return((Image *) NULL);
192    }
193  /*
194    Edge detect the image brighness channel, level, blur, and level again.
195  */
196  edge_image=EdgeImage(image,radius,exception);
197  if (edge_image == (Image *) NULL)
198    {
199      blur_image=DestroyImage(blur_image);
200      return((Image *) NULL);
201    }
202  (void) LevelImage(edge_image,"20%,95%");
203  gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
204  if (gaussian_image != (Image *) NULL)
205    {
206      edge_image=DestroyImage(edge_image);
207      edge_image=gaussian_image;
208    }
209  (void) LevelImage(edge_image,"10%,95%");
210  /*
211    Create a set of kernels from maximum (radius,sigma) to minimum.
212  */
213  width=GetOptimalKernelWidth2D(radius,sigma);
214  kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
215  if (kernel == (double **) NULL)
216    {
217      edge_image=DestroyImage(edge_image);
218      blur_image=DestroyImage(blur_image);
219      ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
220    }
221  (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
222  for (i=0; i < (long) width; i+=2)
223  {
224    kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
225      sizeof(**kernel));
226    if (kernel[i] == (double *) NULL)
227      break;
228    j=0;
229    normalize=0.0;
230    for (v=(-((long) (width-i)/2)); v <= (long) ((width-i)/2); v++)
231    {
232      for (u=(-((long) (width-i)/2)); u <= (long) ((width-i)/2); u++)
233      {
234        alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
235        kernel[i][j]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
236        if (((width-i) < 3) || (u != 0) || (v != 0))
237          normalize+=kernel[i][j];
238        j++;
239      }
240    }
241    kernel[i][j/2]=(double) ((-2.0)*normalize);
242    normalize=0.0;
243    for (j=0; j < (long) ((width-i)*(width-i)); j++)
244      normalize+=kernel[i][j];
245    if (fabs(normalize) <= MagickEpsilon)
246      normalize=1.0;
247    normalize=1.0/normalize;
248    for (j=0; j < (long) ((width-i)*(width-i)); j++)
249      kernel[i][j]=(double) (normalize*kernel[i][j]);
250  }
251  if (i < (long) width)
252    {
253      for (i-=2; i >= 0; i-=2)
254        kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
255      kernel=(double **) RelinquishMagickMemory(kernel);
256      edge_image=DestroyImage(edge_image);
257      blur_image=DestroyImage(blur_image);
258      ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
259    }
260  /*
261    Adaptively blur image.
262  */
263  status=MagickTrue;
264  progress=0;
265  GetMagickPixelPacket(image,&zero);
266  image_view=AcquireCacheViewThreadSet(image);
267  edge_view=AcquireCacheViewThreadSet(edge_image);
268  blur_view=AcquireCacheViewThreadSet(blur_image);
269  #pragma omp parallel for schedule(static,64)
270  for (y=0; y < (long) blur_image->rows; y++)
271  {
272    IndexPacket
273      *blur_indexes,
274      *indexes;
275
276    register const PixelPacket
277      *p,
278      *r;
279
280    register long
281      id,
282      x;
283
284    register PixelPacket
285      *q;
286
287    if (status == MagickFalse)
288      continue;
289    id=GetCacheViewThreadId();
290    r=AcquireCacheViewPixels(edge_view[id],0,y,edge_image->columns,1,
291      exception);
292    q=SetCacheViewPixels(blur_view[id],0,y,blur_image->columns,1);
293    if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
294      {
295        status=MagickFalse;
296        continue;
297      }
298    blur_indexes=GetCacheViewIndexes(blur_view[id]);
299    for (x=0; x < (long) blur_image->columns; x++)
300    {
301      MagickPixelPacket
302        pixel;
303
304      MagickRealType
305        alpha,
306        gamma;
307
308      register const double
309        *k;
310
311      register long
312        i,
313        u,
314        v;
315
316      gamma=0.0;
317      i=(long) (width*QuantumScale*PixelIntensity(r)+0.5);
318      if (i < 0)
319        i=0;
320      else
321        if (i > (long) width)
322          i=(long) width;
323      if ((i & 0x01) != 0)
324        i--;
325      p=AcquireCacheViewPixels(image_view[id],x-((long) (width-i)/2L),y-(long)
326        ((width-i)/2L),width-i,width-i,exception);
327      if (p == (const PixelPacket *) NULL)
328        break;
329      indexes=GetCacheViewIndexes(image_view[id]);
330      pixel=zero;
331      k=kernel[i];
332      for (v=0; v < (long) (width-i); v++)
333      {
334        for (u=0; u < (long) (width-i); u++)
335        {
336          alpha=1.0;
337          if (((channel & OpacityChannel) != 0) &&
338              (image->matte != MagickFalse))
339            alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
340          if ((channel & RedChannel) != 0)
341            pixel.red+=(*k)*alpha*p->red;
342          if ((channel & GreenChannel) != 0)
343            pixel.green+=(*k)*alpha*p->green;
344          if ((channel & BlueChannel) != 0)
345            pixel.blue+=(*k)*alpha*p->blue;
346          if ((channel & OpacityChannel) != 0)
347            pixel.opacity+=(*k)*p->opacity;
348          if (((channel & IndexChannel) != 0) &&
349              (image->colorspace == CMYKColorspace))
350            pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
351          gamma+=(*k)*alpha;
352          k++;
353          p++;
354        }
355      }
356      gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
357      if ((channel & RedChannel) != 0)
358        q->red=RoundToQuantum(gamma*pixel.red+image->bias);
359      if ((channel & GreenChannel) != 0)
360        q->green=RoundToQuantum(gamma*pixel.green+image->bias);
361      if ((channel & BlueChannel) != 0)
362        q->blue=RoundToQuantum(gamma*pixel.blue+image->bias);
363      if ((channel & OpacityChannel) != 0)
364        q->opacity=RoundToQuantum(pixel.opacity+image->bias);
365      if (((channel & IndexChannel) != 0) &&
366          (image->colorspace == CMYKColorspace))
367        blur_indexes[x]=RoundToQuantum(gamma*pixel.index+image->bias);
368      q++;
369      r++;
370    }
371    if (SyncCacheViewPixels(blur_view[id]) == MagickFalse)
372      status=MagickFalse;
373    if (image->progress_monitor != (MagickProgressMonitor) NULL)
374      {
375        MagickBooleanType
376          proceed;
377
378        #pragma omp critical
379        proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
380          image->rows);
381        if (proceed == MagickFalse)
382          status=MagickFalse;
383      }
384  }
385  blur_image->type=image->type;
386  blur_view=DestroyCacheViewThreadSet(blur_view);
387  edge_view=DestroyCacheViewThreadSet(edge_view);
388  image_view=DestroyCacheViewThreadSet(image_view);
389  edge_image=DestroyImage(edge_image);
390  for (i=0; i < (long) width;  i+=2)
391    kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
392  kernel=(double **) RelinquishMagickMemory(kernel);
393  if (status == MagickFalse)
394    blur_image=DestroyImage(blur_image);
395  return(blur_image);
396}
397
398/*
399%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
400%                                                                             %
401%                                                                             %
402%                                                                             %
403%     A d a p t i v e S h a r p e n I m a g e                                 %
404%                                                                             %
405%                                                                             %
406%                                                                             %
407%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
408%
409%  AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
410%  intensely near image edges and less intensely far from edges. We sharpen the
411%  image with a Gaussian operator of the given radius and standard deviation
412%  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
413%  radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
414%
415%  The format of the AdaptiveSharpenImage method is:
416%
417%      Image *AdaptiveSharpenImage(const Image *image,const double radius,
418%        const double sigma,ExceptionInfo *exception)
419%      Image *AdaptiveSharpenImageChannel(const Image *image,
420%        const ChannelType channel,double radius,const double sigma,
421%        ExceptionInfo *exception)
422%
423%  A description of each parameter follows:
424%
425%    o image: the image.
426%
427%    o channel: the channel type.
428%
429%    o radius: the radius of the Gaussian, in pixels, not counting the center
430%      pixel.
431%
432%    o sigma: the standard deviation of the Laplacian, in pixels.
433%
434%    o exception: Return any errors or warnings in this structure.
435%
436*/
437
438MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
439  const double sigma,ExceptionInfo *exception)
440{
441  Image
442    *sharp_image;
443
444  sharp_image=AdaptiveSharpenImageChannel(image,DefaultChannels,radius,sigma,
445    exception);
446  return(sharp_image);
447}
448
449MagickExport Image *AdaptiveSharpenImageChannel(const Image *image,
450  const ChannelType channel,const double radius,const double sigma,
451  ExceptionInfo *exception)
452{
453#define AdaptiveSharpenImageTag  "Convolve/Image"
454#define MagickSigma  (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
455
456  double
457    **kernel;
458
459  Image
460    *sharp_image,
461    *edge_image,
462    *gaussian_image;
463
464  long
465    j,
466    progress,
467    y;
468
469  MagickPixelPacket
470    zero;
471
472  MagickRealType
473    alpha,
474    normalize;
475
476  register long
477    i,
478    u,
479    v;
480
481  unsigned long
482    width;
483
484  ViewInfo
485    **sharp_view,
486    **edge_view,
487    **image_view;
488
489  volatile MagickBooleanType
490    status;
491
492  assert(image != (const Image *) NULL);
493  assert(image->signature == MagickSignature);
494  if (image->debug != MagickFalse)
495    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
496  assert(exception != (ExceptionInfo *) NULL);
497  assert(exception->signature == MagickSignature);
498  sharp_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
499  if (sharp_image == (Image *) NULL)
500    return((Image *) NULL);
501  if (fabs(sigma) <= MagickEpsilon)
502    return(sharp_image);
503  if (SetImageStorageClass(sharp_image,DirectClass) == MagickFalse)
504    {
505      InheritException(exception,&sharp_image->exception);
506      sharp_image=DestroyImage(sharp_image);
507      return((Image *) NULL);
508    }
509  /*
510    Edge detect the image brighness channel, level, sharp, and level again.
511  */
512  edge_image=EdgeImage(image,radius,exception);
513  if (edge_image == (Image *) NULL)
514    {
515      sharp_image=DestroyImage(sharp_image);
516      return((Image *) NULL);
517    }
518  (void) LevelImage(edge_image,"20%,95%");
519  gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
520  if (gaussian_image != (Image *) NULL)
521    {
522      edge_image=DestroyImage(edge_image);
523      edge_image=gaussian_image;
524    }
525  (void) LevelImage(edge_image,"10%,95%");
526  /*
527    Create a set of kernels from maximum (radius,sigma) to minimum.
528  */
529  width=GetOptimalKernelWidth2D(radius,sigma);
530  kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
531  if (kernel == (double **) NULL)
532    {
533      edge_image=DestroyImage(edge_image);
534      sharp_image=DestroyImage(sharp_image);
535      ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
536    }
537  (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
538  for (i=0; i < (long) width; i+=2)
539  {
540    kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
541      sizeof(**kernel));
542    if (kernel[i] == (double *) NULL)
543      break;
544    j=0;
545    normalize=0.0;
546    for (v=(-((long) (width-i)/2)); v <= (long) ((width-i)/2); v++)
547    {
548      for (u=(-((long) (width-i)/2)); u <= (long) ((width-i)/2); u++)
549      {
550        alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
551        kernel[i][j]=(double) (-alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
552        if (((width-i) < 3) || (u != 0) || (v != 0))
553          normalize+=kernel[i][j];
554        j++;
555      }
556    }
557    kernel[i][j/2]=(double) ((-2.0)*normalize);
558    normalize=0.0;
559    for (j=0; j < (long) ((width-i)*(width-i)); j++)
560      normalize+=kernel[i][j];
561    if (fabs(normalize) <= MagickEpsilon)
562      normalize=1.0;
563    normalize=1.0/normalize;
564    for (j=0; j < (long) ((width-i)*(width-i)); j++)
565      kernel[i][j]=(double) (normalize*kernel[i][j]);
566  }
567  if (i < (long) width)
568    {
569      for (i-=2; i >= 0; i-=2)
570        kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
571      kernel=(double **) RelinquishMagickMemory(kernel);
572      edge_image=DestroyImage(edge_image);
573      sharp_image=DestroyImage(sharp_image);
574      ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
575    }
576  /*
577    Adaptively sharp image.
578  */
579  status=MagickTrue;
580  progress=0;
581  GetMagickPixelPacket(image,&zero);
582  image_view=AcquireCacheViewThreadSet(image);
583  edge_view=AcquireCacheViewThreadSet(edge_image);
584  sharp_view=AcquireCacheViewThreadSet(sharp_image);
585  #pragma omp parallel for schedule(static,64)
586  for (y=0; y < (long) sharp_image->rows; y++)
587  {
588    IndexPacket
589      *sharp_indexes,
590      *indexes;
591
592    register const PixelPacket
593      *p,
594      *r;
595
596    register long
597      id,
598      x;
599
600    register PixelPacket
601      *q;
602
603    if (status == MagickFalse)
604      continue;
605    id=GetCacheViewThreadId();
606    r=AcquireCacheViewPixels(edge_view[id],0,y,edge_image->columns,1,
607      exception);
608    q=SetCacheViewPixels(sharp_view[id],0,y,sharp_image->columns,1);
609    if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
610      {
611        status=MagickFalse;
612        continue;
613      }
614    sharp_indexes=GetCacheViewIndexes(sharp_view[id]);
615    for (x=0; x < (long) sharp_image->columns; x++)
616    {
617      MagickPixelPacket
618        pixel;
619
620      MagickRealType
621        alpha,
622        gamma;
623
624      register const double
625        *k;
626
627      register long
628        i,
629        u,
630        v;
631
632      gamma=0.0;
633      i=(long) (width*(QuantumRange-QuantumScale*PixelIntensity(r))+0.5);
634      if (i < 0)
635        i=0;
636      else
637        if (i > (long) width)
638          i=(long) width;
639      if ((i & 0x01) != 0)
640        i--;
641      p=AcquireCacheViewPixels(image_view[id],x-((long) (width-i)/2L),y-(long)
642        ((width-i)/2L),width-i,width-i,exception);
643      if (p == (const PixelPacket *) NULL)
644        break;
645      indexes=GetCacheViewIndexes(image_view[id]);
646      k=kernel[i];
647      pixel=zero;
648      for (v=0; v < (long) (width-i); v++)
649      {
650        for (u=0; u < (long) (width-i); u++)
651        {
652          alpha=1.0;
653          if (((channel & OpacityChannel) != 0) &&
654              (image->matte != MagickFalse))
655            alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
656          if ((channel & RedChannel) != 0)
657            pixel.red+=(*k)*alpha*p->red;
658          if ((channel & GreenChannel) != 0)
659            pixel.green+=(*k)*alpha*p->green;
660          if ((channel & BlueChannel) != 0)
661            pixel.blue+=(*k)*alpha*p->blue;
662          if ((channel & OpacityChannel) != 0)
663            pixel.opacity+=(*k)*p->opacity;
664          if (((channel & IndexChannel) != 0) &&
665              (image->colorspace == CMYKColorspace))
666            pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
667          gamma+=(*k)*alpha;
668          k++;
669          p++;
670        }
671      }
672      gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
673      if ((channel & RedChannel) != 0)
674        q->red=RoundToQuantum(gamma*pixel.red+image->bias);
675      if ((channel & GreenChannel) != 0)
676        q->green=RoundToQuantum(gamma*pixel.green+image->bias);
677      if ((channel & BlueChannel) != 0)
678        q->blue=RoundToQuantum(gamma*pixel.blue+image->bias);
679      if ((channel & OpacityChannel) != 0)
680        q->opacity=RoundToQuantum(pixel.opacity+image->bias);
681      if (((channel & IndexChannel) != 0) &&
682          (image->colorspace == CMYKColorspace))
683        sharp_indexes[x]=RoundToQuantum(gamma*pixel.index+image->bias);
684      q++;
685      r++;
686    }
687    if (SyncCacheViewPixels(sharp_view[id]) == MagickFalse)
688      status=MagickFalse;
689    if (image->progress_monitor != (MagickProgressMonitor) NULL)
690      {
691        MagickBooleanType
692          proceed;
693
694        #pragma omp critical
695        proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
696          image->rows);
697        if (proceed == MagickFalse)
698          status=MagickFalse;
699      }
700  }
701  sharp_image->type=image->type;
702  sharp_view=DestroyCacheViewThreadSet(sharp_view);
703  edge_view=DestroyCacheViewThreadSet(edge_view);
704  image_view=DestroyCacheViewThreadSet(image_view);
705  edge_image=DestroyImage(edge_image);
706  for (i=0; i < (long) width;  i+=2)
707    kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
708  kernel=(double **) RelinquishMagickMemory(kernel);
709  if (status == MagickFalse)
710    sharp_image=DestroyImage(sharp_image);
711  return(sharp_image);
712}
713
714/*
715%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
716%                                                                             %
717%                                                                             %
718%                                                                             %
719%     B l u r I m a g e                                                       %
720%                                                                             %
721%                                                                             %
722%                                                                             %
723%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
724%
725%  BlurImage() blurs an image.  We convolve the image with a Gaussian operator
726%  of the given radius and standard deviation (sigma).  For reasonable results,
727%  the radius should be larger than sigma.  Use a radius of 0 and BlurImage()
728%  selects a suitable radius for you.
729%
730%  BlurImage() differs from GaussianBlurImage() in that it uses a separable
731%  kernel which is faster but mathematically equivalent to the non-separable
732%  kernel.
733%
734%  The format of the BlurImage method is:
735%
736%      Image *BlurImage(const Image *image,const double radius,
737%        const double sigma,ExceptionInfo *exception)
738%      Image *BlurImageChannel(const Image *image,const ChannelType channel,
739%        const double radius,const double sigma,ExceptionInfo *exception)
740%
741%  A description of each parameter follows:
742%
743%    o image: the image.
744%
745%    o channel: the channel type.
746%
747%    o radius: the radius of the Gaussian, in pixels, not counting the center
748%      pixel.
749%
750%    o sigma: the standard deviation of the Gaussian, in pixels.
751%
752%    o exception: Return any errors or warnings in this structure.
753%
754*/
755
756MagickExport Image *BlurImage(const Image *image,const double radius,
757  const double sigma,ExceptionInfo *exception)
758{
759  Image
760    *blur_image;
761
762  blur_image=BlurImageChannel(image,DefaultChannels,radius,sigma,exception);
763  return(blur_image);
764}
765
766static double *GetBlurKernel(unsigned long width,const MagickRealType sigma)
767{
768#define KernelRank 3
769
770  double
771    *kernel;
772
773  long
774    bias;
775
776  MagickRealType
777    alpha,
778    normalize;
779
780  register long
781    i;
782
783  /*
784    Generate a 1-D convolution kernel.
785  */
786  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
787  kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
788  if (kernel == (double *) NULL)
789    return(0);
790  (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
791  bias=KernelRank*(long) width/2;
792  for (i=(-bias); i <= bias; i++)
793  {
794    alpha=exp((-((double) (i*i))/(double) (2.0*KernelRank*KernelRank*
795      MagickSigma*MagickSigma)));
796    kernel[(i+bias)/KernelRank]+=(double) (alpha/(MagickSQ2PI*sigma));
797  }
798  normalize=0.0;
799  for (i=0; i < (long) width; i++)
800    normalize+=kernel[i];
801  for (i=0; i < (long) width; i++)
802    kernel[i]/=normalize;
803  return(kernel);
804}
805
806MagickExport Image *BlurImageChannel(const Image *image,
807  const ChannelType channel,const double radius,const double sigma,
808  ExceptionInfo *exception)
809{
810#define BlurImageTag  "Blur/Image"
811
812  double
813    *kernel;
814
815  Image
816    *blur_image;
817
818  long
819    progress,
820    x,
821    y;
822
823  MagickPixelPacket
824    zero;
825
826  MagickRealType
827    bias;
828
829  register long
830    i;
831
832  unsigned long
833    width;
834
835  ViewInfo
836    **blur_view,
837    **image_view;
838
839  volatile MagickBooleanType
840    status;
841
842  /*
843    Initialize blur image attributes.
844  */
845  assert(image != (Image *) NULL);
846  assert(image->signature == MagickSignature);
847  if (image->debug != MagickFalse)
848    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
849  assert(exception != (ExceptionInfo *) NULL);
850  assert(exception->signature == MagickSignature);
851  blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
852  if (blur_image == (Image *) NULL)
853    return((Image *) NULL);
854  if (fabs(sigma) <= MagickEpsilon)
855    return(blur_image);
856  if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
857    {
858      InheritException(exception,&blur_image->exception);
859      blur_image=DestroyImage(blur_image);
860      return((Image *) NULL);
861    }
862  width=GetOptimalKernelWidth1D(radius,sigma);
863  kernel=GetBlurKernel(width,sigma);
864  if (kernel == (double *) NULL)
865    {
866      blur_image=DestroyImage(blur_image);
867      return((Image *) NULL);
868      ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
869    }
870  if (image->debug != MagickFalse)
871    {
872      char
873        format[MaxTextExtent],
874        *message;
875
876      register const double
877        *k;
878
879      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
880        "  BlurImage with %ld kernel:",width);
881      message=AcquireString("");
882      k=kernel;
883      for (i=0; i < (long) width; i++)
884      {
885        *message='\0';
886        (void) FormatMagickString(format,MaxTextExtent,"%ld: ",i);
887        (void) ConcatenateString(&message,format);
888        (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
889        (void) ConcatenateString(&message,format);
890        (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
891      }
892      message=DestroyString(message);
893    }
894  /*
895    Blur rows.
896  */
897  status=MagickTrue;
898  progress=0;
899  GetMagickPixelPacket(image,&zero);
900  bias=image->bias;
901  image_view=AcquireCacheViewThreadSet(image);
902  blur_view=AcquireCacheViewThreadSet(blur_image);
903  #pragma omp parallel for schedule(static,64)
904  for (y=0; y < (long) blur_image->rows; y++)
905  {
906    const IndexPacket
907      *indexes;
908
909    IndexPacket
910      *blur_indexes;
911
912    register const PixelPacket
913      *p;
914
915    register long
916      id,
917      x;
918
919    register PixelPacket
920      *q;
921
922    if (status == MagickFalse)
923      continue;
924    id=GetCacheViewThreadId();
925    p=AcquireCacheViewPixels(image_view[id],-((long) width/2L),y,image->columns+
926      width,1,exception);
927    q=SetCacheViewPixels(blur_view[id],0,y,blur_image->columns,1);
928    if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
929      {
930        status=MagickFalse;
931        continue;
932      }
933    indexes=AcquireCacheViewIndexes(image_view[id]);
934    blur_indexes=GetCacheViewIndexes(blur_view[id]);
935    for (x=0; x < (long) blur_image->columns; x++)
936    {
937      MagickPixelPacket
938        pixel;
939
940      register const double
941        *k;
942
943      register long
944        i;
945
946      pixel=zero;
947      k=kernel;
948      if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
949        {
950          for (i=0; i < (long) width; i++)
951          {
952            pixel.red+=(*k)*(p+i)->red;
953            pixel.green+=(*k)*(p+i)->green;
954            pixel.blue+=(*k)*(p+i)->blue;
955            pixel.opacity+=(*k)*(p+i)->opacity;
956            k++;
957          }
958          if ((channel & RedChannel) != 0)
959            q->red=RoundToQuantum(pixel.red+bias);
960          if ((channel & GreenChannel) != 0)
961            q->green=RoundToQuantum(pixel.green+bias);
962          if ((channel & BlueChannel) != 0)
963            q->blue=RoundToQuantum(pixel.blue+bias);
964          if ((channel & OpacityChannel) != 0)
965            q->opacity=RoundToQuantum(pixel.opacity+bias);
966          if (((channel & IndexChannel) != 0) &&
967              (image->colorspace == CMYKColorspace))
968            {
969              k=kernel;
970              for (i=0; i < (long) width; i++)
971              {
972                pixel.index+=(*k)*indexes[x+i];
973                k++;
974              }
975              blur_indexes[x]=RoundToQuantum(pixel.index+bias);
976            }
977        }
978      else
979        {
980          MagickRealType
981            alpha,
982            gamma;
983
984          gamma=0.0;
985          for (i=0; i < (long) width; i++)
986          {
987            alpha=(MagickRealType) (QuantumScale*(QuantumRange-(p+i)->opacity));
988            pixel.red+=(*k)*alpha*(p+i)->red;
989            pixel.green+=(*k)*alpha*(p+i)->green;
990            pixel.blue+=(*k)*alpha*(p+i)->blue;
991            pixel.opacity+=(*k)*(p+i)->opacity;
992            gamma+=(*k)*alpha;
993            k++;
994          }
995          gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
996          if ((channel & RedChannel) != 0)
997            q->red=RoundToQuantum(gamma*pixel.red+bias);
998          if ((channel & GreenChannel) != 0)
999            q->green=RoundToQuantum(gamma*pixel.green+bias);
1000          if ((channel & BlueChannel) != 0)
1001            q->blue=RoundToQuantum(gamma*pixel.blue+bias);
1002          if ((channel & OpacityChannel) != 0)
1003            q->opacity=RoundToQuantum(pixel.opacity+bias);
1004          if (((channel & IndexChannel) != 0) &&
1005              (image->colorspace == CMYKColorspace))
1006            {
1007              k=kernel;
1008              for (i=0; i < (long) width; i++)
1009              {
1010                alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1011                  (p+i)->opacity));
1012                pixel.index+=(*k)*alpha*indexes[x+i];
1013                k++;
1014              }
1015              blur_indexes[x]=RoundToQuantum(gamma*pixel.index+bias);
1016            }
1017        }
1018      p++;
1019      q++;
1020    }
1021    if (SyncCacheViewPixels(blur_view[id]) == MagickFalse)
1022      status=MagickFalse;
1023    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1024      {
1025        MagickBooleanType
1026          proceed;
1027
1028        #pragma omp critical
1029        proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1030          blur_image->columns);
1031        if (proceed == MagickFalse)
1032          status=MagickFalse;
1033      }
1034  }
1035  /*
1036    Blur columns.
1037  */
1038  image_view=DestroyCacheViewThreadSet(image_view);
1039  image_view=AcquireCacheViewThreadSet(blur_image);