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