summaryrefslogtreecommitdiff
path: root/include/sane/sanei_ir.h
blob: aaca524139e047f101e5b7c8e6cc70e799635994 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
/** @file sanei_ir.h
 *
 * This file provides an interface to the
 * sanei_ir functions for utilizing the infrared plane
 *
 * Copyright (C) 2012 Michael Rickmann <mrickma@gwdg.de>
 *
 * This file is part of the SANE package.
 *
 * Essentially three things have to be done:
 * - 1) reduce red spectral overlap from the infrared (ired) plane
 * - 2) find the dirt
 * - 3) replace the dirt
 *
 * - 1) is mainly adressed by sanei_ir_spectral_clean
 * - 2) by sanei_ir_filter_madmean
 * - 3) by sanei_ir_dilate_mean
 */


#ifndef SANEI_IR_H
#define SANEI_IR_H

#include <stdint.h>

#define SAMPLE_SIZE	40000				/**< maximal for random sampling */

#define HISTOGRAM_SHIFT	8				/**< standard histogram size */
#define HISTOGRAM_SIZE	(1 << HISTOGRAM_SHIFT)

#define SAFE_LOG(x) ( ((x) > 0.0) ? log ((x)) : (0.0) )	/**< define log (0) = 0 */

#define MAD_WIN2_SIZE(x) ( (((x) * 4) / 3) | 1 )	/**< MAD filter: 2nd window size */

typedef uint16_t SANE_Uint;

/**
 * @brief Pointer to access values of different bit depths
 */
typedef union
{
  uint8_t *b8;			/**< <= 8 bits */
  uint16_t *b16;		/**< > 8, <= 16 bits */
}
SANEI_IR_bufptr;


/** Initialize sanei_ir.
 *
 * Call this before any other sanei_ir function.
 */
extern void sanei_ir_init (void);

/**
 * @brief Create the normalized histogram of a grayscale image
 *
 * @param[in] params describes image
 * @param[in] img_data image pointer { grayscale }
 * @param[out] histogram an array of double with histogram
 *
 * @return
 * - SANE_STATUS_GOOD - success
 * - SANE_STATUS_NO_MEM - if out of memory
 *
 * @note
 * histogram has to be freed by calling routine
 */
extern SANE_Status
sanei_ir_create_norm_histogram (const SANE_Parameters * params,
                           const SANE_Uint *img_data,
                           double ** histogram);

/**
 * @brief Implements Yen's thresholding method
 *
 * @param[in] params describes image
 * @param[in] norm_histo points to a normalized histogram
 * @param[out] thresh found threshold
 *
 * @return
 * - SANE_STATUS_GOOD - success
 * - SANE_STATUS_NO_MEM - if out of memory
 *
 * -# Yen J.C., Chang F.J., and Chang S. (1995) "A New Criterion
 *       for Automatic Multilevel Thresholding" IEEE Trans. on Image
 *       Processing, 4(3): 370-378
 * -# Sezgin M. and Sankur B. (2004) "Survey over Image Thresholding
 *       Techniques and Quantitative Performance Evaluation" Journal of
 *       Electronic Imaging, 13(1): 146-165
 * -# M. Emre Celebi, 06.15.2007, fourier_0.8,
 *       http://sourceforge.net/projects/fourier-ipal/
 * -# ImageJ Multithresholder plugin,
 *       http://rsbweb.nih.gov/ij/plugins/download/AutoThresholder.java
 */
extern SANE_Status
sanei_ir_threshold_yen (const SANE_Parameters * params,
                         double * norm_histo, int *thresh);

/**
 * @brief Implements Otsu's thresholding method
 *
 * @param[in] params describes image
 * @param[in] norm_histo points to a normalized histogram
 * @param[out] thresh found threshold
 *
 * @return
 * - SANE_STATUS_GOOD - success
 * - SANE_STATUS_NO_MEM - if out of memory
 *
 * -# Otsu N. (1979) "A Threshold Selection Method from Gray Level Histograms"
 *      IEEE Trans. on Systems, Man and Cybernetics, 9(1): 62-66
 * -# M. Emre Celebi, 06.15.2007, fourier_0.8
 *      http://sourceforge.net/projects/fourier-ipal/
 */
extern SANE_Status
sanei_ir_threshold_otsu (const SANE_Parameters * params,
                          double * norm_histo, int *thresh);

/**
 * @brief Implements a Maximum Entropy thresholding method
 *
 * @param[in] params describes image
 * @param[in] norm_histo points to a normalized histogram
 * @param[out] thresh found threshold
 *
 * @return
 * - SANE_STATUS_GOOD - success
 * - SANE_STATUS_NO_MEM - if out of memory
 *
 * -# Kapur J.N., Sahoo P.K., and Wong A.K.C. (1985) "A New Method for
 *      Gray-Level Picture Thresholding Using the Entropy of the Histogram"
 *      Graphical Models and Image Processing, 29(3): 273-285
 * -# M. Emre Celebi, 06.15.2007, fourier_0.8
 *      http://sourceforge.net/projects/fourier-ipal/
 * -# ImageJ Multithresholder plugin,
 *       http://rsbweb.nih.gov/ij/plugins/download/AutoThresholder.java
 */
extern SANE_Status
sanei_ir_threshold_maxentropy (const SANE_Parameters * params,
                               double * norm_histo, int *thresh);

/**
 * @brief  Generate gray scale luminance image from separate R, G, B images
 *
 * @param      params points to image description
 * @param[in]  in_img pointer to at least 3 planes of image data
 * @param[out] out_img newly allocated image
 *
 * @return
 * - SANE_STATUS_GOOD - success
 * - SANE_STATUS_NO_MEM - if out of memory
 * - SANE_STATUS_UNSUPPORTED - wrong input bit depth
 *
 * @note out_img has to be freed by the calling routine.
 * @note on input params describe a single color plane,
 *       on output params are updated if image depth is scaled
 */
SANE_Status
sanei_ir_RGB_luminance (SANE_Parameters * params, const SANE_Uint **in_img,
                       SANE_Uint **out_img);

/**
 * @brief Convert image from >8 bit depth to an 8 bit image.
 *
 * @param[in]  params pimage description
 * @param[in]  in_img points to input image data
 * @param[out] out_params if != NULL
 *                        receives description of new image
 * @param[out] out_img newly allocated 8-bit image
 *
 * @return
 * - SANE_STATUS_GOOD - success
 * - SANE_STATUS_NO_MEM - if out of memory
 * - SANE_STATUS_UNSUPPORTED - wrong input bit depth
 *
 * @note
 * out_img has to be freed by the calling routine,
 */

extern SANE_Status
sanei_ir_to_8bit (SANE_Parameters * params, const SANE_Uint *in_img,
                 SANE_Parameters * out_params, SANE_Uint **out_img);

/**
 * @brief Allocate and initialize logarithmic lookup table
 *
 * @param[in]  length of table, usually 1 << depth
 * @param[out] lut_ln adress of pointer to allocated table
 *
 * @return
 * - SANE_STATUS_GOOD - success
 * - SANE_STATUS_NO_MEM - if out of memory
 *
 * @note natural logarithms are provided
 */
SANE_Status sanei_ir_ln_table (int len, double **lut_ln);

/**
 * @brief Reduces red spectral overlap from an infrared image plane
 *
 * @param[in]  params pointer to image description
 * @param[in]  lut_ln pointer lookup table
 *             if NULL it is dynamically handled
 * @param[in]  red_data pointer to red image plane
 * @param      ired_data pointer to ired image plane
 *
 * @return
 * - SANE_STATUS_GOOD - success
 * - SANE_STATUS_NO_MEM - if out of memory
 *
 * This routine is based on the observation that the relation beween the infrared value
 * ired and the red value red of an image point can be described by ired = b + a * ln (red).
 * First points are randomly sampled to calculate the linear regression coefficent a.
 * Then ired' = ired - a  * ln (red) is calculated for each pixel. Finally, the ir' image
 * is scaled between 0 and maximal value. For the logarithms a lookup table is used.
 * Negative films show very little spectral overlap but positive film usually has to be
 * cleaned. As we do a statistical measure of the film here dark margins and lumps of
 * dirt have to be excluded.
 *
 * @note original ired data are replaced by the cleaned ones
*/
extern SANE_Status
sanei_ir_spectral_clean (const SANE_Parameters * params, double *lut_ln,
			const SANE_Uint *red_data,
			SANE_Uint *ir_data);

/**
 * @brief Optimized mean filter
 *
 * @param[in]  params pointer to image description
 * @param[in]  in_img Pointer to grey scale image data
 * @param[out] out_img Pointer to grey scale image data
 * @param[in]  win_rows Height of filtering window, odd
 * @param[in]  win_cols Width of filtering window, odd
 *
 * @return
 * - SANE_STATUS_GOOD - success
 * - SANE_STATUS_NO_MEM - if out of memory
 * - SANE_STATUS_INVAL - wrong window size
 *
 * @note At the image margins the size of the filtering window
 *       is adapted. So there is no need to pad the image.
 * @note Memory for the output image has to be allocated before
 */
extern 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);


/**
 * @brief Find noise by adaptive thresholding
 *
 * @param[in]  params pointer to image description
 * @param[in]  in_img pointer to grey scale image
 * @param[out] out_img address of pointer to newly allocated binary image
 * @param[in] win_size Size of filtering window
 * @param[in]  a_val Parameter, below is definetly clean
 * @param[in]  b_val Parameter, above is definetly noisy
 *
 * @return
 * - SANE_STATUS_GOOD - success
 * - SANE_STATUS_NO_MEM - if out of memory
 *
 * This routine follows the concept of Crnojevic's MAD (median of the absolute deviations
 * from the median) filter. The first median filter step is replaced with a mean filter.
 * The dirty pixels which we wish to remove are always darker than the real signal. But
 * at high resolutions the scanner may generate some noise and the ired cleaning step can
 * reverse things. So a maximum filter will not do.
 * The second median is replaced by a mean filter to reduce computation time. Inspite of
 * these changes Crnojevic's recommendations for the choice of the parameters "a" and "b"
 * are still valid when scaled to the color depth.
 *
 * @reco Crnojevic recommends 10 < a_val < 30 and 50 < b_val < 100 for 8 bit color depth
 *
 * @note a_val, b_val are scaled by the routine according to bit depth
 * @note "0" in the mask output is regarded "dirty", 255 "clean"
 *
 * -# Crnojevic V. (2005) "Impulse Noise Filter with Adaptive Mad-Based Threshold"
 *      Proc. of the IEEE Int. Conf. on Image Processing, 3: 337-340
 */
extern 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);


/**
 * @brief Add dark pixels to mask from static threshold
 *
 * @param[in]  params pointer to image description
 * @param[in]  in_img pointer to grey scale image
 * @param      mask_img pointer to binary image (0, 255)
 * @param[in]  threshold below which the pixel is set 0
 */
void
sanei_ir_add_threshold (const SANE_Parameters * params,
			const SANE_Uint *in_img,
			SANE_Uint * mask_img, int threshold);


/**
 * @brief Calculates minimal Manhattan distances for an image mask
 *
 * @param[in]  params pointer to image description
 * @param[in]  mask_img pointer to binary image (0, 255)
 * @param[out] dist_map integer pointer to map of closest distances
 * @param[out] idx_map integer pointer to indices of closest pixels
 * @param[in]  erode == 0: closest pixel has value 0, != 0: is 255
 *
 * manhattan_dist takes a mask image consisting of 0 or 255  values. Given that
 * a 0 represents a dirty pixel and erode != 0, manhattan_dist will calculate the
 * shortest distance to a clean (255) pixel and record which pixel that was so
 * that the clean parts of the image can be dilated into the dirty ones. Thresholding
 * can be done on the distance. Conversely, if erode == 0 the distance of a clean
 * pixel to the closest dirty one is calculated which can be used to dilate the mask.
 *
 * @ref extended and C version of
 *      http://ostermiller.org/dilate_and_erode.html
 */
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);


/**
 * @brief Dilate or erode a mask image
 *
 * @param[in]  params pointer to image description
 * @param      mask_img pointer to binary image (0, 255)
 * @param      dist_map integer pointer to map of closest distances
 * @param      idx_map integer pointer to indices of closest pixels
 * @param[in]  by number of pixels, > 0 dilate, < 0 erode
 *
 * @note by > 0 will enlarge the 0 valued area
 */
void
sanei_ir_dilate (const SANE_Parameters * params, SANE_Uint * mask_img,
		unsigned int *dist_map, unsigned int *idx_map, int by);

/**
 * @brief Suggest cropping for dark margins of positive film
 *
 * @param[in]  params pointer to image description
 * @param[in]  dist_map integer pointer to map of closest distances
 * @param[in]  inner crop within (!=0) or outside (==0) the image's edges
 * @param[out] edges pointer to array holding top, bottom, left
 *             and right edges
 *
 * The distance map as calculated by sanei_ir_manhattan_dist contains
 * distances to the next clean pixel. Dark margins are detected as dirt.
 * So the first/last rows/columns tell us how to crop. This is rather
 * fast if the distance map has been calculated anyhow.
 */
void
sanei_ir_find_crop (const SANE_Parameters * params,
                    unsigned int * dist_map, int inner, int * edges);

/**
 * @brief Dilate clean image parts into dirty ones and smooth int inner,
 *
 * @param[in] params pointer to image description
 * @param     in_img array of pointers to color planes of image
 * @param[in] mask_img pointer to dirt mask image
 * @param[in] dist_max threshold up to which dilation is done
 * @param[in] expand the dirt mask before replacing the pixels
 * @param[in] win_size size of adaptive mean filtering window
 * @param[in] smooth triangular filter whole image for grain removal
 * @param[in] inner find crop within or outside the image's edges
 * @param[out] crop array of 4 integers, if non-NULL, top, bottom,
 *             left and right values for cropping are returned.
 *
 * @return
 * - SANE_STATUS_GOOD - success
 * - SANE_STATUS_NO_MEM - if out of memory
 *
 * The main purpose of this routine is to replace dirty pixels.
 * As spin-off it obtains half of what is needed for film grain
 * smoothening and most of how to crop positive film.
 * To speed things up these functions are also implemented.
 */
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);


#endif /* not SANEI_IR_H */