/src/imagemagick/MagickCore/gem.c
Line | Count | Source |
1 | | /* |
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 | | % Cristy % |
17 | | % August 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/license/ % |
27 | | % % |
28 | | % Unless required by applicable law or agreed to in writing, software % |
29 | | % distributed under the License is distributed on an "AS IS" BASIS, % |
30 | | % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % |
31 | | % See the License for the specific language governing permissions and % |
32 | | % limitations under the License. % |
33 | | % % |
34 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
35 | | % |
36 | | % |
37 | | % |
38 | | */ |
39 | | |
40 | | /* |
41 | | Include declarations. |
42 | | */ |
43 | | #include "MagickCore/studio.h" |
44 | | #include "MagickCore/color-private.h" |
45 | | #include "MagickCore/draw.h" |
46 | | #include "MagickCore/gem.h" |
47 | | #include "MagickCore/gem-private.h" |
48 | | #include "MagickCore/image.h" |
49 | | #include "MagickCore/image-private.h" |
50 | | #include "MagickCore/log.h" |
51 | | #include "MagickCore/memory_.h" |
52 | | #include "MagickCore/pixel-accessor.h" |
53 | | #include "MagickCore/quantum.h" |
54 | | #include "MagickCore/quantum-private.h" |
55 | | #include "MagickCore/random_.h" |
56 | | #include "MagickCore/resize.h" |
57 | | #include "MagickCore/transform.h" |
58 | | #include "MagickCore/signature-private.h" |
59 | | |
60 | | /* |
61 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
62 | | % % |
63 | | % % |
64 | | % % |
65 | | % E x p a n d A f f i n e % |
66 | | % % |
67 | | % % |
68 | | % % |
69 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
70 | | % |
71 | | % ExpandAffine() computes the affine's expansion factor, i.e. the square root |
72 | | % of the factor by which the affine transform affects area. In an affine |
73 | | % transform composed of scaling, rotation, shearing, and translation, returns |
74 | | % the amount of scaling. |
75 | | % |
76 | | % The format of the ExpandAffine method is: |
77 | | % |
78 | | % double ExpandAffine(const AffineMatrix *affine) |
79 | | % |
80 | | % A description of each parameter follows: |
81 | | % |
82 | | % o expansion: ExpandAffine returns the affine's expansion factor. |
83 | | % |
84 | | % o affine: A pointer the affine transform of type AffineMatrix. |
85 | | % |
86 | | */ |
87 | | MagickExport double ExpandAffine(const AffineMatrix *affine) |
88 | 2.04M | { |
89 | 2.04M | assert(affine != (const AffineMatrix *) NULL); |
90 | 2.04M | return(sqrt(fabs(affine->sx*affine->sy-affine->rx*affine->ry))); |
91 | 2.04M | } |
92 | | |
93 | | /* |
94 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
95 | | % % |
96 | | % % |
97 | | % % |
98 | | % 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 % |
99 | | % % |
100 | | % % |
101 | | % % |
102 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
103 | | % |
104 | | % GenerateDifferentialNoise() generates differential noise. |
105 | | % |
106 | | % The format of the GenerateDifferentialNoise method is: |
107 | | % |
108 | | % double GenerateDifferentialNoise(RandomInfo *random_info, |
109 | | % const Quantum pixel,const NoiseType noise_type,const double attenuate) |
110 | | % |
111 | | % A description of each parameter follows: |
112 | | % |
113 | | % o random_info: the random info. |
114 | | % |
115 | | % o pixel: noise is relative to this pixel value. |
116 | | % |
117 | | % o noise_type: the type of noise. |
118 | | % |
119 | | % o attenuate: attenuate the noise. |
120 | | % |
121 | | */ |
122 | | MagickPrivate double GenerateDifferentialNoise(RandomInfo *random_info, |
123 | | const Quantum pixel,const NoiseType noise_type,const double attenuate) |
124 | 0 | { |
125 | 0 | #define SigmaUniform (attenuate*0.015625) |
126 | 0 | #define SigmaGaussian (attenuate*0.015625) |
127 | 0 | #define SigmaImpulse (attenuate*0.1) |
128 | 0 | #define SigmaLaplacian (attenuate*0.0390625) |
129 | 0 | #define SigmaMultiplicativeGaussian (attenuate*0.5) |
130 | 0 | #define SigmaPoisson (attenuate*12.5) |
131 | 0 | #define SigmaRandom (attenuate) |
132 | 0 | #define TauGaussian (attenuate*0.078125) |
133 | |
|
134 | 0 | double |
135 | 0 | alpha, |
136 | 0 | beta, |
137 | 0 | noise, |
138 | 0 | sigma; |
139 | |
|
140 | 0 | alpha=GetPseudoRandomValue(random_info); |
141 | 0 | switch (noise_type) |
142 | 0 | { |
143 | 0 | case UniformNoise: |
144 | 0 | default: |
145 | 0 | { |
146 | 0 | noise=(double) pixel+(double) QuantumRange*SigmaUniform*(alpha-0.5); |
147 | 0 | break; |
148 | 0 | } |
149 | 0 | case GaussianNoise: |
150 | 0 | { |
151 | 0 | double |
152 | 0 | gamma, |
153 | 0 | tau; |
154 | |
|
155 | 0 | if (fabs(alpha) < MagickEpsilon) |
156 | 0 | alpha=1.0; |
157 | 0 | beta=GetPseudoRandomValue(random_info); |
158 | 0 | gamma=sqrt(-2.0*log(alpha)); |
159 | 0 | sigma=gamma*cos((double) (2.0*MagickPI*beta)); |
160 | 0 | tau=gamma*sin((double) (2.0*MagickPI*beta)); |
161 | 0 | noise=(double) pixel+sqrt((double) pixel)*SigmaGaussian*sigma+ |
162 | 0 | (double) QuantumRange*TauGaussian*tau; |
163 | 0 | break; |
164 | 0 | } |
165 | 0 | case ImpulseNoise: |
166 | 0 | { |
167 | 0 | if (alpha < (SigmaImpulse/2.0)) |
168 | 0 | noise=0.0; |
169 | 0 | else |
170 | 0 | if (alpha >= (1.0-(SigmaImpulse/2.0))) |
171 | 0 | noise=(double) QuantumRange; |
172 | 0 | else |
173 | 0 | noise=(double) pixel; |
174 | 0 | break; |
175 | 0 | } |
176 | 0 | case LaplacianNoise: |
177 | 0 | { |
178 | 0 | if (alpha <= 0.5) |
179 | 0 | { |
180 | 0 | if (alpha <= MagickEpsilon) |
181 | 0 | noise=(double) (pixel-QuantumRange); |
182 | 0 | else |
183 | 0 | noise=(double) pixel+(double) QuantumRange*SigmaLaplacian* |
184 | 0 | log(2.0*alpha)+0.5; |
185 | 0 | break; |
186 | 0 | } |
187 | 0 | beta=1.0-alpha; |
188 | 0 | if (beta <= (0.5*MagickEpsilon)) |
189 | 0 | noise=(double) (pixel+QuantumRange); |
190 | 0 | else |
191 | 0 | noise=(double) pixel-(double) QuantumRange*SigmaLaplacian* |
192 | 0 | log(2.0*beta)+0.5; |
193 | 0 | break; |
194 | 0 | } |
195 | 0 | case MultiplicativeGaussianNoise: |
196 | 0 | { |
197 | 0 | sigma=1.0; |
198 | 0 | if (alpha > MagickEpsilon) |
199 | 0 | sigma=sqrt(-2.0*log(alpha)); |
200 | 0 | beta=GetPseudoRandomValue(random_info); |
201 | 0 | noise=(double) pixel+(double) pixel*SigmaMultiplicativeGaussian*sigma* |
202 | 0 | cos((double) (2.0*MagickPI*beta))/2.0; |
203 | 0 | break; |
204 | 0 | } |
205 | 0 | case PoissonNoise: |
206 | 0 | { |
207 | 0 | double |
208 | 0 | poisson; |
209 | |
|
210 | 0 | ssize_t |
211 | 0 | i; |
212 | |
|
213 | 0 | poisson=exp(-SigmaPoisson*QuantumScale*(double) pixel); |
214 | 0 | for (i=0; alpha > poisson; i++) |
215 | 0 | { |
216 | 0 | beta=GetPseudoRandomValue(random_info); |
217 | 0 | alpha*=beta; |
218 | 0 | } |
219 | 0 | noise=(double) QuantumRange*i*MagickSafeReciprocal(SigmaPoisson); |
220 | 0 | break; |
221 | 0 | } |
222 | 0 | case RandomNoise: |
223 | 0 | { |
224 | 0 | noise=(double) QuantumRange*SigmaRandom*alpha; |
225 | 0 | break; |
226 | 0 | } |
227 | 0 | } |
228 | 0 | return(noise); |
229 | 0 | } |
230 | | |
231 | | /* |
232 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
233 | | % % |
234 | | % % |
235 | | % % |
236 | | % G e t O p t i m a l K e r n e l W i d t h % |
237 | | % % |
238 | | % % |
239 | | % % |
240 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
241 | | % |
242 | | % GetOptimalKernelWidth() computes the optimal kernel radius for a convolution |
243 | | % filter. Start with the minimum value of 3 pixels and walk out until we drop |
244 | | % below the threshold of one pixel numerical accuracy. |
245 | | % |
246 | | % The format of the GetOptimalKernelWidth method is: |
247 | | % |
248 | | % size_t GetOptimalKernelWidth(const double radius, |
249 | | % const double sigma) |
250 | | % |
251 | | % A description of each parameter follows: |
252 | | % |
253 | | % o width: GetOptimalKernelWidth returns the optimal width of a |
254 | | % convolution kernel. |
255 | | % |
256 | | % o radius: the radius of the Gaussian, in pixels, not counting the center |
257 | | % pixel. |
258 | | % |
259 | | % o sigma: the standard deviation of the Gaussian, in pixels. |
260 | | % |
261 | | */ |
262 | | MagickPrivate size_t GetOptimalKernelWidth1D(const double radius, |
263 | | const double sigma) |
264 | 0 | { |
265 | 0 | double |
266 | 0 | alpha, |
267 | 0 | beta, |
268 | 0 | gamma, |
269 | 0 | normalize, |
270 | 0 | value; |
271 | |
|
272 | 0 | size_t |
273 | 0 | width; |
274 | |
|
275 | 0 | ssize_t |
276 | 0 | i, |
277 | 0 | j; |
278 | | |
279 | 0 | if (IsEventLogging() != MagickFalse) |
280 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); |
281 | 0 | if (radius > MagickEpsilon) |
282 | 0 | return((size_t) (2.0*ceil(radius)+1.0)); |
283 | 0 | gamma=fabs(sigma); |
284 | 0 | if (gamma <= MagickEpsilon) |
285 | 0 | return(3UL); |
286 | 0 | alpha=MagickSafeReciprocal(2.0*gamma*gamma); |
287 | 0 | beta=(double) MagickSafeReciprocal((double) MagickSQ2PI*gamma); |
288 | 0 | for (width=5; ; ) |
289 | 0 | { |
290 | 0 | normalize=0.0; |
291 | 0 | j=(ssize_t) (width-1)/2; |
292 | 0 | for (i=(-j); i <= j; i++) |
293 | 0 | normalize+=exp(-((double) (i*i))*alpha)*beta; |
294 | 0 | value=exp(-((double) (j*j))*alpha)*beta/normalize; |
295 | 0 | if ((value < QuantumScale) || (value < MagickEpsilon)) |
296 | 0 | break; |
297 | 0 | width+=2; |
298 | 0 | } |
299 | 0 | return((size_t) (width-2)); |
300 | 0 | } |
301 | | |
302 | | MagickPrivate size_t GetOptimalKernelWidth2D(const double radius, |
303 | | const double sigma) |
304 | 0 | { |
305 | 0 | double |
306 | 0 | alpha, |
307 | 0 | beta, |
308 | 0 | gamma, |
309 | 0 | normalize, |
310 | 0 | value; |
311 | |
|
312 | 0 | size_t |
313 | 0 | width; |
314 | |
|
315 | 0 | ssize_t |
316 | 0 | j, |
317 | 0 | u, |
318 | 0 | v; |
319 | |
|
320 | 0 | if (IsEventLogging() != MagickFalse) |
321 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); |
322 | 0 | if (radius > MagickEpsilon) |
323 | 0 | return((size_t) (2.0*ceil(radius)+1.0)); |
324 | 0 | gamma=fabs(sigma); |
325 | 0 | if (gamma <= MagickEpsilon) |
326 | 0 | return(3UL); |
327 | 0 | alpha=MagickSafeReciprocal(2.0*gamma*gamma); |
328 | 0 | beta=(double) MagickSafeReciprocal((double) Magick2PI*gamma*gamma); |
329 | 0 | for (width=5; ; ) |
330 | 0 | { |
331 | 0 | normalize=0.0; |
332 | 0 | j=(ssize_t) (width-1)/2; |
333 | 0 | for (v=(-j); v <= j; v++) |
334 | 0 | for (u=(-j); u <= j; u++) |
335 | 0 | normalize+=exp(-((double) (u*u+v*v))*alpha)*beta; |
336 | 0 | value=exp(-((double) (j*j))*alpha)*beta/normalize; |
337 | 0 | if ((value < QuantumScale) || (value < MagickEpsilon)) |
338 | 0 | break; |
339 | 0 | width+=2; |
340 | 0 | } |
341 | 0 | return((size_t) (width-2)); |
342 | 0 | } |
343 | | |
344 | | MagickPrivate size_t GetOptimalKernelWidth(const double radius, |
345 | | const double sigma) |
346 | 0 | { |
347 | 0 | return(GetOptimalKernelWidth1D(radius,sigma)); |
348 | 0 | } |