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