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