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