Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/leptonica/src/quadtree.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/leptonica/src/quadtree.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,685 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - Redistribution and use in source and binary forms, with or without + - modification, are permitted provided that the following conditions + - are met: + - 1. Redistributions of source code must retain the above copyright + - notice, this list of conditions and the following disclaimer. + - 2. Redistributions in binary form must reproduce the above + - copyright notice, this list of conditions and the following + - disclaimer in the documentation and/or other materials + - provided with the distribution. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY + - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *====================================================================*/ + +/*! + * \file quadtree.c + * <pre> + * + * Top level quadtree linear statistics + * l_int32 pixQuadtreeMean() + * l_int32 pixQuadtreeVariance() + * + * Statistics in an arbitrary rectangle + * l_int32 pixMeanInRectangle() + * l_int32 pixVarianceInRectangle() + * + * Quadtree regions + * BOXAA *boxaaQuadtreeRegions() + * + * Quadtree access + * l_int32 quadtreeGetParent() + * l_int32 quadtreeGetChildren() + * l_int32 quadtreeMaxLevels() + * + * Display quadtree + * PIX *fpixaDisplayQuadtree() + * + * + * There are many other statistical quantities that can be computed + * in a quadtree, such as rank values, and these can be added as + * the need arises. + * + * Similar results that can approximate a single level of the quadtree + * can be generated by pixGetAverageTiled(). There we specify the + * tile size over which the mean, mean square, and root variance + * are generated; the results are saved in a (reduced size) pix. + * Because the tile dimensions are integers, it is usually not possible + * to obtain tilings that are a power of 2, as required for quadtrees. + * </pre> + */ + +#ifdef HAVE_CONFIG_H +#include <config_auto.h> +#endif /* HAVE_CONFIG_H */ + +#include <math.h> +#include "allheaders.h" + +#ifndef NO_CONSOLE_IO +#define DEBUG_BOXES 0 +#endif /* !NO_CONSOLE_IO */ + + +/*----------------------------------------------------------------------* + * Top-level quadtree linear statistics * + *----------------------------------------------------------------------*/ +/*! + * \brief pixQuadtreeMean() + * + * \param[in] pixs 8 bpp, no colormap + * \param[in] nlevels in quadtree; max allowed depends on image size + * \param[in] pix_ma input mean accumulator; can be null + * \param[out] pfpixa mean values in quadtree + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) The returned fpixa has %nlevels of fpix, each containing + * the mean values at its level. Level 0 has a + * single value; level 1 has 4 values; level 2 has 16; etc. + * </pre> + */ +l_ok +pixQuadtreeMean(PIX *pixs, + l_int32 nlevels, + PIX *pix_ma, + FPIXA **pfpixa) +{ +l_int32 i, j, w, h, size, n; +l_float32 val; +BOX *box; +BOXA *boxa; +BOXAA *baa; +FPIX *fpix; +PIX *pix_mac; + + if (!pfpixa) + return ERROR_INT("&fpixa not defined", __func__, 1); + *pfpixa = NULL; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1); + pixGetDimensions(pixs, &w, &h, NULL); + if (nlevels > quadtreeMaxLevels(w, h)) + return ERROR_INT("nlevels too large for image", __func__, 1); + + if (!pix_ma) + pix_mac = pixBlockconvAccum(pixs); + else + pix_mac = pixClone(pix_ma); + if (!pix_mac) + return ERROR_INT("pix_mac not made", __func__, 1); + + if ((baa = boxaaQuadtreeRegions(w, h, nlevels)) == NULL) { + pixDestroy(&pix_mac); + return ERROR_INT("baa not made", __func__, 1); + } + + *pfpixa = fpixaCreate(nlevels); + for (i = 0; i < nlevels; i++) { + boxa = boxaaGetBoxa(baa, i, L_CLONE); + size = 1 << i; + n = boxaGetCount(boxa); /* n == size * size */ + fpix = fpixCreate(size, size); + for (j = 0; j < n; j++) { + box = boxaGetBox(boxa, j, L_CLONE); + pixMeanInRectangle(pixs, box, pix_mac, &val); + fpixSetPixel(fpix, j % size, j / size, val); + boxDestroy(&box); + } + fpixaAddFPix(*pfpixa, fpix, L_INSERT); + boxaDestroy(&boxa); + } + + pixDestroy(&pix_mac); + boxaaDestroy(&baa); + return 0; +} + + +/*! + * \brief pixQuadtreeVariance() + * + * \param[in] pixs 8 bpp, no colormap + * \param[in] nlevels in quadtree + * \param[in] pix_ma input mean accumulator; can be null + * \param[in] dpix_msa input mean square accumulator; can be null + * \param[out] pfpixa_v [optional] variance values in quadtree + * \param[out] pfpixa_rv [optional] root variance values in quadtree + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) The returned fpixav and fpixarv have %nlevels of fpix, + * each containing at the respective levels the variance + * and root variance values. + * </pre> + */ +l_ok +pixQuadtreeVariance(PIX *pixs, + l_int32 nlevels, + PIX *pix_ma, + DPIX *dpix_msa, + FPIXA **pfpixa_v, + FPIXA **pfpixa_rv) +{ +l_int32 i, j, w, h, size, n; +l_float32 var, rvar; +BOX *box; +BOXA *boxa; +BOXAA *baa; +FPIX *fpixv = NULL, *fpixrv = NULL; +PIX *pix_mac; /* copy of mean accumulator */ +DPIX *dpix_msac; /* msa clone */ + + if (!pfpixa_v && !pfpixa_rv) + return ERROR_INT("neither &fpixav nor &fpixarv defined", __func__, 1); + if (pfpixa_v) *pfpixa_v = NULL; + if (pfpixa_rv) *pfpixa_rv = NULL; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1); + pixGetDimensions(pixs, &w, &h, NULL); + if (nlevels > quadtreeMaxLevels(w, h)) + return ERROR_INT("nlevels too large for image", __func__, 1); + + if (!pix_ma) + pix_mac = pixBlockconvAccum(pixs); + else + pix_mac = pixClone(pix_ma); + if (!pix_mac) + return ERROR_INT("pix_mac not made", __func__, 1); + if (!dpix_msa) + dpix_msac = pixMeanSquareAccum(pixs); + else + dpix_msac = dpixClone(dpix_msa); + if (!dpix_msac) { + pixDestroy(&pix_mac); + return ERROR_INT("dpix_msac not made", __func__, 1); + } + + if ((baa = boxaaQuadtreeRegions(w, h, nlevels)) == NULL) { + pixDestroy(&pix_mac); + dpixDestroy(&dpix_msac); + return ERROR_INT("baa not made", __func__, 1); + } + + if (pfpixa_v) *pfpixa_v = fpixaCreate(nlevels); + if (pfpixa_rv) *pfpixa_rv = fpixaCreate(nlevels); + for (i = 0; i < nlevels; i++) { + boxa = boxaaGetBoxa(baa, i, L_CLONE); + size = 1 << i; + n = boxaGetCount(boxa); /* n == size * size */ + if (pfpixa_v) fpixv = fpixCreate(size, size); + if (pfpixa_rv) fpixrv = fpixCreate(size, size); + for (j = 0; j < n; j++) { + box = boxaGetBox(boxa, j, L_CLONE); + pixVarianceInRectangle(pixs, box, pix_mac, dpix_msac, &var, &rvar); + if (pfpixa_v) fpixSetPixel(fpixv, j % size, j / size, var); + if (pfpixa_rv) fpixSetPixel(fpixrv, j % size, j / size, rvar); + boxDestroy(&box); + } + if (pfpixa_v) fpixaAddFPix(*pfpixa_v, fpixv, L_INSERT); + if (pfpixa_rv) fpixaAddFPix(*pfpixa_rv, fpixrv, L_INSERT); + boxaDestroy(&boxa); + } + + pixDestroy(&pix_mac); + dpixDestroy(&dpix_msac); + boxaaDestroy(&baa); + return 0; +} + + +/*----------------------------------------------------------------------* + * Statistics in an arbitrary rectangle * + *----------------------------------------------------------------------*/ +/*! + * \brief pixMeanInRectangle() + * + * \param[in] pixs 8 bpp + * \param[in] box region to compute mean value + * \param[in] pixma mean accumulator + * \param[out] pval mean value + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This function is intended to be used for many rectangles + * on the same image. It can find the mean within a + * rectangle in O(1), independent of the size of the rectangle. + * </pre> + */ +l_ok +pixMeanInRectangle(PIX *pixs, + BOX *box, + PIX *pixma, + l_float32 *pval) +{ +l_int32 w, h, bx, by, bw, bh; +l_uint32 val00, val01, val10, val11; +l_float32 norm; +BOX *boxc; + + if (!pval) + return ERROR_INT("&val not defined", __func__, 1); + *pval = 0.0; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined", __func__, 1); + if (!box) + return ERROR_INT("box not defined", __func__, 1); + if (!pixma) + return ERROR_INT("pixma not defined", __func__, 1); + + /* Clip rectangle to image */ + pixGetDimensions(pixs, &w, &h, NULL); + boxc = boxClipToRectangle(box, w, h); + boxGetGeometry(boxc, &bx, &by, &bw, &bh); + boxDestroy(&boxc); + + if (bw == 0 || bh == 0) + return ERROR_INT("no pixels in box", __func__, 1); + + /* Use up to 4 points in the accumulator */ + norm = 1.0f / ((l_float32)(bw) * bh); + if (bx > 0 && by > 0) { + pixGetPixel(pixma, bx + bw - 1, by + bh - 1, &val11); + pixGetPixel(pixma, bx + bw - 1, by - 1, &val10); + pixGetPixel(pixma, bx - 1, by + bh - 1, &val01); + pixGetPixel(pixma, bx - 1, by - 1, &val00); + *pval = norm * (val11 - val01 + val00 - val10); + } else if (by > 0) { /* bx == 0 */ + pixGetPixel(pixma, bw - 1, by + bh - 1, &val11); + pixGetPixel(pixma, bw - 1, by - 1, &val10); + *pval = norm * (val11 - val10); + } else if (bx > 0) { /* by == 0 */ + pixGetPixel(pixma, bx + bw - 1, bh - 1, &val11); + pixGetPixel(pixma, bx - 1, bh - 1, &val01); + *pval = norm * (val11 - val01); + } else { /* bx == 0 && by == 0 */ + pixGetPixel(pixma, bw - 1, bh - 1, &val11); + *pval = norm * val11; + } + + return 0; +} + + +/*! + * \brief pixVarianceInRectangle() + * + * \param[in] pixs 8 bpp + * \param[in] box region to compute variance and/or root variance + * \param[in] pix_ma mean accumulator + * \param[in] dpix_msa mean square accumulator + * \param[out] pvar [optional] variance + * \param[out] prvar [optional] root variance + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This function is intended to be used for many rectangles + * on the same image. It can find the variance and/or the + * square root of the variance within a rectangle in O(1), + * independent of the size of the rectangle. + * </pre> + */ +l_ok +pixVarianceInRectangle(PIX *pixs, + BOX *box, + PIX *pix_ma, + DPIX *dpix_msa, + l_float32 *pvar, + l_float32 *prvar) +{ +l_int32 w, h, bx, by, bw, bh; +l_uint32 val00, val01, val10, val11; +l_float64 dval00, dval01, dval10, dval11, mval, msval, var, norm; +BOX *boxc; + + if (!pvar && !prvar) + return ERROR_INT("neither &var nor &rvar defined", __func__, 1); + if (pvar) *pvar = 0.0; + if (prvar) *prvar = 0.0; + if (!pixs || pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not defined", __func__, 1); + if (!box) + return ERROR_INT("box not defined", __func__, 1); + if (!pix_ma) + return ERROR_INT("pix_ma not defined", __func__, 1); + if (!dpix_msa) + return ERROR_INT("dpix_msa not defined", __func__, 1); + + /* Clip rectangle to image */ + pixGetDimensions(pixs, &w, &h, NULL); + boxc = boxClipToRectangle(box, w, h); + boxGetGeometry(boxc, &bx, &by, &bw, &bh); + boxDestroy(&boxc); + + if (bw == 0 || bh == 0) + return ERROR_INT("no pixels in box", __func__, 1); + + /* Use up to 4 points in the accumulators */ + norm = 1.0 / ((l_float32)(bw) * bh); + if (bx > 0 && by > 0) { + pixGetPixel(pix_ma, bx + bw - 1, by + bh - 1, &val11); + pixGetPixel(pix_ma, bx + bw - 1, by - 1, &val10); + pixGetPixel(pix_ma, bx - 1, by + bh - 1, &val01); + pixGetPixel(pix_ma, bx - 1, by - 1, &val00); + dpixGetPixel(dpix_msa, bx + bw - 1, by + bh - 1, &dval11); + dpixGetPixel(dpix_msa, bx + bw - 1, by - 1, &dval10); + dpixGetPixel(dpix_msa, bx - 1, by + bh - 1, &dval01); + dpixGetPixel(dpix_msa, bx - 1, by - 1, &dval00); + mval = norm * (val11 - val01 + val00 - val10); + msval = norm * (dval11 - dval01 + dval00 - dval10); + var = (msval - mval * mval); + if (pvar) *pvar = (l_float32)var; + if (prvar) *prvar = (l_float32)(sqrt(var)); + } else if (by > 0) { /* bx == 0 */ + pixGetPixel(pix_ma, bw - 1, by + bh - 1, &val11); + pixGetPixel(pix_ma, bw - 1, by - 1, &val10); + dpixGetPixel(dpix_msa, bw - 1, by + bh - 1, &dval11); + dpixGetPixel(dpix_msa, bw - 1, by - 1, &dval10); + mval = norm * (val11 - val10); + msval = norm * (dval11 - dval10); + var = (msval - mval * mval); + if (pvar) *pvar = (l_float32)var; + if (prvar) *prvar = (l_float32)(sqrt(var)); + } else if (bx > 0) { /* by == 0 */ + pixGetPixel(pix_ma, bx + bw - 1, bh - 1, &val11); + pixGetPixel(pix_ma, bx - 1, bh - 1, &val01); + dpixGetPixel(dpix_msa, bx + bw - 1, bh - 1, &dval11); + dpixGetPixel(dpix_msa, bx - 1, bh - 1, &dval01); + mval = norm * (val11 - val01); + msval = norm * (dval11 - dval01); + var = (msval - mval * mval); + if (pvar) *pvar = (l_float32)var; + if (prvar) *prvar = (l_float32)(sqrt(var)); + } else { /* bx == 0 && by == 0 */ + pixGetPixel(pix_ma, bw - 1, bh - 1, &val11); + dpixGetPixel(dpix_msa, bw - 1, bh - 1, &dval11); + mval = norm * val11; + msval = norm * dval11; + var = (msval - mval * mval); + if (pvar) *pvar = (l_float32)var; + if (prvar) *prvar = (l_float32)(sqrt(var)); + } + + return 0; +} + + +/*----------------------------------------------------------------------* + * Quadtree regions * + *----------------------------------------------------------------------*/ +/*! + * \brief boxaaQuadtreeRegions() + * + * \param[in] w, h size of pix that is being quadtree-ized + * \param[in] nlevels number of levels in quadtree + * \return baa for quadtree regions at each level, or NULL on error + * + * <pre> + * Notes: + * (1) The returned boxaa has %nlevels of boxa, each containing + * the set of rectangles at that level. The rectangle at + * level 0 is the entire region; at level 1 the region is + * divided into 4 rectangles, and at level n there are n^4 + * rectangles. + * (2) At each level, the rectangles in the boxa are in "raster" + * order, with LR (fast scan) and TB (slow scan). + * </pre> + */ +BOXAA * +boxaaQuadtreeRegions(l_int32 w, + l_int32 h, + l_int32 nlevels) +{ +l_int32 i, j, k, maxpts, nside, nbox, bw, bh; +l_int32 *xstart, *xend, *ystart, *yend; +BOX *box; +BOXA *boxa; +BOXAA *baa; + + if (nlevels < 1) + return (BOXAA *)ERROR_PTR("nlevels must be >= 1", __func__, NULL); + if (w < (1 << (nlevels - 1))) + return (BOXAA *)ERROR_PTR("w doesn't support nlevels", __func__, NULL); + if (h < (1 << (nlevels - 1))) + return (BOXAA *)ERROR_PTR("h doesn't support nlevels", __func__, NULL); + + baa = boxaaCreate(nlevels); + maxpts = 1 << (nlevels - 1); + xstart = (l_int32 *)LEPT_CALLOC(maxpts, sizeof(l_int32)); + xend = (l_int32 *)LEPT_CALLOC(maxpts, sizeof(l_int32)); + ystart = (l_int32 *)LEPT_CALLOC(maxpts, sizeof(l_int32)); + yend = (l_int32 *)LEPT_CALLOC(maxpts, sizeof(l_int32)); + for (k = 0; k < nlevels; k++) { + nside = 1 << k; /* number of boxes in each direction */ + for (i = 0; i < nside; i++) { + xstart[i] = (w - 1) * i / nside; + if (i > 0) xstart[i]++; + xend[i] = (w - 1) * (i + 1) / nside; + ystart[i] = (h - 1) * i / nside; + if (i > 0) ystart[i]++; + yend[i] = (h - 1) * (i + 1) / nside; +#if DEBUG_BOXES + lept_stderr( + "k = %d, xs[%d] = %d, xe[%d] = %d, ys[%d] = %d, ye[%d] = %d\n", + k, i, xstart[i], i, xend[i], i, ystart[i], i, yend[i]); +#endif /* DEBUG_BOXES */ + } + nbox = 1 << (2 * k); + boxa = boxaCreate(nbox); + for (i = 0; i < nside; i++) { + bh = yend[i] - ystart[i] + 1; + for (j = 0; j < nside; j++) { + bw = xend[j] - xstart[j] + 1; + box = boxCreate(xstart[j], ystart[i], bw, bh); + boxaAddBox(boxa, box, L_INSERT); + } + } + boxaaAddBoxa(baa, boxa, L_INSERT); + } + + LEPT_FREE(xstart); + LEPT_FREE(xend); + LEPT_FREE(ystart); + LEPT_FREE(yend); + return baa; +} + + +/*----------------------------------------------------------------------* + * Quadtree access * + *----------------------------------------------------------------------*/ +/*! + * \brief quadtreeGetParent() + * + * \param[in] fpixa mean, variance or root variance + * \param[in] level, x, y of current pixel + * \param[out] pval parent pixel value, or 0.0 on error + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) Check return value for error. On error, val is returned as 0.0. + * (2) The parent is located at: + * level - 1 + * (x/2, y/2) + * </pre> + */ +l_ok +quadtreeGetParent(FPIXA *fpixa, + l_int32 level, + l_int32 x, + l_int32 y, + l_float32 *pval) +{ +l_int32 n; + + if (!pval) + return ERROR_INT("&val not defined", __func__, 1); + *pval = 0.0; + if (!fpixa) + return ERROR_INT("fpixa not defined", __func__, 1); + n = fpixaGetCount(fpixa); + if (level < 1 || level >= n) + return ERROR_INT("invalid level", __func__, 1); + + if (fpixaGetPixel(fpixa, level - 1, x / 2, y / 2, pval) != 0) + return ERROR_INT("invalid coordinates", __func__, 1); + return 0; +} + + +/*! + * \brief quadtreeGetChildren() + * + * \param[in] fpixa mean, variance or root variance + * \param[in] level, x, y of current pixel + * \param[out] pval00, pval01, + * pval10, pval11 four child pixel values + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) Check return value for error. On error, all return vals are 0.0. + * (2) The returned child pixels are located at: + * level + 1 + * (2x, 2y), (2x+1, 2y), (2x, 2y+1), (2x+1, 2y+1) + * </pre> + */ +l_ok +quadtreeGetChildren(FPIXA *fpixa, + l_int32 level, + l_int32 x, + l_int32 y, + l_float32 *pval00, + l_float32 *pval10, + l_float32 *pval01, + l_float32 *pval11) +{ +l_int32 n; + + if (!pval00 || !pval01 || !pval10 || !pval11) + return ERROR_INT("&val* not all defined", __func__, 1); + *pval00 = *pval10 = *pval01 = *pval11 = 0.0; + if (!fpixa) + return ERROR_INT("fpixa not defined", __func__, 1); + n = fpixaGetCount(fpixa); + if (level < 0 || level >= n - 1) + return ERROR_INT("invalid level", __func__, 1); + + if (fpixaGetPixel(fpixa, level + 1, 2 * x, 2 * y, pval00) != 0) + return ERROR_INT("invalid coordinates", __func__, 1); + fpixaGetPixel(fpixa, level + 1, 2 * x + 1, 2 * y, pval10); + fpixaGetPixel(fpixa, level + 1, 2 * x, 2 * y + 1, pval01); + fpixaGetPixel(fpixa, level + 1, 2 * x + 1, 2 * y + 1, pval11); + return 0; +} + + +/*! + * \brief quadtreeMaxLevels() + * + * \param[in] w, h dimensions of image + * \return maxlevels maximum number of levels allowed, or -1 on error + * + * <pre> + * Notes: + * (1) The criterion for maxlevels is that the subdivision not + * go down below the single pixel level. The 1.5 factor + * is intended to keep any rectangle from accidentally + * having zero dimension due to integer truncation. + * </pre> + */ +l_int32 +quadtreeMaxLevels(l_int32 w, + l_int32 h) +{ +l_int32 i, minside; + + minside = L_MIN(w, h); + for (i = 0; i < 20; i++) { /* 2^10 = one million */ + if (minside < (1.5 * (1 << i))) + return i - 1; + } + + return -1; /* fail if the image has over a trillion pixels! */ +} + + +/*----------------------------------------------------------------------* + * Display quadtree * + *----------------------------------------------------------------------*/ +/*! + * \brief fpixaDisplayQuadtree() + * + * \param[in] fpixa mean, variance or root variance + * \param[in] factor replication factor at lowest level + * \param[in] fontsize 4, ... 20 + * \return pixd 8 bpp, mosaic of quadtree images, or NULL on error + * + * <pre> + * Notes: + * (1) The mean and root variance fall naturally in the 8 bpp range, + * but the variance is typically outside the range. This + * function displays 8 bpp pix clipped to 255, so the image + * pixels will mostly be 255 (white). + * </pre> + */ +PIX * +fpixaDisplayQuadtree(FPIXA *fpixa, + l_int32 factor, + l_int32 fontsize) +{ +char buf[256]; +l_int32 nlevels, i, mag, w; +L_BMF *bmf; +FPIX *fpix; +PIX *pixt1, *pixt2, *pixt3, *pixt4 = NULL, *pixd; +PIXA *pixat; + + if (!fpixa) + return (PIX *)ERROR_PTR("fpixa not defined", __func__, NULL); + + if ((nlevels = fpixaGetCount(fpixa)) == 0) + return (PIX *)ERROR_PTR("pixas empty", __func__, NULL); + + if ((bmf = bmfCreate(NULL, fontsize)) == NULL) + L_ERROR("bmf not made; text will not be added", __func__); + pixat = pixaCreate(nlevels); + for (i = 0; i < nlevels; i++) { + fpix = fpixaGetFPix(fpixa, i, L_CLONE); + pixt1 = fpixConvertToPix(fpix, 8, L_CLIP_TO_ZERO, 0); + mag = factor * (1 << (nlevels - i - 1)); + pixt2 = pixExpandReplicate(pixt1, mag); + pixt3 = pixConvertTo32(pixt2); + snprintf(buf, sizeof(buf), "Level %d\n", i); + pixt4 = pixAddSingleTextblock(pixt3, bmf, buf, 0xff000000, + L_ADD_BELOW, NULL); + pixaAddPix(pixat, pixt4, L_INSERT); + fpixDestroy(&fpix); + pixDestroy(&pixt1); + pixDestroy(&pixt2); + pixDestroy(&pixt3); + } + w = pixGetWidth(pixt4); + pixd = pixaDisplayTiledInRows(pixat, 32, nlevels * (w + 80), 1.0, 0, 30, 2); + + pixaDestroy(&pixat); + bmfDestroy(&bmf); + return pixd; +}
