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