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 * pix5.c 00018 * 00019 * This file has these operations: 00020 * 00021 * (1) Measurement of 1 bpp image properties 00022 * (2) Extract rectangular region 00023 * (3) Clip to foreground 00024 * (4) Extract pixel averages and reversals along lines 00025 * (5) Rank row and column transforms 00026 * 00027 * Measurement of properties 00028 * l_int32 pixaFindDimensions() 00029 * NUMA *pixaFindAreaPerimRatio() 00030 * l_int32 pixFindAreaPerimRatio() 00031 * NUMA *pixaFindPerimSizeRatio() 00032 * l_int32 pixFindPerimSizeRatio() 00033 * NUMA *pixaFindAreaFraction() 00034 * l_int32 pixFindAreaFraction() 00035 * NUMA *pixaFindWidthHeightRatio() 00036 * NUMA *pixaFindWidthHeightProduct() 00037 * l_int32 pixFindOverlapFraction() 00038 * BOXA *pixFindRectangleComps() 00039 * l_int32 pixConformsToRectangle() 00040 * 00041 * Extract rectangular region 00042 * PIX *pixClipRectangle() 00043 * PIX *pixClipMasked() 00044 * PIX *pixResizeToMatch() 00045 * 00046 * Clip to foreground 00047 * PIX *pixClipToForeground() 00048 * l_int32 pixClipBoxToForeground() 00049 * l_int32 pixScanForForeground() 00050 * l_int32 pixClipBoxToEdges() 00051 * l_int32 pixScanForEdge() 00052 * 00053 * Extract pixel averages and reversals along lines 00054 * NUMA *pixExtractOnLine() 00055 * l_float32 pixAverageOnLine(); 00056 * NUMA *pixAverageIntensityProfile() 00057 * NUMA *pixReversalProfile() 00058 * 00059 * Rank row and column transforms 00060 * PIX *pixRankRowTransform() 00061 * PIX *pixRankColumnTransform() 00062 */ 00063 00064 #include <string.h> 00065 #include <math.h> 00066 #include "allheaders.h" 00067 00068 static const l_uint32 rmask32[] = {0x0, 00069 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 00070 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 00071 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 00072 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, 00073 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, 00074 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, 00075 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff, 00076 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff}; 00077 00078 #ifndef NO_CONSOLE_IO 00079 #define DEBUG_EDGES 0 00080 #endif /* ~NO_CONSOLE_IO */ 00081 00082 00083 /*-------------------------------------------------------------* 00084 * Measurement of properties * 00085 *-------------------------------------------------------------*/ 00086 /*! 00087 * pixaFindDimensions() 00088 * 00089 * Input: pixa 00090 * &naw (<optional return> numa of pix widths) 00091 * &nah (<optional return> numa of pix heights) 00092 * Return: 0 if OK, 1 on error 00093 */ 00094 l_int32 00095 pixaFindDimensions(PIXA *pixa, 00096 NUMA **pnaw, 00097 NUMA **pnah) 00098 { 00099 l_int32 i, n, w, h; 00100 PIX *pixt; 00101 00102 PROCNAME("pixaFindDimensions"); 00103 00104 if (!pixa) 00105 return ERROR_INT("pixa not defined", procName, 1); 00106 if (!pnaw && !pnah) 00107 return 0; 00108 00109 n = pixaGetCount(pixa); 00110 if (pnaw) *pnaw = numaCreate(n); 00111 if (pnah) *pnah = numaCreate(n); 00112 for (i = 0; i < n; i++) { 00113 pixt = pixaGetPix(pixa, i, L_CLONE); 00114 pixGetDimensions(pixt, &w, &h, NULL); 00115 if (pnaw) 00116 numaAddNumber(*pnaw, w); 00117 if (pnah) 00118 numaAddNumber(*pnah, h); 00119 pixDestroy(&pixt); 00120 } 00121 return 0; 00122 } 00123 00124 00125 /*! 00126 * pixaFindAreaPerimRatio() 00127 * 00128 * Input: pixa (of 1 bpp pix) 00129 * Return: na (of area/perimeter ratio for each pix), or null on error 00130 * 00131 * Notes: 00132 * (1) This is typically used for a pixa consisting of 00133 * 1 bpp connected components. 00134 */ 00135 NUMA * 00136 pixaFindAreaPerimRatio(PIXA *pixa) 00137 { 00138 l_int32 i, n; 00139 l_int32 *tab; 00140 l_float32 fract; 00141 NUMA *na; 00142 PIX *pixt; 00143 00144 PROCNAME("pixaFindAreaPerimRatio"); 00145 00146 if (!pixa) 00147 return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); 00148 00149 n = pixaGetCount(pixa); 00150 na = numaCreate(n); 00151 tab = makePixelSumTab8(); 00152 for (i = 0; i < n; i++) { 00153 pixt = pixaGetPix(pixa, i, L_CLONE); 00154 pixFindAreaPerimRatio(pixt, tab, &fract); 00155 numaAddNumber(na, fract); 00156 pixDestroy(&pixt); 00157 } 00158 FREE(tab); 00159 return na; 00160 } 00161 00162 00163 /*! 00164 * pixFindAreaPerimRatio() 00165 * 00166 * Input: pixs (1 bpp) 00167 * tab (<optional> pixel sum table, can be NULL) 00168 * &fract (<return> area/perimeter ratio) 00169 * Return: 0 if OK, 1 on error 00170 * 00171 * Notes: 00172 * (1) The area is the number of fg pixels that are not on the 00173 * boundary (i.e., not 8-connected to a bg pixel), and the 00174 * perimeter is the number of boundary fg pixels. 00175 * (2) This is typically used for a pixa consisting of 00176 * 1 bpp connected components. 00177 */ 00178 l_int32 00179 pixFindAreaPerimRatio(PIX *pixs, 00180 l_int32 *tab, 00181 l_float32 *pfract) 00182 { 00183 l_int32 *tab8; 00184 l_int32 nin, nbound; 00185 PIX *pixt; 00186 00187 PROCNAME("pixFindAreaPerimRatio"); 00188 00189 if (!pfract) 00190 return ERROR_INT("&fract not defined", procName, 1); 00191 *pfract = 0.0; 00192 if (!pixs || pixGetDepth(pixs) != 1) 00193 return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); 00194 00195 if (!tab) 00196 tab8 = makePixelSumTab8(); 00197 else 00198 tab8 = tab; 00199 00200 pixt = pixErodeBrick(NULL, pixs, 3, 3); 00201 pixCountPixels(pixt, &nin, tab8); 00202 pixXor(pixt, pixt, pixs); 00203 pixCountPixels(pixt, &nbound, tab8); 00204 *pfract = (l_float32)nin / (l_float32)nbound; 00205 00206 if (!tab) 00207 FREE(tab8); 00208 pixDestroy(&pixt); 00209 return 0; 00210 } 00211 00212 00213 /*! 00214 * pixaFindPerimSizeRatio() 00215 * 00216 * Input: pixa (of 1 bpp pix) 00217 * Return: na (of fg perimeter/(w*h) ratio for each pix), or null on error 00218 * 00219 * Notes: 00220 * (1) This is typically used for a pixa consisting of 00221 * 1 bpp connected components. 00222 */ 00223 NUMA * 00224 pixaFindPerimSizeRatio(PIXA *pixa) 00225 { 00226 l_int32 i, n; 00227 l_int32 *tab; 00228 l_float32 ratio; 00229 NUMA *na; 00230 PIX *pixt; 00231 00232 PROCNAME("pixaFindPerimSizeRatio"); 00233 00234 if (!pixa) 00235 return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); 00236 00237 n = pixaGetCount(pixa); 00238 na = numaCreate(n); 00239 tab = makePixelSumTab8(); 00240 for (i = 0; i < n; i++) { 00241 pixt = pixaGetPix(pixa, i, L_CLONE); 00242 pixFindPerimSizeRatio(pixt, tab, &ratio); 00243 numaAddNumber(na, ratio); 00244 pixDestroy(&pixt); 00245 } 00246 FREE(tab); 00247 return na; 00248 } 00249 00250 00251 /*! 00252 * pixFindPerimSizeRatio() 00253 * 00254 * Input: pixs (1 bpp) 00255 * tab (<optional> pixel sum table, can be NULL) 00256 * &ratio (<return> perimeter/size ratio) 00257 * Return: 0 if OK, 1 on error 00258 * 00259 * Notes: 00260 * (1) The size is the sum of the width and height of the pix, 00261 * and the perimeter is the number of boundary fg pixels. 00262 * (2) This has a large value for dendritic, fractal-like components 00263 * with highly irregular boundaries. 00264 * (3) This is typically used for a single connected component. 00265 */ 00266 l_int32 00267 pixFindPerimSizeRatio(PIX *pixs, 00268 l_int32 *tab, 00269 l_float32 *pratio) 00270 { 00271 l_int32 *tab8; 00272 l_int32 w, h, nbound; 00273 PIX *pixt; 00274 00275 PROCNAME("pixFindPerimSizeRatio"); 00276 00277 if (!pratio) 00278 return ERROR_INT("&ratio not defined", procName, 1); 00279 *pratio = 0.0; 00280 if (!pixs || pixGetDepth(pixs) != 1) 00281 return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); 00282 00283 if (!tab) 00284 tab8 = makePixelSumTab8(); 00285 else 00286 tab8 = tab; 00287 00288 pixt = pixErodeBrick(NULL, pixs, 3, 3); 00289 pixXor(pixt, pixt, pixs); 00290 pixCountPixels(pixt, &nbound, tab8); 00291 pixGetDimensions(pixs, &w, &h, NULL); 00292 *pratio = (l_float32)nbound / (l_float32)(w + h); 00293 00294 if (!tab) 00295 FREE(tab8); 00296 pixDestroy(&pixt); 00297 return 0; 00298 } 00299 00300 00301 /*! 00302 * pixaFindAreaFraction() 00303 * 00304 * Input: pixa (of 1 bpp pix) 00305 * Return: na (of area fractions for each pix), or null on error 00306 * 00307 * Notes: 00308 * (1) This is typically used for a pixa consisting of 00309 * 1 bpp connected components. 00310 */ 00311 NUMA * 00312 pixaFindAreaFraction(PIXA *pixa) 00313 { 00314 l_int32 i, n; 00315 l_int32 *tab; 00316 l_float32 fract; 00317 NUMA *na; 00318 PIX *pixt; 00319 00320 PROCNAME("pixaFindAreaFraction"); 00321 00322 if (!pixa) 00323 return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); 00324 00325 n = pixaGetCount(pixa); 00326 na = numaCreate(n); 00327 tab = makePixelSumTab8(); 00328 for (i = 0; i < n; i++) { 00329 pixt = pixaGetPix(pixa, i, L_CLONE); 00330 pixFindAreaFraction(pixt, tab, &fract); 00331 numaAddNumber(na, fract); 00332 pixDestroy(&pixt); 00333 } 00334 FREE(tab); 00335 return na; 00336 } 00337 00338 00339 /*! 00340 * pixFindAreaFraction() 00341 * 00342 * Input: pixs (1 bpp) 00343 * tab (<optional> pixel sum table, can be NULL) 00344 * &fract (<return> fg area/size ratio) 00345 * Return: 0 if OK, 1 on error 00346 * 00347 * Notes: 00348 * (1) This finds the ratio of the number of fg pixels to the 00349 * size of the pix (w * h). It is typically used for a 00350 * single connected component. 00351 */ 00352 l_int32 00353 pixFindAreaFraction(PIX *pixs, 00354 l_int32 *tab, 00355 l_float32 *pfract) 00356 { 00357 l_int32 w, h, d, sum; 00358 l_int32 *tab8; 00359 00360 PROCNAME("pixFindAreaFraction"); 00361 00362 if (!pfract) 00363 return ERROR_INT("&fract not defined", procName, 1); 00364 *pfract = 0.0; 00365 pixGetDimensions(pixs, &w, &h, &d); 00366 if (!pixs || d != 1) 00367 return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); 00368 00369 if (!tab) 00370 tab8 = makePixelSumTab8(); 00371 else 00372 tab8 = tab; 00373 00374 pixCountPixels(pixs, &sum, tab8); 00375 *pfract = (l_float32)sum / (l_float32)(w * h); 00376 00377 if (!tab) 00378 FREE(tab8); 00379 return 0; 00380 } 00381 00382 00383 /*! 00384 * pixaFindWidthHeightRatio() 00385 * 00386 * Input: pixa (of 1 bpp pix) 00387 * Return: na (of width/height ratios for each pix), or null on error 00388 * 00389 * Notes: 00390 * (1) This is typically used for a pixa consisting of 00391 * 1 bpp connected components. 00392 */ 00393 NUMA * 00394 pixaFindWidthHeightRatio(PIXA *pixa) 00395 { 00396 l_int32 i, n, w, h; 00397 NUMA *na; 00398 PIX *pixt; 00399 00400 PROCNAME("pixaFindWidthHeightRatio"); 00401 00402 if (!pixa) 00403 return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); 00404 00405 n = pixaGetCount(pixa); 00406 na = numaCreate(n); 00407 for (i = 0; i < n; i++) { 00408 pixt = pixaGetPix(pixa, i, L_CLONE); 00409 pixGetDimensions(pixt, &w, &h, NULL); 00410 numaAddNumber(na, (l_float32)w / (l_float32)h); 00411 pixDestroy(&pixt); 00412 } 00413 return na; 00414 } 00415 00416 00417 /*! 00418 * pixaFindWidthHeightProduct() 00419 * 00420 * Input: pixa (of 1 bpp pix) 00421 * Return: na (of width*height products for each pix), or null on error 00422 * 00423 * Notes: 00424 * (1) This is typically used for a pixa consisting of 00425 * 1 bpp connected components. 00426 */ 00427 NUMA * 00428 pixaFindWidthHeightProduct(PIXA *pixa) 00429 { 00430 l_int32 i, n, w, h; 00431 NUMA *na; 00432 PIX *pixt; 00433 00434 PROCNAME("pixaFindWidthHeightProduct"); 00435 00436 if (!pixa) 00437 return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); 00438 00439 n = pixaGetCount(pixa); 00440 na = numaCreate(n); 00441 for (i = 0; i < n; i++) { 00442 pixt = pixaGetPix(pixa, i, L_CLONE); 00443 pixGetDimensions(pixt, &w, &h, NULL); 00444 numaAddNumber(na, w * h); 00445 pixDestroy(&pixt); 00446 } 00447 return na; 00448 } 00449 00450 00451 /*! 00452 * pixFindOverlapFraction() 00453 * 00454 * Input: pixs1, pixs2 (1 bpp) 00455 * x2, y2 (location in pixs1 of UL corner of pixs2) 00456 * tab (<optional> pixel sum table, can be null) 00457 * &ratio (<return> ratio fg intersection to fg union) 00458 * &noverlap (<optional return> number of overlapping pixels) 00459 * Return: 0 if OK, 1 on error 00460 * 00461 * Notes: 00462 * (1) The UL corner of pixs2 is placed at (x2, y2) in pixs1. 00463 * (2) This measure is similar to the correlation. 00464 */ 00465 l_int32 00466 pixFindOverlapFraction(PIX *pixs1, 00467 PIX *pixs2, 00468 l_int32 x2, 00469 l_int32 y2, 00470 l_int32 *tab, 00471 l_float32 *pratio, 00472 l_int32 *pnoverlap) 00473 { 00474 l_int32 *tab8; 00475 l_int32 w, h, nintersect, nunion; 00476 PIX *pixt; 00477 00478 PROCNAME("pixFindOverlapFraction"); 00479 00480 if (!pratio) 00481 return ERROR_INT("&ratio not defined", procName, 1); 00482 *pratio = 0.0; 00483 if (!pixs1 || pixGetDepth(pixs1) != 1) 00484 return ERROR_INT("pixs1 not defined or not 1 bpp", procName, 1); 00485 if (!pixs2 || pixGetDepth(pixs2) != 1) 00486 return ERROR_INT("pixs2 not defined or not 1 bpp", procName, 1); 00487 00488 if (!tab) 00489 tab8 = makePixelSumTab8(); 00490 else 00491 tab8 = tab; 00492 00493 pixGetDimensions(pixs2, &w, &h, NULL); 00494 pixt = pixCopy(NULL, pixs1); 00495 pixRasterop(pixt, x2, y2, w, h, PIX_MASK, pixs2, 0, 0); /* AND */ 00496 pixCountPixels(pixt, &nintersect, tab8); 00497 if (pnoverlap) 00498 *pnoverlap = nintersect; 00499 pixCopy(pixt, pixs1); 00500 pixRasterop(pixt, x2, y2, w, h, PIX_PAINT, pixs2, 0, 0); /* OR */ 00501 pixCountPixels(pixt, &nunion, tab8); 00502 *pratio = (l_float32)nintersect / (l_float32)nunion; 00503 00504 if (!tab) 00505 FREE(tab8); 00506 pixDestroy(&pixt); 00507 return 0; 00508 } 00509 00510 00511 /*! 00512 * pixFindRectangleComps() 00513 * 00514 * Input: pixs (1 bpp) 00515 * dist (max distance allowed between bounding box and nearest 00516 * foreground pixel within it) 00517 * minw, minh (minimum size in each direction as a requirement 00518 * for a conforming rectangle) 00519 * Return: boxa (of components that conform), or null on error 00520 * 00521 * Notes: 00522 * (1) This applies the function pixConformsToRectangle() to 00523 * each 8-c.c. in pixs, and returns a boxa containing the 00524 * regions of all components that are conforming. 00525 * (2) Conforming components must satisfy both the size constraint 00526 * given by @minsize and the slop in conforming to a rectangle 00527 * determined by @dist. 00528 */ 00529 BOXA * 00530 pixFindRectangleComps(PIX *pixs, 00531 l_int32 dist, 00532 l_int32 minw, 00533 l_int32 minh) 00534 { 00535 l_int32 w, h, i, n, conforms; 00536 BOX *box; 00537 BOXA *boxa, *boxad; 00538 PIX *pix; 00539 PIXA *pixa; 00540 00541 PROCNAME("pixFindRectangleComps"); 00542 00543 if (!pixs || pixGetDepth(pixs) != 1) 00544 return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); 00545 if (dist < 0) 00546 return (BOXA *)ERROR_PTR("dist must be >= 0", procName, NULL); 00547 if (minw <= 2 * dist && minh <= 2 * dist) 00548 return (BOXA *)ERROR_PTR("invalid parameters", procName, NULL); 00549 00550 boxa = pixConnComp(pixs, &pixa, 8); 00551 boxad = boxaCreate(0); 00552 n = pixaGetCount(pixa); 00553 for (i = 0; i < n; i++) { 00554 pix = pixaGetPix(pixa, i, L_CLONE); 00555 pixGetDimensions(pix, &w, &h, NULL); 00556 if (w < minw || h < minh) { 00557 pixDestroy(&pix); 00558 continue; 00559 } 00560 pixConformsToRectangle(pix, NULL, dist, &conforms); 00561 if (conforms) { 00562 box = boxaGetBox(boxa, i, L_COPY); 00563 boxaAddBox(boxad, box, L_INSERT); 00564 } 00565 pixDestroy(&pix); 00566 } 00567 boxaDestroy(&boxa); 00568 pixaDestroy(&pixa); 00569 00570 return boxad; 00571 } 00572 00573 00574 /*! 00575 * pixConformsToRectangle() 00576 * 00577 * Input: pixs (1 bpp) 00578 * box (<optional> if null, use the entire pixs) 00579 * dist (max distance allowed between bounding box and nearest 00580 * foreground pixel within it) 00581 * &conforms (<return> 0 (false) if not conforming; 00582 * 1 (true) if conforming) 00583 * Return: 0 if OK, 1 on error 00584 * 00585 * Notes: 00586 * (1) There are several ways to test if a connected component has 00587 * an essentially rectangular boundary, such as: 00588 * a. Fraction of fill into the bounding box 00589 * b. Max-min distance of fg pixel from periphery of bounding box 00590 * c. Max depth of bg intrusions into component within bounding box 00591 * The weakness of (a) is that it is highly sensitive to holes 00592 * within the c.c. The weakness of (b) is that it can have 00593 * arbitrarily large intrusions into the c.c. Method (c) tests 00594 * the integrity of the outer boundary of the c.c., with respect 00595 * to the enclosing bounding box, so we use it. 00596 * (2) This tests if the connected component within the box conforms 00597 * to the box at all points on the periphery within @dist. 00598 * Inside, at a distance from the box boundary that is greater 00599 * than @dist, we don't care about the pixels in the c.c. 00600 * (3) We can think of the conforming condition as follows: 00601 * No pixel inside a distance @dist from the boundary 00602 * can connect to the boundary through a path through the bg. 00603 * To implement this, we need to do a flood fill. We can go 00604 * either from inside toward the boundary, or the other direction. 00605 * It's easiest to fill from the boundary, and then verify that 00606 * there are no filled pixels farther than @dist from the boundary. 00607 */ 00608 l_int32 00609 pixConformsToRectangle(PIX *pixs, 00610 BOX *box, 00611 l_int32 dist, 00612 l_int32 *pconforms) 00613 { 00614 l_int32 w, h, empty; 00615 PIX *pixt1, *pixt2; 00616 00617 PROCNAME("pixConformsToRectangle"); 00618 00619 if (!pconforms) 00620 return ERROR_INT("&conforms not defined", procName, 1); 00621 *pconforms = 0; 00622 if (!pixs || pixGetDepth(pixs) != 1) 00623 return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); 00624 if (dist < 0) 00625 return ERROR_INT("dist must be >= 0", procName, 1); 00626 pixGetDimensions(pixs, &w, &h, NULL); 00627 if (w <= 2 * dist || h <= 2 * dist) { 00628 L_WARNING("automatic conformation: distance too large", procName); 00629 *pconforms = 1; 00630 return 0; 00631 } 00632 00633 /* Extract the region, if necessary */ 00634 if (box) 00635 pixt1 = pixClipRectangle(pixs, box, NULL); 00636 else 00637 pixt1 = pixCopy(NULL, pixs); 00638 00639 /* Invert and fill from the boundary into the interior. 00640 * Because we're considering the connected component in an 00641 * 8-connected sense, we do the background filling as 4 c.c. */ 00642 pixInvert(pixt1, pixt1); 00643 pixt2 = pixExtractBorderConnComps(pixt1, 4); 00644 00645 /* Mask out all pixels within a distance @dist from the box 00646 * boundary. Any remaining pixels are from filling that goes 00647 * more than @dist from the boundary. If no pixels remain, 00648 * the component conforms to the bounding rectangle within 00649 * a distance @dist. */ 00650 pixSetOrClearBorder(pixt2, dist, dist, dist, dist, PIX_CLR); 00651 pixZero(pixt2, &empty); 00652 pixDestroy(&pixt1); 00653 pixDestroy(&pixt2); 00654 00655 *pconforms = (empty) ? 1 : 0; 00656 return 0; 00657 } 00658 00659 00660 /*-------------------------------------------------------------* 00661 * Extract rectangular region * 00662 *-------------------------------------------------------------*/ 00663 /*! 00664 * pixClipRectangle() 00665 * 00666 * Input: pixs 00667 * box (requested clipping region; const) 00668 * &boxc (<optional return> actual box of clipped region) 00669 * Return: clipped pix, or null on error or if rectangle 00670 * doesn't intersect pixs 00671 * 00672 * Notes: 00673 * 00674 * This should be simple, but there are choices to be made. 00675 * The box is defined relative to the pix coordinates. However, 00676 * if the box is not contained within the pix, we have two choices: 00677 * 00678 * (1) clip the box to the pix 00679 * (2) make a new pix equal to the full box dimensions, 00680 * but let rasterop do the clipping and positioning 00681 * of the src with respect to the dest 00682 * 00683 * Choice (2) immediately brings up the problem of what pixel values 00684 * to use that were not taken from the src. For example, on a grayscale 00685 * image, do you want the pixels not taken from the src to be black 00686 * or white or something else? To implement choice 2, one needs to 00687 * specify the color of these extra pixels. 00688 * 00689 * So we adopt (1), and clip the box first, if necessary, 00690 * before making the dest pix and doing the rasterop. But there 00691 * is another issue to consider. If you want to paste the 00692 * clipped pix back into pixs, it must be properly aligned, and 00693 * it is necessary to use the clipped box for alignment. 00694 * Accordingly, this function has a third (optional) argument, which is 00695 * the input box clipped to the src pix. 00696 */ 00697 PIX * 00698 pixClipRectangle(PIX *pixs, 00699 BOX *box, 00700 BOX **pboxc) 00701 { 00702 l_int32 w, h, d, bx, by, bw, bh; 00703 BOX *boxc; 00704 PIX *pixd; 00705 00706 PROCNAME("pixClipRectangle"); 00707 00708 if (pboxc) 00709 *pboxc = NULL; 00710 if (!pixs) 00711 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00712 if (!box) 00713 return (PIX *)ERROR_PTR("box not defined", procName, NULL); 00714 00715 /* Clip the input box to the pix */ 00716 pixGetDimensions(pixs, &w, &h, &d); 00717 if ((boxc = boxClipToRectangle(box, w, h)) == NULL) { 00718 L_WARNING("box doesn't overlap pix", procName); 00719 return NULL; 00720 } 00721 boxGetGeometry(boxc, &bx, &by, &bw, &bh); 00722 00723 /* Extract the block */ 00724 if ((pixd = pixCreate(bw, bh, d)) == NULL) 00725 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00726 pixCopyResolution(pixd, pixs); 00727 pixCopyColormap(pixd, pixs); 00728 pixRasterop(pixd, 0, 0, bw, bh, PIX_SRC, pixs, bx, by); 00729 00730 if (pboxc) 00731 *pboxc = boxc; 00732 else 00733 boxDestroy(&boxc); 00734 00735 return pixd; 00736 } 00737 00738 00739 /*! 00740 * pixClipMasked() 00741 * 00742 * Input: pixs (1, 2, 4, 8, 16, 32 bpp; colormap ok) 00743 * pixm (clipping mask, 1 bpp) 00744 * x, y (origin of clipping mask relative to pixs) 00745 * outval (val to use for pixels that are outside the mask) 00746 * Return: pixd, (clipped pix) or null on error or if pixm doesn't 00747 * intersect pixs 00748 * 00749 * Notes: 00750 * (1) If pixs has a colormap, it is preserved in pixd. 00751 * (2) The depth of pixd is the same as that of pixs. 00752 * (3) If the depth of pixs is 1, use @outval = 0 for white background 00753 * and 1 for black; otherwise, use the max value for white 00754 * and 0 for black. If pixs has a colormap, the max value for 00755 * @outval is 0xffffffff; otherwise, it is 2^d - 1. 00756 * (4) When using 1 bpp pixs, this is a simple clip and 00757 * blend operation. For example, if both pix1 and pix2 are 00758 * black text on white background, and you want to OR the 00759 * fg on the two images, let pixm be the inverse of pix2. 00760 * Then the operation takes all of pix1 that's in the bg of 00761 * pix2, and for the remainder (which are the pixels 00762 * corresponding to the fg of the pix2), paint them black 00763 * (1) in pix1. The function call looks like 00764 * pixClipMasked(pix2, pixInvert(pix1, pix1), x, y, 1); 00765 */ 00766 PIX * 00767 pixClipMasked(PIX *pixs, 00768 PIX *pixm, 00769 l_int32 x, 00770 l_int32 y, 00771 l_uint32 outval) 00772 { 00773 l_int32 wm, hm, d, index, rval, gval, bval; 00774 l_uint32 pixel; 00775 BOX *box; 00776 PIX *pixmi, *pixd; 00777 PIXCMAP *cmap; 00778 00779 PROCNAME("pixClipMasked"); 00780 00781 if (!pixs) 00782 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00783 if (!pixm || pixGetDepth(pixm) != 1) 00784 return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", procName, NULL); 00785 00786 /* Clip out the region specified by pixm and (x,y) */ 00787 pixGetDimensions(pixm, &wm, &hm, NULL); 00788 box = boxCreate(x, y, wm, hm); 00789 pixd = pixClipRectangle(pixs, box, NULL); 00790 00791 /* Paint 'outval' (or something close to it if cmapped) through 00792 * the pixels not masked by pixm */ 00793 cmap = pixGetColormap(pixd); 00794 pixmi = pixInvert(NULL, pixm); 00795 d = pixGetDepth(pixd); 00796 if (cmap) { 00797 extractRGBValues(outval, &rval, &gval, &bval); 00798 pixcmapGetNearestIndex(cmap, rval, gval, bval, &index); 00799 pixcmapGetColor(cmap, index, &rval, &gval, &bval); 00800 composeRGBPixel(rval, gval, bval, &pixel); 00801 pixPaintThroughMask(pixd, pixmi, 0, 0, pixel); 00802 } 00803 else 00804 pixPaintThroughMask(pixd, pixmi, 0, 0, outval); 00805 00806 boxDestroy(&box); 00807 pixDestroy(&pixmi); 00808 return pixd; 00809 } 00810 00811 00812 /*! 00813 * pixResizeToMatch() 00814 * 00815 * Input: pixs (1, 2, 4, 8, 16, 32 bpp; colormap ok) 00816 * pixt (can be null; we use only the size) 00817 * w, h (ignored if pixt is defined) 00818 * Return: pixd (resized to match) or null on error 00819 * 00820 * Notes: 00821 * (1) This resizes pixs to make pixd, without scaling, by either 00822 * cropping or extending separately in both width and height. 00823 * Extension is done by replicating the last row or column. 00824 * This is useful in a situation where, due to scaling 00825 * operations, two images that are expected to be the 00826 * same size can differ slightly in each dimension. 00827 * (2) You can use either an existing pixt or specify 00828 * both @w and @h. If pixt is defined, the values 00829 * in @w and @h are ignored. 00830 * (3) If pixt is larger than pixs (or if w and/or d is larger 00831 * than the dimension of pixs, replicate the outer row and 00832 * column of pixels in pixs into pixd. 00833 */ 00834 PIX * 00835 pixResizeToMatch(PIX *pixs, 00836 PIX *pixt, 00837 l_int32 w, 00838 l_int32 h) 00839 { 00840 l_int32 i, j, ws, hs, d; 00841 PIX *pixd; 00842 00843 PROCNAME("pixResizeToMatch"); 00844 00845 if (!pixs) 00846 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00847 if (!pixt && (w <= 0 || h <= 0)) 00848 return (PIX *)ERROR_PTR("both w and h not > 0", procName, NULL); 00849 00850 if (pixt) /* redefine w, h */ 00851 pixGetDimensions(pixt, &w, &h, NULL); 00852 pixGetDimensions(pixs, &ws, &hs, &d); 00853 if (ws == w && hs == h) 00854 return pixCopy(NULL, pixs); 00855 00856 if ((pixd = pixCreate(w, h, d)) == NULL) 00857 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00858 pixCopyResolution(pixd, pixs); 00859 pixCopyColormap(pixd, pixs); 00860 pixCopyText(pixd, pixs); 00861 pixCopyInputFormat(pixd, pixs); 00862 pixRasterop(pixd, 0, 0, ws, hs, PIX_SRC, pixs, 0, 0); 00863 if (ws >= w && hs >= h) 00864 return pixd; 00865 00866 /* Replicate the last column and then the last row */ 00867 if (ws < w) { 00868 for (j = ws; j < w; j++) 00869 pixRasterop(pixd, j, 0, 1, h, PIX_SRC, pixd, ws - 1, 0); 00870 } 00871 if (hs < h) { 00872 for (i = hs; i < h; i++) 00873 pixRasterop(pixd, 0, i, w, 1, PIX_SRC, pixd, 0, hs - 1); 00874 } 00875 00876 return pixd; 00877 } 00878 00879 00880 /*---------------------------------------------------------------------* 00881 * Clip to Foreground * 00882 *---------------------------------------------------------------------*/ 00883 /*! 00884 * pixClipToForeground() 00885 * 00886 * Input: pixs (1 bpp) 00887 * &pixd (<optional return> clipped pix returned) 00888 * &box (<optional return> bounding box) 00889 * Return: 0 if OK; 1 on error or if there are no fg pixels 00890 * 00891 * Notes: 00892 * (1) At least one of {&pixd, &box} must be specified. 00893 * (2) If there are no fg pixels, the returned ptrs are null. 00894 */ 00895 l_int32 00896 pixClipToForeground(PIX *pixs, 00897 PIX **ppixd, 00898 BOX **pbox) 00899 { 00900 l_int32 w, h, wpl, nfullwords, extra, i, j; 00901 l_int32 minx, miny, maxx, maxy; 00902 l_uint32 result, mask; 00903 l_uint32 *data, *line; 00904 BOX *box; 00905 00906 PROCNAME("pixClipToForeground"); 00907 00908 if (!ppixd && !pbox) 00909 return ERROR_INT("neither &pixd nor &box defined", procName, 1); 00910 if (ppixd) 00911 *ppixd = NULL; 00912 if (pbox) 00913 *pbox = NULL; 00914 if (!pixs || (pixGetDepth(pixs) != 1)) 00915 return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); 00916 00917 pixGetDimensions(pixs, &w, &h, NULL); 00918 nfullwords = w / 32; 00919 extra = w & 31; 00920 mask = ~rmask32[32 - extra]; 00921 wpl = pixGetWpl(pixs); 00922 data = pixGetData(pixs); 00923 00924 result = 0; 00925 for (i = 0, miny = 0; i < h; i++, miny++) { 00926 line = data + i * wpl; 00927 for (j = 0; j < nfullwords; j++) 00928 result |= line[j]; 00929 if (extra) 00930 result |= (line[j] & mask); 00931 if (result) 00932 break; 00933 } 00934 if (miny == h) /* no ON pixels */ 00935 return 1; 00936 00937 result = 0; 00938 for (i = h - 1, maxy = h - 1; i >= 0; i--, maxy--) { 00939 line = data + i * wpl; 00940 for (j = 0; j < nfullwords; j++) 00941 result |= line[j]; 00942 if (extra) 00943 result |= (line[j] & mask); 00944 if (result) 00945 break; 00946 } 00947 00948 minx = 0; 00949 for (j = 0, minx = 0; j < w; j++, minx++) { 00950 for (i = 0; i < h; i++) { 00951 line = data + i * wpl; 00952 if (GET_DATA_BIT(line, j)) 00953 goto minx_found; 00954 } 00955 } 00956 00957 minx_found: 00958 for (j = w - 1, maxx = w - 1; j >= 0; j--, maxx--) { 00959 for (i = 0; i < h; i++) { 00960 line = data + i * wpl; 00961 if (GET_DATA_BIT(line, j)) 00962 goto maxx_found; 00963 } 00964 } 00965 00966 maxx_found: 00967 box = boxCreate(minx, miny, maxx - minx + 1, maxy - miny + 1); 00968 00969 if (ppixd) 00970 *ppixd = pixClipRectangle(pixs, box, NULL); 00971 if (pbox) 00972 *pbox = box; 00973 else 00974 boxDestroy(&box); 00975 00976 return 0; 00977 } 00978 00979 00980 /*! 00981 * pixClipBoxToForeground() 00982 * 00983 * Input: pixs (1 bpp) 00984 * boxs (<optional> ; use full image if null) 00985 * &pixd (<optional return> clipped pix returned) 00986 * &boxd (<optional return> bounding box) 00987 * Return: 0 if OK; 1 on error or if there are no fg pixels 00988 * 00989 * Notes: 00990 * (1) At least one of {&pixd, &boxd} must be specified. 00991 * (2) If there are no fg pixels, the returned ptrs are null. 00992 * (3) Do not use &pixs for the 3rd arg or &boxs for the 4th arg; 00993 * this will leak memory. 00994 */ 00995 l_int32 00996 pixClipBoxToForeground(PIX *pixs, 00997 BOX *boxs, 00998 PIX **ppixd, 00999 BOX **pboxd) 01000 { 01001 l_int32 w, h, bx, by, bw, bh, cbw, cbh, left, right, top, bottom; 01002 BOX *boxt, *boxd; 01003 01004 PROCNAME("pixClipBoxToForeground"); 01005 01006 if (!ppixd && !pboxd) 01007 return ERROR_INT("neither &pixd nor &boxd defined", procName, 1); 01008 if (ppixd) *ppixd = NULL; 01009 if (pboxd) *pboxd = NULL; 01010 if (!pixs || (pixGetDepth(pixs) != 1)) 01011 return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); 01012 01013 if (!boxs) 01014 return pixClipToForeground(pixs, ppixd, pboxd); 01015 01016 pixGetDimensions(pixs, &w, &h, NULL); 01017 boxGetGeometry(boxs, &bx, &by, &bw, &bh); 01018 cbw = L_MIN(bw, w - bx); 01019 cbh = L_MIN(bh, h - by); 01020 if (cbw < 0 || cbh < 0) 01021 return ERROR_INT("box not within image", procName, 1); 01022 boxt = boxCreate(bx, by, cbw, cbh); 01023 01024 if (pixScanForForeground(pixs, boxt, L_FROM_LEFT, &left)) { 01025 boxDestroy(&boxt); 01026 return 1; 01027 } 01028 pixScanForForeground(pixs, boxt, L_FROM_RIGHT, &right); 01029 pixScanForForeground(pixs, boxt, L_FROM_TOP, &top); 01030 pixScanForForeground(pixs, boxt, L_FROM_BOTTOM, &bottom); 01031 01032 boxd = boxCreate(left, top, right - left + 1, bottom - top + 1); 01033 if (ppixd) 01034 *ppixd = pixClipRectangle(pixs, boxd, NULL); 01035 if (pboxd) 01036 *pboxd = boxd; 01037 else 01038 boxDestroy(&boxd); 01039 01040 boxDestroy(&boxt); 01041 return 0; 01042 } 01043 01044 01045 /*! 01046 * pixScanForForeground() 01047 * 01048 * Input: pixs (1 bpp) 01049 * box (<optional> within which the search is conducted) 01050 * scanflag (direction of scan; e.g., L_FROM_LEFT) 01051 * &loc (location in scan direction of first black pixel) 01052 * Return: 0 if OK; 1 on error or if no fg pixels are found 01053 * 01054 * Notes: 01055 * (1) If there are no fg pixels, the position is set to 0. 01056 * Caller must check the return value! 01057 * (2) Use @box == NULL to scan from edge of pixs 01058 */ 01059 l_int32 01060 pixScanForForeground(PIX *pixs, 01061 BOX *box, 01062 l_int32 scanflag, 01063 l_int32 *ploc) 01064 { 01065 l_int32 bx, by, bw, bh, x, xstart, xend, y, ystart, yend, wpl; 01066 l_uint32 *data, *line; 01067 BOX *boxt; 01068 01069 PROCNAME("pixScanForForeground"); 01070 01071 if (!ploc) 01072 return ERROR_INT("&ploc not defined", procName, 1); 01073 *ploc = 0; 01074 if (!pixs || (pixGetDepth(pixs) != 1)) 01075 return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); 01076 01077 /* Clip box to pixs if it exists */ 01078 pixGetDimensions(pixs, &bw, &bh, NULL); 01079 if (box) { 01080 if ((boxt = boxClipToRectangle(box, bw, bh)) == NULL) 01081 return ERROR_INT("invalid box", procName, 1); 01082 boxGetGeometry(boxt, &bx, &by, &bw, &bh); 01083 boxDestroy(&boxt); 01084 } 01085 else 01086 bx = by = 0; 01087 xstart = bx; 01088 ystart = by; 01089 xend = bx + bw - 1; 01090 yend = by + bh - 1; 01091 01092 data = pixGetData(pixs); 01093 wpl = pixGetWpl(pixs); 01094 if (scanflag == L_FROM_LEFT) { 01095 for (x = xstart; x <= xend; x++) { 01096 for (y = ystart; y <= yend; y++) { 01097 line = data + y * wpl; 01098 if (GET_DATA_BIT(line, x)) { 01099 *ploc = x; 01100 return 0; 01101 } 01102 } 01103 } 01104 } 01105 else if (scanflag == L_FROM_RIGHT) { 01106 for (x = xend; x >= xstart; x--) { 01107 for (y = ystart; y <= yend; y++) { 01108 line = data + y * wpl; 01109 if (GET_DATA_BIT(line, x)) { 01110 *ploc = x; 01111 return 0; 01112 } 01113 } 01114 } 01115 } 01116 else if (scanflag == L_FROM_TOP) { 01117 for (y = ystart; y <= yend; y++) { 01118 line = data + y * wpl; 01119 for (x = xstart; x <= xend; x++) { 01120 if (GET_DATA_BIT(line, x)) { 01121 *ploc = y; 01122 return 0; 01123 } 01124 } 01125 } 01126 } 01127 else if (scanflag == L_FROM_BOTTOM) { 01128 for (y = yend; y >= ystart; y--) { 01129 line = data + y * wpl; 01130 for (x = xstart; x <= xend; x++) { 01131 if (GET_DATA_BIT(line, x)) { 01132 *ploc = y; 01133 return 0; 01134 } 01135 } 01136 } 01137 } 01138 else 01139 return ERROR_INT("invalid scanflag", procName, 1); 01140 01141 return 1; /* no fg found */ 01142 } 01143 01144 01145 /*! 01146 * pixClipBoxToEdges() 01147 * 01148 * Input: pixs (1 bpp) 01149 * boxs (<optional> ; use full image if null) 01150 * lowthresh (threshold to choose clipping location) 01151 * highthresh (threshold required to find an edge) 01152 * maxwidth (max allowed width between low and high thresh locs) 01153 * factor (sampling factor along pixel counting direction) 01154 * &pixd (<optional return> clipped pix returned) 01155 * &boxd (<optional return> bounding box) 01156 * Return: 0 if OK; 1 on error or if a fg edge is not found from 01157 * all four sides. 01158 * 01159 * Notes: 01160 * (1) At least one of {&pixd, &boxd} must be specified. 01161 * (2) If there are no fg pixels, the returned ptrs are null. 01162 * (3) This function attempts to locate rectangular "image" regions 01163 * of high-density fg pixels, that have well-defined edges 01164 * on the four sides. 01165 * (4) Edges are searched for on each side, iterating in order 01166 * from left, right, top and bottom. As each new edge is 01167 * found, the search box is resized to use that location. 01168 * Once an edge is found, it is held. If no more edges 01169 * are found in one iteration, the search fails. 01170 * (5) See pixScanForEdge() for usage of the thresholds and @maxwidth. 01171 * (6) The thresholds must be at least 1, and the low threshold 01172 * cannot be larger than the high threshold. 01173 * (7) If the low and high thresholds are both 1, this is equivalent 01174 * to pixClipBoxToForeground(). 01175 */ 01176 l_int32 01177 pixClipBoxToEdges(PIX *pixs, 01178 BOX *boxs, 01179 l_int32 lowthresh, 01180 l_int32 highthresh, 01181 l_int32 maxwidth, 01182 l_int32 factor, 01183 PIX **ppixd, 01184 BOX **pboxd) 01185 { 01186 l_int32 w, h, bx, by, bw, bh, cbw, cbh, left, right, top, bottom; 01187 l_int32 lfound, rfound, tfound, bfound, change; 01188 BOX *boxt, *boxd; 01189 01190 PROCNAME("pixClipBoxToEdges"); 01191 01192 if (!ppixd && !pboxd) 01193 return ERROR_INT("neither &pixd nor &boxd defined", procName, 1); 01194 if (ppixd) *ppixd = NULL; 01195 if (pboxd) *pboxd = NULL; 01196 if (!pixs || (pixGetDepth(pixs) != 1)) 01197 return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); 01198 if (lowthresh < 1 || highthresh < 1 || 01199 lowthresh > highthresh || maxwidth < 1) 01200 return ERROR_INT("invalid thresholds", procName, 1); 01201 factor = L_MIN(1, factor); 01202 01203 if (lowthresh == 1 && highthresh == 1) 01204 return pixClipBoxToForeground(pixs, boxs, ppixd, pboxd); 01205 01206 pixGetDimensions(pixs, &w, &h, NULL); 01207 if (boxs) { 01208 boxGetGeometry(boxs, &bx, &by, &bw, &bh); 01209 cbw = L_MIN(bw, w - bx); 01210 cbh = L_MIN(bh, h - by); 01211 if (cbw < 0 || cbh < 0) 01212 return ERROR_INT("box not within image", procName, 1); 01213 boxt = boxCreate(bx, by, cbw, cbh); 01214 } 01215 else 01216 boxt = boxCreate(0, 0, w, h); 01217 01218 lfound = rfound = tfound = bfound = 0; 01219 while (!lfound || !rfound || !tfound || !bfound) { 01220 change = 0; 01221 if (!lfound) { 01222 if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth, 01223 factor, L_FROM_LEFT, &left)) { 01224 lfound = 1; 01225 change = 1; 01226 boxRelocateOneSide(boxt, boxt, left, L_FROM_LEFT); 01227 } 01228 } 01229 if (!rfound) { 01230 if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth, 01231 factor, L_FROM_RIGHT, &right)) { 01232 rfound = 1; 01233 change = 1; 01234 boxRelocateOneSide(boxt, boxt, right, L_FROM_RIGHT); 01235 } 01236 } 01237 if (!tfound) { 01238 if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth, 01239 factor, L_FROM_TOP, &top)) { 01240 tfound = 1; 01241 change = 1; 01242 boxRelocateOneSide(boxt, boxt, top, L_FROM_TOP); 01243 } 01244 } 01245 if (!bfound) { 01246 if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth, 01247 factor, L_FROM_BOTTOM, &bottom)) { 01248 bfound = 1; 01249 change = 1; 01250 boxRelocateOneSide(boxt, boxt, bottom, L_FROM_BOTTOM); 01251 } 01252 } 01253 01254 #if DEBUG_EDGES 01255 fprintf(stderr, "iter: %d %d %d %d\n", lfound, rfound, tfound, bfound); 01256 #endif /* DEBUG_EDGES */ 01257 01258 if (change == 0) break; 01259 } 01260 boxDestroy(&boxt); 01261 01262 if (change == 0) 01263 return ERROR_INT("not all edges found", procName, 1); 01264 01265 boxd = boxCreate(left, top, right - left + 1, bottom - top + 1); 01266 if (ppixd) 01267 *ppixd = pixClipRectangle(pixs, boxd, NULL); 01268 if (pboxd) 01269 *pboxd = boxd; 01270 else 01271 boxDestroy(&boxd); 01272 01273 return 0; 01274 } 01275 01276 01277 /*! 01278 * pixScanForEdge() 01279 * 01280 * Input: pixs (1 bpp) 01281 * box (<optional> within which the search is conducted) 01282 * lowthresh (threshold to choose clipping location) 01283 * highthresh (threshold required to find an edge) 01284 * maxwidth (max allowed width between low and high thresh locs) 01285 * factor (sampling factor along pixel counting direction) 01286 * scanflag (direction of scan; e.g., L_FROM_LEFT) 01287 * &loc (location in scan direction of first black pixel) 01288 * Return: 0 if OK; 1 on error or if the edge is not found 01289 * 01290 * Notes: 01291 * (1) If there are no fg pixels, the position is set to 0. 01292 * Caller must check the return value! 01293 * (2) Use @box == NULL to scan from edge of pixs 01294 * (3) As the scan progresses, the location where the sum of 01295 * pixels equals or excees @lowthresh is noted (loc). The 01296 * scan is stopped when the sum of pixels equals or exceeds 01297 * @highthresh. If the scan distance between loc and that 01298 * point does not exceed @maxwidth, an edge is found and 01299 * its position is taken to be loc. @maxwidth implicitly 01300 * sets a minimum on the required gradient of the edge. 01301 * (4) The thresholds must be at least 1, and the low threshold 01302 * cannot be larger than the high threshold. 01303 */ 01304 l_int32 01305 pixScanForEdge(PIX *pixs, 01306 BOX *box, 01307 l_int32 lowthresh, 01308 l_int32 highthresh, 01309 l_int32 maxwidth, 01310 l_int32 factor, 01311 l_int32 scanflag, 01312 l_int32 *ploc) 01313 { 01314 l_int32 bx, by, bw, bh, foundmin, loc, sum, wpl; 01315 l_int32 x, xstart, xend, y, ystart, yend; 01316 l_uint32 *data, *line; 01317 BOX *boxt; 01318 01319 PROCNAME("pixScanForEdge"); 01320 01321 if (!ploc) 01322 return ERROR_INT("&ploc not defined", procName, 1); 01323 *ploc = 0; 01324 if (!pixs || (pixGetDepth(pixs) != 1)) 01325 return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); 01326 if (lowthresh < 1 || highthresh < 1 || 01327 lowthresh > highthresh || maxwidth < 1) 01328 return ERROR_INT("invalid thresholds", procName, 1); 01329 factor = L_MIN(1, factor); 01330 01331 /* Clip box to pixs if it exists */ 01332 pixGetDimensions(pixs, &bw, &bh, NULL); 01333 if (box) { 01334 if ((boxt = boxClipToRectangle(box, bw, bh)) == NULL) 01335 return ERROR_INT("invalid box", procName, 1); 01336 boxGetGeometry(boxt, &bx, &by, &bw, &bh); 01337 boxDestroy(&boxt); 01338 } 01339 else 01340 bx = by = 0; 01341 xstart = bx; 01342 ystart = by; 01343 xend = bx + bw - 1; 01344 yend = by + bh - 1; 01345 01346 data = pixGetData(pixs); 01347 wpl = pixGetWpl(pixs); 01348 foundmin = 0; 01349 if (scanflag == L_FROM_LEFT) { 01350 for (x = xstart; x <= xend; x++) { 01351 sum = 0; 01352 for (y = ystart; y <= yend; y += factor) { 01353 line = data + y * wpl; 01354 if (GET_DATA_BIT(line, x)) 01355 sum++; 01356 } 01357 if (!foundmin && sum < lowthresh) 01358 continue; 01359 if (!foundmin) { /* save the loc of the beginning of the edge */ 01360 foundmin = 1; 01361 loc = x; 01362 } 01363 if (sum >= highthresh) { 01364 #if DEBUG_EDGES 01365 fprintf(stderr, "Left: x = %d, loc = %d\n", x, loc); 01366 #endif /* DEBUG_EDGES */ 01367 if (x - loc < maxwidth) { 01368 *ploc = loc; 01369 return 0; 01370 } 01371 else return 1; 01372 } 01373 } 01374 } 01375 else if (scanflag == L_FROM_RIGHT) { 01376 for (x = xend; x >= xstart; x--) { 01377 sum = 0; 01378 for (y = ystart; y <= yend; y += factor) { 01379 line = data + y * wpl; 01380 if (GET_DATA_BIT(line, x)) 01381 sum++; 01382 } 01383 if (!foundmin && sum < lowthresh) 01384 continue; 01385 if (!foundmin) { 01386 foundmin = 1; 01387 loc = x; 01388 } 01389 if (sum >= highthresh) { 01390 #if DEBUG_EDGES 01391 fprintf(stderr, "Right: x = %d, loc = %d\n", x, loc); 01392 #endif /* DEBUG_EDGES */ 01393 if (loc - x < maxwidth) { 01394 *ploc = loc; 01395 return 0; 01396 } 01397 else return 1; 01398 } 01399 } 01400 } 01401 else if (scanflag == L_FROM_TOP) { 01402 for (y = ystart; y <= yend; y++) { 01403 sum = 0; 01404 line = data + y * wpl; 01405 for (x = xstart; x <= xend; x += factor) { 01406 if (GET_DATA_BIT(line, x)) 01407 sum++; 01408 } 01409 if (!foundmin && sum < lowthresh) 01410 continue; 01411 if (!foundmin) { 01412 foundmin = 1; 01413 loc = y; 01414 } 01415 if (sum >= highthresh) { 01416 #if DEBUG_EDGES 01417 fprintf(stderr, "Top: y = %d, loc = %d\n", y, loc); 01418 #endif /* DEBUG_EDGES */ 01419 if (y - loc < maxwidth) { 01420 *ploc = loc; 01421 return 0; 01422 } 01423 else return 1; 01424 } 01425 } 01426 } 01427 else if (scanflag == L_FROM_BOTTOM) { 01428 for (y = yend; y >= ystart; y--) { 01429 sum = 0; 01430 line = data + y * wpl; 01431 for (x = xstart; x <= xend; x += factor) { 01432 if (GET_DATA_BIT(line, x)) 01433 sum++; 01434 } 01435 if (!foundmin && sum < lowthresh) 01436 continue; 01437 if (!foundmin) { 01438 foundmin = 1; 01439 loc = y; 01440 } 01441 if (sum >= highthresh) { 01442 #if DEBUG_EDGES 01443 fprintf(stderr, "Bottom: y = %d, loc = %d\n", y, loc); 01444 #endif /* DEBUG_EDGES */ 01445 if (loc - y < maxwidth) { 01446 *ploc = loc; 01447 return 0; 01448 } 01449 else return 1; 01450 } 01451 } 01452 } 01453 else 01454 return ERROR_INT("invalid scanflag", procName, 1); 01455 01456 return 1; /* edge not found */ 01457 } 01458 01459 01460 /*---------------------------------------------------------------------* 01461 * Extract pixel averages and reversals along lines * 01462 *---------------------------------------------------------------------*/ 01463 /*! 01464 * pixExtractOnLine() 01465 * 01466 * Input: pixs (1 bpp or 8 bpp; no colormap) 01467 * x1, y1 (one end point for line) 01468 * x2, y2 (another end pt for line) 01469 * factor (sampling; >= 1) 01470 * Return: na (of pixel values along line), or null on error. 01471 * 01472 * Notes: 01473 * (1) Input end points are clipped to the pix. 01474 * (2) If the line is either horizontal, or closer to horizontal 01475 * than to vertical, the points will be extracted from left 01476 * to right in the pix. Likewise, if the line is vertical, 01477 * or closer to vertical than to horizontal, the points will 01478 * be extracted from top to bottom. 01479 * (3) Can be used with numaCountReverals(), for example, to 01480 * characterize the intensity smoothness along a line. 01481 */ 01482 NUMA * 01483 pixExtractOnLine(PIX *pixs, 01484 l_int32 x1, 01485 l_int32 y1, 01486 l_int32 x2, 01487 l_int32 y2, 01488 l_int32 factor) 01489 { 01490 l_int32 i, w, h, d, xmin, ymin, xmax, ymax, npts, direction; 01491 l_uint32 val; 01492 l_float32 x, y; 01493 l_float64 slope; 01494 NUMA *na; 01495 PTA *pta; 01496 01497 PROCNAME("pixExtractOnLine"); 01498 01499 if (!pixs) 01500 return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); 01501 pixGetDimensions(pixs, &w, &h, &d); 01502 if (d != 1 && d != 8) 01503 return (NUMA *)ERROR_PTR("d not 1 or 8 bpp", procName, NULL); 01504 if (pixGetColormap(pixs)) 01505 return (NUMA *)ERROR_PTR("pixs has a colormap", procName, NULL); 01506 if (factor < 1) { 01507 L_WARNING("factor must be >= 1; setting to 1", procName); 01508 factor = 1; 01509 } 01510 01511 /* Clip line to the image */ 01512 x1 = L_MAX(0, L_MIN(x1, w - 1)); 01513 x2 = L_MAX(0, L_MIN(x2, w - 1)); 01514 y1 = L_MAX(0, L_MIN(y1, h - 1)); 01515 y2 = L_MAX(0, L_MIN(y2, h - 1)); 01516 01517 if (x1 == x2 && y1 == y2) { 01518 pixGetPixel(pixs, x1, y1, &val); 01519 na = numaCreate(1); 01520 numaAddNumber(na, val); 01521 return na; 01522 } 01523 01524 if (y1 == y2) 01525 direction = L_HORIZONTAL_LINE; 01526 else if (x1 == x2) 01527 direction = L_VERTICAL_LINE; 01528 else 01529 direction = L_OBLIQUE_LINE; 01530 01531 na = numaCreate(0); 01532 if (direction == L_HORIZONTAL_LINE) { /* plot against x */ 01533 xmin = L_MIN(x1, x2); 01534 xmax = L_MAX(x1, x2); 01535 numaSetXParameters(na, xmin, factor); 01536 for (i = xmin; i <= xmax; i += factor) { 01537 pixGetPixel(pixs, i, y1, &val); 01538 numaAddNumber(na, val); 01539 } 01540 } 01541 else if (direction == L_VERTICAL_LINE) { /* plot against y */ 01542 ymin = L_MIN(y1, y2); 01543 ymax = L_MAX(y1, y2); 01544 numaSetXParameters(na, ymin, factor); 01545 for (i = ymin; i <= ymax; i += factor) { 01546 pixGetPixel(pixs, x1, i, &val); 01547 numaAddNumber(na, val); 01548 } 01549 } 01550 else { /* direction == L_OBLIQUE_LINE */ 01551 slope = (l_float64)((y2 - y1) / (x2 - x1)); 01552 if (L_ABS(slope) < 1.0) { /* quasi-horizontal */ 01553 xmin = L_MIN(x1, x2); 01554 xmax = L_MAX(x1, x2); 01555 ymin = (xmin == x1) ? y1 : y2; /* pt that goes with xmin */ 01556 ymax = (ymin == y1) ? y2 : y1; /* pt that goes with xmax */ 01557 pta = generatePtaLine(xmin, ymin, xmax, ymax); 01558 numaSetXParameters(na, xmin, (l_float32)factor); 01559 } 01560 else { /* quasi-vertical */ 01561 ymin = L_MIN(y1, y2); 01562 ymax = L_MAX(y1, y2); 01563 xmin = (ymin == y1) ? x1 : x2; /* pt that goes with ymin */ 01564 xmax = (xmin == x1) ? x2 : x1; /* pt that goes with ymax */ 01565 pta = generatePtaLine(xmin, ymin, xmax, ymax); 01566 numaSetXParameters(na, ymin, (l_float32)factor); 01567 } 01568 npts = ptaGetCount(pta); 01569 for (i = 0; i < npts; i += factor) { 01570 ptaGetPt(pta, i, &x, &y); 01571 pixGetPixel(pixs, (l_int32)x, (l_int32)y, &val); 01572 numaAddNumber(na, val); 01573 } 01574 01575 #if 0 /* debugging */ 01576 pixPlotAlongPta(pixs, pta, GPLOT_X11, NULL); 01577 #endif 01578 01579 ptaDestroy(&pta); 01580 } 01581 01582 return na; 01583 } 01584 01585 01586 /*! 01587 * pixAverageOnLine() 01588 * 01589 * Input: pixs (1 bpp or 8 bpp; no colormap) 01590 * x1, y1 (starting pt for line) 01591 * x2, y2 (end pt for line) 01592 * factor (sampling; >= 1) 01593 * Return: average of pixel values along line, or null on error. 01594 * 01595 * Notes: 01596 * (1) The line must be either horizontal or vertical, so either 01597 * y1 == y2 (horizontal) or x1 == x2 (vertical). 01598 * (2) If horizontal, x1 must be <= x2. 01599 * If vertical, y1 must be <= y2. 01600 * characterize the intensity smoothness along a line. 01601 * (3) Input end points are clipped to the pix. 01602 */ 01603 l_float32 01604 pixAverageOnLine(PIX *pixs, 01605 l_int32 x1, 01606 l_int32 y1, 01607 l_int32 x2, 01608 l_int32 y2, 01609 l_int32 factor) 01610 { 01611 l_int32 i, j, w, h, d, direction, count, wpl; 01612 l_uint32 *data, *line; 01613 l_float32 sum; 01614 01615 PROCNAME("pixAverageOnLine"); 01616 01617 if (!pixs) 01618 return ERROR_INT("pixs not defined", procName, 1); 01619 pixGetDimensions(pixs, &w, &h, &d); 01620 if (d != 1 && d != 8) 01621 return ERROR_INT("d not 1 or 8 bpp", procName, 1); 01622 if (pixGetColormap(pixs)) 01623 return ERROR_INT("pixs has a colormap", procName, 1); 01624 if (x1 > x2 || y1 > y2) 01625 return ERROR_INT("x1 > x2 or y1 > y2", procName, 1); 01626 if (y1 == y2) { 01627 x1 = L_MAX(0, x1); 01628 x2 = L_MIN(w - 1, x2); 01629 y1 = L_MAX(0, L_MIN(y1, h - 1)); 01630 direction = L_HORIZONTAL_LINE; 01631 } 01632 else if (x1 == x2) { 01633 y1 = L_MAX(0, y1); 01634 y2 = L_MIN(h - 1, y2); 01635 x1 = L_MAX(0, L_MIN(x1, w - 1)); 01636 direction = L_VERTICAL_LINE; 01637 } 01638 else 01639 return ERROR_INT("line neither horiz nor vert", procName, 1); 01640 if (factor < 1) { 01641 L_WARNING("factor must be >= 1; setting to 1", procName); 01642 factor = 1; 01643 } 01644 01645 data = pixGetData(pixs); 01646 wpl = pixGetWpl(pixs); 01647 sum = 0; 01648 if (direction == L_HORIZONTAL_LINE) { 01649 line = data + y1 * wpl; 01650 for (j = x1, count = 0; j <= x2; count++, j += factor) { 01651 if (d == 1) 01652 sum += GET_DATA_BIT(line, j); 01653 else /* d == 8 */ 01654 sum += GET_DATA_BYTE(line, j); 01655 } 01656 } 01657 else if (direction == L_VERTICAL_LINE) { 01658 for (i = y1, count = 0; i <= y2; count++, i += factor) { 01659 line = data + i * wpl; 01660 if (d == 1) 01661 sum += GET_DATA_BIT(line, x1); 01662 else /* d == 8 */ 01663 sum += GET_DATA_BYTE(line, x1); 01664 } 01665 } 01666 01667 return sum / (l_float32)count; 01668 } 01669 01670 01671 /*! 01672 * pixAverageIntensityProfile() 01673 * 01674 * Input: pixs (any depth; colormap OK) 01675 * fract (fraction of image width or height to be used) 01676 * dir (averaging direction: L_HORIZONTAL_LINE or L_VERTICAL_LINE) 01677 * first, last (span of rows or columns to measure) 01678 * factor1 (sampling along fast scan direction; >= 1) 01679 * factor2 (sampling along slow scan direction; >= 1) 01680 * Return: na (of reversal profile), or null on error. 01681 * 01682 * Notes: 01683 * (1) If d != 1 bpp, colormaps are removed and the result 01684 * is converted to 8 bpp. 01685 * (2) If @dir == L_HORIZONTAL_LINE, the intensity is averaged 01686 * along each horizontal raster line (sampled by @factor1), 01687 * and the profile is the array of these averages in the 01688 * vertical direction between @first and @last raster lines, 01689 * and sampled by @factor2. 01690 * (3) If @dir == L_VERTICAL_LINE, the intensity is averaged 01691 * along each vertical line (sampled by @factor1), 01692 * and the profile is the array of these averages in the 01693 * horizontal direction between @first and @last columns, 01694 * and sampled by @factor2. 01695 * (4) The averages are measured over the central @fract of the image. 01696 * Use @fract == 1.0 to average across the entire width or height. 01697 */ 01698 NUMA * 01699 pixAverageIntensityProfile(PIX *pixs, 01700 l_float32 fract, 01701 l_int32 dir, 01702 l_int32 first, 01703 l_int32 last, 01704 l_int32 factor1, 01705 l_int32 factor2) 01706 { 01707 l_int32 i, j, w, h, d, start, end; 01708 l_float32 ave; 01709 NUMA *nad; 01710 PIX *pixr, *pixg; 01711 01712 PROCNAME("pixAverageIntensityProfile"); 01713 01714 if (!pixs) 01715 return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); 01716 if (fract < 0.0 || fract > 1.0) 01717 return (NUMA *)ERROR_PTR("fract < 0.0 or > 1.0", procName, NULL); 01718 if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE) 01719 return (NUMA *)ERROR_PTR("invalid direction", procName, NULL); 01720 if (first < 0) first = 0; 01721 if (last < first) 01722 return (NUMA *)ERROR_PTR("last must be >= first", procName, NULL); 01723 if (factor1 < 1) { 01724 L_WARNING("factor1 must be >= 1; setting to 1", procName); 01725 factor1 = 1; 01726 } 01727 if (factor2 < 1) { 01728 L_WARNING("factor2 must be >= 1; setting to 1", procName); 01729 factor2 = 1; 01730 } 01731 01732 /* Use 1 or 8 bpp, without colormap */ 01733 if (pixGetColormap(pixs)) 01734 pixr = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); 01735 else 01736 pixr = pixClone(pixs); 01737 pixGetDimensions(pixr, &w, &h, &d); 01738 if (d == 1) 01739 pixg = pixClone(pixr); 01740 else 01741 pixg = pixConvertTo8(pixr, 0); 01742 01743 nad = numaCreate(0); /* output: samples in slow scan direction */ 01744 numaSetXParameters(nad, 0, factor2); 01745 if (dir == L_HORIZONTAL_LINE) { 01746 start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)w); 01747 end = w - start; 01748 if (last > h - 1) { 01749 L_WARNING("last > h - 1; clipping", procName); 01750 last = h - 1; 01751 } 01752 for (i = first; i <= last; i += factor2) { 01753 ave = pixAverageOnLine(pixg, start, i, end, i, factor1); 01754 numaAddNumber(nad, ave); 01755 } 01756 } else if (dir == L_VERTICAL_LINE) { 01757 start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)h); 01758 end = h - start; 01759 if (last > w - 1) { 01760 L_WARNING("last > w - 1; clipping", procName); 01761 last = w - 1; 01762 } 01763 for (j = first; j <= last; j += factor2) { 01764 ave = pixAverageOnLine(pixg, j, start, j, end, factor1); 01765 numaAddNumber(nad, ave); 01766 } 01767 } 01768 01769 pixDestroy(&pixr); 01770 pixDestroy(&pixg); 01771 return nad; 01772 } 01773 01774 01775 /*! 01776 * pixReversalProfile() 01777 * 01778 * Input: pixs (any depth; colormap OK) 01779 * fract (fraction of image width or height to be used) 01780 * dir (profile direction: L_HORIZONTAL_LINE or L_VERTICAL_LINE) 01781 * first, last (span of rows or columns to measure) 01782 * minreversal (minimum change in intensity to trigger a reversal) 01783 * factor1 (sampling along raster line (fast scan); >= 1) 01784 * factor2 (sampling of raster lines (slow scan); >= 1) 01785 * Return: na (of reversal profile), or null on error. 01786 * 01787 * Notes: 01788 * (1) If d != 1 bpp, colormaps are removed and the result 01789 * is converted to 8 bpp. 01790 * (2) If @dir == L_HORIZONTAL_LINE, the the reversals are counted 01791 * along each horizontal raster line (sampled by @factor1), 01792 * and the profile is the array of these sums in the 01793 * vertical direction between @first and @last raster lines, 01794 * and sampled by @factor2. 01795 * (3) If @dir == L_VERTICAL_LINE, the the reversals are counted 01796 * along each vertical column (sampled by @factor1), 01797 * and the profile is the array of these sums in the 01798 * horizontal direction between @first and @last columns, 01799 * and sampled by @factor2. 01800 * (4) For each row or column, the reversals are summed over the 01801 * central @fract of the image. Use @fract == 1.0 to sum 01802 * across the entire width (of row) or height (of column). 01803 * (5) @minreversal is the relative change in intensity that is 01804 * required to resolve peaks and valleys. A typical number for 01805 * locating text in 8 bpp might be 50. For 1 bpp, minreversal 01806 * must be 1. 01807 * (6) The reversal profile is simply the number of reversals 01808 * in a row or column, vs the row or column index. 01809 */ 01810 NUMA * 01811 pixReversalProfile(PIX *pixs, 01812 l_float32 fract, 01813 l_int32 dir, 01814 l_int32 first, 01815 l_int32 last, 01816 l_int32 minreversal, 01817 l_int32 factor1, 01818 l_int32 factor2) 01819 { 01820 l_int32 i, j, w, h, d, start, end, nr; 01821 NUMA *naline, *nad; 01822 PIX *pixr, *pixg; 01823 01824 PROCNAME("pixReversalProfile"); 01825 01826 if (!pixs) 01827 return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); 01828 if (fract < 0.0 || fract > 1.0) 01829 return (NUMA *)ERROR_PTR("fract < 0.0 or > 1.0", procName, NULL); 01830 if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE) 01831 return (NUMA *)ERROR_PTR("invalid direction", procName, NULL); 01832 if (first < 0) first = 0; 01833 if (last < first) 01834 return (NUMA *)ERROR_PTR("last must be >= first", procName, NULL); 01835 if (factor1 < 1) { 01836 L_WARNING("factor1 must be >= 1; setting to 1", procName); 01837 factor1 = 1; 01838 } 01839 if (factor2 < 1) { 01840 L_WARNING("factor2 must be >= 1; setting to 1", procName); 01841 factor2 = 1; 01842 } 01843 01844 /* Use 1 or 8 bpp, without colormap */ 01845 if (pixGetColormap(pixs)) 01846 pixr = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); 01847 else 01848 pixr = pixClone(pixs); 01849 pixGetDimensions(pixr, &w, &h, &d); 01850 if (d == 1) { 01851 pixg = pixClone(pixr); 01852 minreversal = 1; /* enforce this */ 01853 } 01854 else 01855 pixg = pixConvertTo8(pixr, 0); 01856 01857 nad = numaCreate(0); /* output: samples in slow scan direction */ 01858 numaSetXParameters(nad, 0, factor2); 01859 if (dir == L_HORIZONTAL_LINE) { 01860 start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)w); 01861 end = w - start; 01862 if (last > h - 1) { 01863 L_WARNING("last > h - 1; clipping", procName); 01864 last = h - 1; 01865 } 01866 for (i = first; i <= last; i += factor2) { 01867 naline = pixExtractOnLine(pixg, start, i, end, i, factor1); 01868 numaCountReversals(naline, minreversal, &nr, NULL); 01869 numaAddNumber(nad, nr); 01870 numaDestroy(&naline); 01871 } 01872 } else if (dir == L_VERTICAL_LINE) { 01873 start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)h); 01874 end = h - start; 01875 if (last > w - 1) { 01876 L_WARNING("last > w - 1; clipping", procName); 01877 last = w - 1; 01878 } 01879 for (j = first; j <= last; j += factor2) { 01880 naline = pixExtractOnLine(pixg, j, start, j, end, factor1); 01881 numaCountReversals(naline, minreversal, &nr, NULL); 01882 numaAddNumber(nad, nr); 01883 numaDestroy(&naline); 01884 } 01885 } 01886 01887 pixDestroy(&pixr); 01888 pixDestroy(&pixg); 01889 return nad; 01890 } 01891 01892 01893 /*---------------------------------------------------------------------* 01894 * Rank row and column transforms * 01895 *---------------------------------------------------------------------*/ 01896 /*! 01897 * pixRankRowTransform() 01898 * 01899 * Input: pixs (8 bpp; no colormap) 01900 * Return: pixd (with pixels sorted in each row, from 01901 * min to max value) 01902 * 01903 * Notes: 01904 * (1) The time is O(n) in the number of pixels and runs about 01905 * 100 Mpixels/sec on a 3 GHz machine. 01906 */ 01907 PIX * 01908 pixRankRowTransform(PIX *pixs) 01909 { 01910 l_int32 i, j, k, m, w, h, wpl, val; 01911 l_int32 histo[256]; 01912 l_uint32 *datas, *datad, *lines, *lined; 01913 PIX *pixd; 01914 01915 PROCNAME("pixRankRowTransform"); 01916 01917 if (!pixs) 01918 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01919 if (pixGetDepth(pixs) != 8) 01920 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 01921 if (pixGetColormap(pixs)) 01922 return (PIX *)ERROR_PTR("pixs has a colormap", procName, NULL); 01923 01924 pixGetDimensions(pixs, &w, &h, NULL); 01925 pixd = pixCreateTemplateNoInit(pixs); 01926 datas = pixGetData(pixs); 01927 datad = pixGetData(pixd); 01928 wpl = pixGetWpl(pixs); 01929 for (i = 0; i < h; i++) { 01930 memset(histo, 0, 1024); 01931 lines = datas + i * wpl; 01932 lined = datad + i * wpl; 01933 for (j = 0; j < w; j++) { 01934 val = GET_DATA_BYTE(lines, j); 01935 histo[val]++; 01936 } 01937 for (m = 0, j = 0; m < 256; m++) { 01938 for (k = 0; k < histo[m]; k++, j++) 01939 SET_DATA_BYTE(lined, j, m); 01940 } 01941 } 01942 01943 return pixd; 01944 } 01945 01946 01947 /*! 01948 * pixRankColumnTransform() 01949 * 01950 * Input: pixs (8 bpp; no colormap) 01951 * Return: pixd (with pixels sorted in each column, from 01952 * min to max value) 01953 * 01954 * Notes: 01955 * (1) The time is O(n) in the number of pixels and runs about 01956 * 50 Mpixels/sec on a 3 GHz machine. 01957 */ 01958 PIX * 01959 pixRankColumnTransform(PIX *pixs) 01960 { 01961 l_int32 i, j, k, m, w, h, wpl, val; 01962 l_int32 histo[256]; 01963 l_uint32 *datas, *datad; 01964 void **lines8, **lined8; 01965 PIX *pixd; 01966 01967 PROCNAME("pixRankColumnTransform"); 01968 01969 if (!pixs) 01970 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01971 if (pixGetDepth(pixs) != 8) 01972 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 01973 if (pixGetColormap(pixs)) 01974 return (PIX *)ERROR_PTR("pixs has a colormap", procName, NULL); 01975 01976 pixGetDimensions(pixs, &w, &h, NULL); 01977 pixd = pixCreateTemplateNoInit(pixs); 01978 datas = pixGetData(pixs); 01979 datad = pixGetData(pixd); 01980 wpl = pixGetWpl(pixs); 01981 lines8 = pixGetLinePtrs(pixs, NULL); 01982 lined8 = pixGetLinePtrs(pixd, NULL); 01983 for (j = 0; j < w; j++) { 01984 memset(histo, 0, 1024); 01985 for (i = 0; i < h; i++) { 01986 val = GET_DATA_BYTE(lines8[i], j); 01987 histo[val]++; 01988 } 01989 for (m = 0, i = 0; m < 256; m++) { 01990 for (k = 0; k < histo[m]; k++, i++) 01991 SET_DATA_BYTE(lined8[i], j, m); 01992 } 01993 } 01994 01995 FREE(lines8); 01996 FREE(lined8); 01997 return pixd; 01998 } 01999