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