/*! * pixaWriteFiles() * * Input: rootname * pixa * format (defined in imageio.h; see notes for default) * Return: 0 if OK; 1 on error * * Notes: * (1) Use @format = IFF_DEFAULT to decide the output format * individually for each pix. */ l_int32 pixaWriteFiles(const char *rootname, PIXA *pixa, l_int32 format) { PIX *pix; l_int32 i, n, pixformat; char bigbuf[L_BUF_SIZE]; PROCNAME("pixaWriteFiles"); if (!rootname) return ERROR_INT("rootname not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); if (format < 0 || format == IFF_UNKNOWN || format >= NumImageFileFormatExtensions) return ERROR_INT("invalid format", procName, 1); n = pixaGetCount(pixa); for (i = 0; i < n; i++) { pix = pixaGetPix(pixa, i, L_CLONE); if (format == IFF_DEFAULT) pixformat = pixChooseOutputFormat(pix); else pixformat = format; snprintf(bigbuf, L_BUF_SIZE, "%s%03d.%s", rootname, i, ImageFileFormatExtensions[pixformat]); pixWrite(bigbuf, pix, pixformat); pixDestroy(&pix); } return 0; }
/*! * \brief pixConnCompTransform() * * \param[in] pixs 1 bpp * \param[in] connect connectivity: 4 or 8 * \param[in] depth of pixd: 8 or 16 bpp; use 0 for auto determination * \return pixd 8, 16 or 32 bpp, or NULL on error * * <pre> * Notes: * (1) pixd is 8, 16 or 32 bpp, and the pixel values label the * fg component, starting with 1. Pixels in the bg are labelled 0. * (2) If %depth = 0, the depth of pixd is 8 if the number of c.c. * is less than 254, 16 if the number of c.c is less than 0xfffe, * and 32 otherwise. * (3) If %depth = 8, the assigned label for the n-th component is * 1 + n % 254. We use mod 254 because 0 is uniquely assigned * to black: e.g., see pixcmapCreateRandom(). Likewise, * if %depth = 16, the assigned label uses mod(2^16 - 2), and * if %depth = 32, no mod is taken. * </pre> */ PIX * pixConnCompTransform(PIX *pixs, l_int32 connect, l_int32 depth) { l_int32 i, n, index, w, h, xb, yb, wb, hb; BOXA *boxa; PIX *pix1, *pix2, *pixd; PIXA *pixa; PROCNAME("pixConnCompTransform"); if (!pixs || pixGetDepth(pixs) != 1) return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); if (connect != 4 && connect != 8) return (PIX *)ERROR_PTR("connectivity must be 4 or 8", procName, NULL); if (depth != 0 && depth != 8 && depth != 16 && depth != 32) return (PIX *)ERROR_PTR("depth must be 0, 8, 16 or 32", procName, NULL); boxa = pixConnComp(pixs, &pixa, connect); n = pixaGetCount(pixa); boxaDestroy(&boxa); pixGetDimensions(pixs, &w, &h, NULL); if (depth == 0) { if (n < 254) depth = 8; else if (n < 0xfffe) depth = 16; else depth = 32; } pixd = pixCreate(w, h, depth); pixSetSpp(pixd, 1); if (n == 0) { /* no fg */ pixaDestroy(&pixa); return pixd; } /* Label each component and blit it in */ for (i = 0; i < n; i++) { pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb); pix1 = pixaGetPix(pixa, i, L_CLONE); if (depth == 8) { index = 1 + (i % 254); pix2 = pixConvert1To8(NULL, pix1, 0, index); } else if (depth == 16) { index = 1 + (i % 0xfffe); pix2 = pixConvert1To16(NULL, pix1, 0, index); } else { /* depth == 32 */ index = 1 + i; pix2 = pixConvert1To32(NULL, pix1, 0, index); } pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pix2, 0, 0); pixDestroy(&pix1); pixDestroy(&pix2); } pixaDestroy(&pixa); return pixd; }
/*! * 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; }
/*! * \brief recogAddCharstrLabels() * * \param[in] recog * \return 0 if OK, 1 on error */ static l_int32 recogAddCharstrLabels(L_RECOG *recog) { char *text; l_int32 i, j, n1, n2; PIX *pix; PIXA *pixa; PIXAA *paa; PROCNAME("recogAddCharstrLabels"); if (!recog) return ERROR_INT("recog not defined", procName, 1); /* Add the labels to each unscaled pix */ paa = recog->pixaa_u; n1 = pixaaGetCount(paa, NULL); for (i = 0; i < n1; i++) { pixa = pixaaGetPixa(paa, i, L_CLONE); text = sarrayGetString(recog->sa_text, i, L_NOCOPY); n2 = pixaGetCount(pixa); for (j = 0; j < n2; j++) { pix = pixaGetPix(pixa, j, L_CLONE); pixSetText(pix, text); pixDestroy(&pix); } pixaDestroy(&pixa); } 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; }
/*! * jbCorrelation() * * Input: dirin (directory of input images) * thresh (typically ~0.8) * weight (typically ~0.6) * components (JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS) * rootname (for output files) * firstpage (0-based) * npages (use 0 for all pages in dirin) * renderflag (1 to render from templates; 0 to skip) * Return: 0 if OK, 1 on error * * Notes: * (1) The images must be 1 bpp. If they are not, you can convert * them using convertFilesTo1bpp(). * (2) See prog/jbcorrelation for generating more output (e.g., * for debugging) */ l_int32 jbCorrelation(const char *dirin, l_float32 thresh, l_float32 weight, l_int32 components, const char *rootname, l_int32 firstpage, l_int32 npages, l_int32 renderflag) { char filename[L_BUF_SIZE]; l_int32 nfiles, i, numpages; JBDATA *data; JBCLASSER *classer; PIX *pix; PIXA *pixa; SARRAY *safiles; PROCNAME("jbCorrelation"); if (!dirin) return ERROR_INT("dirin not defined", procName, 1); if (!rootname) return ERROR_INT("rootname not defined", procName, 1); if (components != JB_CONN_COMPS && components != JB_CHARACTERS && components != JB_WORDS) return ERROR_INT("components invalid", procName, 1); safiles = getSortedPathnamesInDirectory(dirin, NULL, firstpage, npages); nfiles = sarrayGetCount(safiles); /* Classify components */ classer = jbCorrelationInit(components, 0, 0, thresh, weight); jbAddPages(classer, safiles); /* Save data */ data = jbDataSave(classer); jbDataWrite(rootname, data); /* Optionally, render pages using class templates */ if (renderflag) { pixa = jbDataRender(data, FALSE); numpages = pixaGetCount(pixa); if (numpages != nfiles) fprintf(stderr, "numpages = %d, nfiles = %d, not equal!\n", numpages, nfiles); for (i = 0; i < numpages; i++) { pix = pixaGetPix(pixa, i, L_CLONE); snprintf(filename, L_BUF_SIZE, "%s.%05d", rootname, i); fprintf(stderr, "filename: %s\n", filename); pixWrite(filename, pix, IFF_PNG); pixDestroy(&pix); } pixaDestroy(&pixa); } sarrayDestroy(&safiles); jbClasserDestroy(&classer); jbDataDestroy(&data); return 0; }
/*! * \brief pixaFindStrokeWidth() * * \param[in] pixa of 1 bpp images * \param[in] thresh fractional count threshold relative to distance 1 * \param[in] tab8 [optional] table for counting fg pixels; can be NULL * \param[in] debug 1 for debug output; 0 to skip * \return na array of stroke widths for each pix in %pixa; NULL on error * * <pre> * Notes: * (1) See pixFindStrokeWidth() for details. * </pre> */ NUMA * pixaFindStrokeWidth(PIXA *pixa, l_float32 thresh, l_int32 *tab8, l_int32 debug) { l_int32 i, n, same, maxd; l_int32 *tab; l_float32 width; NUMA *na; PIX *pix; PROCNAME("pixaFindStrokeWidth"); if (!pixa) return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); pixaVerifyDepth(pixa, &same, &maxd); if (maxd > 1) return (NUMA *)ERROR_PTR("pix not all 1 bpp", procName, NULL); tab = (tab8) ? tab8 : makePixelSumTab8(); n = pixaGetCount(pixa); na = numaCreate(n); for (i = 0; i < n; i++) { pix = pixaGetPix(pixa, i, L_CLONE); pixFindStrokeWidth(pix, thresh, tab8, &width, NULL); numaAddNumber(na, width); pixDestroy(&pix); } if (!tab8) LEPT_FREE(tab); return na; }
/*! * 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; }
/*! * \brief pixaModifyStrokeWidth() * * \param[in] pixas of 1 bpp pix * \param[out] targetw desired width for strokes in each pix * \return pixa with modified stroke widths, or NULL on error */ PIXA * pixaModifyStrokeWidth(PIXA *pixas, l_float32 targetw) { l_int32 i, n, same, maxd; l_float32 width; NUMA *na; PIX *pix1, *pix2; PIXA *pixad; PROCNAME("pixaModifyStrokeWidth"); if (!pixas) return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); if (targetw < 1) return (PIXA *)ERROR_PTR("target width < 1", procName, NULL); pixaVerifyDepth(pixas, &same, &maxd); if (maxd > 1) return (PIXA *)ERROR_PTR("pix not all 1 bpp", procName, NULL); na = pixaFindStrokeWidth(pixas, 0.1, NULL, 0); n = pixaGetCount(pixas); pixad = pixaCreate(n); for (i = 0; i < n; i++) { pix1 = pixaGetPix(pixas, i, L_CLONE); numaGetFValue(na, i, &width); pix2 = pixModifyStrokeWidth(pix1, width, targetw); pixaAddPix(pixad, pix2, L_INSERT); pixDestroy(&pix1); } 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; }
/*! * \brief pixaSetStrokeWidth() * * \param[in] pixas of 1 bpp pix * \param[in] width set stroke width to this value, in [1 ... 100]. * \param[in] thinfirst 1 to thin all pix to a skeleton first; 0 to skip * \param[in] connectivity 4 or 8, to be used if %thinfirst == 1 * \return pixa with all stroke widths being %width, or NULL on error * * <pre> * Notes: * (1) If %thinfirst == 1, thin to a skeleton using the specified * %connectivity. Use %thinfirst == 0 if all pix in pixas * have already been thinned as far as possible. * (2) The image is dilated to the required %width. This dilation * is not connectivity preserving, so this is typically * used in a situation where merging of c.c. in the individual * pix is not a problem; e.g., where each pix is a single c.c. * </pre> */ PIXA * pixaSetStrokeWidth(PIXA *pixas, l_int32 width, l_int32 thinfirst, l_int32 connectivity) { l_int32 i, n, maxd, same; PIX *pix1, *pix2; PIXA *pixad; PROCNAME("pixaSetStrokeWidth"); if (!pixas) return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); if (width < 1 || width > 100) return (PIXA *)ERROR_PTR("width not in [1 ... 100]", procName, NULL); if (connectivity != 4 && connectivity != 8) return (PIXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); pixaVerifyDepth(pixas, &same, &maxd); if (maxd > 1) return (PIXA *)ERROR_PTR("pix are not all 1 bpp", procName, NULL); n = pixaGetCount(pixas); pixad = pixaCreate(n); for (i = 0; i < n; i++) { pix1 = pixaGetPix(pixas, i, L_CLONE); pix2 = pixSetStrokeWidth(pix1, width, thinfirst, connectivity); pixaAddPix(pixad, pix2, L_INSERT); pixDestroy(&pix1); } return pixad; }
static void MakePtrasFromPixa(PIXA *pixa, L_PTRA **ppapix, L_PTRA **ppabox, l_int32 copyflag) { l_int32 i, n; BOX *box; PIX *pix; L_PTRA *papix, *pabox; n = pixaGetCount(pixa); papix = ptraCreate(n); pabox = ptraCreate(n); for (i = 0; i < n; i++) { pix = pixaGetPix(pixa, i, copyflag); box = pixaGetBox(pixa, i, copyflag); ptraAdd(papix, pix); ptraAdd(pabox, box); } *ppapix = papix; *ppabox = pabox; return; }
jboolean Java_com_example_ocr_Pixa_nativeWriteToFileRandomCmap(JNIEnv *env, jclass clazz, jint nativePixa, jstring fileName, jint width, jint height) { PIX *pixtemp; PIXA *pixa = (PIXA *) nativePixa; const char *c_fileName = env->GetStringUTFChars(fileName, NULL); if (c_fileName == NULL) { LOGE("could not extract fileName string!"); return JNI_FALSE; } if (pixaGetCount(pixa) > 0) { pixtemp = pixaDisplayRandomCmap(pixa, (l_int32) width, (l_int32) height); } else { pixtemp = pixCreate((l_int32) width, (l_int32) height, 1); } pixWrite(c_fileName, pixtemp, IFF_BMP); pixDestroy(&pixtemp); env->ReleaseStringUTFChars(fileName, c_fileName); return JNI_TRUE; }
/*! * pixaWriteFiles() * * Input: rootname * pixa * format (defined in imageio.h) * Return: 0 if OK; 1 on error */ l_int32 pixaWriteFiles(const char *rootname, PIXA *pixa, l_int32 format) { char bigbuf[L_BUF_SIZE]; l_int32 i, n; PIX *pix; PROCNAME("pixaWriteFiles"); if (!rootname) return ERROR_INT("rootname not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); if (format < 0 || format >= NumImageFileFormatExtensions) return ERROR_INT("invalid format", procName, 1); n = pixaGetCount(pixa); for (i = 0; i < n; i++) { snprintf(bigbuf, L_BUF_SIZE, "%s%03d.%s", rootname, i, ImageFileFormatExtensions[format]); pix = pixaGetPix(pixa, i, L_CLONE); pixWrite(bigbuf, pix, format); pixDestroy(&pix); } return 0; }
/*! * pixaSaveFont() * * Input: indir (directory holding image of character set) * outdir (directory into which the output pixa file * will be written) * size (in pts, at 300 ppi) * Return: 0 if OK, 1 on error * * Notes: * (1) This saves a font of a particular size. * (2) prog/genfonts calls this function for each of the * nine font sizes, to generate all the font pixa files. */ l_int32 pixaSaveFont(const char *indir, const char *outdir, l_int32 size) { char *pathname; l_int32 bl1, bl2, bl3; PIXA *pixa; PROCNAME("pixaSaveFont"); if (size < 4 || size > 20 || (size % 2)) return ERROR_INT("size must be in {4, 6, ..., 20}", procName, 1); if ((pixa = pixaGenerateFont(indir, size, &bl1, &bl2, &bl3)) == NULL) return ERROR_INT("pixa not made", procName, 1); pathname = genPathname(outdir, outputfonts[(size - 4) / 2]); pixaWrite(pathname, pixa); #if DEBUG_FONT_GEN fprintf(stderr, "Found %d chars in font size %d\n", pixaGetCount(pixa), size); fprintf(stderr, "Baselines are at: %d, %d, %d\n", bl1, bl2, bl3); #endif /* DEBUG_FONT_GEN */ FREE(pathname); pixaDestroy(&pixa); return 0; }
/** * Test whether a finalized cluster is valid. */ bool ValidateCluster(PIX *pix8, PIXA *pixa, BOX *box, l_float32 *pconf, HydrogenTextDetector::TextDetectorParameters ¶ms) { *pconf = 0.0; l_float32 aspect = box->w / (l_float32) box->h; l_int32 count = pixaGetCount(pixa); l_float32 fdr = ComputeFDR(pix8); if (box->h < 15) return false; if (aspect < params.cluster_min_aspect) return false; if (count < params.cluster_min_blobs) return false; if (fdr < params.cluster_min_fdr) return false; /* l_int32 edge_max, edge_avg; pixEdgeMax(pix8, &edge_max, &edge_avg); if (edge_max < params.cluster_min_edge || edge_avg < params.cluster_min_edge_avg) return false; */ // TODO(alanv): Combine all of these into a confidence score, higher = better *pconf = log(fdr); //log(fdr * edge_max * edge_avg); return true; }
/*! * pixaInsertPix() * * Input: pixa * index (at which pix is to be inserted) * pixs (new pix to be inserted) * box (<optional> new box to be inserted) * Return: 0 if OK, 1 on error * * Notes: * (1) This shifts pixa[i] --> pixa[i + 1] for all i >= index, * and then inserts at pixa[index]. * (2) To insert at the beginning of the array, set index = 0. * (3) It should not be used repeatedly on large arrays, * because the function is O(n). * (4) To append a pix to a pixa, it's easier to use pixaAddPix(). */ l_int32 pixaInsertPix(PIXA *pixa, l_int32 index, PIX *pixs, BOX *box) { l_int32 i, n; PROCNAME("pixaInsertPix"); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); if (index < 0 || index > n) return ERROR_INT("index not in {0...n}", procName, 1); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (n >= pixa->nalloc) { /* extend both ptr arrays */ pixaExtendArray(pixa); boxaExtendArray(pixa->boxa); } pixa->n++; for (i = n; i > index; i--) pixa->pix[i] = pixa->pix[i - 1]; pixa->pix[index] = pixs; /* Optionally, insert the box */ if (box) boxaInsertBox(pixa->boxa, index, box); return 0; }
/*! * pixaRemovePix() * * Input: pixa * index (of pix to be removed) * Return: 0 if OK, 1 on error * * Notes: * (1) This shifts pixa[i] --> pixa[i - 1] for all i > index. * (2) It should not be used repeatedly on large arrays, * because the function is O(n). * (3) The corresponding box is removed as well, if it exists. */ l_int32 pixaRemovePix(PIXA *pixa, l_int32 index) { l_int32 i, n, nbox; BOXA *boxa; PIX **array; PROCNAME("pixaRemovePix"); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); if (index < 0 || index >= n) return ERROR_INT("index not in {0...n - 1}", procName, 1); /* Remove the pix */ array = pixa->pix; pixDestroy(&array[index]); for (i = index + 1; i < n; i++) array[i - 1] = array[i]; array[n - 1] = NULL; pixa->n--; /* Remove the box if it exists */ boxa = pixa->boxa; nbox = boxaGetCount(boxa); if (index < nbox) boxaRemoveBox(boxa, index); return 0; }
/*! * pixaAddPix() * * Input: pixa * pix (to be added) * copyflag (L_INSERT, L_COPY, L_CLONE) * Return: 0 if OK; 1 on error */ l_int32 pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag) { l_int32 n; PIX *pixc; PROCNAME("pixaAddPix"); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); if (!pix) return ERROR_INT("pix not defined", procName, 1); if (copyflag == L_INSERT) pixc = pix; else if (copyflag == L_COPY) pixc = pixCopy(NULL, pix); else if (copyflag == L_CLONE) pixc = pixClone(pix); else return ERROR_INT("invalid copyflag", procName, 1); if (!pixc) return ERROR_INT("pixc not made", procName, 1); n = pixaGetCount(pixa); if (n >= pixa->nalloc) pixaExtendArray(pixa); pixa->pix[n] = pixc; pixa->n++; return 0; }
/*! * pixaWriteStream() * * Input: stream * pixa * Return: 0 if OK, 1 on error */ l_int32 pixaWriteStream(FILE *fp, PIXA *pixa) { l_int32 n, i; PIX *pix; PROCNAME("pixaWriteStream"); #if !HAVE_LIBPNG /* defined in environ.h */ return ERROR_INT("no libpng: can't write data", procName, 1); #else if (!fp) return ERROR_INT("stream not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); fprintf(fp, "\nPixa Version %d\n", PIXA_VERSION_NUMBER); fprintf(fp, "Number of pix = %d\n", n); boxaWriteStream(fp, pixa->boxa); for (i = 0; i < n; i++) { if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) return ERROR_INT("pix not found", procName, 1); fprintf(fp, " pix[%d]: xres = %d, yres = %d\n", i, pix->xres, pix->yres); pixWriteStreamPng(fp, pix, 0.0); pixDestroy(&pix); } return 0; #endif /* !HAVE_LIBPNG */ }
/*! * wshedRenderFill() * * Input: wshed * Return: pixd (initial image with all basins filled), or null on error */ PIX * wshedRenderFill(L_WSHED *wshed) { l_int32 i, n, level, bx, by; NUMA *na; PIX *pix, *pixd; PIXA *pixa; PROCNAME("wshedRenderFill"); if (!wshed) return (PIX *) ERROR_PTR("wshed not defined", procName, NULL); wshedBasins(wshed, &pixa, &na); pixd = pixCopy(NULL, wshed->pixs); n = pixaGetCount(pixa); for (i = 0; i < n; i++) { pix = pixaGetPix(pixa, i, L_CLONE); pixaGetBoxGeometry(pixa, i, &bx, &by, NULL, NULL); numaGetIValue(na, i, &level); pixPaintThroughMask(pixd, pix, bx, by, level); pixDestroy(&pix); } pixaDestroy(&pixa); numaDestroy(&na); return pixd; }
/*! * recogAddAllSamples() * * Input: recog * paa (pixaa from previously trained recog) * debug * Return: 0 if OK, 1 on error * * Notes: * (1) This is used with the serialization routine recogRead(), * where each pixa in the pixaa represents a set of characters * in a different class. Two different pixa may represent * characters with the same label. Before calling this * function, we verify that the number of character classes, * given by the setsize field in recog, equals the number of * pixa in the paa. The character labels for each set are * in the sa_text field. */ static l_int32 recogAddAllSamples(L_RECOG *recog, PIXAA *paa, l_int32 debug) { char *text; l_int32 i, j, nc, ns; PIX *pix; PIXA *pixa; PROCNAME("recogAddAllSamples"); if (!recog) return ERROR_INT("recog not defined", procName, 1); if (!paa) return ERROR_INT("paa not defined", procName, 1); nc = pixaaGetCount(paa, NULL); for (i = 0; i < nc; i++) { pixa = pixaaGetPixa(paa, i, L_CLONE); ns = pixaGetCount(pixa); text = sarrayGetString(recog->sa_text, i, L_NOCOPY); for (j = 0; j < ns; j++) { pix = pixaGetPix(pixa, j, L_CLONE); if (debug) { fprintf(stderr, "pix[%d,%d]: text = %s\n", i, j, text); } pixaaAddPix(recog->pixaa_u, i, pix, NULL, L_INSERT); } pixaDestroy(&pixa); } recogTrainingFinished(recog, debug); return 0; }
void scriptDetect(Pixa* pixa, string fileName){ int n = pixaGetCount(pixa); Pix* page = pixaGetPix(pixa, n-1, L_CLONE); l_float32 conf; string language = detectLanguage(page, &conf); printf("%s = %s (%.2f)\n", fileName.c_str(), language.c_str(), conf); }
/*! * 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); pixCopyResolution(pixd, pixs); pixCopyColormap(pixd, pixs); pixCopyText(pixd, pixs); pixCopyInputFormat(pixd, pixs); } pixaDestroy(&pixad); return pixd; } }
/*! * pixaaCreateFromPixa() * * Input: pixa * n (number specifying subdivision of pixa) * type (L_CHOOSE_CONSECUTIVE, L_CHOOSE_SKIP_BY) * copyflag (L_CLONE, L_COPY) * Return: pixaa, or null on error * * Notes: * (1) This subdivides a pixa into a set of smaller pixa that * are accumulated into a pixaa. * (2) If type == L_CHOOSE_CONSECUTIVE, the first 'n' pix are * put in a pixa and added to pixaa, then the next 'n', etc. * If type == L_CHOOSE_SKIP_BY, the first pixa is made by * aggregating pix[0], pix[n], pix[2*n], etc. * (3) The copyflag specifies if each new pix is a copy or a clone. */ PIXAA * pixaaCreateFromPixa(PIXA *pixa, l_int32 n, l_int32 type, l_int32 copyflag) { l_int32 count, i, j, npixa; PIX *pix; PIXA *pixat; PIXAA *pixaa; PROCNAME("pixaaCreateFromPixa"); if (!pixa) return (PIXAA *)ERROR_PTR("pixa not defined", procName, NULL); count = pixaGetCount(pixa); if (count == 0) return (PIXAA *)ERROR_PTR("no pix in pixa", procName, NULL); if (n <= 0) return (PIXAA *)ERROR_PTR("n must be > 0", procName, NULL); if (type != L_CHOOSE_CONSECUTIVE && type != L_CHOOSE_SKIP_BY) return (PIXAA *)ERROR_PTR("invalid type", procName, NULL); if (copyflag != L_CLONE && copyflag != L_COPY) return (PIXAA *)ERROR_PTR("invalid copyflag", procName, NULL); if (type == L_CHOOSE_CONSECUTIVE) npixa = (count + n - 1) / n; else /* L_CHOOSE_SKIP_BY */ npixa = L_MIN(n, count); pixaa = pixaaCreate(npixa); if (type == L_CHOOSE_CONSECUTIVE) { for (i = 0; i < count; i++) { if (i % n == 0) pixat = pixaCreate(n); pix = pixaGetPix(pixa, i, copyflag); pixaAddPix(pixat, pix, L_INSERT); if (i % n == n - 1) pixaaAddPixa(pixaa, pixat, L_INSERT); } if (i % n != 0) pixaaAddPixa(pixaa, pixat, L_INSERT); } else { /* L_CHOOSE_SKIP_BY */ for (i = 0; i < npixa; i++) { pixat = pixaCreate(count / npixa + 1); for (j = i; j < count; j += n) { pix = pixaGetPix(pixa, j, copyflag); pixaAddPix(pixat, pix, L_INSERT); } pixaaAddPixa(pixaa, pixat, L_INSERT); } } return pixaa; }
/*! * pixaDisplay() * * Input: pixa * w, h (if set to 0, determines the size from the * b.b. of the components in pixa) * Return: pix, or null on error * * Notes: * (1) This uses the boxes to place each pix in the rendered composite. * (2) Set w = h = 0 to use the b.b. of the components to determine * the size of the returned pix. * (3) Uses the first pix in pixa to determine the depth. * (4) The background is written "white". On 1 bpp, each successive * pix is "painted" (adding foreground), whereas for grayscale * or color each successive pix is blitted with just the src. * (5) If the pixa is empty, returns an empty 1 bpp pix. */ PIX * pixaDisplay(PIXA *pixa, l_int32 w, l_int32 h) { l_int32 i, n, d, xb, yb, wb, hb; BOXA *boxa; PIX *pixt, *pixd; PROCNAME("pixaDisplay"); if (!pixa) return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); n = pixaGetCount(pixa); if (n == 0 && w == 0 && h == 0) return (PIX *)ERROR_PTR("no components; no size", procName, NULL); if (n == 0) { L_WARNING("no components; returning empty 1 bpp pix", procName); return pixCreate(w, h, 1); } /* If w and h not input, determine the minimum size required * to contain the origin and all c.c. */ if (w == 0 || h == 0) { boxa = pixaGetBoxa(pixa, L_CLONE); boxaGetExtent(boxa, &w, &h, NULL); boxaDestroy(&boxa); } /* Use the first pix in pixa to determine the depth. */ pixt = pixaGetPix(pixa, 0, L_CLONE); d = pixGetDepth(pixt); pixDestroy(&pixt); if ((pixd = pixCreate(w, h, d)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); if (d > 1) pixSetAll(pixd); for (i = 0; i < n; i++) { if (pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb)) { L_WARNING("no box found!", procName); continue; } pixt = pixaGetPix(pixa, i, L_CLONE); if (d == 1) pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pixt, 0, 0); else pixRasterop(pixd, xb, yb, wb, hb, PIX_SRC, pixt, 0, 0); pixDestroy(&pixt); } return pixd; }
/*! * pixaAddBorderGeneral() * * Input: pixad (can be null or equal to pixas) * pixas (containing pix of all depths; colormap ok) * left, right, top, bot (number of pixels added) * val (value of added border pixels) * Return: pixad (with border added to each pix), including on error * * Notes: * (1) For binary images: * white: val = 0 * black: val = 1 * For grayscale images: * white: val = 2 ** d - 1 * black: val = 0 * For rgb color images: * white: val = 0xffffff00 * black: val = 0 * For colormapped images, use 'index' found this way: * white: pixcmapGetRankIntensity(cmap, 1.0, &index); * black: pixcmapGetRankIntensity(cmap, 0.0, &index); * (2) For in-place replacement of each pix with a bordered version, * use @pixad = @pixas. To make a new pixa, use @pixad = NULL. * (3) In both cases, the boxa has sides adjusted as if it were * expanded by the border. */ PIXA * pixaAddBorderGeneral(PIXA *pixad, PIXA *pixas, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_uint32 val) { l_int32 i, n, nbox; BOX *box; BOXA *boxad; PIX *pixs, *pixd; PROCNAME("pixaAddBorderGeneral"); if (!pixas) return (PIXA *)ERROR_PTR("pixas not defined", procName, pixad); if (left < 0 || right < 0 || top < 0 || bot < 0) return (PIXA *)ERROR_PTR("negative border added!", procName, pixad); if (pixad && (pixad != pixas)) return (PIXA *)ERROR_PTR("pixad defined but != pixas", procName, pixad); n = pixaGetCount(pixas); if (!pixad) pixad = pixaCreate(n); for (i = 0; i < n; i++) { pixs = pixaGetPix(pixas, i, L_CLONE); pixd = pixAddBorderGeneral(pixs, left, right, top, bot, val); if (pixad == pixas) /* replace */ pixaReplacePix(pixad, i, pixd, NULL); else pixaAddPix(pixad, pixd, L_INSERT); pixDestroy(&pixs); } nbox = pixaGetBoxaCount(pixas); boxad = pixaGetBoxa(pixad, L_CLONE); for (i = 0; i < nbox; i++) { if ((box = pixaGetBox(pixas, i, L_COPY)) == NULL) { L_WARNING_INT("box %d not found", procName, i); break; } boxAdjustSides(box, box, -left, right, -top, bot); if (pixad == pixas) /* replace */ boxaReplaceBox(boxad, i, box); else boxaAddBox(boxad, box, L_INSERT); } boxaDestroy(&boxad); return pixad; }
/*! * pixaDisplayRandomCmap() * * Input: pixa (of 1 bpp components, with boxa) * w, h (if set to 0, determines the size from the * b.b. of the components in pixa) * Return: pix (8 bpp, cmapped, with random colors on the components), * or null on error * * Notes: * (1) This uses the boxes to place each pix in the rendered composite. * (2) By default, the background color is: black, cmap index 0. * This can be changed by pixcmapResetColor() */ PIX * pixaDisplayRandomCmap(PIXA *pixa, l_int32 w, l_int32 h) { l_int32 i, n, d, index, xb, yb, wb, hb; BOXA *boxa; PIX *pixs, *pixt, *pixd; PIXCMAP *cmap; PROCNAME("pixaDisplayRandomCmap"); if (!pixa) return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); n = pixaGetCount(pixa); if (n == 0) return (PIX *)ERROR_PTR("no components", procName, NULL); /* Use the first pix in pixa to verify depth is 1 bpp */ pixs = pixaGetPix(pixa, 0, L_CLONE); d = pixGetDepth(pixs); pixDestroy(&pixs); if (d != 1) return (PIX *)ERROR_PTR("components not 1 bpp", procName, NULL); /* If w and h not input, determine the minimum size required * to contain the origin and all c.c. */ if (w == 0 || h == 0) { boxa = pixaGetBoxa(pixa, L_CLONE); boxaGetExtent(boxa, &w, &h, NULL); boxaDestroy(&boxa); } /* Set up an 8 bpp dest pix, with a colormap with 254 random colors */ if ((pixd = pixCreate(w, h, 8)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); cmap = pixcmapCreateRandom(8, 1, 1); pixSetColormap(pixd, cmap); /* Color each component and blit it in */ for (i = 0; i < n; i++) { index = 1 + (i % 254); pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb); pixs = pixaGetPix(pixa, i, L_CLONE); pixt = pixConvert1To8(NULL, pixs, 0, index); pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pixt, 0, 0); pixDestroy(&pixs); pixDestroy(&pixt); } return pixd; }
/*! * bilateralApply() * * Input: bil * Return: pixd */ static PIX * bilateralApply(L_BILATERAL *bil) { l_int32 i, j, k, ired, jred, w, h, wpls, wpld, ncomps, reduction; l_int32 vals, vald, lowval, hival; l_int32 *kindex; l_float32 fract; l_float32 *kfract; l_uint32 *lines, *lined, *datas, *datad; l_uint32 ***lineset = NULL; /* for set of PBC */ PIX *pixs, *pixd; PIXA *pixac; PROCNAME("bilateralApply"); if (!bil) return (PIX *)ERROR_PTR("bil not defined", procName, NULL); pixs = bil->pixs; ncomps = bil->ncomps; kindex = bil->kindex; kfract = bil->kfract; reduction = bil->reduction; pixac = bil->pixac; lineset = bil->lineset; if (pixaGetCount(pixac) != ncomps) return (PIX *)ERROR_PTR("PBC images do not exist", procName, NULL); if ((pixd = pixCreateTemplate(pixs)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); datas = pixGetData(pixs); wpls = pixGetWpl(pixs); datad = pixGetData(pixd); wpld = pixGetWpl(pixd); pixGetDimensions(pixs, &w, &h, NULL); for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + i * wpld; ired = i / reduction; for (j = 0; j < w; j++) { jred = j / reduction; vals = GET_DATA_BYTE(lines, j); k = kindex[vals]; lowval = GET_DATA_BYTE(lineset[k][ired], jred); hival = GET_DATA_BYTE(lineset[k + 1][ired], jred); fract = kfract[vals]; vald = (l_int32)((1.0 - fract) * lowval + fract * hival + 0.5); SET_DATA_BYTE(lined, j, vald); } } return pixd; }
void writeLastPix(Pixa* pixa, string fileName) { ostringstream outFileName; std::string base_filename = fileName.substr(fileName.find_last_of("/\\") + 1); std::string::size_type const p(base_filename.find_last_of('.')); std::string file_without_extension = base_filename.substr(0, p); outFileName << "last_" << file_without_extension << ".png"; int n = pixaGetCount(pixa); Pix* page = pixaGetPix(pixa, n-1, L_CLONE); pixWrite(outFileName.str().c_str(), page, IFF_PNG); pixDestroy(&page); }