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