/* Reconstruction with compaction */ static PIXA * ReconstructPixa2(L_PTRA *papix, L_PTRA *pabox) { l_int32 i, imax, nactual; BOX *box; PIX *pix; PIXA *pixat; ptraGetMaxIndex(papix, &imax); ptraGetActualCount(papix, &nactual); fprintf(stderr, "Before removal: imax = %4d, actual = %4d\n", imax, nactual); /* Remove half */ pixat = pixaCreate(imax + 1); for (i = 0; i <= imax; i++) { if (i % 2 == 0) { pix = (PIX *)ptraRemove(papix, i, L_NO_COMPACTION); box = (BOX *)ptraRemove(pabox, i, L_NO_COMPACTION); if (pix) pixaAddPix(pixat, pix, L_INSERT); if (box) pixaAddBox(pixat, box, L_INSERT); } } /* Compact */ ptraGetMaxIndex(papix, &imax); ptraGetActualCount(papix, &nactual); fprintf(stderr, "Before compaction: imax = %4d, actual = %4d\n", imax, nactual); ptraCompactArray(papix); ptraCompactArray(pabox); ptraGetMaxIndex(papix, &imax); ptraGetActualCount(papix, &nactual); fprintf(stderr, "After compaction: imax = %4d, actual = %4d\n", imax, nactual); /* Remove the rest (and test compaction with removal) */ while (1) { ptraGetActualCount(papix, &nactual); if (nactual == 0) break; pix = (PIX *)ptraRemove(papix, 0, L_COMPACTION); box = (BOX *)ptraRemove(pabox, 0, L_COMPACTION); pixaAddPix(pixat, pix, L_INSERT); pixaAddBox(pixat, box, L_INSERT); } ptraGetMaxIndex(papix, &imax); ptraGetActualCount(papix, &nactual); fprintf(stderr, "After removal: imax = %4d, actual = %4d\n\n", imax, nactual); return pixat; }
/*! * pixaCreateFromBoxa() * * Input: pixs * boxa * &cropwarn (<optional return> TRUE if the boxa extent * is larger than pixs. * Return: pixad, or null on error * * Notes: * (1) This simply extracts from pixs the region corresponding to each * box in the boxa. * (2) The 3rd arg is optional. If the extent of the boxa exceeds the * size of the pixa, so that some boxes are either clipped * or entirely outside the pix, a warning is returned as TRUE. * (3) pixad will have only the properly clipped elements, and * the internal boxa will be correct. */ PIXA * pixaCreateFromBoxa(PIX *pixs, BOXA *boxa, l_int32 *pcropwarn) { l_int32 i, n, w, h, wbox, hbox, cropwarn; BOX *box, *boxc; PIX *pixd; PIXA *pixad; PROCNAME("pixaCreateFromBoxa"); if (!pixs) return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); if (!boxa) return (PIXA *)ERROR_PTR("boxa not defined", procName, NULL); n = boxaGetCount(boxa); if ((pixad = pixaCreate(n)) == NULL) return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); boxaGetExtent(boxa, &wbox, &hbox, NULL); pixGetDimensions(pixs, &w, &h, NULL); cropwarn = FALSE; if (wbox > w || hbox > h) cropwarn = TRUE; if (pcropwarn) *pcropwarn = cropwarn; for (i = 0; i < n; i++) { box = boxaGetBox(boxa, i, L_COPY); if (cropwarn) { /* if box is outside pixs, pixd is NULL */ pixd = pixClipRectangle(pixs, box, &boxc); /* may be NULL */ if (pixd) { pixaAddPix(pixad, pixd, L_INSERT); pixaAddBox(pixad, boxc, L_INSERT); } boxDestroy(&box); } else { pixd = pixClipRectangle(pixs, box, NULL); pixaAddPix(pixad, pixd, L_INSERT); pixaAddBox(pixad, box, L_INSERT); } } return pixad; }
/*! * 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; }
/*! * 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; }
/* Reconstruction without compaction */ static PIXA * ReconstructPixa1(L_PTRA *papix, L_PTRA *pabox) { l_int32 i, imax, nactual; BOX *box; PIX *pix; PIXA *pixat; ptraGetMaxIndex(papix, &imax); ptraGetActualCount(papix, &nactual); fprintf(stderr, "Before removal: imax = %4d, actual = %4d\n", imax, nactual); pixat = pixaCreate(imax + 1); for (i = 0; i <= imax; i++) { pix = (PIX *)ptraRemove(papix, i, L_NO_COMPACTION); box = (BOX *)ptraRemove(pabox, i, L_NO_COMPACTION); if (pix) pixaAddPix(pixat, pix, L_INSERT); if (box) pixaAddBox(pixat, box, L_INSERT); } ptraGetMaxIndex(papix, &imax); ptraGetActualCount(papix, &nactual); fprintf(stderr, "After removal: imax = %4d, actual = %4d\n\n", imax, nactual); return pixat; }
void Java_com_example_ocr_Pixa_nativeAddBox(JNIEnv *env, jclass clazz, jint nativePixa, jint nativeBox, jint mode) { PIXA *pixa = (PIXA *) nativePixa; BOX *box = (BOX *) nativeBox; pixaAddBox(pixa, box, mode); }
void Java_com_googlecode_leptonica_android_Pixa_nativeAddBox(JNIEnv *env, jclass clazz, jlong nativePixa, jlong nativeBox, jint mode) { PIXA *pixa = (PIXA *) nativePixa; BOX *box = (BOX *) nativeBox; pixaAddBox(pixa, box, mode); }
void Java_com_googlecode_leptonica_android_Pixa_nativeAdd(JNIEnv *env, jclass clazz, jint nativePixa, jint nativePix, jint nativeBox, jint mode) { LOGV(__FUNCTION__); PIXA *pixa = (PIXA *) nativePixa; PIX *pix = (PIX *) nativePix; BOX *box = (BOX *) nativeBox; pixaAddPix(pixa, pix, mode); pixaAddBox(pixa, box, mode); }
/*! * 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; }
/*! * 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; }
/*! * 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; }
/*! * wshedSaveBasin() * * Input: wshed * index (index of basin to be located) * level (filling level reached at the time this function * is called) * Return: 0 if OK, 1 on error * * Notes: * (1) This identifies a single watershed. It does not change * the LUT, which must be done subsequently. * (2) The fill level of a basin is taken to be @level - 1. */ static void wshedSaveBasin(L_WSHED *wshed, l_int32 index, l_int32 level) { BOX *box; PIX *pix; PROCNAME("wshedSaveBasin"); if (!wshed) { L_ERROR("wshed not defined\n", procName); return; } if (identifyWatershedBasin(wshed, index, level, &box, &pix) == 0) { pixaAddPix(wshed->pixad, pix, L_INSERT); pixaAddBox(wshed->pixad, box, L_INSERT); numaAddNumber(wshed->nalevels, level - 1); } return; }
/*! * pixaCopy() * * Input: pixas * copyflag: * L_COPY makes a new pixa and copies each pix and each box * L_CLONE gives a new ref-counted handle to the input pixa * L_COPY_CLONE makes a new pixa and inserts clones of * all pix and boxes * Return: new pixa, or null on error * * Note: see pix.h for description of the copy types. */ PIXA * pixaCopy(PIXA *pixa, l_int32 copyflag) { l_int32 i; BOX *boxc; PIX *pixc; PIXA *pixac; PROCNAME("pixaCopy"); if (!pixa) return (PIXA *)ERROR_PTR("pixa not defined", procName, NULL); if (copyflag == L_CLONE) { pixaChangeRefcount(pixa, 1); return pixa; } if (copyflag != L_COPY && copyflag != L_COPY_CLONE) return (PIXA *)ERROR_PTR("invalid copyflag", procName, NULL); if ((pixac = pixaCreate(pixa->n)) == NULL) return (PIXA *)ERROR_PTR("pixac not made", procName, NULL); for (i = 0; i < pixa->n; i++) { if (copyflag == L_COPY) { pixc = pixaGetPix(pixa, i, L_COPY); boxc = pixaGetBox(pixa, i, L_COPY); } else { /* copy-clone */ pixc = pixaGetPix(pixa, i, L_CLONE); boxc = pixaGetBox(pixa, i, L_CLONE); } pixaAddPix(pixac, pixc, L_INSERT); pixaAddBox(pixac, boxc, L_INSERT); } return pixac; }
/*! * pixSaveTiledOutline() * * Input: pixs (1, 2, 4, 8, 32 bpp) * pixa (the pix are accumulated here) * scalefactor (0.0 to disable; otherwise this is a scale factor) * newrow (0 if placed on the same row as previous; 1 otherwise) * space (horizontal and vertical spacing, in pixels) * linewidth (width of added outline for image; 0 for no outline) * dp (depth of pixa; 8 or 32 bpp; only used on first call) * Return: 0 if OK, 1 on error. * * Notes: * (1) Before calling this function for the first time, use * pixaCreate() to make the @pixa that will accumulate the pix. * This is passed in each time pixSaveTiled() is called. * (2) @scalefactor scales the input image. After scaling and * possible depth conversion, the image is saved in the input * pixa, along with a box that specifies the location to * place it when tiled later. Disable saving the pix by * setting @scalefactor == 0.0. * (3) @newrow and @space specify the location of the new pix * with respect to the last one(s) that were entered. * (4) @dp specifies the depth at which all pix are saved. It can * be only 8 or 32 bpp. Any colormap is removed. This is only * used at the first invocation. * (5) This function uses two variables from call to call. * If they were static, the function would not be .so or thread * safe, and furthermore, there would be interference with two or * more pixa accumulating images at a time. Consequently, * we use the first pix in the pixa to store and obtain both * the depth and the current position of the bottom (one pixel * below the lowest image raster line when laid out using * the boxa). The bottom variable is stored in the input format * field, which is the only field available for storing an int. */ l_int32 pixSaveTiledOutline(PIX *pixs, PIXA *pixa, l_float32 scalefactor, l_int32 newrow, l_int32 space, l_int32 linewidth, l_int32 dp) { l_int32 n, top, left, bx, by, bw, w, h, depth, bottom; BOX *box; PIX *pix1, *pix2, *pix3, *pix4; PROCNAME("pixSaveTiledOutline"); if (scalefactor == 0.0) return 0; if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); if (n == 0) { bottom = 0; if (dp != 8 && dp != 32) { L_WARNING("dp not 8 or 32 bpp; using 32\n", procName); depth = 32; } else { depth = dp; } } else { /* extract the depth and bottom params from the first pix */ pix1 = pixaGetPix(pixa, 0, L_CLONE); depth = pixGetDepth(pix1); bottom = pixGetInputFormat(pix1); /* not typical usage! */ pixDestroy(&pix1); } /* Remove colormap if it exists; otherwise a copy. This * guarantees that pix4 is not a clone of pixs. */ pix1 = pixRemoveColormapGeneral(pixs, REMOVE_CMAP_BASED_ON_SRC, L_COPY); /* Scale and convert to output depth */ if (scalefactor == 1.0) { pix2 = pixClone(pix1); } else if (scalefactor > 1.0) { pix2 = pixScale(pix1, scalefactor, scalefactor); } else if (scalefactor < 1.0) { if (pixGetDepth(pix1) == 1) pix2 = pixScaleToGray(pix1, scalefactor); else pix2 = pixScale(pix1, scalefactor, scalefactor); } pixDestroy(&pix1); if (depth == 8) pix3 = pixConvertTo8(pix2, 0); else pix3 = pixConvertTo32(pix2); pixDestroy(&pix2); /* Add black outline */ if (linewidth > 0) pix4 = pixAddBorder(pix3, linewidth, 0); else pix4 = pixClone(pix3); pixDestroy(&pix3); /* Find position of current pix (UL corner plus size) */ if (n == 0) { top = 0; left = 0; } else if (newrow == 1) { top = bottom + space; left = 0; } else if (n > 0) { pixaGetBoxGeometry(pixa, n - 1, &bx, &by, &bw, NULL); top = by; left = bx + bw + space; } pixGetDimensions(pix4, &w, &h, NULL); bottom = L_MAX(bottom, top + h); box = boxCreate(left, top, w, h); pixaAddPix(pixa, pix4, L_INSERT); pixaAddBox(pixa, box, L_INSERT); /* Save the new bottom value */ pix1 = pixaGetPix(pixa, 0, L_CLONE); pixSetInputFormat(pix1, bottom); /* not typical usage! */ pixDestroy(&pix1); return 0; }
/*! * pixSaveTiledOutline() * * Input: pixs (1, 2, 4, 8, 32 bpp) * pixa (the pix are accumulated here) * reduction (0 to disable; otherwise this is a reduction factor) * newrow (0 if placed on the same row as previous; 1 otherwise) * space (horizontal and vertical spacing, in pixels) * linewidth (width of added outline for image; 0 for no outline) * dp (depth of pixa; 8 or 32 bpp; only used on first call) * Return: 0 if OK, 1 on error. * * Notes: * (1) Before calling this function for the first time, use * pixaCreate() to make the @pixa that will accumulate the pix. * This is passed in each time pixSaveTiled() is called. * (2) @reduction is the integer reduction factor for the input * image. After reduction and possible depth conversion, * the image is saved in the input pixa, along with a box * that specifies the location to place it when tiled later. * Disable saving the pix by setting reduction == 0. * (3) @newrow and @space specify the location of the new pix * with respect to the last one(s) that were entered. * (4) @dp specifies the depth at which all pix are saved. It can * be only 8 or 32 bpp. Any colormap is removed. This is only * used at the first invocation. * (5) This function uses two variables from call to call. * If they were static, the function would not be .so or thread * safe, and furthermore, there would be interference with two or * more pixa accumulating images at a time. Consequently, * we use the first pix in the pixa to store and obtain both * the depth and the current position of the bottom (one pixel * below the lowest image raster line when laid out using * the boxa). The bottom variable is stored in the input format * field, which is the only field available for storing an int. */ l_int32 pixSaveTiledOutline(PIX *pixs, PIXA *pixa, l_int32 reduction, l_int32 newrow, l_int32 space, l_int32 linewidth, l_int32 dp) { l_int32 n, top, left, bx, by, bw, w, h, depth, bottom; l_float32 scale; BOX *box; PIX *pix, *pixt1, *pixt2, *pixt3; PROCNAME("pixSaveTiledOutline"); if (reduction == 0) return 0; if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); if (n == 0) { bottom = 0; if (dp != 8 && dp != 32) { L_WARNING("dp not 8 or 32 bpp; using 32", procName); depth = 32; } else depth = dp; } else { /* extract the depth and bottom params from the first pix */ pix = pixaGetPix(pixa, 0, L_CLONE); depth = pixGetDepth(pix); bottom = pixGetInputFormat(pix); /* not typical usage! */ pixDestroy(&pix); } /* Scale and convert to output depth */ if (reduction == 1) pixt1 = pixClone(pixs); else { scale = 1. / (l_float32)reduction; if (pixGetDepth(pixs) == 1) pixt1 = pixScaleToGray(pixs, scale); else pixt1 = pixScale(pixs, scale, scale); } if (depth == 8) pixt2 = pixConvertTo8(pixt1, 0); else pixt2 = pixConvertTo32(pixt1); pixDestroy(&pixt1); /* Add black outline */ if (linewidth > 0) pixt3 = pixAddBorder(pixt2, linewidth, 0); else pixt3 = pixClone(pixt2); pixDestroy(&pixt2); /* Find position of current pix (UL corner plus size) */ if (n == 0) { top = 0; left = 0; } else if (newrow == 1) { top = bottom + space; left = 0; } else if (n > 0) { pixaGetBoxGeometry(pixa, n - 1, &bx, &by, &bw, NULL); top = by; left = bx + bw + space; } pixGetDimensions(pixt3, &w, &h, NULL); bottom = L_MAX(bottom, top + h); box = boxCreate(left, top, w, h); pixaAddPix(pixa, pixt3, L_INSERT); pixaAddBox(pixa, box, L_INSERT); /* Save the new bottom value */ pix = pixaGetPix(pixa, 0, L_CLONE); pixSetInputFormat(pix, bottom); /* not typical usage! */ pixDestroy(&pix); return 0; }
// do a desperate attempt at cracking lines Pixa *CubeLineSegmenter::CrackLine(Pix *cracked_line_pix, Box *cracked_line_box, int line_cnt) { // create lines pixa array Pixa **lines_pixa = new Pixa*[line_cnt]; if (lines_pixa == NULL) { return NULL; } memset(lines_pixa, 0, line_cnt * sizeof(*lines_pixa)); // compute line conn comps Pixa *line_con_comps_pix; Boxa *line_con_comps = ComputeLineConComps(cracked_line_pix, cracked_line_box, &line_con_comps_pix); if (line_con_comps == NULL) { delete []lines_pixa; return NULL; } // assign each conn comp to the a line based on its centroid for (int con = 0; con < line_con_comps->n; con++) { Box *con_box = line_con_comps->box[con]; Pix *con_pix = line_con_comps_pix->pix[con]; int mid_y = (con_box->y - cracked_line_box->y) + (con_box->h / 2), line_idx = MIN(line_cnt - 1, (mid_y * line_cnt / cracked_line_box->h)); // create the line if it has not been created? if (lines_pixa[line_idx] == NULL) { lines_pixa[line_idx] = pixaCreate(line_con_comps->n); if (lines_pixa[line_idx] == NULL) { delete []lines_pixa; boxaDestroy(&line_con_comps); pixaDestroy(&line_con_comps_pix); return NULL; } } // add the concomp to the line if (pixaAddPix(lines_pixa[line_idx], con_pix, L_CLONE) != 0 || pixaAddBox(lines_pixa[line_idx], con_box, L_CLONE)) { delete []lines_pixa; boxaDestroy(&line_con_comps); pixaDestroy(&line_con_comps_pix); } } // create the lines pixa Pixa *lines = pixaCreate(line_cnt); bool success = true; // create and check the validity of the lines for (int line = 0; line < line_cnt; line++) { Pixa *line_pixa = lines_pixa[line]; // skip invalid lines if (line_pixa == NULL) { continue; } // merge the pix, check the validity of the line // and add it to the lines pixa Box *line_box; Pix *line_pix = Pixa2Pix(line_pixa, &line_box); if (line_pix == NULL || line_box == NULL || ValidLine(line_pix, line_box) == false || pixaAddPix(lines, line_pix, L_INSERT) != 0 || pixaAddBox(lines, line_box, L_INSERT) != 0) { if (line_pix != NULL) { pixDestroy(&line_pix); } if (line_box != NULL) { boxDestroy(&line_box); } success = false; break; } } // cleanup for (int line = 0; line < line_cnt; line++) { if (lines_pixa[line] != NULL) { pixaDestroy(&lines_pixa[line]); } } delete []lines_pixa; boxaDestroy(&line_con_comps); pixaDestroy(&line_con_comps_pix); if (success == false) { pixaDestroy(&lines); lines = NULL; } return lines; }