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