| /*====================================================================* |
| - Copyright (C) 2001 Leptonica. All rights reserved. |
| - This software is distributed in the hope that it will be |
| - useful, but with NO WARRANTY OF ANY KIND. |
| - No author or distributor accepts responsibility to anyone for the |
| - consequences of using this software, or for whether it serves any |
| - particular purpose or works at all, unless he or she says so in |
| - writing. Everyone is granted permission to copy, modify and |
| - redistribute this source code, for commercial or non-commercial |
| - purposes, with the following restrictions: (1) the origin of this |
| - source code must not be misrepresented; (2) modified versions must |
| - be plainly marked as such; and (3) this notice may not be removed |
| - or altered from any source or modified source distribution. |
| *====================================================================*/ |
| |
| /* |
| * pixafunc1.c |
| * |
| * Filters |
| * PIX *pixSelectBySize() |
| * PIXA *pixaSelectBySize() |
| * PIX *pixSelectByAreaPerimRatio() |
| * PIXA *pixaSelectByAreaPerimRatio() |
| * PIX *pixSelectByAreaFraction() |
| * PIXA *pixaSelectByAreaFraction() |
| * PIX *pixSelectByWidthHeightRatio() |
| * PIXA *pixaSelectByWidthHeightRatio() |
| * PIXA *pixaSelectWithIndicator() |
| * l_int32 pixRemoveWithIndicator() |
| * l_int32 pixAddWithIndicator() |
| * |
| * Sort functions |
| * PIXA *pixaSort() |
| * PIXA *pixaBinSort() |
| * PIXA *pixaSortByIndex() |
| * PIXAA *pixaSort2dByIndex() |
| * |
| * Miscellaneous |
| * PIXA *pixaaFlattenToPixa() |
| * l_int32 pixaSizeRange() |
| * PIXA *pixaClipToPix() |
| * l_int32 pixaAnyColormaps() |
| * l_int32 pixaEqual() |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include "allheaders.h" |
| |
| /* For more than this number of c.c. in a binarized image of |
| * semi-perimeter (w + h) about 5000 or less, the O(n) binsort |
| * is faster than the O(nlogn) shellsort. */ |
| static const l_int32 MIN_COMPS_FOR_BIN_SORT = 500; |
| |
| |
| /*---------------------------------------------------------------------* |
| * Filters * |
| *---------------------------------------------------------------------*/ |
| /* |
| * These filters work on the connected components of 1 bpp images. |
| * They are typically used on pixa that have been generated from a Pix |
| * using pixConnComp(), so that the corresponding Boxa is available. |
| * |
| * The filters remove or retain c.c. based on these properties: |
| * (a) size [pixaFindDimensions()] |
| * (b) area-to-perimeter ratio [pixaFindAreaPerimRatio()] |
| * (c) foreground area as a fraction of bounding box area (w * h) |
| * [pixaFindForegroundArea()] |
| * (d) number of foreground pixels [pixaCountPixels()] |
| * (e) width/height aspect ratio [pixFindWidthHeightRatio()] |
| * |
| * We provide two different high-level interfaces: |
| * (1) Functions that use one of the filters on either |
| * a pix or the pixa of components. |
| * (2) A general method that generates numas of indicator functions, |
| * logically combines them, and efficiently removes or adds |
| * the selected components. |
| * |
| * For interface (1), the filtering is performed with a single function call. |
| * This is the easiest way to do simple filtering. These functions |
| * are named pixSelectBy*() and pixaSelectBy*(), where the '*' is one of: |
| * Size |
| * AreaPerimRatio |
| * AreaFraction |
| * WidthHeightRatio |
| * |
| * For more complicated filtering, use the general method (2). |
| * The numa indicator functions for a pixa are generated by these functions: |
| * pixaFindDimensions() |
| * pixaFindAreaPerimRatio() |
| * pixaFindAreaFraction() |
| * pixaCountPixels() |
| * pixaFindWidthHeightRatio() |
| * pixaFindWidthHeightProduct() |
| * |
| * Here is an illustration using the general method. Suppose you want |
| * all 8-connected components that have a height greater than 40 pixels, |
| * a width not more than 30 pixels, between 150 and 300 fg pixels, |
| * and an area-to-perimeter ratio between 2.5 and 4.0. |
| * |
| * // Generate the pixa of 8 cc pieces. |
| * boxa = pixConnComp(pixs, &pixa, 8); |
| * |
| * // Extract the data we need about each component. |
| * pixaFindDimensions(pixa, &naw, &nah); |
| * nas = pixaCountPixels(pixa); |
| * nar = pixaFindAreaPerimRatio(pixa); |
| * |
| * // Build the indicator arrays for the set of components, |
| * // based on thresholds and selection criteria. |
| * na1 = numaMakeThresholdIndicator(nah, 40, L_SELECT_IF_GT); |
| * na2 = numaMakeThresholdIndicator(naw, 30, L_SELECT_IF_LTE); |
| * na3 = numaMakeThresholdIndicator(nas, 150, L_SELECT_IF_GTE); |
| * na4 = numaMakeThresholdIndicator(nas, 300, L_SELECT_IF_LTE); |
| * na5 = numaMakeThresholdIndicator(nar, 2.5, L_SELECT_IF_GTE); |
| * na6 = numaMakeThresholdIndicator(nar, 4.0, L_SELECT_IF_LGE); |
| * |
| * // Combine the indicator arrays logically to find |
| * // the components that will be retained. |
| * nad = numaLogicalOp(NULL, na1, na2, L_INTERSECTION); |
| * numaLogicalOp(nad, nad, na3, L_INTERSECTION); |
| * numaLogicalOp(nad, nad, na4, L_INTERSECTION); |
| * numaLogicalOp(nad, nad, na5, L_INTERSECTION); |
| * numaLogicalOp(nad, nad, na6, L_INTERSECTION); |
| * |
| * // Invert to get the components that will be removed. |
| * numaInvert(nad, nad); |
| * |
| * // Remove the components, in-place. |
| * pixRemoveWithIndicator(pixs, pixa, nad); |
| */ |
| |
| |
| /*! |
| * pixSelectBySize() |
| * |
| * Input: pixs (1 bpp) |
| * width, height (threshold dimensions) |
| * connectivity (4 or 8) |
| * type (L_SELECT_WIDTH, L_SELECT_HEIGHT, |
| * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH) |
| * relation (L_SELECT_IF_LT, L_SELECT_IF_GT, |
| * L_SELECT_IF_LTE, L_SELECT_IF_GTE) |
| * &changed (<optional return> 1 if changed; 0 otherwise) |
| * Return: filtered pixd, or null on error |
| * |
| * Notes: |
| * (1) The args specify constraints on the size of the |
| * components that are kept. |
| * (2) If unchanged, returns a copy of pixs. Otherwise, |
| * returns a new pix with the filtered components. |
| * (3) If the selection type is L_SELECT_WIDTH, the input |
| * height is ignored, and v.v. |
| * (4) To keep small components, use relation = L_SELECT_IF_LT or |
| * L_SELECT_IF_LTE. |
| * To keep large components, use relation = L_SELECT_IF_GT or |
| * L_SELECT_IF_GTE. |
| */ |
| PIX * |
| pixSelectBySize(PIX *pixs, |
| l_int32 width, |
| l_int32 height, |
| l_int32 connectivity, |
| l_int32 type, |
| l_int32 relation, |
| l_int32 *pchanged) |
| { |
| l_int32 w, h, empty, changed, count; |
| BOXA *boxa; |
| PIX *pixd; |
| PIXA *pixas, *pixad; |
| |
| PROCNAME("pixSelectBySize"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (connectivity != 4 && connectivity != 8) |
| return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); |
| if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && |
| type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) |
| return (PIX *)ERROR_PTR("invalid type", procName, NULL); |
| if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && |
| relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) |
| return (PIX *)ERROR_PTR("invalid relation", procName, NULL); |
| if (pchanged) *pchanged = FALSE; |
| |
| /* Check if any components exist */ |
| pixZero(pixs, &empty); |
| if (empty) |
| return pixCopy(NULL, pixs); |
| |
| /* Identify and select the components */ |
| boxa = pixConnComp(pixs, &pixas, connectivity); |
| pixad = pixaSelectBySize(pixas, width, height, type, relation, &changed); |
| boxaDestroy(&boxa); |
| pixaDestroy(&pixas); |
| |
| /* Render the result */ |
| if (!changed) { |
| pixaDestroy(&pixad); |
| return pixCopy(NULL, pixs); |
| } |
| else { |
| if (pchanged) *pchanged = TRUE; |
| pixGetDimensions(pixs, &w, &h, NULL); |
| count = pixaGetCount(pixad); |
| if (count == 0) /* return empty pix */ |
| pixd = pixCreateTemplate(pixs); |
| else |
| pixd = pixaDisplay(pixad, w, h); |
| pixaDestroy(&pixad); |
| return pixd; |
| } |
| } |
| |
| |
| /*! |
| * pixaSelectBySize() |
| * |
| * Input: pixas |
| * width, height (threshold dimensions) |
| * type (L_SELECT_WIDTH, L_SELECT_HEIGHT, |
| * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH) |
| * relation (L_SELECT_IF_LT, L_SELECT_IF_GT, |
| * L_SELECT_IF_LTE, L_SELECT_IF_GTE) |
| * &changed (<optional return> 1 if changed; 0 otherwise) |
| * Return: pixad, or null on error |
| * |
| * Notes: |
| * (1) The args specify constraints on the size of the |
| * components that are kept. |
| * (2) Uses pix and box clones in the new pixa. |
| * (3) If the selection type is L_SELECT_WIDTH, the input |
| * height is ignored, and v.v. |
| * (4) To keep small components, use relation = L_SELECT_IF_LT or |
| * L_SELECT_IF_LTE. |
| * To keep large components, use relation = L_SELECT_IF_GT or |
| * L_SELECT_IF_GTE. |
| */ |
| PIXA * |
| pixaSelectBySize(PIXA *pixas, |
| l_int32 width, |
| l_int32 height, |
| l_int32 type, |
| l_int32 relation, |
| l_int32 *pchanged) |
| { |
| BOXA *boxa; |
| NUMA *na; |
| PIXA *pixad; |
| |
| PROCNAME("pixaSelectBySize"); |
| |
| if (!pixas) |
| return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); |
| if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && |
| type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) |
| return (PIXA *)ERROR_PTR("invalid type", procName, NULL); |
| if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && |
| relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) |
| return (PIXA *)ERROR_PTR("invalid relation", procName, NULL); |
| |
| /* Compute the indicator array for saving components */ |
| boxa = pixaGetBoxa(pixas, L_CLONE); |
| na = boxaMakeSizeIndicator(boxa, width, height, type, relation); |
| boxaDestroy(&boxa); |
| |
| /* Filter to get output */ |
| pixad = pixaSelectWithIndicator(pixas, na, pchanged); |
| |
| numaDestroy(&na); |
| return pixad; |
| } |
| |
| |
| /*! |
| * pixSelectByAreaPerimRatio() |
| * |
| * Input: pixs (1 bpp) |
| * thresh (threshold ratio of interior/boundary pixels) |
| * connectivity (4 or 8) |
| * type (L_SELECT_IF_LT, L_SELECT_IF_GT, |
| * L_SELECT_IF_LTE, L_SELECT_IF_GTE) |
| * &changed (<optional return> 1 if changed; 0 if clone returned) |
| * Return: pixd, or null on error |
| * |
| * Notes: |
| * (1) The args specify constraints on the size of the |
| * components that are kept. |
| * (2) If unchanged, returns a copy of pixs. Otherwise, |
| * returns a new pix with the filtered components. |
| * (3) This filters "thin" components, where a thin component |
| * is defined to have a ratio of interior to boundary pixels |
| * that is smaller than a given threshold value. |
| * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save the thin |
| * components, and L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. |
| */ |
| PIX * |
| pixSelectByAreaPerimRatio(PIX *pixs, |
| l_float32 thresh, |
| l_int32 connectivity, |
| l_int32 type, |
| l_int32 *pchanged) |
| { |
| l_int32 w, h, empty, changed, count; |
| BOXA *boxa; |
| PIX *pixd; |
| PIXA *pixas, *pixad; |
| |
| PROCNAME("pixSelectByAreaPerimRatio"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (connectivity != 4 && connectivity != 8) |
| return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); |
| if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && |
| type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) |
| return (PIX *)ERROR_PTR("invalid type", procName, NULL); |
| if (pchanged) *pchanged = FALSE; |
| |
| /* Check if any components exist */ |
| pixZero(pixs, &empty); |
| if (empty) |
| return pixCopy(NULL, pixs); |
| |
| /* Filter thin components */ |
| boxa = pixConnComp(pixs, &pixas, connectivity); |
| pixad = pixaSelectByAreaPerimRatio(pixas, thresh, type, &changed); |
| boxaDestroy(&boxa); |
| pixaDestroy(&pixas); |
| |
| /* Render the result */ |
| if (!changed) { |
| pixaDestroy(&pixad); |
| return pixCopy(NULL, pixs); |
| } |
| else { |
| if (pchanged) *pchanged = TRUE; |
| pixGetDimensions(pixs, &w, &h, NULL); |
| count = pixaGetCount(pixad); |
| if (count == 0) /* return empty pix */ |
| pixd = pixCreateTemplate(pixs); |
| else |
| pixd = pixaDisplay(pixad, w, h); |
| pixaDestroy(&pixad); |
| return pixd; |
| } |
| } |
| |
| |
| /*! |
| * pixaSelectByAreaPerimRatio() |
| * |
| * Input: pixas |
| * thresh (threshold ratio of interior/boundary pixels) |
| * type (L_SELECT_IF_LT, L_SELECT_IF_GT, |
| * L_SELECT_IF_LTE, L_SELECT_IF_GTE) |
| * &changed (<optional return> 1 if changed; 0 if clone returned) |
| * Return: pixad, or null on error |
| * |
| * Notes: |
| * (1) Returns a pixa clone if no components are removed. |
| * (2) Uses pix and box clones in the new pixa. |
| * (3) We define a thin component here to be one with a ratio |
| * of interior to boundary pixels that is smaller than a given |
| * threshold value. |
| * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save the thin |
| * components, and L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. |
| */ |
| PIXA * |
| pixaSelectByAreaPerimRatio(PIXA *pixas, |
| l_float32 thresh, |
| l_int32 type, |
| l_int32 *pchanged) |
| { |
| NUMA *na, *nai; |
| PIXA *pixad; |
| |
| PROCNAME("pixaSelectByAreaPerimRatio"); |
| |
| if (!pixas) |
| return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); |
| if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && |
| type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) |
| return (PIXA *)ERROR_PTR("invalid type", procName, NULL); |
| |
| /* Compute component ratios. */ |
| na = pixaFindAreaPerimRatio(pixas); |
| |
| /* Generate indicator array for elements to be saved. */ |
| nai = numaMakeThresholdIndicator(na, thresh, type); |
| numaDestroy(&na); |
| |
| /* Filter to get output */ |
| pixad = pixaSelectWithIndicator(pixas, nai, pchanged); |
| |
| numaDestroy(&nai); |
| return pixad; |
| } |
| |
| |
| /*! |
| * pixSelectByAreaFraction() |
| * |
| * Input: pixs (1 bpp) |
| * thresh (threshold ratio of fg pixels to (w * h)) |
| * connectivity (4 or 8) |
| * type (L_SELECT_IF_LT, L_SELECT_IF_GT, |
| * L_SELECT_IF_LTE, L_SELECT_IF_GTE) |
| * &changed (<optional return> 1 if changed; 0 if clone returned) |
| * Return: pixd, or null on error |
| * |
| * Notes: |
| * (1) The args specify constraints on the amount of foreground |
| * coverage of the components that are kept. |
| * (2) If unchanged, returns a copy of pixs. Otherwise, |
| * returns a new pix with the filtered components. |
| * (3) This filters components based on the fraction of fg pixels |
| * of the component in its bounding box. |
| * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components |
| * with less than the threshold fraction of foreground, and |
| * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. |
| */ |
| PIX * |
| pixSelectByAreaFraction(PIX *pixs, |
| l_float32 thresh, |
| l_int32 connectivity, |
| l_int32 type, |
| l_int32 *pchanged) |
| { |
| l_int32 w, h, empty, changed, count; |
| BOXA *boxa; |
| PIX *pixd; |
| PIXA *pixas, *pixad; |
| |
| PROCNAME("pixSelectByAreaFraction"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (connectivity != 4 && connectivity != 8) |
| return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); |
| if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && |
| type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) |
| return (PIX *)ERROR_PTR("invalid type", procName, NULL); |
| if (pchanged) *pchanged = FALSE; |
| |
| /* Check if any components exist */ |
| pixZero(pixs, &empty); |
| if (empty) |
| return pixCopy(NULL, pixs); |
| |
| /* Filter components */ |
| boxa = pixConnComp(pixs, &pixas, connectivity); |
| pixad = pixaSelectByAreaFraction(pixas, thresh, type, &changed); |
| boxaDestroy(&boxa); |
| pixaDestroy(&pixas); |
| |
| /* Render the result */ |
| if (!changed) { |
| pixaDestroy(&pixad); |
| return pixCopy(NULL, pixs); |
| } |
| else { |
| if (pchanged) *pchanged = TRUE; |
| pixGetDimensions(pixs, &w, &h, NULL); |
| count = pixaGetCount(pixad); |
| if (count == 0) /* return empty pix */ |
| pixd = pixCreateTemplate(pixs); |
| else |
| pixd = pixaDisplay(pixad, w, h); |
| pixaDestroy(&pixad); |
| return pixd; |
| } |
| } |
| |
| |
| /*! |
| * pixaSelectByAreaFraction() |
| * |
| * Input: pixas |
| * thresh (threshold ratio of fg pixels to (w * h)) |
| * type (L_SELECT_IF_LT, L_SELECT_IF_GT, |
| * L_SELECT_IF_LTE, L_SELECT_IF_GTE) |
| * &changed (<optional return> 1 if changed; 0 if clone returned) |
| * Return: pixad, or null on error |
| * |
| * Notes: |
| * (1) Returns a pixa clone if no components are removed. |
| * (2) Uses pix and box clones in the new pixa. |
| * (3) This filters components based on the fraction of fg pixels |
| * of the component in its bounding box. |
| * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components |
| * with less than the threshold fraction of foreground, and |
| * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. |
| */ |
| PIXA * |
| pixaSelectByAreaFraction(PIXA *pixas, |
| l_float32 thresh, |
| l_int32 type, |
| l_int32 *pchanged) |
| { |
| NUMA *na, *nai; |
| PIXA *pixad; |
| |
| PROCNAME("pixaSelectByAreaFraction"); |
| |
| if (!pixas) |
| return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); |
| if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && |
| type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) |
| return (PIXA *)ERROR_PTR("invalid type", procName, NULL); |
| |
| /* Compute component ratios. */ |
| na = pixaFindAreaFraction(pixas); |
| |
| /* Generate indicator array for elements to be saved. */ |
| nai = numaMakeThresholdIndicator(na, thresh, type); |
| numaDestroy(&na); |
| |
| /* Filter to get output */ |
| pixad = pixaSelectWithIndicator(pixas, nai, pchanged); |
| |
| numaDestroy(&nai); |
| return pixad; |
| } |
| |
| |
| /*! |
| * pixSelectByWidthHeightRatio() |
| * |
| * Input: pixs (1 bpp) |
| * thresh (threshold ratio of width/height) |
| * connectivity (4 or 8) |
| * type (L_SELECT_IF_LT, L_SELECT_IF_GT, |
| * L_SELECT_IF_LTE, L_SELECT_IF_GTE) |
| * &changed (<optional return> 1 if changed; 0 if clone returned) |
| * Return: pixd, or null on error |
| * |
| * Notes: |
| * (1) The args specify constraints on the width-to-height ratio |
| * for components that are kept. |
| * (2) If unchanged, returns a copy of pixs. Otherwise, |
| * returns a new pix with the filtered components. |
| * (3) This filters components based on the width-to-height ratios. |
| * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components |
| * with less than the threshold ratio, and |
| * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. |
| */ |
| PIX * |
| pixSelectByWidthHeightRatio(PIX *pixs, |
| l_float32 thresh, |
| l_int32 connectivity, |
| l_int32 type, |
| l_int32 *pchanged) |
| { |
| l_int32 w, h, empty, changed, count; |
| BOXA *boxa; |
| PIX *pixd; |
| PIXA *pixas, *pixad; |
| |
| PROCNAME("pixSelectByWidthHeightRatio"); |
| |
| if (!pixs) |
| return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); |
| if (connectivity != 4 && connectivity != 8) |
| return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); |
| if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && |
| type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) |
| return (PIX *)ERROR_PTR("invalid type", procName, NULL); |
| if (pchanged) *pchanged = FALSE; |
| |
| /* Check if any components exist */ |
| pixZero(pixs, &empty); |
| if (empty) |
| return pixCopy(NULL, pixs); |
| |
| /* Filter components */ |
| boxa = pixConnComp(pixs, &pixas, connectivity); |
| pixad = pixaSelectByWidthHeightRatio(pixas, thresh, type, &changed); |
| boxaDestroy(&boxa); |
| pixaDestroy(&pixas); |
| |
| /* Render the result */ |
| if (!changed) { |
| pixaDestroy(&pixad); |
| return pixCopy(NULL, pixs); |
| } |
| else { |
| if (pchanged) *pchanged = TRUE; |
| pixGetDimensions(pixs, &w, &h, NULL); |
| count = pixaGetCount(pixad); |
| if (count == 0) /* return empty pix */ |
| pixd = pixCreateTemplate(pixs); |
| else |
| pixd = pixaDisplay(pixad, w, h); |
| pixaDestroy(&pixad); |
| return pixd; |
| } |
| } |
| |
| |
| /*! |
| * pixaSelectByWidthHeightRatio() |
| * |
| * Input: pixas |
| * thresh (threshold ratio of width/height) |
| * type (L_SELECT_IF_LT, L_SELECT_IF_GT, |
| * L_SELECT_IF_LTE, L_SELECT_IF_GTE) |
| * &changed (<optional return> 1 if changed; 0 if clone returned) |
| * Return: pixad, or null on error |
| * |
| * Notes: |
| * (1) Returns a pixa clone if no components are removed. |
| * (2) Uses pix and box clones in the new pixa. |
| * (3) This filters components based on the width-to-height ratio |
| * of each pix. |
| * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components |
| * with less than the threshold ratio, and |
| * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. |
| */ |
| PIXA * |
| pixaSelectByWidthHeightRatio(PIXA *pixas, |
| l_float32 thresh, |
| l_int32 type, |
| l_int32 *pchanged) |
| { |
| NUMA *na, *nai; |
| PIXA *pixad; |
| |
| PROCNAME("pixaSelectByWidthHeightRatio"); |
| |
| if (!pixas) |
| return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); |
| if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && |
| type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) |
| return (PIXA *)ERROR_PTR("invalid type", procName, NULL); |
| |
| /* Compute component ratios. */ |
| na = pixaFindWidthHeightRatio(pixas); |
| |
| /* Generate indicator array for elements to be saved. */ |
| nai = numaMakeThresholdIndicator(na, thresh, type); |
| numaDestroy(&na); |
| |
| /* Filter to get output */ |
| pixad = pixaSelectWithIndicator(pixas, nai, pchanged); |
| |
| numaDestroy(&nai); |
| return pixad; |
| } |
| |
| |
| /*! |
| * pixaSelectWithIndicator() |
| * |
| * Input: pixas |
| * na (indicator numa) |
| * &changed (<optional return> 1 if changed; 0 if clone returned) |
| * Return: pixad, or null on error |
| * |
| * Notes: |
| * (1) Returns a pixa clone if no components are removed. |
| * (2) Uses pix and box clones in the new pixa. |
| * (3) The indicator numa has values 0 (ignore) and 1 (accept). |
| */ |
| PIXA * |
| pixaSelectWithIndicator(PIXA *pixas, |
| NUMA *na, |
| l_int32 *pchanged) |
| { |
| l_int32 i, n, ival, nsave; |
| BOX *box; |
| PIX *pixt; |
| PIXA *pixad; |
| |
| PROCNAME("pixaSelectWithIndicator"); |
| |
| if (!pixas) |
| return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); |
| if (!na) |
| return (PIXA *)ERROR_PTR("na not defined", procName, NULL); |
| |
| nsave = 0; |
| n = numaGetCount(na); |
| for (i = 0; i < n; i++) { |
| numaGetIValue(na, i, &ival); |
| if (ival == 1) nsave++; |
| } |
| |
| if (nsave == n) { |
| if (pchanged) *pchanged = FALSE; |
| return pixaCopy(pixas, L_CLONE); |
| } |
| if (pchanged) *pchanged = TRUE; |
| pixad = pixaCreate(nsave); |
| for (i = 0; i < n; i++) { |
| numaGetIValue(na, i, &ival); |
| if (ival == 0) continue; |
| pixt = pixaGetPix(pixas, i, L_CLONE); |
| box = pixaGetBox(pixas, i, L_CLONE); |
| pixaAddPix(pixad, pixt, L_INSERT); |
| pixaAddBox(pixad, box, L_INSERT); |
| } |
| |
| return pixad; |
| } |
| |
| |
| /*! |
| * pixRemoveWithIndicator() |
| * |
| * Input: pixs (1 bpp pix from which components are removed; in-place) |
| * pixa (of connected components in pixs) |
| * na (numa indicator: remove components corresponding to 1s) |
| * Return: 0 if OK, 1 on error |
| * |
| * Notes: |
| * (1) This complements pixAddWithIndicator(). Here, the selected |
| * components are set subtracted from pixs. |
| */ |
| l_int32 |
| pixRemoveWithIndicator(PIX *pixs, |
| PIXA *pixa, |
| NUMA *na) |
| { |
| l_int32 i, n, ival, x, y, w, h; |
| BOX *box; |
| PIX *pix; |
| |
| PROCNAME("pixRemoveWithIndicator"); |
| |
| if (!pixs) |
| return ERROR_INT("pixs not defined", procName, 1); |
| if (!pixa) |
| return ERROR_INT("pixa not defined", procName, 1); |
| if (!na) |
| return ERROR_INT("na not defined", procName, 1); |
| n = pixaGetCount(pixa); |
| if (n != numaGetCount(na)) |
| return ERROR_INT("pixa and na sizes not equal", procName, 1); |
| |
| for (i = 0; i < n; i++) { |
| numaGetIValue(na, i, &ival); |
| if (ival == 1) { |
| pix = pixaGetPix(pixa, i, L_CLONE); |
| box = pixaGetBox(pixa, i, L_CLONE); |
| boxGetGeometry(box, &x, &y, &w, &h); |
| pixRasterop(pixs, x, y, w, h, PIX_DST & PIX_NOT(PIX_SRC), |
| pix, 0, 0); |
| boxDestroy(&box); |
| pixDestroy(&pix); |
| } |
| } |
| |
| return 0; |
| } |
| |
| |
| /*! |
| * pixAddWithIndicator() |
| * |
| * Input: pixs (1 bpp pix from which components are added; in-place) |
| * pixa (of connected components, some of which will be put |
| * into pixs) |
| * na (numa indicator: add components corresponding to 1s) |
| * Return: 0 if OK, 1 on error |
| * |
| * Notes: |
| * (1) This complements pixRemoveWithIndicator(). Here, the selected |
| * components are added to pixs. |
| */ |
| l_int32 |
| pixAddWithIndicator(PIX *pixs, |
| PIXA *pixa, |
| NUMA *na) |
| { |
| l_int32 i, n, ival, x, y, w, h; |
| BOX *box; |
| PIX *pix; |
| |
| PROCNAME("pixAddWithIndicator"); |
| |
| if (!pixs) |
| return ERROR_INT("pixs not defined", procName, 1); |
| if (!pixa) |
| return ERROR_INT("pixa not defined", procName, 1); |
| if (!na) |
| return ERROR_INT("na not defined", procName, 1); |
| n = pixaGetCount(pixa); |
| if (n != numaGetCount(na)) |
| return ERROR_INT("pixa and na sizes not equal", procName, 1); |
| |
| for (i = 0; i < n; i++) { |
| numaGetIValue(na, i, &ival); |
| if (ival == 1) { |
| pix = pixaGetPix(pixa, i, L_CLONE); |
| box = pixaGetBox(pixa, i, L_CLONE); |
| boxGetGeometry(box, &x, &y, &w, &h); |
| pixRasterop(pixs, x, y, w, h, PIX_SRC | PIX_DST, pix, 0, 0); |
| boxDestroy(&box); |
| pixDestroy(&pix); |
| } |
| } |
| |
| return 0; |
| } |
| |
| |
| /*---------------------------------------------------------------------* |
| * Sort functions * |
| *---------------------------------------------------------------------*/ |
| /*! |
| * pixaSort() |
| * |
| * Input: pixas |
| * sorttype (L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH, |
| * L_SORT_BY_HEIGHT, L_SORT_BY_MIN_DIMENSION, |
| * L_SORT_BY_MAX_DIMENSION, L_SORT_BY_PERIMETER, |
| * L_SORT_BY_AREA, L_SORT_BY_ASPECT_RATIO) |
| * sortorder (L_SORT_INCREASING, L_SORT_DECREASING) |
| * &naindex (<optional return> index of sorted order into |
| * original array) |
| * copyflag (L_COPY, L_CLONE) |
| * Return: pixad (sorted version of pixas), or null on error |
| * |
| * Notes: |
| * (1) This sorts based on the data in the boxa. If the boxa |
| * count is not the same as the pixa count, this returns an error. |
| * (2) The copyflag refers to the pix and box copies that are |
| * inserted into the sorted pixa. These are either L_COPY |
| * or L_CLONE. |
| */ |
| PIXA * |
| pixaSort(PIXA *pixas, |
| l_int32 sorttype, |
| l_int32 sortorder, |
| NUMA **pnaindex, |
| l_int32 copyflag) |
| { |
| l_int32 i, n, x, y, w, h; |
| BOXA *boxa; |
| NUMA *na, *naindex; |
| PIXA *pixad; |
| |
| PROCNAME("pixaSort"); |
| |
| if (pnaindex) *pnaindex = NULL; |
| if (!pixas) |
| return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); |
| if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && |
| sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && |
| sorttype != L_SORT_BY_MIN_DIMENSION && |
| sorttype != L_SORT_BY_MAX_DIMENSION && |
| sorttype != L_SORT_BY_PERIMETER && |
| sorttype != L_SORT_BY_AREA && |
| sorttype != L_SORT_BY_ASPECT_RATIO) |
| return (PIXA *)ERROR_PTR("invalid sort type", procName, NULL); |
| if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) |
| return (PIXA *)ERROR_PTR("invalid sort order", procName, NULL); |
| if (copyflag != L_COPY && copyflag != L_CLONE) |
| return (PIXA *)ERROR_PTR("invalid copy flag", procName, NULL); |
| |
| if ((boxa = pixas->boxa) == NULL) /* not owned; do not destroy */ |
| return (PIXA *)ERROR_PTR("boxa not found", procName, NULL); |
| n = pixaGetCount(pixas); |
| if (boxaGetCount(boxa) != n) |
| return (PIXA *)ERROR_PTR("boxa and pixa counts differ", procName, NULL); |
| |
| /* Use O(n) binsort if possible */ |
| if (n > MIN_COMPS_FOR_BIN_SORT && |
| ((sorttype == L_SORT_BY_X) || (sorttype == L_SORT_BY_Y) || |
| (sorttype == L_SORT_BY_WIDTH) || (sorttype == L_SORT_BY_HEIGHT) || |
| (sorttype == L_SORT_BY_PERIMETER))) |
| return pixaBinSort(pixas, sorttype, sortorder, pnaindex, copyflag); |
| |
| /* Build up numa of specific data */ |
| if ((na = numaCreate(n)) == NULL) |
| return (PIXA *)ERROR_PTR("na not made", procName, NULL); |
| for (i = 0; i < n; i++) { |
| boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); |
| switch (sorttype) |
| { |
| case L_SORT_BY_X: |
| numaAddNumber(na, x); |
| break; |
| case L_SORT_BY_Y: |
| numaAddNumber(na, y); |
| break; |
| case L_SORT_BY_WIDTH: |
| numaAddNumber(na, w); |
| break; |
| case L_SORT_BY_HEIGHT: |
| numaAddNumber(na, h); |
| break; |
| case L_SORT_BY_MIN_DIMENSION: |
| numaAddNumber(na, L_MIN(w, h)); |
| break; |
| case L_SORT_BY_MAX_DIMENSION: |
| numaAddNumber(na, L_MAX(w, h)); |
| break; |
| case L_SORT_BY_PERIMETER: |
| numaAddNumber(na, w + h); |
| break; |
| case L_SORT_BY_AREA: |
| numaAddNumber(na, w * h); |
| break; |
| case L_SORT_BY_ASPECT_RATIO: |
| numaAddNumber(na, (l_float32)w / (l_float32)h); |
| break; |
| default: |
| L_WARNING("invalid sort type", procName); |
| } |
| } |
| |
| /* Get the sort index for data array */ |
| if ((naindex = numaGetSortIndex(na, sortorder)) == NULL) |
| return (PIXA *)ERROR_PTR("naindex not made", procName, NULL); |
| |
| /* Build up sorted pixa using sort index */ |
| if ((pixad = pixaSortByIndex(pixas, naindex, copyflag)) == NULL) |
| return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); |
| |
| if (pnaindex) |
| *pnaindex = naindex; |
| else |
| numaDestroy(&naindex); |
| numaDestroy(&na); |
| return pixad; |
| } |
| |
| |
| /*! |
| * pixaBinSort() |
| * |
| * Input: pixas |
| * sorttype (L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH, |
| * L_SORT_BY_HEIGHT, L_SORT_BY_PERIMETER) |
| * sortorder (L_SORT_INCREASING, L_SORT_DECREASING) |
| * &naindex (<optional return> index of sorted order into |
| * original array) |
| * copyflag (L_COPY, L_CLONE) |
| * Return: pixad (sorted version of pixas), or null on error |
| * |
| * Notes: |
| * (1) This sorts based on the data in the boxa. If the boxa |
| * count is not the same as the pixa count, this returns an error. |
| * (2) The copyflag refers to the pix and box copies that are |
| * inserted into the sorted pixa. These are either L_COPY |
| * or L_CLONE. |
| * (3) For a large number of boxes (say, greater than 1000), this |
| * O(n) binsort is much faster than the O(nlogn) shellsort. |
| * For 5000 components, this is over 20x faster than boxaSort(). |
| * (4) Consequently, pixaSort() calls this function if it will |
| * likely go much faster. |
| */ |
| PIXA * |
| pixaBinSort(PIXA *pixas, |
| l_int32 sorttype, |
| l_int32 sortorder, |
| NUMA **pnaindex, |
| l_int32 copyflag) |
| { |
| l_int32 i, n, x, y, w, h; |
| BOXA *boxa; |
| NUMA *na, *naindex; |
| PIXA *pixad; |
| |
| PROCNAME("pixaBinSort"); |
| |
| if (pnaindex) *pnaindex = NULL; |
| if (!pixas) |
| return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); |
| if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && |
| sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && |
| sorttype != L_SORT_BY_PERIMETER) |
| return (PIXA *)ERROR_PTR("invalid sort type", procName, NULL); |
| if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) |
| return (PIXA *)ERROR_PTR("invalid sort order", procName, NULL); |
| if (copyflag != L_COPY && copyflag != L_CLONE) |
| return (PIXA *)ERROR_PTR("invalid copy flag", procName, NULL); |
| |
| /* Verify that the pixa and its boxa have the same count */ |
| if ((boxa = pixas->boxa) == NULL) /* not owned; do not destroy */ |
| return (PIXA *)ERROR_PTR("boxa not found", procName, NULL); |
| n = pixaGetCount(pixas); |
| if (boxaGetCount(boxa) != n) |
| return (PIXA *)ERROR_PTR("boxa and pixa counts differ", procName, NULL); |
| |
| /* Generate Numa of appropriate box dimensions */ |
| if ((na = numaCreate(n)) == NULL) |
| return (PIXA *)ERROR_PTR("na not made", procName, NULL); |
| for (i = 0; i < n; i++) { |
| boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); |
| switch (sorttype) |
| { |
| case L_SORT_BY_X: |
| numaAddNumber(na, x); |
| break; |
| case L_SORT_BY_Y: |
| numaAddNumber(na, y); |
| break; |
| case L_SORT_BY_WIDTH: |
| numaAddNumber(na, w); |
| break; |
| case L_SORT_BY_HEIGHT: |
| numaAddNumber(na, h); |
| break; |
| case L_SORT_BY_PERIMETER: |
| numaAddNumber(na, w + h); |
| break; |
| default: |
| L_WARNING("invalid sort type", procName); |
| } |
| } |
| |
| /* Get the sort index for data array */ |
| if ((naindex = numaGetBinSortIndex(na, sortorder)) == NULL) |
| return (PIXA *)ERROR_PTR("naindex not made", procName, NULL); |
| |
| /* Build up sorted pixa using sort index */ |
| if ((pixad = pixaSortByIndex(pixas, naindex, copyflag)) == NULL) |
| return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); |
| |
| if (pnaindex) |
| *pnaindex = naindex; |
| else |
| numaDestroy(&naindex); |
| numaDestroy(&na); |
| return pixad; |
| } |
| |
| |
| /*! |
| * pixaSortByIndex() |
| * |
| * Input: pixas |
| * naindex (na that maps from the new pixa to the input pixa) |
| * copyflag (L_COPY, L_CLONE) |
| * Return: pixad (sorted), or null on error |
| */ |
| PIXA * |
| pixaSortByIndex(PIXA *pixas, |
| NUMA *naindex, |
| l_int32 copyflag) |
| { |
| l_int32 i, n, index; |
| BOX *box; |
| PIX *pix; |
| PIXA *pixad; |
| |
| PROCNAME("pixaSortByIndex"); |
| |
| if (!pixas) |
| return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); |
| if (!naindex) |
| return (PIXA *)ERROR_PTR("naindex not defined", procName, NULL); |
| if (copyflag != L_CLONE && copyflag != L_COPY) |
| return (PIXA *)ERROR_PTR("invalid copyflag", procName, NULL); |
| |
| n = pixaGetCount(pixas); |
| pixad = pixaCreate(n); |
| for (i = 0; i < n; i++) { |
| numaGetIValue(naindex, i, &index); |
| pix = pixaGetPix(pixas, index, copyflag); |
| box = pixaGetBox(pixas, index, copyflag); |
| pixaAddPix(pixad, pix, L_INSERT); |
| pixaAddBox(pixad, box, L_INSERT); |
| } |
| |
| return pixad; |
| } |
| |
| |
| /*! |
| * pixaSort2dByIndex() |
| * |
| * Input: pixas |
| * naa (numaa that maps from the new pixaa to the input pixas) |
| * copyflag (L_CLONE or L_COPY) |
| * Return: pixaa (sorted), or null on error |
| */ |
| PIXAA * |
| pixaSort2dByIndex(PIXA *pixas, |
| NUMAA *naa, |
| l_int32 copyflag) |
| { |
| l_int32 pixtot, ntot, i, j, n, nn, index; |
| BOX *box; |
| NUMA *na; |
| PIX *pix; |
| PIXA *pixa; |
| PIXAA *pixaa; |
| |
| PROCNAME("pixaSort2dByIndex"); |
| |
| if (!pixas) |
| return (PIXAA *)ERROR_PTR("pixas not defined", procName, NULL); |
| if (!naa) |
| return (PIXAA *)ERROR_PTR("naindex not defined", procName, NULL); |
| |
| /* Check counts */ |
| ntot = numaaGetNumberCount(naa); |
| pixtot = pixaGetCount(pixas); |
| if (ntot != pixtot) |
| return (PIXAA *)ERROR_PTR("element count mismatch", procName, NULL); |
| |
| n = numaaGetCount(naa); |
| pixaa = pixaaCreate(n); |
| for (i = 0; i < n; i++) { |
| na = numaaGetNuma(naa, i, L_CLONE); |
| nn = numaGetCount(na); |
| pixa = pixaCreate(nn); |
| for (j = 0; j < nn; j++) { |
| numaGetIValue(na, j, &index); |
| pix = pixaGetPix(pixas, index, copyflag); |
| box = pixaGetBox(pixas, index, copyflag); |
| pixaAddPix(pixa, pix, L_INSERT); |
| pixaAddBox(pixa, box, L_INSERT); |
| } |
| pixaaAddPixa(pixaa, pixa, L_INSERT); |
| numaDestroy(&na); |
| } |
| |
| return pixaa; |
| } |
| |
| |
| |
| /*---------------------------------------------------------------------* |
| * Miscellaneous functions * |
| *---------------------------------------------------------------------*/ |
| /*! |
| * pixaaFlattenToPixa() |
| * |
| * Input: pixaa |
| * &naindex (<optional return> the pixa index in the pixaa) |
| * copyflag (L_COPY or L_CLONE) |
| * Return: pixa, or null on error |
| * |
| * Notes: |
| * (1) This 'flattens' the pixaa to a pixa, taking the pix in |
| * order in the first pixa, then the second, etc. |
| * (2) If &naindex is defined, we generate a Numa that gives, for |
| * each pix in the pixaa, the index of the pixa to which it belongs. |
| */ |
| PIXA * |
| pixaaFlattenToPixa(PIXAA *pixaa, |
| NUMA **pnaindex, |
| l_int32 copyflag) |
| { |
| l_int32 i, j, m, n; |
| BOX *box; |
| NUMA *naindex; |
| PIX *pix; |
| PIXA *pixa, *pixat; |
| |
| PROCNAME("pixaaFlattenToPixa"); |
| |
| if (pnaindex) *pnaindex = NULL; |
| if (!pixaa) |
| return (PIXA *)ERROR_PTR("pixaa not defined", procName, NULL); |
| if (copyflag != L_COPY && copyflag != L_CLONE) |
| return (PIXA *)ERROR_PTR("invalid copyflag", procName, NULL); |
| |
| if (pnaindex) { |
| naindex = numaCreate(0); |
| *pnaindex = naindex; |
| } |
| |
| n = pixaaGetCount(pixaa); |
| pixa = pixaCreate(n); |
| for (i = 0; i < n; i++) { |
| pixat = pixaaGetPixa(pixaa, i, L_CLONE); |
| m = pixaGetCount(pixat); |
| for (j = 0; j < m; j++) { |
| pix = pixaGetPix(pixat, j, copyflag); |
| box = pixaGetBox(pixat, j, copyflag); |
| pixaAddPix(pixa, pix, L_INSERT); |
| pixaAddBox(pixa, box, L_INSERT); |
| if (pnaindex) |
| numaAddNumber(naindex, i); /* save 'row' number */ |
| } |
| pixaDestroy(&pixat); |
| } |
| |
| return pixa; |
| } |
| |
| |
| /*! |
| * pixaSizeRange() |
| * |
| * Input: pixa |
| * &minw, &minh, &maxw, &maxh (<optional return> range of |
| * dimensions of pix in the array) |
| * Return: 0 if OK, 1 on error |
| */ |
| l_int32 |
| pixaSizeRange(PIXA *pixa, |
| l_int32 *pminw, |
| l_int32 *pminh, |
| l_int32 *pmaxw, |
| l_int32 *pmaxh) |
| { |
| l_int32 minw, minh, maxw, maxh, i, n, w, h; |
| PIX *pix; |
| |
| PROCNAME("pixaSizeRange"); |
| |
| if (!pixa) |
| return ERROR_INT("pixa not defined", procName, 1); |
| if (!pminw && !pmaxw && !pminh && !pmaxh) |
| return ERROR_INT("no data can be returned", procName, 1); |
| |
| minw = minh = 1000000; |
| maxw = maxh = 0; |
| n = pixaGetCount(pixa); |
| for (i = 0; i < n; i++) { |
| pix = pixaGetPix(pixa, i, L_CLONE); |
| w = pixGetWidth(pix); |
| h = pixGetHeight(pix); |
| if (w < minw) |
| minw = w; |
| if (h < minh) |
| minh = h; |
| if (w > maxw) |
| maxw = w; |
| if (h > maxh) |
| maxh = h; |
| pixDestroy(&pix); |
| } |
| |
| if (pminw) *pminw = minw; |
| if (pminh) *pminh = minh; |
| if (pmaxw) *pmaxw = maxw; |
| if (pmaxh) *pmaxh = maxh; |
| |
| return 0; |
| } |
| |
| |
| /*! |
| * pixaClipToPix() |
| * |
| * Input: pixas |
| * pixs |
| * Return: pixad, or null on error |
| * |
| * Notes: |
| * (1) This is intended for use in situations where pixas |
| * was originally generated from the input pixs. |
| * (2) Returns a pixad where each pix in pixas is ANDed |
| * with its associated region of the input pixs. This |
| * region is specified by the the box that is associated |
| * with the pix. |
| * (3) In a typical application of this function, pixas has |
| * a set of region masks, so this generates a pixa of |
| * the parts of pixs that correspond to each region |
| * mask component, along with the bounding box for |
| * the region. |
| */ |
| PIXA * |
| pixaClipToPix(PIXA *pixas, |
| PIX *pixs) |
| { |
| l_int32 i, n; |
| BOX *box; |
| PIX *pix, *pixc; |
| PIXA *pixad; |
| |
| PROCNAME("pixaClipToPix"); |
| |
| if (!pixas) |
| return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); |
| if (!pixs) |
| return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); |
| |
| n = pixaGetCount(pixas); |
| if ((pixad = pixaCreate(n)) == NULL) |
| return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); |
| |
| for (i = 0; i < n; i++) { |
| pix = pixaGetPix(pixas, i, L_CLONE); |
| box = pixaGetBox(pixas, i, L_COPY); |
| pixc = pixClipRectangle(pixs, box, NULL); |
| pixAnd(pixc, pixc, pix); |
| pixaAddPix(pixad, pixc, L_INSERT); |
| pixaAddBox(pixad, box, L_INSERT); |
| pixDestroy(&pix); |
| } |
| |
| return pixad; |
| } |
| |
| |
| /*! |
| * pixaAnyColormaps() |
| * |
| * Input: pixa |
| * &hascmap (<return> 1 if any pix has a colormap; 0 otherwise) |
| * Return: 0 if OK; 1 on error |
| */ |
| l_int32 |
| pixaAnyColormaps(PIXA *pixa, |
| l_int32 *phascmap) |
| { |
| l_int32 i, n; |
| PIX *pix; |
| PIXCMAP *cmap; |
| |
| PROCNAME("pixaAnyColormaps"); |
| |
| if (!phascmap) |
| return ERROR_INT("&hascmap not defined", procName, 1); |
| *phascmap = 0; |
| if (!pixa) |
| return ERROR_INT("pixa not defined", procName, 1); |
| |
| n = pixaGetCount(pixa); |
| for (i = 0; i < n; i++) { |
| pix = pixaGetPix(pixa, i, L_CLONE); |
| cmap = pixGetColormap(pix); |
| pixDestroy(&pix); |
| if (cmap) { |
| *phascmap = 1; |
| return 0; |
| } |
| } |
| |
| return 0; |
| } |
| |
| |
| /*! |
| * pixaEqual() |
| * |
| * Input: pixa1 |
| * pixa2 |
| * maxdist |
| * &naindex (<optional return> index array of correspondences |
| * &same (<return> 1 if equal; 0 otherwise) |
| * Return 0 if OK, 1 on error |
| * |
| * Notes: |
| * (1) The two pixa are the "same" if they contain the same |
| * boxa and the same ordered set of pix. However, if they |
| * have boxa, the pix in each pixa can differ in ordering |
| * by an amount given by the parameter @maxdist. If they |
| * don't have a boxa, the @maxdist parameter is ignored, |
| * and the ordering must be identical. |
| * (2) This applies only to boxa geometry, pixels and ordering; |
| * other fields in the pix are ignored. |
| * (3) naindex[i] gives the position of the box in pixa2 that |
| * corresponds to box i in pixa1. It is only returned if the |
| * pixa have boxa and the boxa are equal. |
| * (4) In situations where the ordering is very different, so that |
| * a large @maxdist is required for "equality", this should be |
| * implemented with a hash function for efficiency. |
| */ |
| l_int32 |
| pixaEqual(PIXA *pixa1, |
| PIXA *pixa2, |
| l_int32 maxdist, |
| NUMA **pnaindex, |
| l_int32 *psame) |
| { |
| l_int32 i, j, n, same, sameboxa; |
| BOXA *boxa1, *boxa2; |
| NUMA *na; |
| PIX *pix1, *pix2; |
| |
| PROCNAME("pixaEqual"); |
| |
| if (!psame) |
| return ERROR_INT("&same not defined", procName, 1); |
| *psame = 0; |
| sameboxa = 0; |
| na = NULL; |
| if (!pixa1 || !pixa2) |
| return ERROR_INT("pixa1 and pixa2 not both defined", procName, 1); |
| n = pixaGetCount(pixa1); |
| if (n != pixaGetCount(pixa2)) |
| return 0; |
| boxa1 = pixaGetBoxa(pixa1, L_CLONE); |
| boxa2 = pixaGetBoxa(pixa2, L_CLONE); |
| if (!boxa1 && !boxa2) |
| maxdist = 0; /* exact ordering required */ |
| if (boxa1 && !boxa2) { |
| boxaDestroy(&boxa1); |
| return 0; |
| } |
| if (!boxa1 && boxa2) { |
| boxaDestroy(&boxa2); |
| return 0; |
| } |
| if (boxa1 && boxa2) { |
| boxaEqual(boxa1, boxa2, maxdist, &na, &sameboxa); |
| boxaDestroy(&boxa1); |
| boxaDestroy(&boxa2); |
| if (!sameboxa) { |
| numaDestroy(&na); |
| return 0; |
| } |
| } |
| |
| for (i = 0; i < n; i++) { |
| pix1 = pixaGetPix(pixa1, i, L_CLONE); |
| if (na) |
| numaGetIValue(na, i, &j); |
| else |
| j = i; |
| pix2 = pixaGetPix(pixa2, j, L_CLONE); |
| pixEqual(pix1, pix2, &same); |
| pixDestroy(&pix1); |
| pixDestroy(&pix2); |
| if (!same) { |
| numaDestroy(&na); |
| return 0; |
| } |
| } |
| |
| *psame = 1; |
| if (pnaindex) |
| *pnaindex = na; |
| else |
| numaDestroy(&na); |
| return 0; |
| } |
| |
| |