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