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