/*! * lheapDestroy() * * Input: &lheap (<to be nulled>) * freeflag (TRUE to free each remaining struct in the array) * Return: void * * Notes: * (1) Use freeflag == TRUE when the items in the array can be * simply destroyed using free. If those items require their * own destroy function, they must be destroyed before * calling this function, and then this function is called * with freeflag == FALSE. * (2) To destroy the lheap, we destroy the ptr array, then * the lheap, and then null the contents of the input ptr. */ void lheapDestroy(L_HEAP **plh, l_int32 freeflag) { l_int32 i; L_HEAP *lh; PROCNAME("lheapDestroy"); if (plh == NULL) { L_WARNING("ptr address is NULL", procName); return; } if ((lh = *plh) == NULL) return; if (freeflag) { /* free each struct in the array */ for (i = 0; i < lh->n; i++) FREE(lh->array[i]); } else if (lh->n > 0) /* freeflag == FALSE but elements exist on array */ L_WARNING_INT("memory leak of %d items in lheap!", procName, lh->n); if (lh->array) FREE(lh->array); FREE(lh); *plh = NULL; return; }
/*! * lqueueDestroy() * * Input: &lqueue (<to be nulled>) * freeflag (TRUE to free each remaining struct in the array) * Return: void * * Notes: * (1) If freeflag is TRUE, frees each struct in the array. * (2) If freeflag is FALSE but there are elements on the array, * gives a warning and destroys the array. This will * cause a memory leak of all the items that were on the queue. * So if the items require their own destroy function, they * must be destroyed before the queue. The same applies to the * auxiliary stack, if it is used. * (3) To destroy the L_Queue, we destroy the ptr array, then * the lqueue, and then null the contents of the input ptr. */ void lqueueDestroy(L_QUEUE **plq, l_int32 freeflag) { void *item; L_QUEUE *lq; PROCNAME("lqueueDestroy"); if (plq == NULL) { L_WARNING("ptr address is NULL", procName); return; } if ((lq = *plq) == NULL) return; if (freeflag) { while(lq->nelem > 0) { item = lqueueRemove(lq); FREE(item); } } else if (lq->nelem > 0) L_WARNING_INT("memory leak of %d items in lqueue!", procName, lq->nelem); if (lq->array) FREE(lq->array); if (lq->stack) lstackDestroy(&lq->stack, freeflag); FREE(lq); *plq = NULL; return; }
/*! * 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; }
/*! * pixColorSegmentCluster() * * Input: pixs (32 bpp; 24-bit color) * maxdist (max euclidean dist to existing cluster) * maxcolors (max number of colors allowed in first pass) * Return: pixd (8 bit with colormap), or null on error * * Notes: * (1) This is phase 1. See description in pixColorSegment(). * (2) Greedy unsupervised classification. If the limit 'maxcolors' * is exceeded, the computation is repeated with a larger * allowed cluster size. * (3) On each successive iteration, 'maxdist' is increased by a * constant factor. See comments in pixColorSegment() for * a guideline on parameter selection. * Note that the diagonal of the 8-bit rgb color cube is about * 440, so for 'maxdist' = 440, you are guaranteed to get 1 color! */ PIX * pixColorSegmentCluster(PIX *pixs, l_int32 maxdist, l_int32 maxcolors) { l_int32 w, h, newmaxdist, ret, niters, ncolors, success; PIX *pixd; PIXCMAP *cmap; PROCNAME("pixColorSegmentCluster"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 32) return (PIX *)ERROR_PTR("must be rgb color", procName, NULL); w = pixGetWidth(pixs); h = pixGetHeight(pixs); if ((pixd = pixCreate(w, h, 8)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); cmap = pixcmapCreate(8); pixSetColormap(pixd, cmap); pixCopyResolution(pixd, pixs); newmaxdist = maxdist; niters = 0; success = TRUE; while (1) { ret = pixColorSegmentTryCluster(pixd, pixs, newmaxdist, maxcolors); niters++; if (!ret) { ncolors = pixcmapGetCount(cmap); L_INFO_INT2("Success with %d colors after %d iters", procName, ncolors, niters); break; } if (niters == MAX_ALLOWED_ITERATIONS) { L_WARNING_INT("too many iters; newmaxdist = %d", procName, newmaxdist); success = FALSE; break; } newmaxdist = (l_int32)(DIST_EXPAND_FACT * (l_float32)newmaxdist); } if (!success) { pixDestroy(&pixd); return (PIX *)ERROR_PTR("failure in phase 1", procName, NULL); } return pixd; }
/*! * ptraDestroy() * * Input: &ptra (<to be nulled>) * freeflag (TRUE to free each remaining item in the array) * warnflag (TRUE to warn if any remaining items are not destroyed) * Return: void * * Notes: * (1) If @freeflag == TRUE, frees each item in the array. * (2) If @freeflag == FALSE and warnflag == TRUE, and there are * items on the array, this gives a warning and destroys the array. * If these items are not owned elsewhere, this will cause * a memory leak of all the items that were on the array. * So if the items are not owned elsewhere and require their * own destroy function, they must be destroyed before the ptra. * (3) If warnflag == FALSE, no warnings will be issued. This is * useful if the items are owned elsewhere, such as a * PixMemoryStore(). * (4) To destroy the ptra, we destroy the ptr array, then * the ptra, and then null the contents of the input ptr. */ void ptraDestroy(L_PTRA **ppa, l_int32 freeflag, l_int32 warnflag) { l_int32 i, nactual; void *item; L_PTRA *pa; PROCNAME("ptraDestroy"); if (ppa == NULL) { L_WARNING("ptr address is NULL", procName); return; } if ((pa = *ppa) == NULL) return; ptraGetActualCount(pa, &nactual); if (nactual > 0) { if (freeflag) { for (i = 0; i <= pa->imax; i++) { if ((item = ptraRemove(pa, i, L_NO_COMPACTION)) != NULL) FREE(item); } } else if (warnflag) L_WARNING_INT("potential memory leak of %d items in ptra", procName, nactual); } FREE(pa->array); FREE(pa); *ppa = NULL; return; }
/*! * pixaDisplayTiledInRows() * * Input: pixa * outdepth (output depth: 1, 8 or 32 bpp) * maxwidth (of output image) * scalefactor (applied to every pix; use 1.0 for no scaling) * background (0 for white, 1 for black; this is the color * of the spacing between the images) * spacing (between images, and on outside) * border (width of black border added to each image; * use 0 for no border) * Return: pixd (of tiled images), or null on error * * Notes: * (1) This saves a pixa to a single image file of width not to * exceed maxwidth, with background color either white or black, * and with each row tiled such that the top of each pix is * aligned and separated by 'spacing' from the next one. * A black border can be added to each pix. * (2) All pix are converted to outdepth; existing colormaps are removed. * (3) This does a reasonably spacewise-efficient job of laying * out the individual pix images into a tiled composite. */ PIX * pixaDisplayTiledInRows(PIXA *pixa, l_int32 outdepth, l_int32 maxwidth, l_float32 scalefactor, l_int32 background, l_int32 spacing, l_int32 border) { l_int32 h; /* cumulative height over all the rows */ l_int32 w; /* cumulative height in the current row */ l_int32 bordval, wtry, wt, ht; l_int32 irow; /* index of current pix in current row */ l_int32 wmaxrow; /* width of the largest row */ l_int32 maxh; /* max height in row */ l_int32 i, j, index, n, x, y, nrows, ninrow; NUMA *nainrow; /* number of pix in the row */ NUMA *namaxh; /* height of max pix in the row */ PIX *pix, *pixn, *pixt, *pixd; PIXA *pixan; PROCNAME("pixaDisplayTiledInRows"); if (!pixa) return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); if (outdepth != 1 && outdepth != 8 && outdepth != 32) return (PIX *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL); if (border < 0) border = 0; if (scalefactor <= 0.0) scalefactor = 1.0; if ((n = pixaGetCount(pixa)) == 0) return (PIX *)ERROR_PTR("no components", procName, NULL); /* Normalize depths, scale, remove colormaps; optionally add border */ pixan = pixaCreate(n); bordval = (outdepth == 1) ? 1 : 0; for (i = 0; i < n; i++) { if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) continue; if (outdepth == 1) pixn = pixConvertTo1(pix, 128); else if (outdepth == 8) pixn = pixConvertTo8(pix, FALSE); else /* outdepth == 32 */ pixn = pixConvertTo32(pix); pixDestroy(&pix); if (scalefactor != 1.0) pixt = pixScale(pixn, scalefactor, scalefactor); else pixt = pixClone(pixn); if (border) pixd = pixAddBorder(pixt, border, bordval); else pixd = pixClone(pixt); pixDestroy(&pixn); pixDestroy(&pixt); pixaAddPix(pixan, pixd, L_INSERT); } if (pixaGetCount(pixan) != n) { n = pixaGetCount(pixan); L_WARNING_INT("only got %d components", procName, n); if (n == 0) { pixaDestroy(&pixan); return (PIX *)ERROR_PTR("no components", procName, NULL); } } /* Compute parameters for layout */ nainrow = numaCreate(0); namaxh = numaCreate(0); wmaxrow = 0; w = h = spacing; maxh = 0; /* max height in row */ for (i = 0, irow = 0; i < n; i++, irow++) { pixaGetPixDimensions(pixan, i, &wt, &ht, NULL); wtry = w + wt + spacing; if (wtry > maxwidth) { /* end the current row and start next one */ numaAddNumber(nainrow, irow); numaAddNumber(namaxh, maxh); wmaxrow = L_MAX(wmaxrow, w); h += maxh + spacing; irow = 0; w = wt + 2 * spacing; maxh = ht; } else { w = wtry; maxh = L_MAX(maxh, ht); } } /* Enter the parameters for the last row */ numaAddNumber(nainrow, irow); numaAddNumber(namaxh, maxh); wmaxrow = L_MAX(wmaxrow, w); h += maxh + spacing; if ((pixd = pixCreate(wmaxrow, h, outdepth)) == NULL) { numaDestroy(&nainrow); numaDestroy(&namaxh); pixaDestroy(&pixan); return (PIX *)ERROR_PTR("pixd not made", procName, NULL); } /* Reset the background color if necessary */ if ((background == 1 && outdepth == 1) || (background == 0 && outdepth != 1)) pixSetAll(pixd); /* Blit the images to the dest */ nrows = numaGetCount(nainrow); y = spacing; for (i = 0, index = 0; i < nrows; i++) { /* over rows */ numaGetIValue(nainrow, i, &ninrow); numaGetIValue(namaxh, i, &maxh); x = spacing; for (j = 0; j < ninrow; j++, index++) { /* over pix in row */ pix = pixaGetPix(pixan, index, L_CLONE); pixGetDimensions(pix, &wt, &ht, NULL); pixRasterop(pixd, x, y, wt, ht, PIX_SRC, pix, 0, 0); pixDestroy(&pix); x += wt + spacing; } y += maxh + spacing; } numaDestroy(&nainrow); numaDestroy(&namaxh); pixaDestroy(&pixan); return pixd; }
/*! * expandBinaryPower2Low() */ l_int32 expandBinaryPower2Low(l_uint32 *datad, l_int32 wd, l_int32 hd, l_int32 wpld, l_uint32 *datas, l_int32 ws, l_int32 hs, l_int32 wpls, l_int32 factor) { l_int32 i, j, k, sdibits, sqbits, sbytes; l_uint8 sval; l_uint16 *tab2; l_uint32 *tab4, *tab8; l_uint32 *lines, *lined; PROCNAME("expandBinaryPower2Low"); switch (factor) { case 2: if ((tab2 = makeExpandTab2x()) == NULL) return ERROR_INT("tab2 not made", procName, 1); sbytes = (ws + 7) / 8; for (i = 0; i < hs; i++) { lines = datas + i * wpls; lined = datad + 2 * i * wpld; for (j = 0; j < sbytes; j++) { sval = GET_DATA_BYTE(lines, j); SET_DATA_TWO_BYTES(lined, j, tab2[sval]); } memcpy((char *)(lined + wpld), (char *)lined, 4 * wpld); } FREE(tab2); break; case 4: if ((tab4 = makeExpandTab4x()) == NULL) return ERROR_INT("tab4 not made", procName, 1); sbytes = (ws + 7) / 8; for (i = 0; i < hs; i++) { lines = datas + i * wpls; lined = datad + 4 * i * wpld; for (j = 0; j < sbytes; j++) { sval = GET_DATA_BYTE(lines, j); lined[j] = tab4[sval]; } for (k = 1; k < 4; k++) memcpy((char *)(lined + k * wpld), (char *)lined, 4 * wpld); } FREE(tab4); break; case 8: if ((tab8 = makeExpandTab8x()) == NULL) return ERROR_INT("tab8 not made", procName, 1); sqbits = (ws + 3) / 4; for (i = 0; i < hs; i++) { lines = datas + i * wpls; lined = datad + 8 * i * wpld; for (j = 0; j < sqbits; j++) { sval = GET_DATA_QBIT(lines, j); if (sval > 15) L_WARNING_INT("sval = %d; should be < 16", procName, sval); lined[j] = tab8[sval]; } for (k = 1; k < 8; k++) memcpy((char *)(lined + k * wpld), (char *)lined, 4 * wpld); } FREE(tab8); break; case 16: sdibits = (ws + 1) / 2; for (i = 0; i < hs; i++) { lines = datas + i * wpls; lined = datad + 16 * i * wpld; for (j = 0; j < sdibits; j++) { sval = GET_DATA_DIBIT(lines, j); lined[j] = expandtab16[sval]; } for (k = 1; k < 16; k++) memcpy((char *)(lined + k * wpld), (char *)lined, 4 * wpld); } break; default: return ERROR_INT("expansion factor not in {2,4,8,16}", procName, 1); } return 0; }
/*! * pixSauvolaBinarizeTiled() * * Input: pixs (8 bpp grayscale, not colormapped) * whsize (window half-width for measuring local statistics) * factor (factor for reducing threshold due to variance; >= 0) * nx, ny (subdivision into tiles; >= 1) * &pixth (<optional return> Sauvola threshold values) * &pixd (<optional return> thresholded image) * Return: 0 if OK, 1 on error * * Notes: * (1) The window width and height are 2 * @whsize + 1. The minimum * value for @whsize is 2; typically it is >= 7.. * (2) For nx == ny == 1, this defaults to pixSauvolaBinarize(). * (3) Why a tiled version? * (a) Because the mean value accumulator is a uint32, overflow * can occur for an image with more than 16M pixels. * (b) The mean value accumulator array for 16M pixels is 64 MB. * The mean square accumulator array for 16M pixels is 128 MB. * Using tiles reduces the size of these arrays. * (c) Each tile can be processed independently, in parallel, * on a multicore processor. * (4) The Sauvola threshold is determined from the formula: * t = m * (1 - k * (1 - s / 128)) * See pixSauvolaBinarize() for details. */ l_int32 pixSauvolaBinarizeTiled(PIX *pixs, l_int32 whsize, l_float32 factor, l_int32 nx, l_int32 ny, PIX **ppixth, PIX **ppixd) { l_int32 i, j, w, h, xrat, yrat; PIX *pixth, *pixd, *tileth, *tiled, *pixt; PIX **ptileth, **ptiled; PIXTILING *pt; PROCNAME("pixSauvolaBinarizeTiled"); if (!ppixth && !ppixd) return ERROR_INT("no outputs", procName, 1); if (ppixth) *ppixth = NULL; if (ppixd) *ppixd = NULL; if (!pixs || pixGetDepth(pixs) != 8) return ERROR_INT("pixs undefined or not 8 bpp", procName, 1); if (pixGetColormap(pixs)) return ERROR_INT("pixs is cmapped", procName, 1); pixGetDimensions(pixs, &w, &h, NULL); if (whsize < 2) return ERROR_INT("whsize must be >= 2", procName, 1); if (w < 2 * whsize + 3 || h < 2 * whsize + 3) return ERROR_INT("whsize too large for image", procName, 1); if (factor < 0.0) return ERROR_INT("factor must be >= 0", procName, 1); if (nx <= 1 && ny <= 1) return pixSauvolaBinarize(pixs, whsize, factor, 1, NULL, NULL, ppixth, ppixd); /* Test to see if the tiles are too small. The required * condition is that the tile dimensions must be at least * (whsize + 2) x (whsize + 2). */ xrat = w / nx; yrat = h / ny; if (xrat < whsize + 2) { nx = w / (whsize + 2); L_WARNING_INT("tile width too small; nx reduced to %d", procName, nx); } if (yrat < whsize + 2) { ny = h / (whsize + 2); L_WARNING_INT("tile height too small; ny reduced to %d", procName, ny); } if (nx <= 1 && ny <= 1) return pixSauvolaBinarize(pixs, whsize, factor, 1, NULL, NULL, ppixth, ppixd); /* We can use pixtiling for painting both outputs, if requested */ if (ppixth) { pixth = pixCreateNoInit(w, h, 8); *ppixth = pixth; } if (ppixd) { pixd = pixCreateNoInit(w, h, 1); *ppixd = pixd; } pt = pixTilingCreate(pixs, nx, ny, 0, 0, whsize + 1, whsize + 1); pixTilingNoStripOnPaint(pt); /* pixSauvolaBinarize() does the stripping */ for (i = 0; i < ny; i++) { for (j = 0; j < nx; j++) { pixt = pixTilingGetTile(pt, i, j); ptileth = (ppixth) ? &tileth : NULL; ptiled = (ppixd) ? &tiled : NULL; pixSauvolaBinarize(pixt, whsize, factor, 0, NULL, NULL, ptileth, ptiled); if (ppixth) { /* do not strip */ pixTilingPaintTile(pixth, i, j, tileth, pt); pixDestroy(&tileth); } if (ppixd) { pixTilingPaintTile(pixd, i, j, tiled, pt); pixDestroy(&tiled); } pixDestroy(&pixt); } } pixTilingDestroy(&pt); return 0; }
/*! * sarrayMakeInnerLoopDWACode() */ static SARRAY * sarrayMakeInnerLoopDWACode(SEL *sel, l_int32 index) { char *tstr, *string; char logicalor[] = "|"; char logicaland[] = "&"; char bigbuf[BUFFER_SIZE]; l_int32 i, j, optype, count, nfound, delx, dely; SARRAY *sa; PROCNAME("sarrayMakeInnerLoopDWACode"); if (!sel) return (SARRAY *)ERROR_PTR("sel not defined", procName, NULL); if (index % 2 == 0) { optype = L_MORPH_DILATE; tstr = logicalor; } else { optype = L_MORPH_ERODE; tstr = logicaland; } count = 0; for (i = 0; i < sel->sy; i++) { for (j = 0; j < sel->sx; j++) { if (sel->data[i][j] == 1) count++; } } if ((sa = sarrayCreate(0)) == NULL) return (SARRAY *)ERROR_PTR("sa not made", procName, NULL); if (count == 0) { L_WARNING_INT("no hits in Sel %d", procName, index); return sa; /* no code inside! */ } nfound = 0; for (i = 0; i < sel->sy; i++) { for (j = 0; j < sel->sx; j++) { if (sel->data[i][j] == 1) { nfound++; if (optype == L_MORPH_DILATE) { dely = sel->cy - i; delx = sel->cx - j; } else if (optype == L_MORPH_ERODE) { dely = i - sel->cy; delx = j - sel->cx; } if ((string = makeBarrelshiftString(delx, dely)) == NULL) { L_WARNING("barrel shift string not made", procName); continue; } if (count == 1) /* just one item */ sprintf(bigbuf, " *dptr = %s;", string); else if (nfound == 1) sprintf(bigbuf, " *dptr = %s %s", string, tstr); else if (nfound < count) sprintf(bigbuf, " %s %s", string, tstr); else /* nfound == count */ sprintf(bigbuf, " %s;", string); sarrayAddString(sa, bigbuf, 1); FREE(string); } } } return sa; }