Leptonica 1.68
C Image Processing Library

boxfunc2.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  *   boxfunc2.c
00018  *
00019  *      Boxa/Box transform (shift, scale) and orthogonal rotation
00020  *           BOXA            *boxaTransform()
00021  *           BOX             *boxTransform()
00022  *           BOXA            *boxaTransformOrdered()
00023  *           BOX             *boxTransformOrdered()
00024  *           BOXA            *boxaRotateOrth()
00025  *           BOX             *boxRotateOrth()
00026  *
00027  *      Boxa sort
00028  *           BOXA            *boxaSort()
00029  *           BOXA            *boxaBinSort()
00030  *           BOXA            *boxaSortByIndex()
00031  *           BOXAA           *boxaSort2d()
00032  *           BOXAA           *boxaSort2dByIndex()
00033  *
00034  *      Boxa statistics
00035  *           BOX             *boxaGetRankSize()
00036  *           BOX             *boxaGetMedian()
00037  *
00038  *      Other boxaa functions
00039  *           l_int32          boxaaGetExtent()
00040  *           BOXA            *boxaaFlattenToBoxa()
00041  *           l_int32          boxaaAlignBox()
00042  */
00043 
00044 #include <stdio.h>
00045 #include <stdlib.h>
00046 #include <math.h>
00047 #include "allheaders.h"
00048 
00049     /* For more than this number of c.c. in a binarized image of
00050      * semi-perimeter (w + h) about 5000 or less, the O(n) binsort
00051      * is faster than the O(nlogn) shellsort.  */
00052 static const l_int32   MIN_COMPS_FOR_BIN_SORT = 500;
00053 
00054 
00055 /*---------------------------------------------------------------------*
00056  *      Boxa/Box transform (shift, scale) and orthogonal rotation      *
00057  *---------------------------------------------------------------------*/
00058 /*!
00059  *  boxaTransform()
00060  * 
00061  *      Input:  boxa
00062  *              shiftx, shifty
00063  *              scalex, scaley
00064  *      Return: boxad, or null on error
00065  *
00066  *  Notes:
00067  *      (1) This is a very simple function that first shifts, then scales.
00068  */
00069 BOXA *
00070 boxaTransform(BOXA      *boxas,
00071               l_int32    shiftx,
00072               l_int32    shifty,
00073               l_float32  scalex,
00074               l_float32  scaley)
00075 {
00076 l_int32  i, n;
00077 BOX     *boxs, *boxd;
00078 BOXA    *boxad;
00079 
00080     PROCNAME("boxaTransform");
00081 
00082     if (!boxas)
00083         return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
00084     n = boxaGetCount(boxas);
00085     if ((boxad = boxaCreate(n)) == NULL)
00086         return (BOXA *)ERROR_PTR("boxad not made", procName, NULL);
00087     for (i = 0; i < n; i++) {
00088         if ((boxs = boxaGetBox(boxas, i, L_CLONE)) == NULL)
00089             return (BOXA *)ERROR_PTR("boxs not found", procName, NULL);
00090         boxd = boxTransform(boxs, shiftx, shifty, scalex, scaley);
00091         boxDestroy(&boxs);
00092         boxaAddBox(boxad, boxd, L_INSERT);
00093     }
00094 
00095     return boxad;
00096 }
00097 
00098 
00099 /*!
00100  *  boxTransform()
00101  * 
00102  *      Input:  box
00103  *              shiftx, shifty
00104  *              scalex, scaley
00105  *      Return: boxd, or null on error
00106  *
00107  *  Notes:
00108  *      (1) This is a very simple function that first shifts, then scales.
00109  */
00110 BOX *
00111 boxTransform(BOX       *box,
00112              l_int32    shiftx,
00113              l_int32    shifty,
00114              l_float32  scalex,
00115              l_float32  scaley)
00116 {
00117     PROCNAME("boxTransform");
00118 
00119     if (!box)
00120         return (BOX *)ERROR_PTR("box not defined", procName, NULL);
00121     return boxCreate((l_int32)(scalex * (box->x + shiftx) + 0.5),
00122                      (l_int32)(scaley * (box->y + shifty) + 0.5),
00123                      (l_int32)(L_MAX(1.0, scalex * box->w + 0.5)),
00124                      (l_int32)(L_MAX(1.0, scaley * box->h + 0.5)));
00125 }
00126 
00127 
00128 /*!
00129  *  boxaTransformOrdered()
00130  * 
00131  *      Input:  boxa
00132  *              shiftx, shifty 
00133  *              scalex, scaley
00134  *              xcen, ycen (center of rotation)
00135  *              angle (in radians; clockwise is positive)
00136  *              order (one of 6 combinations: L_TR_SC_RO, ...)
00137  *      Return: boxd, or null on error
00138  *
00139  *  Notes:
00140  *      (1) This allows a sequence of linear transforms on each box.
00141  *          the transforms are from the affine set, composed of
00142  *          shift, scaling and rotation, and the order of the
00143  *          transforms is specified.
00144  *      (2) Although these operations appear to be on an infinite
00145  *          2D plane, in practice the region of interest is clipped
00146  *          to a finite image.  The center of rotation is usually taken
00147  *          with respect to the image (either the UL corner or the
00148  *          center).  A translation can have two very different effects:
00149  *            (a) Moves the boxes across the fixed image region.
00150  *            (b) Moves the image origin, causing a change in the image
00151  *                region and an opposite effective translation of the boxes.
00152  *          This function should only be used for (a), where the image
00153  *          region is fixed on translation.  If the image region is
00154  *          changed by the translation, use instead the functions
00155  *          in affinecompose.c, where the image region and rotation
00156  *          center can be computed from the actual clipping due to
00157  *          translation of the image origin.
00158  *      (3) See boxTransformOrdered() for usage and implementation details.
00159  */
00160 BOXA *
00161 boxaTransformOrdered(BOXA      *boxas,
00162                      l_int32    shiftx,
00163                      l_int32    shifty,
00164                      l_float32  scalex,
00165                      l_float32  scaley,
00166                      l_int32    xcen,
00167                      l_int32    ycen,
00168                      l_float32  angle,
00169                      l_int32    order)
00170 {
00171 l_int32  i, n;
00172 BOX     *boxs, *boxd;
00173 BOXA    *boxad;
00174 
00175     PROCNAME("boxaTransformOrdered");
00176 
00177     if (!boxas)
00178         return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
00179     n = boxaGetCount(boxas);
00180     if ((boxad = boxaCreate(n)) == NULL)
00181         return (BOXA *)ERROR_PTR("boxad not made", procName, NULL);
00182     for (i = 0; i < n; i++) {
00183         if ((boxs = boxaGetBox(boxas, i, L_CLONE)) == NULL)
00184             return (BOXA *)ERROR_PTR("boxs not found", procName, NULL);
00185         boxd = boxTransformOrdered(boxs, shiftx, shifty, scalex, scaley,
00186                                    xcen, ycen, angle, order);
00187         boxDestroy(&boxs);
00188         boxaAddBox(boxad, boxd, L_INSERT);
00189     }
00190 
00191     return boxad;
00192 }
00193 
00194 
00195 /*!
00196  *  boxTransformOrdered()
00197  * 
00198  *      Input:  boxs
00199  *              shiftx, shifty 
00200  *              scalex, scaley
00201  *              xcen, ycen (center of rotation)
00202  *              angle (in radians; clockwise is positive)
00203  *              order (one of 6 combinations: L_TR_SC_RO, ...)
00204  *      Return: boxd, or null on error
00205  *
00206  *  Notes:
00207  *      (1) This allows a sequence of linear transforms, composed of
00208  *          shift, scaling and rotation, where the order of the
00209  *          transforms is specified.
00210  *      (2) The rotation is taken about a point specified by (xcen, ycen).
00211  *          Let the components of the vector from the center of rotation
00212  *          to the box center be (xdif, ydif):
00213  *            xdif = (bx + 0.5 * bw) - xcen
00214  *            ydif = (by + 0.5 * bh) - ycen
00215  *          Then the box center after rotation has new components:
00216  *            bxcen = xcen + xdif * cosa + ydif * sina
00217  *            bycen = ycen + ydif * cosa - xdif * sina
00218  *          where cosa and sina are the cos and sin of the angle,
00219  *          and the enclosing box for the rotated box has size:
00220  *            rw = |bw * cosa| + |bh * sina|
00221  *            rh = |bh * cosa| + |bw * sina|
00222  *          where bw and bh are the unrotated width and height.
00223  *          Then the box UL corner (rx, ry) is
00224  *            rx = bxcen - 0.5 * rw
00225  *            ry = bycen - 0.5 * rh
00226  *      (3) The center of rotation specified by args @xcen and @ycen
00227  *          is the point BEFORE any translation or scaling.  If the
00228  *          rotation is not the first operation, this function finds
00229  *          the actual center at the time of rotation.  It does this
00230  *          by making the following assumptions:
00231  *             (1) Any scaling is with respect to the UL corner, so
00232  *                 that the center location scales accordingly.
00233  *             (2) A translation does not affect the center of
00234  *                 the image; it just moves the boxes.
00235  *          We always use assumption (1).  However, assumption (2)
00236  *          will be incorrect if the apparent translation is due
00237  *          to a clipping operation that, in effect, moves the
00238  *          origin of the image.  In that case, you should NOT use
00239  *          these simple functions.  Instead, use the functions
00240  *          in affinecompose.c, where the rotation center can be
00241  *          computed from the actual clipping due to translation
00242  *          of the image origin.
00243  */
00244 BOX *
00245 boxTransformOrdered(BOX       *boxs,
00246                     l_int32    shiftx,
00247                     l_int32    shifty,
00248                     l_float32  scalex,
00249                     l_float32  scaley,
00250                     l_int32    xcen,
00251                     l_int32    ycen,
00252                     l_float32  angle,
00253                     l_int32    order)
00254 {
00255 l_int32    bx, by, bw, bh, tx, ty, tw, th;
00256 l_int32    xcent, ycent;  /* transformed center of rotation due to scaling */
00257 l_float32  sina, cosa, xdif, ydif, rx, ry, rw, rh;
00258 BOX       *boxd;
00259 
00260     PROCNAME("boxTransformOrdered");
00261 
00262     if (!boxs)
00263         return (BOX *)ERROR_PTR("boxs not defined", procName, NULL);
00264     if (order != L_TR_SC_RO && order != L_SC_RO_TR && order != L_RO_TR_SC &&
00265         order != L_TR_RO_SC && order != L_RO_SC_TR && order != L_SC_TR_RO)
00266         return (BOX *)ERROR_PTR("order invalid", procName, NULL);
00267 
00268     boxGetGeometry(boxs, &bx, &by, &bw, &bh);
00269     if (angle != 0.0) {
00270         sina = sin(angle);
00271         cosa = cos(angle);
00272     }
00273 
00274     if (order == L_TR_SC_RO) {
00275         tx = (l_int32)(scalex * (bx + shiftx) + 0.5);
00276         ty = (l_int32)(scaley * (by + shifty) + 0.5);
00277         tw = (l_int32)(L_MAX(1.0, scalex * bw + 0.5));
00278         th = (l_int32)(L_MAX(1.0, scaley * bh + 0.5));
00279         xcent = (l_int32)(scalex * xcen + 0.5);
00280         ycent = (l_int32)(scaley * ycen + 0.5);
00281         if (angle == 0.0)
00282             boxd = boxCreate(tx, ty, tw, th);
00283         else {
00284             xdif = tx + 0.5 * tw - xcent;
00285             ydif = ty + 0.5 * th - ycent;
00286             rw = L_ABS(tw * cosa) + L_ABS(th * sina);
00287             rh = L_ABS(th * cosa) + L_ABS(tw * sina);
00288             rx = xcent + xdif * cosa - ydif * sina - 0.5 * rw;
00289             ry = ycent + ydif * cosa + xdif * sina - 0.5 * rh;
00290             boxd = boxCreate((l_int32)rx, (l_int32)ry, (l_int32)rw,
00291                              (l_int32)rh);
00292         }
00293     }
00294     else if (order == L_SC_TR_RO) {
00295         tx = (l_int32)(scalex * bx + shiftx + 0.5);
00296         ty = (l_int32)(scaley * by + shifty + 0.5);
00297         tw = (l_int32)(L_MAX(1.0, scalex * bw + 0.5));
00298         th = (l_int32)(L_MAX(1.0, scaley * bh + 0.5));
00299         xcent = (l_int32)(scalex * xcen + 0.5);
00300         ycent = (l_int32)(scaley * ycen + 0.5);
00301         if (angle == 0.0)
00302             boxd = boxCreate(tx, ty, tw, th);
00303         else {
00304             xdif = tx + 0.5 * tw - xcent;
00305             ydif = ty + 0.5 * th - ycent;
00306             rw = L_ABS(tw * cosa) + L_ABS(th * sina);
00307             rh = L_ABS(th * cosa) + L_ABS(tw * sina);
00308             rx = xcent + xdif * cosa - ydif * sina - 0.5 * rw;
00309             ry = ycent + ydif * cosa + xdif * sina - 0.5 * rh;
00310             boxd = boxCreate((l_int32)rx, (l_int32)ry, (l_int32)rw,
00311                              (l_int32)rh);
00312         }
00313     }
00314     else if (order == L_RO_TR_SC) {
00315         if (angle == 0.0) {
00316             rx = bx;
00317             ry = by;
00318             rw = bw;
00319             rh = bh;
00320         }
00321         else {
00322             xdif = bx + 0.5 * bw - xcen;
00323             ydif = by + 0.5 * bh - ycen;
00324             rw = L_ABS(bw * cosa) + L_ABS(bh * sina);
00325             rh = L_ABS(bh * cosa) + L_ABS(bw * sina);
00326             rx = xcen + xdif * cosa - ydif * sina - 0.5 * rw;
00327             ry = ycen + ydif * cosa + xdif * sina - 0.5 * rh;
00328         }
00329         tx = (l_int32)(scalex * (rx + shiftx) + 0.5);
00330         ty = (l_int32)(scaley * (ry + shifty) + 0.5);
00331         tw = (l_int32)(L_MAX(1.0, scalex * rw + 0.5));
00332         th = (l_int32)(L_MAX(1.0, scaley * rh + 0.5));
00333         boxd = boxCreate(tx, ty, tw, th);
00334     }
00335     else if (order == L_RO_SC_TR) {
00336         if (angle == 0.0) {
00337             rx = bx;
00338             ry = by;
00339             rw = bw;
00340             rh = bh;
00341         }
00342         else {
00343             xdif = bx + 0.5 * bw - xcen;
00344             ydif = by + 0.5 * bh - ycen;
00345             rw = L_ABS(bw * cosa) + L_ABS(bh * sina);
00346             rh = L_ABS(bh * cosa) + L_ABS(bw * sina);
00347             rx = xcen + xdif * cosa - ydif * sina - 0.5 * rw;
00348             ry = ycen + ydif * cosa + xdif * sina - 0.5 * rh;
00349         }
00350         tx = (l_int32)(scalex * rx + shiftx + 0.5);
00351         ty = (l_int32)(scaley * ry + shifty + 0.5);
00352         tw = (l_int32)(L_MAX(1.0, scalex * rw + 0.5));
00353         th = (l_int32)(L_MAX(1.0, scaley * rh + 0.5));
00354         boxd = boxCreate(tx, ty, tw, th);
00355     }
00356     else if (order == L_TR_RO_SC) {
00357         tx = bx + shiftx;
00358         ty = by + shifty;
00359         if (angle == 0.0) {
00360             rx = tx;
00361             ry = ty;
00362             rw = bw;
00363             rh = bh;
00364         }
00365         else {
00366             xdif = tx + 0.5 * bw - xcen;
00367             ydif = ty + 0.5 * bh - ycen;
00368             rw = L_ABS(bw * cosa) + L_ABS(bh * sina);
00369             rh = L_ABS(bh * cosa) + L_ABS(bw * sina);
00370             rx = xcen + xdif * cosa - ydif * sina - 0.5 * rw;
00371             ry = ycen + ydif * cosa + xdif * sina - 0.5 * rh;
00372         }
00373         tx = (l_int32)(scalex * rx + 0.5);
00374         ty = (l_int32)(scaley * ry + 0.5);
00375         tw = (l_int32)(L_MAX(1.0, scalex * rw + 0.5));
00376         th = (l_int32)(L_MAX(1.0, scaley * rh + 0.5));
00377         boxd = boxCreate(tx, ty, tw, th);
00378     }
00379     else {  /* order == L_SC_RO_TR) */
00380         tx = (l_int32)(scalex * bx + 0.5);
00381         ty = (l_int32)(scaley * by + 0.5);
00382         tw = (l_int32)(L_MAX(1.0, scalex * bw + 0.5));
00383         th = (l_int32)(L_MAX(1.0, scaley * bh + 0.5));
00384         xcent = (l_int32)(scalex * xcen + 0.5);
00385         ycent = (l_int32)(scaley * ycen + 0.5);
00386         if (angle == 0.0) {
00387             rx = tx;
00388             ry = ty;
00389             rw = tw;
00390             rh = th;
00391         }
00392         else {
00393             xdif = tx + 0.5 * tw - xcent;
00394             ydif = ty + 0.5 * th - ycent;
00395             rw = L_ABS(tw * cosa) + L_ABS(th * sina);
00396             rh = L_ABS(th * cosa) + L_ABS(tw * sina);
00397             rx = xcent + xdif * cosa - ydif * sina - 0.5 * rw;
00398             ry = ycent + ydif * cosa + xdif * sina - 0.5 * rh;
00399         }
00400         tx = (l_int32)(rx + shiftx + 0.5);
00401         ty = (l_int32)(ry + shifty + 0.5);
00402         tw = (l_int32)(rw + 0.5);
00403         th = (l_int32)(rh + 0.5);
00404         boxd = boxCreate(tx, ty, tw, th);
00405     }
00406 
00407     return boxd;
00408 }
00409 
00410 
00411 /*!
00412  *  boxaRotateOrth()
00413  * 
00414  *      Input:  boxa
00415  *              w, h (of image in which the boxa is embedded)
00416  *              rotation (0 = noop, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg;
00417  *                        all rotations are clockwise)
00418  *      Return: boxad, or null on error
00419  *
00420  *  Notes:
00421  *      (1) See boxRotateOrth() for details.
00422  */
00423 BOXA *
00424 boxaRotateOrth(BOXA    *boxas,
00425                l_int32  w,
00426                l_int32  h,
00427                l_int32  rotation)
00428 {
00429 l_int32  i, n;
00430 BOX     *boxs, *boxd;
00431 BOXA    *boxad;
00432 
00433     PROCNAME("boxaRotateOrth");
00434 
00435     if (!boxas)
00436         return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
00437     if (rotation == 0)
00438         return boxaCopy(boxas, L_COPY);
00439     if (rotation < 1 || rotation > 3)
00440         return (BOXA *)ERROR_PTR("rotation not in {0,1,2,3}", procName, NULL);
00441 
00442     n = boxaGetCount(boxas);
00443     if ((boxad = boxaCreate(n)) == NULL)
00444         return (BOXA *)ERROR_PTR("boxad not made", procName, NULL);
00445     for (i = 0; i < n; i++) {
00446         if ((boxs = boxaGetBox(boxas, i, L_CLONE)) == NULL)
00447             return (BOXA *)ERROR_PTR("boxs not found", procName, NULL);
00448         boxd = boxRotateOrth(boxs, w, h, rotation);
00449         boxDestroy(&boxs);
00450         boxaAddBox(boxad, boxd, L_INSERT);
00451     }
00452 
00453     return boxad;
00454 }
00455 
00456 
00457 /*!
00458  *  boxRotateOrth()
00459  * 
00460  *      Input:  box
00461  *              w, h (of image in which the box is embedded)
00462  *              rotation (0 = noop, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg;
00463  *                        all rotations are clockwise)
00464  *      Return: boxd, or null on error
00465  *
00466  *  Notes:
00467  *      (1) Rotate the image with the embedded box by the specified amount.
00468  *      (2) After rotation, the rotated box is always measured with
00469  *          respect to the UL corner of the image.
00470  */
00471 BOX *
00472 boxRotateOrth(BOX     *box,
00473               l_int32  w,
00474               l_int32  h,
00475               l_int32  rotation)
00476 {
00477 l_int32  bx, by, bw, bh, xdist, ydist;
00478 
00479     PROCNAME("boxRotateOrth");
00480 
00481     if (!box)
00482         return (BOX *)ERROR_PTR("box not defined", procName, NULL);
00483     if (rotation == 0)
00484         return boxCopy(box);
00485     if (rotation < 1 || rotation > 3)
00486         return (BOX *)ERROR_PTR("rotation not in {0,1,2,3}", procName, NULL);
00487     boxGetGeometry(box, &bx, &by, &bw, &bh);
00488     ydist = h - by - bh;  /* below box */
00489     xdist = w - bx - bw;  /* to right of box */
00490     if (rotation == 1)   /* 90 deg cw */
00491         return boxCreate(ydist, bx, bh, bw);
00492     else if (rotation == 2)  /* 180 deg cw */
00493         return boxCreate(xdist, ydist, bw, bh);
00494     else  /*  rotation == 3, 270 deg cw */
00495         return boxCreate(by, xdist, bh, bw);
00496 }
00497 
00498 
00499 /*---------------------------------------------------------------------*
00500  *                              Boxa sort                              *
00501  *---------------------------------------------------------------------*/
00502 /*!
00503  *  boxaSort()
00504  * 
00505  *      Input:  boxa
00506  *              sorttype (L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH,
00507  *                        L_SORT_BY_HEIGHT, L_SORT_BY_MIN_DIMENSION,
00508  *                        L_SORT_BY_MAX_DIMENSION, L_SORT_BY_PERIMETER,
00509  *                        L_SORT_BY_AREA, L_SORT_BY_ASPECT_RATIO)
00510  *              sortorder  (L_SORT_INCREASING, L_SORT_DECREASING)
00511  *              &naindex (<optional return> index of sorted order into
00512  *                        original array)
00513  *      Return: boxad (sorted version of boxas), or null on error
00514  */
00515 BOXA *
00516 boxaSort(BOXA    *boxas,
00517          l_int32  sorttype,
00518          l_int32  sortorder,
00519          NUMA   **pnaindex)
00520 {
00521 l_int32    i, n, x, y, w, h, size;
00522 BOXA      *boxad;
00523 NUMA      *na, *naindex;
00524 
00525     PROCNAME("boxaSort");
00526 
00527     if (pnaindex) *pnaindex = NULL;
00528     if (!boxas)
00529         return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
00530     if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && 
00531         sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT &&
00532         sorttype != L_SORT_BY_MIN_DIMENSION &&
00533         sorttype != L_SORT_BY_MAX_DIMENSION &&
00534         sorttype != L_SORT_BY_PERIMETER &&
00535         sorttype != L_SORT_BY_AREA &&
00536         sorttype != L_SORT_BY_ASPECT_RATIO)
00537         return (BOXA *)ERROR_PTR("invalid sort type", procName, NULL);
00538     if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING)
00539         return (BOXA *)ERROR_PTR("invalid sort order", procName, NULL);
00540 
00541         /* Use O(n) binsort if possible */
00542     n = boxaGetCount(boxas);
00543     if (n > MIN_COMPS_FOR_BIN_SORT && 
00544         ((sorttype == L_SORT_BY_X) || (sorttype == L_SORT_BY_Y) || 
00545          (sorttype == L_SORT_BY_WIDTH) || (sorttype == L_SORT_BY_HEIGHT) ||
00546          (sorttype == L_SORT_BY_PERIMETER)))
00547         return boxaBinSort(boxas, sorttype, sortorder, pnaindex);
00548 
00549         /* Build up numa of specific data */
00550     if ((na = numaCreate(n)) == NULL)
00551         return (BOXA *)ERROR_PTR("na not made", procName, NULL);
00552     for (i = 0; i < n; i++) {
00553         boxaGetBoxGeometry(boxas, i, &x, &y, &w, &h);
00554         switch (sorttype)
00555         {
00556         case L_SORT_BY_X:
00557             numaAddNumber(na, x);
00558             break;
00559         case L_SORT_BY_Y:
00560             numaAddNumber(na, y);
00561             break;
00562         case L_SORT_BY_WIDTH:
00563             numaAddNumber(na, w);
00564             break;
00565         case L_SORT_BY_HEIGHT:
00566             numaAddNumber(na, h);
00567             break;
00568         case L_SORT_BY_MIN_DIMENSION:
00569             size = L_MIN(w, h);
00570             numaAddNumber(na, size);
00571             break;
00572         case L_SORT_BY_MAX_DIMENSION:
00573             size = L_MAX(w, h);
00574             numaAddNumber(na, size);
00575             break;
00576         case L_SORT_BY_PERIMETER:
00577             size = w + h;
00578             numaAddNumber(na, size);
00579             break;
00580         case L_SORT_BY_AREA:
00581             size = w * h;
00582             numaAddNumber(na, size);
00583             break;
00584         case L_SORT_BY_ASPECT_RATIO:
00585             numaAddNumber(na, (l_float32)w / (l_float32)h);
00586             break;
00587         default:
00588             L_WARNING("invalid sort type", procName);
00589         }
00590     }
00591 
00592         /* Get the sort index for data array */
00593     if ((naindex = numaGetSortIndex(na, sortorder)) == NULL)
00594         return (BOXA *)ERROR_PTR("naindex not made", procName, NULL);
00595 
00596         /* Build up sorted boxa using sort index */
00597     boxad = boxaSortByIndex(boxas, naindex);
00598 
00599     if (pnaindex)
00600         *pnaindex = naindex;
00601     else
00602         numaDestroy(&naindex);
00603     numaDestroy(&na);
00604     return boxad;
00605 }
00606 
00607 
00608 /*!
00609  *  boxaBinSort()
00610  * 
00611  *      Input:  boxa
00612  *              sorttype (L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH,
00613  *                        L_SORT_BY_HEIGHT, L_SORT_BY_PERIMETER)
00614  *              sortorder  (L_SORT_INCREASING, L_SORT_DECREASING)
00615  *              &naindex (<optional return> index of sorted order into
00616  *                        original array)
00617  *      Return: boxad (sorted version of boxas), or null on error
00618  *
00619  *  Notes:
00620  *      (1) For a large number of boxes (say, greater than 1000), this
00621  *          O(n) binsort is much faster than the O(nlogn) shellsort.
00622  *          For 5000 components, this is over 20x faster than boxaSort().
00623  *      (2) Consequently, boxaSort() calls this function if it will
00624  *          likely go much faster.
00625  */
00626 BOXA *
00627 boxaBinSort(BOXA    *boxas,
00628             l_int32  sorttype,
00629             l_int32  sortorder,
00630             NUMA   **pnaindex)
00631 {
00632 l_int32  i, n, x, y, w, h;
00633 BOXA    *boxad;
00634 NUMA    *na, *naindex;
00635 
00636     PROCNAME("boxaBinSort");
00637 
00638     if (pnaindex) *pnaindex = NULL;
00639     if (!boxas)
00640         return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
00641     if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && 
00642         sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT &&
00643         sorttype != L_SORT_BY_PERIMETER)
00644         return (BOXA *)ERROR_PTR("invalid sort type", procName, NULL);
00645     if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING)
00646         return (BOXA *)ERROR_PTR("invalid sort order", procName, NULL);
00647 
00648         /* Generate Numa of appropriate box dimensions */
00649     n = boxaGetCount(boxas);
00650     if ((na = numaCreate(n)) == NULL)
00651         return (BOXA *)ERROR_PTR("na not made", procName, NULL);
00652     for (i = 0; i < n; i++) {
00653         boxaGetBoxGeometry(boxas, i, &x, &y, &w, &h);
00654         switch (sorttype)
00655         {
00656         case L_SORT_BY_X:
00657             numaAddNumber(na, x);
00658             break;
00659         case L_SORT_BY_Y:
00660             numaAddNumber(na, y);
00661             break;
00662         case L_SORT_BY_WIDTH:
00663             numaAddNumber(na, w);
00664             break;
00665         case L_SORT_BY_HEIGHT:
00666             numaAddNumber(na, h);
00667             break;
00668         case L_SORT_BY_PERIMETER:
00669             numaAddNumber(na, w + h);
00670             break;
00671         default:
00672             L_WARNING("invalid sort type", procName);
00673         }
00674     }
00675 
00676         /* Get the sort index for data array */
00677     if ((naindex = numaGetBinSortIndex(na, sortorder)) == NULL)
00678         return (BOXA *)ERROR_PTR("naindex not made", procName, NULL);
00679 
00680         /* Build up sorted boxa using the sort index */
00681     boxad = boxaSortByIndex(boxas, naindex);
00682 
00683     if (pnaindex)
00684         *pnaindex = naindex;
00685     else
00686         numaDestroy(&naindex);
00687     numaDestroy(&na);
00688     return boxad;
00689 }
00690 
00691 
00692 /*!
00693  *  boxaSortByIndex()
00694  * 
00695  *      Input:  boxas
00696  *              naindex (na that maps from the new boxa to the input boxa)
00697  *      Return: boxad (sorted), or null on error
00698  */
00699 BOXA *
00700 boxaSortByIndex(BOXA  *boxas,
00701                 NUMA  *naindex)
00702 {
00703 l_int32  i, n, index;
00704 BOX     *box;
00705 BOXA    *boxad;
00706 
00707     PROCNAME("boxaSortByIndex");
00708 
00709     if (!boxas)
00710         return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
00711     if (!naindex)
00712         return (BOXA *)ERROR_PTR("naindex not defined", procName, NULL);
00713 
00714     n = boxaGetCount(boxas);
00715     boxad = boxaCreate(n);
00716     for (i = 0; i < n; i++) {
00717         numaGetIValue(naindex, i, &index);
00718         box = boxaGetBox(boxas, index, L_COPY);
00719         boxaAddBox(boxad, box, L_INSERT);
00720     }
00721 
00722     return boxad;
00723 }
00724 
00725 
00726 /*!
00727  *  boxaSort2d()
00728  * 
00729  *      Input:  boxas
00730  *              &naa (<optional return> numaa with sorted indices
00731  *                    whose values are the indices of the input array)
00732  *              delta1 (min overlap that permits aggregation of a box
00733  *                      onto a boxa of horizontally-aligned boxes; pass 1)
00734  *              delta2 (min overlap that permits aggregation of a box
00735  *                      onto a boxa of horizontally-aligned boxes; pass 2)
00736  *              minh1 (components less than this height either join an
00737  *                     existing boxa or are set aside for pass 2)
00738  *      Return: boxaa (2d sorted version of boxa), or null on error
00739  *
00740  *  Notes:
00741  *      (1) The final result is a sort where the 'fast scan' direction is
00742  *          left to right, and the 'slow scan' direction is from top
00743  *          to bottom.  Each boxa in the boxaa represents a sorted set
00744  *          of boxes from left to right.
00745  *      (2) Two passes are used to aggregate the boxas, which can corresond
00746  *          to characters or words in a line of text.  In pass 1, only
00747  *          taller components, which correspond to xheight or larger,
00748  *          are permitted to start a new boxa, whereas in pass 2,
00749  *          the remaining vertically-challenged components are allowed
00750  *          to join an existing boxa or start a new one.
00751  *      (3) If delta1 < 0, the first pass allows aggregation when
00752  *          boxes in the same boxa do not overlap vertically.
00753  *          The distance by which they can miss and still be aggregated
00754  *          is the absolute value |delta1|.   Similar for delta2 on
00755  *          the second pass.
00756  *      (4) On the first pass, any component of height less than minh1
00757  *          cannot start a new boxa; it's put aside for later insertion.
00758  *      (5) On the second pass, any small component that doesn't align
00759  *          with an existing boxa can start a new one.
00760  *      (6) This can be used to identify lines of text from
00761  *          character or word bounding boxes.
00762  */
00763 BOXAA *
00764 boxaSort2d(BOXA    *boxas,
00765            NUMAA  **pnaad,
00766            l_int32  delta1,
00767            l_int32  delta2,
00768            l_int32  minh1)
00769 {
00770 l_int32  i, index, h, nt, ne, n, m, ival;
00771 BOX     *box;
00772 BOXA    *boxa, *boxae, *boxan, *boxat1, *boxat2, *boxav, *boxavs;
00773 BOXAA   *baa, *baad;
00774 NUMA    *naindex, *nae, *nan, *nah, *nav, *nat1, *nat2, *nad;
00775 NUMAA   *naa, *naad;
00776 
00777     PROCNAME("boxaSort2d");
00778 
00779     if (pnaad) *pnaad = NULL;
00780     if (!boxas)
00781         return (BOXAA *)ERROR_PTR("boxas not defined", procName, NULL);
00782 
00783         /* Sort from left to right */
00784     if ((boxa = boxaSort(boxas, L_SORT_BY_X, L_SORT_INCREASING, &naindex))
00785                     == NULL)
00786         return (BOXAA *)ERROR_PTR("boxa not made", procName, NULL);
00787 
00788         /* First pass: assign taller boxes to boxa by row */
00789     nt = boxaGetCount(boxa);
00790     baa = boxaaCreate(0);
00791     naa = numaaCreate(0);
00792     boxae = boxaCreate(0);  /* save small height boxes here */
00793     nae = numaCreate(0);  /* keep track of small height boxes */
00794     for (i = 0; i < nt; i++) {
00795         box = boxaGetBox(boxa, i, L_CLONE);
00796         boxGetGeometry(box, NULL, NULL, NULL, &h);
00797         if (h < minh1) {  /* save for 2nd pass */
00798             boxaAddBox(boxae, box, L_INSERT);
00799             numaAddNumber(nae, i);
00800         }
00801         else {
00802             n = boxaaGetCount(baa);
00803             boxaaAlignBox(baa, box, delta1, &index);
00804             if (index < n) {  /* append to an existing boxa */
00805                 boxaaAddBox(baa, index, box, L_INSERT);
00806             }
00807             else {  /* doesn't align, need new boxa */
00808                 boxan = boxaCreate(0);
00809                 boxaAddBox(boxan, box, L_INSERT);
00810                 boxaaAddBoxa(baa, boxan, L_INSERT);
00811                 nan = numaCreate(0);
00812                 numaaAddNuma(naa, nan, L_INSERT);
00813             }
00814             numaGetIValue(naindex, i, &ival);
00815             numaaAddNumber(naa, index, ival);
00816         }
00817     }
00818     boxaDestroy(&boxa);
00819     numaDestroy(&naindex);
00820 
00821         /* Second pass: feed in small height boxes;
00822          * TODO: this correctly, using local y position! */
00823     ne = boxaGetCount(boxae);
00824     for (i = 0; i < ne; i++) {
00825         box = boxaGetBox(boxae, i, L_CLONE);
00826         n = boxaaGetCount(baa);
00827         boxaaAlignBox(baa, box, delta2, &index);
00828         if (index < n) {  /* append to an existing boxa */
00829             boxaaAddBox(baa, index, box, L_INSERT);
00830         }
00831         else {  /* doesn't align, need new boxa */
00832             boxan = boxaCreate(0);
00833             boxaAddBox(boxan, box, L_INSERT);
00834             boxaaAddBoxa(baa, boxan, L_INSERT);
00835             nan = numaCreate(0);
00836             numaaAddNuma(naa, nan, L_INSERT);
00837         }
00838         numaGetIValue(nae, i, &ival);  /* location in original boxas */
00839         numaaAddNumber(naa, index, ival);
00840     }
00841 
00842         /* Sort each boxa in the boxaa */
00843     m = boxaaGetCount(baa);
00844     for (i = 0; i < m; i++) {
00845         boxat1 = boxaaGetBoxa(baa, i, L_CLONE);
00846         boxat2 = boxaSort(boxat1, L_SORT_BY_X, L_SORT_INCREASING, &nah);
00847         boxaaReplaceBoxa(baa, i, boxat2);
00848         nat1 = numaaGetNuma(naa, i, L_CLONE);
00849         nat2 = numaSortByIndex(nat1, nah);
00850         numaaReplaceNuma(naa, i, nat2);
00851         boxaDestroy(&boxat1);
00852         numaDestroy(&nat1);
00853         numaDestroy(&nah);
00854     }
00855 
00856         /* Sort boxa vertically within boxaa, using the first box
00857          * in each boxa. */
00858     m = boxaaGetCount(baa);
00859     boxav = boxaCreate(m);  /* holds first box in each boxa in baa */
00860     naad = numaaCreate(m);
00861     if (pnaad)
00862         *pnaad = naad;
00863     baad = boxaaCreate(m);
00864     for (i = 0; i < m; i++) {
00865         boxat1 = boxaaGetBoxa(baa, i, L_CLONE);
00866         box = boxaGetBox(boxat1, 0, L_CLONE); 
00867         boxaAddBox(boxav, box, L_INSERT);
00868         boxaDestroy(&boxat1);
00869     }
00870     boxavs = boxaSort(boxav, L_SORT_BY_Y, L_SORT_INCREASING, &nav);
00871     for (i = 0; i < m; i++) {
00872         numaGetIValue(nav, i, &index);
00873         boxa = boxaaGetBoxa(baa, index, L_CLONE);
00874         boxaaAddBoxa(baad, boxa, L_INSERT);
00875         nad = numaaGetNuma(naa, index, L_CLONE);
00876         numaaAddNuma(naad, nad, L_INSERT);
00877     }
00878 
00879 /*    fprintf(stderr, "box count = %d, numaa count = %d\n", nt,
00880             numaaGetNumberCount(naad)); */
00881 
00882     boxaaDestroy(&baa);
00883     boxaDestroy(&boxav);
00884     boxaDestroy(&boxavs);
00885     boxaDestroy(&boxae);
00886     numaDestroy(&nav);
00887     numaDestroy(&nae);
00888     numaaDestroy(&naa);
00889     if (!pnaad)
00890         numaaDestroy(&naad);
00891 
00892     return baad;
00893 }
00894 
00895 
00896 /*!
00897  *  boxaSort2dByIndex()
00898  * 
00899  *      Input:  boxas
00900  *              naa (numaa that maps from the new baa to the input boxa)
00901  *      Return: baa (sorted boxaa), or null on error
00902  */
00903 BOXAA *
00904 boxaSort2dByIndex(BOXA   *boxas,
00905                   NUMAA  *naa)
00906 {
00907 l_int32  ntot, boxtot, i, j, n, nn, index;
00908 BOX     *box;
00909 BOXA    *boxa;
00910 BOXAA   *baa;
00911 NUMA    *na;
00912 
00913     PROCNAME("boxaSort2dByIndex");
00914 
00915     if (!boxas)
00916         return (BOXAA *)ERROR_PTR("boxas not defined", procName, NULL);
00917     if (!naa)
00918         return (BOXAA *)ERROR_PTR("naindex not defined", procName, NULL);
00919 
00920         /* Check counts */
00921     ntot = numaaGetNumberCount(naa);
00922     boxtot = boxaGetCount(boxas);
00923     if (ntot != boxtot)
00924         return (BOXAA *)ERROR_PTR("element count mismatch", procName, NULL);
00925 
00926     n = numaaGetCount(naa);
00927     baa = boxaaCreate(n);
00928     for (i = 0; i < n; i++) {
00929         na = numaaGetNuma(naa, i, L_CLONE);
00930         nn = numaGetCount(na);
00931         boxa = boxaCreate(nn);
00932         for (j = 0; j < nn; j++) {
00933             numaGetIValue(na, i, &index);
00934             box = boxaGetBox(boxas, index, L_COPY);
00935             boxaAddBox(boxa, box, L_INSERT);
00936         }
00937         boxaaAddBoxa(baa, boxa, L_INSERT);
00938         numaDestroy(&na);
00939     }
00940 
00941     return baa;
00942 }
00943 
00944 
00945 /*---------------------------------------------------------------------*
00946  *                            Boxa statistics                          *
00947  *---------------------------------------------------------------------*/
00948 /*!
00949  *  boxaGetRankSize()
00950  *
00951  *      Input:  boxa
00952  *              fract (use 0.0 for smallest, 1.0 for largest)
00953  *      Return: box (with rank values for x, y, w, h), or null on error
00954  *              or if the boxa is empty (has no valid boxes)
00955  *
00956  *  Notes:
00957  *      (1) This function does not assume that all boxes in the boxa are valid
00958  *      (2) The four box parameters are sorted independently.  To assure
00959  *          that the resulting box size is sorted in increasing order:
00960  *             - x and y are sorted in decreasing order
00961  *             - w and h are sorted in increasing order
00962  */
00963 BOX *
00964 boxaGetRankSize(BOXA      *boxa,
00965                 l_float32  fract)
00966 {
00967 l_int32    i, n, x, y, w, h;
00968 l_float32  xval, yval, wval, hval;
00969 NUMA      *nax, *nay, *naw, *nah;
00970 BOX       *box;
00971 
00972     PROCNAME("boxaGetRankSize");
00973 
00974     if (!boxa)
00975         return (BOX *)ERROR_PTR("boxa not defined", procName, NULL);
00976     if (fract < 0.0 || fract > 1.0)
00977         return (BOX *)ERROR_PTR("fract not in [0.0 ... 1.0]", procName, NULL);
00978     if ((n = boxaGetCount(boxa)) == 0)
00979         return (BOX *)ERROR_PTR("boxa is empty", procName, NULL);
00980 
00981     nax = numaCreate(n);
00982     nay = numaCreate(n);
00983     naw = numaCreate(n);
00984     nah = numaCreate(n);
00985     for (i = 0; i < n; i++) {
00986         boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h);
00987         if (w == 0 || h == 0) continue;
00988         numaAddNumber(nax, x);
00989         numaAddNumber(nay, y);
00990         numaAddNumber(naw, w);
00991         numaAddNumber(nah, h);
00992     }
00993     numaGetRankValue(nax, 1.0 - fract, &xval);
00994     numaGetRankValue(nay, 1.0 - fract, &yval);
00995     numaGetRankValue(naw, fract, &wval);
00996     numaGetRankValue(nah, fract, &hval);
00997     box = boxCreate((l_int32)xval, (l_int32)yval, (l_int32)wval, (l_int32)hval);
00998 
00999     numaDestroy(&nax);
01000     numaDestroy(&nay);
01001     numaDestroy(&naw);
01002     numaDestroy(&nah);
01003     return box;
01004 }
01005 
01006 
01007 /*!
01008  *  boxaGetMedian()
01009  *
01010  *      Input:  boxa
01011  *      Return: box (with median values for x, y, w, h), or null on error
01012  *              or if the boxa is empty.
01013  *
01014  *  Notes:
01015  *      (1) See boxaGetRankSize()
01016  */
01017 BOX *
01018 boxaGetMedian(BOXA  *boxa)
01019 {
01020     PROCNAME("boxaGetMedian");
01021 
01022     if (!boxa)
01023         return (BOX *)ERROR_PTR("boxa not defined", procName, NULL);
01024     if (boxaGetCount(boxa) == 0)
01025         return (BOX *)ERROR_PTR("boxa is empty", procName, NULL);
01026 
01027     return boxaGetRankSize(boxa, 0.5);
01028 }
01029 
01030 
01031 /*---------------------------------------------------------------------*
01032  *                        Other Boxaa functions                        *
01033  *---------------------------------------------------------------------*/
01034 /*!
01035  *  boxaaGetExtent()
01036  *
01037  *      Input:  boxaa
01038  *              &w  (<optional return> width)
01039  *              &h  (<optional return> height)
01040  *              &box (<optional return>, minimum box containing all boxa
01041  *                    in boxaa)
01042  *      Return: 0 if OK, 1 on error
01043  *
01044  *  Notes:
01045  *      (1) The returned w and h are the minimum size image
01046  *          that would contain all boxes untranslated.
01047  */
01048 l_int32
01049 boxaaGetExtent(BOXAA    *boxaa,
01050                l_int32  *pw,
01051                l_int32  *ph,
01052                BOX     **pbox)
01053 {
01054 l_int32  i, j, n, m, x, y, w, h, xmax, ymax, xmin, ymin;
01055 BOXA    *boxa;
01056 
01057     PROCNAME("boxaaGetExtent");
01058 
01059     if (!pw && !ph && !pbox)
01060         return ERROR_INT("no ptrs defined", procName, 1);
01061     if (pbox) *pbox = NULL;
01062     if (pw) *pw = 0;
01063     if (ph) *ph = 0;
01064     if (!boxaa)
01065         return ERROR_INT("boxaa not defined", procName, 1);
01066 
01067     n = boxaaGetCount(boxaa);
01068     if (n == 0)
01069         return ERROR_INT("no boxa in boxaa", procName, 1);
01070 
01071     xmax = ymax = 0;
01072     xmin = ymin = 100000000;
01073     for (i = 0; i < n; i++) {
01074         boxa = boxaaGetBoxa(boxaa, i, L_CLONE);
01075         m = boxaGetCount(boxa);
01076         for (j = 0; j < m; j++) {
01077             boxaGetBoxGeometry(boxa, j, &x, &y, &w, &h);
01078             xmin = L_MIN(xmin, x);
01079             ymin = L_MIN(ymin, y);
01080             xmax = L_MAX(xmax, x + w);
01081             ymax = L_MAX(ymax, y + h);
01082         }
01083     }
01084     if (pw) *pw = xmax;
01085     if (ph) *ph = ymax;
01086     if (pbox)
01087       *pbox = boxCreate(xmin, ymin, xmax - xmin, ymax - ymin);
01088 
01089     return 0;
01090 }
01091 
01092 
01093 /*!
01094  *  boxaaFlattenToBoxa()
01095  *
01096  *      Input:  boxaa
01097  *              &naindex  (<optional return> the boxa index in the boxaa)
01098  *              copyflag  (L_COPY or L_CLONE)
01099  *      Return: boxa, or null on error
01100  *
01101  *  Notes:
01102  *      (1) This 'flattens' the boxaa to a boxa, taking the boxes in
01103  *          order in the first boxa, then the second, etc.
01104  *      (2) If &naindex is defined, we generate a Numa that gives, for
01105  *          each box in the boxaa, the index of the boxa to which it belongs.
01106  */
01107 BOXA *
01108 boxaaFlattenToBoxa(BOXAA   *baa,
01109                    NUMA   **pnaindex,
01110                    l_int32  copyflag)
01111 {
01112 l_int32  i, j, m, n;
01113 BOXA    *boxa, *boxat;
01114 BOX     *box;
01115 NUMA    *naindex;
01116 
01117     PROCNAME("boxaaFlattenToBoxa");
01118 
01119     if (pnaindex) *pnaindex = NULL;
01120     if (!baa)
01121         return (BOXA *)ERROR_PTR("baa not defined", procName, NULL);
01122     if (copyflag != L_COPY && copyflag != L_CLONE)
01123         return (BOXA *)ERROR_PTR("invalid copyflag", procName, NULL);
01124     if (pnaindex) {
01125         naindex = numaCreate(0);
01126         *pnaindex = naindex;
01127     }
01128 
01129     n = boxaaGetCount(baa);
01130     boxa = boxaCreate(n);
01131     for (i = 0; i < n; i++) {
01132         boxat = boxaaGetBoxa(baa, i, L_CLONE);
01133         m = boxaGetCount(boxat);
01134         for (j = 0; j < m; j++) {
01135             box = boxaGetBox(boxat, j, copyflag);
01136             boxaAddBox(boxa, box, L_INSERT);
01137             if (pnaindex)
01138                 numaAddNumber(naindex, i);  /* save 'row' number */
01139         }
01140         boxaDestroy(&boxat);
01141     }
01142 
01143     return boxa;
01144 }
01145 
01146 
01147 /*!
01148  *  boxaaAlignBox()
01149  * 
01150  *      Input:  boxaa
01151  *              box (to be aligned with the last of one of the boxa
01152  *                   in boxaa, if possible)
01153  *              delta (amount by which consecutive components can miss
01154  *                     in overlap and still be included in the array)
01155  *              &index (of boxa with best overlap, or if none match,
01156  *                      this is the index of the next boxa to be generated)
01157  *      Return: 0 if OK, 1 on error
01158  *
01159  *  Notes:
01160  *      (1) This is not greedy; it finds the boxa whose last box has
01161  *          the biggest overlap with the input box.
01162  */
01163 l_int32
01164 boxaaAlignBox(BOXAA    *baa,
01165               BOX      *box,
01166               l_int32   delta,
01167               l_int32  *pindex)
01168 {
01169 l_int32  i, n, m, y, yt, h, ht, ovlp, maxovlp, maxindex;
01170 BOXA    *boxa;
01171 
01172     PROCNAME("boxaaAlignBox");
01173 
01174     if (!baa)
01175         return ERROR_INT("baa not defined", procName, 1);
01176     if (!box)
01177         return ERROR_INT("box not defined", procName, 1);
01178     if (!pindex)
01179         return ERROR_INT("&index not defined", procName, 1);
01180 
01181     n = boxaaGetCount(baa);
01182     boxGetGeometry(box, NULL, &y, NULL, &h);
01183     maxovlp = -10000000;
01184     for (i = 0; i < n; i++) {
01185         boxa = boxaaGetBoxa(baa, i, L_CLONE);
01186         if ((m = boxaGetCount(boxa)) == 0) {
01187             L_WARNING("no boxes in boxa", procName);
01188             continue;
01189         }
01190         boxaGetBoxGeometry(boxa, m - 1, NULL, &yt, NULL, &ht);  /* last one */
01191         boxaDestroy(&boxa);
01192 
01193             /* Overlap < 0 means the components do not overlap vertically */
01194         if (yt >= y)
01195             ovlp = y + h - 1 - yt;
01196         else
01197             ovlp = yt + ht - 1 - y;
01198         if (ovlp > maxovlp) {
01199             maxovlp = ovlp;
01200             maxindex = i;
01201         }
01202     }
01203 
01204     if (maxovlp + delta >= 0)
01205         *pindex = maxindex;
01206     else
01207         *pindex = n;
01208     return 0;
01209 }
01210 
01211 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines