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