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