Leptonica 1.68
C Image Processing Library

pix5.c

Go to the documentation of this file.
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 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines