Leptonica 1.68
C Image Processing Library

pixafunc1.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  *   pixafunc1.c
00018  *
00019  *      Filters
00020  *           PIX      *pixSelectBySize()
00021  *           PIXA     *pixaSelectBySize()
00022  *           PIX      *pixSelectByAreaPerimRatio()
00023  *           PIXA     *pixaSelectByAreaPerimRatio()
00024  *           PIX      *pixSelectByAreaFraction()
00025  *           PIXA     *pixaSelectByAreaFraction()
00026  *           PIX      *pixSelectByWidthHeightRatio()
00027  *           PIXA     *pixaSelectByWidthHeightRatio()
00028  *           PIXA     *pixaSelectWithIndicator()
00029  *           l_int32   pixRemoveWithIndicator()
00030  *           l_int32   pixAddWithIndicator()
00031  *
00032  *      Sort functions
00033  *           PIXA     *pixaSort()
00034  *           PIXA     *pixaBinSort()
00035  *           PIXA     *pixaSortByIndex()
00036  *           PIXAA    *pixaSort2dByIndex()
00037  *
00038  *      Miscellaneous
00039  *           PIXA     *pixaAddBorderGeneral()
00040  *           PIXA     *pixaaFlattenToPixa()
00041  *           l_int32   pixaSizeRange()
00042  *           PIXA     *pixaClipToPix()
00043  *           l_int32   pixaAnyColormaps()
00044  *           l_int32   pixaGetDepthInfo()
00045  *           l_int32   pixaEqual()
00046  */
00047 
00048 #include <string.h>
00049 #include "allheaders.h"
00050 
00051     /* For more than this number of c.c. in a binarized image of
00052      * semi-perimeter (w + h) about 5000 or less, the O(n) binsort
00053      * is faster than the O(nlogn) shellsort.  */
00054 static const l_int32   MIN_COMPS_FOR_BIN_SORT = 500;
00055 
00056 
00057 /*---------------------------------------------------------------------*
00058  *                                Filters                              *
00059  *---------------------------------------------------------------------*/
00060 /*
00061  * These filters work on the connected components of 1 bpp images.
00062  * They are typically used on pixa that have been generated from a Pix
00063  * using pixConnComp(), so that the corresponding Boxa is available.
00064  *
00065  * The filters remove or retain c.c. based on these properties:
00066  *    (a) size  [pixaFindDimensions()]
00067  *    (b) area-to-perimeter ratio   [pixaFindAreaPerimRatio()]
00068  *    (c) foreground area as a fraction of bounding box area (w * h)
00069  *        [pixaFindForegroundArea()]
00070  *    (d) number of foreground pixels   [pixaCountPixels()]
00071  *    (e) width/height aspect ratio  [pixFindWidthHeightRatio()]
00072  *
00073  * We provide two different high-level interfaces:
00074  *    (1) Functions that use one of the filters on either
00075  *        a pix or the pixa of components.
00076  *    (2) A general method that generates numas of indicator functions,
00077  *        logically combines them, and efficiently removes or adds
00078  *        the selected components.
00079  *
00080  * For interface (1), the filtering is performed with a single function call.
00081  * This is the easiest way to do simple filtering.  These functions
00082  * are named pixSelectBy*() and pixaSelectBy*(), where the '*' is one of:
00083  *        Size
00084  *        AreaPerimRatio
00085  *        AreaFraction 
00086  *        WidthHeightRatio
00087  *
00088  * For more complicated filtering, use the general method (2).
00089  * The numa indicator functions for a pixa are generated by these functions:
00090  *        pixaFindDimensions()
00091  *        pixaFindAreaPerimRatio()
00092  *        pixaFindAreaFraction()
00093  *        pixaCountPixels()
00094  *        pixaFindWidthHeightRatio()
00095  *        pixaFindWidthHeightProduct()
00096  *
00097  * Here is an illustration using the general method.  Suppose you want
00098  * all 8-connected components that have a height greater than 40 pixels,
00099  * a width not more than 30 pixels, between 150 and 300 fg pixels,
00100  * and an area-to-perimeter ratio between 2.5 and 4.0.
00101  *
00102  *        // Generate the pixa of 8 cc pieces.
00103  *    boxa = pixConnComp(pixs, &pixa, 8);
00104  *
00105  *        // Extract the data we need about each component.
00106  *    pixaFindDimensions(pixa, &naw, &nah);
00107  *    nas = pixaCountPixels(pixa);
00108  *    nar = pixaFindAreaPerimRatio(pixa);
00109  *
00110  *        // Build the indicator arrays for the set of components,
00111  *        // based on thresholds and selection criteria.
00112  *    na1 = numaMakeThresholdIndicator(nah, 40, L_SELECT_IF_GT);
00113  *    na2 = numaMakeThresholdIndicator(naw, 30, L_SELECT_IF_LTE);
00114  *    na3 = numaMakeThresholdIndicator(nas, 150, L_SELECT_IF_GTE);
00115  *    na4 = numaMakeThresholdIndicator(nas, 300, L_SELECT_IF_LTE);
00116  *    na5 = numaMakeThresholdIndicator(nar, 2.5, L_SELECT_IF_GTE);
00117  *    na6 = numaMakeThresholdIndicator(nar, 4.0, L_SELECT_IF_LGE);
00118  *
00119  *        // Combine the indicator arrays logically to find
00120  *        // the components that will be retained.
00121  *    nad = numaLogicalOp(NULL, na1, na2, L_INTERSECTION);
00122  *    numaLogicalOp(nad, nad, na3, L_INTERSECTION);
00123  *    numaLogicalOp(nad, nad, na4, L_INTERSECTION);
00124  *    numaLogicalOp(nad, nad, na5, L_INTERSECTION);
00125  *    numaLogicalOp(nad, nad, na6, L_INTERSECTION);
00126  *
00127  *        // Invert to get the components that will be removed.
00128  *    numaInvert(nad, nad);
00129  *
00130  *        // Remove the components, in-place.
00131  *    pixRemoveWithIndicator(pixs, pixa, nad);
00132  */
00133 
00134 
00135 /*!
00136  *  pixSelectBySize()
00137  *
00138  *      Input:  pixs (1 bpp)
00139  *              width, height (threshold dimensions)
00140  *              connectivity (4 or 8)
00141  *              type (L_SELECT_WIDTH, L_SELECT_HEIGHT,
00142  *                    L_SELECT_IF_EITHER, L_SELECT_IF_BOTH)
00143  *              relation (L_SELECT_IF_LT, L_SELECT_IF_GT,
00144  *                        L_SELECT_IF_LTE, L_SELECT_IF_GTE)
00145  *              &changed (<optional return> 1 if changed; 0 otherwise)
00146  *      Return: filtered pixd, or null on error
00147  *
00148  *  Notes:
00149  *      (1) The args specify constraints on the size of the
00150  *          components that are kept.
00151  *      (2) If unchanged, returns a copy of pixs.  Otherwise,
00152  *          returns a new pix with the filtered components.
00153  *      (3) If the selection type is L_SELECT_WIDTH, the input
00154  *          height is ignored, and v.v.
00155  *      (4) To keep small components, use relation = L_SELECT_IF_LT or
00156  *          L_SELECT_IF_LTE.
00157  *          To keep large components, use relation = L_SELECT_IF_GT or
00158  *          L_SELECT_IF_GTE.
00159  */
00160 PIX *
00161 pixSelectBySize(PIX      *pixs,
00162                 l_int32   width,
00163                 l_int32   height,
00164                 l_int32   connectivity,
00165                 l_int32   type,
00166                 l_int32   relation,
00167                 l_int32  *pchanged)
00168 {
00169 l_int32  w, h, empty, changed, count;
00170 BOXA    *boxa;
00171 PIX     *pixd;
00172 PIXA    *pixas, *pixad;
00173 
00174     PROCNAME("pixSelectBySize");
00175 
00176     if (!pixs)
00177         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00178     if (connectivity != 4 && connectivity != 8)
00179         return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
00180     if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT &&
00181         type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH)
00182         return (PIX *)ERROR_PTR("invalid type", procName, NULL);
00183     if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
00184         relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
00185         return (PIX *)ERROR_PTR("invalid relation", procName, NULL);
00186     if (pchanged) *pchanged = FALSE;
00187     
00188         /* Check if any components exist */
00189     pixZero(pixs, &empty);
00190     if (empty)
00191         return pixCopy(NULL, pixs);
00192 
00193         /* Identify and select the components */
00194     boxa = pixConnComp(pixs, &pixas, connectivity); 
00195     pixad = pixaSelectBySize(pixas, width, height, type, relation, &changed);
00196     boxaDestroy(&boxa);
00197     pixaDestroy(&pixas);
00198 
00199         /* Render the result */
00200     if (!changed) {
00201         pixaDestroy(&pixad);
00202         return pixCopy(NULL, pixs);
00203     }
00204     else {
00205         if (pchanged) *pchanged = TRUE;
00206         pixGetDimensions(pixs, &w, &h, NULL);
00207         count = pixaGetCount(pixad);
00208         if (count == 0)  /* return empty pix */
00209             pixd = pixCreateTemplate(pixs);
00210         else {
00211             pixd = pixaDisplay(pixad, w, h);
00212             pixCopyResolution(pixd, pixs);
00213             pixCopyColormap(pixd, pixs);
00214             pixCopyText(pixd, pixs);
00215             pixCopyInputFormat(pixd, pixs);
00216         }
00217         pixaDestroy(&pixad);
00218         return pixd;
00219     }
00220 }
00221 
00222 
00223 /*!
00224  *  pixaSelectBySize()
00225  *
00226  *      Input:  pixas
00227  *              width, height (threshold dimensions)
00228  *              type (L_SELECT_WIDTH, L_SELECT_HEIGHT,
00229  *                    L_SELECT_IF_EITHER, L_SELECT_IF_BOTH)
00230  *              relation (L_SELECT_IF_LT, L_SELECT_IF_GT,
00231  *                        L_SELECT_IF_LTE, L_SELECT_IF_GTE)
00232  *              &changed (<optional return> 1 if changed; 0 otherwise)
00233  *      Return: pixad, or null on error
00234  *
00235  *  Notes:
00236  *      (1) The args specify constraints on the size of the
00237  *          components that are kept.
00238  *      (2) Uses pix and box clones in the new pixa.
00239  *      (3) If the selection type is L_SELECT_WIDTH, the input
00240  *          height is ignored, and v.v.
00241  *      (4) To keep small components, use relation = L_SELECT_IF_LT or
00242  *          L_SELECT_IF_LTE.
00243  *          To keep large components, use relation = L_SELECT_IF_GT or
00244  *          L_SELECT_IF_GTE.
00245  */
00246 PIXA *
00247 pixaSelectBySize(PIXA     *pixas,
00248                  l_int32   width,
00249                  l_int32   height,
00250                  l_int32   type,
00251                  l_int32   relation,
00252                  l_int32  *pchanged)
00253 {
00254 BOXA    *boxa;
00255 NUMA    *na;
00256 PIXA    *pixad;
00257 
00258     PROCNAME("pixaSelectBySize");
00259 
00260     if (!pixas)
00261         return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL);
00262     if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT &&
00263         type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH)
00264         return (PIXA *)ERROR_PTR("invalid type", procName, NULL);
00265     if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
00266         relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
00267         return (PIXA *)ERROR_PTR("invalid relation", procName, NULL);
00268 
00269         /* Compute the indicator array for saving components */
00270     boxa = pixaGetBoxa(pixas, L_CLONE);
00271     na = boxaMakeSizeIndicator(boxa, width, height, type, relation);
00272     boxaDestroy(&boxa);
00273 
00274         /* Filter to get output */
00275     pixad = pixaSelectWithIndicator(pixas, na, pchanged);
00276 
00277     numaDestroy(&na);
00278     return pixad;
00279 }
00280 
00281 
00282 /*!
00283  *  pixSelectByAreaPerimRatio()
00284  *
00285  *      Input:  pixs (1 bpp)
00286  *              thresh (threshold ratio of interior/boundary pixels)
00287  *              connectivity (4 or 8)
00288  *              type (L_SELECT_IF_LT, L_SELECT_IF_GT,
00289  *                    L_SELECT_IF_LTE, L_SELECT_IF_GTE)
00290  *              &changed (<optional return> 1 if changed; 0 if clone returned)
00291  *      Return: pixd, or null on error
00292  *
00293  *  Notes:
00294  *      (1) The args specify constraints on the size of the
00295  *          components that are kept.
00296  *      (2) If unchanged, returns a copy of pixs.  Otherwise,
00297  *          returns a new pix with the filtered components.
00298  *      (3) This filters "thin" components, where a thin component
00299  *          is defined to have a ratio of interior to boundary pixels
00300  *          that is smaller than a given threshold value.
00301  *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save the thin
00302  *          components, and L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
00303  */
00304 PIX *
00305 pixSelectByAreaPerimRatio(PIX       *pixs,
00306                          l_float32  thresh,
00307                          l_int32    connectivity,
00308                          l_int32    type,
00309                          l_int32   *pchanged)
00310 {
00311 l_int32  w, h, empty, changed, count;
00312 BOXA    *boxa;
00313 PIX     *pixd;
00314 PIXA    *pixas, *pixad;
00315 
00316     PROCNAME("pixSelectByAreaPerimRatio");
00317 
00318     if (!pixs)
00319         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00320     if (connectivity != 4 && connectivity != 8)
00321         return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
00322     if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
00323         type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
00324         return (PIX *)ERROR_PTR("invalid type", procName, NULL);
00325     if (pchanged) *pchanged = FALSE;
00326     
00327         /* Check if any components exist */
00328     pixZero(pixs, &empty);
00329     if (empty)
00330         return pixCopy(NULL, pixs);
00331 
00332         /* Filter thin components */
00333     boxa = pixConnComp(pixs, &pixas, connectivity); 
00334     pixad = pixaSelectByAreaPerimRatio(pixas, thresh, type, &changed);
00335     boxaDestroy(&boxa);
00336     pixaDestroy(&pixas);
00337 
00338         /* Render the result */
00339     if (!changed) {
00340         pixaDestroy(&pixad);
00341         return pixCopy(NULL, pixs);
00342     }
00343     else {
00344         if (pchanged) *pchanged = TRUE;
00345         pixGetDimensions(pixs, &w, &h, NULL);
00346         count = pixaGetCount(pixad);
00347         if (count == 0)  /* return empty pix */
00348             pixd = pixCreateTemplate(pixs);
00349         else {
00350             pixd = pixaDisplay(pixad, w, h);
00351             pixCopyResolution(pixd, pixs);
00352             pixCopyColormap(pixd, pixs);
00353             pixCopyText(pixd, pixs);
00354             pixCopyInputFormat(pixd, pixs);
00355         }
00356         pixaDestroy(&pixad);
00357         return pixd;
00358     }
00359 }
00360 
00361 
00362 /*!
00363  *  pixaSelectByAreaPerimRatio()
00364  *
00365  *      Input:  pixas
00366  *              thresh (threshold ratio of interior/boundary pixels)
00367  *              type (L_SELECT_IF_LT, L_SELECT_IF_GT,
00368  *                    L_SELECT_IF_LTE, L_SELECT_IF_GTE)
00369  *              &changed (<optional return> 1 if changed; 0 if clone returned)
00370  *      Return: pixad, or null on error
00371  *
00372  *  Notes:
00373  *      (1) Returns a pixa clone if no components are removed.
00374  *      (2) Uses pix and box clones in the new pixa.
00375  *      (3) We define a thin component here to be one with a ratio
00376  *          of interior to boundary pixels that is smaller than a given
00377  *          threshold value.
00378  *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save the thin
00379  *          components, and L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
00380  */
00381 PIXA *
00382 pixaSelectByAreaPerimRatio(PIXA      *pixas,
00383                            l_float32  thresh,
00384                            l_int32    type,
00385                            l_int32   *pchanged)
00386 {
00387 NUMA  *na, *nai;
00388 PIXA  *pixad;
00389 
00390     PROCNAME("pixaSelectByAreaPerimRatio");
00391 
00392     if (!pixas)
00393         return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL);
00394     if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
00395         type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
00396         return (PIXA *)ERROR_PTR("invalid type", procName, NULL);
00397 
00398         /* Compute component ratios. */
00399     na = pixaFindAreaPerimRatio(pixas);
00400 
00401         /* Generate indicator array for elements to be saved. */
00402     nai = numaMakeThresholdIndicator(na, thresh, type);
00403     numaDestroy(&na);
00404 
00405         /* Filter to get output */
00406     pixad = pixaSelectWithIndicator(pixas, nai, pchanged);
00407 
00408     numaDestroy(&nai);
00409     return pixad;
00410 }
00411 
00412 
00413 /*!
00414  *  pixSelectByAreaFraction()
00415  *
00416  *      Input:  pixs (1 bpp)
00417  *              thresh (threshold ratio of fg pixels to (w * h))
00418  *              connectivity (4 or 8)
00419  *              type (L_SELECT_IF_LT, L_SELECT_IF_GT,
00420  *                    L_SELECT_IF_LTE, L_SELECT_IF_GTE)
00421  *              &changed (<optional return> 1 if changed; 0 if clone returned)
00422  *      Return: pixd, or null on error
00423  *
00424  *  Notes:
00425  *      (1) The args specify constraints on the amount of foreground
00426  *          coverage of the components that are kept.
00427  *      (2) If unchanged, returns a copy of pixs.  Otherwise,
00428  *          returns a new pix with the filtered components.
00429  *      (3) This filters components based on the fraction of fg pixels
00430  *          of the component in its bounding box.
00431  *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
00432  *          with less than the threshold fraction of foreground, and
00433  *          L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
00434  */
00435 PIX *
00436 pixSelectByAreaFraction(PIX       *pixs,
00437                         l_float32  thresh,
00438                         l_int32    connectivity,
00439                         l_int32    type,
00440                         l_int32   *pchanged)
00441 {
00442 l_int32  w, h, empty, changed, count;
00443 BOXA    *boxa;
00444 PIX     *pixd;
00445 PIXA    *pixas, *pixad;
00446 
00447     PROCNAME("pixSelectByAreaFraction");
00448 
00449     if (!pixs)
00450         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00451     if (connectivity != 4 && connectivity != 8)
00452         return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
00453     if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
00454         type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
00455         return (PIX *)ERROR_PTR("invalid type", procName, NULL);
00456     if (pchanged) *pchanged = FALSE;
00457     
00458         /* Check if any components exist */
00459     pixZero(pixs, &empty);
00460     if (empty)
00461         return pixCopy(NULL, pixs);
00462 
00463         /* Filter components */
00464     boxa = pixConnComp(pixs, &pixas, connectivity); 
00465     pixad = pixaSelectByAreaFraction(pixas, thresh, type, &changed);
00466     boxaDestroy(&boxa);
00467     pixaDestroy(&pixas);
00468 
00469         /* Render the result */
00470     if (!changed) {
00471         pixaDestroy(&pixad);
00472         return pixCopy(NULL, pixs);
00473     }
00474     else {
00475         if (pchanged) *pchanged = TRUE;
00476         pixGetDimensions(pixs, &w, &h, NULL);
00477         count = pixaGetCount(pixad);
00478         if (count == 0)  /* return empty pix */
00479             pixd = pixCreateTemplate(pixs);
00480         else {
00481             pixd = pixaDisplay(pixad, w, h);
00482             pixCopyResolution(pixd, pixs);
00483             pixCopyColormap(pixd, pixs);
00484             pixCopyText(pixd, pixs);
00485             pixCopyInputFormat(pixd, pixs);
00486         }
00487         pixaDestroy(&pixad);
00488         return pixd;
00489     }
00490 }
00491 
00492 
00493 /*!
00494  *  pixaSelectByAreaFraction()
00495  *
00496  *      Input:  pixas
00497  *              thresh (threshold ratio of fg pixels to (w * h))
00498  *              type (L_SELECT_IF_LT, L_SELECT_IF_GT,
00499  *                    L_SELECT_IF_LTE, L_SELECT_IF_GTE)
00500  *              &changed (<optional return> 1 if changed; 0 if clone returned)
00501  *      Return: pixad, or null on error
00502  *
00503  *  Notes:
00504  *      (1) Returns a pixa clone if no components are removed.
00505  *      (2) Uses pix and box clones in the new pixa.
00506  *      (3) This filters components based on the fraction of fg pixels
00507  *          of the component in its bounding box.
00508  *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
00509  *          with less than the threshold fraction of foreground, and
00510  *          L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
00511  */
00512 PIXA *
00513 pixaSelectByAreaFraction(PIXA      *pixas,
00514                          l_float32  thresh,
00515                          l_int32    type,
00516                          l_int32   *pchanged)
00517 {
00518 NUMA  *na, *nai;
00519 PIXA  *pixad;
00520 
00521     PROCNAME("pixaSelectByAreaFraction");
00522 
00523     if (!pixas)
00524         return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL);
00525     if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
00526         type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
00527         return (PIXA *)ERROR_PTR("invalid type", procName, NULL);
00528 
00529         /* Compute component ratios. */
00530     na = pixaFindAreaFraction(pixas);
00531 
00532         /* Generate indicator array for elements to be saved. */
00533     nai = numaMakeThresholdIndicator(na, thresh, type);
00534     numaDestroy(&na);
00535 
00536         /* Filter to get output */
00537     pixad = pixaSelectWithIndicator(pixas, nai, pchanged);
00538 
00539     numaDestroy(&nai);
00540     return pixad;
00541 }
00542 
00543 
00544 /*!
00545  *  pixSelectByWidthHeightRatio()
00546  *
00547  *      Input:  pixs (1 bpp)
00548  *              thresh (threshold ratio of width/height)
00549  *              connectivity (4 or 8)
00550  *              type (L_SELECT_IF_LT, L_SELECT_IF_GT,
00551  *                    L_SELECT_IF_LTE, L_SELECT_IF_GTE)
00552  *              &changed (<optional return> 1 if changed; 0 if clone returned)
00553  *      Return: pixd, or null on error
00554  *
00555  *  Notes:
00556  *      (1) The args specify constraints on the width-to-height ratio
00557  *          for components that are kept.
00558  *      (2) If unchanged, returns a copy of pixs.  Otherwise,
00559  *          returns a new pix with the filtered components.
00560  *      (3) This filters components based on the width-to-height ratios.
00561  *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
00562  *          with less than the threshold ratio, and
00563  *          L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
00564  */
00565 PIX *
00566 pixSelectByWidthHeightRatio(PIX       *pixs,
00567                             l_float32  thresh,
00568                             l_int32    connectivity,
00569                             l_int32    type,
00570                             l_int32   *pchanged)
00571 {
00572 l_int32  w, h, empty, changed, count;
00573 BOXA    *boxa;
00574 PIX     *pixd;
00575 PIXA    *pixas, *pixad;
00576 
00577     PROCNAME("pixSelectByWidthHeightRatio");
00578 
00579     if (!pixs)
00580         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00581     if (connectivity != 4 && connectivity != 8)
00582         return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
00583     if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
00584         type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
00585         return (PIX *)ERROR_PTR("invalid type", procName, NULL);
00586     if (pchanged) *pchanged = FALSE;
00587     
00588         /* Check if any components exist */
00589     pixZero(pixs, &empty);
00590     if (empty)
00591         return pixCopy(NULL, pixs);
00592 
00593         /* Filter components */
00594     boxa = pixConnComp(pixs, &pixas, connectivity); 
00595     pixad = pixaSelectByWidthHeightRatio(pixas, thresh, type, &changed);
00596     boxaDestroy(&boxa);
00597     pixaDestroy(&pixas);
00598 
00599         /* Render the result */
00600     if (!changed) {
00601         pixaDestroy(&pixad);
00602         return pixCopy(NULL, pixs);
00603     }
00604     else {
00605         if (pchanged) *pchanged = TRUE;
00606         pixGetDimensions(pixs, &w, &h, NULL);
00607         count = pixaGetCount(pixad);
00608         if (count == 0)  /* return empty pix */
00609             pixd = pixCreateTemplate(pixs);
00610         else {
00611             pixd = pixaDisplay(pixad, w, h);
00612             pixCopyResolution(pixd, pixs);
00613             pixCopyColormap(pixd, pixs);
00614             pixCopyText(pixd, pixs);
00615             pixCopyInputFormat(pixd, pixs);
00616         }
00617         pixaDestroy(&pixad);
00618         return pixd;
00619     }
00620 }
00621 
00622 
00623 /*!
00624  *  pixaSelectByWidthHeightRatio()
00625  *
00626  *      Input:  pixas
00627  *              thresh (threshold ratio of width/height)
00628  *              type (L_SELECT_IF_LT, L_SELECT_IF_GT,
00629  *                    L_SELECT_IF_LTE, L_SELECT_IF_GTE)
00630  *              &changed (<optional return> 1 if changed; 0 if clone returned)
00631  *      Return: pixad, or null on error
00632  *
00633  *  Notes:
00634  *      (1) Returns a pixa clone if no components are removed.
00635  *      (2) Uses pix and box clones in the new pixa.
00636  *      (3) This filters components based on the width-to-height ratio
00637  *          of each pix.
00638  *      (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components
00639  *          with less than the threshold ratio, and
00640  *          L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them.
00641  */
00642 PIXA *
00643 pixaSelectByWidthHeightRatio(PIXA      *pixas,
00644                              l_float32  thresh,
00645                              l_int32    type,
00646                              l_int32   *pchanged)
00647 {
00648 NUMA  *na, *nai;
00649 PIXA  *pixad;
00650 
00651     PROCNAME("pixaSelectByWidthHeightRatio");
00652 
00653     if (!pixas)
00654         return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL);
00655     if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT &&
00656         type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE)
00657         return (PIXA *)ERROR_PTR("invalid type", procName, NULL);
00658 
00659         /* Compute component ratios. */
00660     na = pixaFindWidthHeightRatio(pixas);
00661 
00662         /* Generate indicator array for elements to be saved. */
00663     nai = numaMakeThresholdIndicator(na, thresh, type);
00664     numaDestroy(&na);
00665 
00666         /* Filter to get output */
00667     pixad = pixaSelectWithIndicator(pixas, nai, pchanged);
00668 
00669     numaDestroy(&nai);
00670     return pixad;
00671 }
00672 
00673 
00674 /*!
00675  *  pixaSelectWithIndicator()
00676  *
00677  *      Input:  pixas
00678  *              na (indicator numa)
00679  *              &changed (<optional return> 1 if changed; 0 if clone returned)
00680  *      Return: pixad, or null on error
00681  *
00682  *  Notes:
00683  *      (1) Returns a pixa clone if no components are removed.
00684  *      (2) Uses pix and box clones in the new pixa.
00685  *      (3) The indicator numa has values 0 (ignore) and 1 (accept).
00686  */
00687 PIXA *
00688 pixaSelectWithIndicator(PIXA     *pixas,
00689                         NUMA     *na,
00690                         l_int32  *pchanged)
00691 {
00692 l_int32  i, n, ival, nsave;
00693 BOX     *box;
00694 PIX     *pixt;
00695 PIXA    *pixad;
00696 
00697     PROCNAME("pixaSelectWithIndicator");
00698 
00699     if (!pixas)
00700         return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL);
00701     if (!na)
00702         return (PIXA *)ERROR_PTR("na not defined", procName, NULL);
00703 
00704     nsave = 0;
00705     n = numaGetCount(na);
00706     for (i = 0; i < n; i++) {
00707         numaGetIValue(na, i, &ival);
00708         if (ival == 1) nsave++;
00709     }
00710 
00711     if (nsave == n) {
00712         if (pchanged) *pchanged = FALSE;
00713         return pixaCopy(pixas, L_CLONE);
00714     }
00715     if (pchanged) *pchanged = TRUE;
00716     pixad = pixaCreate(nsave);
00717     for (i = 0; i < n; i++) {
00718         numaGetIValue(na, i, &ival);
00719         if (ival == 0) continue;
00720         pixt = pixaGetPix(pixas, i, L_CLONE);
00721         box = pixaGetBox(pixas, i, L_CLONE);
00722         pixaAddPix(pixad, pixt, L_INSERT);
00723         pixaAddBox(pixad, box, L_INSERT);
00724     }
00725 
00726     return pixad;
00727 }
00728 
00729 
00730 /*!
00731  *  pixRemoveWithIndicator()
00732  *
00733  *      Input:  pixs (1 bpp pix from which components are removed; in-place)
00734  *              pixa (of connected components in pixs)
00735  *              na (numa indicator: remove components corresponding to 1s)
00736  *      Return: 0 if OK, 1 on error
00737  *
00738  *  Notes:
00739  *      (1) This complements pixAddWithIndicator().   Here, the selected
00740  *          components are set subtracted from pixs.
00741  */
00742 l_int32
00743 pixRemoveWithIndicator(PIX   *pixs,
00744                        PIXA  *pixa,
00745                        NUMA  *na)
00746 {
00747 l_int32  i, n, ival, x, y, w, h;
00748 BOX     *box;
00749 PIX     *pix;
00750 
00751     PROCNAME("pixRemoveWithIndicator");
00752 
00753     if (!pixs)
00754         return ERROR_INT("pixs not defined", procName, 1);
00755     if (!pixa)
00756         return ERROR_INT("pixa not defined", procName, 1);
00757     if (!na)
00758         return ERROR_INT("na not defined", procName, 1);
00759     n = pixaGetCount(pixa);
00760     if (n != numaGetCount(na))
00761         return ERROR_INT("pixa and na sizes not equal", procName, 1);
00762 
00763     for (i = 0; i < n; i++) {
00764         numaGetIValue(na, i, &ival);
00765         if (ival == 1) {
00766             pix = pixaGetPix(pixa, i, L_CLONE);
00767             box = pixaGetBox(pixa, i, L_CLONE);
00768             boxGetGeometry(box, &x, &y, &w, &h);
00769             pixRasterop(pixs, x, y, w, h, PIX_DST & PIX_NOT(PIX_SRC),
00770                         pix, 0, 0);
00771             boxDestroy(&box);
00772             pixDestroy(&pix);
00773         }
00774     }
00775 
00776     return 0;
00777 }
00778 
00779 
00780 /*!
00781  *  pixAddWithIndicator()
00782  *
00783  *      Input:  pixs (1 bpp pix from which components are added; in-place)
00784  *              pixa (of connected components, some of which will be put
00785  *                    into pixs)
00786  *              na (numa indicator: add components corresponding to 1s)
00787  *      Return: 0 if OK, 1 on error
00788  *
00789  *  Notes:
00790  *      (1) This complements pixRemoveWithIndicator().   Here, the selected
00791  *          components are added to pixs.
00792  */
00793 l_int32
00794 pixAddWithIndicator(PIX   *pixs,
00795                     PIXA  *pixa,
00796                     NUMA  *na)
00797 {
00798 l_int32  i, n, ival, x, y, w, h;
00799 BOX     *box;
00800 PIX     *pix;
00801 
00802     PROCNAME("pixAddWithIndicator");
00803 
00804     if (!pixs)
00805         return ERROR_INT("pixs not defined", procName, 1);
00806     if (!pixa)
00807         return ERROR_INT("pixa not defined", procName, 1);
00808     if (!na)
00809         return ERROR_INT("na not defined", procName, 1);
00810     n = pixaGetCount(pixa);
00811     if (n != numaGetCount(na))
00812         return ERROR_INT("pixa and na sizes not equal", procName, 1);
00813 
00814     for (i = 0; i < n; i++) {
00815         numaGetIValue(na, i, &ival);
00816         if (ival == 1) {
00817             pix = pixaGetPix(pixa, i, L_CLONE);
00818             box = pixaGetBox(pixa, i, L_CLONE);
00819             boxGetGeometry(box, &x, &y, &w, &h);
00820             pixRasterop(pixs, x, y, w, h, PIX_SRC | PIX_DST, pix, 0, 0);
00821             boxDestroy(&box);
00822             pixDestroy(&pix);
00823         }
00824     }
00825 
00826     return 0;
00827 }
00828 
00829 
00830 /*---------------------------------------------------------------------*
00831  *                              Sort functions                         *
00832  *---------------------------------------------------------------------*/
00833 /*!
00834  *  pixaSort()
00835  *
00836  *      Input:  pixas
00837  *              sorttype (L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH,
00838  *                        L_SORT_BY_HEIGHT, L_SORT_BY_MIN_DIMENSION,
00839  *                        L_SORT_BY_MAX_DIMENSION, L_SORT_BY_PERIMETER,
00840  *                        L_SORT_BY_AREA, L_SORT_BY_ASPECT_RATIO)
00841  *              sortorder  (L_SORT_INCREASING, L_SORT_DECREASING)
00842  *              &naindex (<optional return> index of sorted order into
00843  *                        original array)
00844  *              copyflag (L_COPY, L_CLONE)
00845  *      Return: pixad (sorted version of pixas), or null on error
00846  *
00847  *  Notes:
00848  *      (1) This sorts based on the data in the boxa.  If the boxa
00849  *          count is not the same as the pixa count, this returns an error.
00850  *      (2) The copyflag refers to the pix and box copies that are
00851  *          inserted into the sorted pixa.  These are either L_COPY
00852  *          or L_CLONE.
00853  */
00854 PIXA *
00855 pixaSort(PIXA    *pixas,
00856          l_int32  sorttype,
00857          l_int32  sortorder,
00858          NUMA   **pnaindex,
00859          l_int32  copyflag)
00860 {
00861 l_int32  i, n, x, y, w, h;
00862 BOXA    *boxa;
00863 NUMA    *na, *naindex;
00864 PIXA    *pixad;
00865 
00866     PROCNAME("pixaSort");
00867 
00868     if (pnaindex) *pnaindex = NULL;
00869     if (!pixas)
00870         return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL);
00871     if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && 
00872         sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT &&
00873         sorttype != L_SORT_BY_MIN_DIMENSION &&
00874         sorttype != L_SORT_BY_MAX_DIMENSION &&
00875         sorttype != L_SORT_BY_PERIMETER &&
00876         sorttype != L_SORT_BY_AREA &&
00877         sorttype != L_SORT_BY_ASPECT_RATIO)
00878         return (PIXA *)ERROR_PTR("invalid sort type", procName, NULL);
00879     if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING)
00880         return (PIXA *)ERROR_PTR("invalid sort order", procName, NULL);
00881     if (copyflag != L_COPY && copyflag != L_CLONE)
00882         return (PIXA *)ERROR_PTR("invalid copy flag", procName, NULL);
00883 
00884     if ((boxa = pixas->boxa) == NULL)   /* not owned; do not destroy */
00885         return (PIXA *)ERROR_PTR("boxa not found", procName, NULL);
00886     n = pixaGetCount(pixas);
00887     if (boxaGetCount(boxa) != n)
00888         return (PIXA *)ERROR_PTR("boxa and pixa counts differ", procName, NULL);
00889 
00890         /* Use O(n) binsort if possible */
00891     if (n > MIN_COMPS_FOR_BIN_SORT &&
00892         ((sorttype == L_SORT_BY_X) || (sorttype == L_SORT_BY_Y) ||
00893          (sorttype == L_SORT_BY_WIDTH) || (sorttype == L_SORT_BY_HEIGHT) ||
00894          (sorttype == L_SORT_BY_PERIMETER)))
00895         return pixaBinSort(pixas, sorttype, sortorder, pnaindex, copyflag);
00896 
00897         /* Build up numa of specific data */
00898     if ((na = numaCreate(n)) == NULL)
00899         return (PIXA *)ERROR_PTR("na not made", procName, NULL);
00900     for (i = 0; i < n; i++) {
00901         boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h);
00902         switch (sorttype)
00903         {
00904         case L_SORT_BY_X:
00905             numaAddNumber(na, x);
00906             break;
00907         case L_SORT_BY_Y:
00908             numaAddNumber(na, y);
00909             break;
00910         case L_SORT_BY_WIDTH:
00911             numaAddNumber(na, w);
00912             break;
00913         case L_SORT_BY_HEIGHT:
00914             numaAddNumber(na, h);
00915             break;
00916         case L_SORT_BY_MIN_DIMENSION:
00917             numaAddNumber(na, L_MIN(w, h));
00918             break;
00919         case L_SORT_BY_MAX_DIMENSION:
00920             numaAddNumber(na, L_MAX(w, h));
00921             break;
00922         case L_SORT_BY_PERIMETER:
00923             numaAddNumber(na, w + h);
00924             break;
00925         case L_SORT_BY_AREA:
00926             numaAddNumber(na, w * h);
00927             break;
00928         case L_SORT_BY_ASPECT_RATIO:
00929             numaAddNumber(na, (l_float32)w / (l_float32)h);
00930             break;
00931         default:
00932             L_WARNING("invalid sort type", procName);
00933         }
00934     }
00935 
00936         /* Get the sort index for data array */
00937     if ((naindex = numaGetSortIndex(na, sortorder)) == NULL)
00938         return (PIXA *)ERROR_PTR("naindex not made", procName, NULL);
00939 
00940         /* Build up sorted pixa using sort index */
00941     if ((pixad = pixaSortByIndex(pixas, naindex, copyflag)) == NULL)
00942         return (PIXA *)ERROR_PTR("pixad not made", procName, NULL);
00943 
00944     if (pnaindex)
00945         *pnaindex = naindex;
00946     else
00947         numaDestroy(&naindex);
00948     numaDestroy(&na);
00949     return pixad;
00950 }
00951 
00952 
00953 /*!
00954  *  pixaBinSort()
00955  *
00956  *      Input:  pixas
00957  *              sorttype (L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH,
00958  *                        L_SORT_BY_HEIGHT, L_SORT_BY_PERIMETER)
00959  *              sortorder  (L_SORT_INCREASING, L_SORT_DECREASING)
00960  *              &naindex (<optional return> index of sorted order into
00961  *                        original array)
00962  *              copyflag (L_COPY, L_CLONE)
00963  *      Return: pixad (sorted version of pixas), or null on error
00964  *
00965  *  Notes:
00966  *      (1) This sorts based on the data in the boxa.  If the boxa
00967  *          count is not the same as the pixa count, this returns an error.
00968  *      (2) The copyflag refers to the pix and box copies that are
00969  *          inserted into the sorted pixa.  These are either L_COPY
00970  *          or L_CLONE.
00971  *      (3) For a large number of boxes (say, greater than 1000), this
00972  *          O(n) binsort is much faster than the O(nlogn) shellsort.
00973  *          For 5000 components, this is over 20x faster than boxaSort().
00974  *      (4) Consequently, pixaSort() calls this function if it will
00975  *          likely go much faster.
00976  */
00977 PIXA *
00978 pixaBinSort(PIXA    *pixas,
00979             l_int32  sorttype,
00980             l_int32  sortorder,
00981             NUMA   **pnaindex,
00982             l_int32  copyflag)
00983 {
00984 l_int32  i, n, x, y, w, h;
00985 BOXA    *boxa;
00986 NUMA    *na, *naindex;
00987 PIXA    *pixad;
00988 
00989     PROCNAME("pixaBinSort");
00990 
00991     if (pnaindex) *pnaindex = NULL;
00992     if (!pixas)
00993         return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL);
00994     if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && 
00995         sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT &&
00996         sorttype != L_SORT_BY_PERIMETER)
00997         return (PIXA *)ERROR_PTR("invalid sort type", procName, NULL);
00998     if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING)
00999         return (PIXA *)ERROR_PTR("invalid sort order", procName, NULL);
01000     if (copyflag != L_COPY && copyflag != L_CLONE)
01001         return (PIXA *)ERROR_PTR("invalid copy flag", procName, NULL);
01002 
01003         /* Verify that the pixa and its boxa have the same count */
01004     if ((boxa = pixas->boxa) == NULL)   /* not owned; do not destroy */
01005         return (PIXA *)ERROR_PTR("boxa not found", procName, NULL);
01006     n = pixaGetCount(pixas);
01007     if (boxaGetCount(boxa) != n)
01008         return (PIXA *)ERROR_PTR("boxa and pixa counts differ", procName, NULL);
01009 
01010         /* Generate Numa of appropriate box dimensions */
01011     if ((na = numaCreate(n)) == NULL)
01012         return (PIXA *)ERROR_PTR("na not made", procName, NULL);
01013     for (i = 0; i < n; i++) {
01014         boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h);
01015         switch (sorttype)
01016         {
01017         case L_SORT_BY_X:
01018             numaAddNumber(na, x);
01019             break;
01020         case L_SORT_BY_Y:
01021             numaAddNumber(na, y);
01022             break;
01023         case L_SORT_BY_WIDTH:
01024             numaAddNumber(na, w);
01025             break;
01026         case L_SORT_BY_HEIGHT:
01027             numaAddNumber(na, h);
01028             break;
01029         case L_SORT_BY_PERIMETER:
01030             numaAddNumber(na, w + h);
01031             break;
01032         default:
01033             L_WARNING("invalid sort type", procName);
01034         }
01035     }
01036 
01037         /* Get the sort index for data array */
01038     if ((naindex = numaGetBinSortIndex(na, sortorder)) == NULL)
01039         return (PIXA *)ERROR_PTR("naindex not made", procName, NULL);
01040 
01041         /* Build up sorted pixa using sort index */
01042     if ((pixad = pixaSortByIndex(pixas, naindex, copyflag)) == NULL)
01043         return (PIXA *)ERROR_PTR("pixad not made", procName, NULL);
01044 
01045     if (pnaindex)
01046         *pnaindex = naindex;
01047     else
01048         numaDestroy(&naindex);
01049     numaDestroy(&na);
01050     return pixad;
01051 }
01052 
01053 
01054 /*!
01055  *  pixaSortByIndex()
01056  * 
01057  *      Input:  pixas
01058  *              naindex (na that maps from the new pixa to the input pixa)
01059  *              copyflag (L_COPY, L_CLONE)
01060  *      Return: pixad (sorted), or null on error
01061  */
01062 PIXA *
01063 pixaSortByIndex(PIXA    *pixas,
01064                 NUMA    *naindex,
01065                 l_int32  copyflag)
01066 {
01067 l_int32  i, n, index;
01068 BOX     *box;
01069 PIX     *pix;
01070 PIXA    *pixad;
01071 
01072     PROCNAME("pixaSortByIndex");
01073 
01074     if (!pixas)
01075         return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL);
01076     if (!naindex)
01077         return (PIXA *)ERROR_PTR("naindex not defined", procName, NULL);
01078     if (copyflag != L_CLONE && copyflag != L_COPY)
01079         return (PIXA *)ERROR_PTR("invalid copyflag", procName, NULL);
01080 
01081     n = pixaGetCount(pixas);
01082     pixad = pixaCreate(n);
01083     for (i = 0; i < n; i++) {
01084         numaGetIValue(naindex, i, &index);
01085         pix = pixaGetPix(pixas, index, copyflag);
01086         box = pixaGetBox(pixas, index, copyflag);
01087         pixaAddPix(pixad, pix, L_INSERT);
01088         pixaAddBox(pixad, box, L_INSERT);
01089     }
01090 
01091     return pixad;
01092 }
01093 
01094 
01095 /*!
01096  *  pixaSort2dByIndex()
01097  * 
01098  *      Input:  pixas
01099  *              naa (numaa that maps from the new pixaa to the input pixas)
01100  *              copyflag (L_CLONE or L_COPY)
01101  *      Return: pixaa (sorted), or null on error
01102  */
01103 PIXAA *
01104 pixaSort2dByIndex(PIXA    *pixas,
01105                   NUMAA   *naa,
01106                   l_int32  copyflag)
01107 {
01108 l_int32  pixtot, ntot, i, j, n, nn, index;
01109 BOX     *box;
01110 NUMA    *na;
01111 PIX     *pix;
01112 PIXA    *pixa;
01113 PIXAA   *pixaa;
01114 
01115     PROCNAME("pixaSort2dByIndex");
01116 
01117     if (!pixas)
01118         return (PIXAA *)ERROR_PTR("pixas not defined", procName, NULL);
01119     if (!naa)
01120         return (PIXAA *)ERROR_PTR("naindex not defined", procName, NULL);
01121 
01122         /* Check counts */
01123     ntot = numaaGetNumberCount(naa);
01124     pixtot = pixaGetCount(pixas);
01125     if (ntot != pixtot)
01126         return (PIXAA *)ERROR_PTR("element count mismatch", procName, NULL);
01127 
01128     n = numaaGetCount(naa);
01129     pixaa = pixaaCreate(n);
01130     for (i = 0; i < n; i++) {
01131         na = numaaGetNuma(naa, i, L_CLONE);
01132         nn = numaGetCount(na);
01133         pixa = pixaCreate(nn);
01134         for (j = 0; j < nn; j++) {
01135             numaGetIValue(na, j, &index);
01136             pix = pixaGetPix(pixas, index, copyflag);
01137             box = pixaGetBox(pixas, index, copyflag);
01138             pixaAddPix(pixa, pix, L_INSERT);
01139             pixaAddBox(pixa, box, L_INSERT);
01140         }
01141         pixaaAddPixa(pixaa, pixa, L_INSERT);
01142         numaDestroy(&na);
01143     }
01144 
01145     return pixaa;
01146 }
01147 
01148 
01149 
01150 /*---------------------------------------------------------------------*
01151  *                        Miscellaneous functions                      *
01152  *---------------------------------------------------------------------*/
01153 /*!
01154  *  pixaAddBorderGeneral()
01155  *
01156  *      Input:  pixad (can be null or equal to pixas)
01157  *              pixas (containing pix of all depths; colormap ok)
01158  *              left, right, top, bot  (number of pixels added)
01159  *              val   (value of added border pixels)
01160  *      Return: pixad (with border added to each pix), including on error
01161  *
01162  *  Notes:
01163  *      (1) For binary images:
01164  *             white:  val = 0
01165  *             black:  val = 1
01166  *          For grayscale images:
01167  *             white:  val = 2 ** d - 1
01168  *             black:  val = 0
01169  *          For rgb color images:
01170  *             white:  val = 0xffffff00
01171  *             black:  val = 0
01172  *          For colormapped images, use 'index' found this way:
01173  *             white: pixcmapGetRankIntensity(cmap, 1.0, &index);
01174  *             black: pixcmapGetRankIntensity(cmap, 0.0, &index);
01175  *      (2) For in-place replacement of each pix with a bordered version,
01176  *          use @pixad = @pixas.  To make a new pixa, use @pixad = NULL.
01177  *      (3) In both cases, the boxa has sides adjusted as if it were
01178  *          expanded by the border.
01179  */
01180 PIXA *
01181 pixaAddBorderGeneral(PIXA     *pixad,
01182                      PIXA     *pixas,
01183                      l_int32   left,
01184                      l_int32   right,
01185                      l_int32   top,
01186                      l_int32   bot,
01187                      l_uint32  val)
01188 {
01189 l_int32  i, n, nbox;
01190 BOX     *box;
01191 BOXA    *boxad;
01192 PIX     *pixs, *pixd;
01193 
01194     PROCNAME("pixaAddBorderGeneral");
01195 
01196     if (!pixas)
01197         return (PIXA *)ERROR_PTR("pixas not defined", procName, pixad);
01198     if (left < 0 || right < 0 || top < 0 || bot < 0)
01199         return (PIXA *)ERROR_PTR("negative border added!", procName, pixad);
01200     if (pixad && (pixad != pixas))
01201         return (PIXA *)ERROR_PTR("pixad defined but != pixas", procName, pixad);
01202 
01203     n = pixaGetCount(pixas);
01204     if (!pixad)
01205         pixad = pixaCreate(n);
01206     for (i = 0; i < n; i++) {
01207         pixs = pixaGetPix(pixas, i, L_CLONE);
01208         pixd = pixAddBorderGeneral(pixs, left, right, top, bot, val);
01209         if (pixad == pixas)  /* replace */
01210             pixaReplacePix(pixad, i, pixd, NULL);
01211         else
01212             pixaAddPix(pixad, pixd, L_INSERT);
01213         pixDestroy(&pixs);
01214     }
01215 
01216     nbox = pixaGetBoxaCount(pixas);
01217     boxad = pixaGetBoxa(pixad, L_CLONE);
01218     for (i = 0; i < nbox; i++) {
01219         if ((box = pixaGetBox(pixas, i, L_COPY)) == NULL) {
01220             L_WARNING_INT("box %d not found", procName, i);
01221             break;
01222         }
01223         boxAdjustSides(box, box, -left, right, -top, bot);
01224         if (pixad == pixas)  /* replace */
01225             boxaReplaceBox(boxad, i, box);
01226         else
01227             boxaAddBox(boxad, box, L_INSERT);
01228     }
01229     boxaDestroy(&boxad);
01230 
01231     return pixad;
01232 }
01233 
01234 
01235 /*!
01236  *  pixaaFlattenToPixa()
01237  *
01238  *      Input:  pixaa
01239  *              &naindex  (<optional return> the pixa index in the pixaa)
01240  *              copyflag  (L_COPY or L_CLONE)
01241  *      Return: pixa, or null on error
01242  *
01243  *  Notes:
01244  *      (1) This 'flattens' the pixaa to a pixa, taking the pix in
01245  *          order in the first pixa, then the second, etc.
01246  *      (2) If &naindex is defined, we generate a Numa that gives, for
01247  *          each pix in the pixaa, the index of the pixa to which it belongs.
01248  */
01249 PIXA *
01250 pixaaFlattenToPixa(PIXAA   *pixaa,
01251                    NUMA   **pnaindex,
01252                    l_int32  copyflag)
01253 {
01254 l_int32  i, j, m, n;
01255 BOX     *box;
01256 NUMA    *naindex;
01257 PIX     *pix;
01258 PIXA    *pixa, *pixat;
01259 
01260     PROCNAME("pixaaFlattenToPixa");
01261 
01262     if (pnaindex) *pnaindex = NULL;
01263     if (!pixaa)
01264         return (PIXA *)ERROR_PTR("pixaa not defined", procName, NULL);
01265     if (copyflag != L_COPY && copyflag != L_CLONE)
01266         return (PIXA *)ERROR_PTR("invalid copyflag", procName, NULL);
01267 
01268     if (pnaindex) {
01269         naindex = numaCreate(0);
01270         *pnaindex = naindex;
01271     }
01272 
01273     n = pixaaGetCount(pixaa);
01274     pixa = pixaCreate(n);
01275     for (i = 0; i < n; i++) {
01276         pixat = pixaaGetPixa(pixaa, i, L_CLONE);
01277         m = pixaGetCount(pixat);
01278         for (j = 0; j < m; j++) {
01279             pix = pixaGetPix(pixat, j, copyflag);
01280             box = pixaGetBox(pixat, j, copyflag);
01281             pixaAddPix(pixa, pix, L_INSERT);
01282             pixaAddBox(pixa, box, L_INSERT);
01283             if (pnaindex)
01284                 numaAddNumber(naindex, i);  /* save 'row' number */
01285         }
01286         pixaDestroy(&pixat);
01287     }
01288 
01289     return pixa;
01290 }
01291 
01292 
01293 /*!
01294  *  pixaSizeRange()
01295  *
01296  *      Input:  pixa
01297  *              &minw, &minh, &maxw, &maxh (<optional return> range of
01298  *                                          dimensions of pix in the array)
01299  *      Return: 0 if OK, 1 on error
01300  */
01301 l_int32  
01302 pixaSizeRange(PIXA     *pixa,
01303               l_int32  *pminw,
01304               l_int32  *pminh,
01305               l_int32  *pmaxw,
01306               l_int32  *pmaxh)
01307 {
01308 l_int32  minw, minh, maxw, maxh, i, n, w, h;
01309 PIX     *pix;
01310 
01311     PROCNAME("pixaSizeRange");
01312 
01313     if (!pixa)
01314         return ERROR_INT("pixa not defined", procName, 1);
01315     if (!pminw && !pmaxw && !pminh && !pmaxh)
01316         return ERROR_INT("no data can be returned", procName, 1);
01317     
01318     minw = minh = 1000000;
01319     maxw = maxh = 0;
01320     n = pixaGetCount(pixa);
01321     for (i = 0; i < n; i++) {
01322         pix = pixaGetPix(pixa, i, L_CLONE);
01323         w = pixGetWidth(pix);
01324         h = pixGetHeight(pix);
01325         if (w < minw)
01326             minw = w;
01327         if (h < minh)
01328             minh = h;
01329         if (w > maxw)
01330             maxw = w;
01331         if (h > maxh)
01332             maxh = h;
01333         pixDestroy(&pix);
01334     }
01335 
01336     if (pminw) *pminw = minw;
01337     if (pminh) *pminh = minh;
01338     if (pmaxw) *pmaxw = maxw;
01339     if (pmaxh) *pmaxh = maxh;
01340 
01341     return 0;
01342 }
01343 
01344 
01345 /*!
01346  *  pixaClipToPix()
01347  *
01348  *      Input:  pixas
01349  *              pixs
01350  *      Return: pixad, or null on error
01351  *
01352  *  Notes:
01353  *      (1) This is intended for use in situations where pixas
01354  *          was originally generated from the input pixs.
01355  *      (2) Returns a pixad where each pix in pixas is ANDed
01356  *          with its associated region of the input pixs.  This
01357  *          region is specified by the the box that is associated
01358  *          with the pix.
01359  *      (3) In a typical application of this function, pixas has
01360  *          a set of region masks, so this generates a pixa of
01361  *          the parts of pixs that correspond to each region
01362  *          mask component, along with the bounding box for
01363  *          the region.
01364  */
01365 PIXA *
01366 pixaClipToPix(PIXA  *pixas,
01367               PIX   *pixs)
01368 {
01369 l_int32  i, n;
01370 BOX     *box;
01371 PIX     *pix, *pixc;
01372 PIXA    *pixad;
01373 
01374     PROCNAME("pixaClipToPix");
01375 
01376     if (!pixas)
01377         return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL);
01378     if (!pixs)
01379         return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL);
01380 
01381     n = pixaGetCount(pixas);
01382     if ((pixad = pixaCreate(n)) == NULL)
01383         return (PIXA *)ERROR_PTR("pixad not made", procName, NULL);
01384 
01385     for (i = 0; i < n; i++) {
01386         pix = pixaGetPix(pixas, i, L_CLONE);
01387         box = pixaGetBox(pixas, i, L_COPY);
01388         pixc = pixClipRectangle(pixs, box, NULL);
01389         pixAnd(pixc, pixc, pix);
01390         pixaAddPix(pixad, pixc, L_INSERT);
01391         pixaAddBox(pixad, box, L_INSERT);
01392         pixDestroy(&pix);
01393     }
01394 
01395     return pixad;
01396 }
01397     
01398 
01399 /*!
01400  *  pixaAnyColormaps()
01401  *
01402  *      Input:  pixa
01403  *              &hascmap (<return> 1 if any pix has a colormap; 0 otherwise)
01404  *      Return: 0 if OK; 1 on error
01405  */
01406 l_int32
01407 pixaAnyColormaps(PIXA     *pixa,
01408                  l_int32  *phascmap)
01409 {
01410 l_int32   i, n;
01411 PIX      *pix;
01412 PIXCMAP  *cmap;
01413 
01414     PROCNAME("pixaAnyColormaps");
01415 
01416     if (!phascmap)
01417         return ERROR_INT("&hascmap not defined", procName, 1);
01418     *phascmap = 0;
01419     if (!pixa)
01420         return ERROR_INT("pixa not defined", procName, 1);
01421 
01422     n = pixaGetCount(pixa);
01423     for (i = 0; i < n; i++) {
01424         pix = pixaGetPix(pixa, i, L_CLONE);
01425         cmap = pixGetColormap(pix);
01426         pixDestroy(&pix);
01427         if (cmap) {
01428             *phascmap = 1;
01429             return 0;
01430         }
01431     }
01432 
01433     return 0;
01434 }
01435 
01436 
01437 /*!
01438  *  pixaGetDepthInfo()
01439  *
01440  *      Input:  pixa
01441  *              &maxdepth (<optional return> max pixel depth of pix in pixa)
01442  *              &same (<optional return> true if all depths are equal)
01443  *      Return: 0 if OK; 1 on error
01444  */
01445 l_int32
01446 pixaGetDepthInfo(PIXA     *pixa,
01447                  l_int32  *pmaxdepth,
01448                  l_int32  *psame)
01449 {
01450 l_int32  i, n, d, d0;
01451 l_int32  maxd, same;  /* depth info */
01452 
01453     PROCNAME("pixaGetDepthInfo");
01454 
01455     if (!pmaxdepth && !psame) return 0;
01456     if (pmaxdepth) *pmaxdepth = 0;
01457     if (psame) *psame = TRUE;
01458     if (!pixa)
01459         return ERROR_INT("pixa not defined", procName, 1);
01460     if ((n = pixaGetCount(pixa)) == 0)
01461         return ERROR_INT("pixa is empty", procName, 1);
01462 
01463     same = TRUE;
01464     maxd = 0;
01465     for (i = 0; i < n; i++) {
01466         pixaGetPixDimensions(pixa, i, NULL, NULL, &d);
01467         if (i == 0)
01468             d0 = d;
01469         else if (d != d0)
01470             same = FALSE;
01471         if (d > maxd) maxd = d;
01472     }
01473 
01474     if (pmaxdepth) *pmaxdepth = maxd;
01475     if (psame) *psame = same;
01476     return 0;
01477 }
01478 
01479 
01480 /*!
01481  *  pixaEqual()
01482  *
01483  *      Input:  pixa1
01484  *              pixa2
01485  *              maxdist
01486  *              &naindex (<optional return> index array of correspondences
01487  *              &same (<return> 1 if equal; 0 otherwise)
01488  *      Return  0 if OK, 1 on error
01489  *
01490  *  Notes:
01491  *      (1) The two pixa are the "same" if they contain the same
01492  *          boxa and the same ordered set of pix.  However, if they
01493  *          have boxa, the pix in each pixa can differ in ordering
01494  *          by an amount given by the parameter @maxdist.  If they
01495  *          don't have a boxa, the @maxdist parameter is ignored,
01496  *          and the ordering must be identical.
01497  *      (2) This applies only to boxa geometry, pixels and ordering;
01498  *          other fields in the pix are ignored.
01499  *      (3) naindex[i] gives the position of the box in pixa2 that
01500  *          corresponds to box i in pixa1.  It is only returned if the
01501  *          pixa have boxa and the boxa are equal.
01502  *      (4) In situations where the ordering is very different, so that
01503  *          a large @maxdist is required for "equality", this should be
01504  *          implemented with a hash function for efficiency.
01505  */
01506 l_int32
01507 pixaEqual(PIXA     *pixa1,
01508           PIXA     *pixa2,
01509           l_int32   maxdist,
01510           NUMA    **pnaindex,
01511           l_int32  *psame)
01512 {
01513 l_int32   i, j, n, same, sameboxa;
01514 BOXA     *boxa1, *boxa2;
01515 NUMA     *na;
01516 PIX      *pix1, *pix2;
01517 
01518     PROCNAME("pixaEqual");
01519 
01520     if (!psame)
01521         return ERROR_INT("&same not defined", procName, 1);
01522     *psame = 0;
01523     sameboxa = 0;
01524     na = NULL;
01525     if (!pixa1 || !pixa2)
01526         return ERROR_INT("pixa1 and pixa2 not both defined", procName, 1);
01527     n = pixaGetCount(pixa1);
01528     if (n != pixaGetCount(pixa2))
01529         return 0;
01530     boxa1 = pixaGetBoxa(pixa1, L_CLONE);
01531     boxa2 = pixaGetBoxa(pixa2, L_CLONE);
01532     if (!boxa1 && !boxa2)
01533         maxdist = 0;  /* exact ordering required */
01534     if (boxa1 && !boxa2) {
01535         boxaDestroy(&boxa1);
01536         return 0;
01537     }
01538     if (!boxa1 && boxa2) {
01539         boxaDestroy(&boxa2);
01540         return 0;
01541     }
01542     if (boxa1 && boxa2) {
01543         boxaEqual(boxa1, boxa2, maxdist, &na, &sameboxa);
01544         boxaDestroy(&boxa1);
01545         boxaDestroy(&boxa2);
01546         if (!sameboxa) {
01547             numaDestroy(&na);
01548             return 0;
01549         }
01550     }
01551 
01552     for (i = 0; i < n; i++) {
01553         pix1 = pixaGetPix(pixa1, i, L_CLONE);
01554         if (na)
01555             numaGetIValue(na, i, &j);
01556         else
01557             j = i;
01558         pix2 = pixaGetPix(pixa2, j, L_CLONE);
01559         pixEqual(pix1, pix2, &same);
01560         pixDestroy(&pix1);
01561         pixDestroy(&pix2);
01562         if (!same) {
01563             numaDestroy(&na);
01564             return 0;
01565         }
01566     }
01567 
01568     *psame = 1;
01569     if (pnaindex)
01570         *pnaindex = na;
01571     else
01572         numaDestroy(&na);
01573     return 0;
01574 }
01575 
01576 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines