Leptonica 1.68
C Image Processing Library
|
00001 /*====================================================================* 00002 - Copyright (C) 2001 Leptonica. All rights reserved. 00003 - This software is distributed in the hope that it will be 00004 - useful, but with NO WARRANTY OF ANY KIND. 00005 - No author or distributor accepts responsibility to anyone for the 00006 - consequences of using this software, or for whether it serves any 00007 - particular purpose or works at all, unless he or she says so in 00008 - writing. Everyone is granted permission to copy, modify and 00009 - redistribute this source code, for commercial or non-commercial 00010 - purposes, with the following restrictions: (1) the origin of this 00011 - source code must not be misrepresented; (2) modified versions must 00012 - be plainly marked as such; and (3) this notice may not be removed 00013 - or altered from any source or modified source distribution. 00014 *====================================================================*/ 00015 00016 /* 00017 * adaptmap.c 00018 * 00019 * =================================================================== 00020 * Image binarization algorithms are found in: 00021 * grayquant.c: standard, simple, general grayscale quantization 00022 * adaptmap.c: local adaptive; mostly gray-to-gray in preparation 00023 * for binarization 00024 * binarize.c: special binarization methods, locally adaptive. 00025 * =================================================================== 00026 * 00027 * Adaptive background normalization (top-level functions) 00028 * PIX *pixBackgroundNormSimple() 8 and 32 bpp 00029 * PIX *pixBackgroundNorm() 8 and 32 bpp 00030 * PIX *pixBackgroundNormMorph() 8 and 32 bpp 00031 * 00032 * Arrays of inverted background values for normalization (16 bpp) 00033 * l_int32 pixBackgroundNormGrayArray() 8 bpp input 00034 * l_int32 pixBackgroundNormRGBArrays() 32 bpp input 00035 * l_int32 pixBackgroundNormGrayArrayMorph() 8 bpp input 00036 * l_int32 pixBackgroundNormRGBArraysMorph() 32 bpp input 00037 * 00038 * Measurement of local background 00039 * l_int32 pixGetBackgroundGrayMap() 8 bpp 00040 * l_int32 pixGetBackgroundRGBMap() 32 bpp 00041 * l_int32 pixGetBackgroundGrayMapMorph() 8 bpp 00042 * l_int32 pixGetBackgroundRGBMapMorph() 32 bpp 00043 * l_int32 pixFillMapHoles() 00044 * PIX *pixExtendByReplication() 8 bpp 00045 * l_int32 pixSmoothConnectedRegions() 8 bpp 00046 * 00047 * Measurement of local foreground 00048 * l_int32 pixGetForegroundGrayMap() 8 bpp 00049 * 00050 * Generate inverted background map for each component 00051 * PIX *pixGetInvBackgroundMap() 16 bpp 00052 * 00053 * Apply inverse background map to image 00054 * PIX *pixApplyInvBackgroundGrayMap() 8 bpp 00055 * PIX *pixApplyInvBackgroundRGBMap() 32 bpp 00056 * 00057 * Apply variable map 00058 * PIX *pixApplyVariableGrayMap() 8 bpp 00059 * 00060 * Non-adaptive (global) mapping 00061 * PIX *pixGlobalNormRGB() 32 bpp or cmapped 00062 * PIX *pixGlobalNormNoSatRGB() 32 bpp 00063 * 00064 * Adaptive threshold spread normalization 00065 * l_int32 pixThresholdSpreadNorm() 8 bpp 00066 * 00067 * Adaptive background normalization (flexible adaptaption) 00068 * PIX *pixBackgroundNormFlex() 8 bpp 00069 * 00070 * Adaptive contrast normalization 00071 * PIX *pixContrastNorm() 8 bpp 00072 * l_int32 pixMinMaxTiles() 00073 * l_int32 pixSetLowContrast() 00074 * PIX *pixLinearTRCTiled() 00075 * static l_int32 *iaaGetLinearTRC() 00076 * 00077 * Background normalization is done by generating a reduced map (or set 00078 * of maps) representing the estimated background value of the 00079 * input image, and using this to shift the pixel values so that 00080 * this background value is set to some constant value. 00081 * 00082 * Specifically, normalization has 3 steps: 00083 * (1) Generate a background map at a reduced scale. 00084 * (2) Make the array of inverted background values by inverting 00085 * the map. The result is an array of local multiplicative factors. 00086 * (3) Apply this inverse background map to the image 00087 * 00088 * The inverse background arrays can be generated in two different ways here: 00089 * (1) Remove the 'foreground' pixels and average over the remaining 00090 * pixels in each tile. Propagate values into tiles where 00091 * values have not been assigned, either because there was not 00092 * enough background in the tile or because the tile is covered 00093 * by a foreground region described by an image mask. 00094 * After the background map is made, the inverse map is generated by 00095 * smoothing over some number of adjacent tiles 00096 * (block convolution) and then inverting. 00097 * (2) Remove the foreground pixels using a morphological closing 00098 * on a subsampled version of the image. Propagate values 00099 * into pixels covered by an optional image mask. Invert the 00100 * background map without preconditioning by convolutional smoothing. 00101 * 00102 * Note: Several of these functions make an implicit assumption about RGB 00103 * component ordering. 00104 * 00105 * Other methods for adaptively normalizing the image are also given here. 00106 * 00107 * (1) pixThresholdSpreadNorm() computes a local threshold over the image 00108 * and normalizes the input pixel values so that this computed threshold 00109 * is a constant across the entire image. 00110 * 00111 * (2) pixContrastNorm() computes and applies a local TRC so that the 00112 * local dynamic range is expanded to the full 8 bits, where the 00113 * darkest pixels are mapped to 0 and the lightest to 255. This is 00114 * useful for improving the appearance of pages with very light 00115 * foreground or very dark background, and where the local TRC 00116 * function doesn't change rapidly with position. 00117 */ 00118 00119 #include <stdio.h> 00120 #include <stdlib.h> 00121 #include "allheaders.h" 00122 00123 00124 /* Default input parameters for pixBackgroundNormSimple() 00125 * Note: 00126 * (1) mincount must never exceed the tile area (width * height) 00127 * (2) bgval must be sufficiently below 255 to avoid accidental 00128 * saturation; otherwise it should be large to avoid 00129 * shrinking the dynamic range 00130 * (3) results should otherwise not be sensitive to these values 00131 */ 00132 static const l_int32 DEFAULT_TILE_WIDTH = 10; 00133 static const l_int32 DEFAULT_TILE_HEIGHT = 15; 00134 static const l_int32 DEFAULT_FG_THRESHOLD = 60; 00135 static const l_int32 DEFAULT_MIN_COUNT = 40; 00136 static const l_int32 DEFAULT_BG_VAL = 200; 00137 static const l_int32 DEFAULT_X_SMOOTH_SIZE = 2; 00138 static const l_int32 DEFAULT_Y_SMOOTH_SIZE = 1; 00139 00140 static l_int32 *iaaGetLinearTRC(l_int32 **iaa, l_int32 diff); 00141 00142 #ifndef NO_CONSOLE_IO 00143 #define DEBUG_GLOBAL 0 00144 #endif /* ~NO_CONSOLE_IO */ 00145 00146 00147 00148 /*------------------------------------------------------------------* 00149 * Adaptive background normalization * 00150 *------------------------------------------------------------------*/ 00151 /*! 00152 * pixBackgroundNormSimple() 00153 * 00154 * Input: pixs (8 bpp grayscale or 32 bpp rgb) 00155 * pixim (<optional> 1 bpp 'image' mask; can be null) 00156 * pixg (<optional> 8 bpp grayscale version; can be null) 00157 * Return: pixd (8 bpp or 32 bpp rgb), or null on error 00158 * 00159 * Notes: 00160 * (1) This is a simplified interface to pixBackgroundNorm(), 00161 * where seven parameters are defaulted. 00162 * (2) The input image is either grayscale or rgb. 00163 * (3) See pixBackgroundNorm() for usage and function. 00164 */ 00165 PIX * 00166 pixBackgroundNormSimple(PIX *pixs, 00167 PIX *pixim, 00168 PIX *pixg) 00169 { 00170 return pixBackgroundNorm(pixs, pixim, pixg, 00171 DEFAULT_TILE_WIDTH, DEFAULT_TILE_HEIGHT, 00172 DEFAULT_FG_THRESHOLD, DEFAULT_MIN_COUNT, 00173 DEFAULT_BG_VAL, DEFAULT_X_SMOOTH_SIZE, 00174 DEFAULT_Y_SMOOTH_SIZE); 00175 } 00176 00177 00178 /*! 00179 * pixBackgroundNorm() 00180 * 00181 * Input: pixs (8 bpp grayscale or 32 bpp rgb) 00182 * pixim (<optional> 1 bpp 'image' mask; can be null) 00183 * pixg (<optional> 8 bpp grayscale version; can be null) 00184 * sx, sy (tile size in pixels) 00185 * thresh (threshold for determining foreground) 00186 * mincount (min threshold on counts in a tile) 00187 * bgval (target bg val; typ. > 128) 00188 * smoothx (half-width of block convolution kernel width) 00189 * smoothy (half-width of block convolution kernel height) 00190 * Return: pixd (8 bpp or 32 bpp rgb), or null on error 00191 * 00192 * Notes: 00193 * (1) This is a top-level interface for normalizing the image intensity 00194 * by mapping the image so that the background is near the input 00195 * value 'bgval'. 00196 * (2) The input image is either grayscale or rgb. 00197 * (3) For each component in the input image, the background value 00198 * in each tile is estimated using the values in the tile that 00199 * are not part of the foreground, where the foreground is 00200 * determined by the input 'thresh' argument. 00201 * (4) An optional binary mask can be specified, with the foreground 00202 * pixels typically over image regions. The resulting background 00203 * map values will be determined by surrounding pixels that are 00204 * not under the mask foreground. The origin (0,0) of this mask 00205 * is assumed to be aligned with the origin of the input image. 00206 * This binary mask must not fully cover pixs, because then there 00207 * will be no pixels in the input image available to compute 00208 * the background. 00209 * (5) An optional grayscale version of the input pixs can be supplied. 00210 * The only reason to do this is if the input is RGB and this 00211 * grayscale version can be used elsewhere. If the input is RGB 00212 * and this is not supplied, it is made internally using only 00213 * the green component, and destroyed after use. 00214 * (6) The dimensions of the pixel tile (sx, sy) give the amount by 00215 * by which the map is reduced in size from the input image. 00216 * (7) The threshold is used to binarize the input image, in order to 00217 * locate the foreground components. If this is set too low, 00218 * some actual foreground may be used to determine the maps; 00219 * if set too high, there may not be enough background 00220 * to determine the map values accurately. Typically, it's 00221 * better to err by setting the threshold too high. 00222 * (8) A 'mincount' threshold is a minimum count of pixels in a 00223 * tile for which a background reading is made, in order for that 00224 * pixel in the map to be valid. This number should perhaps be 00225 * at least 1/3 the size of the tile. 00226 * (9) A 'bgval' target background value for the normalized image. This 00227 * should be at least 128. If set too close to 255, some 00228 * clipping will occur in the result. 00229 * (10) Two factors, 'smoothx' and 'smoothy', are input for smoothing 00230 * the map. Each low-pass filter kernel dimension is 00231 * is 2 * (smoothing factor) + 1, so a 00232 * value of 0 means no smoothing. A value of 1 or 2 is recommended. 00233 */ 00234 PIX * 00235 pixBackgroundNorm(PIX *pixs, 00236 PIX *pixim, 00237 PIX *pixg, 00238 l_int32 sx, 00239 l_int32 sy, 00240 l_int32 thresh, 00241 l_int32 mincount, 00242 l_int32 bgval, 00243 l_int32 smoothx, 00244 l_int32 smoothy) 00245 { 00246 l_int32 d, allfg; 00247 PIX *pixm, *pixmi, *pixd; 00248 PIX *pixmr, *pixmg, *pixmb, *pixmri, *pixmgi, *pixmbi; 00249 00250 PROCNAME("pixBackgroundNorm"); 00251 00252 if (!pixs) 00253 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00254 d = pixGetDepth(pixs); 00255 if (d != 8 && d != 32) 00256 return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); 00257 if (sx < 4 || sy < 4) 00258 return (PIX *)ERROR_PTR("sx and sy must be >= 4", procName, NULL); 00259 if (mincount > sx * sy) { 00260 L_WARNING("mincount too large for tile size", procName); 00261 mincount = (sx * sy) / 3; 00262 } 00263 00264 /* If pixim exists, verify that it is not all foreground. */ 00265 if (pixim) { 00266 pixInvert(pixim, pixim); 00267 pixZero(pixim, &allfg); 00268 pixInvert(pixim, pixim); 00269 if (allfg) 00270 return (PIX *)ERROR_PTR("pixim all foreground", procName, NULL); 00271 } 00272 00273 pixd = NULL; 00274 if (d == 8) { 00275 pixm = NULL; 00276 pixGetBackgroundGrayMap(pixs, pixim, sx, sy, thresh, mincount, &pixm); 00277 if (!pixm) { 00278 L_WARNING("map not made; returning a copy of the source", procName); 00279 return pixCopy(NULL, pixs); 00280 } 00281 00282 pixmi = pixGetInvBackgroundMap(pixm, bgval, smoothx, smoothy); 00283 if (!pixmi) 00284 ERROR_PTR("pixmi not made", procName, NULL); 00285 else 00286 pixd = pixApplyInvBackgroundGrayMap(pixs, pixmi, sx, sy); 00287 00288 pixDestroy(&pixm); 00289 pixDestroy(&pixmi); 00290 } 00291 else { 00292 pixmr = pixmg = pixmb = NULL; 00293 pixGetBackgroundRGBMap(pixs, pixim, pixg, sx, sy, thresh, 00294 mincount, &pixmr, &pixmg, &pixmb); 00295 if (!pixmr || !pixmg || !pixmb) { 00296 pixDestroy(&pixmr); 00297 pixDestroy(&pixmg); 00298 pixDestroy(&pixmb); 00299 L_WARNING("map not made; returning a copy of the source", procName); 00300 return pixCopy(NULL, pixs); 00301 } 00302 00303 pixmri = pixGetInvBackgroundMap(pixmr, bgval, smoothx, smoothy); 00304 pixmgi = pixGetInvBackgroundMap(pixmg, bgval, smoothx, smoothy); 00305 pixmbi = pixGetInvBackgroundMap(pixmb, bgval, smoothx, smoothy); 00306 if (!pixmri || !pixmgi || !pixmbi) 00307 ERROR_PTR("not all pixm*i are made", procName, NULL); 00308 else 00309 pixd = pixApplyInvBackgroundRGBMap(pixs, pixmri, pixmgi, pixmbi, 00310 sx, sy); 00311 00312 pixDestroy(&pixmr); 00313 pixDestroy(&pixmg); 00314 pixDestroy(&pixmb); 00315 pixDestroy(&pixmri); 00316 pixDestroy(&pixmgi); 00317 pixDestroy(&pixmbi); 00318 } 00319 00320 if (!pixd) 00321 ERROR_PTR("pixd not made", procName, NULL); 00322 return pixd; 00323 } 00324 00325 00326 /*! 00327 * pixBackgroundNormMorph() 00328 * 00329 * Input: pixs (8 bpp grayscale or 32 bpp rgb) 00330 * pixim (<optional> 1 bpp 'image' mask; can be null) 00331 * reduction (at which morph closings are done; between 2 and 16) 00332 * size (of square Sel for the closing; use an odd number) 00333 * bgval (target bg val; typ. > 128) 00334 * Return: pixd (8 bpp), or null on error 00335 * 00336 * Notes: 00337 * (1) This is a top-level interface for normalizing the image intensity 00338 * by mapping the image so that the background is near the input 00339 * value 'bgval'. 00340 * (2) The input image is either grayscale or rgb. 00341 * (3) For each component in the input image, the background value 00342 * is estimated using a grayscale closing; hence the 'Morph' 00343 * in the function name. 00344 * (4) An optional binary mask can be specified, with the foreground 00345 * pixels typically over image regions. The resulting background 00346 * map values will be determined by surrounding pixels that are 00347 * not under the mask foreground. The origin (0,0) of this mask 00348 * is assumed to be aligned with the origin of the input image. 00349 * This binary mask must not fully cover pixs, because then there 00350 * will be no pixels in the input image available to compute 00351 * the background. 00352 * (5) The map is computed at reduced size (given by 'reduction') 00353 * from the input pixs and optional pixim. At this scale, 00354 * pixs is closed to remove the background, using a square Sel 00355 * of odd dimension. The product of reduction * size should be 00356 * large enough to remove most of the text foreground. 00357 * (6) No convolutional smoothing needs to be done on the map before 00358 * inverting it. 00359 * (7) A 'bgval' target background value for the normalized image. This 00360 * should be at least 128. If set too close to 255, some 00361 * clipping will occur in the result. 00362 */ 00363 PIX * 00364 pixBackgroundNormMorph(PIX *pixs, 00365 PIX *pixim, 00366 l_int32 reduction, 00367 l_int32 size, 00368 l_int32 bgval) 00369 { 00370 l_int32 d, allfg; 00371 PIX *pixm, *pixmi, *pixd; 00372 PIX *pixmr, *pixmg, *pixmb, *pixmri, *pixmgi, *pixmbi; 00373 00374 PROCNAME("pixBackgroundNormMorph"); 00375 00376 if (!pixs) 00377 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00378 d = pixGetDepth(pixs); 00379 if (d != 8 && d != 32) 00380 return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); 00381 if (reduction < 2 || reduction > 16) 00382 return (PIX *)ERROR_PTR("reduction must be between 2 and 16", 00383 procName, NULL); 00384 00385 /* If pixim exists, verify that it is not all foreground. */ 00386 if (pixim) { 00387 pixInvert(pixim, pixim); 00388 pixZero(pixim, &allfg); 00389 pixInvert(pixim, pixim); 00390 if (allfg) 00391 return (PIX *)ERROR_PTR("pixim all foreground", procName, NULL); 00392 } 00393 00394 pixd = NULL; 00395 if (d == 8) { 00396 pixGetBackgroundGrayMapMorph(pixs, pixim, reduction, size, &pixm); 00397 if (!pixm) 00398 return (PIX *)ERROR_PTR("pixm not made", procName, NULL); 00399 pixmi = pixGetInvBackgroundMap(pixm, bgval, 0, 0); 00400 if (!pixmi) 00401 ERROR_PTR("pixmi not made", procName, NULL); 00402 else 00403 pixd = pixApplyInvBackgroundGrayMap(pixs, pixmi, 00404 reduction, reduction); 00405 pixDestroy(&pixm); 00406 pixDestroy(&pixmi); 00407 } 00408 else { /* d == 32 */ 00409 pixmr = pixmg = pixmb = NULL; 00410 pixGetBackgroundRGBMapMorph(pixs, pixim, reduction, size, 00411 &pixmr, &pixmg, &pixmb); 00412 if (!pixmr || !pixmg || !pixmb) { 00413 pixDestroy(&pixmr); 00414 pixDestroy(&pixmg); 00415 pixDestroy(&pixmb); 00416 return (PIX *)ERROR_PTR("not all pixm*", procName, NULL); 00417 } 00418 00419 pixmri = pixGetInvBackgroundMap(pixmr, bgval, 0, 0); 00420 pixmgi = pixGetInvBackgroundMap(pixmg, bgval, 0, 0); 00421 pixmbi = pixGetInvBackgroundMap(pixmb, bgval, 0, 0); 00422 if (!pixmri || !pixmgi || !pixmbi) 00423 ERROR_PTR("not all pixm*i are made", procName, NULL); 00424 else 00425 pixd = pixApplyInvBackgroundRGBMap(pixs, pixmri, pixmgi, pixmbi, 00426 reduction, reduction); 00427 00428 pixDestroy(&pixmr); 00429 pixDestroy(&pixmg); 00430 pixDestroy(&pixmb); 00431 pixDestroy(&pixmri); 00432 pixDestroy(&pixmgi); 00433 pixDestroy(&pixmbi); 00434 } 00435 00436 if (!pixd) 00437 ERROR_PTR("pixd not made", procName, NULL); 00438 return pixd; 00439 } 00440 00441 00442 /*-------------------------------------------------------------------------* 00443 * Arrays of inverted background values for normalization * 00444 *-------------------------------------------------------------------------* 00445 * Notes for these four functions: * 00446 * (1) They are useful if you need to save the actual mapping array. * 00447 * (2) They could be used in the top-level functions but are * 00448 * not because their use makes those functions less clear. * 00449 * (3) Each component in the input pixs generates a 16 bpp pix array. * 00450 *-------------------------------------------------------------------------*/ 00451 /*! 00452 * pixBackgroundNormGrayArray() 00453 * 00454 * Input: pixs (8 bpp grayscale) 00455 * pixim (<optional> 1 bpp 'image' mask; can be null) 00456 * sx, sy (tile size in pixels) 00457 * thresh (threshold for determining foreground) 00458 * mincount (min threshold on counts in a tile) 00459 * bgval (target bg val; typ. > 128) 00460 * smoothx (half-width of block convolution kernel width) 00461 * smoothy (half-width of block convolution kernel height) 00462 * &pixd (<return> 16 bpp array of inverted background value) 00463 * Return: 0 if OK, 1 on error 00464 * 00465 * Notes: 00466 * (1) See notes in pixBackgroundNorm(). 00467 * (2) This returns a 16 bpp pix that can be used by 00468 * pixApplyInvBackgroundGrayMap() to generate a normalized version 00469 * of the input pixs. 00470 */ 00471 l_int32 00472 pixBackgroundNormGrayArray(PIX *pixs, 00473 PIX *pixim, 00474 l_int32 sx, 00475 l_int32 sy, 00476 l_int32 thresh, 00477 l_int32 mincount, 00478 l_int32 bgval, 00479 l_int32 smoothx, 00480 l_int32 smoothy, 00481 PIX **ppixd) 00482 { 00483 l_int32 allfg; 00484 PIX *pixm; 00485 00486 PROCNAME("pixBackgroundNormGrayArray"); 00487 00488 if (!ppixd) 00489 return ERROR_INT("&pixd not defined", procName, 1); 00490 *ppixd = NULL; 00491 if (!pixs || pixGetDepth(pixs) != 8) 00492 return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); 00493 if (pixGetColormap(pixs)) 00494 return ERROR_INT("pixs is colormapped", procName, 1); 00495 if (pixim && pixGetDepth(pixim) != 1) 00496 return ERROR_INT("pixim not 1 bpp", procName, 1); 00497 if (sx < 4 || sy < 4) 00498 return ERROR_INT("sx and sy must be >= 4", procName, 1); 00499 if (mincount > sx * sy) { 00500 L_WARNING("mincount too large for tile size", procName); 00501 mincount = (sx * sy) / 3; 00502 } 00503 00504 /* If pixim exists, verify that it is not all foreground. */ 00505 if (pixim) { 00506 pixInvert(pixim, pixim); 00507 pixZero(pixim, &allfg); 00508 pixInvert(pixim, pixim); 00509 if (allfg) 00510 return ERROR_INT("pixim all foreground", procName, 1); 00511 } 00512 00513 pixGetBackgroundGrayMap(pixs, pixim, sx, sy, thresh, mincount, &pixm); 00514 if (!pixm) 00515 return ERROR_INT("pixm not made", procName, 1); 00516 *ppixd = pixGetInvBackgroundMap(pixm, bgval, smoothx, smoothy); 00517 pixDestroy(&pixm); 00518 return 0; 00519 } 00520 00521 00522 /*! 00523 * pixBackgroundNormRGBArrays() 00524 * 00525 * Input: pixs (32 bpp rgb) 00526 * pixim (<optional> 1 bpp 'image' mask; can be null) 00527 * pixg (<optional> 8 bpp grayscale version; can be null) 00528 * sx, sy (tile size in pixels) 00529 * thresh (threshold for determining foreground) 00530 * mincount (min threshold on counts in a tile) 00531 * bgval (target bg val; typ. > 128) 00532 * smoothx (half-width of block convolution kernel width) 00533 * smoothy (half-width of block convolution kernel height) 00534 * &pixr (<return> 16 bpp array of inverted R background value) 00535 * &pixg (<return> 16 bpp array of inverted G background value) 00536 * &pixb (<return> 16 bpp array of inverted B background value) 00537 * Return: 0 if OK, 1 on error 00538 * 00539 * Notes: 00540 * (1) See notes in pixBackgroundNorm(). 00541 * (2) This returns a set of three 16 bpp pix that can be used by 00542 * pixApplyInvBackgroundGrayMap() to generate a normalized version 00543 * of each component of the input pixs. 00544 */ 00545 l_int32 00546 pixBackgroundNormRGBArrays(PIX *pixs, 00547 PIX *pixim, 00548 PIX *pixg, 00549 l_int32 sx, 00550 l_int32 sy, 00551 l_int32 thresh, 00552 l_int32 mincount, 00553 l_int32 bgval, 00554 l_int32 smoothx, 00555 l_int32 smoothy, 00556 PIX **ppixr, 00557 PIX **ppixg, 00558 PIX **ppixb) 00559 { 00560 l_int32 allfg; 00561 PIX *pixmr, *pixmg, *pixmb; 00562 00563 PROCNAME("pixBackgroundNormRGBArrays"); 00564 00565 if (!ppixr || !ppixg || !ppixb) 00566 return ERROR_INT("&pixr, &pixg, &pixb not all defined", procName, 1); 00567 *ppixr = *ppixg = *ppixb = NULL; 00568 if (!pixs) 00569 return ERROR_INT("pixs not defined", procName, 1); 00570 if (pixGetDepth(pixs) != 32) 00571 return ERROR_INT("pixs not 32 bpp", procName, 1); 00572 if (pixim && pixGetDepth(pixim) != 1) 00573 return ERROR_INT("pixim not 1 bpp", procName, 1); 00574 if (sx < 4 || sy < 4) 00575 return ERROR_INT("sx and sy must be >= 4", procName, 1); 00576 if (mincount > sx * sy) { 00577 L_WARNING("mincount too large for tile size", procName); 00578 mincount = (sx * sy) / 3; 00579 } 00580 00581 /* If pixim exists, verify that it is not all foreground. */ 00582 if (pixim) { 00583 pixInvert(pixim, pixim); 00584 pixZero(pixim, &allfg); 00585 pixInvert(pixim, pixim); 00586 if (allfg) 00587 return ERROR_INT("pixim all foreground", procName, 1); 00588 } 00589 00590 pixGetBackgroundRGBMap(pixs, pixim, pixg, sx, sy, thresh, mincount, 00591 &pixmr, &pixmg, &pixmb); 00592 if (!pixmr || !pixmg || !pixmb) { 00593 pixDestroy(&pixmr); 00594 pixDestroy(&pixmg); 00595 pixDestroy(&pixmb); 00596 return ERROR_INT("not all pixm* made", procName, 1); 00597 } 00598 00599 *ppixr = pixGetInvBackgroundMap(pixmr, bgval, smoothx, smoothy); 00600 *ppixg = pixGetInvBackgroundMap(pixmg, bgval, smoothx, smoothy); 00601 *ppixb = pixGetInvBackgroundMap(pixmb, bgval, smoothx, smoothy); 00602 pixDestroy(&pixmr); 00603 pixDestroy(&pixmg); 00604 pixDestroy(&pixmb); 00605 return 0; 00606 } 00607 00608 00609 /*! 00610 * pixBackgroundNormGrayArrayMorph() 00611 * 00612 * Input: pixs (8 bpp grayscale) 00613 * pixim (<optional> 1 bpp 'image' mask; can be null) 00614 * reduction (at which morph closings are done; between 2 and 16) 00615 * size (of square Sel for the closing; use an odd number) 00616 * bgval (target bg val; typ. > 128) 00617 * &pixd (<return> 16 bpp array of inverted background value) 00618 * Return: 0 if OK, 1 on error 00619 * 00620 * Notes: 00621 * (1) See notes in pixBackgroundNormMorph(). 00622 * (2) This returns a 16 bpp pix that can be used by 00623 * pixApplyInvBackgroundGrayMap() to generate a normalized version 00624 * of the input pixs. 00625 */ 00626 l_int32 00627 pixBackgroundNormGrayArrayMorph(PIX *pixs, 00628 PIX *pixim, 00629 l_int32 reduction, 00630 l_int32 size, 00631 l_int32 bgval, 00632 PIX **ppixd) 00633 { 00634 l_int32 allfg; 00635 PIX *pixm; 00636 00637 PROCNAME("pixBackgroundNormGrayArrayMorph"); 00638 00639 if (!ppixd) 00640 return ERROR_INT("&pixd not defined", procName, 1); 00641 *ppixd = NULL; 00642 if (!pixs) 00643 return ERROR_INT("pixs not defined", procName, 1); 00644 if (pixGetDepth(pixs) != 8) 00645 return ERROR_INT("pixs not 8 bpp", procName, 1); 00646 if (pixim && pixGetDepth(pixim) != 1) 00647 return ERROR_INT("pixim not 1 bpp", procName, 1); 00648 if (reduction < 2 || reduction > 16) 00649 return ERROR_INT("reduction must be between 2 and 16", procName, 1); 00650 00651 /* If pixim exists, verify that it is not all foreground. */ 00652 if (pixim) { 00653 pixInvert(pixim, pixim); 00654 pixZero(pixim, &allfg); 00655 pixInvert(pixim, pixim); 00656 if (allfg) 00657 return ERROR_INT("pixim all foreground", procName, 1); 00658 } 00659 00660 pixGetBackgroundGrayMapMorph(pixs, pixim, reduction, size, &pixm); 00661 if (!pixm) 00662 return ERROR_INT("pixm not made", procName, 1); 00663 *ppixd = pixGetInvBackgroundMap(pixm, bgval, 0, 0); 00664 pixDestroy(&pixm); 00665 return 0; 00666 } 00667 00668 00669 /*! 00670 * pixBackgroundNormRGBArraysMorph() 00671 * 00672 * Input: pixs (32 bpp rgb) 00673 * pixim (<optional> 1 bpp 'image' mask; can be null) 00674 * reduction (at which morph closings are done; between 2 and 16) 00675 * size (of square Sel for the closing; use an odd number) 00676 * bgval (target bg val; typ. > 128) 00677 * &pixr (<return> 16 bpp array of inverted R background value) 00678 * &pixg (<return> 16 bpp array of inverted G background value) 00679 * &pixb (<return> 16 bpp array of inverted B background value) 00680 * Return: 0 if OK, 1 on error 00681 * 00682 * Notes: 00683 * (1) See notes in pixBackgroundNormMorph(). 00684 * (2) This returns a set of three 16 bpp pix that can be used by 00685 * pixApplyInvBackgroundGrayMap() to generate a normalized version 00686 * of each component of the input pixs. 00687 */ 00688 l_int32 00689 pixBackgroundNormRGBArraysMorph(PIX *pixs, 00690 PIX *pixim, 00691 l_int32 reduction, 00692 l_int32 size, 00693 l_int32 bgval, 00694 PIX **ppixr, 00695 PIX **ppixg, 00696 PIX **ppixb) 00697 { 00698 l_int32 allfg; 00699 PIX *pixmr, *pixmg, *pixmb; 00700 00701 PROCNAME("pixBackgroundNormRGBArraysMorph"); 00702 00703 if (!ppixr || !ppixg || !ppixb) 00704 return ERROR_INT("&pixr, &pixg, &pixb not all defined", procName, 1); 00705 *ppixr = *ppixg = *ppixb = NULL; 00706 if (!pixs) 00707 return ERROR_INT("pixs not defined", procName, 1); 00708 if (pixGetDepth(pixs) != 32) 00709 return ERROR_INT("pixs not 32 bpp", procName, 1); 00710 if (pixim && pixGetDepth(pixim) != 1) 00711 return ERROR_INT("pixim not 1 bpp", procName, 1); 00712 if (reduction < 2 || reduction > 16) 00713 return ERROR_INT("reduction must be between 2 and 16", procName, 1); 00714 00715 /* If pixim exists, verify that it is not all foreground. */ 00716 if (pixim) { 00717 pixInvert(pixim, pixim); 00718 pixZero(pixim, &allfg); 00719 pixInvert(pixim, pixim); 00720 if (allfg) 00721 return ERROR_INT("pixim all foreground", procName, 1); 00722 } 00723 00724 pixGetBackgroundRGBMapMorph(pixs, pixim, reduction, size, 00725 &pixmr, &pixmg, &pixmb); 00726 if (!pixmr || !pixmg || !pixmb) { 00727 pixDestroy(&pixmr); 00728 pixDestroy(&pixmg); 00729 pixDestroy(&pixmb); 00730 return ERROR_INT("not all pixm* made", procName, 1); 00731 } 00732 00733 *ppixr = pixGetInvBackgroundMap(pixmr, bgval, 0, 0); 00734 *ppixg = pixGetInvBackgroundMap(pixmg, bgval, 0, 0); 00735 *ppixb = pixGetInvBackgroundMap(pixmb, bgval, 0, 0); 00736 pixDestroy(&pixmr); 00737 pixDestroy(&pixmg); 00738 pixDestroy(&pixmb); 00739 return 0; 00740 } 00741 00742 00743 /*------------------------------------------------------------------* 00744 * Measurement of local background * 00745 *------------------------------------------------------------------*/ 00746 /*! 00747 * pixGetBackgroundGrayMap() 00748 * 00749 * Input: pixs (8 bpp grayscale; not cmapped) 00750 * pixim (<optional> 1 bpp 'image' mask; can be null; it 00751 * should not have all foreground pixels) 00752 * sx, sy (tile size in pixels) 00753 * thresh (threshold for determining foreground) 00754 * mincount (min threshold on counts in a tile) 00755 * &pixd (<return> 8 bpp grayscale map) 00756 * Return: 0 if OK, 1 on error 00757 * 00758 * Notes: 00759 * (1) The background is measured in regions that don't have 00760 * images. It is then propagated into the image regions, 00761 * and finally smoothed in each image region. 00762 */ 00763 l_int32 00764 pixGetBackgroundGrayMap(PIX *pixs, 00765 PIX *pixim, 00766 l_int32 sx, 00767 l_int32 sy, 00768 l_int32 thresh, 00769 l_int32 mincount, 00770 PIX **ppixd) 00771 { 00772 l_int32 w, h, wd, hd, wim, him, wpls, wplim, wpld, wplf; 00773 l_int32 xim, yim, delx, nx, ny, i, j, k, m; 00774 l_int32 count, sum, val8; 00775 l_int32 empty, fgpixels; 00776 l_uint32 *datas, *dataim, *datad, *dataf, *lines, *lineim, *lined, *linef; 00777 l_float32 scalex, scaley; 00778 PIX *pixd, *piximi, *pixb, *pixf, *pixims; 00779 00780 PROCNAME("pixGetBackgroundGrayMap"); 00781 00782 if (!ppixd) 00783 return ERROR_INT("&pixd not defined", procName, 1); 00784 *ppixd = NULL; 00785 if (!pixs || pixGetDepth(pixs) != 8) 00786 return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); 00787 if (pixGetColormap(pixs)) 00788 return ERROR_INT("pixs is colormapped", procName, 1); 00789 if (pixim && pixGetDepth(pixim) != 1) 00790 return ERROR_INT("pixim not 1 bpp", procName, 1); 00791 if (sx < 4 || sy < 4) 00792 return ERROR_INT("sx and sy must be >= 4", procName, 1); 00793 if (mincount > sx * sy) { 00794 L_WARNING("mincount too large for tile size", procName); 00795 mincount = (sx * sy) / 3; 00796 } 00797 00798 /* Evaluate the 'image' mask, pixim, and make sure 00799 * it is not all fg. */ 00800 fgpixels = 0; /* boolean for existence of fg pixels in the image mask. */ 00801 if (pixim) { 00802 piximi = pixInvert(NULL, pixim); /* set non-'image' pixels to 1 */ 00803 pixZero(piximi, &empty); 00804 pixDestroy(&piximi); 00805 if (empty) 00806 return ERROR_INT("pixim all fg; no background", procName, 1); 00807 pixZero(pixim, &empty); 00808 if (!empty) /* there are fg pixels in pixim */ 00809 fgpixels = 1; 00810 } 00811 00812 /* Generate the foreground mask, pixf, which is at 00813 * full resolution. These pixels will be ignored when 00814 * computing the background values. */ 00815 pixb = pixThresholdToBinary(pixs, thresh); 00816 pixf = pixMorphSequence(pixb, "d7.1 + d1.7", 0); 00817 pixDestroy(&pixb); 00818 00819 00820 /* ------------- Set up the output map pixd --------------- */ 00821 /* Generate pixd, which is reduced by the factors (sx, sy). */ 00822 w = pixGetWidth(pixs); 00823 h = pixGetHeight(pixs); 00824 wd = (w + sx - 1) / sx; 00825 hd = (h + sy - 1) / sy; 00826 pixd = pixCreate(wd, hd, 8); 00827 00828 /* Note: we only compute map values in tiles that are complete. 00829 * In general, tiles at right and bottom edges will not be 00830 * complete, and we must fill them in later. */ 00831 nx = w / sx; 00832 ny = h / sy; 00833 wpls = pixGetWpl(pixs); 00834 datas = pixGetData(pixs); 00835 wpld = pixGetWpl(pixd); 00836 datad = pixGetData(pixd); 00837 wplf = pixGetWpl(pixf); 00838 dataf = pixGetData(pixf); 00839 for (i = 0; i < ny; i++) { 00840 lines = datas + sy * i * wpls; 00841 linef = dataf + sy * i * wplf; 00842 lined = datad + i * wpld; 00843 for (j = 0; j < nx; j++) { 00844 delx = j * sx; 00845 sum = 0; 00846 count = 0; 00847 for (k = 0; k < sy; k++) { 00848 for (m = 0; m < sx; m++) { 00849 if (GET_DATA_BIT(linef + k * wplf, delx + m) == 0) { 00850 sum += GET_DATA_BYTE(lines + k * wpls, delx + m); 00851 count++; 00852 } 00853 } 00854 } 00855 if (count >= mincount) { 00856 val8 = sum / count; 00857 SET_DATA_BYTE(lined, j, val8); 00858 } 00859 } 00860 } 00861 pixDestroy(&pixf); 00862 00863 /* If there is an optional mask with fg pixels, erase the previous 00864 * calculation for the corresponding map pixels, setting the 00865 * map values to 0. Then, when all the map holes are filled, 00866 * these erased pixels will be set by the surrounding map values. 00867 * 00868 * The calculation here is relatively efficient: for each pixel 00869 * in pixd (which corresponds to a tile of mask pixels in pixim) 00870 * we look only at the pixel in pixim that is at the center 00871 * of the tile. If the mask pixel is ON, we reset the map 00872 * pixel in pixd to 0, so that it can later be filled in. */ 00873 pixims = NULL; 00874 if (pixim && fgpixels) { 00875 wim = pixGetWidth(pixim); 00876 him = pixGetHeight(pixim); 00877 dataim = pixGetData(pixim); 00878 wplim = pixGetWpl(pixim); 00879 for (i = 0; i < ny; i++) { 00880 yim = i * sy + sy / 2; 00881 if (yim >= him) 00882 break; 00883 lineim = dataim + yim * wplim; 00884 for (j = 0; j < nx; j++) { 00885 xim = j * sx + sx / 2; 00886 if (xim >= wim) 00887 break; 00888 if (GET_DATA_BIT(lineim, xim)) 00889 pixSetPixel(pixd, j, i, 0); 00890 } 00891 } 00892 } 00893 00894 /* Fill all the holes in the map. */ 00895 if (pixFillMapHoles(pixd, nx, ny, L_FILL_BLACK)) { 00896 pixDestroy(&pixd); 00897 L_WARNING("can't make the map", procName); 00898 return 1; 00899 } 00900 00901 /* Finally, for each connected region corresponding to the 00902 * 'image' mask, reset all pixels to their average value. 00903 * Each of these components represents an image (or part of one) 00904 * in the input, and this smooths the background values 00905 * in each of these regions. */ 00906 if (pixim && fgpixels) { 00907 scalex = 1. / (l_float32)sx; 00908 scaley = 1. / (l_float32)sy; 00909 pixims = pixScaleBySampling(pixim, scalex, scaley); 00910 pixSmoothConnectedRegions(pixd, pixims, 2); 00911 pixDestroy(&pixims); 00912 } 00913 00914 *ppixd = pixd; 00915 return 0; 00916 } 00917 00918 00919 /*! 00920 * pixGetBackgroundRGBMap() 00921 * 00922 * Input: pixs (32 bpp rgb) 00923 * pixim (<optional> 1 bpp 'image' mask; can be null; it 00924 * should not have all foreground pixels) 00925 * pixg (<optional> 8 bpp grayscale version; can be null) 00926 * sx, sy (tile size in pixels) 00927 * thresh (threshold for determining foreground) 00928 * mincount (min threshold on counts in a tile) 00929 * &pixmr, &pixmg, &pixmb (<return> rgb maps) 00930 * Return: 0 if OK, 1 on error 00931 * 00932 * Notes: 00933 * (1) If pixg, which is a grayscale version of pixs, is provided, 00934 * use this internally to generate the foreground mask. 00935 * Otherwise, a grayscale version of pixs will be generated 00936 * from the green component only, used, and destroyed. 00937 */ 00938 l_int32 00939 pixGetBackgroundRGBMap(PIX *pixs, 00940 PIX *pixim, 00941 PIX *pixg, 00942 l_int32 sx, 00943 l_int32 sy, 00944 l_int32 thresh, 00945 l_int32 mincount, 00946 PIX **ppixmr, 00947 PIX **ppixmg, 00948 PIX **ppixmb) 00949 { 00950 l_int32 w, h, wm, hm, wim, him, wpls, wplim, wplf; 00951 l_int32 xim, yim, delx, nx, ny, i, j, k, m; 00952 l_int32 count, rsum, gsum, bsum, rval, gval, bval; 00953 l_int32 empty, fgpixels; 00954 l_uint32 pixel; 00955 l_uint32 *datas, *dataim, *dataf, *lines, *lineim, *linef; 00956 l_float32 scalex, scaley; 00957 PIX *piximi, *pixgc, *pixb, *pixf, *pixims; 00958 PIX *pixmr, *pixmg, *pixmb; 00959 00960 PROCNAME("pixGetBackgroundRGBMap"); 00961 00962 if (!ppixmr || !ppixmg || !ppixmb) 00963 return ERROR_INT("&pixm* not all defined", procName, 1); 00964 *ppixmr = *ppixmg = *ppixmb = NULL; 00965 if (!pixs) 00966 return ERROR_INT("pixs not defined", procName, 1); 00967 if (pixGetDepth(pixs) != 32) 00968 return ERROR_INT("pixs not 32 bpp", procName, 1); 00969 if (pixim && pixGetDepth(pixim) != 1) 00970 return ERROR_INT("pixim not 1 bpp", procName, 1); 00971 if (sx < 4 || sy < 4) 00972 return ERROR_INT("sx and sy must be >= 4", procName, 1); 00973 if (mincount > sx * sy) { 00974 L_WARNING("mincount too large for tile size", procName); 00975 mincount = (sx * sy) / 3; 00976 } 00977 00978 /* Evaluate the mask pixim and make sure it is not all foreground */ 00979 fgpixels = 0; /* boolean for existence of fg mask pixels */ 00980 if (pixim) { 00981 piximi = pixInvert(NULL, pixim); /* set non-'image' pixels to 1 */ 00982 pixZero(piximi, &empty); 00983 pixDestroy(&piximi); 00984 if (empty) 00985 return ERROR_INT("pixim all fg; no background", procName, 1); 00986 pixZero(pixim, &empty); 00987 if (!empty) /* there are fg pixels in pixim */ 00988 fgpixels = 1; 00989 } 00990 00991 /* Generate the foreground mask. These pixels will be 00992 * ignored when computing the background values. */ 00993 if (pixg) /* use the input grayscale version if it is provided */ 00994 pixgc = pixClone(pixg); 00995 else 00996 pixgc = pixConvertRGBToGrayFast(pixs); 00997 pixb = pixThresholdToBinary(pixgc, thresh); 00998 pixf = pixMorphSequence(pixb, "d7.1 + d1.7", 0); 00999 pixDestroy(&pixgc); 01000 pixDestroy(&pixb); 01001 01002 /* Generate the output mask images */ 01003 w = pixGetWidth(pixs); 01004 h = pixGetHeight(pixs); 01005 wm = (w + sx - 1) / sx; 01006 hm = (h + sy - 1) / sy; 01007 pixmr = pixCreate(wm, hm, 8); 01008 pixmg = pixCreate(wm, hm, 8); 01009 pixmb = pixCreate(wm, hm, 8); 01010 01011 /* ------------- Set up the mapping images --------------- */ 01012 /* Note: we only compute map values in tiles that are complete. 01013 * In general, tiles at right and bottom edges will not be 01014 * complete, and we must fill them in later. */ 01015 nx = w / sx; 01016 ny = h / sy; 01017 wpls = pixGetWpl(pixs); 01018 datas = pixGetData(pixs); 01019 wplf = pixGetWpl(pixf); 01020 dataf = pixGetData(pixf); 01021 for (i = 0; i < ny; i++) { 01022 lines = datas + sy * i * wpls; 01023 linef = dataf + sy * i * wplf; 01024 for (j = 0; j < nx; j++) { 01025 delx = j * sx; 01026 rsum = gsum = bsum = 0; 01027 count = 0; 01028 for (k = 0; k < sy; k++) { 01029 for (m = 0; m < sx; m++) { 01030 if (GET_DATA_BIT(linef + k * wplf, delx + m) == 0) { 01031 pixel = *(lines + k * wpls + delx + m); 01032 rsum += (pixel >> 24); 01033 gsum += ((pixel >> 16) & 0xff); 01034 bsum += ((pixel >> 8) & 0xff); 01035 count++; 01036 } 01037 } 01038 } 01039 if (count >= mincount) { 01040 rval = rsum / count; 01041 gval = gsum / count; 01042 bval = bsum / count; 01043 pixSetPixel(pixmr, j, i, rval); 01044 pixSetPixel(pixmg, j, i, gval); 01045 pixSetPixel(pixmb, j, i, bval); 01046 } 01047 } 01048 } 01049 pixDestroy(&pixf); 01050 01051 /* If there is an optional mask with fg pixels, erase the previous 01052 * calculation for the corresponding map pixels, setting the 01053 * map values in each of the 3 color maps to 0. Then, when 01054 * all the map holes are filled, these erased pixels will 01055 * be set by the surrounding map values. */ 01056 if (pixim) { 01057 wim = pixGetWidth(pixim); 01058 him = pixGetHeight(pixim); 01059 dataim = pixGetData(pixim); 01060 wplim = pixGetWpl(pixim); 01061 for (i = 0; i < ny; i++) { 01062 yim = i * sy + sy / 2; 01063 if (yim >= him) 01064 break; 01065 lineim = dataim + yim * wplim; 01066 for (j = 0; j < nx; j++) { 01067 xim = j * sx + sx / 2; 01068 if (xim >= wim) 01069 break; 01070 if (GET_DATA_BIT(lineim, xim)) { 01071 pixSetPixel(pixmr, j, i, 0); 01072 pixSetPixel(pixmg, j, i, 0); 01073 pixSetPixel(pixmb, j, i, 0); 01074 } 01075 } 01076 } 01077 } 01078 01079 /* ----------------- Now fill in the holes ----------------------- */ 01080 if (pixFillMapHoles(pixmr, nx, ny, L_FILL_BLACK) || 01081 pixFillMapHoles(pixmg, nx, ny, L_FILL_BLACK) || 01082 pixFillMapHoles(pixmb, nx, ny, L_FILL_BLACK)) { 01083 pixDestroy(&pixmr); 01084 pixDestroy(&pixmg); 01085 pixDestroy(&pixmb); 01086 L_WARNING("can't make the maps", procName); 01087 return 1; 01088 } 01089 01090 /* Finally, for each connected region corresponding to the 01091 * fg mask, reset all pixels to their average value. */ 01092 if (pixim && fgpixels) { 01093 scalex = 1. / (l_float32)sx; 01094 scaley = 1. / (l_float32)sy; 01095 pixims = pixScaleBySampling(pixim, scalex, scaley); 01096 pixSmoothConnectedRegions(pixmr, pixims, 2); 01097 pixSmoothConnectedRegions(pixmg, pixims, 2); 01098 pixSmoothConnectedRegions(pixmb, pixims, 2); 01099 pixDestroy(&pixims); 01100 } 01101 01102 *ppixmr = pixmr; 01103 *ppixmg = pixmg; 01104 *ppixmb = pixmb; 01105 return 0; 01106 } 01107 01108 01109 /*! 01110 * pixGetBackgroundGrayMapMorph() 01111 * 01112 * Input: pixs (8 bpp grayscale; not cmapped) 01113 * pixim (<optional> 1 bpp 'image' mask; can be null; it 01114 * should not have all foreground pixels) 01115 * reduction (factor at which closing is performed) 01116 * size (of square Sel for the closing; use an odd number) 01117 * &pixm (<return> grayscale map) 01118 * Return: 0 if OK, 1 on error 01119 */ 01120 l_int32 01121 pixGetBackgroundGrayMapMorph(PIX *pixs, 01122 PIX *pixim, 01123 l_int32 reduction, 01124 l_int32 size, 01125 PIX **ppixm) 01126 { 01127 l_int32 nx, ny, empty, fgpixels; 01128 l_float32 scale; 01129 PIX *pixm, *pixt1, *pixt2, *pixt3, *pixims; 01130 01131 PROCNAME("pixGetBackgroundGrayMapMorph"); 01132 01133 if (!ppixm) 01134 return ERROR_INT("&pixm not defined", procName, 1); 01135 *ppixm = NULL; 01136 if (!pixs || pixGetDepth(pixs) != 8) 01137 return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); 01138 if (pixGetColormap(pixs)) 01139 return ERROR_INT("pixs is colormapped", procName, 1); 01140 if (pixim && pixGetDepth(pixim) != 1) 01141 return ERROR_INT("pixim not 1 bpp", procName, 1); 01142 01143 /* Evaluate the mask pixim and make sure it is not all foreground. */ 01144 fgpixels = 0; /* boolean for existence of fg mask pixels */ 01145 if (pixim) { 01146 pixInvert(pixim, pixim); /* set background pixels to 1 */ 01147 pixZero(pixim, &empty); 01148 if (empty) 01149 return ERROR_INT("pixim all fg; no background", procName, 1); 01150 pixInvert(pixim, pixim); /* revert to original mask */ 01151 pixZero(pixim, &empty); 01152 if (!empty) /* there are fg pixels in pixim */ 01153 fgpixels = 1; 01154 } 01155 01156 /* Downscale as requested and do the closing to get the background. */ 01157 scale = 1. / (l_float32)reduction; 01158 pixt1 = pixScaleBySampling(pixs, scale, scale); 01159 pixt2 = pixCloseGray(pixt1, size, size); 01160 pixt3 = pixExtendByReplication(pixt2, 1, 1); 01161 01162 /* Downscale the image mask, if any, and remove it from the 01163 * background. These pixels will be filled in (twice). */ 01164 pixims = NULL; 01165 if (pixim) { 01166 pixims = pixScale(pixim, scale, scale); 01167 pixm = pixConvertTo8(pixims, FALSE); 01168 pixAnd(pixm, pixm, pixt3); 01169 } 01170 else 01171 pixm = pixClone(pixt3); 01172 pixDestroy(&pixt1); 01173 pixDestroy(&pixt2); 01174 pixDestroy(&pixt3); 01175 01176 /* Fill all the holes in the map. */ 01177 nx = pixGetWidth(pixs) / reduction; 01178 ny = pixGetHeight(pixs) / reduction; 01179 if (pixFillMapHoles(pixm, nx, ny, L_FILL_BLACK)) { 01180 pixDestroy(&pixm); 01181 L_WARNING("can't make the map", procName); 01182 return 1; 01183 } 01184 01185 /* Finally, for each connected region corresponding to the 01186 * fg mask, reset all pixels to their average value. */ 01187 if (pixim && fgpixels) { 01188 pixSmoothConnectedRegions(pixm, pixims, 2); 01189 pixDestroy(&pixims); 01190 } 01191 01192 *ppixm = pixm; 01193 return 0; 01194 } 01195 01196 01197 /*! 01198 * pixGetBackgroundRGBMapMorph() 01199 * 01200 * Input: pixs (32 bpp rgb) 01201 * pixim (<optional> 1 bpp 'image' mask; can be null; it 01202 * should not have all foreground pixels) 01203 * reduction (factor at which closing is performed) 01204 * size (of square Sel for the closing; use an odd number) 01205 * &pixmr (<return> red component map) 01206 * &pixmg (<return> green component map) 01207 * &pixmb (<return> blue component map) 01208 * Return: 0 if OK, 1 on error 01209 */ 01210 l_int32 01211 pixGetBackgroundRGBMapMorph(PIX *pixs, 01212 PIX *pixim, 01213 l_int32 reduction, 01214 l_int32 size, 01215 PIX **ppixmr, 01216 PIX **ppixmg, 01217 PIX **ppixmb) 01218 { 01219 l_int32 nx, ny, empty, fgpixels; 01220 l_float32 scale; 01221 PIX *pixm, *pixmr, *pixmg, *pixmb, *pixt1, *pixt2, *pixt3, *pixims; 01222 01223 PROCNAME("pixGetBackgroundRGBMapMorph"); 01224 01225 if (!ppixmr || !ppixmg || !ppixmb) 01226 return ERROR_INT("&pixm* not all defined", procName, 1); 01227 *ppixmr = *ppixmg = *ppixmb = NULL; 01228 if (!pixs) 01229 return ERROR_INT("pixs not defined", procName, 1); 01230 if (pixGetDepth(pixs) != 32) 01231 return ERROR_INT("pixs not 32 bpp", procName, 1); 01232 if (pixim && pixGetDepth(pixim) != 1) 01233 return ERROR_INT("pixim not 1 bpp", procName, 1); 01234 01235 /* Generate an 8 bpp version of the image mask, if it exists */ 01236 scale = 1. / (l_float32)reduction; 01237 pixm = NULL; 01238 if (pixim) { 01239 pixims = pixScale(pixim, scale, scale); 01240 pixm = pixConvertTo8(pixims, FALSE); 01241 } 01242 01243 /* Evaluate the mask pixim and make sure it is not all foreground. */ 01244 fgpixels = 0; /* boolean for existence of fg mask pixels */ 01245 if (pixim) { 01246 pixInvert(pixim, pixim); /* set background pixels to 1 */ 01247 pixZero(pixim, &empty); 01248 if (empty) 01249 return ERROR_INT("pixim all fg; no background", procName, 1); 01250 pixInvert(pixim, pixim); /* revert to original mask */ 01251 pixZero(pixim, &empty); 01252 if (!empty) /* there are fg pixels in pixim */ 01253 fgpixels = 1; 01254 } 01255 01256 /* Downscale as requested and do the closing to get the background. 01257 * Then remove the image mask pixels from the background. They 01258 * will be filled in (twice) later. Do this for all 3 components. */ 01259 pixt1 = pixScaleRGBToGrayFast(pixs, reduction, COLOR_RED); 01260 pixt2 = pixCloseGray(pixt1, size, size); 01261 pixt3 = pixExtendByReplication(pixt2, 1, 1); 01262 if (pixim) 01263 pixmr = pixAnd(NULL, pixm, pixt3); 01264 else 01265 pixmr = pixClone(pixt3); 01266 pixDestroy(&pixt1); 01267 pixDestroy(&pixt2); 01268 pixDestroy(&pixt3); 01269 01270 pixt1 = pixScaleRGBToGrayFast(pixs, reduction, COLOR_GREEN); 01271 pixt2 = pixCloseGray(pixt1, size, size); 01272 pixt3 = pixExtendByReplication(pixt2, 1, 1); 01273 if (pixim) 01274 pixmg = pixAnd(NULL, pixm, pixt3); 01275 else 01276 pixmg = pixClone(pixt3); 01277 pixDestroy(&pixt1); 01278 pixDestroy(&pixt2); 01279 pixDestroy(&pixt3); 01280 01281 pixt1 = pixScaleRGBToGrayFast(pixs, reduction, COLOR_BLUE); 01282 pixt2 = pixCloseGray(pixt1, size, size); 01283 pixt3 = pixExtendByReplication(pixt2, 1, 1); 01284 if (pixim) 01285 pixmb = pixAnd(NULL, pixm, pixt3); 01286 else 01287 pixmb = pixClone(pixt3); 01288 pixDestroy(&pixm); 01289 pixDestroy(&pixt1); 01290 pixDestroy(&pixt2); 01291 pixDestroy(&pixt3); 01292 01293 /* Fill all the holes in the three maps. */ 01294 nx = pixGetWidth(pixs) / reduction; 01295 ny = pixGetHeight(pixs) / reduction; 01296 if (pixFillMapHoles(pixmr, nx, ny, L_FILL_BLACK) || 01297 pixFillMapHoles(pixmg, nx, ny, L_FILL_BLACK) || 01298 pixFillMapHoles(pixmb, nx, ny, L_FILL_BLACK)) { 01299 pixDestroy(&pixmr); 01300 pixDestroy(&pixmg); 01301 pixDestroy(&pixmb); 01302 L_WARNING("can't make the maps", procName); 01303 return 1; 01304 } 01305 01306 /* Finally, for each connected region corresponding to the 01307 * fg mask in each component, reset all pixels to their 01308 * average value. */ 01309 if (pixim && fgpixels) { 01310 pixSmoothConnectedRegions(pixmr, pixims, 2); 01311 pixSmoothConnectedRegions(pixmg, pixims, 2); 01312 pixSmoothConnectedRegions(pixmb, pixims, 2); 01313 pixDestroy(&pixims); 01314 } 01315 01316 *ppixmr = pixmr; 01317 *ppixmg = pixmg; 01318 *ppixmb = pixmb; 01319 return 0; 01320 } 01321 01322 01323 /*! 01324 * pixFillMapHoles() 01325 * 01326 * Input: pix (8 bpp; a map, with one pixel for each tile in 01327 * a larger image) 01328 * nx (number of horizontal pixel tiles that are entirely 01329 * covered with pixels in the original source image) 01330 * ny (ditto for the number of vertical pixel tiles) 01331 * filltype (L_FILL_WHITE or L_FILL_BLACK) 01332 * Return: 0 if OK, 1 on error 01333 * 01334 * Notes: 01335 * (1) This is an in-place operation on pix (the map). pix is 01336 * typically a low-resolution version of some other image 01337 * from which it was derived, where each pixel in pix 01338 * corresponds to a rectangular tile (say, m x n) of pixels 01339 * in the larger image. All we need to know about the larger 01340 * image is whether or not the rightmost column and bottommost 01341 * row of pixels in pix correspond to tiles that are 01342 * only partially covered by pixels in the larger image. 01343 * (2) Typically, some number of pixels in the input map are 01344 * not known, and their values must be determined by near 01345 * pixels that are known. These unknown pixels are the 'holes'. 01346 * They can take on only two values, 0 and 255, and the 01347 * instruction about which to fill is given by the filltype flag. 01348 * (3) The "holes" can come from two sources. The first is when there 01349 * are not enough foreground or background pixels in a tile; 01350 * the second is when a tile is at least partially covered 01351 * by an image mask. If we're filling holes in a fg mask, 01352 * the holes are initialized to black (0) and use L_FILL_BLACK. 01353 * For filling holes in a bg mask, initialize the holes to 01354 * white (255) and use L_FILL_WHITE. 01355 * (4) If w is the map width, nx = w or nx = w - 1; ditto for h and ny. 01356 */ 01357 l_int32 01358 pixFillMapHoles(PIX *pix, 01359 l_int32 nx, 01360 l_int32 ny, 01361 l_int32 filltype) 01362 { 01363 l_int32 w, h, y, nmiss, goodcol, i, j, found, ival, valtest; 01364 l_uint32 val, lastval; 01365 NUMA *na; /* indicates if there is any data in the column */ 01366 PIX *pixt; 01367 01368 PROCNAME("pixFillMapHoles"); 01369 01370 if (!pix || pixGetDepth(pix) != 8) 01371 return ERROR_INT("pix not defined or not 8 bpp", procName, 1); 01372 if (pixGetColormap(pix)) 01373 return ERROR_INT("pix is colormapped", procName, 1); 01374 01375 /* ------------- Fill holes in the mapping image columns ----------- */ 01376 pixGetDimensions(pix, &w, &h, NULL); 01377 na = numaCreate(0); /* holds flag for which columns have data */ 01378 nmiss = 0; 01379 valtest = (filltype == L_FILL_WHITE) ? 255 : 0; 01380 for (j = 0; j < nx; j++) { /* do it by columns */ 01381 found = FALSE; 01382 for (i = 0; i < ny; i++) { 01383 pixGetPixel(pix, j, i, &val); 01384 if (val != valtest) { 01385 y = i; 01386 found = TRUE; 01387 break; 01388 } 01389 } 01390 if (found == FALSE) { 01391 numaAddNumber(na, 0); /* no data in the column */ 01392 nmiss++; 01393 } 01394 else { 01395 numaAddNumber(na, 1); /* data in the column */ 01396 for (i = y - 1; i >= 0; i--) /* replicate upwards to top */ 01397 pixSetPixel(pix, j, i, val); 01398 pixGetPixel(pix, j, 0, &lastval); 01399 for (i = 1; i < h; i++) { /* set going down to bottom */ 01400 pixGetPixel(pix, j, i, &val); 01401 if (val == valtest) 01402 pixSetPixel(pix, j, i, lastval); 01403 else 01404 lastval = val; 01405 } 01406 } 01407 } 01408 numaAddNumber(na, 0); /* last column */ 01409 01410 if (nmiss == nx) { /* no data in any column! */ 01411 numaDestroy(&na); 01412 L_WARNING("no bg found; no data in any column", procName); 01413 return 1; 01414 } 01415 01416 /* ---------- Fill in missing columns by replication ----------- */ 01417 if (nmiss > 0) { /* replicate columns */ 01418 pixt = pixCopy(NULL, pix); 01419 /* Find the first good column */ 01420 goodcol = 0; 01421 for (j = 0; j < w; j++) { 01422 numaGetIValue(na, j, &ival); 01423 if (ival == 1) { 01424 goodcol = j; 01425 break; 01426 } 01427 } 01428 if (goodcol > 0) { /* copy cols backward */ 01429 for (j = goodcol - 1; j >= 0; j--) { 01430 pixRasterop(pix, j, 0, 1, h, PIX_SRC, pixt, j + 1, 0); 01431 pixRasterop(pixt, j, 0, 1, h, PIX_SRC, pix, j, 0); 01432 } 01433 } 01434 for (j = goodcol + 1; j < w; j++) { /* copy cols forward */ 01435 numaGetIValue(na, j, &ival); 01436 if (ival == 0) { 01437 /* Copy the column to the left of j */ 01438 pixRasterop(pix, j, 0, 1, h, PIX_SRC, pixt, j - 1, 0); 01439 pixRasterop(pixt, j, 0, 1, h, PIX_SRC, pix, j, 0); 01440 } 01441 } 01442 pixDestroy(&pixt); 01443 } 01444 if (w > nx) { /* replicate the last column */ 01445 for (i = 0; i < h; i++) { 01446 pixGetPixel(pix, w - 2, i, &val); 01447 pixSetPixel(pix, w - 1, i, val); 01448 } 01449 } 01450 01451 numaDestroy(&na); 01452 return 0; 01453 } 01454 01455 01456 /*! 01457 * pixExtendByReplication() 01458 * 01459 * Input: pixs (8 bpp) 01460 * addw (number of extra pixels horizontally to add) 01461 * addh (number of extra pixels vertically to add) 01462 * Return: pixd (extended with replicated pixel values), or null on error 01463 * 01464 * Notes: 01465 * (1) The pixel values are extended to the left and down, as required. 01466 */ 01467 PIX * 01468 pixExtendByReplication(PIX *pixs, 01469 l_int32 addw, 01470 l_int32 addh) 01471 { 01472 l_int32 w, h, i, j; 01473 l_uint32 val; 01474 PIX *pixd; 01475 01476 PROCNAME("pixExtendByReplication"); 01477 01478 if (!pixs || pixGetDepth(pixs) != 8) 01479 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); 01480 01481 if (addw == 0 && addh == 0) 01482 return pixCopy(NULL, pixs); 01483 01484 pixGetDimensions(pixs, &w, &h, NULL); 01485 if ((pixd = pixCreate(w + addw, h + addh, 8)) == NULL) 01486 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 01487 pixRasterop(pixd, 0, 0, w, h, PIX_SRC, pixs, 0, 0); 01488 01489 if (addw > 0) { 01490 for (i = 0; i < h; i++) { 01491 pixGetPixel(pixd, w - 1, i, &val); 01492 for (j = 0; j < addw; j++) 01493 pixSetPixel(pixd, w + j, i, val); 01494 } 01495 } 01496 01497 if (addh > 0) { 01498 for (j = 0; j < w + addw; j++) { 01499 pixGetPixel(pixd, j, h - 1, &val); 01500 for (i = 0; i < addh; i++) 01501 pixSetPixel(pixd, j, h + i, val); 01502 } 01503 } 01504 01505 return pixd; 01506 } 01507 01508 01509 /*! 01510 * pixSmoothConnectedRegions() 01511 * 01512 * Input: pixs (8 bpp grayscale; no colormap) 01513 * pixm (<optional> 1 bpp; if null, this is a no-op) 01514 * factor (subsampling factor for getting average; >= 1) 01515 * Return: 0 if OK, 1 on error 01516 * 01517 * Notes: 01518 * (1) The pixels in pixs corresponding to those in each 01519 * 8-connected region in the mask are set to the average value. 01520 * (2) This is required for adaptive mapping to avoid the 01521 * generation of stripes in the background map, due to 01522 * variations in the pixel values near the edges of mask regions. 01523 * (3) This function is optimized for background smoothing, where 01524 * there are a relatively small number of components. It will 01525 * be inefficient if used where there are many small components. 01526 */ 01527 l_int32 01528 pixSmoothConnectedRegions(PIX *pixs, 01529 PIX *pixm, 01530 l_int32 factor) 01531 { 01532 l_int32 empty, i, n, x, y; 01533 l_float32 aveval; 01534 BOXA *boxa; 01535 PIX *pixmc; 01536 PIXA *pixa; 01537 01538 PROCNAME("pixSmoothConnectedRegions"); 01539 01540 if (!pixs || pixGetDepth(pixs) != 8) 01541 return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); 01542 if (pixGetColormap(pixs)) 01543 return ERROR_INT("pixs has colormap", procName, 1); 01544 if (!pixm) { 01545 L_INFO("pixm not defined", procName); 01546 return 0; 01547 } 01548 if (pixGetDepth(pixm) != 1) 01549 return ERROR_INT("pixm not 1 bpp", procName, 1); 01550 pixZero(pixm, &empty); 01551 if (empty) { 01552 L_INFO("pixm has no fg pixels; nothing to do", procName); 01553 return 0; 01554 } 01555 01556 boxa = pixConnComp(pixm, &pixa, 8); 01557 n = boxaGetCount(boxa); 01558 for (i = 0; i < n; i++) { 01559 if ((pixmc = pixaGetPix(pixa, i, L_CLONE)) == NULL) { 01560 L_WARNING("missing pixmc!", procName); 01561 continue; 01562 } 01563 boxaGetBoxGeometry(boxa, i, &x, &y, NULL, NULL); 01564 pixGetAverageMasked(pixs, pixmc, x, y, factor, L_MEAN_ABSVAL, &aveval); 01565 pixPaintThroughMask(pixs, pixmc, x, y, (l_int32)aveval); 01566 pixDestroy(&pixmc); 01567 } 01568 01569 boxaDestroy(&boxa); 01570 pixaDestroy(&pixa); 01571 return 0; 01572 } 01573 01574 01575 /*------------------------------------------------------------------* 01576 * Measurement of local foreground * 01577 *------------------------------------------------------------------*/ 01578 #if 0 /* Not working properly: do not use */ 01579 01580 /*! 01581 * pixGetForegroundGrayMap() 01582 * 01583 * Input: pixs (8 bpp) 01584 * pixim (<optional> 1 bpp 'image' mask; can be null) 01585 * sx, sy (src tile size, in pixels) 01586 * thresh (threshold for determining foreground) 01587 * &pixd (<return> 8 bpp grayscale map) 01588 * Return: 0 if OK, 1 on error 01589 * 01590 * Notes: 01591 * (1) Each (sx, sy) tile of pixs gets mapped to one pixel in pixd. 01592 * (2) pixd is the estimate of the fg (darkest) value within each tile. 01593 * (3) All pixels in pixd that are in 'image' regions, as specified 01594 * by pixim, are given the background value 0. 01595 * (4) For pixels in pixd that can't directly be given a fg value, 01596 * the value is inferred by propagating from neighboring pixels. 01597 * (5) In practice, pixd can be used to normalize the fg, and 01598 * it can be done after background normalization. 01599 * (6) The overall procedure is: 01600 * - reduce 2x by sampling 01601 * - paint all 'image' pixels white, so that they don't 01602 * participate in the Min reduction 01603 * - do a further (sx, sy) Min reduction -- think of 01604 * it as a large opening followed by subsampling by the 01605 * reduction factors 01606 * - threshold the result to identify fg, and set the 01607 * bg pixels to 255 (these are 'holes') 01608 * - fill holes by propagation from fg values 01609 * - replicatively expand by 2x, arriving at the final 01610 * resolution of pixd 01611 * - smooth with a 17x17 kernel 01612 * - paint the 'image' regions black 01613 */ 01614 l_int32 01615 pixGetForegroundGrayMap(PIX *pixs, 01616 PIX *pixim, 01617 l_int32 sx, 01618 l_int32 sy, 01619 l_int32 thresh, 01620 PIX **ppixd) 01621 { 01622 l_int32 w, h, d, wd, hd; 01623 l_int32 empty, fgpixels; 01624 PIX *pixd, *piximi, *pixim2, *pixims, *pixs2, *pixb, *pixt1, *pixt2, *pixt3; 01625 01626 PROCNAME("pixGetForegroundGrayMap"); 01627 01628 if (!ppixd) 01629 return ERROR_INT("&pixd not defined", procName, 1); 01630 *ppixd = NULL; 01631 if (!pixs) 01632 return ERROR_INT("pixs not defined", procName, 1); 01633 pixGetDimensions(pixs, &w, &h, &d); 01634 if (d != 8) 01635 return ERROR_INT("pixs not 8 bpp", procName, 1); 01636 if (pixim && pixGetDepth(pixim) != 1) 01637 return ERROR_INT("pixim not 1 bpp", procName, 1); 01638 if (sx < 2 || sy < 2) 01639 return ERROR_INT("sx and sy must be >= 2", procName, 1); 01640 01641 /* Generate pixd, which is reduced by the factors (sx, sy). */ 01642 wd = (w + sx - 1) / sx; 01643 hd = (h + sy - 1) / sy; 01644 pixd = pixCreate(wd, hd, 8); 01645 *ppixd = pixd; 01646 01647 /* Evaluate the 'image' mask, pixim. If it is all fg, 01648 * the output pixd has all pixels with value 0. */ 01649 fgpixels = 0; /* boolean for existence of fg pixels in the image mask. */ 01650 if (pixim) { 01651 piximi = pixInvert(NULL, pixim); /* set non-image pixels to 1 */ 01652 pixZero(piximi, &empty); 01653 pixDestroy(&piximi); 01654 if (empty) /* all 'image'; return with all pixels set to 0 */ 01655 return 0; 01656 pixZero(pixim, &empty); 01657 if (!empty) /* there are fg pixels in pixim */ 01658 fgpixels = 1; 01659 } 01660 01661 /* 2x subsampling; paint white through 'image' mask. */ 01662 pixs2 = pixScaleBySampling(pixs, 0.5, 0.5); 01663 if (pixim && fgpixels) { 01664 pixim2 = pixReduceBinary2(pixim, NULL); 01665 pixPaintThroughMask(pixs2, pixim2, 0, 0, 255); 01666 pixDestroy(&pixim2); 01667 } 01668 01669 /* Min (erosion) downscaling; total reduction (4 sx, 4 sy). */ 01670 pixt1 = pixScaleGrayMinMax(pixs2, sx, sy, L_CHOOSE_MIN); 01671 01672 /* pixDisplay(pixt1, 300, 200); */ 01673 01674 /* Threshold to identify fg; paint bg pixels to white. */ 01675 pixb = pixThresholdToBinary(pixt1, thresh); /* fg pixels */ 01676 pixInvert(pixb, pixb); 01677 pixPaintThroughMask(pixt1, pixb, 0, 0, 255); 01678 pixDestroy(&pixb); 01679 01680 /* Replicative expansion by 2x to (sx, sy). */ 01681 pixt2 = pixExpandReplicate(pixt1, 2); 01682 01683 /* pixDisplay(pixt2, 500, 200); */ 01684 01685 /* Fill holes in the fg by propagation */ 01686 pixFillMapHoles(pixt2, w / sx, h / sy, L_FILL_WHITE); 01687 01688 /* pixDisplay(pixt2, 700, 200); */ 01689 01690 /* Smooth with 17x17 kernel. */ 01691 pixt3 = pixBlockconv(pixt2, 8, 8); 01692 pixRasterop(pixd, 0, 0, wd, hd, PIX_SRC, pixt3, 0, 0); 01693 01694 /* Paint the image parts black. */ 01695 pixims = pixScaleBySampling(pixim, 1. / sx, 1. / sy); 01696 pixPaintThroughMask(pixd, pixims, 0, 0, 0); 01697 01698 pixDestroy(&pixs2); 01699 pixDestroy(&pixt1); 01700 pixDestroy(&pixt2); 01701 pixDestroy(&pixt3); 01702 return 0; 01703 } 01704 #endif /* Not working properly: do not use */ 01705 01706 01707 /*------------------------------------------------------------------* 01708 * Generate inverted background map * 01709 *------------------------------------------------------------------*/ 01710 /*! 01711 * pixGetInvBackgroundMap() 01712 * 01713 * Input: pixs (8 bpp grayscale; no colormap) 01714 * bgval (target bg val; typ. > 128) 01715 * smoothx (half-width of block convolution kernel width) 01716 * smoothy (half-width of block convolution kernel height) 01717 * Return: pixd (16 bpp), or null on error 01718 * 01719 * Note: 01720 * - bgval should typically be > 120 and < 240 01721 * - pixd is a normalization image; the original image is 01722 * multiplied by pixd and the result is divided by 256. 01723 */ 01724 PIX * 01725 pixGetInvBackgroundMap(PIX *pixs, 01726 l_int32 bgval, 01727 l_int32 smoothx, 01728 l_int32 smoothy) 01729 { 01730 l_int32 w, h, wplsm, wpld, i, j; 01731 l_int32 val, val16; 01732 l_uint32 *datasm, *datad, *linesm, *lined; 01733 PIX *pixsm, *pixd; 01734 01735 PROCNAME("pixGetInvBackgroundMap"); 01736 01737 if (!pixs || pixGetDepth(pixs) != 8) 01738 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); 01739 if (pixGetColormap(pixs)) 01740 return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); 01741 pixGetDimensions(pixs, &w, &h, NULL); 01742 if (w < 5 || h < 5) 01743 return (PIX *)ERROR_PTR("w and h must be >= 5", procName, NULL); 01744 01745 /* smooth the map image */ 01746 pixsm = pixBlockconv(pixs, smoothx, smoothy); 01747 datasm = pixGetData(pixsm); 01748 wplsm = pixGetWpl(pixsm); 01749 01750 /* invert the map image, scaling up to preserve dynamic range */ 01751 pixd = pixCreate(w, h, 16); 01752 datad = pixGetData(pixd); 01753 wpld = pixGetWpl(pixd); 01754 for (i = 0; i < h; i++) { 01755 linesm = datasm + i * wplsm; 01756 lined = datad + i * wpld; 01757 for (j = 0; j < w; j++) { 01758 val = GET_DATA_BYTE(linesm, j); 01759 if (val > 0) 01760 val16 = (256 * bgval) / val; 01761 else { /* shouldn't happen */ 01762 L_WARNING("smoothed bg has 0 pixel!", procName); 01763 val16 = bgval / 2; 01764 } 01765 SET_DATA_TWO_BYTES(lined, j, val16); 01766 } 01767 } 01768 01769 pixDestroy(&pixsm); 01770 return pixd; 01771 } 01772 01773 01774 /*------------------------------------------------------------------* 01775 * Apply background map to image * 01776 *------------------------------------------------------------------*/ 01777 /*! 01778 * pixApplyInvBackgroundGrayMap() 01779 * 01780 * Input: pixs (8 bpp grayscale; no colormap) 01781 * pixm (16 bpp, inverse background map) 01782 * sx (tile width in pixels) 01783 * sy (tile height in pixels) 01784 * Return: pixd (8 bpp), or null on error 01785 */ 01786 PIX * 01787 pixApplyInvBackgroundGrayMap(PIX *pixs, 01788 PIX *pixm, 01789 l_int32 sx, 01790 l_int32 sy) 01791 { 01792 l_int32 w, h, wm, hm, wpls, wpld, i, j, k, m, xoff, yoff; 01793 l_int32 vals, vald; 01794 l_uint32 val16; 01795 l_uint32 *datas, *datad, *lines, *lined, *flines, *flined; 01796 PIX *pixd; 01797 01798 PROCNAME("pixApplyInvBackgroundGrayMap"); 01799 01800 if (!pixs || pixGetDepth(pixs) != 8) 01801 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); 01802 if (pixGetColormap(pixs)) 01803 return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); 01804 if (!pixm || pixGetDepth(pixm) != 16) 01805 return (PIX *)ERROR_PTR("pixm undefined or not 16 bpp", procName, NULL); 01806 if (sx == 0 || sy == 0) 01807 return (PIX *)ERROR_PTR("invalid sx and/or sy", procName, NULL); 01808 01809 datas = pixGetData(pixs); 01810 wpls = pixGetWpl(pixs); 01811 pixGetDimensions(pixs, &w, &h, NULL); 01812 pixGetDimensions(pixm, &wm, &hm, NULL); 01813 pixd = pixCreateTemplate(pixs); 01814 datad = pixGetData(pixd); 01815 wpld = pixGetWpl(pixd); 01816 for (i = 0; i < hm; i++) { 01817 lines = datas + sy * i * wpls; 01818 lined = datad + sy * i * wpld; 01819 yoff = sy * i; 01820 for (j = 0; j < wm; j++) { 01821 pixGetPixel(pixm, j, i, &val16); 01822 xoff = sx * j; 01823 for (k = 0; k < sy && yoff + k < h; k++) { 01824 flines = lines + k * wpls; 01825 flined = lined + k * wpld; 01826 for (m = 0; m < sx && xoff + m < w; m++) { 01827 vals = GET_DATA_BYTE(flines, xoff + m); 01828 vald = (vals * val16) / 256; 01829 vald = L_MIN(vald, 255); 01830 SET_DATA_BYTE(flined, xoff + m, vald); 01831 } 01832 } 01833 } 01834 } 01835 01836 return pixd; 01837 } 01838 01839 01840 /*! 01841 * pixApplyInvBackgroundRGBMap() 01842 * 01843 * Input: pixs (32 bpp rbg) 01844 * pixmr (16 bpp, red inverse background map) 01845 * pixmg (16 bpp, green inverse background map) 01846 * pixmb (16 bpp, blue inverse background map) 01847 * sx (tile width in pixels) 01848 * sy (tile height in pixels) 01849 * Return: pixd (32 bpp rbg), or null on error 01850 */ 01851 PIX * 01852 pixApplyInvBackgroundRGBMap(PIX *pixs, 01853 PIX *pixmr, 01854 PIX *pixmg, 01855 PIX *pixmb, 01856 l_int32 sx, 01857 l_int32 sy) 01858 { 01859 l_int32 w, h, wm, hm, wpls, wpld, i, j, k, m, xoff, yoff; 01860 l_int32 rvald, gvald, bvald; 01861 l_uint32 vals; 01862 l_uint32 rval16, gval16, bval16; 01863 l_uint32 *datas, *datad, *lines, *lined, *flines, *flined; 01864 PIX *pixd; 01865 01866 PROCNAME("pixApplyInvBackgroundRGBMap"); 01867 01868 if (!pixs) 01869 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01870 if (pixGetDepth(pixs) != 32) 01871 return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); 01872 if (!pixmr || !pixmg || !pixmb) 01873 return (PIX *)ERROR_PTR("pix maps not all defined", procName, NULL); 01874 if (pixGetDepth(pixmr) != 16 || pixGetDepth(pixmg) != 16 || 01875 pixGetDepth(pixmb) != 16) 01876 return (PIX *)ERROR_PTR("pix maps not all 16 bpp", procName, NULL); 01877 if (sx == 0 || sy == 0) 01878 return (PIX *)ERROR_PTR("invalid sx and/or sy", procName, NULL); 01879 01880 datas = pixGetData(pixs); 01881 wpls = pixGetWpl(pixs); 01882 w = pixGetWidth(pixs); 01883 h = pixGetHeight(pixs); 01884 wm = pixGetWidth(pixmr); 01885 hm = pixGetHeight(pixmr); 01886 pixd = pixCreateTemplate(pixs); 01887 datad = pixGetData(pixd); 01888 wpld = pixGetWpl(pixd); 01889 for (i = 0; i < hm; i++) { 01890 lines = datas + sy * i * wpls; 01891 lined = datad + sy * i * wpld; 01892 yoff = sy * i; 01893 for (j = 0; j < wm; j++) { 01894 pixGetPixel(pixmr, j, i, &rval16); 01895 pixGetPixel(pixmg, j, i, &gval16); 01896 pixGetPixel(pixmb, j, i, &bval16); 01897 xoff = sx * j; 01898 for (k = 0; k < sy && yoff + k < h; k++) { 01899 flines = lines + k * wpls; 01900 flined = lined + k * wpld; 01901 for (m = 0; m < sx && xoff + m < w; m++) { 01902 vals = *(flines + xoff + m); 01903 rvald = ((vals >> 24) * rval16) / 256; 01904 rvald = L_MIN(rvald, 255); 01905 gvald = (((vals >> 16) & 0xff) * gval16) / 256; 01906 gvald = L_MIN(gvald, 255); 01907 bvald = (((vals >> 8) & 0xff) * bval16) / 256; 01908 bvald = L_MIN(bvald, 255); 01909 composeRGBPixel(rvald, gvald, bvald, flined + xoff + m); 01910 } 01911 } 01912 } 01913 } 01914 01915 return pixd; 01916 } 01917 01918 01919 /*------------------------------------------------------------------* 01920 * Apply variable map * 01921 *------------------------------------------------------------------*/ 01922 /*! 01923 * pixApplyVariableGrayMap() 01924 * 01925 * Input: pixs (8 bpp) 01926 * pixg (8 bpp, variable map) 01927 * target (typ. 128 for threshold) 01928 * Return: pixd (8 bpp), or null on error 01929 * 01930 * Notes: 01931 * (1) Suppose you have an image that you want to transform based 01932 * on some photometric measurement at each point, such as the 01933 * threshold value for binarization. Representing the photometric 01934 * measurement as an image pixg, you can threshold in input image 01935 * using pixVarThresholdToBinary(). Alternatively, you can map 01936 * the input image pointwise so that the threshold over the 01937 * entire image becomes a constant, such as 128. For example, 01938 * if a pixel in pixg is 150 and the target is 128, the 01939 * corresponding pixel in pixs is mapped linearly to a value 01940 * (128/150) of the input value. If the resulting mapped image 01941 * pixd were then thresholded at 128, you would obtain the 01942 * same result as a direct binarization using pixg with 01943 * pixVarThresholdToBinary(). 01944 * (2) The sizes of pixs and pixg must be equal. 01945 */ 01946 PIX * 01947 pixApplyVariableGrayMap(PIX *pixs, 01948 PIX *pixg, 01949 l_int32 target) 01950 { 01951 l_int32 i, j, w, h, d, wpls, wplg, wpld, vals, valg, vald; 01952 l_uint8 *lut; 01953 l_uint32 *datas, *datag, *datad, *lines, *lineg, *lined; 01954 l_float32 fval; 01955 PIX *pixd; 01956 01957 PROCNAME("pixApplyVariableGrayMap"); 01958 01959 if (!pixs) 01960 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01961 if (!pixg) 01962 return (PIX *)ERROR_PTR("pixg not defined", procName, NULL); 01963 if (!pixSizesEqual(pixs, pixg)) 01964 return (PIX *)ERROR_PTR("pix sizes not equal", procName, NULL); 01965 pixGetDimensions(pixs, &w, &h, &d); 01966 if (d != 8) 01967 return (PIX *)ERROR_PTR("depth not 8 bpp", procName, NULL); 01968 01969 /* Generate a LUT for the mapping if the image is large enough 01970 * to warrant the overhead. The LUT is of size 2^16. For the 01971 * index to the table, get the MSB from pixs and the LSB from pixg. 01972 * Note: this LUT is bigger than the typical 32K L1 cache, so 01973 * we expect cache misses. L2 latencies are about 5ns. But 01974 * division is slooooow. For large images, this function is about 01975 * 4x faster when using the LUT. C'est la vie. */ 01976 lut = NULL; 01977 if (w * h > 100000) { /* more pixels than 2^16 */ 01978 if ((lut = (l_uint8 *)CALLOC(0x10000, sizeof(l_uint8))) == NULL) 01979 return (PIX *)ERROR_PTR("lut not made", procName, NULL); 01980 for (i = 0; i < 256; i++) { 01981 for (j = 0; j < 256; j++) { 01982 fval = (l_float32)(i * target) / (j + 0.5); 01983 lut[(i << 8) + j] = L_MIN(255, (l_int32)(fval + 0.5)); 01984 } 01985 } 01986 } 01987 01988 pixd = pixCreateNoInit(w, h, 8); 01989 datad = pixGetData(pixd); 01990 wpld = pixGetWpl(pixd); 01991 datas = pixGetData(pixs); 01992 wpls = pixGetWpl(pixs); 01993 datag = pixGetData(pixg); 01994 wplg = pixGetWpl(pixg); 01995 for (i = 0; i < h; i++) { 01996 lines = datas + i * wpls; 01997 lineg = datag + i * wplg; 01998 lined = datad + i * wpld; 01999 if (lut) { 02000 for (j = 0; j < w; j++) { 02001 vals = GET_DATA_BYTE(lines, j); 02002 valg = GET_DATA_BYTE(lineg, j); 02003 vald = lut[(vals << 8) + valg]; 02004 SET_DATA_BYTE(lined, j, vald); 02005 } 02006 } 02007 else { 02008 for (j = 0; j < w; j++) { 02009 vals = GET_DATA_BYTE(lines, j); 02010 valg = GET_DATA_BYTE(lineg, j); 02011 fval = (l_float32)(vals * target) / (valg + 0.5); 02012 vald = L_MIN(255, (l_int32)(fval + 0.5)); 02013 SET_DATA_BYTE(lined, j, vald); 02014 } 02015 } 02016 } 02017 02018 if (lut) FREE(lut); 02019 return pixd; 02020 } 02021 02022 02023 /*------------------------------------------------------------------* 02024 * Non-adaptive (global) mapping * 02025 *------------------------------------------------------------------*/ 02026 /*! 02027 * pixGlobalNormRGB() 02028 * 02029 * Input: pixd (<optional> null, existing or equal to pixs) 02030 * pixs (32 bpp rgb, or colormapped) 02031 * rval, gval, bval (pixel values in pixs that are 02032 * linearly mapped to mapval) 02033 * mapval (use 255 for mapping to white) 02034 * Return: pixd (32 bpp rgb or colormapped), or null on error 02035 * 02036 * Notes: 02037 * (1) The value of pixd determines if the results are written to a 02038 * new pix (use NULL), in-place to pixs (use pixs), or to some 02039 * other existing pix. 02040 * (2) This does a global normalization of an image where the 02041 * r,g,b color components are not balanced. Thus, white in pixs is 02042 * represented by a set of r,g,b values that are not all 255. 02043 * (3) The input values (rval, gval, bval) should be chosen to 02044 * represent the gray color (mapval, mapval, mapval) in src. 02045 * Thus, this function will map (rval, gval, bval) to that gray color. 02046 * (4) Typically, mapval = 255, so that (rval, gval, bval) 02047 * corresponds to the white point of src. In that case, these 02048 * parameters should be chosen so that few pixels have higher values. 02049 * (5) In all cases, we do a linear TRC separately on each of the 02050 * components, saturating at 255. 02051 * (6) If the input pix is 8 bpp without a colormap, you can get 02052 * this functionality with mapval = 255 by calling: 02053 * pixGammaTRC(pixd, pixs, 1.0, 0, bgval); 02054 * where bgval is the value you want to be mapped to 255. 02055 * Or more generally, if you want bgval to be mapped to mapval: 02056 * pixGammaTRC(pixd, pixs, 1.0, 0, 255 * bgval / mapval); 02057 */ 02058 PIX * 02059 pixGlobalNormRGB(PIX *pixd, 02060 PIX *pixs, 02061 l_int32 rval, 02062 l_int32 gval, 02063 l_int32 bval, 02064 l_int32 mapval) 02065 { 02066 l_int32 w, h, d, i, j, ncolors, rv, gv, bv, wpl; 02067 l_int32 *rarray, *garray, *barray; 02068 l_uint32 *data, *line; 02069 NUMA *nar, *nag, *nab; 02070 PIXCMAP *cmap; 02071 02072 PROCNAME("pixGlobalNormRGB"); 02073 02074 if (!pixs) 02075 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 02076 cmap = pixGetColormap(pixs); 02077 pixGetDimensions(pixs, &w, &h, &d); 02078 if (!cmap && d != 32) 02079 return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); 02080 if (mapval <= 0) { 02081 L_WARNING("mapval must be > 0; setting to 255", procName); 02082 mapval = 255; 02083 } 02084 02085 /* Prepare pixd to be a copy of pixs */ 02086 if ((pixd = pixCopy(pixd, pixs)) == NULL) 02087 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 02088 02089 /* Generate the TRC maps for each component. Make sure the 02090 * upper range for each color is greater than zero. */ 02091 nar = numaGammaTRC(1.0, 0, L_MAX(1, 255 * rval / mapval)); 02092 nag = numaGammaTRC(1.0, 0, L_MAX(1, 255 * gval / mapval)); 02093 nab = numaGammaTRC(1.0, 0, L_MAX(1, 255 * bval / mapval)); 02094 if (!nar || !nag || !nab) 02095 return (PIX *)ERROR_PTR("trc maps not all made", procName, pixd); 02096 02097 /* Extract copies of the internal arrays */ 02098 rarray = numaGetIArray(nar); 02099 garray = numaGetIArray(nag); 02100 barray = numaGetIArray(nab); 02101 if (!rarray || !garray || !barray) 02102 return (PIX *)ERROR_PTR("*arrays not all made", procName, pixd); 02103 02104 if (cmap) { 02105 ncolors = pixcmapGetCount(cmap); 02106 for (i = 0; i < ncolors; i++) { 02107 pixcmapGetColor(cmap, i, &rv, &gv, &bv); 02108 pixcmapResetColor(cmap, i, rarray[rv], garray[gv], barray[bv]); 02109 } 02110 } 02111 else { 02112 data = pixGetData(pixd); 02113 wpl = pixGetWpl(pixd); 02114 for (i = 0; i < h; i++) { 02115 line = data + i * wpl; 02116 for (j = 0; j < w; j++) { 02117 extractRGBValues(line[j], &rv, &gv, &bv); 02118 composeRGBPixel(rarray[rv], garray[gv], barray[bv], line + j); 02119 } 02120 } 02121 } 02122 02123 numaDestroy(&nar); 02124 numaDestroy(&nag); 02125 numaDestroy(&nab); 02126 FREE(rarray); 02127 FREE(garray); 02128 FREE(barray); 02129 return pixd; 02130 } 02131 02132 02133 /*! 02134 * pixGlobalNormNoSatRGB() 02135 * 02136 * Input: pixd (<optional> null, existing or equal to pixs) 02137 * pixs (32 bpp rgb) 02138 * rval, gval, bval (pixel values in pixs that are 02139 * linearly mapped to mapval; but see below) 02140 * factor (subsampling factor; integer >= 1) 02141 * rank (between 0.0 and 1.0; typ. use a value near 1.0) 02142 * Return: pixd (32 bpp rgb), or null on error 02143 * 02144 * Notes: 02145 * (1) This is a version of pixGlobalNormRGB(), where the output 02146 * intensity is scaled back so that a controlled fraction of 02147 * pixel components is allowed to saturate. See comments in 02148 * pixGlobalNormRGB(). 02149 * (2) The value of pixd determines if the results are written to a 02150 * new pix (use NULL), in-place to pixs (use pixs), or to some 02151 * other existing pix. 02152 * (3) This does a global normalization of an image where the 02153 * r,g,b color components are not balanced. Thus, white in pixs is 02154 * represented by a set of r,g,b values that are not all 255. 02155 * (4) The input values (rval, gval, bval) can be chosen to be the 02156 * color that, after normalization, becomes white background. 02157 * For images that are mostly background, the closer these values 02158 * are to the median component values, the closer the resulting 02159 * background will be to gray, becoming white at the brightest places. 02160 * (5) The mapval used in pixGlobalNormRGB() is computed here to 02161 * avoid saturation of any component in the image (save for a 02162 * fraction of the pixels given by the input rank value). 02163 */ 02164 PIX * 02165 pixGlobalNormNoSatRGB(PIX *pixd, 02166 PIX *pixs, 02167 l_int32 rval, 02168 l_int32 gval, 02169 l_int32 bval, 02170 l_int32 factor, 02171 l_float32 rank) 02172 { 02173 l_int32 mapval; 02174 l_float32 rankrval, rankgval, rankbval; 02175 l_float32 rfract, gfract, bfract, maxfract; 02176 02177 PROCNAME("pixGlobalNormNoSatRGB"); 02178 02179 if (!pixs) 02180 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 02181 if (pixGetDepth(pixs) != 32) 02182 return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); 02183 if (factor < 1) 02184 return (PIX *)ERROR_PTR("sampling factor < 1", procName, NULL); 02185 if (rank < 0.0 || rank > 1.0) 02186 return (PIX *)ERROR_PTR("rank not in [0.0 ... 1.0]", procName, NULL); 02187 if (rval <= 0 || gval <= 0 || bval <= 0) 02188 return (PIX *)ERROR_PTR("invalid estim. color values", procName, NULL); 02189 02190 /* The max value for each component may be larger than the 02191 * input estimated background value. In that case, mapping 02192 * for those pixels would saturate. To prevent saturation, 02193 * we compute the fraction for each component by which we 02194 * would oversaturate. Then take the max of these, and 02195 * reduce, uniformly over all components, the output intensity 02196 * by this value. Then no component will saturate. 02197 * In practice, if rank < 1.0, a fraction of pixels 02198 * may have a component saturate. By keeping rank close to 1.0, 02199 * that fraction can be made arbitrarily small. */ 02200 pixGetRankValueMaskedRGB(pixs, NULL, 0, 0, factor, rank, &rankrval, 02201 &rankgval, &rankbval); 02202 rfract = rankrval / (l_float32)rval; 02203 gfract = rankgval / (l_float32)gval; 02204 bfract = rankbval / (l_float32)bval; 02205 maxfract = L_MAX(rfract, gfract); 02206 maxfract = L_MAX(maxfract, bfract); 02207 #if DEBUG_GLOBAL 02208 fprintf(stderr, "rankrval = %7.2f, rankgval = %7.2f, rankbval = %7.2f\n", 02209 rankrval, rankgval, rankbval); 02210 fprintf(stderr, "rfract = %7.4f, gfract = %7.4f, bfract = %7.4f\n", 02211 rfract, gfract, bfract); 02212 #endif /* DEBUG_GLOBAL */ 02213 02214 mapval = (l_int32)(255. / maxfract); 02215 pixd = pixGlobalNormRGB(pixd, pixs, rval, gval, bval, mapval); 02216 return pixd; 02217 } 02218 02219 02220 /*------------------------------------------------------------------* 02221 * Adaptive threshold spread normalization * 02222 *------------------------------------------------------------------*/ 02223 /*! 02224 * pixThresholdSpreadNorm() 02225 * 02226 * Input: pixs (8 bpp grayscale; not colormapped) 02227 * filtertype (L_SOBEL_EDGE or L_TWO_SIDED_EDGE); 02228 * edgethresh (threshold on magnitude of edge filter; typ 10-20) 02229 * smoothx, smoothy (half-width of convolution kernel applied to 02230 * spread threshold: use 0 for no smoothing) 02231 * gamma (gamma correction; typ. about 0.7) 02232 * minval (input value that gives 0 for output; typ. -25) 02233 * maxval (input value that gives 255 for output; typ. 255) 02234 * targetthresh (target threshold for normalization) 02235 * &pixth (<optional return> computed local threshold value) 02236 * &pixb (<optional return> thresholded normalized image) 02237 * &pixd (<optional return> normalized image) 02238 * Return: 0 if OK, 1 on error 02239 * 02240 * Notes: 02241 * (1) The basis of this approach is the use of seed spreading 02242 * on a (possibly) sparse set of estimates for the local threshold. 02243 * The resulting dense estimates are smoothed by convolution 02244 * and used to either threshold the input image or normalize it 02245 * with a local transformation that linearly maps the pixels so 02246 * that the local threshold estimate becomes constant over the 02247 * resulting image. This approach is one of several that 02248 * have been suggested (and implemented) by Ray Smith. 02249 * (2) You can use either the Sobel or TwoSided edge filters. 02250 * The results appear to be similar, using typical values 02251 * of edgethresh in the rang 10-20. 02252 * (3) To skip the trc enhancement, use gamma = 1.0, minval = 0 02253 * and maxval = 255. 02254 * (4) For the normalized image pixd, each pixel is linearly mapped 02255 * in such a way that the local threshold is equal to targetthresh. 02256 * (5) The full width and height of the convolution kernel 02257 * are (2 * smoothx + 1) and (2 * smoothy + 1). 02258 * (6) This function can be used with the pixtiling utility if the 02259 * images are too large. See pixOtsuAdaptiveThreshold() for 02260 * an example of this. 02261 */ 02262 l_int32 02263 pixThresholdSpreadNorm(PIX *pixs, 02264 l_int32 filtertype, 02265 l_int32 edgethresh, 02266 l_int32 smoothx, 02267 l_int32 smoothy, 02268 l_float32 gamma, 02269 l_int32 minval, 02270 l_int32 maxval, 02271 l_int32 targetthresh, 02272 PIX **ppixth, 02273 PIX **ppixb, 02274 PIX **ppixd) 02275 { 02276 PIX *pixe, *pixet, *pixsd, *pixg1, *pixg2, *pixth; 02277 02278 PROCNAME("pixThresholdSpreadNorm"); 02279 02280 if (ppixth) *ppixth = NULL; 02281 if (ppixb) *ppixb = NULL; 02282 if (ppixd) *ppixd = NULL; 02283 if (!pixs || pixGetDepth(pixs) != 8) 02284 return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); 02285 if (pixGetColormap(pixs)) 02286 return ERROR_INT("pixs is colormapped", procName, 1); 02287 if (!ppixth && !ppixb && !ppixd) 02288 return ERROR_INT("no output requested", procName, 1); 02289 if (filtertype != L_SOBEL_EDGE && filtertype != L_TWO_SIDED_EDGE) 02290 return ERROR_INT("invalid filter type", procName, 1); 02291 02292 /* Get the thresholded edge pixels. These are the ones 02293 * that have values in pixs near the local optimal fg/bg threshold. */ 02294 if (filtertype == L_SOBEL_EDGE) 02295 pixe = pixSobelEdgeFilter(pixs, L_VERTICAL_EDGES); 02296 else /* L_TWO_SIDED_EDGE */ 02297 pixe = pixTwoSidedEdgeFilter(pixs, L_VERTICAL_EDGES); 02298 pixet = pixThresholdToBinary(pixe, edgethresh); 02299 pixInvert(pixet, pixet); 02300 02301 /* Build a seed image whose only nonzero values are those 02302 * values of pixs corresponding to pixels in the fg of pixet. */ 02303 pixsd = pixCreateTemplate(pixs); 02304 pixCombineMasked(pixsd, pixs, pixet); 02305 02306 /* Spread the seed and optionally smooth to reduce noise */ 02307 pixg1 = pixSeedspread(pixsd, 4); 02308 pixg2 = pixBlockconv(pixg1, smoothx, smoothy); 02309 02310 /* Optionally do a gamma enhancement */ 02311 pixth = pixGammaTRC(NULL, pixg2, gamma, minval, maxval); 02312 02313 /* Do the mapping and thresholding */ 02314 if (ppixd) { 02315 *ppixd = pixApplyVariableGrayMap(pixs, pixth, targetthresh); 02316 if (ppixb) 02317 *ppixb = pixThresholdToBinary(*ppixd, targetthresh); 02318 } 02319 else if (ppixb) 02320 *ppixb = pixVarThresholdToBinary(pixs, pixth); 02321 02322 if (ppixth) 02323 *ppixth = pixth; 02324 else 02325 pixDestroy(&pixth); 02326 02327 pixDestroy(&pixe); 02328 pixDestroy(&pixet); 02329 pixDestroy(&pixsd); 02330 pixDestroy(&pixg1); 02331 pixDestroy(&pixg2); 02332 return 0; 02333 } 02334 02335 02336 /*------------------------------------------------------------------* 02337 * Adaptive background normalization (flexible adaptaption) * 02338 *------------------------------------------------------------------*/ 02339 /*! 02340 * pixBackgroundNormFlex() 02341 * 02342 * Input: pixs (8 bpp grayscale; not colormapped) 02343 * sx, sy (desired tile dimensions; actual size may vary; use 02344 * values between 3 and 10) 02345 * smoothx, smoothy (half-width of convolution kernel applied to 02346 * threshold array: use values between 1 and 3) 02347 * delta (difference parameter in basin filling; use 0 02348 * to skip) 02349 * Return: pixd (8 bpp, background-normalized), or null on error) 02350 * 02351 * Notes: 02352 * (1) This does adaptation flexibly to a quickly varying background. 02353 * For that reason, all input parameters should be small. 02354 * (2) sx and sy give the tile size; they should be in [5 - 7]. 02355 * (3) The full width and height of the convolution kernel 02356 * are (2 * smoothx + 1) and (2 * smoothy + 1). They 02357 * should be in [1 - 2]. 02358 * (4) Basin filling is used to fill the large fg regions. The 02359 * parameter @delta measures the height that the black 02360 * background is raised from the local minima. By raising 02361 * the background, it is possible to threshold the large 02362 * fg regions to foreground. If @delta is too large, 02363 * bg regions will be lifted, causing thickening of 02364 * the fg regions. Use 0 to skip. 02365 */ 02366 PIX * 02367 pixBackgroundNormFlex(PIX *pixs, 02368 l_int32 sx, 02369 l_int32 sy, 02370 l_int32 smoothx, 02371 l_int32 smoothy, 02372 l_int32 delta) 02373 { 02374 l_float32 scalex, scaley; 02375 PIX *pixt, *pixsd, *pixmin, *pixbg, *pixbgi, *pixd; 02376 02377 PROCNAME("pixBackgroundNormFlex"); 02378 02379 if (!pixs || pixGetDepth(pixs) != 8) 02380 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); 02381 if (pixGetColormap(pixs)) 02382 return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL); 02383 if (sx < 3 || sy < 3) 02384 return (PIX *)ERROR_PTR("sx and/or sy less than 3", procName, NULL); 02385 if (sx > 10 || sy > 10) 02386 return (PIX *)ERROR_PTR("sx and/or sy exceed 10", procName, NULL); 02387 if (smoothx < 1 || smoothy < 1) 02388 return (PIX *)ERROR_PTR("smooth params less than 1", procName, NULL); 02389 if (smoothx > 3 || smoothy > 3) 02390 return (PIX *)ERROR_PTR("smooth params exceed 3", procName, NULL); 02391 02392 /* Generate the bg estimate using smoothed average with subsampling */ 02393 scalex = 1. / (l_float32)sx; 02394 scaley = 1. / (l_float32)sy; 02395 pixt = pixScaleSmooth(pixs, scalex, scaley); 02396 02397 /* Do basin filling on the bg estimate if requested */ 02398 if (delta <= 0) 02399 pixsd = pixClone(pixt); 02400 else { 02401 pixLocalExtrema(pixt, 0, 0, &pixmin, NULL); 02402 pixsd = pixSeedfillGrayBasin(pixmin, pixt, delta, 4); 02403 pixDestroy(&pixmin); 02404 } 02405 pixbg = pixExtendByReplication(pixsd, 1, 1); 02406 02407 /* Map the bg to 200 */ 02408 pixbgi = pixGetInvBackgroundMap(pixbg, 200, smoothx, smoothy); 02409 pixd = pixApplyInvBackgroundGrayMap(pixs, pixbgi, sx, sy); 02410 02411 pixDestroy(&pixt); 02412 pixDestroy(&pixsd); 02413 pixDestroy(&pixbg); 02414 pixDestroy(&pixbgi); 02415 return pixd; 02416 } 02417 02418 02419 /*------------------------------------------------------------------* 02420 * Adaptive contrast normalization * 02421 *------------------------------------------------------------------*/ 02422 /*! 02423 * pixContrastNorm() 02424 * 02425 * Input: pixd (<optional> 8 bpp; null or equal to pixs) 02426 * pixs (8 bpp grayscale; not colormapped) 02427 * sx, sy (tile dimensions) 02428 * mindiff (minimum difference to accept as valid) 02429 * smoothx, smoothy (half-width of convolution kernel applied to 02430 * min and max arrays: use 0 for no smoothing) 02431 * Return: pixd always 02432 * 02433 * Notes: 02434 * (1) This function adaptively attempts to expand the contrast 02435 * to the full dynamic range in each tile. If the contrast in 02436 * a tile is smaller than @mindiff, it uses the min and max 02437 * pixel values from neighboring tiles. It also can use 02438 * convolution to smooth the min and max values from 02439 * neighboring tiles. After all that processing, it is 02440 * possible that the actual pixel values in the tile are outside 02441 * the computed [min ... max] range for local contrast 02442 * normalization. Such pixels are taken to be at either 0 02443 * (if below the min) or 255 (if above the max). 02444 * (2) pixd can be equal to pixs (in-place operation) or 02445 * null (makes a new pixd). 02446 * (3) sx and sy give the tile size; they are typically at least 20. 02447 * (4) mindiff is used to eliminate results for tiles where it is 02448 * likely that either fg or bg is missing. A value around 50 02449 * or more is reasonable. 02450 * (5) The full width and height of the convolution kernel 02451 * are (2 * smoothx + 1) and (2 * smoothy + 1). Some smoothing 02452 * is typically useful, and we limit the smoothing half-widths 02453 * to the range from 0 to 8. 02454 * (6) A linear TRC (gamma = 1.0) is applied to increase the contrast 02455 * in each tile. The result can subsequently be globally corrected, 02456 * by applying pixGammaTRC() with arbitrary values of gamma 02457 * and the 0 and 255 points of the mapping. 02458 */ 02459 PIX * 02460 pixContrastNorm(PIX *pixd, 02461 PIX *pixs, 02462 l_int32 sx, 02463 l_int32 sy, 02464 l_int32 mindiff, 02465 l_int32 smoothx, 02466 l_int32 smoothy) 02467 { 02468 PIX *pixmin, *pixmax; 02469 02470 PROCNAME("pixContrastNorm"); 02471 02472 if (!pixs || pixGetDepth(pixs) != 8) 02473 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, pixd); 02474 if (pixd && pixd != pixs) 02475 return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd); 02476 if (pixGetColormap(pixs)) 02477 return (PIX *)ERROR_PTR("pixs is colormapped", procName, pixd); 02478 if (sx < 5 || sy < 5) 02479 return (PIX *)ERROR_PTR("sx and/or sy less than 5", procName, pixd); 02480 if (smoothx < 0 || smoothy < 0) 02481 return (PIX *)ERROR_PTR("smooth params less than 0", procName, pixd); 02482 if (smoothx > 8 || smoothy > 8) 02483 return (PIX *)ERROR_PTR("smooth params exceed 8", procName, pixd); 02484 02485 /* Get the min and max pixel values in each tile, and represent 02486 * each value as a pixel in pixmin and pixmax, respectively. */ 02487 pixMinMaxTiles(pixs, sx, sy, mindiff, smoothx, smoothy, &pixmin, &pixmax); 02488 02489 /* For each tile, do a linear expansion of the dynamic range 02490 * of pixels so that the min value is mapped to 0 and the 02491 * max value is mapped to 255. */ 02492 pixd = pixLinearTRCTiled(pixd, pixs, sx, sy, pixmin, pixmax); 02493 02494 pixDestroy(&pixmin); 02495 pixDestroy(&pixmax); 02496 return pixd; 02497 } 02498 02499 02500 /*! 02501 * pixMinMaxTiles() 02502 * 02503 * Input: pixs (8 bpp grayscale; not colormapped) 02504 * sx, sy (tile dimensions) 02505 * mindiff (minimum difference to accept as valid) 02506 * smoothx, smoothy (half-width of convolution kernel applied to 02507 * min and max arrays: use 0 for no smoothing) 02508 * &pixmin (<return> tiled minima) 02509 * &pixmax (<return> tiled maxima) 02510 * Return: 0 if OK, 1 on error 02511 * 02512 * Notes: 02513 * (1) This computes filtered and smoothed values for the min and 02514 * max pixel values in each tile of the image. 02515 * (2) See pixContrastNorm() for usage. 02516 */ 02517 l_int32 02518 pixMinMaxTiles(PIX *pixs, 02519 l_int32 sx, 02520 l_int32 sy, 02521 l_int32 mindiff, 02522 l_int32 smoothx, 02523 l_int32 smoothy, 02524 PIX **ppixmin, 02525 PIX **ppixmax) 02526 { 02527 l_int32 w, h; 02528 PIX *pixmin1, *pixmax1, *pixmin2, *pixmax2; 02529 02530 PROCNAME("pixMinMaxTiles"); 02531 02532 if (!ppixmin || !ppixmax) 02533 return ERROR_INT("&pixmin or &pixmax undefined", procName, 1); 02534 *ppixmin = *ppixmax = NULL; 02535 if (!pixs || pixGetDepth(pixs) != 8) 02536 return ERROR_INT("pixs undefined or not 8 bpp", procName, 1); 02537 if (pixGetColormap(pixs)) 02538 return ERROR_INT("pixs is colormapped", procName, 1); 02539 if (sx < 5 || sy < 5) 02540 return ERROR_INT("sx and/or sy less than 3", procName, 1); 02541 if (smoothx < 0 || smoothy < 0) 02542 return ERROR_INT("smooth params less than 0", procName, 1); 02543 if (smoothx > 5 || smoothy > 5) 02544 return ERROR_INT("smooth params exceed 5", procName, 1); 02545 02546 /* Get the min and max values in each tile */ 02547 pixmin1 = pixScaleGrayMinMax(pixs, sx, sy, L_CHOOSE_MIN); 02548 pixmax1 = pixScaleGrayMinMax(pixs, sx, sy, L_CHOOSE_MAX); 02549 02550 pixmin2 = pixExtendByReplication(pixmin1, 1, 1); 02551 pixmax2 = pixExtendByReplication(pixmax1, 1, 1); 02552 pixDestroy(&pixmin1); 02553 pixDestroy(&pixmax1); 02554 02555 /* Make sure no value is 0 */ 02556 pixAddConstantGray(pixmin2, 1); 02557 pixAddConstantGray(pixmax2, 1); 02558 02559 /* Generate holes where the contrast is too small */ 02560 pixSetLowContrast(pixmin2, pixmax2, mindiff); 02561 02562 /* Fill the holes (0 values) */ 02563 pixGetDimensions(pixmin2, &w, &h, NULL); 02564 pixFillMapHoles(pixmin2, w, h, L_FILL_BLACK); 02565 pixFillMapHoles(pixmax2, w, h, L_FILL_BLACK); 02566 02567 /* Smooth if requested */ 02568 if (smoothx > 0 || smoothy > 0) { 02569 smoothx = L_MIN(smoothx, (w - 1) / 2); 02570 smoothy = L_MIN(smoothy, (h - 1) / 2); 02571 *ppixmin = pixBlockconv(pixmin2, smoothx, smoothy); 02572 *ppixmax = pixBlockconv(pixmax2, smoothx, smoothy); 02573 } 02574 else { 02575 *ppixmin = pixClone(pixmin2); 02576 *ppixmax = pixClone(pixmax2); 02577 } 02578 pixDestroy(&pixmin2); 02579 pixDestroy(&pixmax2); 02580 02581 return 0; 02582 } 02583 02584 02585 /*! 02586 * pixSetLowContrast() 02587 * 02588 * Input: pixs1 (8 bpp) 02589 * pixs2 (8 bpp) 02590 * mindiff (minimum difference to accept as valid) 02591 * Return: 0 if OK; 1 if no pixel diffs are large enough, or on error 02592 * 02593 * Notes: 02594 * (1) This compares corresponding pixels in pixs1 and pixs2. 02595 * When they differ by less than @mindiff, set the pixel 02596 * values to 0 in each. Each pixel typically represents a tile 02597 * in a larger image, and a very small difference between 02598 * the min and max in the tile indicates that the min and max 02599 * values are not to be trusted. 02600 * (2) If contrast (pixel difference) detection is expected to fail, 02601 * caller should check return value. 02602 */ 02603 l_int32 02604 pixSetLowContrast(PIX *pixs1, 02605 PIX *pixs2, 02606 l_int32 mindiff) 02607 { 02608 l_int32 i, j, w, h, d, wpl, val1, val2, found; 02609 l_uint32 *data1, *data2, *line1, *line2; 02610 02611 PROCNAME("pixSetLowContrast"); 02612 02613 if (!pixs1 || !pixs2) 02614 return ERROR_INT("pixs1 and pixs2 not both defined", procName, 1); 02615 if (pixSizesEqual(pixs1, pixs2) == 0) 02616 return ERROR_INT("pixs1 and pixs2 not equal size", procName, 1); 02617 pixGetDimensions(pixs1, &w, &h, &d); 02618 if (d != 8) 02619 return ERROR_INT("depth not 8 bpp", procName, 1); 02620 if (mindiff > 254) return 0; 02621 02622 data1 = pixGetData(pixs1); 02623 data2 = pixGetData(pixs2); 02624 wpl = pixGetWpl(pixs1); 02625 found = 0; /* init to not finding any diffs >= mindiff */ 02626 for (i = 0; i < h; i++) { 02627 line1 = data1 + i * wpl; 02628 line2 = data2 + i * wpl; 02629 for (j = 0; j < w; j++) { 02630 val1 = GET_DATA_BYTE(line1, j); 02631 val2 = GET_DATA_BYTE(line2, j); 02632 if (L_ABS(val1 - val2) >= mindiff) { 02633 found = 1; 02634 break; 02635 } 02636 } 02637 if (found) break; 02638 } 02639 if (!found) { 02640 L_WARNING("no pixel pair diffs as large as mindiff", procName); 02641 pixClearAll(pixs1); 02642 pixClearAll(pixs2); 02643 return 1; 02644 } 02645 02646 for (i = 0; i < h; i++) { 02647 line1 = data1 + i * wpl; 02648 line2 = data2 + i * wpl; 02649 for (j = 0; j < w; j++) { 02650 val1 = GET_DATA_BYTE(line1, j); 02651 val2 = GET_DATA_BYTE(line2, j); 02652 if (L_ABS(val1 - val2) < mindiff) { 02653 SET_DATA_BYTE(line1, j, 0); 02654 SET_DATA_BYTE(line2, j, 0); 02655 } 02656 } 02657 } 02658 02659 return 0; 02660 } 02661 02662 02663 /*! 02664 * pixLinearTRCTiled() 02665 * 02666 * Input: pixd (<optional> 8 bpp) 02667 * pixs (8 bpp, not colormapped) 02668 * sx, sy (tile dimensions) 02669 * pixmin (pix of min values in tiles) 02670 * pixmax (pix of max values in tiles) 02671 * Return: pixd always 02672 * 02673 * Notes: 02674 * (1) pixd can be equal to pixs (in-place operation) or 02675 * null (makes a new pixd). 02676 * (2) sx and sy give the tile size; they are typically at least 20. 02677 * (3) pixmin and pixmax are generated by pixMinMaxTiles() 02678 * (4) For each tile, this does a linear expansion of the dynamic 02679 * range so that the min value in the tile becomes 0 and the 02680 * max value in the tile becomes 255. 02681 * (5) The LUTs that do the mapping are generated as needed 02682 * and stored for reuse in an integer array within the ptr array iaa[]. 02683 */ 02684 PIX * 02685 pixLinearTRCTiled(PIX *pixd, 02686 PIX *pixs, 02687 l_int32 sx, 02688 l_int32 sy, 02689 PIX *pixmin, 02690 PIX *pixmax) 02691 { 02692 l_int32 i, j, k, m, w, h, wt, ht, wpl, wplt, xoff, yoff; 02693 l_int32 minval, maxval, val, sval; 02694 l_int32 *ia; 02695 l_int32 **iaa; 02696 l_uint32 *data, *datamin, *datamax, *line, *tline, *linemin, *linemax; 02697 02698 PROCNAME("pixLinearTRCTiled"); 02699 02700 if (!pixs || pixGetDepth(pixs) != 8) 02701 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, pixd); 02702 if (pixd && pixd != pixs) 02703 return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd); 02704 if (pixGetColormap(pixs)) 02705 return (PIX *)ERROR_PTR("pixs is colormapped", procName, pixd); 02706 if (!pixmin || !pixmax) 02707 return (PIX *)ERROR_PTR("pixmin & pixmax not defined", procName, pixd); 02708 if (sx < 5 || sy < 5) 02709 return (PIX *)ERROR_PTR("sx and/or sy less than 5", procName, pixd); 02710 02711 pixd = pixCopy(pixd, pixs); 02712 iaa = (l_int32 **)CALLOC(256, sizeof(l_int32 *)); 02713 pixGetDimensions(pixd, &w, &h, NULL); 02714 02715 data = pixGetData(pixd); 02716 wpl = pixGetWpl(pixd); 02717 datamin = pixGetData(pixmin); 02718 datamax = pixGetData(pixmax); 02719 wplt = pixGetWpl(pixmin); 02720 pixGetDimensions(pixmin, &wt, &ht, NULL); 02721 for (i = 0; i < ht; i++) { 02722 line = data + sy * i * wpl; 02723 linemin = datamin + i * wplt; 02724 linemax = datamax + i * wplt; 02725 yoff = sy * i; 02726 for (j = 0; j < wt; j++) { 02727 xoff = sx * j; 02728 minval = GET_DATA_BYTE(linemin, j); 02729 maxval = GET_DATA_BYTE(linemax, j); 02730 if (maxval == minval) { /* this is bad */ 02731 /* fprintf(stderr, "should't happen! i,j = %d,%d, minval = %d\n", 02732 i, j, minval); */ 02733 continue; 02734 } 02735 ia = iaaGetLinearTRC(iaa, maxval - minval); 02736 for (k = 0; k < sy && yoff + k < h; k++) { 02737 tline = line + k * wpl; 02738 for (m = 0; m < sx && xoff + m < w; m++) { 02739 val = GET_DATA_BYTE(tline, xoff + m); 02740 sval = val - minval; 02741 sval = L_MAX(0, sval); 02742 SET_DATA_BYTE(tline, xoff + m, ia[sval]); 02743 } 02744 } 02745 } 02746 } 02747 02748 for (i = 0; i < 256; i++) 02749 if (iaa[i]) FREE(iaa[i]); 02750 FREE(iaa); 02751 return pixd; 02752 } 02753 02754 02755 /*! 02756 * iaaGetLinearTRC() 02757 * 02758 * Input: iaa (bare array of ptrs to l_int32) 02759 * diff (between min and max pixel values that are 02760 * to be mapped to 0 and 255) 02761 * Return: ia (LUT with input (val - minval) and output a 02762 * value between 0 and 255) 02763 */ 02764 static l_int32 * 02765 iaaGetLinearTRC(l_int32 **iaa, 02766 l_int32 diff) 02767 { 02768 l_int32 i; 02769 l_int32 *ia; 02770 l_float32 factor; 02771 02772 PROCNAME("iaaGetLinearTRC"); 02773 02774 if (!iaa) 02775 return (l_int32 *)ERROR_PTR("iaa not defined", procName, NULL); 02776 02777 if (iaa[diff] != NULL) /* already have it */ 02778 return iaa[diff]; 02779 02780 if ((ia = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL) 02781 return (l_int32 *)ERROR_PTR("ia not made", procName, NULL); 02782 iaa[diff] = ia; 02783 if (diff == 0) { /* shouldn't happen */ 02784 for (i = 0; i < 256; i++) 02785 ia[i] = 128; 02786 } 02787 else { 02788 factor = 255. / (l_float32)diff; 02789 for (i = 0; i < diff + 1; i++) 02790 ia[i] = (l_int32)(factor * i + 0.5); 02791 for (i = diff + 1; i < 256; i++) 02792 ia[i] = 255; 02793 } 02794 02795 return ia; 02796 } 02797 02798