1 /** @file sanei_ir.c
2 *
3 * sanei_ir - functions for utilizing the infrared plane
4 *
5 * Copyright (C) 2012 Michael Rickmann <mrickma@gwdg.de>
6 *
7 * This file is part of the SANE package.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 *
22 * The threshold yen, otsu and max_entropy routines have been
23 * adapted from the FOURIER 0.8 library by M. Emre Celebi,
24 * http://sourceforge.net/projects/fourier-ipal/ which is
25 * licensed under the GNU General Public License version 2 or later.
26 */
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <float.h>
31 #include <limits.h>
32 #include <math.h>
33
34 #define BACKEND_NAME sanei_ir /* name of this module for debugging */
35
36 #include "../include/sane/sane.h"
37 #include "../include/sane/sanei_debug.h"
38 #include "../include/sane/sanei_ir.h"
39 #include "../include/sane/sanei_magic.h"
40
41
42 double *
43 sanei_ir_create_norm_histo (const SANE_Parameters * params, const SANE_Uint *img_data);
44 double * sanei_ir_accumulate_norm_histo (double * histo_data);
45
46
47 /* Initialize sanei_ir
48 */
49 void
sanei_ir_init(void)50 sanei_ir_init (void)
51 {
52 DBG_INIT ();
53 }
54
55
56 /* Create a normalized histogram of a grayscale image, internal
57 */
58 double *
sanei_ir_create_norm_histo(const SANE_Parameters * params,const SANE_Uint * img_data)59 sanei_ir_create_norm_histo (const SANE_Parameters * params,
60 const SANE_Uint *img_data)
61 {
62 int is, i;
63 int num_pixels;
64 int *histo_data;
65 double *histo;
66 double term;
67
68 DBG (10, "sanei_ir_create_norm_histo\n");
69
70 if ((params->format != SANE_FRAME_GRAY)
71 && (params->format != SANE_FRAME_RED)
72 && (params->format != SANE_FRAME_GREEN)
73 && (params->format != SANE_FRAME_BLUE))
74 {
75 DBG (5, "sanei_ir_create_norm_histo: invalid format\n");
76 return NULL;
77 }
78
79 /* Allocate storage for the histogram */
80 histo_data = calloc (HISTOGRAM_SIZE, sizeof (int));
81 histo = malloc (HISTOGRAM_SIZE * sizeof (double));
82 if ((histo == NULL) || (histo_data == NULL))
83 {
84 DBG (5, "sanei_ir_create_norm_histo: no buffers\n");
85 if (histo) free (histo);
86 if (histo_data) free (histo_data);
87 return NULL;
88 }
89
90 num_pixels = params->pixels_per_line * params->lines;
91
92 DBG (1, "sanei_ir_create_norm_histo: %d pixels_per_line, %d lines => %d num_pixels\n", params->pixels_per_line, params->lines, num_pixels);
93 DBG (1, "sanei_ir_create_norm_histo: histo_data[] with %d x %ld bytes\n", HISTOGRAM_SIZE, sizeof(int));
94 /* Populate the histogram */
95 is = 16 - HISTOGRAM_SHIFT; /* Number of data bits to ignore */
96 DBG (1, "sanei_ir_create_norm_histo: depth %d, HISTOGRAM_SHIFT %d => ignore %d bits\n", params->depth, HISTOGRAM_SHIFT, is);
97 for (i = num_pixels; i > 0; i--) {
98 histo_data[*img_data++ >> is]++;
99 }
100
101 /* Calculate the normalized histogram */
102 term = 1.0 / (double) num_pixels;
103 for (i = 0; i < HISTOGRAM_SIZE; i++)
104 histo[i] = term * (double) histo_data[i];
105
106 free (histo_data);
107 return histo;
108 }
109
110
111 /* Create the normalized histogram of a grayscale image
112 */
113 SANE_Status
sanei_ir_create_norm_histogram(const SANE_Parameters * params,const SANE_Uint * img_data,double ** histogram)114 sanei_ir_create_norm_histogram (const SANE_Parameters * params,
115 const SANE_Uint *img_data,
116 double ** histogram)
117 {
118 double *histo;
119
120 DBG (10, "sanei_ir_create_norm_histogram\n");
121
122 histo = sanei_ir_create_norm_histo (params, img_data);
123 if (!histo)
124 return SANE_STATUS_NO_MEM;
125
126 *histogram = histo;
127 return SANE_STATUS_GOOD;
128 }
129
130 /* Accumulate a normalized histogram, internal
131 */
132 double *
sanei_ir_accumulate_norm_histo(double * histo_data)133 sanei_ir_accumulate_norm_histo (double * histo_data)
134 {
135 int i;
136 double *accum_data;
137
138 accum_data = malloc (HISTOGRAM_SIZE * sizeof (double));
139 if (accum_data == NULL)
140 {
141 DBG (5, "sanei_ir_accumulate_norm_histo: Insufficient memory !\n");
142 return NULL;
143 }
144
145 accum_data[0] = histo_data[0];
146 for (i = 1; i < HISTOGRAM_SIZE; i++)
147 accum_data[i] = accum_data[i - 1] + histo_data[i];
148
149 return accum_data;
150 }
151
152 /* Implements Yen's thresholding method
153 */
154 SANE_Status
sanei_ir_threshold_yen(const SANE_Parameters * params,double * norm_histo,int * thresh)155 sanei_ir_threshold_yen (const SANE_Parameters * params,
156 double * norm_histo, int *thresh)
157 {
158 double *P1 = NULL; /* cumulative normalized histogram */
159 double *P1_sq = NULL; /* cumulative normalized histogram */
160 double *P2_sq = NULL;
161 double crit, max_crit;
162 int threshold, i;
163 SANE_Status ret = SANE_STATUS_NO_MEM;
164
165 DBG (10, "sanei_ir_threshold_yen\n");
166
167 P1 = sanei_ir_accumulate_norm_histo (norm_histo);
168 P1_sq = malloc (HISTOGRAM_SIZE * sizeof (double));
169 P2_sq = malloc (HISTOGRAM_SIZE * sizeof (double));
170 if (!P1 || !P1_sq || !P2_sq)
171 {
172 DBG (5, "sanei_ir_threshold_yen: no buffers\n");
173 goto cleanup;
174 }
175
176 /* calculate cumulative squares */
177 P1_sq[0] = norm_histo[0] * norm_histo[0];
178 for (i = 1; i < HISTOGRAM_SIZE; i++)
179 P1_sq[i] = P1_sq[i - 1] + norm_histo[i] * norm_histo[i];
180 P2_sq[HISTOGRAM_SIZE - 1] = 0.0;
181 for (i = HISTOGRAM_SIZE - 2; i >= 0; i--)
182 P2_sq[i] = P2_sq[i + 1] + norm_histo[i + 1] * norm_histo[i + 1];
183
184 /* Find the threshold that maximizes the criterion */
185 threshold = INT_MIN;
186 max_crit = DBL_MIN;
187 for (i = 0; i < HISTOGRAM_SIZE; i++)
188 {
189 crit =
190 -1.0 * SAFE_LOG (P1_sq[i] * P2_sq[i]) +
191 2 * SAFE_LOG (P1[i] * (1.0 - P1[i]));
192 if (crit > max_crit)
193 {
194 max_crit = crit;
195 threshold = i;
196 }
197 }
198
199 if (threshold == INT_MIN)
200 {
201 DBG (5, "sanei_ir_threshold_yen: no threshold found\n");
202 ret = SANE_STATUS_INVAL;
203 }
204 else
205 {
206 ret = SANE_STATUS_GOOD;
207 if (params->depth > 8)
208 {
209 i = 1 << (params->depth - HISTOGRAM_SHIFT);
210 *thresh = threshold * i + i / 2;
211 }
212 else
213 *thresh = threshold;
214 DBG (10, "sanei_ir_threshold_yen: threshold %d\n", *thresh);
215 }
216
217 cleanup:
218 if (P1)
219 free (P1);
220 if (P1_sq)
221 free (P1_sq);
222 if (P2_sq)
223 free (P2_sq);
224 return ret;
225 }
226
227
228 /* Implements Otsu's thresholding method
229 */
230 SANE_Status
sanei_ir_threshold_otsu(const SANE_Parameters * params,double * norm_histo,int * thresh)231 sanei_ir_threshold_otsu (const SANE_Parameters * params,
232 double * norm_histo, int *thresh)
233 {
234 double *cnh = NULL;
235 double *mean = NULL;
236 double total_mean;
237 double bcv, max_bcv;
238 int first_bin, last_bin;
239 int threshold, i;
240 SANE_Status ret = SANE_STATUS_NO_MEM;
241
242 DBG (10, "sanei_ir_threshold_otsu\n");
243
244 cnh = sanei_ir_accumulate_norm_histo (norm_histo);
245 mean = malloc (HISTOGRAM_SIZE * sizeof (double));
246 if (!cnh || !mean)
247 {
248 DBG (5, "sanei_ir_threshold_otsu: no buffers\n");
249 goto cleanup;
250 }
251
252 mean[0] = 0.0;
253 for (i = 1; i < HISTOGRAM_SIZE; i++)
254 mean[i] = mean[i - 1] + i * norm_histo[i];
255 total_mean = mean[HISTOGRAM_SIZE - 1];
256
257 first_bin = 0;
258 for (i = 0; i < HISTOGRAM_SIZE; i++)
259 if (cnh[i] != 0)
260 {
261 first_bin = i;
262 break;
263 }
264 last_bin = HISTOGRAM_SIZE - 1;
265 for (i = HISTOGRAM_SIZE - 1; i >= first_bin; i--)
266 if (1.0 - cnh[i] != 0)
267 {
268 last_bin = i;
269 break;
270 }
271
272 threshold = INT_MIN;
273 max_bcv = 0.0;
274 for (i = first_bin; i <= last_bin; i++)
275 {
276 bcv = total_mean * cnh[i] - mean[i];
277 bcv *= bcv / (cnh[i] * (1.0 - cnh[i]));
278 if (max_bcv < bcv)
279 {
280 max_bcv = bcv;
281 threshold = i;
282 }
283 }
284
285 if (threshold == INT_MIN)
286 {
287 DBG (5, "sanei_ir_threshold_otsu: no threshold found\n");
288 ret = SANE_STATUS_INVAL;
289 }
290 else
291 {
292 ret = SANE_STATUS_GOOD;
293 if (params->depth > 8)
294 {
295 i = 1 << (params->depth - HISTOGRAM_SHIFT);
296 *thresh = threshold * i + i / 2;
297 }
298 else
299 *thresh = threshold;
300 DBG (10, "sanei_ir_threshold_otsu: threshold %d\n", *thresh);
301 }
302 cleanup:
303 if (cnh)
304 free (cnh);
305 if (mean)
306 free (mean);
307 return ret;
308 }
309
310
311 /* Implements a Maximum Entropy thresholding method
312 */
313 SANE_Status
sanei_ir_threshold_maxentropy(const SANE_Parameters * params,double * norm_histo,int * thresh)314 sanei_ir_threshold_maxentropy (const SANE_Parameters * params,
315 double * norm_histo, int *thresh)
316 {
317 int ih, it;
318 int threshold;
319 int first_bin;
320 int last_bin;
321 double tot_ent, max_ent; /* entropies */
322 double ent_back, ent_obj;
323 double *P1; /* cumulative normalized histogram */
324 double *P2;
325 SANE_Status ret = SANE_STATUS_NO_MEM;
326
327 DBG (10, "sanei_ir_threshold_maxentropy\n");
328
329 /* Calculate the cumulative normalized histogram */
330 P1 = sanei_ir_accumulate_norm_histo (norm_histo);
331 P2 = malloc (HISTOGRAM_SIZE * sizeof (double));
332 if (!P1 || !P2)
333 {
334 DBG (5, "sanei_ir_threshold_maxentropy: no buffers\n");
335 goto cleanup;
336 }
337
338 for ( ih = 0; ih < HISTOGRAM_SIZE; ih++ )
339 P2[ih] = 1.0 - P1[ih];
340
341 first_bin = 0;
342 for ( ih = 0; ih < HISTOGRAM_SIZE; ih++ )
343 if (P1[ih] != 0)
344 {
345 first_bin = ih;
346 break;
347 }
348 last_bin = HISTOGRAM_SIZE - 1;
349 for ( ih = HISTOGRAM_SIZE - 1; ih >= first_bin; ih-- )
350 if (P2[ih] != 0)
351 {
352 last_bin = ih;
353 break;
354 }
355
356 /* Calculate the total entropy each gray-level
357 * and find the threshold that maximizes it
358 */
359 threshold = INT_MIN;
360 max_ent = DBL_MIN;
361 for ( it = first_bin; it <= last_bin; it++ )
362 {
363 /* Entropy of the background pixels */
364 ent_back = 0.0;
365 for ( ih = 0; ih <= it; ih++ )
366 if (norm_histo[ih] != 0)
367 ent_back -= ( norm_histo[ih] / P1[it] ) * log ( norm_histo[ih] / P1[it] );
368
369 /* Entropy of the object pixels */
370 ent_obj = 0.0;
371 for ( ih = it + 1; ih < HISTOGRAM_SIZE; ih++ )
372 if (norm_histo[ih] != 0)
373 ent_obj -= ( norm_histo[ih] / P2[it] ) * log ( norm_histo[ih] / P2[it] );
374
375 /* Total entropy */
376 tot_ent = ent_back + ent_obj;
377
378 if ( max_ent < tot_ent )
379 {
380 max_ent = tot_ent;
381 threshold = it;
382 }
383 }
384
385 if (threshold == INT_MIN)
386 {
387 DBG (5, "sanei_ir_threshold_maxentropy: no threshold found\n");
388 ret = SANE_STATUS_INVAL;
389 }
390 else
391 {
392 ret = SANE_STATUS_GOOD;
393 if (params->depth > 8)
394 {
395 it = 1 << (params->depth - HISTOGRAM_SHIFT);
396 *thresh = threshold * it + it / 2;
397 }
398 else
399 *thresh = threshold;
400 DBG (10, "sanei_ir_threshold_maxentropy: threshold %d\n", *thresh);
401 }
402
403 cleanup:
404 if (P1)
405 free (P1);
406 if (P2)
407 free (P2);
408 return ret;
409 }
410
411 /* Generate gray scale luminance image from separate R, G, B images
412 */
413 SANE_Status
sanei_ir_RGB_luminance(SANE_Parameters * params,const SANE_Uint ** in_img,SANE_Uint ** out_img)414 sanei_ir_RGB_luminance (SANE_Parameters * params, const SANE_Uint **in_img,
415 SANE_Uint **out_img)
416 {
417 SANE_Uint *outi;
418 int itop, i;
419
420 if ((params->depth < 8) || (params->depth > 16) ||
421 (params->format != SANE_FRAME_GRAY))
422 {
423 DBG (5, "sanei_ir_RGB_luminance: invalid format\n");
424 return SANE_STATUS_UNSUPPORTED;
425 }
426
427 itop = params->pixels_per_line * params->lines;
428 outi = malloc (itop * sizeof(SANE_Uint));
429 if (!outi)
430 {
431 DBG (5, "sanei_ir_RGB_luminance: can not allocate out_img\n");
432 return SANE_STATUS_NO_MEM;
433 }
434
435 for (i = itop; i > 0; i--)
436 *(outi++) = (218 * (int) *(in_img[0]++) +
437 732 * (int) *(in_img[1]++) +
438 74 * (int) *(in_img[2]++)) >> 10;
439 *out_img = outi;
440 return SANE_STATUS_GOOD;
441 }
442
443 /* Convert image from >8 bit depth to an 8 bit image
444 */
445 SANE_Status
sanei_ir_to_8bit(SANE_Parameters * params,const SANE_Uint * in_img,SANE_Parameters * out_params,SANE_Uint ** out_img)446 sanei_ir_to_8bit (SANE_Parameters * params, const SANE_Uint *in_img,
447 SANE_Parameters * out_params, SANE_Uint **out_img)
448 {
449 SANE_Uint *outi;
450 size_t ssize;
451 int i, is;
452
453 if ((params->depth < 8) || (params->depth > 16))
454 {
455 DBG (5, "sanei_ir_to_8bit: invalid format\n");
456 return SANE_STATUS_UNSUPPORTED;
457 }
458 ssize = params->pixels_per_line * params->lines;
459 if (params->format == SANE_FRAME_RGB)
460 ssize *= 3;
461 outi = malloc (ssize * sizeof(SANE_Uint));
462 if (!outi)
463 {
464 DBG (5, "sanei_ir_to_8bit: can not allocate out_img\n");
465 return SANE_STATUS_NO_MEM;
466 }
467
468 if (out_params)
469 {
470 memmove (out_params, params, sizeof(SANE_Parameters));
471 out_params->bytes_per_line = out_params->pixels_per_line;
472 if (params->format == SANE_FRAME_RGB)
473 out_params->bytes_per_line *= 3;
474 out_params->depth = 8;
475 }
476
477 memmove (outi, in_img, ssize * sizeof(SANE_Uint));
478 is = params->depth - 8;
479 for (i = ssize; i > 0; i--) {
480 *outi = *outi >> is, outi += 2;
481 }
482
483 *out_img = outi;
484 return SANE_STATUS_GOOD;
485 }
486
487 /* allocate and initialize logarithmic lookup table
488 */
489 SANE_Status
sanei_ir_ln_table(int len,double ** lut_ln)490 sanei_ir_ln_table (int len, double **lut_ln)
491 {
492 double *llut;
493 int i;
494
495 DBG (10, "sanei_ir_ln_table\n");
496
497 llut = malloc (len * sizeof (double));
498 if (!llut)
499 {
500 DBG (5, "sanei_ir_ln_table: no table\n");
501 return SANE_STATUS_NO_MEM;
502 }
503 llut[0] = 0;
504 llut[1] = 0;
505 for (i = 2; i < len; i++)
506 llut[i] = log ((double) i);
507
508 *lut_ln = llut;
509 return SANE_STATUS_GOOD;
510 }
511
512
513 /* Reduce red spectral overlap from an infrared image plane
514 */
515 SANE_Status
sanei_ir_spectral_clean(const SANE_Parameters * params,double * lut_ln,const SANE_Uint * red_data,SANE_Uint * ir_data)516 sanei_ir_spectral_clean (const SANE_Parameters * params, double *lut_ln,
517 const SANE_Uint *red_data,
518 SANE_Uint *ir_data)
519 {
520 const SANE_Uint *rptr;
521 SANE_Uint *iptr;
522 SANE_Int depth;
523 double *llut;
524 double rval, rsum, rrsum;
525 double risum, rfac, radd;
526 double *norm_histo;
527 int64_t isum;
528 int *calc_buf, *calc_ptr;
529 int ival, imin, imax;
530 int itop, len, ssize;
531 int thresh_low, thresh;
532 int irand, i;
533 SANE_Status status;
534
535 DBG (10, "sanei_ir_spectral_clean\n");
536
537 itop = params->pixels_per_line * params->lines;
538 calc_buf = malloc (itop * sizeof (int)); /* could save this */
539 if (!calc_buf)
540 {
541 DBG (5, "sanei_ir_spectral_clean: no buffer\n");
542 return SANE_STATUS_NO_MEM;
543 }
544
545 depth = params->depth;
546 len = 1 << depth;
547 if (lut_ln)
548 llut = lut_ln;
549 else
550 {
551 status = sanei_ir_ln_table (len, &llut);
552 if (status != SANE_STATUS_GOOD) {
553 free (calc_buf);
554 return status;
555 }
556 }
557
558 /* determine not transparent areas to exclude them later
559 * TODO: this has not been tested for negatives
560 */
561 thresh_low = INT_MAX;
562 status =
563 sanei_ir_create_norm_histogram (params, ir_data, &norm_histo);
564 if (status != SANE_STATUS_GOOD)
565 {
566 DBG (5, "sanei_ir_spectral_clean: no buffer\n");
567 free (calc_buf);
568 return SANE_STATUS_NO_MEM;
569 }
570
571 /* TODO: remember only needed if cropping is not ok */
572 status = sanei_ir_threshold_maxentropy (params, norm_histo, &thresh);
573 if (status == SANE_STATUS_GOOD)
574 thresh_low = thresh;
575 status = sanei_ir_threshold_otsu (params, norm_histo, &thresh);
576 if ((status == SANE_STATUS_GOOD) && (thresh < thresh_low))
577 thresh_low = thresh;
578 status = sanei_ir_threshold_yen (params, norm_histo, &thresh);
579 if ((status == SANE_STATUS_GOOD) && (thresh < thresh_low))
580 thresh_low = thresh;
581 if (thresh_low == INT_MAX)
582 thresh_low = 0;
583 else
584 thresh_low /= 2;
585 DBG (10, "sanei_ir_spectral_clean: low threshold %d\n", thresh_low);
586
587 /* calculate linear regression ired (red) from randomly chosen points */
588 ssize = itop / 2;
589 if (SAMPLE_SIZE < ssize)
590 ssize = SAMPLE_SIZE;
591 isum = 0;
592 rsum = rrsum = risum = 0.0;
593 i = ssize;
594 while (i > 0)
595 {
596 irand = rand () % itop;
597 rval = llut[red_data[irand]];
598 ival = ir_data[irand];
599 if (ival > thresh_low)
600 {
601 isum += ival;
602 rsum += rval;
603 rrsum += rval * rval;
604 risum += rval * (double) ival;
605 i--;
606 }
607 }
608
609 /* "a" in ired = b + a * ln (red) */
610 rfac =
611 ((double) ssize * risum -
612 rsum * (double) isum) / ((double) ssize * rrsum - rsum * rsum);
613 radd = ((double) isum - rfac * rsum) / (double) ssize; /* "b" unused */
614
615 DBG (10, "sanei_ir_spectral_clean: n = %d, ired(red) = %f * ln(red) + %f\n",
616 ssize, rfac, radd);
617
618 /* now calculate ired' = ired - a * ln (red) */
619 imin = INT_MAX;
620 imax = INT_MIN;
621 rptr = red_data;
622 iptr = ir_data;
623 calc_ptr = calc_buf;
624 for (i = itop; i > 0; i--)
625 {
626 ival = *iptr++ - (int) (rfac * llut[*rptr++] + 0.5);
627 if (ival > imax)
628 imax = ival;
629 if (ival < imin)
630 imin = ival;
631 *calc_ptr++ = ival;
632 }
633
634 /* scale the result back into the ired image */
635 calc_ptr = calc_buf;
636 iptr = ir_data;
637 rfac = (double) (len - 1) / (double) (imax - imin);
638 for (i = itop; i > 0; i--)
639 *iptr++ = (double) (*calc_ptr++ - imin) * rfac;
640
641 if (!lut_ln)
642 free (llut);
643 free (calc_buf);
644 free (norm_histo);
645 return SANE_STATUS_GOOD;
646 }
647
648
649 /* Hopefully fast mean filter
650 * JV: what does this do? Remove local mean?
651 */
652 SANE_Status
sanei_ir_filter_mean(const SANE_Parameters * params,const SANE_Uint * in_img,SANE_Uint * out_img,int win_rows,int win_cols)653 sanei_ir_filter_mean (const SANE_Parameters * params,
654 const SANE_Uint *in_img, SANE_Uint *out_img,
655 int win_rows, int win_cols)
656 {
657 const SANE_Uint *src;
658 SANE_Uint *dest;
659 int num_cols, num_rows;
660 int itop, iadd, isub;
661 int ndiv, the_sum;
662 int nrow, ncol;
663 int hwr, hwc;
664 int *sum;
665 int i, j;
666
667 DBG (10, "sanei_ir_filter_mean, window: %d x%d\n", win_rows, win_cols);
668
669 if (((win_rows & 1) == 0) || ((win_cols & 1) == 0))
670 {
671 DBG (5, "sanei_ir_filter_mean: window even sized\n");
672 return SANE_STATUS_INVAL;
673 }
674
675 num_cols = params->pixels_per_line;
676 num_rows = params->lines;
677
678 sum = malloc (num_cols * sizeof (int));
679 if (!sum)
680 {
681 DBG (5, "sanei_ir_filter_mean: no buffer for sums\n");
682 return SANE_STATUS_NO_MEM;
683 }
684 dest = out_img;
685
686 hwr = win_rows / 2; /* half window sizes */
687 hwc = win_cols / 2;
688
689 /* pre-pre calculation */
690 for (j = 0; j < num_cols; j++)
691 {
692 sum[j] = 0;
693 src = in_img + j;
694 for (i = 0; i < hwr; i++)
695 {
696 sum[j] += *src;
697 src += num_cols;
698 }
699 }
700
701 itop = num_rows * num_cols;
702 iadd = hwr * num_cols;
703 isub = (hwr - win_rows) * num_cols;
704 nrow = hwr;
705
706 for (i = 0; i < num_rows; i++)
707 {
708 /* update row sums if possible */
709 if (isub >= 0) /* subtract old row */
710 {
711 nrow--;
712 src = in_img + isub;
713 for (j = 0; j < num_cols; j++)
714 sum[j] -= *src++;
715 }
716 isub += num_cols;
717
718 if (iadd < itop) /* add new row */
719 {
720 nrow++;
721 src = in_img + iadd;
722 for (j = 0; j < num_cols; j++)
723 sum[j] += *src++;
724 }
725 iadd += num_cols;
726
727 /* now we do the image columns using only the precalculated sums */
728
729 the_sum = 0; /* precalculation */
730 for (j = 0; j < hwc; j++)
731 the_sum += sum[j];
732 ncol = hwc;
733
734 /* at the left margin, real index hwc lower */
735 for (j = hwc; j < win_cols; j++)
736 {
737 ncol++;
738 the_sum += sum[j];
739 *dest++ = the_sum / (ncol * nrow);
740 }
741
742 ndiv = ncol * nrow;
743 /* in the middle, real index hwc + 1 higher */
744 for (j = 0; j < num_cols - win_cols; j++)
745 {
746 the_sum -= sum[j];
747 the_sum += sum[j + win_cols];
748 *dest++ = the_sum / ndiv;
749 }
750
751 /* at the right margin, real index hwc + 1 higher */
752 for (j = num_cols - win_cols; j < num_cols - hwc - 1; j++)
753 {
754 ncol--;
755 the_sum -= sum[j]; /* j - hwc - 1 */
756 *dest++ = the_sum / (ncol * nrow);
757 }
758 }
759 free (sum);
760 return SANE_STATUS_GOOD;
761 }
762
763
764 /* Find noise by adaptive thresholding
765 */
766 SANE_Status
sanei_ir_filter_madmean(const SANE_Parameters * params,const SANE_Uint * in_img,SANE_Uint ** out_img,int win_size,int a_val,int b_val)767 sanei_ir_filter_madmean (const SANE_Parameters * params,
768 const SANE_Uint *in_img,
769 SANE_Uint ** out_img, int win_size,
770 int a_val, int b_val)
771 {
772 SANE_Uint *delta_ij, *delta_ptr;
773 SANE_Uint *mad_ij;
774 const SANE_Uint *mad_ptr;
775 SANE_Uint *out_ij, *dest8;
776 double ab_term;
777 int num_rows, num_cols;
778 int threshold, itop;
779 size_t size;
780 int ival, i;
781 int depth;
782 SANE_Status ret = SANE_STATUS_NO_MEM;
783
784 DBG (10, "sanei_ir_filter_madmean\n");
785
786 depth = params->depth;
787 if (depth != 8)
788 {
789 a_val = a_val << (depth - 8);
790 b_val = b_val << (depth - 8);
791 }
792 num_cols = params->pixels_per_line;
793 num_rows = params->lines;
794 itop = num_rows * num_cols;
795 size = itop * sizeof (SANE_Uint);
796 out_ij = malloc (size);
797 delta_ij = malloc (size);
798 mad_ij = malloc (size);
799
800 if (out_ij && delta_ij && mad_ij)
801 {
802 /* get the differences to the local mean */
803 mad_ptr = in_img;
804 if (sanei_ir_filter_mean (params, mad_ptr, delta_ij, win_size, win_size)
805 == SANE_STATUS_GOOD)
806 {
807 delta_ptr = delta_ij;
808 for (i = 0; i < itop; i++)
809 {
810 ival = *mad_ptr++ - *delta_ptr;
811 *delta_ptr++ = abs (ival);
812 }
813 /* make the second filtering window a bit larger */
814 win_size = MAD_WIN2_SIZE(win_size);
815 /* and get the local mean differences */
816 if (sanei_ir_filter_mean
817 (params, delta_ij, mad_ij, win_size,
818 win_size) == SANE_STATUS_GOOD)
819 {
820 mad_ptr = mad_ij;
821 delta_ptr = delta_ij;
822 dest8 = out_ij;
823 /* construct the noise map */
824 ab_term = (b_val - a_val) / (double) b_val;
825 for (i = 0; i < itop; i++)
826 {
827 /* by calculating the threshold */
828 ival = *mad_ptr++;
829 if (ival >= b_val) /* outlier */
830 threshold = a_val;
831 else
832 threshold = a_val + (double) ival *ab_term;
833 /* above threshold is noise, indicated by 0 */
834 if (*delta_ptr++ >= threshold)
835 *dest8++ = 0;
836 else
837 *dest8++ = 255;
838 }
839 *out_img = out_ij;
840 ret = SANE_STATUS_GOOD;
841 }
842 }
843 }
844 else
845 DBG (5, "sanei_ir_filter_madmean: Cannot allocate buffers\n");
846
847 free (mad_ij);
848 free (delta_ij);
849 return ret;
850 }
851
852
853 /* Add dark pixels to mask from static threshold
854 */
855 void
sanei_ir_add_threshold(const SANE_Parameters * params,const SANE_Uint * in_img,SANE_Uint * mask_img,int threshold)856 sanei_ir_add_threshold (const SANE_Parameters * params,
857 const SANE_Uint *in_img,
858 SANE_Uint * mask_img, int threshold)
859 {
860 const SANE_Uint *in_ptr;
861 SANE_Uint *mask_ptr;
862 int itop, i;
863
864 DBG (10, "sanei_ir_add_threshold\n");
865
866 itop = params->pixels_per_line * params->lines;
867 in_ptr = in_img;
868 mask_ptr = mask_img;
869
870 for (i = itop; i > 0; i--)
871 {
872 if (*in_ptr++ <= threshold)
873 *mask_ptr = 0;
874 mask_ptr++;
875 }
876 }
877
878
879 /* Calculate minimal Manhattan distances for an image mask
880 */
881 void
sanei_ir_manhattan_dist(const SANE_Parameters * params,const SANE_Uint * mask_img,unsigned int * dist_map,unsigned int * idx_map,unsigned int erode)882 sanei_ir_manhattan_dist (const SANE_Parameters * params,
883 const SANE_Uint * mask_img, unsigned int *dist_map,
884 unsigned int *idx_map, unsigned int erode)
885 {
886 const SANE_Uint *mask;
887 unsigned int *index, *manhattan;
888 int rows, cols, itop;
889 int i, j;
890
891 DBG (10, "sanei_ir_manhattan_dist\n");
892
893 if (erode != 0)
894 erode = 255;
895
896 /* initialize maps */
897 cols = params->pixels_per_line;
898 rows = params->lines;
899 itop = rows * cols;
900 mask = mask_img;
901 manhattan = dist_map;
902 index = idx_map;
903 for (i = 0; i < itop; i++)
904 {
905 *manhattan++ = *mask++;
906 *index++ = i;
907 }
908
909 /* traverse from top left to bottom right */
910 manhattan = dist_map;
911 index = idx_map;
912 for (i = 0; i < rows; i++)
913 for (j = 0; j < cols; j++)
914 {
915 if (*manhattan == erode)
916 {
917 /* take original, distance = 0, index stays the same */
918 *manhattan = 0;
919 }
920 else
921 {
922 /* assume maximal distance to clean pixel */
923 *manhattan = cols + rows;
924 /* or one further away than pixel to the top */
925 if (i > 0)
926 if (manhattan[-cols] + 1 < *manhattan)
927 {
928 *manhattan = manhattan[-cols] + 1;
929 *index = index[-cols]; /* index follows */
930 }
931 /* or one further away than pixel to the left */
932 if (j > 0)
933 {
934 if (manhattan[-1] + 1 < *manhattan)
935 {
936 *manhattan = manhattan[-1] + 1;
937 *index = index[-1]; /* index follows */
938 }
939 if (manhattan[-1] + 1 == *manhattan)
940 if (rand () % 2 == 0) /* chose index */
941 *index = index[-1];
942 }
943 }
944 manhattan++;
945 index++;
946 }
947
948 /* traverse from bottom right to top left */
949 manhattan = dist_map + itop - 1;
950 index = idx_map + itop - 1;
951 for (i = rows - 1; i >= 0; i--)
952 for (j = cols - 1; j >= 0; j--)
953 {
954 if (i < rows - 1)
955 {
956 /* either what we had on the first pass
957 or one more than the pixel to the bottm */
958 if (manhattan[+cols] + 1 < *manhattan)
959 {
960 *manhattan = manhattan[+cols] + 1;
961 *index = index[+cols]; /* index follows */
962 }
963 if (manhattan[+cols] + 1 == *manhattan)
964 if (rand () % 2 == 0) /* chose index */
965 *index = index[+cols];
966 }
967 if (j < cols - 1)
968 {
969 /* or one more than pixel to the right */
970 if (manhattan[1] + 1 < *manhattan)
971 {
972 *manhattan = manhattan[1] + 1;
973 *index = index[1]; /* index follows */
974 }
975 if (manhattan[1] + 1 == *manhattan)
976 if (rand () % 2 == 0) /* chose index */
977 *index = index[1];
978 }
979 manhattan--;
980 index--;
981 }
982 }
983
984
985 /* dilate or erode a mask image */
986
987 void
sanei_ir_dilate(const SANE_Parameters * params,SANE_Uint * mask_img,unsigned int * dist_map,unsigned int * idx_map,int by)988 sanei_ir_dilate (const SANE_Parameters *params, SANE_Uint *mask_img,
989 unsigned int *dist_map, unsigned int *idx_map, int by)
990 {
991 SANE_Uint *mask;
992 unsigned int *manhattan;
993 unsigned int erode;
994 unsigned int thresh;
995 int i, itop;
996
997 DBG (10, "sanei_ir_dilate\n");
998
999 if (by == 0)
1000 return;
1001 if (by > 0)
1002 {
1003 erode = 0;
1004 thresh = by;
1005 }
1006 else
1007 {
1008 erode = 1;
1009 thresh = -by;
1010 }
1011
1012 itop = params->pixels_per_line * params->lines;
1013 mask = mask_img;
1014 sanei_ir_manhattan_dist (params, mask_img, dist_map, idx_map, erode);
1015
1016 manhattan = dist_map;
1017 for (i = 0; i < itop; i++)
1018 {
1019 if (*manhattan++ <= thresh)
1020 *mask++ = 0;
1021 else
1022 *mask++ = 255;
1023 }
1024
1025 return;
1026 }
1027
1028
1029 /* Suggest cropping for dark margins of positive film
1030 */
1031 void
sanei_ir_find_crop(const SANE_Parameters * params,unsigned int * dist_map,int inner,int * edges)1032 sanei_ir_find_crop (const SANE_Parameters * params,
1033 unsigned int * dist_map, int inner, int * edges)
1034 {
1035 int width = params->pixels_per_line;
1036 int height = params->lines;
1037 uint64_t sum_x, sum_y, n;
1038 int64_t sum_xx, sum_xy;
1039 double a, b, mami;
1040 unsigned int *src;
1041 int off1, off2, inc, wh, i, j;
1042
1043 DBG (10, "sanei_ir_find_crop\n");
1044
1045 /* loop through top, bottom, left, right */
1046 for (j = 0; j < 4; j++)
1047 {
1048 if (j < 2) /* top, bottom */
1049 {
1050 off1 = width / 8; /* only middle 3/4 */
1051 off2 = width - off1;
1052 n = width - 2 * off1;
1053 src = dist_map + off1; /* first row */
1054 inc = 1;
1055 wh = width;
1056 if (j == 1) /* last row */
1057 src += (height - 1) * width;
1058 }
1059 else /* left, right */
1060 {
1061 off1 = height / 8; /* only middle 3/4 */
1062 off2 = height - off1;
1063 n = height - 2 * off1;
1064 src = dist_map + (off1 * width); /* first column */
1065 inc = width;
1066 wh = height;
1067 if (j == 3)
1068 src += width - 1; /* last column */
1069 }
1070
1071 /* calculate linear regression */
1072 sum_x = 0; sum_y = 0;
1073 sum_xx = 0; sum_xy = 0;
1074 for (i = off1; i < off2; i++)
1075 {
1076 sum_x += i;
1077 sum_y += *src;
1078 sum_xx += i * i;
1079 sum_xy += i * (*src);
1080 src += inc;
1081 }
1082 b = ((double) n * (double) sum_xy - (double) sum_x * (double) sum_y)
1083 / ((double) n * (double) sum_xx - (double) sum_x * (double) sum_x);
1084 a = ((double) sum_y - b * (double) sum_x) / (double) n;
1085
1086 DBG (10, "sanei_ir_find_crop: y = %f + %f * x\n", a, b);
1087
1088 /* take maximal/minimal value from either side */
1089 mami = a + b * (wh - 1);
1090 if (inner)
1091 {
1092 if (a > mami)
1093 mami = a;
1094 }
1095 else
1096 {
1097 if (a < mami)
1098 mami = a;
1099 }
1100 edges[j] = mami + 0.5;
1101 }
1102 edges[1] = height - edges[1];
1103 edges[3] = width - edges[3];
1104
1105 DBG (10, "sanei_ir_find_crop: would crop at top: %d, bot: %d, left %d, right %d\n",
1106 edges[0], edges[1], edges[2], edges[3]);
1107
1108 return;
1109 }
1110
1111
1112 /* Dilate clean image parts into dirty ones and smooth
1113 */
1114 SANE_Status
sanei_ir_dilate_mean(const SANE_Parameters * params,SANE_Uint ** in_img,SANE_Uint * mask_img,int dist_max,int expand,int win_size,SANE_Bool smooth,int inner,int * crop)1115 sanei_ir_dilate_mean (const SANE_Parameters * params,
1116 SANE_Uint **in_img,
1117 SANE_Uint * mask_img,
1118 int dist_max, int expand, int win_size,
1119 SANE_Bool smooth, int inner,
1120 int *crop)
1121 {
1122 SANE_Uint *color;
1123 SANE_Uint *plane;
1124 unsigned int *dist_map, *manhattan;
1125 unsigned int *idx_map, *index;
1126 int dist;
1127 int rows, cols;
1128 int k, i, itop;
1129 SANE_Status ret = SANE_STATUS_NO_MEM;
1130
1131 DBG (10, "sanei_ir_dilate_mean(): dist max = %d, expand = %d, win size = %d, smooth = %d, inner = %d\n",
1132 dist_max, expand, win_size, smooth, inner);
1133
1134 cols = params->pixels_per_line;
1135 rows = params->lines;
1136 itop = rows * cols;
1137 idx_map = malloc (itop * sizeof (unsigned int));
1138 dist_map = malloc (itop * sizeof (unsigned int));
1139 plane = malloc (itop * sizeof (SANE_Uint));
1140
1141 if (!idx_map || !dist_map || !plane)
1142 DBG (5, "sanei_ir_dilate_mean: Cannot allocate buffers\n");
1143 else
1144 {
1145 /* expand dirty regions into their half dirty surround*/
1146 if (expand > 0)
1147 sanei_ir_dilate (params, mask_img, dist_map, idx_map, expand);
1148 /* for dirty pixels determine the closest clean ones */
1149 sanei_ir_manhattan_dist (params, mask_img, dist_map, idx_map, 1);
1150
1151 /* use the distance map to find how to crop dark edges */
1152 if (crop)
1153 sanei_ir_find_crop (params, dist_map, inner, crop);
1154
1155 /* replace dirty pixels */
1156 for (k = 0; k < 3; k++)
1157 {
1158 manhattan = dist_map;
1159 index = idx_map;
1160 color = in_img[k];
1161 /* first replacement */
1162 for (i = 0; i < itop; i++)
1163 {
1164 dist = *manhattan++;
1165 if ((dist != 0) && (dist <= dist_max))
1166 color[i] = color[index[i]];
1167 }
1168 /* adapt pixels to their new surround and
1169 * smooth the whole image or the replaced pixels only */
1170 ret =
1171 sanei_ir_filter_mean (params, color, plane, win_size, win_size);
1172 if (ret != SANE_STATUS_GOOD)
1173 break;
1174 else
1175 if (smooth)
1176 {
1177 /* a second mean results in triangular blur */
1178 DBG (10, "sanei_ir_dilate_mean(): smoothing whole image\n");
1179 ret =
1180 sanei_ir_filter_mean (params, plane, color, win_size,
1181 win_size);
1182 if (ret != SANE_STATUS_GOOD)
1183 break;
1184 }
1185 else
1186 {
1187 /* replace with smoothened pixels only */
1188 DBG (10, "sanei_ir_dilate_mean(): smoothing replaced pixels only\n");
1189 manhattan = dist_map;
1190 for (i = 0; i < itop; i++)
1191 {
1192 dist = *manhattan++;
1193 if ((dist != 0) && (dist <= dist_max))
1194 color[i] = plane[i];
1195 }
1196 }
1197 }
1198 }
1199 free (plane);
1200 free (dist_map);
1201 free (idx_map);
1202
1203 return ret;
1204 }
1205