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-2016 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 % http://www.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,image->columns,image->rows,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) ResetMagickMemory(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,4) shared(progress,status) \
256 magick_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 (GetPixelReadMask(image,p+center) == 0))
340 {
341 SetPixelChannel(blur_image,channel,p[center+i],q);
342 continue;
343 }
344 k=kernel[j];
345 pixels=p;
346 pixel=0.0;
347 gamma=0.0;
348 if ((blur_traits & BlendPixelTrait) == 0)
349 {
350 /*
351 No alpha blending.
352 */
353 for (v=0; v < (ssize_t) (width-j); v++)
354 {
355 for (u=0; u < (ssize_t) (width-j); u++)
356 {
357 pixel+=(*k)*pixels[i];
358 gamma+=(*k);
359 k++;
360 pixels+=GetPixelChannels(image);
361 }
362 }
363 gamma=PerceptibleReciprocal(gamma);
364 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
365 continue;
366 }
367 /*
368 Alpha blending.
369 */
370 for (v=0; v < (ssize_t) (width-j); v++)
371 {
372 for (u=0; u < (ssize_t) (width-j); u++)
373 {
374 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
375 pixel+=(*k)*alpha*pixels[i];
376 gamma+=(*k)*alpha;
377 k++;
378 pixels+=GetPixelChannels(image);
379 }
380 }
381 gamma=PerceptibleReciprocal(gamma);
382 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
383 }
384 q+=GetPixelChannels(blur_image);
385 r+=GetPixelChannels(edge_image);
386 }
387 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
388 status=MagickFalse;
389 if (image->progress_monitor != (MagickProgressMonitor) NULL)
390 {
391 MagickBooleanType
392 proceed;
393
394 #if defined(MAGICKCORE_OPENMP_SUPPORT)
395 #pragma omp critical (MagickCore_AdaptiveBlurImage)
396 #endif
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,image->columns,image->rows,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) ResetMagickMemory(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,4) shared(progress,status) \
577 magick_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 (GetPixelReadMask(image,p+center) == 0))
661 {
662 SetPixelChannel(sharp_image,channel,p[center+i],q);
663 continue;
664 }
665 k=kernel[j];
666 pixels=p;
667 pixel=0.0;
668 gamma=0.0;
669 if ((sharp_traits & BlendPixelTrait) == 0)
670 {
671 /*
672 No alpha blending.
673 */
674 for (v=0; v < (ssize_t) (width-j); v++)
675 {
676 for (u=0; u < (ssize_t) (width-j); u++)
677 {
678 pixel+=(*k)*pixels[i];
679 gamma+=(*k);
680 k++;
681 pixels+=GetPixelChannels(image);
682 }
683 }
684 gamma=PerceptibleReciprocal(gamma);
685 SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
686 continue;
687 }
688 /*
689 Alpha blending.
690 */
691 for (v=0; v < (ssize_t) (width-j); v++)
692 {
693 for (u=0; u < (ssize_t) (width-j); u++)
694 {
695 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
696 pixel+=(*k)*alpha*pixels[i];
697 gamma+=(*k)*alpha;
698 k++;
699 pixels+=GetPixelChannels(image);
700 }
701 }
702 gamma=PerceptibleReciprocal(gamma);
703 SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
704 }
705 q+=GetPixelChannels(sharp_image);
706 r+=GetPixelChannels(edge_image);
707 }
708 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
709 status=MagickFalse;
710 if (image->progress_monitor != (MagickProgressMonitor) NULL)
711 {
712 MagickBooleanType
713 proceed;
714
715 #if defined(MAGICKCORE_OPENMP_SUPPORT)
716 #pragma omp critical (MagickCore_AdaptiveSharpenImage)
717 #endif
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,4) \
901 magick_threads(image,image,1,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,4) \
938 magick_threads(image,image,1,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) ResetMagickMemory(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) ResetMagickMemory(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) ResetMagickMemory(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,image->columns,image->rows,MagickTrue,
1479 exception);
1480 if (kuwahara_image == (Image *) NULL)
1481 {
1482 gaussian_image=DestroyImage(gaussian_image);
1483 return((Image *) NULL);
1484 }
1485 if (SetImageStorageClass(kuwahara_image,DirectClass,exception) == MagickFalse)
1486 {
1487 gaussian_image=DestroyImage(gaussian_image);
1488 kuwahara_image=DestroyImage(kuwahara_image);
1489 return((Image *) NULL);
1490 }
1491 /*
1492 Edge preserving noise reduction filter.
1493 */
1494 status=MagickTrue;
1495 progress=0;
1496 image_view=AcquireVirtualCacheView(gaussian_image,exception);
1497 kuwahara_view=AcquireAuthenticCacheView(kuwahara_image,exception);
1498 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1499 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1500 magick_threads(image,kuwahara_image,image->rows,1)
1501 #endif
1502 for (y=0; y < (ssize_t) gaussian_image->rows; y++)
1503 {
1504 register Quantum
1505 *magick_restrict q;
1506
1507 register ssize_t
1508 x;
1509
1510 if (status == MagickFalse)
1511 continue;
1512 q=QueueCacheViewAuthenticPixels(kuwahara_view,0,y,kuwahara_image->columns,1,
1513 exception);
1514 if (q == (Quantum *) NULL)
1515 {
1516 status=MagickFalse;
1517 continue;
1518 }
1519 for (x=0; x < (ssize_t) gaussian_image->columns; x++)
1520 {
1521 const Quantum
1522 *magick_restrict p;
1523
1524 double
1525 min_variance;
1526
1527 RectangleInfo
1528 quadrant,
1529 target;
1530
1531 register size_t
1532 i;
1533
1534 min_variance=MagickMaximumValue;
1535 SetGeometry(gaussian_image,&target);
1536 quadrant.width=width;
1537 quadrant.height=width;
1538 for (i=0; i < 4; i++)
1539 {
1540 const Quantum
1541 *magick_restrict k;
1542
1543 double
1544 mean[MaxPixelChannels],
1545 variance;
1546
1547 register ssize_t
1548 n;
1549
1550 ssize_t
1551 j;
1552
1553 quadrant.x=x;
1554 quadrant.y=y;
1555 switch (i)
1556 {
1557 case 0:
1558 {
1559 quadrant.x=x-(ssize_t) (width-1);
1560 quadrant.y=y-(ssize_t) (width-1);
1561 break;
1562 }
1563 case 1:
1564 {
1565 quadrant.y=y-(ssize_t) (width-1);
1566 break;
1567 }
1568 case 2:
1569 {
1570 quadrant.x=x-(ssize_t) (width-1);
1571 break;
1572 }
1573 case 3:
1574 default:
1575 break;
1576 }
1577 p=GetCacheViewVirtualPixels(image_view,quadrant.x,quadrant.y,
1578 quadrant.width,quadrant.height,exception);
1579 if (p == (const Quantum *) NULL)
1580 break;
1581 for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
1582 mean[j]=0.0;
1583 k=p;
1584 for (n=0; n < (ssize_t) (width*width); n++)
1585 {
1586 for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
1587 mean[j]+=(double) k[j];
1588 k+=GetPixelChannels(gaussian_image);
1589 }
1590 for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
1591 mean[j]/=(double) (width*width);
1592 k=p;
1593 variance=0.0;
1594 for (n=0; n < (ssize_t) (width*width); n++)
1595 {
1596 double
1597 luma;
1598
1599 luma=GetPixelLuma(gaussian_image,k);
1600 variance+=(luma-GetMeanLuma(gaussian_image,mean))*
1601 (luma-GetMeanLuma(gaussian_image,mean));
1602 k+=GetPixelChannels(gaussian_image);
1603 }
1604 if (variance < min_variance)
1605 {
1606 min_variance=variance;
1607 target=quadrant;
1608 }
1609 }
1610 if (i < 4)
1611 {
1612 status=MagickFalse;
1613 break;
1614 }
1615 status=InterpolatePixelChannels(gaussian_image,image_view,kuwahara_image,
1616 UndefinedInterpolatePixel,(double) target.x+target.width/2.0,(double)
1617 target.y+target.height/2.0,q,exception);
1618 q+=GetPixelChannels(kuwahara_image);
1619 }
1620 if (SyncCacheViewAuthenticPixels(kuwahara_view,exception) == MagickFalse)
1621 status=MagickFalse;
1622 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1623 {
1624 MagickBooleanType
1625 proceed;
1626
1627 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1628 #pragma omp critical (MagickCore_KuwaharaImage)
1629 #endif
1630 proceed=SetImageProgress(image,KuwaharaImageTag,progress++,image->rows);
1631 if (proceed == MagickFalse)
1632 status=MagickFalse;
1633 }
1634 }
1635 kuwahara_view=DestroyCacheView(kuwahara_view);
1636 image_view=DestroyCacheView(image_view);
1637 gaussian_image=DestroyImage(gaussian_image);
1638 if (status == MagickFalse)
1639 kuwahara_image=DestroyImage(kuwahara_image);
1640 return(kuwahara_image);
1641 }
1642
1643 /*
1644 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1645 % %
1646 % %
1647 % %
1648 % L o c a l C o n t r a s t I m a g e %
1649 % %
1650 % %
1651 % %
1652 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1653 %
1654 % LocalContrastImage() attempts to increase the appearance of large-scale
1655 % light-dark transitions. Local contrast enhancement works similarly to
1656 % sharpening with an unsharp mask, however the mask is instead created using
1657 % an image with a greater blur distance.
1658 %
1659 % The format of the LocalContrastImage method is:
1660 %
1661 % Image *LocalContrastImage(const Image *image, const double radius,
1662 % const double strength,ExceptionInfo *exception)
1663 %
1664 % A description of each parameter follows:
1665 %
1666 % o image: the image.
1667 %
1668 % o radius: the radius of the Gaussian blur, in percentage with 100%
1669 % resulting in a blur radius of 20% of largest dimension.
1670 %
1671 % o strength: the strength of the blur mask in percentage.
1672 %
1673 % o exception: return any errors or warnings in this structure.
1674 %
1675 */
LocalContrastImage(const Image * image,const double radius,const double strength,ExceptionInfo * exception)1676 MagickExport Image *LocalContrastImage(const Image *image,const double radius,
1677 const double strength,ExceptionInfo *exception)
1678 {
1679 #define LocalContrastImageTag "LocalContrast/Image"
1680
1681 CacheView
1682 *image_view,
1683 *contrast_view;
1684
1685 float
1686 *interImage,
1687 *scanLinePixels,
1688 totalWeight;
1689
1690 Image
1691 *contrast_image;
1692
1693 MagickBooleanType
1694 status;
1695
1696 MemoryInfo
1697 *scanLinePixels_info,
1698 *interImage_info;
1699
1700 ssize_t
1701 scanLineSize,
1702 width;
1703
1704 /*
1705 Initialize contrast image attributes.
1706 */
1707 assert(image != (const Image *) NULL);
1708 assert(image->signature == MagickCoreSignature);
1709 if (image->debug != MagickFalse)
1710 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1711 assert(exception != (ExceptionInfo *) NULL);
1712 assert(exception->signature == MagickCoreSignature);
1713 #if defined(MAGICKCORE_OPENCL_SUPPORT)
1714 contrast_image=AccelerateLocalContrastImage(image,radius,strength,exception);
1715 if (contrast_image != (Image *) NULL)
1716 return(contrast_image);
1717 #endif
1718 contrast_image=CloneImage(image,0,0,MagickTrue,exception);
1719 if (contrast_image == (Image *) NULL)
1720 return((Image *) NULL);
1721 if (SetImageStorageClass(contrast_image,DirectClass,exception) == MagickFalse)
1722 {
1723 contrast_image=DestroyImage(contrast_image);
1724 return((Image *) NULL);
1725 }
1726 image_view=AcquireVirtualCacheView(image,exception);
1727 contrast_view=AcquireAuthenticCacheView(contrast_image,exception);
1728 scanLineSize=(ssize_t) MagickMax(image->columns,image->rows);
1729 width=(ssize_t) scanLineSize*0.002f*fabs(radius);
1730 scanLineSize+=(2*width);
1731 scanLinePixels_info=AcquireVirtualMemory((size_t) GetOpenMPMaximumThreads()*
1732 scanLineSize,sizeof(*scanLinePixels));
1733 if (scanLinePixels_info == (MemoryInfo *) NULL)
1734 {
1735 contrast_view=DestroyCacheView(contrast_view);
1736 image_view=DestroyCacheView(image_view);
1737 contrast_image=DestroyImage(contrast_image);
1738 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1739 }
1740 scanLinePixels=(float *) GetVirtualMemoryBlob(scanLinePixels_info);
1741 /*
1742 Create intermediate buffer.
1743 */
1744 interImage_info=AcquireVirtualMemory(image->rows*(image->columns+(2*width)),
1745 sizeof(*interImage));
1746 if (interImage_info == (MemoryInfo *) NULL)
1747 {
1748 scanLinePixels_info=RelinquishVirtualMemory(scanLinePixels_info);
1749 contrast_view=DestroyCacheView(contrast_view);
1750 image_view=DestroyCacheView(image_view);
1751 contrast_image=DestroyImage(contrast_image);
1752 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1753 }
1754 interImage=(float *) GetVirtualMemoryBlob(interImage_info);
1755 totalWeight=(float) ((width+1)*(width+1));
1756 /*
1757 Vertical pass.
1758 */
1759 status=MagickTrue;
1760 {
1761 ssize_t
1762 x;
1763
1764 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1765 #pragma omp parallel for schedule(static,4) \
1766 magick_threads(image,image,image->columns,1)
1767 #endif
1768 for (x=0; x < (ssize_t) image->columns; x++)
1769 {
1770 const int
1771 id = GetOpenMPThreadId();
1772
1773 const Quantum
1774 *magick_restrict p;
1775
1776 float
1777 *out,
1778 *pix,
1779 *pixels;
1780
1781 register ssize_t
1782 y;
1783
1784 ssize_t
1785 i;
1786
1787 if (status == MagickFalse)
1788 continue;
1789 pixels=scanLinePixels;
1790 pixels+=id*scanLineSize;
1791 pix=pixels;
1792 p=GetCacheViewVirtualPixels(image_view,x,-width,1,image->rows+(2*width),
1793 exception);
1794 if (p == (const Quantum *) NULL)
1795 {
1796 status=MagickFalse;
1797 continue;
1798 }
1799 for (y=0; y < (ssize_t) image->rows+(2*width); y++)
1800 {
1801 *pix++=(float)GetPixelLuma(image,p);
1802 p+=image->number_channels;
1803 }
1804 out=interImage+x+width;
1805 for (y=0; y < (ssize_t) image->rows; y++)
1806 {
1807 float
1808 sum,
1809 weight;
1810
1811 weight=1.0f;
1812 sum=0;
1813 pix=pixels+y;
1814 for (i=0; i < width; i++)
1815 {
1816 sum+=weight*(*pix++);
1817 weight+=1.0f;
1818 }
1819 for (i=width+1; i < (2*width); i++)
1820 {
1821 sum+=weight*(*pix++);
1822 weight-=1.0f;
1823 }
1824 /* write to output */
1825 *out=sum/totalWeight;
1826 /* mirror into padding */
1827 if (x <= width && x != 0)
1828 *(out-(x*2))=*out;
1829 if ((x > (ssize_t) image->columns-width-2) &&
1830 (x != (ssize_t) image->columns-1))
1831 *(out+((image->columns-x-1)*2))=*out;
1832 out+=image->columns+(width*2);
1833 }
1834 }
1835 }
1836 /*
1837 Horizontal pass.
1838 */
1839 {
1840 ssize_t
1841 y;
1842
1843 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1844 #pragma omp parallel for schedule(static,4) \
1845 magick_threads(image,image,image->rows,1)
1846 #endif
1847 for (y=0; y < (ssize_t) image->rows; y++)
1848 {
1849 const int
1850 id = GetOpenMPThreadId();
1851
1852 const Quantum
1853 *magick_restrict p;
1854
1855 float
1856 *pix,
1857 *pixels;
1858
1859 register Quantum
1860 *magick_restrict q;
1861
1862 register ssize_t
1863 x;
1864
1865 ssize_t
1866 i;
1867
1868 if (status == MagickFalse)
1869 continue;
1870 pixels=scanLinePixels;
1871 pixels+=id*scanLineSize;
1872 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1873 q=GetCacheViewAuthenticPixels(contrast_view,0,y,image->columns,1,
1874 exception);
1875 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1876 {
1877 status=MagickFalse;
1878 continue;
1879 }
1880 memcpy(pixels,interImage+(y*(image->columns+(2*width))),(image->columns+
1881 (2*width))*sizeof(float));
1882 for (x=0; x < (ssize_t) image->columns; x++)
1883 {
1884 float
1885 mult,
1886 srcVal,
1887 sum,
1888 weight;
1889
1890 weight=1.0f;
1891 sum=0;
1892 pix=pixels+x;
1893 for (i=0; i < width; i++)
1894 {
1895 sum+=weight*(*pix++);
1896 weight+=1.0f;
1897 }
1898 for (i=width+1; i < (2*width); i++)
1899 {
1900 sum+=weight*(*pix++);
1901 weight-=1.0f;
1902 }
1903 /* Apply and write */
1904 srcVal=(float) GetPixelLuma(image,p);
1905 mult=(srcVal-(sum/totalWeight))*(strength/100.0f);
1906 mult=(srcVal+mult)/srcVal;
1907 SetPixelRed(contrast_image,ClampToQuantum(GetPixelRed(image,p)*mult),
1908 q);
1909 SetPixelGreen(contrast_image,ClampToQuantum(GetPixelGreen(image,p)*
1910 mult),q);
1911 SetPixelBlue(contrast_image,ClampToQuantum(GetPixelBlue(image,p)*mult),
1912 q);
1913 p+=image->number_channels;
1914 q+=contrast_image->number_channels;
1915 }
1916 if (SyncCacheViewAuthenticPixels(contrast_view,exception) == MagickFalse)
1917 status=MagickFalse;
1918 }
1919 }
1920 scanLinePixels_info=RelinquishVirtualMemory(scanLinePixels_info);
1921 interImage_info=RelinquishVirtualMemory(interImage_info);
1922 contrast_view=DestroyCacheView(contrast_view);
1923 image_view=DestroyCacheView(image_view);
1924 if (status == MagickFalse)
1925 contrast_image=DestroyImage(contrast_image);
1926 return(contrast_image);
1927 }
1928
1929 /*
1930 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1931 % %
1932 % %
1933 % %
1934 % M o t i o n B l u r I m a g e %
1935 % %
1936 % %
1937 % %
1938 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1939 %
1940 % MotionBlurImage() simulates motion blur. We convolve the image with a
1941 % Gaussian operator of the given radius and standard deviation (sigma).
1942 % For reasonable results, radius should be larger than sigma. Use a
1943 % radius of 0 and MotionBlurImage() selects a suitable radius for you.
1944 % Angle gives the angle of the blurring motion.
1945 %
1946 % Andrew Protano contributed this effect.
1947 %
1948 % The format of the MotionBlurImage method is:
1949 %
1950 % Image *MotionBlurImage(const Image *image,const double radius,
1951 % const double sigma,const double angle,ExceptionInfo *exception)
1952 %
1953 % A description of each parameter follows:
1954 %
1955 % o image: the image.
1956 %
1957 % o radius: the radius of the Gaussian, in pixels, not counting
1958 % the center pixel.
1959 %
1960 % o sigma: the standard deviation of the Gaussian, in pixels.
1961 %
1962 % o angle: Apply the effect along this angle.
1963 %
1964 % o exception: return any errors or warnings in this structure.
1965 %
1966 */
1967
GetMotionBlurKernel(const size_t width,const double sigma)1968 static MagickRealType *GetMotionBlurKernel(const size_t width,
1969 const double sigma)
1970 {
1971 MagickRealType
1972 *kernel,
1973 normalize;
1974
1975 register ssize_t
1976 i;
1977
1978 /*
1979 Generate a 1-D convolution kernel.
1980 */
1981 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1982 kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
1983 width,sizeof(*kernel)));
1984 if (kernel == (MagickRealType *) NULL)
1985 return(kernel);
1986 normalize=0.0;
1987 for (i=0; i < (ssize_t) width; i++)
1988 {
1989 kernel[i]=(MagickRealType) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
1990 MagickSigma)))/(MagickSQ2PI*MagickSigma));
1991 normalize+=kernel[i];
1992 }
1993 for (i=0; i < (ssize_t) width; i++)
1994 kernel[i]/=normalize;
1995 return(kernel);
1996 }
1997
MotionBlurImage(const Image * image,const double radius,const double sigma,const double angle,ExceptionInfo * exception)1998 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
1999 const double sigma,const double angle,ExceptionInfo *exception)
2000 {
2001 #define BlurImageTag "Blur/Image"
2002
2003 CacheView
2004 *blur_view,
2005 *image_view,
2006 *motion_view;
2007
2008 Image
2009 *blur_image;
2010
2011 MagickBooleanType
2012 status;
2013
2014 MagickOffsetType
2015 progress;
2016
2017 MagickRealType
2018 *kernel;
2019
2020 OffsetInfo
2021 *offset;
2022
2023 PointInfo
2024 point;
2025
2026 register ssize_t
2027 i;
2028
2029 size_t
2030 width;
2031
2032 ssize_t
2033 y;
2034
2035 assert(image != (Image *) NULL);
2036 assert(image->signature == MagickCoreSignature);
2037 if (image->debug != MagickFalse)
2038 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2039 assert(exception != (ExceptionInfo *) NULL);
2040 width=GetOptimalKernelWidth1D(radius,sigma);
2041 kernel=GetMotionBlurKernel(width,sigma);
2042 if (kernel == (MagickRealType *) NULL)
2043 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2044 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2045 if (offset == (OffsetInfo *) NULL)
2046 {
2047 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2048 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2049 }
2050 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2051 if (blur_image == (Image *) NULL)
2052 {
2053 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2054 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2055 return((Image *) NULL);
2056 }
2057 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2058 {
2059 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2060 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2061 blur_image=DestroyImage(blur_image);
2062 return((Image *) NULL);
2063 }
2064 point.x=(double) width*sin(DegreesToRadians(angle));
2065 point.y=(double) width*cos(DegreesToRadians(angle));
2066 for (i=0; i < (ssize_t) width; i++)
2067 {
2068 offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
2069 offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
2070 }
2071 /*
2072 Motion blur image.
2073 */
2074 status=MagickTrue;
2075 progress=0;
2076 image_view=AcquireVirtualCacheView(image,exception);
2077 motion_view=AcquireVirtualCacheView(image,exception);
2078 blur_view=AcquireAuthenticCacheView(blur_image,exception);
2079 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2080 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2081 magick_threads(image,blur_image,image->rows,1)
2082 #endif
2083 for (y=0; y < (ssize_t) image->rows; y++)
2084 {
2085 register const Quantum
2086 *magick_restrict p;
2087
2088 register Quantum
2089 *magick_restrict q;
2090
2091 register ssize_t
2092 x;
2093
2094 if (status == MagickFalse)
2095 continue;
2096 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2097 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2098 exception);
2099 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2100 {
2101 status=MagickFalse;
2102 continue;
2103 }
2104 for (x=0; x < (ssize_t) image->columns; x++)
2105 {
2106 register ssize_t
2107 i;
2108
2109 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2110 {
2111 double
2112 alpha,
2113 gamma,
2114 pixel;
2115
2116 PixelChannel
2117 channel;
2118
2119 PixelTrait
2120 blur_traits,
2121 traits;
2122
2123 register const Quantum
2124 *magick_restrict r;
2125
2126 register MagickRealType
2127 *magick_restrict k;
2128
2129 register ssize_t
2130 j;
2131
2132 channel=GetPixelChannelChannel(image,i);
2133 traits=GetPixelChannelTraits(image,channel);
2134 blur_traits=GetPixelChannelTraits(blur_image,channel);
2135 if ((traits == UndefinedPixelTrait) ||
2136 (blur_traits == UndefinedPixelTrait))
2137 continue;
2138 if (((blur_traits & CopyPixelTrait) != 0) ||
2139 (GetPixelReadMask(image,p) == 0))
2140 {
2141 SetPixelChannel(blur_image,channel,p[i],q);
2142 continue;
2143 }
2144 k=kernel;
2145 pixel=0.0;
2146 if ((blur_traits & BlendPixelTrait) == 0)
2147 {
2148 for (j=0; j < (ssize_t) width; j++)
2149 {
2150 r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+
2151 offset[j].y,1,1,exception);
2152 if (r == (const Quantum *) NULL)
2153 {
2154 status=MagickFalse;
2155 continue;
2156 }
2157 pixel+=(*k)*r[i];
2158 k++;
2159 }
2160 SetPixelChannel(blur_image,channel,ClampToQuantum(pixel),q);
2161 continue;
2162 }
2163 alpha=0.0;
2164 gamma=0.0;
2165 for (j=0; j < (ssize_t) width; j++)
2166 {
2167 r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+offset[j].y,1,
2168 1,exception);
2169 if (r == (const Quantum *) NULL)
2170 {
2171 status=MagickFalse;
2172 continue;
2173 }
2174 alpha=(double) (QuantumScale*GetPixelAlpha(image,r));
2175 pixel+=(*k)*alpha*r[i];
2176 gamma+=(*k)*alpha;
2177 k++;
2178 }
2179 gamma=PerceptibleReciprocal(gamma);
2180 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2181 }
2182 p+=GetPixelChannels(image);
2183 q+=GetPixelChannels(blur_image);
2184 }
2185 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2186 status=MagickFalse;
2187 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2188 {
2189 MagickBooleanType
2190 proceed;
2191
2192 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2193 #pragma omp critical (MagickCore_MotionBlurImage)
2194 #endif
2195 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2196 if (proceed == MagickFalse)
2197 status=MagickFalse;
2198 }
2199 }
2200 blur_view=DestroyCacheView(blur_view);
2201 motion_view=DestroyCacheView(motion_view);
2202 image_view=DestroyCacheView(image_view);
2203 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2204 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2205 if (status == MagickFalse)
2206 blur_image=DestroyImage(blur_image);
2207 return(blur_image);
2208 }
2209
2210 /*
2211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2212 % %
2213 % %
2214 % %
2215 % P r e v i e w I m a g e %
2216 % %
2217 % %
2218 % %
2219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2220 %
2221 % PreviewImage() tiles 9 thumbnails of the specified image with an image
2222 % processing operation applied with varying parameters. This may be helpful
2223 % pin-pointing an appropriate parameter for a particular image processing
2224 % operation.
2225 %
2226 % The format of the PreviewImages method is:
2227 %
2228 % Image *PreviewImages(const Image *image,const PreviewType preview,
2229 % ExceptionInfo *exception)
2230 %
2231 % A description of each parameter follows:
2232 %
2233 % o image: the image.
2234 %
2235 % o preview: the image processing operation.
2236 %
2237 % o exception: return any errors or warnings in this structure.
2238 %
2239 */
PreviewImage(const Image * image,const PreviewType preview,ExceptionInfo * exception)2240 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
2241 ExceptionInfo *exception)
2242 {
2243 #define NumberTiles 9
2244 #define PreviewImageTag "Preview/Image"
2245 #define DefaultPreviewGeometry "204x204+10+10"
2246
2247 char
2248 factor[MagickPathExtent],
2249 label[MagickPathExtent];
2250
2251 double
2252 degrees,
2253 gamma,
2254 percentage,
2255 radius,
2256 sigma,
2257 threshold;
2258
2259 extern const char
2260 DefaultTileFrame[];
2261
2262 Image
2263 *images,
2264 *montage_image,
2265 *preview_image,
2266 *thumbnail;
2267
2268 ImageInfo
2269 *preview_info;
2270
2271 MagickBooleanType
2272 proceed;
2273
2274 MontageInfo
2275 *montage_info;
2276
2277 QuantizeInfo
2278 quantize_info;
2279
2280 RectangleInfo
2281 geometry;
2282
2283 register ssize_t
2284 i,
2285 x;
2286
2287 size_t
2288 colors;
2289
2290 ssize_t
2291 y;
2292
2293 /*
2294 Open output image file.
2295 */
2296 assert(image != (Image *) NULL);
2297 assert(image->signature == MagickCoreSignature);
2298 if (image->debug != MagickFalse)
2299 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2300 colors=2;
2301 degrees=0.0;
2302 gamma=(-0.2f);
2303 preview_info=AcquireImageInfo();
2304 SetGeometry(image,&geometry);
2305 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2306 &geometry.width,&geometry.height);
2307 images=NewImageList();
2308 percentage=12.5;
2309 GetQuantizeInfo(&quantize_info);
2310 radius=0.0;
2311 sigma=1.0;
2312 threshold=0.0;
2313 x=0;
2314 y=0;
2315 for (i=0; i < NumberTiles; i++)
2316 {
2317 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2318 if (thumbnail == (Image *) NULL)
2319 break;
2320 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2321 (void *) NULL);
2322 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel,exception);
2323 if (i == (NumberTiles/2))
2324 {
2325 (void) QueryColorCompliance("#dfdfdf",AllCompliance,
2326 &thumbnail->alpha_color,exception);
2327 AppendImageToList(&images,thumbnail);
2328 continue;
2329 }
2330 switch (preview)
2331 {
2332 case RotatePreview:
2333 {
2334 degrees+=45.0;
2335 preview_image=RotateImage(thumbnail,degrees,exception);
2336 (void) FormatLocaleString(label,MagickPathExtent,"rotate %g",degrees);
2337 break;
2338 }
2339 case ShearPreview:
2340 {
2341 degrees+=5.0;
2342 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
2343 (void) FormatLocaleString(label,MagickPathExtent,"shear %gx%g",degrees,
2344 2.0*degrees);
2345 break;
2346 }
2347 case RollPreview:
2348 {
2349 x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
2350 y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
2351 preview_image=RollImage(thumbnail,x,y,exception);
2352 (void) FormatLocaleString(label,MagickPathExtent,"roll %+.20gx%+.20g",
2353 (double) x,(double) y);
2354 break;
2355 }
2356 case HuePreview:
2357 {
2358 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2359 if (preview_image == (Image *) NULL)
2360 break;
2361 (void) FormatLocaleString(factor,MagickPathExtent,"100,100,%g",2.0*
2362 percentage);
2363 (void) ModulateImage(preview_image,factor,exception);
2364 (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
2365 break;
2366 }
2367 case SaturationPreview:
2368 {
2369 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2370 if (preview_image == (Image *) NULL)
2371 break;
2372 (void) FormatLocaleString(factor,MagickPathExtent,"100,%g",2.0*percentage);
2373 (void) ModulateImage(preview_image,factor,exception);
2374 (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
2375 break;
2376 }
2377 case BrightnessPreview:
2378 {
2379 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2380 if (preview_image == (Image *) NULL)
2381 break;
2382 (void) FormatLocaleString(factor,MagickPathExtent,"%g",2.0*percentage);
2383 (void) ModulateImage(preview_image,factor,exception);
2384 (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
2385 break;
2386 }
2387 case GammaPreview:
2388 default:
2389 {
2390 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2391 if (preview_image == (Image *) NULL)
2392 break;
2393 gamma+=0.4f;
2394 (void) GammaImage(preview_image,gamma,exception);
2395 (void) FormatLocaleString(label,MagickPathExtent,"gamma %g",gamma);
2396 break;
2397 }
2398 case SpiffPreview:
2399 {
2400 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2401 if (preview_image != (Image *) NULL)
2402 for (x=0; x < i; x++)
2403 (void) ContrastImage(preview_image,MagickTrue,exception);
2404 (void) FormatLocaleString(label,MagickPathExtent,"contrast (%.20g)",
2405 (double) i+1);
2406 break;
2407 }
2408 case DullPreview:
2409 {
2410 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2411 if (preview_image == (Image *) NULL)
2412 break;
2413 for (x=0; x < i; x++)
2414 (void) ContrastImage(preview_image,MagickFalse,exception);
2415 (void) FormatLocaleString(label,MagickPathExtent,"+contrast (%.20g)",
2416 (double) i+1);
2417 break;
2418 }
2419 case GrayscalePreview:
2420 {
2421 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2422 if (preview_image == (Image *) NULL)
2423 break;
2424 colors<<=1;
2425 quantize_info.number_colors=colors;
2426 quantize_info.colorspace=GRAYColorspace;
2427 (void) QuantizeImage(&quantize_info,preview_image,exception);
2428 (void) FormatLocaleString(label,MagickPathExtent,
2429 "-colorspace gray -colors %.20g",(double) colors);
2430 break;
2431 }
2432 case QuantizePreview:
2433 {
2434 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2435 if (preview_image == (Image *) NULL)
2436 break;
2437 colors<<=1;
2438 quantize_info.number_colors=colors;
2439 (void) QuantizeImage(&quantize_info,preview_image,exception);
2440 (void) FormatLocaleString(label,MagickPathExtent,"colors %.20g",(double)
2441 colors);
2442 break;
2443 }
2444 case DespecklePreview:
2445 {
2446 for (x=0; x < (i-1); x++)
2447 {
2448 preview_image=DespeckleImage(thumbnail,exception);
2449 if (preview_image == (Image *) NULL)
2450 break;
2451 thumbnail=DestroyImage(thumbnail);
2452 thumbnail=preview_image;
2453 }
2454 preview_image=DespeckleImage(thumbnail,exception);
2455 if (preview_image == (Image *) NULL)
2456 break;
2457 (void) FormatLocaleString(label,MagickPathExtent,"despeckle (%.20g)",
2458 (double) i+1);
2459 break;
2460 }
2461 case ReduceNoisePreview:
2462 {
2463 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
2464 (size_t) radius,exception);
2465 (void) FormatLocaleString(label,MagickPathExtent,"noise %g",radius);
2466 break;
2467 }
2468 case AddNoisePreview:
2469 {
2470 switch ((int) i)
2471 {
2472 case 0:
2473 {
2474 (void) CopyMagickString(factor,"uniform",MagickPathExtent);
2475 break;
2476 }
2477 case 1:
2478 {
2479 (void) CopyMagickString(factor,"gaussian",MagickPathExtent);
2480 break;
2481 }
2482 case 2:
2483 {
2484 (void) CopyMagickString(factor,"multiplicative",MagickPathExtent);
2485 break;
2486 }
2487 case 3:
2488 {
2489 (void) CopyMagickString(factor,"impulse",MagickPathExtent);
2490 break;
2491 }
2492 case 5:
2493 {
2494 (void) CopyMagickString(factor,"laplacian",MagickPathExtent);
2495 break;
2496 }
2497 case 6:
2498 {
2499 (void) CopyMagickString(factor,"Poisson",MagickPathExtent);
2500 break;
2501 }
2502 default:
2503 {
2504 (void) CopyMagickString(thumbnail->magick,"NULL",MagickPathExtent);
2505 break;
2506 }
2507 }
2508 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
2509 (size_t) i,exception);
2510 (void) FormatLocaleString(label,MagickPathExtent,"+noise %s",factor);
2511 break;
2512 }
2513 case SharpenPreview:
2514 {
2515 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
2516 (void) FormatLocaleString(label,MagickPathExtent,"sharpen %gx%g",radius,
2517 sigma);
2518 break;
2519 }
2520 case BlurPreview:
2521 {
2522 preview_image=BlurImage(thumbnail,radius,sigma,exception);
2523 (void) FormatLocaleString(label,MagickPathExtent,"blur %gx%g",radius,
2524 sigma);
2525 break;
2526 }
2527 case ThresholdPreview:
2528 {
2529 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2530 if (preview_image == (Image *) NULL)
2531 break;
2532 (void) BilevelImage(thumbnail,(double) (percentage*((double)
2533 QuantumRange+1.0))/100.0,exception);
2534 (void) FormatLocaleString(label,MagickPathExtent,"threshold %g",(double)
2535 (percentage*((double) QuantumRange+1.0))/100.0);
2536 break;
2537 }
2538 case EdgeDetectPreview:
2539 {
2540 preview_image=EdgeImage(thumbnail,radius,exception);
2541 (void) FormatLocaleString(label,MagickPathExtent,"edge %g",radius);
2542 break;
2543 }
2544 case SpreadPreview:
2545 {
2546 preview_image=SpreadImage(thumbnail,image->interpolate,radius,
2547 exception);
2548 (void) FormatLocaleString(label,MagickPathExtent,"spread %g",
2549 radius+0.5);
2550 break;
2551 }
2552 case SolarizePreview:
2553 {
2554 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2555 if (preview_image == (Image *) NULL)
2556 break;
2557 (void) SolarizeImage(preview_image,(double) QuantumRange*percentage/
2558 100.0,exception);
2559 (void) FormatLocaleString(label,MagickPathExtent,"solarize %g",
2560 (QuantumRange*percentage)/100.0);
2561 break;
2562 }
2563 case ShadePreview:
2564 {
2565 degrees+=10.0;
2566 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2567 exception);
2568 (void) FormatLocaleString(label,MagickPathExtent,"shade %gx%g",degrees,
2569 degrees);
2570 break;
2571 }
2572 case RaisePreview:
2573 {
2574 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2575 if (preview_image == (Image *) NULL)
2576 break;
2577 geometry.width=(size_t) (2*i+2);
2578 geometry.height=(size_t) (2*i+2);
2579 geometry.x=(i-1)/2;
2580 geometry.y=(i-1)/2;
2581 (void) RaiseImage(preview_image,&geometry,MagickTrue,exception);
2582 (void) FormatLocaleString(label,MagickPathExtent,
2583 "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
2584 geometry.height,(double) geometry.x,(double) geometry.y);
2585 break;
2586 }
2587 case SegmentPreview:
2588 {
2589 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2590 if (preview_image == (Image *) NULL)
2591 break;
2592 threshold+=0.4f;
2593 (void) SegmentImage(preview_image,sRGBColorspace,MagickFalse,threshold,
2594 threshold,exception);
2595 (void) FormatLocaleString(label,MagickPathExtent,"segment %gx%g",
2596 threshold,threshold);
2597 break;
2598 }
2599 case SwirlPreview:
2600 {
2601 preview_image=SwirlImage(thumbnail,degrees,image->interpolate,
2602 exception);
2603 (void) FormatLocaleString(label,MagickPathExtent,"swirl %g",degrees);
2604 degrees+=45.0;
2605 break;
2606 }
2607 case ImplodePreview:
2608 {
2609 degrees+=0.1f;
2610 preview_image=ImplodeImage(thumbnail,degrees,image->interpolate,
2611 exception);
2612 (void) FormatLocaleString(label,MagickPathExtent,"implode %g",degrees);
2613 break;
2614 }
2615 case WavePreview:
2616 {
2617 degrees+=5.0f;
2618 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,
2619 image->interpolate,exception);
2620 (void) FormatLocaleString(label,MagickPathExtent,"wave %gx%g",0.5*degrees,
2621 2.0*degrees);
2622 break;
2623 }
2624 case OilPaintPreview:
2625 {
2626 preview_image=OilPaintImage(thumbnail,(double) radius,(double) sigma,
2627 exception);
2628 (void) FormatLocaleString(label,MagickPathExtent,"charcoal %gx%g",radius,
2629 sigma);
2630 break;
2631 }
2632 case CharcoalDrawingPreview:
2633 {
2634 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2635 exception);
2636 (void) FormatLocaleString(label,MagickPathExtent,"charcoal %gx%g",radius,
2637 sigma);
2638 break;
2639 }
2640 case JPEGPreview:
2641 {
2642 char
2643 filename[MagickPathExtent];
2644
2645 int
2646 file;
2647
2648 MagickBooleanType
2649 status;
2650
2651 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2652 if (preview_image == (Image *) NULL)
2653 break;
2654 preview_info->quality=(size_t) percentage;
2655 (void) FormatLocaleString(factor,MagickPathExtent,"%.20g",(double)
2656 preview_info->quality);
2657 file=AcquireUniqueFileResource(filename);
2658 if (file != -1)
2659 file=close(file)-1;
2660 (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
2661 "jpeg:%s",filename);
2662 status=WriteImage(preview_info,preview_image,exception);
2663 if (status != MagickFalse)
2664 {
2665 Image
2666 *quality_image;
2667
2668 (void) CopyMagickString(preview_info->filename,
2669 preview_image->filename,MagickPathExtent);
2670 quality_image=ReadImage(preview_info,exception);
2671 if (quality_image != (Image *) NULL)
2672 {
2673 preview_image=DestroyImage(preview_image);
2674 preview_image=quality_image;
2675 }
2676 }
2677 (void) RelinquishUniqueFileResource(preview_image->filename);
2678 if ((GetBlobSize(preview_image)/1024) >= 1024)
2679 (void) FormatLocaleString(label,MagickPathExtent,"quality %s\n%gmb ",
2680 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2681 1024.0/1024.0);
2682 else
2683 if (GetBlobSize(preview_image) >= 1024)
2684 (void) FormatLocaleString(label,MagickPathExtent,
2685 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
2686 GetBlobSize(preview_image))/1024.0);
2687 else
2688 (void) FormatLocaleString(label,MagickPathExtent,"quality %s\n%.20gb ",
2689 factor,(double) ((MagickOffsetType) GetBlobSize(thumbnail)));
2690 break;
2691 }
2692 }
2693 thumbnail=DestroyImage(thumbnail);
2694 percentage+=12.5;
2695 radius+=0.5;
2696 sigma+=0.25;
2697 if (preview_image == (Image *) NULL)
2698 break;
2699 (void) DeleteImageProperty(preview_image,"label");
2700 (void) SetImageProperty(preview_image,"label",label,exception);
2701 AppendImageToList(&images,preview_image);
2702 proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2703 NumberTiles);
2704 if (proceed == MagickFalse)
2705 break;
2706 }
2707 if (images == (Image *) NULL)
2708 {
2709 preview_info=DestroyImageInfo(preview_info);
2710 return((Image *) NULL);
2711 }
2712 /*
2713 Create the montage.
2714 */
2715 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2716 (void) CopyMagickString(montage_info->filename,image->filename,MagickPathExtent);
2717 montage_info->shadow=MagickTrue;
2718 (void) CloneString(&montage_info->tile,"3x3");
2719 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2720 (void) CloneString(&montage_info->frame,DefaultTileFrame);
2721 montage_image=MontageImages(images,montage_info,exception);
2722 montage_info=DestroyMontageInfo(montage_info);
2723 images=DestroyImageList(images);
2724 if (montage_image == (Image *) NULL)
2725 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2726 if (montage_image->montage != (char *) NULL)
2727 {
2728 /*
2729 Free image directory.
2730 */
2731 montage_image->montage=(char *) RelinquishMagickMemory(
2732 montage_image->montage);
2733 if (image->directory != (char *) NULL)
2734 montage_image->directory=(char *) RelinquishMagickMemory(
2735 montage_image->directory);
2736 }
2737 preview_info=DestroyImageInfo(preview_info);
2738 return(montage_image);
2739 }
2740
2741 /*
2742 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2743 % %
2744 % %
2745 % %
2746 % R o t a t i o n a l B l u r I m a g e %
2747 % %
2748 % %
2749 % %
2750 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2751 %
2752 % RotationalBlurImage() applies a radial blur to the image.
2753 %
2754 % Andrew Protano contributed this effect.
2755 %
2756 % The format of the RotationalBlurImage method is:
2757 %
2758 % Image *RotationalBlurImage(const Image *image,const double angle,
2759 % ExceptionInfo *exception)
2760 %
2761 % A description of each parameter follows:
2762 %
2763 % o image: the image.
2764 %
2765 % o angle: the angle of the radial blur.
2766 %
2767 % o blur: the blur.
2768 %
2769 % o exception: return any errors or warnings in this structure.
2770 %
2771 */
RotationalBlurImage(const Image * image,const double angle,ExceptionInfo * exception)2772 MagickExport Image *RotationalBlurImage(const Image *image,const double angle,
2773 ExceptionInfo *exception)
2774 {
2775 CacheView
2776 *blur_view,
2777 *image_view,
2778 *radial_view;
2779
2780 double
2781 blur_radius,
2782 *cos_theta,
2783 offset,
2784 *sin_theta,
2785 theta;
2786
2787 Image
2788 *blur_image;
2789
2790 MagickBooleanType
2791 status;
2792
2793 MagickOffsetType
2794 progress;
2795
2796 PointInfo
2797 blur_center;
2798
2799 register ssize_t
2800 i;
2801
2802 size_t
2803 n;
2804
2805 ssize_t
2806 y;
2807
2808 /*
2809 Allocate blur image.
2810 */
2811 assert(image != (Image *) NULL);
2812 assert(image->signature == MagickCoreSignature);
2813 if (image->debug != MagickFalse)
2814 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2815 assert(exception != (ExceptionInfo *) NULL);
2816 assert(exception->signature == MagickCoreSignature);
2817 #if defined(MAGICKCORE_OPENCL_SUPPORT)
2818 blur_image=AccelerateRotationalBlurImage(image,angle,exception);
2819 if (blur_image != (Image *) NULL)
2820 return(blur_image);
2821 #endif
2822 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2823 if (blur_image == (Image *) NULL)
2824 return((Image *) NULL);
2825 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2826 {
2827 blur_image=DestroyImage(blur_image);
2828 return((Image *) NULL);
2829 }
2830 blur_center.x=(double) (image->columns-1)/2.0;
2831 blur_center.y=(double) (image->rows-1)/2.0;
2832 blur_radius=hypot(blur_center.x,blur_center.y);
2833 n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
2834 theta=DegreesToRadians(angle)/(double) (n-1);
2835 cos_theta=(double *) AcquireQuantumMemory((size_t) n,
2836 sizeof(*cos_theta));
2837 sin_theta=(double *) AcquireQuantumMemory((size_t) n,
2838 sizeof(*sin_theta));
2839 if ((cos_theta == (double *) NULL) ||
2840 (sin_theta == (double *) NULL))
2841 {
2842 blur_image=DestroyImage(blur_image);
2843 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2844 }
2845 offset=theta*(double) (n-1)/2.0;
2846 for (i=0; i < (ssize_t) n; i++)
2847 {
2848 cos_theta[i]=cos((double) (theta*i-offset));
2849 sin_theta[i]=sin((double) (theta*i-offset));
2850 }
2851 /*
2852 Radial blur image.
2853 */
2854 status=MagickTrue;
2855 progress=0;
2856 image_view=AcquireVirtualCacheView(image,exception);
2857 radial_view=AcquireVirtualCacheView(image,exception);
2858 blur_view=AcquireAuthenticCacheView(blur_image,exception);
2859 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2860 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2861 magick_threads(image,blur_image,image->rows,1)
2862 #endif
2863 for (y=0; y < (ssize_t) image->rows; y++)
2864 {
2865 register const Quantum
2866 *magick_restrict p;
2867
2868 register Quantum
2869 *magick_restrict q;
2870
2871 register ssize_t
2872 x;
2873
2874 if (status == MagickFalse)
2875 continue;
2876 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2877 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2878 exception);
2879 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2880 {
2881 status=MagickFalse;
2882 continue;
2883 }
2884 for (x=0; x < (ssize_t) image->columns; x++)
2885 {
2886 double
2887 radius;
2888
2889 PointInfo
2890 center;
2891
2892 register ssize_t
2893 i;
2894
2895 size_t
2896 step;
2897
2898 center.x=(double) x-blur_center.x;
2899 center.y=(double) y-blur_center.y;
2900 radius=hypot((double) center.x,center.y);
2901 if (radius == 0)
2902 step=1;
2903 else
2904 {
2905 step=(size_t) (blur_radius/radius);
2906 if (step == 0)
2907 step=1;
2908 else
2909 if (step >= n)
2910 step=n-1;
2911 }
2912 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2913 {
2914 double
2915 gamma,
2916 pixel;
2917
2918 PixelChannel
2919 channel;
2920
2921 PixelTrait
2922 blur_traits,
2923 traits;
2924
2925 register const Quantum
2926 *magick_restrict r;
2927
2928 register ssize_t
2929 j;
2930
2931 channel=GetPixelChannelChannel(image,i);
2932 traits=GetPixelChannelTraits(image,channel);
2933 blur_traits=GetPixelChannelTraits(blur_image,channel);
2934 if ((traits == UndefinedPixelTrait) ||
2935 (blur_traits == UndefinedPixelTrait))
2936 continue;
2937 if (((blur_traits & CopyPixelTrait) != 0) ||
2938 (GetPixelReadMask(image,p) == 0))
2939 {
2940 SetPixelChannel(blur_image,channel,p[i],q);
2941 continue;
2942 }
2943 gamma=0.0;
2944 pixel=0.0;
2945 if ((GetPixelChannelTraits(image,AlphaChannel) == UndefinedPixelTrait) ||
2946 (channel == AlphaPixelChannel))
2947 {
2948 for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2949 {
2950 r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
2951 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2952 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2953 1,1,exception);
2954 if (r == (const Quantum *) NULL)
2955 {
2956 status=MagickFalse;
2957 continue;
2958 }
2959 pixel+=r[i];
2960 gamma++;
2961 }
2962 gamma=PerceptibleReciprocal(gamma);
2963 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2964 continue;
2965 }
2966 for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2967 {
2968 double
2969 alpha;
2970
2971 r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
2972 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2973 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2974 1,1,exception);
2975 if (r == (const Quantum *) NULL)
2976 {
2977 status=MagickFalse;
2978 continue;
2979 }
2980 alpha=(double) QuantumScale*GetPixelAlpha(image,r);
2981 pixel+=alpha*r[i];
2982 gamma+=alpha;
2983 }
2984 gamma=PerceptibleReciprocal(gamma);
2985 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2986 }
2987 p+=GetPixelChannels(image);
2988 q+=GetPixelChannels(blur_image);
2989 }
2990 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2991 status=MagickFalse;
2992 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2993 {
2994 MagickBooleanType
2995 proceed;
2996
2997 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2998 #pragma omp critical (MagickCore_RotationalBlurImage)
2999 #endif
3000 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3001 if (proceed == MagickFalse)
3002 status=MagickFalse;
3003 }
3004 }
3005 blur_view=DestroyCacheView(blur_view);
3006 radial_view=DestroyCacheView(radial_view);
3007 image_view=DestroyCacheView(image_view);
3008 cos_theta=(double *) RelinquishMagickMemory(cos_theta);
3009 sin_theta=(double *) RelinquishMagickMemory(sin_theta);
3010 if (status == MagickFalse)
3011 blur_image=DestroyImage(blur_image);
3012 return(blur_image);
3013 }
3014
3015 /*
3016 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3017 % %
3018 % %
3019 % %
3020 % S e l e c t i v e B l u r I m a g e %
3021 % %
3022 % %
3023 % %
3024 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3025 %
3026 % SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3027 % It is similar to the unsharpen mask that sharpens everything with contrast
3028 % above a certain threshold.
3029 %
3030 % The format of the SelectiveBlurImage method is:
3031 %
3032 % Image *SelectiveBlurImage(const Image *image,const double radius,
3033 % const double sigma,const double threshold,ExceptionInfo *exception)
3034 %
3035 % A description of each parameter follows:
3036 %
3037 % o image: the image.
3038 %
3039 % o radius: the radius of the Gaussian, in pixels, not counting the center
3040 % pixel.
3041 %
3042 % o sigma: the standard deviation of the Gaussian, in pixels.
3043 %
3044 % o threshold: only pixels within this contrast threshold are included
3045 % in the blur operation.
3046 %
3047 % o exception: return any errors or warnings in this structure.
3048 %
3049 */
SelectiveBlurImage(const Image * image,const double radius,const double sigma,const double threshold,ExceptionInfo * exception)3050 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
3051 const double sigma,const double threshold,ExceptionInfo *exception)
3052 {
3053 #define SelectiveBlurImageTag "SelectiveBlur/Image"
3054
3055 CacheView
3056 *blur_view,
3057 *image_view,
3058 *luminance_view;
3059
3060 Image
3061 *blur_image,
3062 *luminance_image;
3063
3064 MagickBooleanType
3065 status;
3066
3067 MagickOffsetType
3068 progress;
3069
3070 MagickRealType
3071 *kernel;
3072
3073 register ssize_t
3074 i;
3075
3076 size_t
3077 width;
3078
3079 ssize_t
3080 center,
3081 j,
3082 u,
3083 v,
3084 y;
3085
3086 /*
3087 Initialize blur image attributes.
3088 */
3089 assert(image != (Image *) NULL);
3090 assert(image->signature == MagickCoreSignature);
3091 if (image->debug != MagickFalse)
3092 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3093 assert(exception != (ExceptionInfo *) NULL);
3094 assert(exception->signature == MagickCoreSignature);
3095 width=GetOptimalKernelWidth1D(radius,sigma);
3096 kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
3097 width,width*sizeof(*kernel)));
3098 if (kernel == (MagickRealType *) NULL)
3099 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3100 j=(ssize_t) (width-1)/2;
3101 i=0;
3102 for (v=(-j); v <= j; v++)
3103 {
3104 for (u=(-j); u <= j; u++)
3105 kernel[i++]=(MagickRealType) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
3106 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3107 }
3108 if (image->debug != MagickFalse)
3109 {
3110 char
3111 format[MagickPathExtent],
3112 *message;
3113
3114 register const MagickRealType
3115 *k;
3116
3117 ssize_t
3118 u,
3119 v;
3120
3121 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
3122 " SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
3123 width);
3124 message=AcquireString("");
3125 k=kernel;
3126 for (v=0; v < (ssize_t) width; v++)
3127 {
3128 *message='\0';
3129 (void) FormatLocaleString(format,MagickPathExtent,"%.20g: ",(double) v);
3130 (void) ConcatenateString(&message,format);
3131 for (u=0; u < (ssize_t) width; u++)
3132 {
3133 (void) FormatLocaleString(format,MagickPathExtent,"%+f ",(double) *k++);
3134 (void) ConcatenateString(&message,format);
3135 }
3136 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3137 }
3138 message=DestroyString(message);
3139 }
3140 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3141 if (blur_image == (Image *) NULL)
3142 return((Image *) NULL);
3143 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
3144 {
3145 blur_image=DestroyImage(blur_image);
3146 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3147 return((Image *) NULL);
3148 }
3149 luminance_image=CloneImage(image,0,0,MagickTrue,exception);
3150 if (luminance_image == (Image *) NULL)
3151 {
3152 blur_image=DestroyImage(blur_image);
3153 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3154 return((Image *) NULL);
3155 }
3156 status=TransformImageColorspace(luminance_image,GRAYColorspace,exception);
3157 if (status == MagickFalse)
3158 {
3159 luminance_image=DestroyImage(luminance_image);
3160 blur_image=DestroyImage(blur_image);
3161 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3162 return((Image *) NULL);
3163 }
3164 /*
3165 Threshold blur image.
3166 */
3167 status=MagickTrue;
3168 progress=0;
3169 center=(ssize_t) (GetPixelChannels(image)*(image->columns+width)*
3170 ((width-1)/2L)+GetPixelChannels(image)*((width-1)/2L));
3171 image_view=AcquireVirtualCacheView(image,exception);
3172 luminance_view=AcquireVirtualCacheView(luminance_image,exception);
3173 blur_view=AcquireAuthenticCacheView(blur_image,exception);
3174 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3175 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3176 magick_threads(image,blur_image,image->rows,1)
3177 #endif
3178 for (y=0; y < (ssize_t) image->rows; y++)
3179 {
3180 double
3181 contrast;
3182
3183 MagickBooleanType
3184 sync;
3185
3186 register const Quantum
3187 *magick_restrict l,
3188 *magick_restrict p;
3189
3190 register Quantum
3191 *magick_restrict q;
3192
3193 register ssize_t
3194 x;
3195
3196 if (status == MagickFalse)
3197 continue;
3198 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) (width-1)/2L),y-(ssize_t)
3199 ((width-1)/2L),image->columns+width,width,exception);
3200 l=GetCacheViewVirtualPixels(luminance_view,-((ssize_t) (width-1)/2L),y-
3201 (ssize_t) ((width-1)/2L),luminance_image->columns+width,width,exception);
3202 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3203 exception);
3204 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3205 {
3206 status=MagickFalse;
3207 continue;
3208 }
3209 for (x=0; x < (ssize_t) image->columns; x++)
3210 {
3211 double
3212 intensity;
3213
3214 register ssize_t
3215 i;
3216
3217 intensity=GetPixelIntensity(image,p+center);
3218 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3219 {
3220 double
3221 alpha,
3222 gamma,
3223 pixel;
3224
3225 PixelChannel
3226 channel;
3227
3228 PixelTrait
3229 blur_traits,
3230 traits;
3231
3232 register const MagickRealType
3233 *magick_restrict k;
3234
3235 register const Quantum
3236 *magick_restrict luminance_pixels,
3237 *magick_restrict pixels;
3238
3239 register ssize_t
3240 u;
3241
3242 ssize_t
3243 v;
3244
3245 channel=GetPixelChannelChannel(image,i);
3246 traits=GetPixelChannelTraits(image,channel);
3247 blur_traits=GetPixelChannelTraits(blur_image,channel);
3248 if ((traits == UndefinedPixelTrait) ||
3249 (blur_traits == UndefinedPixelTrait))
3250 continue;
3251 if (((blur_traits & CopyPixelTrait) != 0) ||
3252 (GetPixelReadMask(image,p+center) == 0))
3253 {
3254 SetPixelChannel(blur_image,channel,p[center+i],q);
3255 continue;
3256 }
3257 k=kernel;
3258 pixel=0.0;
3259 pixels=p;
3260 luminance_pixels=l;
3261 gamma=0.0;
3262 if ((blur_traits & BlendPixelTrait) == 0)
3263 {
3264 for (v=0; v < (ssize_t) width; v++)
3265 {
3266 for (u=0; u < (ssize_t) width; u++)
3267 {
3268 contrast=GetPixelIntensity(luminance_image,luminance_pixels)-
3269 intensity;
3270 if (fabs(contrast) < threshold)
3271 {
3272 pixel+=(*k)*pixels[i];
3273 gamma+=(*k);
3274 }
3275 k++;
3276 pixels+=GetPixelChannels(image);
3277 luminance_pixels+=GetPixelChannels(luminance_image);
3278 }
3279 pixels+=GetPixelChannels(image)*image->columns;
3280 luminance_pixels+=GetPixelChannels(luminance_image)*
3281 luminance_image->columns;
3282 }
3283 if (fabs((double) gamma) < MagickEpsilon)
3284 {
3285 SetPixelChannel(blur_image,channel,p[center+i],q);
3286 continue;
3287 }
3288 gamma=PerceptibleReciprocal(gamma);
3289 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3290 continue;
3291 }
3292 for (v=0; v < (ssize_t) width; v++)
3293 {
3294 for (u=0; u < (ssize_t) width; u++)
3295 {
3296 contrast=GetPixelIntensity(image,pixels)-intensity;
3297 if (fabs(contrast) < threshold)
3298 {
3299 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
3300 pixel+=(*k)*alpha*pixels[i];
3301 gamma+=(*k)*alpha;
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 }
3319 p+=GetPixelChannels(image);
3320 l+=GetPixelChannels(luminance_image);
3321 q+=GetPixelChannels(blur_image);
3322 }
3323 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3324 if (sync == MagickFalse)
3325 status=MagickFalse;
3326 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3327 {
3328 MagickBooleanType
3329 proceed;
3330
3331 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3332 #pragma omp critical (MagickCore_SelectiveBlurImage)
3333 #endif
3334 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
3335 image->rows);
3336 if (proceed == MagickFalse)
3337 status=MagickFalse;
3338 }
3339 }
3340 blur_image->type=image->type;
3341 blur_view=DestroyCacheView(blur_view);
3342 image_view=DestroyCacheView(image_view);
3343 luminance_image=DestroyImage(luminance_image);
3344 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3345 if (status == MagickFalse)
3346 blur_image=DestroyImage(blur_image);
3347 return(blur_image);
3348 }
3349
3350 /*
3351 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3352 % %
3353 % %
3354 % %
3355 % S h a d e I m a g e %
3356 % %
3357 % %
3358 % %
3359 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3360 %
3361 % ShadeImage() shines a distant light on an image to create a
3362 % three-dimensional effect. You control the positioning of the light with
3363 % azimuth and elevation; azimuth is measured in degrees off the x axis
3364 % and elevation is measured in pixels above the Z axis.
3365 %
3366 % The format of the ShadeImage method is:
3367 %
3368 % Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3369 % const double azimuth,const double elevation,ExceptionInfo *exception)
3370 %
3371 % A description of each parameter follows:
3372 %
3373 % o image: the image.
3374 %
3375 % o gray: A value other than zero shades the intensity of each pixel.
3376 %
3377 % o azimuth, elevation: Define the light source direction.
3378 %
3379 % o exception: return any errors or warnings in this structure.
3380 %
3381 */
ShadeImage(const Image * image,const MagickBooleanType gray,const double azimuth,const double elevation,ExceptionInfo * exception)3382 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3383 const double azimuth,const double elevation,ExceptionInfo *exception)
3384 {
3385 #define ShadeImageTag "Shade/Image"
3386
3387 CacheView
3388 *image_view,
3389 *shade_view;
3390
3391 Image
3392 *linear_image,
3393 *shade_image;
3394
3395 MagickBooleanType
3396 status;
3397
3398 MagickOffsetType
3399 progress;
3400
3401 PrimaryInfo
3402 light;
3403
3404 ssize_t
3405 y;
3406
3407 /*
3408 Initialize shaded image attributes.
3409 */
3410 assert(image != (const Image *) NULL);
3411 assert(image->signature == MagickCoreSignature);
3412 if (image->debug != MagickFalse)
3413 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3414 assert(exception != (ExceptionInfo *) NULL);
3415 assert(exception->signature == MagickCoreSignature);
3416 linear_image=CloneImage(image,0,0,MagickTrue,exception);
3417 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3418 if ((linear_image == (Image *) NULL) || (shade_image == (Image *) NULL))
3419 {
3420 if (linear_image != (Image *) NULL)
3421 linear_image=DestroyImage(linear_image);
3422 if (shade_image != (Image *) NULL)
3423 shade_image=DestroyImage(shade_image);
3424 return((Image *) NULL);
3425 }
3426 if (SetImageStorageClass(shade_image,DirectClass,exception) == MagickFalse)
3427 {
3428 linear_image=DestroyImage(linear_image);
3429 shade_image=DestroyImage(shade_image);
3430 return((Image *) NULL);
3431 }
3432 /*
3433 Compute the light vector.
3434 */
3435 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3436 cos(DegreesToRadians(elevation));
3437 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3438 cos(DegreesToRadians(elevation));
3439 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3440 /*
3441 Shade image.
3442 */
3443 status=MagickTrue;
3444 progress=0;
3445 image_view=AcquireVirtualCacheView(linear_image,exception);
3446 shade_view=AcquireAuthenticCacheView(shade_image,exception);
3447 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3448 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3449 magick_threads(linear_image,shade_image,linear_image->rows,1)
3450 #endif
3451 for (y=0; y < (ssize_t) linear_image->rows; y++)
3452 {
3453 double
3454 distance,
3455 normal_distance,
3456 shade;
3457
3458 PrimaryInfo
3459 normal;
3460
3461 register const Quantum
3462 *magick_restrict center,
3463 *magick_restrict p,
3464 *magick_restrict post,
3465 *magick_restrict pre;
3466
3467 register Quantum
3468 *magick_restrict q;
3469
3470 register ssize_t
3471 x;
3472
3473 if (status == MagickFalse)
3474 continue;
3475 p=GetCacheViewVirtualPixels(image_view,-1,y-1,linear_image->columns+2,3,
3476 exception);
3477 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3478 exception);
3479 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3480 {
3481 status=MagickFalse;
3482 continue;
3483 }
3484 /*
3485 Shade this row of pixels.
3486 */
3487 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
3488 for (x=0; x < (ssize_t) linear_image->columns; x++)
3489 {
3490 register ssize_t
3491 i;
3492
3493 /*
3494 Determine the surface normal and compute shading.
3495 */
3496 pre=p+GetPixelChannels(linear_image);
3497 center=pre+(linear_image->columns+2)*GetPixelChannels(linear_image);
3498 post=center+(linear_image->columns+2)*GetPixelChannels(linear_image);
3499 normal.x=(double) (
3500 GetPixelIntensity(linear_image,pre-GetPixelChannels(linear_image))+
3501 GetPixelIntensity(linear_image,center-GetPixelChannels(linear_image))+
3502 GetPixelIntensity(linear_image,post-GetPixelChannels(linear_image))-
3503 GetPixelIntensity(linear_image,pre+GetPixelChannels(linear_image))-
3504 GetPixelIntensity(linear_image,center+GetPixelChannels(linear_image))-
3505 GetPixelIntensity(linear_image,post+GetPixelChannels(linear_image)));
3506 normal.y=(double) (
3507 GetPixelIntensity(linear_image,post-GetPixelChannels(linear_image))+
3508 GetPixelIntensity(linear_image,post)+
3509 GetPixelIntensity(linear_image,post+GetPixelChannels(linear_image))-
3510 GetPixelIntensity(linear_image,pre-GetPixelChannels(linear_image))-
3511 GetPixelIntensity(linear_image,pre)-
3512 GetPixelIntensity(linear_image,pre+GetPixelChannels(linear_image)));
3513 if ((fabs(normal.x) <= MagickEpsilon) &&
3514 (fabs(normal.y) <= MagickEpsilon))
3515 shade=light.z;
3516 else
3517 {
3518 shade=0.0;
3519 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3520 if (distance > MagickEpsilon)
3521 {
3522 normal_distance=normal.x*normal.x+normal.y*normal.y+
3523 normal.z*normal.z;
3524 if (normal_distance > (MagickEpsilon*MagickEpsilon))
3525 shade=distance/sqrt((double) normal_distance);
3526 }
3527 }
3528 for (i=0; i < (ssize_t) GetPixelChannels(linear_image); i++)
3529 {
3530 PixelChannel
3531 channel;
3532
3533 PixelTrait
3534 shade_traits,
3535 traits;
3536
3537 channel=GetPixelChannelChannel(linear_image,i);
3538 traits=GetPixelChannelTraits(linear_image,channel);
3539 shade_traits=GetPixelChannelTraits(shade_image,channel);
3540 if ((traits == UndefinedPixelTrait) ||
3541 (shade_traits == UndefinedPixelTrait))
3542 continue;
3543 if (((shade_traits & CopyPixelTrait) != 0) ||
3544 (GetPixelReadMask(linear_image,center) == 0))
3545 {
3546 SetPixelChannel(shade_image,channel,center[i],q);
3547 continue;
3548 }
3549 if ((traits & UpdatePixelTrait) == 0)
3550 {
3551 SetPixelChannel(shade_image,channel,center[i],q);
3552 continue;
3553 }
3554 if (gray != MagickFalse)
3555 {
3556 SetPixelChannel(shade_image,channel,ClampToQuantum(shade),q);
3557 continue;
3558 }
3559 SetPixelChannel(shade_image,channel,ClampToQuantum(QuantumScale*shade*
3560 center[i]),q);
3561 }
3562 p+=GetPixelChannels(linear_image);
3563 q+=GetPixelChannels(shade_image);
3564 }
3565 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3566 status=MagickFalse;
3567 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3568 {
3569 MagickBooleanType
3570 proceed;
3571
3572 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3573 #pragma omp critical (MagickCore_ShadeImage)
3574 #endif
3575 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
3576 if (proceed == MagickFalse)
3577 status=MagickFalse;
3578 }
3579 }
3580 shade_view=DestroyCacheView(shade_view);
3581 image_view=DestroyCacheView(image_view);
3582 linear_image=DestroyImage(linear_image);
3583 if (status == MagickFalse)
3584 shade_image=DestroyImage(shade_image);
3585 return(shade_image);
3586 }
3587
3588 /*
3589 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3590 % %
3591 % %
3592 % %
3593 % S h a r p e n I m a g e %
3594 % %
3595 % %
3596 % %
3597 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3598 %
3599 % SharpenImage() sharpens the image. We convolve the image with a Gaussian
3600 % operator of the given radius and standard deviation (sigma). For
3601 % reasonable results, radius should be larger than sigma. Use a radius of 0
3602 % and SharpenImage() selects a suitable radius for you.
3603 %
3604 % Using a separable kernel would be faster, but the negative weights cancel
3605 % out on the corners of the kernel producing often undesirable ringing in the
3606 % filtered result; this can be avoided by using a 2D gaussian shaped image
3607 % sharpening kernel instead.
3608 %
3609 % The format of the SharpenImage method is:
3610 %
3611 % Image *SharpenImage(const Image *image,const double radius,
3612 % const double sigma,ExceptionInfo *exception)
3613 %
3614 % A description of each parameter follows:
3615 %
3616 % o image: the image.
3617 %
3618 % o radius: the radius of the Gaussian, in pixels, not counting the center
3619 % pixel.
3620 %
3621 % o sigma: the standard deviation of the Laplacian, in pixels.
3622 %
3623 % o exception: return any errors or warnings in this structure.
3624 %
3625 */
SharpenImage(const Image * image,const double radius,const double sigma,ExceptionInfo * exception)3626 MagickExport Image *SharpenImage(const Image *image,const double radius,
3627 const double sigma,ExceptionInfo *exception)
3628 {
3629 double
3630 gamma,
3631 normalize;
3632
3633 Image
3634 *sharp_image;
3635
3636 KernelInfo
3637 *kernel_info;
3638
3639 register ssize_t
3640 i;
3641
3642 size_t
3643 width;
3644
3645 ssize_t
3646 j,
3647 u,
3648 v;
3649
3650 assert(image != (const Image *) NULL);
3651 assert(image->signature == MagickCoreSignature);
3652 if (image->debug != MagickFalse)
3653 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3654 assert(exception != (ExceptionInfo *) NULL);
3655 assert(exception->signature == MagickCoreSignature);
3656 width=GetOptimalKernelWidth2D(radius,sigma);
3657 kernel_info=AcquireKernelInfo((const char *) NULL,exception);
3658 if (kernel_info == (KernelInfo *) NULL)
3659 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3660 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
3661 kernel_info->width=width;
3662 kernel_info->height=width;
3663 kernel_info->x=(ssize_t) (width-1)/2;
3664 kernel_info->y=(ssize_t) (width-1)/2;
3665 kernel_info->signature=MagickCoreSignature;
3666 kernel_info->values=(MagickRealType *) MagickAssumeAligned(
3667 AcquireAlignedMemory(kernel_info->width,kernel_info->height*
3668 sizeof(*kernel_info->values)));
3669 if (kernel_info->values == (MagickRealType *) NULL)
3670 {
3671 kernel_info=DestroyKernelInfo(kernel_info);
3672 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3673 }
3674 normalize=0.0;
3675 j=(ssize_t) (kernel_info->width-1)/2;
3676 i=0;
3677 for (v=(-j); v <= j; v++)
3678 {
3679 for (u=(-j); u <= j; u++)
3680 {
3681 kernel_info->values[i]=(MagickRealType) (-exp(-((double) u*u+v*v)/(2.0*
3682 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3683 normalize+=kernel_info->values[i];
3684 i++;
3685 }
3686 }
3687 kernel_info->values[i/2]=(double) ((-2.0)*normalize);
3688 normalize=0.0;
3689 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
3690 normalize+=kernel_info->values[i];
3691 gamma=PerceptibleReciprocal(normalize);
3692 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
3693 kernel_info->values[i]*=gamma;
3694 sharp_image=ConvolveImage(image,kernel_info,exception);
3695 kernel_info=DestroyKernelInfo(kernel_info);
3696 return(sharp_image);
3697 }
3698
3699 /*
3700 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3701 % %
3702 % %
3703 % %
3704 % S p r e a d I m a g e %
3705 % %
3706 % %
3707 % %
3708 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3709 %
3710 % SpreadImage() is a special effects method that randomly displaces each
3711 % pixel in a square area defined by the radius parameter.
3712 %
3713 % The format of the SpreadImage method is:
3714 %
3715 % Image *SpreadImage(const Image *image,
3716 % const PixelInterpolateMethod method,const double radius,
3717 % ExceptionInfo *exception)
3718 %
3719 % A description of each parameter follows:
3720 %
3721 % o image: the image.
3722 %
3723 % o method: intepolation method.
3724 %
3725 % o radius: choose a random pixel in a neighborhood of this extent.
3726 %
3727 % o exception: return any errors or warnings in this structure.
3728 %
3729 */
SpreadImage(const Image * image,const PixelInterpolateMethod method,const double radius,ExceptionInfo * exception)3730 MagickExport Image *SpreadImage(const Image *image,
3731 const PixelInterpolateMethod method,const double radius,
3732 ExceptionInfo *exception)
3733 {
3734 #define SpreadImageTag "Spread/Image"
3735
3736 CacheView
3737 *image_view,
3738 *spread_view;
3739
3740 Image
3741 *spread_image;
3742
3743 MagickBooleanType
3744 status;
3745
3746 MagickOffsetType
3747 progress;
3748
3749 RandomInfo
3750 **magick_restrict random_info;
3751
3752 size_t
3753 width;
3754
3755 ssize_t
3756 y;
3757
3758 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3759 unsigned long
3760 key;
3761 #endif
3762
3763 /*
3764 Initialize spread image attributes.
3765 */
3766 assert(image != (Image *) NULL);
3767 assert(image->signature == MagickCoreSignature);
3768 if (image->debug != MagickFalse)
3769 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3770 assert(exception != (ExceptionInfo *) NULL);
3771 assert(exception->signature == MagickCoreSignature);
3772 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3773 exception);
3774 if (spread_image == (Image *) NULL)
3775 return((Image *) NULL);
3776 if (SetImageStorageClass(spread_image,DirectClass,exception) == MagickFalse)
3777 {
3778 spread_image=DestroyImage(spread_image);
3779 return((Image *) NULL);
3780 }
3781 /*
3782 Spread image.
3783 */
3784 status=MagickTrue;
3785 progress=0;
3786 width=GetOptimalKernelWidth1D(radius,0.5);
3787 random_info=AcquireRandomInfoThreadSet();
3788 image_view=AcquireVirtualCacheView(image,exception);
3789 spread_view=AcquireAuthenticCacheView(spread_image,exception);
3790 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3791 key=GetRandomSecretKey(random_info[0]);
3792 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3793 magick_threads(image,spread_image,image->rows,key == ~0UL)
3794 #endif
3795 for (y=0; y < (ssize_t) image->rows; y++)
3796 {
3797 const int
3798 id = GetOpenMPThreadId();
3799
3800 register Quantum
3801 *magick_restrict q;
3802
3803 register ssize_t
3804 x;
3805
3806 if (status == MagickFalse)
3807 continue;
3808 q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
3809 exception);
3810 if (q == (Quantum *) NULL)
3811 {
3812 status=MagickFalse;
3813 continue;
3814 }
3815 for (x=0; x < (ssize_t) image->columns; x++)
3816 {
3817 PointInfo
3818 point;
3819
3820 point.x=GetPseudoRandomValue(random_info[id]);
3821 point.y=GetPseudoRandomValue(random_info[id]);
3822 status=InterpolatePixelChannels(image,image_view,spread_image,method,
3823 (double) x+width*(point.x-0.5),(double) y+width*(point.y-0.5),q,
3824 exception);
3825 q+=GetPixelChannels(spread_image);
3826 }
3827 if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
3828 status=MagickFalse;
3829 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3830 {
3831 MagickBooleanType
3832 proceed;
3833
3834 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3835 #pragma omp critical (MagickCore_SpreadImage)
3836 #endif
3837 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3838 if (proceed == MagickFalse)
3839 status=MagickFalse;
3840 }
3841 }
3842 spread_view=DestroyCacheView(spread_view);
3843 image_view=DestroyCacheView(image_view);
3844 random_info=DestroyRandomInfoThreadSet(random_info);
3845 if (status == MagickFalse)
3846 spread_image=DestroyImage(spread_image);
3847 return(spread_image);
3848 }
3849
3850 /*
3851 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3852 % %
3853 % %
3854 % %
3855 % U n s h a r p M a s k I m a g e %
3856 % %
3857 % %
3858 % %
3859 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3860 %
3861 % UnsharpMaskImage() sharpens one or more image channels. We convolve the
3862 % image with a Gaussian operator of the given radius and standard deviation
3863 % (sigma). For reasonable results, radius should be larger than sigma. Use a
3864 % radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
3865 %
3866 % The format of the UnsharpMaskImage method is:
3867 %
3868 % Image *UnsharpMaskImage(const Image *image,const double radius,
3869 % const double sigma,const double amount,const double threshold,
3870 % ExceptionInfo *exception)
3871 %
3872 % A description of each parameter follows:
3873 %
3874 % o image: the image.
3875 %
3876 % o radius: the radius of the Gaussian, in pixels, not counting the center
3877 % pixel.
3878 %
3879 % o sigma: the standard deviation of the Gaussian, in pixels.
3880 %
3881 % o gain: the percentage of the difference between the original and the
3882 % blur image that is added back into the original.
3883 %
3884 % o threshold: the threshold in pixels needed to apply the diffence gain.
3885 %
3886 % o exception: return any errors or warnings in this structure.
3887 %
3888 */
UnsharpMaskImage(const Image * image,const double radius,const double sigma,const double gain,const double threshold,ExceptionInfo * exception)3889 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
3890 const double sigma,const double gain,const double threshold,
3891 ExceptionInfo *exception)
3892 {
3893 #define SharpenImageTag "Sharpen/Image"
3894
3895 CacheView
3896 *image_view,
3897 *unsharp_view;
3898
3899 Image
3900 *unsharp_image;
3901
3902 MagickBooleanType
3903 status;
3904
3905 MagickOffsetType
3906 progress;
3907
3908 double
3909 quantum_threshold;
3910
3911 ssize_t
3912 y;
3913
3914 assert(image != (const Image *) NULL);
3915 assert(image->signature == MagickCoreSignature);
3916 if (image->debug != MagickFalse)
3917 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3918 assert(exception != (ExceptionInfo *) NULL);
3919 #if defined(MAGICKCORE_OPENCL_SUPPORT)
3920 unsharp_image=AccelerateUnsharpMaskImage(image,radius,sigma,gain,threshold,
3921 exception);
3922 if (unsharp_image != (Image *) NULL)
3923 return(unsharp_image);
3924 #endif
3925 unsharp_image=BlurImage(image,radius,sigma,exception);
3926 if (unsharp_image == (Image *) NULL)
3927 return((Image *) NULL);
3928 quantum_threshold=(double) QuantumRange*threshold;
3929 /*
3930 Unsharp-mask image.
3931 */
3932 status=MagickTrue;
3933 progress=0;
3934 image_view=AcquireVirtualCacheView(image,exception);
3935 unsharp_view=AcquireAuthenticCacheView(unsharp_image,exception);
3936 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3937 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3938 magick_threads(image,unsharp_image,image->rows,1)
3939 #endif
3940 for (y=0; y < (ssize_t) image->rows; y++)
3941 {
3942 register const Quantum
3943 *magick_restrict p;
3944
3945 register Quantum
3946 *magick_restrict q;
3947
3948 register ssize_t
3949 x;
3950
3951 if (status == MagickFalse)
3952 continue;
3953 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3954 q=QueueCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
3955 exception);
3956 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3957 {
3958 status=MagickFalse;
3959 continue;
3960 }
3961 for (x=0; x < (ssize_t) image->columns; x++)
3962 {
3963 register ssize_t
3964 i;
3965
3966 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3967 {
3968 double
3969 pixel;
3970
3971 PixelChannel
3972 channel;
3973
3974 PixelTrait
3975 traits,
3976 unsharp_traits;
3977
3978 channel=GetPixelChannelChannel(image,i);
3979 traits=GetPixelChannelTraits(image,channel);
3980 unsharp_traits=GetPixelChannelTraits(unsharp_image,channel);
3981 if ((traits == UndefinedPixelTrait) ||
3982 (unsharp_traits == UndefinedPixelTrait))
3983 continue;
3984 if (((unsharp_traits & CopyPixelTrait) != 0) ||
3985 (GetPixelReadMask(image,p) == 0))
3986 {
3987 SetPixelChannel(unsharp_image,channel,p[i],q);
3988 continue;
3989 }
3990 pixel=p[i]-(double) GetPixelChannel(unsharp_image,channel,q);
3991 if (fabs(2.0*pixel) < quantum_threshold)
3992 pixel=(double) p[i];
3993 else
3994 pixel=(double) p[i]+gain*pixel;
3995 SetPixelChannel(unsharp_image,channel,ClampToQuantum(pixel),q);
3996 }
3997 p+=GetPixelChannels(image);
3998 q+=GetPixelChannels(unsharp_image);
3999 }
4000 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4001 status=MagickFalse;
4002 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4003 {
4004 MagickBooleanType
4005 proceed;
4006
4007 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4008 #pragma omp critical (MagickCore_UnsharpMaskImage)
4009 #endif
4010 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
4011 if (proceed == MagickFalse)
4012 status=MagickFalse;
4013 }
4014 }
4015 unsharp_image->type=image->type;
4016 unsharp_view=DestroyCacheView(unsharp_view);
4017 image_view=DestroyCacheView(image_view);
4018 if (status == MagickFalse)
4019 unsharp_image=DestroyImage(unsharp_image);
4020 return(unsharp_image);
4021 }
4022