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