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