blob: b67fb1da4968fb4b47adb5ea3f16ca480618d560 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% GGGG EEEEE M M %
7% G E MM MM %
8% G GG EEE M M M %
9% G G E M M %
10% GGGG EEEEE M M %
11% %
12% %
13% Graphic Gems - Graphic Support Methods %
14% %
15% Software Design %
16% John Cristy %
17% August 1996 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
43#include "magick/studio.h"
44#include "magick/color-private.h"
45#include "magick/draw.h"
46#include "magick/gem.h"
47#include "magick/image.h"
48#include "magick/image-private.h"
49#include "magick/log.h"
50#include "magick/memory_.h"
51#include "magick/pixel-private.h"
52#include "magick/quantum.h"
53#include "magick/random_.h"
54#include "magick/resize.h"
55#include "magick/transform.h"
56#include "magick/signature-private.h"
57
58/*
59%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
60% %
61% %
62% %
63% C o n v e r t H S B T o R G B %
64% %
65% %
66% %
67%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
68%
69% ConvertHSBToRGB() transforms a (hue, saturation, brightness) to a (red,
70% green, blue) triple.
71%
72% The format of the ConvertHSBToRGBImage method is:
73%
74% void ConvertHSBToRGB(const double hue,const double saturation,
75% const double brightness,Quantum *red,Quantum *green,Quantum *blue)
76%
77% A description of each parameter follows:
78%
79% o hue, saturation, brightness: A double value representing a
80% component of the HSB color space.
81%
82% o red, green, blue: A pointer to a pixel component of type Quantum.
83%
84*/
85MagickExport void ConvertHSBToRGB(const double hue,const double saturation,
86 const double brightness,Quantum *red,Quantum *green,Quantum *blue)
87{
88 MagickRealType
89 f,
90 h,
91 p,
92 q,
93 t;
94
95 /*
96 Convert HSB to RGB colorspace.
97 */
98 assert(red != (Quantum *) NULL);
99 assert(green != (Quantum *) NULL);
100 assert(blue != (Quantum *) NULL);
cristy98a65d52010-04-14 02:12:38 +0000101 if (saturation == 0.0)
cristy3ed852e2009-09-05 21:47:34 +0000102 {
cristybcfb0432010-05-06 01:45:33 +0000103 *red=ClampToQuantum((MagickRealType) QuantumRange*brightness);
cristy3ed852e2009-09-05 21:47:34 +0000104 *green=(*red);
105 *blue=(*red);
106 return;
107 }
cristy98a65d52010-04-14 02:12:38 +0000108 h=6.0*(hue-floor(hue));
109 f=h-floor((double) h);
110 p=brightness*(1.0-saturation);
111 q=brightness*(1.0-saturation*f);
112 t=brightness*(1.0-(saturation*(1.0-f)));
cristy3ed852e2009-09-05 21:47:34 +0000113 switch ((int) h)
114 {
115 case 0:
116 default:
117 {
cristybcfb0432010-05-06 01:45:33 +0000118 *red=ClampToQuantum((MagickRealType) QuantumRange*brightness);
119 *green=ClampToQuantum((MagickRealType) QuantumRange*t);
120 *blue=ClampToQuantum((MagickRealType) QuantumRange*p);
cristy3ed852e2009-09-05 21:47:34 +0000121 break;
122 }
123 case 1:
124 {
cristybcfb0432010-05-06 01:45:33 +0000125 *red=ClampToQuantum((MagickRealType) QuantumRange*q);
126 *green=ClampToQuantum((MagickRealType) QuantumRange*brightness);
127 *blue=ClampToQuantum((MagickRealType) QuantumRange*p);
cristy3ed852e2009-09-05 21:47:34 +0000128 break;
129 }
130 case 2:
131 {
cristybcfb0432010-05-06 01:45:33 +0000132 *red=ClampToQuantum((MagickRealType) QuantumRange*p);
133 *green=ClampToQuantum((MagickRealType) QuantumRange*brightness);
134 *blue=ClampToQuantum((MagickRealType) QuantumRange*t);
cristy3ed852e2009-09-05 21:47:34 +0000135 break;
136 }
137 case 3:
138 {
cristybcfb0432010-05-06 01:45:33 +0000139 *red=ClampToQuantum((MagickRealType) QuantumRange*p);
140 *green=ClampToQuantum((MagickRealType) QuantumRange*q);
141 *blue=ClampToQuantum((MagickRealType) QuantumRange*brightness);
cristy3ed852e2009-09-05 21:47:34 +0000142 break;
143 }
144 case 4:
145 {
cristybcfb0432010-05-06 01:45:33 +0000146 *red=ClampToQuantum((MagickRealType) QuantumRange*t);
147 *green=ClampToQuantum((MagickRealType) QuantumRange*p);
148 *blue=ClampToQuantum((MagickRealType) QuantumRange*brightness);
cristy3ed852e2009-09-05 21:47:34 +0000149 break;
150 }
151 case 5:
152 {
cristybcfb0432010-05-06 01:45:33 +0000153 *red=ClampToQuantum((MagickRealType) QuantumRange*brightness);
154 *green=ClampToQuantum((MagickRealType) QuantumRange*p);
155 *blue=ClampToQuantum((MagickRealType) QuantumRange*q);
cristy3ed852e2009-09-05 21:47:34 +0000156 break;
157 }
158 }
159}
160
161/*
162%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
163% %
164% %
165% %
166% C o n v e r t H S L T o R G B %
167% %
168% %
169% %
170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
171%
172% ConvertHSLToRGB() transforms a (hue, saturation, lightness) to a (red,
173% green, blue) triple.
174%
175% The format of the ConvertHSLToRGBImage method is:
176%
177% void ConvertHSLToRGB(const double hue,const double saturation,
178% const double lightness,Quantum *red,Quantum *green,Quantum *blue)
179%
180% A description of each parameter follows:
181%
182% o hue, saturation, lightness: A double value representing a
183% component of the HSL color space.
184%
185% o red, green, blue: A pointer to a pixel component of type Quantum.
186%
187*/
188
189static inline MagickRealType ConvertHueToRGB(MagickRealType m1,
190 MagickRealType m2,MagickRealType hue)
191{
192 if (hue < 0.0)
193 hue+=1.0;
194 if (hue > 1.0)
195 hue-=1.0;
196 if ((6.0*hue) < 1.0)
cristya163b0c2010-04-13 01:04:49 +0000197 return(m1+6.0*(m2-m1)*hue);
198 if ((2.0*hue) < 1.0)
199 return(m2);
200 if ((3.0*hue) < 2.0)
201 return(m1+6.0*(m2-m1)*(2.0/3.0-hue));
202 return(m1);
cristy3ed852e2009-09-05 21:47:34 +0000203}
204
205MagickExport void ConvertHSLToRGB(const double hue,const double saturation,
206 const double lightness,Quantum *red,Quantum *green,Quantum *blue)
207{
208 MagickRealType
209 b,
210 g,
211 r,
212 m1,
213 m2;
214
215 /*
216 Convert HSL to RGB colorspace.
217 */
218 assert(red != (Quantum *) NULL);
219 assert(green != (Quantum *) NULL);
220 assert(blue != (Quantum *) NULL);
cristy98a65d52010-04-14 02:12:38 +0000221 if (saturation == 0)
cristy3ed852e2009-09-05 21:47:34 +0000222 {
cristybcfb0432010-05-06 01:45:33 +0000223 *red=ClampToQuantum((MagickRealType) QuantumRange*lightness);
cristy3ed852e2009-09-05 21:47:34 +0000224 *green=(*red);
225 *blue=(*red);
226 return;
227 }
cristy27cabb82010-04-14 11:33:57 +0000228 if (lightness < 0.5)
cristy98a65d52010-04-14 02:12:38 +0000229 m2=lightness*(saturation+1.0);
230 else
231 m2=(lightness+saturation)-(lightness*saturation);
232 m1=2.0*lightness-m2;
cristy3ed852e2009-09-05 21:47:34 +0000233 r=ConvertHueToRGB(m1,m2,hue+1.0/3.0);
234 g=ConvertHueToRGB(m1,m2,hue);
235 b=ConvertHueToRGB(m1,m2,hue-1.0/3.0);
cristybcfb0432010-05-06 01:45:33 +0000236 *red=ClampToQuantum((MagickRealType) QuantumRange*r);
237 *green=ClampToQuantum((MagickRealType) QuantumRange*g);
238 *blue=ClampToQuantum((MagickRealType) QuantumRange*b);
cristy3ed852e2009-09-05 21:47:34 +0000239}
240
241/*
242%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
243% %
244% %
245% %
246% C o n v e r t H W B T o R G B %
247% %
248% %
249% %
250%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
251%
252% ConvertHWBToRGB() transforms a (hue, whiteness, blackness) to a (red, green,
253% blue) triple.
254%
255% The format of the ConvertHWBToRGBImage method is:
256%
257% void ConvertHWBToRGB(const double hue,const double whiteness,
258% const double blackness,Quantum *red,Quantum *green,Quantum *blue)
259%
260% A description of each parameter follows:
261%
262% o hue, whiteness, blackness: A double value representing a
263% component of the HWB color space.
264%
265% o red, green, blue: A pointer to a pixel component of type Quantum.
266%
267*/
268MagickExport void ConvertHWBToRGB(const double hue,const double whiteness,
269 const double blackness,Quantum *red,Quantum *green,Quantum *blue)
270{
271 MagickRealType
272 b,
273 f,
274 g,
275 n,
276 r,
277 v;
278
cristybb503372010-05-27 20:51:26 +0000279 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000280 i;
281
282 /*
283 Convert HWB to RGB colorspace.
284 */
285 assert(red != (Quantum *) NULL);
286 assert(green != (Quantum *) NULL);
287 assert(blue != (Quantum *) NULL);
288 v=1.0-blackness;
289 if (hue == 0.0)
290 {
cristybcfb0432010-05-06 01:45:33 +0000291 *red=ClampToQuantum((MagickRealType) QuantumRange*v);
292 *green=ClampToQuantum((MagickRealType) QuantumRange*v);
293 *blue=ClampToQuantum((MagickRealType) QuantumRange*v);
cristy3ed852e2009-09-05 21:47:34 +0000294 return;
295 }
cristybb503372010-05-27 20:51:26 +0000296 i=(ssize_t) floor(6.0*hue);
cristy3ed852e2009-09-05 21:47:34 +0000297 f=6.0*hue-i;
298 if ((i & 0x01) != 0)
299 f=1.0-f;
300 n=whiteness+f*(v-whiteness); /* linear interpolation */
301 switch (i)
302 {
303 default:
304 case 6:
305 case 0: r=v; g=n; b=whiteness; break;
306 case 1: r=n; g=v; b=whiteness; break;
307 case 2: r=whiteness; g=v; b=n; break;
308 case 3: r=whiteness; g=n; b=v; break;
309 case 4: r=n; g=whiteness; b=v; break;
310 case 5: r=v; g=whiteness; b=n; break;
311 }
cristybcfb0432010-05-06 01:45:33 +0000312 *red=ClampToQuantum((MagickRealType) QuantumRange*r);
313 *green=ClampToQuantum((MagickRealType) QuantumRange*g);
314 *blue=ClampToQuantum((MagickRealType) QuantumRange*b);
cristy3ed852e2009-09-05 21:47:34 +0000315}
316
317/*
318%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
319% %
320% %
321% %
322% C o n v e r t R G B T o H S B %
323% %
324% %
325% %
326%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
327%
328% ConvertRGBToHSB() transforms a (red, green, blue) to a (hue, saturation,
329% brightness) triple.
330%
331% The format of the ConvertRGBToHSB method is:
332%
333% void ConvertRGBToHSB(const Quantum red,const Quantum green,
334% const Quantum blue,double *hue,double *saturation,double *brightness)
335%
336% A description of each parameter follows:
337%
338% o red, green, blue: A Quantum value representing the red, green, and
339% blue component of a pixel..
340%
341% o hue, saturation, brightness: A pointer to a double value representing a
342% component of the HSB color space.
343%
344*/
345MagickExport void ConvertRGBToHSB(const Quantum red,const Quantum green,
346 const Quantum blue,double *hue,double *saturation,double *brightness)
347{
348 MagickRealType
349 delta,
350 max,
351 min;
352
353 /*
354 Convert RGB to HSB colorspace.
355 */
356 assert(hue != (double *) NULL);
357 assert(saturation != (double *) NULL);
358 assert(brightness != (double *) NULL);
359 *hue=0.0;
360 *saturation=0.0;
361 *brightness=0.0;
362 min=(MagickRealType) (red < green ? red : green);
363 if ((MagickRealType) blue < min)
364 min=(MagickRealType) blue;
365 max=(MagickRealType) (red > green ? red : green);
366 if ((MagickRealType) blue > max)
367 max=(MagickRealType) blue;
368 if (max == 0.0)
369 return;
370 delta=max-min;
371 *saturation=(double) (delta/max);
372 *brightness=(double) (QuantumScale*max);
373 if (delta == 0.0)
374 return;
cristy18b17442009-10-25 18:36:48 +0000375 if ((MagickRealType) red == max)
376 *hue=(double) ((green-(MagickRealType) blue)/delta);
cristy3ed852e2009-09-05 21:47:34 +0000377 else
cristy18b17442009-10-25 18:36:48 +0000378 if ((MagickRealType) green == max)
379 *hue=(double) (2.0+(blue-(MagickRealType) red)/delta);
cristy3ed852e2009-09-05 21:47:34 +0000380 else
cristy18b17442009-10-25 18:36:48 +0000381 *hue=(double) (4.0+(red-(MagickRealType) green)/delta);
382 *hue/=6.0;
cristy3ed852e2009-09-05 21:47:34 +0000383 if (*hue < 0.0)
384 *hue+=1.0;
385}
386
387/*
388%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
389% %
390% %
391% %
392% C o n v e r t R G B T o H S L %
393% %
394% %
395% %
396%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
397%
398% ConvertRGBToHSL() transforms a (red, green, blue) to a (hue, saturation,
399% lightness) triple.
400%
401% The format of the ConvertRGBToHSL method is:
402%
403% void ConvertRGBToHSL(const Quantum red,const Quantum green,
404% const Quantum blue,double *hue,double *saturation,double *lightness)
405%
406% A description of each parameter follows:
407%
408% o red, green, blue: A Quantum value representing the red, green, and
409% blue component of a pixel..
410%
411% o hue, saturation, lightness: A pointer to a double value representing a
412% component of the HSL color space.
413%
414*/
415
416static inline double MagickMax(const double x,const double y)
417{
418 if (x > y)
419 return(x);
420 return(y);
421}
422
423static inline double MagickMin(const double x,const double y)
424{
425 if (x < y)
426 return(x);
427 return(y);
428}
429
430MagickExport void ConvertRGBToHSL(const Quantum red,const Quantum green,
431 const Quantum blue,double *hue,double *saturation,double *lightness)
432{
433 MagickRealType
434 b,
435 delta,
436 g,
437 max,
438 min,
439 r;
440
441 /*
442 Convert RGB to HSL colorspace.
443 */
444 assert(hue != (double *) NULL);
445 assert(saturation != (double *) NULL);
446 assert(lightness != (double *) NULL);
447 r=QuantumScale*red;
448 g=QuantumScale*green;
449 b=QuantumScale*blue;
450 max=MagickMax(r,MagickMax(g,b));
451 min=MagickMin(r,MagickMin(g,b));
452 *lightness=(double) ((min+max)/2.0);
453 delta=max-min;
454 if (delta == 0.0)
455 {
456 *hue=0.0;
457 *saturation=0.0;
458 return;
459 }
460 if (*lightness < 0.5)
461 *saturation=(double) (delta/(min+max));
462 else
463 *saturation=(double) (delta/(2.0-max-min));
464 if (r == max)
465 *hue=((((max-b)/6.0)+(delta/2.0))-(((max-g)/6.0)+(delta/2.0)))/delta;
466 else
467 if (g == max)
468 *hue=(1.0/3.0)+((((max-r)/6.0)+(delta/2.0))-(((max-b)/6.0)+(delta/2.0)))/
469 delta;
470 else
471 if (b == max)
472 *hue=(2.0/3.0)+((((max-g)/6.0)+(delta/2.0))-(((max-r)/6.0)+
473 (delta/2.0)))/delta;
474 if (*hue < 0.0)
475 *hue+=1.0;
476 if (*hue > 1.0)
477 *hue-=1.0;
478}
479
480/*
481%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
482% %
483% %
484% %
485% C o n v e r t R G B T o H W B %
486% %
487% %
488% %
489%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
490%
491% ConvertRGBToHWB() transforms a (red, green, blue) to a (hue, whiteness,
492% blackness) triple.
493%
494% The format of the ConvertRGBToHWB method is:
495%
496% void ConvertRGBToHWB(const Quantum red,const Quantum green,
497% const Quantum blue,double *hue,double *whiteness,double *blackness)
498%
499% A description of each parameter follows:
500%
501% o red, green, blue: A Quantum value representing the red, green, and
502% blue component of a pixel.
503%
504% o hue, whiteness, blackness: A pointer to a double value representing a
505% component of the HWB color space.
506%
507*/
508MagickExport void ConvertRGBToHWB(const Quantum red,const Quantum green,
509 const Quantum blue,double *hue,double *whiteness,double *blackness)
510{
cristybb503372010-05-27 20:51:26 +0000511 long
512 i;
513
cristy3ed852e2009-09-05 21:47:34 +0000514 MagickRealType
515 f,
516 v,
517 w;
518
cristy3ed852e2009-09-05 21:47:34 +0000519 /*
520 Convert RGB to HWB colorspace.
521 */
522 assert(hue != (double *) NULL);
523 assert(whiteness != (double *) NULL);
524 assert(blackness != (double *) NULL);
525 w=(MagickRealType) MagickMin((double) red,MagickMin((double) green,(double)
526 blue));
527 v=(MagickRealType) MagickMax((double) red,MagickMax((double) green,(double)
528 blue));
529 *blackness=1.0-QuantumScale*v;
530 *whiteness=QuantumScale*w;
531 if (v == w)
532 {
533 *hue=0.0;
534 return;
535 }
536 f=((MagickRealType) red == w) ? green-(MagickRealType) blue :
537 (((MagickRealType) green == w) ? blue-(MagickRealType) red : red-
538 (MagickRealType) green);
539 i=((MagickRealType) red == w) ? 3 : (((MagickRealType) green == w) ? 5 : 1);
540 *hue=((double) i-f/(v-1.0*w))/6.0;
541}
542
543/*
544%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
545% %
546% %
547% %
548% E x p a n d A f f i n e %
549% %
550% %
551% %
552%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
553%
554% ExpandAffine() computes the affine's expansion factor, i.e. the square root
555% of the factor by which the affine transform affects area. In an affine
556% transform composed of scaling, rotation, shearing, and translation, returns
557% the amount of scaling.
558%
559% The format of the ExpandAffine method is:
560%
561% double ExpandAffine(const AffineMatrix *affine)
562%
563% A description of each parameter follows:
564%
565% o expansion: Method ExpandAffine returns the affine's expansion factor.
566%
567% o affine: A pointer the affine transform of type AffineMatrix.
568%
569*/
570MagickExport double ExpandAffine(const AffineMatrix *affine)
571{
572 assert(affine != (const AffineMatrix *) NULL);
573 return(sqrt(fabs(affine->sx*affine->sy-affine->rx*affine->ry)));
574}
575
576/*
577%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
578% %
579% %
580% %
581% G e n e r a t e D i f f e r e n t i a l N o i s e %
582% %
583% %
584% %
585%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
586%
cristy82b15832009-10-06 19:17:37 +0000587% GenerateDifferentialNoise() generates differentual noise.
cristy3ed852e2009-09-05 21:47:34 +0000588%
589% The format of the GenerateDifferentialNoise method is:
590%
591% double GenerateDifferentialNoise(RandomInfo *random_info,
592% const Quantum pixel,const NoiseType noise_type,
593% const MagickRealType attenuate)
594%
595% A description of each parameter follows:
596%
597% o random_info: the random info.
598%
599% o pixel: noise is relative to this pixel value.
600%
601% o noise_type: the type of noise.
602%
603% o attenuate: attenuate the noise.
604%
605*/
606MagickExport double GenerateDifferentialNoise(RandomInfo *random_info,
607 const Quantum pixel,const NoiseType noise_type,const MagickRealType attenuate)
608{
609#define NoiseEpsilon (attenuate*1.0e-5)
cristy20a777c2009-10-23 13:03:15 +0000610#define SigmaUniform (attenuate*4.0)
611#define SigmaGaussian (attenuate*4.0)
cristy3ed852e2009-09-05 21:47:34 +0000612#define SigmaImpulse (attenuate*0.10)
cristy20a777c2009-10-23 13:03:15 +0000613#define SigmaLaplacian (attenuate*10.0)
614#define SigmaMultiplicativeGaussian (attenuate*1.0)
cristy3ed852e2009-09-05 21:47:34 +0000615#define SigmaPoisson (attenuate*0.05)
cristy20a777c2009-10-23 13:03:15 +0000616#define TauGaussian (attenuate*20.0)
cristy3ed852e2009-09-05 21:47:34 +0000617
cristyadb41ca2009-10-22 15:02:28 +0000618 double
cristy3ed852e2009-09-05 21:47:34 +0000619 alpha,
620 beta,
621 noise,
622 sigma;
623
624 alpha=GetPseudoRandomValue(random_info);
cristy3ed852e2009-09-05 21:47:34 +0000625 switch (noise_type)
626 {
627 case UniformNoise:
628 default:
629 {
cristy20a777c2009-10-23 13:03:15 +0000630 noise=(double) pixel+ScaleCharToQuantum((unsigned char)
631 (SigmaUniform*(alpha)));
cristy3ed852e2009-09-05 21:47:34 +0000632 break;
633 }
634 case GaussianNoise:
635 {
cristyadb41ca2009-10-22 15:02:28 +0000636 double
cristy62faa602010-02-20 03:36:17 +0000637 gamma,
cristy3ed852e2009-09-05 21:47:34 +0000638 tau;
639
cristyadb41ca2009-10-22 15:02:28 +0000640 if (alpha == 0.0)
641 alpha=1.0;
cristy3ed852e2009-09-05 21:47:34 +0000642 beta=GetPseudoRandomValue(random_info);
cristy62faa602010-02-20 03:36:17 +0000643 gamma=sqrt(-2.0*log(alpha));
644 sigma=gamma*cos(2.0*MagickPI*beta);
645 tau=gamma*sin(2.0*MagickPI*beta);
cristy20a777c2009-10-23 13:03:15 +0000646 noise=(double) pixel+sqrt((double) pixel)*SigmaGaussian*sigma+
cristy3ed852e2009-09-05 21:47:34 +0000647 TauGaussian*tau;
648 break;
649 }
650 case MultiplicativeGaussianNoise:
651 {
652 if (alpha <= NoiseEpsilon)
cristyadb41ca2009-10-22 15:02:28 +0000653 sigma=(double) QuantumRange;
cristy3ed852e2009-09-05 21:47:34 +0000654 else
cristyadb41ca2009-10-22 15:02:28 +0000655 sigma=sqrt(-2.0*log(alpha));
cristy3ed852e2009-09-05 21:47:34 +0000656 beta=GetPseudoRandomValue(random_info);
cristyadb41ca2009-10-22 15:02:28 +0000657 noise=(double) pixel+pixel*SigmaMultiplicativeGaussian*sigma/2.0*
658 cos((2.0*MagickPI*beta));
cristy3ed852e2009-09-05 21:47:34 +0000659 break;
660 }
661 case ImpulseNoise:
662 {
663 if (alpha < (SigmaImpulse/2.0))
664 noise=0.0;
665 else
666 if (alpha >= (1.0-(SigmaImpulse/2.0)))
cristyadb41ca2009-10-22 15:02:28 +0000667 noise=(double) QuantumRange;
cristy3ed852e2009-09-05 21:47:34 +0000668 else
cristyadb41ca2009-10-22 15:02:28 +0000669 noise=(double) pixel;
cristy3ed852e2009-09-05 21:47:34 +0000670 break;
671 }
672 case LaplacianNoise:
673 {
674 if (alpha <= 0.5)
675 {
676 if (alpha <= NoiseEpsilon)
cristyadb41ca2009-10-22 15:02:28 +0000677 noise=(double) pixel-(double) QuantumRange;
cristy3ed852e2009-09-05 21:47:34 +0000678 else
cristyadb41ca2009-10-22 15:02:28 +0000679 noise=(double) pixel+ScaleCharToQuantum((unsigned char)
680 (SigmaLaplacian*log((2.0*alpha))+0.5));
cristy3ed852e2009-09-05 21:47:34 +0000681 break;
682 }
683 beta=1.0-alpha;
684 if (beta <= (0.5*NoiseEpsilon))
cristyadb41ca2009-10-22 15:02:28 +0000685 noise=(double) (pixel+QuantumRange);
cristy3ed852e2009-09-05 21:47:34 +0000686 else
cristyadb41ca2009-10-22 15:02:28 +0000687 noise=(double) pixel-ScaleCharToQuantum((unsigned char)
688 (SigmaLaplacian*log((2.0*beta))+0.5));
cristy3ed852e2009-09-05 21:47:34 +0000689 break;
690 }
691 case PoissonNoise:
692 {
cristyadb41ca2009-10-22 15:02:28 +0000693 double
cristy3ed852e2009-09-05 21:47:34 +0000694 poisson;
695
cristybb503372010-05-27 20:51:26 +0000696 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000697 i;
698
cristyadb41ca2009-10-22 15:02:28 +0000699 poisson=exp(-SigmaPoisson*ScaleQuantumToChar(pixel));
cristy3ed852e2009-09-05 21:47:34 +0000700 for (i=0; alpha > poisson; i++)
701 {
702 beta=GetPseudoRandomValue(random_info);
703 alpha*=beta;
704 }
cristyadb41ca2009-10-22 15:02:28 +0000705 noise=(double) ScaleCharToQuantum((unsigned char) (i/SigmaPoisson));
cristy3ed852e2009-09-05 21:47:34 +0000706 break;
707 }
708 case RandomNoise:
709 {
cristy2a42b1b2010-02-20 23:21:13 +0000710 noise=(double) QuantumRange*alpha;
cristy3ed852e2009-09-05 21:47:34 +0000711 break;
712 }
713 }
714 return(noise);
715}
716
717/*
718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
719% %
720% %
721% %
722% G e t O p t i m a l K e r n e l W i d t h %
723% %
724% %
725% %
726%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727%
728% GetOptimalKernelWidth() computes the optimal kernel radius for a convolution
729% filter. Start with the minimum value of 3 pixels and walk out until we drop
730% below the threshold of one pixel numerical accuracy.
731%
732% The format of the GetOptimalKernelWidth method is:
733%
cristybb503372010-05-27 20:51:26 +0000734% size_t GetOptimalKernelWidth(const double radius,
cristy3ed852e2009-09-05 21:47:34 +0000735% const double sigma)
736%
737% A description of each parameter follows:
738%
739% o width: Method GetOptimalKernelWidth returns the optimal width of
740% a convolution kernel.
741%
742% o radius: the radius of the Gaussian, in pixels, not counting the center
743% pixel.
744%
745% o sigma: the standard deviation of the Gaussian, in pixels.
746%
747*/
cristybb503372010-05-27 20:51:26 +0000748MagickExport size_t GetOptimalKernelWidth1D(const double radius,
cristye96405a2010-05-19 02:24:31 +0000749 const double sigma)
cristy3ed852e2009-09-05 21:47:34 +0000750{
cristye96405a2010-05-19 02:24:31 +0000751 double
752 alpha,
753 beta,
754 gamma,
cristy3ed852e2009-09-05 21:47:34 +0000755 normalize,
cristye96405a2010-05-19 02:24:31 +0000756 value;
cristy3ed852e2009-09-05 21:47:34 +0000757
cristybb503372010-05-27 20:51:26 +0000758 ssize_t
cristy47e00502009-12-17 19:19:57 +0000759 j;
760
cristybb503372010-05-27 20:51:26 +0000761 register ssize_t
cristy47e00502009-12-17 19:19:57 +0000762 i;
763
cristybb503372010-05-27 20:51:26 +0000764 size_t
cristy47e00502009-12-17 19:19:57 +0000765 width;
cristy3ed852e2009-09-05 21:47:34 +0000766
767 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy47e00502009-12-17 19:19:57 +0000768 if (radius > MagickEpsilon)
cristybb503372010-05-27 20:51:26 +0000769 return((size_t) (2.0*ceil(radius)+1.0));
cristye96405a2010-05-19 02:24:31 +0000770 gamma=fabs(sigma);
771 if (gamma <= MagickEpsilon)
anthonyc1061722010-05-14 06:23:49 +0000772 return(3UL);
cristye96405a2010-05-19 02:24:31 +0000773 alpha=1.0/(2.0*gamma*gamma);
774 beta=1.0/(MagickSQ2PI*gamma);
cristy3ed852e2009-09-05 21:47:34 +0000775 for (width=5; ; )
776 {
777 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000778 j=(ssize_t) width/2;
cristy47e00502009-12-17 19:19:57 +0000779 for (i=(-j); i <= j; i++)
cristye96405a2010-05-19 02:24:31 +0000780 normalize+=exp(-((double) (i*i))*alpha)*beta;
781 value=exp(-((double) (j*j))*alpha)*beta/normalize;
cristy20908da2009-12-02 14:34:11 +0000782 if ((value < QuantumScale) || (value < MagickEpsilon))
cristy3ed852e2009-09-05 21:47:34 +0000783 break;
784 width+=2;
785 }
cristybb503372010-05-27 20:51:26 +0000786 return((size_t) (width-2));
cristy3ed852e2009-09-05 21:47:34 +0000787}
788
cristybb503372010-05-27 20:51:26 +0000789MagickExport size_t GetOptimalKernelWidth2D(const double radius,
cristye96405a2010-05-19 02:24:31 +0000790 const double sigma)
cristy3ed852e2009-09-05 21:47:34 +0000791{
cristy47e00502009-12-17 19:19:57 +0000792 double
cristye96405a2010-05-19 02:24:31 +0000793 alpha,
794 beta,
795 gamma,
cristy3ed852e2009-09-05 21:47:34 +0000796 normalize,
cristye96405a2010-05-19 02:24:31 +0000797 value;
cristy3ed852e2009-09-05 21:47:34 +0000798
cristybb503372010-05-27 20:51:26 +0000799 ssize_t
cristy47e00502009-12-17 19:19:57 +0000800 j,
cristy3ed852e2009-09-05 21:47:34 +0000801 u,
802 v;
803
cristybb503372010-05-27 20:51:26 +0000804 size_t
cristy47e00502009-12-17 19:19:57 +0000805 width;
806
cristy3ed852e2009-09-05 21:47:34 +0000807 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy47e00502009-12-17 19:19:57 +0000808 if (radius > MagickEpsilon)
cristybb503372010-05-27 20:51:26 +0000809 return((size_t) (2.0*ceil(radius)+1.0));
cristye96405a2010-05-19 02:24:31 +0000810 gamma=fabs(sigma);
811 if (gamma <= MagickEpsilon)
anthonyc1061722010-05-14 06:23:49 +0000812 return(3UL);
cristye96405a2010-05-19 02:24:31 +0000813 alpha=1.0/(2.0*gamma*gamma);
814 beta=1.0/(Magick2PI*gamma*gamma);
cristy3ed852e2009-09-05 21:47:34 +0000815 for (width=5; ; )
816 {
817 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000818 j=(ssize_t) width/2;
cristy47e00502009-12-17 19:19:57 +0000819 for (v=(-j); v <= j; v++)
cristy47e00502009-12-17 19:19:57 +0000820 for (u=(-j); u <= j; u++)
cristye96405a2010-05-19 02:24:31 +0000821 normalize+=exp(-((double) (u*u+v*v))*alpha)*beta;
822 value=exp(-((double) (j*j))*alpha)*beta/normalize;
cristy20908da2009-12-02 14:34:11 +0000823 if ((value < QuantumScale) || (value < MagickEpsilon))
cristy3ed852e2009-09-05 21:47:34 +0000824 break;
825 width+=2;
826 }
cristybb503372010-05-27 20:51:26 +0000827 return((size_t) (width-2));
cristy3ed852e2009-09-05 21:47:34 +0000828}
829
cristybb503372010-05-27 20:51:26 +0000830MagickExport size_t GetOptimalKernelWidth(const double radius,
cristy3ed852e2009-09-05 21:47:34 +0000831 const double sigma)
832{
833 return(GetOptimalKernelWidth1D(radius,sigma));
834}