1 /** @file sanei_ir.h
2  *
3  * This file provides an interface to the
4  * sanei_ir functions for utilizing the infrared plane
5  *
6  * Copyright (C) 2012 Michael Rickmann <mrickma@gwdg.de>
7  *
8  * This file is part of the SANE package.
9  *
10  * Essentially three things have to be done:
11  * - 1) reduce red spectral overlap from the infrared (ired) plane
12  * - 2) find the dirt
13  * - 3) replace the dirt
14  *
15  * - 1) is mainly addressed by sanei_ir_spectral_clean
16  * - 2) by sanei_ir_filter_madmean
17  * - 3) by sanei_ir_dilate_mean
18  */
19 
20 
21 #ifndef SANEI_IR_H
22 #define SANEI_IR_H
23 
24 #include <stdint.h>
25 
26 #define SAMPLE_SIZE	40000				/**< maximal for random sampling */
27 
28 #define HISTOGRAM_SHIFT	8				/**< standard histogram size */
29 #define HISTOGRAM_SIZE	(1 << HISTOGRAM_SHIFT)
30 
31 #define SAFE_LOG(x) ( ((x) > 0.0) ? log ((x)) : (0.0) )	/**< define log (0) = 0 */
32 
33 #define MAD_WIN2_SIZE(x) ( (((x) * 4) / 3) | 1 )	/**< MAD filter: 2nd window size */
34 
35 typedef uint16_t SANE_Uint;
36 
37 /**
38  * @brief Pointer to access values of different bit depths
39  */
40 typedef union
41 {
42   uint8_t *b8;			/**< <= 8 bits */
43   uint16_t *b16;		/**< > 8, <= 16 bits */
44 }
45 SANEI_IR_bufptr;
46 
47 
48 /** Initialize sanei_ir.
49  *
50  * Call this before any other sanei_ir function.
51  */
52 extern void sanei_ir_init (void);
53 
54 /**
55  * @brief Create the normalized histogram of a grayscale image
56  *
57  * @param[in] params describes image
58  * @param[in] img_data image pointer { grayscale }
59  * @param[out] histogram an array of double with histogram
60  *
61  * @return
62  * - SANE_STATUS_GOOD - success
63  * - SANE_STATUS_NO_MEM - if out of memory
64  *
65  * @note
66  * histogram has to be freed by calling routine
67  */
68 extern SANE_Status
69 sanei_ir_create_norm_histogram (const SANE_Parameters * params,
70                            const SANE_Uint *img_data,
71                            double ** histogram);
72 
73 /**
74  * @brief Implements Yen's thresholding method
75  *
76  * @param[in] params describes image
77  * @param[in] norm_histo points to a normalized histogram
78  * @param[out] thresh found threshold
79  *
80  * @return
81  * - SANE_STATUS_GOOD - success
82  * - SANE_STATUS_NO_MEM - if out of memory
83  *
84  * -# Yen J.C., Chang F.J., and Chang S. (1995) "A New Criterion
85  *       for Automatic Multilevel Thresholding" IEEE Trans. on Image
86  *       Processing, 4(3): 370-378
87  * -# Sezgin M. and Sankur B. (2004) "Survey over Image Thresholding
88  *       Techniques and Quantitative Performance Evaluation" Journal of
89  *       Electronic Imaging, 13(1): 146-165
90  * -# M. Emre Celebi, 06.15.2007, fourier_0.8,
91  *       http://sourceforge.net/projects/fourier-ipal/
92  * -# ImageJ Multithresholder plugin,
93  *       http://rsbweb.nih.gov/ij/plugins/download/AutoThresholder.java
94  */
95 extern SANE_Status
96 sanei_ir_threshold_yen (const SANE_Parameters * params,
97                          double * norm_histo, int *thresh);
98 
99 /**
100  * @brief Implements Otsu's thresholding method
101  *
102  * @param[in] params describes image
103  * @param[in] norm_histo points to a normalized histogram
104  * @param[out] thresh found threshold
105  *
106  * @return
107  * - SANE_STATUS_GOOD - success
108  * - SANE_STATUS_NO_MEM - if out of memory
109  *
110  * -# Otsu N. (1979) "A Threshold Selection Method from Gray Level Histograms"
111  *      IEEE Trans. on Systems, Man and Cybernetics, 9(1): 62-66
112  * -# M. Emre Celebi, 06.15.2007, fourier_0.8
113  *      http://sourceforge.net/projects/fourier-ipal/
114  */
115 extern SANE_Status
116 sanei_ir_threshold_otsu (const SANE_Parameters * params,
117                           double * norm_histo, int *thresh);
118 
119 /**
120  * @brief Implements a Maximum Entropy thresholding method
121  *
122  * @param[in] params describes image
123  * @param[in] norm_histo points to a normalized histogram
124  * @param[out] thresh found threshold
125  *
126  * @return
127  * - SANE_STATUS_GOOD - success
128  * - SANE_STATUS_NO_MEM - if out of memory
129  *
130  * -# Kapur J.N., Sahoo P.K., and Wong A.K.C. (1985) "A New Method for
131  *      Gray-Level Picture Thresholding Using the Entropy of the Histogram"
132  *      Graphical Models and Image Processing, 29(3): 273-285
133  * -# M. Emre Celebi, 06.15.2007, fourier_0.8
134  *      http://sourceforge.net/projects/fourier-ipal/
135  * -# ImageJ Multithresholder plugin,
136  *       http://rsbweb.nih.gov/ij/plugins/download/AutoThresholder.java
137  */
138 extern SANE_Status
139 sanei_ir_threshold_maxentropy (const SANE_Parameters * params,
140                                double * norm_histo, int *thresh);
141 
142 /**
143  * @brief  Generate gray scale luminance image from separate R, G, B images
144  *
145  * @param      params points to image description
146  * @param[in]  in_img pointer to at least 3 planes of image data
147  * @param[out] out_img newly allocated image
148  *
149  * @return
150  * - SANE_STATUS_GOOD - success
151  * - SANE_STATUS_NO_MEM - if out of memory
152  * - SANE_STATUS_UNSUPPORTED - wrong input bit depth
153  *
154  * @note out_img has to be freed by the calling routine.
155  * @note on input params describe a single color plane,
156  *       on output params are updated if image depth is scaled
157  */
158 SANE_Status
159 sanei_ir_RGB_luminance (SANE_Parameters * params, const SANE_Uint **in_img,
160                        SANE_Uint **out_img);
161 
162 /**
163  * @brief Convert image from >8 bit depth to an 8 bit image.
164  *
165  * @param[in]  params pimage description
166  * @param[in]  in_img points to input image data
167  * @param[out] out_params if != NULL
168  *                        receives description of new image
169  * @param[out] out_img newly allocated 8-bit image
170  *
171  * @return
172  * - SANE_STATUS_GOOD - success
173  * - SANE_STATUS_NO_MEM - if out of memory
174  * - SANE_STATUS_UNSUPPORTED - wrong input bit depth
175  *
176  * @note
177  * out_img has to be freed by the calling routine,
178  */
179 
180 extern SANE_Status
181 sanei_ir_to_8bit (SANE_Parameters * params, const SANE_Uint *in_img,
182                  SANE_Parameters * out_params, SANE_Uint **out_img);
183 
184 /**
185  * @brief Allocate and initialize logarithmic lookup table
186  *
187  * @param[in]  len length of table, usually 1 << depth
188  * @param[out] lut_ln address of pointer to allocated table
189  *
190  * @return
191  * - SANE_STATUS_GOOD - success
192  * - SANE_STATUS_NO_MEM - if out of memory
193  *
194  * @note natural logarithms are provided
195  */
196 SANE_Status sanei_ir_ln_table (int len, double **lut_ln);
197 
198 /**
199  * @brief Reduces red spectral overlap from an infrared image plane
200  *
201  * @param[in]  params pointer to image description
202  * @param[in]  lut_ln pointer lookup table
203  *             if NULL it is dynamically handled
204  * @param[in]  red_data pointer to red image plane
205  * @param      ir_data pointer to ir image plane
206  *
207  * @return
208  * - SANE_STATUS_GOOD - success
209  * - SANE_STATUS_NO_MEM - if out of memory
210  *
211  * This routine is based on the observation that the relation between the infrared value
212  * ired and the red value red of an image point can be described by ired = b + a * ln (red).
213  * First points are randomly sampled to calculate the linear regression coefficient a.
214  * Then ired' = ired - a  * ln (red) is calculated for each pixel. Finally, the ir' image
215  * is scaled between 0 and maximal value. For the logarithms a lookup table is used.
216  * Negative films show very little spectral overlap but positive film usually has to be
217  * cleaned. As we do a statistical measure of the film here dark margins and lumps of
218  * dirt have to be excluded.
219  *
220  * @note original ired data are replaced by the cleaned ones
221 */
222 extern SANE_Status
223 sanei_ir_spectral_clean (const SANE_Parameters * params, double *lut_ln,
224 			const SANE_Uint *red_data,
225 			SANE_Uint *ir_data);
226 
227 /**
228  * @brief Optimized mean filter
229  *
230  * @param[in]  params pointer to image description
231  * @param[in]  in_img Pointer to grey scale image data
232  * @param[out] out_img Pointer to grey scale image data
233  * @param[in]  win_rows Height of filtering window, odd
234  * @param[in]  win_cols Width of filtering window, odd
235  *
236  * @return
237  * - SANE_STATUS_GOOD - success
238  * - SANE_STATUS_NO_MEM - if out of memory
239  * - SANE_STATUS_INVAL - wrong window size
240  *
241  * @note At the image margins the size of the filtering window
242  *       is adapted. So there is no need to pad the image.
243  * @note Memory for the output image has to be allocated before
244  */
245 extern SANE_Status
246 sanei_ir_filter_mean (const SANE_Parameters * params,
247 		      const SANE_Uint *in_img, SANE_Uint *out_img,
248 		      int win_rows, int win_cols);
249 
250 
251 /**
252  * @brief Find noise by adaptive thresholding
253  *
254  * @param[in]  params pointer to image description
255  * @param[in]  in_img pointer to grey scale image
256  * @param[out] out_img address of pointer to newly allocated binary image
257  * @param[in] win_size Size of filtering window
258  * @param[in]  a_val Parameter, below is definitely clean
259  * @param[in]  b_val Parameter, above is definitely noisy
260  *
261  * @return
262  * - SANE_STATUS_GOOD - success
263  * - SANE_STATUS_NO_MEM - if out of memory
264  *
265  * This routine follows the concept of Crnojevic's MAD (median of the absolute deviations
266  * from the median) filter. The first median filter step is replaced with a mean filter.
267  * The dirty pixels which we wish to remove are always darker than the real signal. But
268  * at high resolutions the scanner may generate some noise and the ired cleaning step can
269  * reverse things. So a maximum filter will not do.
270  * The second median is replaced by a mean filter to reduce computation time. In spite of
271  * these changes Crnojevic's recommendations for the choice of the parameters "a" and "b"
272  * are still valid when scaled to the color depth.
273  *
274  * @reco Crnojevic recommends 10 < a_val < 30 and 50 < b_val < 100 for 8 bit color depth
275  *
276  * @note a_val, b_val are scaled by the routine according to bit depth
277  * @note "0" in the mask output is regarded "dirty", 255 "clean"
278  *
279  * -# Crnojevic V. (2005) "Impulse Noise Filter with Adaptive Mad-Based Threshold"
280  *      Proc. of the IEEE Int. Conf. on Image Processing, 3: 337-340
281  */
282 extern SANE_Status
283 sanei_ir_filter_madmean (const SANE_Parameters * params,
284 			 const SANE_Uint *in_img,
285 			 SANE_Uint ** out_img, int win_size,
286 			 int a_val, int b_val);
287 
288 
289 /**
290  * @brief Add dark pixels to mask from static threshold
291  *
292  * @param[in]  params pointer to image description
293  * @param[in]  in_img pointer to grey scale image
294  * @param      mask_img pointer to binary image (0, 255)
295  * @param[in]  threshold below which the pixel is set 0
296  */
297 void
298 sanei_ir_add_threshold (const SANE_Parameters * params,
299 			const SANE_Uint *in_img,
300 			SANE_Uint * mask_img, int threshold);
301 
302 
303 /**
304  * @brief Calculates minimal Manhattan distances for an image mask
305  *
306  * @param[in]  params pointer to image description
307  * @param[in]  mask_img pointer to binary image (0, 255)
308  * @param[out] dist_map integer pointer to map of closest distances
309  * @param[out] idx_map integer pointer to indices of closest pixels
310  * @param[in]  erode == 0: closest pixel has value 0, != 0: is 255
311  *
312  * manhattan_dist takes a mask image consisting of 0 or 255  values. Given that
313  * a 0 represents a dirty pixel and erode != 0, manhattan_dist will calculate the
314  * shortest distance to a clean (255) pixel and record which pixel that was so
315  * that the clean parts of the image can be dilated into the dirty ones. Thresholding
316  * can be done on the distance. Conversely, if erode == 0 the distance of a clean
317  * pixel to the closest dirty one is calculated which can be used to dilate the mask.
318  *
319  * @ref extended and C version of
320  *      http://ostermiller.org/dilate_and_erode.html
321  */
322 void
323 sanei_ir_manhattan_dist (const SANE_Parameters * params,
324 			const SANE_Uint * mask_img, unsigned int *dist_map,
325 			unsigned int *idx_map, unsigned int erode);
326 
327 
328 /**
329  * @brief Dilate or erode a mask image
330  *
331  * @param[in]  params pointer to image description
332  * @param      mask_img pointer to binary image (0, 255)
333  * @param      dist_map integer pointer to map of closest distances
334  * @param      idx_map integer pointer to indices of closest pixels
335  * @param[in]  by number of pixels, > 0 dilate, < 0 erode
336  *
337  * @note by > 0 will enlarge the 0 valued area
338  */
339 void
340 sanei_ir_dilate (const SANE_Parameters * params, SANE_Uint * mask_img,
341 		unsigned int *dist_map, unsigned int *idx_map, int by);
342 
343 /**
344  * @brief Suggest cropping for dark margins of positive film
345  *
346  * @param[in]  params pointer to image description
347  * @param[in]  dist_map integer pointer to map of closest distances
348  * @param[in]  inner crop within (!=0) or outside (==0) the image's edges
349  * @param[out] edges pointer to array holding top, bottom, left
350  *             and right edges
351  *
352  * The distance map as calculated by sanei_ir_manhattan_dist contains
353  * distances to the next clean pixel. Dark margins are detected as dirt.
354  * So the first/last rows/columns tell us how to crop. This is rather
355  * fast if the distance map has been calculated anyhow.
356  */
357 void
358 sanei_ir_find_crop (const SANE_Parameters * params,
359                     unsigned int * dist_map, int inner, int * edges);
360 
361 /**
362  * @brief Dilate clean image parts into dirty ones and smooth int inner,
363  *
364  * @param[in] params pointer to image description
365  * @param     in_img array of pointers to color planes of image
366  * @param[in] mask_img pointer to dirt mask image
367  * @param[in] dist_max threshold up to which dilation is done
368  * @param[in] expand the dirt mask before replacing the pixels
369  * @param[in] win_size size of adaptive mean filtering window
370  * @param[in] smooth triangular filter whole image for grain removal
371  * @param[in] inner find crop within or outside the image's edges
372  * @param[out] crop array of 4 integers, if non-NULL, top, bottom,
373  *             left and right values for cropping are returned.
374  *
375  * @return
376  * - SANE_STATUS_GOOD - success
377  * - SANE_STATUS_NO_MEM - if out of memory
378  *
379  * The main purpose of this routine is to replace dirty pixels.
380  * As spin-off it obtains half of what is needed for film grain
381  * smoothening and most of how to crop positive film.
382  * To speed things up these functions are also implemented.
383  */
384 SANE_Status
385 sanei_ir_dilate_mean (const SANE_Parameters * params,
386                       SANE_Uint **in_img,
387                       SANE_Uint *mask_img,
388                       int dist_max, int expand, int win_size,
389                       SANE_Bool smooth, int inner,
390                       int *crop);
391 
392 
393 #endif /* not SANEI_IR_H */
394