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