1 /* despeckle.c: Bitmap despeckler
2 
3    Copyright (C) 2001 David A. Bartold / Martin Weber
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public License
7    as published by the Free Software Foundation; either version 2.1 of
8    the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18    USA. */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif /* Def: HAVE_CONFIG_H */
23 
24 #include <assert.h>
25 #include <math.h>
26 #include <stdio.h>
27 #include <time.h>
28 #include "xstd.h"
29 #include "logreport.h"
30 #include "types.h"
31 #include "bitmap.h"
32 #include "despeckle.h"
33 
34 /* Calculate Error - compute the error between two colors
35  *
36  *   Input parameters:
37  *     Two 24 bit RGB colors
38  *
39  *   Returns:
40  *     The squared error between the two colors
41  */
42 
calc_error(unsigned char * color1,unsigned char * color2)43 static int calc_error(unsigned char *color1, unsigned char *color2)
44 {
45   int the_error;
46   int temp;
47 
48   temp = color1[0] - color2[0];
49   the_error = temp * temp;
50   temp = color1[1] - color2[1];
51   the_error += temp * temp;
52   temp = color1[2] - color2[2];
53   the_error += temp * temp;
54 
55   return the_error;
56 }
57 
58 /* Calculate Error - compute the error between two colors
59  *
60  *   Input parameters:
61  *     Two 8 bit gray scale colors
62  *
63  *   Returns:
64  *     The squared error between the two colors
65  */
66 
calc_error_8(unsigned char * color1,unsigned char * color2)67 static int calc_error_8(unsigned char *color1, unsigned char *color2)
68 {
69   int the_error;
70 
71   the_error = abs(color1[0] - color2[0]);
72 
73   return the_error;
74 }
75 
76 /* Find Size - Find the number of adjacent pixels of the same color
77  *
78  * Input Parameters:
79  *   An 24 bit image, the current location inside the image, and the palette
80  *   index of the color we are looking for
81  *
82  * Modified Parameters:
83  *   A mask array used to prevent backtracking over already counted pixels
84  *
85  * Returns:
86  *   Number of adjacent pixels found having the same color
87  */
88 
find_size(unsigned char * index,int x,int y,int width,int height,unsigned char * bitmap,unsigned char * mask)89 static int find_size( /* in */ unsigned char *index,
90                      /* in */ int x,
91                      /* in */ int y,
92                      /* in */ int width,
93                      /* in */ int height,
94                      /* in */ unsigned char *bitmap,
95                      /* in/out */ unsigned char *mask)
96 {
97   int count;
98   int x1, x2;
99 
100   if (y < 0 || y >= height || mask[y * width + x] == 1 || bitmap[3 * (y * width + x)] != index[0] || bitmap[3 * (y * width + x) + 1] != index[1] || bitmap[3 * (y * width + x) + 2] != index[2])
101     return 0;
102 
103   for (x1 = x; x1 >= 0 && bitmap[3 * (y * width + x1)] == index[0] && bitmap[3 * (y * width + x1) + 1] == index[1] && bitmap[3 * (y * width + x1) + 2] == index[2] && mask[y * width + x] != 1; x1--) ;
104   x1++;
105 
106   for (x2 = x; x2 < width && bitmap[3 * (y * width + x2)] == index[0] && bitmap[3 * (y * width + x2) + 1] == index[1] && bitmap[3 * (y * width + x2) + 2] == index[2] && mask[y * width + x] != 1; x2++) ;
107   x2--;
108 
109   count = x2 - x1 + 1;
110   for (x = x1; x <= x2; x++)
111     mask[y * width + x] = 1;
112 
113   for (x = x1; x <= x2; x++) {
114     count += find_size(index, x, y - 1, width, height, bitmap, mask);
115     count += find_size(index, x, y + 1, width, height, bitmap, mask);
116   }
117 
118   return count;
119 }
120 
121 /* Find Size - Find the number of adjacent pixels of the same color
122  *
123  * Input Parameters:
124  *   An 8 bit image, the current location inside the image, and the palette
125  *   index of the color we are looking for
126  *
127  * Modified Parameters:
128  *   A mask array used to prevent backtracking over already counted pixels
129  *
130  * Returns:
131  *   Number of adjacent pixels found having the same color
132  */
133 
find_size_8(unsigned char * index,int x,int y,int width,int height,unsigned char * bitmap,unsigned char * mask)134 static int find_size_8( /* in */ unsigned char *index,
135                        /* in */ int x,
136                        /* in */ int y,
137                        /* in */ int width,
138                        /* in */ int height,
139                        /* in */ unsigned char *bitmap,
140                        /* in/out */ unsigned char *mask)
141 {
142   int count;
143   int x1, x2;
144 
145   if (y < 0 || y >= height || mask[y * width + x] == 1 || bitmap[(y * width + x)] != index[0])
146     return 0;
147 
148   for (x1 = x; x1 >= 0 && bitmap[(y * width + x1)] == index[0] && mask[y * width + x] != 1; x1--) ;
149   x1++;
150 
151   for (x2 = x; x2 < width && bitmap[(y * width + x2)] == index[0] && mask[y * width + x] != 1; x2++) ;
152   x2--;
153 
154   count = x2 - x1 + 1;
155   for (x = x1; x <= x2; x++)
156     mask[y * width + x] = 1;
157 
158   for (x = x1; x <= x2; x++) {
159     count += find_size_8(index, x, y - 1, width, height, bitmap, mask);
160     count += find_size_8(index, x, y + 1, width, height, bitmap, mask);
161   }
162 
163   return count;
164 }
165 
166 /* Find Most Similar Neighbor - Given a position in an 24 bit bitmap and a color
167  * index, traverse over a blob of adjacent pixels having the same value.
168  * Return the color index of the neighbor pixel that has the most similar
169  * color.
170  *
171  * Input parameters:
172  *   24 bit bitmap, the current location inside the image,
173  *   and the color index of the blob
174  *
175  * Modified parameters:
176  *   Mask used to prevent backtracking
177  *
178  * Output parameters:
179  *   Closest index != index and the error between the two colors squared
180  */
181 
find_most_similar_neighbor(unsigned char * index,unsigned char ** closest_index,int * error_amt,int x,int y,int width,int height,unsigned char * bitmap,unsigned char * mask)182 static void find_most_similar_neighbor( /* in */ unsigned char *index,
183                                        /* in/out */ unsigned char **closest_index,
184                                        /* in/out */ int *error_amt,
185                                        /* in */ int x,
186                                        /* in */ int y,
187                                        /* in */ int width,
188                                        /* in */ int height,
189                                        /* in */ unsigned char *bitmap,
190                                        /* in/out */ unsigned char *mask)
191 {
192   int x1, x2;
193   int temp_error;
194   unsigned char *value, *temp;
195 
196   if (y < 0 || y >= height || mask[y * width + x] == 2)
197     return;
198 
199   temp = &bitmap[3 * (y * width + x)];
200 
201   assert(closest_index != NULL);
202 
203   if (temp[0] != index[0] || temp[1] != index[1] || temp[2] != index[2]) {
204     value = temp;
205 
206     temp_error = calc_error(index, value);
207 
208     if (*closest_index == NULL || temp_error < *error_amt)
209       *closest_index = value, *error_amt = temp_error;
210 
211     return;
212   }
213 
214   for (x1 = x; x1 >= 0 && bitmap[3 * (y * width + x1)] == index[0] && bitmap[3 * (y * width + x1) + 1] == index[1] && bitmap[3 * (y * width + x1) + 2] == index[2]; x1--) ;
215   x1++;
216 
217   for (x2 = x; x2 < width && bitmap[3 * (y * width + x2)] == index[0] && bitmap[3 * (y * width + x2) + 1] == index[1] && bitmap[3 * (y * width + x2) + 2] == index[2]; x2++) ;
218   x2--;
219 
220   if (x1 > 0) {
221     value = &bitmap[3 * (y * width + x1 - 1)];
222 
223     temp_error = calc_error(index, value);
224 
225     if (*closest_index == NULL || temp_error < *error_amt)
226       *closest_index = value, *error_amt = temp_error;
227   }
228 
229   if (x2 < width - 1) {
230     value = &bitmap[3 * (y * width + x2 + 1)];
231 
232     temp_error = calc_error(index, value);
233 
234     if (*closest_index == NULL || temp_error < *error_amt)
235       *closest_index = value, *error_amt = temp_error;
236   }
237 
238   for (x = x1; x <= x2; x++)
239     mask[y * width + x] = 2;
240 
241   for (x = x1; x <= x2; x++) {
242     find_most_similar_neighbor(index, closest_index, error_amt, x, y - 1, width, height, bitmap, mask);
243     find_most_similar_neighbor(index, closest_index, error_amt, x, y + 1, width, height, bitmap, mask);
244   }
245 }
246 
247 /* Find Most Similar Neighbor - Given a position in an 8 bit bitmap and a color
248  * index, traverse over a blob of adjacent pixels having the same value.
249  * Return the color index of the neighbor pixel that has the most similar
250  * color.
251  *
252  * Input parameters:
253  *   8 bit bitmap, the current location inside the image,
254  *   and the color index of the blob
255  *
256  * Modified parameters:
257  *   Mask used to prevent backtracking
258  *
259  * Output parameters:
260  *   Closest index != index and the error between the two colors squared
261  */
262 
find_most_similar_neighbor_8(unsigned char * index,unsigned char ** closest_index,int * error_amt,int x,int y,int width,int height,unsigned char * bitmap,unsigned char * mask)263 static void find_most_similar_neighbor_8( /* in */ unsigned char *index,
264                                          /* in/out */ unsigned char **closest_index,
265                                          /* in/out */ int *error_amt,
266                                          /* in */ int x,
267                                          /* in */ int y,
268                                          /* in */ int width,
269                                          /* in */ int height,
270                                          /* in */ unsigned char *bitmap,
271                                          /* in/out */ unsigned char *mask)
272 {
273   int x1, x2;
274   int temp_error;
275   unsigned char *value, *temp;
276 
277   if (y < 0 || y >= height || mask[y * width + x] == 2)
278     return;
279 
280   temp = &bitmap[(y * width + x)];
281 
282   assert(closest_index != NULL);
283 
284   if (temp[0] != index[0]) {
285     value = temp;
286 
287     temp_error = calc_error_8(index, value);
288 
289     if (*closest_index == NULL || temp_error < *error_amt)
290       *closest_index = value, *error_amt = temp_error;
291 
292     return;
293   }
294 
295   for (x1 = x; x1 >= 0 && bitmap[(y * width + x1)] == index[0]; x1--) ;
296   x1++;
297 
298   for (x2 = x; x2 < width && bitmap[(y * width + x2)] == index[0]; x2++) ;
299   x2--;
300 
301   if (x1 > 0) {
302     value = &bitmap[(y * width + x1 - 1)];
303 
304     temp_error = calc_error_8(index, value);
305 
306     if (*closest_index == NULL || temp_error < *error_amt)
307       *closest_index = value, *error_amt = temp_error;
308   }
309 
310   if (x2 < width - 1) {
311     value = &bitmap[(y * width + x2 + 1)];
312 
313     temp_error = calc_error_8(index, value);
314 
315     if (*closest_index == NULL || temp_error < *error_amt)
316       *closest_index = value, *error_amt = temp_error;
317   }
318 
319   for (x = x1; x <= x2; x++)
320     mask[y * width + x] = 2;
321 
322   for (x = x1; x <= x2; x++) {
323     find_most_similar_neighbor_8(index, closest_index, error_amt, x, y - 1, width, height, bitmap, mask);
324     find_most_similar_neighbor_8(index, closest_index, error_amt, x, y + 1, width, height, bitmap, mask);
325   }
326 }
327 
328 /* Fill - change the color of a blob
329  *
330  * Input parameters:
331  *   The new color
332  *
333  * Modified parameters:
334  *   24 bit pixbuf and its mask (used to prevent backtracking)
335  */
336 
fill(unsigned char * to_index,int x,int y,int width,int height,unsigned char * bitmap,unsigned char * mask)337 static void fill( /* in */ unsigned char *to_index,
338                  /* in */ int x,
339                  /* in */ int y,
340                  /* in */ int width,
341                  /* in */ int height,
342                  /* in/out */ unsigned char *bitmap,
343                  /* in/out */ unsigned char *mask)
344 {
345   int x1, x2;
346 
347   if (y < 0 || y >= height || mask[y * width + x] != 2)
348     return;
349 
350   for (x1 = x; x1 >= 0 && mask[y * width + x1] == 2; x1--) ;
351   x1++;
352   for (x2 = x; x2 < width && mask[y * width + x2] == 2; x2++) ;
353   x2--;
354 
355   assert(x1 >= 0 && x2 < width);
356 
357   for (x = x1; x <= x2; x++) {
358     bitmap[3 * (y * width + x)] = to_index[0];
359     bitmap[3 * (y * width + x) + 1] = to_index[1];
360     bitmap[3 * (y * width + x) + 2] = to_index[2];
361     mask[y * width + x] = 3;
362   }
363 
364   for (x = x1; x <= x2; x++) {
365     fill(to_index, x, y - 1, width, height, bitmap, mask);
366     fill(to_index, x, y + 1, width, height, bitmap, mask);
367   }
368 }
369 
370 /* Fill - change the color of a blob
371  *
372  * Input parameters:
373  *   The new color
374  *
375  * Modified parameters:
376  *   8 bit pixbuf and its mask (used to prevent backtracking)
377  */
378 
fill_8(unsigned char * to_index,int x,int y,int width,int height,unsigned char * bitmap,unsigned char * mask)379 static void fill_8( /* in */ unsigned char *to_index,
380                    /* in */ int x,
381                    /* in */ int y,
382                    /* in */ int width,
383                    /* in */ int height,
384                    /* in/out */ unsigned char *bitmap,
385                    /* in/out */ unsigned char *mask)
386 {
387   int x1, x2;
388 
389   if (y < 0 || y >= height || mask[y * width + x] != 2)
390     return;
391 
392   for (x1 = x; x1 >= 0 && mask[y * width + x1] == 2; x1--) ;
393   x1++;
394   for (x2 = x; x2 < width && mask[y * width + x2] == 2; x2++) ;
395   x2--;
396 
397   assert(x1 >= 0 && x2 < width);
398 
399   for (x = x1; x <= x2; x++) {
400     bitmap[(y * width + x)] = to_index[0];
401     mask[y * width + x] = 3;
402   }
403 
404   for (x = x1; x <= x2; x++) {
405     fill_8(to_index, x, y - 1, width, height, bitmap, mask);
406     fill_8(to_index, x, y + 1, width, height, bitmap, mask);
407   }
408 }
409 
410 /* Ignore - blob is big enough, mask it off
411  *
412  * Modified parameters:
413  *   its mask (used to prevent backtracking)
414  */
415 
ignore(int x,int y,int width,int height,unsigned char * mask)416 static void ignore( /* in */ int x,
417                    /* in */ int y,
418                    /* in */ int width,
419                    /* in */ int height,
420                    /* in/out */ unsigned char *mask)
421 {
422   int x1, x2;
423 
424   if (y < 0 || y >= height || mask[y * width + x] != 1)
425     return;
426 
427   for (x1 = x; x1 >= 0 && mask[y * width + x1] == 1; x1--) ;
428   x1++;
429   for (x2 = x; x2 < width && mask[y * width + x2] == 1; x2++) ;
430   x2--;
431 
432   assert(x1 >= 0 && x2 < width);
433 
434   for (x = x1; x <= x2; x++)
435     mask[y * width + x] = 3;
436 
437   for (x = x1; x <= x2; x++) {
438     ignore(x, y - 1, width, height, mask);
439     ignore(x, y + 1, width, height, mask);
440   }
441 }
442 
443 /* Recolor - conditionally change a feature's color to the closest color of all
444  * neighboring pixels
445  *
446  * Input parameters:
447  *   The color palette, current blob size, and adaptive tightness
448  *
449  *   Adaptive Tightness: (integer 1 to 256)
450  *     1   = really tight
451  *     256 = turn off the feature
452  *
453  * Modified parameters:
454  *   24 bit pixbuf and its mask (used to prevent backtracking)
455  *
456  * Returns:
457  *   TRUE  - feature was recolored, thus coalesced
458  *   FALSE - feature wasn't recolored
459  */
460 
recolor(double adaptive_tightness,int x,int y,int width,int height,unsigned char * bitmap,unsigned char * mask)461 static gboolean recolor( /* in */ double adaptive_tightness,
462                         /* in */ int x,
463                         /* in */ int y,
464                         /* in */ int width,
465                         /* in */ int height,
466                         /* in/out */ unsigned char *bitmap,
467                         /* in/out */ unsigned char *mask)
468 {
469   unsigned char *index, *to_index;
470   int error_amt, max_error;
471 
472   index = &bitmap[3 * (y * width + x)];
473   to_index = NULL;
474   error_amt = 0;
475   max_error = (int)(3.0 * adaptive_tightness * adaptive_tightness);
476 
477   find_most_similar_neighbor(index, &to_index, &error_amt, x, y, width, height, bitmap, mask);
478 
479   /* This condition only fails if the bitmap is all the same color */
480   if (to_index != NULL) {
481     /*
482      * If the difference between the two colors is too great,
483      * don't coalesce the feature with its neighbor(s).  This prevents a
484      * color from turning into its complement.
485      */
486 
487     if (calc_error(index, to_index) > max_error)
488       fill(index, x, y, width, height, bitmap, mask);
489     else {
490       fill(to_index, x, y, width, height, bitmap, mask);
491 
492       return TRUE;
493     }
494   }
495 
496   return FALSE;
497 }
498 
499 /* Recolor - conditionally change a feature's color to the closest color of all
500  * neighboring pixels
501  *
502  * Input parameters:
503  *   The color palette, current blob size, and adaptive tightness
504  *
505  *   Adaptive Tightness: (integer 1 to 256)
506  *     1   = really tight
507  *     256 = turn off the feature
508  *
509  * Modified parameters:
510  *   8 bit pixbuf and its mask (used to prevent backtracking)
511  *
512  * Returns:
513  *   TRUE  - feature was recolored, thus coalesced
514  *   FALSE - feature wasn't recolored
515  */
516 
recolor_8(double adaptive_tightness,int x,int y,int width,int height,unsigned char * bitmap,unsigned char * mask)517 static gboolean recolor_8( /* in */ double adaptive_tightness,
518                           /* in */ int x,
519                           /* in */ int y,
520                           /* in */ int width,
521                           /* in */ int height,
522                           /* in/out */ unsigned char *bitmap,
523                           /* in/out */ unsigned char *mask)
524 {
525   unsigned char *index, *to_index;
526   int error_amt;
527 
528   index = &bitmap[(y * width + x)];
529   to_index = NULL;
530   error_amt = 0;
531 
532   find_most_similar_neighbor_8(index, &to_index, &error_amt, x, y, width, height, bitmap, mask);
533 
534   /* This condition only fails if the bitmap is all the same color */
535   if (to_index != NULL) {
536     /*
537      * If the difference between the two colors is too great,
538      * don't coalesce the feature with its neighbor(s).  This prevents a
539      * color from turning into its complement.
540      */
541 
542     if (calc_error_8(index, to_index) > adaptive_tightness)
543       fill_8(index, x, y, width, height, bitmap, mask);
544     else {
545       fill_8(to_index, x, y, width, height, bitmap, mask);
546 
547       return TRUE;
548     }
549   }
550 
551   return FALSE;
552 }
553 
554 /* Despeckle Iteration - Despeckle all regions smaller than cur_size pixels
555  *
556  * Input Parameters:
557  *   Current blob size, maximum blob size
558  *   for all iterations (used to selectively recolor blobs), adaptive
559  *   tightness and noise removal
560  *
561  * Modified Parameters:
562  *   The 24 bit pixbuf is despeckled
563  */
564 
despeckle_iteration(int level,double adaptive_tightness,double noise_max,int width,int height,unsigned char * bitmap)565 static void despeckle_iteration( /* in */ int level,
566                                 /* in */ double adaptive_tightness,
567                                 /* in */ double noise_max,
568                                 /* in */ int width,
569                                 /* in */ int height,
570                                 /* in/out */ unsigned char *bitmap)
571 {
572   unsigned char *mask;
573   int x, y;
574   int current_size;
575   int tightness;
576 
577   /* Size doubles each iteration level, so current_size = 2^level */
578   current_size = 1 << level;
579   tightness = (int)(noise_max / (1.0 + adaptive_tightness * level));
580 
581   mask = (unsigned char *)calloc(width * height, sizeof(unsigned char));
582   for (y = 0; y < height; y++) {
583     for (x = 0; x < width; x++) {
584       if (mask[y * width + x] == 0) {
585         int size;
586 
587         size = find_size(&bitmap[3 * (y * width + x)], x, y, width, height, bitmap, mask);
588 
589         assert(size > 0);
590 
591         if (size < current_size) {
592           if (recolor(tightness, x, y, width, height, bitmap, mask))
593             x--;
594         } else
595           ignore(x, y, width, height, mask);
596       }
597     }
598   }
599 
600   free(mask);
601 }
602 
603 /* Despeckle Iteration - Despeckle all regions smaller than cur_size pixels
604  *
605  * Input Parameters:
606  *   Current blob size, maximum blob size
607  *   for all iterations (used to selectively recolor blobs), adaptive
608  *   tightness and noise removal
609  *
610  * Modified Parameters:
611  *   The 8 bit pixbuf is despeckled
612  */
613 
despeckle_iteration_8(int level,double adaptive_tightness,double noise_max,int width,int height,unsigned char * bitmap)614 static void despeckle_iteration_8( /* in */ int level,
615                                   /* in */ double adaptive_tightness,
616                                   /* in */ double noise_max,
617                                   /* in */ int width,
618                                   /* in */ int height,
619                                   /* in/out */ unsigned char *bitmap)
620 {
621   unsigned char *mask;
622   int x, y;
623   int current_size;
624   int tightness;
625 
626   /* Size doubles each iteration level, so current_size = 2^level */
627   current_size = 1 << level;
628   tightness = (int)(noise_max / (1.0 + adaptive_tightness * level));
629 
630   mask = (unsigned char *)calloc(width * height, sizeof(unsigned char));
631   for (y = 0; y < height; y++) {
632     for (x = 0; x < width; x++) {
633       if (mask[y * width + x] == 0) {
634         int size;
635 
636         size = find_size_8(&bitmap[(y * width + x)], x, y, width, height, bitmap, mask);
637 
638         assert(size > 0);
639 
640         if (size < current_size) {
641           if (recolor_8(tightness, x, y, width, height, bitmap, mask))
642             x--;
643         } else
644           ignore(x, y, width, height, mask);
645       }
646     }
647   }
648 
649   free(mask);
650 }
651 
652 /* Despeckle - Despeckle a 8 or 24 bit image
653  *
654  * Input Parameters:
655  *   Adaptive feature coalescing value, the despeckling level and noise removal
656  *
657  *   Despeckling level (level): Integer from 0 to ~20
658  *     0 = perform no despeckling
659  *     An increase of the despeckling level by one doubles the size of features.
660  *     The Maximum value must be smaller then the logarithm base two of the number
661  *     of pixels.
662  *
663  *   Feature coalescing (tightness): Real from 0.0 to ~8.0
664  *     0 = Turn it off (whites may turn black and vice versa, etc)
665  *     3 = Good middle value
666  *     8 = Really tight
667  *
668  *   Noise removal (noise_removal): Real from 1.0 to 0.0
669  *     1 = Maximum noise removal
670  *     You should always use the highest value, only if certain parts of the image
671  *     disappear you should lower it.
672  *
673  * Modified Parameters:
674  *   The bitmap is despeckled.
675  */
676 
despeckle(at_bitmap * bitmap,int level,gfloat tightness,gfloat noise_removal,at_exception_type * excep)677 void despeckle( /* in/out */ at_bitmap * bitmap,
678                /* in */ int level,
679                /* in */ gfloat tightness,
680                /* in */ gfloat noise_removal,
681                /* exception handling */ at_exception_type * excep)
682 {
683   int i, planes, max_level;
684   short width, height;
685   unsigned char *bits;
686   double noise_max, adaptive_tightness;
687 
688   planes = AT_BITMAP_PLANES(bitmap);
689   noise_max = noise_removal * 255.0;
690   width = AT_BITMAP_WIDTH(bitmap);
691   height = AT_BITMAP_HEIGHT(bitmap);
692   bits = AT_BITMAP_BITS(bitmap);
693   max_level = (int)(log(width * height) / log(2.0) - 0.5);
694   if (level > max_level)
695     level = max_level;
696   adaptive_tightness = (noise_removal * (1.0 + tightness * level) - 1.0) / level;
697 
698   if (planes == 3) {
699     for (i = 0; i < level; i++)
700       despeckle_iteration(i, adaptive_tightness, noise_max, width, height, bits);
701   } else if (planes == 1) {
702     for (i = 0; i < level; i++)
703       despeckle_iteration_8(i, adaptive_tightness, noise_max, width, height, bits);
704   } else {
705     LOG("despeckle: %u-plane images are not supported", planes);
706     at_exception_fatal(excep, "despeckle: wrong plane images are passed");
707     return;
708   }
709 
710 }
711