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-2019 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*(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*(columns+2)+x_offset);
935 s=q-(y_offset*(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 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2596 if (preview_image == (Image *) NULL)
2597 break;
2598 geometry.width=(size_t) (2*i+2);
2599 geometry.height=(size_t) (2*i+2);
2600 geometry.x=(i-1)/2;
2601 geometry.y=(i-1)/2;
2602 (void) RaiseImage(preview_image,&geometry,MagickTrue,exception);
2603 (void) FormatLocaleString(label,MagickPathExtent,
2604 "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
2605 geometry.height,(double) geometry.x,(double) geometry.y);
2606 break;
2607 }
2608 case SegmentPreview:
2609 {
2610 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2611 if (preview_image == (Image *) NULL)
2612 break;
2613 threshold+=0.4f;
2614 (void) SegmentImage(preview_image,sRGBColorspace,MagickFalse,threshold,
2615 threshold,exception);
2616 (void) FormatLocaleString(label,MagickPathExtent,"segment %gx%g",
2617 threshold,threshold);
2618 break;
2619 }
2620 case SwirlPreview:
2621 {
2622 preview_image=SwirlImage(thumbnail,degrees,image->interpolate,
2623 exception);
2624 (void) FormatLocaleString(label,MagickPathExtent,"swirl %g",degrees);
2625 degrees+=45.0;
2626 break;
2627 }
2628 case ImplodePreview:
2629 {
2630 degrees+=0.1f;
2631 preview_image=ImplodeImage(thumbnail,degrees,image->interpolate,
2632 exception);
2633 (void) FormatLocaleString(label,MagickPathExtent,"implode %g",degrees);
2634 break;
2635 }
2636 case WavePreview:
2637 {
2638 degrees+=5.0f;
2639 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,
2640 image->interpolate,exception);
2641 (void) FormatLocaleString(label,MagickPathExtent,"wave %gx%g",0.5*
2642 degrees,2.0*degrees);
2643 break;
2644 }
2645 case OilPaintPreview:
2646 {
2647 preview_image=OilPaintImage(thumbnail,(double) radius,(double) sigma,
2648 exception);
2649 (void) FormatLocaleString(label,MagickPathExtent,"charcoal %gx%g",
2650 radius,sigma);
2651 break;
2652 }
2653 case CharcoalDrawingPreview:
2654 {
2655 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2656 exception);
2657 (void) FormatLocaleString(label,MagickPathExtent,"charcoal %gx%g",
2658 radius,sigma);
2659 break;
2660 }
2661 case JPEGPreview:
2662 {
2663 char
2664 filename[MagickPathExtent];
2665
2666 int
2667 file;
2668
2669 MagickBooleanType
2670 status;
2671
2672 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2673 if (preview_image == (Image *) NULL)
2674 break;
2675 preview_info->quality=(size_t) percentage;
2676 (void) FormatLocaleString(factor,MagickPathExtent,"%.20g",(double)
2677 preview_info->quality);
2678 file=AcquireUniqueFileResource(filename);
2679 if (file != -1)
2680 file=close(file)-1;
2681 (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
2682 "jpeg:%s",filename);
2683 status=WriteImage(preview_info,preview_image,exception);
2684 if (status != MagickFalse)
2685 {
2686 Image
2687 *quality_image;
2688
2689 (void) CopyMagickString(preview_info->filename,
2690 preview_image->filename,MagickPathExtent);
2691 quality_image=ReadImage(preview_info,exception);
2692 if (quality_image != (Image *) NULL)
2693 {
2694 preview_image=DestroyImage(preview_image);
2695 preview_image=quality_image;
2696 }
2697 }
2698 (void) RelinquishUniqueFileResource(preview_image->filename);
2699 if ((GetBlobSize(preview_image)/1024) >= 1024)
2700 (void) FormatLocaleString(label,MagickPathExtent,"quality %s\n%gmb ",
2701 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2702 1024.0/1024.0);
2703 else
2704 if (GetBlobSize(preview_image) >= 1024)
2705 (void) FormatLocaleString(label,MagickPathExtent,
2706 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
2707 GetBlobSize(preview_image))/1024.0);
2708 else
2709 (void) FormatLocaleString(label,MagickPathExtent,
2710 "quality %s\n%.20gb ",factor,(double) ((MagickOffsetType)
2711 GetBlobSize(thumbnail)));
2712 break;
2713 }
2714 }
2715 thumbnail=DestroyImage(thumbnail);
2716 percentage+=12.5;
2717 radius+=0.5;
2718 sigma+=0.25;
2719 if (preview_image == (Image *) NULL)
2720 break;
2721 (void) DeleteImageProperty(preview_image,"label");
2722 (void) SetImageProperty(preview_image,"label",label,exception);
2723 AppendImageToList(&images,preview_image);
2724 proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2725 NumberTiles);
2726 if (proceed == MagickFalse)
2727 break;
2728 }
2729 if (images == (Image *) NULL)
2730 {
2731 preview_info=DestroyImageInfo(preview_info);
2732 return((Image *) NULL);
2733 }
2734 /*
2735 Create the montage.
2736 */
2737 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2738 (void) CopyMagickString(montage_info->filename,image->filename,
2739 MagickPathExtent);
2740 montage_info->shadow=MagickTrue;
2741 (void) CloneString(&montage_info->tile,"3x3");
2742 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2743 (void) CloneString(&montage_info->frame,DefaultTileFrame);
2744 montage_image=MontageImages(images,montage_info,exception);
2745 montage_info=DestroyMontageInfo(montage_info);
2746 images=DestroyImageList(images);
2747 if (montage_image == (Image *) NULL)
2748 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2749 if (montage_image->montage != (char *) NULL)
2750 {
2751 /*
2752 Free image directory.
2753 */
2754 montage_image->montage=(char *) RelinquishMagickMemory(
2755 montage_image->montage);
2756 if (image->directory != (char *) NULL)
2757 montage_image->directory=(char *) RelinquishMagickMemory(
2758 montage_image->directory);
2759 }
2760 preview_info=DestroyImageInfo(preview_info);
2761 return(montage_image);
2762 }
2763
2764 /*
2765 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2766 % %
2767 % %
2768 % %
2769 % R o t a t i o n a l B l u r I m a g e %
2770 % %
2771 % %
2772 % %
2773 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2774 %
2775 % RotationalBlurImage() applies a radial blur to the image.
2776 %
2777 % Andrew Protano contributed this effect.
2778 %
2779 % The format of the RotationalBlurImage method is:
2780 %
2781 % Image *RotationalBlurImage(const Image *image,const double angle,
2782 % ExceptionInfo *exception)
2783 %
2784 % A description of each parameter follows:
2785 %
2786 % o image: the image.
2787 %
2788 % o angle: the angle of the radial blur.
2789 %
2790 % o blur: the blur.
2791 %
2792 % o exception: return any errors or warnings in this structure.
2793 %
2794 */
RotationalBlurImage(const Image * image,const double angle,ExceptionInfo * exception)2795 MagickExport Image *RotationalBlurImage(const Image *image,const double angle,
2796 ExceptionInfo *exception)
2797 {
2798 CacheView
2799 *blur_view,
2800 *image_view,
2801 *radial_view;
2802
2803 double
2804 blur_radius,
2805 *cos_theta,
2806 offset,
2807 *sin_theta,
2808 theta;
2809
2810 Image
2811 *blur_image;
2812
2813 MagickBooleanType
2814 status;
2815
2816 MagickOffsetType
2817 progress;
2818
2819 PointInfo
2820 blur_center;
2821
2822 register ssize_t
2823 i;
2824
2825 size_t
2826 n;
2827
2828 ssize_t
2829 y;
2830
2831 /*
2832 Allocate blur image.
2833 */
2834 assert(image != (Image *) NULL);
2835 assert(image->signature == MagickCoreSignature);
2836 if (image->debug != MagickFalse)
2837 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2838 assert(exception != (ExceptionInfo *) NULL);
2839 assert(exception->signature == MagickCoreSignature);
2840 #if defined(MAGICKCORE_OPENCL_SUPPORT)
2841 blur_image=AccelerateRotationalBlurImage(image,angle,exception);
2842 if (blur_image != (Image *) NULL)
2843 return(blur_image);
2844 #endif
2845 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2846 if (blur_image == (Image *) NULL)
2847 return((Image *) NULL);
2848 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2849 {
2850 blur_image=DestroyImage(blur_image);
2851 return((Image *) NULL);
2852 }
2853 blur_center.x=(double) (image->columns-1)/2.0;
2854 blur_center.y=(double) (image->rows-1)/2.0;
2855 blur_radius=hypot(blur_center.x,blur_center.y);
2856 n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
2857 theta=DegreesToRadians(angle)/(double) (n-1);
2858 cos_theta=(double *) AcquireQuantumMemory((size_t) n,
2859 sizeof(*cos_theta));
2860 sin_theta=(double *) AcquireQuantumMemory((size_t) n,
2861 sizeof(*sin_theta));
2862 if ((cos_theta == (double *) NULL) ||
2863 (sin_theta == (double *) NULL))
2864 {
2865 if (cos_theta != (double *) NULL)
2866 cos_theta=(double *) RelinquishMagickMemory(cos_theta);
2867 if (sin_theta != (double *) NULL)
2868 sin_theta=(double *) RelinquishMagickMemory(sin_theta);
2869 blur_image=DestroyImage(blur_image);
2870 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2871 }
2872 offset=theta*(double) (n-1)/2.0;
2873 for (i=0; i < (ssize_t) n; i++)
2874 {
2875 cos_theta[i]=cos((double) (theta*i-offset));
2876 sin_theta[i]=sin((double) (theta*i-offset));
2877 }
2878 /*
2879 Radial blur image.
2880 */
2881 status=MagickTrue;
2882 progress=0;
2883 image_view=AcquireVirtualCacheView(image,exception);
2884 radial_view=AcquireVirtualCacheView(image,exception);
2885 blur_view=AcquireAuthenticCacheView(blur_image,exception);
2886 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2887 #pragma omp parallel for schedule(static) shared(progress,status) \
2888 magick_number_threads(image,blur_image,image->rows,1)
2889 #endif
2890 for (y=0; y < (ssize_t) image->rows; y++)
2891 {
2892 register const Quantum
2893 *magick_restrict p;
2894
2895 register Quantum
2896 *magick_restrict q;
2897
2898 register ssize_t
2899 x;
2900
2901 if (status == MagickFalse)
2902 continue;
2903 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2904 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2905 exception);
2906 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2907 {
2908 status=MagickFalse;
2909 continue;
2910 }
2911 for (x=0; x < (ssize_t) image->columns; x++)
2912 {
2913 double
2914 radius;
2915
2916 PointInfo
2917 center;
2918
2919 register ssize_t
2920 i;
2921
2922 size_t
2923 step;
2924
2925 center.x=(double) x-blur_center.x;
2926 center.y=(double) y-blur_center.y;
2927 radius=hypot((double) center.x,center.y);
2928 if (radius == 0)
2929 step=1;
2930 else
2931 {
2932 step=(size_t) (blur_radius/radius);
2933 if (step == 0)
2934 step=1;
2935 else
2936 if (step >= n)
2937 step=n-1;
2938 }
2939 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2940 {
2941 double
2942 gamma,
2943 pixel;
2944
2945 PixelChannel
2946 channel;
2947
2948 PixelTrait
2949 blur_traits,
2950 traits;
2951
2952 register const Quantum
2953 *magick_restrict r;
2954
2955 register ssize_t
2956 j;
2957
2958 channel=GetPixelChannelChannel(image,i);
2959 traits=GetPixelChannelTraits(image,channel);
2960 blur_traits=GetPixelChannelTraits(blur_image,channel);
2961 if ((traits == UndefinedPixelTrait) ||
2962 (blur_traits == UndefinedPixelTrait))
2963 continue;
2964 if ((blur_traits & CopyPixelTrait) != 0)
2965 {
2966 SetPixelChannel(blur_image,channel,p[i],q);
2967 continue;
2968 }
2969 gamma=0.0;
2970 pixel=0.0;
2971 if ((GetPixelChannelTraits(image,AlphaPixelChannel) == UndefinedPixelTrait) ||
2972 (channel == AlphaPixelChannel))
2973 {
2974 for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2975 {
2976 r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
2977 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2978 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2979 1,1,exception);
2980 if (r == (const Quantum *) NULL)
2981 {
2982 status=MagickFalse;
2983 continue;
2984 }
2985 pixel+=r[i];
2986 gamma++;
2987 }
2988 gamma=PerceptibleReciprocal(gamma);
2989 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2990 continue;
2991 }
2992 for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2993 {
2994 double
2995 alpha;
2996
2997 r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
2998 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2999 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
3000 1,1,exception);
3001 if (r == (const Quantum *) NULL)
3002 {
3003 status=MagickFalse;
3004 continue;
3005 }
3006 alpha=(double) QuantumScale*GetPixelAlpha(image,r);
3007 pixel+=alpha*r[i];
3008 gamma+=alpha;
3009 }
3010 gamma=PerceptibleReciprocal(gamma);
3011 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3012 }
3013 p+=GetPixelChannels(image);
3014 q+=GetPixelChannels(blur_image);
3015 }
3016 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3017 status=MagickFalse;
3018 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3019 {
3020 MagickBooleanType
3021 proceed;
3022
3023 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3024 #pragma omp atomic
3025 #endif
3026 progress++;
3027 proceed=SetImageProgress(image,BlurImageTag,progress,image->rows);
3028 if (proceed == MagickFalse)
3029 status=MagickFalse;
3030 }
3031 }
3032 blur_view=DestroyCacheView(blur_view);
3033 radial_view=DestroyCacheView(radial_view);
3034 image_view=DestroyCacheView(image_view);
3035 cos_theta=(double *) RelinquishMagickMemory(cos_theta);
3036 sin_theta=(double *) RelinquishMagickMemory(sin_theta);
3037 if (status == MagickFalse)
3038 blur_image=DestroyImage(blur_image);
3039 return(blur_image);
3040 }
3041
3042 /*
3043 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3044 % %
3045 % %
3046 % %
3047 % S e l e c t i v e B l u r I m a g e %
3048 % %
3049 % %
3050 % %
3051 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3052 %
3053 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3054 % It is similar to the unsharpen mask that sharpens everything with contrast
3055 % above a certain threshold.
3056 %
3057 % The format of the SelectiveBlurImage method is:
3058 %
3059 % Image *SelectiveBlurImage(const Image *image,const double radius,
3060 % const double sigma,const double threshold,ExceptionInfo *exception)
3061 %
3062 % A description of each parameter follows:
3063 %
3064 % o image: the image.
3065 %
3066 % o radius: the radius of the Gaussian, in pixels, not counting the center
3067 % pixel.
3068 %
3069 % o sigma: the standard deviation of the Gaussian, in pixels.
3070 %
3071 % o threshold: only pixels within this contrast threshold are included
3072 % in the blur operation.
3073 %
3074 % o exception: return any errors or warnings in this structure.
3075 %
3076 */
SelectiveBlurImage(const Image * image,const double radius,const double sigma,const double threshold,ExceptionInfo * exception)3077 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
3078 const double sigma,const double threshold,ExceptionInfo *exception)
3079 {
3080 #define SelectiveBlurImageTag "SelectiveBlur/Image"
3081
3082 CacheView
3083 *blur_view,
3084 *image_view,
3085 *luminance_view;
3086
3087 Image
3088 *blur_image,
3089 *luminance_image;
3090
3091 MagickBooleanType
3092 status;
3093
3094 MagickOffsetType
3095 progress;
3096
3097 MagickRealType
3098 *kernel;
3099
3100 register ssize_t
3101 i;
3102
3103 size_t
3104 width;
3105
3106 ssize_t
3107 center,
3108 j,
3109 u,
3110 v,
3111 y;
3112
3113 /*
3114 Initialize blur image attributes.
3115 */
3116 assert(image != (Image *) NULL);
3117 assert(image->signature == MagickCoreSignature);
3118 if (image->debug != MagickFalse)
3119 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3120 assert(exception != (ExceptionInfo *) NULL);
3121 assert(exception->signature == MagickCoreSignature);
3122 width=GetOptimalKernelWidth1D(radius,sigma);
3123 kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
3124 width,width*sizeof(*kernel)));
3125 if (kernel == (MagickRealType *) NULL)
3126 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3127 j=(ssize_t) (width-1)/2;
3128 i=0;
3129 for (v=(-j); v <= j; v++)
3130 {
3131 for (u=(-j); u <= j; u++)
3132 kernel[i++]=(MagickRealType) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
3133 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3134 }
3135 if (image->debug != MagickFalse)
3136 {
3137 char
3138 format[MagickPathExtent],
3139 *message;
3140
3141 register const MagickRealType
3142 *k;
3143
3144 ssize_t
3145 u,
3146 v;
3147
3148 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
3149 " SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
3150 width);
3151 message=AcquireString("");
3152 k=kernel;
3153 for (v=0; v < (ssize_t) width; v++)
3154 {
3155 *message='\0';
3156 (void) FormatLocaleString(format,MagickPathExtent,"%.20g: ",(double) v);
3157 (void) ConcatenateString(&message,format);
3158 for (u=0; u < (ssize_t) width; u++)
3159 {
3160 (void) FormatLocaleString(format,MagickPathExtent,"%+f ",(double)
3161 *k++);
3162 (void) ConcatenateString(&message,format);
3163 }
3164 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3165 }
3166 message=DestroyString(message);
3167 }
3168 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3169 if (blur_image == (Image *) NULL)
3170 return((Image *) NULL);
3171 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
3172 {
3173 blur_image=DestroyImage(blur_image);
3174 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3175 return((Image *) NULL);
3176 }
3177 luminance_image=CloneImage(image,0,0,MagickTrue,exception);
3178 if (luminance_image == (Image *) NULL)
3179 {
3180 blur_image=DestroyImage(blur_image);
3181 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3182 return((Image *) NULL);
3183 }
3184 status=TransformImageColorspace(luminance_image,GRAYColorspace,exception);
3185 if (status == MagickFalse)
3186 {
3187 luminance_image=DestroyImage(luminance_image);
3188 blur_image=DestroyImage(blur_image);
3189 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3190 return((Image *) NULL);
3191 }
3192 /*
3193 Threshold blur image.
3194 */
3195 status=MagickTrue;
3196 progress=0;
3197 center=(ssize_t) (GetPixelChannels(image)*(image->columns+width)*
3198 ((width-1)/2L)+GetPixelChannels(image)*((width-1)/2L));
3199 image_view=AcquireVirtualCacheView(image,exception);
3200 luminance_view=AcquireVirtualCacheView(luminance_image,exception);
3201 blur_view=AcquireAuthenticCacheView(blur_image,exception);
3202 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3203 #pragma omp parallel for schedule(static) shared(progress,status) \
3204 magick_number_threads(image,blur_image,image->rows,1)
3205 #endif
3206 for (y=0; y < (ssize_t) image->rows; y++)
3207 {
3208 double
3209 contrast;
3210
3211 MagickBooleanType
3212 sync;
3213
3214 register const Quantum
3215 *magick_restrict l,
3216 *magick_restrict p;
3217
3218 register Quantum
3219 *magick_restrict q;
3220
3221 register ssize_t
3222 x;
3223
3224 if (status == MagickFalse)
3225 continue;
3226 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) (width-1)/2L),y-(ssize_t)
3227 ((width-1)/2L),image->columns+width,width,exception);
3228 l=GetCacheViewVirtualPixels(luminance_view,-((ssize_t) (width-1)/2L),y-
3229 (ssize_t) ((width-1)/2L),luminance_image->columns+width,width,exception);
3230 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3231 exception);
3232 if ((p == (const Quantum *) NULL) || (l == (const Quantum *) NULL) ||
3233 (q == (Quantum *) NULL))
3234 {
3235 status=MagickFalse;
3236 continue;
3237 }
3238 for (x=0; x < (ssize_t) image->columns; x++)
3239 {
3240 double
3241 intensity;
3242
3243 register ssize_t
3244 i;
3245
3246 intensity=GetPixelIntensity(image,p+center);
3247 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3248 {
3249 double
3250 alpha,
3251 gamma,
3252 pixel;
3253
3254 PixelChannel
3255 channel;
3256
3257 PixelTrait
3258 blur_traits,
3259 traits;
3260
3261 register const MagickRealType
3262 *magick_restrict k;
3263
3264 register const Quantum
3265 *magick_restrict luminance_pixels,
3266 *magick_restrict pixels;
3267
3268 register ssize_t
3269 u;
3270
3271 ssize_t
3272 v;
3273
3274 channel=GetPixelChannelChannel(image,i);
3275 traits=GetPixelChannelTraits(image,channel);
3276 blur_traits=GetPixelChannelTraits(blur_image,channel);
3277 if ((traits == UndefinedPixelTrait) ||
3278 (blur_traits == UndefinedPixelTrait))
3279 continue;
3280 if ((blur_traits & CopyPixelTrait) != 0)
3281 {
3282 SetPixelChannel(blur_image,channel,p[center+i],q);
3283 continue;
3284 }
3285 k=kernel;
3286 pixel=0.0;
3287 pixels=p;
3288 luminance_pixels=l;
3289 gamma=0.0;
3290 if ((blur_traits & BlendPixelTrait) == 0)
3291 {
3292 for (v=0; v < (ssize_t) width; v++)
3293 {
3294 for (u=0; u < (ssize_t) width; u++)
3295 {
3296 contrast=GetPixelIntensity(luminance_image,luminance_pixels)-
3297 intensity;
3298 if (fabs(contrast) < threshold)
3299 {
3300 pixel+=(*k)*pixels[i];
3301 gamma+=(*k);
3302 }
3303 k++;
3304 pixels+=GetPixelChannels(image);
3305 luminance_pixels+=GetPixelChannels(luminance_image);
3306 }
3307 pixels+=GetPixelChannels(image)*image->columns;
3308 luminance_pixels+=GetPixelChannels(luminance_image)*
3309 luminance_image->columns;
3310 }
3311 if (fabs((double) gamma) < MagickEpsilon)
3312 {
3313 SetPixelChannel(blur_image,channel,p[center+i],q);
3314 continue;
3315 }
3316 gamma=PerceptibleReciprocal(gamma);
3317 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3318 continue;
3319 }
3320 for (v=0; v < (ssize_t) width; v++)
3321 {
3322 for (u=0; u < (ssize_t) width; u++)
3323 {
3324 contrast=GetPixelIntensity(image,pixels)-intensity;
3325 if (fabs(contrast) < threshold)
3326 {
3327 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
3328 pixel+=(*k)*alpha*pixels[i];
3329 gamma+=(*k)*alpha;
3330 }
3331 k++;
3332 pixels+=GetPixelChannels(image);
3333 luminance_pixels+=GetPixelChannels(luminance_image);
3334 }
3335 pixels+=GetPixelChannels(image)*image->columns;
3336 luminance_pixels+=GetPixelChannels(luminance_image)*
3337 luminance_image->columns;
3338 }
3339 if (fabs((double) gamma) < MagickEpsilon)
3340 {
3341 SetPixelChannel(blur_image,channel,p[center+i],q);
3342 continue;
3343 }
3344 gamma=PerceptibleReciprocal(gamma);
3345 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3346 }
3347 p+=GetPixelChannels(image);
3348 l+=GetPixelChannels(luminance_image);
3349 q+=GetPixelChannels(blur_image);
3350 }
3351 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3352 if (sync == MagickFalse)
3353 status=MagickFalse;
3354 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3355 {
3356 MagickBooleanType
3357 proceed;
3358
3359 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3360 #pragma omp atomic
3361 #endif
3362 progress++;
3363 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress,
3364 image->rows);
3365 if (proceed == MagickFalse)
3366 status=MagickFalse;
3367 }
3368 }
3369 blur_image->type=image->type;
3370 blur_view=DestroyCacheView(blur_view);
3371 image_view=DestroyCacheView(image_view);
3372 luminance_image=DestroyImage(luminance_image);
3373 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3374 if (status == MagickFalse)
3375 blur_image=DestroyImage(blur_image);
3376 return(blur_image);
3377 }
3378
3379 /*
3380 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3381 % %
3382 % %
3383 % %
3384 % S h a d e I m a g e %
3385 % %
3386 % %
3387 % %
3388 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3389 %
3390 % ShadeImage() shines a distant light on an image to create a
3391 % three-dimensional effect. You control the positioning of the light with
3392 % azimuth and elevation; azimuth is measured in degrees off the x axis
3393 % and elevation is measured in pixels above the Z axis.
3394 %
3395 % The format of the ShadeImage method is:
3396 %
3397 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3398 % const double azimuth,const double elevation,ExceptionInfo *exception)
3399 %
3400 % A description of each parameter follows:
3401 %
3402 % o image: the image.
3403 %
3404 % o gray: A value other than zero shades the intensity of each pixel.
3405 %
3406 % o azimuth, elevation: Define the light source direction.
3407 %
3408 % o exception: return any errors or warnings in this structure.
3409 %
3410 */
ShadeImage(const Image * image,const MagickBooleanType gray,const double azimuth,const double elevation,ExceptionInfo * exception)3411 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3412 const double azimuth,const double elevation,ExceptionInfo *exception)
3413 {
3414 #define GetShadeIntensity(image,pixel) \
3415 ClampPixel(GetPixelIntensity((image),(pixel)))
3416 #define ShadeImageTag "Shade/Image"
3417
3418 CacheView
3419 *image_view,
3420 *shade_view;
3421
3422 Image
3423 *linear_image,
3424 *shade_image;
3425
3426 MagickBooleanType
3427 status;
3428
3429 MagickOffsetType
3430 progress;
3431
3432 PrimaryInfo
3433 light;
3434
3435 ssize_t
3436 y;
3437
3438 /*
3439 Initialize shaded image attributes.
3440 */
3441 assert(image != (const Image *) NULL);
3442 assert(image->signature == MagickCoreSignature);
3443 if (image->debug != MagickFalse)
3444 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3445 assert(exception != (ExceptionInfo *) NULL);
3446 assert(exception->signature == MagickCoreSignature);
3447 linear_image=CloneImage(image,0,0,MagickTrue,exception);
3448 shade_image=CloneImage(image,0,0,MagickTrue,exception);
3449 if ((linear_image == (Image *) NULL) || (shade_image == (Image *) NULL))
3450 {
3451 if (linear_image != (Image *) NULL)
3452 linear_image=DestroyImage(linear_image);
3453 if (shade_image != (Image *) NULL)
3454 shade_image=DestroyImage(shade_image);
3455 return((Image *) NULL);
3456 }
3457 if (SetImageStorageClass(shade_image,DirectClass,exception) == MagickFalse)
3458 {
3459 linear_image=DestroyImage(linear_image);
3460 shade_image=DestroyImage(shade_image);
3461 return((Image *) NULL);
3462 }
3463 /*
3464 Compute the light vector.
3465 */
3466 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3467 cos(DegreesToRadians(elevation));
3468 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3469 cos(DegreesToRadians(elevation));
3470 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3471 /*
3472 Shade image.
3473 */
3474 status=MagickTrue;
3475 progress=0;
3476 image_view=AcquireVirtualCacheView(linear_image,exception);
3477 shade_view=AcquireAuthenticCacheView(shade_image,exception);
3478 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3479 #pragma omp parallel for schedule(static) shared(progress,status) \
3480 magick_number_threads(linear_image,shade_image,linear_image->rows,1)
3481 #endif
3482 for (y=0; y < (ssize_t) linear_image->rows; y++)
3483 {
3484 double
3485 distance,
3486 normal_distance,
3487 shade;
3488
3489 PrimaryInfo
3490 normal;
3491
3492 register const Quantum
3493 *magick_restrict center,
3494 *magick_restrict p,
3495 *magick_restrict post,
3496 *magick_restrict pre;
3497
3498 register Quantum
3499 *magick_restrict q;
3500
3501 register ssize_t
3502 x;
3503
3504 if (status == MagickFalse)
3505 continue;
3506 p=GetCacheViewVirtualPixels(image_view,-1,y-1,linear_image->columns+2,3,
3507 exception);
3508 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3509 exception);
3510 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3511 {
3512 status=MagickFalse;
3513 continue;
3514 }
3515 /*
3516 Shade this row of pixels.
3517 */
3518 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
3519 for (x=0; x < (ssize_t) linear_image->columns; x++)
3520 {
3521 register ssize_t
3522 i;
3523
3524 /*
3525 Determine the surface normal and compute shading.
3526 */
3527 pre=p+GetPixelChannels(linear_image);
3528 center=pre+(linear_image->columns+2)*GetPixelChannels(linear_image);
3529 post=center+(linear_image->columns+2)*GetPixelChannels(linear_image);
3530 normal.x=(double) (
3531 GetShadeIntensity(linear_image,pre-GetPixelChannels(linear_image))+
3532 GetShadeIntensity(linear_image,center-GetPixelChannels(linear_image))+
3533 GetShadeIntensity(linear_image,post-GetPixelChannels(linear_image))-
3534 GetShadeIntensity(linear_image,pre+GetPixelChannels(linear_image))-
3535 GetShadeIntensity(linear_image,center+GetPixelChannels(linear_image))-
3536 GetShadeIntensity(linear_image,post+GetPixelChannels(linear_image)));
3537 normal.y=(double) (
3538 GetShadeIntensity(linear_image,post-GetPixelChannels(linear_image))+
3539 GetShadeIntensity(linear_image,post)+
3540 GetShadeIntensity(linear_image,post+GetPixelChannels(linear_image))-
3541 GetShadeIntensity(linear_image,pre-GetPixelChannels(linear_image))-
3542 GetShadeIntensity(linear_image,pre)-
3543 GetShadeIntensity(linear_image,pre+GetPixelChannels(linear_image)));
3544 if ((fabs(normal.x) <= MagickEpsilon) &&
3545 (fabs(normal.y) <= MagickEpsilon))
3546 shade=light.z;
3547 else
3548 {
3549 shade=0.0;
3550 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3551 if (distance > MagickEpsilon)
3552 {
3553 normal_distance=normal.x*normal.x+normal.y*normal.y+
3554 normal.z*normal.z;
3555 if (normal_distance > (MagickEpsilon*MagickEpsilon))
3556 shade=distance/sqrt((double) normal_distance);
3557 }
3558 }
3559 for (i=0; i < (ssize_t) GetPixelChannels(linear_image); i++)
3560 {
3561 PixelChannel
3562 channel;
3563
3564 PixelTrait
3565 shade_traits,
3566 traits;
3567
3568 channel=GetPixelChannelChannel(linear_image,i);
3569 traits=GetPixelChannelTraits(linear_image,channel);
3570 shade_traits=GetPixelChannelTraits(shade_image,channel);
3571 if ((traits == UndefinedPixelTrait) ||
3572 (shade_traits == UndefinedPixelTrait))
3573 continue;
3574 if ((shade_traits & CopyPixelTrait) != 0)
3575 {
3576 SetPixelChannel(shade_image,channel,center[i],q);
3577 continue;
3578 }
3579 if ((traits & UpdatePixelTrait) == 0)
3580 {
3581 SetPixelChannel(shade_image,channel,center[i],q);
3582 continue;
3583 }
3584 if (gray != MagickFalse)
3585 {
3586 SetPixelChannel(shade_image,channel,ClampToQuantum(shade),q);
3587 continue;
3588 }
3589 SetPixelChannel(shade_image,channel,ClampToQuantum(QuantumScale*shade*
3590 center[i]),q);
3591 }
3592 p+=GetPixelChannels(linear_image);
3593 q+=GetPixelChannels(shade_image);
3594 }
3595 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3596 status=MagickFalse;
3597 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3598 {
3599 MagickBooleanType
3600 proceed;
3601
3602 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3603 #pragma omp atomic
3604 #endif
3605 progress++;
3606 proceed=SetImageProgress(image,ShadeImageTag,progress,image->rows);
3607 if (proceed == MagickFalse)
3608 status=MagickFalse;
3609 }
3610 }
3611 shade_view=DestroyCacheView(shade_view);
3612 image_view=DestroyCacheView(image_view);
3613 linear_image=DestroyImage(linear_image);
3614 if (status == MagickFalse)
3615 shade_image=DestroyImage(shade_image);
3616 return(shade_image);
3617 }
3618
3619 /*
3620 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3621 % %
3622 % %
3623 % %
3624 % S h a r p e n I m a g e %
3625 % %
3626 % %
3627 % %
3628 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3629 %
3630 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
3631 % operator of the given radius and standard deviation (sigma). For
3632 % reasonable results, radius should be larger than sigma. Use a radius of 0
3633 % and SharpenImage() selects a suitable radius for you.
3634 %
3635 % Using a separable kernel would be faster, but the negative weights cancel
3636 % out on the corners of the kernel producing often undesirable ringing in the
3637 % filtered result; this can be avoided by using a 2D gaussian shaped image
3638 % sharpening kernel instead.
3639 %
3640 % The format of the SharpenImage method is:
3641 %
3642 % Image *SharpenImage(const Image *image,const double radius,
3643 % const double sigma,ExceptionInfo *exception)
3644 %
3645 % A description of each parameter follows:
3646 %
3647 % o image: the image.
3648 %
3649 % o radius: the radius of the Gaussian, in pixels, not counting the center
3650 % pixel.
3651 %
3652 % o sigma: the standard deviation of the Laplacian, in pixels.
3653 %
3654 % o exception: return any errors or warnings in this structure.
3655 %
3656 */
SharpenImage(const Image * image,const double radius,const double sigma,ExceptionInfo * exception)3657 MagickExport Image *SharpenImage(const Image *image,const double radius,
3658 const double sigma,ExceptionInfo *exception)
3659 {
3660 double
3661 gamma,
3662 normalize;
3663
3664 Image
3665 *sharp_image;
3666
3667 KernelInfo
3668 *kernel_info;
3669
3670 register ssize_t
3671 i;
3672
3673 size_t
3674 width;
3675
3676 ssize_t
3677 j,
3678 u,
3679 v;
3680
3681 assert(image != (const Image *) NULL);
3682 assert(image->signature == MagickCoreSignature);
3683 if (image->debug != MagickFalse)
3684 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3685 assert(exception != (ExceptionInfo *) NULL);
3686 assert(exception->signature == MagickCoreSignature);
3687 width=GetOptimalKernelWidth2D(radius,sigma);
3688 kernel_info=AcquireKernelInfo((const char *) NULL,exception);
3689 if (kernel_info == (KernelInfo *) NULL)
3690 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3691 (void) memset(kernel_info,0,sizeof(*kernel_info));
3692 kernel_info->width=width;
3693 kernel_info->height=width;
3694 kernel_info->x=(ssize_t) (width-1)/2;
3695 kernel_info->y=(ssize_t) (width-1)/2;
3696 kernel_info->signature=MagickCoreSignature;
3697 kernel_info->values=(MagickRealType *) MagickAssumeAligned(
3698 AcquireAlignedMemory(kernel_info->width,kernel_info->height*
3699 sizeof(*kernel_info->values)));
3700 if (kernel_info->values == (MagickRealType *) NULL)
3701 {
3702 kernel_info=DestroyKernelInfo(kernel_info);
3703 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3704 }
3705 normalize=0.0;
3706 j=(ssize_t) (kernel_info->width-1)/2;
3707 i=0;
3708 for (v=(-j); v <= j; v++)
3709 {
3710 for (u=(-j); u <= j; u++)
3711 {
3712 kernel_info->values[i]=(MagickRealType) (-exp(-((double) u*u+v*v)/(2.0*
3713 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3714 normalize+=kernel_info->values[i];
3715 i++;
3716 }
3717 }
3718 kernel_info->values[i/2]=(double) ((-2.0)*normalize);
3719 normalize=0.0;
3720 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
3721 normalize+=kernel_info->values[i];
3722 gamma=PerceptibleReciprocal(normalize);
3723 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
3724 kernel_info->values[i]*=gamma;
3725 sharp_image=ConvolveImage(image,kernel_info,exception);
3726 kernel_info=DestroyKernelInfo(kernel_info);
3727 return(sharp_image);
3728 }
3729
3730 /*
3731 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3732 % %
3733 % %
3734 % %
3735 % S p r e a d I m a g e %
3736 % %
3737 % %
3738 % %
3739 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3740 %
3741 % SpreadImage() is a special effects method that randomly displaces each
3742 % pixel in a square area defined by the radius parameter.
3743 %
3744 % The format of the SpreadImage method is:
3745 %
3746 % Image *SpreadImage(const Image *image,
3747 % const PixelInterpolateMethod method,const double radius,
3748 % ExceptionInfo *exception)
3749 %
3750 % A description of each parameter follows:
3751 %
3752 % o image: the image.
3753 %
3754 % o method: intepolation method.
3755 %
3756 % o radius: choose a random pixel in a neighborhood of this extent.
3757 %
3758 % o exception: return any errors or warnings in this structure.
3759 %
3760 */
SpreadImage(const Image * image,const PixelInterpolateMethod method,const double radius,ExceptionInfo * exception)3761 MagickExport Image *SpreadImage(const Image *image,
3762 const PixelInterpolateMethod method,const double radius,
3763 ExceptionInfo *exception)
3764 {
3765 #define SpreadImageTag "Spread/Image"
3766
3767 CacheView
3768 *image_view,
3769 *spread_view;
3770
3771 Image
3772 *spread_image;
3773
3774 MagickBooleanType
3775 status;
3776
3777 MagickOffsetType
3778 progress;
3779
3780 RandomInfo
3781 **magick_restrict random_info;
3782
3783 size_t
3784 width;
3785
3786 ssize_t
3787 y;
3788
3789 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3790 unsigned long
3791 key;
3792 #endif
3793
3794 /*
3795 Initialize spread image attributes.
3796 */
3797 assert(image != (Image *) NULL);
3798 assert(image->signature == MagickCoreSignature);
3799 if (image->debug != MagickFalse)
3800 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3801 assert(exception != (ExceptionInfo *) NULL);
3802 assert(exception->signature == MagickCoreSignature);
3803 spread_image=CloneImage(image,0,0,MagickTrue,exception);
3804 if (spread_image == (Image *) NULL)
3805 return((Image *) NULL);
3806 if (SetImageStorageClass(spread_image,DirectClass,exception) == MagickFalse)
3807 {
3808 spread_image=DestroyImage(spread_image);
3809 return((Image *) NULL);
3810 }
3811 /*
3812 Spread image.
3813 */
3814 status=MagickTrue;
3815 progress=0;
3816 width=GetOptimalKernelWidth1D(radius,0.5);
3817 random_info=AcquireRandomInfoThreadSet();
3818 image_view=AcquireVirtualCacheView(image,exception);
3819 spread_view=AcquireAuthenticCacheView(spread_image,exception);
3820 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3821 key=GetRandomSecretKey(random_info[0]);
3822 #pragma omp parallel for schedule(static) shared(progress,status) \
3823 magick_number_threads(image,spread_image,image->rows,key == ~0UL)
3824 #endif
3825 for (y=0; y < (ssize_t) image->rows; y++)
3826 {
3827 const int
3828 id = GetOpenMPThreadId();
3829
3830 register Quantum
3831 *magick_restrict q;
3832
3833 register ssize_t
3834 x;
3835
3836 if (status == MagickFalse)
3837 continue;
3838 q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
3839 exception);
3840 if (q == (Quantum *) NULL)
3841 {
3842 status=MagickFalse;
3843 continue;
3844 }
3845 for (x=0; x < (ssize_t) image->columns; x++)
3846 {
3847 PointInfo
3848 point;
3849
3850 point.x=GetPseudoRandomValue(random_info[id]);
3851 point.y=GetPseudoRandomValue(random_info[id]);
3852 status=InterpolatePixelChannels(image,image_view,spread_image,method,
3853 (double) x+width*(point.x-0.5),(double) y+width*(point.y-0.5),q,
3854 exception);
3855 if (status == MagickFalse)
3856 break;
3857 q+=GetPixelChannels(spread_image);
3858 }
3859 if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
3860 status=MagickFalse;
3861 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3862 {
3863 MagickBooleanType
3864 proceed;
3865
3866 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3867 #pragma omp atomic
3868 #endif
3869 progress++;
3870 proceed=SetImageProgress(image,SpreadImageTag,progress,image->rows);
3871 if (proceed == MagickFalse)
3872 status=MagickFalse;
3873 }
3874 }
3875 spread_view=DestroyCacheView(spread_view);
3876 image_view=DestroyCacheView(image_view);
3877 random_info=DestroyRandomInfoThreadSet(random_info);
3878 if (status == MagickFalse)
3879 spread_image=DestroyImage(spread_image);
3880 return(spread_image);
3881 }
3882
3883 /*
3884 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3885 % %
3886 % %
3887 % %
3888 % U n s h a r p M a s k I m a g e %
3889 % %
3890 % %
3891 % %
3892 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3893 %
3894 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
3895 % image with a Gaussian operator of the given radius and standard deviation
3896 % (sigma). For reasonable results, radius should be larger than sigma. Use a
3897 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
3898 %
3899 % The format of the UnsharpMaskImage method is:
3900 %
3901 % Image *UnsharpMaskImage(const Image *image,const double radius,
3902 % const double sigma,const double amount,const double threshold,
3903 % ExceptionInfo *exception)
3904 %
3905 % A description of each parameter follows:
3906 %
3907 % o image: the image.
3908 %
3909 % o radius: the radius of the Gaussian, in pixels, not counting the center
3910 % pixel.
3911 %
3912 % o sigma: the standard deviation of the Gaussian, in pixels.
3913 %
3914 % o gain: the percentage of the difference between the original and the
3915 % blur image that is added back into the original.
3916 %
3917 % o threshold: the threshold in pixels needed to apply the diffence gain.
3918 %
3919 % o exception: return any errors or warnings in this structure.
3920 %
3921 */
UnsharpMaskImage(const Image * image,const double radius,const double sigma,const double gain,const double threshold,ExceptionInfo * exception)3922 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
3923 const double sigma,const double gain,const double threshold,
3924 ExceptionInfo *exception)
3925 {
3926 #define SharpenImageTag "Sharpen/Image"
3927
3928 CacheView
3929 *image_view,
3930 *unsharp_view;
3931
3932 Image
3933 *unsharp_image;
3934
3935 MagickBooleanType
3936 status;
3937
3938 MagickOffsetType
3939 progress;
3940
3941 double
3942 quantum_threshold;
3943
3944 ssize_t
3945 y;
3946
3947 assert(image != (const Image *) NULL);
3948 assert(image->signature == MagickCoreSignature);
3949 if (image->debug != MagickFalse)
3950 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3951 assert(exception != (ExceptionInfo *) NULL);
3952 #if defined(MAGICKCORE_OPENCL_SUPPORT)
3953 unsharp_image=AccelerateUnsharpMaskImage(image,radius,sigma,gain,threshold,
3954 exception);
3955 if (unsharp_image != (Image *) NULL)
3956 return(unsharp_image);
3957 #endif
3958 unsharp_image=BlurImage(image,radius,sigma,exception);
3959 if (unsharp_image == (Image *) NULL)
3960 return((Image *) NULL);
3961 quantum_threshold=(double) QuantumRange*threshold;
3962 /*
3963 Unsharp-mask image.
3964 */
3965 status=MagickTrue;
3966 progress=0;
3967 image_view=AcquireVirtualCacheView(image,exception);
3968 unsharp_view=AcquireAuthenticCacheView(unsharp_image,exception);
3969 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3970 #pragma omp parallel for schedule(static) shared(progress,status) \
3971 magick_number_threads(image,unsharp_image,image->rows,1)
3972 #endif
3973 for (y=0; y < (ssize_t) image->rows; y++)
3974 {
3975 register const Quantum
3976 *magick_restrict p;
3977
3978 register Quantum
3979 *magick_restrict q;
3980
3981 register ssize_t
3982 x;
3983
3984 if (status == MagickFalse)
3985 continue;
3986 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3987 q=QueueCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
3988 exception);
3989 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3990 {
3991 status=MagickFalse;
3992 continue;
3993 }
3994 for (x=0; x < (ssize_t) image->columns; x++)
3995 {
3996 register ssize_t
3997 i;
3998
3999 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4000 {
4001 double
4002 pixel;
4003
4004 PixelChannel
4005 channel;
4006
4007 PixelTrait
4008 traits,
4009 unsharp_traits;
4010
4011 channel=GetPixelChannelChannel(image,i);
4012 traits=GetPixelChannelTraits(image,channel);
4013 unsharp_traits=GetPixelChannelTraits(unsharp_image,channel);
4014 if ((traits == UndefinedPixelTrait) ||
4015 (unsharp_traits == UndefinedPixelTrait))
4016 continue;
4017 if ((unsharp_traits & CopyPixelTrait) != 0)
4018 {
4019 SetPixelChannel(unsharp_image,channel,p[i],q);
4020 continue;
4021 }
4022 pixel=p[i]-(double) GetPixelChannel(unsharp_image,channel,q);
4023 if (fabs(2.0*pixel) < quantum_threshold)
4024 pixel=(double) p[i];
4025 else
4026 pixel=(double) p[i]+gain*pixel;
4027 SetPixelChannel(unsharp_image,channel,ClampToQuantum(pixel),q);
4028 }
4029 p+=GetPixelChannels(image);
4030 q+=GetPixelChannels(unsharp_image);
4031 }
4032 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4033 status=MagickFalse;
4034 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4035 {
4036 MagickBooleanType
4037 proceed;
4038
4039 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4040 #pragma omp atomic
4041 #endif
4042 progress++;
4043 proceed=SetImageProgress(image,SharpenImageTag,progress,image->rows);
4044 if (proceed == MagickFalse)
4045 status=MagickFalse;
4046 }
4047 }
4048 unsharp_image->type=image->type;
4049 unsharp_view=DestroyCacheView(unsharp_view);
4050 image_view=DestroyCacheView(image_view);
4051 if (status == MagickFalse)
4052 unsharp_image=DestroyImage(unsharp_image);
4053 return(unsharp_image);
4054 }
4055