diff --git a/3rdparty/gpdf/.gitignore b/3rdparty/gpdf/.gitignore deleted file mode 100644 index 56641e7b..00000000 --- a/3rdparty/gpdf/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -################################################################################ -# 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。 -################################################################################ - -/.vs -/debug -/release/qmake/qtvars_Win32_Release.props -/release/qmake -/Win32 -/Makefile.Release -/Makefile.Debug -/Makefile -/hg_gpdf.vcxproj.user -/hg_gpdf.vcxproj.filters -/hg_gpdf.vcxproj -/hg_gpdf.sln -/hg_gpdf.pro.user -/.qmake.stash -/3rdparty/tesseract/tessdata -/main.cpp diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/Makefile.am b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/Makefile.am deleted file mode 100644 index 7bdec541..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/Makefile.am +++ /dev/null @@ -1,102 +0,0 @@ -AM_CPPFLAGS = $(ZLIB_CFLAGS) $(LIBPNG_CFLAGS) $(JPEG_CFLAGS) $(LIBTIFF_CFLAGS) $(LIBWEBP_CFLAGS) $(LIBWEBPMUX_CFLAGS) $(LIBJP2K_CFLAGS) - -lib_LTLIBRARIES = liblept.la -liblept_la_LIBADD = $(LIBPNG_LIBS) $(JPEG_LIBS) $(GIFLIB_LIBS) $(LIBTIFF_LIBS) $(LIBWEBP_LIBS) $(LIBWEBPMUX_LIBS) $(LIBJP2K_LIBS) $(GDI_LIBS) $(LIBM) $(ZLIB_LIBS) - -liblept_la_LDFLAGS = -no-undefined -version-info 5:3:0 - -liblept_la_SOURCES = adaptmap.c affine.c \ - affinecompose.c arrayaccess.c \ - bardecode.c baseline.c bbuffer.c \ - bilateral.c bilinear.c binarize.c \ - binexpand.c binreduce.c \ - blend.c bmf.c bmpio.c bmpiostub.c \ - bootnumgen1.c bootnumgen2.c \ - bootnumgen3.c bootnumgen4.c \ - boxbasic.c boxfunc1.c boxfunc2.c boxfunc3.c \ - boxfunc4.c boxfunc5.c bytearray.c \ - ccbord.c ccthin.c checkerboard.c \ - classapp.c colorcontent.c coloring.c \ - colormap.c colormorph.c \ - colorquant1.c colorquant2.c \ - colorseg.c colorspace.c \ - compare.c conncomp.c convertfiles.c \ - convolve.c correlscore.c \ - dewarp1.c dewarp2.c dewarp3.c dewarp4.c \ - dnabasic.c dnafunc1.c dnahash.c \ - dwacomb.2.c dwacomblow.2.c \ - edge.c encoding.c enhance.c \ - fhmtauto.c fhmtgen.1.c fhmtgenlow.1.c \ - finditalic.c flipdetect.c fliphmtgen.c \ - fmorphauto.c fmorphgen.1.c fmorphgenlow.1.c \ - fpix1.c fpix2.c gifio.c gifiostub.c \ - gplot.c graphics.c graymorph.c \ - grayquant.c heap.c jbclass.c \ - jp2kheader.c jp2kheaderstub.c \ - jp2kio.c jp2kiostub.c jpegio.c jpegiostub.c \ - kernel.c leptwin.c libversions.c list.c map.c maze.c \ - morph.c morphapp.c morphdwa.c morphseq.c \ - numabasic.c numafunc1.c numafunc2.c \ - pageseg.c paintcmap.c \ - parseprotos.c partify.c partition.c \ - pdfio1.c pdfio1stub.c pdfio2.c pdfio2stub.c \ - pix1.c pix2.c pix3.c pix4.c pix5.c \ - pixabasic.c pixacc.c pixafunc1.c pixafunc2.c \ - pixalloc.c pixarith.c pixcomp.c pixconv.c \ - pixlabel.c pixtiling.c pngio.c pngiostub.c \ - pnmio.c pnmiostub.c projective.c \ - psio1.c psio1stub.c psio2.c psio2stub.c \ - ptabasic.c ptafunc1.c ptafunc2.c ptra.c \ - quadtree.c queue.c rank.c rbtree.c \ - readbarcode.c readfile.c \ - recogbasic.c recogdid.c recogident.c \ - recogtrain.c regutils.c \ - rop.c roplow.c \ - rotate.c rotateam.c rotateorth.c rotateshear.c \ - runlength.c sarray1.c sarray2.c \ - scale1.c scale2.c seedfill.c \ - sel1.c sel2.c selgen.c \ - shear.c skew.c spixio.c \ - stack.c stringcode.c \ - strokes.c sudoku.c textops.c \ - tiffio.c tiffiostub.c \ - utils1.c utils2.c warper.c watershed.c \ - webpio.c webpiostub.c webpanimio.c webpanimiostub.c \ - writefile.c zlibmem.c zlibmemstub.c - -pkginclude_HEADERS = allheaders.h alltypes.h \ - array.h arrayaccess.h bbuffer.h bilateral.h \ - bmf.h bmfdata.h bmp.h ccbord.h \ - dewarp.h endianness.h environ.h \ - gplot.h heap.h imageio.h jbclass.h \ - leptwin.h list.h \ - morph.h pix.h ptra.h queue.h rbtree.h \ - readbarcode.h recog.h regutils.h stack.h \ - stringcode.h sudoku.h watershed.h - -LDADD = liblept.la - -EXTRA_DIST = hmttemplate1.txt hmttemplate2.txt \ - leptonica-license.txt \ - morphtemplate1.txt morphtemplate2.txt \ - stringtemplate1.txt stringtemplate2.txt - -$(top_builddir)/prog/xtractprotos$(EXEEXT): liblept.la - $(MAKE) -C $(top_builddir)/prog xtractprotos$(EXEEXT) - -allheaders: $(top_builddir)/prog/xtractprotos$(EXEEXT) $(liblept_la_SOURCES) - cd $(srcdir) && $(abs_top_builddir)/prog/xtractprotos$(EXEEXT) -prestring=LEPT_DLL -protos=inline $(liblept_la_SOURCES) - -install-data-hook: - cd $(DESTDIR)$(libdir);\ - for ext in a la so sl dylib; do\ - if test -f liblept.$$ext; then\ - $(LN_S) liblept.$$ext libleptonica.$$ext;\ - fi;\ - done - -uninstall-hook: - cd $(DESTDIR)$(libdir);\ - for ext in a la so sl dylib; do\ - rm -f libleptonica.$$ext;\ - done diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/adaptmap.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/adaptmap.c deleted file mode 100644 index 634be33b..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/adaptmap.c +++ /dev/null @@ -1,2950 +0,0 @@ -/*====================================================================* - - 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 adaptmap.c - *
- * - * ------------------------------------------------------------------- - * - * Image binarization algorithms are found in: - * grayquant.c: standard, simple, general grayscale quantization - * adaptmap.c: local adaptive; mostly gray-to-gray in preparation - * for binarization - * binarize.c: special binarization methods, locally adaptive. - * - * ------------------------------------------------------------------- - * - * Clean background to white using background normalization - * PIX *pixCleanBackgroundToWhite() - * - * Adaptive background normalization (top-level functions) - * PIX *pixBackgroundNormSimple() 8 and 32 bpp - * PIX *pixBackgroundNorm() 8 and 32 bpp - * PIX *pixBackgroundNormMorph() 8 and 32 bpp - * - * Arrays of inverted background values for normalization (16 bpp) - * l_int32 pixBackgroundNormGrayArray() 8 bpp input - * l_int32 pixBackgroundNormRGBArrays() 32 bpp input - * l_int32 pixBackgroundNormGrayArrayMorph() 8 bpp input - * l_int32 pixBackgroundNormRGBArraysMorph() 32 bpp input - * - * Measurement of local background - * l_int32 pixGetBackgroundGrayMap() 8 bpp - * l_int32 pixGetBackgroundRGBMap() 32 bpp - * l_int32 pixGetBackgroundGrayMapMorph() 8 bpp - * l_int32 pixGetBackgroundRGBMapMorph() 32 bpp - * l_int32 pixFillMapHoles() - * PIX *pixExtendByReplication() 8 bpp - * l_int32 pixSmoothConnectedRegions() 8 bpp - * - * Measurement of local foreground - * l_int32 pixGetForegroundGrayMap() 8 bpp - * - * Generate inverted background map for each component - * PIX *pixGetInvBackgroundMap() 16 bpp - * - * Apply inverse background map to image - * PIX *pixApplyInvBackgroundGrayMap() 8 bpp - * PIX *pixApplyInvBackgroundRGBMap() 32 bpp - * - * Apply variable map - * PIX *pixApplyVariableGrayMap() 8 bpp - * - * Non-adaptive (global) mapping - * PIX *pixGlobalNormRGB() 32 bpp or cmapped - * PIX *pixGlobalNormNoSatRGB() 32 bpp - * - * Adaptive threshold spread normalization - * l_int32 pixThresholdSpreadNorm() 8 bpp - * - * Adaptive background normalization (flexible adaptaption) - * PIX *pixBackgroundNormFlex() 8 bpp - * - * Adaptive contrast normalization - * PIX *pixContrastNorm() 8 bpp - * l_int32 pixMinMaxTiles() - * l_int32 pixSetLowContrast() - * PIX *pixLinearTRCTiled() - * static l_int32 *iaaGetLinearTRC() - * - * Background normalization is done by generating a reduced map (or set - * of maps) representing the estimated background value of the - * input image, and using this to shift the pixel values so that - * this background value is set to some constant value. - * - * Specifically, normalization has 3 steps: - * (1) Generate a background map at a reduced scale. - * (2) Make the array of inverted background values by inverting - * the map. The result is an array of local multiplicative factors. - * (3) Apply this inverse background map to the image - * - * The inverse background arrays can be generated in two different ways here: - * (1) Remove the 'foreground' pixels and average over the remaining - * pixels in each tile. Propagate values into tiles where - * values have not been assigned, either because there was not - * enough background in the tile or because the tile is covered - * by a foreground region described by an image mask. - * After the background map is made, the inverse map is generated by - * smoothing over some number of adjacent tiles - * (block convolution) and then inverting. - * (2) Remove the foreground pixels using a morphological closing - * on a subsampled version of the image. Propagate values - * into pixels covered by an optional image mask. Invert the - * background map without preconditioning by convolutional smoothing. - * - * Other methods for adaptively normalizing the image are also given here. - * - * (1) pixThresholdSpreadNorm() computes a local threshold over the image - * and normalizes the input pixel values so that this computed threshold - * is a constant across the entire image. - * - * (2) pixContrastNorm() computes and applies a local TRC so that the - * local dynamic range is expanded to the full 8 bits, where the - * darkest pixels are mapped to 0 and the lightest to 255. This is - * useful for improving the appearance of pages with very light - * foreground or very dark background, and where the local TRC - * function doesn't change rapidly with position. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This is a simplified interface for cleaning an image. - * For comparison, see pixAdaptThresholdToBinaryGen(). - * (2) The suggested default values for the input parameters are: - * gamma: 1.0 (reduce this to increase the contrast; e.g., - * for light text) - * blackval 70 (a bit more than 60) - * whiteval 190 (a bit less than 200) - *- */ -PIX * -pixCleanBackgroundToWhite(PIX *pixs, - PIX *pixim, - PIX *pixg, - l_float32 gamma, - l_int32 blackval, - l_int32 whiteval) -{ -l_int32 d; -PIX *pixd; - - PROCNAME("pixCleanBackgroundToWhite"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - if (d != 8 && d != 32) - return (PIX *)ERROR_PTR("depth not 8 or 32", procName, NULL); - - pixd = pixBackgroundNormSimple(pixs, pixim, pixg); - if (!pixd) - return (PIX *)ERROR_PTR("background norm failedd", procName, NULL); - pixGammaTRC(pixd, pixd, gamma, blackval, whiteval); - return pixd; -} - - -/*------------------------------------------------------------------* - * Adaptive background normalization * - *------------------------------------------------------------------*/ -/*! - * \brief pixBackgroundNormSimple() - * - * \param[in] pixs 8 bpp grayscale or 32 bpp rgb - * \param[in] pixim [optional] 1 bpp 'image' mask; can be null - * \param[in] pixg [optional] 8 bpp grayscale version; can be null - * \return pixd 8 bpp or 32 bpp rgb, or NULL on error - * - *
- * Notes: - * (1) This is a simplified interface to pixBackgroundNorm(), - * where seven parameters are defaulted. - * (2) The input image is either grayscale or rgb. - * (3) See pixBackgroundNorm() for usage and function. - *- */ -PIX * -pixBackgroundNormSimple(PIX *pixs, - PIX *pixim, - PIX *pixg) -{ - return pixBackgroundNorm(pixs, pixim, pixg, - DefaultTileWidth, DefaultTileHeight, - DefaultFgThreshold, DefaultMinCount, - DefaultBgVal, DefaultXSmoothSize, - DefaultYSmoothSize); -} - - -/*! - * \brief pixBackgroundNorm() - * - * \param[in] pixs 8 bpp grayscale or 32 bpp rgb - * \param[in] pixim [optional] 1 bpp 'image' mask; can be null - * \param[in] pixg [optional] 8 bpp grayscale version; can be null - * \param[in] sx, sy tile size in pixels - * \param[in] thresh threshold for determining foreground - * \param[in] mincount min threshold on counts in a tile - * \param[in] bgval target bg val; typ. > 128 - * \param[in] smoothx half-width of block convolution kernel width - * \param[in] smoothy half-width of block convolution kernel height - * \return pixd 8 bpp or 32 bpp rgb, or NULL on error - * - *
- * Notes: - * (1) This is a top-level interface for normalizing the image intensity - * by mapping the image so that the background is near the input - * value 'bgval'. - * (2) The input image is either grayscale or rgb. - * (3) For each component in the input image, the background value - * in each tile is estimated using the values in the tile that - * are not part of the foreground, where the foreground is - * determined by the input 'thresh' argument. - * (4) An optional binary mask can be specified, with the foreground - * pixels typically over image regions. The resulting background - * map values will be determined by surrounding pixels that are - * not under the mask foreground. The origin (0,0) of this mask - * is assumed to be aligned with the origin of the input image. - * This binary mask must not fully cover pixs, because then there - * will be no pixels in the input image available to compute - * the background. - * (5) An optional grayscale version of the input pixs can be supplied. - * The only reason to do this is if the input is RGB and this - * grayscale version can be used elsewhere. If the input is RGB - * and this is not supplied, it is made internally using only - * the green component, and destroyed after use. - * (6) The dimensions of the pixel tile (sx, sy) give the amount by - * by which the map is reduced in size from the input image. - * (7) The threshold is used to binarize the input image, in order to - * locate the foreground components. If this is set too low, - * some actual foreground may be used to determine the maps; - * if set too high, there may not be enough background - * to determine the map values accurately. Typically, it's - * better to err by setting the threshold too high. - * (8) A 'mincount' threshold is a minimum count of pixels in a - * tile for which a background reading is made, in order for that - * pixel in the map to be valid. This number should perhaps be - * at least 1/3 the size of the tile. - * (9) A 'bgval' target background value for the normalized image. This - * should be at least 128. If set too close to 255, some - * clipping will occur in the result. - * (10) Two factors, 'smoothx' and 'smoothy', are input for smoothing - * the map. Each low-pass filter kernel dimension is - * is 2 * (smoothing factor) + 1, so a - * value of 0 means no smoothing. A value of 1 or 2 is recommended. - *- */ -PIX * -pixBackgroundNorm(PIX *pixs, - PIX *pixim, - PIX *pixg, - l_int32 sx, - l_int32 sy, - l_int32 thresh, - l_int32 mincount, - l_int32 bgval, - l_int32 smoothx, - l_int32 smoothy) -{ -l_int32 d, allfg; -PIX *pixm, *pixmi, *pixd; -PIX *pixmr, *pixmg, *pixmb, *pixmri, *pixmgi, *pixmbi; - - PROCNAME("pixBackgroundNorm"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - if (d != 8 && d != 32) - return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); - if (sx < 4 || sy < 4) - return (PIX *)ERROR_PTR("sx and sy must be >= 4", procName, NULL); - if (mincount > sx * sy) { - L_WARNING("mincount too large for tile size\n", procName); - mincount = (sx * sy) / 3; - } - - /* If pixim exists, verify that it is not all foreground. */ - if (pixim) { - pixInvert(pixim, pixim); - pixZero(pixim, &allfg); - pixInvert(pixim, pixim); - if (allfg) - return (PIX *)ERROR_PTR("pixim all foreground", procName, NULL); - } - - pixd = NULL; - if (d == 8) { - pixm = NULL; - pixGetBackgroundGrayMap(pixs, pixim, sx, sy, thresh, mincount, &pixm); - if (!pixm) { - L_WARNING("map not made; return a copy of the source\n", procName); - return pixCopy(NULL, pixs); - } - - pixmi = pixGetInvBackgroundMap(pixm, bgval, smoothx, smoothy); - if (!pixmi) { - L_WARNING("pixmi not made; return a copy of source\n", procName); - pixDestroy(&pixm); - return pixCopy(NULL, pixs); - } else { - pixd = pixApplyInvBackgroundGrayMap(pixs, pixmi, sx, sy); - } - - pixDestroy(&pixm); - pixDestroy(&pixmi); - } - else { - pixmr = pixmg = pixmb = NULL; - pixGetBackgroundRGBMap(pixs, pixim, pixg, sx, sy, thresh, - mincount, &pixmr, &pixmg, &pixmb); - if (!pixmr || !pixmg || !pixmb) { - pixDestroy(&pixmr); - pixDestroy(&pixmg); - pixDestroy(&pixmb); - L_WARNING("map not made; return a copy of the source\n", procName); - return pixCopy(NULL, pixs); - } - - pixmri = pixGetInvBackgroundMap(pixmr, bgval, smoothx, smoothy); - pixmgi = pixGetInvBackgroundMap(pixmg, bgval, smoothx, smoothy); - pixmbi = pixGetInvBackgroundMap(pixmb, bgval, smoothx, smoothy); - if (!pixmri || !pixmgi || !pixmbi) { - L_WARNING("not all pixm*i are made; return src copy\n", procName); - pixd = pixCopy(NULL, pixs); - } else { - pixd = pixApplyInvBackgroundRGBMap(pixs, pixmri, pixmgi, pixmbi, - sx, sy); - } - - pixDestroy(&pixmr); - pixDestroy(&pixmg); - pixDestroy(&pixmb); - pixDestroy(&pixmri); - pixDestroy(&pixmgi); - pixDestroy(&pixmbi); - } - - if (!pixd) - ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - return pixd; -} - - -/*! - * \brief pixBackgroundNormMorph() - * - * \param[in] pixs 8 bpp grayscale or 32 bpp rgb - * \param[in] pixim [optional] 1 bpp 'image' mask; can be null - * \param[in] reduction at which morph closings are done; between 2 and 16 - * \param[in] size of square Sel for the closing; use an odd number - * \param[in] bgval target bg val; typ. > 128 - * \return pixd 8 bpp, or NULL on error - * - *
- * Notes: - * (1) This is a top-level interface for normalizing the image intensity - * by mapping the image so that the background is near the input - * value 'bgval'. - * (2) The input image is either grayscale or rgb. - * (3) For each component in the input image, the background value - * is estimated using a grayscale closing; hence the 'Morph' - * in the function name. - * (4) An optional binary mask can be specified, with the foreground - * pixels typically over image regions. The resulting background - * map values will be determined by surrounding pixels that are - * not under the mask foreground. The origin (0,0) of this mask - * is assumed to be aligned with the origin of the input image. - * This binary mask must not fully cover pixs, because then there - * will be no pixels in the input image available to compute - * the background. - * (5) The map is computed at reduced size (given by 'reduction') - * from the input pixs and optional pixim. At this scale, - * pixs is closed to remove the background, using a square Sel - * of odd dimension. The product of reduction * size should be - * large enough to remove most of the text foreground. - * (6) No convolutional smoothing needs to be done on the map before - * inverting it. - * (7) A 'bgval' target background value for the normalized image. This - * should be at least 128. If set too close to 255, some - * clipping will occur in the result. - *- */ -PIX * -pixBackgroundNormMorph(PIX *pixs, - PIX *pixim, - l_int32 reduction, - l_int32 size, - l_int32 bgval) -{ -l_int32 d, allfg; -PIX *pixm, *pixmi, *pixd; -PIX *pixmr, *pixmg, *pixmb, *pixmri, *pixmgi, *pixmbi; - - PROCNAME("pixBackgroundNormMorph"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - if (d != 8 && d != 32) - return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); - if (reduction < 2 || reduction > 16) - return (PIX *)ERROR_PTR("reduction must be between 2 and 16", - procName, NULL); - - /* If pixim exists, verify that it is not all foreground. */ - if (pixim) { - pixInvert(pixim, pixim); - pixZero(pixim, &allfg); - pixInvert(pixim, pixim); - if (allfg) - return (PIX *)ERROR_PTR("pixim all foreground", procName, NULL); - } - - pixd = NULL; - if (d == 8) { - pixGetBackgroundGrayMapMorph(pixs, pixim, reduction, size, &pixm); - if (!pixm) - return (PIX *)ERROR_PTR("pixm not made", procName, NULL); - pixmi = pixGetInvBackgroundMap(pixm, bgval, 0, 0); - if (!pixmi) - ERROR_PTR("pixmi not made", procName, NULL); - else - pixd = pixApplyInvBackgroundGrayMap(pixs, pixmi, - reduction, reduction); - pixDestroy(&pixm); - pixDestroy(&pixmi); - } - else { /* d == 32 */ - pixmr = pixmg = pixmb = NULL; - pixGetBackgroundRGBMapMorph(pixs, pixim, reduction, size, - &pixmr, &pixmg, &pixmb); - if (!pixmr || !pixmg || !pixmb) { - pixDestroy(&pixmr); - pixDestroy(&pixmg); - pixDestroy(&pixmb); - return (PIX *)ERROR_PTR("not all pixm*", procName, NULL); - } - - pixmri = pixGetInvBackgroundMap(pixmr, bgval, 0, 0); - pixmgi = pixGetInvBackgroundMap(pixmg, bgval, 0, 0); - pixmbi = pixGetInvBackgroundMap(pixmb, bgval, 0, 0); - if (!pixmri || !pixmgi || !pixmbi) - ERROR_PTR("not all pixm*i are made", procName, NULL); - else - pixd = pixApplyInvBackgroundRGBMap(pixs, pixmri, pixmgi, pixmbi, - reduction, reduction); - - pixDestroy(&pixmr); - pixDestroy(&pixmg); - pixDestroy(&pixmb); - pixDestroy(&pixmri); - pixDestroy(&pixmgi); - pixDestroy(&pixmbi); - } - - if (!pixd) - ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - return pixd; -} - - -/*-------------------------------------------------------------------------* - * Arrays of inverted background values for normalization * - *-------------------------------------------------------------------------* - * Notes for these four functions: * - * (1) They are useful if you need to save the actual mapping array. * - * (2) They could be used in the top-level functions but are * - * not because their use makes those functions less clear. * - * (3) Each component in the input pixs generates a 16 bpp pix array. * - *-------------------------------------------------------------------------*/ -/*! - * \brief pixBackgroundNormGrayArray() - * - * \param[in] pixs 8 bpp grayscale - * \param[in] pixim [optional] 1 bpp 'image' mask; can be null - * \param[in] sx, sy tile size in pixels - * \param[in] thresh threshold for determining foreground - * \param[in] mincount min threshold on counts in a tile - * \param[in] bgval target bg val; typ. > 128 - * \param[in] smoothx half-width of block convolution kernel width - * \param[in] smoothy half-width of block convolution kernel height - * \param[out] ppixd 16 bpp array of inverted background value - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) See notes in pixBackgroundNorm(). - * (2) This returns a 16 bpp pix that can be used by - * pixApplyInvBackgroundGrayMap() to generate a normalized version - * of the input pixs. - *- */ -l_ok -pixBackgroundNormGrayArray(PIX *pixs, - PIX *pixim, - l_int32 sx, - l_int32 sy, - l_int32 thresh, - l_int32 mincount, - l_int32 bgval, - l_int32 smoothx, - l_int32 smoothy, - PIX **ppixd) -{ -l_int32 allfg; -PIX *pixm; - - PROCNAME("pixBackgroundNormGrayArray"); - - if (!ppixd) - return ERROR_INT("&pixd not defined", procName, 1); - *ppixd = NULL; - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); - if (pixGetColormap(pixs)) - return ERROR_INT("pixs is colormapped", procName, 1); - if (pixim && pixGetDepth(pixim) != 1) - return ERROR_INT("pixim not 1 bpp", procName, 1); - if (sx < 4 || sy < 4) - return ERROR_INT("sx and sy must be >= 4", procName, 1); - if (mincount > sx * sy) { - L_WARNING("mincount too large for tile size\n", procName); - mincount = (sx * sy) / 3; - } - - /* If pixim exists, verify that it is not all foreground. */ - if (pixim) { - pixInvert(pixim, pixim); - pixZero(pixim, &allfg); - pixInvert(pixim, pixim); - if (allfg) - return ERROR_INT("pixim all foreground", procName, 1); - } - - pixGetBackgroundGrayMap(pixs, pixim, sx, sy, thresh, mincount, &pixm); - if (!pixm) - return ERROR_INT("pixm not made", procName, 1); - *ppixd = pixGetInvBackgroundMap(pixm, bgval, smoothx, smoothy); - pixCopyResolution(*ppixd, pixs); - pixDestroy(&pixm); - return 0; -} - - -/*! - * \brief pixBackgroundNormRGBArrays() - * - * \param[in] pixs 32 bpp rgb - * \param[in] pixim [optional] 1 bpp 'image' mask; can be null - * \param[in] pixg [optional] 8 bpp grayscale version; can be null - * \param[in] sx, sy tile size in pixels - * \param[in] thresh threshold for determining foreground - * \param[in] mincount min threshold on counts in a tile - * \param[in] bgval target bg val; typ. > 128 - * \param[in] smoothx half-width of block convolution kernel width - * \param[in] smoothy half-width of block convolution kernel height - * \param[out] ppixr 16 bpp array of inverted R background value - * \param[out] ppixg 16 bpp array of inverted G background value - * \param[out] ppixb 16 bpp array of inverted B background value - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) See notes in pixBackgroundNorm(). - * (2) This returns a set of three 16 bpp pix that can be used by - * pixApplyInvBackgroundGrayMap() to generate a normalized version - * of each component of the input pixs. - *- */ -l_ok -pixBackgroundNormRGBArrays(PIX *pixs, - PIX *pixim, - PIX *pixg, - l_int32 sx, - l_int32 sy, - l_int32 thresh, - l_int32 mincount, - l_int32 bgval, - l_int32 smoothx, - l_int32 smoothy, - PIX **ppixr, - PIX **ppixg, - PIX **ppixb) -{ -l_int32 allfg; -PIX *pixmr, *pixmg, *pixmb; - - PROCNAME("pixBackgroundNormRGBArrays"); - - if (!ppixr || !ppixg || !ppixb) - return ERROR_INT("&pixr, &pixg, &pixb not all defined", procName, 1); - *ppixr = *ppixg = *ppixb = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not 32 bpp", procName, 1); - if (pixim && pixGetDepth(pixim) != 1) - return ERROR_INT("pixim not 1 bpp", procName, 1); - if (sx < 4 || sy < 4) - return ERROR_INT("sx and sy must be >= 4", procName, 1); - if (mincount > sx * sy) { - L_WARNING("mincount too large for tile size\n", procName); - mincount = (sx * sy) / 3; - } - - /* If pixim exists, verify that it is not all foreground. */ - if (pixim) { - pixInvert(pixim, pixim); - pixZero(pixim, &allfg); - pixInvert(pixim, pixim); - if (allfg) - return ERROR_INT("pixim all foreground", procName, 1); - } - - pixGetBackgroundRGBMap(pixs, pixim, pixg, sx, sy, thresh, mincount, - &pixmr, &pixmg, &pixmb); - if (!pixmr || !pixmg || !pixmb) { - pixDestroy(&pixmr); - pixDestroy(&pixmg); - pixDestroy(&pixmb); - return ERROR_INT("not all pixm* made", procName, 1); - } - - *ppixr = pixGetInvBackgroundMap(pixmr, bgval, smoothx, smoothy); - *ppixg = pixGetInvBackgroundMap(pixmg, bgval, smoothx, smoothy); - *ppixb = pixGetInvBackgroundMap(pixmb, bgval, smoothx, smoothy); - pixDestroy(&pixmr); - pixDestroy(&pixmg); - pixDestroy(&pixmb); - return 0; -} - - -/*! - * \brief pixBackgroundNormGrayArrayMorph() - * - * \param[in] pixs 8 bpp grayscale - * \param[in] pixim [optional] 1 bpp 'image' mask; can be null - * \param[in] reduction at which morph closings are done; between 2 and 16 - * \param[in] size of square Sel for the closing; use an odd number - * \param[in] bgval target bg val; typ. > 128 - * \param[out] ppixd 16 bpp array of inverted background value - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) See notes in pixBackgroundNormMorph(). - * (2) This returns a 16 bpp pix that can be used by - * pixApplyInvBackgroundGrayMap() to generate a normalized version - * of the input pixs. - *- */ -l_ok -pixBackgroundNormGrayArrayMorph(PIX *pixs, - PIX *pixim, - l_int32 reduction, - l_int32 size, - l_int32 bgval, - PIX **ppixd) -{ -l_int32 allfg; -PIX *pixm; - - PROCNAME("pixBackgroundNormGrayArrayMorph"); - - if (!ppixd) - return ERROR_INT("&pixd not defined", procName, 1); - *ppixd = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not 8 bpp", procName, 1); - if (pixim && pixGetDepth(pixim) != 1) - return ERROR_INT("pixim not 1 bpp", procName, 1); - if (reduction < 2 || reduction > 16) - return ERROR_INT("reduction must be between 2 and 16", procName, 1); - - /* If pixim exists, verify that it is not all foreground. */ - if (pixim) { - pixInvert(pixim, pixim); - pixZero(pixim, &allfg); - pixInvert(pixim, pixim); - if (allfg) - return ERROR_INT("pixim all foreground", procName, 1); - } - - pixGetBackgroundGrayMapMorph(pixs, pixim, reduction, size, &pixm); - if (!pixm) - return ERROR_INT("pixm not made", procName, 1); - *ppixd = pixGetInvBackgroundMap(pixm, bgval, 0, 0); - pixCopyResolution(*ppixd, pixs); - pixDestroy(&pixm); - return 0; -} - - -/*! - * \brief pixBackgroundNormRGBArraysMorph() - * - * \param[in] pixs 32 bpp rgb - * \param[in] pixim [optional] 1 bpp 'image' mask; can be null - * \param[in] reduction at which morph closings are done; between 2 and 16 - * \param[in] size of square Sel for the closing; use an odd number - * \param[in] bgval target bg val; typ. > 128 - * \param[out] ppixr 16 bpp array of inverted R background value - * \param[out] ppixg 16 bpp array of inverted G background value - * \param[out] ppixb 16 bpp array of inverted B background value - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) See notes in pixBackgroundNormMorph(). - * (2) This returns a set of three 16 bpp pix that can be used by - * pixApplyInvBackgroundGrayMap() to generate a normalized version - * of each component of the input pixs. - *- */ -l_ok -pixBackgroundNormRGBArraysMorph(PIX *pixs, - PIX *pixim, - l_int32 reduction, - l_int32 size, - l_int32 bgval, - PIX **ppixr, - PIX **ppixg, - PIX **ppixb) -{ -l_int32 allfg; -PIX *pixmr, *pixmg, *pixmb; - - PROCNAME("pixBackgroundNormRGBArraysMorph"); - - if (!ppixr || !ppixg || !ppixb) - return ERROR_INT("&pixr, &pixg, &pixb not all defined", procName, 1); - *ppixr = *ppixg = *ppixb = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not 32 bpp", procName, 1); - if (pixim && pixGetDepth(pixim) != 1) - return ERROR_INT("pixim not 1 bpp", procName, 1); - if (reduction < 2 || reduction > 16) - return ERROR_INT("reduction must be between 2 and 16", procName, 1); - - /* If pixim exists, verify that it is not all foreground. */ - if (pixim) { - pixInvert(pixim, pixim); - pixZero(pixim, &allfg); - pixInvert(pixim, pixim); - if (allfg) - return ERROR_INT("pixim all foreground", procName, 1); - } - - pixGetBackgroundRGBMapMorph(pixs, pixim, reduction, size, - &pixmr, &pixmg, &pixmb); - if (!pixmr || !pixmg || !pixmb) { - pixDestroy(&pixmr); - pixDestroy(&pixmg); - pixDestroy(&pixmb); - return ERROR_INT("not all pixm* made", procName, 1); - } - - *ppixr = pixGetInvBackgroundMap(pixmr, bgval, 0, 0); - *ppixg = pixGetInvBackgroundMap(pixmg, bgval, 0, 0); - *ppixb = pixGetInvBackgroundMap(pixmb, bgval, 0, 0); - pixDestroy(&pixmr); - pixDestroy(&pixmg); - pixDestroy(&pixmb); - return 0; -} - - -/*------------------------------------------------------------------* - * Measurement of local background * - *------------------------------------------------------------------*/ -/*! - * \brief pixGetBackgroundGrayMap() - * - * \param[in] pixs 8 bpp grayscale; not cmapped - * \param[in] pixim [optional] 1 bpp 'image' mask; can be null; - * it should not have only foreground pixels - * \param[in] sx, sy tile size in pixels - * \param[in] thresh threshold for determining foreground - * \param[in] mincount min threshold on counts in a tile - * \param[out] ppixd 8 bpp grayscale map - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The background is measured in regions that don't have - * images. It is then propagated into the image regions, - * and finally smoothed in each image region. - *- */ -l_ok -pixGetBackgroundGrayMap(PIX *pixs, - PIX *pixim, - l_int32 sx, - l_int32 sy, - l_int32 thresh, - l_int32 mincount, - PIX **ppixd) -{ -l_int32 w, h, wd, hd, wim, him, wpls, wplim, wpld, wplf; -l_int32 xim, yim, delx, nx, ny, i, j, k, m; -l_int32 count, sum, val8; -l_int32 empty, fgpixels; -l_uint32 *datas, *dataim, *datad, *dataf, *lines, *lineim, *lined, *linef; -l_float32 scalex, scaley; -PIX *pixd, *piximi, *pixb, *pixf, *pixims; - - PROCNAME("pixGetBackgroundGrayMap"); - - if (!ppixd) - return ERROR_INT("&pixd not defined", procName, 1); - *ppixd = NULL; - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); - if (pixGetColormap(pixs)) - return ERROR_INT("pixs is colormapped", procName, 1); - if (pixim && pixGetDepth(pixim) != 1) - return ERROR_INT("pixim not 1 bpp", procName, 1); - if (sx < 4 || sy < 4) - return ERROR_INT("sx and sy must be >= 4", procName, 1); - if (mincount > sx * sy) { - L_WARNING("mincount too large for tile size\n", procName); - mincount = (sx * sy) / 3; - } - - /* Evaluate the 'image' mask, pixim, and make sure - * it is not all fg. */ - fgpixels = 0; /* boolean for existence of fg pixels in the image mask. */ - if (pixim) { - piximi = pixInvert(NULL, pixim); /* set non-'image' pixels to 1 */ - pixZero(piximi, &empty); - pixDestroy(&piximi); - if (empty) - return ERROR_INT("pixim all fg; no background", procName, 1); - pixZero(pixim, &empty); - if (!empty) /* there are fg pixels in pixim */ - fgpixels = 1; - } - - /* Generate the foreground mask, pixf, which is at - * full resolution. These pixels will be ignored when - * computing the background values. */ - pixb = pixThresholdToBinary(pixs, thresh); - pixf = pixMorphSequence(pixb, "d7.1 + d1.7", 0); - pixDestroy(&pixb); - - - /* ------------- Set up the output map pixd --------------- */ - /* Generate pixd, which is reduced by the factors (sx, sy). */ - w = pixGetWidth(pixs); - h = pixGetHeight(pixs); - wd = (w + sx - 1) / sx; - hd = (h + sy - 1) / sy; - pixd = pixCreate(wd, hd, 8); - - /* Note: we only compute map values in tiles that are complete. - * In general, tiles at right and bottom edges will not be - * complete, and we must fill them in later. */ - nx = w / sx; - ny = h / sy; - wpls = pixGetWpl(pixs); - datas = pixGetData(pixs); - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - wplf = pixGetWpl(pixf); - dataf = pixGetData(pixf); - for (i = 0; i < ny; i++) { - lines = datas + sy * i * wpls; - linef = dataf + sy * i * wplf; - lined = datad + i * wpld; - for (j = 0; j < nx; j++) { - delx = j * sx; - sum = 0; - count = 0; - for (k = 0; k < sy; k++) { - for (m = 0; m < sx; m++) { - if (GET_DATA_BIT(linef + k * wplf, delx + m) == 0) { - sum += GET_DATA_BYTE(lines + k * wpls, delx + m); - count++; - } - } - } - if (count >= mincount) { - val8 = sum / count; - SET_DATA_BYTE(lined, j, val8); - } - } - } - pixDestroy(&pixf); - - /* If there is an optional mask with fg pixels, erase the previous - * calculation for the corresponding map pixels, setting the - * map values to 0. Then, when all the map holes are filled, - * these erased pixels will be set by the surrounding map values. - * - * The calculation here is relatively efficient: for each pixel - * in pixd (which corresponds to a tile of mask pixels in pixim) - * we look only at the pixel in pixim that is at the center - * of the tile. If the mask pixel is ON, we reset the map - * pixel in pixd to 0, so that it can later be filled in. */ - pixims = NULL; - if (pixim && fgpixels) { - wim = pixGetWidth(pixim); - him = pixGetHeight(pixim); - dataim = pixGetData(pixim); - wplim = pixGetWpl(pixim); - for (i = 0; i < ny; i++) { - yim = i * sy + sy / 2; - if (yim >= him) - break; - lineim = dataim + yim * wplim; - for (j = 0; j < nx; j++) { - xim = j * sx + sx / 2; - if (xim >= wim) - break; - if (GET_DATA_BIT(lineim, xim)) - pixSetPixel(pixd, j, i, 0); - } - } - } - - /* Fill all the holes in the map. */ - if (pixFillMapHoles(pixd, nx, ny, L_FILL_BLACK)) { - pixDestroy(&pixd); - L_WARNING("can't make the map\n", procName); - return 1; - } - - /* Finally, for each connected region corresponding to the - * 'image' mask, reset all pixels to their average value. - * Each of these components represents an image (or part of one) - * in the input, and this smooths the background values - * in each of these regions. */ - if (pixim && fgpixels) { - scalex = 1. / (l_float32)sx; - scaley = 1. / (l_float32)sy; - pixims = pixScaleBySampling(pixim, scalex, scaley); - pixSmoothConnectedRegions(pixd, pixims, 2); - pixDestroy(&pixims); - } - - *ppixd = pixd; - pixCopyResolution(*ppixd, pixs); - return 0; -} - - -/*! - * \brief pixGetBackgroundRGBMap() - * - * \param[in] pixs 32 bpp rgb - * \param[in] pixim [optional] 1 bpp 'image' mask; can be null; it - * should not have all foreground pixels - * \param[in] pixg [optional] 8 bpp grayscale version; can be null - * \param[in] sx, sy tile size in pixels - * \param[in] thresh threshold for determining foreground - * \param[in] mincount min threshold on counts in a tile - * \param[out] ppixmr red component map - * \param[out] ppixmg green component map - * \param[out] ppixmb blue component map - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) If pixg, which is a grayscale version of pixs, is provided, - * use this internally to generate the foreground mask. - * Otherwise, a grayscale version of pixs will be generated - * from the green component only, used, and destroyed. - *- */ -l_ok -pixGetBackgroundRGBMap(PIX *pixs, - PIX *pixim, - PIX *pixg, - l_int32 sx, - l_int32 sy, - l_int32 thresh, - l_int32 mincount, - PIX **ppixmr, - PIX **ppixmg, - PIX **ppixmb) -{ -l_int32 w, h, wm, hm, wim, him, wpls, wplim, wplf; -l_int32 xim, yim, delx, nx, ny, i, j, k, m; -l_int32 count, rsum, gsum, bsum, rval, gval, bval; -l_int32 empty, fgpixels; -l_uint32 pixel; -l_uint32 *datas, *dataim, *dataf, *lines, *lineim, *linef; -l_float32 scalex, scaley; -PIX *piximi, *pixgc, *pixb, *pixf, *pixims; -PIX *pixmr, *pixmg, *pixmb; - - PROCNAME("pixGetBackgroundRGBMap"); - - if (!ppixmr || !ppixmg || !ppixmb) - return ERROR_INT("&pixm* not all defined", procName, 1); - *ppixmr = *ppixmg = *ppixmb = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not 32 bpp", procName, 1); - if (pixim && pixGetDepth(pixim) != 1) - return ERROR_INT("pixim not 1 bpp", procName, 1); - if (sx < 4 || sy < 4) - return ERROR_INT("sx and sy must be >= 4", procName, 1); - if (mincount > sx * sy) { - L_WARNING("mincount too large for tile size\n", procName); - mincount = (sx * sy) / 3; - } - - /* Evaluate the mask pixim and make sure it is not all foreground */ - fgpixels = 0; /* boolean for existence of fg mask pixels */ - if (pixim) { - piximi = pixInvert(NULL, pixim); /* set non-'image' pixels to 1 */ - pixZero(piximi, &empty); - pixDestroy(&piximi); - if (empty) - return ERROR_INT("pixim all fg; no background", procName, 1); - pixZero(pixim, &empty); - if (!empty) /* there are fg pixels in pixim */ - fgpixels = 1; - } - - /* Generate the foreground mask. These pixels will be - * ignored when computing the background values. */ - if (pixg) /* use the input grayscale version if it is provided */ - pixgc = pixClone(pixg); - else - pixgc = pixConvertRGBToGrayFast(pixs); - pixb = pixThresholdToBinary(pixgc, thresh); - pixf = pixMorphSequence(pixb, "d7.1 + d1.7", 0); - pixDestroy(&pixgc); - pixDestroy(&pixb); - - /* Generate the output mask images */ - w = pixGetWidth(pixs); - h = pixGetHeight(pixs); - wm = (w + sx - 1) / sx; - hm = (h + sy - 1) / sy; - pixmr = pixCreate(wm, hm, 8); - pixmg = pixCreate(wm, hm, 8); - pixmb = pixCreate(wm, hm, 8); - - /* ------------- Set up the mapping images --------------- */ - /* Note: we only compute map values in tiles that are complete. - * In general, tiles at right and bottom edges will not be - * complete, and we must fill them in later. */ - nx = w / sx; - ny = h / sy; - wpls = pixGetWpl(pixs); - datas = pixGetData(pixs); - wplf = pixGetWpl(pixf); - dataf = pixGetData(pixf); - for (i = 0; i < ny; i++) { - lines = datas + sy * i * wpls; - linef = dataf + sy * i * wplf; - for (j = 0; j < nx; j++) { - delx = j * sx; - rsum = gsum = bsum = 0; - count = 0; - for (k = 0; k < sy; k++) { - for (m = 0; m < sx; m++) { - if (GET_DATA_BIT(linef + k * wplf, delx + m) == 0) { - pixel = *(lines + k * wpls + delx + m); - rsum += (pixel >> 24); - gsum += ((pixel >> 16) & 0xff); - bsum += ((pixel >> 8) & 0xff); - count++; - } - } - } - if (count >= mincount) { - rval = rsum / count; - gval = gsum / count; - bval = bsum / count; - pixSetPixel(pixmr, j, i, rval); - pixSetPixel(pixmg, j, i, gval); - pixSetPixel(pixmb, j, i, bval); - } - } - } - pixDestroy(&pixf); - - /* If there is an optional mask with fg pixels, erase the previous - * calculation for the corresponding map pixels, setting the - * map values in each of the 3 color maps to 0. Then, when - * all the map holes are filled, these erased pixels will - * be set by the surrounding map values. */ - if (pixim) { - wim = pixGetWidth(pixim); - him = pixGetHeight(pixim); - dataim = pixGetData(pixim); - wplim = pixGetWpl(pixim); - for (i = 0; i < ny; i++) { - yim = i * sy + sy / 2; - if (yim >= him) - break; - lineim = dataim + yim * wplim; - for (j = 0; j < nx; j++) { - xim = j * sx + sx / 2; - if (xim >= wim) - break; - if (GET_DATA_BIT(lineim, xim)) { - pixSetPixel(pixmr, j, i, 0); - pixSetPixel(pixmg, j, i, 0); - pixSetPixel(pixmb, j, i, 0); - } - } - } - } - - /* ----------------- Now fill in the holes ----------------------- */ - if (pixFillMapHoles(pixmr, nx, ny, L_FILL_BLACK) || - pixFillMapHoles(pixmg, nx, ny, L_FILL_BLACK) || - pixFillMapHoles(pixmb, nx, ny, L_FILL_BLACK)) { - pixDestroy(&pixmr); - pixDestroy(&pixmg); - pixDestroy(&pixmb); - L_WARNING("can't make the maps\n", procName); - return 1; - } - - /* Finally, for each connected region corresponding to the - * fg mask, reset all pixels to their average value. */ - if (pixim && fgpixels) { - scalex = 1. / (l_float32)sx; - scaley = 1. / (l_float32)sy; - pixims = pixScaleBySampling(pixim, scalex, scaley); - pixSmoothConnectedRegions(pixmr, pixims, 2); - pixSmoothConnectedRegions(pixmg, pixims, 2); - pixSmoothConnectedRegions(pixmb, pixims, 2); - pixDestroy(&pixims); - } - - *ppixmr = pixmr; - *ppixmg = pixmg; - *ppixmb = pixmb; - pixCopyResolution(*ppixmr, pixs); - pixCopyResolution(*ppixmg, pixs); - pixCopyResolution(*ppixmb, pixs); - return 0; -} - - -/*! - * \brief pixGetBackgroundGrayMapMorph() - * - * \param[in] pixs 8 bpp grayscale; not cmapped - * \param[in] pixim [optional] 1 bpp 'image' mask; can be null; it - * should not have all foreground pixels - * \param[in] reduction factor at which closing is performed - * \param[in] size of square Sel for the closing; use an odd number - * \param[out] ppixm grayscale map - * \return 0 if OK, 1 on error - */ -l_ok -pixGetBackgroundGrayMapMorph(PIX *pixs, - PIX *pixim, - l_int32 reduction, - l_int32 size, - PIX **ppixm) -{ -l_int32 nx, ny, empty, fgpixels; -l_float32 scale; -PIX *pixm, *pix1, *pix2, *pix3, *pixims; - - PROCNAME("pixGetBackgroundGrayMapMorph"); - - if (!ppixm) - return ERROR_INT("&pixm not defined", procName, 1); - *ppixm = NULL; - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); - if (pixGetColormap(pixs)) - return ERROR_INT("pixs is colormapped", procName, 1); - if (pixim && pixGetDepth(pixim) != 1) - return ERROR_INT("pixim not 1 bpp", procName, 1); - - /* Evaluate the mask pixim and make sure it is not all foreground. */ - fgpixels = 0; /* boolean for existence of fg mask pixels */ - if (pixim) { - pixInvert(pixim, pixim); /* set background pixels to 1 */ - pixZero(pixim, &empty); - if (empty) - return ERROR_INT("pixim all fg; no background", procName, 1); - pixInvert(pixim, pixim); /* revert to original mask */ - pixZero(pixim, &empty); - if (!empty) /* there are fg pixels in pixim */ - fgpixels = 1; - } - - /* Downscale as requested and do the closing to get the background. */ - scale = 1. / (l_float32)reduction; - pix1 = pixScaleBySampling(pixs, scale, scale); - pix2 = pixCloseGray(pix1, size, size); - pix3 = pixExtendByReplication(pix2, 1, 1); - pixDestroy(&pix1); - pixDestroy(&pix2); - - /* Downscale the image mask, if any, and remove it from the - * background. These pixels will be filled in (twice). */ - pixims = NULL; - if (pixim) { - pixims = pixScale(pixim, scale, scale); - pixm = pixConvertTo8(pixims, FALSE); - pixAnd(pixm, pixm, pix3); - } - else - pixm = pixClone(pix3); - pixDestroy(&pix3); - - /* Fill all the holes in the map. */ - nx = pixGetWidth(pixs) / reduction; - ny = pixGetHeight(pixs) / reduction; - if (pixFillMapHoles(pixm, nx, ny, L_FILL_BLACK)) { - pixDestroy(&pixm); - pixDestroy(&pixims); - L_WARNING("can't make the map\n", procName); - return 1; - } - - /* Finally, for each connected region corresponding to the - * fg mask, reset all pixels to their average value. */ - if (pixim && fgpixels) - pixSmoothConnectedRegions(pixm, pixims, 2); - pixDestroy(&pixims); - - *ppixm = pixm; - pixCopyResolution(*ppixm, pixs); - return 0; -} - - -/*! - * \brief pixGetBackgroundRGBMapMorph() - * - * \param[in] pixs 32 bpp rgb - * \param[in] pixim [optional] 1 bpp 'image' mask; can be null; it - * should not have all foreground pixels - * \param[in] reduction factor at which closing is performed - * \param[in] size of square Sel for the closing; use an odd number - * \param[out] ppixmr red component map - * \param[out] ppixmg green component map - * \param[out] ppixmb blue component map - * \return 0 if OK, 1 on error - */ -l_ok -pixGetBackgroundRGBMapMorph(PIX *pixs, - PIX *pixim, - l_int32 reduction, - l_int32 size, - PIX **ppixmr, - PIX **ppixmg, - PIX **ppixmb) -{ -l_int32 nx, ny, empty, fgpixels; -l_float32 scale; -PIX *pixm, *pixmr, *pixmg, *pixmb, *pix1, *pix2, *pix3, *pixims; - - PROCNAME("pixGetBackgroundRGBMapMorph"); - - if (!ppixmr || !ppixmg || !ppixmb) - return ERROR_INT("&pixm* not all defined", procName, 1); - *ppixmr = *ppixmg = *ppixmb = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not 32 bpp", procName, 1); - if (pixim && pixGetDepth(pixim) != 1) - return ERROR_INT("pixim not 1 bpp", procName, 1); - - /* Evaluate the mask pixim and make sure it is not all foreground. */ - fgpixels = 0; /* boolean for existence of fg mask pixels */ - if (pixim) { - pixInvert(pixim, pixim); /* set background pixels to 1 */ - pixZero(pixim, &empty); - if (empty) - return ERROR_INT("pixim all fg; no background", procName, 1); - pixInvert(pixim, pixim); /* revert to original mask */ - pixZero(pixim, &empty); - if (!empty) /* there are fg pixels in pixim */ - fgpixels = 1; - } - - /* Generate an 8 bpp version of the image mask, if it exists */ - scale = 1. / (l_float32)reduction; - pixims = NULL; - pixm = NULL; - if (pixim) { - pixims = pixScale(pixim, scale, scale); - pixm = pixConvertTo8(pixims, FALSE); - } - - /* Downscale as requested and do the closing to get the background. - * Then remove the image mask pixels from the background. They - * will be filled in (twice) later. Do this for all 3 components. */ - pix1 = pixScaleRGBToGrayFast(pixs, reduction, COLOR_RED); - pix2 = pixCloseGray(pix1, size, size); - pix3 = pixExtendByReplication(pix2, 1, 1); - if (pixim) - pixmr = pixAnd(NULL, pixm, pix3); - else - pixmr = pixClone(pix3); - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - - pix1 = pixScaleRGBToGrayFast(pixs, reduction, COLOR_GREEN); - pix2 = pixCloseGray(pix1, size, size); - pix3 = pixExtendByReplication(pix2, 1, 1); - if (pixim) - pixmg = pixAnd(NULL, pixm, pix3); - else - pixmg = pixClone(pix3); - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - - pix1 = pixScaleRGBToGrayFast(pixs, reduction, COLOR_BLUE); - pix2 = pixCloseGray(pix1, size, size); - pix3 = pixExtendByReplication(pix2, 1, 1); - if (pixim) - pixmb = pixAnd(NULL, pixm, pix3); - else - pixmb = pixClone(pix3); - pixDestroy(&pixm); - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - - /* Fill all the holes in the three maps. */ - nx = pixGetWidth(pixs) / reduction; - ny = pixGetHeight(pixs) / reduction; - if (pixFillMapHoles(pixmr, nx, ny, L_FILL_BLACK) || - pixFillMapHoles(pixmg, nx, ny, L_FILL_BLACK) || - pixFillMapHoles(pixmb, nx, ny, L_FILL_BLACK)) { - pixDestroy(&pixmr); - pixDestroy(&pixmg); - pixDestroy(&pixmb); - pixDestroy(&pixims); - L_WARNING("can't make the maps\n", procName); - return 1; - } - - /* Finally, for each connected region corresponding to the - * fg mask in each component, reset all pixels to their - * average value. */ - if (pixim && fgpixels) { - pixSmoothConnectedRegions(pixmr, pixims, 2); - pixSmoothConnectedRegions(pixmg, pixims, 2); - pixSmoothConnectedRegions(pixmb, pixims, 2); - pixDestroy(&pixims); - } - - *ppixmr = pixmr; - *ppixmg = pixmg; - *ppixmb = pixmb; - pixCopyResolution(*ppixmr, pixs); - pixCopyResolution(*ppixmg, pixs); - pixCopyResolution(*ppixmb, pixs); - return 0; -} - - -/*! - * \brief pixFillMapHoles() - * - * \param[in] pix 8 bpp; a map, with one pixel for each tile in - * a larger image - * \param[in] nx number of horizontal pixel tiles that are entirely - * covered with pixels in the original source image - * \param[in] ny ditto for the number of vertical pixel tiles - * \param[in] filltype L_FILL_WHITE or L_FILL_BLACK - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is an in-place operation on pix (the map). pix is - * typically a low-resolution version of some other image - * from which it was derived, where each pixel in pix - * corresponds to a rectangular tile (say, m x n) of pixels - * in the larger image. All we need to know about the larger - * image is whether or not the rightmost column and bottommost - * row of pixels in pix correspond to tiles that are - * only partially covered by pixels in the larger image. - * (2) Typically, some number of pixels in the input map are - * not known, and their values must be determined by near - * pixels that are known. These unknown pixels are the 'holes'. - * They can take on only two values, 0 and 255, and the - * instruction about which to fill is given by the filltype flag. - * (3) The "holes" can come from two sources. The first is when there - * are not enough foreground or background pixels in a tile; - * the second is when a tile is at least partially covered - * by an image mask. If we're filling holes in a fg mask, - * the holes are initialized to black (0) and use L_FILL_BLACK. - * For filling holes in a bg mask, initialize the holes to - * white (255) and use L_FILL_WHITE. - * (4) If w is the map width, nx = w or nx = w - 1; ditto for h and ny. - *- */ -l_ok -pixFillMapHoles(PIX *pix, - l_int32 nx, - l_int32 ny, - l_int32 filltype) -{ -l_int32 w, h, y, nmiss, goodcol, i, j, found, ival, valtest; -l_uint32 val, lastval; -NUMA *na; /* indicates if there is any data in the column */ -PIX *pixt; - - PROCNAME("pixFillMapHoles"); - - if (!pix || pixGetDepth(pix) != 8) - return ERROR_INT("pix not defined or not 8 bpp", procName, 1); - if (pixGetColormap(pix)) - return ERROR_INT("pix is colormapped", procName, 1); - - /* ------------- Fill holes in the mapping image columns ----------- */ - pixGetDimensions(pix, &w, &h, NULL); - na = numaCreate(0); /* holds flag for which columns have data */ - nmiss = 0; - valtest = (filltype == L_FILL_WHITE) ? 255 : 0; - for (j = 0; j < nx; j++) { /* do it by columns */ - found = FALSE; - for (i = 0; i < ny; i++) { - pixGetPixel(pix, j, i, &val); - if (val != valtest) { - y = i; - found = TRUE; - break; - } - } - if (found == FALSE) { - numaAddNumber(na, 0); /* no data in the column */ - nmiss++; - } - else { - numaAddNumber(na, 1); /* data in the column */ - for (i = y - 1; i >= 0; i--) /* replicate upwards to top */ - pixSetPixel(pix, j, i, val); - pixGetPixel(pix, j, 0, &lastval); - for (i = 1; i < h; i++) { /* set going down to bottom */ - pixGetPixel(pix, j, i, &val); - if (val == valtest) - pixSetPixel(pix, j, i, lastval); - else - lastval = val; - } - } - } - numaAddNumber(na, 0); /* last column */ - - if (nmiss == nx) { /* no data in any column! */ - numaDestroy(&na); - L_WARNING("no bg found; no data in any column\n", procName); - return 1; - } - - /* ---------- Fill in missing columns by replication ----------- */ - if (nmiss > 0) { /* replicate columns */ - pixt = pixCopy(NULL, pix); - /* Find the first good column */ - goodcol = 0; - for (j = 0; j < w; j++) { - numaGetIValue(na, j, &ival); - if (ival == 1) { - goodcol = j; - break; - } - } - if (goodcol > 0) { /* copy cols backward */ - for (j = goodcol - 1; j >= 0; j--) { - pixRasterop(pix, j, 0, 1, h, PIX_SRC, pixt, j + 1, 0); - pixRasterop(pixt, j, 0, 1, h, PIX_SRC, pix, j, 0); - } - } - for (j = goodcol + 1; j < w; j++) { /* copy cols forward */ - numaGetIValue(na, j, &ival); - if (ival == 0) { - /* Copy the column to the left of j */ - pixRasterop(pix, j, 0, 1, h, PIX_SRC, pixt, j - 1, 0); - pixRasterop(pixt, j, 0, 1, h, PIX_SRC, pix, j, 0); - } - } - pixDestroy(&pixt); - } - if (w > nx) { /* replicate the last column */ - for (i = 0; i < h; i++) { - pixGetPixel(pix, w - 2, i, &val); - pixSetPixel(pix, w - 1, i, val); - } - } - - numaDestroy(&na); - return 0; -} - - -/*! - * \brief pixExtendByReplication() - * - * \param[in] pixs 8 bpp - * \param[in] addw number of extra pixels horizontally to add - * \param[in] addh number of extra pixels vertically to add - * \return pixd extended with replicated pixel values, or NULL on error - * - *
- * Notes: - * (1) The pixel values are extended to the left and down, as required. - *- */ -PIX * -pixExtendByReplication(PIX *pixs, - l_int32 addw, - l_int32 addh) -{ -l_int32 w, h, i, j; -l_uint32 val; -PIX *pixd; - - PROCNAME("pixExtendByReplication"); - - if (!pixs || pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - - if (addw == 0 && addh == 0) - return pixCopy(NULL, pixs); - - pixGetDimensions(pixs, &w, &h, NULL); - if ((pixd = pixCreate(w + addw, h + addh, 8)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixRasterop(pixd, 0, 0, w, h, PIX_SRC, pixs, 0, 0); - - if (addw > 0) { - for (i = 0; i < h; i++) { - pixGetPixel(pixd, w - 1, i, &val); - for (j = 0; j < addw; j++) - pixSetPixel(pixd, w + j, i, val); - } - } - - if (addh > 0) { - for (j = 0; j < w + addw; j++) { - pixGetPixel(pixd, j, h - 1, &val); - for (i = 0; i < addh; i++) - pixSetPixel(pixd, j, h + i, val); - } - } - - pixCopyResolution(pixd, pixs); - return pixd; -} - - -/*! - * \brief pixSmoothConnectedRegions() - * - * \param[in] pixs 8 bpp grayscale; no colormap - * \param[in] pixm [optional] 1 bpp; if null, this is a no-op - * \param[in] factor subsampling factor for getting average; >= 1 - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The pixels in pixs corresponding to those in each - * 8-connected region in the mask are set to the average value. - * (2) This is required for adaptive mapping to avoid the - * generation of stripes in the background map, due to - * variations in the pixel values near the edges of mask regions. - * (3) This function is optimized for background smoothing, where - * there are a relatively small number of components. It will - * be inefficient if used where there are many small components. - *- */ -l_ok -pixSmoothConnectedRegions(PIX *pixs, - PIX *pixm, - l_int32 factor) -{ -l_int32 empty, i, n, x, y; -l_float32 aveval; -BOXA *boxa; -PIX *pixmc; -PIXA *pixa; - - PROCNAME("pixSmoothConnectedRegions"); - - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); - if (pixGetColormap(pixs)) - return ERROR_INT("pixs has colormap", procName, 1); - if (!pixm) { - L_INFO("pixm not defined\n", procName); - return 0; - } - if (pixGetDepth(pixm) != 1) - return ERROR_INT("pixm not 1 bpp", procName, 1); - pixZero(pixm, &empty); - if (empty) { - L_INFO("pixm has no fg pixels; nothing to do\n", procName); - return 0; - } - - boxa = pixConnComp(pixm, &pixa, 8); - n = boxaGetCount(boxa); - for (i = 0; i < n; i++) { - if ((pixmc = pixaGetPix(pixa, i, L_CLONE)) == NULL) { - L_WARNING("missing pixmc!\n", procName); - continue; - } - boxaGetBoxGeometry(boxa, i, &x, &y, NULL, NULL); - pixGetAverageMasked(pixs, pixmc, x, y, factor, L_MEAN_ABSVAL, &aveval); - pixPaintThroughMask(pixs, pixmc, x, y, (l_int32)aveval); - pixDestroy(&pixmc); - } - - boxaDestroy(&boxa); - pixaDestroy(&pixa); - return 0; -} - - -/*------------------------------------------------------------------* - * Measurement of local foreground * - *------------------------------------------------------------------*/ -#if 0 /* Not working properly: do not use */ - -/*! - * \brief pixGetForegroundGrayMap() - * - * \param[in] pixs 8 bpp - * \param[in] pixim [optional] 1 bpp 'image' mask; can be null - * \param[in] sx, sy src tile size, in pixels - * \param[in] thresh threshold for determining foreground - * \param[out] ppixd 8 bpp grayscale map - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Each (sx, sy) tile of pixs gets mapped to one pixel in pixd. - * (2) pixd is the estimate of the fg (darkest) value within each tile. - * (3) All pixels in pixd that are in 'image' regions, as specified - * by pixim, are given the background value 0. - * (4) For pixels in pixd that can't directly be given a fg value, - * the value is inferred by propagating from neighboring pixels. - * (5) In practice, pixd can be used to normalize the fg, and - * it can be done after background normalization. - * (6) The overall procedure is: - * ~ reduce 2x by sampling - * ~ paint all 'image' pixels white, so that they don't - * ~ participate in the Min reduction - * ~ do a further (sx, sy) Min reduction -- think of - * it as a large opening followed by subsampling by the - * reduction factors - * ~ threshold the result to identify fg, and set the - * bg pixels to 255 (these are 'holes') - * ~ fill holes by propagation from fg values - * ~ replicatively expand by 2x, arriving at the final - * resolution of pixd - * ~ smooth with a 17x17 kernel - * ~ paint the 'image' regions black - *- */ -l_ok -pixGetForegroundGrayMap(PIX *pixs, - PIX *pixim, - l_int32 sx, - l_int32 sy, - l_int32 thresh, - PIX **ppixd) -{ -l_int32 w, h, d, wd, hd; -l_int32 empty, fgpixels; -PIX *pixd, *piximi, *pixim2, *pixims, *pixs2, *pixb, *pixt1, *pixt2, *pixt3; - - PROCNAME("pixGetForegroundGrayMap"); - - if (!ppixd) - return ERROR_INT("&pixd not defined", procName, 1); - *ppixd = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return ERROR_INT("pixs not 8 bpp", procName, 1); - if (pixim && pixGetDepth(pixim) != 1) - return ERROR_INT("pixim not 1 bpp", procName, 1); - if (sx < 2 || sy < 2) - return ERROR_INT("sx and sy must be >= 2", procName, 1); - - /* Generate pixd, which is reduced by the factors (sx, sy). */ - wd = (w + sx - 1) / sx; - hd = (h + sy - 1) / sy; - pixd = pixCreate(wd, hd, 8); - *ppixd = pixd; - - /* Evaluate the 'image' mask, pixim. If it is all fg, - * the output pixd has all pixels with value 0. */ - fgpixels = 0; /* boolean for existence of fg pixels in the image mask. */ - if (pixim) { - piximi = pixInvert(NULL, pixim); /* set non-image pixels to 1 */ - pixZero(piximi, &empty); - pixDestroy(&piximi); - if (empty) /* all 'image'; return with all pixels set to 0 */ - return 0; - pixZero(pixim, &empty); - if (!empty) /* there are fg pixels in pixim */ - fgpixels = 1; - } - - /* 2x subsampling; paint white through 'image' mask. */ - pixs2 = pixScaleBySampling(pixs, 0.5, 0.5); - if (pixim && fgpixels) { - pixim2 = pixReduceBinary2(pixim, NULL); - pixPaintThroughMask(pixs2, pixim2, 0, 0, 255); - pixDestroy(&pixim2); - } - - /* Min (erosion) downscaling; total reduction (4 sx, 4 sy). */ - pixt1 = pixScaleGrayMinMax(pixs2, sx, sy, L_CHOOSE_MIN); - -/* pixDisplay(pixt1, 300, 200); */ - - /* Threshold to identify fg; paint bg pixels to white. */ - pixb = pixThresholdToBinary(pixt1, thresh); /* fg pixels */ - pixInvert(pixb, pixb); - pixPaintThroughMask(pixt1, pixb, 0, 0, 255); - pixDestroy(&pixb); - - /* Replicative expansion by 2x to (sx, sy). */ - pixt2 = pixExpandReplicate(pixt1, 2); - -/* pixDisplay(pixt2, 500, 200); */ - - /* Fill holes in the fg by propagation */ - pixFillMapHoles(pixt2, w / sx, h / sy, L_FILL_WHITE); - -/* pixDisplay(pixt2, 700, 200); */ - - /* Smooth with 17x17 kernel. */ - pixt3 = pixBlockconv(pixt2, 8, 8); - pixRasterop(pixd, 0, 0, wd, hd, PIX_SRC, pixt3, 0, 0); - - /* Paint the image parts black. */ - pixims = pixScaleBySampling(pixim, 1. / sx, 1. / sy); - pixPaintThroughMask(pixd, pixims, 0, 0, 0); - - pixDestroy(&pixs2); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - pixDestroy(&pixt3); - return 0; -} -#endif /* Not working properly: do not use */ - - -/*------------------------------------------------------------------* - * Generate inverted background map * - *------------------------------------------------------------------*/ -/*! - * \brief pixGetInvBackgroundMap() - * - * \param[in] pixs 8 bpp grayscale; no colormap - * \param[in] bgval target bg val; typ. > 128 - * \param[in] smoothx half-width of block convolution kernel width - * \param[in] smoothy half-width of block convolution kernel height - * \return pixd 16 bpp, or NULL on error - * - *
- * Notes: - * (1) bgval should typically be > 120 and < 240 - * (2) pixd is a normalization image; the original image is - * multiplied by pixd and the result is divided by 256. - *- */ -PIX * -pixGetInvBackgroundMap(PIX *pixs, - l_int32 bgval, - l_int32 smoothx, - l_int32 smoothy) -{ -l_int32 w, h, wplsm, wpld, i, j; -l_int32 val, val16; -l_uint32 *datasm, *datad, *linesm, *lined; -PIX *pixsm, *pixd; - - PROCNAME("pixGetInvBackgroundMap"); - - if (!pixs || pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - if (w < 5 || h < 5) - return (PIX *)ERROR_PTR("w and h must be >= 5", procName, NULL); - - /* smooth the map image */ - pixsm = pixBlockconv(pixs, smoothx, smoothy); - datasm = pixGetData(pixsm); - wplsm = pixGetWpl(pixsm); - - /* invert the map image, scaling up to preserve dynamic range */ - pixd = pixCreate(w, h, 16); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - linesm = datasm + i * wplsm; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(linesm, j); - if (val > 0) - val16 = (256 * bgval) / val; - else { /* shouldn't happen */ - L_WARNING("smoothed bg has 0 pixel!\n", procName); - val16 = bgval / 2; - } - SET_DATA_TWO_BYTES(lined, j, val16); - } - } - - pixDestroy(&pixsm); - pixCopyResolution(pixd, pixs); - return pixd; -} - - -/*------------------------------------------------------------------* - * Apply background map to image * - *------------------------------------------------------------------*/ -/*! - * \brief pixApplyInvBackgroundGrayMap() - * - * \param[in] pixs 8 bpp grayscale; no colormap - * \param[in] pixm 16 bpp, inverse background map - * \param[in] sx tile width in pixels - * \param[in] sy tile height in pixels - * \return pixd 8 bpp, or NULL on error - */ -PIX * -pixApplyInvBackgroundGrayMap(PIX *pixs, - PIX *pixm, - l_int32 sx, - l_int32 sy) -{ -l_int32 w, h, wm, hm, wpls, wpld, i, j, k, m, xoff, yoff; -l_int32 vals, vald; -l_uint32 val16; -l_uint32 *datas, *datad, *lines, *lined, *flines, *flined; -PIX *pixd; - - PROCNAME("pixApplyInvBackgroundGrayMap"); - - if (!pixs || pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); - if (!pixm || pixGetDepth(pixm) != 16) - return (PIX *)ERROR_PTR("pixm undefined or not 16 bpp", procName, NULL); - if (sx == 0 || sy == 0) - return (PIX *)ERROR_PTR("invalid sx and/or sy", procName, NULL); - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - pixGetDimensions(pixs, &w, &h, NULL); - pixGetDimensions(pixm, &wm, &hm, NULL); - if ((pixd = pixCreateTemplate(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < hm; i++) { - lines = datas + sy * i * wpls; - lined = datad + sy * i * wpld; - yoff = sy * i; - for (j = 0; j < wm; j++) { - pixGetPixel(pixm, j, i, &val16); - xoff = sx * j; - for (k = 0; k < sy && yoff + k < h; k++) { - flines = lines + k * wpls; - flined = lined + k * wpld; - for (m = 0; m < sx && xoff + m < w; m++) { - vals = GET_DATA_BYTE(flines, xoff + m); - vald = (vals * val16) / 256; - vald = L_MIN(vald, 255); - SET_DATA_BYTE(flined, xoff + m, vald); - } - } - } - } - - return pixd; -} - - -/*! - * \brief pixApplyInvBackgroundRGBMap() - * - * \param[in] pixs 32 bpp rbg - * \param[in] pixmr 16 bpp, red inverse background map - * \param[in] pixmg 16 bpp, green inverse background map - * \param[in] pixmb 16 bpp, blue inverse background map - * \param[in] sx tile width in pixels - * \param[in] sy tile height in pixels - * \return pixd 32 bpp rbg, or NULL on error - */ -PIX * -pixApplyInvBackgroundRGBMap(PIX *pixs, - PIX *pixmr, - PIX *pixmg, - PIX *pixmb, - l_int32 sx, - l_int32 sy) -{ -l_int32 w, h, wm, hm, wpls, wpld, i, j, k, m, xoff, yoff; -l_int32 rvald, gvald, bvald; -l_uint32 vals; -l_uint32 rval16, gval16, bval16; -l_uint32 *datas, *datad, *lines, *lined, *flines, *flined; -PIX *pixd; - - PROCNAME("pixApplyInvBackgroundRGBMap"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (!pixmr || !pixmg || !pixmb) - return (PIX *)ERROR_PTR("pix maps not all defined", procName, NULL); - if (pixGetDepth(pixmr) != 16 || pixGetDepth(pixmg) != 16 || - pixGetDepth(pixmb) != 16) - return (PIX *)ERROR_PTR("pix maps not all 16 bpp", procName, NULL); - if (sx == 0 || sy == 0) - return (PIX *)ERROR_PTR("invalid sx and/or sy", procName, NULL); - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - w = pixGetWidth(pixs); - h = pixGetHeight(pixs); - wm = pixGetWidth(pixmr); - hm = pixGetHeight(pixmr); - if ((pixd = pixCreateTemplate(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < hm; i++) { - lines = datas + sy * i * wpls; - lined = datad + sy * i * wpld; - yoff = sy * i; - for (j = 0; j < wm; j++) { - pixGetPixel(pixmr, j, i, &rval16); - pixGetPixel(pixmg, j, i, &gval16); - pixGetPixel(pixmb, j, i, &bval16); - xoff = sx * j; - for (k = 0; k < sy && yoff + k < h; k++) { - flines = lines + k * wpls; - flined = lined + k * wpld; - for (m = 0; m < sx && xoff + m < w; m++) { - vals = *(flines + xoff + m); - rvald = ((vals >> 24) * rval16) / 256; - rvald = L_MIN(rvald, 255); - gvald = (((vals >> 16) & 0xff) * gval16) / 256; - gvald = L_MIN(gvald, 255); - bvald = (((vals >> 8) & 0xff) * bval16) / 256; - bvald = L_MIN(bvald, 255); - composeRGBPixel(rvald, gvald, bvald, flined + xoff + m); - } - } - } - } - - return pixd; -} - - -/*------------------------------------------------------------------* - * Apply variable map * - *------------------------------------------------------------------*/ -/*! - * \brief pixApplyVariableGrayMap() - * - * \param[in] pixs 8 bpp - * \param[in] pixg 8 bpp, variable map - * \param[in] target typ. 128 for threshold - * \return pixd 8 bpp, or NULL on error - * - *
- * Notes: - * (1) Suppose you have an image that you want to transform based - * on some photometric measurement at each point, such as the - * threshold value for binarization. Representing the photometric - * measurement as an image pixg, you can threshold in input image - * using pixVarThresholdToBinary(). Alternatively, you can map - * the input image pointwise so that the threshold over the - * entire image becomes a constant, such as 128. For example, - * if a pixel in pixg is 150 and the target is 128, the - * corresponding pixel in pixs is mapped linearly to a value - * (128/150) of the input value. If the resulting mapped image - * pixd were then thresholded at 128, you would obtain the - * same result as a direct binarization using pixg with - * pixVarThresholdToBinary(). - * (2) The sizes of pixs and pixg must be equal. - *- */ -PIX * -pixApplyVariableGrayMap(PIX *pixs, - PIX *pixg, - l_int32 target) -{ -l_int32 i, j, w, h, d, wpls, wplg, wpld, vals, valg, vald; -l_uint8 *lut; -l_uint32 *datas, *datag, *datad, *lines, *lineg, *lined; -l_float32 fval; -PIX *pixd; - - PROCNAME("pixApplyVariableGrayMap"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!pixg) - return (PIX *)ERROR_PTR("pixg not defined", procName, NULL); - if (!pixSizesEqual(pixs, pixg)) - return (PIX *)ERROR_PTR("pix sizes not equal", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("depth not 8 bpp", procName, NULL); - - /* Generate a LUT for the mapping if the image is large enough - * to warrant the overhead. The LUT is of size 2^16. For the - * index to the table, get the MSB from pixs and the LSB from pixg. - * Note: this LUT is bigger than the typical 32K L1 cache, so - * we expect cache misses. L2 latencies are about 5ns. But - * division is slooooow. For large images, this function is about - * 4x faster when using the LUT. C'est la vie. */ - lut = NULL; - if (w * h > 100000) { /* more pixels than 2^16 */ - if ((lut = (l_uint8 *)LEPT_CALLOC(0x10000, sizeof(l_uint8))) == NULL) - return (PIX *)ERROR_PTR("lut not made", procName, NULL); - for (i = 0; i < 256; i++) { - for (j = 0; j < 256; j++) { - fval = (l_float32)(i * target) / (j + 0.5); - lut[(i << 8) + j] = L_MIN(255, (l_int32)(fval + 0.5)); - } - } - } - - if ((pixd = pixCreateNoInit(w, h, 8)) == NULL) { - LEPT_FREE(lut); - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - pixCopyResolution(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datag = pixGetData(pixg); - wplg = pixGetWpl(pixg); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lineg = datag + i * wplg; - lined = datad + i * wpld; - if (lut) { - for (j = 0; j < w; j++) { - vals = GET_DATA_BYTE(lines, j); - valg = GET_DATA_BYTE(lineg, j); - vald = lut[(vals << 8) + valg]; - SET_DATA_BYTE(lined, j, vald); - } - } - else { - for (j = 0; j < w; j++) { - vals = GET_DATA_BYTE(lines, j); - valg = GET_DATA_BYTE(lineg, j); - fval = (l_float32)(vals * target) / (valg + 0.5); - vald = L_MIN(255, (l_int32)(fval + 0.5)); - SET_DATA_BYTE(lined, j, vald); - } - } - } - - LEPT_FREE(lut); - return pixd; -} - - -/*------------------------------------------------------------------* - * Non-adaptive (global) mapping * - *------------------------------------------------------------------*/ -/*! - * \brief pixGlobalNormRGB() - * - * \param[in] pixd [optional] null, existing or equal to pixs - * \param[in] pixs 32 bpp rgb, or colormapped - * \param[in] rval, gval, bval pixel values in pixs that are - * linearly mapped to mapval - * \param[in] mapval use 255 for mapping to white - * \return pixd 32 bpp rgb or colormapped, or NULL on error - * - *
- * Notes: - * (1) The value of pixd determines if the results are written to a - * new pix (use NULL), in-place to pixs (use pixs), or to some - * other existing pix. - * (2) This does a global normalization of an image where the - * r,g,b color components are not balanced. Thus, white in pixs is - * represented by a set of r,g,b values that are not all 255. - * (3) The input values (rval, gval, bval) should be chosen to - * represent the gray color (mapval, mapval, mapval) in src. - * Thus, this function will map (rval, gval, bval) to that gray color. - * (4) Typically, mapval = 255, so that (rval, gval, bval) - * corresponds to the white point of src. In that case, these - * parameters should be chosen so that few pixels have higher values. - * (5) In all cases, we do a linear TRC separately on each of the - * components, saturating at 255. - * (6) If the input pix is 8 bpp without a colormap, you can get - * this functionality with mapval = 255 by calling: - * pixGammaTRC(pixd, pixs, 1.0, 0, bgval); - * where bgval is the value you want to be mapped to 255. - * Or more generally, if you want bgval to be mapped to mapval: - * pixGammaTRC(pixd, pixs, 1.0, 0, 255 * bgval / mapval); - *- */ -PIX * -pixGlobalNormRGB(PIX *pixd, - PIX *pixs, - l_int32 rval, - l_int32 gval, - l_int32 bval, - l_int32 mapval) -{ -l_int32 w, h, d, i, j, ncolors, rv, gv, bv, wpl; -l_int32 *rarray, *garray, *barray; -l_uint32 *data, *line; -NUMA *nar, *nag, *nab; -PIXCMAP *cmap; - - PROCNAME("pixGlobalNormRGB"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - cmap = pixGetColormap(pixs); - pixGetDimensions(pixs, &w, &h, &d); - if (!cmap && d != 32) - return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); - if (mapval <= 0) { - L_WARNING("mapval must be > 0; setting to 255\n", procName); - mapval = 255; - } - - /* Prepare pixd to be a copy of pixs */ - if ((pixd = pixCopy(pixd, pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - - /* Generate the TRC maps for each component. Make sure the - * upper range for each color is greater than zero. */ - nar = numaGammaTRC(1.0, 0, L_MAX(1, 255 * rval / mapval)); - nag = numaGammaTRC(1.0, 0, L_MAX(1, 255 * gval / mapval)); - nab = numaGammaTRC(1.0, 0, L_MAX(1, 255 * bval / mapval)); - - /* Extract copies of the internal arrays */ - rarray = numaGetIArray(nar); - garray = numaGetIArray(nag); - barray = numaGetIArray(nab); - if (!nar || !nag || !nab || !rarray || !garray || !barray) { - L_ERROR("allocation failure in arrays\n", procName); - goto cleanup_arrays; - } - - if (cmap) { - ncolors = pixcmapGetCount(cmap); - for (i = 0; i < ncolors; i++) { - pixcmapGetColor(cmap, i, &rv, &gv, &bv); - pixcmapResetColor(cmap, i, rarray[rv], garray[gv], barray[bv]); - } - } - else { - data = pixGetData(pixd); - wpl = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - extractRGBValues(line[j], &rv, &gv, &bv); - composeRGBPixel(rarray[rv], garray[gv], barray[bv], line + j); - } - } - } - -cleanup_arrays: - numaDestroy(&nar); - numaDestroy(&nag); - numaDestroy(&nab); - LEPT_FREE(rarray); - LEPT_FREE(garray); - LEPT_FREE(barray); - return pixd; -} - - -/*! - * \brief pixGlobalNormNoSatRGB() - * - * \param[in] pixd [optional] null, existing or equal to pixs - * \param[in] pixs 32 bpp rgb - * \param[in] rval, gval, bval pixel values in pixs that are - * linearly mapped to mapval; but see below - * \param[in] factor subsampling factor; integer >= 1 - * \param[in] rank between 0.0 and 1.0; typ. use a value near 1.0 - * \return pixd 32 bpp rgb, or NULL on error - * - *
- * Notes: - * (1) This is a version of pixGlobalNormRGB(), where the output - * intensity is scaled back so that a controlled fraction of - * pixel components is allowed to saturate. See comments in - * pixGlobalNormRGB(). - * (2) The value of pixd determines if the results are written to a - * new pix (use NULL), in-place to pixs (use pixs), or to some - * other existing pix. - * (3) This does a global normalization of an image where the - * r,g,b color components are not balanced. Thus, white in pixs is - * represented by a set of r,g,b values that are not all 255. - * (4) The input values (rval, gval, bval) can be chosen to be the - * color that, after normalization, becomes white background. - * For images that are mostly background, the closer these values - * are to the median component values, the closer the resulting - * background will be to gray, becoming white at the brightest places. - * (5) The mapval used in pixGlobalNormRGB() is computed here to - * avoid saturation of any component in the image (save for a - * fraction of the pixels given by the input rank value). - *- */ -PIX * -pixGlobalNormNoSatRGB(PIX *pixd, - PIX *pixs, - l_int32 rval, - l_int32 gval, - l_int32 bval, - l_int32 factor, - l_float32 rank) -{ -l_int32 mapval; -l_float32 rankrval, rankgval, rankbval; -l_float32 rfract, gfract, bfract, maxfract; - - PROCNAME("pixGlobalNormNoSatRGB"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (factor < 1) - return (PIX *)ERROR_PTR("sampling factor < 1", procName, NULL); - if (rank < 0.0 || rank > 1.0) - return (PIX *)ERROR_PTR("rank not in [0.0 ... 1.0]", procName, NULL); - if (rval <= 0 || gval <= 0 || bval <= 0) - return (PIX *)ERROR_PTR("invalid estim. color values", procName, NULL); - - /* The max value for each component may be larger than the - * input estimated background value. In that case, mapping - * for those pixels would saturate. To prevent saturation, - * we compute the fraction for each component by which we - * would oversaturate. Then take the max of these, and - * reduce, uniformly over all components, the output intensity - * by this value. Then no component will saturate. - * In practice, if rank < 1.0, a fraction of pixels - * may have a component saturate. By keeping rank close to 1.0, - * that fraction can be made arbitrarily small. */ - pixGetRankValueMaskedRGB(pixs, NULL, 0, 0, factor, rank, &rankrval, - &rankgval, &rankbval); - rfract = rankrval / (l_float32)rval; - gfract = rankgval / (l_float32)gval; - bfract = rankbval / (l_float32)bval; - maxfract = L_MAX(rfract, gfract); - maxfract = L_MAX(maxfract, bfract); -#if DEBUG_GLOBAL - lept_stderr("rankrval = %7.2f, rankgval = %7.2f, rankbval = %7.2f\n", - rankrval, rankgval, rankbval); - lept_stderr("rfract = %7.4f, gfract = %7.4f, bfract = %7.4f\n", - rfract, gfract, bfract); -#endif /* DEBUG_GLOBAL */ - - mapval = (l_int32)(255. / maxfract); - pixd = pixGlobalNormRGB(pixd, pixs, rval, gval, bval, mapval); - return pixd; -} - - -/*------------------------------------------------------------------* - * Adaptive threshold spread normalization * - *------------------------------------------------------------------*/ -/*! - * \brief pixThresholdSpreadNorm() - * - * \param[in] pixs 8 bpp grayscale; not colormapped - * \param[in] filtertype L_SOBEL_EDGE or L_TWO_SIDED_EDGE; - * \param[in] edgethresh threshold on magnitude of edge filter; - * typ 10-20 - * \param[in] smoothx, smoothy half-width of convolution kernel applied to - * spread threshold: use 0 for no smoothing - * \param[in] gamma gamma correction; typ. about 0.7 - * \param[in] minval input value that gives 0 for output; typ. -25 - * \param[in] maxval input value that gives 255 for output; - * typ. 255 - * \param[in] targetthresh target threshold for normalization - * \param[out] ppixth [optional] computed local threshold value - * \param[out] ppixb [optional] thresholded normalized image - * \param[out] ppixd [optional] normalized image - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The basis of this approach is the use of seed spreading - * on a (possibly) sparse set of estimates for the local threshold. - * The resulting dense estimates are smoothed by convolution - * and used to either threshold the input image or normalize it - * with a local transformation that linearly maps the pixels so - * that the local threshold estimate becomes constant over the - * resulting image. This approach is one of several that - * have been suggested (and implemented) by Ray Smith. - * (2) You can use either the Sobel or TwoSided edge filters. - * The results appear to be similar, using typical values - * of edgethresh in the rang 10-20. - * (3) To skip the trc enhancement, use gamma = 1.0, minval = 0 - * and maxval = 255. - * (4) For the normalized image pixd, each pixel is linearly mapped - * in such a way that the local threshold is equal to targetthresh. - * (5) The full width and height of the convolution kernel - * are (2 * smoothx + 1) and (2 * smoothy + 1). - * (6) This function can be used with the pixtiling utility if the - * images are too large. See pixOtsuAdaptiveThreshold() for - * an example of this. - *- */ -l_ok -pixThresholdSpreadNorm(PIX *pixs, - l_int32 filtertype, - l_int32 edgethresh, - l_int32 smoothx, - l_int32 smoothy, - l_float32 gamma, - l_int32 minval, - l_int32 maxval, - l_int32 targetthresh, - PIX **ppixth, - PIX **ppixb, - PIX **ppixd) -{ -PIX *pixe, *pixet, *pixsd, *pixg1, *pixg2, *pixth; - - PROCNAME("pixThresholdSpreadNorm"); - - if (ppixth) *ppixth = NULL; - if (ppixb) *ppixb = NULL; - if (ppixd) *ppixd = NULL; - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); - if (pixGetColormap(pixs)) - return ERROR_INT("pixs is colormapped", procName, 1); - if (!ppixth && !ppixb && !ppixd) - return ERROR_INT("no output requested", procName, 1); - if (filtertype != L_SOBEL_EDGE && filtertype != L_TWO_SIDED_EDGE) - return ERROR_INT("invalid filter type", procName, 1); - - /* Get the thresholded edge pixels. These are the ones - * that have values in pixs near the local optimal fg/bg threshold. */ - if (filtertype == L_SOBEL_EDGE) - pixe = pixSobelEdgeFilter(pixs, L_VERTICAL_EDGES); - else /* L_TWO_SIDED_EDGE */ - pixe = pixTwoSidedEdgeFilter(pixs, L_VERTICAL_EDGES); - pixet = pixThresholdToBinary(pixe, edgethresh); - pixInvert(pixet, pixet); - - /* Build a seed image whose only nonzero values are those - * values of pixs corresponding to pixels in the fg of pixet. */ - pixsd = pixCreateTemplate(pixs); - pixCombineMasked(pixsd, pixs, pixet); - - /* Spread the seed and optionally smooth to reduce noise */ - pixg1 = pixSeedspread(pixsd, 4); - pixg2 = pixBlockconv(pixg1, smoothx, smoothy); - - /* Optionally do a gamma enhancement */ - pixth = pixGammaTRC(NULL, pixg2, gamma, minval, maxval); - - /* Do the mapping and thresholding */ - if (ppixd) { - *ppixd = pixApplyVariableGrayMap(pixs, pixth, targetthresh); - if (ppixb) - *ppixb = pixThresholdToBinary(*ppixd, targetthresh); - } - else if (ppixb) - *ppixb = pixVarThresholdToBinary(pixs, pixth); - - if (ppixth) - *ppixth = pixth; - else - pixDestroy(&pixth); - - pixDestroy(&pixe); - pixDestroy(&pixet); - pixDestroy(&pixsd); - pixDestroy(&pixg1); - pixDestroy(&pixg2); - return 0; -} - - -/*------------------------------------------------------------------* - * Adaptive background normalization (flexible adaptaption) * - *------------------------------------------------------------------*/ -/*! - * \brief pixBackgroundNormFlex() - * - * \param[in] pixs 8 bpp grayscale; not colormapped - * \param[in] sx, sy desired tile dimensions; size may vary; - * use values between 3 and 10 - * \param[in] smoothx, smoothy half-width of convolution kernel applied to - * threshold array: use values between 1 and 3 - * \param[in] delta difference parameter in basin filling; - * use 0 to skip - * \return pixd 8 bpp, background-normalized), or NULL on error - * - *
- * Notes: - * (1) This does adaptation flexibly to a quickly varying background. - * For that reason, all input parameters should be small. - * (2) sx and sy give the tile size; they should be in [5 - 7]. - * (3) The full width and height of the convolution kernel - * are (2 * smoothx + 1) and (2 * smoothy + 1). They - * should be in [1 - 2]. - * (4) Basin filling is used to fill the large fg regions. The - * parameter %delta measures the height that the black - * background is raised from the local minima. By raising - * the background, it is possible to threshold the large - * fg regions to foreground. If %delta is too large, - * bg regions will be lifted, causing thickening of - * the fg regions. Use 0 to skip. - *- */ -PIX * -pixBackgroundNormFlex(PIX *pixs, - l_int32 sx, - l_int32 sy, - l_int32 smoothx, - l_int32 smoothy, - l_int32 delta) -{ -l_float32 scalex, scaley; -PIX *pixt, *pixsd, *pixmin, *pixbg, *pixbgi, *pixd; - - PROCNAME("pixBackgroundNormFlex"); - - if (!pixs || pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL); - if (sx < 3 || sy < 3) - return (PIX *)ERROR_PTR("sx and/or sy less than 3", procName, NULL); - if (sx > 10 || sy > 10) - return (PIX *)ERROR_PTR("sx and/or sy exceed 10", procName, NULL); - if (smoothx < 1 || smoothy < 1) - return (PIX *)ERROR_PTR("smooth params less than 1", procName, NULL); - if (smoothx > 3 || smoothy > 3) - return (PIX *)ERROR_PTR("smooth params exceed 3", procName, NULL); - - /* Generate the bg estimate using smoothed average with subsampling */ - scalex = 1. / (l_float32)sx; - scaley = 1. / (l_float32)sy; - pixt = pixScaleSmooth(pixs, scalex, scaley); - - /* Do basin filling on the bg estimate if requested */ - if (delta <= 0) - pixsd = pixClone(pixt); - else { - pixLocalExtrema(pixt, 0, 0, &pixmin, NULL); - pixsd = pixSeedfillGrayBasin(pixmin, pixt, delta, 4); - pixDestroy(&pixmin); - } - pixbg = pixExtendByReplication(pixsd, 1, 1); - - /* Map the bg to 200 */ - pixbgi = pixGetInvBackgroundMap(pixbg, 200, smoothx, smoothy); - pixd = pixApplyInvBackgroundGrayMap(pixs, pixbgi, sx, sy); - - pixDestroy(&pixt); - pixDestroy(&pixsd); - pixDestroy(&pixbg); - pixDestroy(&pixbgi); - return pixd; -} - - -/*------------------------------------------------------------------* - * Adaptive contrast normalization * - *------------------------------------------------------------------*/ -/*! - * \brief pixContrastNorm() - * - * \param[in] pixd [optional] 8 bpp; null or equal to pixs - * \param[in] pixs 8 bpp grayscale; not colormapped - * \param[in] sx, sy tile dimensions - * \param[in] mindiff minimum difference to accept as valid - * \param[in] smoothx, smoothy half-width of convolution kernel applied to - * min and max arrays: use 0 for no smoothing - * \return pixd always - * - *
- * Notes: - * (1) This function adaptively attempts to expand the contrast - * to the full dynamic range in each tile. If the contrast in - * a tile is smaller than %mindiff, it uses the min and max - * pixel values from neighboring tiles. It also can use - * convolution to smooth the min and max values from - * neighboring tiles. After all that processing, it is - * possible that the actual pixel values in the tile are outside - * the computed [min ... max] range for local contrast - * normalization. Such pixels are taken to be at either 0 - * (if below the min) or 255 (if above the max). - * (2) pixd can be equal to pixs (in-place operation) or - * null (makes a new pixd). - * (3) sx and sy give the tile size; they are typically at least 20. - * (4) mindiff is used to eliminate results for tiles where it is - * likely that either fg or bg is missing. A value around 50 - * or more is reasonable. - * (5) The full width and height of the convolution kernel - * are (2 * smoothx + 1) and (2 * smoothy + 1). Some smoothing - * is typically useful, and we limit the smoothing half-widths - * to the range from 0 to 8. - * (6) A linear TRC (gamma = 1.0) is applied to increase the contrast - * in each tile. The result can subsequently be globally corrected, - * by applying pixGammaTRC() with arbitrary values of gamma - * and the 0 and 255 points of the mapping. - *- */ -PIX * -pixContrastNorm(PIX *pixd, - PIX *pixs, - l_int32 sx, - l_int32 sy, - l_int32 mindiff, - l_int32 smoothx, - l_int32 smoothy) -{ -PIX *pixmin, *pixmax; - - PROCNAME("pixContrastNorm"); - - if (!pixs || pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, pixd); - if (pixd && pixd != pixs) - return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs is colormapped", procName, pixd); - if (sx < 5 || sy < 5) - return (PIX *)ERROR_PTR("sx and/or sy less than 5", procName, pixd); - if (smoothx < 0 || smoothy < 0) - return (PIX *)ERROR_PTR("smooth params less than 0", procName, pixd); - if (smoothx > 8 || smoothy > 8) - return (PIX *)ERROR_PTR("smooth params exceed 8", procName, pixd); - - /* Get the min and max pixel values in each tile, and represent - * each value as a pixel in pixmin and pixmax, respectively. */ - pixMinMaxTiles(pixs, sx, sy, mindiff, smoothx, smoothy, &pixmin, &pixmax); - - /* For each tile, do a linear expansion of the dynamic range - * of pixels so that the min value is mapped to 0 and the - * max value is mapped to 255. */ - pixd = pixLinearTRCTiled(pixd, pixs, sx, sy, pixmin, pixmax); - - pixDestroy(&pixmin); - pixDestroy(&pixmax); - return pixd; -} - - -/*! - * \brief pixMinMaxTiles() - * - * \param[in] pixs 8 bpp grayscale; not colormapped - * \param[in] sx, sy tile dimensions - * \param[in] mindiff minimum difference to accept as valid - * \param[in] smoothx, smoothy half-width of convolution kernel applied to - * min and max arrays: use 0 for no smoothing - * \param[out] ppixmin tiled minima - * \param[out] ppixmax tiled maxima - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This computes filtered and smoothed values for the min and - * max pixel values in each tile of the image. - * (2) See pixContrastNorm() for usage. - *- */ -l_ok -pixMinMaxTiles(PIX *pixs, - l_int32 sx, - l_int32 sy, - l_int32 mindiff, - l_int32 smoothx, - l_int32 smoothy, - PIX **ppixmin, - PIX **ppixmax) -{ -l_int32 w, h; -PIX *pixmin1, *pixmax1, *pixmin2, *pixmax2; - - PROCNAME("pixMinMaxTiles"); - - if (ppixmin) *ppixmin = NULL; - if (ppixmax) *ppixmax = NULL; - if (!ppixmin || !ppixmax) - return ERROR_INT("&pixmin or &pixmax undefined", procName, 1); - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs undefined or not 8 bpp", procName, 1); - if (pixGetColormap(pixs)) - return ERROR_INT("pixs is colormapped", procName, 1); - if (sx < 5 || sy < 5) - return ERROR_INT("sx and/or sy less than 3", procName, 1); - if (smoothx < 0 || smoothy < 0) - return ERROR_INT("smooth params less than 0", procName, 1); - if (smoothx > 5 || smoothy > 5) - return ERROR_INT("smooth params exceed 5", procName, 1); - - /* Get the min and max values in each tile */ - pixmin1 = pixScaleGrayMinMax(pixs, sx, sy, L_CHOOSE_MIN); - pixmax1 = pixScaleGrayMinMax(pixs, sx, sy, L_CHOOSE_MAX); - - pixmin2 = pixExtendByReplication(pixmin1, 1, 1); - pixmax2 = pixExtendByReplication(pixmax1, 1, 1); - pixDestroy(&pixmin1); - pixDestroy(&pixmax1); - - /* Make sure no value is 0 */ - pixAddConstantGray(pixmin2, 1); - pixAddConstantGray(pixmax2, 1); - - /* Generate holes where the contrast is too small */ - pixSetLowContrast(pixmin2, pixmax2, mindiff); - - /* Fill the holes (0 values) */ - pixGetDimensions(pixmin2, &w, &h, NULL); - pixFillMapHoles(pixmin2, w, h, L_FILL_BLACK); - pixFillMapHoles(pixmax2, w, h, L_FILL_BLACK); - - /* Smooth if requested */ - if (smoothx > 0 || smoothy > 0) { - smoothx = L_MIN(smoothx, (w - 1) / 2); - smoothy = L_MIN(smoothy, (h - 1) / 2); - *ppixmin = pixBlockconv(pixmin2, smoothx, smoothy); - *ppixmax = pixBlockconv(pixmax2, smoothx, smoothy); - } - else { - *ppixmin = pixClone(pixmin2); - *ppixmax = pixClone(pixmax2); - } - pixCopyResolution(*ppixmin, pixs); - pixCopyResolution(*ppixmax, pixs); - pixDestroy(&pixmin2); - pixDestroy(&pixmax2); - - return 0; -} - - -/*! - * \brief pixSetLowContrast() - * - * \param[in] pixs1 8 bpp - * \param[in] pixs2 8 bpp - * \param[in] mindiff minimum difference to accept as valid - * \return 0 if OK; 1 if no pixel diffs are large enough, or on error - * - *
- * Notes: - * (1) This compares corresponding pixels in pixs1 and pixs2. - * When they differ by less than %mindiff, set the pixel - * values to 0 in each. Each pixel typically represents a tile - * in a larger image, and a very small difference between - * the min and max in the tile indicates that the min and max - * values are not to be trusted. - * (2) If contrast (pixel difference) detection is expected to fail, - * caller should check return value. - *- */ -l_ok -pixSetLowContrast(PIX *pixs1, - PIX *pixs2, - l_int32 mindiff) -{ -l_int32 i, j, w, h, d, wpl, val1, val2, found; -l_uint32 *data1, *data2, *line1, *line2; - - PROCNAME("pixSetLowContrast"); - - if (!pixs1 || !pixs2) - return ERROR_INT("pixs1 and pixs2 not both defined", procName, 1); - if (pixSizesEqual(pixs1, pixs2) == 0) - return ERROR_INT("pixs1 and pixs2 not equal size", procName, 1); - pixGetDimensions(pixs1, &w, &h, &d); - if (d != 8) - return ERROR_INT("depth not 8 bpp", procName, 1); - if (mindiff > 254) return 0; - - data1 = pixGetData(pixs1); - data2 = pixGetData(pixs2); - wpl = pixGetWpl(pixs1); - found = 0; /* init to not finding any diffs >= mindiff */ - for (i = 0; i < h; i++) { - line1 = data1 + i * wpl; - line2 = data2 + i * wpl; - for (j = 0; j < w; j++) { - val1 = GET_DATA_BYTE(line1, j); - val2 = GET_DATA_BYTE(line2, j); - if (L_ABS(val1 - val2) >= mindiff) { - found = 1; - break; - } - } - if (found) break; - } - if (!found) { - L_WARNING("no pixel pair diffs as large as mindiff\n", procName); - pixClearAll(pixs1); - pixClearAll(pixs2); - return 1; - } - - for (i = 0; i < h; i++) { - line1 = data1 + i * wpl; - line2 = data2 + i * wpl; - for (j = 0; j < w; j++) { - val1 = GET_DATA_BYTE(line1, j); - val2 = GET_DATA_BYTE(line2, j); - if (L_ABS(val1 - val2) < mindiff) { - SET_DATA_BYTE(line1, j, 0); - SET_DATA_BYTE(line2, j, 0); - } - } - } - - return 0; -} - - -/*! - * \brief pixLinearTRCTiled() - * - * \param[in] pixd [optional] 8 bpp - * \param[in] pixs 8 bpp, not colormapped - * \param[in] sx, sy tile dimensions - * \param[in] pixmin pix of min values in tiles - * \param[in] pixmax pix of max values in tiles - * \return pixd always - * - *
- * Notes: - * (1) pixd can be equal to pixs (in-place operation) or - * null (makes a new pixd). - * (2) sx and sy give the tile size; they are typically at least 20. - * (3) pixmin and pixmax are generated by pixMinMaxTiles() - * (4) For each tile, this does a linear expansion of the dynamic - * range so that the min value in the tile becomes 0 and the - * max value in the tile becomes 255. - * (5) The LUTs that do the mapping are generated as needed - * and stored for reuse in an integer array within the ptr array iaa[]. - *- */ -PIX * -pixLinearTRCTiled(PIX *pixd, - PIX *pixs, - l_int32 sx, - l_int32 sy, - PIX *pixmin, - PIX *pixmax) -{ -l_int32 i, j, k, m, w, h, wt, ht, wpl, wplt, xoff, yoff; -l_int32 minval, maxval, val, sval; -l_int32 *ia; -l_int32 **iaa; -l_uint32 *data, *datamin, *datamax, *line, *tline, *linemin, *linemax; - - PROCNAME("pixLinearTRCTiled"); - - if (!pixs || pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, pixd); - if (pixd && pixd != pixs) - return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs is colormapped", procName, pixd); - if (!pixmin || !pixmax) - return (PIX *)ERROR_PTR("pixmin & pixmax not defined", procName, pixd); - if (sx < 5 || sy < 5) - return (PIX *)ERROR_PTR("sx and/or sy less than 5", procName, pixd); - - if ((iaa = (l_int32 **)LEPT_CALLOC(256, sizeof(l_int32 *))) == NULL) - return (PIX *)ERROR_PTR("iaa not made", procName, NULL); - if ((pixd = pixCopy(pixd, pixs)) == NULL) { - LEPT_FREE(iaa); - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - pixGetDimensions(pixd, &w, &h, NULL); - - data = pixGetData(pixd); - wpl = pixGetWpl(pixd); - datamin = pixGetData(pixmin); - datamax = pixGetData(pixmax); - wplt = pixGetWpl(pixmin); - pixGetDimensions(pixmin, &wt, &ht, NULL); - for (i = 0; i < ht; i++) { - line = data + sy * i * wpl; - linemin = datamin + i * wplt; - linemax = datamax + i * wplt; - yoff = sy * i; - for (j = 0; j < wt; j++) { - xoff = sx * j; - minval = GET_DATA_BYTE(linemin, j); - maxval = GET_DATA_BYTE(linemax, j); - if (maxval == minval) { - L_ERROR("shouldn't happen! i,j = %d,%d, minval = %d\n", - procName, i, j, minval); - continue; - } - if ((ia = iaaGetLinearTRC(iaa, maxval - minval)) == NULL) { - L_ERROR("failure to make ia for j = %d!\n", procName, j); - continue; - } - for (k = 0; k < sy && yoff + k < h; k++) { - tline = line + k * wpl; - for (m = 0; m < sx && xoff + m < w; m++) { - val = GET_DATA_BYTE(tline, xoff + m); - sval = val - minval; - sval = L_MAX(0, sval); - SET_DATA_BYTE(tline, xoff + m, ia[sval]); - } - } - } - } - - for (i = 0; i < 256; i++) - LEPT_FREE(iaa[i]); - LEPT_FREE(iaa); - return pixd; -} - - -/*! - * \brief iaaGetLinearTRC() - * - * \param[in] iaa bare array of ptrs to l_int32 - * \param[in] diff between min and max pixel values that are - * to be mapped to 0 and 255 - * \return ia LUT with input (val - minval) and output a - * value between 0 and 255) - */ -static l_int32 * -iaaGetLinearTRC(l_int32 **iaa, - l_int32 diff) -{ -l_int32 i; -l_int32 *ia; -l_float32 factor; - - PROCNAME("iaaGetLinearTRC"); - - if (!iaa) - return (l_int32 *)ERROR_PTR("iaa not defined", procName, NULL); - - if (iaa[diff] != NULL) /* already have it */ - return iaa[diff]; - - if ((ia = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32))) == NULL) - return (l_int32 *)ERROR_PTR("ia not made", procName, NULL); - iaa[diff] = ia; - if (diff == 0) { /* shouldn't happen */ - for (i = 0; i < 256; i++) - ia[i] = 128; - } - else { - factor = 255. / (l_float32)diff; - for (i = 0; i < diff + 1; i++) - ia[i] = (l_int32)(factor * i + 0.5); - for (i = diff + 1; i < 256; i++) - ia[i] = 255; - } - - return ia; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/affine.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/affine.c deleted file mode 100644 index 5c0214e4..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/affine.c +++ /dev/null @@ -1,1624 +0,0 @@ -/*====================================================================* - - 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 affine.c - *
- * - * Affine (3 pt) image transformation using a sampled - * (to nearest integer) transform on each dest point - * PIX *pixAffineSampledPta() - * PIX *pixAffineSampled() - * - * Affine (3 pt) image transformation using interpolation - * (or area mapping) for anti-aliasing images that are - * 2, 4, or 8 bpp gray, or colormapped, or 32 bpp RGB - * PIX *pixAffinePta() - * PIX *pixAffine() - * PIX *pixAffinePtaColor() - * PIX *pixAffineColor() - * PIX *pixAffinePtaGray() - * PIX *pixAffineGray() - * - * Affine transform including alpha (blend) component - * PIX *pixAffinePtaWithAlpha() - * - * Affine coordinate transformation - * l_int32 getAffineXformCoeffs() - * l_int32 affineInvertXform() - * l_int32 affineXformSampledPt() - * l_int32 affineXformPt() - * - * Interpolation helper functions - * l_int32 linearInterpolatePixelGray() - * l_int32 linearInterpolatePixelColor() - * - * Gauss-jordan linear equation solver - * l_int32 gaussjordan() - * - * Affine image transformation using a sequence of - * shear/scale/translation operations - * PIX *pixAffineSequential() - * - * One can define a coordinate space by the location of the origin, - * the orientation of x and y axes, and the unit scaling along - * each axis. An affine transform is a general linear - * transformation from one coordinate space to another. - * - * For the general case, we can define the affine transform using - * two sets of three (noncollinear) points in a plane. One set - * corresponds to the input (src) coordinate space; the other to the - * transformed (dest) coordinate space. Each point in the - * src corresponds to one of the points in the dest. With two - * sets of three points, we get a set of 6 equations in 6 unknowns - * that specifies the mapping between the coordinate spaces. - * The interface here allows you to specify either the corresponding - * sets of 3 points, or the transform itself (as a vector of 6 - * coefficients). - * - * Given the transform as a vector of 6 coefficients, we can compute - * both a a pointwise affine coordinate transformation and an - * affine image transformation. - * - * To compute the coordinate transform, we need the coordinate - * value (x',y') in the transformed space for any point (x,y) - * in the original space. To derive this transform from the - * three corresponding points, it is convenient to express the affine - * coordinate transformation using an LU decomposition of - * a set of six linear equations that express the six coordinates - * of the three points in the transformed space as a function of - * the six coordinates in the original space. Once we have - * this transform matrix , we can transform an image by - * finding, for each destination pixel, the pixel (or pixels) - * in the source that give rise to it. - * - * This 'pointwise' transformation can be done either by sampling - * and picking a single pixel in the src to replicate into the dest, - * or by interpolating (or averaging) over four src pixels to - * determine the value of the dest pixel. The first method is - * implemented by pixAffineSampled() and the second method by - * pixAffine(). The interpolated method can only be used for - * images with more than 1 bpp, but for these, the image quality - * is significantly better than the sampled method, due to - * the 'antialiasing' effect of weighting the src pixels. - * - * Interpolation works well when there is relatively little scaling, - * or if there is image expansion in general. However, if there - * is significant image reduction, one should apply a low-pass - * filter before subsampling to avoid aliasing the high frequencies. - * - * A typical application might be to align two images, which - * may be scaled, rotated and translated versions of each other. - * Through some pre-processing, three corresponding points are - * located in each of the two images. One of the images is - * then to be (affine) transformed to align with the other. - * As mentioned, the standard way to do this is to use three - * sets of points, compute the 6 transformation coefficients - * from these points that describe the linear transformation, - * - * x' = ax + by + c - * y' = dx + ey + f - * - * and use this in a pointwise manner to transform the image. - * - * N.B. Be sure to see the comment in getAffineXformCoeffs(), - * regarding using the inverse of the affine transform for points - * to transform images. - * - * There is another way to do this transformation; namely, - * by doing a sequence of simple affine transforms, without - * computing directly the affine coordinate transformation. - * We have at our disposal (1) translations (using rasterop), - * (2) horizontal and vertical shear about any horizontal and vertical - * line, respectively, and (3) non-isotropic scaling by two - * arbitrary x and y scaling factors. We also have rotation - * about an arbitrary point, but this is equivalent to a set - * of three shears so we do not need to use it. - * - * Why might we do this? For binary images, it is usually - * more efficient to do such transformations by a sequence - * of word parallel operations. Shear and translation can be - * done in-place and word parallel; arbitrary scaling is - * mostly pixel-wise. - * - * Suppose that we are transforming image 1 to correspond to image 2. - * We have a set of three points, describing the coordinate space - * embedded in image 1, and we need to transform image 1 until - * those three points exactly correspond to the new coordinate space - * defined by the second set of three points. In our image - * matching application, the latter set of three points was - * found to be the corresponding points in image 2. - * - * The most elegant way I can think of to do such a sequential - * implementation is to imagine that we're going to transform - * BOTH images until they're aligned. (We don't really want - * to transform both, because in fact we may only have one image - * that is undergoing a general affine transformation.) - * - * Choose the 3 corresponding points as follows: - * ~ The 1st point is an origin - * ~ The 2nd point gives the orientation and scaling of the - * "x" axis with respect to the origin - * ~ The 3rd point does likewise for the "y" axis. - * These "axes" must not be collinear; otherwise they are - * arbitrary (although some strange things will happen if - * the handedness sweeping through the minimum angle between - * the axes is opposite). - * - * An important constraint is that we have shear operations - * about an arbitrary horizontal or vertical line, but always - * parallel to the x or y axis. If we continue to pretend that - * we have an unprimed coordinate space embedded in image 1 and - * a primed coordinate space embedded in image 2, we imagine - * (a) transforming image 1 by horizontal and vertical shears about - * point 1 to align points 3 and 2 along the y and x axes, - * respectively, and (b) transforming image 2 by horizontal and - * vertical shears about point 1' to align points 3' and 2' along - * the y and x axes. Then we scale image 1 so that the distances - * from 1 to 2 and from 1 to 3 are equal to the distances in - * image 2 from 1' to 2' and from 1' to 3'. This scaling operation - * leaves the true image origin, at (0,0) invariant, and will in - * general translate point 1. The original points 1 and 1' will - * typically not coincide in any event, so we must translate - * the origin of image 1, at its current point 1, to the origin - * of image 2 at 1'. The images should now be aligned. But - * because we never really transformed image 2 (and image 2 may - * not even exist), we now perform on image 1 the reverse of - * the shear transforms that we imagined doing on image 2; - * namely, the negative vertical shear followed by the negative - * horizontal shear. Image 1 should now have its transformed - * unprimed coordinates aligned with the original primed - * coordinates. In all this, it is only necessary to keep track - * of the shear angles and translations of points during the shears. - * What has been accomplished is a general affine transformation - * on image 1. - * - * Having described all this, if you are going to use an - * affine transformation in an application, this is what you - * need to know: - * - * (1) You should NEVER use the sequential method, because - * the image quality for 1 bpp text is much poorer - * (even though it is about 2x faster than the pointwise sampled - * method), and for images with depth greater than 1, it is - * nearly 20x slower than the pointwise sampled method - * and over 10x slower than the pointwise interpolated method! - * The sequential method is given here for purely - * pedagogical reasons. - * - * (2) For 1 bpp images, use the pointwise sampled function - * pixAffineSampled(). For all other images, the best - * quality results result from using the pointwise - * interpolated function pixAffinePta() or pixAffine(); - * the cost is less than a doubling of the computation time - * with respect to the sampled function. If you use - * interpolation on colormapped images, the colormap will - * be removed, resulting in either a grayscale or color - * image, depending on the values in the colormap. - * If you want to retain the colormap, use pixAffineSampled(). - * - * Typical relative timing of pointwise transforms (sampled = 1.0): - * 8 bpp: sampled 1.0 - * interpolated 1.6 - * 32 bpp: sampled 1.0 - * interpolated 1.8 - * Additionally, the computation time/pixel is nearly the same - * for 8 bpp and 32 bpp, for both sampled and interpolated. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) Brings in either black or white pixels from the boundary. - * (2) Retains colormap, which you can do for a sampled transform.. - * (3) The 3 points must not be collinear. - * (4) The order of the 3 points is arbitrary; however, to compare - * with the sequential transform they must be in these locations - * and in this order: origin, x-axis, y-axis. - * (5) For 1 bpp images, this has much better quality results - * than pixAffineSequential(), particularly for text. - * It is about 3x slower, but does not require additional - * border pixels. The poor quality of pixAffineSequential() - * is due to repeated quantized transforms. It is strongly - * recommended that pixAffineSampled() be used for 1 bpp images. - * (6) For 8 or 32 bpp, much better quality is obtained by the - * somewhat slower pixAffinePta(). See that function - * for relative timings between sampled and interpolated. - * (7) To repeat, use of the sequential transform, - * pixAffineSequential(), for any images, is discouraged. - *- */ -PIX * -pixAffineSampledPta(PIX *pixs, - PTA *ptad, - PTA *ptas, - l_int32 incolor) -{ -l_float32 *vc; -PIX *pixd; - - PROCNAME("pixAffineSampledPta"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!ptas) - return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); - if (!ptad) - return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); - if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) - return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); - if (ptaGetCount(ptas) != 3) - return (PIX *)ERROR_PTR("ptas count not 3", procName, NULL); - if (ptaGetCount(ptad) != 3) - return (PIX *)ERROR_PTR("ptad count not 3", procName, NULL); - - /* Get backwards transform from dest to src, and apply it */ - getAffineXformCoeffs(ptad, ptas, &vc); - pixd = pixAffineSampled(pixs, vc, incolor); - LEPT_FREE(vc); - - return pixd; -} - - -/*! - * \brief pixAffineSampled() - * - * \param[in] pixs all depths - * \param[in] vc vector of 6 coefficients for affine transformation - * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Brings in either black or white pixels from the boundary. - * (2) Retains colormap, which you can do for a sampled transform.. - * (3) For 8 or 32 bpp, much better quality is obtained by the - * somewhat slower pixAffine(). See that function - * for relative timings between sampled and interpolated. - *- */ -PIX * -pixAffineSampled(PIX *pixs, - l_float32 *vc, - l_int32 incolor) -{ -l_int32 i, j, w, h, d, x, y, wpls, wpld, color, cmapindex; -l_uint32 val; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixAffineSampled"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!vc) - return (PIX *)ERROR_PTR("vc not defined", procName, NULL); - if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) - return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32) - return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8 or 16", procName, NULL); - - /* Init all dest pixels to color to be brought in from outside */ - pixd = pixCreateTemplate(pixs); - if ((cmap = pixGetColormap(pixs)) != NULL) { - if (incolor == L_BRING_IN_WHITE) - color = 1; - else - color = 0; - pixcmapAddBlackOrWhite(cmap, color, &cmapindex); - pixSetAllArbitrary(pixd, cmapindex); - } else { - if ((d == 1 && incolor == L_BRING_IN_WHITE) || - (d > 1 && incolor == L_BRING_IN_BLACK)) { - pixClearAll(pixd); - } else { - pixSetAll(pixd); - } - } - - /* Scan over the dest pixels */ - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - affineXformSampledPt(vc, j, i, &x, &y); - if (x < 0 || y < 0 || x >=w || y >= h) - continue; - lines = datas + y * wpls; - if (d == 1) { - val = GET_DATA_BIT(lines, x); - SET_DATA_BIT_VAL(lined, j, val); - } else if (d == 8) { - val = GET_DATA_BYTE(lines, x); - SET_DATA_BYTE(lined, j, val); - } else if (d == 32) { - lined[j] = lines[x]; - } else if (d == 2) { - val = GET_DATA_DIBIT(lines, x); - SET_DATA_DIBIT(lined, j, val); - } else if (d == 4) { - val = GET_DATA_QBIT(lines, x); - SET_DATA_QBIT(lined, j, val); - } - } - } - - return pixd; -} - - -/*---------------------------------------------------------------------* - * Interpolated affine image transformation * - *---------------------------------------------------------------------*/ -/*! - * \brief pixAffinePta() - * - * \param[in] pixs all depths; colormap ok - * \param[in] ptad 3 pts of final coordinate space - * \param[in] ptas 3 pts of initial coordinate space - * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Brings in either black or white pixels from the boundary - * (2) Removes any existing colormap, if necessary, before transforming - *- */ -PIX * -pixAffinePta(PIX *pixs, - PTA *ptad, - PTA *ptas, - l_int32 incolor) -{ -l_int32 d; -l_uint32 colorval; -PIX *pixt1, *pixt2, *pixd; - - PROCNAME("pixAffinePta"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!ptas) - return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); - if (!ptad) - return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); - if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) - return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); - if (ptaGetCount(ptas) != 3) - return (PIX *)ERROR_PTR("ptas count not 3", procName, NULL); - if (ptaGetCount(ptad) != 3) - return (PIX *)ERROR_PTR("ptad count not 3", procName, NULL); - - if (pixGetDepth(pixs) == 1) - return pixAffineSampledPta(pixs, ptad, ptas, incolor); - - /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ - pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - d = pixGetDepth(pixt1); - if (d < 8) - pixt2 = pixConvertTo8(pixt1, FALSE); - else - pixt2 = pixClone(pixt1); - d = pixGetDepth(pixt2); - - /* Compute actual color to bring in from edges */ - colorval = 0; - if (incolor == L_BRING_IN_WHITE) { - if (d == 8) - colorval = 255; - else /* d == 32 */ - colorval = 0xffffff00; - } - - if (d == 8) - pixd = pixAffinePtaGray(pixt2, ptad, ptas, colorval); - else /* d == 32 */ - pixd = pixAffinePtaColor(pixt2, ptad, ptas, colorval); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - return pixd; -} - - -/*! - * \brief pixAffine() - * - * \param[in] pixs all depths; colormap ok - * \param[in] vc vector of 6 coefficients for affine transformation - * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Brings in either black or white pixels from the boundary - * (2) Removes any existing colormap, if necessary, before transforming - *- */ -PIX * -pixAffine(PIX *pixs, - l_float32 *vc, - l_int32 incolor) -{ -l_int32 d; -l_uint32 colorval; -PIX *pixt1, *pixt2, *pixd; - - PROCNAME("pixAffine"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!vc) - return (PIX *)ERROR_PTR("vc not defined", procName, NULL); - - if (pixGetDepth(pixs) == 1) - return pixAffineSampled(pixs, vc, incolor); - - /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ - pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - d = pixGetDepth(pixt1); - if (d < 8) - pixt2 = pixConvertTo8(pixt1, FALSE); - else - pixt2 = pixClone(pixt1); - d = pixGetDepth(pixt2); - - /* Compute actual color to bring in from edges */ - colorval = 0; - if (incolor == L_BRING_IN_WHITE) { - if (d == 8) - colorval = 255; - else /* d == 32 */ - colorval = 0xffffff00; - } - - if (d == 8) - pixd = pixAffineGray(pixt2, vc, colorval); - else /* d == 32 */ - pixd = pixAffineColor(pixt2, vc, colorval); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - return pixd; -} - - -/*! - * \brief pixAffinePtaColor() - * - * \param[in] pixs 32 bpp - * \param[in] ptad 3 pts of final coordinate space - * \param[in] ptas 3 pts of initial coordinate space - * \param[in] colorval e.g.: 0 to bring in BLACK, 0xffffff00 for WHITE - * \return pixd, or NULL on error - */ -PIX * -pixAffinePtaColor(PIX *pixs, - PTA *ptad, - PTA *ptas, - l_uint32 colorval) -{ -l_float32 *vc; -PIX *pixd; - - PROCNAME("pixAffinePtaColor"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!ptas) - return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); - if (!ptad) - return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); - if (ptaGetCount(ptas) != 3) - return (PIX *)ERROR_PTR("ptas count not 3", procName, NULL); - if (ptaGetCount(ptad) != 3) - return (PIX *)ERROR_PTR("ptad count not 3", procName, NULL); - - /* Get backwards transform from dest to src, and apply it */ - getAffineXformCoeffs(ptad, ptas, &vc); - pixd = pixAffineColor(pixs, vc, colorval); - LEPT_FREE(vc); - - return pixd; -} - - -/*! - * \brief pixAffineColor() - * - * \param[in] pixs 32 bpp - * \param[in] vc vector of 6 coefficients for affine transformation - * \param[in] colorval e.g.: 0 to bring in BLACK, 0xffffff00 for WHITE - * \return pixd, or NULL on error - */ -PIX * -pixAffineColor(PIX *pixs, - l_float32 *vc, - l_uint32 colorval) -{ -l_int32 i, j, w, h, d, wpls, wpld; -l_uint32 val; -l_uint32 *datas, *datad, *lined; -l_float32 x, y; -PIX *pix1, *pix2, *pixd; - - PROCNAME("pixAffineColor"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 32) - return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); - if (!vc) - return (PIX *)ERROR_PTR("vc not defined", procName, NULL); - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - pixd = pixCreateTemplate(pixs); - pixSetAllArbitrary(pixd, colorval); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - /* Iterate over destination pixels */ - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - /* Compute float src pixel location corresponding to (i,j) */ - affineXformPt(vc, j, i, &x, &y); - linearInterpolatePixelColor(datas, wpls, w, h, x, y, colorval, - &val); - *(lined + j) = val; - } - } - - /* If rgba, transform the pixs alpha channel and insert in pixd */ - if (pixGetSpp(pixs) == 4) { - pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); - pix2 = pixAffineGray(pix1, vc, 255); /* bring in opaque */ - pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - - return pixd; -} - - -/*! - * \brief pixAffinePtaGray() - * - * \param[in] pixs 8 bpp - * \param[in] ptad 3 pts of final coordinate space - * \param[in] ptas 3 pts of initial coordinate space - * \param[in] grayval e.g.: 0 to bring in BLACK, 255 for WHITE - * \return pixd, or NULL on error - */ -PIX * -pixAffinePtaGray(PIX *pixs, - PTA *ptad, - PTA *ptas, - l_uint8 grayval) -{ -l_float32 *vc; -PIX *pixd; - - PROCNAME("pixAffinePtaGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!ptas) - return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); - if (!ptad) - return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); - if (ptaGetCount(ptas) != 3) - return (PIX *)ERROR_PTR("ptas count not 3", procName, NULL); - if (ptaGetCount(ptad) != 3) - return (PIX *)ERROR_PTR("ptad count not 3", procName, NULL); - - /* Get backwards transform from dest to src, and apply it */ - getAffineXformCoeffs(ptad, ptas, &vc); - pixd = pixAffineGray(pixs, vc, grayval); - LEPT_FREE(vc); - - return pixd; -} - - - -/*! - * \brief pixAffineGray() - * - * \param[in] pixs 8 bpp - * \param[in] vc vector of 6 coefficients for affine transformation - * \param[in] grayval e.g.: 0 to bring in BLACK, 255 for WHITE - * \return pixd, or NULL on error - */ -PIX * -pixAffineGray(PIX *pixs, - l_float32 *vc, - l_uint8 grayval) -{ -l_int32 i, j, w, h, wpls, wpld, val; -l_uint32 *datas, *datad, *lined; -l_float32 x, y; -PIX *pixd; - - PROCNAME("pixAffineGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); - if (!vc) - return (PIX *)ERROR_PTR("vc not defined", procName, NULL); - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - pixd = pixCreateTemplate(pixs); - pixSetAllArbitrary(pixd, grayval); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - /* Iterate over destination pixels */ - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - /* Compute float src pixel location corresponding to (i,j) */ - affineXformPt(vc, j, i, &x, &y); - linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val); - SET_DATA_BYTE(lined, j, val); - } - } - - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Affine transform including alpha (blend) component * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixAffinePtaWithAlpha() - * - * \param[in] pixs 32 bpp rgb - * \param[in] ptad 3 pts of final coordinate space - * \param[in] ptas 3 pts of initial coordinate space - * \param[in] pixg [optional] 8 bpp, can be null - * \param[in] fract between 0.0 and 1.0, with 0.0 fully transparent - * and 1.0 fully opaque - * \param[in] border of pixels added to capture transformed source pixels - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) The alpha channel is transformed separately from pixs, - * and aligns with it, being fully transparent outside the - * boundary of the transformed pixs. For pixels that are fully - * transparent, a blending function like pixBlendWithGrayMask() - * will give zero weight to corresponding pixels in pixs. - * (2) If pixg is NULL, it is generated as an alpha layer that is - * partially opaque, using %fract. Otherwise, it is cropped - * to pixs if required and %fract is ignored. The alpha channel - * in pixs is never used. - * (3) Colormaps are removed. - * (4) When pixs is transformed, it doesn't matter what color is brought - * in because the alpha channel will be transparent (0) there. - * (5) To avoid losing source pixels in the destination, it may be - * necessary to add a border to the source pix before doing - * the affine transformation. This can be any non-negative number. - * (6) The input %ptad and %ptas are in a coordinate space before - * the border is added. Internally, we compensate for this - * before doing the affine transform on the image after the border - * is added. - * (7) The default setting for the border values in the alpha channel - * is 0 (transparent) for the outermost ring of pixels and - * (0.5 * fract * 255) for the second ring. When blended over - * a second image, this - * (a) shrinks the visible image to make a clean overlap edge - * with an image below, and - * (b) softens the edges by weakening the aliasing there. - * Use l_setAlphaMaskBorder() to change these values. - *- */ -PIX * -pixAffinePtaWithAlpha(PIX *pixs, - PTA *ptad, - PTA *ptas, - PIX *pixg, - l_float32 fract, - l_int32 border) -{ -l_int32 ws, hs, d; -PIX *pixd, *pixb1, *pixb2, *pixg2, *pixga; -PTA *ptad2, *ptas2; - - PROCNAME("pixAffinePtaWithAlpha"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &ws, &hs, &d); - if (d != 32 && pixGetColormap(pixs) == NULL) - return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); - if (pixg && pixGetDepth(pixg) != 8) { - L_WARNING("pixg not 8 bpp; using 'fract' transparent alpha\n", - procName); - pixg = NULL; - } - if (!pixg && (fract < 0.0 || fract > 1.0)) { - L_WARNING("invalid fract; using 1.0 (fully transparent)\n", procName); - fract = 1.0; - } - if (!pixg && fract == 0.0) - L_WARNING("fully opaque alpha; image will not be blended\n", procName); - if (!ptad) - return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); - if (!ptas) - return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); - - /* Add border; the color doesn't matter */ - pixb1 = pixAddBorder(pixs, border, 0); - - /* Transform the ptr arrays to work on the bordered image */ - ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); - ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); - - /* Do separate affine transform of rgb channels of pixs and of pixg */ - pixd = pixAffinePtaColor(pixb1, ptad2, ptas2, 0); - if (!pixg) { - pixg2 = pixCreate(ws, hs, 8); - if (fract == 1.0) - pixSetAll(pixg2); - else - pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract)); - } else { - pixg2 = pixResizeToMatch(pixg, NULL, ws, hs); - } - if (ws > 10 && hs > 10) { /* see note 7 */ - pixSetBorderRingVal(pixg2, 1, - (l_int32)(255.0 * fract * AlphaMaskBorderVals[0])); - pixSetBorderRingVal(pixg2, 2, - (l_int32)(255.0 * fract * AlphaMaskBorderVals[1])); - - } - pixb2 = pixAddBorder(pixg2, border, 0); /* must be black border */ - pixga = pixAffinePtaGray(pixb2, ptad2, ptas2, 0); - pixSetRGBComponent(pixd, pixga, L_ALPHA_CHANNEL); - pixSetSpp(pixd, 4); - - pixDestroy(&pixg2); - pixDestroy(&pixb1); - pixDestroy(&pixb2); - pixDestroy(&pixga); - ptaDestroy(&ptad2); - ptaDestroy(&ptas2); - return pixd; -} - - -/*-------------------------------------------------------------* - * Affine coordinate transformation * - *-------------------------------------------------------------*/ -/*! - * \brief getAffineXformCoeffs() - * - * \param[in] ptas source 3 points; unprimed - * \param[in] ptad transformed 3 points; primed - * \param[out] pvc vector of coefficients of transform - * \return 0 if OK; 1 on error - * - *
- * We have a set of six equations, describing the affine - * transformation that takes 3 points ptas into 3 other - * points ptad. These equations are: - * - * x1' = c[0]*x1 + c[1]*y1 + c[2] - * y1' = c[3]*x1 + c[4]*y1 + c[5] - * x2' = c[0]*x2 + c[1]*y2 + c[2] - * y2' = c[3]*x2 + c[4]*y2 + c[5] - * x3' = c[0]*x3 + c[1]*y3 + c[2] - * y3' = c[3]*x3 + c[4]*y3 + c[5] - * - * This can be represented as - * - * AC = B - * - * where B and C are column vectors - * - * B = [ x1' y1' x2' y2' x3' y3' ] - * C = [ c[0] c[1] c[2] c[3] c[4] c[5] c[6] ] - * - * and A is the 6x6 matrix - * - * x1 y1 1 0 0 0 - * 0 0 0 x1 y1 1 - * x2 y2 1 0 0 0 - * 0 0 0 x2 y2 1 - * x3 y3 1 0 0 0 - * 0 0 0 x3 y3 1 - * - * These six equations are solved here for the coefficients C. - * - * These six coefficients can then be used to find the dest - * point x',y') corresponding to any src point (x,y, according - * to the equations - * - * x' = c[0]x + c[1]y + c[2] - * y' = c[3]x + c[4]y + c[5] - * - * that are implemented in affineXformPt. - * - * !!!!!!!!!!!!!!!!!! Very important !!!!!!!!!!!!!!!!!!!!!! - * - * When the affine transform is composed from a set of simple - * operations such as translation, scaling and rotation, - * it is built in a form to convert from the un-transformed src - * point to the transformed dest point. However, when an - * affine transform is used on images, it is used in an inverted - * way: it converts from the transformed dest point to the - * un-transformed src point. So, for example, if you transform - * a boxa using transform A, to transform an image in the same - * way you must use the inverse of A. - * - * For example, if you transform a boxa with a 3x3 affine matrix - * 'mat', the analogous image transformation must use 'matinv': - * \code - * boxad = boxaAffineTransform(boxas, mat); - * affineInvertXform(mat, &matinv); - * pixd = pixAffine(pixs, matinv, L_BRING_IN_WHITE); - * \endcode - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - *- */ -l_ok -getAffineXformCoeffs(PTA *ptas, - PTA *ptad, - l_float32 **pvc) -{ -l_int32 i; -l_float32 x1, y1, x2, y2, x3, y3; -l_float32 *b; /* rhs vector of primed coords X'; coeffs returned in *pvc */ -l_float32 *a[6]; /* 6x6 matrix A */ - - PROCNAME("getAffineXformCoeffs"); - - if (!ptas) - return ERROR_INT("ptas not defined", procName, 1); - if (!ptad) - return ERROR_INT("ptad not defined", procName, 1); - if (!pvc) - return ERROR_INT("&vc not defined", procName, 1); - - if ((b = (l_float32 *)LEPT_CALLOC(6, sizeof(l_float32))) == NULL) - return ERROR_INT("b not made", procName, 1); - *pvc = b; - - ptaGetPt(ptas, 0, &x1, &y1); - ptaGetPt(ptas, 1, &x2, &y2); - ptaGetPt(ptas, 2, &x3, &y3); - ptaGetPt(ptad, 0, &b[0], &b[1]); - ptaGetPt(ptad, 1, &b[2], &b[3]); - ptaGetPt(ptad, 2, &b[4], &b[5]); - - for (i = 0; i < 6; i++) - if ((a[i] = (l_float32 *)LEPT_CALLOC(6, sizeof(l_float32))) == NULL) - return ERROR_INT("a[i] not made", procName, 1); - - a[0][0] = x1; - a[0][1] = y1; - a[0][2] = 1.; - a[1][3] = x1; - a[1][4] = y1; - a[1][5] = 1.; - a[2][0] = x2; - a[2][1] = y2; - a[2][2] = 1.; - a[3][3] = x2; - a[3][4] = y2; - a[3][5] = 1.; - a[4][0] = x3; - a[4][1] = y3; - a[4][2] = 1.; - a[5][3] = x3; - a[5][4] = y3; - a[5][5] = 1.; - - gaussjordan(a, b, 6); - - for (i = 0; i < 6; i++) - LEPT_FREE(a[i]); - - return 0; -} - - -/*! - * \brief affineInvertXform() - * - * \param[in] vc vector of 6 coefficients - * \param[out] pvci inverted transform - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) The 6 affine transform coefficients are the first - * two rows of a 3x3 matrix where the last row has - * only a 1 in the third column. We invert this - * using gaussjordan(), and select the first 2 rows - * as the coefficients of the inverse affine transform. - * (2) Alternatively, we can find the inverse transform - * coefficients by inverting the 2x2 submatrix, - * and treating the top 2 coefficients in the 3rd column as - * a RHS vector for that 2x2 submatrix. Then the - * 6 inverted transform coefficients are composed of - * the inverted 2x2 submatrix and the negative of the - * transformed RHS vector. Why is this so? We have - * Y = AX + R (2 equations in 6 unknowns) - * Then - * X = A'Y - A'R - * Gauss-jordan solves - * AF = R - * and puts the solution for F, which is A'R, - * into the input R vector. - * - *- */ -l_ok -affineInvertXform(l_float32 *vc, - l_float32 **pvci) -{ -l_int32 i; -l_float32 *vci; -l_float32 *a[3]; -l_float32 b[3] = {1.0, 1.0, 1.0}; /* anything; results ignored */ - - PROCNAME("affineInvertXform"); - - if (!pvci) - return ERROR_INT("&vci not defined", procName, 1); - *pvci = NULL; - if (!vc) - return ERROR_INT("vc not defined", procName, 1); - -#if 1 - for (i = 0; i < 3; i++) - a[i] = (l_float32 *)LEPT_CALLOC(3, sizeof(l_float32)); - a[0][0] = vc[0]; - a[0][1] = vc[1]; - a[0][2] = vc[2]; - a[1][0] = vc[3]; - a[1][1] = vc[4]; - a[1][2] = vc[5]; - a[2][2] = 1.0; - gaussjordan(a, b, 3); /* this inverts matrix a */ - vci = (l_float32 *)LEPT_CALLOC(6, sizeof(l_float32)); - *pvci = vci; - vci[0] = a[0][0]; - vci[1] = a[0][1]; - vci[2] = a[0][2]; - vci[3] = a[1][0]; - vci[4] = a[1][1]; - vci[5] = a[1][2]; - for (i = 0; i < 3; i++) - LEPT_FREE(a[i]); - -#else - - /* Alternative version, inverting a 2x2 matrix */ - { l_float32 *a2[2]; - for (i = 0; i < 2; i++) - a2[i] = (l_float32 *)LEPT_CALLOC(2, sizeof(l_float32)); - a2[0][0] = vc[0]; - a2[0][1] = vc[1]; - a2[1][0] = vc[3]; - a2[1][1] = vc[4]; - b[0] = vc[2]; - b[1] = vc[5]; - gaussjordan(a2, b, 2); /* this inverts matrix a2 */ - vci = (l_float32 *)LEPT_CALLOC(6, sizeof(l_float32)); - *pvci = vci; - vci[0] = a2[0][0]; - vci[1] = a2[0][1]; - vci[2] = -b[0]; /* note sign */ - vci[3] = a2[1][0]; - vci[4] = a2[1][1]; - vci[5] = -b[1]; /* note sign */ - for (i = 0; i < 2; i++) - LEPT_FREE(a2[i]); - } -#endif - - return 0; -} - - -/*! - * \brief affineXformSampledPt() - * - * \param[in] vc vector of 6 coefficients - * \param[in] x, y initial point - * \param[out] pxp, pyp transformed point - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This finds the nearest pixel coordinates of the transformed point. - * (2) It does not check ptrs for returned data! - *- */ -l_ok -affineXformSampledPt(l_float32 *vc, - l_int32 x, - l_int32 y, - l_int32 *pxp, - l_int32 *pyp) -{ - PROCNAME("affineXformSampledPt"); - - if (!vc) - return ERROR_INT("vc not defined", procName, 1); - - *pxp = (l_int32)(vc[0] * x + vc[1] * y + vc[2] + 0.5); - *pyp = (l_int32)(vc[3] * x + vc[4] * y + vc[5] + 0.5); - return 0; -} - - -/*! - * \brief affineXformPt() - * - * \param[in] vc vector of 6 coefficients - * \param[in] x, y initial point - * \param[out] pxp, pyp transformed point - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This computes the floating point location of the transformed point. - * (2) It does not check ptrs for returned data! - *- */ -l_ok -affineXformPt(l_float32 *vc, - l_int32 x, - l_int32 y, - l_float32 *pxp, - l_float32 *pyp) -{ - PROCNAME("affineXformPt"); - - if (!vc) - return ERROR_INT("vc not defined", procName, 1); - - *pxp = vc[0] * x + vc[1] * y + vc[2]; - *pyp = vc[3] * x + vc[4] * y + vc[5]; - return 0; -} - - -/*-------------------------------------------------------------* - * Interpolation helper functions * - *-------------------------------------------------------------*/ -/*! - * \brief linearInterpolatePixelColor() - * - * \param[in] datas ptr to beginning of image data - * \param[in] wpls 32-bit word/line for this data array - * \param[in] w, h of image - * \param[in] x, y floating pt location for evaluation - * \param[in] colorval color brought in from the outside when the - * input x,y location is outside the image; - * in 0xrrggbb00 format) - * \param[out] pval interpolated color value - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is a standard linear interpolation function. It is - * equivalent to area weighting on each component, and - * avoids "jaggies" when rendering sharp edges. - *- */ -l_ok -linearInterpolatePixelColor(l_uint32 *datas, - l_int32 wpls, - l_int32 w, - l_int32 h, - l_float32 x, - l_float32 y, - l_uint32 colorval, - l_uint32 *pval) -{ -l_int32 valid, xpm, ypm, xp, xp2, yp, xf, yf; -l_int32 rval, gval, bval; -l_uint32 word00, word01, word10, word11; -l_uint32 *lines; - - PROCNAME("linearInterpolatePixelColor"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = colorval; - if (!datas) - return ERROR_INT("datas not defined", procName, 1); - - /* Skip if x or y are invalid. (x,y) must be in the source image. - * Failure to detect an invalid point will cause a mem address fault. - * Occasionally, x or y will be a nan, and relational checks always - * fail for nans. Therefore we check if the point is inside the pix */ - valid = (x >= 0.0 && y >= 0.0 && x < w && y < h); - if (!valid) return 0; - - xpm = (l_int32)(16.0 * x); - ypm = (l_int32)(16.0 * y); - xp = xpm >> 4; - xp2 = xp + 1 < w ? xp + 1 : xp; - yp = ypm >> 4; - if (yp + 1 >= h) wpls = 0; - xf = xpm & 0x0f; - yf = ypm & 0x0f; - -#if DEBUG - if (xf < 0 || yf < 0) - lept_stderr("xp = %d, yp = %d, xf = %d, yf = %d\n", xp, yp, xf, yf); -#endif /* DEBUG */ - - /* Do area weighting (eqiv. to linear interpolation) */ - lines = datas + yp * wpls; - word00 = *(lines + xp); - word10 = *(lines + xp2); - word01 = *(lines + wpls + xp); - word11 = *(lines + wpls + xp2); - rval = ((16 - xf) * (16 - yf) * ((word00 >> L_RED_SHIFT) & 0xff) + - xf * (16 - yf) * ((word10 >> L_RED_SHIFT) & 0xff) + - (16 - xf) * yf * ((word01 >> L_RED_SHIFT) & 0xff) + - xf * yf * ((word11 >> L_RED_SHIFT) & 0xff)) / 256; - gval = ((16 - xf) * (16 - yf) * ((word00 >> L_GREEN_SHIFT) & 0xff) + - xf * (16 - yf) * ((word10 >> L_GREEN_SHIFT) & 0xff) + - (16 - xf) * yf * ((word01 >> L_GREEN_SHIFT) & 0xff) + - xf * yf * ((word11 >> L_GREEN_SHIFT) & 0xff)) / 256; - bval = ((16 - xf) * (16 - yf) * ((word00 >> L_BLUE_SHIFT) & 0xff) + - xf * (16 - yf) * ((word10 >> L_BLUE_SHIFT) & 0xff) + - (16 - xf) * yf * ((word01 >> L_BLUE_SHIFT) & 0xff) + - xf * yf * ((word11 >> L_BLUE_SHIFT) & 0xff)) / 256; - composeRGBPixel(rval, gval, bval, pval); - return 0; -} - - -/*! - * \brief linearInterpolatePixelGray() - * - * \param[in] datas ptr to beginning of image data - * \param[in] wpls 32-bit word/line for this data array - * \param[in] w, h of image - * \param[in] x, y floating pt location for evaluation - * \param[in] grayval color brought in from the outside when the - * input x,y location is outside the image - * \param[out] pval interpolated gray value - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is a standard linear interpolation function. It is - * equivalent to area weighting on each component, and - * avoids "jaggies" when rendering sharp edges. - *- */ -l_ok -linearInterpolatePixelGray(l_uint32 *datas, - l_int32 wpls, - l_int32 w, - l_int32 h, - l_float32 x, - l_float32 y, - l_int32 grayval, - l_int32 *pval) -{ -l_int32 valid, xpm, ypm, xp, xp2, yp, xf, yf, v00, v10, v01, v11; -l_uint32 *lines; - - PROCNAME("linearInterpolatePixelGray"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = grayval; - if (!datas) - return ERROR_INT("datas not defined", procName, 1); - - /* Skip if x or y is invalid. (x,y) must be in the source image. - * Failure to detect an invalid point will cause a mem address fault. - * Occasionally, x or y will be a nan, and relational checks always - * fail for nans. Therefore we check if the point is inside the pix */ - valid = (x >= 0.0 && y >= 0.0 && x < w && y < h); - if (!valid) return 0; - - xpm = (l_int32)(16.0 * x); - ypm = (l_int32)(16.0 * y); - xp = xpm >> 4; - xp2 = xp + 1 < w ? xp + 1 : xp; - yp = ypm >> 4; - if (yp + 1 >= h) wpls = 0; - xf = xpm & 0x0f; - yf = ypm & 0x0f; - -#if DEBUG - if (xf < 0 || yf < 0) - lept_stderr("xp = %d, yp = %d, xf = %d, yf = %d\n", xp, yp, xf, yf); -#endif /* DEBUG */ - - /* Interpolate by area weighting. */ - lines = datas + yp * wpls; - v00 = (16 - xf) * (16 - yf) * GET_DATA_BYTE(lines, xp); - v10 = xf * (16 - yf) * GET_DATA_BYTE(lines, xp2); - v01 = (16 - xf) * yf * GET_DATA_BYTE(lines + wpls, xp); - v11 = xf * yf * GET_DATA_BYTE(lines + wpls, xp2); - *pval = (v00 + v01 + v10 + v11) / 256; - return 0; -} - - - -/*-------------------------------------------------------------* - * Gauss-jordan linear equation solver * - *-------------------------------------------------------------*/ -#define SWAP(a,b) {temp = (a); (a) = (b); (b) = temp;} - -/*! - * \brief gaussjordan() - * - * \param[in] a n x n matrix - * \param[in] b n x 1 right-hand side column vector - * \param[in] n dimension - * \return 0 if ok, 1 on error - * - *
- * Notes: - * (1) There are two side-effects: - * * The matrix a is transformed to its inverse A - * * The rhs vector b is transformed to the solution x - * of the linear equation ax = b - * (2) The inverse A can then be used to solve the same equation with - * different rhs vectors c by multiplication: x = Ac - * (3) Adapted from "Numerical Recipes in C, Second Edition", 1992, - * pp. 36-41 (gauss-jordan elimination) - *- */ -l_int32 -gaussjordan(l_float32 **a, - l_float32 *b, - l_int32 n) -{ -l_int32 i, icol, irow, j, k, col, row, success; -l_int32 *indexc, *indexr, *ipiv; -l_float32 maxval, val, pivinv, temp; - - PROCNAME("gaussjordan"); - - if (!a) - return ERROR_INT("a not defined", procName, 1); - if (!b) - return ERROR_INT("b not defined", procName, 1); - - success = TRUE; - indexc = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32)); - indexr = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32)); - ipiv = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32)); - if (!indexc || !indexr || !ipiv) { - L_ERROR("array not made\n", procName); - success = FALSE; - goto cleanup_arrays; - } - - icol = irow = 0; /* silence static checker */ - for (i = 0; i < n; i++) { - maxval = 0.0; - for (j = 0; j < n; j++) { - if (ipiv[j] != 1) { - for (k = 0; k < n; k++) { - if (ipiv[k] == 0) { - if (fabs(a[j][k]) >= maxval) { - maxval = fabs(a[j][k]); - irow = j; - icol = k; - } - } else if (ipiv[k] > 1) { - L_ERROR("singular matrix\n", procName); - success = FALSE; - goto cleanup_arrays; - } - } - } - } - ++(ipiv[icol]); - - if (irow != icol) { - for (col = 0; col < n; col++) - SWAP(a[irow][col], a[icol][col]); - SWAP(b[irow], b[icol]); - } - - indexr[i] = irow; - indexc[i] = icol; - if (a[icol][icol] == 0.0) { - L_ERROR("singular matrix\n", procName); - success = FALSE; - goto cleanup_arrays; - } - pivinv = 1.0 / a[icol][icol]; - a[icol][icol] = 1.0; - for (col = 0; col < n; col++) - a[icol][col] *= pivinv; - b[icol] *= pivinv; - - for (row = 0; row < n; row++) { - if (row != icol) { - val = a[row][icol]; - a[row][icol] = 0.0; - for (col = 0; col < n; col++) - a[row][col] -= a[icol][col] * val; - b[row] -= b[icol] * val; - } - } - } - - for (col = n - 1; col >= 0; col--) { - if (indexr[col] != indexc[col]) { - for (k = 0; k < n; k++) - SWAP(a[k][indexr[col]], a[k][indexc[col]]); - } - } - -cleanup_arrays: - LEPT_FREE(indexr); - LEPT_FREE(indexc); - LEPT_FREE(ipiv); - return (success) ? 0 : 1; -} - - -/*-------------------------------------------------------------* - * Sequential affine image transformation * - *-------------------------------------------------------------*/ -/*! - * \brief pixAffineSequential() - * - * \param[in] pixs - * \param[in] ptad 3 pts of final coordinate space - * \param[in] ptas 3 pts of initial coordinate space - * \param[in] bw pixels of additional border width during computation - * \param[in] bh pixels of additional border height during computation - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) The 3 pts must not be collinear. - * (2) The 3 pts must be given in this order: - * ~ origin - * ~ a location along the x-axis - * ~ a location along the y-axis. - * (3) You must guess how much border must be added so that no - * pixels are lost in the transformations from src to - * dest coordinate space. (This can be calculated but it - * is a lot of work!) For coordinate spaces that are nearly - * at right angles, on a 300 ppi scanned page, the addition - * of 1000 pixels on each side is usually sufficient. - * (4) This is here for pedagogical reasons. It is about 3x faster - * on 1 bpp images than pixAffineSampled(), but the results - * on text are much inferior. - *- */ -PIX * -pixAffineSequential(PIX *pixs, - PTA *ptad, - PTA *ptas, - l_int32 bw, - l_int32 bh) -{ -l_int32 x1, y1, x2, y2, x3, y3; /* ptas */ -l_int32 x1p, y1p, x2p, y2p, x3p, y3p; /* ptad */ -l_int32 x1sc, y1sc; /* scaled origin */ -l_float32 x2s, x2sp, scalex, scaley; -l_float32 th3, th3p, ph2, ph2p; -#if DEBUG -l_float32 rad2deg; -#endif /* DEBUG */ -PIX *pix1, *pix2, *pixd; - - PROCNAME("pixAffineSequential"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!ptas) - return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); - if (!ptad) - return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); - - if (ptaGetCount(ptas) != 3) - return (PIX *)ERROR_PTR("ptas count not 3", procName, NULL); - if (ptaGetCount(ptad) != 3) - return (PIX *)ERROR_PTR("ptad count not 3", procName, NULL); - ptaGetIPt(ptas, 0, &x1, &y1); - ptaGetIPt(ptas, 1, &x2, &y2); - ptaGetIPt(ptas, 2, &x3, &y3); - ptaGetIPt(ptad, 0, &x1p, &y1p); - ptaGetIPt(ptad, 1, &x2p, &y2p); - ptaGetIPt(ptad, 2, &x3p, &y3p); - - pix1 = pix2 = pixd = NULL; - - if (y1 == y3) - return (PIX *)ERROR_PTR("y1 == y3!", procName, NULL); - if (y1p == y3p) - return (PIX *)ERROR_PTR("y1p == y3p!", procName, NULL); - - if (bw != 0 || bh != 0) { - /* resize all points and add border to pixs */ - x1 = x1 + bw; - y1 = y1 + bh; - x2 = x2 + bw; - y2 = y2 + bh; - x3 = x3 + bw; - y3 = y3 + bh; - x1p = x1p + bw; - y1p = y1p + bh; - x2p = x2p + bw; - y2p = y2p + bh; - x3p = x3p + bw; - y3p = y3p + bh; - - if ((pix1 = pixAddBorderGeneral(pixs, bw, bw, bh, bh, 0)) == NULL) - return (PIX *)ERROR_PTR("pix1 not made", procName, NULL); - } else { - pix1 = pixCopy(NULL, pixs); - } - - /*-------------------------------------------------------------* - The horizontal shear is done to move the 3rd point to the - y axis. This moves the 2nd point either towards or away - from the y axis, depending on whether it is above or below - the x axis. That motion must be computed so that we know - the angle of vertical shear to use to get the 2nd point - on the x axis. We must also know the x coordinate of the - 2nd point in order to compute how much scaling is required - to match points on the axis. - *-------------------------------------------------------------*/ - - /* Shear angles required to put src points on x and y axes */ - th3 = atan2((l_float64)(x1 - x3), (l_float64)(y1 - y3)); - x2s = (l_float32)(x2 - ((l_float32)(y1 - y2) * (x3 - x1)) / (y1 - y3)); - if (x2s == (l_float32)x1) { - L_ERROR("x2s == x1!\n", procName); - goto cleanup_pix; - } - ph2 = atan2((l_float64)(y1 - y2), (l_float64)(x2s - x1)); - - /* Shear angles required to put dest points on x and y axes. - * Use the negative of these values to instead move the - * src points from the axes to the actual dest position. - * These values are also needed to scale the image. */ - th3p = atan2((l_float64)(x1p - x3p), (l_float64)(y1p - y3p)); - x2sp = (l_float32)(x2p - - ((l_float32)(y1p - y2p) * (x3p - x1p)) / (y1p - y3p)); - if (x2sp == (l_float32)x1p) { - L_ERROR("x2sp == x1p!\n", procName); - goto cleanup_pix; - } - ph2p = atan2((l_float64)(y1p - y2p), (l_float64)(x2sp - x1p)); - - /* Shear image to first put src point 3 on the y axis, - * and then to put src point 2 on the x axis */ - pixHShearIP(pix1, y1, th3, L_BRING_IN_WHITE); - pixVShearIP(pix1, x1, ph2, L_BRING_IN_WHITE); - - /* Scale image to match dest scale. The dest scale - * is calculated above from the angles th3p and ph2p - * that would be required to move the dest points to - * the x and y axes. */ - scalex = (l_float32)(x2sp - x1p) / (x2s - x1); - scaley = (l_float32)(y3p - y1p) / (y3 - y1); - if ((pix2 = pixScale(pix1, scalex, scaley)) == NULL) { - L_ERROR("pix2 not made\n", procName); - goto cleanup_pix; - } - -#if DEBUG - rad2deg = 180. / 3.1415926535; - lept_stderr("th3 = %5.1f deg, ph2 = %5.1f deg\n", - rad2deg * th3, rad2deg * ph2); - lept_stderr("th3' = %5.1f deg, ph2' = %5.1f deg\n", - rad2deg * th3p, rad2deg * ph2p); - lept_stderr("scalex = %6.3f, scaley = %6.3f\n", scalex, scaley); -#endif /* DEBUG */ - - /*-------------------------------------------------------------* - Scaling moves the 1st src point, which is the origin. - It must now be moved again to coincide with the origin - (1st point) of the dest. After this is done, the 2nd - and 3rd points must be sheared back to the original - positions of the 2nd and 3rd dest points. We use the - negative of the angles that were previously computed - for shearing those points in the dest image to x and y - axes, and take the shears in reverse order as well. - *-------------------------------------------------------------*/ - /* Shift image to match dest origin. */ - x1sc = (l_int32)(scalex * x1 + 0.5); /* x comp of origin after scaling */ - y1sc = (l_int32)(scaley * y1 + 0.5); /* y comp of origin after scaling */ - pixRasteropIP(pix2, x1p - x1sc, y1p - y1sc, L_BRING_IN_WHITE); - - /* Shear image to take points 2 and 3 off the axis and - * put them in the original dest position */ - pixVShearIP(pix2, x1p, -ph2p, L_BRING_IN_WHITE); - pixHShearIP(pix2, y1p, -th3p, L_BRING_IN_WHITE); - - if (bw != 0 || bh != 0) { - if ((pixd = pixRemoveBorderGeneral(pix2, bw, bw, bh, bh)) == NULL) - L_ERROR("pixd not made\n", procName); - } else { - pixd = pixClone(pix2); - } - -cleanup_pix: - pixDestroy(&pix1); - pixDestroy(&pix2); - return pixd; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/affinecompose.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/affinecompose.c deleted file mode 100644 index 8f4805b1..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/affinecompose.c +++ /dev/null @@ -1,665 +0,0 @@ -/*====================================================================* - - 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 affinecompose.c - *
- * - * Composable coordinate transforms - * l_float32 *createMatrix2dTranslate() - * l_float32 *createMatrix2dScale() - * l_float32 *createMatrix2dRotate() - * - * Special coordinate transforms on pta - * PTA *ptaTranslate() - * PTA *ptaScale() - * PTA *ptaRotate() - * - * Special coordinate transforms on boxa - * BOXA *boxaTranslate() - * BOXA *boxaScale() - * BOXA *boxaRotate() - * - * General coordinate transform on pta and boxa - * PTA *ptaAffineTransform() - * BOXA *boxaAffineTransform() - * - * Matrix operations - * l_int32 l_productMatVec() - * l_int32 l_productMat2() - * l_int32 l_productMat3() - * l_int32 l_productMat4() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) The translation is equivalent to: - * v' = Av - * where v and v' are 1x3 column vectors in the form - * v = [x, y, 1]^ ^ denotes transpose - * and the affine translation matrix is - * A = [ 1 0 tx - * 0 1 ty - * 0 0 1 ] - * - * (2) We consider translation as with respect to a fixed origin. - * In a clipping operation, the origin moves and the points - * are fixed, and you use (-tx, -ty) where (tx, ty) is the - * translation vector of the origin. - *- */ -l_float32 * -createMatrix2dTranslate(l_float32 transx, - l_float32 transy) -{ -l_float32 *mat; - - mat = (l_float32 *)LEPT_CALLOC(9, sizeof(l_float32)); - mat[0] = mat[4] = mat[8] = 1; - mat[2] = transx; - mat[5] = transy; - return mat; -} - - -/*! - * \brief createMatrix2dScale() - * - * \param[in] scalex horizontal scale factor - * \param[in] scaley vertical scale factor - * \return 3x3 transform matrix, or NULL on error - * - *
- * Notes: - * (1) The scaling is equivalent to: - * v' = Av - * where v and v' are 1x3 column vectors in the form - * v = [x, y, 1]^ ^ denotes transpose - * and the affine scaling matrix is - * A = [ sx 0 0 - * 0 sy 0 - * 0 0 1 ] - * - * (2) We consider scaling as with respect to a fixed origin. - * In other words, the origin is the only point that doesn't - * move in the scaling transform. - *- */ -l_float32 * -createMatrix2dScale(l_float32 scalex, - l_float32 scaley) -{ -l_float32 *mat; - - mat = (l_float32 *)LEPT_CALLOC(9, sizeof(l_float32)); - mat[0] = scalex; - mat[4] = scaley; - mat[8] = 1; - return mat; -} - - -/*! - * \brief createMatrix2dRotate() - * - * \param[in] xc, yc location of center of rotation - * \param[in] angle rotation in radians; clockwise is positive - * \return 3x3 transform matrix, or NULL on error - * - *
- * Notes: - * (1) The rotation is equivalent to: - * v' = Av - * where v and v' are 1x3 column vectors in the form - * v = [x, y, 1]^ ^ denotes transpose - * and the affine rotation matrix is - * A = [ cosa -sina xc*1-cosa + yc*sina - * sina cosa yc*1-cosa - xc*sina - * 0 0 1 ] - * - * If the rotation is about the origin, xc, yc) = (0, 0 and - * this simplifies to - * A = [ cosa -sina 0 - * sina cosa 0 - * 0 0 1 ] - * - * These relations follow from the following equations, which - * you can convince yourself are correct as follows. Draw a - * circle centered on xc,yc) and passing through (x,y), with - * (x',y') on the arc at an angle 'a' clockwise from (x,y). - * [ Hint: cosa + b = cosa * cosb - sina * sinb - * sina + b = sina * cosb + cosa * sinb ] - * - * x' - xc = x - xc) * cosa - (y - yc * sina - * y' - yc = x - xc) * sina + (y - yc * cosa - *- */ -l_float32 * -createMatrix2dRotate(l_float32 xc, - l_float32 yc, - l_float32 angle) -{ -l_float32 sina, cosa; -l_float32 *mat; - - mat = (l_float32 *)LEPT_CALLOC(9, sizeof(l_float32)); - sina = sin(angle); - cosa = cos(angle); - mat[0] = mat[4] = cosa; - mat[1] = -sina; - mat[2] = xc * (1.0 - cosa) + yc * sina; - mat[3] = sina; - mat[5] = yc * (1.0 - cosa) - xc * sina; - mat[8] = 1; - return mat; -} - - - -/*-------------------------------------------------------------* - * Special coordinate transforms on pta * - *-------------------------------------------------------------*/ -/*! - * \brief ptaTranslate() - * - * \param[in] ptas for initial points - * \param[in] transx x component of translation wrt. the origin - * \param[in] transy y component of translation wrt. the origin - * \return ptad translated points, or NULL on error - * - *
- * Notes: - * (1) See createMatrix2dTranslate() for details of transform. - *- */ -PTA * -ptaTranslate(PTA *ptas, - l_float32 transx, - l_float32 transy) -{ -l_int32 i, npts; -l_float32 x, y; -PTA *ptad; - - PROCNAME("ptaTranslate"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - - npts = ptaGetCount(ptas); - if ((ptad = ptaCreate(npts)) == NULL) - return (PTA *)ERROR_PTR("ptad not made", procName, NULL); - for (i = 0; i < npts; i++) { - ptaGetPt(ptas, i, &x, &y); - ptaAddPt(ptad, x + transx, y + transy); - } - - return ptad; -} - - -/*! - * \brief ptaScale() - * - * \param[in] ptas for initial points - * \param[in] scalex horizontal scale factor - * \param[in] scaley vertical scale factor - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) See createMatrix2dScale() for details of transform. - *- */ -PTA * -ptaScale(PTA *ptas, - l_float32 scalex, - l_float32 scaley) -{ -l_int32 i, npts; -l_float32 x, y; -PTA *ptad; - - PROCNAME("ptaScale"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - - npts = ptaGetCount(ptas); - if ((ptad = ptaCreate(npts)) == NULL) - return (PTA *)ERROR_PTR("ptad not made", procName, NULL); - for (i = 0; i < npts; i++) { - ptaGetPt(ptas, i, &x, &y); - ptaAddPt(ptad, scalex * x, scaley * y); - } - - return ptad; -} - - -/*! - * \brief ptaRotate() - * - * \param[in] ptas for initial points - * \param[in] xc, yc location of center of rotation - * \param[in] angle rotation in radians; clockwise is positive - * \return 0 if OK; 1 on error - * - *
- * Notes; - * (1) See createMatrix2dScale() for details of transform. - * (2) This transform can be thought of as composed of the - * sum of two parts: - * a) an (x,y)-dependent rotation about the origin: - * xr = x * cosa - y * sina - * yr = x * sina + y * cosa - * b) an (x,y)-independent translation that depends on the - * rotation center and the angle: - * xt = xc - xc * cosa + yc * sina - * yt = yc - xc * sina - yc * cosa - * The translation part (xt,yt) is equal to the difference - * between the center (xc,yc) and the location of the - * center after it is rotated about the origin. - *- */ -PTA * -ptaRotate(PTA *ptas, - l_float32 xc, - l_float32 yc, - l_float32 angle) -{ -l_int32 i, npts; -l_float32 x, y, xp, yp, sina, cosa; -PTA *ptad; - - PROCNAME("ptaRotate"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - - npts = ptaGetCount(ptas); - if ((ptad = ptaCreate(npts)) == NULL) - return (PTA *)ERROR_PTR("ptad not made", procName, NULL); - sina = sin(angle); - cosa = cos(angle); - for (i = 0; i < npts; i++) { - ptaGetPt(ptas, i, &x, &y); - xp = xc + (x - xc) * cosa - (y - yc) * sina; - yp = yc + (x - xc) * sina + (y - yc) * cosa; - ptaAddPt(ptad, xp, yp); - } - - return ptad; -} - - -/*-------------------------------------------------------------* - * Special coordinate transforms on boxa * - *-------------------------------------------------------------*/ -/*! - * \brief boxaTranslate() - * - * \param[in] boxas - * \param[in] transx x component of translation wrt. the origin - * \param[in] transy y component of translation wrt. the origin - * \return boxad translated boxas, or NULL on error - * - * Notes: - * (1) See createMatrix2dTranslate() for details of transform. - */ -BOXA * -boxaTranslate(BOXA *boxas, - l_float32 transx, - l_float32 transy) -{ -PTA *ptas, *ptad; -BOXA *boxad; - - PROCNAME("boxaTranslate"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - - ptas = boxaConvertToPta(boxas, 4); - ptad = ptaTranslate(ptas, transx, transy); - boxad = ptaConvertToBoxa(ptad, 4); - ptaDestroy(&ptas); - ptaDestroy(&ptad); - return boxad; -} - - -/*! - * \brief boxaScale() - * - * \param[in] boxas - * \param[in] scalex horizontal scale factor - * \param[in] scaley vertical scale factor - * \return boxad scaled boxas, or NULL on error - * - * Notes: - * (1) See createMatrix2dScale() for details of transform. - */ -BOXA * -boxaScale(BOXA *boxas, - l_float32 scalex, - l_float32 scaley) -{ -PTA *ptas, *ptad; -BOXA *boxad; - - PROCNAME("boxaScale"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - - ptas = boxaConvertToPta(boxas, 4); - ptad = ptaScale(ptas, scalex, scaley); - boxad = ptaConvertToBoxa(ptad, 4); - ptaDestroy(&ptas); - ptaDestroy(&ptad); - return boxad; -} - - -/*! - * \brief boxaRotate() - * - * \param[in] boxas - * \param[in] xc, yc location of center of rotation - * \param[in] angle rotation in radians; clockwise is positive - * \return boxad scaled boxas, or NULL on error - * - * Notes: - * (1) See createMatrix2dRotate() for details of transform. - */ -BOXA * -boxaRotate(BOXA *boxas, - l_float32 xc, - l_float32 yc, - l_float32 angle) -{ -PTA *ptas, *ptad; -BOXA *boxad; - - PROCNAME("boxaRotate"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - - ptas = boxaConvertToPta(boxas, 4); - ptad = ptaRotate(ptas, xc, yc, angle); - boxad = ptaConvertToBoxa(ptad, 4); - ptaDestroy(&ptas); - ptaDestroy(&ptad); - return boxad; -} - - -/*-------------------------------------------------------------* - * General affine coordinate transform * - *-------------------------------------------------------------*/ -/*! - * \brief ptaAffineTransform() - * - * \param[in] ptas for initial points - * \param[in] mat 3x3 transform matrix; canonical form - * \return ptad transformed points, or NULL on error - */ -PTA * -ptaAffineTransform(PTA *ptas, - l_float32 *mat) -{ -l_int32 i, npts; -l_float32 vecs[3], vecd[3]; -PTA *ptad; - - PROCNAME("ptaAffineTransform"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - if (!mat) - return (PTA *)ERROR_PTR("transform not defined", procName, NULL); - - vecs[2] = 1; - npts = ptaGetCount(ptas); - if ((ptad = ptaCreate(npts)) == NULL) - return (PTA *)ERROR_PTR("ptad not made", procName, NULL); - for (i = 0; i < npts; i++) { - ptaGetPt(ptas, i, &vecs[0], &vecs[1]); - l_productMatVec(mat, vecs, vecd, 3); - ptaAddPt(ptad, vecd[0], vecd[1]); - } - - return ptad; -} - - -/*! - * \brief boxaAffineTransform() - * - * \param[in] boxas - * \param[in] mat 3x3 transform matrix; canonical form - * \return boxad transformed boxas, or NULL on error - */ -BOXA * -boxaAffineTransform(BOXA *boxas, - l_float32 *mat) -{ -PTA *ptas, *ptad; -BOXA *boxad; - - PROCNAME("boxaAffineTransform"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (!mat) - return (BOXA *)ERROR_PTR("transform not defined", procName, NULL); - - ptas = boxaConvertToPta(boxas, 4); - ptad = ptaAffineTransform(ptas, mat); - boxad = ptaConvertToBoxa(ptad, 4); - ptaDestroy(&ptas); - ptaDestroy(&ptad); - return boxad; -} - - -/*-------------------------------------------------------------* - * Matrix operations * - *-------------------------------------------------------------*/ -/*! - * \brief l_productMatVec() - * - * \param[in] mat square matrix, as a 1-dimensional %size^2 array - * \param[in] vecs input column vector of length %size - * \param[in] vecd result column vector - * \param[in] size matrix is %size x %size; vectors are length %size - * \return 0 if OK, 1 on error - */ -l_ok -l_productMatVec(l_float32 *mat, - l_float32 *vecs, - l_float32 *vecd, - l_int32 size) -{ -l_int32 i, j; - - PROCNAME("l_productMatVec"); - - if (!mat) - return ERROR_INT("matrix not defined", procName, 1); - if (!vecs) - return ERROR_INT("input vector not defined", procName, 1); - if (!vecd) - return ERROR_INT("result vector not defined", procName, 1); - - for (i = 0; i < size; i++) { - vecd[i] = 0; - for (j = 0; j < size; j++) { - vecd[i] += mat[size * i + j] * vecs[j]; - } - } - return 0; -} - - -/*! - * \brief l_productMat2() - * - * \param[in] mat1 square matrix, as a 1-dimensional size^2 array - * \param[in] mat2 square matrix, as a 1-dimensional size^2 array - * \param[in] matd square matrix; product stored here - * \param[in] size of matrices - * \return 0 if OK, 1 on error - */ -l_ok -l_productMat2(l_float32 *mat1, - l_float32 *mat2, - l_float32 *matd, - l_int32 size) -{ -l_int32 i, j, k, index; - - PROCNAME("l_productMat2"); - - if (!mat1) - return ERROR_INT("matrix 1 not defined", procName, 1); - if (!mat2) - return ERROR_INT("matrix 2 not defined", procName, 1); - if (!matd) - return ERROR_INT("result matrix not defined", procName, 1); - - for (i = 0; i < size; i++) { - for (j = 0; j < size; j++) { - index = size * i + j; - matd[index] = 0; - for (k = 0; k < size; k++) - matd[index] += mat1[size * i + k] * mat2[size * k + j]; - } - } - return 0; -} - - -/*! - * \brief l_productMat3() - * - * \param[in] mat1 square matrix, as a 1-dimensional size^2 array - * \param[in] mat2 square matrix, as a 1-dimensional size^2 array - * \param[in] mat3 square matrix, as a 1-dimensional size^2 array - * \param[in] matd square matrix; product stored here - * \param[in] size of matrices - * \return 0 if OK, 1 on error - */ -l_ok -l_productMat3(l_float32 *mat1, - l_float32 *mat2, - l_float32 *mat3, - l_float32 *matd, - l_int32 size) -{ -l_float32 *matt; - - PROCNAME("l_productMat3"); - - if (!mat1) - return ERROR_INT("matrix 1 not defined", procName, 1); - if (!mat2) - return ERROR_INT("matrix 2 not defined", procName, 1); - if (!mat3) - return ERROR_INT("matrix 3 not defined", procName, 1); - if (!matd) - return ERROR_INT("result matrix not defined", procName, 1); - - if ((matt = (l_float32 *)LEPT_CALLOC((size_t)size * size, - sizeof(l_float32))) == NULL) - return ERROR_INT("matt not made", procName, 1); - l_productMat2(mat1, mat2, matt, size); - l_productMat2(matt, mat3, matd, size); - LEPT_FREE(matt); - return 0; -} - - -/*! - * \brief l_productMat4() - * - * \param[in] mat1 square matrix, as a 1-dimensional size^2 array - * \param[in] mat2 square matrix, as a 1-dimensional size^2 array - * \param[in] mat3 square matrix, as a 1-dimensional size^2 array - * \param[in] mat4 square matrix, as a 1-dimensional size^2 array - * \param[in] matd square matrix; product stored here - * \param[in] size of matrices - * \return 0 if OK, 1 on error - */ -l_ok -l_productMat4(l_float32 *mat1, - l_float32 *mat2, - l_float32 *mat3, - l_float32 *mat4, - l_float32 *matd, - l_int32 size) -{ -l_float32 *matt; - - PROCNAME("l_productMat4"); - - if (!mat1) - return ERROR_INT("matrix 1 not defined", procName, 1); - if (!mat2) - return ERROR_INT("matrix 2 not defined", procName, 1); - if (!mat3) - return ERROR_INT("matrix 3 not defined", procName, 1); - if (!matd) - return ERROR_INT("result matrix not defined", procName, 1); - - if ((matt = (l_float32 *)LEPT_CALLOC((size_t)size * size, - sizeof(l_float32))) == NULL) - return ERROR_INT("matt not made", procName, 1); - l_productMat3(mat1, mat2, mat3, matt, size); - l_productMat2(matt, mat4, matd, size); - LEPT_FREE(matt); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/allheaders.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/allheaders.h deleted file mode 100644 index 1c00620d..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/allheaders.h +++ /dev/null @@ -1,2768 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_ALLHEADERS_H -#define LEPTONICA_ALLHEADERS_H - - -#define LIBLEPT_MAJOR_VERSION 1 -#define LIBLEPT_MINOR_VERSION 79 -#define LIBLEPT_PATCH_VERSION 0 - -#include "alltypes.h" - -#ifndef NO_PROTOS -/* - * These prototypes were autogen'd by xtractprotos, v. 1.5 - */ -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -LEPT_DLL extern PIX * pixCleanBackgroundToWhite ( PIX *pixs, PIX *pixim, PIX *pixg, l_float32 gamma, l_int32 blackval, l_int32 whiteval ); -LEPT_DLL extern PIX * pixBackgroundNormSimple ( PIX *pixs, PIX *pixim, PIX *pixg ); -LEPT_DLL extern PIX * pixBackgroundNorm ( PIX *pixs, PIX *pixim, PIX *pixg, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, l_int32 bgval, l_int32 smoothx, l_int32 smoothy ); -LEPT_DLL extern PIX * pixBackgroundNormMorph ( PIX *pixs, PIX *pixim, l_int32 reduction, l_int32 size, l_int32 bgval ); -LEPT_DLL extern l_ok pixBackgroundNormGrayArray ( PIX *pixs, PIX *pixim, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, l_int32 bgval, l_int32 smoothx, l_int32 smoothy, PIX **ppixd ); -LEPT_DLL extern l_ok pixBackgroundNormRGBArrays ( PIX *pixs, PIX *pixim, PIX *pixg, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, l_int32 bgval, l_int32 smoothx, l_int32 smoothy, PIX **ppixr, PIX **ppixg, PIX **ppixb ); -LEPT_DLL extern l_ok pixBackgroundNormGrayArrayMorph ( PIX *pixs, PIX *pixim, l_int32 reduction, l_int32 size, l_int32 bgval, PIX **ppixd ); -LEPT_DLL extern l_ok pixBackgroundNormRGBArraysMorph ( PIX *pixs, PIX *pixim, l_int32 reduction, l_int32 size, l_int32 bgval, PIX **ppixr, PIX **ppixg, PIX **ppixb ); -LEPT_DLL extern l_ok pixGetBackgroundGrayMap ( PIX *pixs, PIX *pixim, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, PIX **ppixd ); -LEPT_DLL extern l_ok pixGetBackgroundRGBMap ( PIX *pixs, PIX *pixim, PIX *pixg, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, PIX **ppixmr, PIX **ppixmg, PIX **ppixmb ); -LEPT_DLL extern l_ok pixGetBackgroundGrayMapMorph ( PIX *pixs, PIX *pixim, l_int32 reduction, l_int32 size, PIX **ppixm ); -LEPT_DLL extern l_ok pixGetBackgroundRGBMapMorph ( PIX *pixs, PIX *pixim, l_int32 reduction, l_int32 size, PIX **ppixmr, PIX **ppixmg, PIX **ppixmb ); -LEPT_DLL extern l_ok pixFillMapHoles ( PIX *pix, l_int32 nx, l_int32 ny, l_int32 filltype ); -LEPT_DLL extern PIX * pixExtendByReplication ( PIX *pixs, l_int32 addw, l_int32 addh ); -LEPT_DLL extern l_ok pixSmoothConnectedRegions ( PIX *pixs, PIX *pixm, l_int32 factor ); -LEPT_DLL extern PIX * pixGetInvBackgroundMap ( PIX *pixs, l_int32 bgval, l_int32 smoothx, l_int32 smoothy ); -LEPT_DLL extern PIX * pixApplyInvBackgroundGrayMap ( PIX *pixs, PIX *pixm, l_int32 sx, l_int32 sy ); -LEPT_DLL extern PIX * pixApplyInvBackgroundRGBMap ( PIX *pixs, PIX *pixmr, PIX *pixmg, PIX *pixmb, l_int32 sx, l_int32 sy ); -LEPT_DLL extern PIX * pixApplyVariableGrayMap ( PIX *pixs, PIX *pixg, l_int32 target ); -LEPT_DLL extern PIX * pixGlobalNormRGB ( PIX *pixd, PIX *pixs, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 mapval ); -LEPT_DLL extern PIX * pixGlobalNormNoSatRGB ( PIX *pixd, PIX *pixs, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 factor, l_float32 rank ); -LEPT_DLL extern l_ok pixThresholdSpreadNorm ( PIX *pixs, l_int32 filtertype, l_int32 edgethresh, l_int32 smoothx, l_int32 smoothy, l_float32 gamma, l_int32 minval, l_int32 maxval, l_int32 targetthresh, PIX **ppixth, PIX **ppixb, PIX **ppixd ); -LEPT_DLL extern PIX * pixBackgroundNormFlex ( PIX *pixs, l_int32 sx, l_int32 sy, l_int32 smoothx, l_int32 smoothy, l_int32 delta ); -LEPT_DLL extern PIX * pixContrastNorm ( PIX *pixd, PIX *pixs, l_int32 sx, l_int32 sy, l_int32 mindiff, l_int32 smoothx, l_int32 smoothy ); -LEPT_DLL extern l_ok pixMinMaxTiles ( PIX *pixs, l_int32 sx, l_int32 sy, l_int32 mindiff, l_int32 smoothx, l_int32 smoothy, PIX **ppixmin, PIX **ppixmax ); -LEPT_DLL extern l_ok pixSetLowContrast ( PIX *pixs1, PIX *pixs2, l_int32 mindiff ); -LEPT_DLL extern PIX * pixLinearTRCTiled ( PIX *pixd, PIX *pixs, l_int32 sx, l_int32 sy, PIX *pixmin, PIX *pixmax ); -LEPT_DLL extern PIX * pixAffineSampledPta ( PIX *pixs, PTA *ptad, PTA *ptas, l_int32 incolor ); -LEPT_DLL extern PIX * pixAffineSampled ( PIX *pixs, l_float32 *vc, l_int32 incolor ); -LEPT_DLL extern PIX * pixAffinePta ( PIX *pixs, PTA *ptad, PTA *ptas, l_int32 incolor ); -LEPT_DLL extern PIX * pixAffine ( PIX *pixs, l_float32 *vc, l_int32 incolor ); -LEPT_DLL extern PIX * pixAffinePtaColor ( PIX *pixs, PTA *ptad, PTA *ptas, l_uint32 colorval ); -LEPT_DLL extern PIX * pixAffineColor ( PIX *pixs, l_float32 *vc, l_uint32 colorval ); -LEPT_DLL extern PIX * pixAffinePtaGray ( PIX *pixs, PTA *ptad, PTA *ptas, l_uint8 grayval ); -LEPT_DLL extern PIX * pixAffineGray ( PIX *pixs, l_float32 *vc, l_uint8 grayval ); -LEPT_DLL extern PIX * pixAffinePtaWithAlpha ( PIX *pixs, PTA *ptad, PTA *ptas, PIX *pixg, l_float32 fract, l_int32 border ); -LEPT_DLL extern l_ok getAffineXformCoeffs ( PTA *ptas, PTA *ptad, l_float32 **pvc ); -LEPT_DLL extern l_ok affineInvertXform ( l_float32 *vc, l_float32 **pvci ); -LEPT_DLL extern l_ok affineXformSampledPt ( l_float32 *vc, l_int32 x, l_int32 y, l_int32 *pxp, l_int32 *pyp ); -LEPT_DLL extern l_ok affineXformPt ( l_float32 *vc, l_int32 x, l_int32 y, l_float32 *pxp, l_float32 *pyp ); -LEPT_DLL extern l_ok linearInterpolatePixelColor ( l_uint32 *datas, l_int32 wpls, l_int32 w, l_int32 h, l_float32 x, l_float32 y, l_uint32 colorval, l_uint32 *pval ); -LEPT_DLL extern l_ok linearInterpolatePixelGray ( l_uint32 *datas, l_int32 wpls, l_int32 w, l_int32 h, l_float32 x, l_float32 y, l_int32 grayval, l_int32 *pval ); -LEPT_DLL extern l_int32 gaussjordan ( l_float32 **a, l_float32 *b, l_int32 n ); -LEPT_DLL extern PIX * pixAffineSequential ( PIX *pixs, PTA *ptad, PTA *ptas, l_int32 bw, l_int32 bh ); -LEPT_DLL extern l_float32 * createMatrix2dTranslate ( l_float32 transx, l_float32 transy ); -LEPT_DLL extern l_float32 * createMatrix2dScale ( l_float32 scalex, l_float32 scaley ); -LEPT_DLL extern l_float32 * createMatrix2dRotate ( l_float32 xc, l_float32 yc, l_float32 angle ); -LEPT_DLL extern PTA * ptaTranslate ( PTA *ptas, l_float32 transx, l_float32 transy ); -LEPT_DLL extern PTA * ptaScale ( PTA *ptas, l_float32 scalex, l_float32 scaley ); -LEPT_DLL extern PTA * ptaRotate ( PTA *ptas, l_float32 xc, l_float32 yc, l_float32 angle ); -LEPT_DLL extern BOXA * boxaTranslate ( BOXA *boxas, l_float32 transx, l_float32 transy ); -LEPT_DLL extern BOXA * boxaScale ( BOXA *boxas, l_float32 scalex, l_float32 scaley ); -LEPT_DLL extern BOXA * boxaRotate ( BOXA *boxas, l_float32 xc, l_float32 yc, l_float32 angle ); -LEPT_DLL extern PTA * ptaAffineTransform ( PTA *ptas, l_float32 *mat ); -LEPT_DLL extern BOXA * boxaAffineTransform ( BOXA *boxas, l_float32 *mat ); -LEPT_DLL extern l_ok l_productMatVec ( l_float32 *mat, l_float32 *vecs, l_float32 *vecd, l_int32 size ); -LEPT_DLL extern l_ok l_productMat2 ( l_float32 *mat1, l_float32 *mat2, l_float32 *matd, l_int32 size ); -LEPT_DLL extern l_ok l_productMat3 ( l_float32 *mat1, l_float32 *mat2, l_float32 *mat3, l_float32 *matd, l_int32 size ); -LEPT_DLL extern l_ok l_productMat4 ( l_float32 *mat1, l_float32 *mat2, l_float32 *mat3, l_float32 *mat4, l_float32 *matd, l_int32 size ); -LEPT_DLL extern l_int32 l_getDataBit ( const void *line, l_int32 n ); -LEPT_DLL extern void l_setDataBit ( void *line, l_int32 n ); -LEPT_DLL extern void l_clearDataBit ( void *line, l_int32 n ); -LEPT_DLL extern void l_setDataBitVal ( void *line, l_int32 n, l_int32 val ); -LEPT_DLL extern l_int32 l_getDataDibit ( const void *line, l_int32 n ); -LEPT_DLL extern void l_setDataDibit ( void *line, l_int32 n, l_int32 val ); -LEPT_DLL extern void l_clearDataDibit ( void *line, l_int32 n ); -LEPT_DLL extern l_int32 l_getDataQbit ( const void *line, l_int32 n ); -LEPT_DLL extern void l_setDataQbit ( void *line, l_int32 n, l_int32 val ); -LEPT_DLL extern void l_clearDataQbit ( void *line, l_int32 n ); -LEPT_DLL extern l_int32 l_getDataByte ( const void *line, l_int32 n ); -LEPT_DLL extern void l_setDataByte ( void *line, l_int32 n, l_int32 val ); -LEPT_DLL extern l_int32 l_getDataTwoBytes ( const void *line, l_int32 n ); -LEPT_DLL extern void l_setDataTwoBytes ( void *line, l_int32 n, l_int32 val ); -LEPT_DLL extern l_int32 l_getDataFourBytes ( const void *line, l_int32 n ); -LEPT_DLL extern void l_setDataFourBytes ( void *line, l_int32 n, l_int32 val ); -LEPT_DLL extern char * barcodeDispatchDecoder ( char *barstr, l_int32 format, l_int32 debugflag ); -LEPT_DLL extern l_int32 barcodeFormatIsSupported ( l_int32 format ); -LEPT_DLL extern NUMA * pixFindBaselines ( PIX *pixs, PTA **ppta, PIXA *pixadb ); -LEPT_DLL extern PIX * pixDeskewLocal ( PIX *pixs, l_int32 nslices, l_int32 redsweep, l_int32 redsearch, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta ); -LEPT_DLL extern l_ok pixGetLocalSkewTransform ( PIX *pixs, l_int32 nslices, l_int32 redsweep, l_int32 redsearch, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta, PTA **pptas, PTA **pptad ); -LEPT_DLL extern NUMA * pixGetLocalSkewAngles ( PIX *pixs, l_int32 nslices, l_int32 redsweep, l_int32 redsearch, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta, l_float32 *pa, l_float32 *pb, l_int32 debug ); -LEPT_DLL extern L_BBUFFER * bbufferCreate ( const l_uint8 *indata, l_int32 nalloc ); -LEPT_DLL extern void bbufferDestroy ( L_BBUFFER **pbb ); -LEPT_DLL extern l_uint8 * bbufferDestroyAndSaveData ( L_BBUFFER **pbb, size_t *pnbytes ); -LEPT_DLL extern l_ok bbufferRead ( L_BBUFFER *bb, l_uint8 *src, l_int32 nbytes ); -LEPT_DLL extern l_ok bbufferReadStream ( L_BBUFFER *bb, FILE *fp, l_int32 nbytes ); -LEPT_DLL extern l_ok bbufferExtendArray ( L_BBUFFER *bb, l_int32 nbytes ); -LEPT_DLL extern l_ok bbufferWrite ( L_BBUFFER *bb, l_uint8 *dest, size_t nbytes, size_t *pnout ); -LEPT_DLL extern l_ok bbufferWriteStream ( L_BBUFFER *bb, FILE *fp, size_t nbytes, size_t *pnout ); -LEPT_DLL extern PIX * pixBilateral ( PIX *pixs, l_float32 spatial_stdev, l_float32 range_stdev, l_int32 ncomps, l_int32 reduction ); -LEPT_DLL extern PIX * pixBilateralGray ( PIX *pixs, l_float32 spatial_stdev, l_float32 range_stdev, l_int32 ncomps, l_int32 reduction ); -LEPT_DLL extern PIX * pixBilateralExact ( PIX *pixs, L_KERNEL *spatial_kel, L_KERNEL *range_kel ); -LEPT_DLL extern PIX * pixBilateralGrayExact ( PIX *pixs, L_KERNEL *spatial_kel, L_KERNEL *range_kel ); -LEPT_DLL extern PIX* pixBlockBilateralExact ( PIX *pixs, l_float32 spatial_stdev, l_float32 range_stdev ); -LEPT_DLL extern L_KERNEL * makeRangeKernel ( l_float32 range_stdev ); -LEPT_DLL extern PIX * pixBilinearSampledPta ( PIX *pixs, PTA *ptad, PTA *ptas, l_int32 incolor ); -LEPT_DLL extern PIX * pixBilinearSampled ( PIX *pixs, l_float32 *vc, l_int32 incolor ); -LEPT_DLL extern PIX * pixBilinearPta ( PIX *pixs, PTA *ptad, PTA *ptas, l_int32 incolor ); -LEPT_DLL extern PIX * pixBilinear ( PIX *pixs, l_float32 *vc, l_int32 incolor ); -LEPT_DLL extern PIX * pixBilinearPtaColor ( PIX *pixs, PTA *ptad, PTA *ptas, l_uint32 colorval ); -LEPT_DLL extern PIX * pixBilinearColor ( PIX *pixs, l_float32 *vc, l_uint32 colorval ); -LEPT_DLL extern PIX * pixBilinearPtaGray ( PIX *pixs, PTA *ptad, PTA *ptas, l_uint8 grayval ); -LEPT_DLL extern PIX * pixBilinearGray ( PIX *pixs, l_float32 *vc, l_uint8 grayval ); -LEPT_DLL extern PIX * pixBilinearPtaWithAlpha ( PIX *pixs, PTA *ptad, PTA *ptas, PIX *pixg, l_float32 fract, l_int32 border ); -LEPT_DLL extern l_ok getBilinearXformCoeffs ( PTA *ptas, PTA *ptad, l_float32 **pvc ); -LEPT_DLL extern l_ok bilinearXformSampledPt ( l_float32 *vc, l_int32 x, l_int32 y, l_int32 *pxp, l_int32 *pyp ); -LEPT_DLL extern l_ok bilinearXformPt ( l_float32 *vc, l_int32 x, l_int32 y, l_float32 *pxp, l_float32 *pyp ); -LEPT_DLL extern l_ok pixOtsuAdaptiveThreshold ( PIX *pixs, l_int32 sx, l_int32 sy, l_int32 smoothx, l_int32 smoothy, l_float32 scorefract, PIX **ppixth, PIX **ppixd ); -LEPT_DLL extern PIX * pixOtsuThreshOnBackgroundNorm ( PIX *pixs, PIX *pixim, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, l_int32 bgval, l_int32 smoothx, l_int32 smoothy, l_float32 scorefract, l_int32 *pthresh ); -LEPT_DLL extern PIX * pixMaskedThreshOnBackgroundNorm ( PIX *pixs, PIX *pixim, l_int32 sx, l_int32 sy, l_int32 thresh, l_int32 mincount, l_int32 smoothx, l_int32 smoothy, l_float32 scorefract, l_int32 *pthresh ); -LEPT_DLL extern l_ok pixSauvolaBinarizeTiled ( PIX *pixs, l_int32 whsize, l_float32 factor, l_int32 nx, l_int32 ny, PIX **ppixth, PIX **ppixd ); -LEPT_DLL extern l_ok pixSauvolaBinarize ( PIX *pixs, l_int32 whsize, l_float32 factor, l_int32 addborder, PIX **ppixm, PIX **ppixsd, PIX **ppixth, PIX **ppixd ); -LEPT_DLL extern l_ok pixThresholdByConnComp ( PIX *pixs, PIX *pixm, l_int32 start, l_int32 end, l_int32 incr, l_float32 thresh48, l_float32 threshdiff, l_int32 *pglobthresh, PIX **ppixd, l_int32 debugflag ); -LEPT_DLL extern l_ok pixThresholdByHisto ( PIX *pixs, l_int32 factor, l_int32 halfw, l_float32 delta, l_int32 *pthresh, PIX **ppixd, PIX **ppixhisto ); -LEPT_DLL extern PIX * pixExpandBinaryReplicate ( PIX *pixs, l_int32 xfact, l_int32 yfact ); -LEPT_DLL extern PIX * pixExpandBinaryPower2 ( PIX *pixs, l_int32 factor ); -LEPT_DLL extern PIX * pixReduceBinary2 ( PIX *pixs, l_uint8 *intab ); -LEPT_DLL extern PIX * pixReduceRankBinaryCascade ( PIX *pixs, l_int32 level1, l_int32 level2, l_int32 level3, l_int32 level4 ); -LEPT_DLL extern PIX * pixReduceRankBinary2 ( PIX *pixs, l_int32 level, l_uint8 *intab ); -LEPT_DLL extern l_uint8 * makeSubsampleTab2x ( void ); -LEPT_DLL extern PIX * pixBlend ( PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract ); -LEPT_DLL extern PIX * pixBlendMask ( PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract, l_int32 type ); -LEPT_DLL extern PIX * pixBlendGray ( PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract, l_int32 type, l_int32 transparent, l_uint32 transpix ); -LEPT_DLL extern PIX * pixBlendGrayInverse ( PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract ); -LEPT_DLL extern PIX * pixBlendColor ( PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract, l_int32 transparent, l_uint32 transpix ); -LEPT_DLL extern PIX * pixBlendColorByChannel ( PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 rfract, l_float32 gfract, l_float32 bfract, l_int32 transparent, l_uint32 transpix ); -LEPT_DLL extern PIX * pixBlendGrayAdapt ( PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract, l_int32 shift ); -LEPT_DLL extern PIX * pixFadeWithGray ( PIX *pixs, PIX *pixb, l_float32 factor, l_int32 type ); -LEPT_DLL extern PIX * pixBlendHardLight ( PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract ); -LEPT_DLL extern l_ok pixBlendCmap ( PIX *pixs, PIX *pixb, l_int32 x, l_int32 y, l_int32 sindex ); -LEPT_DLL extern PIX * pixBlendWithGrayMask ( PIX *pixs1, PIX *pixs2, PIX *pixg, l_int32 x, l_int32 y ); -LEPT_DLL extern PIX * pixBlendBackgroundToColor ( PIX *pixd, PIX *pixs, BOX *box, l_uint32 color, l_float32 gamma, l_int32 minval, l_int32 maxval ); -LEPT_DLL extern PIX * pixMultiplyByColor ( PIX *pixd, PIX *pixs, BOX *box, l_uint32 color ); -LEPT_DLL extern PIX * pixAlphaBlendUniform ( PIX *pixs, l_uint32 color ); -LEPT_DLL extern PIX * pixAddAlphaToBlend ( PIX *pixs, l_float32 fract, l_int32 invert ); -LEPT_DLL extern PIX * pixSetAlphaOverWhite ( PIX *pixs ); -LEPT_DLL extern l_ok pixLinearEdgeFade ( PIX *pixs, l_int32 dir, l_int32 fadeto, l_float32 distfract, l_float32 maxfade ); -LEPT_DLL extern L_BMF * bmfCreate ( const char *dir, l_int32 fontsize ); -LEPT_DLL extern void bmfDestroy ( L_BMF **pbmf ); -LEPT_DLL extern PIX * bmfGetPix ( L_BMF *bmf, char chr ); -LEPT_DLL extern l_ok bmfGetWidth ( L_BMF *bmf, char chr, l_int32 *pw ); -LEPT_DLL extern l_ok bmfGetBaseline ( L_BMF *bmf, char chr, l_int32 *pbaseline ); -LEPT_DLL extern PIXA * pixaGetFont ( const char *dir, l_int32 fontsize, l_int32 *pbl0, l_int32 *pbl1, l_int32 *pbl2 ); -LEPT_DLL extern l_ok pixaSaveFont ( const char *indir, const char *outdir, l_int32 fontsize ); -LEPT_DLL extern PIX * pixReadStreamBmp ( FILE *fp ); -LEPT_DLL extern PIX * pixReadMemBmp ( const l_uint8 *cdata, size_t size ); -LEPT_DLL extern l_ok pixWriteStreamBmp ( FILE *fp, PIX *pix ); -LEPT_DLL extern l_ok pixWriteMemBmp ( l_uint8 **pfdata, size_t *pfsize, PIX *pixs ); -LEPT_DLL extern PIXA * l_bootnum_gen1 ( void ); -LEPT_DLL extern PIXA * l_bootnum_gen2 ( void ); -LEPT_DLL extern PIXA * l_bootnum_gen3 ( void ); -LEPT_DLL extern PIXA * l_bootnum_gen4 ( l_int32 nsamp ); -LEPT_DLL extern BOX * boxCreate ( l_int32 x, l_int32 y, l_int32 w, l_int32 h ); -LEPT_DLL extern BOX * boxCreateValid ( l_int32 x, l_int32 y, l_int32 w, l_int32 h ); -LEPT_DLL extern BOX * boxCopy ( BOX *box ); -LEPT_DLL extern BOX * boxClone ( BOX *box ); -LEPT_DLL extern void boxDestroy ( BOX **pbox ); -LEPT_DLL extern l_ok boxGetGeometry ( BOX *box, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph ); -LEPT_DLL extern l_ok boxSetGeometry ( BOX *box, l_int32 x, l_int32 y, l_int32 w, l_int32 h ); -LEPT_DLL extern l_ok boxGetSideLocations ( BOX *box, l_int32 *pl, l_int32 *pr, l_int32 *pt, l_int32 *pb ); -LEPT_DLL extern l_ok boxSetSideLocations ( BOX *box, l_int32 l, l_int32 r, l_int32 t, l_int32 b ); -LEPT_DLL extern l_int32 boxGetRefcount ( BOX *box ); -LEPT_DLL extern l_ok boxChangeRefcount ( BOX *box, l_int32 delta ); -LEPT_DLL extern l_ok boxIsValid ( BOX *box, l_int32 *pvalid ); -LEPT_DLL extern BOXA * boxaCreate ( l_int32 n ); -LEPT_DLL extern BOXA * boxaCopy ( BOXA *boxa, l_int32 copyflag ); -LEPT_DLL extern void boxaDestroy ( BOXA **pboxa ); -LEPT_DLL extern l_ok boxaAddBox ( BOXA *boxa, BOX *box, l_int32 copyflag ); -LEPT_DLL extern l_ok boxaExtendArray ( BOXA *boxa ); -LEPT_DLL extern l_ok boxaExtendArrayToSize ( BOXA *boxa, l_int32 size ); -LEPT_DLL extern l_int32 boxaGetCount ( BOXA *boxa ); -LEPT_DLL extern l_int32 boxaGetValidCount ( BOXA *boxa ); -LEPT_DLL extern BOX * boxaGetBox ( BOXA *boxa, l_int32 index, l_int32 accessflag ); -LEPT_DLL extern BOX * boxaGetValidBox ( BOXA *boxa, l_int32 index, l_int32 accessflag ); -LEPT_DLL extern NUMA * boxaFindInvalidBoxes ( BOXA *boxa ); -LEPT_DLL extern l_ok boxaGetBoxGeometry ( BOXA *boxa, l_int32 index, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph ); -LEPT_DLL extern l_ok boxaIsFull ( BOXA *boxa, l_int32 *pfull ); -LEPT_DLL extern l_ok boxaReplaceBox ( BOXA *boxa, l_int32 index, BOX *box ); -LEPT_DLL extern l_ok boxaInsertBox ( BOXA *boxa, l_int32 index, BOX *box ); -LEPT_DLL extern l_ok boxaRemoveBox ( BOXA *boxa, l_int32 index ); -LEPT_DLL extern l_ok boxaRemoveBoxAndSave ( BOXA *boxa, l_int32 index, BOX **pbox ); -LEPT_DLL extern BOXA * boxaSaveValid ( BOXA *boxas, l_int32 copyflag ); -LEPT_DLL extern l_ok boxaInitFull ( BOXA *boxa, BOX *box ); -LEPT_DLL extern l_ok boxaClear ( BOXA *boxa ); -LEPT_DLL extern BOXAA * boxaaCreate ( l_int32 n ); -LEPT_DLL extern BOXAA * boxaaCopy ( BOXAA *baas, l_int32 copyflag ); -LEPT_DLL extern void boxaaDestroy ( BOXAA **pbaa ); -LEPT_DLL extern l_ok boxaaAddBoxa ( BOXAA *baa, BOXA *ba, l_int32 copyflag ); -LEPT_DLL extern l_ok boxaaExtendArray ( BOXAA *baa ); -LEPT_DLL extern l_ok boxaaExtendArrayToSize ( BOXAA *baa, l_int32 size ); -LEPT_DLL extern l_int32 boxaaGetCount ( BOXAA *baa ); -LEPT_DLL extern l_int32 boxaaGetBoxCount ( BOXAA *baa ); -LEPT_DLL extern BOXA * boxaaGetBoxa ( BOXAA *baa, l_int32 index, l_int32 accessflag ); -LEPT_DLL extern BOX * boxaaGetBox ( BOXAA *baa, l_int32 iboxa, l_int32 ibox, l_int32 accessflag ); -LEPT_DLL extern l_ok boxaaInitFull ( BOXAA *baa, BOXA *boxa ); -LEPT_DLL extern l_ok boxaaExtendWithInit ( BOXAA *baa, l_int32 maxindex, BOXA *boxa ); -LEPT_DLL extern l_ok boxaaReplaceBoxa ( BOXAA *baa, l_int32 index, BOXA *boxa ); -LEPT_DLL extern l_ok boxaaInsertBoxa ( BOXAA *baa, l_int32 index, BOXA *boxa ); -LEPT_DLL extern l_ok boxaaRemoveBoxa ( BOXAA *baa, l_int32 index ); -LEPT_DLL extern l_ok boxaaAddBox ( BOXAA *baa, l_int32 index, BOX *box, l_int32 accessflag ); -LEPT_DLL extern BOXAA * boxaaReadFromFiles ( const char *dirname, const char *substr, l_int32 first, l_int32 nfiles ); -LEPT_DLL extern BOXAA * boxaaRead ( const char *filename ); -LEPT_DLL extern BOXAA * boxaaReadStream ( FILE *fp ); -LEPT_DLL extern BOXAA * boxaaReadMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok boxaaWrite ( const char *filename, BOXAA *baa ); -LEPT_DLL extern l_ok boxaaWriteStream ( FILE *fp, BOXAA *baa ); -LEPT_DLL extern l_ok boxaaWriteMem ( l_uint8 **pdata, size_t *psize, BOXAA *baa ); -LEPT_DLL extern BOXA * boxaRead ( const char *filename ); -LEPT_DLL extern BOXA * boxaReadStream ( FILE *fp ); -LEPT_DLL extern BOXA * boxaReadMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok boxaWriteDebug ( const char *filename, BOXA *boxa ); -LEPT_DLL extern l_ok boxaWrite ( const char *filename, BOXA *boxa ); -LEPT_DLL extern l_ok boxaWriteStream ( FILE *fp, BOXA *boxa ); -LEPT_DLL extern l_ok boxaWriteStderr ( BOXA *boxa ); -LEPT_DLL extern l_ok boxaWriteMem ( l_uint8 **pdata, size_t *psize, BOXA *boxa ); -LEPT_DLL extern l_ok boxPrintStreamInfo ( FILE *fp, BOX *box ); -LEPT_DLL extern l_ok boxContains ( BOX *box1, BOX *box2, l_int32 *presult ); -LEPT_DLL extern l_ok boxIntersects ( BOX *box1, BOX *box2, l_int32 *presult ); -LEPT_DLL extern BOXA * boxaContainedInBox ( BOXA *boxas, BOX *box ); -LEPT_DLL extern l_ok boxaContainedInBoxCount ( BOXA *boxa, BOX *box, l_int32 *pcount ); -LEPT_DLL extern l_ok boxaContainedInBoxa ( BOXA *boxa1, BOXA *boxa2, l_int32 *pcontained ); -LEPT_DLL extern BOXA * boxaIntersectsBox ( BOXA *boxas, BOX *box ); -LEPT_DLL extern l_ok boxaIntersectsBoxCount ( BOXA *boxa, BOX *box, l_int32 *pcount ); -LEPT_DLL extern BOXA * boxaClipToBox ( BOXA *boxas, BOX *box ); -LEPT_DLL extern BOXA * boxaCombineOverlaps ( BOXA *boxas, PIXA *pixadb ); -LEPT_DLL extern l_ok boxaCombineOverlapsInPair ( BOXA *boxas1, BOXA *boxas2, BOXA **pboxad1, BOXA **pboxad2, PIXA *pixadb ); -LEPT_DLL extern BOX * boxOverlapRegion ( BOX *box1, BOX *box2 ); -LEPT_DLL extern BOX * boxBoundingRegion ( BOX *box1, BOX *box2 ); -LEPT_DLL extern l_ok boxOverlapFraction ( BOX *box1, BOX *box2, l_float32 *pfract ); -LEPT_DLL extern l_ok boxOverlapArea ( BOX *box1, BOX *box2, l_int32 *parea ); -LEPT_DLL extern BOXA * boxaHandleOverlaps ( BOXA *boxas, l_int32 op, l_int32 range, l_float32 min_overlap, l_float32 max_ratio, NUMA **pnamap ); -LEPT_DLL extern l_ok boxOverlapDistance ( BOX *box1, BOX *box2, l_int32 *ph_ovl, l_int32 *pv_ovl ); -LEPT_DLL extern l_ok boxSeparationDistance ( BOX *box1, BOX *box2, l_int32 *ph_sep, l_int32 *pv_sep ); -LEPT_DLL extern l_ok boxCompareSize ( BOX *box1, BOX *box2, l_int32 type, l_int32 *prel ); -LEPT_DLL extern l_ok boxContainsPt ( BOX *box, l_float32 x, l_float32 y, l_int32 *pcontains ); -LEPT_DLL extern BOX * boxaGetNearestToPt ( BOXA *boxa, l_int32 x, l_int32 y ); -LEPT_DLL extern BOX * boxaGetNearestToLine ( BOXA *boxa, l_int32 x, l_int32 y ); -LEPT_DLL extern l_ok boxaFindNearestBoxes ( BOXA *boxa, l_int32 dist_select, l_int32 range, NUMAA **pnaaindex, NUMAA **pnaadist ); -LEPT_DLL extern l_ok boxaGetNearestByDirection ( BOXA *boxa, l_int32 i, l_int32 dir, l_int32 dist_select, l_int32 range, l_int32 *pindex, l_int32 *pdist ); -LEPT_DLL extern l_ok boxGetCenter ( BOX *box, l_float32 *pcx, l_float32 *pcy ); -LEPT_DLL extern l_ok boxIntersectByLine ( BOX *box, l_int32 x, l_int32 y, l_float32 slope, l_int32 *px1, l_int32 *py1, l_int32 *px2, l_int32 *py2, l_int32 *pn ); -LEPT_DLL extern BOX * boxClipToRectangle ( BOX *box, l_int32 wi, l_int32 hi ); -LEPT_DLL extern l_ok boxClipToRectangleParams ( BOX *box, l_int32 w, l_int32 h, l_int32 *pxstart, l_int32 *pystart, l_int32 *pxend, l_int32 *pyend, l_int32 *pbw, l_int32 *pbh ); -LEPT_DLL extern BOX * boxRelocateOneSide ( BOX *boxd, BOX *boxs, l_int32 loc, l_int32 sideflag ); -LEPT_DLL extern BOXA * boxaAdjustSides ( BOXA *boxas, l_int32 delleft, l_int32 delright, l_int32 deltop, l_int32 delbot ); -LEPT_DLL extern l_ok boxaAdjustBoxSides ( BOXA *boxa, l_int32 index, l_int32 delleft, l_int32 delright, l_int32 deltop, l_int32 delbot ); -LEPT_DLL extern BOX * boxAdjustSides ( BOX *boxd, BOX *boxs, l_int32 delleft, l_int32 delright, l_int32 deltop, l_int32 delbot ); -LEPT_DLL extern BOXA * boxaSetSide ( BOXA *boxad, BOXA *boxas, l_int32 side, l_int32 val, l_int32 thresh ); -LEPT_DLL extern l_ok boxSetSide ( BOX *boxs, l_int32 side, l_int32 val, l_int32 thresh ); -LEPT_DLL extern BOXA * boxaAdjustWidthToTarget ( BOXA *boxad, BOXA *boxas, l_int32 sides, l_int32 target, l_int32 thresh ); -LEPT_DLL extern BOXA * boxaAdjustHeightToTarget ( BOXA *boxad, BOXA *boxas, l_int32 sides, l_int32 target, l_int32 thresh ); -LEPT_DLL extern l_ok boxEqual ( BOX *box1, BOX *box2, l_int32 *psame ); -LEPT_DLL extern l_ok boxaEqual ( BOXA *boxa1, BOXA *boxa2, l_int32 maxdist, NUMA **pnaindex, l_int32 *psame ); -LEPT_DLL extern l_ok boxSimilar ( BOX *box1, BOX *box2, l_int32 leftdiff, l_int32 rightdiff, l_int32 topdiff, l_int32 botdiff, l_int32 *psimilar ); -LEPT_DLL extern l_ok boxaSimilar ( BOXA *boxa1, BOXA *boxa2, l_int32 leftdiff, l_int32 rightdiff, l_int32 topdiff, l_int32 botdiff, l_int32 debug, l_int32 *psimilar, NUMA **pnasim ); -LEPT_DLL extern l_ok boxaJoin ( BOXA *boxad, BOXA *boxas, l_int32 istart, l_int32 iend ); -LEPT_DLL extern l_ok boxaaJoin ( BOXAA *baad, BOXAA *baas, l_int32 istart, l_int32 iend ); -LEPT_DLL extern l_ok boxaSplitEvenOdd ( BOXA *boxa, l_int32 fillflag, BOXA **pboxae, BOXA **pboxao ); -LEPT_DLL extern BOXA * boxaMergeEvenOdd ( BOXA *boxae, BOXA *boxao, l_int32 fillflag ); -LEPT_DLL extern BOXA * boxaTransform ( BOXA *boxas, l_int32 shiftx, l_int32 shifty, l_float32 scalex, l_float32 scaley ); -LEPT_DLL extern BOX * boxTransform ( BOX *box, l_int32 shiftx, l_int32 shifty, l_float32 scalex, l_float32 scaley ); -LEPT_DLL extern BOXA * boxaTransformOrdered ( BOXA *boxas, l_int32 shiftx, l_int32 shifty, l_float32 scalex, l_float32 scaley, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 order ); -LEPT_DLL extern BOX * boxTransformOrdered ( BOX *boxs, l_int32 shiftx, l_int32 shifty, l_float32 scalex, l_float32 scaley, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 order ); -LEPT_DLL extern BOXA * boxaRotateOrth ( BOXA *boxas, l_int32 w, l_int32 h, l_int32 rotation ); -LEPT_DLL extern BOX * boxRotateOrth ( BOX *box, l_int32 w, l_int32 h, l_int32 rotation ); -LEPT_DLL extern BOXA * boxaShiftWithPta ( BOXA *boxas, PTA *pta, l_int32 dir ); -LEPT_DLL extern BOXA * boxaSort ( BOXA *boxas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex ); -LEPT_DLL extern BOXA * boxaBinSort ( BOXA *boxas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex ); -LEPT_DLL extern BOXA * boxaSortByIndex ( BOXA *boxas, NUMA *naindex ); -LEPT_DLL extern BOXAA * boxaSort2d ( BOXA *boxas, NUMAA **pnaad, l_int32 delta1, l_int32 delta2, l_int32 minh1 ); -LEPT_DLL extern BOXAA * boxaSort2dByIndex ( BOXA *boxas, NUMAA *naa ); -LEPT_DLL extern l_ok boxaExtractAsNuma ( BOXA *boxa, NUMA **pnal, NUMA **pnat, NUMA **pnar, NUMA **pnab, NUMA **pnaw, NUMA **pnah, l_int32 keepinvalid ); -LEPT_DLL extern l_ok boxaExtractAsPta ( BOXA *boxa, PTA **pptal, PTA **pptat, PTA **pptar, PTA **pptab, PTA **pptaw, PTA **pptah, l_int32 keepinvalid ); -LEPT_DLL extern PTA * boxaExtractCorners ( BOXA *boxa, l_int32 loc ); -LEPT_DLL extern l_ok boxaGetRankVals ( BOXA *boxa, l_float32 fract, l_int32 *px, l_int32 *py, l_int32 *pr, l_int32 *pb, l_int32 *pw, l_int32 *ph ); -LEPT_DLL extern l_ok boxaGetMedianVals ( BOXA *boxa, l_int32 *px, l_int32 *py, l_int32 *pr, l_int32 *pb, l_int32 *pw, l_int32 *ph ); -LEPT_DLL extern l_ok boxaGetAverageSize ( BOXA *boxa, l_float32 *pw, l_float32 *ph ); -LEPT_DLL extern l_ok boxaaGetExtent ( BOXAA *baa, l_int32 *pw, l_int32 *ph, BOX **pbox, BOXA **pboxa ); -LEPT_DLL extern BOXA * boxaaFlattenToBoxa ( BOXAA *baa, NUMA **pnaindex, l_int32 copyflag ); -LEPT_DLL extern BOXA * boxaaFlattenAligned ( BOXAA *baa, l_int32 num, BOX *fillerbox, l_int32 copyflag ); -LEPT_DLL extern BOXAA * boxaEncapsulateAligned ( BOXA *boxa, l_int32 num, l_int32 copyflag ); -LEPT_DLL extern BOXAA * boxaaTranspose ( BOXAA *baas ); -LEPT_DLL extern l_ok boxaaAlignBox ( BOXAA *baa, BOX *box, l_int32 delta, l_int32 *pindex ); -LEPT_DLL extern PIX * pixMaskConnComp ( PIX *pixs, l_int32 connectivity, BOXA **pboxa ); -LEPT_DLL extern PIX * pixMaskBoxa ( PIX *pixd, PIX *pixs, BOXA *boxa, l_int32 op ); -LEPT_DLL extern PIX * pixPaintBoxa ( PIX *pixs, BOXA *boxa, l_uint32 val ); -LEPT_DLL extern PIX * pixSetBlackOrWhiteBoxa ( PIX *pixs, BOXA *boxa, l_int32 op ); -LEPT_DLL extern PIX * pixPaintBoxaRandom ( PIX *pixs, BOXA *boxa ); -LEPT_DLL extern PIX * pixBlendBoxaRandom ( PIX *pixs, BOXA *boxa, l_float32 fract ); -LEPT_DLL extern PIX * pixDrawBoxa ( PIX *pixs, BOXA *boxa, l_int32 width, l_uint32 val ); -LEPT_DLL extern PIX * pixDrawBoxaRandom ( PIX *pixs, BOXA *boxa, l_int32 width ); -LEPT_DLL extern PIX * boxaaDisplay ( PIX *pixs, BOXAA *baa, l_int32 linewba, l_int32 linewb, l_uint32 colorba, l_uint32 colorb, l_int32 w, l_int32 h ); -LEPT_DLL extern PIXA * pixaDisplayBoxaa ( PIXA *pixas, BOXAA *baa, l_int32 colorflag, l_int32 width ); -LEPT_DLL extern BOXA * pixSplitIntoBoxa ( PIX *pixs, l_int32 minsum, l_int32 skipdist, l_int32 delta, l_int32 maxbg, l_int32 maxcomps, l_int32 remainder ); -LEPT_DLL extern BOXA * pixSplitComponentIntoBoxa ( PIX *pix, BOX *box, l_int32 minsum, l_int32 skipdist, l_int32 delta, l_int32 maxbg, l_int32 maxcomps, l_int32 remainder ); -LEPT_DLL extern BOXA * makeMosaicStrips ( l_int32 w, l_int32 h, l_int32 direction, l_int32 size ); -LEPT_DLL extern l_ok boxaCompareRegions ( BOXA *boxa1, BOXA *boxa2, l_int32 areathresh, l_int32 *pnsame, l_float32 *pdiffarea, l_float32 *pdiffxor, PIX **ppixdb ); -LEPT_DLL extern BOX * pixSelectLargeULComp ( PIX *pixs, l_float32 areaslop, l_int32 yslop, l_int32 connectivity ); -LEPT_DLL extern BOX * boxaSelectLargeULBox ( BOXA *boxas, l_float32 areaslop, l_int32 yslop ); -LEPT_DLL extern BOXA * boxaSelectRange ( BOXA *boxas, l_int32 first, l_int32 last, l_int32 copyflag ); -LEPT_DLL extern BOXAA * boxaaSelectRange ( BOXAA *baas, l_int32 first, l_int32 last, l_int32 copyflag ); -LEPT_DLL extern BOXA * boxaSelectBySize ( BOXA *boxas, l_int32 width, l_int32 height, l_int32 type, l_int32 relation, l_int32 *pchanged ); -LEPT_DLL extern NUMA * boxaMakeSizeIndicator ( BOXA *boxa, l_int32 width, l_int32 height, l_int32 type, l_int32 relation ); -LEPT_DLL extern BOXA * boxaSelectByArea ( BOXA *boxas, l_int32 area, l_int32 relation, l_int32 *pchanged ); -LEPT_DLL extern NUMA * boxaMakeAreaIndicator ( BOXA *boxa, l_int32 area, l_int32 relation ); -LEPT_DLL extern BOXA * boxaSelectByWHRatio ( BOXA *boxas, l_float32 ratio, l_int32 relation, l_int32 *pchanged ); -LEPT_DLL extern NUMA * boxaMakeWHRatioIndicator ( BOXA *boxa, l_float32 ratio, l_int32 relation ); -LEPT_DLL extern BOXA * boxaSelectWithIndicator ( BOXA *boxas, NUMA *na, l_int32 *pchanged ); -LEPT_DLL extern BOXA * boxaPermutePseudorandom ( BOXA *boxas ); -LEPT_DLL extern BOXA * boxaPermuteRandom ( BOXA *boxad, BOXA *boxas ); -LEPT_DLL extern l_ok boxaSwapBoxes ( BOXA *boxa, l_int32 i, l_int32 j ); -LEPT_DLL extern PTA * boxaConvertToPta ( BOXA *boxa, l_int32 ncorners ); -LEPT_DLL extern BOXA * ptaConvertToBoxa ( PTA *pta, l_int32 ncorners ); -LEPT_DLL extern PTA * boxConvertToPta ( BOX *box, l_int32 ncorners ); -LEPT_DLL extern BOX * ptaConvertToBox ( PTA *pta ); -LEPT_DLL extern l_ok boxaGetExtent ( BOXA *boxa, l_int32 *pw, l_int32 *ph, BOX **pbox ); -LEPT_DLL extern l_ok boxaGetCoverage ( BOXA *boxa, l_int32 wc, l_int32 hc, l_int32 exactflag, l_float32 *pfract ); -LEPT_DLL extern l_ok boxaaSizeRange ( BOXAA *baa, l_int32 *pminw, l_int32 *pminh, l_int32 *pmaxw, l_int32 *pmaxh ); -LEPT_DLL extern l_ok boxaSizeRange ( BOXA *boxa, l_int32 *pminw, l_int32 *pminh, l_int32 *pmaxw, l_int32 *pmaxh ); -LEPT_DLL extern l_ok boxaLocationRange ( BOXA *boxa, l_int32 *pminx, l_int32 *pminy, l_int32 *pmaxx, l_int32 *pmaxy ); -LEPT_DLL extern l_ok boxaGetSizes ( BOXA *boxa, NUMA **pnaw, NUMA **pnah ); -LEPT_DLL extern l_ok boxaGetArea ( BOXA *boxa, l_int32 *parea ); -LEPT_DLL extern PIX * boxaDisplayTiled ( BOXA *boxas, PIXA *pixa, l_int32 first, l_int32 last, l_int32 maxwidth, l_int32 linewidth, l_float32 scalefactor, l_int32 background, l_int32 spacing, l_int32 border ); -LEPT_DLL extern BOXA * boxaSmoothSequenceMedian ( BOXA *boxas, l_int32 halfwin, l_int32 subflag, l_int32 maxdiff, l_int32 extrapixels, l_int32 debug ); -LEPT_DLL extern BOXA * boxaWindowedMedian ( BOXA *boxas, l_int32 halfwin, l_int32 debug ); -LEPT_DLL extern BOXA * boxaModifyWithBoxa ( BOXA *boxas, BOXA *boxam, l_int32 subflag, l_int32 maxdiff, l_int32 extrapixels ); -LEPT_DLL extern BOXA * boxaConstrainSize ( BOXA *boxas, l_int32 width, l_int32 widthflag, l_int32 height, l_int32 heightflag ); -LEPT_DLL extern BOXA * boxaReconcileEvenOddHeight ( BOXA *boxas, l_int32 sides, l_int32 delh, l_int32 op, l_float32 factor, l_int32 start ); -LEPT_DLL extern BOXA * boxaReconcilePairWidth ( BOXA *boxas, l_int32 delw, l_int32 op, l_float32 factor, NUMA *na ); -LEPT_DLL extern l_ok boxaSizeConsistency1 ( BOXA *boxas, l_int32 type, l_float32 threshp, l_float32 threshm, l_float32 *pfvarp, l_float32 *pfvarm, l_int32 *psame ); -LEPT_DLL extern l_ok boxaSizeConsistency2 ( BOXA *boxas, l_float32 *pfdevw, l_float32 *pfdevh, l_int32 debug ); -LEPT_DLL extern BOXA * boxaReconcileAllByMedian ( BOXA *boxas, l_int32 select1, l_int32 select2, l_int32 thresh, l_int32 extra, PIXA *pixadb ); -LEPT_DLL extern BOXA * boxaReconcileSidesByMedian ( BOXA *boxas, l_int32 select, l_int32 thresh, l_int32 extra, PIXA *pixadb ); -LEPT_DLL extern BOXA * boxaReconcileSizeByMedian ( BOXA *boxas, l_int32 type, l_float32 dfract, l_float32 sfract, l_float32 factor, NUMA **pnadelw, NUMA **pnadelh, l_float32 *pratiowh ); -LEPT_DLL extern l_ok boxaPlotSides ( BOXA *boxa, const char *plotname, NUMA **pnal, NUMA **pnat, NUMA **pnar, NUMA **pnab, PIX **ppixd ); -LEPT_DLL extern l_ok boxaPlotSizes ( BOXA *boxa, const char *plotname, NUMA **pnaw, NUMA **pnah, PIX **ppixd ); -LEPT_DLL extern BOXA * boxaFillSequence ( BOXA *boxas, l_int32 useflag, l_int32 debug ); -LEPT_DLL extern l_ok boxaSizeVariation ( BOXA *boxa, l_int32 type, l_float32 *pdel_evenodd, l_float32 *prms_even, l_float32 *prms_odd, l_float32 *prms_all ); -LEPT_DLL extern l_ok boxaMedianDimensions ( BOXA *boxas, l_int32 *pmedw, l_int32 *pmedh, l_int32 *pmedwe, l_int32 *pmedwo, l_int32 *pmedhe, l_int32 *pmedho, NUMA **pnadelw, NUMA **pnadelh ); -LEPT_DLL extern L_BYTEA * l_byteaCreate ( size_t nbytes ); -LEPT_DLL extern L_BYTEA * l_byteaInitFromMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern L_BYTEA * l_byteaInitFromFile ( const char *fname ); -LEPT_DLL extern L_BYTEA * l_byteaInitFromStream ( FILE *fp ); -LEPT_DLL extern L_BYTEA * l_byteaCopy ( L_BYTEA *bas, l_int32 copyflag ); -LEPT_DLL extern void l_byteaDestroy ( L_BYTEA **pba ); -LEPT_DLL extern size_t l_byteaGetSize ( L_BYTEA *ba ); -LEPT_DLL extern l_uint8 * l_byteaGetData ( L_BYTEA *ba, size_t *psize ); -LEPT_DLL extern l_uint8 * l_byteaCopyData ( L_BYTEA *ba, size_t *psize ); -LEPT_DLL extern l_ok l_byteaAppendData ( L_BYTEA *ba, const l_uint8 *newdata, size_t newbytes ); -LEPT_DLL extern l_ok l_byteaAppendString ( L_BYTEA *ba, const char *str ); -LEPT_DLL extern l_ok l_byteaJoin ( L_BYTEA *ba1, L_BYTEA **pba2 ); -LEPT_DLL extern l_ok l_byteaSplit ( L_BYTEA *ba1, size_t splitloc, L_BYTEA **pba2 ); -LEPT_DLL extern l_ok l_byteaFindEachSequence ( L_BYTEA *ba, const l_uint8 *sequence, size_t seqlen, L_DNA **pda ); -LEPT_DLL extern l_ok l_byteaWrite ( const char *fname, L_BYTEA *ba, size_t startloc, size_t nbytes ); -LEPT_DLL extern l_ok l_byteaWriteStream ( FILE *fp, L_BYTEA *ba, size_t startloc, size_t nbytes ); -LEPT_DLL extern CCBORDA * ccbaCreate ( PIX *pixs, l_int32 n ); -LEPT_DLL extern void ccbaDestroy ( CCBORDA **pccba ); -LEPT_DLL extern CCBORD * ccbCreate ( PIX *pixs ); -LEPT_DLL extern void ccbDestroy ( CCBORD **pccb ); -LEPT_DLL extern l_ok ccbaAddCcb ( CCBORDA *ccba, CCBORD *ccb ); -LEPT_DLL extern l_int32 ccbaGetCount ( CCBORDA *ccba ); -LEPT_DLL extern CCBORD * ccbaGetCcb ( CCBORDA *ccba, l_int32 index ); -LEPT_DLL extern CCBORDA * pixGetAllCCBorders ( PIX *pixs ); -LEPT_DLL extern PTAA * pixGetOuterBordersPtaa ( PIX *pixs ); -LEPT_DLL extern l_ok pixGetOuterBorder ( CCBORD *ccb, PIX *pixs, BOX *box ); -LEPT_DLL extern l_ok ccbaGenerateGlobalLocs ( CCBORDA *ccba ); -LEPT_DLL extern l_ok ccbaGenerateStepChains ( CCBORDA *ccba ); -LEPT_DLL extern l_ok ccbaStepChainsToPixCoords ( CCBORDA *ccba, l_int32 coordtype ); -LEPT_DLL extern l_ok ccbaGenerateSPGlobalLocs ( CCBORDA *ccba, l_int32 ptsflag ); -LEPT_DLL extern l_ok ccbaGenerateSinglePath ( CCBORDA *ccba ); -LEPT_DLL extern PTA * getCutPathForHole ( PIX *pix, PTA *pta, BOX *boxinner, l_int32 *pdir, l_int32 *plen ); -LEPT_DLL extern PIX * ccbaDisplayBorder ( CCBORDA *ccba ); -LEPT_DLL extern PIX * ccbaDisplaySPBorder ( CCBORDA *ccba ); -LEPT_DLL extern PIX * ccbaDisplayImage1 ( CCBORDA *ccba ); -LEPT_DLL extern PIX * ccbaDisplayImage2 ( CCBORDA *ccba ); -LEPT_DLL extern l_ok ccbaWrite ( const char *filename, CCBORDA *ccba ); -LEPT_DLL extern l_ok ccbaWriteStream ( FILE *fp, CCBORDA *ccba ); -LEPT_DLL extern CCBORDA * ccbaRead ( const char *filename ); -LEPT_DLL extern CCBORDA * ccbaReadStream ( FILE *fp ); -LEPT_DLL extern l_ok ccbaWriteSVG ( const char *filename, CCBORDA *ccba ); -LEPT_DLL extern char * ccbaWriteSVGString ( const char *filename, CCBORDA *ccba ); -LEPT_DLL extern PIXA * pixaThinConnected ( PIXA *pixas, l_int32 type, l_int32 connectivity, l_int32 maxiters ); -LEPT_DLL extern PIX * pixThinConnected ( PIX *pixs, l_int32 type, l_int32 connectivity, l_int32 maxiters ); -LEPT_DLL extern PIX * pixThinConnectedBySet ( PIX *pixs, l_int32 type, SELA *sela, l_int32 maxiters ); -LEPT_DLL extern SELA * selaMakeThinSets ( l_int32 index, l_int32 debug ); -LEPT_DLL extern l_ok pixFindCheckerboardCorners ( PIX *pixs, l_int32 size, l_int32 dilation, l_int32 nsels, PIX **ppix_corners, PTA **ppta_corners, PIXA *pixadb ); -LEPT_DLL extern SELA * makeCheckerboardCornerSela ( l_int32 size, l_int32 dilation, l_int32 nsels, PIXA *pixadb ); -LEPT_DLL extern l_ok 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 ); -LEPT_DLL extern l_ok jbRankHaus ( const char *dirin, l_int32 size, l_float32 rank, l_int32 components, const char *rootname, l_int32 firstpage, l_int32 npages, l_int32 renderflag ); -LEPT_DLL extern JBCLASSER * jbWordsInTextlines ( const char *dirin, l_int32 reduction, l_int32 maxwidth, l_int32 maxheight, l_float32 thresh, l_float32 weight, NUMA **pnatl, l_int32 firstpage, l_int32 npages ); -LEPT_DLL extern l_ok pixGetWordsInTextlines ( PIX *pixs, l_int32 minwidth, l_int32 minheight, l_int32 maxwidth, l_int32 maxheight, BOXA **pboxad, PIXA **ppixad, NUMA **pnai ); -LEPT_DLL extern l_ok pixGetWordBoxesInTextlines ( PIX *pixs, l_int32 minwidth, l_int32 minheight, l_int32 maxwidth, l_int32 maxheight, BOXA **pboxad, NUMA **pnai ); -LEPT_DLL extern l_ok pixFindWordAndCharacterBoxes ( PIX *pixs, BOX *boxs, l_int32 thresh, BOXA **pboxaw, BOXAA **pboxaac, const char *debugdir ); -LEPT_DLL extern NUMAA * boxaExtractSortedPattern ( BOXA *boxa, NUMA *na ); -LEPT_DLL extern l_ok numaaCompareImagesByBoxes ( NUMAA *naa1, NUMAA *naa2, l_int32 nperline, l_int32 nreq, l_int32 maxshiftx, l_int32 maxshifty, l_int32 delx, l_int32 dely, l_int32 *psame, l_int32 debugflag ); -LEPT_DLL extern l_ok pixColorContent ( PIX *pixs, l_int32 rref, l_int32 gref, l_int32 bref, l_int32 mingray, PIX **ppixr, PIX **ppixg, PIX **ppixb ); -LEPT_DLL extern PIX * pixColorMagnitude ( PIX *pixs, l_int32 rref, l_int32 gref, l_int32 bref, l_int32 type ); -LEPT_DLL extern PIX * pixMaskOverColorPixels ( PIX *pixs, l_int32 threshdiff, l_int32 mindist ); -LEPT_DLL extern PIX * pixMaskOverGrayPixels ( PIX *pixs, l_int32 maxlimit, l_int32 satlimit ); -LEPT_DLL extern PIX * pixMaskOverColorRange ( PIX *pixs, l_int32 rmin, l_int32 rmax, l_int32 gmin, l_int32 gmax, l_int32 bmin, l_int32 bmax ); -LEPT_DLL extern l_ok pixColorFraction ( PIX *pixs, l_int32 darkthresh, l_int32 lightthresh, l_int32 diffthresh, l_int32 factor, l_float32 *ppixfract, l_float32 *pcolorfract ); -LEPT_DLL extern l_ok pixFindColorRegions ( PIX *pixs, PIX *pixm, l_int32 factor, l_int32 lightthresh, l_int32 darkthresh, l_int32 mindiff, l_int32 colordiff, l_float32 edgefract, l_float32 *pcolorfract, PIX **pcolormask1, PIX **pcolormask2, PIXA *pixadb ); -LEPT_DLL extern l_ok pixNumSignificantGrayColors ( PIX *pixs, l_int32 darkthresh, l_int32 lightthresh, l_float32 minfract, l_int32 factor, l_int32 *pncolors ); -LEPT_DLL extern l_ok pixColorsForQuantization ( PIX *pixs, l_int32 thresh, l_int32 *pncolors, l_int32 *piscolor, l_int32 debug ); -LEPT_DLL extern l_ok pixNumColors ( PIX *pixs, l_int32 factor, l_int32 *pncolors ); -LEPT_DLL extern PIX * pixConvertRGBToCmap ( PIX *pixs ); -LEPT_DLL extern l_ok pixGetMostPopulatedColors ( PIX *pixs, l_int32 sigbits, l_int32 factor, l_int32 ncolors, l_uint32 **parray, PIXCMAP **pcmap ); -LEPT_DLL extern PIX * pixSimpleColorQuantize ( PIX *pixs, l_int32 sigbits, l_int32 factor, l_int32 ncolors ); -LEPT_DLL extern NUMA * pixGetRGBHistogram ( PIX *pixs, l_int32 sigbits, l_int32 factor ); -LEPT_DLL extern l_ok makeRGBIndexTables ( l_uint32 **prtab, l_uint32 **pgtab, l_uint32 **pbtab, l_int32 sigbits ); -LEPT_DLL extern l_ok getRGBFromIndex ( l_uint32 index, l_int32 sigbits, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); -LEPT_DLL extern l_ok pixHasHighlightRed ( PIX *pixs, l_int32 factor, l_float32 fract, l_float32 fthresh, l_int32 *phasred, l_float32 *pratio, PIX **ppixdb ); -LEPT_DLL extern PIX * pixColorGrayRegions ( PIX *pixs, BOXA *boxa, l_int32 type, l_int32 thresh, l_int32 rval, l_int32 gval, l_int32 bval ); -LEPT_DLL extern l_ok pixColorGray ( PIX *pixs, BOX *box, l_int32 type, l_int32 thresh, l_int32 rval, l_int32 gval, l_int32 bval ); -LEPT_DLL extern PIX * pixColorGrayMasked ( PIX *pixs, PIX *pixm, l_int32 type, l_int32 thresh, l_int32 rval, l_int32 gval, l_int32 bval ); -LEPT_DLL extern PIX * pixSnapColor ( PIX *pixd, PIX *pixs, l_uint32 srcval, l_uint32 dstval, l_int32 diff ); -LEPT_DLL extern PIX * pixSnapColorCmap ( PIX *pixd, PIX *pixs, l_uint32 srcval, l_uint32 dstval, l_int32 diff ); -LEPT_DLL extern PIX * pixLinearMapToTargetColor ( PIX *pixd, PIX *pixs, l_uint32 srcval, l_uint32 dstval ); -LEPT_DLL extern l_ok pixelLinearMapToTargetColor ( l_uint32 scolor, l_uint32 srcmap, l_uint32 dstmap, l_uint32 *pdcolor ); -LEPT_DLL extern PIX * pixShiftByComponent ( PIX *pixd, PIX *pixs, l_uint32 srcval, l_uint32 dstval ); -LEPT_DLL extern l_ok pixelShiftByComponent ( l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 srcval, l_uint32 dstval, l_uint32 *ppixel ); -LEPT_DLL extern l_ok pixelFractionalShift ( l_int32 rval, l_int32 gval, l_int32 bval, l_float32 fraction, l_uint32 *ppixel ); -LEPT_DLL extern PIXCMAP * pixcmapCreate ( l_int32 depth ); -LEPT_DLL extern PIXCMAP * pixcmapCreateRandom ( l_int32 depth, l_int32 hasblack, l_int32 haswhite ); -LEPT_DLL extern PIXCMAP * pixcmapCreateLinear ( l_int32 d, l_int32 nlevels ); -LEPT_DLL extern PIXCMAP * pixcmapCopy ( const PIXCMAP *cmaps ); -LEPT_DLL extern void pixcmapDestroy ( PIXCMAP **pcmap ); -LEPT_DLL extern l_ok pixcmapIsValid ( const PIXCMAP *cmap, l_int32 *pvalid ); -LEPT_DLL extern l_ok pixcmapAddColor ( PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval ); -LEPT_DLL extern l_ok pixcmapAddRGBA ( PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 aval ); -LEPT_DLL extern l_ok pixcmapAddNewColor ( PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pindex ); -LEPT_DLL extern l_ok pixcmapAddNearestColor ( PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pindex ); -LEPT_DLL extern l_ok pixcmapUsableColor ( PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pusable ); -LEPT_DLL extern l_ok pixcmapAddBlackOrWhite ( PIXCMAP *cmap, l_int32 color, l_int32 *pindex ); -LEPT_DLL extern l_ok pixcmapSetBlackAndWhite ( PIXCMAP *cmap, l_int32 setblack, l_int32 setwhite ); -LEPT_DLL extern l_int32 pixcmapGetCount ( const PIXCMAP *cmap ); -LEPT_DLL extern l_int32 pixcmapGetFreeCount ( PIXCMAP *cmap ); -LEPT_DLL extern l_int32 pixcmapGetDepth ( PIXCMAP *cmap ); -LEPT_DLL extern l_ok pixcmapGetMinDepth ( PIXCMAP *cmap, l_int32 *pmindepth ); -LEPT_DLL extern l_ok pixcmapClear ( PIXCMAP *cmap ); -LEPT_DLL extern l_ok pixcmapGetColor ( PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); -LEPT_DLL extern l_ok pixcmapGetColor32 ( PIXCMAP *cmap, l_int32 index, l_uint32 *pval32 ); -LEPT_DLL extern l_ok pixcmapGetRGBA ( PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval, l_int32 *paval ); -LEPT_DLL extern l_ok pixcmapGetRGBA32 ( PIXCMAP *cmap, l_int32 index, l_uint32 *pval32 ); -LEPT_DLL extern l_ok pixcmapResetColor ( PIXCMAP *cmap, l_int32 index, l_int32 rval, l_int32 gval, l_int32 bval ); -LEPT_DLL extern l_ok pixcmapSetAlpha ( PIXCMAP *cmap, l_int32 index, l_int32 aval ); -LEPT_DLL extern l_int32 pixcmapGetIndex ( PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pindex ); -LEPT_DLL extern l_ok pixcmapHasColor ( PIXCMAP *cmap, l_int32 *pcolor ); -LEPT_DLL extern l_ok pixcmapIsOpaque ( PIXCMAP *cmap, l_int32 *popaque ); -LEPT_DLL extern l_ok pixcmapIsBlackAndWhite ( PIXCMAP *cmap, l_int32 *pblackwhite ); -LEPT_DLL extern l_ok pixcmapCountGrayColors ( PIXCMAP *cmap, l_int32 *pngray ); -LEPT_DLL extern l_ok pixcmapGetRankIntensity ( PIXCMAP *cmap, l_float32 rankval, l_int32 *pindex ); -LEPT_DLL extern l_ok pixcmapGetNearestIndex ( PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pindex ); -LEPT_DLL extern l_ok pixcmapGetNearestGrayIndex ( PIXCMAP *cmap, l_int32 val, l_int32 *pindex ); -LEPT_DLL extern l_ok pixcmapGetDistanceToColor ( PIXCMAP *cmap, l_int32 index, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pdist ); -LEPT_DLL extern l_ok pixcmapGetRangeValues ( PIXCMAP *cmap, l_int32 select, l_int32 *pminval, l_int32 *pmaxval, l_int32 *pminindex, l_int32 *pmaxindex ); -LEPT_DLL extern PIXCMAP * pixcmapGrayToColor ( l_uint32 color ); -LEPT_DLL extern PIXCMAP * pixcmapColorToGray ( PIXCMAP *cmaps, l_float32 rwt, l_float32 gwt, l_float32 bwt ); -LEPT_DLL extern PIXCMAP * pixcmapConvertTo4 ( PIXCMAP *cmaps ); -LEPT_DLL extern PIXCMAP * pixcmapConvertTo8 ( PIXCMAP *cmaps ); -LEPT_DLL extern PIXCMAP * pixcmapRead ( const char *filename ); -LEPT_DLL extern PIXCMAP * pixcmapReadStream ( FILE *fp ); -LEPT_DLL extern PIXCMAP * pixcmapReadMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok pixcmapWrite ( const char *filename, const PIXCMAP *cmap ); -LEPT_DLL extern l_ok pixcmapWriteStream ( FILE *fp, const PIXCMAP *cmap ); -LEPT_DLL extern l_ok pixcmapWriteMem ( l_uint8 **pdata, size_t *psize, const PIXCMAP *cmap ); -LEPT_DLL extern l_ok pixcmapToArrays ( const PIXCMAP *cmap, l_int32 **prmap, l_int32 **pgmap, l_int32 **pbmap, l_int32 **pamap ); -LEPT_DLL extern l_ok pixcmapToRGBTable ( PIXCMAP *cmap, l_uint32 **ptab, l_int32 *pncolors ); -LEPT_DLL extern l_ok pixcmapSerializeToMemory ( PIXCMAP *cmap, l_int32 cpc, l_int32 *pncolors, l_uint8 **pdata ); -LEPT_DLL extern PIXCMAP * pixcmapDeserializeFromMemory ( l_uint8 *data, l_int32 cpc, l_int32 ncolors ); -LEPT_DLL extern char * pixcmapConvertToHex ( l_uint8 *data, l_int32 ncolors ); -LEPT_DLL extern l_ok pixcmapGammaTRC ( PIXCMAP *cmap, l_float32 gamma, l_int32 minval, l_int32 maxval ); -LEPT_DLL extern l_ok pixcmapContrastTRC ( PIXCMAP *cmap, l_float32 factor ); -LEPT_DLL extern l_ok pixcmapShiftIntensity ( PIXCMAP *cmap, l_float32 fraction ); -LEPT_DLL extern l_ok pixcmapShiftByComponent ( PIXCMAP *cmap, l_uint32 srcval, l_uint32 dstval ); -LEPT_DLL extern PIX * pixColorMorph ( PIX *pixs, l_int32 type, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixOctreeColorQuant ( PIX *pixs, l_int32 colors, l_int32 ditherflag ); -LEPT_DLL extern PIX * pixOctreeColorQuantGeneral ( PIX *pixs, l_int32 colors, l_int32 ditherflag, l_float32 validthresh, l_float32 colorthresh ); -LEPT_DLL extern l_ok makeRGBToIndexTables ( l_int32 cqlevels, l_uint32 **prtab, l_uint32 **pgtab, l_uint32 **pbtab ); -LEPT_DLL extern void getOctcubeIndexFromRGB ( l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 *rtab, l_uint32 *gtab, l_uint32 *btab, l_uint32 *pindex ); -LEPT_DLL extern PIX * pixOctreeQuantByPopulation ( PIX *pixs, l_int32 level, l_int32 ditherflag ); -LEPT_DLL extern PIX * pixOctreeQuantNumColors ( PIX *pixs, l_int32 maxcolors, l_int32 subsample ); -LEPT_DLL extern PIX * pixOctcubeQuantMixedWithGray ( PIX *pixs, l_int32 depth, l_int32 graylevels, l_int32 delta ); -LEPT_DLL extern PIX * pixFixedOctcubeQuant256 ( PIX *pixs, l_int32 ditherflag ); -LEPT_DLL extern PIX * pixFewColorsOctcubeQuant1 ( PIX *pixs, l_int32 level ); -LEPT_DLL extern PIX * pixFewColorsOctcubeQuant2 ( PIX *pixs, l_int32 level, NUMA *na, l_int32 ncolors, l_int32 *pnerrors ); -LEPT_DLL extern PIX * pixFewColorsOctcubeQuantMixed ( PIX *pixs, l_int32 level, l_int32 darkthresh, l_int32 lightthresh, l_int32 diffthresh, l_float32 minfract, l_int32 maxspan ); -LEPT_DLL extern PIX * pixFixedOctcubeQuantGenRGB ( PIX *pixs, l_int32 level ); -LEPT_DLL extern PIX * pixQuantFromCmap ( PIX *pixs, PIXCMAP *cmap, l_int32 mindepth, l_int32 level, l_int32 metric ); -LEPT_DLL extern PIX * pixOctcubeQuantFromCmap ( PIX *pixs, PIXCMAP *cmap, l_int32 mindepth, l_int32 level, l_int32 metric ); -LEPT_DLL extern NUMA * pixOctcubeHistogram ( PIX *pixs, l_int32 level, l_int32 *pncolors ); -LEPT_DLL extern l_int32 * pixcmapToOctcubeLUT ( PIXCMAP *cmap, l_int32 level, l_int32 metric ); -LEPT_DLL extern l_ok pixRemoveUnusedColors ( PIX *pixs ); -LEPT_DLL extern l_ok pixNumberOccupiedOctcubes ( PIX *pix, l_int32 level, l_int32 mincount, l_float32 minfract, l_int32 *pncolors ); -LEPT_DLL extern PIX * pixMedianCutQuant ( PIX *pixs, l_int32 ditherflag ); -LEPT_DLL extern PIX * pixMedianCutQuantGeneral ( PIX *pixs, l_int32 ditherflag, l_int32 outdepth, l_int32 maxcolors, l_int32 sigbits, l_int32 maxsub, l_int32 checkbw ); -LEPT_DLL extern PIX * pixMedianCutQuantMixed ( PIX *pixs, l_int32 ncolor, l_int32 ngray, l_int32 darkthresh, l_int32 lightthresh, l_int32 diffthresh ); -LEPT_DLL extern PIX * pixFewColorsMedianCutQuantMixed ( PIX *pixs, l_int32 ncolor, l_int32 ngray, l_int32 maxncolors, l_int32 darkthresh, l_int32 lightthresh, l_int32 diffthresh ); -LEPT_DLL extern l_int32 * pixMedianCutHisto ( PIX *pixs, l_int32 sigbits, l_int32 subsample ); -LEPT_DLL extern PIX * pixColorSegment ( PIX *pixs, l_int32 maxdist, l_int32 maxcolors, l_int32 selsize, l_int32 finalcolors, l_int32 debugflag ); -LEPT_DLL extern PIX * pixColorSegmentCluster ( PIX *pixs, l_int32 maxdist, l_int32 maxcolors, l_int32 debugflag ); -LEPT_DLL extern l_ok pixAssignToNearestColor ( PIX *pixd, PIX *pixs, PIX *pixm, l_int32 level, l_int32 *countarray ); -LEPT_DLL extern l_ok pixColorSegmentClean ( PIX *pixs, l_int32 selsize, l_int32 *countarray ); -LEPT_DLL extern l_ok pixColorSegmentRemoveColors ( PIX *pixd, PIX *pixs, l_int32 finalcolors ); -LEPT_DLL extern PIX * pixConvertRGBToHSV ( PIX *pixd, PIX *pixs ); -LEPT_DLL extern PIX * pixConvertHSVToRGB ( PIX *pixd, PIX *pixs ); -LEPT_DLL extern l_ok convertRGBToHSV ( l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *phval, l_int32 *psval, l_int32 *pvval ); -LEPT_DLL extern l_ok convertHSVToRGB ( l_int32 hval, l_int32 sval, l_int32 vval, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); -LEPT_DLL extern l_ok pixcmapConvertRGBToHSV ( PIXCMAP *cmap ); -LEPT_DLL extern l_ok pixcmapConvertHSVToRGB ( PIXCMAP *cmap ); -LEPT_DLL extern PIX * pixConvertRGBToHue ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvertRGBToSaturation ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvertRGBToValue ( PIX *pixs ); -LEPT_DLL extern PIX * pixMakeRangeMaskHS ( PIX *pixs, l_int32 huecenter, l_int32 huehw, l_int32 satcenter, l_int32 sathw, l_int32 regionflag ); -LEPT_DLL extern PIX * pixMakeRangeMaskHV ( PIX *pixs, l_int32 huecenter, l_int32 huehw, l_int32 valcenter, l_int32 valhw, l_int32 regionflag ); -LEPT_DLL extern PIX * pixMakeRangeMaskSV ( PIX *pixs, l_int32 satcenter, l_int32 sathw, l_int32 valcenter, l_int32 valhw, l_int32 regionflag ); -LEPT_DLL extern PIX * pixMakeHistoHS ( PIX *pixs, l_int32 factor, NUMA **pnahue, NUMA **pnasat ); -LEPT_DLL extern PIX * pixMakeHistoHV ( PIX *pixs, l_int32 factor, NUMA **pnahue, NUMA **pnaval ); -LEPT_DLL extern PIX * pixMakeHistoSV ( PIX *pixs, l_int32 factor, NUMA **pnasat, NUMA **pnaval ); -LEPT_DLL extern l_ok pixFindHistoPeaksHSV ( PIX *pixs, l_int32 type, l_int32 width, l_int32 height, l_int32 npeaks, l_float32 erasefactor, PTA **ppta, NUMA **pnatot, PIXA **ppixa ); -LEPT_DLL extern PIX * displayHSVColorRange ( l_int32 hval, l_int32 sval, l_int32 vval, l_int32 huehw, l_int32 sathw, l_int32 nsamp, l_int32 factor ); -LEPT_DLL extern PIX * pixConvertRGBToYUV ( PIX *pixd, PIX *pixs ); -LEPT_DLL extern PIX * pixConvertYUVToRGB ( PIX *pixd, PIX *pixs ); -LEPT_DLL extern l_ok convertRGBToYUV ( l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pyval, l_int32 *puval, l_int32 *pvval ); -LEPT_DLL extern l_ok convertYUVToRGB ( l_int32 yval, l_int32 uval, l_int32 vval, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); -LEPT_DLL extern l_ok pixcmapConvertRGBToYUV ( PIXCMAP *cmap ); -LEPT_DLL extern l_ok pixcmapConvertYUVToRGB ( PIXCMAP *cmap ); -LEPT_DLL extern FPIXA * pixConvertRGBToXYZ ( PIX *pixs ); -LEPT_DLL extern PIX * fpixaConvertXYZToRGB ( FPIXA *fpixa ); -LEPT_DLL extern l_ok convertRGBToXYZ ( l_int32 rval, l_int32 gval, l_int32 bval, l_float32 *pfxval, l_float32 *pfyval, l_float32 *pfzval ); -LEPT_DLL extern l_ok convertXYZToRGB ( l_float32 fxval, l_float32 fyval, l_float32 fzval, l_int32 blackout, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); -LEPT_DLL extern FPIXA * fpixaConvertXYZToLAB ( FPIXA *fpixas ); -LEPT_DLL extern FPIXA * fpixaConvertLABToXYZ ( FPIXA *fpixas ); -LEPT_DLL extern l_ok convertXYZToLAB ( l_float32 xval, l_float32 yval, l_float32 zval, l_float32 *plval, l_float32 *paval, l_float32 *pbval ); -LEPT_DLL extern l_ok convertLABToXYZ ( l_float32 lval, l_float32 aval, l_float32 bval, l_float32 *pxval, l_float32 *pyval, l_float32 *pzval ); -LEPT_DLL extern FPIXA * pixConvertRGBToLAB ( PIX *pixs ); -LEPT_DLL extern PIX * fpixaConvertLABToRGB ( FPIXA *fpixa ); -LEPT_DLL extern l_ok convertRGBToLAB ( l_int32 rval, l_int32 gval, l_int32 bval, l_float32 *pflval, l_float32 *pfaval, l_float32 *pfbval ); -LEPT_DLL extern l_ok convertLABToRGB ( l_float32 flval, l_float32 faval, l_float32 fbval, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); -LEPT_DLL extern l_ok pixEqual ( PIX *pix1, PIX *pix2, l_int32 *psame ); -LEPT_DLL extern l_ok pixEqualWithAlpha ( PIX *pix1, PIX *pix2, l_int32 use_alpha, l_int32 *psame ); -LEPT_DLL extern l_ok pixEqualWithCmap ( PIX *pix1, PIX *pix2, l_int32 *psame ); -LEPT_DLL extern l_ok cmapEqual ( PIXCMAP *cmap1, PIXCMAP *cmap2, l_int32 ncomps, l_int32 *psame ); -LEPT_DLL extern l_ok pixUsesCmapColor ( PIX *pixs, l_int32 *pcolor ); -LEPT_DLL extern l_ok pixCorrelationBinary ( PIX *pix1, PIX *pix2, l_float32 *pval ); -LEPT_DLL extern PIX * pixDisplayDiffBinary ( PIX *pix1, PIX *pix2 ); -LEPT_DLL extern l_ok pixCompareBinary ( PIX *pix1, PIX *pix2, l_int32 comptype, l_float32 *pfract, PIX **ppixdiff ); -LEPT_DLL extern l_ok pixCompareGrayOrRGB ( PIX *pix1, PIX *pix2, l_int32 comptype, l_int32 plottype, l_int32 *psame, l_float32 *pdiff, l_float32 *prmsdiff, PIX **ppixdiff ); -LEPT_DLL extern l_ok pixCompareGray ( PIX *pix1, PIX *pix2, l_int32 comptype, l_int32 plottype, l_int32 *psame, l_float32 *pdiff, l_float32 *prmsdiff, PIX **ppixdiff ); -LEPT_DLL extern l_ok pixCompareRGB ( PIX *pix1, PIX *pix2, l_int32 comptype, l_int32 plottype, l_int32 *psame, l_float32 *pdiff, l_float32 *prmsdiff, PIX **ppixdiff ); -LEPT_DLL extern l_ok pixCompareTiled ( PIX *pix1, PIX *pix2, l_int32 sx, l_int32 sy, l_int32 type, PIX **ppixdiff ); -LEPT_DLL extern NUMA * pixCompareRankDifference ( PIX *pix1, PIX *pix2, l_int32 factor ); -LEPT_DLL extern l_ok pixTestForSimilarity ( PIX *pix1, PIX *pix2, l_int32 factor, l_int32 mindiff, l_float32 maxfract, l_float32 maxave, l_int32 *psimilar, l_int32 details ); -LEPT_DLL extern l_ok pixGetDifferenceStats ( PIX *pix1, PIX *pix2, l_int32 factor, l_int32 mindiff, l_float32 *pfractdiff, l_float32 *pavediff, l_int32 details ); -LEPT_DLL extern NUMA * pixGetDifferenceHistogram ( PIX *pix1, PIX *pix2, l_int32 factor ); -LEPT_DLL extern l_ok pixGetPerceptualDiff ( PIX *pixs1, PIX *pixs2, l_int32 sampling, l_int32 dilation, l_int32 mindiff, l_float32 *pfract, PIX **ppixdiff1, PIX **ppixdiff2 ); -LEPT_DLL extern l_ok pixGetPSNR ( PIX *pix1, PIX *pix2, l_int32 factor, l_float32 *ppsnr ); -LEPT_DLL extern l_ok pixaComparePhotoRegionsByHisto ( PIXA *pixa, l_float32 minratio, l_float32 textthresh, l_int32 factor, l_int32 n, l_float32 simthresh, NUMA **pnai, l_float32 **pscores, PIX **ppixd, l_int32 debug ); -LEPT_DLL extern l_ok pixComparePhotoRegionsByHisto ( PIX *pix1, PIX *pix2, BOX *box1, BOX *box2, l_float32 minratio, l_int32 factor, l_int32 n, l_float32 *pscore, l_int32 debugflag ); -LEPT_DLL extern l_ok pixGenPhotoHistos ( PIX *pixs, BOX *box, l_int32 factor, l_float32 thresh, l_int32 n, NUMAA **pnaa, l_int32 *pw, l_int32 *ph, l_int32 debugindex ); -LEPT_DLL extern PIX * pixPadToCenterCentroid ( PIX *pixs, l_int32 factor ); -LEPT_DLL extern l_ok pixCentroid8 ( PIX *pixs, l_int32 factor, l_float32 *pcx, l_float32 *pcy ); -LEPT_DLL extern l_ok pixDecideIfPhotoImage ( PIX *pix, l_int32 factor, l_float32 thresh, l_int32 n, NUMAA **pnaa, PIXA *pixadebug ); -LEPT_DLL extern l_ok compareTilesByHisto ( NUMAA *naa1, NUMAA *naa2, l_float32 minratio, l_int32 w1, l_int32 h1, l_int32 w2, l_int32 h2, l_float32 *pscore, PIXA *pixadebug ); -LEPT_DLL extern l_ok pixCompareGrayByHisto ( PIX *pix1, PIX *pix2, BOX *box1, BOX *box2, l_float32 minratio, l_int32 maxgray, l_int32 factor, l_int32 n, l_float32 *pscore, l_int32 debugflag ); -LEPT_DLL extern l_ok pixCropAlignedToCentroid ( PIX *pix1, PIX *pix2, l_int32 factor, BOX **pbox1, BOX **pbox2 ); -LEPT_DLL extern l_uint8 * l_compressGrayHistograms ( NUMAA *naa, l_int32 w, l_int32 h, size_t *psize ); -LEPT_DLL extern NUMAA * l_uncompressGrayHistograms ( l_uint8 *bytea, size_t size, l_int32 *pw, l_int32 *ph ); -LEPT_DLL extern l_ok pixCompareWithTranslation ( PIX *pix1, PIX *pix2, l_int32 thresh, l_int32 *pdelx, l_int32 *pdely, l_float32 *pscore, l_int32 debugflag ); -LEPT_DLL extern l_ok pixBestCorrelation ( PIX *pix1, PIX *pix2, l_int32 area1, l_int32 area2, l_int32 etransx, l_int32 etransy, l_int32 maxshift, l_int32 *tab8, l_int32 *pdelx, l_int32 *pdely, l_float32 *pscore, l_int32 debugflag ); -LEPT_DLL extern BOXA * pixConnComp ( PIX *pixs, PIXA **ppixa, l_int32 connectivity ); -LEPT_DLL extern BOXA * pixConnCompPixa ( PIX *pixs, PIXA **ppixa, l_int32 connectivity ); -LEPT_DLL extern BOXA * pixConnCompBB ( PIX *pixs, l_int32 connectivity ); -LEPT_DLL extern l_ok pixCountConnComp ( PIX *pixs, l_int32 connectivity, l_int32 *pcount ); -LEPT_DLL extern l_int32 nextOnPixelInRaster ( PIX *pixs, l_int32 xstart, l_int32 ystart, l_int32 *px, l_int32 *py ); -LEPT_DLL extern BOX * pixSeedfillBB ( PIX *pixs, L_STACK *stack, l_int32 x, l_int32 y, l_int32 connectivity ); -LEPT_DLL extern BOX * pixSeedfill4BB ( PIX *pixs, L_STACK *stack, l_int32 x, l_int32 y ); -LEPT_DLL extern BOX * pixSeedfill8BB ( PIX *pixs, L_STACK *stack, l_int32 x, l_int32 y ); -LEPT_DLL extern l_ok pixSeedfill ( PIX *pixs, L_STACK *stack, l_int32 x, l_int32 y, l_int32 connectivity ); -LEPT_DLL extern l_ok pixSeedfill4 ( PIX *pixs, L_STACK *stack, l_int32 x, l_int32 y ); -LEPT_DLL extern l_ok pixSeedfill8 ( PIX *pixs, L_STACK *stack, l_int32 x, l_int32 y ); -LEPT_DLL extern l_ok convertFilesTo1bpp ( const char *dirin, const char *substr, l_int32 upscaling, l_int32 thresh, l_int32 firstpage, l_int32 npages, const char *dirout, l_int32 outformat ); -LEPT_DLL extern PIX * pixBlockconv ( PIX *pix, l_int32 wc, l_int32 hc ); -LEPT_DLL extern PIX * pixBlockconvGray ( PIX *pixs, PIX *pixacc, l_int32 wc, l_int32 hc ); -LEPT_DLL extern PIX * pixBlockconvAccum ( PIX *pixs ); -LEPT_DLL extern PIX * pixBlockconvGrayUnnormalized ( PIX *pixs, l_int32 wc, l_int32 hc ); -LEPT_DLL extern PIX * pixBlockconvTiled ( PIX *pix, l_int32 wc, l_int32 hc, l_int32 nx, l_int32 ny ); -LEPT_DLL extern PIX * pixBlockconvGrayTile ( PIX *pixs, PIX *pixacc, l_int32 wc, l_int32 hc ); -LEPT_DLL extern l_ok pixWindowedStats ( PIX *pixs, l_int32 wc, l_int32 hc, l_int32 hasborder, PIX **ppixm, PIX **ppixms, FPIX **pfpixv, FPIX **pfpixrv ); -LEPT_DLL extern PIX * pixWindowedMean ( PIX *pixs, l_int32 wc, l_int32 hc, l_int32 hasborder, l_int32 normflag ); -LEPT_DLL extern PIX * pixWindowedMeanSquare ( PIX *pixs, l_int32 wc, l_int32 hc, l_int32 hasborder ); -LEPT_DLL extern l_ok pixWindowedVariance ( PIX *pixm, PIX *pixms, FPIX **pfpixv, FPIX **pfpixrv ); -LEPT_DLL extern DPIX * pixMeanSquareAccum ( PIX *pixs ); -LEPT_DLL extern PIX * pixBlockrank ( PIX *pixs, PIX *pixacc, l_int32 wc, l_int32 hc, l_float32 rank ); -LEPT_DLL extern PIX * pixBlocksum ( PIX *pixs, PIX *pixacc, l_int32 wc, l_int32 hc ); -LEPT_DLL extern PIX * pixCensusTransform ( PIX *pixs, l_int32 halfsize, PIX *pixacc ); -LEPT_DLL extern PIX * pixConvolve ( PIX *pixs, L_KERNEL *kel, l_int32 outdepth, l_int32 normflag ); -LEPT_DLL extern PIX * pixConvolveSep ( PIX *pixs, L_KERNEL *kelx, L_KERNEL *kely, l_int32 outdepth, l_int32 normflag ); -LEPT_DLL extern PIX * pixConvolveRGB ( PIX *pixs, L_KERNEL *kel ); -LEPT_DLL extern PIX * pixConvolveRGBSep ( PIX *pixs, L_KERNEL *kelx, L_KERNEL *kely ); -LEPT_DLL extern FPIX * fpixConvolve ( FPIX *fpixs, L_KERNEL *kel, l_int32 normflag ); -LEPT_DLL extern FPIX * fpixConvolveSep ( FPIX *fpixs, L_KERNEL *kelx, L_KERNEL *kely, l_int32 normflag ); -LEPT_DLL extern PIX * pixConvolveWithBias ( PIX *pixs, L_KERNEL *kel1, L_KERNEL *kel2, l_int32 force8, l_int32 *pbias ); -LEPT_DLL extern void l_setConvolveSampling ( l_int32 xfact, l_int32 yfact ); -LEPT_DLL extern PIX * pixAddGaussianNoise ( PIX *pixs, l_float32 stdev ); -LEPT_DLL extern l_float32 gaussDistribSampling ( void ); -LEPT_DLL extern l_ok pixCorrelationScore ( PIX *pix1, PIX *pix2, l_int32 area1, l_int32 area2, l_float32 delx, l_float32 dely, l_int32 maxdiffw, l_int32 maxdiffh, l_int32 *tab, l_float32 *pscore ); -LEPT_DLL extern l_int32 pixCorrelationScoreThresholded ( PIX *pix1, PIX *pix2, l_int32 area1, l_int32 area2, l_float32 delx, l_float32 dely, l_int32 maxdiffw, l_int32 maxdiffh, l_int32 *tab, l_int32 *downcount, l_float32 score_threshold ); -LEPT_DLL extern l_ok pixCorrelationScoreSimple ( PIX *pix1, PIX *pix2, l_int32 area1, l_int32 area2, l_float32 delx, l_float32 dely, l_int32 maxdiffw, l_int32 maxdiffh, l_int32 *tab, l_float32 *pscore ); -LEPT_DLL extern l_ok pixCorrelationScoreShifted ( PIX *pix1, PIX *pix2, l_int32 area1, l_int32 area2, l_int32 delx, l_int32 dely, l_int32 *tab, l_float32 *pscore ); -LEPT_DLL extern L_DEWARP * dewarpCreate ( PIX *pixs, l_int32 pageno ); -LEPT_DLL extern L_DEWARP * dewarpCreateRef ( l_int32 pageno, l_int32 refpage ); -LEPT_DLL extern void dewarpDestroy ( L_DEWARP **pdew ); -LEPT_DLL extern L_DEWARPA * dewarpaCreate ( l_int32 nptrs, l_int32 sampling, l_int32 redfactor, l_int32 minlines, l_int32 maxdist ); -LEPT_DLL extern L_DEWARPA * dewarpaCreateFromPixacomp ( PIXAC *pixac, l_int32 useboth, l_int32 sampling, l_int32 minlines, l_int32 maxdist ); -LEPT_DLL extern void dewarpaDestroy ( L_DEWARPA **pdewa ); -LEPT_DLL extern l_ok dewarpaDestroyDewarp ( L_DEWARPA *dewa, l_int32 pageno ); -LEPT_DLL extern l_ok dewarpaInsertDewarp ( L_DEWARPA *dewa, L_DEWARP *dew ); -LEPT_DLL extern L_DEWARP * dewarpaGetDewarp ( L_DEWARPA *dewa, l_int32 index ); -LEPT_DLL extern l_ok dewarpaSetCurvatures ( L_DEWARPA *dewa, l_int32 max_linecurv, l_int32 min_diff_linecurv, l_int32 max_diff_linecurv, l_int32 max_edgecurv, l_int32 max_diff_edgecurv, l_int32 max_edgeslope ); -LEPT_DLL extern l_ok dewarpaUseBothArrays ( L_DEWARPA *dewa, l_int32 useboth ); -LEPT_DLL extern l_ok dewarpaSetCheckColumns ( L_DEWARPA *dewa, l_int32 check_columns ); -LEPT_DLL extern l_ok dewarpaSetMaxDistance ( L_DEWARPA *dewa, l_int32 maxdist ); -LEPT_DLL extern L_DEWARP * dewarpRead ( const char *filename ); -LEPT_DLL extern L_DEWARP * dewarpReadStream ( FILE *fp ); -LEPT_DLL extern L_DEWARP * dewarpReadMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok dewarpWrite ( const char *filename, L_DEWARP *dew ); -LEPT_DLL extern l_ok dewarpWriteStream ( FILE *fp, L_DEWARP *dew ); -LEPT_DLL extern l_ok dewarpWriteMem ( l_uint8 **pdata, size_t *psize, L_DEWARP *dew ); -LEPT_DLL extern L_DEWARPA * dewarpaRead ( const char *filename ); -LEPT_DLL extern L_DEWARPA * dewarpaReadStream ( FILE *fp ); -LEPT_DLL extern L_DEWARPA * dewarpaReadMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok dewarpaWrite ( const char *filename, L_DEWARPA *dewa ); -LEPT_DLL extern l_ok dewarpaWriteStream ( FILE *fp, L_DEWARPA *dewa ); -LEPT_DLL extern l_ok dewarpaWriteMem ( l_uint8 **pdata, size_t *psize, L_DEWARPA *dewa ); -LEPT_DLL extern l_ok dewarpBuildPageModel ( L_DEWARP *dew, const char *debugfile ); -LEPT_DLL extern l_ok dewarpFindVertDisparity ( L_DEWARP *dew, PTAA *ptaa, l_int32 rotflag ); -LEPT_DLL extern l_ok dewarpFindHorizDisparity ( L_DEWARP *dew, PTAA *ptaa ); -LEPT_DLL extern PTAA * dewarpGetTextlineCenters ( PIX *pixs, l_int32 debugflag ); -LEPT_DLL extern PTAA * dewarpRemoveShortLines ( PIX *pixs, PTAA *ptaas, l_float32 fract, l_int32 debugflag ); -LEPT_DLL extern l_ok dewarpFindHorizSlopeDisparity ( L_DEWARP *dew, PIX *pixb, l_float32 fractthresh, l_int32 parity ); -LEPT_DLL extern l_ok dewarpBuildLineModel ( L_DEWARP *dew, l_int32 opensize, const char *debugfile ); -LEPT_DLL extern l_ok dewarpaModelStatus ( L_DEWARPA *dewa, l_int32 pageno, l_int32 *pvsuccess, l_int32 *phsuccess ); -LEPT_DLL extern l_ok dewarpaApplyDisparity ( L_DEWARPA *dewa, l_int32 pageno, PIX *pixs, l_int32 grayin, l_int32 x, l_int32 y, PIX **ppixd, const char *debugfile ); -LEPT_DLL extern l_ok dewarpaApplyDisparityBoxa ( L_DEWARPA *dewa, l_int32 pageno, PIX *pixs, BOXA *boxas, l_int32 mapdir, l_int32 x, l_int32 y, BOXA **pboxad, const char *debugfile ); -LEPT_DLL extern l_ok dewarpMinimize ( L_DEWARP *dew ); -LEPT_DLL extern l_ok dewarpPopulateFullRes ( L_DEWARP *dew, PIX *pix, l_int32 x, l_int32 y ); -LEPT_DLL extern l_ok dewarpSinglePage ( PIX *pixs, l_int32 thresh, l_int32 adaptive, l_int32 useboth, l_int32 check_columns, PIX **ppixd, L_DEWARPA **pdewa, l_int32 debug ); -LEPT_DLL extern l_ok dewarpSinglePageInit ( PIX *pixs, l_int32 thresh, l_int32 adaptive, l_int32 useboth, l_int32 check_columns, PIX **ppixb, L_DEWARPA **pdewa ); -LEPT_DLL extern l_ok dewarpSinglePageRun ( PIX *pixs, PIX *pixb, L_DEWARPA *dewa, PIX **ppixd, l_int32 debug ); -LEPT_DLL extern l_ok dewarpaListPages ( L_DEWARPA *dewa ); -LEPT_DLL extern l_ok dewarpaSetValidModels ( L_DEWARPA *dewa, l_int32 notests, l_int32 debug ); -LEPT_DLL extern l_ok dewarpaInsertRefModels ( L_DEWARPA *dewa, l_int32 notests, l_int32 debug ); -LEPT_DLL extern l_ok dewarpaStripRefModels ( L_DEWARPA *dewa ); -LEPT_DLL extern l_ok dewarpaRestoreModels ( L_DEWARPA *dewa ); -LEPT_DLL extern l_ok dewarpaInfo ( FILE *fp, L_DEWARPA *dewa ); -LEPT_DLL extern l_ok dewarpaModelStats ( L_DEWARPA *dewa, l_int32 *pnnone, l_int32 *pnvsuccess, l_int32 *pnvvalid, l_int32 *pnhsuccess, l_int32 *pnhvalid, l_int32 *pnref ); -LEPT_DLL extern l_ok dewarpaShowArrays ( L_DEWARPA *dewa, l_float32 scalefact, l_int32 first, l_int32 last ); -LEPT_DLL extern l_ok dewarpDebug ( L_DEWARP *dew, const char *subdirs, l_int32 index ); -LEPT_DLL extern l_ok dewarpShowResults ( L_DEWARPA *dewa, SARRAY *sa, BOXA *boxa, l_int32 firstpage, l_int32 lastpage, const char *pdfout ); -LEPT_DLL extern L_DNA * l_dnaCreate ( l_int32 n ); -LEPT_DLL extern L_DNA * l_dnaCreateFromIArray ( l_int32 *iarray, l_int32 size ); -LEPT_DLL extern L_DNA * l_dnaCreateFromDArray ( l_float64 *darray, l_int32 size, l_int32 copyflag ); -LEPT_DLL extern L_DNA * l_dnaMakeSequence ( l_float64 startval, l_float64 increment, l_int32 size ); -LEPT_DLL extern void l_dnaDestroy ( L_DNA **pda ); -LEPT_DLL extern L_DNA * l_dnaCopy ( L_DNA *da ); -LEPT_DLL extern L_DNA * l_dnaClone ( L_DNA *da ); -LEPT_DLL extern l_ok l_dnaEmpty ( L_DNA *da ); -LEPT_DLL extern l_ok l_dnaAddNumber ( L_DNA *da, l_float64 val ); -LEPT_DLL extern l_ok l_dnaInsertNumber ( L_DNA *da, l_int32 index, l_float64 val ); -LEPT_DLL extern l_ok l_dnaRemoveNumber ( L_DNA *da, l_int32 index ); -LEPT_DLL extern l_ok l_dnaReplaceNumber ( L_DNA *da, l_int32 index, l_float64 val ); -LEPT_DLL extern l_int32 l_dnaGetCount ( L_DNA *da ); -LEPT_DLL extern l_ok l_dnaSetCount ( L_DNA *da, l_int32 newcount ); -LEPT_DLL extern l_ok l_dnaGetDValue ( L_DNA *da, l_int32 index, l_float64 *pval ); -LEPT_DLL extern l_ok l_dnaGetIValue ( L_DNA *da, l_int32 index, l_int32 *pival ); -LEPT_DLL extern l_ok l_dnaSetValue ( L_DNA *da, l_int32 index, l_float64 val ); -LEPT_DLL extern l_ok l_dnaShiftValue ( L_DNA *da, l_int32 index, l_float64 diff ); -LEPT_DLL extern l_int32 * l_dnaGetIArray ( L_DNA *da ); -LEPT_DLL extern l_float64 * l_dnaGetDArray ( L_DNA *da, l_int32 copyflag ); -LEPT_DLL extern l_int32 l_dnaGetRefcount ( L_DNA *da ); -LEPT_DLL extern l_ok l_dnaChangeRefcount ( L_DNA *da, l_int32 delta ); -LEPT_DLL extern l_ok l_dnaGetParameters ( L_DNA *da, l_float64 *pstartx, l_float64 *pdelx ); -LEPT_DLL extern l_ok l_dnaSetParameters ( L_DNA *da, l_float64 startx, l_float64 delx ); -LEPT_DLL extern l_ok l_dnaCopyParameters ( L_DNA *dad, L_DNA *das ); -LEPT_DLL extern L_DNA * l_dnaRead ( const char *filename ); -LEPT_DLL extern L_DNA * l_dnaReadStream ( FILE *fp ); -LEPT_DLL extern l_ok l_dnaWrite ( const char *filename, L_DNA *da ); -LEPT_DLL extern l_ok l_dnaWriteStream ( FILE *fp, L_DNA *da ); -LEPT_DLL extern L_DNAA * l_dnaaCreate ( l_int32 n ); -LEPT_DLL extern L_DNAA * l_dnaaCreateFull ( l_int32 nptr, l_int32 n ); -LEPT_DLL extern l_ok l_dnaaTruncate ( L_DNAA *daa ); -LEPT_DLL extern void l_dnaaDestroy ( L_DNAA **pdaa ); -LEPT_DLL extern l_ok l_dnaaAddDna ( L_DNAA *daa, L_DNA *da, l_int32 copyflag ); -LEPT_DLL extern l_int32 l_dnaaGetCount ( L_DNAA *daa ); -LEPT_DLL extern l_int32 l_dnaaGetDnaCount ( L_DNAA *daa, l_int32 index ); -LEPT_DLL extern l_int32 l_dnaaGetNumberCount ( L_DNAA *daa ); -LEPT_DLL extern L_DNA * l_dnaaGetDna ( L_DNAA *daa, l_int32 index, l_int32 accessflag ); -LEPT_DLL extern l_ok l_dnaaReplaceDna ( L_DNAA *daa, l_int32 index, L_DNA *da ); -LEPT_DLL extern l_ok l_dnaaGetValue ( L_DNAA *daa, l_int32 i, l_int32 j, l_float64 *pval ); -LEPT_DLL extern l_ok l_dnaaAddNumber ( L_DNAA *daa, l_int32 index, l_float64 val ); -LEPT_DLL extern L_DNAA * l_dnaaRead ( const char *filename ); -LEPT_DLL extern L_DNAA * l_dnaaReadStream ( FILE *fp ); -LEPT_DLL extern l_ok l_dnaaWrite ( const char *filename, L_DNAA *daa ); -LEPT_DLL extern l_ok l_dnaaWriteStream ( FILE *fp, L_DNAA *daa ); -LEPT_DLL extern l_ok l_dnaJoin ( L_DNA *dad, L_DNA *das, l_int32 istart, l_int32 iend ); -LEPT_DLL extern L_DNA * l_dnaaFlattenToDna ( L_DNAA *daa ); -LEPT_DLL extern NUMA * l_dnaConvertToNuma ( L_DNA *da ); -LEPT_DLL extern L_DNA * numaConvertToDna ( NUMA *na ); -LEPT_DLL extern L_DNA * l_dnaUnionByAset ( L_DNA *da1, L_DNA *da2 ); -LEPT_DLL extern L_DNA * l_dnaRemoveDupsByAset ( L_DNA *das ); -LEPT_DLL extern L_DNA * l_dnaIntersectionByAset ( L_DNA *da1, L_DNA *da2 ); -LEPT_DLL extern L_ASET * l_asetCreateFromDna ( L_DNA *da ); -LEPT_DLL extern L_DNA * l_dnaDiffAdjValues ( L_DNA *das ); -LEPT_DLL extern L_DNAHASH * l_dnaHashCreate ( l_int32 nbuckets, l_int32 initsize ); -LEPT_DLL extern void l_dnaHashDestroy ( L_DNAHASH **pdahash ); -LEPT_DLL extern l_int32 l_dnaHashGetCount ( L_DNAHASH *dahash ); -LEPT_DLL extern l_int32 l_dnaHashGetTotalCount ( L_DNAHASH *dahash ); -LEPT_DLL extern L_DNA * l_dnaHashGetDna ( L_DNAHASH *dahash, l_uint64 key, l_int32 copyflag ); -LEPT_DLL extern l_ok l_dnaHashAdd ( L_DNAHASH *dahash, l_uint64 key, l_float64 value ); -LEPT_DLL extern L_DNAHASH * l_dnaHashCreateFromDna ( L_DNA *da ); -LEPT_DLL extern l_ok l_dnaRemoveDupsByHash ( L_DNA *das, L_DNA **pdad, L_DNAHASH **pdahash ); -LEPT_DLL extern l_ok l_dnaMakeHistoByHash ( L_DNA *das, L_DNAHASH **pdahash, L_DNA **pdav, L_DNA **pdac ); -LEPT_DLL extern L_DNA * l_dnaIntersectionByHash ( L_DNA *da1, L_DNA *da2 ); -LEPT_DLL extern l_ok l_dnaFindValByHash ( L_DNA *da, L_DNAHASH *dahash, l_float64 val, l_int32 *pindex ); -LEPT_DLL extern PIX * pixMorphDwa_2 ( PIX *pixd, PIX *pixs, l_int32 operation, char *selname ); -LEPT_DLL extern PIX * pixFMorphopGen_2 ( PIX *pixd, PIX *pixs, l_int32 operation, char *selname ); -LEPT_DLL extern l_int32 fmorphopgen_low_2 ( l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld, l_uint32 *datas, l_int32 wpls, l_int32 index ); -LEPT_DLL extern PIX * pixSobelEdgeFilter ( PIX *pixs, l_int32 orientflag ); -LEPT_DLL extern PIX * pixTwoSidedEdgeFilter ( PIX *pixs, l_int32 orientflag ); -LEPT_DLL extern l_ok pixMeasureEdgeSmoothness ( PIX *pixs, l_int32 side, l_int32 minjump, l_int32 minreversal, l_float32 *pjpl, l_float32 *pjspl, l_float32 *prpl, const char *debugfile ); -LEPT_DLL extern NUMA * pixGetEdgeProfile ( PIX *pixs, l_int32 side, const char *debugfile ); -LEPT_DLL extern l_ok pixGetLastOffPixelInRun ( PIX *pixs, l_int32 x, l_int32 y, l_int32 direction, l_int32 *ploc ); -LEPT_DLL extern l_int32 pixGetLastOnPixelInRun ( PIX *pixs, l_int32 x, l_int32 y, l_int32 direction, l_int32 *ploc ); -LEPT_DLL extern char * encodeBase64 ( const l_uint8 *inarray, l_int32 insize, l_int32 *poutsize ); -LEPT_DLL extern l_uint8 * decodeBase64 ( const char *inarray, l_int32 insize, l_int32 *poutsize ); -LEPT_DLL extern char * encodeAscii85 ( const l_uint8 *inarray, l_int32 insize, l_int32 *poutsize ); -LEPT_DLL extern l_uint8 * decodeAscii85 ( const char *inarray, l_int32 insize, l_int32 *poutsize ); -LEPT_DLL extern char * reformatPacked64 ( const char *inarray, l_int32 insize, l_int32 leadspace, l_int32 linechars, l_int32 addquotes, l_int32 *poutsize ); -LEPT_DLL extern PIX * pixGammaTRC ( PIX *pixd, PIX *pixs, l_float32 gamma, l_int32 minval, l_int32 maxval ); -LEPT_DLL extern PIX * pixGammaTRCMasked ( PIX *pixd, PIX *pixs, PIX *pixm, l_float32 gamma, l_int32 minval, l_int32 maxval ); -LEPT_DLL extern PIX * pixGammaTRCWithAlpha ( PIX *pixd, PIX *pixs, l_float32 gamma, l_int32 minval, l_int32 maxval ); -LEPT_DLL extern NUMA * numaGammaTRC ( l_float32 gamma, l_int32 minval, l_int32 maxval ); -LEPT_DLL extern PIX * pixContrastTRC ( PIX *pixd, PIX *pixs, l_float32 factor ); -LEPT_DLL extern PIX * pixContrastTRCMasked ( PIX *pixd, PIX *pixs, PIX *pixm, l_float32 factor ); -LEPT_DLL extern NUMA * numaContrastTRC ( l_float32 factor ); -LEPT_DLL extern PIX * pixEqualizeTRC ( PIX *pixd, PIX *pixs, l_float32 fract, l_int32 factor ); -LEPT_DLL extern NUMA * numaEqualizeTRC ( PIX *pix, l_float32 fract, l_int32 factor ); -LEPT_DLL extern l_int32 pixTRCMap ( PIX *pixs, PIX *pixm, NUMA *na ); -LEPT_DLL extern l_int32 pixTRCMapGeneral ( PIX *pixs, PIX *pixm, NUMA *nar, NUMA *nag, NUMA *nab ); -LEPT_DLL extern PIX * pixUnsharpMasking ( PIX *pixs, l_int32 halfwidth, l_float32 fract ); -LEPT_DLL extern PIX * pixUnsharpMaskingGray ( PIX *pixs, l_int32 halfwidth, l_float32 fract ); -LEPT_DLL extern PIX * pixUnsharpMaskingFast ( PIX *pixs, l_int32 halfwidth, l_float32 fract, l_int32 direction ); -LEPT_DLL extern PIX * pixUnsharpMaskingGrayFast ( PIX *pixs, l_int32 halfwidth, l_float32 fract, l_int32 direction ); -LEPT_DLL extern PIX * pixUnsharpMaskingGray1D ( PIX *pixs, l_int32 halfwidth, l_float32 fract, l_int32 direction ); -LEPT_DLL extern PIX * pixUnsharpMaskingGray2D ( PIX *pixs, l_int32 halfwidth, l_float32 fract ); -LEPT_DLL extern PIX * pixModifyHue ( PIX *pixd, PIX *pixs, l_float32 fract ); -LEPT_DLL extern PIX * pixModifySaturation ( PIX *pixd, PIX *pixs, l_float32 fract ); -LEPT_DLL extern l_int32 pixMeasureSaturation ( PIX *pixs, l_int32 factor, l_float32 *psat ); -LEPT_DLL extern PIX * pixModifyBrightness ( PIX *pixd, PIX *pixs, l_float32 fract ); -LEPT_DLL extern PIX * pixMosaicColorShiftRGB ( PIX *pixs, l_float32 roff, l_float32 goff, l_float32 boff, l_float32 delta, l_int32 nincr ); -LEPT_DLL extern PIX * pixColorShiftRGB ( PIX *pixs, l_float32 rfract, l_float32 gfract, l_float32 bfract ); -LEPT_DLL extern PIX * pixDarkenGray ( PIX *pixd, PIX *pixs, l_int32 thresh, l_int32 satlimit ); -LEPT_DLL extern PIX * pixMultConstantColor ( PIX *pixs, l_float32 rfact, l_float32 gfact, l_float32 bfact ); -LEPT_DLL extern PIX * pixMultMatrixColor ( PIX *pixs, L_KERNEL *kel ); -LEPT_DLL extern PIX * pixHalfEdgeByBandpass ( PIX *pixs, l_int32 sm1h, l_int32 sm1v, l_int32 sm2h, l_int32 sm2v ); -LEPT_DLL extern l_ok fhmtautogen ( SELA *sela, l_int32 fileindex, const char *filename ); -LEPT_DLL extern l_ok fhmtautogen1 ( SELA *sela, l_int32 fileindex, const char *filename ); -LEPT_DLL extern l_ok fhmtautogen2 ( SELA *sela, l_int32 fileindex, const char *filename ); -LEPT_DLL extern PIX * pixHMTDwa_1 ( PIX *pixd, PIX *pixs, const char *selname ); -LEPT_DLL extern PIX * pixFHMTGen_1 ( PIX *pixd, PIX *pixs, const char *selname ); -LEPT_DLL extern l_int32 fhmtgen_low_1 ( l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld, l_uint32 *datas, l_int32 wpls, l_int32 index ); -LEPT_DLL extern l_ok pixItalicWords ( PIX *pixs, BOXA *boxaw, PIX *pixw, BOXA **pboxa, l_int32 debugflag ); -LEPT_DLL extern PIX * pixOrientCorrect ( PIX *pixs, l_float32 minupconf, l_float32 minratio, l_float32 *pupconf, l_float32 *pleftconf, l_int32 *protation, l_int32 debug ); -LEPT_DLL extern l_ok pixOrientDetect ( PIX *pixs, l_float32 *pupconf, l_float32 *pleftconf, l_int32 mincount, l_int32 debug ); -LEPT_DLL extern l_ok makeOrientDecision ( l_float32 upconf, l_float32 leftconf, l_float32 minupconf, l_float32 minratio, l_int32 *porient, l_int32 debug ); -LEPT_DLL extern l_ok pixUpDownDetect ( PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 debug ); -LEPT_DLL extern l_ok pixUpDownDetectGeneral ( PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 npixels, l_int32 debug ); -LEPT_DLL extern l_ok pixOrientDetectDwa ( PIX *pixs, l_float32 *pupconf, l_float32 *pleftconf, l_int32 mincount, l_int32 debug ); -LEPT_DLL extern l_ok pixUpDownDetectDwa ( PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 debug ); -LEPT_DLL extern l_ok pixUpDownDetectGeneralDwa ( PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 npixels, l_int32 debug ); -LEPT_DLL extern l_ok pixMirrorDetect ( PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 debug ); -LEPT_DLL extern l_ok pixMirrorDetectDwa ( PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 debug ); -LEPT_DLL extern PIX * pixFlipFHMTGen ( PIX *pixd, PIX *pixs, const char *selname ); -LEPT_DLL extern l_ok fmorphautogen ( SELA *sela, l_int32 fileindex, const char *filename ); -LEPT_DLL extern l_ok fmorphautogen1 ( SELA *sela, l_int32 fileindex, const char *filename ); -LEPT_DLL extern l_int32 fmorphautogen2 ( SELA *sela, l_int32 fileindex, const char *filename ); -LEPT_DLL extern PIX * pixMorphDwa_1 ( PIX *pixd, PIX *pixs, l_int32 operation, char *selname ); -LEPT_DLL extern PIX * pixFMorphopGen_1 ( PIX *pixd, PIX *pixs, l_int32 operation, char *selname ); -LEPT_DLL extern l_int32 fmorphopgen_low_1 ( l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld, l_uint32 *datas, l_int32 wpls, l_int32 index ); -LEPT_DLL extern FPIX * fpixCreate ( l_int32 width, l_int32 height ); -LEPT_DLL extern FPIX * fpixCreateTemplate ( FPIX *fpixs ); -LEPT_DLL extern FPIX * fpixClone ( FPIX *fpix ); -LEPT_DLL extern FPIX * fpixCopy ( FPIX *fpixd, FPIX *fpixs ); -LEPT_DLL extern l_ok fpixResizeImageData ( FPIX *fpixd, FPIX *fpixs ); -LEPT_DLL extern void fpixDestroy ( FPIX **pfpix ); -LEPT_DLL extern l_ok fpixGetDimensions ( FPIX *fpix, l_int32 *pw, l_int32 *ph ); -LEPT_DLL extern l_ok fpixSetDimensions ( FPIX *fpix, l_int32 w, l_int32 h ); -LEPT_DLL extern l_int32 fpixGetWpl ( FPIX *fpix ); -LEPT_DLL extern l_ok fpixSetWpl ( FPIX *fpix, l_int32 wpl ); -LEPT_DLL extern l_int32 fpixGetRefcount ( FPIX *fpix ); -LEPT_DLL extern l_ok fpixChangeRefcount ( FPIX *fpix, l_int32 delta ); -LEPT_DLL extern l_ok fpixGetResolution ( FPIX *fpix, l_int32 *pxres, l_int32 *pyres ); -LEPT_DLL extern l_ok fpixSetResolution ( FPIX *fpix, l_int32 xres, l_int32 yres ); -LEPT_DLL extern l_ok fpixCopyResolution ( FPIX *fpixd, FPIX *fpixs ); -LEPT_DLL extern l_float32 * fpixGetData ( FPIX *fpix ); -LEPT_DLL extern l_ok fpixSetData ( FPIX *fpix, l_float32 *data ); -LEPT_DLL extern l_ok fpixGetPixel ( FPIX *fpix, l_int32 x, l_int32 y, l_float32 *pval ); -LEPT_DLL extern l_ok fpixSetPixel ( FPIX *fpix, l_int32 x, l_int32 y, l_float32 val ); -LEPT_DLL extern FPIXA * fpixaCreate ( l_int32 n ); -LEPT_DLL extern FPIXA * fpixaCopy ( FPIXA *fpixa, l_int32 copyflag ); -LEPT_DLL extern void fpixaDestroy ( FPIXA **pfpixa ); -LEPT_DLL extern l_ok fpixaAddFPix ( FPIXA *fpixa, FPIX *fpix, l_int32 copyflag ); -LEPT_DLL extern l_int32 fpixaGetCount ( FPIXA *fpixa ); -LEPT_DLL extern l_ok fpixaChangeRefcount ( FPIXA *fpixa, l_int32 delta ); -LEPT_DLL extern FPIX * fpixaGetFPix ( FPIXA *fpixa, l_int32 index, l_int32 accesstype ); -LEPT_DLL extern l_ok fpixaGetFPixDimensions ( FPIXA *fpixa, l_int32 index, l_int32 *pw, l_int32 *ph ); -LEPT_DLL extern l_float32 * fpixaGetData ( FPIXA *fpixa, l_int32 index ); -LEPT_DLL extern l_ok fpixaGetPixel ( FPIXA *fpixa, l_int32 index, l_int32 x, l_int32 y, l_float32 *pval ); -LEPT_DLL extern l_ok fpixaSetPixel ( FPIXA *fpixa, l_int32 index, l_int32 x, l_int32 y, l_float32 val ); -LEPT_DLL extern DPIX * dpixCreate ( l_int32 width, l_int32 height ); -LEPT_DLL extern DPIX * dpixCreateTemplate ( DPIX *dpixs ); -LEPT_DLL extern DPIX * dpixClone ( DPIX *dpix ); -LEPT_DLL extern DPIX * dpixCopy ( DPIX *dpixd, DPIX *dpixs ); -LEPT_DLL extern l_ok dpixResizeImageData ( DPIX *dpixd, DPIX *dpixs ); -LEPT_DLL extern void dpixDestroy ( DPIX **pdpix ); -LEPT_DLL extern l_ok dpixGetDimensions ( DPIX *dpix, l_int32 *pw, l_int32 *ph ); -LEPT_DLL extern l_ok dpixSetDimensions ( DPIX *dpix, l_int32 w, l_int32 h ); -LEPT_DLL extern l_int32 dpixGetWpl ( DPIX *dpix ); -LEPT_DLL extern l_ok dpixSetWpl ( DPIX *dpix, l_int32 wpl ); -LEPT_DLL extern l_int32 dpixGetRefcount ( DPIX *dpix ); -LEPT_DLL extern l_ok dpixChangeRefcount ( DPIX *dpix, l_int32 delta ); -LEPT_DLL extern l_ok dpixGetResolution ( DPIX *dpix, l_int32 *pxres, l_int32 *pyres ); -LEPT_DLL extern l_ok dpixSetResolution ( DPIX *dpix, l_int32 xres, l_int32 yres ); -LEPT_DLL extern l_ok dpixCopyResolution ( DPIX *dpixd, DPIX *dpixs ); -LEPT_DLL extern l_float64 * dpixGetData ( DPIX *dpix ); -LEPT_DLL extern l_ok dpixSetData ( DPIX *dpix, l_float64 *data ); -LEPT_DLL extern l_ok dpixGetPixel ( DPIX *dpix, l_int32 x, l_int32 y, l_float64 *pval ); -LEPT_DLL extern l_ok dpixSetPixel ( DPIX *dpix, l_int32 x, l_int32 y, l_float64 val ); -LEPT_DLL extern FPIX * fpixRead ( const char *filename ); -LEPT_DLL extern FPIX * fpixReadStream ( FILE *fp ); -LEPT_DLL extern FPIX * fpixReadMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok fpixWrite ( const char *filename, FPIX *fpix ); -LEPT_DLL extern l_ok fpixWriteStream ( FILE *fp, FPIX *fpix ); -LEPT_DLL extern l_ok fpixWriteMem ( l_uint8 **pdata, size_t *psize, FPIX *fpix ); -LEPT_DLL extern FPIX * fpixEndianByteSwap ( FPIX *fpixd, FPIX *fpixs ); -LEPT_DLL extern DPIX * dpixRead ( const char *filename ); -LEPT_DLL extern DPIX * dpixReadStream ( FILE *fp ); -LEPT_DLL extern DPIX * dpixReadMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok dpixWrite ( const char *filename, DPIX *dpix ); -LEPT_DLL extern l_ok dpixWriteStream ( FILE *fp, DPIX *dpix ); -LEPT_DLL extern l_ok dpixWriteMem ( l_uint8 **pdata, size_t *psize, DPIX *dpix ); -LEPT_DLL extern DPIX * dpixEndianByteSwap ( DPIX *dpixd, DPIX *dpixs ); -LEPT_DLL extern l_ok fpixPrintStream ( FILE *fp, FPIX *fpix, l_int32 factor ); -LEPT_DLL extern FPIX * pixConvertToFPix ( PIX *pixs, l_int32 ncomps ); -LEPT_DLL extern DPIX * pixConvertToDPix ( PIX *pixs, l_int32 ncomps ); -LEPT_DLL extern PIX * fpixConvertToPix ( FPIX *fpixs, l_int32 outdepth, l_int32 negvals, l_int32 errorflag ); -LEPT_DLL extern PIX * fpixDisplayMaxDynamicRange ( FPIX *fpixs ); -LEPT_DLL extern DPIX * fpixConvertToDPix ( FPIX *fpix ); -LEPT_DLL extern PIX * dpixConvertToPix ( DPIX *dpixs, l_int32 outdepth, l_int32 negvals, l_int32 errorflag ); -LEPT_DLL extern FPIX * dpixConvertToFPix ( DPIX *dpix ); -LEPT_DLL extern l_ok fpixGetMin ( FPIX *fpix, l_float32 *pminval, l_int32 *pxminloc, l_int32 *pyminloc ); -LEPT_DLL extern l_ok fpixGetMax ( FPIX *fpix, l_float32 *pmaxval, l_int32 *pxmaxloc, l_int32 *pymaxloc ); -LEPT_DLL extern l_ok dpixGetMin ( DPIX *dpix, l_float64 *pminval, l_int32 *pxminloc, l_int32 *pyminloc ); -LEPT_DLL extern l_ok dpixGetMax ( DPIX *dpix, l_float64 *pmaxval, l_int32 *pxmaxloc, l_int32 *pymaxloc ); -LEPT_DLL extern FPIX * fpixScaleByInteger ( FPIX *fpixs, l_int32 factor ); -LEPT_DLL extern DPIX * dpixScaleByInteger ( DPIX *dpixs, l_int32 factor ); -LEPT_DLL extern FPIX * fpixLinearCombination ( FPIX *fpixd, FPIX *fpixs1, FPIX *fpixs2, l_float32 a, l_float32 b ); -LEPT_DLL extern l_ok fpixAddMultConstant ( FPIX *fpix, l_float32 addc, l_float32 multc ); -LEPT_DLL extern DPIX * dpixLinearCombination ( DPIX *dpixd, DPIX *dpixs1, DPIX *dpixs2, l_float32 a, l_float32 b ); -LEPT_DLL extern l_ok dpixAddMultConstant ( DPIX *dpix, l_float64 addc, l_float64 multc ); -LEPT_DLL extern l_ok fpixSetAllArbitrary ( FPIX *fpix, l_float32 inval ); -LEPT_DLL extern l_ok dpixSetAllArbitrary ( DPIX *dpix, l_float64 inval ); -LEPT_DLL extern FPIX * fpixAddBorder ( FPIX *fpixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); -LEPT_DLL extern FPIX * fpixRemoveBorder ( FPIX *fpixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); -LEPT_DLL extern FPIX * fpixAddMirroredBorder ( FPIX *fpixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); -LEPT_DLL extern FPIX * fpixAddContinuedBorder ( FPIX *fpixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); -LEPT_DLL extern FPIX * fpixAddSlopeBorder ( FPIX *fpixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); -LEPT_DLL extern l_ok fpixRasterop ( FPIX *fpixd, l_int32 dx, l_int32 dy, l_int32 dw, l_int32 dh, FPIX *fpixs, l_int32 sx, l_int32 sy ); -LEPT_DLL extern FPIX * fpixRotateOrth ( FPIX *fpixs, l_int32 quads ); -LEPT_DLL extern FPIX * fpixRotate180 ( FPIX *fpixd, FPIX *fpixs ); -LEPT_DLL extern FPIX * fpixRotate90 ( FPIX *fpixs, l_int32 direction ); -LEPT_DLL extern FPIX * fpixFlipLR ( FPIX *fpixd, FPIX *fpixs ); -LEPT_DLL extern FPIX * fpixFlipTB ( FPIX *fpixd, FPIX *fpixs ); -LEPT_DLL extern FPIX * fpixAffinePta ( FPIX *fpixs, PTA *ptad, PTA *ptas, l_int32 border, l_float32 inval ); -LEPT_DLL extern FPIX * fpixAffine ( FPIX *fpixs, l_float32 *vc, l_float32 inval ); -LEPT_DLL extern FPIX * fpixProjectivePta ( FPIX *fpixs, PTA *ptad, PTA *ptas, l_int32 border, l_float32 inval ); -LEPT_DLL extern FPIX * fpixProjective ( FPIX *fpixs, l_float32 *vc, l_float32 inval ); -LEPT_DLL extern l_ok linearInterpolatePixelFloat ( l_float32 *datas, l_int32 w, l_int32 h, l_float32 x, l_float32 y, l_float32 inval, l_float32 *pval ); -LEPT_DLL extern PIX * fpixThresholdToPix ( FPIX *fpix, l_float32 thresh ); -LEPT_DLL extern FPIX * pixComponentFunction ( PIX *pix, l_float32 rnum, l_float32 gnum, l_float32 bnum, l_float32 rdenom, l_float32 gdenom, l_float32 bdenom ); -LEPT_DLL extern PIX * pixReadStreamGif ( FILE *fp ); -LEPT_DLL extern PIX * pixReadMemGif ( const l_uint8 *cdata, size_t size ); -LEPT_DLL extern l_ok pixWriteStreamGif ( FILE *fp, PIX *pix ); -LEPT_DLL extern l_ok pixWriteMemGif ( l_uint8 **pdata, size_t *psize, PIX *pix ); -LEPT_DLL extern GPLOT * gplotCreate ( const char *rootname, l_int32 outformat, const char *title, const char *xlabel, const char *ylabel ); -LEPT_DLL extern void gplotDestroy ( GPLOT **pgplot ); -LEPT_DLL extern l_ok gplotAddPlot ( GPLOT *gplot, NUMA *nax, NUMA *nay, l_int32 plotstyle, const char *plotlabel ); -LEPT_DLL extern l_ok gplotSetScaling ( GPLOT *gplot, l_int32 scaling ); -LEPT_DLL extern PIX * gplotMakeOutputPix ( GPLOT *gplot ); -LEPT_DLL extern l_ok gplotMakeOutput ( GPLOT *gplot ); -LEPT_DLL extern l_ok gplotGenCommandFile ( GPLOT *gplot ); -LEPT_DLL extern l_ok gplotGenDataFiles ( GPLOT *gplot ); -LEPT_DLL extern l_ok gplotSimple1 ( NUMA *na, l_int32 outformat, const char *outroot, const char *title ); -LEPT_DLL extern l_ok gplotSimple2 ( NUMA *na1, NUMA *na2, l_int32 outformat, const char *outroot, const char *title ); -LEPT_DLL extern l_ok gplotSimpleN ( NUMAA *naa, l_int32 outformat, const char *outroot, const char *title ); -LEPT_DLL extern PIX * gplotSimplePix1 ( NUMA *na, const char *title ); -LEPT_DLL extern PIX * gplotSimplePix2 ( NUMA *na1, NUMA *na2, const char *title ); -LEPT_DLL extern PIX * gplotSimplePixN ( NUMAA *naa, const char *title ); -LEPT_DLL extern GPLOT * gplotSimpleXY1 ( NUMA *nax, NUMA *nay, l_int32 plotstyle, l_int32 outformat, const char *outroot, const char *title ); -LEPT_DLL extern GPLOT * gplotSimpleXY2 ( NUMA *nax, NUMA *nay1, NUMA *nay2, l_int32 plotstyle, l_int32 outformat, const char *outroot, const char *title ); -LEPT_DLL extern GPLOT * gplotSimpleXYN ( NUMA *nax, NUMAA *naay, l_int32 plotstyle, l_int32 outformat, const char *outroot, const char *title ); -LEPT_DLL extern PIX * gplotGeneralPix1 ( NUMA *na, l_int32 plotstyle, const char *rootname, const char *title, const char *xlabel, const char *ylabel ); -LEPT_DLL extern PIX * gplotGeneralPix2 ( NUMA *na1, NUMA *na2, l_int32 plotstyle, const char *rootname, const char *title, const char *xlabel, const char *ylabel ); -LEPT_DLL extern PIX * gplotGeneralPixN ( NUMA *nax, NUMAA *naay, l_int32 plotstyle, const char *rootname, const char *title, const char *xlabel, const char *ylabel ); -LEPT_DLL extern GPLOT * gplotRead ( const char *filename ); -LEPT_DLL extern l_ok gplotWrite ( const char *filename, GPLOT *gplot ); -LEPT_DLL extern PTA * generatePtaLine ( l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2 ); -LEPT_DLL extern PTA * generatePtaWideLine ( l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 width ); -LEPT_DLL extern PTA * generatePtaBox ( BOX *box, l_int32 width ); -LEPT_DLL extern PTA * generatePtaBoxa ( BOXA *boxa, l_int32 width, l_int32 removedups ); -LEPT_DLL extern PTA * generatePtaHashBox ( BOX *box, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline ); -LEPT_DLL extern PTA * generatePtaHashBoxa ( BOXA *boxa, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline, l_int32 removedups ); -LEPT_DLL extern PTAA * generatePtaaBoxa ( BOXA *boxa ); -LEPT_DLL extern PTAA * generatePtaaHashBoxa ( BOXA *boxa, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline ); -LEPT_DLL extern PTA * generatePtaPolyline ( PTA *ptas, l_int32 width, l_int32 closeflag, l_int32 removedups ); -LEPT_DLL extern PTA * generatePtaGrid ( l_int32 w, l_int32 h, l_int32 nx, l_int32 ny, l_int32 width ); -LEPT_DLL extern PTA * convertPtaLineTo4cc ( PTA *ptas ); -LEPT_DLL extern PTA * generatePtaFilledCircle ( l_int32 radius ); -LEPT_DLL extern PTA * generatePtaFilledSquare ( l_int32 side ); -LEPT_DLL extern PTA * generatePtaLineFromPt ( l_int32 x, l_int32 y, l_float64 length, l_float64 radang ); -LEPT_DLL extern l_ok locatePtRadially ( l_int32 xr, l_int32 yr, l_float64 dist, l_float64 radang, l_float64 *px, l_float64 *py ); -LEPT_DLL extern l_ok pixRenderPlotFromNuma ( PIX **ppix, NUMA *na, l_int32 plotloc, l_int32 linewidth, l_int32 max, l_uint32 color ); -LEPT_DLL extern PTA * makePlotPtaFromNuma ( NUMA *na, l_int32 size, l_int32 plotloc, l_int32 linewidth, l_int32 max ); -LEPT_DLL extern l_ok pixRenderPlotFromNumaGen ( PIX **ppix, NUMA *na, l_int32 orient, l_int32 linewidth, l_int32 refpos, l_int32 max, l_int32 drawref, l_uint32 color ); -LEPT_DLL extern PTA * makePlotPtaFromNumaGen ( NUMA *na, l_int32 orient, l_int32 linewidth, l_int32 refpos, l_int32 max, l_int32 drawref ); -LEPT_DLL extern l_ok pixRenderPta ( PIX *pix, PTA *pta, l_int32 op ); -LEPT_DLL extern l_ok pixRenderPtaArb ( PIX *pix, PTA *pta, l_uint8 rval, l_uint8 gval, l_uint8 bval ); -LEPT_DLL extern l_ok pixRenderPtaBlend ( PIX *pix, PTA *pta, l_uint8 rval, l_uint8 gval, l_uint8 bval, l_float32 fract ); -LEPT_DLL extern l_ok pixRenderLine ( PIX *pix, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 width, l_int32 op ); -LEPT_DLL extern l_ok pixRenderLineArb ( PIX *pix, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval ); -LEPT_DLL extern l_ok pixRenderLineBlend ( PIX *pix, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval, l_float32 fract ); -LEPT_DLL extern l_ok pixRenderBox ( PIX *pix, BOX *box, l_int32 width, l_int32 op ); -LEPT_DLL extern l_ok pixRenderBoxArb ( PIX *pix, BOX *box, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval ); -LEPT_DLL extern l_ok pixRenderBoxBlend ( PIX *pix, BOX *box, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval, l_float32 fract ); -LEPT_DLL extern l_ok pixRenderBoxa ( PIX *pix, BOXA *boxa, l_int32 width, l_int32 op ); -LEPT_DLL extern l_ok pixRenderBoxaArb ( PIX *pix, BOXA *boxa, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval ); -LEPT_DLL extern l_ok pixRenderBoxaBlend ( PIX *pix, BOXA *boxa, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval, l_float32 fract, l_int32 removedups ); -LEPT_DLL extern l_ok pixRenderHashBox ( PIX *pix, BOX *box, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline, l_int32 op ); -LEPT_DLL extern l_ok pixRenderHashBoxArb ( PIX *pix, BOX *box, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline, l_int32 rval, l_int32 gval, l_int32 bval ); -LEPT_DLL extern l_ok pixRenderHashBoxBlend ( PIX *pix, BOX *box, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline, l_int32 rval, l_int32 gval, l_int32 bval, l_float32 fract ); -LEPT_DLL extern l_ok pixRenderHashMaskArb ( PIX *pix, PIX *pixm, l_int32 x, l_int32 y, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline, l_int32 rval, l_int32 gval, l_int32 bval ); -LEPT_DLL extern l_ok pixRenderHashBoxa ( PIX *pix, BOXA *boxa, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline, l_int32 op ); -LEPT_DLL extern l_ok pixRenderHashBoxaArb ( PIX *pix, BOXA *boxa, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline, l_int32 rval, l_int32 gval, l_int32 bval ); -LEPT_DLL extern l_ok pixRenderHashBoxaBlend ( PIX *pix, BOXA *boxa, l_int32 spacing, l_int32 width, l_int32 orient, l_int32 outline, l_int32 rval, l_int32 gval, l_int32 bval, l_float32 fract ); -LEPT_DLL extern l_ok pixRenderPolyline ( PIX *pix, PTA *ptas, l_int32 width, l_int32 op, l_int32 closeflag ); -LEPT_DLL extern l_ok pixRenderPolylineArb ( PIX *pix, PTA *ptas, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval, l_int32 closeflag ); -LEPT_DLL extern l_ok pixRenderPolylineBlend ( PIX *pix, PTA *ptas, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval, l_float32 fract, l_int32 closeflag, l_int32 removedups ); -LEPT_DLL extern l_ok pixRenderGridArb ( PIX *pix, l_int32 nx, l_int32 ny, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval ); -LEPT_DLL extern PIX * pixRenderRandomCmapPtaa ( PIX *pix, PTAA *ptaa, l_int32 polyflag, l_int32 width, l_int32 closeflag ); -LEPT_DLL extern PIX * pixRenderPolygon ( PTA *ptas, l_int32 width, l_int32 *pxmin, l_int32 *pymin ); -LEPT_DLL extern PIX * pixFillPolygon ( PIX *pixs, PTA *pta, l_int32 xmin, l_int32 ymin ); -LEPT_DLL extern PIX * pixRenderContours ( PIX *pixs, l_int32 startval, l_int32 incr, l_int32 outdepth ); -LEPT_DLL extern PIX * fpixAutoRenderContours ( FPIX *fpix, l_int32 ncontours ); -LEPT_DLL extern PIX * fpixRenderContours ( FPIX *fpixs, l_float32 incr, l_float32 proxim ); -LEPT_DLL extern PTA * pixGeneratePtaBoundary ( PIX *pixs, l_int32 width ); -LEPT_DLL extern PIX * pixErodeGray ( PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixDilateGray ( PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixOpenGray ( PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixCloseGray ( PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixErodeGray3 ( PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixDilateGray3 ( PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixOpenGray3 ( PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixCloseGray3 ( PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixDitherToBinary ( PIX *pixs ); -LEPT_DLL extern PIX * pixDitherToBinarySpec ( PIX *pixs, l_int32 lowerclip, l_int32 upperclip ); -LEPT_DLL extern void ditherToBinaryLineLow ( l_uint32 *lined, l_int32 w, l_uint32 *bufs1, l_uint32 *bufs2, l_int32 lowerclip, l_int32 upperclip, l_int32 lastlineflag ); -LEPT_DLL extern PIX * pixThresholdToBinary ( PIX *pixs, l_int32 thresh ); -LEPT_DLL extern void thresholdToBinaryLineLow ( l_uint32 *lined, l_int32 w, l_uint32 *lines, l_int32 d, l_int32 thresh ); -LEPT_DLL extern PIX * pixVarThresholdToBinary ( PIX *pixs, PIX *pixg ); -LEPT_DLL extern PIX * pixAdaptThresholdToBinary ( PIX *pixs, PIX *pixm, l_float32 gamma ); -LEPT_DLL extern PIX * pixAdaptThresholdToBinaryGen ( PIX *pixs, PIX *pixm, l_float32 gamma, l_int32 blackval, l_int32 whiteval, l_int32 thresh ); -LEPT_DLL extern PIX * pixGenerateMaskByValue ( PIX *pixs, l_int32 val, l_int32 usecmap ); -LEPT_DLL extern PIX * pixGenerateMaskByBand ( PIX *pixs, l_int32 lower, l_int32 upper, l_int32 inband, l_int32 usecmap ); -LEPT_DLL extern PIX * pixDitherTo2bpp ( PIX *pixs, l_int32 cmapflag ); -LEPT_DLL extern PIX * pixDitherTo2bppSpec ( PIX *pixs, l_int32 lowerclip, l_int32 upperclip, l_int32 cmapflag ); -LEPT_DLL extern PIX * pixThresholdTo2bpp ( PIX *pixs, l_int32 nlevels, l_int32 cmapflag ); -LEPT_DLL extern PIX * pixThresholdTo4bpp ( PIX *pixs, l_int32 nlevels, l_int32 cmapflag ); -LEPT_DLL extern PIX * pixThresholdOn8bpp ( PIX *pixs, l_int32 nlevels, l_int32 cmapflag ); -LEPT_DLL extern PIX * pixThresholdGrayArb ( PIX *pixs, const char *edgevals, l_int32 outdepth, l_int32 use_average, l_int32 setblack, l_int32 setwhite ); -LEPT_DLL extern l_int32 * makeGrayQuantIndexTable ( l_int32 nlevels ); -LEPT_DLL extern l_ok makeGrayQuantTableArb ( NUMA *na, l_int32 outdepth, l_int32 **ptab, PIXCMAP **pcmap ); -LEPT_DLL extern PIX * pixGenerateMaskByBand32 ( PIX *pixs, l_uint32 refval, l_int32 delm, l_int32 delp, l_float32 fractm, l_float32 fractp ); -LEPT_DLL extern PIX * pixGenerateMaskByDiscr32 ( PIX *pixs, l_uint32 refval1, l_uint32 refval2, l_int32 distflag ); -LEPT_DLL extern PIX * pixGrayQuantFromHisto ( PIX *pixd, PIX *pixs, PIX *pixm, l_float32 minfract, l_int32 maxsize ); -LEPT_DLL extern PIX * pixGrayQuantFromCmap ( PIX *pixs, PIXCMAP *cmap, l_int32 mindepth ); -LEPT_DLL extern L_HEAP * lheapCreate ( l_int32 n, l_int32 direction ); -LEPT_DLL extern void lheapDestroy ( L_HEAP **plh, l_int32 freeflag ); -LEPT_DLL extern l_ok lheapAdd ( L_HEAP *lh, void *item ); -LEPT_DLL extern void * lheapRemove ( L_HEAP *lh ); -LEPT_DLL extern l_int32 lheapGetCount ( L_HEAP *lh ); -LEPT_DLL extern void * lheapGetElement ( L_HEAP *lh, l_int32 index ); -LEPT_DLL extern l_ok lheapSort ( L_HEAP *lh ); -LEPT_DLL extern l_ok lheapSortStrictOrder ( L_HEAP *lh ); -LEPT_DLL extern l_ok lheapPrint ( FILE *fp, L_HEAP *lh ); -LEPT_DLL extern JBCLASSER * jbRankHausInit ( l_int32 components, l_int32 maxwidth, l_int32 maxheight, l_int32 size, l_float32 rank ); -LEPT_DLL extern JBCLASSER * jbCorrelationInit ( l_int32 components, l_int32 maxwidth, l_int32 maxheight, l_float32 thresh, l_float32 weightfactor ); -LEPT_DLL extern JBCLASSER * jbCorrelationInitWithoutComponents ( l_int32 components, l_int32 maxwidth, l_int32 maxheight, l_float32 thresh, l_float32 weightfactor ); -LEPT_DLL extern l_ok jbAddPages ( JBCLASSER *classer, SARRAY *safiles ); -LEPT_DLL extern l_ok jbAddPage ( JBCLASSER *classer, PIX *pixs ); -LEPT_DLL extern l_ok jbAddPageComponents ( JBCLASSER *classer, PIX *pixs, BOXA *boxas, PIXA *pixas ); -LEPT_DLL extern l_ok jbClassifyRankHaus ( JBCLASSER *classer, BOXA *boxa, PIXA *pixas ); -LEPT_DLL extern l_int32 pixHaustest ( PIX *pix1, PIX *pix2, PIX *pix3, PIX *pix4, l_float32 delx, l_float32 dely, l_int32 maxdiffw, l_int32 maxdiffh ); -LEPT_DLL extern l_int32 pixRankHaustest ( PIX *pix1, PIX *pix2, PIX *pix3, PIX *pix4, l_float32 delx, l_float32 dely, l_int32 maxdiffw, l_int32 maxdiffh, l_int32 area1, l_int32 area3, l_float32 rank, l_int32 *tab8 ); -LEPT_DLL extern l_ok jbClassifyCorrelation ( JBCLASSER *classer, BOXA *boxa, PIXA *pixas ); -LEPT_DLL extern l_ok jbGetComponents ( PIX *pixs, l_int32 components, l_int32 maxwidth, l_int32 maxheight, BOXA **pboxad, PIXA **ppixad ); -LEPT_DLL extern l_ok pixWordMaskByDilation ( PIX *pixs, PIX **ppixm, l_int32 *psize, PIXA *pixadb ); -LEPT_DLL extern l_ok pixWordBoxesByDilation ( PIX *pixs, l_int32 minwidth, l_int32 minheight, l_int32 maxwidth, l_int32 maxheight, BOXA **pboxa, l_int32 *psize, PIXA *pixadb ); -LEPT_DLL extern PIXA * jbAccumulateComposites ( PIXAA *pixaa, NUMA **pna, PTA **pptat ); -LEPT_DLL extern PIXA * jbTemplatesFromComposites ( PIXA *pixac, NUMA *na ); -LEPT_DLL extern JBCLASSER * jbClasserCreate ( l_int32 method, l_int32 components ); -LEPT_DLL extern void jbClasserDestroy ( JBCLASSER **pclasser ); -LEPT_DLL extern JBDATA * jbDataSave ( JBCLASSER *classer ); -LEPT_DLL extern void jbDataDestroy ( JBDATA **pdata ); -LEPT_DLL extern l_ok jbDataWrite ( const char *rootout, JBDATA *jbdata ); -LEPT_DLL extern JBDATA * jbDataRead ( const char *rootname ); -LEPT_DLL extern PIXA * jbDataRender ( JBDATA *data, l_int32 debugflag ); -LEPT_DLL extern l_ok jbGetULCorners ( JBCLASSER *classer, PIX *pixs, BOXA *boxa ); -LEPT_DLL extern l_ok jbGetLLCorners ( JBCLASSER *classer ); -LEPT_DLL extern l_ok readHeaderJp2k ( const char *filename, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp ); -LEPT_DLL extern l_ok freadHeaderJp2k ( FILE *fp, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp ); -LEPT_DLL extern l_ok readHeaderMemJp2k ( const l_uint8 *data, size_t size, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp ); -LEPT_DLL extern l_int32 fgetJp2kResolution ( FILE *fp, l_int32 *pxres, l_int32 *pyres ); -LEPT_DLL extern PIX * pixReadJp2k ( const char *filename, l_uint32 reduction, BOX *box, l_int32 hint, l_int32 debug ); -LEPT_DLL extern PIX * pixReadStreamJp2k ( FILE *fp, l_uint32 reduction, BOX *box, l_int32 hint, l_int32 debug ); -LEPT_DLL extern l_ok pixWriteJp2k ( const char *filename, PIX *pix, l_int32 quality, l_int32 nlevels, l_int32 hint, l_int32 debug ); -LEPT_DLL extern l_ok pixWriteStreamJp2k ( FILE *fp, PIX *pix, l_int32 quality, l_int32 nlevels, l_int32 hint, l_int32 debug ); -LEPT_DLL extern PIX * pixReadMemJp2k ( const l_uint8 *data, size_t size, l_uint32 reduction, BOX *box, l_int32 hint, l_int32 debug ); -LEPT_DLL extern l_ok pixWriteMemJp2k ( l_uint8 **pdata, size_t *psize, PIX *pix, l_int32 quality, l_int32 nlevels, l_int32 hint, l_int32 debug ); -LEPT_DLL extern PIX * pixReadJpeg ( const char *filename, l_int32 cmapflag, l_int32 reduction, l_int32 *pnwarn, l_int32 hint ); -LEPT_DLL extern PIX * pixReadStreamJpeg ( FILE *fp, l_int32 cmapflag, l_int32 reduction, l_int32 *pnwarn, l_int32 hint ); -LEPT_DLL extern l_ok readHeaderJpeg ( const char *filename, l_int32 *pw, l_int32 *ph, l_int32 *pspp, l_int32 *pycck, l_int32 *pcmyk ); -LEPT_DLL extern l_ok freadHeaderJpeg ( FILE *fp, l_int32 *pw, l_int32 *ph, l_int32 *pspp, l_int32 *pycck, l_int32 *pcmyk ); -LEPT_DLL extern l_int32 fgetJpegResolution ( FILE *fp, l_int32 *pxres, l_int32 *pyres ); -LEPT_DLL extern l_int32 fgetJpegComment ( FILE *fp, l_uint8 **pcomment ); -LEPT_DLL extern l_ok pixWriteJpeg ( const char *filename, PIX *pix, l_int32 quality, l_int32 progressive ); -LEPT_DLL extern l_ok pixWriteStreamJpeg ( FILE *fp, PIX *pixs, l_int32 quality, l_int32 progressive ); -LEPT_DLL extern PIX * pixReadMemJpeg ( const l_uint8 *data, size_t size, l_int32 cmflag, l_int32 reduction, l_int32 *pnwarn, l_int32 hint ); -LEPT_DLL extern l_ok readHeaderMemJpeg ( const l_uint8 *data, size_t size, l_int32 *pw, l_int32 *ph, l_int32 *pspp, l_int32 *pycck, l_int32 *pcmyk ); -LEPT_DLL extern l_ok readResolutionMemJpeg ( const l_uint8 *data, size_t size, l_int32 *pxres, l_int32 *pyres ); -LEPT_DLL extern l_ok pixWriteMemJpeg ( l_uint8 **pdata, size_t *psize, PIX *pix, l_int32 quality, l_int32 progressive ); -LEPT_DLL extern l_ok pixSetChromaSampling ( PIX *pix, l_int32 sampling ); -LEPT_DLL extern L_KERNEL * kernelCreate ( l_int32 height, l_int32 width ); -LEPT_DLL extern void kernelDestroy ( L_KERNEL **pkel ); -LEPT_DLL extern L_KERNEL * kernelCopy ( L_KERNEL *kels ); -LEPT_DLL extern l_ok kernelGetElement ( L_KERNEL *kel, l_int32 row, l_int32 col, l_float32 *pval ); -LEPT_DLL extern l_ok kernelSetElement ( L_KERNEL *kel, l_int32 row, l_int32 col, l_float32 val ); -LEPT_DLL extern l_ok kernelGetParameters ( L_KERNEL *kel, l_int32 *psy, l_int32 *psx, l_int32 *pcy, l_int32 *pcx ); -LEPT_DLL extern l_ok kernelSetOrigin ( L_KERNEL *kel, l_int32 cy, l_int32 cx ); -LEPT_DLL extern l_ok kernelGetSum ( L_KERNEL *kel, l_float32 *psum ); -LEPT_DLL extern l_ok kernelGetMinMax ( L_KERNEL *kel, l_float32 *pmin, l_float32 *pmax ); -LEPT_DLL extern L_KERNEL * kernelNormalize ( L_KERNEL *kels, l_float32 normsum ); -LEPT_DLL extern L_KERNEL * kernelInvert ( L_KERNEL *kels ); -LEPT_DLL extern l_float32 ** create2dFloatArray ( l_int32 sy, l_int32 sx ); -LEPT_DLL extern L_KERNEL * kernelRead ( const char *fname ); -LEPT_DLL extern L_KERNEL * kernelReadStream ( FILE *fp ); -LEPT_DLL extern l_ok kernelWrite ( const char *fname, L_KERNEL *kel ); -LEPT_DLL extern l_ok kernelWriteStream ( FILE *fp, L_KERNEL *kel ); -LEPT_DLL extern L_KERNEL * kernelCreateFromString ( l_int32 h, l_int32 w, l_int32 cy, l_int32 cx, const char *kdata ); -LEPT_DLL extern L_KERNEL * kernelCreateFromFile ( const char *filename ); -LEPT_DLL extern L_KERNEL * kernelCreateFromPix ( PIX *pix, l_int32 cy, l_int32 cx ); -LEPT_DLL extern PIX * kernelDisplayInPix ( L_KERNEL *kel, l_int32 size, l_int32 gthick ); -LEPT_DLL extern NUMA * parseStringForNumbers ( const char *str, const char *seps ); -LEPT_DLL extern L_KERNEL * makeFlatKernel ( l_int32 height, l_int32 width, l_int32 cy, l_int32 cx ); -LEPT_DLL extern L_KERNEL * makeGaussianKernel ( l_int32 halfh, l_int32 halfw, l_float32 stdev, l_float32 max ); -LEPT_DLL extern l_ok makeGaussianKernelSep ( l_int32 halfh, l_int32 halfw, l_float32 stdev, l_float32 max, L_KERNEL **pkelx, L_KERNEL **pkely ); -LEPT_DLL extern L_KERNEL * makeDoGKernel ( l_int32 halfh, l_int32 halfw, l_float32 stdev, l_float32 ratio ); -LEPT_DLL extern char * getImagelibVersions ( void ); -LEPT_DLL extern void listDestroy ( DLLIST **phead ); -LEPT_DLL extern l_ok listAddToHead ( DLLIST **phead, void *data ); -LEPT_DLL extern l_ok listAddToTail ( DLLIST **phead, DLLIST **ptail, void *data ); -LEPT_DLL extern l_ok listInsertBefore ( DLLIST **phead, DLLIST *elem, void *data ); -LEPT_DLL extern l_ok listInsertAfter ( DLLIST **phead, DLLIST *elem, void *data ); -LEPT_DLL extern void * listRemoveElement ( DLLIST **phead, DLLIST *elem ); -LEPT_DLL extern void * listRemoveFromHead ( DLLIST **phead ); -LEPT_DLL extern void * listRemoveFromTail ( DLLIST **phead, DLLIST **ptail ); -LEPT_DLL extern DLLIST * listFindElement ( DLLIST *head, void *data ); -LEPT_DLL extern DLLIST * listFindTail ( DLLIST *head ); -LEPT_DLL extern l_int32 listGetCount ( DLLIST *head ); -LEPT_DLL extern l_ok listReverse ( DLLIST **phead ); -LEPT_DLL extern l_ok listJoin ( DLLIST **phead1, DLLIST **phead2 ); -LEPT_DLL extern L_AMAP * l_amapCreate ( l_int32 keytype ); -LEPT_DLL extern RB_TYPE * l_amapFind ( L_AMAP *m, RB_TYPE key ); -LEPT_DLL extern void l_amapInsert ( L_AMAP *m, RB_TYPE key, RB_TYPE value ); -LEPT_DLL extern void l_amapDelete ( L_AMAP *m, RB_TYPE key ); -LEPT_DLL extern void l_amapDestroy ( L_AMAP **pm ); -LEPT_DLL extern L_AMAP_NODE * l_amapGetFirst ( L_AMAP *m ); -LEPT_DLL extern L_AMAP_NODE * l_amapGetNext ( L_AMAP_NODE *n ); -LEPT_DLL extern L_AMAP_NODE * l_amapGetLast ( L_AMAP *m ); -LEPT_DLL extern L_AMAP_NODE * l_amapGetPrev ( L_AMAP_NODE *n ); -LEPT_DLL extern l_int32 l_amapSize ( L_AMAP *m ); -LEPT_DLL extern L_ASET * l_asetCreate ( l_int32 keytype ); -LEPT_DLL extern RB_TYPE * l_asetFind ( L_ASET *s, RB_TYPE key ); -LEPT_DLL extern void l_asetInsert ( L_ASET *s, RB_TYPE key ); -LEPT_DLL extern void l_asetDelete ( L_ASET *s, RB_TYPE key ); -LEPT_DLL extern void l_asetDestroy ( L_ASET **ps ); -LEPT_DLL extern L_ASET_NODE * l_asetGetFirst ( L_ASET *s ); -LEPT_DLL extern L_ASET_NODE * l_asetGetNext ( L_ASET_NODE *n ); -LEPT_DLL extern L_ASET_NODE * l_asetGetLast ( L_ASET *s ); -LEPT_DLL extern L_ASET_NODE * l_asetGetPrev ( L_ASET_NODE *n ); -LEPT_DLL extern l_int32 l_asetSize ( L_ASET *s ); -LEPT_DLL extern PIX * generateBinaryMaze ( l_int32 w, l_int32 h, l_int32 xi, l_int32 yi, l_float32 wallps, l_float32 ranis ); -LEPT_DLL extern PTA * pixSearchBinaryMaze ( PIX *pixs, l_int32 xi, l_int32 yi, l_int32 xf, l_int32 yf, PIX **ppixd ); -LEPT_DLL extern PTA * pixSearchGrayMaze ( PIX *pixs, l_int32 xi, l_int32 yi, l_int32 xf, l_int32 yf, PIX **ppixd ); -LEPT_DLL extern PIX * pixDilate ( PIX *pixd, PIX *pixs, SEL *sel ); -LEPT_DLL extern PIX * pixErode ( PIX *pixd, PIX *pixs, SEL *sel ); -LEPT_DLL extern PIX * pixHMT ( PIX *pixd, PIX *pixs, SEL *sel ); -LEPT_DLL extern PIX * pixOpen ( PIX *pixd, PIX *pixs, SEL *sel ); -LEPT_DLL extern PIX * pixClose ( PIX *pixd, PIX *pixs, SEL *sel ); -LEPT_DLL extern PIX * pixCloseSafe ( PIX *pixd, PIX *pixs, SEL *sel ); -LEPT_DLL extern PIX * pixOpenGeneralized ( PIX *pixd, PIX *pixs, SEL *sel ); -LEPT_DLL extern PIX * pixCloseGeneralized ( PIX *pixd, PIX *pixs, SEL *sel ); -LEPT_DLL extern PIX * pixDilateBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixErodeBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixOpenBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixCloseBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixCloseSafeBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern l_int32 selectComposableSels ( l_int32 size, l_int32 direction, SEL **psel1, SEL **psel2 ); -LEPT_DLL extern l_ok selectComposableSizes ( l_int32 size, l_int32 *pfactor1, l_int32 *pfactor2 ); -LEPT_DLL extern PIX * pixDilateCompBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixErodeCompBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixOpenCompBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixCloseCompBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixCloseSafeCompBrick ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern void resetMorphBoundaryCondition ( l_int32 bc ); -LEPT_DLL extern l_uint32 getMorphBorderPixelColor ( l_int32 type, l_int32 depth ); -LEPT_DLL extern PIX * pixExtractBoundary ( PIX *pixs, l_int32 type ); -LEPT_DLL extern PIX * pixMorphSequenceMasked ( PIX *pixs, PIX *pixm, const char *sequence, l_int32 dispsep ); -LEPT_DLL extern PIX * pixMorphSequenceByComponent ( PIX *pixs, const char *sequence, l_int32 connectivity, l_int32 minw, l_int32 minh, BOXA **pboxa ); -LEPT_DLL extern PIXA * pixaMorphSequenceByComponent ( PIXA *pixas, const char *sequence, l_int32 minw, l_int32 minh ); -LEPT_DLL extern PIX * pixMorphSequenceByRegion ( PIX *pixs, PIX *pixm, const char *sequence, l_int32 connectivity, l_int32 minw, l_int32 minh, BOXA **pboxa ); -LEPT_DLL extern PIXA * pixaMorphSequenceByRegion ( PIX *pixs, PIXA *pixam, const char *sequence, l_int32 minw, l_int32 minh ); -LEPT_DLL extern PIX * pixUnionOfMorphOps ( PIX *pixs, SELA *sela, l_int32 type ); -LEPT_DLL extern PIX * pixIntersectionOfMorphOps ( PIX *pixs, SELA *sela, l_int32 type ); -LEPT_DLL extern PIX * pixSelectiveConnCompFill ( PIX *pixs, l_int32 connectivity, l_int32 minw, l_int32 minh ); -LEPT_DLL extern l_ok pixRemoveMatchedPattern ( PIX *pixs, PIX *pixp, PIX *pixe, l_int32 x0, l_int32 y0, l_int32 dsize ); -LEPT_DLL extern PIX * pixDisplayMatchedPattern ( PIX *pixs, PIX *pixp, PIX *pixe, l_int32 x0, l_int32 y0, l_uint32 color, l_float32 scale, l_int32 nlevels ); -LEPT_DLL extern PIXA * pixaExtendByMorph ( PIXA *pixas, l_int32 type, l_int32 niters, SEL *sel, l_int32 include ); -LEPT_DLL extern PIXA * pixaExtendByScaling ( PIXA *pixas, NUMA *nasc, l_int32 type, l_int32 include ); -LEPT_DLL extern PIX * pixSeedfillMorph ( PIX *pixs, PIX *pixm, l_int32 maxiters, l_int32 connectivity ); -LEPT_DLL extern NUMA * pixRunHistogramMorph ( PIX *pixs, l_int32 runtype, l_int32 direction, l_int32 maxsize ); -LEPT_DLL extern PIX * pixTophat ( PIX *pixs, l_int32 hsize, l_int32 vsize, l_int32 type ); -LEPT_DLL extern PIX * pixHDome ( PIX *pixs, l_int32 height, l_int32 connectivity ); -LEPT_DLL extern PIX * pixFastTophat ( PIX *pixs, l_int32 xsize, l_int32 ysize, l_int32 type ); -LEPT_DLL extern PIX * pixMorphGradient ( PIX *pixs, l_int32 hsize, l_int32 vsize, l_int32 smoothing ); -LEPT_DLL extern PTA * pixaCentroids ( PIXA *pixa ); -LEPT_DLL extern l_ok pixCentroid ( PIX *pix, l_int32 *centtab, l_int32 *sumtab, l_float32 *pxave, l_float32 *pyave ); -LEPT_DLL extern PIX * pixDilateBrickDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixErodeBrickDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixOpenBrickDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixCloseBrickDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixDilateCompBrickDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixErodeCompBrickDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixOpenCompBrickDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixCloseCompBrickDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixDilateCompBrickExtendDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixErodeCompBrickExtendDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixOpenCompBrickExtendDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern PIX * pixCloseCompBrickExtendDwa ( PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern l_ok getExtendedCompositeParameters ( l_int32 size, l_int32 *pn, l_int32 *pextra, l_int32 *pactualsize ); -LEPT_DLL extern PIX * pixMorphSequence ( PIX *pixs, const char *sequence, l_int32 dispsep ); -LEPT_DLL extern PIX * pixMorphCompSequence ( PIX *pixs, const char *sequence, l_int32 dispsep ); -LEPT_DLL extern PIX * pixMorphSequenceDwa ( PIX *pixs, const char *sequence, l_int32 dispsep ); -LEPT_DLL extern PIX * pixMorphCompSequenceDwa ( PIX *pixs, const char *sequence, l_int32 dispsep ); -LEPT_DLL extern l_int32 morphSequenceVerify ( SARRAY *sa ); -LEPT_DLL extern PIX * pixGrayMorphSequence ( PIX *pixs, const char *sequence, l_int32 dispsep, l_int32 dispy ); -LEPT_DLL extern PIX * pixColorMorphSequence ( PIX *pixs, const char *sequence, l_int32 dispsep, l_int32 dispy ); -LEPT_DLL extern NUMA * numaCreate ( l_int32 n ); -LEPT_DLL extern NUMA * numaCreateFromIArray ( l_int32 *iarray, l_int32 size ); -LEPT_DLL extern NUMA * numaCreateFromFArray ( l_float32 *farray, l_int32 size, l_int32 copyflag ); -LEPT_DLL extern NUMA * numaCreateFromString ( const char *str ); -LEPT_DLL extern void numaDestroy ( NUMA **pna ); -LEPT_DLL extern NUMA * numaCopy ( NUMA *na ); -LEPT_DLL extern NUMA * numaClone ( NUMA *na ); -LEPT_DLL extern l_ok numaEmpty ( NUMA *na ); -LEPT_DLL extern l_ok numaAddNumber ( NUMA *na, l_float32 val ); -LEPT_DLL extern l_ok numaInsertNumber ( NUMA *na, l_int32 index, l_float32 val ); -LEPT_DLL extern l_ok numaRemoveNumber ( NUMA *na, l_int32 index ); -LEPT_DLL extern l_ok numaReplaceNumber ( NUMA *na, l_int32 index, l_float32 val ); -LEPT_DLL extern l_int32 numaGetCount ( NUMA *na ); -LEPT_DLL extern l_ok numaSetCount ( NUMA *na, l_int32 newcount ); -LEPT_DLL extern l_ok numaGetFValue ( NUMA *na, l_int32 index, l_float32 *pval ); -LEPT_DLL extern l_ok numaGetIValue ( NUMA *na, l_int32 index, l_int32 *pival ); -LEPT_DLL extern l_ok numaSetValue ( NUMA *na, l_int32 index, l_float32 val ); -LEPT_DLL extern l_ok numaShiftValue ( NUMA *na, l_int32 index, l_float32 diff ); -LEPT_DLL extern l_int32 * numaGetIArray ( NUMA *na ); -LEPT_DLL extern l_float32 * numaGetFArray ( NUMA *na, l_int32 copyflag ); -LEPT_DLL extern l_int32 numaGetRefcount ( NUMA *na ); -LEPT_DLL extern l_ok numaChangeRefcount ( NUMA *na, l_int32 delta ); -LEPT_DLL extern l_ok numaGetParameters ( NUMA *na, l_float32 *pstartx, l_float32 *pdelx ); -LEPT_DLL extern l_ok numaSetParameters ( NUMA *na, l_float32 startx, l_float32 delx ); -LEPT_DLL extern l_ok numaCopyParameters ( NUMA *nad, NUMA *nas ); -LEPT_DLL extern SARRAY * numaConvertToSarray ( NUMA *na, l_int32 size1, l_int32 size2, l_int32 addzeros, l_int32 type ); -LEPT_DLL extern NUMA * numaRead ( const char *filename ); -LEPT_DLL extern NUMA * numaReadStream ( FILE *fp ); -LEPT_DLL extern NUMA * numaReadMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok numaWriteDebug ( const char *filename, NUMA *na ); -LEPT_DLL extern l_ok numaWrite ( const char *filename, NUMA *na ); -LEPT_DLL extern l_ok numaWriteStream ( FILE *fp, NUMA *na ); -LEPT_DLL extern l_ok numaWriteStderr ( NUMA *na ); -LEPT_DLL extern l_ok numaWriteMem ( l_uint8 **pdata, size_t *psize, NUMA *na ); -LEPT_DLL extern NUMAA * numaaCreate ( l_int32 n ); -LEPT_DLL extern NUMAA * numaaCreateFull ( l_int32 nptr, l_int32 n ); -LEPT_DLL extern l_ok numaaTruncate ( NUMAA *naa ); -LEPT_DLL extern void numaaDestroy ( NUMAA **pnaa ); -LEPT_DLL extern l_ok numaaAddNuma ( NUMAA *naa, NUMA *na, l_int32 copyflag ); -LEPT_DLL extern l_int32 numaaGetCount ( NUMAA *naa ); -LEPT_DLL extern l_int32 numaaGetNumaCount ( NUMAA *naa, l_int32 index ); -LEPT_DLL extern l_int32 numaaGetNumberCount ( NUMAA *naa ); -LEPT_DLL extern NUMA ** numaaGetPtrArray ( NUMAA *naa ); -LEPT_DLL extern NUMA * numaaGetNuma ( NUMAA *naa, l_int32 index, l_int32 accessflag ); -LEPT_DLL extern l_ok numaaReplaceNuma ( NUMAA *naa, l_int32 index, NUMA *na ); -LEPT_DLL extern l_ok numaaGetValue ( NUMAA *naa, l_int32 i, l_int32 j, l_float32 *pfval, l_int32 *pival ); -LEPT_DLL extern l_ok numaaAddNumber ( NUMAA *naa, l_int32 index, l_float32 val ); -LEPT_DLL extern NUMAA * numaaRead ( const char *filename ); -LEPT_DLL extern NUMAA * numaaReadStream ( FILE *fp ); -LEPT_DLL extern NUMAA * numaaReadMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok numaaWrite ( const char *filename, NUMAA *naa ); -LEPT_DLL extern l_ok numaaWriteStream ( FILE *fp, NUMAA *naa ); -LEPT_DLL extern l_ok numaaWriteMem ( l_uint8 **pdata, size_t *psize, NUMAA *naa ); -LEPT_DLL extern NUMA * numaArithOp ( NUMA *nad, NUMA *na1, NUMA *na2, l_int32 op ); -LEPT_DLL extern NUMA * numaLogicalOp ( NUMA *nad, NUMA *na1, NUMA *na2, l_int32 op ); -LEPT_DLL extern NUMA * numaInvert ( NUMA *nad, NUMA *nas ); -LEPT_DLL extern l_int32 numaSimilar ( NUMA *na1, NUMA *na2, l_float32 maxdiff, l_int32 *psimilar ); -LEPT_DLL extern l_ok numaAddToNumber ( NUMA *na, l_int32 index, l_float32 val ); -LEPT_DLL extern l_ok numaGetMin ( NUMA *na, l_float32 *pminval, l_int32 *piminloc ); -LEPT_DLL extern l_ok numaGetMax ( NUMA *na, l_float32 *pmaxval, l_int32 *pimaxloc ); -LEPT_DLL extern l_ok numaGetSum ( NUMA *na, l_float32 *psum ); -LEPT_DLL extern NUMA * numaGetPartialSums ( NUMA *na ); -LEPT_DLL extern l_ok numaGetSumOnInterval ( NUMA *na, l_int32 first, l_int32 last, l_float32 *psum ); -LEPT_DLL extern l_ok numaHasOnlyIntegers ( NUMA *na, l_int32 maxsamples, l_int32 *pallints ); -LEPT_DLL extern NUMA * numaSubsample ( NUMA *nas, l_int32 subfactor ); -LEPT_DLL extern NUMA * numaMakeDelta ( NUMA *nas ); -LEPT_DLL extern NUMA * numaMakeSequence ( l_float32 startval, l_float32 increment, l_int32 size ); -LEPT_DLL extern NUMA * numaMakeConstant ( l_float32 val, l_int32 size ); -LEPT_DLL extern NUMA * numaMakeAbsValue ( NUMA *nad, NUMA *nas ); -LEPT_DLL extern NUMA * numaAddBorder ( NUMA *nas, l_int32 left, l_int32 right, l_float32 val ); -LEPT_DLL extern NUMA * numaAddSpecifiedBorder ( NUMA *nas, l_int32 left, l_int32 right, l_int32 type ); -LEPT_DLL extern NUMA * numaRemoveBorder ( NUMA *nas, l_int32 left, l_int32 right ); -LEPT_DLL extern l_ok numaCountNonzeroRuns ( NUMA *na, l_int32 *pcount ); -LEPT_DLL extern l_ok numaGetNonzeroRange ( NUMA *na, l_float32 eps, l_int32 *pfirst, l_int32 *plast ); -LEPT_DLL extern l_ok numaGetCountRelativeToZero ( NUMA *na, l_int32 type, l_int32 *pcount ); -LEPT_DLL extern NUMA * numaClipToInterval ( NUMA *nas, l_int32 first, l_int32 last ); -LEPT_DLL extern NUMA * numaMakeThresholdIndicator ( NUMA *nas, l_float32 thresh, l_int32 type ); -LEPT_DLL extern NUMA * numaUniformSampling ( NUMA *nas, l_int32 nsamp ); -LEPT_DLL extern NUMA * numaReverse ( NUMA *nad, NUMA *nas ); -LEPT_DLL extern NUMA * numaLowPassIntervals ( NUMA *nas, l_float32 thresh, l_float32 maxn ); -LEPT_DLL extern NUMA * numaThresholdEdges ( NUMA *nas, l_float32 thresh1, l_float32 thresh2, l_float32 maxn ); -LEPT_DLL extern l_int32 numaGetSpanValues ( NUMA *na, l_int32 span, l_int32 *pstart, l_int32 *pend ); -LEPT_DLL extern l_int32 numaGetEdgeValues ( NUMA *na, l_int32 edge, l_int32 *pstart, l_int32 *pend, l_int32 *psign ); -LEPT_DLL extern l_ok numaInterpolateEqxVal ( l_float32 startx, l_float32 deltax, NUMA *nay, l_int32 type, l_float32 xval, l_float32 *pyval ); -LEPT_DLL extern l_ok numaInterpolateArbxVal ( NUMA *nax, NUMA *nay, l_int32 type, l_float32 xval, l_float32 *pyval ); -LEPT_DLL extern l_ok numaInterpolateEqxInterval ( l_float32 startx, l_float32 deltax, NUMA *nasy, l_int32 type, l_float32 x0, l_float32 x1, l_int32 npts, NUMA **pnax, NUMA **pnay ); -LEPT_DLL extern l_ok numaInterpolateArbxInterval ( NUMA *nax, NUMA *nay, l_int32 type, l_float32 x0, l_float32 x1, l_int32 npts, NUMA **pnadx, NUMA **pnady ); -LEPT_DLL extern l_ok numaFitMax ( NUMA *na, l_float32 *pmaxval, NUMA *naloc, l_float32 *pmaxloc ); -LEPT_DLL extern l_ok numaDifferentiateInterval ( NUMA *nax, NUMA *nay, l_float32 x0, l_float32 x1, l_int32 npts, NUMA **pnadx, NUMA **pnady ); -LEPT_DLL extern l_ok numaIntegrateInterval ( NUMA *nax, NUMA *nay, l_float32 x0, l_float32 x1, l_int32 npts, l_float32 *psum ); -LEPT_DLL extern l_ok numaSortGeneral ( NUMA *na, NUMA **pnasort, NUMA **pnaindex, NUMA **pnainvert, l_int32 sortorder, l_int32 sorttype ); -LEPT_DLL extern NUMA * numaSortAutoSelect ( NUMA *nas, l_int32 sortorder ); -LEPT_DLL extern NUMA * numaSortIndexAutoSelect ( NUMA *nas, l_int32 sortorder ); -LEPT_DLL extern l_int32 numaChooseSortType ( NUMA *nas ); -LEPT_DLL extern NUMA * numaSort ( NUMA *naout, NUMA *nain, l_int32 sortorder ); -LEPT_DLL extern NUMA * numaBinSort ( NUMA *nas, l_int32 sortorder ); -LEPT_DLL extern NUMA * numaGetSortIndex ( NUMA *na, l_int32 sortorder ); -LEPT_DLL extern NUMA * numaGetBinSortIndex ( NUMA *nas, l_int32 sortorder ); -LEPT_DLL extern NUMA * numaSortByIndex ( NUMA *nas, NUMA *naindex ); -LEPT_DLL extern l_int32 numaIsSorted ( NUMA *nas, l_int32 sortorder, l_int32 *psorted ); -LEPT_DLL extern l_ok numaSortPair ( NUMA *nax, NUMA *nay, l_int32 sortorder, NUMA **pnasx, NUMA **pnasy ); -LEPT_DLL extern NUMA * numaInvertMap ( NUMA *nas ); -LEPT_DLL extern NUMA * numaPseudorandomSequence ( l_int32 size, l_int32 seed ); -LEPT_DLL extern NUMA * numaRandomPermutation ( NUMA *nas, l_int32 seed ); -LEPT_DLL extern l_ok numaGetRankValue ( NUMA *na, l_float32 fract, NUMA *nasort, l_int32 usebins, l_float32 *pval ); -LEPT_DLL extern l_ok numaGetMedian ( NUMA *na, l_float32 *pval ); -LEPT_DLL extern l_ok numaGetBinnedMedian ( NUMA *na, l_int32 *pval ); -LEPT_DLL extern l_ok numaGetMeanDevFromMedian ( NUMA *na, l_float32 med, l_float32 *pdev ); -LEPT_DLL extern l_ok numaGetMedianDevFromMedian ( NUMA *na, l_float32 *pmed, l_float32 *pdev ); -LEPT_DLL extern l_ok numaGetMode ( NUMA *na, l_float32 *pval, l_int32 *pcount ); -LEPT_DLL extern l_ok numaJoin ( NUMA *nad, NUMA *nas, l_int32 istart, l_int32 iend ); -LEPT_DLL extern l_ok numaaJoin ( NUMAA *naad, NUMAA *naas, l_int32 istart, l_int32 iend ); -LEPT_DLL extern NUMA * numaaFlattenToNuma ( NUMAA *naa ); -LEPT_DLL extern NUMA * numaErode ( NUMA *nas, l_int32 size ); -LEPT_DLL extern NUMA * numaDilate ( NUMA *nas, l_int32 size ); -LEPT_DLL extern NUMA * numaOpen ( NUMA *nas, l_int32 size ); -LEPT_DLL extern NUMA * numaClose ( NUMA *nas, l_int32 size ); -LEPT_DLL extern NUMA * numaTransform ( NUMA *nas, l_float32 shift, l_float32 scale ); -LEPT_DLL extern l_ok numaSimpleStats ( NUMA *na, l_int32 first, l_int32 last, l_float32 *pmean, l_float32 *pvar, l_float32 *prvar ); -LEPT_DLL extern l_ok numaWindowedStats ( NUMA *nas, l_int32 wc, NUMA **pnam, NUMA **pnams, NUMA **pnav, NUMA **pnarv ); -LEPT_DLL extern NUMA * numaWindowedMean ( NUMA *nas, l_int32 wc ); -LEPT_DLL extern NUMA * numaWindowedMeanSquare ( NUMA *nas, l_int32 wc ); -LEPT_DLL extern l_ok numaWindowedVariance ( NUMA *nam, NUMA *nams, NUMA **pnav, NUMA **pnarv ); -LEPT_DLL extern NUMA * numaWindowedMedian ( NUMA *nas, l_int32 halfwin ); -LEPT_DLL extern NUMA * numaConvertToInt ( NUMA *nas ); -LEPT_DLL extern NUMA * numaMakeHistogram ( NUMA *na, l_int32 maxbins, l_int32 *pbinsize, l_int32 *pbinstart ); -LEPT_DLL extern NUMA * numaMakeHistogramAuto ( NUMA *na, l_int32 maxbins ); -LEPT_DLL extern NUMA * numaMakeHistogramClipped ( NUMA *na, l_float32 binsize, l_float32 maxsize ); -LEPT_DLL extern NUMA * numaRebinHistogram ( NUMA *nas, l_int32 newsize ); -LEPT_DLL extern NUMA * numaNormalizeHistogram ( NUMA *nas, l_float32 tsum ); -LEPT_DLL extern l_ok numaGetStatsUsingHistogram ( NUMA *na, l_int32 maxbins, l_float32 *pmin, l_float32 *pmax, l_float32 *pmean, l_float32 *pvariance, l_float32 *pmedian, l_float32 rank, l_float32 *prval, NUMA **phisto ); -LEPT_DLL extern l_ok numaGetHistogramStats ( NUMA *nahisto, l_float32 startx, l_float32 deltax, l_float32 *pxmean, l_float32 *pxmedian, l_float32 *pxmode, l_float32 *pxvariance ); -LEPT_DLL extern l_ok numaGetHistogramStatsOnInterval ( NUMA *nahisto, l_float32 startx, l_float32 deltax, l_int32 ifirst, l_int32 ilast, l_float32 *pxmean, l_float32 *pxmedian, l_float32 *pxmode, l_float32 *pxvariance ); -LEPT_DLL extern l_ok numaMakeRankFromHistogram ( l_float32 startx, l_float32 deltax, NUMA *nasy, l_int32 npts, NUMA **pnax, NUMA **pnay ); -LEPT_DLL extern l_ok numaHistogramGetRankFromVal ( NUMA *na, l_float32 rval, l_float32 *prank ); -LEPT_DLL extern l_ok numaHistogramGetValFromRank ( NUMA *na, l_float32 rank, l_float32 *prval ); -LEPT_DLL extern l_ok numaDiscretizeRankAndIntensity ( NUMA *na, l_int32 nbins, NUMA **pnarbin, NUMA **pnam, NUMA **pnar, NUMA **pnabb ); -LEPT_DLL extern l_ok numaGetRankBinValues ( NUMA *na, l_int32 nbins, NUMA **pnarbin, NUMA **pnam ); -LEPT_DLL extern l_ok numaSplitDistribution ( NUMA *na, l_float32 scorefract, l_int32 *psplitindex, l_float32 *pave1, l_float32 *pave2, l_float32 *pnum1, l_float32 *pnum2, NUMA **pnascore ); -LEPT_DLL extern l_ok grayHistogramsToEMD ( NUMAA *naa1, NUMAA *naa2, NUMA **pnad ); -LEPT_DLL extern l_ok numaEarthMoverDistance ( NUMA *na1, NUMA *na2, l_float32 *pdist ); -LEPT_DLL extern l_ok grayInterHistogramStats ( NUMAA *naa, l_int32 wc, NUMA **pnam, NUMA **pnams, NUMA **pnav, NUMA **pnarv ); -LEPT_DLL extern NUMA * numaFindPeaks ( NUMA *nas, l_int32 nmax, l_float32 fract1, l_float32 fract2 ); -LEPT_DLL extern NUMA * numaFindExtrema ( NUMA *nas, l_float32 delta, NUMA **pnav ); -LEPT_DLL extern l_ok numaFindLocForThreshold ( NUMA *na, l_int32 skip, l_int32 *pthresh, l_float32 *pfract ); -LEPT_DLL extern l_ok numaCountReversals ( NUMA *nas, l_float32 minreversal, l_int32 *pnr, l_float32 *prd ); -LEPT_DLL extern l_ok numaSelectCrossingThreshold ( NUMA *nax, NUMA *nay, l_float32 estthresh, l_float32 *pbestthresh ); -LEPT_DLL extern NUMA * numaCrossingsByThreshold ( NUMA *nax, NUMA *nay, l_float32 thresh ); -LEPT_DLL extern NUMA * numaCrossingsByPeaks ( NUMA *nax, NUMA *nay, l_float32 delta ); -LEPT_DLL extern l_ok numaEvalBestHaarParameters ( NUMA *nas, l_float32 relweight, l_int32 nwidth, l_int32 nshift, l_float32 minwidth, l_float32 maxwidth, l_float32 *pbestwidth, l_float32 *pbestshift, l_float32 *pbestscore ); -LEPT_DLL extern l_ok numaEvalHaarSum ( NUMA *nas, l_float32 width, l_float32 shift, l_float32 relweight, l_float32 *pscore ); -LEPT_DLL extern NUMA * genConstrainedNumaInRange ( l_int32 first, l_int32 last, l_int32 nmax, l_int32 use_pairs ); -LEPT_DLL extern l_ok pixGetRegionsBinary ( PIX *pixs, PIX **ppixhm, PIX **ppixtm, PIX **ppixtb, PIXA *pixadb ); -LEPT_DLL extern PIX * pixGenHalftoneMask ( PIX *pixs, PIX **ppixtext, l_int32 *phtfound, l_int32 debug ); -LEPT_DLL extern PIX * pixGenerateHalftoneMask ( PIX *pixs, PIX **ppixtext, l_int32 *phtfound, PIXA *pixadb ); -LEPT_DLL extern PIX * pixGenTextlineMask ( PIX *pixs, PIX **ppixvws, l_int32 *ptlfound, PIXA *pixadb ); -LEPT_DLL extern PIX * pixGenTextblockMask ( PIX *pixs, PIX *pixvws, PIXA *pixadb ); -LEPT_DLL extern BOX * pixFindPageForeground ( PIX *pixs, l_int32 threshold, l_int32 mindist, l_int32 erasedist, l_int32 showmorph, PIXAC *pixac ); -LEPT_DLL extern l_ok pixSplitIntoCharacters ( PIX *pixs, l_int32 minw, l_int32 minh, BOXA **pboxa, PIXA **ppixa, PIX **ppixdebug ); -LEPT_DLL extern BOXA * pixSplitComponentWithProfile ( PIX *pixs, l_int32 delta, l_int32 mindel, PIX **ppixdebug ); -LEPT_DLL extern PIXA * pixExtractTextlines ( PIX *pixs, l_int32 maxw, l_int32 maxh, l_int32 minw, l_int32 minh, l_int32 adjw, l_int32 adjh, PIXA *pixadb ); -LEPT_DLL extern PIXA * pixExtractRawTextlines ( PIX *pixs, l_int32 maxw, l_int32 maxh, l_int32 adjw, l_int32 adjh, PIXA *pixadb ); -LEPT_DLL extern l_ok pixCountTextColumns ( PIX *pixs, l_float32 deltafract, l_float32 peakfract, l_float32 clipfract, l_int32 *pncols, PIXA *pixadb ); -LEPT_DLL extern l_ok pixDecideIfText ( PIX *pixs, BOX *box, l_int32 *pistext, PIXA *pixadb ); -LEPT_DLL extern l_ok pixFindThreshFgExtent ( PIX *pixs, l_int32 thresh, l_int32 *ptop, l_int32 *pbot ); -LEPT_DLL extern l_ok pixDecideIfTable ( PIX *pixs, BOX *box, l_int32 orient, l_int32 *pscore, PIXA *pixadb ); -LEPT_DLL extern PIX * pixPrepare1bpp ( PIX *pixs, BOX *box, l_float32 cropfract, l_int32 outres ); -LEPT_DLL extern l_ok pixEstimateBackground ( PIX *pixs, l_int32 darkthresh, l_float32 edgecrop, l_int32 *pbg ); -LEPT_DLL extern l_ok pixFindLargeRectangles ( PIX *pixs, l_int32 polarity, l_int32 nrect, BOXA **pboxa, PIX **ppixdb ); -LEPT_DLL extern l_ok pixFindLargestRectangle ( PIX *pixs, l_int32 polarity, BOX **pbox, PIX **ppixdb ); -LEPT_DLL extern BOX * pixFindRectangleInCC ( PIX *pixs, BOX *boxs, l_float32 fract, l_int32 dir, l_int32 select, l_int32 debug ); -LEPT_DLL extern PIX * pixAutoPhotoinvert ( PIX *pixs, l_int32 thresh, PIX **ppixm, PIXA *pixadb ); -LEPT_DLL extern l_ok pixSetSelectCmap ( PIX *pixs, BOX *box, l_int32 sindex, l_int32 rval, l_int32 gval, l_int32 bval ); -LEPT_DLL extern l_ok pixColorGrayRegionsCmap ( PIX *pixs, BOXA *boxa, l_int32 type, l_int32 rval, l_int32 gval, l_int32 bval ); -LEPT_DLL extern l_ok pixColorGrayCmap ( PIX *pixs, BOX *box, l_int32 type, l_int32 rval, l_int32 gval, l_int32 bval ); -LEPT_DLL extern l_ok pixColorGrayMaskedCmap ( PIX *pixs, PIX *pixm, l_int32 type, l_int32 rval, l_int32 gval, l_int32 bval ); -LEPT_DLL extern l_ok addColorizedGrayToCmap ( PIXCMAP *cmap, l_int32 type, l_int32 rval, l_int32 gval, l_int32 bval, NUMA **pna ); -LEPT_DLL extern l_ok pixSetSelectMaskedCmap ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 sindex, l_int32 rval, l_int32 gval, l_int32 bval ); -LEPT_DLL extern l_ok pixSetMaskedCmap ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 rval, l_int32 gval, l_int32 bval ); -LEPT_DLL extern char * parseForProtos ( const char *filein, const char *prestring ); -LEPT_DLL extern l_ok partifyFiles ( const char *dirname, const char *substr, l_int32 nparts, const char *outroot, const char *debugfile ); -LEPT_DLL extern l_ok partifyPixac ( PIXAC *pixac, l_int32 nparts, const char *outroot, PIXA *pixadb ); -LEPT_DLL extern BOXA * boxaGetWhiteblocks ( BOXA *boxas, BOX *box, l_int32 sortflag, l_int32 maxboxes, l_float32 maxoverlap, l_int32 maxperim, l_float32 fract, l_int32 maxpops ); -LEPT_DLL extern BOXA * boxaPruneSortedOnOverlap ( BOXA *boxas, l_float32 maxoverlap ); -LEPT_DLL extern l_ok convertFilesToPdf ( const char *dirname, const char *substr, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, const char *fileout ); -LEPT_DLL extern l_ok saConvertFilesToPdf ( SARRAY *sa, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, const char *fileout ); -LEPT_DLL extern l_ok saConvertFilesToPdfData ( SARRAY *sa, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, l_uint8 **pdata, size_t *pnbytes ); -LEPT_DLL extern l_ok selectDefaultPdfEncoding ( PIX *pix, l_int32 *ptype ); -LEPT_DLL extern l_ok convertUnscaledFilesToPdf ( const char *dirname, const char *substr, const char *title, const char *fileout ); -LEPT_DLL extern l_ok saConvertUnscaledFilesToPdf ( SARRAY *sa, const char *title, const char *fileout ); -LEPT_DLL extern l_ok saConvertUnscaledFilesToPdfData ( SARRAY *sa, const char *title, l_uint8 **pdata, size_t *pnbytes ); -LEPT_DLL extern l_ok convertUnscaledToPdfData ( const char *fname, const char *title, l_uint8 **pdata, size_t *pnbytes ); -LEPT_DLL extern l_ok pixaConvertToPdf ( PIXA *pixa, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, const char *fileout ); -LEPT_DLL extern l_ok pixaConvertToPdfData ( PIXA *pixa, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, l_uint8 **pdata, size_t *pnbytes ); -LEPT_DLL extern l_ok convertToPdf ( const char *filein, l_int32 type, l_int32 quality, const char *fileout, l_int32 x, l_int32 y, l_int32 res, const char *title, L_PDF_DATA **plpd, l_int32 position ); -LEPT_DLL extern l_ok convertImageDataToPdf ( l_uint8 *imdata, size_t size, l_int32 type, l_int32 quality, const char *fileout, l_int32 x, l_int32 y, l_int32 res, const char *title, L_PDF_DATA **plpd, l_int32 position ); -LEPT_DLL extern l_ok convertToPdfData ( const char *filein, l_int32 type, l_int32 quality, l_uint8 **pdata, size_t *pnbytes, l_int32 x, l_int32 y, l_int32 res, const char *title, L_PDF_DATA **plpd, l_int32 position ); -LEPT_DLL extern l_ok convertImageDataToPdfData ( l_uint8 *imdata, size_t size, l_int32 type, l_int32 quality, l_uint8 **pdata, size_t *pnbytes, l_int32 x, l_int32 y, l_int32 res, const char *title, L_PDF_DATA **plpd, l_int32 position ); -LEPT_DLL extern l_ok pixConvertToPdf ( PIX *pix, l_int32 type, l_int32 quality, const char *fileout, l_int32 x, l_int32 y, l_int32 res, const char *title, L_PDF_DATA **plpd, l_int32 position ); -LEPT_DLL extern l_ok pixWriteStreamPdf ( FILE *fp, PIX *pix, l_int32 res, const char *title ); -LEPT_DLL extern l_ok pixWriteMemPdf ( l_uint8 **pdata, size_t *pnbytes, PIX *pix, l_int32 res, const char *title ); -LEPT_DLL extern l_ok convertSegmentedFilesToPdf ( const char *dirname, const char *substr, l_int32 res, l_int32 type, l_int32 thresh, BOXAA *baa, l_int32 quality, l_float32 scalefactor, const char *title, const char *fileout ); -LEPT_DLL extern BOXAA * convertNumberedMasksToBoxaa ( const char *dirname, const char *substr, l_int32 numpre, l_int32 numpost ); -LEPT_DLL extern l_ok convertToPdfSegmented ( const char *filein, l_int32 res, l_int32 type, l_int32 thresh, BOXA *boxa, l_int32 quality, l_float32 scalefactor, const char *title, const char *fileout ); -LEPT_DLL extern l_ok pixConvertToPdfSegmented ( PIX *pixs, l_int32 res, l_int32 type, l_int32 thresh, BOXA *boxa, l_int32 quality, l_float32 scalefactor, const char *title, const char *fileout ); -LEPT_DLL extern l_ok convertToPdfDataSegmented ( const char *filein, l_int32 res, l_int32 type, l_int32 thresh, BOXA *boxa, l_int32 quality, l_float32 scalefactor, const char *title, l_uint8 **pdata, size_t *pnbytes ); -LEPT_DLL extern l_ok pixConvertToPdfDataSegmented ( PIX *pixs, l_int32 res, l_int32 type, l_int32 thresh, BOXA *boxa, l_int32 quality, l_float32 scalefactor, const char *title, l_uint8 **pdata, size_t *pnbytes ); -LEPT_DLL extern l_ok concatenatePdf ( const char *dirname, const char *substr, const char *fileout ); -LEPT_DLL extern l_ok saConcatenatePdf ( SARRAY *sa, const char *fileout ); -LEPT_DLL extern l_ok ptraConcatenatePdf ( L_PTRA *pa, const char *fileout ); -LEPT_DLL extern l_ok concatenatePdfToData ( const char *dirname, const char *substr, l_uint8 **pdata, size_t *pnbytes ); -LEPT_DLL extern l_ok saConcatenatePdfToData ( SARRAY *sa, l_uint8 **pdata, size_t *pnbytes ); -LEPT_DLL extern l_ok pixConvertToPdfData ( PIX *pix, l_int32 type, l_int32 quality, l_uint8 **pdata, size_t *pnbytes, l_int32 x, l_int32 y, l_int32 res, const char *title, L_PDF_DATA **plpd, l_int32 position ); -LEPT_DLL extern l_ok ptraConcatenatePdfToData ( L_PTRA *pa_data, SARRAY *sa, l_uint8 **pdata, size_t *pnbytes ); -LEPT_DLL extern l_ok convertTiffMultipageToPdf ( const char *filein, const char *fileout ); -LEPT_DLL extern l_ok l_generateCIDataForPdf ( const char *fname, PIX *pix, l_int32 quality, L_COMP_DATA **pcid ); -LEPT_DLL extern L_COMP_DATA * l_generateFlateDataPdf ( const char *fname, PIX *pixs ); -LEPT_DLL extern L_COMP_DATA * l_generateJpegData ( const char *fname, l_int32 ascii85flag ); -LEPT_DLL extern L_COMP_DATA * l_generateJpegDataMem ( l_uint8 *data, size_t nbytes, l_int32 ascii85flag ); -LEPT_DLL extern l_ok l_generateCIData ( const char *fname, l_int32 type, l_int32 quality, l_int32 ascii85, L_COMP_DATA **pcid ); -LEPT_DLL extern l_ok pixGenerateCIData ( PIX *pixs, l_int32 type, l_int32 quality, l_int32 ascii85, L_COMP_DATA **pcid ); -LEPT_DLL extern L_COMP_DATA * l_generateFlateData ( const char *fname, l_int32 ascii85flag ); -LEPT_DLL extern L_COMP_DATA * l_generateG4Data ( const char *fname, l_int32 ascii85flag ); -LEPT_DLL extern l_ok cidConvertToPdfData ( L_COMP_DATA *cid, const char *title, l_uint8 **pdata, size_t *pnbytes ); -LEPT_DLL extern void l_CIDataDestroy ( L_COMP_DATA **pcid ); -LEPT_DLL extern void l_pdfSetG4ImageMask ( l_int32 flag ); -LEPT_DLL extern void l_pdfSetDateAndVersion ( l_int32 flag ); -LEPT_DLL extern void setPixMemoryManager ( alloc_fn allocator, dealloc_fn deallocator ); -LEPT_DLL extern PIX * pixCreate ( l_int32 width, l_int32 height, l_int32 depth ); -LEPT_DLL extern PIX * pixCreateNoInit ( l_int32 width, l_int32 height, l_int32 depth ); -LEPT_DLL extern PIX * pixCreateTemplate ( const PIX *pixs ); -LEPT_DLL extern PIX * pixCreateTemplateNoInit ( const PIX *pixs ); -LEPT_DLL extern PIX * pixCreateWithCmap ( l_int32 width, l_int32 height, l_int32 depth, l_int32 initcolor ); -LEPT_DLL extern PIX * pixCreateHeader ( l_int32 width, l_int32 height, l_int32 depth ); -LEPT_DLL extern PIX * pixClone ( PIX *pixs ); -LEPT_DLL extern void pixDestroy ( PIX **ppix ); -LEPT_DLL extern PIX * pixCopy ( PIX *pixd, const PIX *pixs ); -LEPT_DLL extern l_ok pixResizeImageData ( PIX *pixd, const PIX *pixs ); -LEPT_DLL extern l_ok pixCopyColormap ( PIX *pixd, const PIX *pixs ); -LEPT_DLL extern l_int32 pixSizesEqual ( const PIX *pix1, const PIX *pix2 ); -LEPT_DLL extern l_ok pixTransferAllData ( PIX *pixd, PIX **ppixs, l_int32 copytext, l_int32 copyformat ); -LEPT_DLL extern l_ok pixSwapAndDestroy ( PIX **ppixd, PIX **ppixs ); -LEPT_DLL extern l_int32 pixGetWidth ( const PIX *pix ); -LEPT_DLL extern l_int32 pixSetWidth ( PIX *pix, l_int32 width ); -LEPT_DLL extern l_int32 pixGetHeight ( const PIX *pix ); -LEPT_DLL extern l_int32 pixSetHeight ( PIX *pix, l_int32 height ); -LEPT_DLL extern l_int32 pixGetDepth ( const PIX *pix ); -LEPT_DLL extern l_int32 pixSetDepth ( PIX *pix, l_int32 depth ); -LEPT_DLL extern l_ok pixGetDimensions ( const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd ); -LEPT_DLL extern l_ok pixSetDimensions ( PIX *pix, l_int32 w, l_int32 h, l_int32 d ); -LEPT_DLL extern l_ok pixCopyDimensions ( PIX *pixd, const PIX *pixs ); -LEPT_DLL extern l_int32 pixGetSpp ( const PIX *pix ); -LEPT_DLL extern l_int32 pixSetSpp ( PIX *pix, l_int32 spp ); -LEPT_DLL extern l_ok pixCopySpp ( PIX *pixd, const PIX *pixs ); -LEPT_DLL extern l_int32 pixGetWpl ( const PIX *pix ); -LEPT_DLL extern l_int32 pixSetWpl ( PIX *pix, l_int32 wpl ); -LEPT_DLL extern l_int32 pixGetRefcount ( const PIX *pix ); -LEPT_DLL extern l_int32 pixChangeRefcount ( PIX *pix, l_int32 delta ); -LEPT_DLL extern l_int32 pixGetXRes ( const PIX *pix ); -LEPT_DLL extern l_int32 pixSetXRes ( PIX *pix, l_int32 res ); -LEPT_DLL extern l_int32 pixGetYRes ( const PIX *pix ); -LEPT_DLL extern l_int32 pixSetYRes ( PIX *pix, l_int32 res ); -LEPT_DLL extern l_ok pixGetResolution ( const PIX *pix, l_int32 *pxres, l_int32 *pyres ); -LEPT_DLL extern l_ok pixSetResolution ( PIX *pix, l_int32 xres, l_int32 yres ); -LEPT_DLL extern l_int32 pixCopyResolution ( PIX *pixd, const PIX *pixs ); -LEPT_DLL extern l_int32 pixScaleResolution ( PIX *pix, l_float32 xscale, l_float32 yscale ); -LEPT_DLL extern l_int32 pixGetInputFormat ( const PIX *pix ); -LEPT_DLL extern l_int32 pixSetInputFormat ( PIX *pix, l_int32 informat ); -LEPT_DLL extern l_int32 pixCopyInputFormat ( PIX *pixd, const PIX *pixs ); -LEPT_DLL extern l_int32 pixSetSpecial ( PIX *pix, l_int32 special ); -LEPT_DLL extern char * pixGetText ( PIX *pix ); -LEPT_DLL extern l_ok pixSetText ( PIX *pix, const char *textstring ); -LEPT_DLL extern l_ok pixAddText ( PIX *pix, const char *textstring ); -LEPT_DLL extern l_int32 pixCopyText ( PIX *pixd, const PIX *pixs ); -LEPT_DLL extern PIXCMAP * pixGetColormap ( PIX *pix ); -LEPT_DLL extern l_ok pixSetColormap ( PIX *pix, PIXCMAP *colormap ); -LEPT_DLL extern l_ok pixDestroyColormap ( PIX *pix ); -LEPT_DLL extern l_uint32 * pixGetData ( PIX *pix ); -LEPT_DLL extern l_int32 pixSetData ( PIX *pix, l_uint32 *data ); -LEPT_DLL extern l_uint32 * pixExtractData ( PIX *pixs ); -LEPT_DLL extern l_int32 pixFreeData ( PIX *pix ); -LEPT_DLL extern void ** pixGetLinePtrs ( PIX *pix, l_int32 *psize ); -LEPT_DLL extern l_ok pixPrintStreamInfo ( FILE *fp, const PIX *pix, const char *text ); -LEPT_DLL extern l_ok pixGetPixel ( PIX *pix, l_int32 x, l_int32 y, l_uint32 *pval ); -LEPT_DLL extern l_ok pixSetPixel ( PIX *pix, l_int32 x, l_int32 y, l_uint32 val ); -LEPT_DLL extern l_ok pixGetRGBPixel ( PIX *pix, l_int32 x, l_int32 y, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); -LEPT_DLL extern l_ok pixSetRGBPixel ( PIX *pix, l_int32 x, l_int32 y, l_int32 rval, l_int32 gval, l_int32 bval ); -LEPT_DLL extern l_ok pixSetCmapPixel ( PIX *pix, l_int32 x, l_int32 y, l_int32 rval, l_int32 gval, l_int32 bval ); -LEPT_DLL extern l_ok pixGetRandomPixel ( PIX *pix, l_uint32 *pval, l_int32 *px, l_int32 *py ); -LEPT_DLL extern l_ok pixClearPixel ( PIX *pix, l_int32 x, l_int32 y ); -LEPT_DLL extern l_ok pixFlipPixel ( PIX *pix, l_int32 x, l_int32 y ); -LEPT_DLL extern void setPixelLow ( l_uint32 *line, l_int32 x, l_int32 depth, l_uint32 val ); -LEPT_DLL extern l_ok pixGetBlackOrWhiteVal ( PIX *pixs, l_int32 op, l_uint32 *pval ); -LEPT_DLL extern l_ok pixClearAll ( PIX *pix ); -LEPT_DLL extern l_ok pixSetAll ( PIX *pix ); -LEPT_DLL extern l_ok pixSetAllGray ( PIX *pix, l_int32 grayval ); -LEPT_DLL extern l_ok pixSetAllArbitrary ( PIX *pix, l_uint32 val ); -LEPT_DLL extern l_ok pixSetBlackOrWhite ( PIX *pixs, l_int32 op ); -LEPT_DLL extern l_ok pixSetComponentArbitrary ( PIX *pix, l_int32 comp, l_int32 val ); -LEPT_DLL extern l_ok pixClearInRect ( PIX *pix, BOX *box ); -LEPT_DLL extern l_ok pixSetInRect ( PIX *pix, BOX *box ); -LEPT_DLL extern l_ok pixSetInRectArbitrary ( PIX *pix, BOX *box, l_uint32 val ); -LEPT_DLL extern l_ok pixBlendInRect ( PIX *pixs, BOX *box, l_uint32 val, l_float32 fract ); -LEPT_DLL extern l_ok pixSetPadBits ( PIX *pix, l_int32 val ); -LEPT_DLL extern l_ok pixSetPadBitsBand ( PIX *pix, l_int32 by, l_int32 bh, l_int32 val ); -LEPT_DLL extern l_ok pixSetOrClearBorder ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_int32 op ); -LEPT_DLL extern l_ok pixSetBorderVal ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_uint32 val ); -LEPT_DLL extern l_ok pixSetBorderRingVal ( PIX *pixs, l_int32 dist, l_uint32 val ); -LEPT_DLL extern l_ok pixSetMirroredBorder ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); -LEPT_DLL extern PIX * pixCopyBorder ( PIX *pixd, PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); -LEPT_DLL extern PIX * pixAddBorder ( PIX *pixs, l_int32 npix, l_uint32 val ); -LEPT_DLL extern PIX * pixAddBlackOrWhiteBorder ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_int32 op ); -LEPT_DLL extern PIX * pixAddBorderGeneral ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_uint32 val ); -LEPT_DLL extern PIX * pixRemoveBorder ( PIX *pixs, l_int32 npix ); -LEPT_DLL extern PIX * pixRemoveBorderGeneral ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); -LEPT_DLL extern PIX * pixRemoveBorderToSize ( PIX *pixs, l_int32 wd, l_int32 hd ); -LEPT_DLL extern PIX * pixAddMirroredBorder ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); -LEPT_DLL extern PIX * pixAddRepeatedBorder ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); -LEPT_DLL extern PIX * pixAddMixedBorder ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); -LEPT_DLL extern PIX * pixAddContinuedBorder ( PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot ); -LEPT_DLL extern l_ok pixShiftAndTransferAlpha ( PIX *pixd, PIX *pixs, l_float32 shiftx, l_float32 shifty ); -LEPT_DLL extern PIX * pixDisplayLayersRGBA ( PIX *pixs, l_uint32 val, l_int32 maxw ); -LEPT_DLL extern PIX * pixCreateRGBImage ( PIX *pixr, PIX *pixg, PIX *pixb ); -LEPT_DLL extern PIX * pixGetRGBComponent ( PIX *pixs, l_int32 comp ); -LEPT_DLL extern l_ok pixSetRGBComponent ( PIX *pixd, PIX *pixs, l_int32 comp ); -LEPT_DLL extern PIX * pixGetRGBComponentCmap ( PIX *pixs, l_int32 comp ); -LEPT_DLL extern l_ok pixCopyRGBComponent ( PIX *pixd, PIX *pixs, l_int32 comp ); -LEPT_DLL extern l_ok composeRGBPixel ( l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 *ppixel ); -LEPT_DLL extern l_ok composeRGBAPixel ( l_int32 rval, l_int32 gval, l_int32 bval, l_int32 aval, l_uint32 *ppixel ); -LEPT_DLL extern void extractRGBValues ( l_uint32 pixel, l_int32 *prval, l_int32 *pgval, l_int32 *pbval ); -LEPT_DLL extern void extractRGBAValues ( l_uint32 pixel, l_int32 *prval, l_int32 *pgval, l_int32 *pbval, l_int32 *paval ); -LEPT_DLL extern l_int32 extractMinMaxComponent ( l_uint32 pixel, l_int32 type ); -LEPT_DLL extern l_ok pixGetRGBLine ( PIX *pixs, l_int32 row, l_uint8 *bufr, l_uint8 *bufg, l_uint8 *bufb ); -LEPT_DLL extern l_ok setLineDataVal ( l_uint32 *line, l_int32 j, l_int32 d, l_uint32 val ); -LEPT_DLL extern PIX * pixEndianByteSwapNew ( PIX *pixs ); -LEPT_DLL extern l_ok pixEndianByteSwap ( PIX *pixs ); -LEPT_DLL extern l_int32 lineEndianByteSwap ( l_uint32 *datad, l_uint32 *datas, l_int32 wpl ); -LEPT_DLL extern PIX * pixEndianTwoByteSwapNew ( PIX *pixs ); -LEPT_DLL extern l_ok pixEndianTwoByteSwap ( PIX *pixs ); -LEPT_DLL extern l_ok pixGetRasterData ( PIX *pixs, l_uint8 **pdata, size_t *pnbytes ); -LEPT_DLL extern l_ok pixAlphaIsOpaque ( PIX *pix, l_int32 *popaque ); -LEPT_DLL extern l_uint8 ** pixSetupByteProcessing ( PIX *pix, l_int32 *pw, l_int32 *ph ); -LEPT_DLL extern l_ok pixCleanupByteProcessing ( PIX *pix, l_uint8 **lineptrs ); -LEPT_DLL extern void l_setAlphaMaskBorder ( l_float32 val1, l_float32 val2 ); -LEPT_DLL extern l_ok pixSetMasked ( PIX *pixd, PIX *pixm, l_uint32 val ); -LEPT_DLL extern l_ok pixSetMaskedGeneral ( PIX *pixd, PIX *pixm, l_uint32 val, l_int32 x, l_int32 y ); -LEPT_DLL extern l_ok pixCombineMasked ( PIX *pixd, PIX *pixs, PIX *pixm ); -LEPT_DLL extern l_ok pixCombineMaskedGeneral ( PIX *pixd, PIX *pixs, PIX *pixm, l_int32 x, l_int32 y ); -LEPT_DLL extern l_ok pixPaintThroughMask ( PIX *pixd, PIX *pixm, l_int32 x, l_int32 y, l_uint32 val ); -LEPT_DLL extern PIX * pixCopyWithBoxa ( PIX *pixs, BOXA *boxa, l_int32 background ); -LEPT_DLL extern l_ok pixPaintSelfThroughMask ( PIX *pixd, PIX *pixm, l_int32 x, l_int32 y, l_int32 searchdir, l_int32 mindist, l_int32 tilesize, l_int32 ntiles, l_int32 distblend ); -LEPT_DLL extern PIX * pixMakeMaskFromVal ( PIX *pixs, l_int32 val ); -LEPT_DLL extern PIX * pixMakeMaskFromLUT ( PIX *pixs, l_int32 *tab ); -LEPT_DLL extern PIX * pixMakeArbMaskFromRGB ( PIX *pixs, l_float32 rc, l_float32 gc, l_float32 bc, l_float32 thresh ); -LEPT_DLL extern PIX * pixSetUnderTransparency ( PIX *pixs, l_uint32 val, l_int32 debug ); -LEPT_DLL extern PIX * pixMakeAlphaFromMask ( PIX *pixs, l_int32 dist, BOX **pbox ); -LEPT_DLL extern l_ok pixGetColorNearMaskBoundary ( PIX *pixs, PIX *pixm, BOX *box, l_int32 dist, l_uint32 *pval, l_int32 debug ); -LEPT_DLL extern PIX * pixDisplaySelectedPixels ( PIX *pixs, PIX *pixm, SEL *sel, l_uint32 val ); -LEPT_DLL extern PIX * pixInvert ( PIX *pixd, PIX *pixs ); -LEPT_DLL extern PIX * pixOr ( PIX *pixd, PIX *pixs1, PIX *pixs2 ); -LEPT_DLL extern PIX * pixAnd ( PIX *pixd, PIX *pixs1, PIX *pixs2 ); -LEPT_DLL extern PIX * pixXor ( PIX *pixd, PIX *pixs1, PIX *pixs2 ); -LEPT_DLL extern PIX * pixSubtract ( PIX *pixd, PIX *pixs1, PIX *pixs2 ); -LEPT_DLL extern l_ok pixZero ( PIX *pix, l_int32 *pempty ); -LEPT_DLL extern l_ok pixForegroundFraction ( PIX *pix, l_float32 *pfract ); -LEPT_DLL extern NUMA * pixaCountPixels ( PIXA *pixa ); -LEPT_DLL extern l_ok pixCountPixels ( PIX *pixs, l_int32 *pcount, l_int32 *tab8 ); -LEPT_DLL extern l_ok pixCountPixelsInRect ( PIX *pixs, BOX *box, l_int32 *pcount, l_int32 *tab8 ); -LEPT_DLL extern NUMA * pixCountByRow ( PIX *pix, BOX *box ); -LEPT_DLL extern NUMA * pixCountByColumn ( PIX *pix, BOX *box ); -LEPT_DLL extern NUMA * pixCountPixelsByRow ( PIX *pix, l_int32 *tab8 ); -LEPT_DLL extern NUMA * pixCountPixelsByColumn ( PIX *pix ); -LEPT_DLL extern l_ok pixCountPixelsInRow ( PIX *pix, l_int32 row, l_int32 *pcount, l_int32 *tab8 ); -LEPT_DLL extern NUMA * pixGetMomentByColumn ( PIX *pix, l_int32 order ); -LEPT_DLL extern l_ok pixThresholdPixelSum ( PIX *pix, l_int32 thresh, l_int32 *pabove, l_int32 *tab8 ); -LEPT_DLL extern l_int32 * makePixelSumTab8 ( void ); -LEPT_DLL extern l_int32 * makePixelCentroidTab8 ( void ); -LEPT_DLL extern NUMA * pixAverageByRow ( PIX *pix, BOX *box, l_int32 type ); -LEPT_DLL extern NUMA * pixAverageByColumn ( PIX *pix, BOX *box, l_int32 type ); -LEPT_DLL extern l_ok pixAverageInRect ( PIX *pixs, PIX *pixm, BOX *box, l_int32 minval, l_int32 maxval, l_int32 subsamp, l_float32 *pave ); -LEPT_DLL extern l_ok pixAverageInRectRGB ( PIX *pixs, PIX *pixm, BOX *box, l_int32 subsamp, l_uint32 *pave ); -LEPT_DLL extern NUMA * pixVarianceByRow ( PIX *pix, BOX *box ); -LEPT_DLL extern NUMA * pixVarianceByColumn ( PIX *pix, BOX *box ); -LEPT_DLL extern l_ok pixVarianceInRect ( PIX *pix, BOX *box, l_float32 *prootvar ); -LEPT_DLL extern NUMA * pixAbsDiffByRow ( PIX *pix, BOX *box ); -LEPT_DLL extern NUMA * pixAbsDiffByColumn ( PIX *pix, BOX *box ); -LEPT_DLL extern l_ok pixAbsDiffInRect ( PIX *pix, BOX *box, l_int32 dir, l_float32 *pabsdiff ); -LEPT_DLL extern l_ok pixAbsDiffOnLine ( PIX *pix, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_float32 *pabsdiff ); -LEPT_DLL extern l_int32 pixCountArbInRect ( PIX *pixs, BOX *box, l_int32 val, l_int32 factor, l_int32 *pcount ); -LEPT_DLL extern PIX * pixMirroredTiling ( PIX *pixs, l_int32 w, l_int32 h ); -LEPT_DLL extern l_ok pixFindRepCloseTile ( PIX *pixs, BOX *box, l_int32 searchdir, l_int32 mindist, l_int32 tsize, l_int32 ntiles, BOX **pboxtile, l_int32 debug ); -LEPT_DLL extern NUMA * pixGetGrayHistogram ( PIX *pixs, l_int32 factor ); -LEPT_DLL extern NUMA * pixGetGrayHistogramMasked ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor ); -LEPT_DLL extern NUMA * pixGetGrayHistogramInRect ( PIX *pixs, BOX *box, l_int32 factor ); -LEPT_DLL extern NUMAA * pixGetGrayHistogramTiled ( PIX *pixs, l_int32 factor, l_int32 nx, l_int32 ny ); -LEPT_DLL extern l_ok pixGetColorHistogram ( PIX *pixs, l_int32 factor, NUMA **pnar, NUMA **pnag, NUMA **pnab ); -LEPT_DLL extern l_ok pixGetColorHistogramMasked ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor, NUMA **pnar, NUMA **pnag, NUMA **pnab ); -LEPT_DLL extern NUMA * pixGetCmapHistogram ( PIX *pixs, l_int32 factor ); -LEPT_DLL extern NUMA * pixGetCmapHistogramMasked ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor ); -LEPT_DLL extern NUMA * pixGetCmapHistogramInRect ( PIX *pixs, BOX *box, l_int32 factor ); -LEPT_DLL extern l_ok pixCountRGBColors ( PIX *pixs, l_int32 factor, l_int32 *pncolors ); -LEPT_DLL extern L_AMAP * pixGetColorAmapHistogram ( PIX *pixs, l_int32 factor ); -LEPT_DLL extern l_int32 amapGetCountForColor ( L_AMAP *amap, l_uint32 val ); -LEPT_DLL extern l_ok pixGetRankValue ( PIX *pixs, l_int32 factor, l_float32 rank, l_uint32 *pvalue ); -LEPT_DLL extern l_ok pixGetRankValueMaskedRGB ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor, l_float32 rank, l_float32 *prval, l_float32 *pgval, l_float32 *pbval ); -LEPT_DLL extern l_ok pixGetRankValueMasked ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor, l_float32 rank, l_float32 *pval, NUMA **pna ); -LEPT_DLL extern l_ok pixGetPixelAverage ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor, l_uint32 *pval ); -LEPT_DLL extern l_ok pixGetPixelStats ( PIX *pixs, l_int32 factor, l_int32 type, l_uint32 *pvalue ); -LEPT_DLL extern l_ok pixGetAverageMaskedRGB ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor, l_int32 type, l_float32 *prval, l_float32 *pgval, l_float32 *pbval ); -LEPT_DLL extern l_ok pixGetAverageMasked ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor, l_int32 type, l_float32 *pval ); -LEPT_DLL extern l_ok pixGetAverageTiledRGB ( PIX *pixs, l_int32 sx, l_int32 sy, l_int32 type, PIX **ppixr, PIX **ppixg, PIX **ppixb ); -LEPT_DLL extern PIX * pixGetAverageTiled ( PIX *pixs, l_int32 sx, l_int32 sy, l_int32 type ); -LEPT_DLL extern l_int32 pixRowStats ( PIX *pixs, BOX *box, NUMA **pnamean, NUMA **pnamedian, NUMA **pnamode, NUMA **pnamodecount, NUMA **pnavar, NUMA **pnarootvar ); -LEPT_DLL extern l_int32 pixColumnStats ( PIX *pixs, BOX *box, NUMA **pnamean, NUMA **pnamedian, NUMA **pnamode, NUMA **pnamodecount, NUMA **pnavar, NUMA **pnarootvar ); -LEPT_DLL extern l_ok pixGetRangeValues ( PIX *pixs, l_int32 factor, l_int32 color, l_int32 *pminval, l_int32 *pmaxval ); -LEPT_DLL extern l_ok pixGetExtremeValue ( PIX *pixs, l_int32 factor, l_int32 type, l_int32 *prval, l_int32 *pgval, l_int32 *pbval, l_int32 *pgrayval ); -LEPT_DLL extern l_ok pixGetMaxValueInRect ( PIX *pixs, BOX *box, l_uint32 *pmaxval, l_int32 *pxmax, l_int32 *pymax ); -LEPT_DLL extern l_ok pixGetBinnedComponentRange ( PIX *pixs, l_int32 nbins, l_int32 factor, l_int32 color, l_int32 *pminval, l_int32 *pmaxval, l_uint32 **pcarray, l_int32 fontsize ); -LEPT_DLL extern l_ok pixGetRankColorArray ( PIX *pixs, l_int32 nbins, l_int32 type, l_int32 factor, l_uint32 **pcarray, PIXA *pixadb, l_int32 fontsize ); -LEPT_DLL extern l_ok pixGetBinnedColor ( PIX *pixs, PIX *pixg, l_int32 factor, l_int32 nbins, NUMA *nalut, l_uint32 **pcarray, PIXA *pixadb ); -LEPT_DLL extern PIX * pixDisplayColorArray ( l_uint32 *carray, l_int32 ncolors, l_int32 side, l_int32 ncols, l_int32 fontsize ); -LEPT_DLL extern PIX * pixRankBinByStrip ( PIX *pixs, l_int32 direction, l_int32 size, l_int32 nbins, l_int32 type ); -LEPT_DLL extern PIX * pixaGetAlignedStats ( PIXA *pixa, l_int32 type, l_int32 nbins, l_int32 thresh ); -LEPT_DLL extern l_ok pixaExtractColumnFromEachPix ( PIXA *pixa, l_int32 col, PIX *pixd ); -LEPT_DLL extern l_ok pixGetRowStats ( PIX *pixs, l_int32 type, l_int32 nbins, l_int32 thresh, l_float32 *colvect ); -LEPT_DLL extern l_ok pixGetColumnStats ( PIX *pixs, l_int32 type, l_int32 nbins, l_int32 thresh, l_float32 *rowvect ); -LEPT_DLL extern l_ok pixSetPixelColumn ( PIX *pix, l_int32 col, l_float32 *colvect ); -LEPT_DLL extern l_ok pixThresholdForFgBg ( PIX *pixs, l_int32 factor, l_int32 thresh, l_int32 *pfgval, l_int32 *pbgval ); -LEPT_DLL extern l_ok pixSplitDistributionFgBg ( PIX *pixs, l_float32 scorefract, l_int32 factor, l_int32 *pthresh, l_int32 *pfgval, l_int32 *pbgval, PIX **ppixdb ); -LEPT_DLL extern l_ok pixaFindDimensions ( PIXA *pixa, NUMA **pnaw, NUMA **pnah ); -LEPT_DLL extern l_ok pixFindAreaPerimRatio ( PIX *pixs, l_int32 *tab, l_float32 *pfract ); -LEPT_DLL extern NUMA * pixaFindPerimToAreaRatio ( PIXA *pixa ); -LEPT_DLL extern l_ok pixFindPerimToAreaRatio ( PIX *pixs, l_int32 *tab, l_float32 *pfract ); -LEPT_DLL extern NUMA * pixaFindPerimSizeRatio ( PIXA *pixa ); -LEPT_DLL extern l_ok pixFindPerimSizeRatio ( PIX *pixs, l_int32 *tab, l_float32 *pratio ); -LEPT_DLL extern NUMA * pixaFindAreaFraction ( PIXA *pixa ); -LEPT_DLL extern l_ok pixFindAreaFraction ( PIX *pixs, l_int32 *tab, l_float32 *pfract ); -LEPT_DLL extern NUMA * pixaFindAreaFractionMasked ( PIXA *pixa, PIX *pixm, l_int32 debug ); -LEPT_DLL extern l_ok pixFindAreaFractionMasked ( PIX *pixs, BOX *box, PIX *pixm, l_int32 *tab, l_float32 *pfract ); -LEPT_DLL extern NUMA * pixaFindWidthHeightRatio ( PIXA *pixa ); -LEPT_DLL extern NUMA * pixaFindWidthHeightProduct ( PIXA *pixa ); -LEPT_DLL extern l_ok pixFindOverlapFraction ( PIX *pixs1, PIX *pixs2, l_int32 x2, l_int32 y2, l_int32 *tab, l_float32 *pratio, l_int32 *pnoverlap ); -LEPT_DLL extern BOXA * pixFindRectangleComps ( PIX *pixs, l_int32 dist, l_int32 minw, l_int32 minh ); -LEPT_DLL extern l_ok pixConformsToRectangle ( PIX *pixs, BOX *box, l_int32 dist, l_int32 *pconforms ); -LEPT_DLL extern PIXA * pixClipRectangles ( PIX *pixs, BOXA *boxa ); -LEPT_DLL extern PIX * pixClipRectangle ( PIX *pixs, BOX *box, BOX **pboxc ); -LEPT_DLL extern PIX * pixClipMasked ( PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_uint32 outval ); -LEPT_DLL extern l_ok pixCropToMatch ( PIX *pixs1, PIX *pixs2, PIX **ppixd1, PIX **ppixd2 ); -LEPT_DLL extern PIX * pixCropToSize ( PIX *pixs, l_int32 w, l_int32 h ); -LEPT_DLL extern PIX * pixResizeToMatch ( PIX *pixs, PIX *pixt, l_int32 w, l_int32 h ); -LEPT_DLL extern PIX * pixSelectComponentBySize ( PIX *pixs, l_int32 rankorder, l_int32 type, l_int32 connectivity, BOX **pbox ); -LEPT_DLL extern PIX * pixFilterComponentBySize ( PIX *pixs, l_int32 rankorder, l_int32 type, l_int32 connectivity, BOX **pbox ); -LEPT_DLL extern PIX * pixMakeSymmetricMask ( l_int32 w, l_int32 h, l_float32 hf, l_float32 vf, l_int32 type ); -LEPT_DLL extern PIX * pixMakeFrameMask ( l_int32 w, l_int32 h, l_float32 hf1, l_float32 hf2, l_float32 vf1, l_float32 vf2 ); -LEPT_DLL extern PIX * pixMakeCoveringOfRectangles ( PIX *pixs, l_int32 maxiters ); -LEPT_DLL extern l_ok pixFractionFgInMask ( PIX *pix1, PIX *pix2, l_float32 *pfract ); -LEPT_DLL extern l_ok pixClipToForeground ( PIX *pixs, PIX **ppixd, BOX **pbox ); -LEPT_DLL extern l_ok pixTestClipToForeground ( PIX *pixs, l_int32 *pcanclip ); -LEPT_DLL extern l_ok pixClipBoxToForeground ( PIX *pixs, BOX *boxs, PIX **ppixd, BOX **pboxd ); -LEPT_DLL extern l_ok pixScanForForeground ( PIX *pixs, BOX *box, l_int32 scanflag, l_int32 *ploc ); -LEPT_DLL extern l_ok pixClipBoxToEdges ( PIX *pixs, BOX *boxs, l_int32 lowthresh, l_int32 highthresh, l_int32 maxwidth, l_int32 factor, PIX **ppixd, BOX **pboxd ); -LEPT_DLL extern l_ok pixScanForEdge ( PIX *pixs, BOX *box, l_int32 lowthresh, l_int32 highthresh, l_int32 maxwidth, l_int32 factor, l_int32 scanflag, l_int32 *ploc ); -LEPT_DLL extern NUMA * pixExtractOnLine ( PIX *pixs, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 factor ); -LEPT_DLL extern l_float32 pixAverageOnLine ( PIX *pixs, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 factor ); -LEPT_DLL extern NUMA * pixAverageIntensityProfile ( PIX *pixs, l_float32 fract, l_int32 dir, l_int32 first, l_int32 last, l_int32 factor1, l_int32 factor2 ); -LEPT_DLL extern NUMA * pixReversalProfile ( PIX *pixs, l_float32 fract, l_int32 dir, l_int32 first, l_int32 last, l_int32 minreversal, l_int32 factor1, l_int32 factor2 ); -LEPT_DLL extern l_ok pixWindowedVarianceOnLine ( PIX *pixs, l_int32 dir, l_int32 loc, l_int32 c1, l_int32 c2, l_int32 size, NUMA **pnad ); -LEPT_DLL extern l_ok pixMinMaxNearLine ( PIX *pixs, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 dist, l_int32 direction, NUMA **pnamin, NUMA **pnamax, l_float32 *pminave, l_float32 *pmaxave ); -LEPT_DLL extern PIX * pixRankRowTransform ( PIX *pixs ); -LEPT_DLL extern PIX * pixRankColumnTransform ( PIX *pixs ); -LEPT_DLL extern PIXA * pixaCreate ( l_int32 n ); -LEPT_DLL extern PIXA * pixaCreateFromPix ( PIX *pixs, l_int32 n, l_int32 cellw, l_int32 cellh ); -LEPT_DLL extern PIXA * pixaCreateFromBoxa ( PIX *pixs, BOXA *boxa, l_int32 start, l_int32 num, l_int32 *pcropwarn ); -LEPT_DLL extern PIXA * pixaSplitPix ( PIX *pixs, l_int32 nx, l_int32 ny, l_int32 borderwidth, l_uint32 bordercolor ); -LEPT_DLL extern void pixaDestroy ( PIXA **ppixa ); -LEPT_DLL extern PIXA * pixaCopy ( PIXA *pixa, l_int32 copyflag ); -LEPT_DLL extern l_ok pixaAddPix ( PIXA *pixa, PIX *pix, l_int32 copyflag ); -LEPT_DLL extern l_ok pixaAddBox ( PIXA *pixa, BOX *box, l_int32 copyflag ); -LEPT_DLL extern l_ok pixaExtendArrayToSize ( PIXA *pixa, l_int32 size ); -LEPT_DLL extern l_int32 pixaGetCount ( PIXA *pixa ); -LEPT_DLL extern l_ok pixaChangeRefcount ( PIXA *pixa, l_int32 delta ); -LEPT_DLL extern PIX * pixaGetPix ( PIXA *pixa, l_int32 index, l_int32 accesstype ); -LEPT_DLL extern l_ok pixaGetPixDimensions ( PIXA *pixa, l_int32 index, l_int32 *pw, l_int32 *ph, l_int32 *pd ); -LEPT_DLL extern BOXA * pixaGetBoxa ( PIXA *pixa, l_int32 accesstype ); -LEPT_DLL extern l_int32 pixaGetBoxaCount ( PIXA *pixa ); -LEPT_DLL extern BOX * pixaGetBox ( PIXA *pixa, l_int32 index, l_int32 accesstype ); -LEPT_DLL extern l_ok pixaGetBoxGeometry ( PIXA *pixa, l_int32 index, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph ); -LEPT_DLL extern l_ok pixaSetBoxa ( PIXA *pixa, BOXA *boxa, l_int32 accesstype ); -LEPT_DLL extern PIX ** pixaGetPixArray ( PIXA *pixa ); -LEPT_DLL extern l_ok pixaVerifyDepth ( PIXA *pixa, l_int32 *psame, l_int32 *pmaxd ); -LEPT_DLL extern l_ok pixaVerifyDimensions ( PIXA *pixa, l_int32 *psame, l_int32 *pmaxw, l_int32 *pmaxh ); -LEPT_DLL extern l_ok pixaIsFull ( PIXA *pixa, l_int32 *pfullpa, l_int32 *pfullba ); -LEPT_DLL extern l_ok pixaCountText ( PIXA *pixa, l_int32 *pntext ); -LEPT_DLL extern l_ok pixaSetText ( PIXA *pixa, const char *text, SARRAY *sa ); -LEPT_DLL extern void *** pixaGetLinePtrs ( PIXA *pixa, l_int32 *psize ); -LEPT_DLL extern l_ok pixaWriteStreamInfo ( FILE *fp, PIXA *pixa ); -LEPT_DLL extern l_ok pixaReplacePix ( PIXA *pixa, l_int32 index, PIX *pix, BOX *box ); -LEPT_DLL extern l_ok pixaInsertPix ( PIXA *pixa, l_int32 index, PIX *pixs, BOX *box ); -LEPT_DLL extern l_ok pixaRemovePix ( PIXA *pixa, l_int32 index ); -LEPT_DLL extern l_ok pixaRemovePixAndSave ( PIXA *pixa, l_int32 index, PIX **ppix, BOX **pbox ); -LEPT_DLL extern l_ok pixaRemoveSelected ( PIXA *pixa, NUMA *naindex ); -LEPT_DLL extern l_ok pixaInitFull ( PIXA *pixa, PIX *pix, BOX *box ); -LEPT_DLL extern l_ok pixaClear ( PIXA *pixa ); -LEPT_DLL extern l_ok pixaJoin ( PIXA *pixad, PIXA *pixas, l_int32 istart, l_int32 iend ); -LEPT_DLL extern PIXA * pixaInterleave ( PIXA *pixa1, PIXA *pixa2, l_int32 copyflag ); -LEPT_DLL extern l_ok pixaaJoin ( PIXAA *paad, PIXAA *paas, l_int32 istart, l_int32 iend ); -LEPT_DLL extern PIXAA * pixaaCreate ( l_int32 n ); -LEPT_DLL extern PIXAA * pixaaCreateFromPixa ( PIXA *pixa, l_int32 n, l_int32 type, l_int32 copyflag ); -LEPT_DLL extern void pixaaDestroy ( PIXAA **ppaa ); -LEPT_DLL extern l_ok pixaaAddPixa ( PIXAA *paa, PIXA *pixa, l_int32 copyflag ); -LEPT_DLL extern l_ok pixaaExtendArray ( PIXAA *paa ); -LEPT_DLL extern l_ok pixaaAddPix ( PIXAA *paa, l_int32 index, PIX *pix, BOX *box, l_int32 copyflag ); -LEPT_DLL extern l_ok pixaaAddBox ( PIXAA *paa, BOX *box, l_int32 copyflag ); -LEPT_DLL extern l_int32 pixaaGetCount ( PIXAA *paa, NUMA **pna ); -LEPT_DLL extern PIXA * pixaaGetPixa ( PIXAA *paa, l_int32 index, l_int32 accesstype ); -LEPT_DLL extern BOXA * pixaaGetBoxa ( PIXAA *paa, l_int32 accesstype ); -LEPT_DLL extern PIX * pixaaGetPix ( PIXAA *paa, l_int32 index, l_int32 ipix, l_int32 accessflag ); -LEPT_DLL extern l_ok pixaaVerifyDepth ( PIXAA *paa, l_int32 *psame, l_int32 *pmaxd ); -LEPT_DLL extern l_ok pixaaVerifyDimensions ( PIXAA *paa, l_int32 *psame, l_int32 *pmaxw, l_int32 *pmaxh ); -LEPT_DLL extern l_int32 pixaaIsFull ( PIXAA *paa, l_int32 *pfull ); -LEPT_DLL extern l_ok pixaaInitFull ( PIXAA *paa, PIXA *pixa ); -LEPT_DLL extern l_ok pixaaReplacePixa ( PIXAA *paa, l_int32 index, PIXA *pixa ); -LEPT_DLL extern l_ok pixaaClear ( PIXAA *paa ); -LEPT_DLL extern l_ok pixaaTruncate ( PIXAA *paa ); -LEPT_DLL extern PIXA * pixaRead ( const char *filename ); -LEPT_DLL extern PIXA * pixaReadStream ( FILE *fp ); -LEPT_DLL extern PIXA * pixaReadMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok pixaWriteDebug ( const char *fname, PIXA *pixa ); -LEPT_DLL extern l_ok pixaWrite ( const char *filename, PIXA *pixa ); -LEPT_DLL extern l_ok pixaWriteStream ( FILE *fp, PIXA *pixa ); -LEPT_DLL extern l_ok pixaWriteMem ( l_uint8 **pdata, size_t *psize, PIXA *pixa ); -LEPT_DLL extern PIXA * pixaReadBoth ( const char *filename ); -LEPT_DLL extern PIXAA * pixaaReadFromFiles ( const char *dirname, const char *substr, l_int32 first, l_int32 nfiles ); -LEPT_DLL extern PIXAA * pixaaRead ( const char *filename ); -LEPT_DLL extern PIXAA * pixaaReadStream ( FILE *fp ); -LEPT_DLL extern PIXAA * pixaaReadMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok pixaaWrite ( const char *filename, PIXAA *paa ); -LEPT_DLL extern l_ok pixaaWriteStream ( FILE *fp, PIXAA *paa ); -LEPT_DLL extern l_ok pixaaWriteMem ( l_uint8 **pdata, size_t *psize, PIXAA *paa ); -LEPT_DLL extern PIXACC * pixaccCreate ( l_int32 w, l_int32 h, l_int32 negflag ); -LEPT_DLL extern PIXACC * pixaccCreateFromPix ( PIX *pix, l_int32 negflag ); -LEPT_DLL extern void pixaccDestroy ( PIXACC **ppixacc ); -LEPT_DLL extern PIX * pixaccFinal ( PIXACC *pixacc, l_int32 outdepth ); -LEPT_DLL extern PIX * pixaccGetPix ( PIXACC *pixacc ); -LEPT_DLL extern l_int32 pixaccGetOffset ( PIXACC *pixacc ); -LEPT_DLL extern l_ok pixaccAdd ( PIXACC *pixacc, PIX *pix ); -LEPT_DLL extern l_ok pixaccSubtract ( PIXACC *pixacc, PIX *pix ); -LEPT_DLL extern l_ok pixaccMultConst ( PIXACC *pixacc, l_float32 factor ); -LEPT_DLL extern l_ok pixaccMultConstAccumulate ( PIXACC *pixacc, PIX *pix, l_float32 factor ); -LEPT_DLL extern PIX * pixSelectBySize ( PIX *pixs, l_int32 width, l_int32 height, l_int32 connectivity, l_int32 type, l_int32 relation, l_int32 *pchanged ); -LEPT_DLL extern PIXA * pixaSelectBySize ( PIXA *pixas, l_int32 width, l_int32 height, l_int32 type, l_int32 relation, l_int32 *pchanged ); -LEPT_DLL extern NUMA * pixaMakeSizeIndicator ( PIXA *pixa, l_int32 width, l_int32 height, l_int32 type, l_int32 relation ); -LEPT_DLL extern PIX * pixSelectByPerimToAreaRatio ( PIX *pixs, l_float32 thresh, l_int32 connectivity, l_int32 type, l_int32 *pchanged ); -LEPT_DLL extern PIXA * pixaSelectByPerimToAreaRatio ( PIXA *pixas, l_float32 thresh, l_int32 type, l_int32 *pchanged ); -LEPT_DLL extern PIX * pixSelectByPerimSizeRatio ( PIX *pixs, l_float32 thresh, l_int32 connectivity, l_int32 type, l_int32 *pchanged ); -LEPT_DLL extern PIXA * pixaSelectByPerimSizeRatio ( PIXA *pixas, l_float32 thresh, l_int32 type, l_int32 *pchanged ); -LEPT_DLL extern PIX * pixSelectByAreaFraction ( PIX *pixs, l_float32 thresh, l_int32 connectivity, l_int32 type, l_int32 *pchanged ); -LEPT_DLL extern PIXA * pixaSelectByAreaFraction ( PIXA *pixas, l_float32 thresh, l_int32 type, l_int32 *pchanged ); -LEPT_DLL extern PIX * pixSelectByWidthHeightRatio ( PIX *pixs, l_float32 thresh, l_int32 connectivity, l_int32 type, l_int32 *pchanged ); -LEPT_DLL extern PIXA * pixaSelectByWidthHeightRatio ( PIXA *pixas, l_float32 thresh, l_int32 type, l_int32 *pchanged ); -LEPT_DLL extern PIXA * pixaSelectByNumConnComp ( PIXA *pixas, l_int32 nmin, l_int32 nmax, l_int32 connectivity, l_int32 *pchanged ); -LEPT_DLL extern PIXA * pixaSelectWithIndicator ( PIXA *pixas, NUMA *na, l_int32 *pchanged ); -LEPT_DLL extern l_ok pixRemoveWithIndicator ( PIX *pixs, PIXA *pixa, NUMA *na ); -LEPT_DLL extern l_ok pixAddWithIndicator ( PIX *pixs, PIXA *pixa, NUMA *na ); -LEPT_DLL extern PIXA * pixaSelectWithString ( PIXA *pixas, const char *str, l_int32 *perror ); -LEPT_DLL extern PIX * pixaRenderComponent ( PIX *pixs, PIXA *pixa, l_int32 index ); -LEPT_DLL extern PIXA * pixaSort ( PIXA *pixas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex, l_int32 copyflag ); -LEPT_DLL extern PIXA * pixaBinSort ( PIXA *pixas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex, l_int32 copyflag ); -LEPT_DLL extern PIXA * pixaSortByIndex ( PIXA *pixas, NUMA *naindex, l_int32 copyflag ); -LEPT_DLL extern PIXAA * pixaSort2dByIndex ( PIXA *pixas, NUMAA *naa, l_int32 copyflag ); -LEPT_DLL extern PIXA * pixaSelectRange ( PIXA *pixas, l_int32 first, l_int32 last, l_int32 copyflag ); -LEPT_DLL extern PIXAA * pixaaSelectRange ( PIXAA *paas, l_int32 first, l_int32 last, l_int32 copyflag ); -LEPT_DLL extern PIXAA * pixaaScaleToSize ( PIXAA *paas, l_int32 wd, l_int32 hd ); -LEPT_DLL extern PIXAA * pixaaScaleToSizeVar ( PIXAA *paas, NUMA *nawd, NUMA *nahd ); -LEPT_DLL extern PIXA * pixaScaleToSize ( PIXA *pixas, l_int32 wd, l_int32 hd ); -LEPT_DLL extern PIXA * pixaScaleToSizeRel ( PIXA *pixas, l_int32 delw, l_int32 delh ); -LEPT_DLL extern PIXA * pixaScale ( PIXA *pixas, l_float32 scalex, l_float32 scaley ); -LEPT_DLL extern PIXA * pixaScaleBySampling ( PIXA *pixas, l_float32 scalex, l_float32 scaley ); -LEPT_DLL extern PIXA * pixaRotate ( PIXA *pixas, l_float32 angle, l_int32 type, l_int32 incolor, l_int32 width, l_int32 height ); -LEPT_DLL extern PIXA * pixaRotateOrth ( PIXA *pixas, l_int32 rotation ); -LEPT_DLL extern PIXA * pixaTranslate ( PIXA *pixas, l_int32 hshift, l_int32 vshift, l_int32 incolor ); -LEPT_DLL extern PIXA * pixaAddBorderGeneral ( PIXA *pixad, PIXA *pixas, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_uint32 val ); -LEPT_DLL extern PIXA * pixaaFlattenToPixa ( PIXAA *paa, NUMA **pnaindex, l_int32 copyflag ); -LEPT_DLL extern l_ok pixaaSizeRange ( PIXAA *paa, l_int32 *pminw, l_int32 *pminh, l_int32 *pmaxw, l_int32 *pmaxh ); -LEPT_DLL extern l_ok pixaSizeRange ( PIXA *pixa, l_int32 *pminw, l_int32 *pminh, l_int32 *pmaxw, l_int32 *pmaxh ); -LEPT_DLL extern PIXA * pixaClipToPix ( PIXA *pixas, PIX *pixs ); -LEPT_DLL extern l_ok pixaClipToForeground ( PIXA *pixas, PIXA **ppixad, BOXA **pboxa ); -LEPT_DLL extern l_ok pixaGetRenderingDepth ( PIXA *pixa, l_int32 *pdepth ); -LEPT_DLL extern l_ok pixaHasColor ( PIXA *pixa, l_int32 *phascolor ); -LEPT_DLL extern l_ok pixaAnyColormaps ( PIXA *pixa, l_int32 *phascmap ); -LEPT_DLL extern l_ok pixaGetDepthInfo ( PIXA *pixa, l_int32 *pmaxdepth, l_int32 *psame ); -LEPT_DLL extern PIXA * pixaConvertToSameDepth ( PIXA *pixas ); -LEPT_DLL extern l_ok pixaEqual ( PIXA *pixa1, PIXA *pixa2, l_int32 maxdist, NUMA **pnaindex, l_int32 *psame ); -LEPT_DLL extern l_ok pixaSetFullSizeBoxa ( PIXA *pixa ); -LEPT_DLL extern PIX * pixaDisplay ( PIXA *pixa, l_int32 w, l_int32 h ); -LEPT_DLL extern PIX * pixaDisplayRandomCmap ( PIXA *pixa, l_int32 w, l_int32 h ); -LEPT_DLL extern PIX * pixaDisplayLinearly ( PIXA *pixas, l_int32 direction, l_float32 scalefactor, l_int32 background, l_int32 spacing, l_int32 border, BOXA **pboxa ); -LEPT_DLL extern PIX * pixaDisplayOnLattice ( PIXA *pixa, l_int32 cellw, l_int32 cellh, l_int32 *pncols, BOXA **pboxa ); -LEPT_DLL extern PIX * pixaDisplayUnsplit ( PIXA *pixa, l_int32 nx, l_int32 ny, l_int32 borderwidth, l_uint32 bordercolor ); -LEPT_DLL extern PIX * pixaDisplayTiled ( PIXA *pixa, l_int32 maxwidth, l_int32 background, l_int32 spacing ); -LEPT_DLL extern PIX * pixaDisplayTiledInRows ( PIXA *pixa, l_int32 outdepth, l_int32 maxwidth, l_float32 scalefactor, l_int32 background, l_int32 spacing, l_int32 border ); -LEPT_DLL extern PIX * pixaDisplayTiledInColumns ( PIXA *pixas, l_int32 nx, l_float32 scalefactor, l_int32 spacing, l_int32 border ); -LEPT_DLL extern PIX * pixaDisplayTiledAndScaled ( PIXA *pixa, l_int32 outdepth, l_int32 tilewidth, l_int32 ncols, l_int32 background, l_int32 spacing, l_int32 border ); -LEPT_DLL extern PIX * pixaDisplayTiledWithText ( PIXA *pixa, l_int32 maxwidth, l_float32 scalefactor, l_int32 spacing, l_int32 border, l_int32 fontsize, l_uint32 textcolor ); -LEPT_DLL extern PIX * pixaDisplayTiledByIndex ( PIXA *pixa, NUMA *na, l_int32 width, l_int32 spacing, l_int32 border, l_int32 fontsize, l_uint32 textcolor ); -LEPT_DLL extern PIX * pixaaDisplay ( PIXAA *paa, l_int32 w, l_int32 h ); -LEPT_DLL extern PIX * pixaaDisplayByPixa ( PIXAA *paa, l_int32 maxnx, l_float32 scalefactor, l_int32 hspacing, l_int32 vspacing, l_int32 border ); -LEPT_DLL extern PIXA * pixaaDisplayTiledAndScaled ( PIXAA *paa, l_int32 outdepth, l_int32 tilewidth, l_int32 ncols, l_int32 background, l_int32 spacing, l_int32 border ); -LEPT_DLL extern PIXA * pixaConvertTo1 ( PIXA *pixas, l_int32 thresh ); -LEPT_DLL extern PIXA * pixaConvertTo8 ( PIXA *pixas, l_int32 cmapflag ); -LEPT_DLL extern PIXA * pixaConvertTo8Colormap ( PIXA *pixas, l_int32 dither ); -LEPT_DLL extern PIXA * pixaConvertTo32 ( PIXA *pixas ); -LEPT_DLL extern PIXA * pixaConstrainedSelect ( PIXA *pixas, l_int32 first, l_int32 last, l_int32 nmax, l_int32 use_pairs, l_int32 copyflag ); -LEPT_DLL extern l_ok pixaSelectToPdf ( PIXA *pixas, l_int32 first, l_int32 last, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, l_uint32 color, l_int32 fontsize, const char *fileout ); -LEPT_DLL extern PIXA * pixaMakeFromTiledPixa ( PIXA *pixas, l_int32 w, l_int32 h, l_int32 nsamp ); -LEPT_DLL extern PIXA * pixaMakeFromTiledPix ( PIX *pixs, l_int32 w, l_int32 h, l_int32 start, l_int32 num, BOXA *boxa ); -LEPT_DLL extern l_ok pixGetTileCount ( PIX *pix, l_int32 *pn ); -LEPT_DLL extern PIXA * pixaDisplayMultiTiled ( PIXA *pixas, l_int32 nx, l_int32 ny, l_int32 maxw, l_int32 maxh, l_float32 scalefactor, l_int32 spacing, l_int32 border ); -LEPT_DLL extern l_ok pixaSplitIntoFiles ( PIXA *pixas, l_int32 nsplit, l_float32 scale, l_int32 outwidth, l_int32 write_pixa, l_int32 write_pix, l_int32 write_pdf ); -LEPT_DLL extern l_ok convertToNUpFiles ( const char *dir, const char *substr, l_int32 nx, l_int32 ny, l_int32 tw, l_int32 spacing, l_int32 border, l_int32 fontsize, const char *outdir ); -LEPT_DLL extern PIXA * convertToNUpPixa ( const char *dir, const char *substr, l_int32 nx, l_int32 ny, l_int32 tw, l_int32 spacing, l_int32 border, l_int32 fontsize ); -LEPT_DLL extern PIXA * pixaConvertToNUpPixa ( PIXA *pixas, SARRAY *sa, l_int32 nx, l_int32 ny, l_int32 tw, l_int32 spacing, l_int32 border, l_int32 fontsize ); -LEPT_DLL extern l_ok pixaCompareInPdf ( PIXA *pixa1, PIXA *pixa2, l_int32 nx, l_int32 ny, l_int32 tw, l_int32 spacing, l_int32 border, l_int32 fontsize, const char *fileout ); -LEPT_DLL extern l_ok pmsCreate ( size_t minsize, size_t smallest, NUMA *numalloc, const char *logfile ); -LEPT_DLL extern void pmsDestroy ( void ); -LEPT_DLL extern void * pmsCustomAlloc ( size_t nbytes ); -LEPT_DLL extern void pmsCustomDealloc ( void *data ); -LEPT_DLL extern void * pmsGetAlloc ( size_t nbytes ); -LEPT_DLL extern l_ok pmsGetLevelForAlloc ( size_t nbytes, l_int32 *plevel ); -LEPT_DLL extern l_ok pmsGetLevelForDealloc ( void *data, l_int32 *plevel ); -LEPT_DLL extern void pmsLogInfo ( void ); -LEPT_DLL extern l_ok pixAddConstantGray ( PIX *pixs, l_int32 val ); -LEPT_DLL extern l_ok pixMultConstantGray ( PIX *pixs, l_float32 val ); -LEPT_DLL extern PIX * pixAddGray ( PIX *pixd, PIX *pixs1, PIX *pixs2 ); -LEPT_DLL extern PIX * pixSubtractGray ( PIX *pixd, PIX *pixs1, PIX *pixs2 ); -LEPT_DLL extern PIX * pixMultiplyGray ( PIX *pixs, PIX *pixg, l_float32 norm ); -LEPT_DLL extern PIX * pixThresholdToValue ( PIX *pixd, PIX *pixs, l_int32 threshval, l_int32 setval ); -LEPT_DLL extern PIX * pixInitAccumulate ( l_int32 w, l_int32 h, l_uint32 offset ); -LEPT_DLL extern PIX * pixFinalAccumulate ( PIX *pixs, l_uint32 offset, l_int32 depth ); -LEPT_DLL extern PIX * pixFinalAccumulateThreshold ( PIX *pixs, l_uint32 offset, l_uint32 threshold ); -LEPT_DLL extern l_ok pixAccumulate ( PIX *pixd, PIX *pixs, l_int32 op ); -LEPT_DLL extern l_ok pixMultConstAccumulate ( PIX *pixs, l_float32 factor, l_uint32 offset ); -LEPT_DLL extern PIX * pixAbsDifference ( PIX *pixs1, PIX *pixs2 ); -LEPT_DLL extern PIX * pixAddRGB ( PIX *pixs1, PIX *pixs2 ); -LEPT_DLL extern PIX * pixMinOrMax ( PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 type ); -LEPT_DLL extern PIX * pixMaxDynamicRange ( PIX *pixs, l_int32 type ); -LEPT_DLL extern PIX * pixMaxDynamicRangeRGB ( PIX *pixs, l_int32 type ); -LEPT_DLL extern l_uint32 linearScaleRGBVal ( l_uint32 sval, l_float32 factor ); -LEPT_DLL extern l_uint32 logScaleRGBVal ( l_uint32 sval, l_float32 *tab, l_float32 factor ); -LEPT_DLL extern l_float32 * makeLogBase2Tab ( void ); -LEPT_DLL extern l_float32 getLogBase2 ( l_int32 val, l_float32 *logtab ); -LEPT_DLL extern PIXC * pixcompCreateFromPix ( PIX *pix, l_int32 comptype ); -LEPT_DLL extern PIXC * pixcompCreateFromString ( l_uint8 *data, size_t size, l_int32 copyflag ); -LEPT_DLL extern PIXC * pixcompCreateFromFile ( const char *filename, l_int32 comptype ); -LEPT_DLL extern void pixcompDestroy ( PIXC **ppixc ); -LEPT_DLL extern PIXC * pixcompCopy ( PIXC *pixcs ); -LEPT_DLL extern l_ok pixcompGetDimensions ( PIXC *pixc, l_int32 *pw, l_int32 *ph, l_int32 *pd ); -LEPT_DLL extern l_ok pixcompGetParameters ( PIXC *pixc, l_int32 *pxres, l_int32 *pyres, l_int32 *pcomptype, l_int32 *pcmapflag ); -LEPT_DLL extern l_ok pixcompDetermineFormat ( l_int32 comptype, l_int32 d, l_int32 cmapflag, l_int32 *pformat ); -LEPT_DLL extern PIX * pixCreateFromPixcomp ( PIXC *pixc ); -LEPT_DLL extern PIXAC * pixacompCreate ( l_int32 n ); -LEPT_DLL extern PIXAC * pixacompCreateWithInit ( l_int32 n, l_int32 offset, PIX *pix, l_int32 comptype ); -LEPT_DLL extern PIXAC * pixacompCreateFromPixa ( PIXA *pixa, l_int32 comptype, l_int32 accesstype ); -LEPT_DLL extern PIXAC * pixacompCreateFromFiles ( const char *dirname, const char *substr, l_int32 comptype ); -LEPT_DLL extern PIXAC * pixacompCreateFromSA ( SARRAY *sa, l_int32 comptype ); -LEPT_DLL extern void pixacompDestroy ( PIXAC **ppixac ); -LEPT_DLL extern l_ok pixacompAddPix ( PIXAC *pixac, PIX *pix, l_int32 comptype ); -LEPT_DLL extern l_ok pixacompAddPixcomp ( PIXAC *pixac, PIXC *pixc, l_int32 copyflag ); -LEPT_DLL extern l_ok pixacompReplacePix ( PIXAC *pixac, l_int32 index, PIX *pix, l_int32 comptype ); -LEPT_DLL extern l_ok pixacompReplacePixcomp ( PIXAC *pixac, l_int32 index, PIXC *pixc ); -LEPT_DLL extern l_ok pixacompAddBox ( PIXAC *pixac, BOX *box, l_int32 copyflag ); -LEPT_DLL extern l_int32 pixacompGetCount ( PIXAC *pixac ); -LEPT_DLL extern PIXC * pixacompGetPixcomp ( PIXAC *pixac, l_int32 index, l_int32 copyflag ); -LEPT_DLL extern PIX * pixacompGetPix ( PIXAC *pixac, l_int32 index ); -LEPT_DLL extern l_ok pixacompGetPixDimensions ( PIXAC *pixac, l_int32 index, l_int32 *pw, l_int32 *ph, l_int32 *pd ); -LEPT_DLL extern BOXA * pixacompGetBoxa ( PIXAC *pixac, l_int32 accesstype ); -LEPT_DLL extern l_int32 pixacompGetBoxaCount ( PIXAC *pixac ); -LEPT_DLL extern BOX * pixacompGetBox ( PIXAC *pixac, l_int32 index, l_int32 accesstype ); -LEPT_DLL extern l_ok pixacompGetBoxGeometry ( PIXAC *pixac, l_int32 index, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph ); -LEPT_DLL extern l_int32 pixacompGetOffset ( PIXAC *pixac ); -LEPT_DLL extern l_ok pixacompSetOffset ( PIXAC *pixac, l_int32 offset ); -LEPT_DLL extern PIXA * pixaCreateFromPixacomp ( PIXAC *pixac, l_int32 accesstype ); -LEPT_DLL extern l_ok pixacompJoin ( PIXAC *pixacd, PIXAC *pixacs, l_int32 istart, l_int32 iend ); -LEPT_DLL extern PIXAC * pixacompInterleave ( PIXAC *pixac1, PIXAC *pixac2 ); -LEPT_DLL extern PIXAC * pixacompRead ( const char *filename ); -LEPT_DLL extern PIXAC * pixacompReadStream ( FILE *fp ); -LEPT_DLL extern PIXAC * pixacompReadMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok pixacompWrite ( const char *filename, PIXAC *pixac ); -LEPT_DLL extern l_ok pixacompWriteStream ( FILE *fp, PIXAC *pixac ); -LEPT_DLL extern l_ok pixacompWriteMem ( l_uint8 **pdata, size_t *psize, PIXAC *pixac ); -LEPT_DLL extern l_ok pixacompConvertToPdf ( PIXAC *pixac, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, const char *fileout ); -LEPT_DLL extern l_ok pixacompConvertToPdfData ( PIXAC *pixac, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, l_uint8 **pdata, size_t *pnbytes ); -LEPT_DLL extern l_ok pixacompFastConvertToPdfData ( PIXAC *pixac, const char *title, l_uint8 **pdata, size_t *pnbytes ); -LEPT_DLL extern l_ok pixacompWriteStreamInfo ( FILE *fp, PIXAC *pixac, const char *text ); -LEPT_DLL extern l_ok pixcompWriteStreamInfo ( FILE *fp, PIXC *pixc, const char *text ); -LEPT_DLL extern PIX * pixacompDisplayTiledAndScaled ( PIXAC *pixac, l_int32 outdepth, l_int32 tilewidth, l_int32 ncols, l_int32 background, l_int32 spacing, l_int32 border ); -LEPT_DLL extern l_ok pixacompWriteFiles ( PIXAC *pixac, const char *subdir ); -LEPT_DLL extern l_ok pixcompWriteFile ( const char *rootname, PIXC *pixc ); -LEPT_DLL extern PIX * pixThreshold8 ( PIX *pixs, l_int32 d, l_int32 nlevels, l_int32 cmapflag ); -LEPT_DLL extern PIX * pixRemoveColormapGeneral ( PIX *pixs, l_int32 type, l_int32 ifnocmap ); -LEPT_DLL extern PIX * pixRemoveColormap ( PIX *pixs, l_int32 type ); -LEPT_DLL extern l_ok pixAddGrayColormap8 ( PIX *pixs ); -LEPT_DLL extern PIX * pixAddMinimalGrayColormap8 ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvertRGBToLuminance ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvertRGBToGray ( PIX *pixs, l_float32 rwt, l_float32 gwt, l_float32 bwt ); -LEPT_DLL extern PIX * pixConvertRGBToGrayFast ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvertRGBToGrayMinMax ( PIX *pixs, l_int32 type ); -LEPT_DLL extern PIX * pixConvertRGBToGraySatBoost ( PIX *pixs, l_int32 refval ); -LEPT_DLL extern PIX * pixConvertRGBToGrayArb ( PIX *pixs, l_float32 rc, l_float32 gc, l_float32 bc ); -LEPT_DLL extern PIX * pixConvertRGBToBinaryArb ( PIX *pixs, l_float32 rc, l_float32 gc, l_float32 bc, l_int32 thresh, l_int32 relation ); -LEPT_DLL extern PIX * pixConvertGrayToColormap ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvertGrayToColormap8 ( PIX *pixs, l_int32 mindepth ); -LEPT_DLL extern PIX * pixColorizeGray ( PIX *pixs, l_uint32 color, l_int32 cmapflag ); -LEPT_DLL extern PIX * pixConvertRGBToColormap ( PIX *pixs, l_int32 ditherflag ); -LEPT_DLL extern PIX * pixConvertCmapTo1 ( PIX *pixs ); -LEPT_DLL extern l_ok pixQuantizeIfFewColors ( PIX *pixs, l_int32 maxcolors, l_int32 mingraycolors, l_int32 octlevel, PIX **ppixd ); -LEPT_DLL extern PIX * pixConvert16To8 ( PIX *pixs, l_int32 type ); -LEPT_DLL extern PIX * pixConvertGrayToFalseColor ( PIX *pixs, l_float32 gamma ); -LEPT_DLL extern PIX * pixUnpackBinary ( PIX *pixs, l_int32 depth, l_int32 invert ); -LEPT_DLL extern PIX * pixConvert1To16 ( PIX *pixd, PIX *pixs, l_uint16 val0, l_uint16 val1 ); -LEPT_DLL extern PIX * pixConvert1To32 ( PIX *pixd, PIX *pixs, l_uint32 val0, l_uint32 val1 ); -LEPT_DLL extern PIX * pixConvert1To2Cmap ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvert1To2 ( PIX *pixd, PIX *pixs, l_int32 val0, l_int32 val1 ); -LEPT_DLL extern PIX * pixConvert1To4Cmap ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvert1To4 ( PIX *pixd, PIX *pixs, l_int32 val0, l_int32 val1 ); -LEPT_DLL extern PIX * pixConvert1To8Cmap ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvert1To8 ( PIX *pixd, PIX *pixs, l_uint8 val0, l_uint8 val1 ); -LEPT_DLL extern PIX * pixConvert2To8 ( PIX *pixs, l_uint8 val0, l_uint8 val1, l_uint8 val2, l_uint8 val3, l_int32 cmapflag ); -LEPT_DLL extern PIX * pixConvert4To8 ( PIX *pixs, l_int32 cmapflag ); -LEPT_DLL extern PIX * pixConvert8To16 ( PIX *pixs, l_int32 leftshift ); -LEPT_DLL extern PIX * pixConvertTo2 ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvert8To2 ( PIX *pix ); -LEPT_DLL extern PIX * pixConvertTo4 ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvert8To4 ( PIX *pix ); -LEPT_DLL extern PIX * pixConvertTo1Adaptive ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvertTo1 ( PIX *pixs, l_int32 threshold ); -LEPT_DLL extern PIX * pixConvertTo1BySampling ( PIX *pixs, l_int32 factor, l_int32 threshold ); -LEPT_DLL extern PIX * pixConvertTo8 ( PIX *pixs, l_int32 cmapflag ); -LEPT_DLL extern PIX * pixConvertTo8BySampling ( PIX *pixs, l_int32 factor, l_int32 cmapflag ); -LEPT_DLL extern PIX * pixConvertTo8Colormap ( PIX *pixs, l_int32 dither ); -LEPT_DLL extern PIX * pixConvertTo16 ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvertTo32 ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvertTo32BySampling ( PIX *pixs, l_int32 factor ); -LEPT_DLL extern PIX * pixConvert8To32 ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvertTo8Or32 ( PIX *pixs, l_int32 copyflag, l_int32 warnflag ); -LEPT_DLL extern PIX * pixConvert24To32 ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvert32To24 ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvert32To16 ( PIX *pixs, l_int32 type ); -LEPT_DLL extern PIX * pixConvert32To8 ( PIX *pixs, l_int32 type16, l_int32 type8 ); -LEPT_DLL extern PIX * pixRemoveAlpha ( PIX *pixs ); -LEPT_DLL extern PIX * pixAddAlphaTo1bpp ( PIX *pixd, PIX *pixs ); -LEPT_DLL extern PIX * pixConvertLossless ( PIX *pixs, l_int32 d ); -LEPT_DLL extern PIX * pixConvertForPSWrap ( PIX *pixs ); -LEPT_DLL extern PIX * pixConvertToSubpixelRGB ( PIX *pixs, l_float32 scalex, l_float32 scaley, l_int32 order ); -LEPT_DLL extern PIX * pixConvertGrayToSubpixelRGB ( PIX *pixs, l_float32 scalex, l_float32 scaley, l_int32 order ); -LEPT_DLL extern PIX * pixConvertColorToSubpixelRGB ( PIX *pixs, l_float32 scalex, l_float32 scaley, l_int32 order ); -LEPT_DLL extern void l_setNeutralBoostVal ( l_int32 val ); -LEPT_DLL extern PIX * pixConnCompTransform ( PIX *pixs, l_int32 connect, l_int32 depth ); -LEPT_DLL extern PIX * pixConnCompAreaTransform ( PIX *pixs, l_int32 connect ); -LEPT_DLL extern l_ok pixConnCompIncrInit ( PIX *pixs, l_int32 conn, PIX **ppixd, PTAA **pptaa, l_int32 *pncc ); -LEPT_DLL extern l_int32 pixConnCompIncrAdd ( PIX *pixs, PTAA *ptaa, l_int32 *pncc, l_float32 x, l_float32 y, l_int32 debug ); -LEPT_DLL extern l_ok pixGetSortedNeighborValues ( PIX *pixs, l_int32 x, l_int32 y, l_int32 conn, l_int32 **pneigh, l_int32 *pnvals ); -LEPT_DLL extern PIX * pixLocToColorTransform ( PIX *pixs ); -LEPT_DLL extern PIXTILING * pixTilingCreate ( PIX *pixs, l_int32 nx, l_int32 ny, l_int32 w, l_int32 h, l_int32 xoverlap, l_int32 yoverlap ); -LEPT_DLL extern void pixTilingDestroy ( PIXTILING **ppt ); -LEPT_DLL extern l_ok pixTilingGetCount ( PIXTILING *pt, l_int32 *pnx, l_int32 *pny ); -LEPT_DLL extern l_ok pixTilingGetSize ( PIXTILING *pt, l_int32 *pw, l_int32 *ph ); -LEPT_DLL extern PIX * pixTilingGetTile ( PIXTILING *pt, l_int32 i, l_int32 j ); -LEPT_DLL extern l_ok pixTilingNoStripOnPaint ( PIXTILING *pt ); -LEPT_DLL extern l_ok pixTilingPaintTile ( PIX *pixd, l_int32 i, l_int32 j, PIX *pixs, PIXTILING *pt ); -LEPT_DLL extern PIX * pixReadStreamPng ( FILE *fp ); -LEPT_DLL extern l_ok readHeaderPng ( const char *filename, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap ); -LEPT_DLL extern l_ok freadHeaderPng ( FILE *fp, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap ); -LEPT_DLL extern l_ok readHeaderMemPng ( const l_uint8 *data, size_t size, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap ); -LEPT_DLL extern l_int32 fgetPngResolution ( FILE *fp, l_int32 *pxres, l_int32 *pyres ); -LEPT_DLL extern l_ok isPngInterlaced ( const char *filename, l_int32 *pinterlaced ); -LEPT_DLL extern l_ok fgetPngColormapInfo ( FILE *fp, PIXCMAP **pcmap, l_int32 *ptransparency ); -LEPT_DLL extern l_ok pixWritePng ( const char *filename, PIX *pix, l_float32 gamma ); -LEPT_DLL extern l_ok pixWriteStreamPng ( FILE *fp, PIX *pix, l_float32 gamma ); -LEPT_DLL extern l_ok pixSetZlibCompression ( PIX *pix, l_int32 compval ); -LEPT_DLL extern void l_pngSetReadStrip16To8 ( l_int32 flag ); -LEPT_DLL extern PIX * pixReadMemPng ( const l_uint8 *filedata, size_t filesize ); -LEPT_DLL extern l_ok pixWriteMemPng ( l_uint8 **pfiledata, size_t *pfilesize, PIX *pix, l_float32 gamma ); -LEPT_DLL extern PIX * pixReadStreamPnm ( FILE *fp ); -LEPT_DLL extern l_ok readHeaderPnm ( const char *filename, l_int32 *pw, l_int32 *ph, l_int32 *pd, l_int32 *ptype, l_int32 *pbps, l_int32 *pspp ); -LEPT_DLL extern l_ok freadHeaderPnm ( FILE *fp, l_int32 *pw, l_int32 *ph, l_int32 *pd, l_int32 *ptype, l_int32 *pbps, l_int32 *pspp ); -LEPT_DLL extern l_ok pixWriteStreamPnm ( FILE *fp, PIX *pix ); -LEPT_DLL extern l_ok pixWriteStreamAsciiPnm ( FILE *fp, PIX *pix ); -LEPT_DLL extern l_ok pixWriteStreamPam ( FILE *fp, PIX *pix ); -LEPT_DLL extern PIX * pixReadMemPnm ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok readHeaderMemPnm ( const l_uint8 *data, size_t size, l_int32 *pw, l_int32 *ph, l_int32 *pd, l_int32 *ptype, l_int32 *pbps, l_int32 *pspp ); -LEPT_DLL extern l_ok pixWriteMemPnm ( l_uint8 **pdata, size_t *psize, PIX *pix ); -LEPT_DLL extern l_ok pixWriteMemPam ( l_uint8 **pdata, size_t *psize, PIX *pix ); -LEPT_DLL extern PIX * pixProjectiveSampledPta ( PIX *pixs, PTA *ptad, PTA *ptas, l_int32 incolor ); -LEPT_DLL extern PIX * pixProjectiveSampled ( PIX *pixs, l_float32 *vc, l_int32 incolor ); -LEPT_DLL extern PIX * pixProjectivePta ( PIX *pixs, PTA *ptad, PTA *ptas, l_int32 incolor ); -LEPT_DLL extern PIX * pixProjective ( PIX *pixs, l_float32 *vc, l_int32 incolor ); -LEPT_DLL extern PIX * pixProjectivePtaColor ( PIX *pixs, PTA *ptad, PTA *ptas, l_uint32 colorval ); -LEPT_DLL extern PIX * pixProjectiveColor ( PIX *pixs, l_float32 *vc, l_uint32 colorval ); -LEPT_DLL extern PIX * pixProjectivePtaGray ( PIX *pixs, PTA *ptad, PTA *ptas, l_uint8 grayval ); -LEPT_DLL extern PIX * pixProjectiveGray ( PIX *pixs, l_float32 *vc, l_uint8 grayval ); -LEPT_DLL extern PIX * pixProjectivePtaWithAlpha ( PIX *pixs, PTA *ptad, PTA *ptas, PIX *pixg, l_float32 fract, l_int32 border ); -LEPT_DLL extern l_ok getProjectiveXformCoeffs ( PTA *ptas, PTA *ptad, l_float32 **pvc ); -LEPT_DLL extern l_ok projectiveXformSampledPt ( l_float32 *vc, l_int32 x, l_int32 y, l_int32 *pxp, l_int32 *pyp ); -LEPT_DLL extern l_ok projectiveXformPt ( l_float32 *vc, l_int32 x, l_int32 y, l_float32 *pxp, l_float32 *pyp ); -LEPT_DLL extern l_ok convertFilesToPS ( const char *dirin, const char *substr, l_int32 res, const char *fileout ); -LEPT_DLL extern l_ok sarrayConvertFilesToPS ( SARRAY *sa, l_int32 res, const char *fileout ); -LEPT_DLL extern l_ok convertFilesFittedToPS ( const char *dirin, const char *substr, l_float32 xpts, l_float32 ypts, const char *fileout ); -LEPT_DLL extern l_ok sarrayConvertFilesFittedToPS ( SARRAY *sa, l_float32 xpts, l_float32 ypts, const char *fileout ); -LEPT_DLL extern l_ok writeImageCompressedToPSFile ( const char *filein, const char *fileout, l_int32 res, l_int32 *pindex ); -LEPT_DLL extern l_ok convertSegmentedPagesToPS ( const char *pagedir, const char *pagestr, l_int32 page_numpre, const char *maskdir, const char *maskstr, l_int32 mask_numpre, l_int32 numpost, l_int32 maxnum, l_float32 textscale, l_float32 imagescale, l_int32 threshold, const char *fileout ); -LEPT_DLL extern l_ok pixWriteSegmentedPageToPS ( PIX *pixs, PIX *pixm, l_float32 textscale, l_float32 imagescale, l_int32 threshold, l_int32 pageno, const char *fileout ); -LEPT_DLL extern l_ok pixWriteMixedToPS ( PIX *pixb, PIX *pixc, l_float32 scale, l_int32 pageno, const char *fileout ); -LEPT_DLL extern l_ok convertToPSEmbed ( const char *filein, const char *fileout, l_int32 level ); -LEPT_DLL extern l_ok pixaWriteCompressedToPS ( PIXA *pixa, const char *fileout, l_int32 res, l_int32 level ); -LEPT_DLL extern l_ok pixWriteCompressedToPS ( PIX *pix, const char *fileout, l_int32 res, l_int32 level, l_int32 *pindex ); -LEPT_DLL extern l_ok pixWritePSEmbed ( const char *filein, const char *fileout ); -LEPT_DLL extern l_ok pixWriteStreamPS ( FILE *fp, PIX *pix, BOX *box, l_int32 res, l_float32 scale ); -LEPT_DLL extern char * pixWriteStringPS ( PIX *pixs, BOX *box, l_int32 res, l_float32 scale ); -LEPT_DLL extern char * generateUncompressedPS ( char *hexdata, l_int32 w, l_int32 h, l_int32 d, l_int32 psbpl, l_int32 bps, l_float32 xpt, l_float32 ypt, l_float32 wpt, l_float32 hpt, l_int32 boxflag ); -LEPT_DLL extern l_ok convertJpegToPSEmbed ( const char *filein, const char *fileout ); -LEPT_DLL extern l_ok convertJpegToPS ( const char *filein, const char *fileout, const char *operation, l_int32 x, l_int32 y, l_int32 res, l_float32 scale, l_int32 pageno, l_int32 endpage ); -LEPT_DLL extern l_ok convertG4ToPSEmbed ( const char *filein, const char *fileout ); -LEPT_DLL extern l_ok convertG4ToPS ( const char *filein, const char *fileout, const char *operation, l_int32 x, l_int32 y, l_int32 res, l_float32 scale, l_int32 pageno, l_int32 maskflag, l_int32 endpage ); -LEPT_DLL extern l_ok convertTiffMultipageToPS ( const char *filein, const char *fileout, l_float32 fillfract ); -LEPT_DLL extern l_ok convertFlateToPSEmbed ( const char *filein, const char *fileout ); -LEPT_DLL extern l_ok convertFlateToPS ( const char *filein, const char *fileout, const char *operation, l_int32 x, l_int32 y, l_int32 res, l_float32 scale, l_int32 pageno, l_int32 endpage ); -LEPT_DLL extern l_ok pixWriteMemPS ( l_uint8 **pdata, size_t *psize, PIX *pix, BOX *box, l_int32 res, l_float32 scale ); -LEPT_DLL extern l_int32 getResLetterPage ( l_int32 w, l_int32 h, l_float32 fillfract ); -LEPT_DLL extern l_int32 getResA4Page ( l_int32 w, l_int32 h, l_float32 fillfract ); -LEPT_DLL extern void l_psWriteBoundingBox ( l_int32 flag ); -LEPT_DLL extern PTA * ptaCreate ( l_int32 n ); -LEPT_DLL extern PTA * ptaCreateFromNuma ( NUMA *nax, NUMA *nay ); -LEPT_DLL extern void ptaDestroy ( PTA **ppta ); -LEPT_DLL extern PTA * ptaCopy ( PTA *pta ); -LEPT_DLL extern PTA * ptaCopyRange ( PTA *ptas, l_int32 istart, l_int32 iend ); -LEPT_DLL extern PTA * ptaClone ( PTA *pta ); -LEPT_DLL extern l_ok ptaEmpty ( PTA *pta ); -LEPT_DLL extern l_ok ptaAddPt ( PTA *pta, l_float32 x, l_float32 y ); -LEPT_DLL extern l_ok ptaInsertPt ( PTA *pta, l_int32 index, l_int32 x, l_int32 y ); -LEPT_DLL extern l_ok ptaRemovePt ( PTA *pta, l_int32 index ); -LEPT_DLL extern l_int32 ptaGetRefcount ( PTA *pta ); -LEPT_DLL extern l_int32 ptaChangeRefcount ( PTA *pta, l_int32 delta ); -LEPT_DLL extern l_int32 ptaGetCount ( PTA *pta ); -LEPT_DLL extern l_ok ptaGetPt ( PTA *pta, l_int32 index, l_float32 *px, l_float32 *py ); -LEPT_DLL extern l_ok ptaGetIPt ( PTA *pta, l_int32 index, l_int32 *px, l_int32 *py ); -LEPT_DLL extern l_ok ptaSetPt ( PTA *pta, l_int32 index, l_float32 x, l_float32 y ); -LEPT_DLL extern l_ok ptaGetArrays ( PTA *pta, NUMA **pnax, NUMA **pnay ); -LEPT_DLL extern PTA * ptaRead ( const char *filename ); -LEPT_DLL extern PTA * ptaReadStream ( FILE *fp ); -LEPT_DLL extern PTA * ptaReadMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok ptaWriteDebug ( const char *filename, PTA *pta, l_int32 type ); -LEPT_DLL extern l_ok ptaWrite ( const char *filename, PTA *pta, l_int32 type ); -LEPT_DLL extern l_ok ptaWriteStream ( FILE *fp, PTA *pta, l_int32 type ); -LEPT_DLL extern l_ok ptaWriteMem ( l_uint8 **pdata, size_t *psize, PTA *pta, l_int32 type ); -LEPT_DLL extern PTAA * ptaaCreate ( l_int32 n ); -LEPT_DLL extern void ptaaDestroy ( PTAA **pptaa ); -LEPT_DLL extern l_ok ptaaAddPta ( PTAA *ptaa, PTA *pta, l_int32 copyflag ); -LEPT_DLL extern l_int32 ptaaGetCount ( PTAA *ptaa ); -LEPT_DLL extern PTA * ptaaGetPta ( PTAA *ptaa, l_int32 index, l_int32 accessflag ); -LEPT_DLL extern l_ok ptaaGetPt ( PTAA *ptaa, l_int32 ipta, l_int32 jpt, l_float32 *px, l_float32 *py ); -LEPT_DLL extern l_ok ptaaInitFull ( PTAA *ptaa, PTA *pta ); -LEPT_DLL extern l_ok ptaaReplacePta ( PTAA *ptaa, l_int32 index, PTA *pta ); -LEPT_DLL extern l_ok ptaaAddPt ( PTAA *ptaa, l_int32 ipta, l_float32 x, l_float32 y ); -LEPT_DLL extern l_ok ptaaTruncate ( PTAA *ptaa ); -LEPT_DLL extern PTAA * ptaaRead ( const char *filename ); -LEPT_DLL extern PTAA * ptaaReadStream ( FILE *fp ); -LEPT_DLL extern PTAA * ptaaReadMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok ptaaWriteDebug ( const char *filename, PTAA *ptaa, l_int32 type ); -LEPT_DLL extern l_ok ptaaWrite ( const char *filename, PTAA *ptaa, l_int32 type ); -LEPT_DLL extern l_ok ptaaWriteStream ( FILE *fp, PTAA *ptaa, l_int32 type ); -LEPT_DLL extern l_ok ptaaWriteMem ( l_uint8 **pdata, size_t *psize, PTAA *ptaa, l_int32 type ); -LEPT_DLL extern PTA * ptaSubsample ( PTA *ptas, l_int32 subfactor ); -LEPT_DLL extern l_ok ptaJoin ( PTA *ptad, PTA *ptas, l_int32 istart, l_int32 iend ); -LEPT_DLL extern l_ok ptaaJoin ( PTAA *ptaad, PTAA *ptaas, l_int32 istart, l_int32 iend ); -LEPT_DLL extern PTA * ptaReverse ( PTA *ptas, l_int32 type ); -LEPT_DLL extern PTA * ptaTranspose ( PTA *ptas ); -LEPT_DLL extern PTA * ptaCyclicPerm ( PTA *ptas, l_int32 xs, l_int32 ys ); -LEPT_DLL extern PTA * ptaSelectRange ( PTA *ptas, l_int32 first, l_int32 last ); -LEPT_DLL extern BOX * ptaGetBoundingRegion ( PTA *pta ); -LEPT_DLL extern l_ok ptaGetRange ( PTA *pta, l_float32 *pminx, l_float32 *pmaxx, l_float32 *pminy, l_float32 *pmaxy ); -LEPT_DLL extern PTA * ptaGetInsideBox ( PTA *ptas, BOX *box ); -LEPT_DLL extern PTA * pixFindCornerPixels ( PIX *pixs ); -LEPT_DLL extern l_int32 ptaContainsPt ( PTA *pta, l_int32 x, l_int32 y ); -LEPT_DLL extern l_int32 ptaTestIntersection ( PTA *pta1, PTA *pta2 ); -LEPT_DLL extern PTA * ptaTransform ( PTA *ptas, l_int32 shiftx, l_int32 shifty, l_float32 scalex, l_float32 scaley ); -LEPT_DLL extern l_int32 ptaPtInsidePolygon ( PTA *pta, l_float32 x, l_float32 y, l_int32 *pinside ); -LEPT_DLL extern l_float32 l_angleBetweenVectors ( l_float32 x1, l_float32 y1, l_float32 x2, l_float32 y2 ); -LEPT_DLL extern l_ok ptaGetMinMax ( PTA *pta, l_float32 *pxmin, l_float32 *pymin, l_float32 *pxmax, l_float32 *pymax ); -LEPT_DLL extern PTA * ptaSelectByValue ( PTA *ptas, l_float32 xth, l_float32 yth, l_int32 type, l_int32 relation ); -LEPT_DLL extern PTA * ptaCropToMask ( PTA *ptas, PIX *pixm ); -LEPT_DLL extern l_ok ptaGetLinearLSF ( PTA *pta, l_float32 *pa, l_float32 *pb, NUMA **pnafit ); -LEPT_DLL extern l_ok ptaGetQuadraticLSF ( PTA *pta, l_float32 *pa, l_float32 *pb, l_float32 *pc, NUMA **pnafit ); -LEPT_DLL extern l_ok ptaGetCubicLSF ( PTA *pta, l_float32 *pa, l_float32 *pb, l_float32 *pc, l_float32 *pd, NUMA **pnafit ); -LEPT_DLL extern l_ok ptaGetQuarticLSF ( PTA *pta, l_float32 *pa, l_float32 *pb, l_float32 *pc, l_float32 *pd, l_float32 *pe, NUMA **pnafit ); -LEPT_DLL extern l_ok ptaNoisyLinearLSF ( PTA *pta, l_float32 factor, PTA **pptad, l_float32 *pa, l_float32 *pb, l_float32 *pmederr, NUMA **pnafit ); -LEPT_DLL extern l_ok ptaNoisyQuadraticLSF ( PTA *pta, l_float32 factor, PTA **pptad, l_float32 *pa, l_float32 *pb, l_float32 *pc, l_float32 *pmederr, NUMA **pnafit ); -LEPT_DLL extern l_ok applyLinearFit ( l_float32 a, l_float32 b, l_float32 x, l_float32 *py ); -LEPT_DLL extern l_ok applyQuadraticFit ( l_float32 a, l_float32 b, l_float32 c, l_float32 x, l_float32 *py ); -LEPT_DLL extern l_ok applyCubicFit ( l_float32 a, l_float32 b, l_float32 c, l_float32 d, l_float32 x, l_float32 *py ); -LEPT_DLL extern l_ok applyQuarticFit ( l_float32 a, l_float32 b, l_float32 c, l_float32 d, l_float32 e, l_float32 x, l_float32 *py ); -LEPT_DLL extern l_ok pixPlotAlongPta ( PIX *pixs, PTA *pta, l_int32 outformat, const char *title ); -LEPT_DLL extern PTA * ptaGetPixelsFromPix ( PIX *pixs, BOX *box ); -LEPT_DLL extern PIX * pixGenerateFromPta ( PTA *pta, l_int32 w, l_int32 h ); -LEPT_DLL extern PTA * ptaGetBoundaryPixels ( PIX *pixs, l_int32 type ); -LEPT_DLL extern PTAA * ptaaGetBoundaryPixels ( PIX *pixs, l_int32 type, l_int32 connectivity, BOXA **pboxa, PIXA **ppixa ); -LEPT_DLL extern PTAA * ptaaIndexLabeledPixels ( PIX *pixs, l_int32 *pncc ); -LEPT_DLL extern PTA * ptaGetNeighborPixLocs ( PIX *pixs, l_int32 x, l_int32 y, l_int32 conn ); -LEPT_DLL extern PTA * numaConvertToPta1 ( NUMA *na ); -LEPT_DLL extern PTA * numaConvertToPta2 ( NUMA *nax, NUMA *nay ); -LEPT_DLL extern l_ok ptaConvertToNuma ( PTA *pta, NUMA **pnax, NUMA **pnay ); -LEPT_DLL extern PIX * pixDisplayPta ( PIX *pixd, PIX *pixs, PTA *pta ); -LEPT_DLL extern PIX * pixDisplayPtaaPattern ( PIX *pixd, PIX *pixs, PTAA *ptaa, PIX *pixp, l_int32 cx, l_int32 cy ); -LEPT_DLL extern PIX * pixDisplayPtaPattern ( PIX *pixd, PIX *pixs, PTA *pta, PIX *pixp, l_int32 cx, l_int32 cy, l_uint32 color ); -LEPT_DLL extern PTA * ptaReplicatePattern ( PTA *ptas, PIX *pixp, PTA *ptap, l_int32 cx, l_int32 cy, l_int32 w, l_int32 h ); -LEPT_DLL extern PIX * pixDisplayPtaa ( PIX *pixs, PTAA *ptaa ); -LEPT_DLL extern PTA * ptaSort ( PTA *ptas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex ); -LEPT_DLL extern l_ok ptaGetSortIndex ( PTA *ptas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex ); -LEPT_DLL extern PTA * ptaSortByIndex ( PTA *ptas, NUMA *naindex ); -LEPT_DLL extern PTAA * ptaaSortByIndex ( PTAA *ptaas, NUMA *naindex ); -LEPT_DLL extern l_ok ptaGetRankValue ( PTA *pta, l_float32 fract, PTA *ptasort, l_int32 sorttype, l_float32 *pval ); -LEPT_DLL extern PTA * ptaSort2d ( PTA *pta ); -LEPT_DLL extern l_ok ptaEqual ( PTA *pta1, PTA *pta2, l_int32 *psame ); -LEPT_DLL extern PTA * ptaUnionByAset ( PTA *pta1, PTA *pta2 ); -LEPT_DLL extern PTA * ptaRemoveDupsByAset ( PTA *ptas ); -LEPT_DLL extern PTA * ptaIntersectionByAset ( PTA *pta1, PTA *pta2 ); -LEPT_DLL extern L_ASET * l_asetCreateFromPta ( PTA *pta ); -LEPT_DLL extern PTA * ptaUnionByHash ( PTA *pta1, PTA *pta2 ); -LEPT_DLL extern l_ok ptaRemoveDupsByHash ( PTA *ptas, PTA **pptad, L_DNAHASH **pdahash ); -LEPT_DLL extern PTA * ptaIntersectionByHash ( PTA *pta1, PTA *pta2 ); -LEPT_DLL extern l_ok ptaFindPtByHash ( PTA *pta, L_DNAHASH *dahash, l_int32 x, l_int32 y, l_int32 *pindex ); -LEPT_DLL extern L_DNAHASH * l_dnaHashCreateFromPta ( PTA *pta ); -LEPT_DLL extern L_PTRA * ptraCreate ( l_int32 n ); -LEPT_DLL extern void ptraDestroy ( L_PTRA **ppa, l_int32 freeflag, l_int32 warnflag ); -LEPT_DLL extern l_ok ptraAdd ( L_PTRA *pa, void *item ); -LEPT_DLL extern l_ok ptraInsert ( L_PTRA *pa, l_int32 index, void *item, l_int32 shiftflag ); -LEPT_DLL extern void * ptraRemove ( L_PTRA *pa, l_int32 index, l_int32 flag ); -LEPT_DLL extern void * ptraRemoveLast ( L_PTRA *pa ); -LEPT_DLL extern void * ptraReplace ( L_PTRA *pa, l_int32 index, void *item, l_int32 freeflag ); -LEPT_DLL extern l_ok ptraSwap ( L_PTRA *pa, l_int32 index1, l_int32 index2 ); -LEPT_DLL extern l_ok ptraCompactArray ( L_PTRA *pa ); -LEPT_DLL extern l_ok ptraReverse ( L_PTRA *pa ); -LEPT_DLL extern l_ok ptraJoin ( L_PTRA *pa1, L_PTRA *pa2 ); -LEPT_DLL extern l_ok ptraGetMaxIndex ( L_PTRA *pa, l_int32 *pmaxindex ); -LEPT_DLL extern l_ok ptraGetActualCount ( L_PTRA *pa, l_int32 *pcount ); -LEPT_DLL extern void * ptraGetPtrToItem ( L_PTRA *pa, l_int32 index ); -LEPT_DLL extern L_PTRAA * ptraaCreate ( l_int32 n ); -LEPT_DLL extern void ptraaDestroy ( L_PTRAA **ppaa, l_int32 freeflag, l_int32 warnflag ); -LEPT_DLL extern l_ok ptraaGetSize ( L_PTRAA *paa, l_int32 *psize ); -LEPT_DLL extern l_ok ptraaInsertPtra ( L_PTRAA *paa, l_int32 index, L_PTRA *pa ); -LEPT_DLL extern L_PTRA * ptraaGetPtra ( L_PTRAA *paa, l_int32 index, l_int32 accessflag ); -LEPT_DLL extern L_PTRA * ptraaFlattenToPtra ( L_PTRAA *paa ); -LEPT_DLL extern l_ok pixQuadtreeMean ( PIX *pixs, l_int32 nlevels, PIX *pix_ma, FPIXA **pfpixa ); -LEPT_DLL extern l_ok pixQuadtreeVariance ( PIX *pixs, l_int32 nlevels, PIX *pix_ma, DPIX *dpix_msa, FPIXA **pfpixa_v, FPIXA **pfpixa_rv ); -LEPT_DLL extern l_ok pixMeanInRectangle ( PIX *pixs, BOX *box, PIX *pixma, l_float32 *pval ); -LEPT_DLL extern l_ok pixVarianceInRectangle ( PIX *pixs, BOX *box, PIX *pix_ma, DPIX *dpix_msa, l_float32 *pvar, l_float32 *prvar ); -LEPT_DLL extern BOXAA * boxaaQuadtreeRegions ( l_int32 w, l_int32 h, l_int32 nlevels ); -LEPT_DLL extern l_ok quadtreeGetParent ( FPIXA *fpixa, l_int32 level, l_int32 x, l_int32 y, l_float32 *pval ); -LEPT_DLL extern 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 ); -LEPT_DLL extern l_int32 quadtreeMaxLevels ( l_int32 w, l_int32 h ); -LEPT_DLL extern PIX * fpixaDisplayQuadtree ( FPIXA *fpixa, l_int32 factor, l_int32 fontsize ); -LEPT_DLL extern L_QUEUE * lqueueCreate ( l_int32 nalloc ); -LEPT_DLL extern void lqueueDestroy ( L_QUEUE **plq, l_int32 freeflag ); -LEPT_DLL extern l_ok lqueueAdd ( L_QUEUE *lq, void *item ); -LEPT_DLL extern void * lqueueRemove ( L_QUEUE *lq ); -LEPT_DLL extern l_int32 lqueueGetCount ( L_QUEUE *lq ); -LEPT_DLL extern l_ok lqueuePrint ( FILE *fp, L_QUEUE *lq ); -LEPT_DLL extern PIX * pixRankFilter ( PIX *pixs, l_int32 wf, l_int32 hf, l_float32 rank ); -LEPT_DLL extern PIX * pixRankFilterRGB ( PIX *pixs, l_int32 wf, l_int32 hf, l_float32 rank ); -LEPT_DLL extern PIX * pixRankFilterGray ( PIX *pixs, l_int32 wf, l_int32 hf, l_float32 rank ); -LEPT_DLL extern PIX * pixMedianFilter ( PIX *pixs, l_int32 wf, l_int32 hf ); -LEPT_DLL extern PIX * pixRankFilterWithScaling ( PIX *pixs, l_int32 wf, l_int32 hf, l_float32 rank, l_float32 scalefactor ); -LEPT_DLL extern L_RBTREE * l_rbtreeCreate ( l_int32 keytype ); -LEPT_DLL extern RB_TYPE * l_rbtreeLookup ( L_RBTREE *t, RB_TYPE key ); -LEPT_DLL extern void l_rbtreeInsert ( L_RBTREE *t, RB_TYPE key, RB_TYPE value ); -LEPT_DLL extern void l_rbtreeDelete ( L_RBTREE *t, RB_TYPE key ); -LEPT_DLL extern void l_rbtreeDestroy ( L_RBTREE **pt ); -LEPT_DLL extern L_RBTREE_NODE * l_rbtreeGetFirst ( L_RBTREE *t ); -LEPT_DLL extern L_RBTREE_NODE * l_rbtreeGetNext ( L_RBTREE_NODE *n ); -LEPT_DLL extern L_RBTREE_NODE * l_rbtreeGetLast ( L_RBTREE *t ); -LEPT_DLL extern L_RBTREE_NODE * l_rbtreeGetPrev ( L_RBTREE_NODE *n ); -LEPT_DLL extern l_int32 l_rbtreeGetCount ( L_RBTREE *t ); -LEPT_DLL extern void l_rbtreePrint ( FILE *fp, L_RBTREE *t ); -LEPT_DLL extern SARRAY * pixProcessBarcodes ( PIX *pixs, l_int32 format, l_int32 method, SARRAY **psaw, l_int32 debugflag ); -LEPT_DLL extern PIXA * pixExtractBarcodes ( PIX *pixs, l_int32 debugflag ); -LEPT_DLL extern SARRAY * pixReadBarcodes ( PIXA *pixa, l_int32 format, l_int32 method, SARRAY **psaw, l_int32 debugflag ); -LEPT_DLL extern NUMA * pixReadBarcodeWidths ( PIX *pixs, l_int32 method, l_int32 debugflag ); -LEPT_DLL extern BOXA * pixLocateBarcodes ( PIX *pixs, l_int32 thresh, PIX **ppixb, PIX **ppixm ); -LEPT_DLL extern PIX * pixDeskewBarcode ( PIX *pixs, PIX *pixb, BOX *box, l_int32 margin, l_int32 threshold, l_float32 *pangle, l_float32 *pconf ); -LEPT_DLL extern NUMA * pixExtractBarcodeWidths1 ( PIX *pixs, l_float32 thresh, l_float32 binfract, NUMA **pnaehist, NUMA **pnaohist, l_int32 debugflag ); -LEPT_DLL extern NUMA * pixExtractBarcodeWidths2 ( PIX *pixs, l_float32 thresh, l_float32 *pwidth, NUMA **pnac, l_int32 debugflag ); -LEPT_DLL extern NUMA * pixExtractBarcodeCrossings ( PIX *pixs, l_float32 thresh, l_int32 debugflag ); -LEPT_DLL extern NUMA * numaQuantizeCrossingsByWidth ( NUMA *nas, l_float32 binfract, NUMA **pnaehist, NUMA **pnaohist, l_int32 debugflag ); -LEPT_DLL extern NUMA * numaQuantizeCrossingsByWindow ( NUMA *nas, l_float32 ratio, l_float32 *pwidth, l_float32 *pfirstloc, NUMA **pnac, l_int32 debugflag ); -LEPT_DLL extern PIXA * pixaReadFiles ( const char *dirname, const char *substr ); -LEPT_DLL extern PIXA * pixaReadFilesSA ( SARRAY *sa ); -LEPT_DLL extern PIX * pixRead ( const char *filename ); -LEPT_DLL extern PIX * pixReadWithHint ( const char *filename, l_int32 hint ); -LEPT_DLL extern PIX * pixReadIndexed ( SARRAY *sa, l_int32 index ); -LEPT_DLL extern PIX * pixReadStream ( FILE *fp, l_int32 hint ); -LEPT_DLL extern l_ok pixReadHeader ( const char *filename, l_int32 *pformat, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap ); -LEPT_DLL extern l_ok findFileFormat ( const char *filename, l_int32 *pformat ); -LEPT_DLL extern l_ok findFileFormatStream ( FILE *fp, l_int32 *pformat ); -LEPT_DLL extern l_ok findFileFormatBuffer ( const l_uint8 *buf, l_int32 *pformat ); -LEPT_DLL extern l_int32 fileFormatIsTiff ( FILE *fp ); -LEPT_DLL extern PIX * pixReadMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok pixReadHeaderMem ( const l_uint8 *data, size_t size, l_int32 *pformat, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap ); -LEPT_DLL extern l_ok writeImageFileInfo ( const char *filename, FILE *fpout, l_int32 headeronly ); -LEPT_DLL extern l_ok ioFormatTest ( const char *filename ); -LEPT_DLL extern L_RECOG * recogCreateFromRecog ( L_RECOG *recs, l_int32 scalew, l_int32 scaleh, l_int32 linew, l_int32 threshold, l_int32 maxyshift ); -LEPT_DLL extern L_RECOG * recogCreateFromPixa ( PIXA *pixa, l_int32 scalew, l_int32 scaleh, l_int32 linew, l_int32 threshold, l_int32 maxyshift ); -LEPT_DLL extern L_RECOG * recogCreateFromPixaNoFinish ( PIXA *pixa, l_int32 scalew, l_int32 scaleh, l_int32 linew, l_int32 threshold, l_int32 maxyshift ); -LEPT_DLL extern L_RECOG * recogCreate ( l_int32 scalew, l_int32 scaleh, l_int32 linew, l_int32 threshold, l_int32 maxyshift ); -LEPT_DLL extern void recogDestroy ( L_RECOG **precog ); -LEPT_DLL extern l_int32 recogGetCount ( L_RECOG *recog ); -LEPT_DLL extern l_ok recogSetParams ( L_RECOG *recog, l_int32 type, l_int32 min_nopad, l_float32 max_wh_ratio, l_float32 max_ht_ratio ); -LEPT_DLL extern l_int32 recogGetClassIndex ( L_RECOG *recog, l_int32 val, char *text, l_int32 *pindex ); -LEPT_DLL extern l_ok recogStringToIndex ( L_RECOG *recog, char *text, l_int32 *pindex ); -LEPT_DLL extern l_int32 recogGetClassString ( L_RECOG *recog, l_int32 index, char **pcharstr ); -LEPT_DLL extern l_ok l_convertCharstrToInt ( const char *str, l_int32 *pval ); -LEPT_DLL extern L_RECOG * recogRead ( const char *filename ); -LEPT_DLL extern L_RECOG * recogReadStream ( FILE *fp ); -LEPT_DLL extern L_RECOG * recogReadMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok recogWrite ( const char *filename, L_RECOG *recog ); -LEPT_DLL extern l_ok recogWriteStream ( FILE *fp, L_RECOG *recog ); -LEPT_DLL extern l_ok recogWriteMem ( l_uint8 **pdata, size_t *psize, L_RECOG *recog ); -LEPT_DLL extern PIXA * recogExtractPixa ( L_RECOG *recog ); -LEPT_DLL extern BOXA * recogDecode ( L_RECOG *recog, PIX *pixs, l_int32 nlevels, PIX **ppixdb ); -LEPT_DLL extern l_ok recogCreateDid ( L_RECOG *recog, PIX *pixs ); -LEPT_DLL extern l_ok recogDestroyDid ( L_RECOG *recog ); -LEPT_DLL extern l_int32 recogDidExists ( L_RECOG *recog ); -LEPT_DLL extern L_RDID * recogGetDid ( L_RECOG *recog ); -LEPT_DLL extern l_ok recogSetChannelParams ( L_RECOG *recog, l_int32 nlevels ); -LEPT_DLL extern l_ok recogIdentifyMultiple ( L_RECOG *recog, PIX *pixs, l_int32 minh, l_int32 skipsplit, BOXA **pboxa, PIXA **ppixa, PIX **ppixdb, l_int32 debugsplit ); -LEPT_DLL extern l_ok recogSplitIntoCharacters ( L_RECOG *recog, PIX *pixs, l_int32 minh, l_int32 skipsplit, BOXA **pboxa, PIXA **ppixa, l_int32 debug ); -LEPT_DLL extern l_ok recogCorrelationBestRow ( L_RECOG *recog, PIX *pixs, BOXA **pboxa, NUMA **pnascore, NUMA **pnaindex, SARRAY **psachar, l_int32 debug ); -LEPT_DLL extern l_ok recogCorrelationBestChar ( L_RECOG *recog, PIX *pixs, BOX **pbox, l_float32 *pscore, l_int32 *pindex, char **pcharstr, PIX **ppixdb ); -LEPT_DLL extern l_ok recogIdentifyPixa ( L_RECOG *recog, PIXA *pixa, PIX **ppixdb ); -LEPT_DLL extern l_ok recogIdentifyPix ( L_RECOG *recog, PIX *pixs, PIX **ppixdb ); -LEPT_DLL extern l_ok recogSkipIdentify ( L_RECOG *recog ); -LEPT_DLL extern void rchaDestroy ( L_RCHA **prcha ); -LEPT_DLL extern void rchDestroy ( L_RCH **prch ); -LEPT_DLL extern l_ok rchaExtract ( L_RCHA *rcha, NUMA **pnaindex, NUMA **pnascore, SARRAY **psatext, NUMA **pnasample, NUMA **pnaxloc, NUMA **pnayloc, NUMA **pnawidth ); -LEPT_DLL extern l_ok rchExtract ( L_RCH *rch, l_int32 *pindex, l_float32 *pscore, char **ptext, l_int32 *psample, l_int32 *pxloc, l_int32 *pyloc, l_int32 *pwidth ); -LEPT_DLL extern PIX * recogProcessToIdentify ( L_RECOG *recog, PIX *pixs, l_int32 pad ); -LEPT_DLL extern SARRAY * recogExtractNumbers ( L_RECOG *recog, BOXA *boxas, l_float32 scorethresh, l_int32 spacethresh, BOXAA **pbaa, NUMAA **pnaa ); -LEPT_DLL extern PIXA * showExtractNumbers ( PIX *pixs, SARRAY *sa, BOXAA *baa, NUMAA *naa, PIX **ppixdb ); -LEPT_DLL extern l_ok recogTrainLabeled ( L_RECOG *recog, PIX *pixs, BOX *box, char *text, l_int32 debug ); -LEPT_DLL extern l_ok recogProcessLabeled ( L_RECOG *recog, PIX *pixs, BOX *box, char *text, PIX **ppix ); -LEPT_DLL extern l_ok recogAddSample ( L_RECOG *recog, PIX *pix, l_int32 debug ); -LEPT_DLL extern PIX * recogModifyTemplate ( L_RECOG *recog, PIX *pixs ); -LEPT_DLL extern l_int32 recogAverageSamples ( L_RECOG **precog, l_int32 debug ); -LEPT_DLL extern l_int32 pixaAccumulateSamples ( PIXA *pixa, PTA *pta, PIX **ppixd, l_float32 *px, l_float32 *py ); -LEPT_DLL extern l_ok recogTrainingFinished ( L_RECOG **precog, l_int32 modifyflag, l_int32 minsize, l_float32 minfract ); -LEPT_DLL extern PIXA * recogFilterPixaBySize ( PIXA *pixas, l_int32 setsize, l_int32 maxkeep, l_float32 max_ht_ratio, NUMA **pna ); -LEPT_DLL extern PIXAA * recogSortPixaByClass ( PIXA *pixa, l_int32 setsize ); -LEPT_DLL extern l_ok recogRemoveOutliers1 ( L_RECOG **precog, l_float32 minscore, l_int32 mintarget, l_int32 minsize, PIX **ppixsave, PIX **ppixrem ); -LEPT_DLL extern PIXA * pixaRemoveOutliers1 ( PIXA *pixas, l_float32 minscore, l_int32 mintarget, l_int32 minsize, PIX **ppixsave, PIX **ppixrem ); -LEPT_DLL extern l_ok recogRemoveOutliers2 ( L_RECOG **precog, l_float32 minscore, l_int32 minsize, PIX **ppixsave, PIX **ppixrem ); -LEPT_DLL extern PIXA * pixaRemoveOutliers2 ( PIXA *pixas, l_float32 minscore, l_int32 minsize, PIX **ppixsave, PIX **ppixrem ); -LEPT_DLL extern PIXA * recogTrainFromBoot ( L_RECOG *recogboot, PIXA *pixas, l_float32 minscore, l_int32 threshold, l_int32 debug ); -LEPT_DLL extern l_ok recogPadDigitTrainingSet ( L_RECOG **precog, l_int32 scaleh, l_int32 linew ); -LEPT_DLL extern l_int32 recogIsPaddingNeeded ( L_RECOG *recog, SARRAY **psa ); -LEPT_DLL extern PIXA * recogAddDigitPadTemplates ( L_RECOG *recog, SARRAY *sa ); -LEPT_DLL extern L_RECOG * recogMakeBootDigitRecog ( l_int32 nsamp, l_int32 scaleh, l_int32 linew, l_int32 maxyshift, l_int32 debug ); -LEPT_DLL extern PIXA * recogMakeBootDigitTemplates ( l_int32 nsamp, l_int32 debug ); -LEPT_DLL extern l_ok recogShowContent ( FILE *fp, L_RECOG *recog, l_int32 index, l_int32 display ); -LEPT_DLL extern l_ok recogDebugAverages ( L_RECOG **precog, l_int32 debug ); -LEPT_DLL extern l_int32 recogShowAverageTemplates ( L_RECOG *recog ); -LEPT_DLL extern l_ok recogShowMatchesInRange ( L_RECOG *recog, PIXA *pixa, l_float32 minscore, l_float32 maxscore, l_int32 display ); -LEPT_DLL extern PIX * recogShowMatch ( L_RECOG *recog, PIX *pix1, PIX *pix2, BOX *box, l_int32 index, l_float32 score ); -LEPT_DLL extern l_ok regTestSetup ( l_int32 argc, char **argv, L_REGPARAMS **prp ); -LEPT_DLL extern l_ok regTestCleanup ( L_REGPARAMS *rp ); -LEPT_DLL extern l_ok regTestCompareValues ( L_REGPARAMS *rp, l_float32 val1, l_float32 val2, l_float32 delta ); -LEPT_DLL extern l_ok regTestCompareStrings ( L_REGPARAMS *rp, l_uint8 *string1, size_t bytes1, l_uint8 *string2, size_t bytes2 ); -LEPT_DLL extern l_ok regTestComparePix ( L_REGPARAMS *rp, PIX *pix1, PIX *pix2 ); -LEPT_DLL extern l_ok regTestCompareSimilarPix ( L_REGPARAMS *rp, PIX *pix1, PIX *pix2, l_int32 mindiff, l_float32 maxfract, l_int32 printstats ); -LEPT_DLL extern l_ok regTestCheckFile ( L_REGPARAMS *rp, const char *localname ); -LEPT_DLL extern l_ok regTestCompareFiles ( L_REGPARAMS *rp, l_int32 index1, l_int32 index2 ); -LEPT_DLL extern l_ok regTestWritePixAndCheck ( L_REGPARAMS *rp, PIX *pix, l_int32 format ); -LEPT_DLL extern l_ok regTestWriteDataAndCheck ( L_REGPARAMS *rp, void *data, size_t nbytes, const char *ext ); -LEPT_DLL extern char * regTestGenLocalFilename ( L_REGPARAMS *rp, l_int32 index, l_int32 format ); -LEPT_DLL extern l_ok pixRasterop ( PIX *pixd, l_int32 dx, l_int32 dy, l_int32 dw, l_int32 dh, l_int32 op, PIX *pixs, l_int32 sx, l_int32 sy ); -LEPT_DLL extern l_ok pixRasteropVip ( PIX *pixd, l_int32 bx, l_int32 bw, l_int32 vshift, l_int32 incolor ); -LEPT_DLL extern l_ok pixRasteropHip ( PIX *pixd, l_int32 by, l_int32 bh, l_int32 hshift, l_int32 incolor ); -LEPT_DLL extern PIX * pixTranslate ( PIX *pixd, PIX *pixs, l_int32 hshift, l_int32 vshift, l_int32 incolor ); -LEPT_DLL extern l_ok pixRasteropIP ( PIX *pixd, l_int32 hshift, l_int32 vshift, l_int32 incolor ); -LEPT_DLL extern l_ok pixRasteropFullImage ( PIX *pixd, PIX *pixs, l_int32 op ); -LEPT_DLL extern void rasteropUniLow ( l_uint32 *datad, l_int32 dpixw, l_int32 dpixh, l_int32 depth, l_int32 dwpl, l_int32 dx, l_int32 dy, l_int32 dw, l_int32 dh, l_int32 op ); -LEPT_DLL extern void rasteropLow ( l_uint32 *datad, l_int32 dpixw, l_int32 dpixh, l_int32 depth, l_int32 dwpl, l_int32 dx, l_int32 dy, l_int32 dw, l_int32 dh, l_int32 op, l_uint32 *datas, l_int32 spixw, l_int32 spixh, l_int32 swpl, l_int32 sx, l_int32 sy ); -LEPT_DLL extern void rasteropVipLow ( l_uint32 *data, l_int32 pixw, l_int32 pixh, l_int32 depth, l_int32 wpl, l_int32 x, l_int32 w, l_int32 shift ); -LEPT_DLL extern void rasteropHipLow ( l_uint32 *data, l_int32 pixh, l_int32 depth, l_int32 wpl, l_int32 y, l_int32 h, l_int32 shift ); -LEPT_DLL extern PIX * pixRotate ( PIX *pixs, l_float32 angle, l_int32 type, l_int32 incolor, l_int32 width, l_int32 height ); -LEPT_DLL extern PIX * pixEmbedForRotation ( PIX *pixs, l_float32 angle, l_int32 incolor, l_int32 width, l_int32 height ); -LEPT_DLL extern PIX * pixRotateBySampling ( PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor ); -LEPT_DLL extern PIX * pixRotateBinaryNice ( PIX *pixs, l_float32 angle, l_int32 incolor ); -LEPT_DLL extern PIX * pixRotateWithAlpha ( PIX *pixs, l_float32 angle, PIX *pixg, l_float32 fract ); -LEPT_DLL extern PIX * pixRotateAM ( PIX *pixs, l_float32 angle, l_int32 incolor ); -LEPT_DLL extern PIX * pixRotateAMColor ( PIX *pixs, l_float32 angle, l_uint32 colorval ); -LEPT_DLL extern PIX * pixRotateAMGray ( PIX *pixs, l_float32 angle, l_uint8 grayval ); -LEPT_DLL extern PIX * pixRotateAMCorner ( PIX *pixs, l_float32 angle, l_int32 incolor ); -LEPT_DLL extern PIX * pixRotateAMColorCorner ( PIX *pixs, l_float32 angle, l_uint32 fillval ); -LEPT_DLL extern PIX * pixRotateAMGrayCorner ( PIX *pixs, l_float32 angle, l_uint8 grayval ); -LEPT_DLL extern PIX * pixRotateAMColorFast ( PIX *pixs, l_float32 angle, l_uint32 colorval ); -LEPT_DLL extern PIX * pixRotateOrth ( PIX *pixs, l_int32 quads ); -LEPT_DLL extern PIX * pixRotate180 ( PIX *pixd, PIX *pixs ); -LEPT_DLL extern PIX * pixRotate90 ( PIX *pixs, l_int32 direction ); -LEPT_DLL extern PIX * pixFlipLR ( PIX *pixd, PIX *pixs ); -LEPT_DLL extern PIX * pixFlipTB ( PIX *pixd, PIX *pixs ); -LEPT_DLL extern PIX * pixRotateShear ( PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor ); -LEPT_DLL extern PIX * pixRotate2Shear ( PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor ); -LEPT_DLL extern PIX * pixRotate3Shear ( PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor ); -LEPT_DLL extern l_ok pixRotateShearIP ( PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor ); -LEPT_DLL extern PIX * pixRotateShearCenter ( PIX *pixs, l_float32 angle, l_int32 incolor ); -LEPT_DLL extern l_ok pixRotateShearCenterIP ( PIX *pixs, l_float32 angle, l_int32 incolor ); -LEPT_DLL extern PIX * pixStrokeWidthTransform ( PIX *pixs, l_int32 color, l_int32 depth, l_int32 nangles ); -LEPT_DLL extern PIX * pixRunlengthTransform ( PIX *pixs, l_int32 color, l_int32 direction, l_int32 depth ); -LEPT_DLL extern l_ok pixFindHorizontalRuns ( PIX *pix, l_int32 y, l_int32 *xstart, l_int32 *xend, l_int32 *pn ); -LEPT_DLL extern l_ok pixFindVerticalRuns ( PIX *pix, l_int32 x, l_int32 *ystart, l_int32 *yend, l_int32 *pn ); -LEPT_DLL extern NUMA * pixFindMaxRuns ( PIX *pix, l_int32 direction, NUMA **pnastart ); -LEPT_DLL extern l_ok pixFindMaxHorizontalRunOnLine ( PIX *pix, l_int32 y, l_int32 *pxstart, l_int32 *psize ); -LEPT_DLL extern l_ok pixFindMaxVerticalRunOnLine ( PIX *pix, l_int32 x, l_int32 *pystart, l_int32 *psize ); -LEPT_DLL extern l_ok runlengthMembershipOnLine ( l_int32 *buffer, l_int32 size, l_int32 depth, l_int32 *start, l_int32 *end, l_int32 n ); -LEPT_DLL extern l_int32 * makeMSBitLocTab ( l_int32 bitval ); -LEPT_DLL extern SARRAY * sarrayCreate ( l_int32 n ); -LEPT_DLL extern SARRAY * sarrayCreateInitialized ( l_int32 n, const char *initstr ); -LEPT_DLL extern SARRAY * sarrayCreateWordsFromString ( const char *string ); -LEPT_DLL extern SARRAY * sarrayCreateLinesFromString ( const char *string, l_int32 blankflag ); -LEPT_DLL extern void sarrayDestroy ( SARRAY **psa ); -LEPT_DLL extern SARRAY * sarrayCopy ( SARRAY *sa ); -LEPT_DLL extern SARRAY * sarrayClone ( SARRAY *sa ); -LEPT_DLL extern l_ok sarrayAddString ( SARRAY *sa, const char *string, l_int32 copyflag ); -LEPT_DLL extern char * sarrayRemoveString ( SARRAY *sa, l_int32 index ); -LEPT_DLL extern l_ok sarrayReplaceString ( SARRAY *sa, l_int32 index, char *newstr, l_int32 copyflag ); -LEPT_DLL extern l_ok sarrayClear ( SARRAY *sa ); -LEPT_DLL extern l_int32 sarrayGetCount ( SARRAY *sa ); -LEPT_DLL extern char ** sarrayGetArray ( SARRAY *sa, l_int32 *pnalloc, l_int32 *pn ); -LEPT_DLL extern char * sarrayGetString ( SARRAY *sa, l_int32 index, l_int32 copyflag ); -LEPT_DLL extern l_int32 sarrayGetRefcount ( SARRAY *sa ); -LEPT_DLL extern l_ok sarrayChangeRefcount ( SARRAY *sa, l_int32 delta ); -LEPT_DLL extern char * sarrayToString ( SARRAY *sa, l_int32 addnlflag ); -LEPT_DLL extern char * sarrayToStringRange ( SARRAY *sa, l_int32 first, l_int32 nstrings, l_int32 addnlflag ); -LEPT_DLL extern l_ok sarrayJoin ( SARRAY *sa1, SARRAY *sa2 ); -LEPT_DLL extern l_ok sarrayAppendRange ( SARRAY *sa1, SARRAY *sa2, l_int32 start, l_int32 end ); -LEPT_DLL extern l_ok sarrayPadToSameSize ( SARRAY *sa1, SARRAY *sa2, const char *padstring ); -LEPT_DLL extern SARRAY * sarrayConvertWordsToLines ( SARRAY *sa, l_int32 linesize ); -LEPT_DLL extern l_int32 sarraySplitString ( SARRAY *sa, const char *str, const char *separators ); -LEPT_DLL extern SARRAY * sarraySelectBySubstring ( SARRAY *sain, const char *substr ); -LEPT_DLL extern SARRAY * sarraySelectByRange ( SARRAY *sain, l_int32 first, l_int32 last ); -LEPT_DLL extern l_int32 sarrayParseRange ( SARRAY *sa, l_int32 start, l_int32 *pactualstart, l_int32 *pend, l_int32 *pnewstart, const char *substr, l_int32 loc ); -LEPT_DLL extern SARRAY * sarrayRead ( const char *filename ); -LEPT_DLL extern SARRAY * sarrayReadStream ( FILE *fp ); -LEPT_DLL extern SARRAY * sarrayReadMem ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok sarrayWrite ( const char *filename, SARRAY *sa ); -LEPT_DLL extern l_ok sarrayWriteStream ( FILE *fp, SARRAY *sa ); -LEPT_DLL extern l_ok sarrayWriteMem ( l_uint8 **pdata, size_t *psize, SARRAY *sa ); -LEPT_DLL extern l_ok sarrayAppend ( const char *filename, SARRAY *sa ); -LEPT_DLL extern SARRAY * getNumberedPathnamesInDirectory ( const char *dirname, const char *substr, l_int32 numpre, l_int32 numpost, l_int32 maxnum ); -LEPT_DLL extern SARRAY * getSortedPathnamesInDirectory ( const char *dirname, const char *substr, l_int32 first, l_int32 nfiles ); -LEPT_DLL extern SARRAY * convertSortedToNumberedPathnames ( SARRAY *sa, l_int32 numpre, l_int32 numpost, l_int32 maxnum ); -LEPT_DLL extern SARRAY * getFilenamesInDirectory ( const char *dirname ); -LEPT_DLL extern SARRAY * sarraySort ( SARRAY *saout, SARRAY *sain, l_int32 sortorder ); -LEPT_DLL extern SARRAY * sarraySortByIndex ( SARRAY *sain, NUMA *naindex ); -LEPT_DLL extern l_int32 stringCompareLexical ( const char *str1, const char *str2 ); -LEPT_DLL extern SARRAY * sarrayUnionByAset ( SARRAY *sa1, SARRAY *sa2 ); -LEPT_DLL extern SARRAY * sarrayRemoveDupsByAset ( SARRAY *sas ); -LEPT_DLL extern SARRAY * sarrayIntersectionByAset ( SARRAY *sa1, SARRAY *sa2 ); -LEPT_DLL extern L_ASET * l_asetCreateFromSarray ( SARRAY *sa ); -LEPT_DLL extern l_ok sarrayRemoveDupsByHash ( SARRAY *sas, SARRAY **psad, L_DNAHASH **pdahash ); -LEPT_DLL extern SARRAY * sarrayIntersectionByHash ( SARRAY *sa1, SARRAY *sa2 ); -LEPT_DLL extern l_ok sarrayFindStringByHash ( SARRAY *sa, L_DNAHASH *dahash, const char *str, l_int32 *pindex ); -LEPT_DLL extern L_DNAHASH * l_dnaHashCreateFromSarray ( SARRAY *sa ); -LEPT_DLL extern SARRAY * sarrayGenerateIntegers ( l_int32 n ); -LEPT_DLL extern l_ok sarrayLookupCSKV ( SARRAY *sa, const char *keystring, char **pvalstring ); -LEPT_DLL extern PIX * pixScale ( PIX *pixs, l_float32 scalex, l_float32 scaley ); -LEPT_DLL extern PIX * pixScaleToSizeRel ( PIX *pixs, l_int32 delw, l_int32 delh ); -LEPT_DLL extern PIX * pixScaleToSize ( PIX *pixs, l_int32 wd, l_int32 hd ); -LEPT_DLL extern PIX * pixScaleToResolution ( PIX *pixs, l_float32 target, l_float32 assumed, l_float32 *pscalefact ); -LEPT_DLL extern PIX * pixScaleGeneral ( PIX *pixs, l_float32 scalex, l_float32 scaley, l_float32 sharpfract, l_int32 sharpwidth ); -LEPT_DLL extern PIX * pixScaleLI ( PIX *pixs, l_float32 scalex, l_float32 scaley ); -LEPT_DLL extern PIX * pixScaleColorLI ( PIX *pixs, l_float32 scalex, l_float32 scaley ); -LEPT_DLL extern PIX * pixScaleColor2xLI ( PIX *pixs ); -LEPT_DLL extern PIX * pixScaleColor4xLI ( PIX *pixs ); -LEPT_DLL extern PIX * pixScaleGrayLI ( PIX *pixs, l_float32 scalex, l_float32 scaley ); -LEPT_DLL extern PIX * pixScaleGray2xLI ( PIX *pixs ); -LEPT_DLL extern PIX * pixScaleGray4xLI ( PIX *pixs ); -LEPT_DLL extern PIX * pixScaleGray2xLIThresh ( PIX *pixs, l_int32 thresh ); -LEPT_DLL extern PIX * pixScaleGray2xLIDither ( PIX *pixs ); -LEPT_DLL extern PIX * pixScaleGray4xLIThresh ( PIX *pixs, l_int32 thresh ); -LEPT_DLL extern PIX * pixScaleGray4xLIDither ( PIX *pixs ); -LEPT_DLL extern PIX * pixScaleBySampling ( PIX *pixs, l_float32 scalex, l_float32 scaley ); -LEPT_DLL extern PIX * pixScaleBySamplingToSize ( PIX *pixs, l_int32 wd, l_int32 hd ); -LEPT_DLL extern PIX * pixScaleByIntSampling ( PIX *pixs, l_int32 factor ); -LEPT_DLL extern PIX * pixScaleRGBToGrayFast ( PIX *pixs, l_int32 factor, l_int32 color ); -LEPT_DLL extern PIX * pixScaleRGBToBinaryFast ( PIX *pixs, l_int32 factor, l_int32 thresh ); -LEPT_DLL extern PIX * pixScaleGrayToBinaryFast ( PIX *pixs, l_int32 factor, l_int32 thresh ); -LEPT_DLL extern PIX * pixScaleSmooth ( PIX *pix, l_float32 scalex, l_float32 scaley ); -LEPT_DLL extern PIX * pixScaleSmoothToSize ( PIX *pixs, l_int32 wd, l_int32 hd ); -LEPT_DLL extern PIX * pixScaleRGBToGray2 ( PIX *pixs, l_float32 rwt, l_float32 gwt, l_float32 bwt ); -LEPT_DLL extern PIX * pixScaleAreaMap ( PIX *pix, l_float32 scalex, l_float32 scaley ); -LEPT_DLL extern PIX * pixScaleAreaMap2 ( PIX *pix ); -LEPT_DLL extern PIX * pixScaleAreaMapToSize ( PIX *pixs, l_int32 wd, l_int32 hd ); -LEPT_DLL extern PIX * pixScaleBinary ( PIX *pixs, l_float32 scalex, l_float32 scaley ); -LEPT_DLL extern PIX * pixScaleToGray ( PIX *pixs, l_float32 scalefactor ); -LEPT_DLL extern PIX * pixScaleToGrayFast ( PIX *pixs, l_float32 scalefactor ); -LEPT_DLL extern PIX * pixScaleToGray2 ( PIX *pixs ); -LEPT_DLL extern PIX * pixScaleToGray3 ( PIX *pixs ); -LEPT_DLL extern PIX * pixScaleToGray4 ( PIX *pixs ); -LEPT_DLL extern PIX * pixScaleToGray6 ( PIX *pixs ); -LEPT_DLL extern PIX * pixScaleToGray8 ( PIX *pixs ); -LEPT_DLL extern PIX * pixScaleToGray16 ( PIX *pixs ); -LEPT_DLL extern PIX * pixScaleToGrayMipmap ( PIX *pixs, l_float32 scalefactor ); -LEPT_DLL extern PIX * pixScaleMipmap ( PIX *pixs1, PIX *pixs2, l_float32 scale ); -LEPT_DLL extern PIX * pixExpandReplicate ( PIX *pixs, l_int32 factor ); -LEPT_DLL extern PIX * pixScaleGrayMinMax ( PIX *pixs, l_int32 xfact, l_int32 yfact, l_int32 type ); -LEPT_DLL extern PIX * pixScaleGrayMinMax2 ( PIX *pixs, l_int32 type ); -LEPT_DLL extern PIX * pixScaleGrayRankCascade ( PIX *pixs, l_int32 level1, l_int32 level2, l_int32 level3, l_int32 level4 ); -LEPT_DLL extern PIX * pixScaleGrayRank2 ( PIX *pixs, l_int32 rank ); -LEPT_DLL extern l_ok pixScaleAndTransferAlpha ( PIX *pixd, PIX *pixs, l_float32 scalex, l_float32 scaley ); -LEPT_DLL extern PIX * pixScaleWithAlpha ( PIX *pixs, l_float32 scalex, l_float32 scaley, PIX *pixg, l_float32 fract ); -LEPT_DLL extern PIX * pixSeedfillBinary ( PIX *pixd, PIX *pixs, PIX *pixm, l_int32 connectivity ); -LEPT_DLL extern PIX * pixSeedfillBinaryRestricted ( PIX *pixd, PIX *pixs, PIX *pixm, l_int32 connectivity, l_int32 xmax, l_int32 ymax ); -LEPT_DLL extern PIX * pixHolesByFilling ( PIX *pixs, l_int32 connectivity ); -LEPT_DLL extern PIX * pixFillClosedBorders ( PIX *pixs, l_int32 connectivity ); -LEPT_DLL extern PIX * pixExtractBorderConnComps ( PIX *pixs, l_int32 connectivity ); -LEPT_DLL extern PIX * pixRemoveBorderConnComps ( PIX *pixs, l_int32 connectivity ); -LEPT_DLL extern PIX * pixFillBgFromBorder ( PIX *pixs, l_int32 connectivity ); -LEPT_DLL extern PIX * pixFillHolesToBoundingRect ( PIX *pixs, l_int32 minsize, l_float32 maxhfract, l_float32 minfgfract ); -LEPT_DLL extern l_ok pixSeedfillGray ( PIX *pixs, PIX *pixm, l_int32 connectivity ); -LEPT_DLL extern l_ok pixSeedfillGrayInv ( PIX *pixs, PIX *pixm, l_int32 connectivity ); -LEPT_DLL extern l_ok pixSeedfillGraySimple ( PIX *pixs, PIX *pixm, l_int32 connectivity ); -LEPT_DLL extern l_ok pixSeedfillGrayInvSimple ( PIX *pixs, PIX *pixm, l_int32 connectivity ); -LEPT_DLL extern PIX * pixSeedfillGrayBasin ( PIX *pixb, PIX *pixm, l_int32 delta, l_int32 connectivity ); -LEPT_DLL extern PIX * pixDistanceFunction ( PIX *pixs, l_int32 connectivity, l_int32 outdepth, l_int32 boundcond ); -LEPT_DLL extern PIX * pixSeedspread ( PIX *pixs, l_int32 connectivity ); -LEPT_DLL extern l_ok pixLocalExtrema ( PIX *pixs, l_int32 maxmin, l_int32 minmax, PIX **ppixmin, PIX **ppixmax ); -LEPT_DLL extern l_ok pixSelectedLocalExtrema ( PIX *pixs, l_int32 mindist, PIX **ppixmin, PIX **ppixmax ); -LEPT_DLL extern PIX * pixFindEqualValues ( PIX *pixs1, PIX *pixs2 ); -LEPT_DLL extern l_ok pixSelectMinInConnComp ( PIX *pixs, PIX *pixm, PTA **ppta, NUMA **pnav ); -LEPT_DLL extern PIX * pixRemoveSeededComponents ( PIX *pixd, PIX *pixs, PIX *pixm, l_int32 connectivity, l_int32 bordersize ); -LEPT_DLL extern SELA * selaCreate ( l_int32 n ); -LEPT_DLL extern void selaDestroy ( SELA **psela ); -LEPT_DLL extern SEL * selCreate ( l_int32 height, l_int32 width, const char *name ); -LEPT_DLL extern void selDestroy ( SEL **psel ); -LEPT_DLL extern SEL * selCopy ( SEL *sel ); -LEPT_DLL extern SEL * selCreateBrick ( l_int32 h, l_int32 w, l_int32 cy, l_int32 cx, l_int32 type ); -LEPT_DLL extern SEL * selCreateComb ( l_int32 factor1, l_int32 factor2, l_int32 direction ); -LEPT_DLL extern l_int32 ** create2dIntArray ( l_int32 sy, l_int32 sx ); -LEPT_DLL extern l_ok selaAddSel ( SELA *sela, SEL *sel, const char *selname, l_int32 copyflag ); -LEPT_DLL extern l_int32 selaGetCount ( SELA *sela ); -LEPT_DLL extern SEL * selaGetSel ( SELA *sela, l_int32 i ); -LEPT_DLL extern char * selGetName ( SEL *sel ); -LEPT_DLL extern l_ok selSetName ( SEL *sel, const char *name ); -LEPT_DLL extern l_ok selaFindSelByName ( SELA *sela, const char *name, l_int32 *pindex, SEL **psel ); -LEPT_DLL extern l_ok selGetElement ( SEL *sel, l_int32 row, l_int32 col, l_int32 *ptype ); -LEPT_DLL extern l_ok selSetElement ( SEL *sel, l_int32 row, l_int32 col, l_int32 type ); -LEPT_DLL extern l_ok selGetParameters ( SEL *sel, l_int32 *psy, l_int32 *psx, l_int32 *pcy, l_int32 *pcx ); -LEPT_DLL extern l_ok selSetOrigin ( SEL *sel, l_int32 cy, l_int32 cx ); -LEPT_DLL extern l_ok selGetTypeAtOrigin ( SEL *sel, l_int32 *ptype ); -LEPT_DLL extern char * selaGetBrickName ( SELA *sela, l_int32 hsize, l_int32 vsize ); -LEPT_DLL extern char * selaGetCombName ( SELA *sela, l_int32 size, l_int32 direction ); -LEPT_DLL extern l_ok getCompositeParameters ( l_int32 size, l_int32 *psize1, l_int32 *psize2, char **pnameh1, char **pnameh2, char **pnamev1, char **pnamev2 ); -LEPT_DLL extern SARRAY * selaGetSelnames ( SELA *sela ); -LEPT_DLL extern l_ok selFindMaxTranslations ( SEL *sel, l_int32 *pxp, l_int32 *pyp, l_int32 *pxn, l_int32 *pyn ); -LEPT_DLL extern SEL * selRotateOrth ( SEL *sel, l_int32 quads ); -LEPT_DLL extern SELA * selaRead ( const char *fname ); -LEPT_DLL extern SELA * selaReadStream ( FILE *fp ); -LEPT_DLL extern SEL * selRead ( const char *fname ); -LEPT_DLL extern SEL * selReadStream ( FILE *fp ); -LEPT_DLL extern l_ok selaWrite ( const char *fname, SELA *sela ); -LEPT_DLL extern l_ok selaWriteStream ( FILE *fp, SELA *sela ); -LEPT_DLL extern l_ok selWrite ( const char *fname, SEL *sel ); -LEPT_DLL extern l_ok selWriteStream ( FILE *fp, SEL *sel ); -LEPT_DLL extern SEL * selCreateFromString ( const char *text, l_int32 h, l_int32 w, const char *name ); -LEPT_DLL extern char * selPrintToString ( SEL *sel ); -LEPT_DLL extern SELA * selaCreateFromFile ( const char *filename ); -LEPT_DLL extern SEL * selCreateFromPta ( PTA *pta, l_int32 cy, l_int32 cx, const char *name ); -LEPT_DLL extern SEL * selCreateFromPix ( PIX *pix, l_int32 cy, l_int32 cx, const char *name ); -LEPT_DLL extern SEL * selReadFromColorImage ( const char *pathname ); -LEPT_DLL extern SEL * selCreateFromColorPix ( PIX *pixs, const char *selname ); -LEPT_DLL extern SELA * selaCreateFromColorPixa ( PIXA *pixa, SARRAY *sa ); -LEPT_DLL extern PIX * selDisplayInPix ( SEL *sel, l_int32 size, l_int32 gthick ); -LEPT_DLL extern PIX * selaDisplayInPix ( SELA *sela, l_int32 size, l_int32 gthick, l_int32 spacing, l_int32 ncols ); -LEPT_DLL extern SELA * selaAddBasic ( SELA *sela ); -LEPT_DLL extern SELA * selaAddHitMiss ( SELA *sela ); -LEPT_DLL extern SELA * selaAddDwaLinear ( SELA *sela ); -LEPT_DLL extern SELA * selaAddDwaCombs ( SELA *sela ); -LEPT_DLL extern SELA * selaAddCrossJunctions ( SELA *sela, l_float32 hlsize, l_float32 mdist, l_int32 norient, l_int32 debugflag ); -LEPT_DLL extern SELA * selaAddTJunctions ( SELA *sela, l_float32 hlsize, l_float32 mdist, l_int32 norient, l_int32 debugflag ); -LEPT_DLL extern SELA * sela4ccThin ( SELA *sela ); -LEPT_DLL extern SELA * sela8ccThin ( SELA *sela ); -LEPT_DLL extern SELA * sela4and8ccThin ( SELA *sela ); -LEPT_DLL extern SEL * selMakePlusSign ( l_int32 size, l_int32 linewidth ); -LEPT_DLL extern SEL * pixGenerateSelWithRuns ( PIX *pixs, l_int32 nhlines, l_int32 nvlines, l_int32 distance, l_int32 minlength, l_int32 toppix, l_int32 botpix, l_int32 leftpix, l_int32 rightpix, PIX **ppixe ); -LEPT_DLL extern SEL * pixGenerateSelRandom ( PIX *pixs, l_float32 hitfract, l_float32 missfract, l_int32 distance, l_int32 toppix, l_int32 botpix, l_int32 leftpix, l_int32 rightpix, PIX **ppixe ); -LEPT_DLL extern SEL * pixGenerateSelBoundary ( PIX *pixs, l_int32 hitdist, l_int32 missdist, l_int32 hitskip, l_int32 missskip, l_int32 topflag, l_int32 botflag, l_int32 leftflag, l_int32 rightflag, PIX **ppixe ); -LEPT_DLL extern NUMA * pixGetRunCentersOnLine ( PIX *pixs, l_int32 x, l_int32 y, l_int32 minlength ); -LEPT_DLL extern NUMA * pixGetRunsOnLine ( PIX *pixs, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2 ); -LEPT_DLL extern PTA * pixSubsampleBoundaryPixels ( PIX *pixs, l_int32 skip ); -LEPT_DLL extern l_int32 adjacentOnPixelInRaster ( PIX *pixs, l_int32 x, l_int32 y, l_int32 *pxa, l_int32 *pya ); -LEPT_DLL extern PIX * pixDisplayHitMissSel ( PIX *pixs, SEL *sel, l_int32 scalefactor, l_uint32 hitcolor, l_uint32 misscolor ); -LEPT_DLL extern PIX * pixHShear ( PIX *pixd, PIX *pixs, l_int32 yloc, l_float32 radang, l_int32 incolor ); -LEPT_DLL extern PIX * pixVShear ( PIX *pixd, PIX *pixs, l_int32 xloc, l_float32 radang, l_int32 incolor ); -LEPT_DLL extern PIX * pixHShearCorner ( PIX *pixd, PIX *pixs, l_float32 radang, l_int32 incolor ); -LEPT_DLL extern PIX * pixVShearCorner ( PIX *pixd, PIX *pixs, l_float32 radang, l_int32 incolor ); -LEPT_DLL extern PIX * pixHShearCenter ( PIX *pixd, PIX *pixs, l_float32 radang, l_int32 incolor ); -LEPT_DLL extern PIX * pixVShearCenter ( PIX *pixd, PIX *pixs, l_float32 radang, l_int32 incolor ); -LEPT_DLL extern l_ok pixHShearIP ( PIX *pixs, l_int32 yloc, l_float32 radang, l_int32 incolor ); -LEPT_DLL extern l_ok pixVShearIP ( PIX *pixs, l_int32 xloc, l_float32 radang, l_int32 incolor ); -LEPT_DLL extern PIX * pixHShearLI ( PIX *pixs, l_int32 yloc, l_float32 radang, l_int32 incolor ); -LEPT_DLL extern PIX * pixVShearLI ( PIX *pixs, l_int32 xloc, l_float32 radang, l_int32 incolor ); -LEPT_DLL extern PIX * pixDeskewBoth ( PIX *pixs, l_int32 redsearch ); -LEPT_DLL extern PIX * pixDeskew ( PIX *pixs, l_int32 redsearch ); -LEPT_DLL extern PIX * pixFindSkewAndDeskew ( PIX *pixs, l_int32 redsearch, l_float32 *pangle, l_float32 *pconf ); -LEPT_DLL extern PIX * pixDeskewGeneral ( PIX *pixs, l_int32 redsweep, l_float32 sweeprange, l_float32 sweepdelta, l_int32 redsearch, l_int32 thresh, l_float32 *pangle, l_float32 *pconf ); -LEPT_DLL extern l_ok pixFindSkew ( PIX *pixs, l_float32 *pangle, l_float32 *pconf ); -LEPT_DLL extern l_ok pixFindSkewSweep ( PIX *pixs, l_float32 *pangle, l_int32 reduction, l_float32 sweeprange, l_float32 sweepdelta ); -LEPT_DLL extern l_ok pixFindSkewSweepAndSearch ( PIX *pixs, l_float32 *pangle, l_float32 *pconf, l_int32 redsweep, l_int32 redsearch, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta ); -LEPT_DLL extern l_ok pixFindSkewSweepAndSearchScore ( PIX *pixs, l_float32 *pangle, l_float32 *pconf, l_float32 *pendscore, l_int32 redsweep, l_int32 redsearch, l_float32 sweepcenter, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta ); -LEPT_DLL extern l_ok pixFindSkewSweepAndSearchScorePivot ( PIX *pixs, l_float32 *pangle, l_float32 *pconf, l_float32 *pendscore, l_int32 redsweep, l_int32 redsearch, l_float32 sweepcenter, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta, l_int32 pivot ); -LEPT_DLL extern l_int32 pixFindSkewOrthogonalRange ( PIX *pixs, l_float32 *pangle, l_float32 *pconf, l_int32 redsweep, l_int32 redsearch, l_float32 sweeprange, l_float32 sweepdelta, l_float32 minbsdelta, l_float32 confprior ); -LEPT_DLL extern l_ok pixFindDifferentialSquareSum ( PIX *pixs, l_float32 *psum ); -LEPT_DLL extern l_ok pixFindNormalizedSquareSum ( PIX *pixs, l_float32 *phratio, l_float32 *pvratio, l_float32 *pfract ); -LEPT_DLL extern PIX * pixReadStreamSpix ( FILE *fp ); -LEPT_DLL extern l_ok readHeaderSpix ( const char *filename, l_int32 *pwidth, l_int32 *pheight, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap ); -LEPT_DLL extern l_ok freadHeaderSpix ( FILE *fp, l_int32 *pwidth, l_int32 *pheight, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap ); -LEPT_DLL extern l_ok sreadHeaderSpix ( const l_uint32 *data, l_int32 *pwidth, l_int32 *pheight, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap ); -LEPT_DLL extern l_ok pixWriteStreamSpix ( FILE *fp, PIX *pix ); -LEPT_DLL extern PIX * pixReadMemSpix ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok pixWriteMemSpix ( l_uint8 **pdata, size_t *psize, PIX *pix ); -LEPT_DLL extern l_ok pixSerializeToMemory ( PIX *pixs, l_uint32 **pdata, size_t *pnbytes ); -LEPT_DLL extern PIX * pixDeserializeFromMemory ( const l_uint32 *data, size_t nbytes ); -LEPT_DLL extern L_STACK * lstackCreate ( l_int32 n ); -LEPT_DLL extern void lstackDestroy ( L_STACK **plstack, l_int32 freeflag ); -LEPT_DLL extern l_ok lstackAdd ( L_STACK *lstack, void *item ); -LEPT_DLL extern void * lstackRemove ( L_STACK *lstack ); -LEPT_DLL extern l_int32 lstackGetCount ( L_STACK *lstack ); -LEPT_DLL extern l_ok lstackPrint ( FILE *fp, L_STACK *lstack ); -LEPT_DLL extern L_STRCODE * strcodeCreate ( l_int32 fileno ); -LEPT_DLL extern l_ok strcodeCreateFromFile ( const char *filein, l_int32 fileno, const char *outdir ); -LEPT_DLL extern l_ok strcodeGenerate ( L_STRCODE *strcode, const char *filein, const char *type ); -LEPT_DLL extern l_int32 strcodeFinalize ( L_STRCODE **pstrcode, const char *outdir ); -LEPT_DLL extern l_int32 l_getStructStrFromFile ( const char *filename, l_int32 field, char **pstr ); -LEPT_DLL extern l_ok pixFindStrokeLength ( PIX *pixs, l_int32 *tab8, l_int32 *plength ); -LEPT_DLL extern l_ok pixFindStrokeWidth ( PIX *pixs, l_float32 thresh, l_int32 *tab8, l_float32 *pwidth, NUMA **pnahisto ); -LEPT_DLL extern NUMA * pixaFindStrokeWidth ( PIXA *pixa, l_float32 thresh, l_int32 *tab8, l_int32 debug ); -LEPT_DLL extern PIXA * pixaModifyStrokeWidth ( PIXA *pixas, l_float32 targetw ); -LEPT_DLL extern PIX * pixModifyStrokeWidth ( PIX *pixs, l_float32 width, l_float32 targetw ); -LEPT_DLL extern PIXA * pixaSetStrokeWidth ( PIXA *pixas, l_int32 width, l_int32 thinfirst, l_int32 connectivity ); -LEPT_DLL extern PIX * pixSetStrokeWidth ( PIX *pixs, l_int32 width, l_int32 thinfirst, l_int32 connectivity ); -LEPT_DLL extern l_int32 * sudokuReadFile ( const char *filename ); -LEPT_DLL extern l_int32 * sudokuReadString ( const char *str ); -LEPT_DLL extern L_SUDOKU * sudokuCreate ( l_int32 *array ); -LEPT_DLL extern void sudokuDestroy ( L_SUDOKU **psud ); -LEPT_DLL extern l_int32 sudokuSolve ( L_SUDOKU *sud ); -LEPT_DLL extern l_ok sudokuTestUniqueness ( l_int32 *array, l_int32 *punique ); -LEPT_DLL extern L_SUDOKU * sudokuGenerate ( l_int32 *array, l_int32 seed, l_int32 minelems, l_int32 maxtries ); -LEPT_DLL extern l_int32 sudokuOutput ( L_SUDOKU *sud, l_int32 arraytype ); -LEPT_DLL extern PIX * pixAddSingleTextblock ( PIX *pixs, L_BMF *bmf, const char *textstr, l_uint32 val, l_int32 location, l_int32 *poverflow ); -LEPT_DLL extern PIX * pixAddTextlines ( PIX *pixs, L_BMF *bmf, const char *textstr, l_uint32 val, l_int32 location ); -LEPT_DLL extern l_ok pixSetTextblock ( PIX *pixs, L_BMF *bmf, const char *textstr, l_uint32 val, l_int32 x0, l_int32 y0, l_int32 wtext, l_int32 firstindent, l_int32 *poverflow ); -LEPT_DLL extern l_ok pixSetTextline ( PIX *pixs, L_BMF *bmf, const char *textstr, l_uint32 val, l_int32 x0, l_int32 y0, l_int32 *pwidth, l_int32 *poverflow ); -LEPT_DLL extern PIXA * pixaAddTextNumber ( PIXA *pixas, L_BMF *bmf, NUMA *na, l_uint32 val, l_int32 location ); -LEPT_DLL extern PIXA * pixaAddTextlines ( PIXA *pixas, L_BMF *bmf, SARRAY *sa, l_uint32 val, l_int32 location ); -LEPT_DLL extern l_ok pixaAddPixWithText ( PIXA *pixa, PIX *pixs, l_int32 reduction, L_BMF *bmf, const char *textstr, l_uint32 val, l_int32 location ); -LEPT_DLL extern SARRAY * bmfGetLineStrings ( L_BMF *bmf, const char *textstr, l_int32 maxw, l_int32 firstindent, l_int32 *ph ); -LEPT_DLL extern NUMA * bmfGetWordWidths ( L_BMF *bmf, const char *textstr, SARRAY *sa ); -LEPT_DLL extern l_ok bmfGetStringWidth ( L_BMF *bmf, const char *textstr, l_int32 *pw ); -LEPT_DLL extern SARRAY * splitStringToParagraphs ( char *textstr, l_int32 splitflag ); -LEPT_DLL extern PIX * pixReadTiff ( const char *filename, l_int32 n ); -LEPT_DLL extern PIX * pixReadStreamTiff ( FILE *fp, l_int32 n ); -LEPT_DLL extern l_ok pixWriteTiff ( const char *filename, PIX *pix, l_int32 comptype, const char *modestr ); -LEPT_DLL extern l_ok pixWriteTiffCustom ( const char *filename, PIX *pix, l_int32 comptype, const char *modestr, NUMA *natags, SARRAY *savals, SARRAY *satypes, NUMA *nasizes ); -LEPT_DLL extern l_ok pixWriteStreamTiff ( FILE *fp, PIX *pix, l_int32 comptype ); -LEPT_DLL extern l_ok pixWriteStreamTiffWA ( FILE *fp, PIX *pix, l_int32 comptype, const char *modestr ); -LEPT_DLL extern PIX * pixReadFromMultipageTiff ( const char *fname, size_t *poffset ); -LEPT_DLL extern PIXA * pixaReadMultipageTiff ( const char *filename ); -LEPT_DLL extern l_ok pixaWriteMultipageTiff ( const char *fname, PIXA *pixa ); -LEPT_DLL extern l_ok writeMultipageTiff ( const char *dirin, const char *substr, const char *fileout ); -LEPT_DLL extern l_ok writeMultipageTiffSA ( SARRAY *sa, const char *fileout ); -LEPT_DLL extern l_ok fprintTiffInfo ( FILE *fpout, const char *tiffile ); -LEPT_DLL extern l_ok tiffGetCount ( FILE *fp, l_int32 *pn ); -LEPT_DLL extern l_ok getTiffResolution ( FILE *fp, l_int32 *pxres, l_int32 *pyres ); -LEPT_DLL extern l_ok readHeaderTiff ( const char *filename, l_int32 n, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *pres, l_int32 *pcmap, l_int32 *pformat ); -LEPT_DLL extern l_ok freadHeaderTiff ( FILE *fp, l_int32 n, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *pres, l_int32 *pcmap, l_int32 *pformat ); -LEPT_DLL extern l_ok readHeaderMemTiff ( const l_uint8 *cdata, size_t size, l_int32 n, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *pres, l_int32 *pcmap, l_int32 *pformat ); -LEPT_DLL extern l_ok findTiffCompression ( FILE *fp, l_int32 *pcomptype ); -LEPT_DLL extern l_ok extractG4DataFromFile ( const char *filein, l_uint8 **pdata, size_t *pnbytes, l_int32 *pw, l_int32 *ph, l_int32 *pminisblack ); -LEPT_DLL extern PIX * pixReadMemTiff ( const l_uint8 *cdata, size_t size, l_int32 n ); -LEPT_DLL extern PIX * pixReadMemFromMultipageTiff ( const l_uint8 *cdata, size_t size, size_t *poffset ); -LEPT_DLL extern PIXA * pixaReadMemMultipageTiff ( const l_uint8 *data, size_t size ); -LEPT_DLL extern l_ok pixaWriteMemMultipageTiff ( l_uint8 **pdata, size_t *psize, PIXA *pixa ); -LEPT_DLL extern l_ok pixWriteMemTiff ( l_uint8 **pdata, size_t *psize, PIX *pix, l_int32 comptype ); -LEPT_DLL extern l_ok pixWriteMemTiffCustom ( l_uint8 **pdata, size_t *psize, PIX *pix, l_int32 comptype, NUMA *natags, SARRAY *savals, SARRAY *satypes, NUMA *nasizes ); -LEPT_DLL extern l_int32 setMsgSeverity ( l_int32 newsev ); -LEPT_DLL extern l_int32 returnErrorInt ( const char *msg, const char *procname, l_int32 ival ); -LEPT_DLL extern l_float32 returnErrorFloat ( const char *msg, const char *procname, l_float32 fval ); -LEPT_DLL extern void * returnErrorPtr ( const char *msg, const char *procname, void *pval ); -LEPT_DLL extern void leptSetStderrHandler ( void ( *handler ) ( const char * ) ); -LEPT_DLL extern void lept_stderr ( const char *fmt, ... ); -LEPT_DLL extern l_ok filesAreIdentical ( const char *fname1, const char *fname2, l_int32 *psame ); -LEPT_DLL extern l_uint16 convertOnLittleEnd16 ( l_uint16 shortin ); -LEPT_DLL extern l_uint16 convertOnBigEnd16 ( l_uint16 shortin ); -LEPT_DLL extern l_uint32 convertOnLittleEnd32 ( l_uint32 wordin ); -LEPT_DLL extern l_uint32 convertOnBigEnd32 ( l_uint32 wordin ); -LEPT_DLL extern l_ok fileCorruptByDeletion ( const char *filein, l_float32 loc, l_float32 size, const char *fileout ); -LEPT_DLL extern l_ok fileCorruptByMutation ( const char *filein, l_float32 loc, l_float32 size, const char *fileout ); -LEPT_DLL extern l_ok fileReplaceBytes ( const char *filein, l_int32 start, l_int32 nbytes, l_uint8 *newdata, size_t newsize, const char *fileout ); -LEPT_DLL extern l_ok genRandomIntegerInRange ( l_int32 range, l_int32 seed, l_int32 *pval ); -LEPT_DLL extern l_int32 lept_roundftoi ( l_float32 fval ); -LEPT_DLL extern l_ok l_hashStringToUint64 ( const char *str, l_uint64 *phash ); -LEPT_DLL extern l_ok l_hashPtToUint64 ( l_int32 x, l_int32 y, l_uint64 *phash ); -LEPT_DLL extern l_ok l_hashFloat64ToUint64 ( l_int32 nbuckets, l_float64 val, l_uint64 *phash ); -LEPT_DLL extern l_ok findNextLargerPrime ( l_int32 start, l_uint32 *pprime ); -LEPT_DLL extern l_ok lept_isPrime ( l_uint64 n, l_int32 *pis_prime, l_uint32 *pfactor ); -LEPT_DLL extern l_uint32 convertIntToGrayCode ( l_uint32 val ); -LEPT_DLL extern l_uint32 convertGrayCodeToInt ( l_uint32 val ); -LEPT_DLL extern char * getLeptonicaVersion ( void ); -LEPT_DLL extern void startTimer ( void ); -LEPT_DLL extern l_float32 stopTimer ( void ); -LEPT_DLL extern L_TIMER startTimerNested ( void ); -LEPT_DLL extern l_float32 stopTimerNested ( L_TIMER rusage_start ); -LEPT_DLL extern void l_getCurrentTime ( l_int32 *sec, l_int32 *usec ); -LEPT_DLL extern L_WALLTIMER * startWallTimer ( void ); -LEPT_DLL extern l_float32 stopWallTimer ( L_WALLTIMER **ptimer ); -LEPT_DLL extern char * l_getFormattedDate ( void ); -LEPT_DLL extern char * stringNew ( const char *src ); -LEPT_DLL extern l_ok stringCopy ( char *dest, const char *src, l_int32 n ); -LEPT_DLL extern char * stringCopySegment ( const char *src, l_int32 start, l_int32 nbytes ); -LEPT_DLL extern l_ok stringReplace ( char **pdest, const char *src ); -LEPT_DLL extern l_int32 stringLength ( const char *src, size_t size ); -LEPT_DLL extern l_int32 stringCat ( char *dest, size_t size, const char *src ); -LEPT_DLL extern char * stringConcatNew ( const char *first, ... ); -LEPT_DLL extern char * stringJoin ( const char *src1, const char *src2 ); -LEPT_DLL extern l_ok stringJoinIP ( char **psrc1, const char *src2 ); -LEPT_DLL extern char * stringReverse ( const char *src ); -LEPT_DLL extern char * strtokSafe ( char *cstr, const char *seps, char **psaveptr ); -LEPT_DLL extern l_ok stringSplitOnToken ( char *cstr, const char *seps, char **phead, char **ptail ); -LEPT_DLL extern l_ok stringCheckForChars ( const char *src, const char *chars, l_int32 *pfound ); -LEPT_DLL extern char * stringRemoveChars ( const char *src, const char *remchars ); -LEPT_DLL extern char * stringReplaceEachSubstr ( const char *src, const char *sub1, const char *sub2, l_int32 *pcount ); -LEPT_DLL extern char * stringReplaceSubstr ( const char *src, const char *sub1, const char *sub2, l_int32 *ploc, l_int32 *pfound ); -LEPT_DLL extern L_DNA * stringFindEachSubstr ( const char *src, const char *sub ); -LEPT_DLL extern l_int32 stringFindSubstr ( const char *src, const char *sub, l_int32 *ploc ); -LEPT_DLL extern l_uint8 * arrayReplaceEachSequence ( const l_uint8 *datas, size_t dataslen, const l_uint8 *seq, size_t seqlen, const l_uint8 *newseq, size_t newseqlen, size_t *pdatadlen, l_int32 *pcount ); -LEPT_DLL extern L_DNA * arrayFindEachSequence ( const l_uint8 *data, size_t datalen, const l_uint8 *sequence, size_t seqlen ); -LEPT_DLL extern l_ok arrayFindSequence ( const l_uint8 *data, size_t datalen, const l_uint8 *sequence, size_t seqlen, l_int32 *poffset, l_int32 *pfound ); -LEPT_DLL extern void * reallocNew ( void **pindata, l_int32 oldsize, l_int32 newsize ); -LEPT_DLL extern l_uint8 * l_binaryRead ( const char *filename, size_t *pnbytes ); -LEPT_DLL extern l_uint8 * l_binaryReadStream ( FILE *fp, size_t *pnbytes ); -LEPT_DLL extern l_uint8 * l_binaryReadSelect ( const char *filename, size_t start, size_t nbytes, size_t *pnread ); -LEPT_DLL extern l_uint8 * l_binaryReadSelectStream ( FILE *fp, size_t start, size_t nbytes, size_t *pnread ); -LEPT_DLL extern l_ok l_binaryWrite ( const char *filename, const char *operation, const void *data, size_t nbytes ); -LEPT_DLL extern size_t nbytesInFile ( const char *filename ); -LEPT_DLL extern size_t fnbytesInFile ( FILE *fp ); -LEPT_DLL extern l_uint8 * l_binaryCopy ( const l_uint8 *datas, size_t size ); -LEPT_DLL extern l_ok l_binaryCompare ( const l_uint8 *data1, size_t size1, const l_uint8 *data2, size_t size2, l_int32 *psame ); -LEPT_DLL extern l_ok fileCopy ( const char *srcfile, const char *newfile ); -LEPT_DLL extern l_ok fileConcatenate ( const char *srcfile, const char *destfile ); -LEPT_DLL extern l_ok fileAppendString ( const char *filename, const char *str ); -LEPT_DLL extern FILE * fopenReadStream ( const char *filename ); -LEPT_DLL extern FILE * fopenWriteStream ( const char *filename, const char *modestring ); -LEPT_DLL extern FILE * fopenReadFromMemory ( const l_uint8 *data, size_t size ); -LEPT_DLL extern FILE * fopenWriteWinTempfile ( void ); -LEPT_DLL extern FILE * lept_fopen ( const char *filename, const char *mode ); -LEPT_DLL extern l_ok lept_fclose ( FILE *fp ); -LEPT_DLL extern void * lept_calloc ( size_t nmemb, size_t size ); -LEPT_DLL extern void lept_free ( void *ptr ); -LEPT_DLL extern l_int32 lept_mkdir ( const char *subdir ); -LEPT_DLL extern l_int32 lept_rmdir ( const char *subdir ); -LEPT_DLL extern void lept_direxists ( const char *dir, l_int32 *pexists ); -LEPT_DLL extern l_int32 lept_rm_match ( const char *subdir, const char *substr ); -LEPT_DLL extern l_int32 lept_rm ( const char *subdir, const char *tail ); -LEPT_DLL extern l_int32 lept_rmfile ( const char *filepath ); -LEPT_DLL extern l_int32 lept_mv ( const char *srcfile, const char *newdir, const char *newtail, char **pnewpath ); -LEPT_DLL extern l_int32 lept_cp ( const char *srcfile, const char *newdir, const char *newtail, char **pnewpath ); -LEPT_DLL extern void callSystemDebug ( const char *cmd ); -LEPT_DLL extern l_ok splitPathAtDirectory ( const char *pathname, char **pdir, char **ptail ); -LEPT_DLL extern l_ok splitPathAtExtension ( const char *pathname, char **pbasename, char **pextension ); -LEPT_DLL extern char * pathJoin ( const char *dir, const char *fname ); -LEPT_DLL extern char * appendSubdirs ( const char *basedir, const char *subdirs ); -LEPT_DLL extern l_ok convertSepCharsInPath ( char *path, l_int32 type ); -LEPT_DLL extern char * genPathname ( const char *dir, const char *fname ); -LEPT_DLL extern l_ok makeTempDirname ( char *result, size_t nbytes, const char *subdir ); -LEPT_DLL extern l_ok modifyTrailingSlash ( char *path, size_t nbytes, l_int32 flag ); -LEPT_DLL extern char * l_makeTempFilename ( void ); -LEPT_DLL extern l_int32 extractNumberFromFilename ( const char *fname, l_int32 numpre, l_int32 numpost ); -LEPT_DLL extern PIX * pixSimpleCaptcha ( PIX *pixs, l_int32 border, l_int32 nterms, l_uint32 seed, l_uint32 color, l_int32 cmapflag ); -LEPT_DLL extern PIX * pixRandomHarmonicWarp ( PIX *pixs, l_float32 xmag, l_float32 ymag, l_float32 xfreq, l_float32 yfreq, l_int32 nx, l_int32 ny, l_uint32 seed, l_int32 grayval ); -LEPT_DLL extern PIX * pixWarpStereoscopic ( PIX *pixs, l_int32 zbend, l_int32 zshiftt, l_int32 zshiftb, l_int32 ybendt, l_int32 ybendb, l_int32 redleft ); -LEPT_DLL extern PIX * pixStretchHorizontal ( PIX *pixs, l_int32 dir, l_int32 type, l_int32 hmax, l_int32 operation, l_int32 incolor ); -LEPT_DLL extern PIX * pixStretchHorizontalSampled ( PIX *pixs, l_int32 dir, l_int32 type, l_int32 hmax, l_int32 incolor ); -LEPT_DLL extern PIX * pixStretchHorizontalLI ( PIX *pixs, l_int32 dir, l_int32 type, l_int32 hmax, l_int32 incolor ); -LEPT_DLL extern PIX * pixQuadraticVShear ( PIX *pixs, l_int32 dir, l_int32 vmaxt, l_int32 vmaxb, l_int32 operation, l_int32 incolor ); -LEPT_DLL extern PIX * pixQuadraticVShearSampled ( PIX *pixs, l_int32 dir, l_int32 vmaxt, l_int32 vmaxb, l_int32 incolor ); -LEPT_DLL extern PIX * pixQuadraticVShearLI ( PIX *pixs, l_int32 dir, l_int32 vmaxt, l_int32 vmaxb, l_int32 incolor ); -LEPT_DLL extern PIX * pixStereoFromPair ( PIX *pix1, PIX *pix2, l_float32 rwt, l_float32 gwt, l_float32 bwt ); -LEPT_DLL extern L_WSHED * wshedCreate ( PIX *pixs, PIX *pixm, l_int32 mindepth, l_int32 debugflag ); -LEPT_DLL extern void wshedDestroy ( L_WSHED **pwshed ); -LEPT_DLL extern l_ok wshedApply ( L_WSHED *wshed ); -LEPT_DLL extern l_ok wshedBasins ( L_WSHED *wshed, PIXA **ppixa, NUMA **pnalevels ); -LEPT_DLL extern PIX * wshedRenderFill ( L_WSHED *wshed ); -LEPT_DLL extern PIX * wshedRenderColors ( L_WSHED *wshed ); -LEPT_DLL extern l_ok pixaWriteWebPAnim ( const char *filename, PIXA *pixa, l_int32 loopcount, l_int32 duration, l_int32 quality, l_int32 lossless ); -LEPT_DLL extern l_ok pixaWriteStreamWebPAnim ( FILE *fp, PIXA *pixa, l_int32 loopcount, l_int32 duration, l_int32 quality, l_int32 lossless ); -LEPT_DLL extern l_ok pixaWriteMemWebPAnim ( l_uint8 **pencdata, size_t *pencsize, PIXA *pixa, l_int32 loopcount, l_int32 duration, l_int32 quality, l_int32 lossless ); -LEPT_DLL extern PIX * pixReadStreamWebP ( FILE *fp ); -LEPT_DLL extern PIX * pixReadMemWebP ( const l_uint8 *filedata, size_t filesize ); -LEPT_DLL extern l_ok readHeaderWebP ( const char *filename, l_int32 *pw, l_int32 *ph, l_int32 *pspp ); -LEPT_DLL extern l_ok readHeaderMemWebP ( const l_uint8 *data, size_t size, l_int32 *pw, l_int32 *ph, l_int32 *pspp ); -LEPT_DLL extern l_ok pixWriteWebP ( const char *filename, PIX *pixs, l_int32 quality, l_int32 lossless ); -LEPT_DLL extern l_ok pixWriteStreamWebP ( FILE *fp, PIX *pixs, l_int32 quality, l_int32 lossless ); -LEPT_DLL extern l_ok pixWriteMemWebP ( l_uint8 **pencdata, size_t *pencsize, PIX *pixs, l_int32 quality, l_int32 lossless ); -LEPT_DLL extern l_int32 l_jpegSetQuality ( l_int32 new_quality ); -LEPT_DLL extern void setLeptDebugOK ( l_int32 allow ); -LEPT_DLL extern l_ok pixaWriteFiles ( const char *rootname, PIXA *pixa, l_int32 format ); -LEPT_DLL extern l_ok pixWriteDebug ( const char *fname, PIX *pix, l_int32 format ); -LEPT_DLL extern l_ok pixWrite ( const char *fname, PIX *pix, l_int32 format ); -LEPT_DLL extern l_ok pixWriteAutoFormat ( const char *filename, PIX *pix ); -LEPT_DLL extern l_ok pixWriteStream ( FILE *fp, PIX *pix, l_int32 format ); -LEPT_DLL extern l_ok pixWriteImpliedFormat ( const char *filename, PIX *pix, l_int32 quality, l_int32 progressive ); -LEPT_DLL extern l_int32 pixChooseOutputFormat ( PIX *pix ); -LEPT_DLL extern l_int32 getImpliedFileFormat ( const char *filename ); -LEPT_DLL extern l_ok pixGetAutoFormat ( PIX *pix, l_int32 *pformat ); -LEPT_DLL extern const char * getFormatExtension ( l_int32 format ); -LEPT_DLL extern l_ok pixWriteMem ( l_uint8 **pdata, size_t *psize, PIX *pix, l_int32 format ); -LEPT_DLL extern l_ok l_fileDisplay ( const char *fname, l_int32 x, l_int32 y, l_float32 scale ); -LEPT_DLL extern l_ok pixDisplay ( PIX *pixs, l_int32 x, l_int32 y ); -LEPT_DLL extern l_ok pixDisplayWithTitle ( PIX *pixs, l_int32 x, l_int32 y, const char *title, l_int32 dispflag ); -LEPT_DLL extern PIX * pixMakeColorSquare ( l_uint32 color, l_int32 size, l_int32 addlabel, l_int32 location, l_uint32 textcolor ); -LEPT_DLL extern void l_chooseDisplayProg ( l_int32 selection ); -LEPT_DLL extern void changeFormatForMissingLib ( l_int32 *pformat ); -LEPT_DLL extern l_ok pixDisplayWrite ( PIX *pixs, l_int32 reduction ); -LEPT_DLL extern l_ok pixSaveTiled ( PIX *pixs, PIXA *pixa, l_float32 scalefactor, l_int32 newrow, l_int32 space, l_int32 dp ); -LEPT_DLL extern l_ok pixSaveTiledOutline ( PIX *pixs, PIXA *pixa, l_float32 scalefactor, l_int32 newrow, l_int32 space, l_int32 linewidth, l_int32 dp ); -LEPT_DLL extern l_ok pixSaveTiledWithText ( PIX *pixs, PIXA *pixa, l_int32 outwidth, l_int32 newrow, l_int32 space, l_int32 linewidth, L_BMF *bmf, const char *textstr, l_uint32 val, l_int32 location ); -LEPT_DLL extern l_uint8 * zlibCompress ( l_uint8 *datain, size_t nin, size_t *pnout ); -LEPT_DLL extern l_uint8 * zlibUncompress ( l_uint8 *datain, size_t nin, size_t *pnout ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* NO_PROTOS */ - - -#endif /* LEPTONICA_ALLHEADERS_H */ - diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/alltypes.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/alltypes.h deleted file mode 100644 index a84c0bfa..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/alltypes.h +++ /dev/null @@ -1,66 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_ALLTYPES_H -#define LEPTONICA_ALLTYPES_H - - /* Standard */ -#include
- * Contains the following structs: - * struct Numa - * struct Numaa - * struct L_Dna - * struct L_Dnaa - * struct L_DnaHash - * struct Sarray - * struct L_Bytea - * - * Contains definitions for: - * Numa interpolation flags - * Numa and FPix border flags - * Numa data type conversion to string - *- */ - - -/*------------------------------------------------------------------------* - * Array Structs * - *------------------------------------------------------------------------*/ - -/*! Numa version for serialization */ -#define NUMA_VERSION_NUMBER 1 - - /*! Number array: an array of floats */ -struct Numa -{ - l_int32 nalloc; /*!< size of allocated number array */ - l_int32 n; /*!< number of numbers saved */ - l_int32 refcount; /*!< reference count (1 if no clones) */ - l_float32 startx; /*!< x value assigned to array[0] */ - l_float32 delx; /*!< change in x value as i --> i + 1 */ - l_float32 *array; /*!< number array */ -}; -typedef struct Numa NUMA; - - /*! Array of number arrays */ -struct Numaa -{ - l_int32 nalloc; /*!< size of allocated ptr array */ - l_int32 n; /*!< number of Numa saved */ - struct Numa **numa; /*!< array of Numa */ -}; -typedef struct Numaa NUMAA; - -/*! Dna version for serialization */ -#define DNA_VERSION_NUMBER 1 - - /*! Double number array: an array of doubles */ -struct L_Dna -{ - l_int32 nalloc; /*!< size of allocated number array */ - l_int32 n; /*!< number of numbers saved */ - l_int32 refcount; /*!< reference count (1 if no clones) */ - l_float64 startx; /*!< x value assigned to array[0] */ - l_float64 delx; /*!< change in x value as i --> i + 1 */ - l_float64 *array; /*!< number array */ -}; -typedef struct L_Dna L_DNA; - - /*! Array of double number arrays */ -struct L_Dnaa -{ - l_int32 nalloc; /*!< size of allocated ptr array */ - l_int32 n; /*!< number of L_Dna saved */ - struct L_Dna **dna; /*!< array of L_Dna */ -}; -typedef struct L_Dnaa L_DNAA; - - /*! A hash table of Dnas */ -struct L_DnaHash -{ - l_int32 nbuckets; - l_int32 initsize; /*!< initial size of each dna that is made */ - struct L_Dna **dna; /*!< array of L_Dna */ -}; -typedef struct L_DnaHash L_DNAHASH; - -/*! Sarray version for serialization */ -#define SARRAY_VERSION_NUMBER 1 - - /*! String array: an array of C strings */ -struct Sarray -{ - l_int32 nalloc; /*!< size of allocated ptr array */ - l_int32 n; /*!< number of strings allocated */ - l_int32 refcount; /*!< reference count (1 if no clones) */ - char **array; /*!< string array */ -}; -typedef struct Sarray SARRAY; - - /*! Byte array (analogous to C++ "string") */ -struct L_Bytea -{ - size_t nalloc; /*!< number of bytes allocated in data array */ - size_t size; /*!< number of bytes presently used */ - l_int32 refcount; /*!< reference count (1 if no clones) */ - l_uint8 *data; /*!< data array */ -}; -typedef struct L_Bytea L_BYTEA; - - -/*------------------------------------------------------------------------* - * Array flags * - *------------------------------------------------------------------------*/ -/*! Numa Interpolation */ -enum { - L_LINEAR_INTERP = 1, /*!< linear */ - L_QUADRATIC_INTERP = 2 /*!< quadratic */ -}; - -/*! Border Adding */ -enum { - L_CONTINUED_BORDER = 1, /*!< extended with same value */ - L_SLOPE_BORDER = 2, /*!< extended with constant normal derivative */ - L_MIRRORED_BORDER = 3 /*!< mirrored */ -}; - -/*! Numa Data Conversion */ -enum { - L_INTEGER_VALUE = 1, /*!< convert to integer */ - L_FLOAT_VALUE = 2 /*!< convert to float */ -}; - -#endif /* LEPTONICA_ARRAY_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/arrayaccess.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/arrayaccess.c deleted file mode 100644 index 6675a535..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/arrayaccess.c +++ /dev/null @@ -1,367 +0,0 @@ -/*====================================================================* - - 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 arrayaccess.c - *
- * - * Access within an array of 32-bit words - * - * l_int32 l_getDataBit() - * void l_setDataBit() - * void l_clearDataBit() - * void l_setDataBitVal() - * l_int32 l_getDataDibit() - * void l_setDataDibit() - * void l_clearDataDibit() - * l_int32 l_getDataQbit() - * void l_setDataQbit() - * void l_clearDataQbit() - * l_int32 l_getDataByte() - * void l_setDataByte() - * l_int32 l_getDataTwoBytes() - * void l_setDataTwoBytes() - * l_int32 l_getDataFourBytes() - * void l_setDataFourBytes() - * - * Note that these all require 32-bit alignment, and hence an input - * ptr to l_uint32. However, this is not enforced by the compiler. - * Instead, we allow the use of a void* ptr, because the line ptrs - * are an efficient way to get random access (see pixGetLinePtrs()). - * It is then necessary to cast internally within each function - * because ptr arithmetic requires knowing the size of the units - * being referenced. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This is an accessor for a 1 bpp pix. - * (2) It is actually a little slower than using: - * if (val == 0) - * l_ClearDataBit(line, n); - * else - * l_SetDataBit(line, n); - *- */ -void -l_setDataBitVal(void *line, - l_int32 n, - l_int32 val) -{ -l_uint32 *pword; - - pword = (l_uint32 *)line + (n >> 5); - *pword &= ~(0x80000000 >> (n & 31)); /* clear */ - *pword |= (l_uint32)val << (31 - (n & 31)); /* set */ - return; -} - - -/*! - * \brief l_getDataDibit() - * - * \param[in] line ptr to beginning of data line - * \param[in] n pixel index - * \return val of the nth 2-bit pixel. - */ -l_int32 -l_getDataDibit(const void *line, - l_int32 n) -{ - return (*((const l_uint32 *)line + (n >> 4)) >> (2 * (15 - (n & 15)))) & 3; -} - - -/*! - * \brief l_setDataDibit() - * - * \param[in] line ptr to beginning of data line - * \param[in] n pixel index - * \param[in] val val to be inserted: 0 - 3 - * \return void - */ -void -l_setDataDibit(void *line, - l_int32 n, - l_int32 val) -{ -l_uint32 *pword; - - pword = (l_uint32 *)line + (n >> 4); - *pword &= ~(0xc0000000 >> (2 * (n & 15))); /* clear */ - *pword |= (l_uint32)(val & 3) << (30 - 2 * (n & 15)); /* set */ - return; -} - - -/*! - * \brief l_clearDataDibit() - * - * \param[in] line ptr to beginning of data line - * \param[in] n pixel index - * \return void - * - * Action: sets the 2-bit pixel to 0 - */ -void -l_clearDataDibit(void *line, - l_int32 n) -{ - *((l_uint32 *)line + (n >> 4)) &= ~(0xc0000000 >> (2 * (n & 15))); -} - - -/*! - * \brief l_getDataQbit() - * - * \param[in] line ptr to beginning of data line - * \param[in] n pixel index - * \return val of the nth 4-bit pixel. - */ -l_int32 -l_getDataQbit(const void *line, - l_int32 n) -{ - return (*((const l_uint32 *)line + (n >> 3)) >> (4 * (7 - (n & 7)))) & 0xf; -} - - -/*! - * \brief l_setDataQbit() - * - * \param[in] line ptr to beginning of data line - * \param[in] n pixel index - * \param[in] val val to be inserted: 0 - 0xf - * \return void - */ -void -l_setDataQbit(void *line, - l_int32 n, - l_int32 val) -{ -l_uint32 *pword; - - pword = (l_uint32 *)line + (n >> 3); - *pword &= ~(0xf0000000 >> (4 * (n & 7))); /* clear */ - *pword |= (l_uint32)(val & 15) << (28 - 4 * (n & 7)); /* set */ - return; -} - - -/*! - * \brief l_clearDataQbit() - * - * \param[in] line ptr to beginning of data line - * \param[in] n pixel index - * \return void - * - * Action: sets the 4-bit pixel to 0 - */ -void -l_clearDataQbit(void *line, - l_int32 n) -{ - *((l_uint32 *)line + (n >> 3)) &= ~(0xf0000000 >> (4 * (n & 7))); -} - - -/*! - * \brief l_getDataByte() - * - * \param[in] line ptr to beginning of data line - * \param[in] n pixel index - * \return value of the n-th byte pixel - */ -l_int32 -l_getDataByte(const void *line, - l_int32 n) -{ -#ifdef L_BIG_ENDIAN - return *((const l_uint8 *)line + n); -#else /* L_LITTLE_ENDIAN */ - return *(l_uint8 *)((l_uintptr_t)((const l_uint8 *)line + n) ^ 3); -#endif /* L_BIG_ENDIAN */ -} - - -/*! - * \brief l_setDataByte() - * - * \param[in] line ptr to beginning of data line - * \param[in] n pixel index - * \param[in] val val to be inserted: 0 - 0xff - * \return void - */ -void -l_setDataByte(void *line, - l_int32 n, - l_int32 val) -{ -#ifdef L_BIG_ENDIAN - *((l_uint8 *)line + n) = val; -#else /* L_LITTLE_ENDIAN */ - *(l_uint8 *)((l_uintptr_t)((l_uint8 *)line + n) ^ 3) = val; -#endif /* L_BIG_ENDIAN */ -} - - -/*! - * \brief l_getDataTwoBytes() - * - * \param[in] line ptr to beginning of data line - * \param[in] n pixel index - * \return value of the n-th 2-byte pixel - */ -l_int32 -l_getDataTwoBytes(const void *line, - l_int32 n) -{ -#ifdef L_BIG_ENDIAN - return *((const l_uint16 *)line + n); -#else /* L_LITTLE_ENDIAN */ - return *(l_uint16 *)((l_uintptr_t)((const l_uint16 *)line + n) ^ 2); -#endif /* L_BIG_ENDIAN */ -} - - -/*! - * \brief l_setDataTwoBytes() - * - * \param[in] line ptr to beginning of data line - * \param[in] n pixel index - * \param[in] val val to be inserted: 0 - 0xffff - * \return void - */ -void -l_setDataTwoBytes(void *line, - l_int32 n, - l_int32 val) -{ -#ifdef L_BIG_ENDIAN - *((l_uint16 *)line + n) = val; -#else /* L_LITTLE_ENDIAN */ - *(l_uint16 *)((l_uintptr_t)((l_uint16 *)line + n) ^ 2) = val; -#endif /* L_BIG_ENDIAN */ -} - - -/*! - * \brief l_getDataFourBytes() - * - * \param[in] line ptr to beginning of data line - * \param[in] n pixel index - * \return value of the n-th 4-byte pixel - */ -l_int32 -l_getDataFourBytes(const void *line, - l_int32 n) -{ - return *((const l_uint32 *)line + n); -} - - -/*! - * \brief l_setDataFourBytes() - * - * \param[in] line ptr to beginning of data line - * \param[in] n pixel index - * \param[in] val val to be inserted: 0 - 0xffffffff - * \return void - */ -void -l_setDataFourBytes(void *line, - l_int32 n, - l_int32 val) -{ - *((l_uint32 *)line + n) = val; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/arrayaccess.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/arrayaccess.h deleted file mode 100644 index 1a831bcc..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/arrayaccess.h +++ /dev/null @@ -1,270 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_ARRAY_ACCESS_H -#define LEPTONICA_ARRAY_ACCESS_H - -/*! - * \file arrayaccess.h - * - *
- * 1, 2, 4, 8, 16 and 32 bit data access within an array of 32-bit words - * - * This is used primarily to access 1, 2, 4, 8, 16 and 32 bit pixels - * in a line of image data, represented as an array of 32-bit words. - * - * pdata: pointer to first 32-bit word in the array - * n: index of the pixel in the array - * - * Function calls for these accessors are defined in arrayaccess.c. - * - * However, for efficiency we use the inline macros for all accesses. - * Even though the 2 and 4 bit set* accessors are more complicated, - * they are about 10% faster than the function calls. - * - * The 32 bit access is just a cast and ptr arithmetic. We include - * it so that the input ptr can be void*. - * - * At the end of this file is code for invoking the function calls - * instead of inlining. - * - * The macro SET_DATA_BIT_VAL(pdata, n, val) is a bit slower than - * if (val == 0) - * CLEAR_DATA_BIT(pdata, n); - * else - * SET_DATA_BIT(pdata, n); - * - * Some compilers complain when the SET macros are surrounded by - * parentheses, because parens require an evaluation and it is not - * defined for SET macros. If SET_DATA_QBIT were defined as a - * compound macro, in analogy to l_setDataQbit(), it requires - * surrounding braces: - * \code - * #define SET_DATA_QBIT(pdata, n, val) \ - * {l_uint32 *_TEMP_WORD_PTR_; \ - * _TEMP_WORD_PTR_ = (l_uint32 *)(pdata) + ((n) >> 3); \ - * *_TEMP_WORD_PTR_ &= ~(0xf0000000 >> (4 * ((n) & 7))); \ - * *_TEMP_WORD_PTR_ |= (((val) & 15) << (28 - 4 * ((n) & 7)));} - * \endcode - * but if used in an if/else - * \code - * if (x) - * SET_DATA_QBIT(...); - * else - * ... - * \endcode - * the compiler sees - * \code - * if (x) - * {......}; - * else - * ... - * \endcode - * The semicolon comes after the brace and will not compile. - * This can be fixed in the call by either omitting the semicolon - * or requiring another set of braces around SET_DATA_QBIT(), but - * both these options break compatibility with current code, and - * require special attention by anyone using the macros. - * - * There are (at least) two ways to fix this in the macro definitions, - * suggested by Dave Bryan. - * (1) Surround the braces in the macro above with - * do {....} while(0) - * Then the semicolon just terminates the expression. - * (2) Reduce the blocks to a single expression; e.g, - * *((l_uint32 *)(pdata) + ((n) >> 3)) = \ - * *((l_uint32 *)(pdata) + ((n) >> 3)) \ - * & ~(0xf0000000 >> (4 * ((n) & 7))) \ - * | (((val) & 15) << (28 - 4 * ((n) & 7))) - * This appears to cause redundant computation, but the compiler - * should evaluate the common subexpression only once. - * All these methods have the same performance, giving about 300M - * SET_DATA_QBIT operations per second on a fast 64 bit system. - * Using the function calls instead of the macros results in about 250M - * SET_DATA_QBIT operations per second, a performance hit of nearly 20%. - *- */ - -#define USE_INLINE_ACCESSORS 1 - -#if USE_INLINE_ACCESSORS - - /*=============================================================*/ - /* Faster: use in line accessors */ - /*=============================================================*/ - - /*--------------------------------------------------* - * 1 bit access * - *--------------------------------------------------*/ -/*! 1 bit access - get */ -#define GET_DATA_BIT(pdata, n) \ - ((*((const l_uint32 *)(pdata) + ((n) >> 5)) >> (31 - ((n) & 31))) & 1) - -/*! 1 bit access - set */ -#define SET_DATA_BIT(pdata, n) \ - *((l_uint32 *)(pdata) + ((n) >> 5)) |= (0x80000000 >> ((n) & 31)) - -/*! 1 bit access - clear */ -#define CLEAR_DATA_BIT(pdata, n) \ - *((l_uint32 *)(pdata) + ((n) >> 5)) &= ~(0x80000000 >> ((n) & 31)) - -/*! 1 bit access - set value (0 or 1) */ -#define SET_DATA_BIT_VAL(pdata, n, val) \ - *((l_uint32 *)(pdata) + ((n) >> 5)) = \ - ((*((l_uint32 *)(pdata) + ((n) >> 5)) \ - & (~(0x80000000 >> ((n) & 31)))) \ - | ((l_uint32)(val) << (31 - ((n) & 31)))) - - /*--------------------------------------------------* - * 2 bit access * - *--------------------------------------------------*/ -/*! 2 bit access - get */ -#define GET_DATA_DIBIT(pdata, n) \ - ((*((const l_uint32 *)(pdata) + ((n) >> 4)) >> (2 * (15 - ((n) & 15)))) & 3) - -/*! 2 bit access - set value (0 ... 3) */ -#define SET_DATA_DIBIT(pdata, n, val) \ - *((l_uint32 *)(pdata) + ((n) >> 4)) = \ - ((*((l_uint32 *)(pdata) + ((n) >> 4)) \ - & (~(0xc0000000 >> (2 * ((n) & 15))))) \ - | ((l_uint32)((val) & 3) << (30 - 2 * ((n) & 15)))) - -/*! 2 bit access - clear */ -#define CLEAR_DATA_DIBIT(pdata, n) \ - *((l_uint32 *)(pdata) + ((n) >> 4)) &= ~(0xc0000000 >> (2 * ((n) & 15))) - - - /*--------------------------------------------------* - * 4 bit access * - *--------------------------------------------------*/ -/*! 4 bit access - get */ -#define GET_DATA_QBIT(pdata, n) \ - ((*((const l_uint32 *)(pdata) + ((n) >> 3)) >> (4 * (7 - ((n) & 7)))) & 0xf) - -/*! 4 bit access - set value (0 ... 15) */ -#define SET_DATA_QBIT(pdata, n, val) \ - *((l_uint32 *)(pdata) + ((n) >> 3)) = \ - ((*((l_uint32 *)(pdata) + ((n) >> 3)) \ - & (~(0xf0000000 >> (4 * ((n) & 7))))) \ - | ((l_uint32)((val) & 15) << (28 - 4 * ((n) & 7)))) - -/*! 4 bit access - clear */ -#define CLEAR_DATA_QBIT(pdata, n) \ - *((l_uint32 *)(pdata) + ((n) >> 3)) &= ~(0xf0000000 >> (4 * ((n) & 7))) - - - /*--------------------------------------------------* - * 8 bit access * - *--------------------------------------------------*/ -#ifdef L_BIG_ENDIAN -/*! 8 bit access - get */ -#define GET_DATA_BYTE(pdata, n) \ - (*((const l_uint8 *)(pdata) + (n))) -#else /* L_LITTLE_ENDIAN */ -/*! 8 bit access - get */ -#define GET_DATA_BYTE(pdata, n) \ - (*(l_uint8 *)((l_uintptr_t)((const l_uint8 *)(pdata) + (n)) ^ 3)) -#endif /* L_BIG_ENDIAN */ - -#ifdef L_BIG_ENDIAN -/*! 8 bit access - set value (0 ... 255) */ -#define SET_DATA_BYTE(pdata, n, val) \ - *((l_uint8 *)(pdata) + (n)) = (val) -#else /* L_LITTLE_ENDIAN */ -/*! 8 bit access - set value (0 ... 255) */ -#define SET_DATA_BYTE(pdata, n, val) \ - *(l_uint8 *)((l_uintptr_t)((l_uint8 *)(pdata) + (n)) ^ 3) = (val) -#endif /* L_BIG_ENDIAN */ - - - /*--------------------------------------------------* - * 16 bit access * - *--------------------------------------------------*/ -#ifdef L_BIG_ENDIAN -/*! 16 bit access - get */ -#define GET_DATA_TWO_BYTES(pdata, n) \ - (*((const l_uint16 *)(pdata) + (n))) -#else /* L_LITTLE_ENDIAN */ -/*! 16 bit access - get */ -#define GET_DATA_TWO_BYTES(pdata, n) \ - (*(l_uint16 *)((l_uintptr_t)((const l_uint16 *)(pdata) + (n)) ^ 2)) -#endif /* L_BIG_ENDIAN */ - -#ifdef L_BIG_ENDIAN -/*! 16 bit access - set value (0 ... 65535) */ -#define SET_DATA_TWO_BYTES(pdata, n, val) \ - *((l_uint16 *)(pdata) + (n)) = (val) -#else /* L_LITTLE_ENDIAN */ -/*! 16 bit access - set value (0 ... 65535) */ -#define SET_DATA_TWO_BYTES(pdata, n, val) \ - *(l_uint16 *)((l_uintptr_t)((l_uint16 *)(pdata) + (n)) ^ 2) = (val) -#endif /* L_BIG_ENDIAN */ - - - /*--------------------------------------------------* - * 32 bit access * - *--------------------------------------------------*/ -/*! 32 bit access - get */ -#define GET_DATA_FOUR_BYTES(pdata, n) \ - (*((const l_uint32 *)(pdata) + (n))) - -/*! 32 bit access - set (0 ... 4294967295) */ -#define SET_DATA_FOUR_BYTES(pdata, n, val) \ - *((l_uint32 *)(pdata) + (n)) = (val) - - -#else - - /*=============================================================*/ - /* Slower: use function calls for all accessors */ - /*=============================================================*/ - -#define GET_DATA_BIT(pdata, n) l_getDataBit(pdata, n) -#define SET_DATA_BIT(pdata, n) l_setDataBit(pdata, n) -#define CLEAR_DATA_BIT(pdata, n) l_clearDataBit(pdata, n) -#define SET_DATA_BIT_VAL(pdata, n, val) l_setDataBitVal(pdata, n, val) - -#define GET_DATA_DIBIT(pdata, n) l_getDataDibit(pdata, n) -#define SET_DATA_DIBIT(pdata, n, val) l_setDataDibit(pdata, n, val) -#define CLEAR_DATA_DIBIT(pdata, n) l_clearDataDibit(pdata, n) - -#define GET_DATA_QBIT(pdata, n) l_getDataQbit(pdata, n) -#define SET_DATA_QBIT(pdata, n, val) l_setDataQbit(pdata, n, val) -#define CLEAR_DATA_QBIT(pdata, n) l_clearDataQbit(pdata, n) - -#define GET_DATA_BYTE(pdata, n) l_getDataByte(pdata, n) -#define SET_DATA_BYTE(pdata, n, val) l_setDataByte(pdata, n, val) - -#define GET_DATA_TWO_BYTES(pdata, n) l_getDataTwoBytes(pdata, n) -#define SET_DATA_TWO_BYTES(pdata, n, val) l_setDataTwoBytes(pdata, n, val) - -#define GET_DATA_FOUR_BYTES(pdata, n) l_getDataFourBytes(pdata, n) -#define SET_DATA_FOUR_BYTES(pdata, n, val) l_setDataFourBytes(pdata, n, val) - -#endif /* USE_INLINE_ACCESSORS */ - - -#endif /* LEPTONICA_ARRAY_ACCESS_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bardecode.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bardecode.c deleted file mode 100644 index 87956da0..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bardecode.c +++ /dev/null @@ -1,1034 +0,0 @@ -/*====================================================================* - - 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 bardecode.c - *
- * - * Dispatcher - * char *barcodeDispatchDecoder() - * - * Format Determination - * static l_int32 barcodeFindFormat() - * l_int32 barcodeFormatIsSupported() - * static l_int32 barcodeVerifyFormat() - * - * Decode 2 of 5 - * static char *barcodeDecode2of5() - * - * Decode Interleaved 2 of 5 - * static char *barcodeDecodeI2of5() - * - * Decode Code 93 - * static char *barcodeDecode93() - * - * Decode Code 39 - * static char *barcodeDecode39() - * - * Decode Codabar - * static char *barcodeDecodeCodabar() - * - * Decode UPC-A - * static char *barcodeDecodeUpca() - * - * Decode EAN 13 - * static char *barcodeDecodeEan13() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) If valid == 1, the barcode is of the given format in the - * forward order; if valid == 2, it is backwards. - * (2) If the barcode needs to be reversed to read it, and &reverse - * is provided, a 1 is put into %reverse. - * (3) Add to this as more formats are supported. - *- */ -static l_int32 -barcodeVerifyFormat(char *barstr, - l_int32 format, - l_int32 *pvalid, - l_int32 *preverse) -{ -char *revbarstr; -l_int32 i, start, len, stop, mid; - - PROCNAME("barcodeVerifyFormat"); - - if (!pvalid) - return ERROR_INT("barstr not defined", procName, 1); - *pvalid = 0; - if (preverse) *preverse = 0; - if (!barstr) - return ERROR_INT("barstr not defined", procName, 1); - - switch (format) - { - case L_BF_CODE2OF5: - start = !strncmp(barstr, Code2of5[C25_START], 3); - len = strlen(barstr); - stop = !strncmp(&barstr[len - 5], Code2of5[C25_STOP], 5); - if (start && stop) { - *pvalid = 1; - } else { - revbarstr = stringReverse(barstr); - start = !strncmp(revbarstr, Code2of5[C25_START], 3); - stop = !strncmp(&revbarstr[len - 5], Code2of5[C25_STOP], 5); - LEPT_FREE(revbarstr); - if (start && stop) { - *pvalid = 1; - if (preverse) *preverse = 1; - } - } - break; - case L_BF_CODEI2OF5: - start = !strncmp(barstr, CodeI2of5[CI25_START], 4); - len = strlen(barstr); - stop = !strncmp(&barstr[len - 3], CodeI2of5[CI25_STOP], 3); - if (start && stop) { - *pvalid = 1; - } else { - revbarstr = stringReverse(barstr); - start = !strncmp(revbarstr, CodeI2of5[CI25_START], 4); - stop = !strncmp(&revbarstr[len - 3], CodeI2of5[CI25_STOP], 3); - LEPT_FREE(revbarstr); - if (start && stop) { - *pvalid = 1; - if (preverse) *preverse = 1; - } - } - break; - case L_BF_CODE93: - start = !strncmp(barstr, Code93[C93_START], 6); - len = strlen(barstr); - stop = !strncmp(&barstr[len - 7], Code93[C93_STOP], 6); - if (start && stop) { - *pvalid = 1; - } else { - revbarstr = stringReverse(barstr); - start = !strncmp(revbarstr, Code93[C93_START], 6); - stop = !strncmp(&revbarstr[len - 7], Code93[C93_STOP], 6); - LEPT_FREE(revbarstr); - if (start && stop) { - *pvalid = 1; - if (preverse) *preverse = 1; - } - } - break; - case L_BF_CODE39: - start = !strncmp(barstr, Code39[C39_START], 9); - len = strlen(barstr); - stop = !strncmp(&barstr[len - 9], Code39[C39_STOP], 9); - if (start && stop) { - *pvalid = 1; - } else { - revbarstr = stringReverse(barstr); - start = !strncmp(revbarstr, Code39[C39_START], 9); - stop = !strncmp(&revbarstr[len - 9], Code39[C39_STOP], 9); - LEPT_FREE(revbarstr); - if (start && stop) { - *pvalid = 1; - if (preverse) *preverse = 1; - } - } - break; - case L_BF_CODABAR: - start = stop = 0; - len = strlen(barstr); - for (i = 16; i <= 19; i++) /* any of these will do */ - start += !strncmp(barstr, Codabar[i], 7); - for (i = 16; i <= 19; i++) /* ditto */ - stop += !strncmp(&barstr[len - 7], Codabar[i], 7); - if (start && stop) { - *pvalid = 1; - } else { - start = stop = 0; - revbarstr = stringReverse(barstr); - for (i = 16; i <= 19; i++) - start += !strncmp(revbarstr, Codabar[i], 7); - for (i = 16; i <= 19; i++) - stop += !strncmp(&revbarstr[len - 7], Codabar[i], 7); - LEPT_FREE(revbarstr); - if (start && stop) { - *pvalid = 1; - if (preverse) *preverse = 1; - } - } - break; - case L_BF_UPCA: - case L_BF_EAN13: - len = strlen(barstr); - if (len == 59) { - start = !strncmp(barstr, Upca[UPCA_START], 3); - mid = !strncmp(&barstr[27], Upca[UPCA_MID], 5); - stop = !strncmp(&barstr[len - 3], Upca[UPCA_STOP], 3); - if (start && mid && stop) - *pvalid = 1; - } - break; - default: - return ERROR_INT("format not supported", procName, 1); - } - - return 0; -} - - -/*------------------------------------------------------------------------* - * Code 2 of 5 * - *------------------------------------------------------------------------*/ -/*! - * \brief barcodeDecode2of5() - * - * \param[in] barstr of widths, in set {1, 2} - * \param[in] debugflag - * \return data string of digits, or NULL if none found or on error - * - *
- * Notes: - * (1) Ref: http://en.wikipedia.org/wiki/Two-out-of-five_code (Note: - * the codes given here are wrong!) - * http://morovia.com/education/symbology/code25.asp - * (2) This is a very low density encoding for the 10 digits. - * Each digit is encoded with 5 black bars, of which 2 are wide - * and 3 are narrow. No information is carried in the spaces - * between the bars, which are all equal in width, represented by - * a "1" in our encoding. - * (3) The mapping from the sequence of five bar widths to the - * digit is identical to the mapping used by the interleaved - * 2 of 5 code. The start code is 21211, representing two - * wide bars and a narrow bar, and the interleaved "1" spaces - * are explicit. The stop code is 21112. For all codes - * (including start and stop), the trailing space "1" is - * implicit -- there is no reason to represent it in the - * Code2of5[] array. - *- */ -static char * -barcodeDecode2of5(char *barstr, - l_int32 debugflag) -{ -char *data, *vbarstr; -char code[10]; -l_int32 valid, reverse, i, j, len, error, ndigits, start, found; - - PROCNAME("barcodeDecodeI2of5"); - - if (!barstr) - return (char *)ERROR_PTR("barstr not defined", procName, NULL); - - /* Verify format; reverse if necessary */ - barcodeVerifyFormat(barstr, L_BF_CODE2OF5, &valid, &reverse); - if (!valid) - return (char *)ERROR_PTR("barstr not in 2of5 format", procName, NULL); - if (reverse) - vbarstr = stringReverse(barstr); - else - vbarstr = stringNew(barstr); - - /* Verify size */ - len = strlen(vbarstr); - if ((len - 11) % 10 != 0) { - LEPT_FREE(vbarstr); - return (char *)ERROR_PTR("size not divisible by 10: invalid 2of5 code", - procName, NULL); - } - - error = FALSE; - ndigits = (len - 11) / 10; - data = (char *)LEPT_CALLOC(ndigits + 1, sizeof(char)); - memset(code, 0, 10); - for (i = 0; i < ndigits; i++) { - start = 6 + 10 * i; - for (j = 0; j < 9; j++) - code[j] = vbarstr[start + j]; - - if (debugflag) - lept_stderr("code: %s\n", code); - - found = FALSE; - for (j = 0; j < 10; j++) { - if (!strcmp(code, Code2of5[j])) { - data[i] = 0x30 + j; - found = TRUE; - break; - } - } - if (!found) error = TRUE; - } - LEPT_FREE(vbarstr); - - if (error) { - LEPT_FREE(data); - return (char *)ERROR_PTR("error in decoding", procName, NULL); - } - - return data; -} - - -/*------------------------------------------------------------------------* - * Interleaved Code 2 of 5 * - *------------------------------------------------------------------------*/ -/*! - * \brief barcodeDecodeI2of5() - * - * \param[in] barstr of widths, in set {1, 2} - * \param[in] debugflag - * \return data string of digits, or NULL if none found or on error - * - *
- * Notes: - * (1) Ref: http://en.wikipedia.org/wiki/Interleaved_2_of_5 - * (2) This always encodes an even number of digits. - * The start code is 1111; the stop code is 211. - *- */ -static char * -barcodeDecodeI2of5(char *barstr, - l_int32 debugflag) -{ -char *data, *vbarstr; -char code1[6], code2[6]; -l_int32 valid, reverse, i, j, len, error, npairs, start, found; - - PROCNAME("barcodeDecodeI2of5"); - - if (!barstr) - return (char *)ERROR_PTR("barstr not defined", procName, NULL); - - /* Verify format; reverse if necessary */ - barcodeVerifyFormat(barstr, L_BF_CODEI2OF5, &valid, &reverse); - if (!valid) - return (char *)ERROR_PTR("barstr not in i2of5 format", procName, NULL); - if (reverse) - vbarstr = stringReverse(barstr); - else - vbarstr = stringNew(barstr); - - /* Verify size */ - len = strlen(vbarstr); - if ((len - 7) % 10 != 0) { - LEPT_FREE(vbarstr); - return (char *)ERROR_PTR("size not divisible by 10: invalid I2of5 code", - procName, NULL); - } - - error = FALSE; - npairs = (len - 7) / 10; - data = (char *)LEPT_CALLOC(2 * npairs + 1, sizeof(char)); - memset(code1, 0, 6); - memset(code2, 0, 6); - for (i = 0; i < npairs; i++) { - start = 4 + 10 * i; - for (j = 0; j < 5; j++) { - code1[j] = vbarstr[start + 2 * j]; - code2[j] = vbarstr[start + 2 * j + 1]; - } - - if (debugflag) - lept_stderr("code1: %s, code2: %s\n", code1, code2); - - found = FALSE; - for (j = 0; j < 10; j++) { - if (!strcmp(code1, CodeI2of5[j])) { - data[2 * i] = 0x30 + j; - found = TRUE; - break; - } - } - if (!found) error = TRUE; - found = FALSE; - for (j = 0; j < 10; j++) { - if (!strcmp(code2, CodeI2of5[j])) { - data[2 * i + 1] = 0x30 + j; - found = TRUE; - break; - } - } - if (!found) error = TRUE; - } - LEPT_FREE(vbarstr); - - if (error) { - LEPT_FREE(data); - return (char *)ERROR_PTR("error in decoding", procName, NULL); - } - - return data; -} - - -/*------------------------------------------------------------------------* - * Code 93 * - *------------------------------------------------------------------------*/ -/*! - * \brief barcodeDecode93() - * - * \param[in] barstr of widths, in set {1, 2, 3, 4} - * \param[in] debugflag - * \return data string of digits, or NULL if none found or on error - * - *
- * Notes: - * (1) Ref: http://en.wikipedia.org/wiki/Code93 - * http://morovia.com/education/symbology/code93.asp - * (2) Each symbol has 3 black and 3 white bars. - * The start and stop codes are 111141; the stop code then is - * terminated with a final (1) bar. - * (3) The last two codes are check codes. We are checking them - * for correctness, and issuing a warning on failure. Should - * probably not return any data on failure. - *- */ -static char * -barcodeDecode93(char *barstr, - l_int32 debugflag) -{ -const char *checkc, *checkk; -char *data, *vbarstr; -char code[7]; -l_int32 valid, reverse, i, j, len, error, nsymb, start, found, sum; -l_int32 *index; - - PROCNAME("barcodeDecode93"); - - if (!barstr) - return (char *)ERROR_PTR("barstr not defined", procName, NULL); - - /* Verify format; reverse if necessary */ - barcodeVerifyFormat(barstr, L_BF_CODE93, &valid, &reverse); - if (!valid) - return (char *)ERROR_PTR("barstr not in code93 format", procName, NULL); - if (reverse) - vbarstr = stringReverse(barstr); - else - vbarstr = stringNew(barstr); - - /* Verify size; skip the first 6 and last 7 bars. */ - len = strlen(vbarstr); - if ((len - 13) % 6 != 0) { - LEPT_FREE(vbarstr); - return (char *)ERROR_PTR("size not divisible by 6: invalid code 93", - procName, NULL); - } - - /* Decode the symbols */ - nsymb = (len - 13) / 6; - data = (char *)LEPT_CALLOC(nsymb + 1, sizeof(char)); - index = (l_int32 *)LEPT_CALLOC(nsymb, sizeof(l_int32)); - memset(code, 0, 7); - error = FALSE; - for (i = 0; i < nsymb; i++) { - start = 6 + 6 * i; - for (j = 0; j < 6; j++) - code[j] = vbarstr[start + j]; - - if (debugflag) - lept_stderr("code: %s\n", code); - - found = FALSE; - for (j = 0; j < C93_START; j++) { - if (!strcmp(code, Code93[j])) { - data[i] = Code93Val[j]; - index[i] = j; - found = TRUE; - break; - } - } - if (!found) error = TRUE; - } - LEPT_FREE(vbarstr); - - if (error) { - LEPT_FREE(index); - LEPT_FREE(data); - return (char *)ERROR_PTR("error in decoding", procName, NULL); - } - - /* Do check sums. For character "C", use only the - * actual data in computing the sum. For character "K", - * use the actual data plus the check character "C". */ - sum = 0; - for (i = 0; i < nsymb - 2; i++) /* skip the "C" and "K" */ - sum += ((i % 20) + 1) * index[nsymb - 3 - i]; - if (data[nsymb - 2] != Code93Val[sum % 47]) - L_WARNING("Error for check C\n", procName); - - if (debugflag) { - checkc = Code93[sum % 47]; - lept_stderr("checkc = %s\n", checkc); - } - - sum = 0; - for (i = 0; i < nsymb - 1; i++) /* skip the "K" */ - sum += ((i % 15) + 1) * index[nsymb - 2 - i]; - if (data[nsymb - 1] != Code93Val[sum % 47]) - L_WARNING("Error for check K\n", procName); - - if (debugflag) { - checkk = Code93[sum % 47]; - lept_stderr("checkk = %s\n", checkk); - } - - /* Remove the two check codes from the output */ - data[nsymb - 2] = '\0'; - - LEPT_FREE(index); - return data; -} - - -/*------------------------------------------------------------------------* - * Code 39 * - *------------------------------------------------------------------------*/ -/*! - * \brief barcodeDecode39() - * - * \param[in] barstr of widths, in set {1, 2} - * \param[in] debugflag - * \return data string of digits, or NULL if none found or on error - * - *
- * Notes: - * (1) Ref: http://en.wikipedia.org/wiki/Code39 - * http://morovia.com/education/symbology/code39.asp - * (2) Each symbol has 5 black and 4 white bars. - * The start and stop codes are 121121211 (the asterisk) - * (3) This decoder was contributed by Roger Hyde. - *- */ -static char * -barcodeDecode39(char *barstr, - l_int32 debugflag) -{ -char *data, *vbarstr; -char code[10]; -l_int32 valid, reverse, i, j, len, error, nsymb, start, found; - - PROCNAME("barcodeDecode39"); - - if (!barstr) - return (char *)ERROR_PTR("barstr not defined", procName, NULL); - - /* Verify format; reverse if necessary */ - barcodeVerifyFormat(barstr, L_BF_CODE39, &valid, &reverse); - if (!valid) - return (char *)ERROR_PTR("barstr not in code39 format", procName, NULL); - if (reverse) - vbarstr = stringReverse(barstr); - else - vbarstr = stringNew(barstr); - - /* Verify size */ - len = strlen(vbarstr); - if ((len + 1) % 10 != 0) { - LEPT_FREE(vbarstr); - return (char *)ERROR_PTR("size+1 not divisible by 10: invalid code 39", - procName, NULL); - } - - /* Decode the symbols */ - nsymb = (len - 19) / 10; - data = (char *)LEPT_CALLOC(nsymb + 1, sizeof(char)); - memset(code, 0, 10); - error = FALSE; - for (i = 0; i < nsymb; i++) { - start = 10 + 10 * i; - for (j = 0; j < 9; j++) - code[j] = vbarstr[start + j]; - - if (debugflag) - lept_stderr("code: %s\n", code); - - found = FALSE; - for (j = 0; j < C39_START; j++) { - if (!strcmp(code, Code39[j])) { - data[i] = Code39Val[j]; - found = TRUE; - break; - } - } - if (!found) error = TRUE; - } - LEPT_FREE(vbarstr); - - if (error) { - LEPT_FREE(data); - return (char *)ERROR_PTR("error in decoding", procName, NULL); - } - - return data; -} - - -/*------------------------------------------------------------------------* - * Codabar * - *------------------------------------------------------------------------*/ -/*! - * \brief barcodeDecodeCodabar() - * - * \param[in] barstr of widths, in set {1, 2} - * \param[in] debugflag - * \return data string of digits, or NULL if none found or on error - * - *
- * Notes: - * (1) Ref: http://en.wikipedia.org/wiki/Codabar - * http://morovia.com/education/symbology/codabar.asp - * (2) Each symbol has 4 black and 3 white bars. They represent the - * 10 digits, and optionally 6 other characters. The start and - * stop codes can be any of four (typically denoted A,B,C,D). - *- */ -static char * -barcodeDecodeCodabar(char *barstr, - l_int32 debugflag) -{ -char *data, *vbarstr; -char code[8]; -l_int32 valid, reverse, i, j, len, error, nsymb, start, found; - - PROCNAME("barcodeDecodeCodabar"); - - if (!barstr) - return (char *)ERROR_PTR("barstr not defined", procName, NULL); - - /* Verify format; reverse if necessary */ - barcodeVerifyFormat(barstr, L_BF_CODABAR, &valid, &reverse); - if (!valid) - return (char *)ERROR_PTR("barstr not in codabar format", - procName, NULL); - if (reverse) - vbarstr = stringReverse(barstr); - else - vbarstr = stringNew(barstr); - - /* Verify size */ - len = strlen(vbarstr); - if ((len + 1) % 8 != 0) { - LEPT_FREE(vbarstr); - return (char *)ERROR_PTR("size+1 not divisible by 8: invalid codabar", - procName, NULL); - } - - /* Decode the symbols */ - nsymb = (len - 15) / 8; - data = (char *)LEPT_CALLOC(nsymb + 1, sizeof(char)); - memset(code, 0, 8); - error = FALSE; - for (i = 0; i < nsymb; i++) { - start = 8 + 8 * i; - for (j = 0; j < 7; j++) - code[j] = vbarstr[start + j]; - - if (debugflag) - lept_stderr("code: %s\n", code); - - found = FALSE; - for (j = 0; j < 16; j++) { - if (!strcmp(code, Codabar[j])) { - data[i] = CodabarVal[j]; - found = TRUE; - break; - } - } - if (!found) error = TRUE; - } - LEPT_FREE(vbarstr); - - if (error) { - LEPT_FREE(data); - return (char *)ERROR_PTR("error in decoding", procName, NULL); - } - - return data; -} - - -/*------------------------------------------------------------------------* - * Code UPC-A * - *------------------------------------------------------------------------*/ -/*! - * \brief barcodeDecodeUpca() - * - * \param[in] barstr of widths, in set {1, 2, 3, 4} - * \param[in] debugflag - * \return data string of digits, or NULL if none found or on error - * - *
- * Notes: - * (1) Ref: http://en.wikipedia.org/wiki/UniversalProductCode - * http://morovia.com/education/symbology/upc-a.asp - * (2) Each symbol has 2 black and 2 white bars, and encodes a digit. - * The start and stop codes are 111 and 111. There are a total of - * 30 black bars, encoding 12 digits in two sets of 6, with - * 2 black bars separating the sets. - * (3) The last digit is a check digit. We check for correctness, and - * issue a warning on failure. Should probably not return any - * data on failure. - *- */ -static char * -barcodeDecodeUpca(char *barstr, - l_int32 debugflag) -{ -char *data, *vbarstr; -char code[5]; -l_int32 valid, i, j, len, error, start, found, sum, checkdigit; - - PROCNAME("barcodeDecodeUpca"); - - if (!barstr) - return (char *)ERROR_PTR("barstr not defined", procName, NULL); - - /* Verify format; reverse has no meaning here -- we must test both */ - barcodeVerifyFormat(barstr, L_BF_UPCA, &valid, NULL); - if (!valid) - return (char *)ERROR_PTR("barstr not in UPC-A format", procName, NULL); - - /* Verify size */ - len = strlen(barstr); - if (len != 59) - return (char *)ERROR_PTR("size not 59; invalid UPC-A barcode", - procName, NULL); - - /* Check the first digit. If invalid, reverse the string. */ - memset(code, 0, 5); - for (i = 0; i < 4; i++) - code[i] = barstr[i + 3]; - found = FALSE; - for (i = 0; i < 10; i++) { - if (!strcmp(code, Upca[i])) { - found = TRUE; - break; - } - } - if (found == FALSE) - vbarstr = stringReverse(barstr); - else - vbarstr = stringNew(barstr); - - /* Decode the 12 symbols */ - data = (char *)LEPT_CALLOC(13, sizeof(char)); - memset(code, 0, 5); - error = FALSE; - for (i = 0; i < 12; i++) { - if (i < 6) - start = 3 + 4 * i; - else - start = 32 + 4 * (i - 6); - for (j = 0; j < 4; j++) - code[j] = vbarstr[start + j]; - - if (debugflag) - lept_stderr("code: %s\n", code); - - found = FALSE; - for (j = 0; j < 10; j++) { - if (!strcmp(code, Upca[j])) { - data[i] = 0x30 + j; - found = TRUE; - break; - } - } - if (!found) error = TRUE; - } - LEPT_FREE(vbarstr); - - if (error) { - LEPT_FREE(data); - return (char *)ERROR_PTR("error in decoding", procName, NULL); - } - - /* Calculate the check digit (data[11]). */ - sum = 0; - for (i = 0; i < 12; i += 2) /* "even" digits */ - sum += 3 * (data[i] - 0x30); - for (i = 1; i < 11; i += 2) /* "odd" digits */ - sum += (data[i] - 0x30); - checkdigit = sum % 10; - if (checkdigit) /* not 0 */ - checkdigit = 10 - checkdigit; - if (checkdigit + 0x30 != data[11]) - L_WARNING("Error for UPC-A check character\n", procName); - - return data; -} - - -/*------------------------------------------------------------------------* - * Code EAN-13 * - *------------------------------------------------------------------------*/ -/*! - * \brief barcodeDecodeEan13() - * - * \param[in] barstr of widths, in set {1, 2, 3, 4} - * \param[in] first first digit: 0 - 9 - * \param[in] debugflag - * \return data string of digits, or NULL if none found or on error - * - *
- * Notes: - * (1) Ref: http://en.wikipedia.org/wiki/UniversalProductCode - * http://morovia.com/education/symbology/ean-13.asp - * (2) The encoding is essentially the same as UPC-A, except - * there are 13 digits in total, of which 12 are encoded - * by bars (as with UPC-A) and the 13th is a leading digit - * that determines the encoding of the next 6 digits, - * selecting each digit from one of two tables. - * encoded in the bars (as with UPC-A). If the first digit - * is 0, the encoding is identical to UPC-A. - * (3) As with UPC-A, the last digit is a check digit. - * (4) For now, we assume the first digit is input to this function. - * Eventually, we will read it by pattern matching. - * - * TODO: fix this for multiple tables, depending on the value of %first - *- */ -static char * -barcodeDecodeEan13(char *barstr, - l_int32 first, - l_int32 debugflag) -{ -char *data, *vbarstr; -char code[5]; -l_int32 valid, i, j, len, error, start, found, sum, checkdigit; - - PROCNAME("barcodeDecodeEan13"); - - if (!barstr) - return (char *)ERROR_PTR("barstr not defined", procName, NULL); - - /* Verify format. You can't tell the orientation by the start - * and stop codes, but you can by the location of the digits. - * Use the UPCA verifier for EAN 13 -- it is identical. */ - barcodeVerifyFormat(barstr, L_BF_UPCA, &valid, NULL); - if (!valid) - return (char *)ERROR_PTR("barstr not in EAN 13 format", procName, NULL); - - /* Verify size */ - len = strlen(barstr); - if (len != 59) - return (char *)ERROR_PTR("size not 59; invalid EAN 13 barcode", - procName, NULL); - - /* Check the first digit. If invalid, reverse the string. */ - memset(code, 0, 5); - for (i = 0; i < 4; i++) - code[i] = barstr[i + 3]; - found = FALSE; - for (i = 0; i < 10; i++) { - if (!strcmp(code, Upca[i])) { - found = TRUE; - break; - } - } - if (found == FALSE) - vbarstr = stringReverse(barstr); - else - vbarstr = stringNew(barstr); - - /* Decode the 12 symbols */ - data = (char *)LEPT_CALLOC(13, sizeof(char)); - memset(code, 0, 5); - error = FALSE; - for (i = 0; i < 12; i++) { - if (i < 6) - start = 3 + 4 * i; - else - start = 32 + 4 * (i - 6); - for (j = 0; j < 4; j++) - code[j] = vbarstr[start + j]; - - if (debugflag) - lept_stderr("code: %s\n", code); - - found = FALSE; - for (j = 0; j < 10; j++) { - if (!strcmp(code, Upca[j])) { - data[i] = 0x30 + j; - found = TRUE; - break; - } - } - if (!found) error = TRUE; - } - LEPT_FREE(vbarstr); - - if (error) { - LEPT_FREE(data); - return (char *)ERROR_PTR("error in decoding", procName, NULL); - } - - /* Calculate the check digit (data[11]). */ - sum = 0; - for (i = 0; i < 12; i += 2) /* "even" digits */ - sum += 3 * (data[i] - 0x30); - for (i = 1; i < 12; i += 2) /* "odd" digits */ - sum += (data[i] - 0x30); - checkdigit = sum % 10; - if (checkdigit) /* not 0 */ - checkdigit = 10 - checkdigit; - if (checkdigit + 0x30 != data[11]) - L_WARNING("Error for EAN-13 check character\n", procName); - - return data; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/baseline.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/baseline.c deleted file mode 100644 index 94607961..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/baseline.c +++ /dev/null @@ -1,600 +0,0 @@ -/*====================================================================* - - 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 baseline.c - *
- * - * Locate text baselines in an image - * NUMA *pixFindBaselines() - * - * Projective transform to remove local skew - * PIX *pixDeskewLocal() - * - * Determine local skew - * l_int32 pixGetLocalSkewTransform() - * NUMA *pixGetLocalSkewAngles() - * - * We have two apparently different functions here: - * ~ finding baselines - * ~ finding a projective transform to remove keystone warping - * The function pixGetLocalSkewAngles() returns an array of angles, - * one for each raster line, and the baselines of the text lines - * should intersect the left edge of the image with that angle. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) Input binary image must have text lines already aligned - * horizontally. This can be done by either rotating the - * image with pixDeskew(), or, if a projective transform - * is required, by doing pixDeskewLocal() first. - * (2) Input null for &pta if you don't want this returned. - * The pta will come in pairs of points (left and right end - * of each baseline). - * (3) Caution: this will not work properly on text with multiple - * columns, where the lines are not aligned between columns. - * If there are multiple columns, they should be extracted - * separately before finding the baselines. - * (4) This function constructs different types of output - * for baselines; namely, a set of raster line values and - * a set of end points of each baseline. - * (5) This function was designed to handle short and long text lines - * without using dangerous thresholds on the peak heights. It does - * this by combining the differential signal with a morphological - * analysis of the locations of the text lines. One can also - * combine this data to normalize the peak heights, by weighting - * the differential signal in the region of each baseline - * by the inverse of the width of the text line found there. - *- */ -NUMA * -pixFindBaselines(PIX *pixs, - PTA **ppta, - PIXA *pixadb) -{ -l_int32 h, i, j, nbox, val1, val2, ndiff, bx, by, bw, bh; -l_int32 imaxloc, peakthresh, zerothresh, inpeak; -l_int32 mintosearch, max, maxloc, nloc, locval; -l_int32 *array; -l_float32 maxval; -BOXA *boxa1, *boxa2, *boxa3; -GPLOT *gplot; -NUMA *nasum, *nadiff, *naloc, *naval; -PIX *pix1, *pix2; -PTA *pta; - - PROCNAME("pixFindBaselines"); - - if (ppta) *ppta = NULL; - if (!pixs || pixGetDepth(pixs) != 1) - return (NUMA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - - /* Close up the text characters, removing noise */ - pix1 = pixMorphSequence(pixs, "c25.1 + e15.1", 0); - - /* Estimate the resolution */ - if (pixadb) pixaAddPix(pixadb, pixScale(pix1, 0.25, 0.25), L_INSERT); - - /* Save the difference of adjacent row sums. - * The high positive-going peaks are the baselines */ - if ((nasum = pixCountPixelsByRow(pix1, NULL)) == NULL) { - pixDestroy(&pix1); - return (NUMA *)ERROR_PTR("nasum not made", procName, NULL); - } - h = pixGetHeight(pixs); - nadiff = numaCreate(h); - numaGetIValue(nasum, 0, &val2); - for (i = 0; i < h - 1; i++) { - val1 = val2; - numaGetIValue(nasum, i + 1, &val2); - numaAddNumber(nadiff, val1 - val2); - } - numaDestroy(&nasum); - - if (pixadb) { /* show the difference signal */ - lept_mkdir("lept/baseline"); - gplotSimple1(nadiff, GPLOT_PNG, "/tmp/lept/baseline/diff", "Diff Sig"); - pix2 = pixRead("/tmp/lept/baseline/diff.png"); - pixaAddPix(pixadb, pix2, L_INSERT); - } - - /* Use the zeroes of the profile to locate each baseline. */ - array = numaGetIArray(nadiff); - ndiff = numaGetCount(nadiff); - numaGetMax(nadiff, &maxval, &imaxloc); - numaDestroy(&nadiff); - - /* Use this to begin locating a new peak: */ - peakthresh = (l_int32)maxval / PeakThresholdRatio; - /* Use this to begin a region between peaks: */ - zerothresh = (l_int32)maxval / ZeroThresholdRatio; - - naloc = numaCreate(0); - naval = numaCreate(0); - inpeak = FALSE; - for (i = 0; i < ndiff; i++) { - if (inpeak == FALSE) { - if (array[i] > peakthresh) { /* transition to in-peak */ - inpeak = TRUE; - mintosearch = i + MinDistInPeak; /* accept no zeros - * between i and mintosearch */ - max = array[i]; - maxloc = i; - } - } else { /* inpeak == TRUE; look for max */ - if (array[i] > max) { - max = array[i]; - maxloc = i; - mintosearch = i + MinDistInPeak; - } else if (i > mintosearch && array[i] <= zerothresh) { /* leave */ - inpeak = FALSE; - numaAddNumber(naval, max); - numaAddNumber(naloc, maxloc); - } - } - } - LEPT_FREE(array); - - /* If array[ndiff-1] is max, eg. no descenders, baseline at bottom */ - if (inpeak) { - numaAddNumber(naval, max); - numaAddNumber(naloc, maxloc); - } - - if (pixadb) { /* show the raster locations for the peaks */ - gplot = gplotCreate("/tmp/lept/baseline/loc", GPLOT_PNG, "Peak locs", - "rasterline", "height"); - gplotAddPlot(gplot, naloc, naval, GPLOT_POINTS, "locs"); - gplotMakeOutput(gplot); - gplotDestroy(&gplot); - pix2 = pixRead("/tmp/lept/baseline/loc.png"); - pixaAddPix(pixadb, pix2, L_INSERT); - } - numaDestroy(&naval); - - /* Generate an approximate profile of text line width. - * First, filter the boxes of text, where there may be - * more than one box for a given textline. */ - pix2 = pixMorphSequence(pix1, "r11 + c20.1 + o30.1 +c1.3", 0); - if (pixadb) pixaAddPix(pixadb, pix2, L_COPY); - boxa1 = pixConnComp(pix2, NULL, 4); - pixDestroy(&pix1); - pixDestroy(&pix2); - if (boxaGetCount(boxa1) == 0) { - numaDestroy(&naloc); - boxaDestroy(&boxa1); - L_INFO("no compnents after filtering\n", procName); - return NULL; - } - boxa2 = boxaTransform(boxa1, 0, 0, 4., 4.); - boxa3 = boxaSort(boxa2, L_SORT_BY_Y, L_SORT_INCREASING, NULL); - boxaDestroy(&boxa1); - boxaDestroy(&boxa2); - - /* Optionally, find the baseline segments */ - pta = NULL; - if (ppta) { - pta = ptaCreate(0); - *ppta = pta; - } - if (pta) { - nloc = numaGetCount(naloc); - nbox = boxaGetCount(boxa3); - for (i = 0; i < nbox; i++) { - boxaGetBoxGeometry(boxa3, i, &bx, &by, &bw, &bh); - for (j = 0; j < nloc; j++) { - numaGetIValue(naloc, j, &locval); - if (L_ABS(locval - (by + bh)) > 25) - continue; - ptaAddPt(pta, bx, locval); - ptaAddPt(pta, bx + bw, locval); - break; - } - } - } - boxaDestroy(&boxa3); - - if (pixadb && pta) { /* display baselines */ - l_int32 npts, x1, y1, x2, y2; - pix1 = pixConvertTo32(pixs); - npts = ptaGetCount(pta); - for (i = 0; i < npts; i += 2) { - ptaGetIPt(pta, i, &x1, &y1); - ptaGetIPt(pta, i + 1, &x2, &y2); - pixRenderLineArb(pix1, x1, y1, x2, y2, 2, 255, 0, 0); - } - pixWriteDebug("/tmp/lept/baseline/baselines.png", pix1, IFF_PNG); - pixaAddPix(pixadb, pixScale(pix1, 0.25, 0.25), L_INSERT); - pixDestroy(&pix1); - } - - return naloc; -} - - -/*---------------------------------------------------------------------* - * Projective transform to remove local skew * - *---------------------------------------------------------------------*/ -/*! - * \brief pixDeskewLocal() - * - * \param[in] pixs 1 bpp - * \param[in] nslices the number of horizontal overlapping slices; - * must be larger than 1 and not exceed 20; - * use 0 for default - * \param[in] redsweep sweep reduction factor: 1, 2, 4 or 8; - * use 0 for default value - * \param[in] redsearch search reduction factor: 1, 2, 4 or 8, and - * not larger than redsweep; use 0 for default value - * \param[in] sweeprange half the full range, assumed about 0; in degrees; - * use 0.0 for default value - * \param[in] sweepdelta angle increment of sweep; in degrees; - * use 0.0 for default value - * \param[in] minbsdelta min binary search increment angle; in degrees; - * use 0.0 for default value - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) This function allows deskew of a page whose skew changes - * approximately linearly with vertical position. It uses - * a projective transform that in effect does a differential - * shear about the LHS of the page, and makes all text lines - * horizontal. - * (2) The origin of the keystoning can be either a cheap document - * feeder that rotates the page as it is passed through, or a - * camera image taken from either the left or right side - * of the vertical. - * (3) The image transformation is a projective warping, - * not a rotation. Apart from this function, the text lines - * must be properly aligned vertically with respect to each - * other. This can be done by pre-processing the page; e.g., - * by rotating or horizontally shearing it. - * Typically, this can be achieved by vertically aligning - * the page edge. - *- */ -PIX * -pixDeskewLocal(PIX *pixs, - l_int32 nslices, - l_int32 redsweep, - l_int32 redsearch, - l_float32 sweeprange, - l_float32 sweepdelta, - l_float32 minbsdelta) -{ -l_int32 ret; -PIX *pixd; -PTA *ptas, *ptad; - - PROCNAME("pixDeskewLocal"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - - /* Skew array gives skew angle (deg) as fctn of raster line - * where it intersects the LHS of the image */ - ret = pixGetLocalSkewTransform(pixs, nslices, redsweep, redsearch, - sweeprange, sweepdelta, minbsdelta, - &ptas, &ptad); - if (ret != 0) - return (PIX *)ERROR_PTR("transform pts not found", procName, NULL); - - /* Use a projective transform */ - pixd = pixProjectiveSampledPta(pixs, ptad, ptas, L_BRING_IN_WHITE); - - ptaDestroy(&ptas); - ptaDestroy(&ptad); - return pixd; -} - - -/*---------------------------------------------------------------------* - * Determine the local skew * - *---------------------------------------------------------------------*/ -/*! - * \brief pixGetLocalSkewTransform() - * - * \param[in] pixs - * \param[in] nslices the number of horizontal overlapping slices; - * must be larger than 1 and not exceed 20; - * use 0 for default - * \param[in] redsweep sweep reduction factor: 1, 2, 4 or 8; - * use 0 for default value - * \param[in] redsearch search reduction factor: 1, 2, 4 or 8, and not - * larger than redsweep; use 0 for default value - * \param[in] sweeprange half the full range, assumed about 0; - * in degrees; use 0.0 for default value - * \param[in] sweepdelta angle increment of sweep; in degrees; - * use 0.0 for default value - * \param[in] minbsdelta min binary search increment angle; in degrees; - * use 0.0 for default value - * \param[out] pptas 4 points in the source - * \param[out] pptad the corresponding 4 pts in the dest - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This generates two pairs of points in the src, each pair - * corresponding to a pair of points that would lie along - * the same raster line in a transformed (dewarped) image. - * (2) The sets of 4 src and 4 dest points returned by this function - * can then be used, in a projective or bilinear transform, - * to remove keystoning in the src. - *- */ -l_ok -pixGetLocalSkewTransform(PIX *pixs, - l_int32 nslices, - l_int32 redsweep, - l_int32 redsearch, - l_float32 sweeprange, - l_float32 sweepdelta, - l_float32 minbsdelta, - PTA **pptas, - PTA **pptad) -{ -l_int32 w, h, i; -l_float32 deg2rad, angr, angd, dely; -NUMA *naskew; -PTA *ptas, *ptad; - - PROCNAME("pixGetLocalSkewTransform"); - - if (!pptas || !pptad) - return ERROR_INT("&ptas and &ptad not defined", procName, 1); - *pptas = *pptad = NULL; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (nslices < 2 || nslices > 20) - nslices = DefaultSlices; - if (redsweep < 1 || redsweep > 8) - redsweep = DefaultSweepReduction; - if (redsearch < 1 || redsearch > redsweep) - redsearch = DefaultBsReduction; - if (sweeprange == 0.0) - sweeprange = DefaultSweepRange; - if (sweepdelta == 0.0) - sweepdelta = DefaultSweepDelta; - if (minbsdelta == 0.0) - minbsdelta = DefaultMinbsDelta; - - naskew = pixGetLocalSkewAngles(pixs, nslices, redsweep, redsearch, - sweeprange, sweepdelta, minbsdelta, - NULL, NULL, 0); - if (!naskew) - return ERROR_INT("naskew not made", procName, 1); - - deg2rad = 3.14159265 / 180.; - w = pixGetWidth(pixs); - h = pixGetHeight(pixs); - ptas = ptaCreate(4); - ptad = ptaCreate(4); - *pptas = ptas; - *pptad = ptad; - - /* Find i for skew line that intersects LHS at i and RHS at h / 20 */ - for (i = 0; i < h; i++) { - numaGetFValue(naskew, i, &angd); - angr = angd * deg2rad; - dely = w * tan(angr); - if (i - dely > 0.05 * h) - break; - } - ptaAddPt(ptas, 0, i); - ptaAddPt(ptas, w - 1, i - dely); - ptaAddPt(ptad, 0, i); - ptaAddPt(ptad, w - 1, i); - - /* Find i for skew line that intersects LHS at i and RHS at 19h / 20 */ - for (i = h - 1; i > 0; i--) { - numaGetFValue(naskew, i, &angd); - angr = angd * deg2rad; - dely = w * tan(angr); - if (i - dely < 0.95 * h) - break; - } - ptaAddPt(ptas, 0, i); - ptaAddPt(ptas, w - 1, i - dely); - ptaAddPt(ptad, 0, i); - ptaAddPt(ptad, w - 1, i); - - numaDestroy(&naskew); - return 0; -} - - -/*! - * \brief pixGetLocalSkewAngles() - * - * \param[in] pixs 1 bpp - * \param[in] nslices the number of horizontal overlapping slices; - * must be larger than 1 and not exceed 20; - * use 0 for default - * \param[in] redsweep sweep reduction factor: 1, 2, 4 or 8; - * use 0 for default value - * \param[in] redsearch search reduction factor: 1, 2, 4 or 8, and not - * larger than redsweep; use 0 for default value - * \param[in] sweeprange half the full range, assumed about 0; - * in degrees; use 0.0 for default value - * \param[in] sweepdelta angle increment of sweep; in degrees; - * use 0.0 for default value - * \param[in] minbsdelta min binary search increment angle; in degrees; - * use 0.0 for default value - * \param[out] pa [optional] slope of skew as fctn of y - * \param[out] pb [optional] intercept at y = 0 of skew, - 8 as a function of y - * \param[in] debug 1 for generating plot of skew angle vs. y; - * 0 otherwise - * \return naskew, or NULL on error - * - *
- * Notes: - * (1) The local skew is measured in a set of overlapping strips. - * We then do a least square linear fit parameters to get - * the slope and intercept parameters a and b in - * skew-angle = a * y + b (degrees) - * for the local skew as a function of raster line y. - * This is then used to make naskew, which can be interpreted - * as the computed skew angle (in degrees) at the left edge - * of each raster line. - * (2) naskew can then be used to find the baselines of text, because - * each text line has a baseline that should intersect - * the left edge of the image with the angle given by this - * array, evaluated at the raster line of intersection. - *- */ -NUMA * -pixGetLocalSkewAngles(PIX *pixs, - l_int32 nslices, - l_int32 redsweep, - l_int32 redsearch, - l_float32 sweeprange, - l_float32 sweepdelta, - l_float32 minbsdelta, - l_float32 *pa, - l_float32 *pb, - l_int32 debug) -{ -l_int32 w, h, hs, i, ystart, yend, ovlap, npts; -l_float32 angle, conf, ycenter, a, b; -BOX *box; -GPLOT *gplot; -NUMA *naskew, *nax, *nay; -PIX *pix; -PTA *pta; - - PROCNAME("pixGetLocalSkewAngles"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (NUMA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (nslices < 2 || nslices > 20) - nslices = DefaultSlices; - if (redsweep < 1 || redsweep > 8) - redsweep = DefaultSweepReduction; - if (redsearch < 1 || redsearch > redsweep) - redsearch = DefaultBsReduction; - if (sweeprange == 0.0) - sweeprange = DefaultSweepRange; - if (sweepdelta == 0.0) - sweepdelta = DefaultSweepDelta; - if (minbsdelta == 0.0) - minbsdelta = DefaultMinbsDelta; - - pixGetDimensions(pixs, &w, &h, NULL); - hs = h / nslices; - ovlap = (l_int32)(OverlapFraction * hs); - pta = ptaCreate(nslices); - for (i = 0; i < nslices; i++) { - ystart = L_MAX(0, hs * i - ovlap); - yend = L_MIN(h - 1, hs * (i + 1) + ovlap); - ycenter = (l_float32)(ystart + yend) / 2; - box = boxCreate(0, ystart, w, yend - ystart + 1); - pix = pixClipRectangle(pixs, box, NULL); - pixFindSkewSweepAndSearch(pix, &angle, &conf, redsweep, redsearch, - sweeprange, sweepdelta, minbsdelta); - if (conf > MinAllowedConfidence) - ptaAddPt(pta, ycenter, angle); - pixDestroy(&pix); - boxDestroy(&box); - } - - /* Do linear least squares fit */ - if ((npts = ptaGetCount(pta)) < 2) { - ptaDestroy(&pta); - return (NUMA *)ERROR_PTR("can't fit skew", procName, NULL); - } - ptaGetLinearLSF(pta, &a, &b, NULL); - if (pa) *pa = a; - if (pb) *pb = b; - - /* Make skew angle array as function of raster line */ - naskew = numaCreate(h); - for (i = 0; i < h; i++) { - angle = a * i + b; - numaAddNumber(naskew, angle); - } - - if (debug) { - lept_mkdir("lept/baseline"); - ptaGetArrays(pta, &nax, &nay); - gplot = gplotCreate("/tmp/lept/baseline/skew", GPLOT_PNG, - "skew as fctn of y", "y (in raster lines from top)", - "angle (in degrees)"); - gplotAddPlot(gplot, NULL, naskew, GPLOT_POINTS, "linear lsf"); - gplotAddPlot(gplot, nax, nay, GPLOT_POINTS, "actual data pts"); - gplotMakeOutput(gplot); - gplotDestroy(&gplot); - numaDestroy(&nax); - numaDestroy(&nay); - } - - ptaDestroy(&pta); - return naskew; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bbuffer.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bbuffer.c deleted file mode 100644 index 4e0e3074..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bbuffer.c +++ /dev/null @@ -1,486 +0,0 @@ -/*====================================================================* - - 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 bbuffer.c - *
- * - * Create/Destroy BBuffer - * L_BBUFFER *bbufferCreate() - * void *bbufferDestroy() - * l_uint8 *bbufferDestroyAndSaveData() - * - * Operations to read data TO a BBuffer - * l_int32 bbufferRead() - * l_int32 bbufferReadStream() - * l_int32 bbufferExtendArray() - * - * Operations to write data FROM a BBuffer - * l_int32 bbufferWrite() - * l_int32 bbufferWriteStream() - * - * The bbuffer is an implementation of a byte queue. - * The bbuffer holds a byte array from which bytes are - * processed in a first-in/first-out fashion. As with - * any queue, bbuffer maintains two "pointers," one to the - * tail of the queue (where you read new bytes onto it) - * and one to the head of the queue (where you start from - * when writing bytes out of it. - * - * The queue can be visualized: - * - * \code - * byte 0 byte (nalloc - 1) - * | | - * -------------------------------------------------- - * H T - * [ aw ][ bytes currently on queue ][ anr ] - * - * ---: all allocated data in bbuffer - * H: queue head (ptr to next byte to be written out) - * T: queue tail (ptr to first byte to be written to) - * aw: already written from queue - * anr: allocated but not yet read to - * \endcode - * The purpose of bbuffer is to allow you to safely read - * bytes in, and to sequentially write them out as well. - * In the process of writing bytes out, you don't actually - * remove the bytes in the array; you just move the pointer - * (nwritten) which points to the head of the queue. In - * the process of reading bytes in, you sometimes need to - * expand the array size. If a read is performed after a - * write, so that the head of the queue is not at the - * beginning of the array, the bytes already written are - * first removed by copying the others over them; then the - * new bytes are read onto the tail of the queue. - * - * Note that the meaning of "read into" and "write from" - * the bbuffer is OPPOSITE to that for a stream, where - * you read "from" a stream and write "into" a stream. - * As a mnemonic for remembering the direction: - * ~ to read bytes from a stream into the bbuffer, - * you call fread on the stream - * ~ to write bytes from the bbuffer into a stream, - * you call fwrite on the stream - * - * See zlibmem.c for an example use of bbuffer, where we - * compress and decompress an array of bytes in memory. - * - * We can also use the bbuffer trivially to read from stdin - * into memory; e.g., to capture bytes piped from the stdout - * of another program. This is equivalent to repeatedly - * calling bbufferReadStream() until the input queue is empty. - * This is implemented in l_binaryReadStream(). - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) If a buffer address is given, you should read all the data in. - * (2) Allocates a bbuffer with associated byte array of - * the given size. If a buffer address is given, - * it then reads the number of bytes into the byte array. - *- */ -L_BBUFFER * -bbufferCreate(const l_uint8 *indata, - l_int32 nalloc) -{ -L_BBUFFER *bb; - - PROCNAME("bbufferCreate"); - - if (nalloc <= 0 || nalloc > MaxArraySize) - nalloc = InitialArraySize; - - bb = (L_BBUFFER *)LEPT_CALLOC(1, sizeof(L_BBUFFER)); - if ((bb->array = (l_uint8 *)LEPT_CALLOC(nalloc, sizeof(l_uint8))) == NULL) { - LEPT_FREE(bb); - return (L_BBUFFER *)ERROR_PTR("byte array not made", procName, NULL); - } - bb->nalloc = nalloc; - bb->nwritten = 0; - - if (indata) { - memcpy(bb->array, indata, nalloc); - bb->n = nalloc; - } else { - bb->n = 0; - } - - return bb; -} - - -/*! - * \brief bbufferDestroy() - * - * \param[in,out] pbb will be set to null before returning - * \return void - * - *
- * Notes: - * (1) Destroys the byte array in the bbuffer and then the bbuffer; - * then nulls the contents of the input ptr. - *- */ -void -bbufferDestroy(L_BBUFFER **pbb) -{ -L_BBUFFER *bb; - - PROCNAME("bbufferDestroy"); - - if (pbb == NULL) { - L_WARNING("ptr address is NULL\n", procName); - return; - } - - if ((bb = *pbb) == NULL) - return; - - if (bb->array) - LEPT_FREE(bb->array); - LEPT_FREE(bb); - *pbb = NULL; - - return; -} - - -/*! - * \brief bbufferDestroyAndSaveData() - * - * \param[in,out] pbb input data buffer; will be nulled - * \param[out] pnbytes number of bytes saved in array - * \return barray newly allocated array of data - * - *
- * Notes: - * (1) Copies data to newly allocated array; then destroys the bbuffer. - *- */ -l_uint8 * -bbufferDestroyAndSaveData(L_BBUFFER **pbb, - size_t *pnbytes) -{ -l_uint8 *array; -size_t nbytes; -L_BBUFFER *bb; - - PROCNAME("bbufferDestroyAndSaveData"); - - if (pbb == NULL) { - L_WARNING("ptr address is NULL\n", procName); - return NULL; - } - if (pnbytes == NULL) { - L_WARNING("&nbytes is NULL\n", procName); - bbufferDestroy(pbb); - return NULL; - } - - if ((bb = *pbb) == NULL) - return NULL; - - /* write all unwritten bytes out to a new array */ - nbytes = bb->n - bb->nwritten; - *pnbytes = nbytes; - if ((array = (l_uint8 *)LEPT_CALLOC(nbytes, sizeof(l_uint8))) == NULL) { - L_WARNING("calloc failure for array\n", procName); - return NULL; - } - memcpy(array, bb->array + bb->nwritten, nbytes); - - bbufferDestroy(pbb); - return array; -} - - -/*--------------------------------------------------------------------------* - * Operations to read data INTO a BBuffer * - *--------------------------------------------------------------------------*/ -/*! - * \brief bbufferRead() - * - * \param[in] bb bbuffer - * \param[in] src source memory buffer from which bytes are read - * \param[in] nbytes bytes to be read - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) For a read after write, first remove the written - * bytes by shifting the unwritten bytes in the array, - * then check if there is enough room to add the new bytes. - * If not, realloc with bbufferExpandArray(), resulting - * in a second writing of the unwritten bytes. While less - * efficient, this is simpler than making a special case - * of reallocNew(). - *- */ -l_ok -bbufferRead(L_BBUFFER *bb, - l_uint8 *src, - l_int32 nbytes) -{ -l_int32 navail, nadd, nwritten; - - PROCNAME("bbufferRead"); - - if (!bb) - return ERROR_INT("bb not defined", procName, 1); - if (!src) - return ERROR_INT("src not defined", procName, 1); - if (nbytes == 0) - return ERROR_INT("no bytes to read", procName, 1); - - if ((nwritten = bb->nwritten)) { /* move the unwritten bytes over */ - memmove(bb->array, bb->array + nwritten, bb->n - nwritten); - bb->nwritten = 0; - bb->n -= nwritten; - } - - /* If necessary, expand the allocated array. Do so by - * by at least a factor of two. */ - navail = bb->nalloc - bb->n; - if (nbytes > navail) { - nadd = L_MAX(bb->nalloc, nbytes); - bbufferExtendArray(bb, nadd); - } - - /* Read in the new bytes */ - memcpy(bb->array + bb->n, src, nbytes); - bb->n += nbytes; - - return 0; -} - - -/*! - * \brief bbufferReadStream() - * - * \param[in] bb bbuffer - * \param[in] fp source stream from which bytes are read - * \param[in] nbytes bytes to be read - * \return 0 if OK, 1 on error - */ -l_ok -bbufferReadStream(L_BBUFFER *bb, - FILE *fp, - l_int32 nbytes) -{ -l_int32 navail, nadd, nread, nwritten; - - PROCNAME("bbufferReadStream"); - - if (!bb) - return ERROR_INT("bb not defined", procName, 1); - if (!fp) - return ERROR_INT("fp not defined", procName, 1); - if (nbytes == 0) - return ERROR_INT("no bytes to read", procName, 1); - - if ((nwritten = bb->nwritten)) { /* move any unwritten bytes over */ - memmove(bb->array, bb->array + nwritten, bb->n - nwritten); - bb->nwritten = 0; - bb->n -= nwritten; - } - - /* If necessary, expand the allocated array. Do so by - * by at least a factor of two. */ - navail = bb->nalloc - bb->n; - if (nbytes > navail) { - nadd = L_MAX(bb->nalloc, nbytes); - bbufferExtendArray(bb, nadd); - } - - /* Read in the new bytes */ - nread = fread(bb->array + bb->n, 1, nbytes, fp); - bb->n += nread; - - return 0; -} - - -/*! - * \brief bbufferExtendArray() - * - * \param[in] bb bbuffer - * \param[in] nbytes number of bytes to extend array size - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) reallocNew() copies all bb->nalloc bytes, even though - * only bb->n are data. - *- */ -l_ok -bbufferExtendArray(L_BBUFFER *bb, - l_int32 nbytes) -{ - PROCNAME("bbufferExtendArray"); - - if (!bb) - return ERROR_INT("bb not defined", procName, 1); - - if ((bb->array = (l_uint8 *)reallocNew((void **)&bb->array, - bb->nalloc, - bb->nalloc + nbytes)) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - - bb->nalloc += nbytes; - return 0; -} - - -/*--------------------------------------------------------------------------* - * Operations to write data FROM a BBuffer * - *--------------------------------------------------------------------------*/ -/*! - * \brief bbufferWrite() - * - * \param[in] bb bbuffer - * \param[in] dest dest memory buffer to which bytes are written - * \param[in] nbytes bytes requested to be written - * \param[out] pnout bytes actually written - * \return 0 if OK, 1 on error - */ -l_ok -bbufferWrite(L_BBUFFER *bb, - l_uint8 *dest, - size_t nbytes, - size_t *pnout) -{ -size_t nleft, nout; - - PROCNAME("bbufferWrite"); - - if (!bb) - return ERROR_INT("bb not defined", procName, 1); - if (!dest) - return ERROR_INT("dest not defined", procName, 1); - if (nbytes <= 0) - return ERROR_INT("no bytes requested to write", procName, 1); - if (!pnout) - return ERROR_INT("&nout not defined", procName, 1); - - nleft = bb->n - bb->nwritten; - nout = L_MIN(nleft, nbytes); - *pnout = nout; - - if (nleft == 0) { /* nothing to write; reinitialize the buffer */ - bb->n = 0; - bb->nwritten = 0; - return 0; - } - - /* nout > 0; transfer the data out */ - memcpy(dest, bb->array + bb->nwritten, nout); - bb->nwritten += nout; - - /* If all written; "empty" the buffer */ - if (nout == nleft) { - bb->n = 0; - bb->nwritten = 0; - } - - return 0; -} - - -/*! - * \brief bbufferWriteStream() - * - * \param[in] bb bbuffer - * \param[in] fp dest stream to which bytes are written - * \param[in] nbytes bytes requested to be written - * \param[out] pnout bytes actually written - * \return 0 if OK, 1 on error - */ -l_ok -bbufferWriteStream(L_BBUFFER *bb, - FILE *fp, - size_t nbytes, - size_t *pnout) -{ -size_t nleft, nout; - - PROCNAME("bbufferWriteStream"); - - if (!bb) - return ERROR_INT("bb not defined", procName, 1); - if (!fp) - return ERROR_INT("output stream not defined", procName, 1); - if (nbytes <= 0) - return ERROR_INT("no bytes requested to write", procName, 1); - if (!pnout) - return ERROR_INT("&nout not defined", procName, 1); - - nleft = bb->n - bb->nwritten; - nout = L_MIN(nleft, nbytes); - *pnout = nout; - - if (nleft == 0) { /* nothing to write; reinitialize the buffer */ - bb->n = 0; - bb->nwritten = 0; - return 0; - } - - /* nout > 0; transfer the data out */ - fwrite(bb->array + bb->nwritten, 1, nout, fp); - bb->nwritten += nout; - - /* If all written; "empty" the buffer */ - if (nout == nleft) { - bb->n = 0; - bb->nwritten = 0; - } - - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bbuffer.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bbuffer.h deleted file mode 100644 index 945cbb0f..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bbuffer.h +++ /dev/null @@ -1,60 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_BBUFFER_H -#define LEPTONICA_BBUFFER_H - -/*! - * \file bbuffer.h - * - *
- * Expandable byte buffer for reading data in from memory and - * writing data out to other memory. - * - * This implements a queue of bytes, so data read in is put - * on the "back" of the queue (i.e., the end of the byte array) - * and data written out is taken from the "front" of the queue - * (i.e., from an index marker "nwritten" that is initially set at - * the beginning of the array.) As usual with expandable - * arrays, we keep the size of the allocated array and the - * number of bytes that have been read into the array. - * - * For implementation details, see bbuffer.c. - *- */ - -/*! Expandable byte buffer for memory read/write operations */ -struct L_ByteBuffer -{ - l_int32 nalloc; /*!< size of allocated byte array */ - l_int32 n; /*!< number of bytes read into to the array */ - l_int32 nwritten; /*!< number of bytes written from the array */ - l_uint8 *array; /*!< byte array */ -}; -typedef struct L_ByteBuffer L_BBUFFER; - - -#endif /* LEPTONICA_BBUFFER_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bilateral.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bilateral.c deleted file mode 100644 index 7b29fb19..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bilateral.c +++ /dev/null @@ -1,813 +0,0 @@ -/*====================================================================* - - 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 bilateral.c - *
- * - * Top level approximate separable grayscale or color bilateral filtering - * PIX *pixBilateral() - * PIX *pixBilateralGray() - * - * Implementation of approximate separable bilateral filter - * static L_BILATERAL *bilateralCreate() - * static void *bilateralDestroy() - * static PIX *bilateralApply() - * - * Slow, exact implementation of grayscale or color bilateral filtering - * PIX *pixBilateralExact() - * PIX *pixBilateralGrayExact() - * PIX *pixBlockBilateralExact() - * - * Kernel helper function - * L_KERNEL *makeRangeKernel() - * - * This includes both a slow, exact implementation of the bilateral - * filter algorithm (given by Sylvain Paris and Frédo Durand), - * and a fast, approximate and separable implementation (following - * Yang, Tan and Ahuja). See bilateral.h for algorithmic details. - * - * The bilateral filter has the nice property of applying a gaussian - * filter to smooth parts of the image that don't vary too quickly, - * while at the same time preserving edges. The filter is nonlinear - * and cannot be decomposed into two separable filters; however, - * there exists an approximate method that is separable. To further - * speed up the separable implementation, you can generate the - * intermediate data at reduced resolution. - * - * The full kernel is composed of two parts: a spatial gaussian filter - * and a nonlinear "range" filter that depends on the intensity difference - * between the reference pixel at the spatial kernel origin and any other - * pixel within the kernel support. - * - * In our implementations, the range filter is a parameterized, - * one-sided, 256-element, monotonically decreasing gaussian function - * of the absolute value of the difference between pixel values; namely, - * abs(I2 - I1). In general, any decreasing function can be used, - * and more generally, any two-dimensional kernel can be used if - * you wish to relax the 'abs' condition. (In that case, the range - * filter can be 256 x 256). - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This performs a relatively fast, separable bilateral - * filtering operation. The time is proportional to ncomps - * and varies inversely approximately as the cube of the - * reduction factor. See bilateral.h for algorithm details. - * (2) We impose minimum values for range_stdev and ncomps to - * avoid nasty artifacts when either are too small. We also - * impose a constraint on their product: - * ncomps * range_stdev >= 100. - * So for values of range_stdev >= 25, ncomps can be as small as 4. - * Here is a qualitative, intuitive explanation for this constraint. - * Call the difference in k values between the J(k) == 'delta', where - * 'delta' ~ 200 / ncomps - * Then this constraint is roughly equivalent to the condition: - * 'delta' < 2 * range_stdev - * Note that at an intensity difference of (2 * range_stdev), the - * range part of the kernel reduces the effect by the factor 0.14. - * This constraint requires that we have a sufficient number of - * PCBs (i.e, a small enough 'delta'), so that for any value of - * image intensity I, there exists a k (and a PCB, J(k), such that - * |I - k| < range_stdev - * Any fewer PCBs and we don't have enough to support this condition. - * (3) The upper limit of 30 on ncomps is imposed because the - * gain in accuracy is not worth the extra computation. - * (4) The size of the gaussian kernel is twice the spatial_stdev - * on each side of the origin. The minimum value of - * spatial_stdev, 0.5, is required to have a finite sized - * spatial kernel. In practice, a much larger value is used. - * (5) Computation of the intermediate images goes inversely - * as the cube of the reduction factor. If you can use a - * reduction of 2 or 4, it is well-advised. - * (6) The range kernel is defined over the absolute value of pixel - * grayscale differences, and hence must have size 256 x 1. - * Values in the array represent the multiplying weight - * depending on the absolute gray value difference between - * the source pixel and the neighboring pixel, and should - * be monotonically decreasing. - * (7) Interesting observation. Run this on prog/fish24.jpg, with - * range_stdev = 60, ncomps = 6, and spatial_dev = {10, 30, 50}. - * As spatial_dev gets larger, we get the counter-intuitive - * result that the body of the red fish becomes less blurry. - *- */ -PIX * -pixBilateral(PIX *pixs, - l_float32 spatial_stdev, - l_float32 range_stdev, - l_int32 ncomps, - l_int32 reduction) -{ -l_int32 d; -l_float32 sstdev; /* scaled spatial stdev */ -PIX *pixt, *pixr, *pixg, *pixb, *pixd; - - PROCNAME("pixBilateral"); - - if (!pixs || pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs not defined or cmapped", procName, NULL); - d = pixGetDepth(pixs); - if (d != 8 && d != 32) - return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); - if (reduction != 1 && reduction != 2 && reduction != 4) - return (PIX *)ERROR_PTR("reduction invalid", procName, NULL); - sstdev = spatial_stdev / (l_float32)reduction; /* reduced spat. stdev */ - if (sstdev < 0.5) - return (PIX *)ERROR_PTR("sstdev < 0.5", procName, NULL); - if (range_stdev <= 5.0) - return (PIX *)ERROR_PTR("range_stdev <= 5.0", procName, NULL); - if (ncomps < 4 || ncomps > 30) - return (PIX *)ERROR_PTR("ncomps not in [4 ... 30]", procName, NULL); - if (ncomps * range_stdev < 100.0) - return (PIX *)ERROR_PTR("ncomps * range_stdev < 100.0", procName, NULL); - - if (d == 8) - return pixBilateralGray(pixs, spatial_stdev, range_stdev, - ncomps, reduction); - - pixt = pixGetRGBComponent(pixs, COLOR_RED); - pixr = pixBilateralGray(pixt, spatial_stdev, range_stdev, ncomps, - reduction); - pixDestroy(&pixt); - pixt = pixGetRGBComponent(pixs, COLOR_GREEN); - pixg = pixBilateralGray(pixt, spatial_stdev, range_stdev, ncomps, - reduction); - pixDestroy(&pixt); - pixt = pixGetRGBComponent(pixs, COLOR_BLUE); - pixb = pixBilateralGray(pixt, spatial_stdev, range_stdev, ncomps, - reduction); - pixDestroy(&pixt); - pixd = pixCreateRGBImage(pixr, pixg, pixb); - pixDestroy(&pixr); - pixDestroy(&pixg); - pixDestroy(&pixb); - return pixd; -} - - -/*! - * \brief pixBilateralGray() - * - * \param[in] pixs 8 bpp gray - * \param[in] spatial_stdev of gaussian kernel; in pixels, > 0.5 - * \param[in] range_stdev of gaussian range kernel; > 5.0; typ. 50.0 - * \param[in] ncomps number of intermediate sums J(k,x); - * in [4 ... 30] - * \param[in] reduction 1, 2 or 4 - * \return pixd 8 bpp bilateral filtered image, or NULL on error - * - *
- * Notes: - * (1) See pixBilateral() for constraints on the input parameters. - * (2) See pixBilateral() for algorithm details. - *- */ -PIX * -pixBilateralGray(PIX *pixs, - l_float32 spatial_stdev, - l_float32 range_stdev, - l_int32 ncomps, - l_int32 reduction) -{ -l_float32 sstdev; /* scaled spatial stdev */ -PIX *pixd; -L_BILATERAL *bil; - - PROCNAME("pixBilateralGray"); - - if (!pixs || pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs not defined or cmapped", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp gray", procName, NULL); - if (reduction != 1 && reduction != 2 && reduction != 4) - return (PIX *)ERROR_PTR("reduction invalid", procName, NULL); - sstdev = spatial_stdev / (l_float32)reduction; /* reduced spat. stdev */ - if (sstdev < 0.5) - return (PIX *)ERROR_PTR("sstdev < 0.5", procName, NULL); - if (range_stdev <= 5.0) - return (PIX *)ERROR_PTR("range_stdev <= 5.0", procName, NULL); - if (ncomps < 4 || ncomps > 30) - return (PIX *)ERROR_PTR("ncomps not in [4 ... 30]", procName, NULL); - if (ncomps * range_stdev < 100.0) - return (PIX *)ERROR_PTR("ncomps * range_stdev < 100.0", procName, NULL); - - bil = bilateralCreate(pixs, spatial_stdev, range_stdev, ncomps, reduction); - if (!bil) return (PIX *)ERROR_PTR("bil not made", procName, NULL); - pixd = bilateralApply(bil); - bilateralDestroy(&bil); - return pixd; -} - - -/*----------------------------------------------------------------------* - * Implementation of approximate separable bilateral filter * - *----------------------------------------------------------------------*/ -/*! - * \brief bilateralCreate() - * - * \param[in] pixs 8 bpp gray, no colormap - * \param[in] spatial_stdev of gaussian kernel; in pixels, > 0.5 - * \param[in] range_stdev of gaussian range kernel; > 5.0; typ. 50.0 - * \param[in] ncomps number of intermediate sums J(k,x); - * in [4 ... 30] - * \param[in] reduction 1, 2 or 4 - * \return bil, or NULL on error - * - *
- * Notes: - * (1) This initializes a bilateral filtering operation, generating all - * the data required. It takes most of the time in the bilateral - * filtering operation. - * (2) See bilateral.h for details of the algorithm. - * (3) See pixBilateral() for constraints on input parameters, which - * are not checked here. - *- */ -static L_BILATERAL * -bilateralCreate(PIX *pixs, - l_float32 spatial_stdev, - l_float32 range_stdev, - l_int32 ncomps, - l_int32 reduction) -{ -l_int32 w, ws, wd, h, hs, hd, i, j, k, index; -l_int32 border, minval, maxval, spatial_size; -l_int32 halfwidth, wpls, wplt, wpld, kval, nval, dval; -l_float32 sstdev, fval1, fval2, denom, sum, norm, kern; -l_int32 *nc, *kindex; -l_float32 *kfract, *range, *spatial; -l_uint32 *datas, *datat, *datad, *lines, *linet, *lined; -L_BILATERAL *bil; -PIX *pixt, *pixt2, *pixsc, *pixd; -PIXA *pixac; - - PROCNAME("bilateralCreate"); - - sstdev = spatial_stdev / (l_float32)reduction; /* reduced spat. stdev */ - if ((bil = (L_BILATERAL *)LEPT_CALLOC(1, sizeof(L_BILATERAL))) == NULL) - return (L_BILATERAL *)ERROR_PTR("bil not made", procName, NULL); - bil->spatial_stdev = sstdev; - bil->range_stdev = range_stdev; - bil->reduction = reduction; - bil->ncomps = ncomps; - - if (reduction == 1) { - pixt = pixClone(pixs); - } else if (reduction == 2) { - pixt = pixScaleAreaMap2(pixs); - } else { /* reduction == 4) */ - pixt2 = pixScaleAreaMap2(pixs); - pixt = pixScaleAreaMap2(pixt2); - pixDestroy(&pixt2); - } - - pixGetExtremeValue(pixt, 1, L_SELECT_MIN, NULL, NULL, NULL, &minval); - pixGetExtremeValue(pixt, 1, L_SELECT_MAX, NULL, NULL, NULL, &maxval); - bil->minval = minval; - bil->maxval = maxval; - - border = (l_int32)(2 * sstdev + 1); - pixsc = pixAddMirroredBorder(pixt, border, border, border, border); - bil->pixsc = pixsc; - pixDestroy(&pixt); - bil->pixs = pixClone(pixs); - - - /* -------------------------------------------------------------------- * - * Generate arrays for interpolation of J(k,x): - * (1.0 - kfract[.]) * J(kindex[.], x) + kfract[.] * J(kindex[.] + 1, x), - * where I(x) is the index into kfract[] and kindex[], - * and x is an index into the 2D image array. - * -------------------------------------------------------------------- */ - /* nc is the set of k values to be used in J(k,x) */ - nc = (l_int32 *)LEPT_CALLOC(ncomps, sizeof(l_int32)); - for (i = 0; i < ncomps; i++) - nc[i] = minval + i * (maxval - minval) / (ncomps - 1); - bil->nc = nc; - - /* kindex maps from intensity I(x) to the lower k index for J(k,x) */ - kindex = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - for (i = minval, k = 0; i <= maxval && k < ncomps - 1; k++) { - fval2 = nc[k + 1]; - while (i < fval2) { - kindex[i] = k; - i++; - } - } - kindex[maxval] = ncomps - 2; - bil->kindex = kindex; - - /* kfract maps from intensity I(x) to the fraction of J(k+1,x) used */ - kfract = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32)); /* from lower */ - for (i = minval, k = 0; i <= maxval && k < ncomps - 1; k++) { - fval1 = nc[k]; - fval2 = nc[k + 1]; - while (i < fval2) { - kfract[i] = (l_float32)(i - fval1) / (l_float32)(fval2 - fval1); - i++; - } - } - kfract[maxval] = 1.0; - bil->kfract = kfract; - -#if DEBUG_BILATERAL - for (i = minval; i <= maxval; i++) - lept_stderr("kindex[%d] = %d; kfract[%d] = %5.3f\n", - i, kindex[i], i, kfract[i]); - for (i = 0; i < ncomps; i++) - lept_stderr("nc[%d] = %d\n", i, nc[i]); -#endif /* DEBUG_BILATERAL */ - - - /* -------------------------------------------------------------------- * - * Generate 1-D kernel arrays (spatial and range) * - * -------------------------------------------------------------------- */ - spatial_size = 2 * sstdev + 1; - spatial = (l_float32 *)LEPT_CALLOC(spatial_size, sizeof(l_float32)); - denom = 2. * sstdev * sstdev; - for (i = 0; i < spatial_size; i++) - spatial[i] = expf(-(l_float32)(i * i) / denom); - bil->spatial = spatial; - - range = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32)); - denom = 2. * range_stdev * range_stdev; - for (i = 0; i < 256; i++) - range[i] = expf(-(l_float32)(i * i) / denom); - bil->range = range; - - - /* -------------------------------------------------------------------- * - * Generate principal bilateral component images * - * -------------------------------------------------------------------- */ - pixac = pixaCreate(ncomps); - pixGetDimensions(pixsc, &ws, &hs, NULL); - datas = pixGetData(pixsc); - wpls = pixGetWpl(pixsc); - pixGetDimensions(pixs, &w, &h, NULL); - wd = (w + reduction - 1) / reduction; - hd = (h + reduction - 1) / reduction; - halfwidth = (l_int32)(2.0 * sstdev); - for (index = 0; index < ncomps; index++) { - pixt = pixCopy(NULL, pixsc); - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - kval = nc[index]; - /* Separable convolutions: horizontal first */ - for (i = 0; i < hd; i++) { - lines = datas + (border + i) * wpls; - linet = datat + (border + i) * wplt; - for (j = 0; j < wd; j++) { - sum = 0.0; - norm = 0.0; - for (k = -halfwidth; k <= halfwidth; k++) { - nval = GET_DATA_BYTE(lines, border + j + k); - kern = spatial[L_ABS(k)] * range[L_ABS(kval - nval)]; - sum += kern * nval; - norm += kern; - } - dval = (l_int32)((sum / norm) + 0.5); - SET_DATA_BYTE(linet, border + j, dval); - } - } - /* Vertical convolution */ - pixd = pixCreate(wd, hd, 8); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < hd; i++) { - linet = datat + (border + i) * wplt; - lined = datad + i * wpld; - for (j = 0; j < wd; j++) { - sum = 0.0; - norm = 0.0; - for (k = -halfwidth; k <= halfwidth; k++) { - nval = GET_DATA_BYTE(linet + k * wplt, border + j); - kern = spatial[L_ABS(k)] * range[L_ABS(kval - nval)]; - sum += kern * nval; - norm += kern; - } - dval = (l_int32)((sum / norm) + 0.5); - SET_DATA_BYTE(lined, j, dval); - } - } - pixDestroy(&pixt); - pixaAddPix(pixac, pixd, L_INSERT); - } - bil->pixac = pixac; - bil->lineset = (l_uint32 ***)pixaGetLinePtrs(pixac, NULL); - - return bil; -} - - -/*! - * \brief bilateralApply() - * - * \param[in] 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; -} - - -/*! - * \brief bilateralDestroy() - * - * \param[in,out] pbil will be set to null before returning - */ -static void -bilateralDestroy(L_BILATERAL **pbil) -{ -l_int32 i; -L_BILATERAL *bil; - - PROCNAME("bilateralDestroy"); - - if (pbil == NULL) { - L_WARNING("ptr address is null!\n", procName); - return; - } - - if ((bil = *pbil) == NULL) - return; - - pixDestroy(&bil->pixs); - pixDestroy(&bil->pixsc); - pixaDestroy(&bil->pixac); - LEPT_FREE(bil->spatial); - LEPT_FREE(bil->range); - LEPT_FREE(bil->nc); - LEPT_FREE(bil->kindex); - LEPT_FREE(bil->kfract); - for (i = 0; i < bil->ncomps; i++) - LEPT_FREE(bil->lineset[i]); - LEPT_FREE(bil->lineset); - LEPT_FREE(bil); - *pbil = NULL; - return; -} - - -/*----------------------------------------------------------------------* - * Exact implementation of grayscale or color bilateral filtering * - *----------------------------------------------------------------------*/ -/*! - * \brief pixBilateralExact() - * - * \param[in] pixs 8 bpp gray or 32 bpp rgb - * \param[in] spatial_kel gaussian kernel - * \param[in] range_kel [optional] 256 x 1, monotonically decreasing - * \return pixd 8 bpp bilateral filtered image - * - *
- * Notes: - * (1) The spatial_kel is a conventional smoothing kernel, typically a - * 2-d Gaussian kernel or other block kernel. It can be either - * normalized or not, but must be everywhere positive. - * (2) The range_kel is defined over the absolute value of pixel - * grayscale differences, and hence must have size 256 x 1. - * Values in the array represent the multiplying weight for each - * gray value difference between the target pixel and center of the - * kernel, and should be monotonically decreasing. - * (3) If range_kel == NULL, a constant weight is applied regardless - * of the range value difference. This degenerates to a regular - * pixConvolve() with a normalized kernel. - *- */ -PIX * -pixBilateralExact(PIX *pixs, - L_KERNEL *spatial_kel, - L_KERNEL *range_kel) -{ -l_int32 d; -PIX *pixt, *pixr, *pixg, *pixb, *pixd; - - PROCNAME("pixBilateralExact"); - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetColormap(pixs) != NULL) - return (PIX *)ERROR_PTR("pixs is cmapped", procName, NULL); - d = pixGetDepth(pixs); - if (d != 8 && d != 32) - return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); - if (!spatial_kel) - return (PIX *)ERROR_PTR("spatial_ke not defined", procName, NULL); - - if (d == 8) { - return pixBilateralGrayExact(pixs, spatial_kel, range_kel); - } else { /* d == 32 */ - pixt = pixGetRGBComponent(pixs, COLOR_RED); - pixr = pixBilateralGrayExact(pixt, spatial_kel, range_kel); - pixDestroy(&pixt); - pixt = pixGetRGBComponent(pixs, COLOR_GREEN); - pixg = pixBilateralGrayExact(pixt, spatial_kel, range_kel); - pixDestroy(&pixt); - pixt = pixGetRGBComponent(pixs, COLOR_BLUE); - pixb = pixBilateralGrayExact(pixt, spatial_kel, range_kel); - pixDestroy(&pixt); - pixd = pixCreateRGBImage(pixr, pixg, pixb); - - pixDestroy(&pixr); - pixDestroy(&pixg); - pixDestroy(&pixb); - return pixd; - } -} - - -/*! - * \brief pixBilateralGrayExact() - * - * \param[in] pixs 8 bpp gray - * \param[in] spatial_kel gaussian kernel - * \param[in] range_kel [optional] 256 x 1, monotonically decreasing - * \return pixd 8 bpp bilateral filtered image - * - *
- * Notes: - * (1) See pixBilateralExact(). - *- */ -PIX * -pixBilateralGrayExact(PIX *pixs, - L_KERNEL *spatial_kel, - L_KERNEL *range_kel) -{ -l_int32 i, j, id, jd, k, m, w, h, d, sx, sy, cx, cy, wplt, wpld; -l_int32 val, center_val; -l_uint32 *datat, *datad, *linet, *lined; -l_float32 sum, weight_sum, weight; -L_KERNEL *keli; -PIX *pixt, *pixd; - - PROCNAME("pixBilateralGrayExact"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs must be gray", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (!spatial_kel) - return (PIX *)ERROR_PTR("spatial kel not defined", procName, NULL); - - if (!range_kel) - return pixConvolve(pixs, spatial_kel, 8, 1); - if (range_kel->sx != 256 || range_kel->sy != 1) - return (PIX *)ERROR_PTR("range kel not {256 x 1", procName, NULL); - - keli = kernelInvert(spatial_kel); - kernelGetParameters(keli, &sy, &sx, &cy, &cx); - if ((pixt = pixAddMirroredBorder(pixs, cx, sx - cx, cy, sy - cy)) == NULL) { - kernelDestroy(&keli); - return (PIX *)ERROR_PTR("pixt not made", procName, NULL); - } - - pixd = pixCreate(w, h, 8); - datat = pixGetData(pixt); - datad = pixGetData(pixd); - wplt = pixGetWpl(pixt); - wpld = pixGetWpl(pixd); - for (i = 0, id = 0; id < h; i++, id++) { - lined = datad + id * wpld; - for (j = 0, jd = 0; jd < w; j++, jd++) { - center_val = GET_DATA_BYTE(datat + (i + cy) * wplt, j + cx); - weight_sum = 0.0; - sum = 0.0; - for (k = 0; k < sy; k++) { - linet = datat + (i + k) * wplt; - for (m = 0; m < sx; m++) { - val = GET_DATA_BYTE(linet, j + m); - weight = keli->data[k][m] * - range_kel->data[0][L_ABS(center_val - val)]; - weight_sum += weight; - sum += val * weight; - } - } - SET_DATA_BYTE(lined, jd, (l_int32)(sum / weight_sum + 0.5)); - } - } - - kernelDestroy(&keli); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixBlockBilateralExact() - * - * \param[in] pixs 8 bpp gray or 32 bpp rgb - * \param[in] spatial_stdev must be > 0.0 - * \param[in] range_stdev must be > 0.0 - * \return pixd 8 bpp or 32 bpp bilateral filtered image - * - *
- * Notes: - * (1) See pixBilateralExact(). This provides an interface using - * the standard deviations of the spatial and range filters. - * (2) The convolution window halfwidth is 2 * spatial_stdev, - * and the square filter size is 4 * spatial_stdev + 1. - * The kernel captures 95% of total energy. This is compensated - * by normalization. - * (3) The range_stdev is analogous to spatial_halfwidth in the - * grayscale domain [0...255], and determines how much damping of the - * smoothing operation is applied across edges. The larger this - * value is, the smaller the damping. The smaller the value, the - * more edge details are preserved. These approximations are useful - * for deciding the appropriate cutoff. - * kernel[1 * stdev] ~= 0.6 * kernel[0] - * kernel[2 * stdev] ~= 0.14 * kernel[0] - * kernel[3 * stdev] ~= 0.01 * kernel[0] - * If range_stdev is infinite there is no damping, and this - * becomes a conventional gaussian smoothing. - * This value does not affect the run time. - * (4) If range_stdev is negative or zero, the range kernel is - * ignored and this degenerates to a straight gaussian convolution. - * (5) This is very slow for large spatial filters. The time - * on a 3GHz pentium is roughly - * T = 1.2 * 10^-8 * (A * sh^2) sec - * where A = # of pixels, sh = spatial halfwidth of filter. - *- */ -PIX* -pixBlockBilateralExact(PIX *pixs, - l_float32 spatial_stdev, - l_float32 range_stdev) -{ -l_int32 d, halfwidth; -L_KERNEL *spatial_kel, *range_kel; -PIX *pixd; - - PROCNAME("pixBlockBilateralExact"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - if (d != 8 && d != 32) - return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); - if (pixGetColormap(pixs) != NULL) - return (PIX *)ERROR_PTR("pixs is cmapped", procName, NULL); - if (spatial_stdev <= 0.0) - return (PIX *)ERROR_PTR("invalid spatial stdev", procName, NULL); - if (range_stdev <= 0.0) - return (PIX *)ERROR_PTR("invalid range stdev", procName, NULL); - - halfwidth = 2 * spatial_stdev; - spatial_kel = makeGaussianKernel(halfwidth, halfwidth, spatial_stdev, 1.0); - range_kel = makeRangeKernel(range_stdev); - pixd = pixBilateralExact(pixs, spatial_kel, range_kel); - kernelDestroy(&spatial_kel); - kernelDestroy(&range_kel); - return pixd; -} - - -/*----------------------------------------------------------------------* - * Kernel helper function * - *----------------------------------------------------------------------*/ -/*! - * \brief makeRangeKernel() - * - * \param[in] range_stdev must be > 0.0 - * \return kel, or NULL on error - * - *
- * Notes: - * (1) Creates a one-sided Gaussian kernel with the given - * standard deviation. At grayscale difference of one stdev, - * the kernel falls to 0.6, and to 0.01 at three stdev. - * (2) A typical input number might be 20. Then pixels whose - * value differs by 60 from the center pixel have their - * weight in the convolution reduced by a factor of about 0.01. - *- */ -L_KERNEL * -makeRangeKernel(l_float32 range_stdev) -{ -l_int32 x; -l_float32 val, denom; -L_KERNEL *kel; - - PROCNAME("makeRangeKernel"); - - if (range_stdev <= 0.0) - return (L_KERNEL *)ERROR_PTR("invalid stdev <= 0", procName, NULL); - - denom = 2. * range_stdev * range_stdev; - if ((kel = kernelCreate(1, 256)) == NULL) - return (L_KERNEL *)ERROR_PTR("kel not made", procName, NULL); - kernelSetOrigin(kel, 0, 0); - for (x = 0; x < 256; x++) { - val = expf(-(l_float32)(x * x) / denom); - kernelSetElement(kel, 0, x, val); - } - return kel; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bilateral.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bilateral.h deleted file mode 100644 index e5b5bbdd..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bilateral.h +++ /dev/null @@ -1,136 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_BILATERAL_H -#define LEPTONICA_BILATERAL_H - -/*! - * \file bilateral.h - * - *
- * Contains the following struct - * struct L_Bilateral - * - * - * For a tutorial introduction to bilateral filters, which apply a - * gaussian blur to smooth parts of the image while preserving edges, see - * http://people.csail.mit.edu/sparis/bf_course/slides/03_definition_bf.pdf - * - * We give an implementation of a bilateral filtering algorithm given in: - * "Real-Time O(1) Bilateral Filtering," by Yang, Tan and Ahuja, CVPR 2009 - * which is at: - * http://vision.ai.uiuc.edu/~qyang6/publications/cvpr-09-qingxiong-yang.pdf - * This is based on an earlier algorithm by Sylvain Paris and Frédo Durand: - * http://people.csail.mit.edu/sparis/publi/2006/eccv/ - * Paris_06_Fast_Approximation.pdf - * - * The kernel of the filter is a product of a spatial gaussian and a - * monotonically decreasing function of the difference in intensity - * between the source pixel and the neighboring pixel. The intensity - * part of the filter gives higher influence for pixels with intensities - * that are near to the source pixel, and the spatial part of the - * filter gives higher weight to pixels that are near the source pixel. - * This combination smooths in relatively uniform regions, while - * maintaining edges. - * - * The advantage of the appoach of Yang et al is that it is separable, - * so the computation time is linear in the gaussian filter size. - * Furthermore, it is possible to do much of the computation as a reduced - * scale, which gives a good approximation to the full resolution version - * but greatly speeds it up. - * - * The bilateral filtered value at x is: - * - * sum[y in N(x)]: spatial(|y - x|) * range(|I(x) - I(y)|) * I(y) - * I'(x) = -------------------------------------------------------------- - * sum[y in N(x)]: spatial(|y - x|) * range(|I(x) - I(y)|) - * - * where I() is the input image, I'() is the filtered image, N(x) is the - * set of pixels around x in the filter support, and spatial() and range() - * are gaussian functions: - * spatial(x) = exp(-x^2 / (2 * s_s^2)) - * range(x) = exp(-x^2 / (2 * s_r^2)) - * and s_s and s_r and the standard deviations of the two gaussians. - * - * Yang et al use a separable approximation to this, by defining a set - * of related but separable functions J(k,x), that we call Principal - * Bilateral Components (PBC): - * - * sum[y in N(x)]: spatial(|y - x|) * range(|k - I(y)|) * I(y) - * J(k,x) = ----------------------------------------------------------- - * sum[y in N(x)]: spatial(|y - x|) * range(|k - I(y)|) - * - * which are computed quickly for a set of n values k[p], p = 0 ... n-1. - * Then each output pixel is found using a linear interpolation: - * - * I'(x) = (1 - q) * J(k[p],x) + q * J(k[p+1],x) - * - * where J(k[p],x) and J(k[p+1],x) are PBC for which - * k[p] <= I(x) and k[p+1] >= I(x), and - * q = (I(x) - k[p]) / (k[p+1] - k[p]). - * - * We can also subsample I(x), create subsampled versions of J(k,x), - * which are then interpolated between for I'(x). - * - * We generate 'pixsc', by optionally downscaling the input image - * (using area mapping by the factor 'reduction'), and then adding - * a mirrored border to avoid boundary cases. This is then used - * to compute 'ncomps' PBCs. - * - * The 'spatial_stdev' is also downscaled by 'reduction'. The size - * of the 'spatial' array is 4 * (reduced 'spatial_stdev') + 1. - * The size of the 'range' array is 256. - *- */ - - -/*------------------------------------------------------------------------* - * Bilateral filter * - *------------------------------------------------------------------------*/ - -/*! Bilateral filter */ -struct L_Bilateral -{ - struct Pix *pixs; /*!< clone of source pix */ - struct Pix *pixsc; /*!< downscaled pix with mirrored border */ - l_int32 reduction; /*!< 1, 2 or 4x for intermediates */ - l_float32 spatial_stdev; /*!< stdev of spatial gaussian */ - l_float32 range_stdev; /*!< stdev of range gaussian */ - l_float32 *spatial; /*!< 1D gaussian spatial kernel */ - l_float32 *range; /*!< one-sided gaussian range kernel */ - l_int32 minval; /*!< min value in 8 bpp pix */ - l_int32 maxval; /*!< max value in 8 bpp pix */ - l_int32 ncomps; /*!< number of intermediate results */ - l_int32 *nc; /*!< set of k values (size ncomps) */ - l_int32 *kindex; /*!< mapping from intensity to lower k */ - l_float32 *kfract; /*!< mapping from intensity to fract k */ - struct Pixa *pixac; /*!< intermediate result images (PBC) */ - l_uint32 ***lineset; /*!< lineptrs for pixac */ -}; -typedef struct L_Bilateral L_BILATERAL; - - -#endif /* LEPTONICA_BILATERAL_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bilinear.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bilinear.c deleted file mode 100644 index 7336e91b..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bilinear.c +++ /dev/null @@ -1,912 +0,0 @@ -/*====================================================================* - - 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 bilinear.c - *
- * - * Bilinear (4 pt) image transformation using a sampled - * (to nearest integer) transform on each dest point - * PIX *pixBilinearSampledPta() - * PIX *pixBilinearSampled() - * - * Bilinear (4 pt) image transformation using interpolation - * (or area mapping) for anti-aliasing images that are - * 2, 4, or 8 bpp gray, or colormapped, or 32 bpp RGB - * PIX *pixBilinearPta() - * PIX *pixBilinear() - * PIX *pixBilinearPtaColor() - * PIX *pixBilinearColor() - * PIX *pixBilinearPtaGray() - * PIX *pixBilinearGray() - * - * Bilinear transform including alpha (blend) component - * PIX *pixBilinearPtaWithAlpha() - * - * Bilinear coordinate transformation - * l_int32 getBilinearXformCoeffs() - * l_int32 bilinearXformSampledPt() - * l_int32 bilinearXformPt() - * - * A bilinear transform can be specified as a specific functional - * mapping between 4 points in the source and 4 points in the dest. - * It can be used as an approximation to a (nonlinear) projective - * transform, because for small warps it is very similar and - * it is more stable. (Projective transforms have a division - * by a quantity that can get arbitrarily small.) - * - * We give both a bilinear coordinate transformation and - * a bilinear image transformation. - * - * For the former, we ask for the coordinate value (x',y') - * in the transformed space for any point (x,y) in the original - * space. The coefficients of the transformation are found by - * solving 8 simultaneous equations for the 8 coordinates of - * the 4 points in src and dest. The transformation can then - * be used to compute the associated image transform, by - * computing, for each dest pixel, the relevant pixel(s) in - * the source. This can be done either by taking the closest - * src pixel to each transformed dest pixel ("sampling") or - * by doing an interpolation and averaging over 4 source - * pixels with appropriate weightings ("interpolated"). - * - * A typical application would be to remove some of the - * keystoning due to a projective transform in the imaging system. - * - * The bilinear transform is given by specifying two equations: - * - * x' = ax + by + cxy + d - * y' = ex + fy + gxy + h - * - * where the eight coefficients have been computed from four - * sets of these equations, each for two corresponding data pts. - * In practice, once the coefficients are known, we use the - * equations "backwards": for each point (x,y) in the dest image, - * these two equations are used to compute the corresponding point - * (x',y') in the src. That computed point in the src is then used - * to determine the corresponding dest pixel value in one of two ways: - * - * ~ sampling: simply take the value of the src pixel in which this - * point falls - * ~ interpolation: take appropriate linear combinations of the - * four src pixels that this dest pixel would - * overlap, with the coefficients proportional - * to the amount of overlap - * - * For small warp, like rotation, area mapping in the - * interpolation is equivalent to linear interpolation. - * - * Typical relative timing of transforms (sampled = 1.0): - * 8 bpp: sampled 1.0 - * interpolated 1.6 - * 32 bpp: sampled 1.0 - * interpolated 1.8 - * Additionally, the computation time/pixel is nearly the same - * for 8 bpp and 32 bpp, for both sampled and interpolated. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) Brings in either black or white pixels from the boundary. - * (2) Retains colormap, which you can do for a sampled transform.. - * (3) No 3 of the 4 points may be collinear. - * (4) For 8 and 32 bpp pix, better quality is obtained by the - * somewhat slower pixBilinearPta(). See that - * function for relative timings between sampled and interpolated. - *- */ -PIX * -pixBilinearSampledPta(PIX *pixs, - PTA *ptad, - PTA *ptas, - l_int32 incolor) -{ -l_float32 *vc; -PIX *pixd; - - PROCNAME("pixBilinearSampledPta"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!ptas) - return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); - if (!ptad) - return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); - if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) - return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); - if (ptaGetCount(ptas) != 4) - return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); - if (ptaGetCount(ptad) != 4) - return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); - - /* Get backwards transform from dest to src, and apply it */ - getBilinearXformCoeffs(ptad, ptas, &vc); - pixd = pixBilinearSampled(pixs, vc, incolor); - LEPT_FREE(vc); - - return pixd; -} - - -/*! - * \brief pixBilinearSampled() - * - * \param[in] pixs all depths - * \param[in] vc vector of 8 coefficients for bilinear transformation - * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Brings in either black or white pixels from the boundary. - * (2) Retains colormap, which you can do for a sampled transform.. - * (3) For 8 or 32 bpp, much better quality is obtained by the - * somewhat slower pixBilinear(). See that function - * for relative timings between sampled and interpolated. - *- */ -PIX * -pixBilinearSampled(PIX *pixs, - l_float32 *vc, - l_int32 incolor) -{ -l_int32 i, j, w, h, d, x, y, wpls, wpld, color, cmapindex; -l_uint32 val; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixBilinearSampled"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!vc) - return (PIX *)ERROR_PTR("vc not defined", procName, NULL); - if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) - return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32) - return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8 or 16", procName, NULL); - - /* Init all dest pixels to color to be brought in from outside */ - pixd = pixCreateTemplate(pixs); - if ((cmap = pixGetColormap(pixs)) != NULL) { - if (incolor == L_BRING_IN_WHITE) - color = 1; - else - color = 0; - pixcmapAddBlackOrWhite(cmap, color, &cmapindex); - pixSetAllArbitrary(pixd, cmapindex); - } else { - if ((d == 1 && incolor == L_BRING_IN_WHITE) || - (d > 1 && incolor == L_BRING_IN_BLACK)) { - pixClearAll(pixd); - } else { - pixSetAll(pixd); - } - } - - /* Scan over the dest pixels */ - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - bilinearXformSampledPt(vc, j, i, &x, &y); - if (x < 0 || y < 0 || x >=w || y >= h) - continue; - lines = datas + y * wpls; - if (d == 1) { - val = GET_DATA_BIT(lines, x); - SET_DATA_BIT_VAL(lined, j, val); - } else if (d == 8) { - val = GET_DATA_BYTE(lines, x); - SET_DATA_BYTE(lined, j, val); - } else if (d == 32) { - lined[j] = lines[x]; - } else if (d == 2) { - val = GET_DATA_DIBIT(lines, x); - SET_DATA_DIBIT(lined, j, val); - } else if (d == 4) { - val = GET_DATA_QBIT(lines, x); - SET_DATA_QBIT(lined, j, val); - } - } - } - - return pixd; -} - - -/*---------------------------------------------------------------------* - * Interpolated bilinear image transformation * - *---------------------------------------------------------------------*/ -/*! - * \brief pixBilinearPta() - * - * \param[in] pixs all depths; colormap ok - * \param[in] ptad 4 pts of final coordinate space - * \param[in] ptas 4 pts of initial coordinate space - * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Brings in either black or white pixels from the boundary - * (2) Removes any existing colormap, if necessary, before transforming - *- */ -PIX * -pixBilinearPta(PIX *pixs, - PTA *ptad, - PTA *ptas, - l_int32 incolor) -{ -l_int32 d; -l_uint32 colorval; -PIX *pixt1, *pixt2, *pixd; - - PROCNAME("pixBilinearPta"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!ptas) - return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); - if (!ptad) - return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); - if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) - return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); - if (ptaGetCount(ptas) != 4) - return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); - if (ptaGetCount(ptad) != 4) - return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); - - if (pixGetDepth(pixs) == 1) - return pixBilinearSampledPta(pixs, ptad, ptas, incolor); - - /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ - pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - d = pixGetDepth(pixt1); - if (d < 8) - pixt2 = pixConvertTo8(pixt1, FALSE); - else - pixt2 = pixClone(pixt1); - d = pixGetDepth(pixt2); - - /* Compute actual color to bring in from edges */ - colorval = 0; - if (incolor == L_BRING_IN_WHITE) { - if (d == 8) - colorval = 255; - else /* d == 32 */ - colorval = 0xffffff00; - } - - if (d == 8) - pixd = pixBilinearPtaGray(pixt2, ptad, ptas, colorval); - else /* d == 32 */ - pixd = pixBilinearPtaColor(pixt2, ptad, ptas, colorval); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - return pixd; -} - - -/*! - * \brief pixBilinear() - * - * \param[in] pixs all depths; colormap ok - * \param[in] vc vector of 8 coefficients for bilinear transformation - * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Brings in either black or white pixels from the boundary - * (2) Removes any existing colormap, if necessary, before transforming - *- */ -PIX * -pixBilinear(PIX *pixs, - l_float32 *vc, - l_int32 incolor) -{ -l_int32 d; -l_uint32 colorval; -PIX *pixt1, *pixt2, *pixd; - - PROCNAME("pixBilinear"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!vc) - return (PIX *)ERROR_PTR("vc not defined", procName, NULL); - - if (pixGetDepth(pixs) == 1) - return pixBilinearSampled(pixs, vc, incolor); - - /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ - pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - d = pixGetDepth(pixt1); - if (d < 8) - pixt2 = pixConvertTo8(pixt1, FALSE); - else - pixt2 = pixClone(pixt1); - d = pixGetDepth(pixt2); - - /* Compute actual color to bring in from edges */ - colorval = 0; - if (incolor == L_BRING_IN_WHITE) { - if (d == 8) - colorval = 255; - else /* d == 32 */ - colorval = 0xffffff00; - } - - if (d == 8) - pixd = pixBilinearGray(pixt2, vc, colorval); - else /* d == 32 */ - pixd = pixBilinearColor(pixt2, vc, colorval); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - return pixd; -} - - -/*! - * \brief pixBilinearPtaColor() - * - * \param[in] pixs 32 bpp - * \param[in] ptad 4 pts of final coordinate space - * \param[in] ptas 4 pts of initial coordinate space - * \param[in] colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE - * \return pixd, or NULL on error - */ -PIX * -pixBilinearPtaColor(PIX *pixs, - PTA *ptad, - PTA *ptas, - l_uint32 colorval) -{ -l_float32 *vc; -PIX *pixd; - - PROCNAME("pixBilinearPtaColor"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!ptas) - return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); - if (!ptad) - return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); - if (ptaGetCount(ptas) != 4) - return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); - if (ptaGetCount(ptad) != 4) - return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); - - /* Get backwards transform from dest to src, and apply it */ - getBilinearXformCoeffs(ptad, ptas, &vc); - pixd = pixBilinearColor(pixs, vc, colorval); - LEPT_FREE(vc); - - return pixd; -} - - -/*! - * \brief pixBilinearColor() - * - * \param[in] pixs 32 bpp - * \param[in] vc vector of 8 coefficients for bilinear transformation - * \param[in] colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE - * \return pixd, or NULL on error - */ -PIX * -pixBilinearColor(PIX *pixs, - l_float32 *vc, - l_uint32 colorval) -{ -l_int32 i, j, w, h, d, wpls, wpld; -l_uint32 val; -l_uint32 *datas, *datad, *lined; -l_float32 x, y; -PIX *pix1, *pix2, *pixd; - - PROCNAME("pixBilinearColor"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 32) - return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); - if (!vc) - return (PIX *)ERROR_PTR("vc not defined", procName, NULL); - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - pixd = pixCreateTemplate(pixs); - pixSetAllArbitrary(pixd, colorval); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - /* Iterate over destination pixels */ - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - /* Compute float src pixel location corresponding to (i,j) */ - bilinearXformPt(vc, j, i, &x, &y); - linearInterpolatePixelColor(datas, wpls, w, h, x, y, colorval, - &val); - *(lined + j) = val; - } - } - - /* If rgba, transform the pixs alpha channel and insert in pixd */ - if (pixGetSpp(pixs) == 4) { - pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); - pix2 = pixBilinearGray(pix1, vc, 255); /* bring in opaque */ - pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - - return pixd; -} - - -/*! - * \brief pixBilinearPtaGray() - * - * \param[in] pixs 8 bpp - * \param[in] ptad 4 pts of final coordinate space - * \param[in] ptas 4 pts of initial coordinate space - * \param[in] grayval e.g., 0 to bring in BLACK, 255 for WHITE - * \return pixd, or NULL on error - */ -PIX * -pixBilinearPtaGray(PIX *pixs, - PTA *ptad, - PTA *ptas, - l_uint8 grayval) -{ -l_float32 *vc; -PIX *pixd; - - PROCNAME("pixBilinearPtaGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!ptas) - return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); - if (!ptad) - return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); - if (ptaGetCount(ptas) != 4) - return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); - if (ptaGetCount(ptad) != 4) - return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); - - /* Get backwards transform from dest to src, and apply it */ - getBilinearXformCoeffs(ptad, ptas, &vc); - pixd = pixBilinearGray(pixs, vc, grayval); - LEPT_FREE(vc); - - return pixd; -} - - -/*! - * \brief pixBilinearGray() - * - * \param[in] pixs 8 bpp - * \param[in] vc vector of 8 coefficients for bilinear transformation - * \param[in] grayval e.g., 0 to bring in BLACK, 255 for WHITE - * \return pixd, or NULL on error - */ -PIX * -pixBilinearGray(PIX *pixs, - l_float32 *vc, - l_uint8 grayval) -{ -l_int32 i, j, w, h, wpls, wpld, val; -l_uint32 *datas, *datad, *lined; -l_float32 x, y; -PIX *pixd; - - PROCNAME("pixBilinearGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); - if (!vc) - return (PIX *)ERROR_PTR("vc not defined", procName, NULL); - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - pixd = pixCreateTemplate(pixs); - pixSetAllArbitrary(pixd, grayval); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - /* Iterate over destination pixels */ - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - /* Compute float src pixel location corresponding to (i,j) */ - bilinearXformPt(vc, j, i, &x, &y); - linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val); - SET_DATA_BYTE(lined, j, val); - } - } - - return pixd; -} - - -/*-------------------------------------------------------------------------* - * Bilinear transform including alpha (blend) component * - *-------------------------------------------------------------------------*/ -/*! - * \brief pixBilinearPtaWithAlpha() - * - * \param[in] pixs 32 bpp rgb - * \param[in] ptad 4 pts of final coordinate space - * \param[in] ptas 4 pts of initial coordinate space - * \param[in] pixg [optional] 8 bpp, can be null - * \param[in] fract between 0.0 and 1.0, with 0.0 fully transparent - * and 1.0 fully opaque - * \param[in] border of pixels added to capture transformed source pixels - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) The alpha channel is transformed separately from pixs, - * and aligns with it, being fully transparent outside the - * boundary of the transformed pixs. For pixels that are fully - * transparent, a blending function like pixBlendWithGrayMask() - * will give zero weight to corresponding pixels in pixs. - * (2) If %pixg is NULL, it is generated as an alpha layer that is - * partially opaque, using %fract. Otherwise, it is cropped - * to %pixs if required and %fract is ignored. The alpha channel - * in %pixs is never used. - * (3) Colormaps are removed. - * (4) When pixs is transformed, it doesn't matter what color is brought - * in because the alpha channel will be transparent (0) there. - * (5) To avoid losing source pixels in the destination, it may be - * necessary to add a border to the source pix before doing - * the bilinear transformation. This can be any non-negative number. - * (6) The input %ptad and %ptas are in a coordinate space before - * the border is added. Internally, we compensate for this - * before doing the bilinear transform on the image after - * the border is added. - * (7) The default setting for the border values in the alpha channel - * is 0 (transparent) for the outermost ring of pixels and - * (0.5 * fract * 255) for the second ring. When blended over - * a second image, this - * (a) shrinks the visible image to make a clean overlap edge - * with an image below, and - * (b) softens the edges by weakening the aliasing there. - * Use l_setAlphaMaskBorder() to change these values. - *- */ -PIX * -pixBilinearPtaWithAlpha(PIX *pixs, - PTA *ptad, - PTA *ptas, - PIX *pixg, - l_float32 fract, - l_int32 border) -{ -l_int32 ws, hs, d; -PIX *pixd, *pixb1, *pixb2, *pixg2, *pixga; -PTA *ptad2, *ptas2; - - PROCNAME("pixBilinearPtaWithAlpha"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &ws, &hs, &d); - if (d != 32 && pixGetColormap(pixs) == NULL) - return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); - if (pixg && pixGetDepth(pixg) != 8) { - L_WARNING("pixg not 8 bpp; using 'fract' transparent alpha\n", - procName); - pixg = NULL; - } - if (!pixg && (fract < 0.0 || fract > 1.0)) { - L_WARNING("invalid fract; using 1.0 (fully transparent)\n", procName); - fract = 1.0; - } - if (!pixg && fract == 0.0) - L_WARNING("fully opaque alpha; image cannot be blended\n", procName); - if (!ptad) - return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); - if (!ptas) - return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); - - /* Add border; the color doesn't matter */ - pixb1 = pixAddBorder(pixs, border, 0); - - /* Transform the ptr arrays to work on the bordered image */ - ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); - ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); - - /* Do separate bilinear transform of rgb channels of pixs and of pixg */ - pixd = pixBilinearPtaColor(pixb1, ptad2, ptas2, 0); - if (!pixg) { - pixg2 = pixCreate(ws, hs, 8); - if (fract == 1.0) - pixSetAll(pixg2); - else - pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract)); - } else { - pixg2 = pixResizeToMatch(pixg, NULL, ws, hs); - } - if (ws > 10 && hs > 10) { /* see note 7 */ - pixSetBorderRingVal(pixg2, 1, - (l_int32)(255.0 * fract * AlphaMaskBorderVals[0])); - pixSetBorderRingVal(pixg2, 2, - (l_int32)(255.0 * fract * AlphaMaskBorderVals[1])); - - } - pixb2 = pixAddBorder(pixg2, border, 0); /* must be black border */ - pixga = pixBilinearPtaGray(pixb2, ptad2, ptas2, 0); - pixSetRGBComponent(pixd, pixga, L_ALPHA_CHANNEL); - pixSetSpp(pixd, 4); - - pixDestroy(&pixg2); - pixDestroy(&pixb1); - pixDestroy(&pixb2); - pixDestroy(&pixga); - ptaDestroy(&ptad2); - ptaDestroy(&ptas2); - return pixd; -} - - -/*-------------------------------------------------------------* - * Bilinear coordinate transformation * - *-------------------------------------------------------------*/ -/*! - * \brief getBilinearXformCoeffs() - * - * \param[in] ptas source 4 points; unprimed - * \param[in] ptad transformed 4 points; primed - * \param[out] pvc vector of coefficients of transform - * \return 0 if OK; 1 on error - * - *
- * We have a set of 8 equations, describing the bilinear - * transformation that takes 4 points ptas into 4 other - * points ptad. These equations are: - * - * x1' = c[0]*x1 + c[1]*y1 + c[2]*x1*y1 + c[3] - * y1' = c[4]*x1 + c[5]*y1 + c[6]*x1*y1 + c[7] - * x2' = c[0]*x2 + c[1]*y2 + c[2]*x2*y2 + c[3] - * y2' = c[4]*x2 + c[5]*y2 + c[6]*x2*y2 + c[7] - * x3' = c[0]*x3 + c[1]*y3 + c[2]*x3*y3 + c[3] - * y3' = c[4]*x3 + c[5]*y3 + c[6]*x3*y3 + c[7] - * x4' = c[0]*x4 + c[1]*y4 + c[2]*x4*y4 + c[3] - * y4' = c[4]*x4 + c[5]*y4 + c[6]*x4*y4 + c[7] - * - * This can be represented as - * - * AC = B - * - * where B and C are column vectors - * - * B = [ x1' y1' x2' y2' x3' y3' x4' y4' ] - * C = [ c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] ] - * - * and A is the 8x8 matrix - * - * x1 y1 x1*y1 1 0 0 0 0 - * 0 0 0 0 x1 y1 x1*y1 1 - * x2 y2 x2*y2 1 0 0 0 0 - * 0 0 0 0 x2 y2 x2*y2 1 - * x3 y3 x3*y3 1 0 0 0 0 - * 0 0 0 0 x3 y3 x3*y3 1 - * x4 y4 x4*y4 1 0 0 0 0 - * 0 0 0 0 x4 y4 x4*y4 1 - * - * These eight equations are solved here for the coefficients C. - * - * These eight coefficients can then be used to find the mapping - * x,y) --> (x',y': - * - * x' = c[0]x + c[1]y + c[2]xy + c[3] - * y' = c[4]x + c[5]y + c[6]xy + c[7] - * - * that are implemented in bilinearXformSampledPt and - * bilinearXFormPt. - *- */ -l_ok -getBilinearXformCoeffs(PTA *ptas, - PTA *ptad, - l_float32 **pvc) -{ -l_int32 i; -l_float32 x1, y1, x2, y2, x3, y3, x4, y4; -l_float32 *b; /* rhs vector of primed coords X'; coeffs returned in *pvc */ -l_float32 *a[8]; /* 8x8 matrix A */ - - PROCNAME("getBilinearXformCoeffs"); - - if (!ptas) - return ERROR_INT("ptas not defined", procName, 1); - if (!ptad) - return ERROR_INT("ptad not defined", procName, 1); - if (!pvc) - return ERROR_INT("&vc not defined", procName, 1); - - b = (l_float32 *)LEPT_CALLOC(8, sizeof(l_float32)); - *pvc = b; - ptaGetPt(ptas, 0, &x1, &y1); - ptaGetPt(ptas, 1, &x2, &y2); - ptaGetPt(ptas, 2, &x3, &y3); - ptaGetPt(ptas, 3, &x4, &y4); - ptaGetPt(ptad, 0, &b[0], &b[1]); - ptaGetPt(ptad, 1, &b[2], &b[3]); - ptaGetPt(ptad, 2, &b[4], &b[5]); - ptaGetPt(ptad, 3, &b[6], &b[7]); - - for (i = 0; i < 8; i++) - a[i] = (l_float32 *)LEPT_CALLOC(8, sizeof(l_float32)); - a[0][0] = x1; - a[0][1] = y1; - a[0][2] = x1 * y1; - a[0][3] = 1.; - a[1][4] = x1; - a[1][5] = y1; - a[1][6] = x1 * y1; - a[1][7] = 1.; - a[2][0] = x2; - a[2][1] = y2; - a[2][2] = x2 * y2; - a[2][3] = 1.; - a[3][4] = x2; - a[3][5] = y2; - a[3][6] = x2 * y2; - a[3][7] = 1.; - a[4][0] = x3; - a[4][1] = y3; - a[4][2] = x3 * y3; - a[4][3] = 1.; - a[5][4] = x3; - a[5][5] = y3; - a[5][6] = x3 * y3; - a[5][7] = 1.; - a[6][0] = x4; - a[6][1] = y4; - a[6][2] = x4 * y4; - a[6][3] = 1.; - a[7][4] = x4; - a[7][5] = y4; - a[7][6] = x4 * y4; - a[7][7] = 1.; - - gaussjordan(a, b, 8); - - for (i = 0; i < 8; i++) - LEPT_FREE(a[i]); - return 0; -} - - -/*! - * \brief bilinearXformSampledPt() - * - * \param[in] vc vector of 8 coefficients - * \param[in] x, y initial point - * \param[out] pxp, pyp transformed point - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This finds the nearest pixel coordinates of the transformed point. - * (2) It does not check ptrs for returned data! - *- */ -l_ok -bilinearXformSampledPt(l_float32 *vc, - l_int32 x, - l_int32 y, - l_int32 *pxp, - l_int32 *pyp) -{ - - PROCNAME("bilinearXformSampledPt"); - - if (!vc) - return ERROR_INT("vc not defined", procName, 1); - - *pxp = (l_int32)(vc[0] * x + vc[1] * y + vc[2] * x * y + vc[3] + 0.5); - *pyp = (l_int32)(vc[4] * x + vc[5] * y + vc[6] * x * y + vc[7] + 0.5); - return 0; -} - - -/*! - * \brief bilinearXformPt() - * - * \param[in] vc vector of 8 coefficients - * \param[in] x, y initial point - * \param[out] pxp, pyp transformed point - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This computes the floating point location of the transformed point. - * (2) It does not check ptrs for returned data! - *- */ -l_ok -bilinearXformPt(l_float32 *vc, - l_int32 x, - l_int32 y, - l_float32 *pxp, - l_float32 *pyp) -{ - PROCNAME("bilinearXformPt"); - - if (!vc) - return ERROR_INT("vc not defined", procName, 1); - - *pxp = vc[0] * x + vc[1] * y + vc[2] * x * y + vc[3]; - *pyp = vc[4] * x + vc[5] * y + vc[6] * x * y + vc[7]; - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/binarize.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/binarize.c deleted file mode 100644 index c21c1634..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/binarize.c +++ /dev/null @@ -1,1103 +0,0 @@ -/*====================================================================* - - 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 binarize.c - *
- * - * =================================================================== - * Image binarization algorithms are found in: - * grayquant.c: standard, simple, general grayscale quantization - * adaptmap.c: local adaptive; mostly gray-to-gray in preparation - * for binarization - * binarize.c: special binarization methods, locally adaptive and - * global. - * =================================================================== - * - * Adaptive Otsu-based thresholding - * l_int32 pixOtsuAdaptiveThreshold() 8 bpp - * - * Otsu thresholding on adaptive background normalization - * PIX *pixOtsuThreshOnBackgroundNorm() 8 bpp - * - * Masking and Otsu estimate on adaptive background normalization - * PIX *pixMaskedThreshOnBackgroundNorm() 8 bpp - * - * Sauvola local thresholding - * l_int32 pixSauvolaBinarizeTiled() - * l_int32 pixSauvolaBinarize() - * static PIX *pixSauvolaGetThreshold() - * static PIX *pixApplyLocalThreshold(); - * - * Global thresholding using connected components - * PIX *pixThresholdByConnComp() - * - * Global thresholding by histogram - * PIX *pixThresholdByHisto() - * - * Notes: - * (1) pixOtsuAdaptiveThreshold() computes a global threshold over each - * tile and performs the threshold operation, resulting in a - * binary image for each tile. These are stitched into the - * final result. - * (2) pixOtsuThreshOnBackgroundNorm() and - * pixMaskedThreshOnBackgroundNorm() are binarization functions - * that use background normalization with other techniques. - * (3) Sauvola binarization computes a local threshold based on - * the local average and square average. It takes two constants: - * the window size for the measurement at each pixel and a - * parameter that determines the amount of normalized local - * standard deviation to subtract from the local average value. - * (4) pixThresholdByConnComp() uses the numbers of 4 and 8 connected - * components at different thresholding to determine if a - * global threshold can be used (for text or line-art) and the - * value it should have. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) The Otsu method finds a single global threshold for an image. - * This function allows a locally adapted threshold to be - * found for each tile into which the image is broken up. - * (2) The array of threshold values, one for each tile, constitutes - * a highly downscaled image. This array is optionally - * smoothed using a convolution. The full width and height of the - * convolution kernel are (2 * %smoothx + 1) and (2 * %smoothy + 1). - * (3) The minimum tile dimension allowed is 16. If such small - * tiles are used, it is recommended to use smoothing, because - * without smoothing, each small tile determines the splitting - * threshold independently. A tile that is entirely in the - * image bg will then hallucinate fg, resulting in a very noisy - * binarization. The smoothing should be large enough that no - * tile is only influenced by one type (fg or bg) of pixels, - * because it will force a split of its pixels. - * (4) To get a single global threshold for the entire image, use - * input values of %sx and %sy that are larger than the image. - * For this situation, the smoothing parameters are ignored. - * (5) The threshold values partition the image pixels into two classes: - * one whose values are less than the threshold and another - * whose values are greater than or equal to the threshold. - * This is the same use of 'threshold' as in pixThresholdToBinary(). - * (6) The scorefract is the fraction of the maximum Otsu score, which - * is used to determine the range over which the histogram minimum - * is searched. See numaSplitDistribution() for details on the - * underlying method of choosing a threshold. - * (7) This uses enables a modified version of the Otsu criterion for - * splitting the distribution of pixels in each tile into a - * fg and bg part. The modification consists of searching for - * a minimum in the histogram over a range of pixel values where - * the Otsu score is within a defined fraction, %scorefract, - * of the max score. To get the original Otsu algorithm, set - * %scorefract == 0. - * (8) N.B. This method is NOT recommended for images with weak text - * and significant background noise, such as bleedthrough, because - * of the problem noted in (3) above for tiling. Use Sauvola. - *- */ -l_ok -pixOtsuAdaptiveThreshold(PIX *pixs, - l_int32 sx, - l_int32 sy, - l_int32 smoothx, - l_int32 smoothy, - l_float32 scorefract, - PIX **ppixth, - PIX **ppixd) -{ -l_int32 w, h, nx, ny, i, j, thresh; -l_uint32 val; -PIX *pixt, *pixb, *pixthresh, *pixth, *pixd; -PIXTILING *pt; - - PROCNAME("pixOtsuAdaptiveThreshold"); - - if (!ppixth && !ppixd) - return ERROR_INT("neither &pixth nor &pixd defined", procName, 1); - if (ppixth) *ppixth = NULL; - if (ppixd) *ppixd = NULL; - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); - if (sx < 16 || sy < 16) - return ERROR_INT("sx and sy must be >= 16", procName, 1); - - /* Compute the threshold array for the tiles */ - pixGetDimensions(pixs, &w, &h, NULL); - nx = L_MAX(1, w / sx); - ny = L_MAX(1, h / sy); - smoothx = L_MIN(smoothx, (nx - 1) / 2); - smoothy = L_MIN(smoothy, (ny - 1) / 2); - pt = pixTilingCreate(pixs, nx, ny, 0, 0, 0, 0); - pixthresh = pixCreate(nx, ny, 8); - for (i = 0; i < ny; i++) { - for (j = 0; j < nx; j++) { - pixt = pixTilingGetTile(pt, i, j); - pixSplitDistributionFgBg(pixt, scorefract, 1, &thresh, - NULL, NULL, NULL); - pixSetPixel(pixthresh, j, i, thresh); /* see note (4) */ - pixDestroy(&pixt); - } - } - - /* Optionally smooth the threshold array */ - if (smoothx > 0 || smoothy > 0) - pixth = pixBlockconv(pixthresh, smoothx, smoothy); - else - pixth = pixClone(pixthresh); - pixDestroy(&pixthresh); - - /* Optionally apply the threshold array to binarize pixs */ - if (ppixd) { - pixd = pixCreate(w, h, 1); - pixCopyResolution(pixd, pixs); - for (i = 0; i < ny; i++) { - for (j = 0; j < nx; j++) { - pixt = pixTilingGetTile(pt, i, j); - pixGetPixel(pixth, j, i, &val); - pixb = pixThresholdToBinary(pixt, val); - pixTilingPaintTile(pixd, i, j, pixb, pt); - pixDestroy(&pixt); - pixDestroy(&pixb); - } - } - *ppixd = pixd; - } - - if (ppixth) - *ppixth = pixth; - else - pixDestroy(&pixth); - - pixTilingDestroy(&pt); - return 0; -} - - -/*------------------------------------------------------------------* - * Otsu thresholding on adaptive background normalization * - *------------------------------------------------------------------*/ -/*! - * \brief pixOtsuThreshOnBackgroundNorm() - * - * \param[in] pixs 8 bpp grayscale; not colormapped - * \param[in] pixim [optional] 1 bpp 'image' mask; can be null - * \param[in] sx, sy tile size in pixels - * \param[in] thresh threshold for determining foreground - * \param[in] mincount min threshold on counts in a tile - * \param[in] bgval target bg val; typ. > 128 - * \param[in] smoothx half-width of block convolution kernel width - * \param[in] smoothy half-width of block convolution kernel height - * \param[in] scorefract fraction of the max Otsu score; typ. 0.1 - * \param[out] pthresh [optional] threshold value that was - * used on the normalized image - * \return pixd 1 bpp thresholded image, or NULL on error - * - *
- * Notes: - * (1) This does background normalization followed by Otsu - * thresholding. Otsu binarization attempts to split the - * image into two roughly equal sets of pixels, and it does - * a very poor job when there are large amounts of dark - * background. By doing a background normalization first, - * to get the background near 255, we remove this problem. - * Then we use a modified Otsu to estimate the best global - * threshold on the normalized image. - * (2) See pixBackgroundNorm() for meaning and typical values - * of input parameters. For a start, you can try: - * sx, sy = 10, 15 - * thresh = 100 - * mincount = 50 - * bgval = 255 - * smoothx, smoothy = 2 - *- */ -PIX * -pixOtsuThreshOnBackgroundNorm(PIX *pixs, - PIX *pixim, - l_int32 sx, - l_int32 sy, - l_int32 thresh, - l_int32 mincount, - l_int32 bgval, - l_int32 smoothx, - l_int32 smoothy, - l_float32 scorefract, - l_int32 *pthresh) -{ -l_int32 w, h; -l_uint32 val; -PIX *pixn, *pixt, *pixd; - - PROCNAME("pixOtsuThreshOnBackgroundNorm"); - - if (pthresh) *pthresh = 0; - if (!pixs || pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL); - if (sx < 4 || sy < 4) - return (PIX *)ERROR_PTR("sx and sy must be >= 4", procName, NULL); - if (mincount > sx * sy) { - L_WARNING("mincount too large for tile size\n", procName); - mincount = (sx * sy) / 3; - } - - pixn = pixBackgroundNorm(pixs, pixim, NULL, sx, sy, thresh, - mincount, bgval, smoothx, smoothy); - if (!pixn) - return (PIX *)ERROR_PTR("pixn not made", procName, NULL); - - /* Just use 1 tile for a global threshold, which is stored - * as a single pixel in pixt. */ - pixGetDimensions(pixn, &w, &h, NULL); - pixOtsuAdaptiveThreshold(pixn, w, h, 0, 0, scorefract, &pixt, &pixd); - pixDestroy(&pixn); - - if (pixt && pthresh) { - pixGetPixel(pixt, 0, 0, &val); - *pthresh = val; - } - pixDestroy(&pixt); - - if (!pixd) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - else - return pixd; -} - - - -/*----------------------------------------------------------------------* - * Masking and Otsu estimate on adaptive background normalization * - *----------------------------------------------------------------------*/ -/*! - * \brief pixMaskedThreshOnBackgroundNorm() - * - * \param[in] pixs 8 bpp grayscale; not colormapped - * \param[in] pixim [optional] 1 bpp 'image' mask; can be null - * \param[in] sx, sy tile size in pixels - * \param[in] thresh threshold for determining foreground - * \param[in] mincount min threshold on counts in a tile - * \param[in] smoothx half-width of block convolution kernel width - * \param[in] smoothy half-width of block convolution kernel height - * \param[in] scorefract fraction of the max Otsu score; typ. ~ 0.1 - * \param[out] pthresh [optional] threshold value that was - * used on the normalized image - * \return pixd 1 bpp thresholded image, or NULL on error - * - *
- * Notes: - * (1) This begins with a standard background normalization. - * Additionally, there is a flexible background norm, that - * will adapt to a rapidly varying background, and this - * puts white pixels in the background near regions with - * significant foreground. The white pixels are turned into - * a 1 bpp selection mask by binarization followed by dilation. - * Otsu thresholding is performed on the input image to get an - * estimate of the threshold in the non-mask regions. - * The background normalized image is thresholded with two - * different values, and the result is combined using - * the selection mask. - * (2) Note that the numbers 255 (for bgval target) and 190 (for - * thresholding on pixn) are tied together, and explicitly - * defined in this function. - * (3) See pixBackgroundNorm() for meaning and typical values - * of input parameters. For a start, you can try: - * sx, sy = 10, 15 - * thresh = 100 - * mincount = 50 - * smoothx, smoothy = 2 - *- */ -PIX * -pixMaskedThreshOnBackgroundNorm(PIX *pixs, - PIX *pixim, - l_int32 sx, - l_int32 sy, - l_int32 thresh, - l_int32 mincount, - l_int32 smoothx, - l_int32 smoothy, - l_float32 scorefract, - l_int32 *pthresh) -{ -l_int32 w, h, highthresh; -l_uint32 val; -PIX *pixn, *pixm, *pixd, *pix1, *pix2, *pix3, *pix4; - - PROCNAME("pixMaskedThreshOnBackgroundNorm"); - - if (pthresh) *pthresh = 0; - if (!pixs || pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL); - if (sx < 4 || sy < 4) - return (PIX *)ERROR_PTR("sx and sy must be >= 4", procName, NULL); - if (mincount > sx * sy) { - L_WARNING("mincount too large for tile size\n", procName); - mincount = (sx * sy) / 3; - } - - /* Standard background normalization */ - pixn = pixBackgroundNorm(pixs, pixim, NULL, sx, sy, thresh, - mincount, 255, smoothx, smoothy); - if (!pixn) - return (PIX *)ERROR_PTR("pixn not made", procName, NULL); - - /* Special background normalization for adaptation to quickly - * varying background. Threshold on the very light parts, - * which tend to be near significant edges, and dilate to - * form a mask over regions that are typically text. The - * dilation size is chosen to cover the text completely, - * except for very thick fonts. */ - pix1 = pixBackgroundNormFlex(pixs, 7, 7, 1, 1, 20); - pix2 = pixThresholdToBinary(pix1, 240); - pixInvert(pix2, pix2); - pixm = pixMorphSequence(pix2, "d21.21", 0); - pixDestroy(&pix1); - pixDestroy(&pix2); - - /* Use Otsu to get a global threshold estimate for the image, - * which is stored as a single pixel in pix3. */ - pixGetDimensions(pixs, &w, &h, NULL); - pixOtsuAdaptiveThreshold(pixs, w, h, 0, 0, scorefract, &pix3, NULL); - pixGetPixel(pix3, 0, 0, &val); - if (pthresh) *pthresh = val; - pixDestroy(&pix3); - - /* Threshold the background normalized images differentially, - * using a high value correlated with the background normalization - * for the part of the image under the mask (i.e., near the - * darker, thicker foreground), and a value that depends on the Otsu - * threshold for the rest of the image. This gives a solid - * (high) thresholding for the foreground parts of the image, - * while allowing the background and light foreground to be - * reasonably well cleaned using a threshold adapted to the - * input image. */ - highthresh = L_MIN(256, val + 30); - pixd = pixThresholdToBinary(pixn, highthresh); /* for bg and light fg */ - pix4 = pixThresholdToBinary(pixn, 190); /* for heavier fg */ - pixCombineMasked(pixd, pix4, pixm); - pixDestroy(&pix4); - pixDestroy(&pixm); - pixDestroy(&pixn); - - if (!pixd) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - else - return pixd; -} - - -/*----------------------------------------------------------------------* - * Sauvola binarization * - *----------------------------------------------------------------------*/ -/*! - * \brief pixSauvolaBinarizeTiled() - * - * \param[in] pixs 8 bpp grayscale, not colormapped - * \param[in] whsize window half-width for measuring local statistics - * \param[in] factor factor for reducing threshold due to variance; >= 0 - * \param[in] nx, ny subdivision into tiles; >= 1 - * \param[out] ppixth [optional] Sauvola threshold values - * \param[out] ppixd [optional] 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_ok -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("tile width too small; nx reduced to %d\n", procName, nx); - } - if (yrat < whsize + 2) { - ny = h / (whsize + 2); - L_WARNING("tile height too small; ny reduced to %d\n", 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; -} - - -/*! - * \brief pixSauvolaBinarize() - * - * \param[in] pixs 8 bpp grayscale; not colormapped - * \param[in] whsize window half-width for measuring local statistics - * \param[in] factor factor for reducing threshold due to variance; >= 0 - * \param[in] addborder 1 to add border of width (%whsize + 1) on all sides - * \param[out] ppixm [optional] local mean values - * \param[out] ppixsd [optional] local standard deviation values - * \param[out] ppixth [optional] threshold values - * \param[out] ppixd [optional] 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) The local statistics, measured over the window, are the - * average and standard deviation. - * (3) The measurements of the mean and standard deviation are - * performed inside a border of (%whsize + 1) pixels. If pixs does - * not have these added border pixels, use %addborder = 1 to add - * it here; otherwise use %addborder = 0. - * (4) The Sauvola threshold is determined from the formula: - * t = m * (1 - k * (1 - s / 128)) - * where: - * t = local threshold - * m = local mean - * k = %factor (>= 0) [ typ. 0.35 ] - * s = local standard deviation, which is maximized at - * 127.5 when half the samples are 0 and half are 255. - * (5) The basic idea of Niblack and Sauvola binarization is that - * the local threshold should be less than the median value, - * and the larger the variance, the closer to the median - * it should be chosen. Typical values for k are between - * 0.2 and 0.5. - *- */ -l_ok -pixSauvolaBinarize(PIX *pixs, - l_int32 whsize, - l_float32 factor, - l_int32 addborder, - PIX **ppixm, - PIX **ppixsd, - PIX **ppixth, - PIX **ppixd) -{ -l_int32 w, h; -PIX *pixg, *pixsc, *pixm, *pixms, *pixth, *pixd; - - PROCNAME("pixSauvolaBinarize"); - - if (ppixm) *ppixm = NULL; - if (ppixsd) *ppixsd = NULL; - if (ppixth) *ppixth = NULL; - if (ppixd) *ppixd = NULL; - if (!ppixm && !ppixsd && !ppixth && !ppixd) - return ERROR_INT("no outputs", procName, 1); - 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 (addborder) { - pixg = pixAddMirroredBorder(pixs, whsize + 1, whsize + 1, - whsize + 1, whsize + 1); - pixsc = pixClone(pixs); - } else { - pixg = pixClone(pixs); - pixsc = pixRemoveBorder(pixs, whsize + 1); - } - if (!pixg || !pixsc) - return ERROR_INT("pixg and pixsc not made", procName, 1); - - /* All these functions strip off the border pixels. */ - if (ppixm || ppixth || ppixd) - pixm = pixWindowedMean(pixg, whsize, whsize, 1, 1); - if (ppixsd || ppixth || ppixd) - pixms = pixWindowedMeanSquare(pixg, whsize, whsize, 1); - if (ppixth || ppixd) - pixth = pixSauvolaGetThreshold(pixm, pixms, factor, ppixsd); - if (ppixd) { - pixd = pixApplyLocalThreshold(pixsc, pixth); - pixCopyResolution(pixd, pixs); - } - - if (ppixm) - *ppixm = pixm; - else - pixDestroy(&pixm); - pixDestroy(&pixms); - if (ppixth) - *ppixth = pixth; - else - pixDestroy(&pixth); - if (ppixd) - *ppixd = pixd; - pixDestroy(&pixg); - pixDestroy(&pixsc); - return 0; -} - - -/*! - * \brief pixSauvolaGetThreshold() - * - * \param[in] pixm 8 bpp grayscale; not colormapped - * \param[in] pixms 32 bpp - * \param[in] factor factor for reducing threshold due to variance; >= 0 - * \param[out] ppixsd [optional] local standard deviation - * \return pixd 8 bpp, sauvola threshold values, or NULL on error - * - *
- * Notes: - * (1) The Sauvola threshold is determined from the formula: - * t = m * (1 - k * (1 - s / 128)) - * where: - * t = local threshold - * m = local mean - * k = %factor (>= 0) [ typ. 0.35 ] - * s = local standard deviation, which is maximized at - * 127.5 when half the samples are 0 and half are 255. - * (2) See pixSauvolaBinarize() for other details. - * (3) Important definitions and relations for computing averages: - * v == pixel value - * E(p) == expected value of p == average of p over some pixel set - * S(v) == square of v == v * v - * mv == E(v) == expected pixel value == mean value - * ms == E(S(v)) == expected square of pixel values - * == mean square value - * var == variance == expected square of deviation from mean - * == E(S(v - mv)) = E(S(v) - 2 * S(v * mv) + S(mv)) - * = E(S(v)) - S(mv) - * = ms - mv * mv - * s == standard deviation = sqrt(var) - * So for evaluating the standard deviation in the Sauvola - * threshold, we take - * s = sqrt(ms - mv * mv) - *- */ -static PIX * -pixSauvolaGetThreshold(PIX *pixm, - PIX *pixms, - l_float32 factor, - PIX **ppixsd) -{ -l_int32 i, j, w, h, tabsize, wplm, wplms, wplsd, wpld, usetab; -l_int32 mv, ms, var, thresh; -l_uint32 *datam, *datams, *datasd, *datad; -l_uint32 *linem, *linems, *linesd, *lined; -l_float32 sd; -l_float32 *tab; /* of 2^16 square roots */ -PIX *pixsd, *pixd; - - PROCNAME("pixSauvolaGetThreshold"); - - if (ppixsd) *ppixsd = NULL; - if (!pixm || pixGetDepth(pixm) != 8) - return (PIX *)ERROR_PTR("pixm undefined or not 8 bpp", procName, NULL); - if (pixGetColormap(pixm)) - return (PIX *)ERROR_PTR("pixm is colormapped", procName, NULL); - if (!pixms || pixGetDepth(pixms) != 32) - return (PIX *)ERROR_PTR("pixms undefined or not 32 bpp", - procName, NULL); - if (factor < 0.0) - return (PIX *)ERROR_PTR("factor must be >= 0", procName, NULL); - - /* Only make a table of 2^16 square roots if there - * are enough pixels to justify it. */ - pixGetDimensions(pixm, &w, &h, NULL); - usetab = (w * h > 100000) ? 1 : 0; - if (usetab) { - tabsize = 1 << 16; - tab = (l_float32 *)LEPT_CALLOC(tabsize, sizeof(l_float32)); - for (i = 0; i < tabsize; i++) - tab[i] = sqrtf((l_float32)i); - } - - pixd = pixCreate(w, h, 8); - if (ppixsd) { - pixsd = pixCreate(w, h, 8); - *ppixsd = pixsd; - } - datam = pixGetData(pixm); - datams = pixGetData(pixms); - if (ppixsd) datasd = pixGetData(pixsd); - datad = pixGetData(pixd); - wplm = pixGetWpl(pixm); - wplms = pixGetWpl(pixms); - if (ppixsd) wplsd = pixGetWpl(pixsd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - linem = datam + i * wplm; - linems = datams + i * wplms; - if (ppixsd) linesd = datasd + i * wplsd; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - mv = GET_DATA_BYTE(linem, j); - ms = linems[j]; - var = ms - mv * mv; - if (usetab) - sd = tab[var]; - else - sd = sqrtf((l_float32)var); - if (ppixsd) SET_DATA_BYTE(linesd, j, (l_int32)sd); - thresh = (l_int32)(mv * (1.0 - factor * (1.0 - sd / 128.))); - SET_DATA_BYTE(lined, j, thresh); - } - } - - if (usetab) LEPT_FREE(tab); - return pixd; -} - - -/*! - * \brief pixApplyLocalThreshold() - * - * \param[in] pixs 8 bpp grayscale; not colormapped - * \param[in] pixth 8 bpp array of local thresholds - * \return pixd 1 bpp, thresholded image, or NULL on error - */ -static PIX * -pixApplyLocalThreshold(PIX *pixs, - PIX *pixth) -{ -l_int32 i, j, w, h, wpls, wplt, wpld, vals, valt; -l_uint32 *datas, *datat, *datad, *lines, *linet, *lined; -PIX *pixd; - - PROCNAME("pixApplyLocalThreshold"); - - if (!pixs || pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs is colormapped", procName, NULL); - if (!pixth || pixGetDepth(pixth) != 8) - return (PIX *)ERROR_PTR("pixth undefined or not 8 bpp", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - pixd = pixCreate(w, h, 1); - datas = pixGetData(pixs); - datat = pixGetData(pixth); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wplt = pixGetWpl(pixth); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - linet = datat + i * wplt; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - vals = GET_DATA_BYTE(lines, j); - valt = GET_DATA_BYTE(linet, j); - if (vals < valt) - SET_DATA_BIT(lined, j); - } - } - - return pixd; -} - - -/*----------------------------------------------------------------------* - * Global thresholding using connected components * - *----------------------------------------------------------------------*/ -/*! - * \brief pixThresholdByConnComp() - * - * \param[in] pixs depth > 1, colormap OK - * \param[in] pixm [optional] 1 bpp mask giving region to ignore - * by setting pixels to white; use NULL if no mask - * \param[in] start, end, incr binarization threshold levels to test - * \param[in] thresh48 threshold on normalized difference between the - * numbers of 4 and 8 connected components - * \param[in] threshdiff threshold on normalized difference between the - * number of 4 cc at successive iterations - * \param[out] pglobthresh [optional] best global threshold; 0 - * if no threshold is found - * \param[out] ppixd [optional] image thresholded to binary, or - * null if no threshold is found - * \param[in] debugflag 1 for plotted results - * \return 0 if OK, 1 on error or if no threshold is found - * - *
- * Notes: - * (1) This finds a global threshold based on connected components. - * Although slow, it is reasonable to use it in a situation where - * (a) the background in the image is relatively uniform, and - * (b) the result will be fed to an OCR program that accepts 1 bpp - * images and works best with easily segmented characters. - * The reason for (b) is that this selects a threshold with a - * minimum number of both broken characters and merged characters. - * (2) If the pix has color, it is converted to gray using the - * max component. - * (3) Input 0 to use default values for any of these inputs: - * %start, %end, %incr, %thresh48, %threshdiff. - * (4) This approach can be understood as follows. When the - * binarization threshold is varied, the numbers of c.c. identify - * four regimes: - * (a) For low thresholds, text is broken into small pieces, and - * the number of c.c. is large, with the 4 c.c. significantly - * exceeding the 8 c.c. - * (b) As the threshold rises toward the optimum value, the text - * characters coalesce and there is very little difference - * between the numbers of 4 and 8 c.c, which both go - * through a minimum. - * (c) Above this, the image background gets noisy because some - * pixels are(thresholded to foreground, and the numbers - * of c.c. quickly increase, with the 4 c.c. significantly - * larger than the 8 c.c. - * (d) At even higher thresholds, the image background noise - * coalesces as it becomes mostly foreground, and the - * number of c.c. drops quickly. - * (5) If there is no global threshold that distinguishes foreground - * text from background (e.g., weak text over a background that - * has significant variation and/or bleedthrough), this returns 1, - * which the caller should check. - *- */ -l_ok -pixThresholdByConnComp(PIX *pixs, - PIX *pixm, - l_int32 start, - l_int32 end, - l_int32 incr, - l_float32 thresh48, - l_float32 threshdiff, - l_int32 *pglobthresh, - PIX **ppixd, - l_int32 debugflag) -{ -l_int32 i, thresh, n, n4, n8, mincounts, found, globthresh; -l_float32 count4, count8, firstcount4, prevcount4, diff48, diff4; -GPLOT *gplot; -NUMA *na4, *na8; -PIX *pix1, *pix2, *pix3; - - PROCNAME("pixThresholdByConnComp"); - - if (pglobthresh) *pglobthresh = 0; - if (ppixd) *ppixd = NULL; - if (!pixs || pixGetDepth(pixs) == 1) - return ERROR_INT("pixs undefined or 1 bpp", procName, 1); - if (pixm && pixGetDepth(pixm) != 1) - return ERROR_INT("pixm must be 1 bpp", procName, 1); - - /* Assign default values if requested */ - if (start <= 0) start = 80; - if (end <= 0) end = 200; - if (incr <= 0) incr = 10; - if (thresh48 <= 0.0) thresh48 = 0.01; - if (threshdiff <= 0.0) threshdiff = 0.01; - if (start > end) - return ERROR_INT("invalid start,end", procName, 1); - - /* Make 8 bpp, using the max component if color. */ - if (pixGetColormap(pixs)) - pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - else - pix1 = pixClone(pixs); - if (pixGetDepth(pix1) == 32) - pix2 = pixConvertRGBToGrayMinMax(pix1, L_CHOOSE_MAX); - else - pix2 = pixConvertTo8(pix1, 0); - pixDestroy(&pix1); - - /* Mask out any non-text regions. Do this in-place, because pix2 - * can never be the same pix as pixs. */ - if (pixm) - pixSetMasked(pix2, pixm, 255); - - /* Make sure there are enough components to get a valid signal */ - pix3 = pixConvertTo1(pix2, start); - pixCountConnComp(pix3, 4, &n4); - pixDestroy(&pix3); - mincounts = 500; - if (n4 < mincounts) { - L_INFO("Insufficient component count: %d\n", procName, n4); - pixDestroy(&pix2); - return 1; - } - - /* Compute the c.c. data */ - na4 = numaCreate(0); - na8 = numaCreate(0); - numaSetParameters(na4, start, incr); - numaSetParameters(na8, start, incr); - for (thresh = start, i = 0; thresh <= end; thresh += incr, i++) { - pix3 = pixConvertTo1(pix2, thresh); - pixCountConnComp(pix3, 4, &n4); - pixCountConnComp(pix3, 8, &n8); - numaAddNumber(na4, n4); - numaAddNumber(na8, n8); - pixDestroy(&pix3); - } - if (debugflag) { - lept_mkdir("lept/binarize"); - gplot = gplotCreate("/tmp/lept/binarize", GPLOT_PNG, - "number of cc vs. threshold", - "threshold", "number of cc"); - gplotAddPlot(gplot, NULL, na4, GPLOT_LINES, "plot 4cc"); - gplotAddPlot(gplot, NULL, na8, GPLOT_LINES, "plot 8cc"); - gplotMakeOutput(gplot); - gplotDestroy(&gplot); - } - - n = numaGetCount(na4); - found = FALSE; - for (i = 0; i < n; i++) { - if (i == 0) { - numaGetFValue(na4, i, &firstcount4); - prevcount4 = firstcount4; - } else { - numaGetFValue(na4, i, &count4); - numaGetFValue(na8, i, &count8); - diff48 = (count4 - count8) / firstcount4; - diff4 = L_ABS(prevcount4 - count4) / firstcount4; - if (debugflag) { - lept_stderr("diff48 = %7.3f, diff4 = %7.3f\n", - diff48, diff4); - } - if (diff48 < thresh48 && diff4 < threshdiff) { - found = TRUE; - break; - } - prevcount4 = count4; - } - } - numaDestroy(&na4); - numaDestroy(&na8); - - if (found) { - globthresh = start + i * incr; - if (pglobthresh) *pglobthresh = globthresh; - if (ppixd) { - *ppixd = pixConvertTo1(pix2, globthresh); - pixCopyResolution(*ppixd, pixs); - } - if (debugflag) lept_stderr("global threshold = %d\n", globthresh); - pixDestroy(&pix2); - return 0; - } - - if (debugflag) lept_stderr("no global threshold found\n"); - pixDestroy(&pix2); - return 1; -} - -/*----------------------------------------------------------------------* - * Global thresholding by histogram * - *----------------------------------------------------------------------*/ -/*! - * \brief pixThresholdByHisto() - * - * \param[in] pixs gray 8 bpp, no colormap - * \param[in] factor subsampling factor >= 1 - * \param[in] halfw half of window width for smoothing; - * use 0 for default - * \param[in] delta relative amount to resolve peaks and valleys; - * in (0 ... 1], use 0 for default - * \param[out] pthresh best global threshold; 0 if no threshold is found - * \param[out] ppixd [optional] thresholded 1 bpp pix - * \param[out] ppixhisto [optional] rescaled histogram of gray values - * \return 0 if OK, 1 on error or if no threshold is found - * - *
- * Notes: - * (1) This finds a global threshold. It is best for an image that - * has a fairly well-defined fg and bg. - * (2) If it finds a good threshold and %ppixd is defined, the binarized - * image is returned in &pixd; otherwise it return null. - * (3) Suggest using default values for %half and %delta. - * (4) Returns 0 in %pthresh if it can't find a good threshold. - *- */ -l_ok -pixThresholdByHisto(PIX *pixs, - l_int32 factor, - l_int32 halfw, - l_float32 delta, - l_int32 *pthresh, - PIX **ppixd, - PIX **ppixhisto) -{ -l_int32 i, n; -l_float32 maxval, val1, val2, fract; -NUMA *na1, *na2, *na3, *naloc, *nav; -PIX *pix1; - - PROCNAME("pixThresholdByHisto"); - - if (ppixhisto) *ppixhisto = NULL; - if (ppixd) *ppixd = NULL; - if (!pthresh) - return ERROR_INT("&thresh not defined", procName, 1); - *pthresh = 0; - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs undefined or not 8 bpp", procName, 1); - if (pixGetColormap(pixs)) - return ERROR_INT("pixs has colormap", procName, 1); - if (factor < 1) - return ERROR_INT("sampling must be >= 1", procName, 1); - if (halfw <= 0) halfw = 20; - if (delta <= 0.0) delta = 0.1; - - /* Make a histogram of pixel values where the largest peak - * is normalized to a value of 1.0. */ - na1 = pixGetGrayHistogram(pixs, factor); - na2 = numaWindowedMean(na1, halfw); /* smoothing */ - numaGetMax(na2, &maxval, NULL); - na3 = numaTransform(na2, 0.0, 1.0 / maxval); /* rescale to max of 1.0 */ - numaDestroy(&na1); - numaDestroy(&na2); - - numaFindLocForThreshold(na3, 0, pthresh, &fract); - L_INFO("fractional area under first peak: %5.3f\n", procName, fract); - - if (ppixhisto) { - lept_mkdir("lept/histo"); - gplotSimple1(na3, GPLOT_PNG, "/tmp/lept/histo/histo", NULL); - *ppixhisto = pixRead("/tmp/lept/histo/histo.png"); - } - numaDestroy(&na3); - - if (*pthresh > 0 && ppixd) - *ppixd = pixThresholdToBinary(pixs, *pthresh); - return 0; -} - diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/binexpand.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/binexpand.c deleted file mode 100644 index c7a8c1a1..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/binexpand.c +++ /dev/null @@ -1,306 +0,0 @@ -/*====================================================================* - - 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 binexpand.c - *
- * - * Replicated expansion (integer scaling) - * PIX *pixExpandBinaryReplicate() - * - * Special case: power of 2 replicated expansion - * PIX *pixExpandBinaryPower2() - * - * Expansion tables for power of 2 expansion - * static l_uint16 *makeExpandTab2x() - * static l_uint32 *makeExpandTab4x() - * static l_uint32 *makeExpandTab8x() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * - * Subsampled 2x reduction - * PIX *pixReduceBinary2() - * - * Rank filtered 2x reductions - * PIX *pixReduceRankBinaryCascade() - * PIX *pixReduceRankBinary2() - * - * Permutation table for 2x rank binary reduction - * l_uint8 *makeSubsampleTab2x(void) - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) After folding, the data is in bytes 0 and 2 of the word, - * and the bits in each byte are in the following order - * (with 0 being the leftmost originating pair and 7 being - * the rightmost originating pair): - * 0 4 1 5 2 6 3 7 - * These need to be permuted to - * 0 1 2 3 4 5 6 7 - * which is done with an 8-bit table generated by makeSubsampleTab2x(). - *- */ -PIX * -pixReduceBinary2(PIX *pixs, - l_uint8 *intab) -{ -l_uint8 byte0, byte1; -l_uint8 *tab; -l_uint16 shortd; -l_int32 i, id, j, ws, hs, wpls, wpld, wplsi; -l_uint32 word; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; - - PROCNAME("pixReduceBinary2"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - - pixGetDimensions(pixs, &ws, &hs, NULL); - if (hs <= 1) - return (PIX *)ERROR_PTR("hs must be at least 2", procName, NULL); - wpls = pixGetWpl(pixs); - datas = pixGetData(pixs); - - if ((pixd = pixCreate(ws / 2, hs / 2, 1)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixScaleResolution(pixd, 0.5, 0.5); - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - - tab = (intab) ? intab : makeSubsampleTab2x(); - if (!tab) { - pixDestroy(&pixd); - return (PIX *)ERROR_PTR("tab not made", procName, NULL); - } - - /* e.g., if ws = 65: wd = 32, wpls = 3, wpld = 1 --> trouble */ - wplsi = L_MIN(wpls, 2 * wpld); /* iterate over this number of words */ - - for (i = 0, id = 0; i < hs - 1; i += 2, id++) { - lines = datas + i * wpls; - lined = datad + id * wpld; - for (j = 0; j < wplsi; j++) { - word = *(lines + j); - word = word & 0xaaaaaaaa; /* mask */ - word = word | (word << 7); /* fold; data in bytes 0 & 2 */ - byte0 = word >> 24; - byte1 = (word >> 8) & 0xff; - shortd = (tab[byte0] << 8) | tab[byte1]; - SET_DATA_TWO_BYTES(lined, j, shortd); - } - } - - if (!intab) LEPT_FREE(tab); - return pixd; -} - - -/*------------------------------------------------------------------* - * Rank filtered binary reductions * - *------------------------------------------------------------------*/ -/*! - * \brief pixReduceRankBinaryCascade() - * - * \param[in] pixs 1 bpp - * \param[in] level1 threshold, in the set {0, 1, 2, 3, 4} - * \param[in] level2 threshold, in the set {0, 1, 2, 3, 4} - * \param[in] level3 threshold, in the set {0, 1, 2, 3, 4} - * \param[in] level4 threshold, in the set {0, 1, 2, 3, 4} - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) This performs up to four cascaded 2x rank reductions. - * (2) Use level = 0 to truncate the cascade. - *- */ -PIX * -pixReduceRankBinaryCascade(PIX *pixs, - l_int32 level1, - l_int32 level2, - l_int32 level3, - l_int32 level4) -{ -PIX *pix1, *pix2, *pix3, *pix4; -l_uint8 *tab; - - PROCNAME("pixReduceRankBinaryCascade"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs must be binary", procName, NULL); - if (level1 > 4 || level2 > 4 || level3 > 4 || level4 > 4) - return (PIX *)ERROR_PTR("levels must not exceed 4", procName, NULL); - - if (level1 <= 0) { - L_WARNING("no reduction because level1 not > 0\n", procName); - return pixCopy(NULL, pixs); - } - - if ((tab = makeSubsampleTab2x()) == NULL) - return (PIX *)ERROR_PTR("tab not made", procName, NULL); - - pix1 = pixReduceRankBinary2(pixs, level1, tab); - if (level2 <= 0) { - LEPT_FREE(tab); - return pix1; - } - - pix2 = pixReduceRankBinary2(pix1, level2, tab); - pixDestroy(&pix1); - if (level3 <= 0) { - LEPT_FREE(tab); - return pix2; - } - - pix3 = pixReduceRankBinary2(pix2, level3, tab); - pixDestroy(&pix2); - if (level4 <= 0) { - LEPT_FREE(tab); - return pix3; - } - - pix4 = pixReduceRankBinary2(pix3, level4, tab); - pixDestroy(&pix3); - LEPT_FREE(tab); - return pix4; -} - - -/*! - * \brief pixReduceRankBinary2() - * - * \param[in] pixs 1 bpp - * \param[in] level rank threshold: 1, 2, 3, 4 - * \param[in] intab [optional]; if null, a table is made here - * and destroyed before exit - * \return pixd 1 bpp, 2x rank threshold reduced, or NULL on error - * - *
- * Notes: - * (1) pixd is downscaled by 2x from pixs. - * (2) The rank threshold specifies the minimum number of ON - * pixels in each 2x2 region of pixs that are required to - * set the corresponding pixel ON in pixd. - * (3) Rank filtering is done to the UL corner of each 2x2 pixel block, - * using only logical operations. Then these pixels are chosen - * in the 2x subsampling process, subsampled, as described - * above in pixReduceBinary2(). - *- */ -PIX * -pixReduceRankBinary2(PIX *pixs, - l_int32 level, - l_uint8 *intab) -{ -l_uint8 byte0, byte1; -l_uint8 *tab; -l_uint16 shortd; -l_int32 i, id, j, ws, hs, wpls, wpld, wplsi; -l_uint32 word1, word2, word3, word4; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; - - PROCNAME("pixReduceRankBinary2"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not binary", procName, NULL); - if (level < 1 || level > 4) - return (PIX *)ERROR_PTR("level must be in set {1,2,3,4}", - procName, NULL); - - pixGetDimensions(pixs, &ws, &hs, NULL); - if (hs <= 1) - return (PIX *)ERROR_PTR("hs must be at least 2", procName, NULL); - wpls = pixGetWpl(pixs); - datas = pixGetData(pixs); - - if ((pixd = pixCreate(ws / 2, hs / 2, 1)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixScaleResolution(pixd, 0.5, 0.5); - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - - tab = (intab) ? intab : makeSubsampleTab2x(); - if (!tab) { - pixDestroy(&pixd); - return (PIX *)ERROR_PTR("tab not made", procName, NULL); - } - - /* e.g., if ws = 65: wd = 32, wpls = 3, wpld = 1 --> trouble */ - wplsi = L_MIN(wpls, 2 * wpld); /* iterate over this number of words */ - - switch (level) - { - - case 1: - for (i = 0, id = 0; i < hs - 1; i += 2, id++) { - lines = datas + i * wpls; - lined = datad + id * wpld; - for (j = 0; j < wplsi; j++) { - word1 = *(lines + j); - word2 = *(lines + wpls + j); - - /* OR/OR */ - word2 = word1 | word2; - word2 = word2 | (word2 << 1); - - word2 = word2 & 0xaaaaaaaa; /* mask */ - word1 = word2 | (word2 << 7); /* fold; data in bytes 0 & 2 */ - byte0 = word1 >> 24; - byte1 = (word1 >> 8) & 0xff; - shortd = (tab[byte0] << 8) | tab[byte1]; - SET_DATA_TWO_BYTES(lined, j, shortd); - } - } - break; - - case 2: - for (i = 0, id = 0; i < hs - 1; i += 2, id++) { - lines = datas + i * wpls; - lined = datad + id * wpld; - for (j = 0; j < wplsi; j++) { - word1 = *(lines + j); - word2 = *(lines + wpls + j); - - /* (AND/OR) OR (OR/AND) */ - word3 = word1 & word2; - word3 = word3 | (word3 << 1); - word4 = word1 | word2; - word4 = word4 & (word4 << 1); - word2 = word3 | word4; - - word2 = word2 & 0xaaaaaaaa; /* mask */ - word1 = word2 | (word2 << 7); /* fold; data in bytes 0 & 2 */ - byte0 = word1 >> 24; - byte1 = (word1 >> 8) & 0xff; - shortd = (tab[byte0] << 8) | tab[byte1]; - SET_DATA_TWO_BYTES(lined, j, shortd); - } - } - break; - - case 3: - for (i = 0, id = 0; i < hs - 1; i += 2, id++) { - lines = datas + i * wpls; - lined = datad + id * wpld; - for (j = 0; j < wplsi; j++) { - word1 = *(lines + j); - word2 = *(lines + wpls + j); - - /* (AND/OR) AND (OR/AND) */ - word3 = word1 & word2; - word3 = word3 | (word3 << 1); - word4 = word1 | word2; - word4 = word4 & (word4 << 1); - word2 = word3 & word4; - - word2 = word2 & 0xaaaaaaaa; /* mask */ - word1 = word2 | (word2 << 7); /* fold; data in bytes 0 & 2 */ - byte0 = word1 >> 24; - byte1 = (word1 >> 8) & 0xff; - shortd = (tab[byte0] << 8) | tab[byte1]; - SET_DATA_TWO_BYTES(lined, j, shortd); - } - } - break; - - case 4: - for (i = 0, id = 0; i < hs - 1; i += 2, id++) { - lines = datas + i * wpls; - lined = datad + id * wpld; - for (j = 0; j < wplsi; j++) { - word1 = *(lines + j); - word2 = *(lines + wpls + j); - - /* AND/AND */ - word2 = word1 & word2; - word2 = word2 & (word2 << 1); - - word2 = word2 & 0xaaaaaaaa; /* mask */ - word1 = word2 | (word2 << 7); /* fold; data in bytes 0 & 2 */ - byte0 = word1 >> 24; - byte1 = (word1 >> 8) & 0xff; - shortd = (tab[byte0] << 8) | tab[byte1]; - SET_DATA_TWO_BYTES(lined, j, shortd); - } - } - break; - } - - if (!intab) LEPT_FREE(tab); - return pixd; -} - - -/*! - * \brief makeSubsampleTab2x() - * - * \return tab table of 256 permutations, or NULL on error - * - *
- * Notes: - * Permutation table for 2x rank binary reduction - * This table permutes the bits in a byte, from - * 0 4 1 5 2 6 3 7 - * to - * 0 1 2 3 4 5 6 7 - *- */ -l_uint8 * -makeSubsampleTab2x(void) -{ -l_uint8 *tab; -l_int32 i; - - PROCNAME("makeSubsampleTab2x"); - - if ((tab = (l_uint8 *) LEPT_CALLOC(256, sizeof(l_uint8))) == NULL) - return (l_uint8 *)ERROR_PTR("tab not made", procName, NULL); - - for (i = 0; i < 256; i++) - tab[i] = ((i & 0x01) ) | /* 7 */ - ((i & 0x04) >> 1) | /* 6 */ - ((i & 0x10) >> 2) | /* 5 */ - ((i & 0x40) >> 3) | /* 4 */ - ((i & 0x02) << 3) | /* 3 */ - ((i & 0x08) << 2) | /* 2 */ - ((i & 0x20) << 1) | /* 1 */ - ((i & 0x80) ); /* 0 */ - - return tab; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/blend.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/blend.c deleted file mode 100644 index 1cb79c61..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/blend.c +++ /dev/null @@ -1,2295 +0,0 @@ -/*====================================================================* - - 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 blend.c - *
- * - * Blending two images that are not colormapped - * PIX *pixBlend() - * PIX *pixBlendMask() - * PIX *pixBlendGray() - * PIX *pixBlendGrayInverse() - * PIX *pixBlendColor() - * PIX *pixBlendColorByChannel() - * PIX *pixBlendGrayAdapt() - * static l_int32 blendComponents() - * PIX *pixFadeWithGray() - * PIX *pixBlendHardLight() - * static l_int32 blendHardLightComponents() - * - * Blending two colormapped images - * l_int32 pixBlendCmap() - * - * Blending two images using a third (alpha mask) - * PIX *pixBlendWithGrayMask() - * - * Blending background to a specific color - * PIX *pixBlendBackgroundToColor() - * - * Multiplying by a specific color - * PIX *pixMultiplyByColor() - * - * Rendering with alpha blending over a uniform background - * PIX *pixAlphaBlendUniform() - * - * Adding an alpha layer for blending - * PIX *pixAddAlphaToBlend() - * - * Setting a transparent alpha component over a white background - * PIX *pixSetAlphaOverWhite() - * - * Fading from the edge - * l_int32 pixLinearEdgeFade() - * - * In blending operations a new pix is produced where typically - * a subset of pixels in src1 are changed by the set of pixels - * in src2, when src2 is located in a given position relative - * to src1. This is similar to rasterop, except that the - * blending operations we allow are more complex, and typically - * result in dest pixels that are a linear combination of two - * pixels, such as src1 and its inverse. I find it convenient - * to think of src2 as the "blender" (the one that takes the action) - * and src1 as the "blendee" (the one that changes). - * - * Blending works best when src1 is 8 or 32 bpp. We also allow - * src1 to be colormapped, but the colormap is removed before blending, - * so if src1 is colormapped, we can't allow in-place blending. - * - * Because src2 is typically smaller than src1, we can implement by - * clipping src2 to src1 and then transforming some of the dest - * pixels that are under the support of src2. In practice, we - * do the clipping in the inner pixel loop. For grayscale and - * color src2, we also allow a simple form of transparency, where - * pixels of a particular value in src2 are transparent; for those pixels, - * no blending is done. - * - * The blending functions are categorized by the depth of src2, - * the blender, and not that of src1, the blendee. - * - * ~ If src2 is 1 bpp, we can do one of three things: - * (1) L_BLEND_WITH_INVERSE: Blend a given fraction of src1 with its - * inverse color for those pixels in src2 that are fg (ON), - * and leave the dest pixels unchanged for pixels in src2 that - * are bg (OFF). - * (2) L_BLEND_TO_WHITE: Fade the src1 pixels toward white by a - * given fraction for those pixels in src2 that are fg (ON), - * and leave the dest pixels unchanged for pixels in src2 that - * are bg (OFF). - * (3) L_BLEND_TO_BLACK: Fade the src1 pixels toward black by a - * given fraction for those pixels in src2 that are fg (ON), - * and leave the dest pixels unchanged for pixels in src2 that - * are bg (OFF). - * The blending function is pixBlendMask(). - * - * ~ If src2 is 8 bpp grayscale, we can do one of two things - * (but see pixFadeWithGray() below): - * (1) L_BLEND_GRAY: If src1 is 8 bpp, mix the two values, using - * a fraction of src2 and (1 - fraction) of src1. - * If src1 is 32 bpp (rgb), mix the fraction of src2 with - * each of the color components in src1. - * (2) L_BLEND_GRAY_WITH_INVERSE: Use the grayscale value in src2 - * to determine how much of the inverse of a src1 pixel is - * to be combined with the pixel value. The input fraction - * further acts to scale the change in the src1 pixel. - * The blending function is pixBlendGray(). - * - * ~ If src2 is color, we blend a given fraction of src2 with - * src1. If src1 is 8 bpp, the resulting image is 32 bpp. - * The blending function is pixBlendColor(). - * - * ~ For all three blending functions -- pixBlendMask(), pixBlendGray() - * and pixBlendColor() -- you can apply the blender to the blendee - * either in-place or generating a new pix. For the in-place - * operation, this requires that the depth of the resulting pix - * must equal that of the input pixs1. - * - * ~ We remove colormaps from src1 and src2 before blending. - * Any quantization would have to be done after blending. - * - * We include another function, pixFadeWithGray(), that blends - * a gray or color src1 with a gray src2. It does one of these things: - * (1) L_BLEND_TO_WHITE: Fade the src1 pixels toward white by - * a number times the value in src2. - * (2) L_BLEND_TO_BLACK: Fade the src1 pixels toward black by - * a number times the value in src2. - * - * Also included is a generalization of the so-called "hard light" - * blending: pixBlendHardLight(). We generalize by allowing a fraction < 1.0 - * of the blender to be admixed with the blendee. The standard function - * does full mixing. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This is a simple top-level interface. For more flexibility, - * call directly into pixBlendMask(), etc. - *- */ -PIX * -pixBlend(PIX *pixs1, - PIX *pixs2, - l_int32 x, - l_int32 y, - l_float32 fract) -{ -l_int32 w1, h1, d1, d2; -BOX *box; -PIX *pixc, *pixt, *pixd; - - PROCNAME("pixBlend"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); - - /* check relative depths */ - d1 = pixGetDepth(pixs1); - d2 = pixGetDepth(pixs2); - if (d1 == 1 && d2 > 1) - return (PIX *)ERROR_PTR("mixing gray or color with 1 bpp", - procName, NULL); - - /* remove colormap from pixs2 if necessary */ - pixt = pixRemoveColormap(pixs2, REMOVE_CMAP_BASED_ON_SRC); - d2 = pixGetDepth(pixt); - - /* Check if pixs2 is clipped by its position with respect - * to pixs1; if so, clip it and redefine x and y if necessary. - * This actually isn't necessary, as the specific blending - * functions do the clipping directly in the pixel loop - * over pixs2, but it's included here to show how it can - * easily be done on pixs2 first. */ - pixGetDimensions(pixs1, &w1, &h1, NULL); - box = boxCreate(-x, -y, w1, h1); /* box of pixs1 relative to pixs2 */ - pixc = pixClipRectangle(pixt, box, NULL); - boxDestroy(&box); - if (!pixc) { - L_WARNING("box doesn't overlap pix\n", procName); - pixDestroy(&pixt); - return NULL; - } - x = L_MAX(0, x); - y = L_MAX(0, y); - - if (d2 == 1) { - pixd = pixBlendMask(NULL, pixs1, pixc, x, y, fract, - L_BLEND_WITH_INVERSE); - } else if (d2 == 8) { - pixd = pixBlendGray(NULL, pixs1, pixc, x, y, fract, - L_BLEND_GRAY, 0, 0); - } else { /* d2 == 32 */ - pixd = pixBlendColor(NULL, pixs1, pixc, x, y, fract, 0, 0); - } - - pixDestroy(&pixc); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixBlendMask() - * - * \param[in] pixd [optional]; either NULL or equal to pixs1 for in-place - * \param[in] pixs1 blendee, depth > 1 - * \param[in] pixs2 blender, 1 bpp; typ. smaller in size than pixs1 - * \param[in] x,y origin [UL corner] of pixs2 relative to - * the origin of pixs1; can be < 0 - * \param[in] fract blending fraction - * \param[in] type L_BLEND_WITH_INVERSE, L_BLEND_TO_WHITE, - * L_BLEND_TO_BLACK - * \return pixd if OK; null on error - * - *
- * Notes: - * (1) Clipping of pixs2 to pixs1 is done in the inner pixel loop. - * (2) If pixs1 has a colormap, it is removed. - * (3) For inplace operation (pixs1 not cmapped), call it this way: - * pixBlendMask(pixs1, pixs1, pixs2, ...) - * (4) For generating a new pixd: - * pixd = pixBlendMask(NULL, pixs1, pixs2, ...) - * (5) Only call in-place if pixs1 does not have a colormap. - * (6) Invalid %fract defaults to 0.5 with a warning. - * Invalid %type defaults to L_BLEND_WITH_INVERSE with a warning. - *- */ -PIX * -pixBlendMask(PIX *pixd, - PIX *pixs1, - PIX *pixs2, - l_int32 x, - l_int32 y, - l_float32 fract, - l_int32 type) -{ -l_int32 i, j, d, wc, hc, w, h, wplc; -l_int32 val, rval, gval, bval; -l_uint32 pixval; -l_uint32 *linec, *datac; -PIX *pixc, *pix1, *pix2; - - PROCNAME("pixBlendMask"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); - if (pixGetDepth(pixs1) == 1) - return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, NULL); - if (pixGetDepth(pixs2) != 1) - return (PIX *)ERROR_PTR("pixs2 not 1 bpp", procName, NULL); - if (pixd == pixs1 && pixGetColormap(pixs1)) - return (PIX *)ERROR_PTR("inplace; pixs1 has colormap", procName, NULL); - if (pixd && (pixd != pixs1)) - return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, NULL); - if (fract < 0.0 || fract > 1.0) { - L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName); - fract = 0.5; - } - if (type != L_BLEND_WITH_INVERSE && type != L_BLEND_TO_WHITE && - type != L_BLEND_TO_BLACK) { - L_WARNING("invalid blend type; setting to L_BLEND_WITH_INVERSE\n", - procName); - type = L_BLEND_WITH_INVERSE; - } - - /* If pixd != NULL, we know that it is equal to pixs1 and - * that pixs1 does not have a colormap, so that an in-place operation - * can be done. Otherwise, remove colormap from pixs1 if - * it exists and unpack to at least 8 bpp if necessary, - * to do the blending on a new pix. */ - if (!pixd) { - pix1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); - if (pixGetDepth(pix1) < 8) - pix2 = pixConvertTo8(pix1, FALSE); - else - pix2 = pixClone(pix1); - pixd = pixCopy(NULL, pix2); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - - pixGetDimensions(pixd, &w, &h, &d); /* d must be either 8 or 32 bpp */ - pixc = pixClone(pixs2); - wc = pixGetWidth(pixc); - hc = pixGetHeight(pixc); - datac = pixGetData(pixc); - wplc = pixGetWpl(pixc); - - /* Check limits for src1, in case clipping was not done. */ - switch (type) - { - case L_BLEND_WITH_INVERSE: - /* - * The basic logic for this blending is: - * p --> (1 - f) * p + f * (1 - p) - * where p is a normalized value: p = pixval / 255. - * Thus, - * p --> p + f * (1 - 2 * p) - */ - for (i = 0; i < hc; i++) { - if (i + y < 0 || i + y >= h) continue; - linec = datac + i * wplc; - for (j = 0; j < wc; j++) { - if (j + x < 0 || j + x >= w) continue; - bval = GET_DATA_BIT(linec, j); - if (bval) { - switch (d) - { - case 8: - pixGetPixel(pixd, x + j, y + i, &pixval); - val = (l_int32)(pixval + fract * (255 - 2 * pixval)); - pixSetPixel(pixd, x + j, y + i, val); - break; - case 32: - pixGetPixel(pixd, x + j, y + i, &pixval); - extractRGBValues(pixval, &rval, &gval, &bval); - rval = (l_int32)(rval + fract * (255 - 2 * rval)); - gval = (l_int32)(gval + fract * (255 - 2 * gval)); - bval = (l_int32)(bval + fract * (255 - 2 * bval)); - composeRGBPixel(rval, gval, bval, &pixval); - pixSetPixel(pixd, x + j, y + i, pixval); - break; - default: - L_WARNING("d neither 8 nor 32 bpp; no blend\n", - procName); - } - } - } - } - break; - case L_BLEND_TO_WHITE: - /* - * The basic logic for this blending is: - * p --> p + f * (1 - p) (p normalized to [0...1]) - */ - for (i = 0; i < hc; i++) { - if (i + y < 0 || i + y >= h) continue; - linec = datac + i * wplc; - for (j = 0; j < wc; j++) { - if (j + x < 0 || j + x >= w) continue; - bval = GET_DATA_BIT(linec, j); - if (bval) { - switch (d) - { - case 8: - pixGetPixel(pixd, x + j, y + i, &pixval); - val = (l_int32)(pixval + fract * (255 - pixval)); - pixSetPixel(pixd, x + j, y + i, val); - break; - case 32: - pixGetPixel(pixd, x + j, y + i, &pixval); - extractRGBValues(pixval, &rval, &gval, &bval); - rval = (l_int32)(rval + fract * (255 - rval)); - gval = (l_int32)(gval + fract * (255 - gval)); - bval = (l_int32)(bval + fract * (255 - bval)); - composeRGBPixel(rval, gval, bval, &pixval); - pixSetPixel(pixd, x + j, y + i, pixval); - break; - default: - L_WARNING("d neither 8 nor 32 bpp; no blend\n", - procName); - } - } - } - } - break; - case L_BLEND_TO_BLACK: - /* - * The basic logic for this blending is: - * p --> (1 - f) * p (p normalized to [0...1]) - */ - for (i = 0; i < hc; i++) { - if (i + y < 0 || i + y >= h) continue; - linec = datac + i * wplc; - for (j = 0; j < wc; j++) { - if (j + x < 0 || j + x >= w) continue; - bval = GET_DATA_BIT(linec, j); - if (bval) { - switch (d) - { - case 8: - pixGetPixel(pixd, x + j, y + i, &pixval); - val = (l_int32)((1. - fract) * pixval); - pixSetPixel(pixd, x + j, y + i, val); - break; - case 32: - pixGetPixel(pixd, x + j, y + i, &pixval); - extractRGBValues(pixval, &rval, &gval, &bval); - rval = (l_int32)((1. - fract) * rval); - gval = (l_int32)((1. - fract) * gval); - bval = (l_int32)((1. - fract) * bval); - composeRGBPixel(rval, gval, bval, &pixval); - pixSetPixel(pixd, x + j, y + i, pixval); - break; - default: - L_WARNING("d neither 8 nor 32 bpp; no blend\n", - procName); - } - } - } - } - break; - default: - L_WARNING("invalid binary mask blend type\n", procName); - break; - } - - pixDestroy(&pixc); - return pixd; -} - - -/*! - * \brief pixBlendGray() - * - * \param[in] pixd [optional] either equal to pixs1 for in-place, - * or NULL - * \param[in] pixs1 blendee, depth > 1 - * \param[in] pixs2 blender, any depth; typically, the area of - * pixs2 is smaller than pixs1 - * \param[in] x,y origin [UL corner] of pixs2 relative to - * the origin of pixs1; can be < 0 - * \param[in] fract blending fraction - * \param[in] type L_BLEND_GRAY, L_BLEND_GRAY_WITH_INVERSE - * \param[in] transparent 1 to use transparency; 0 otherwise - * \param[in] transpix pixel grayval in pixs2 that is to be transparent - * \return pixd if OK; pixs1 on error - * - *
- * Notes: - * (1) For inplace operation (pixs1 not cmapped), call it this way: - * pixBlendGray(pixs1, pixs1, pixs2, ...) - * (2) For generating a new pixd: - * pixd = pixBlendGray(NULL, pixs1, pixs2, ...) - * (3) Clipping of pixs2 to pixs1 is done in the inner pixel loop. - * (4) If pixs1 has a colormap, it is removed; otherwise, if pixs1 - * has depth < 8, it is unpacked to generate a 8 bpp pix. - * (5) If transparent = 0, the blending fraction (fract) is - * applied equally to all pixels. - * (6) If transparent = 1, all pixels of value transpix (typically - * either 0 or 0xff) in pixs2 are transparent in the blend. - * (7) After processing pixs1, it is either 8 bpp or 32 bpp: - * ~ if 8 bpp, the fraction of pixs2 is mixed with pixs1. - * ~ if 32 bpp, each component of pixs1 is mixed with - * the same fraction of pixs2. - * (8) For L_BLEND_GRAY_WITH_INVERSE, the white values of the blendee - * (cval == 255 in the code below) result in a delta of 0. - * Thus, these pixels are intrinsically transparent! - * The "pivot" value of the src, at which no blending occurs, is - * 128. Compare with the adaptive pivot in pixBlendGrayAdapt(). - * (9) Invalid %fract defaults to 0.5 with a warning. - * Invalid %type defaults to L_BLEND_GRAY with a warning. - *- */ -PIX * -pixBlendGray(PIX *pixd, - PIX *pixs1, - PIX *pixs2, - l_int32 x, - l_int32 y, - l_float32 fract, - l_int32 type, - l_int32 transparent, - l_uint32 transpix) -{ -l_int32 i, j, d, wc, hc, w, h, wplc, wpld, delta; -l_int32 ival, irval, igval, ibval, cval, dval; -l_uint32 val32; -l_uint32 *linec, *lined, *datac, *datad; -PIX *pixc, *pix1, *pix2; - - PROCNAME("pixBlendGray"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); - if (pixGetDepth(pixs1) == 1) - return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd); - if (pixd == pixs1 && pixGetColormap(pixs1)) - return (PIX *)ERROR_PTR("can't do in-place with cmap", procName, pixd); - if (pixd && (pixd != pixs1)) - return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd); - if (fract < 0.0 || fract > 1.0) { - L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName); - fract = 0.5; - } - if (type != L_BLEND_GRAY && type != L_BLEND_GRAY_WITH_INVERSE) { - L_WARNING("invalid blend type; setting to L_BLEND_GRAY\n", procName); - type = L_BLEND_GRAY; - } - - /* If pixd != NULL, we know that it is equal to pixs1 and - * that pixs1 does not have a colormap, so that an in-place operation - * can be done. Otherwise, remove colormap from pixs1 if - * it exists and unpack to at least 8 bpp if necessary, - * to do the blending on a new pix. */ - if (!pixd) { - pix1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); - if (pixGetDepth(pix1) < 8) - pix2 = pixConvertTo8(pix1, FALSE); - else - pix2 = pixClone(pix1); - pixd = pixCopy(NULL, pix2); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - - pixGetDimensions(pixd, &w, &h, &d); /* 8 or 32 bpp */ - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - pixc = pixConvertTo8(pixs2, 0); - pixGetDimensions(pixc, &wc, &hc, NULL); - datac = pixGetData(pixc); - wplc = pixGetWpl(pixc); - - /* Check limits for src1, in case clipping was not done */ - if (type == L_BLEND_GRAY) { - /* - * The basic logic for this blending is: - * p --> (1 - f) * p + f * c - * where c is the 8 bpp blender. All values are normalized to [0...1]. - */ - for (i = 0; i < hc; i++) { - if (i + y < 0 || i + y >= h) continue; - linec = datac + i * wplc; - lined = datad + (i + y) * wpld; - switch (d) - { - case 8: - for (j = 0; j < wc; j++) { - if (j + x < 0 || j + x >= w) continue; - cval = GET_DATA_BYTE(linec, j); - if (transparent == 0 || cval != transpix) { - dval = GET_DATA_BYTE(lined, j + x); - ival = (l_int32)((1. - fract) * dval + fract * cval); - SET_DATA_BYTE(lined, j + x, ival); - } - } - break; - case 32: - for (j = 0; j < wc; j++) { - if (j + x < 0 || j + x >= w) continue; - cval = GET_DATA_BYTE(linec, j); - if (transparent == 0 || cval != transpix) { - val32 = *(lined + j + x); - extractRGBValues(val32, &irval, &igval, &ibval); - irval = (l_int32)((1. - fract) * irval + fract * cval); - igval = (l_int32)((1. - fract) * igval + fract * cval); - ibval = (l_int32)((1. - fract) * ibval + fract * cval); - composeRGBPixel(irval, igval, ibval, &val32); - *(lined + j + x) = val32; - } - } - break; - default: - break; /* shouldn't happen */ - } - } - } else { /* L_BLEND_GRAY_WITH_INVERSE */ - for (i = 0; i < hc; i++) { - if (i + y < 0 || i + y >= h) continue; - linec = datac + i * wplc; - lined = datad + (i + y) * wpld; - switch (d) - { - case 8: - /* - * For 8 bpp, the dest pix is shifted by a signed amount - * proportional to the distance from 128 (the pivot value), - * and to the darkness of src2. If the dest is darker - * than 128, it becomes lighter, and v.v. - * The basic logic is: - * d --> d + f * (0.5 - d) * (1 - c) - * where d and c are normalized pixel values for src1 and - * src2, respectively, with 8 bit normalization to [0...1]. - */ - for (j = 0; j < wc; j++) { - if (j + x < 0 || j + x >= w) continue; - cval = GET_DATA_BYTE(linec, j); - if (transparent == 0 || cval != transpix) { - ival = GET_DATA_BYTE(lined, j + x); - delta = (128 - ival) * (255 - cval) / 256; - ival += (l_int32)(fract * delta + 0.5); - SET_DATA_BYTE(lined, j + x, ival); - } - } - break; - case 32: - /* Each component is shifted by the same formula for 8 bpp */ - for (j = 0; j < wc; j++) { - if (j + x < 0 || j + x >= w) continue; - cval = GET_DATA_BYTE(linec, j); - if (transparent == 0 || cval != transpix) { - val32 = *(lined + j + x); - extractRGBValues(val32, &irval, &igval, &ibval); - delta = (128 - irval) * (255 - cval) / 256; - irval += (l_int32)(fract * delta + 0.5); - delta = (128 - igval) * (255 - cval) / 256; - igval += (l_int32)(fract * delta + 0.5); - delta = (128 - ibval) * (255 - cval) / 256; - ibval += (l_int32)(fract * delta + 0.5); - composeRGBPixel(irval, igval, ibval, &val32); - *(lined + j + x) = val32; - } - } - break; - default: - break; /* shouldn't happen */ - } - } - } - - pixDestroy(&pixc); - return pixd; -} - - -/*! - * \brief pixBlendGrayInverse() - * - * \param[in] pixd [optional] either equal to pixs1 for in-place, or NULL - * \param[in] pixd [optional] either NULL or equal to pixs1 for in-place - * \param[in] pixs1 blendee, depth > 1 - * \param[in] pixs2 blender, any depth; typ. smaller in size than pixs1 - * \param[in] x,y origin [UL corner] of pixs2 relative to - * the origin of pixs1; can be < 0 - * \param[in] fract blending fraction - * \return pixd if OK; pixs1 on error - * - *
- * Notes: - * (1) For inplace operation (pixs1 not cmapped), call it this way: - * pixBlendGrayInverse(pixs1, pixs1, pixs2, ...) - * (2) For generating a new pixd: - * pixd = pixBlendGrayInverse(NULL, pixs1, pixs2, ...) - * (3) Clipping of pixs2 to pixs1 is done in the inner pixel loop. - * (4) If pixs1 has a colormap, it is removed; otherwise if pixs1 - * has depth < 8, it is unpacked to generate a 8 bpp pix. - * (5) This is a no-nonsense blender. It changes the src1 pixel except - * when the src1 pixel is midlevel gray. Use fract == 1 for the most - * aggressive blending, where, if the gray pixel in pixs2 is 0, - * we get a complete inversion of the color of the src pixel in pixs1. - * (6) The basic logic is that each component transforms by: - d --> c * d + (1 - c ) * (f * (1 - d) + d * (1 - f)) - * where c is the blender pixel from pixs2, - * f is %fract, - * c and d are normalized to [0...1] - * This has the property that for f == 0 (no blend) or c == 1 (white): - * d --> d - * For c == 0 (black) we get maximum inversion: - * d --> f * (1 - d) + d * (1 - f) [inversion by fraction f] - *- */ -PIX * -pixBlendGrayInverse(PIX *pixd, - PIX *pixs1, - PIX *pixs2, - l_int32 x, - l_int32 y, - l_float32 fract) -{ -l_int32 i, j, d, wc, hc, w, h, wplc, wpld; -l_int32 irval, igval, ibval, cval, dval; -l_float32 a; -l_uint32 val32; -l_uint32 *linec, *lined, *datac, *datad; -PIX *pixc, *pix1, *pix2; - - PROCNAME("pixBlendGrayInverse"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); - if (pixGetDepth(pixs1) == 1) - return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd); - if (pixd == pixs1 && pixGetColormap(pixs1)) - return (PIX *)ERROR_PTR("can't do in-place with cmap", procName, pixd); - if (pixd && (pixd != pixs1)) - return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd); - if (fract < 0.0 || fract > 1.0) { - L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName); - fract = 0.5; - } - - /* If pixd != NULL, we know that it is equal to pixs1 and - * that pixs1 does not have a colormap, so that an in-place operation - * can be done. Otherwise, remove colormap from pixs1 if - * it exists and unpack to at least 8 bpp if necessary, - * to do the blending on a new pix. */ - if (!pixd) { - pix1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); - if (pixGetDepth(pix1) < 8) - pix2 = pixConvertTo8(pix1, FALSE); - else - pix2 = pixClone(pix1); - pixd = pixCopy(NULL, pix2); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - - pixGetDimensions(pixd, &w, &h, &d); /* 8 or 32 bpp */ - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - pixc = pixConvertTo8(pixs2, 0); - pixGetDimensions(pixc, &wc, &hc, NULL); - datac = pixGetData(pixc); - wplc = pixGetWpl(pixc); - - /* Check limits for src1, in case clipping was not done */ - for (i = 0; i < hc; i++) { - if (i + y < 0 || i + y >= h) continue; - linec = datac + i * wplc; - lined = datad + (i + y) * wpld; - switch (d) - { - case 8: - for (j = 0; j < wc; j++) { - if (j + x < 0 || j + x >= w) continue; - cval = GET_DATA_BYTE(linec, j); - dval = GET_DATA_BYTE(lined, j + x); - a = (1.0 - fract) * dval + fract * (255.0 - dval); - dval = (l_int32)(cval * dval / 255.0 + - a * (255.0 - cval) / 255.0); - SET_DATA_BYTE(lined, j + x, dval); - } - break; - case 32: - for (j = 0; j < wc; j++) { - if (j + x < 0 || j + x >= w) continue; - cval = GET_DATA_BYTE(linec, j); - val32 = *(lined + j + x); - extractRGBValues(val32, &irval, &igval, &ibval); - a = (1.0 - fract) * irval + fract * (255.0 - irval); - irval = (l_int32)(cval * irval / 255.0 + - a * (255.0 - cval) / 255.0); - a = (1.0 - fract) * igval + fract * (255.0 - igval); - igval = (l_int32)(cval * igval / 255.0 + - a * (255.0 - cval) / 255.0); - a = (1.0 - fract) * ibval + fract * (255.0 - ibval); - ibval = (l_int32)(cval * ibval / 255.0 + - a * (255.0 - cval) / 255.0); - composeRGBPixel(irval, igval, ibval, &val32); - *(lined + j + x) = val32; - } - break; - default: - break; /* shouldn't happen */ - } - } - - pixDestroy(&pixc); - return pixd; -} - - -/*! - * \brief pixBlendColor() - * - * \param[in] pixd [optional] either equal to pixs1 for in-place, - * or NULL - * \param[in] pixs1 blendee; depth > 1 - * \param[in] pixs2 blender, any depth; typically, the area of - * pixs2 is smaller than pixs1 - * \param[in] x,y origin [UL corner] of pixs2 relative to - * the origin of pixs1 - * \param[in] fract blending fraction - * \param[in] transparent 1 to use transparency; 0 otherwise - * \param[in] transpix pixel color in pixs2 that is to be transparent - * \return pixd, or null on error - * - *
- * Notes: - * (1) For inplace operation (pixs1 must be 32 bpp), call it this way: - * pixBlendColor(pixs1, pixs1, pixs2, ...) - * (2) For generating a new pixd: - * pixd = pixBlendColor(NULL, pixs1, pixs2, ...) - * (3) If pixs2 is not 32 bpp rgb, it is converted. - * (4) Clipping of pixs2 to pixs1 is done in the inner pixel loop. - * (5) If pixs1 has a colormap, it is removed to generate a 32 bpp pix. - * (6) If pixs1 has depth < 32, it is unpacked to generate a 32 bpp pix. - * (7) If transparent = 0, the blending fraction (fract) is - * applied equally to all pixels. - * (8) If transparent = 1, all pixels of value transpix (typically - * either 0 or 0xffffff00) in pixs2 are transparent in the blend. - *- */ -PIX * -pixBlendColor(PIX *pixd, - PIX *pixs1, - PIX *pixs2, - l_int32 x, - l_int32 y, - l_float32 fract, - l_int32 transparent, - l_uint32 transpix) -{ -l_int32 i, j, wc, hc, w, h, wplc, wpld; -l_int32 rval, gval, bval, rcval, gcval, bcval; -l_uint32 cval32, val32; -l_uint32 *linec, *lined, *datac, *datad; -PIX *pixc; - - PROCNAME("pixBlendColor"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); - if (pixGetDepth(pixs1) == 1) - return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, NULL); - if (pixd == pixs1 && pixGetDepth(pixs1) != 32) - return (PIX *)ERROR_PTR("inplace; pixs1 not 32 bpp", procName, NULL); - if (pixd && (pixd != pixs1)) - return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, NULL); - if (fract < 0.0 || fract > 1.0) { - L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName); - fract = 0.5; - } - - /* If pixd != null, we know that it is equal to pixs1 and - * that pixs1 is 32 bpp rgb, so that an in-place operation - * can be done. Otherwise, pixConvertTo32() will remove a - * colormap from pixs1 if it exists and unpack to 32 bpp - * (if necessary) to do the blending on a new 32 bpp Pix. */ - if (!pixd) - pixd = pixConvertTo32(pixs1); - pixGetDimensions(pixd, &w, &h, NULL); - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - pixc = pixConvertTo32(pixs2); /* blend with 32 bpp rgb */ - pixGetDimensions(pixc, &wc, &hc, NULL); - datac = pixGetData(pixc); - wplc = pixGetWpl(pixc); - - /* Check limits for src1, in case clipping was not done */ - for (i = 0; i < hc; i++) { - /* - * The basic logic for this blending is: - * p --> (1 - f) * p + f * c - * for each color channel. c is a color component of the blender. - * All values are normalized to [0...1]. - */ - if (i + y < 0 || i + y >= h) continue; - linec = datac + i * wplc; - lined = datad + (i + y) * wpld; - for (j = 0; j < wc; j++) { - if (j + x < 0 || j + x >= w) continue; - cval32 = *(linec + j); - if (transparent == 0 || - ((cval32 & 0xffffff00) != (transpix & 0xffffff00))) { - val32 = *(lined + j + x); - extractRGBValues(cval32, &rcval, &gcval, &bcval); - extractRGBValues(val32, &rval, &gval, &bval); - rval = (l_int32)((1. - fract) * rval + fract * rcval); - gval = (l_int32)((1. - fract) * gval + fract * gcval); - bval = (l_int32)((1. - fract) * bval + fract * bcval); - composeRGBPixel(rval, gval, bval, &val32); - *(lined + j + x) = val32; - } - } - } - - pixDestroy(&pixc); - return pixd; -} - - -/* - * \brief pixBlendColorByChannel() - * - * \param[in] pixd [optional] either equal to pixs1 for in-place, - * or NULL - * \param[in] pixs1 blendee; depth > 1 - * \param[in] pixs2 blender, any depth; typically, the area of - * pixs2 is smaller than pixs1 - * \param[in] x,y origin [UL corner] of pixs2 relative to - * the origin of pixs1 - * \param[in] rfract blending fraction in red channel - * \param[in] gfract blending fraction in green channel - * \param[in] bfract blending fraction in blue channel - * \param[in] transparent 1 to use transparency; 0 otherwise - * \param[in] transpix pixel color in pixs2 that is to be transparent - * \return pixd if OK; pixd on error - * - *
- * Notes: - * (1) This generalizes pixBlendColor() in two ways: - * (a) The mixing fraction is specified per channel. - * (b) The mixing fraction may be < 0 or > 1, in which case, - * the min or max of two images are taken, respectively. - * (2) Specifically, - * for p = pixs1[i], c = pixs2[i], f = fract[i], i = 1, 2, 3: - * f < 0.0: p --> min(p, c) - * 0.0 <= f <= 1.0: p --> (1 - f) * p + f * c - * f > 1.0: p --> max(a, c) - * Special cases: - * f = 0: p --> p - * f = 1: p --> c - * (3) See usage notes in pixBlendColor() - * (4) pixBlendColor() would be equivalent to - * pixBlendColorChannel(..., fract, fract, fract, ...); - * at a small cost of efficiency. - *- */ -PIX * -pixBlendColorByChannel(PIX *pixd, - PIX *pixs1, - PIX *pixs2, - l_int32 x, - l_int32 y, - l_float32 rfract, - l_float32 gfract, - l_float32 bfract, - l_int32 transparent, - l_uint32 transpix) -{ -l_int32 i, j, wc, hc, w, h, wplc, wpld; -l_int32 rval, gval, bval, rcval, gcval, bcval; -l_uint32 cval32, val32; -l_uint32 *linec, *lined, *datac, *datad; -PIX *pixc; - - PROCNAME("pixBlendColorByChannel"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); - if (pixGetDepth(pixs1) == 1) - return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd); - if (pixd == pixs1 && pixGetDepth(pixs1) != 32) - return (PIX *)ERROR_PTR("inplace; pixs1 not 32 bpp", procName, pixd); - if (pixd && (pixd != pixs1)) - return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd); - - /* If pixd != NULL, we know that it is equal to pixs1 and - * that pixs1 is 32 bpp rgb, so that an in-place operation - * can be done. Otherwise, pixConvertTo32() will remove a - * colormap from pixs1 if it exists and unpack to 32 bpp - * (if necessary) to do the blending on a new 32 bpp Pix. */ - if (!pixd) - pixd = pixConvertTo32(pixs1); - pixGetDimensions(pixd, &w, &h, NULL); - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - pixc = pixConvertTo32(pixs2); - pixGetDimensions(pixc, &wc, &hc, NULL); - datac = pixGetData(pixc); - wplc = pixGetWpl(pixc); - - /* Check limits for src1, in case clipping was not done */ - for (i = 0; i < hc; i++) { - if (i + y < 0 || i + y >= h) continue; - linec = datac + i * wplc; - lined = datad + (i + y) * wpld; - for (j = 0; j < wc; j++) { - if (j + x < 0 || j + x >= w) continue; - cval32 = *(linec + j); - if (transparent == 0 || - ((cval32 & 0xffffff00) != (transpix & 0xffffff00))) { - val32 = *(lined + j + x); - extractRGBValues(cval32, &rcval, &gcval, &bcval); - extractRGBValues(val32, &rval, &gval, &bval); - rval = blendComponents(rval, rcval, rfract); - gval = blendComponents(gval, gcval, gfract); - bval = blendComponents(bval, bcval, bfract); - composeRGBPixel(rval, gval, bval, &val32); - *(lined + j + x) = val32; - } - } - } - - pixDestroy(&pixc); - return pixd; -} - - -static l_int32 -blendComponents(l_int32 a, - l_int32 b, - l_float32 fract) -{ - if (fract < 0.) - return ((a < b) ? a : b); - if (fract > 1.) - return ((a > b) ? a : b); - return (l_int32)((1. - fract) * a + fract * b); -} - - -/*! - * \brief pixBlendGrayAdapt() - * - * \param[in] pixd [optional] either equal to pixs1 for in-place, or NULL - * \param[in] pixs1 blendee; depth > 1 - * \param[in] pixs2 blender, any depth; typically, the area of - * pixs2 is smaller than pixs1 - * \param[in] x,y origin [UL corner] of pixs2 relative to - * the origin of pixs1; can be < 0 - * \param[in] fract blending fraction - * \param[in] shift >= 0 but <= 128: shift of zero blend value from - * median source; use -1 for default value; - * \return pixd if OK; pixs1 on error - * - *
- * Notes: - * (1) For inplace operation (pixs1 not cmapped), call it this way: - * pixBlendGrayAdapt(pixs1, pixs1, pixs2, ...) - * For generating a new pixd: - * pixd = pixBlendGrayAdapt(NULL, pixs1, pixs2, ...) - * (2) Clipping of pixs2 to pixs1 is done in the inner pixel loop. - * (3) If pixs1 has a colormap, it is removed. - * (4) If pixs1 has depth < 8, it is unpacked to generate a 8 bpp pix. - * (5) This does a blend with inverse. Whereas in pixGlendGray(), the - * zero blend point is where the blendee pixel is 128, here - * the zero blend point is found adaptively, with respect to the - * median of the blendee region. If the median is < 128, - * the zero blend point is found from - * median + shift. - * Otherwise, if the median >= 128, the zero blend point is - * median - shift. - * The purpose of shifting the zero blend point away from the - * median is to prevent a situation in pixBlendGray() where - * the median is 128 and the blender is not visible. - * The default value of shift is 64. - * (6) After processing pixs1, it is either 8 bpp or 32 bpp: - * ~ if 8 bpp, the fraction of pixs2 is mixed with pixs1. - * ~ if 32 bpp, each component of pixs1 is mixed with - * the same fraction of pixs2. - * (7) The darker the blender, the more it mixes with the blendee. - * A blender value of 0 has maximum mixing; a value of 255 - * has no mixing and hence is transparent. - *- */ -PIX * -pixBlendGrayAdapt(PIX *pixd, - PIX *pixs1, - PIX *pixs2, - l_int32 x, - l_int32 y, - l_float32 fract, - l_int32 shift) -{ -l_int32 i, j, d, wc, hc, w, h, wplc, wpld, delta, overlap; -l_int32 rval, gval, bval, cval, dval, mval, median, pivot; -l_uint32 val32; -l_uint32 *linec, *lined, *datac, *datad; -l_float32 fmedian, factor; -BOX *box, *boxt; -PIX *pixc, *pix1, *pix2; - - PROCNAME("pixBlendGrayAdapt"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); - if (pixGetDepth(pixs1) == 1) - return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd); - if (pixd == pixs1 && pixGetColormap(pixs1)) - return (PIX *)ERROR_PTR("can't do in-place with cmap", procName, pixd); - if (pixd && (pixd != pixs1)) - return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd); - if (fract < 0.0 || fract > 1.0) { - L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName); - fract = 0.5; - } - if (shift == -1) shift = 64; /* default value */ - if (shift < 0 || shift > 127) { - L_WARNING("invalid shift; setting to 64\n", procName); - shift = 64; - } - - /* Test for overlap */ - pixGetDimensions(pixs1, &w, &h, NULL); - pixGetDimensions(pixs2, &wc, &hc, NULL); - box = boxCreate(x, y, wc, hc); - boxt = boxCreate(0, 0, w, h); - boxIntersects(box, boxt, &overlap); - boxDestroy(&boxt); - if (!overlap) { - boxDestroy(&box); - return (PIX *)ERROR_PTR("no image overlap", procName, pixd); - } - - /* If pixd != NULL, we know that it is equal to pixs1 and - * that pixs1 does not have a colormap, so that an in-place operation - * can be done. Otherwise, remove colormap from pixs1 if - * it exists and unpack to at least 8 bpp if necessary, - * to do the blending on a new pix. */ - if (!pixd) { - pix1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); - if (pixGetDepth(pix1) < 8) - pix2 = pixConvertTo8(pix1, FALSE); - else - pix2 = pixClone(pix1); - pixd = pixCopy(NULL, pix2); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - - /* Get the median value in the region of blending */ - pix1 = pixClipRectangle(pixd, box, NULL); - pix2 = pixConvertTo8(pix1, 0); - pixGetRankValueMasked(pix2, NULL, 0, 0, 1, 0.5, &fmedian, NULL); - median = (l_int32)(fmedian + 0.5); - if (median < 128) - pivot = median + shift; - else - pivot = median - shift; - pixDestroy(&pix1); - pixDestroy(&pix2); - boxDestroy(&box); - - /* Process over src2; clip to src1. */ - d = pixGetDepth(pixd); - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - pixc = pixConvertTo8(pixs2, 0); - datac = pixGetData(pixc); - wplc = pixGetWpl(pixc); - for (i = 0; i < hc; i++) { - if (i + y < 0 || i + y >= h) continue; - linec = datac + i * wplc; - lined = datad + (i + y) * wpld; - switch (d) - { - case 8: - /* - * For 8 bpp, the dest pix is shifted by an amount - * proportional to the distance from the pivot value, - * and to the darkness of src2. In no situation will it - * pass the pivot value in intensity. - * The basic logic is: - * d --> d + f * (np - d) * (1 - c) - * where np, d and c are normalized pixel values for - * the pivot, src1 and src2, respectively, with normalization - * to 255. - */ - for (j = 0; j < wc; j++) { - if (j + x < 0 || j + x >= w) continue; - dval = GET_DATA_BYTE(lined, j + x); - cval = GET_DATA_BYTE(linec, j); - delta = (pivot - dval) * (255 - cval) / 256; - dval += (l_int32)(fract * delta + 0.5); - SET_DATA_BYTE(lined, j + x, dval); - } - break; - case 32: - /* - * For 32 bpp, the dest pix is shifted by an amount - * proportional to the max component distance from the - * pivot value, and to the darkness of src2. Each component - * is shifted by the same fraction, either up or down, - * depending on the shift direction (which is toward the - * pivot). The basic logic for the red component is: - * r --> r + f * (np - m) * (1 - c) * (r / m) - * where np, r, m and c are normalized pixel values for - * the pivot, the r component of src1, the max component - * of src1, and src2, respectively, again with normalization - * to 255. Likewise for the green and blue components. - */ - for (j = 0; j < wc; j++) { - if (j + x < 0 || j + x >= w) continue; - cval = GET_DATA_BYTE(linec, j); - val32 = *(lined + j + x); - extractRGBValues(val32, &rval, &gval, &bval); - mval = L_MAX(rval, gval); - mval = L_MAX(mval, bval); - mval = L_MAX(mval, 1); - delta = (pivot - mval) * (255 - cval) / 256; - factor = fract * delta / mval; - rval += (l_int32)(factor * rval + 0.5); - gval += (l_int32)(factor * gval + 0.5); - bval += (l_int32)(factor * bval + 0.5); - composeRGBPixel(rval, gval, bval, &val32); - *(lined + j + x) = val32; - } - break; - default: - break; /* shouldn't happen */ - } - } - - pixDestroy(&pixc); - return pixd; -} - - -/*! - * \brief pixFadeWithGray() - * - * \param[in] pixs colormapped or 8 bpp or 32 bpp - * \param[in] pixb 8 bpp blender - * \param[in] factor multiplicative factor to apply to blender value - * \param[in] type L_BLEND_TO_WHITE, L_BLEND_TO_BLACK - * \return pixd, or null on error - * - *
- * Notes: - * (1) This function combines two pix aligned to the UL corner; they - * need not be the same size. - * (2) Each pixel in pixb is multiplied by 'factor' divided by 255, and - * clipped to the range [0 ... 1]. This gives the fade fraction - * to be applied to pixs. Fade either to white (L_BLEND_TO_WHITE) - * or to black (L_BLEND_TO_BLACK). - *- */ -PIX * -pixFadeWithGray(PIX *pixs, - PIX *pixb, - l_float32 factor, - l_int32 type) -{ -l_int32 i, j, w, h, d, wb, hb, db, wd, hd, wplb, wpld; -l_int32 valb, vald, nvald, rval, gval, bval, nrval, ngval, nbval; -l_float32 nfactor, fract; -l_uint32 val32, nval32; -l_uint32 *lined, *datad, *lineb, *datab; -PIX *pixd; - - PROCNAME("pixFadeWithGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!pixb) - return (PIX *)ERROR_PTR("pixb not defined", procName, NULL); - if (pixGetDepth(pixs) == 1) - return (PIX *)ERROR_PTR("pixs is 1 bpp", procName, NULL); - pixGetDimensions(pixb, &wb, &hb, &db); - if (db != 8) - return (PIX *)ERROR_PTR("pixb not 8 bpp", procName, NULL); - if (factor < 0.0 || factor > 255.0) - return (PIX *)ERROR_PTR("factor not in [0.0...255.0]", procName, NULL); - if (type != L_BLEND_TO_WHITE && type != L_BLEND_TO_BLACK) - return (PIX *)ERROR_PTR("invalid fade type", procName, NULL); - - /* Remove colormap if it exists; otherwise copy */ - pixd = pixRemoveColormapGeneral(pixs, REMOVE_CMAP_BASED_ON_SRC, L_COPY); - pixGetDimensions(pixd, &wd, &hd, &d); - w = L_MIN(wb, wd); - h = L_MIN(hb, hd); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - datab = pixGetData(pixb); - wplb = pixGetWpl(pixb); - - /* The basic logic for this blending is, for each component p of pixs: - * fade-to-white: p --> p + (f * c) * (1 - p) - * fade-to-black: p --> p - (f * c) * p - * with c being the 8 bpp blender pixel of pixb, and with both - * p and c normalized to [0...1]. */ - nfactor = factor / 255.; - for (i = 0; i < h; i++) { - lineb = datab + i * wplb; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - valb = GET_DATA_BYTE(lineb, j); - fract = nfactor * (l_float32)valb; - fract = L_MIN(fract, 1.0); - if (d == 8) { - vald = GET_DATA_BYTE(lined, j); - if (type == L_BLEND_TO_WHITE) - nvald = vald + (l_int32)(fract * (255. - (l_float32)vald)); - else /* L_BLEND_TO_BLACK */ - nvald = vald - (l_int32)(fract * (l_float32)vald); - SET_DATA_BYTE(lined, j, nvald); - } else { /* d == 32 */ - val32 = lined[j]; - extractRGBValues(val32, &rval, &gval, &bval); - if (type == L_BLEND_TO_WHITE) { - nrval = rval + (l_int32)(fract * (255. - (l_float32)rval)); - ngval = gval + (l_int32)(fract * (255. - (l_float32)gval)); - nbval = bval + (l_int32)(fract * (255. - (l_float32)bval)); - } else { - nrval = rval - (l_int32)(fract * (l_float32)rval); - ngval = gval - (l_int32)(fract * (l_float32)gval); - nbval = bval - (l_int32)(fract * (l_float32)bval); - } - composeRGBPixel(nrval, ngval, nbval, &nval32); - lined[j] = nval32; - } - } - } - - return pixd; -} - - -/* - * \brief pixBlendHardLight() - * - * \param[in] pixd either NULL or equal to pixs1 for in-place - * \param[in] pixs1 blendee; depth > 1, may be cmapped - * \param[in] pixs2 blender, 8 or 32 bpp; may be colormapped; - * typ. smaller in size than pixs1 - * \param[in] x,y origin [UL corner] of pixs2 relative to - * the origin of pixs1 - * \param[in] fract blending fraction, or 'opacity factor' - * \return pixd if OK; pixs1 on error - * - *
- * Notes: - * (1) pixs2 must be 8 or 32 bpp; either may have a colormap. - * (2) Clipping of pixs2 to pixs1 is done in the inner pixel loop. - * (3) Only call in-place if pixs1 is not colormapped. - * (4) If pixs1 has a colormap, it is removed to generate either an - * 8 or 32 bpp pix, depending on the colormap. - * (5) For inplace operation, call it this way: - * pixBlendHardLight(pixs1, pixs1, pixs2, ...) - * (6) For generating a new pixd: - * pixd = pixBlendHardLight(NULL, pixs1, pixs2, ...) - * (7) This is a generalization of the usual hard light blending, - * where fract == 1.0. - * (8) "Overlay" blending is the same as hard light blending, with - * fract == 1.0, except that the components are switched - * in the test. (Note that the result is symmetric in the - * two components.) - * (9) See, e.g.: - * http://www.pegtop.net/delphi/articles/blendmodes/hardlight.htm - * http://www.digitalartform.com/imageArithmetic.htm - * (10) This function was built by Paco Galanes. - *- */ -PIX * -pixBlendHardLight(PIX *pixd, - PIX *pixs1, - PIX *pixs2, - l_int32 x, - l_int32 y, - l_float32 fract) -{ -l_int32 i, j, w, h, d, wc, hc, dc, wplc, wpld; -l_int32 cval, dval, rcval, gcval, bcval, rdval, gdval, bdval; -l_uint32 cval32, dval32; -l_uint32 *linec, *lined, *datac, *datad; -PIX *pixc, *pixt; - - PROCNAME("pixBlendHardLight"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); - pixGetDimensions(pixs1, &w, &h, &d); - pixGetDimensions(pixs2, &wc, &hc, &dc); - if (d == 1) - return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd); - if (dc != 8 && dc != 32) - return (PIX *)ERROR_PTR("pixs2 not 8 or 32 bpp", procName, pixd); - if (pixd && (pixd != pixs1)) - return (PIX *)ERROR_PTR("inplace and pixd != pixs1", procName, pixd); - if (pixd == pixs1 && pixGetColormap(pixs1)) - return (PIX *)ERROR_PTR("inplace and pixs1 cmapped", procName, pixd); - if (pixd && d != 8 && d != 32) - return (PIX *)ERROR_PTR("inplace and not 8 or 32 bpp", procName, pixd); - - if (fract < 0.0 || fract > 1.0) { - L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName); - fract = 0.5; - } - - /* If pixs2 has a colormap, remove it */ - pixc = pixRemoveColormap(pixs2, REMOVE_CMAP_BASED_ON_SRC); /* clone ok */ - dc = pixGetDepth(pixc); - - /* There are 4 cases: - * * pixs1 has or doesn't have a colormap - * * pixc is either 8 or 32 bpp - * In all situations, if pixs has a colormap it must be removed, - * and pixd must have a depth that is equal to or greater than pixc. */ - if (dc == 32) { - if (pixGetColormap(pixs1)) { /* pixd == NULL */ - pixd = pixRemoveColormap(pixs1, REMOVE_CMAP_TO_FULL_COLOR); - } else { - if (!pixd) { - pixd = pixConvertTo32(pixs1); - } else { - pixt = pixConvertTo32(pixs1); - pixCopy(pixd, pixt); - pixDestroy(&pixt); - } - } - d = 32; - } else { /* dc == 8 */ - if (pixGetColormap(pixs1)) /* pixd == NULL */ - pixd = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); - else - pixd = pixCopy(pixd, pixs1); - d = pixGetDepth(pixd); - } - - if (!(d == 8 && dc == 8) && /* 3 cases only */ - !(d == 32 && dc == 8) && - !(d == 32 && dc == 32)) { - pixDestroy(&pixc); - return (PIX *)ERROR_PTR("bad! -- invalid depth combo!", procName, pixd); - } - - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - datac = pixGetData(pixc); - wplc = pixGetWpl(pixc); - for (i = 0; i < hc; i++) { - if (i + y < 0 || i + y >= h) continue; - linec = datac + i * wplc; - lined = datad + (i + y) * wpld; - for (j = 0; j < wc; j++) { - if (j + x < 0 || j + x >= w) continue; - if (d == 8 && dc == 8) { - dval = GET_DATA_BYTE(lined, x + j); - cval = GET_DATA_BYTE(linec, j); - dval = blendHardLightComponents(dval, cval, fract); - SET_DATA_BYTE(lined, x + j, dval); - } else if (d == 32 && dc == 8) { - dval32 = *(lined + x + j); - extractRGBValues(dval32, &rdval, &gdval, &bdval); - cval = GET_DATA_BYTE(linec, j); - rdval = blendHardLightComponents(rdval, cval, fract); - gdval = blendHardLightComponents(gdval, cval, fract); - bdval = blendHardLightComponents(bdval, cval, fract); - composeRGBPixel(rdval, gdval, bdval, &dval32); - *(lined + x + j) = dval32; - } else if (d == 32 && dc == 32) { - dval32 = *(lined + x + j); - extractRGBValues(dval32, &rdval, &gdval, &bdval); - cval32 = *(linec + j); - extractRGBValues(cval32, &rcval, &gcval, &bcval); - rdval = blendHardLightComponents(rdval, rcval, fract); - gdval = blendHardLightComponents(gdval, gcval, fract); - bdval = blendHardLightComponents(bdval, bcval, fract); - composeRGBPixel(rdval, gdval, bdval, &dval32); - *(lined + x + j) = dval32; - } - } - } - - pixDestroy(&pixc); - return pixd; -} - - -/* - * \brief blendHardLightComponents() - * - * \param[in] a 8 bpp blendee component - * \param[in] b 8 bpp blender component - * \param[in] fract fraction of blending; use 1.0 for usual definition - * \return blended 8 bpp component - * - *
- * Notes: - * - * The basic logic for this blending is: - * b < 0.5: - * a --> 2 * a * (0.5 - f * (0.5 - b)) - * b >= 0.5: - * a --> 1 - 2 * (1 - a) * (1 - (0.5 - f * (0.5 - b))) - * - * In the limit that f == 1 (standard hardlight blending): - * b < 0.5: a --> 2 * a * b - * or - * a --> a - a * (1 - 2 * b) - * b >= 0.5: a --> 1 - 2 * (1 - a) * (1 - b) - * or - * a --> a + (1 - a) * (2 * b - 1) - * - * You can see that for standard hardlight blending: - * b < 0.5: a is pushed linearly with b down to 0 - * b >= 0.5: a is pushed linearly with b up to 1 - * a is unchanged if b = 0.5 - * - * Our opacity factor f reduces the deviation of b from 0.5: - * f == 0: b --> 0.5, so no blending occurs - * f == 1: b --> b, so we get full conventional blending - * - * There is a variant of hardlight blending called "softlight" blending: - * (e.g., http://jswidget.com/blog/tag/hard-light/) - * b < 0.5: - * a --> a - a * (0.5 - b) * (1 - Abs(2 * a - 1)) - * b >= 0.5: - * a --> a + (1 - a) * (b - 0.5) * (1 - Abs(2 * a - 1)) - * which limits the amount that 'a' can be moved to a maximum of - * halfway toward 0 or 1, and further reduces it as 'a' moves - * away from 0.5. - * As you can see, there are a nearly infinite number of different - * blending formulas that can be conjured up. - *- */ -static l_int32 blendHardLightComponents(l_int32 a, - l_int32 b, - l_float32 fract) -{ - if (b < 0x80) { - b = 0x80 - (l_int32)(fract * (0x80 - b)); - return (a * b) >> 7; - } else { - b = 0x80 + (l_int32)(fract * (b - 0x80)); - return 0xff - (((0xff - b) * (0xff - a)) >> 7); - } -} - - -/*-------------------------------------------------------------* - * Blending two colormapped images * - *-------------------------------------------------------------*/ -/*! - * \brief pixBlendCmap() - * - * \param[in] pixs 2, 4 or 8 bpp, with colormap - * \param[in] pixb colormapped blender - * \param[in] x, y UL corner of blender relative to pixs - * \param[in] sindex colormap index of pixels in pixs to be changed - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This function combines two colormaps, and replaces the pixels - * in pixs that have a specified color value with those in pixb. - * (2) sindex must be in the existing colormap; otherwise an - * error is returned. In use, sindex will typically be the index - * for white (255, 255, 255). - * (3) Blender colors that already exist in the colormap are used; - * others are added. If any blender colors cannot be - * stored in the colormap, an error is returned. - * (4) In the implementation, a mapping is generated from each - * original blender colormap index to the corresponding index - * in the expanded colormap for pixs. Then for each pixel in - * pixs with value sindex, and which is covered by a blender pixel, - * the new index corresponding to the blender pixel is substituted - * for sindex. - *- */ -l_ok -pixBlendCmap(PIX *pixs, - PIX *pixb, - l_int32 x, - l_int32 y, - l_int32 sindex) -{ -l_int32 rval, gval, bval; -l_int32 i, j, w, h, d, ncb, wb, hb, wpls; -l_int32 index, val, nadded; -l_int32 lut[256]; -l_uint32 pval; -l_uint32 *lines, *datas; -PIXCMAP *cmaps, *cmapb, *cmapsc; - - PROCNAME("pixBlendCmap"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!pixb) - return ERROR_INT("pixb not defined", procName, 1); - if ((cmaps = pixGetColormap(pixs)) == NULL) - return ERROR_INT("no colormap in pixs", procName, 1); - if ((cmapb = pixGetColormap(pixb)) == NULL) - return ERROR_INT("no colormap in pixb", procName, 1); - ncb = pixcmapGetCount(cmapb); - - pixGetDimensions(pixs, &w, &h, &d); - if (d != 2 && d != 4 && d != 8) - return ERROR_INT("depth not in {2,4,8}", procName, 1); - - /* Make a copy of cmaps; we'll add to this if necessary - * and substitute at the end if we found there was enough room - * to hold all the new colors. */ - cmapsc = pixcmapCopy(cmaps); - - /* Add new colors if necessary; get mapping array between - * cmaps and cmapb. */ - for (i = 0, nadded = 0; i < ncb; i++) { - pixcmapGetColor(cmapb, i, &rval, &gval, &bval); - if (pixcmapGetIndex(cmapsc, rval, gval, bval, &index)) { /* not found */ - if (pixcmapAddColor(cmapsc, rval, gval, bval)) { - pixcmapDestroy(&cmapsc); - return ERROR_INT("not enough room in cmaps", procName, 1); - } - lut[i] = pixcmapGetCount(cmapsc) - 1; - nadded++; - } else { - lut[i] = index; - } - } - - /* Replace cmaps if colors have been added. */ - if (nadded == 0) - pixcmapDestroy(&cmapsc); - else - pixSetColormap(pixs, cmapsc); - - /* Replace each pixel value sindex by mapped colormap index when - * a blender pixel in pixbc overlays it. */ - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - pixGetDimensions(pixb, &wb, &hb, NULL); - for (i = 0; i < hb; i++) { - if (i + y < 0 || i + y >= h) continue; - lines = datas + (y + i) * wpls; - for (j = 0; j < wb; j++) { - if (j + x < 0 || j + x >= w) continue; - switch (d) { - case 2: - val = GET_DATA_DIBIT(lines, x + j); - if (val == sindex) { - pixGetPixel(pixb, j, i, &pval); - SET_DATA_DIBIT(lines, x + j, lut[pval]); - } - break; - case 4: - val = GET_DATA_QBIT(lines, x + j); - if (val == sindex) { - pixGetPixel(pixb, j, i, &pval); - SET_DATA_QBIT(lines, x + j, lut[pval]); - } - break; - case 8: - val = GET_DATA_BYTE(lines, x + j); - if (val == sindex) { - pixGetPixel(pixb, j, i, &pval); - SET_DATA_BYTE(lines, x + j, lut[pval]); - } - break; - default: - return ERROR_INT("depth not in {2,4,8}", procName, 1); - } - } - } - - return 0; -} - - -/*---------------------------------------------------------------------* - * Blending two images using a third * - *---------------------------------------------------------------------*/ -/*! - * \brief pixBlendWithGrayMask() - * - * \param[in] pixs1 8 bpp gray, rgb, rgba or colormapped - * \param[in] pixs2 8 bpp gray, rgb, rgba or colormapped - * \param[in] pixg [optional] 8 bpp gray, for transparency of pixs2; - * can be null - * \param[in] x, y UL corner of pixs2 and pixg with respect to pixs1 - * \return pixd blended image, or null on error - * - *
- * Notes: - * (1) The result is 8 bpp grayscale if both pixs1 and pixs2 are - * 8 bpp gray. Otherwise, the result is 32 bpp rgb. - * (2) pixg is an 8 bpp transparency image, where 0 is transparent - * and 255 is opaque. It determines the transparency of pixs2 - * when applied over pixs1. It can be null if pixs2 is rgba, - * in which case we use the alpha component of pixs2. - * (3) If pixg exists, it need not be the same size as pixs2. - * However, we assume their UL corners are aligned with each other, - * and placed at the location (x, y) in pixs1. - * (4) The pixels in pixd are a combination of those in pixs1 - * and pixs2, where the amount from pixs2 is proportional to - * the value of the pixel (p) in pixg, and the amount from pixs1 - * is proportional to (255 - p). Thus pixg is a transparency - * image (usually called an alpha blender) where each pixel - * can be associated with a pixel in pixs2, and determines - * the amount of the pixs2 pixel in the final result. - * For example, if pixg is all 0, pixs2 is transparent and - * the result in pixd is simply pixs1. - * (5) A typical use is for the pixs2/pixg combination to be - * a small watermark that is applied to pixs1. - *- */ -PIX * -pixBlendWithGrayMask(PIX *pixs1, - PIX *pixs2, - PIX *pixg, - l_int32 x, - l_int32 y) -{ -l_int32 w1, h1, d1, w2, h2, d2, spp, wg, hg, wmin, hmin, wpld, wpls, wplg; -l_int32 i, j, val, dval, sval; -l_int32 drval, dgval, dbval, srval, sgval, sbval; -l_uint32 dval32, sval32; -l_uint32 *datad, *datas, *datag, *lined, *lines, *lineg; -l_float32 fract; -PIX *pixr1, *pixr2, *pix1, *pix2, *pixg2, *pixd; - - PROCNAME("pixBlendWithGrayMask"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); - pixGetDimensions(pixs1, &w1, &h1, &d1); - pixGetDimensions(pixs2, &w2, &h2, &d2); - if (d1 == 1 || d2 == 1) - return (PIX *)ERROR_PTR("pixs1 or pixs2 is 1 bpp", procName, NULL); - if (pixg) { - if (pixGetDepth(pixg) != 8) - return (PIX *)ERROR_PTR("pixg not 8 bpp", procName, NULL); - pixGetDimensions(pixg, &wg, &hg, NULL); - wmin = L_MIN(w2, wg); - hmin = L_MIN(h2, hg); - pixg2 = pixClone(pixg); - } else { /* use the alpha component of pixs2 */ - spp = pixGetSpp(pixs2); - if (d2 != 32 || spp != 4) - return (PIX *)ERROR_PTR("no alpha; pixs2 not rgba", procName, NULL); - wmin = w2; - hmin = h2; - pixg2 = pixGetRGBComponent(pixs2, L_ALPHA_CHANNEL); - } - - /* Remove colormaps if they exist; clones are OK */ - pixr1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); - pixr2 = pixRemoveColormap(pixs2, REMOVE_CMAP_BASED_ON_SRC); - - /* Regularize to the same depth if necessary */ - d1 = pixGetDepth(pixr1); - d2 = pixGetDepth(pixr2); - if (d1 == 32) { /* convert d2 to rgb if necessary */ - pix1 = pixClone(pixr1); - if (d2 != 32) - pix2 = pixConvertTo32(pixr2); - else - pix2 = pixClone(pixr2); - } else if (d2 == 32) { /* and d1 != 32; convert to 32 */ - pix2 = pixClone(pixr2); - pix1 = pixConvertTo32(pixr1); - } else { /* both are 8 bpp or less */ - pix1 = pixConvertTo8(pixr1, FALSE); - pix2 = pixConvertTo8(pixr2, FALSE); - } - pixDestroy(&pixr1); - pixDestroy(&pixr2); - - /* Sanity check: both either 8 or 32 bpp */ - d1 = pixGetDepth(pix1); - d2 = pixGetDepth(pix2); - if (d1 != d2 || (d1 != 8 && d1 != 32)) { - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pixg2); - return (PIX *)ERROR_PTR("depths not regularized! bad!", procName, NULL); - } - - /* Start with a copy of pix1 */ - pixd = pixCopy(NULL, pix1); - pixDestroy(&pix1); - - /* Blend pix2 onto pixd, using pixg2. - * Let the normalized pixel value of pixg2 be f = pixval / 255, - * and the pixel values of pixd and pix2 be p1 and p2, rsp. - * Then the blended value is: - * p = (1.0 - f) * p1 + f * p2 - * Blending is done component-wise if rgb. - * Scan over pix2 and pixg2, clipping to pixd where necessary. */ - datad = pixGetData(pixd); - datas = pixGetData(pix2); - datag = pixGetData(pixg2); - wpld = pixGetWpl(pixd); - wpls = pixGetWpl(pix2); - wplg = pixGetWpl(pixg2); - for (i = 0; i < hmin; i++) { - if (i + y < 0 || i + y >= h1) continue; - lined = datad + (i + y) * wpld; - lines = datas + i * wpls; - lineg = datag + i * wplg; - for (j = 0; j < wmin; j++) { - if (j + x < 0 || j + x >= w1) continue; - val = GET_DATA_BYTE(lineg, j); - if (val == 0) continue; /* pix2 is transparent */ - fract = (l_float32)val / 255.; - if (d1 == 8) { - dval = GET_DATA_BYTE(lined, j + x); - sval = GET_DATA_BYTE(lines, j); - dval = (l_int32)((1.0 - fract) * dval + fract * sval); - SET_DATA_BYTE(lined, j + x, dval); - } else { /* 32 */ - dval32 = *(lined + j + x); - sval32 = *(lines + j); - extractRGBValues(dval32, &drval, &dgval, &dbval); - extractRGBValues(sval32, &srval, &sgval, &sbval); - drval = (l_int32)((1.0 - fract) * drval + fract * srval); - dgval = (l_int32)((1.0 - fract) * dgval + fract * sgval); - dbval = (l_int32)((1.0 - fract) * dbval + fract * sbval); - composeRGBPixel(drval, dgval, dbval, &dval32); - *(lined + j + x) = dval32; - } - } - } - - pixDestroy(&pixg2); - pixDestroy(&pix2); - return pixd; -} - - -/*---------------------------------------------------------------------* - * Blending background to a specific color * - *---------------------------------------------------------------------*/ -/*! - * \brief pixBlendBackgroundToColor() - * - * \param[in] pixd can be NULL or pixs - * \param[in] pixs 32 bpp rgb - * \param[in] box region for blending; can be NULL) - * \param[in] color 32 bit color in 0xrrggbb00 format - * \param[in] gamma, minval, maxval args for grayscale TRC mapping - * \return pixd always - * - *
- * Notes: - * (1) This in effect replaces light background pixels in pixs - * by the input color. It does it by alpha blending so that - * there are no visible artifacts from hard cutoffs. - * (2) If pixd == pixs, this is done in-place. - * (3) If box == NULL, this is performed on all of pixs. - * (4) The alpha component for blending is derived from pixs, - * by converting to grayscale and enhancing with a TRC. - * (5) The last three arguments specify the TRC operation. - * Suggested values are: %gamma = 0.3, %minval = 50, %maxval = 200. - * To skip the TRC, use %gamma == 1, %minval = 0, %maxval = 255. - * See pixGammaTRC() for details. - *- */ -PIX * -pixBlendBackgroundToColor(PIX *pixd, - PIX *pixs, - BOX *box, - l_uint32 color, - l_float32 gamma, - l_int32 minval, - l_int32 maxval) -{ -l_int32 x, y, w, h; -BOX *boxt; -PIX *pixt, *pixc, *pixr, *pixg; - - PROCNAME("pixBlendBackgroundToColor"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, pixd); - if (pixd && (pixd != pixs)) - return (PIX *)ERROR_PTR("pixd neither null nor pixs", procName, pixd); - - /* Extract the (optionally cropped) region, pixr, and generate - * an identically sized pixc with the uniform color. */ - if (!pixd) - pixd = pixCopy(NULL, pixs); - if (box) { - pixr = pixClipRectangle(pixd, box, &boxt); - boxGetGeometry(boxt, &x, &y, &w, &h); - pixc = pixCreate(w, h, 32); - boxDestroy(&boxt); - } else { - pixc = pixCreateTemplate(pixs); - pixr = pixClone(pixd); - } - pixSetAllArbitrary(pixc, color); - - /* Set up the alpha channel */ - pixg = pixConvertTo8(pixr, 0); - pixGammaTRC(pixg, pixg, gamma, minval, maxval); - pixSetRGBComponent(pixc, pixg, L_ALPHA_CHANNEL); - - /* Blend and replace in pixd */ - pixt = pixBlendWithGrayMask(pixr, pixc, NULL, 0, 0); - if (box) { - pixRasterop(pixd, x, y, w, h, PIX_SRC, pixt, 0, 0); - pixDestroy(&pixt); - } else { - pixTransferAllData(pixd, &pixt, 0, 0); - } - - pixDestroy(&pixc); - pixDestroy(&pixr); - pixDestroy(&pixg); - return pixd; -} - - -/*---------------------------------------------------------------------* - * Multiplying by a specific color * - *---------------------------------------------------------------------*/ -/*! - * \brief pixMultiplyByColor() - * - * \param[in] pixd can be NULL or pixs - * \param[in] pixs 32 bpp rgb - * \param[in] box region for filtering; can be NULL) - * \param[in] color 32 bit color in 0xrrggbb00 format - * \return pixd always - * - *
- * Notes: - * (1) This filters all pixels in the specified region by - * multiplying each component by the input color. - * This leaves black invariant and transforms white to the - * input color. - * (2) If pixd == pixs, this is done in-place. - * (3) If box == NULL, this is performed on all of pixs. - *- */ -PIX * -pixMultiplyByColor(PIX *pixd, - PIX *pixs, - BOX *box, - l_uint32 color) -{ -l_int32 i, j, bx, by, w, h, wpl; -l_int32 red, green, blue, rval, gval, bval, nrval, ngval, nbval; -l_float32 frval, fgval, fbval; -l_uint32 *data, *line; -PIX *pixt; - - PROCNAME("pixMultiplyByColor"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, pixd); - if (pixd && (pixd != pixs)) - return (PIX *)ERROR_PTR("pixd neither null nor pixs", procName, pixd); - - if (!pixd) - pixd = pixCopy(NULL, pixs); - if (box) { - boxGetGeometry(box, &bx, &by, NULL, NULL); - pixt = pixClipRectangle(pixd, box, NULL); - } else { - pixt = pixClone(pixd); - } - - /* Multiply each pixel in pixt by the color */ - extractRGBValues(color, &red, &green, &blue); - frval = (1. / 255.) * red; - fgval = (1. / 255.) * green; - fbval = (1. / 255.) * blue; - data = pixGetData(pixt); - wpl = pixGetWpl(pixt); - pixGetDimensions(pixt, &w, &h, NULL); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - extractRGBValues(line[j], &rval, &gval, &bval); - nrval = (l_int32)(frval * rval + 0.5); - ngval = (l_int32)(fgval * gval + 0.5); - nbval = (l_int32)(fbval * bval + 0.5); - composeRGBPixel(nrval, ngval, nbval, line + j); - } - } - - /* Replace */ - if (box) - pixRasterop(pixd, bx, by, w, h, PIX_SRC, pixt, 0, 0); - pixDestroy(&pixt); - return pixd; -} - - -/*---------------------------------------------------------------------* - * Rendering with alpha blending over a uniform background * - *---------------------------------------------------------------------*/ -/*! - * \brief pixAlphaBlendUniform() - * - * \param[in] pixs 32 bpp rgba, with alpha - * \param[in] color 32 bit color in 0xrrggbb00 format - * \return pixd 32 bpp rgb: pixs blended over uniform color %color, - * a clone of pixs if no alpha, and null on error - * - *
- * Notes: - * (1) This is a convenience function that renders 32 bpp RGBA images - * (with an alpha channel) over a uniform background of - * value %color. To render over a white background, - * use %color = 0xffffff00. The result is an RGB image. - * (2) If pixs does not have an alpha channel, it returns a clone - * of pixs. - *- */ -PIX * -pixAlphaBlendUniform(PIX *pixs, - l_uint32 color) -{ -PIX *pixt, *pixd; - - PROCNAME("pixAlphaBlendUniform"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (pixGetSpp(pixs) != 4) { - L_WARNING("no alpha channel; returning clone\n", procName); - return pixClone(pixs); - } - - pixt = pixCreateTemplate(pixs); - pixSetAllArbitrary(pixt, color); - pixSetSpp(pixt, 3); /* not required */ - pixd = pixBlendWithGrayMask(pixt, pixs, NULL, 0, 0); - - pixDestroy(&pixt); - return pixd; -} - - -/*---------------------------------------------------------------------* - * Adding an alpha layer for blending * - *---------------------------------------------------------------------*/ -/*! - * \brief pixAddAlphaToBlend() - * - * \param[in] pixs any depth - * \param[in] fract fade fraction in the alpha component - * \param[in] invert 1 to photometrically invert pixs - * \return pixd 32 bpp with alpha, or null on error - * - *
- * Notes: - * (1) This is a simple alpha layer generator, where typically white has - * maximum transparency and black has minimum. - * (2) If %invert == 1, generate the same alpha layer but invert - * the input image photometrically. This is useful for blending - * over dark images, where you want dark regions in pixs, such - * as text, to be lighter in the blended image. - * (3) The fade %fract gives the minimum transparency (i.e., - * maximum opacity). A small fraction is useful for adding - * a watermark to an image. - * (4) If pixs has a colormap, it is removed to rgb. - * (5) If pixs already has an alpha layer, it is overwritten. - *- */ -PIX * -pixAddAlphaToBlend(PIX *pixs, - l_float32 fract, - l_int32 invert) -{ -PIX *pixd, *pix1, *pix2; - - PROCNAME("pixAddAlphaToBlend"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (fract < 0.0 || fract > 1.0) - return (PIX *)ERROR_PTR("invalid fract", procName, NULL); - - /* Convert to 32 bpp */ - if (pixGetColormap(pixs)) - pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); - else - pix1 = pixClone(pixs); - pixd = pixConvertTo32(pix1); /* new */ - - /* Use an inverted image if this will be blended with a dark image */ - if (invert) pixInvert(pixd, pixd); - - /* Generate alpha layer */ - pix2 = pixConvertTo8(pix1, 0); /* new */ - pixInvert(pix2, pix2); - pixMultConstantGray(pix2, fract); - pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); - - pixDestroy(&pix1); - pixDestroy(&pix2); - return pixd; -} - - - -/*---------------------------------------------------------------------* - * Setting a transparent alpha component over a white background * - *---------------------------------------------------------------------*/ -/*! - * \brief pixSetAlphaOverWhite() - * - * \param[in] pixs colormapped or 32 bpp rgb; no alpha - * \return pixd new pix with meaningful alpha component, - * or null on error - * - *
- * Notes: - * (1) The generated alpha component is transparent over white - * (background) pixels in pixs, and quickly grades to opaque - * away from the transparent parts. This is a cheap and - * dirty alpha generator. The 2 pixel gradation is useful - * to blur the boundary between the transparent region - * (that will render entirely from a backing image) and - * the remainder which renders from pixs. - * (2) All alpha component bits in pixs are overwritten. - *- */ -PIX * -pixSetAlphaOverWhite(PIX *pixs) -{ -PIX *pixd, *pix1, *pix2, *pix3, *pix4; - - PROCNAME("pixSetAlphaOverWhite"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!(pixGetDepth(pixs) == 32 || pixGetColormap(pixs))) - return (PIX *)ERROR_PTR("pixs not 32 bpp or cmapped", procName, NULL); - - /* Remove colormap if it exists; otherwise copy */ - pixd = pixRemoveColormapGeneral(pixs, REMOVE_CMAP_TO_FULL_COLOR, L_COPY); - - /* Generate a 1 bpp image where a white pixel in pixd is 0. - * In the comments below, a "white" pixel refers to pixd. - * pix1 is rgb, pix2 is 8 bpp gray, pix3 is 1 bpp. */ - pix1 = pixInvert(NULL, pixd); /* send white (255) to 0 for each sample */ - pix2 = pixConvertRGBToGrayMinMax(pix1, L_CHOOSE_MAX); /* 0 if white */ - pix3 = pixThresholdToBinary(pix2, 1); /* sets white pixels to 1 */ - pixInvert(pix3, pix3); /* sets white pixels to 0 */ - - /* Generate the alpha component using the distance transform, - * which measures the distance to the nearest bg (0) pixel in pix3. - * After multiplying by 128, its value is 0 (transparent) - * over white pixels, and goes to opaque (255) two pixels away - * from the nearest white pixel. */ - pix4 = pixDistanceFunction(pix3, 8, 8, L_BOUNDARY_FG); - pixMultConstantGray(pix4, 128.0); - pixSetRGBComponent(pixd, pix4, L_ALPHA_CHANNEL); - - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - pixDestroy(&pix4); - return pixd; -} - - -/*---------------------------------------------------------------------* - * Fading from the edge * - *---------------------------------------------------------------------*/ -/*! - * \brief pixLinearEdgeFade() - * - * \param[in] pixs 8 or 32 bpp; no colormap - * \param[in] dir L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOT - * \param[in] fadeto L_BLEND_TO_WHITE, L_BLEND_TO_BLACK - * \param[in] distfract fraction of width or height over which fading occurs - * \param[in] maxfade fraction of fading at the edge, <= 1.0 - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) In-place operation. - * (2) Maximum fading fraction %maxfade occurs at the edge of the image, - * and the fraction goes to 0 at the fractional distance %distfract - * from the edge. %maxfade must be in [0, 1]. - * (3) %distrfact must be in [0, 1], and typically it would be <= 0.5. - *- */ -l_ok -pixLinearEdgeFade(PIX *pixs, - l_int32 dir, - l_int32 fadeto, - l_float32 distfract, - l_float32 maxfade) -{ -l_int32 i, j, w, h, d, wpl, xmin, ymin, range, val, rval, gval, bval; -l_float32 slope, limit, del; -l_uint32 *data, *line; - - PROCNAME("pixLinearEdgeFade"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetColormap(pixs) != NULL) - return ERROR_INT("pixs has a colormap", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8 && d != 32) - return ERROR_INT("pixs not 8 or 32 bpp", procName, 1); - if (dir != L_FROM_LEFT && dir != L_FROM_RIGHT && - dir != L_FROM_TOP && dir != L_FROM_BOT) - return ERROR_INT("invalid fade direction from edge", procName, 1); - if (fadeto != L_BLEND_TO_WHITE && fadeto != L_BLEND_TO_BLACK) - return ERROR_INT("invalid fadeto photometry", procName, 1); - if (maxfade <= 0) return 0; - if (maxfade > 1.0) - return ERROR_INT("invalid maxfade", procName, 1); - if (distfract <= 0 || distfract * L_MIN(w, h) < 1.0) { - L_INFO("distfract is too small\n", procName); - return 0; - } - if (distfract > 1.0) - return ERROR_INT("invalid distfract", procName, 1); - - /* Set up parameters */ - if (dir == L_FROM_LEFT) { - range = (l_int32)(distfract * w); - xmin = 0; - slope = maxfade / (l_float32)range; - } else if (dir == L_FROM_RIGHT) { - range = (l_int32)(distfract * w); - xmin = w - range; - slope = maxfade / (l_float32)range; - } else if (dir == L_FROM_TOP) { - range = (l_int32)(distfract * h); - ymin = 0; - slope = maxfade / (l_float32)range; - } else if (dir == L_FROM_BOT) { - range = (l_int32)(distfract * h); - ymin = h - range; - slope = maxfade / (l_float32)range; - } - - limit = (fadeto == L_BLEND_TO_WHITE) ? 255.0 : 0.0; - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - if (dir == L_FROM_LEFT || dir == L_FROM_RIGHT) { - for (j = 0; j < range; j++) { - del = (dir == L_FROM_LEFT) ? maxfade - slope * j - : maxfade - slope * (range - j); - for (i = 0; i < h; i++) { - line = data + i * wpl; - if (d == 8) { - val = GET_DATA_BYTE(line, xmin + j); - val += (limit - val) * del + 0.5; - SET_DATA_BYTE(line, xmin + j, val); - } else { /* rgb */ - extractRGBValues(*(line + xmin + j), &rval, &gval, &bval); - rval += (limit - rval) * del + 0.5; - gval += (limit - gval) * del + 0.5; - bval += (limit - bval) * del + 0.5; - composeRGBPixel(rval, gval, bval, line + xmin + j); - } - } - } - } else { /* dir == L_FROM_TOP || L_FROM_BOT */ - for (i = 0; i < range; i++) { - del = (dir == L_FROM_TOP) ? maxfade - slope * i - : maxfade - slope * (range - i); - line = data + (ymin + i) * wpl; - for (j = 0; j < w; j++) { - if (d == 8) { - val = GET_DATA_BYTE(line, j); - val += (limit - val) * del + 0.5; - SET_DATA_BYTE(line, j, val); - } else { /* rgb */ - extractRGBValues(*(line + j), &rval, &gval, &bval); - rval += (limit - rval) * del + 0.5; - gval += (limit - gval) * del + 0.5; - bval += (limit - bval) * del + 0.5; - composeRGBPixel(rval, gval, bval, line + j); - } - } - } - } - - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmf.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmf.c deleted file mode 100644 index 2f03ddb9..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmf.c +++ /dev/null @@ -1,877 +0,0 @@ -/*====================================================================* - - 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 bmf.c - *
- * - * Acquisition and generation of bitmap fonts. - * - * L_BMF *bmfCreate() - * L_BMF *bmfDestroy() - * - * PIX *bmfGetPix() - * l_int32 bmfGetWidth() - * l_int32 bmfGetBaseline() - * - * PIXA *pixaGetFont() - * l_int32 pixaSaveFont() - * static PIXA *pixaGenerateFontFromFile() - * static PIXA *pixaGenerateFontFromString() - * static PIXA *pixaGenerateFont() - * static l_int32 pixGetTextBaseline() - * static l_int32 bmfMakeAsciiTables() - * - * This is not a very general utility, because it only uses bitmap - * representations of a single font, Palatino-Roman, with the - * normal style. It uses bitmaps generated for nine sizes, from - * 4 to 20 pts, rendered at 300 ppi. Generalization to different - * fonts, styles and sizes is straightforward. - * - * I chose Palatino-Roman is because I like it. - * The input font images were generated from a set of small - * PostScript files, such as chars-12.ps, which were rendered - * into the inputfont[] bitmap files using GhostScript. See, for - * example, the bash script prog/ps2tiff, which will "rip" a - * PostScript file into a set of ccitt-g4 compressed tiff files. - * - * The set of ascii characters from 32 through 126 are the 95 - * printable ascii chars. Palatino-Roman is missing char 92, '\'. - * I have substituted an LR flip of '/', char 47, for 92, so that - * there are no missing printable chars in this set. The space is - * char 32, and I have given it a width equal to twice the width of '!'. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) If %dir == null, this generates the font bitmaps from a - * compiled string. - * (2) Otherwise, this tries to read a pre-computed pixa file with the - * 95 ascii chars in it. If the file is not found, it then - * attempts to generate the pixa and associated baseline - * data from a tiff image containing all the characters. If - * that fails, it uses the compiled string. - *- */ -L_BMF * -bmfCreate(const char *dir, - l_int32 fontsize) -{ -L_BMF *bmf; -PIXA *pixa; - - PROCNAME("bmfCreate"); - - if (fontsize < 4 || fontsize > 20 || (fontsize % 2)) - return (L_BMF *)ERROR_PTR("fontsize must be in {4, 6, ..., 20}", - procName, NULL); - - bmf = (L_BMF *)LEPT_CALLOC(1, sizeof(L_BMF)); - - if (!dir) { /* Generate from a string */ - pixa = pixaGenerateFontFromString(fontsize, &bmf->baseline1, - &bmf->baseline2, &bmf->baseline3); - } else { /* Look for the pixa in a directory */ - pixa = pixaGetFont(dir, fontsize, &bmf->baseline1, &bmf->baseline2, - &bmf->baseline3); - if (!pixa) { /* Not found; make it from a file */ - L_INFO("Generating pixa of bitmap fonts from file\n", procName); - pixa = pixaGenerateFontFromFile(dir, fontsize, &bmf->baseline1, - &bmf->baseline2, &bmf->baseline3); - if (!pixa) { /* Not made; make it from a string after all */ - L_ERROR("Failed to make font; use string\n", procName); - pixa = pixaGenerateFontFromString(fontsize, &bmf->baseline1, - &bmf->baseline2, &bmf->baseline3); - } - } - } - - if (!pixa) { - bmfDestroy(&bmf); - return (L_BMF *)ERROR_PTR("font pixa not made", procName, NULL); - } - - bmf->pixa = pixa; - bmf->size = fontsize; - if (dir) bmf->directory = stringNew(dir); - bmfMakeAsciiTables(bmf); - return bmf; -} - - -/*! - * \brief bmfDestroy() - * - * \param[in,out] pbmf will be set to null before returning - * \return void - */ -void -bmfDestroy(L_BMF **pbmf) -{ -L_BMF *bmf; - - PROCNAME("bmfDestroy"); - - if (pbmf == NULL) { - L_WARNING("ptr address is null!\n", procName); - return; - } - - if ((bmf = *pbmf) == NULL) - return; - - pixaDestroy(&bmf->pixa); - LEPT_FREE(bmf->directory); - LEPT_FREE(bmf->fonttab); - LEPT_FREE(bmf->baselinetab); - LEPT_FREE(bmf->widthtab); - LEPT_FREE(bmf); - *pbmf = NULL; - return; -} - - -/*---------------------------------------------------------------------*/ -/* Bmf accessors */ -/*---------------------------------------------------------------------*/ -/*! - * \brief bmfGetPix() - * - * \param[in] bmf - * \param[in] chr should be one of the 95 supported printable bitmaps - * \return pix clone of pix in bmf, or NULL on error - */ -PIX * -bmfGetPix(L_BMF *bmf, - char chr) -{ -l_int32 i, index; -PIXA *pixa; - - PROCNAME("bmfGetPix"); - - if ((index = (l_int32)chr) == 10) /* NL */ - return NULL; - if (!bmf) - return (PIX *)ERROR_PTR("bmf not defined", procName, NULL); - - i = bmf->fonttab[index]; - if (i == UNDEF) { - L_ERROR("no bitmap representation for %d\n", procName, index); - return NULL; - } - - if ((pixa = bmf->pixa) == NULL) - return (PIX *)ERROR_PTR("pixa not found", procName, NULL); - - return pixaGetPix(pixa, i, L_CLONE); -} - - -/*! - * \brief bmfGetWidth() - * - * \param[in] bmf - * \param[in] chr should be one of the 95 supported bitmaps - * \param[out] pw character width; -1 if not printable - * \return 0 if OK, 1 on error - */ -l_ok -bmfGetWidth(L_BMF *bmf, - char chr, - l_int32 *pw) -{ -l_int32 i, index; -PIXA *pixa; - - PROCNAME("bmfGetWidth"); - - if (!pw) - return ERROR_INT("&w not defined", procName, 1); - *pw = -1; - if (!bmf) - return ERROR_INT("bmf not defined", procName, 1); - if ((index = (l_int32)chr) == 10) /* NL */ - return 0; - - i = bmf->fonttab[index]; - if (i == UNDEF) { - L_ERROR("no bitmap representation for %d\n", procName, index); - return 1; - } - - if ((pixa = bmf->pixa) == NULL) - return ERROR_INT("pixa not found", procName, 1); - - return pixaGetPixDimensions(pixa, i, pw, NULL, NULL); -} - - -/*! - * \brief bmfGetBaseline() - * - * \param[in] bmf - * \param[in] chr should be one of the 95 supported bitmaps - * \param[out] pbaseline distance below UL corner of bitmap char - * \return 0 if OK, 1 on error - */ -l_ok -bmfGetBaseline(L_BMF *bmf, - char chr, - l_int32 *pbaseline) -{ -l_int32 bl, index; - - PROCNAME("bmfGetBaseline"); - - if (!pbaseline) - return ERROR_INT("&baseline not defined", procName, 1); - *pbaseline = 0; - if (!bmf) - return ERROR_INT("bmf not defined", procName, 1); - if ((index = (l_int32)chr) == 10) /* NL */ - return 0; - - bl = bmf->baselinetab[index]; - if (bl == UNDEF) { - L_ERROR("no bitmap representation for %d\n", procName, index); - return 1; - } - - *pbaseline = bl; - return 0; -} - - -/*---------------------------------------------------------------------*/ -/* Font bitmap acquisition and generation */ -/*---------------------------------------------------------------------*/ -/*! - * \brief pixaGetFont() - * - * \param[in] dir directory holding pixa of character set - * \param[in] fontsize 4, 6, 8, ... , 20 - * \param[out] pbl0 baseline of row 1 - * \param[out] pbl1 baseline of row 2 - * \param[out] pbl2 baseline of row 3 - * \return pixa of font bitmaps for 95 characters, or NULL on error - * - *
- * Notes: - * (1) This reads a pre-computed pixa file with the 95 ascii chars. - *- */ -PIXA * -pixaGetFont(const char *dir, - l_int32 fontsize, - l_int32 *pbl0, - l_int32 *pbl1, - l_int32 *pbl2) -{ -char *pathname; -l_int32 fileno; -PIXA *pixa; - - PROCNAME("pixaGetFont"); - - fileno = (fontsize / 2) - 2; - if (fileno < 0 || fileno >= NUM_FONTS) - return (PIXA *)ERROR_PTR("font size invalid", procName, NULL); - if (!pbl0 || !pbl1 || !pbl2) - return (PIXA *)ERROR_PTR("&bl not all defined", procName, NULL); - *pbl0 = baselines[fileno][0]; - *pbl1 = baselines[fileno][1]; - *pbl2 = baselines[fileno][2]; - - pathname = pathJoin(dir, outputfonts[fileno]); - pixa = pixaRead(pathname); - LEPT_FREE(pathname); - - if (!pixa) - L_WARNING("pixa of char bitmaps not found\n", procName); - return pixa; -} - - -/*! - * \brief pixaSaveFont() - * - * \param[in] indir [optional] directory holding image of character set - * \param[in] outdir directory into which the output pixa file - * will be written - * \param[in] fontsize in pts, at 300 ppi - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This saves a font of a particular size. - * (2) If %indir == null, this generates the font bitmaps from a - * compiled string. - * (3) prog/genfonts calls this function for each of the - * nine font sizes, to generate all the font pixa files. - *- */ -l_ok -pixaSaveFont(const char *indir, - const char *outdir, - l_int32 fontsize) -{ -char *pathname; -l_int32 bl1, bl2, bl3; -PIXA *pixa; - - PROCNAME("pixaSaveFont"); - - if (fontsize < 4 || fontsize > 20 || (fontsize % 2)) - return ERROR_INT("fontsize must be in {4, 6, ..., 20}", procName, 1); - - if (!indir) /* Generate from a string */ - pixa = pixaGenerateFontFromString(fontsize, &bl1, &bl2, &bl3); - else /* Generate from an image file */ - pixa = pixaGenerateFontFromFile(indir, fontsize, &bl1, &bl2, &bl3); - if (!pixa) - return ERROR_INT("pixa not made", procName, 1); - - pathname = pathJoin(outdir, outputfonts[(fontsize - 4) / 2]); - pixaWrite(pathname, pixa); - -#if DEBUG_FONT_GEN - L_INFO("Found %d chars in font size %d\n", procName, pixaGetCount(pixa), - fontsize); - L_INFO("Baselines are at: %d, %d, %d\n", procName, bl1, bl2, bl3); -#endif /* DEBUG_FONT_GEN */ - - LEPT_FREE(pathname); - pixaDestroy(&pixa); - return 0; -} - - -/*! - * \brief pixaGenerateFontFromFile() - * - * \param[in] dir directory holding image of character set - * \param[in] fontsize 4, 6, 8, ... , 20, in pts at 300 ppi - * \param[out] pbl0 baseline of row 1 - * \param[out] pbl1 baseline of row 2 - * \param[out] pbl2 baseline of row 3 - * \return pixa of font bitmaps for 95 characters, or NULL on error - * - * These font generation functions use 9 sets, each with bitmaps - * of 94 ascii characters, all in Palatino-Roman font. - * Each input bitmap has 3 rows of characters. The range of - * ascii values in each row is as follows: - * row 0: 32-57 32 is a space - * row 1: 58-91 92, '\', is not represented in this font - * row 2: 93-126 - * We LR flip the '/' char to generate a bitmap for the missing - * '\' character, so that we have representations of all 95 - * printable chars. - * - * Typically, use pixaGetFont() to generate the character bitmaps - * in memory for a bmf. This will simply access the bitmap files - * in a serialized pixa that were produced in prog/genfonts.c using - * this function. - */ -static PIXA * -pixaGenerateFontFromFile(const char *dir, - l_int32 fontsize, - l_int32 *pbl0, - l_int32 *pbl1, - l_int32 *pbl2) -{ -char *pathname; -l_int32 fileno; -PIX *pix; -PIXA *pixa; - - PROCNAME("pixaGenerateFontFromFile"); - - if (!pbl0 || !pbl1 || !pbl2) - return (PIXA *)ERROR_PTR("&bl not all defined", procName, NULL); - *pbl0 = *pbl1 = *pbl2 = 0; - if (!dir) - return (PIXA *)ERROR_PTR("dir not defined", procName, NULL); - fileno = (fontsize / 2) - 2; - if (fileno < 0 || fileno >= NUM_FONTS) - return (PIXA *)ERROR_PTR("font size invalid", procName, NULL); - - pathname = pathJoin(dir, inputfonts[fileno]); - pix = pixRead(pathname); - LEPT_FREE(pathname); - if (!pix) { - L_ERROR("pix not found for font size %d\n", procName, fontsize); - return NULL; - } - - pixa = pixaGenerateFont(pix, fontsize, pbl0, pbl1, pbl2); - pixDestroy(&pix); - return pixa; -} - - -/*! - * \brief pixaGenerateFontFromString() - * - * \param[in] fontsize 4, 6, 8, ... , 20, in pts at 300 ppi - * \param[out] pbl0 baseline of row 1 - * \param[out] pbl1 baseline of row 2 - * \param[out] pbl2 baseline of row 3 - * \return pixa of font bitmaps for 95 characters, or NULL on error - * - *
- * Notes: - * (1) See pixaGenerateFontFromFile() for details. - *- */ -static PIXA * -pixaGenerateFontFromString(l_int32 fontsize, - l_int32 *pbl0, - l_int32 *pbl1, - l_int32 *pbl2) -{ -l_uint8 *data; -l_int32 redsize, nbytes; -PIX *pix; -PIXA *pixa; - - PROCNAME("pixaGenerateFontFromString"); - - if (!pbl0 || !pbl1 || !pbl2) - return (PIXA *)ERROR_PTR("&bl not all defined", procName, NULL); - *pbl0 = *pbl1 = *pbl2 = 0; - redsize = (fontsize / 2) - 2; - if (redsize < 0 || redsize >= NUM_FONTS) - return (PIXA *)ERROR_PTR("invalid font size", procName, NULL); - - if (fontsize == 4) { - data = decodeBase64(fontdata_4, strlen(fontdata_4), &nbytes); - } else if (fontsize == 6) { - data = decodeBase64(fontdata_6, strlen(fontdata_6), &nbytes); - } else if (fontsize == 8) { - data = decodeBase64(fontdata_8, strlen(fontdata_8), &nbytes); - } else if (fontsize == 10) { - data = decodeBase64(fontdata_10, strlen(fontdata_10), &nbytes); - } else if (fontsize == 12) { - data = decodeBase64(fontdata_12, strlen(fontdata_12), &nbytes); - } else if (fontsize == 14) { - data = decodeBase64(fontdata_14, strlen(fontdata_14), &nbytes); - } else if (fontsize == 16) { - data = decodeBase64(fontdata_16, strlen(fontdata_16), &nbytes); - } else if (fontsize == 18) { - data = decodeBase64(fontdata_18, strlen(fontdata_18), &nbytes); - } else { /* fontsize == 20 */ - data = decodeBase64(fontdata_20, strlen(fontdata_20), &nbytes); - } - if (!data) - return (PIXA *)ERROR_PTR("data not made", procName, NULL); - - pix = pixReadMem(data, nbytes); - LEPT_FREE(data); - if (!pix) - return (PIXA *)ERROR_PTR("pix not made", procName, NULL); - - pixa = pixaGenerateFont(pix, fontsize, pbl0, pbl1, pbl2); - pixDestroy(&pix); - return pixa; -} - - -/*! - * \brief pixaGenerateFont() - * - * \param[in] pixs of 95 characters in 3 rows - * \param[in] fontsize 4, 6, 8, ... , 20, in pts at 300 ppi - * \param[out] pbl0 baseline of row 1 - * \param[out] pbl1 baseline of row 2 - * \param[out] pbl2 baseline of row 3 - * \return pixa of font bitmaps for 95 characters, or NULL on error - * - *
- * Notes: - * (1) This does all the work. See pixaGenerateFontFromFile() - * for an overview. - * (2) The pix is for one of the 9 fonts. %fontsize is only - * used here for debugging. - *- */ -static PIXA * -pixaGenerateFont(PIX *pixs, - l_int32 fontsize, - l_int32 *pbl0, - l_int32 *pbl1, - l_int32 *pbl2) -{ -l_int32 i, j, nrows, nrowchars, nchars, h, yval; -l_int32 width, height; -l_int32 baseline[3]; -l_int32 *tab = NULL; -BOX *box, *box1, *box2; -BOXA *boxar, *boxac, *boxacs; -PIX *pix1, *pix2, *pixr, *pixrc, *pixc; -PIXA *pixa; -l_int32 n, w, inrow, top; -l_int32 *ia; -NUMA *na; - - PROCNAME("pixaGenerateFont"); - - if (!pbl0 || !pbl1 || !pbl2) - return (PIXA *)ERROR_PTR("&bl not all defined", procName, NULL); - *pbl0 = *pbl1 = *pbl2 = 0; - if (!pixs) - return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); - - /* Locate the 3 rows of characters */ - w = pixGetWidth(pixs); - na = pixCountPixelsByRow(pixs, NULL); - boxar = boxaCreate(0); - n = numaGetCount(na); - ia = numaGetIArray(na); - inrow = 0; - for (i = 0; i < n; i++) { - if (!inrow && ia[i] > 0) { - inrow = 1; - top = i; - } else if (inrow && ia[i] == 0) { - inrow = 0; - box = boxCreate(0, top, w, i - top); - boxaAddBox(boxar, box, L_INSERT); - } - } - LEPT_FREE(ia); - numaDestroy(&na); - nrows = boxaGetCount(boxar); -#if DEBUG_FONT_GEN - L_INFO("For fontsize %s, have %d rows\n", procName, fontsize, nrows); -#endif /* DEBUG_FONT_GEN */ - if (nrows != 3) { - L_INFO("nrows = %d; skipping fontsize %d\n", procName, nrows, fontsize); - boxaDestroy(&boxar); - return (PIXA *)ERROR_PTR("3 rows not generated", procName, NULL); - } - - /* Grab the character images and baseline data */ -#if DEBUG_BASELINE - lept_rmdir("baseline"); - lept_mkdir("baseline"); -#endif /* DEBUG_BASELINE */ - tab = makePixelSumTab8(); - pixa = pixaCreate(95); - for (i = 0; i < nrows; i++) { - box = boxaGetBox(boxar, i, L_CLONE); - pixr = pixClipRectangle(pixs, box, NULL); /* row of chars */ - pixGetTextBaseline(pixr, tab, &yval); - baseline[i] = yval; - -#if DEBUG_BASELINE - L_INFO("Baseline info: row %d, yval = %d, h = %d\n", procName, - i, yval, pixGetHeight(pixr)); - pix1 = pixCopy(NULL, pixr); - pixRenderLine(pix1, 0, yval, pixGetWidth(pix1), yval, 1, - L_FLIP_PIXELS); - if (i == 0 ) - pixWriteDebug("/tmp/baseline/row0.png", pix1, IFF_PNG); - else if (i == 1) - pixWriteDebug("/tmp/baseline/row1.png", pix1, IFF_PNG); - else - pixWriteDebug("/tmp/baseline/row2.png", pix1, IFF_PNG); - pixDestroy(&pix1); -#endif /* DEBUG_BASELINE */ - - boxDestroy(&box); - pixrc = pixCloseSafeBrick(NULL, pixr, 1, 35); - boxac = pixConnComp(pixrc, NULL, 8); - boxacs = boxaSort(boxac, L_SORT_BY_X, L_SORT_INCREASING, NULL); - if (i == 0) { /* consolidate the two components of '"' */ - box1 = boxaGetBox(boxacs, 1, L_CLONE); - box2 = boxaGetBox(boxacs, 2, L_CLONE); - box1->w = box2->x + box2->w - box1->x; /* increase width */ - boxDestroy(&box1); - boxDestroy(&box2); - boxaRemoveBox(boxacs, 2); - } - h = pixGetHeight(pixr); - nrowchars = boxaGetCount(boxacs); - for (j = 0; j < nrowchars; j++) { - box = boxaGetBox(boxacs, j, L_COPY); - if (box->w <= 2 && box->h == 1) { /* skip 1x1, 2x1 components */ - boxDestroy(&box); - continue; - } - box->y = 0; - box->h = h - 1; - pixc = pixClipRectangle(pixr, box, NULL); - boxDestroy(&box); - if (i == 0 && j == 0) /* add a pix for the space; change later */ - pixaAddPix(pixa, pixc, L_COPY); - if (i == 2 && j == 0) /* add a pix for the '\'; change later */ - pixaAddPix(pixa, pixc, L_COPY); - pixaAddPix(pixa, pixc, L_INSERT); - } - pixDestroy(&pixr); - pixDestroy(&pixrc); - boxaDestroy(&boxac); - boxaDestroy(&boxacs); - } - LEPT_FREE(tab); - - nchars = pixaGetCount(pixa); - if (nchars != 95) - return (PIXA *)ERROR_PTR("95 chars not generated", procName, NULL); - - *pbl0 = baseline[0]; - *pbl1 = baseline[1]; - *pbl2 = baseline[2]; - - /* Fix the space character up; it should have no ON pixels, - * and be about twice as wide as the '!' character. */ - pix1 = pixaGetPix(pixa, 0, L_CLONE); - width = 2 * pixGetWidth(pix1); - height = pixGetHeight(pix1); - pixDestroy(&pix1); - pix1 = pixCreate(width, height, 1); - pixaReplacePix(pixa, 0, pix1, NULL); - - /* Fix up the '\' character; use a LR flip of the '/' char */ - pix1 = pixaGetPix(pixa, 15, L_CLONE); - pix2 = pixFlipLR(NULL, pix1); - pixDestroy(&pix1); - pixaReplacePix(pixa, 60, pix2, NULL); - -#if DEBUG_CHARS - pix1 = pixaDisplayTiled(pixa, 1500, 0, 10); - pixDisplay(pix1, 100 * i, 200); - pixDestroy(&pix1); -#endif /* DEBUG_CHARS */ - - boxaDestroy(&boxar); - return pixa; -} - - -/*! - * \brief pixGetTextBaseline() - * - * \param[in] pixs 1 bpp, one textline character set - * \param[in] tab8 [optional] pixel sum table - * \param[out] py baseline value - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Method: find the largest difference in pixel sums from one - * raster line to the next one below it. The baseline is the - * upper raster line for the pair of raster lines that - * maximizes this function. - *- */ -static l_int32 -pixGetTextBaseline(PIX *pixs, - l_int32 *tab8, - l_int32 *py) -{ -l_int32 i, h, val1, val2, diff, diffmax, ymax; -l_int32 *tab; -NUMA *na; - - PROCNAME("pixGetTextBaseline"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!py) - return ERROR_INT("&y not defined", procName, 1); - *py = 0; - if (!tab8) - tab = makePixelSumTab8(); - else - tab = tab8; - - na = pixCountPixelsByRow(pixs, tab); - h = numaGetCount(na); - diffmax = 0; - ymax = 0; - for (i = 1; i < h; i++) { - numaGetIValue(na, i - 1, &val1); - numaGetIValue(na, i, &val2); - diff = L_MAX(0, val1 - val2); - if (diff > diffmax) { - diffmax = diff; - ymax = i - 1; /* upper raster line */ - } - } - *py = ymax; - - if (!tab8) - LEPT_FREE(tab); - numaDestroy(&na); - return 0; -} - - -/*! - * \brief bmfMakeAsciiTables - * - * \param[in] bmf - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This makes three tables, each of size 128, as follows: - * ~ fonttab is a table containing the index of the Pix - * that corresponds to each input ascii character; - * it maps (ascii-index) --> Pixa index - * ~ baselinetab is a table containing the baseline offset - * for the Pix that corresponds to each input ascii character; - * it maps (ascii-index) --> baseline offset - * ~ widthtab is a table containing the character width in - * pixels for the Pix that corresponds to that character; - * it maps (ascii-index) --> bitmap width - * (2) This also computes - * ~ lineheight (sum of maximum character extensions above and - * below the baseline) - * ~ kernwidth (spacing between characters within a word) - * ~ spacewidth (space between words) - * ~ vertlinesep (extra vertical spacing between textlines) - * (3) The baselines apply as follows: - * baseline1 (ascii 32 - 57), ascii 92 - * baseline2 (ascii 58 - 91) - * baseline3 (ascii 93 - 126) - * (4) The only array in bmf that is not ascii-based is the - * array of bitmaps in the pixa, which starts at ascii 32. - *- */ -static l_int32 -bmfMakeAsciiTables(L_BMF *bmf) -{ -l_int32 i, maxh, height, charwidth, xwidth, kernwidth; -l_int32 *fonttab, *baselinetab, *widthtab; -PIX *pix; - - PROCNAME("bmfMakeAsciiTables"); - - if (!bmf) - return ERROR_INT("bmf not defined", procName, 1); - - /* First get the fonttab; we use this later for the char widths */ - fonttab = (l_int32 *)LEPT_CALLOC(128, sizeof(l_int32)); - bmf->fonttab = fonttab; - for (i = 0; i < 128; i++) - fonttab[i] = UNDEF; - for (i = 32; i < 127; i++) - fonttab[i] = i - 32; - - baselinetab = (l_int32 *)LEPT_CALLOC(128, sizeof(l_int32)); - bmf->baselinetab = baselinetab; - for (i = 0; i < 128; i++) - baselinetab[i] = UNDEF; - for (i = 32; i <= 57; i++) - baselinetab[i] = bmf->baseline1; - for (i = 58; i <= 91; i++) - baselinetab[i] = bmf->baseline2; - baselinetab[92] = bmf->baseline1; /* the '\' char */ - for (i = 93; i < 127; i++) - baselinetab[i] = bmf->baseline3; - - /* Generate array of character widths; req's fonttab to exist */ - widthtab = (l_int32 *)LEPT_CALLOC(128, sizeof(l_int32)); - bmf->widthtab = widthtab; - for (i = 0; i < 128; i++) - widthtab[i] = UNDEF; - for (i = 32; i < 127; i++) { - bmfGetWidth(bmf, i, &charwidth); - widthtab[i] = charwidth; - } - - /* Get the line height of text characters, from the highest - * ascender to the lowest descender; req's fonttab to exist. */ - pix = bmfGetPix(bmf, 32); - maxh = pixGetHeight(pix); - pixDestroy(&pix); - pix = bmfGetPix(bmf, 58); - height = pixGetHeight(pix); - pixDestroy(&pix); - maxh = L_MAX(maxh, height); - pix = bmfGetPix(bmf, 93); - height = pixGetHeight(pix); - pixDestroy(&pix); - maxh = L_MAX(maxh, height); - bmf->lineheight = maxh; - - /* Get the kern width (distance between characters). - * We let it be the same for all characters in a given - * font size, and scale it linearly with the size; - * req's fonttab to be built first. */ - bmfGetWidth(bmf, 120, &xwidth); - kernwidth = (l_int32)(0.08 * (l_float32)xwidth + 0.5); - bmf->kernwidth = L_MAX(1, kernwidth); - - /* Save the space width (between words) */ - bmfGetWidth(bmf, 32, &charwidth); - bmf->spacewidth = charwidth; - - /* Save the extra vertical space between lines */ - bmf->vertlinesep = (l_int32)(VertFractSep * bmf->lineheight + 0.5); - - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmf.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmf.h deleted file mode 100644 index 328e2c0d..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmf.h +++ /dev/null @@ -1,64 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_BMF_H -#define LEPTONICA_BMF_H - -/*! - * \file bmf.h - * - * Simple data structure to hold bitmap fonts and related data - */ - - /*! Constants for deciding when text block is divided into paragraphs */ -/*! Split Text */ -enum { - SPLIT_ON_LEADING_WHITE = 1, /*!< tab or space at beginning of line */ - SPLIT_ON_BLANK_LINE = 2, /*!< newline with optional white space */ - SPLIT_ON_BOTH = 3 /*!< leading white space or newline */ -}; - - -/*! Data structure to hold bitmap fonts and related data */ -struct L_Bmf -{ - struct Pixa *pixa; /*!< pixa of bitmaps for 93 characters */ - l_int32 size; /*!< font size (in points at 300 ppi) */ - char *directory; /*!< directory containing font bitmaps */ - l_int32 baseline1; /*!< baseline offset for ascii 33 - 57 */ - l_int32 baseline2; /*!< baseline offset for ascii 58 - 91 */ - l_int32 baseline3; /*!< baseline offset for ascii 93 - 126 */ - l_int32 lineheight; /*!< max height of line of chars */ - l_int32 kernwidth; /*!< pixel dist between char bitmaps */ - l_int32 spacewidth; /*!< pixel dist between word bitmaps */ - l_int32 vertlinesep; /*!< extra vertical space between text lines */ - l_int32 *fonttab; /*!< table mapping ascii --> font index */ - l_int32 *baselinetab; /*!< table mapping ascii --> baseline offset */ - l_int32 *widthtab; /*!< table mapping ascii --> char width */ -}; -typedef struct L_Bmf L_BMF; - -#endif /* LEPTONICA_BMF_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmfdata.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmfdata.h deleted file mode 100644 index 30e2b5ad..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmfdata.h +++ /dev/null @@ -1,636 +0,0 @@ -/*====================================================================* - - 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 bmfdata.h - * - *
- * This file contains data for constructing the bitmap fonts. - * - * The fontdata string holds all 9 sets of bitmap fonts in a base64 - * encoding of a pixacomp representation of the tiff compressed images. - * It was generated by prog/genfonts and pasted in. This allows - * the use of the bitmap fonts for image labelling without accessing - * stored versions of either the tiff images for each set, or the pixa - * of the 95 printable character images that was derived from the tiff image. - * - * In use, to get the bmf for a specific font size, from the encoded - * string in this file, call - * bmfCreate(NULL, fontsize); - *- */ - -#ifndef LEPTONICA_BMFDATA_H -#define LEPTONICA_BMFDATA_H - -#define NUM_FONTS 9 -static const char *inputfonts[] = {"chars-4.tif", "chars-6.tif", - "chars-8.tif", "chars-10.tif", - "chars-12.tif", "chars-14.tif", - "chars-16.tif", "chars-18.tif", - "chars-20.tif"}; -static const char *outputfonts[] = {"chars-4.pa", "chars-6.pa", - "chars-8.pa", "chars-10.pa", - "chars-12.pa", "chars-14.pa", - "chars-16.pa", "chars-18.pa", - "chars-20.pa"}; -static const l_int32 baselines[NUM_FONTS][3] = {{11, 12, 12}, {18, 18, 18}, - {24, 24, 24}, {30, 30, 30}, - {36, 36, 36}, {42, 42, 42}, - {48, 48, 48}, {54, 54, 54}, - {60, 60, 60}}; - -static const char fontdata_4[] = - "SUkqACYFAAAmoHICP///////////////////////kFcchgc45Bgc45AgcgxBY5DY5DY5Agcg" - "jkM45A8GocgxBA8M45BfCGgchhzOQxZBiNe/CDQRT6RQ+k4QV6BHcgvBBjCC+KoSjQI7wjj/" - "16I+EUPTpV0rI4LilVtAjjyPuR58jg3CRd6dJkcDMCj+v//qlVsMgQPVY6vugih9Lr/8RCF+" - "OqUUK6C/fHFV9RStf8MulG10fKcN6X+lXOBg+GexX71wxSPCf4/+kE0uR5zE0rtfCFg3oIp0" - "R+GF5DSmQaMS/oG1xen0X2wyh8WXwoI46VPt/kNYcf9J4h/pUHB///2H+t+lkCByDj/r9ZBX" - "H1BAtUr7u/IEOQanrS0eByO16tpVaSWtaEVsNiG66WrBgg05wM4bCYNWDCWIiDCER6HGhERE" - "RER3ZHBfXjaSQ7iOP/////////////////////////////////////////////////////+Q" - "JgK95DIDRZAjCDccgRMhn4g5yC9CD0IL+QxhuIfCCYQTC4IJhBiyLBB7J4QX4gvQgxxBehBi" - "yGDkPhdkEw1kPZY5cEHck5BIJOQc9aI+wjE7DL7RdsMu2GXoZehGDYaDCDQaDSCDQdIOGEEX" - "bDLzCLthl5ojzkeL0NMJhNNbVoJ6kclXuggyOGfugnw3vugv/0u+9IN7pBvdJ//brT3VtdLy" - "B4NxyGsOPRnv9R7xx3/9L+EU/3/f4jj/t+3TdDvkFZyC7hYdKkCCKHQI76SW/pD/6XCKdAin" - "29L9L6/9eEUOrD0kv8IIMNKkq/j/zD5h+P4r//99LfBKcDR9utK62NLxEIIhnmGGlpek3Lz/" - "jj5cv/ul7f+EvimH///0l6CENpfrHt/y9l7kr/4RT/f7f+PwRTkG7/tpav26XtrxoVI5/vSx" - "xsP/7ful7fdd1tv/7FRoj//DLgQZgQCFhlYlfv1kx9//28mPx/7ruu3/t9K3pEh/IKzkF3DL" - "g2BENDtBr9Jh4S12H/+3+17GwwltpbZBx0u0unr0v9IMjhrBYYpO0KZmDikMJsYTCDCeE2Gh" - "p6DTdiEE2KCdo8GcNj3pJsJofjiIiIiIiIiI4iIiIiIhhCIiIiIiIr1SMwyQbOkEiGQCvd4i" - "I//////////////////////////////////////////////////////+QVo7IEDkGwchpOQV" - "nIa0ENKCGhyC7kHchocgZschnHIMPtKk7oIP7ulv6f9Yj5DIDaH/3gjjr///+rI4aiIEXngg" - "RZBfCBEWQXsofKggu5DD5Y+Qw5UHghiCoIEYQw5VkCMIO5TkF7shhzOQxZ4IJZxy3IO5nIJZ" - "4IP//1iiPOGd0R+iPQgR3TQIIXZ3/S7BBnezui87MOiPbKHRHqftNNXvTTUjy/9JkcFjTpOk" - "9NsKmFTu+Etppw06VtMjhhO0OLCd3S+rSdIUvyDD+Iha8fQ//+K//3/+D/vbQRT7d9LsjhgI" - "7nH8Ivf/lw0bS/4RT////7f//pfq+lhr6/v/Yf/t//3/+D/sO2NNhpfiP66Xat8L/2//3S0r" - "XIMD/rvUEd9Isf/4Mp5wCDgYBlOzgO0fB3aem2mmnYTtipwCAZQ6DnAXDgynapwk20h/+IiI" - "iIy9ERxEREREZHDLiIiIiIjjj6kNWdP//qP/pMjhq8bSXwojsGkEwmliIiP/////////////" - "/////////////////////////wAQAQ4AAAEDAAEAAACSAwAAAQEDAAEAAAA2AgAAAgEDAAEA" - "AAABAAAAAwEDAAEAAAAEAAAABgEDAAEAAAABAAAAEQEEAAEAAAAIAAAAEgEDAAEAAAABAAAA" - "FQEDAAEAAAABAAAAFgEDAAEAAAA2AgAAFwEEAAEAAAAeBQAAGgEFAAEAAADUBQAAGwEFAAEA" - "AADcBQAAHAEDAAEAAAABAAAAKAEDAAEAAAACAAAAAAAAAAAAwBIAAAQAAADAEgAABAA="; - -static const char fontdata_6[] = - "SUkqAMoGAAAmoHVf///////////////////////////////IZAUfsgeBdyGdyDjkMgI+QPKC" - "GIO5AhzOgyGiCMcgYtUrIKHohowhschs4hnwgXcgRQhsgguQQXwhov6/QYQI7qgRUUk2QIfV" - "F5hQmmugqCMTCBHj/9F8j9JuknWm7rSbCBFPLtou2sjhlBSOKkE3Qf3+kv9fpcMQaXY9PTwR" - "T6WvpX/0v19aVbeQ0D6X7+v/X//QIQfj6xSS4QLS3xx69IVtL/EQy8CvbSqhq4I7//pJeVnT" - "Dr/+Niloufj9fpJLxalYrDtdr2DGk/etf6CDrkduzQkw21/w2prRfYZcNbj1+kQMQuL03hF5" - "sQRT+CEMMj7pAjuk/5DVDINfr+k9b06Stj+GXgW6pN9/kNsdL/XQg/+nSx/0v20vxSv0v/S3" - "/yDA/19sV/6WkQ0D5DY/6+lkDyf/SX9h65BRBDTdJ/StLILuk2lWkl399U2kw0Thpa0r7S0U" - "A7S20rSVtJL/iGrFMSPJv+qYoEaA+KBA4pikmKCWIiDVCINaQ0KiIiIiIoFhoRfSodbS1xbp" - "Id0hx8f///////////////////////////////////////////////////IHMFnMgTA0hyGQ" - "G45DLcg0jkQfyGQDNxBv5DLcg3QQ2EEHDIEaEHDIaDkMTJzIeZBJkEmTwh5kNmEPhB7ITCGi" - "ZDOghsmQ0IIbJhHUEMzPAh8jYOeIuRsEZFHCZEHBDhdoww1DLm0bOGXGwZccGXHCMDgwQMED" - "BAwQMEi4ZwQdAg2GEEbYYZc2EbYYZcwwjB5dmDgwQMIMJoNbQNqHuRxF6I7YQIN+6BBrDf+E" - "E//pf3oEG9tAg3vC9//126bQWlXh0gyODd+l7fXwv/0u1gio0m90m916x9uu60nXXyB4G7kN" - "tx6JwU9oEU/4944qP/pcEU8EU+37f7f4j/q6q2tpDXhYaShBBDer1XfJD5IdL/0vtf9L9L//" - "ergin9JukvIHk5BiAggw+kn1fSr///9L3r2/fS30of9r1exWqXp4QQYaWl9XH/a2vH+l9/t/" - "6X58mgN//r07dJe04QRDYGGGgvpVeXb/jj5gT8X7r7f+CX6CDD/bp6bXY/xEIIQw16Xq8N/y" - "5ZcvT/Lp/de3/j+2QMd/r/p0l6CDdf0h73//ZF7/w37r99/fuD/vVq9SP3S9hpd+lLj/6444" - "a/9v7r39L0tt/7Xq9b0vDDIbAwQQu2ElKHq/fr3f/2/dfb39/b/V6jjSb1Io/hhiEFbEECFK" - "r/euRR+//28ivxXt913XZBcf/jaevr8geTkCHDDCCIF3bEk9XpN6X7f/7f7+xtpbaW+l2l9K" - "3pfpqGGEErBhJfCTBk4wl+wf/7f9fsMJba7cMJbDSa9JvSX2sPCwxCQYQaFBikIQQwQMMYIG" - "CBggeCBsNCgg3CBhBuGKBA2KBA24hAgbFdOlYIGh+NCIiIiIiIiI4iIiIhxEGCERERERER9L" - "GHfVBF0Tgtg0dSBoDTYk+h40PiP/////////////////////////////////////////////" - "//////5A887IHkOQbLIE8EFaCGvBBmsgosgaDcg3HIbHwaIbIvVVIZTkGHVUtv9IOHRHBU+D" - "g5DJBx//QRTr69fr/+3X+I+v/pa//v/9N0Q2XnshsshsjIaMyGjMhlOQIHycZAhyDUOQy+IZ" - "xzWQUWUOQYc7kGMyGdyTkH41kH4scnZB4JwQxhrIYp/64hF56DCLzBF4aLzQNF8+DyuCguuF" - "Kw/ApXIvMFTCI7FhU0XmgYUL/ap0tow3/6TdN2XCTpB0rVJqJHmHD6BYbNhoDEjzSbDDLhJo" - "NnHSdQ4cMJoMJQ0DpBphVC//x9v/ScMEkwqf9Lpp6dJum18cQwX3V9XXWv/pN9OkKX/9f6X1" - "1/TpdX+6umrDdRSS2yBGFv4iQZu/9D//4r//f/58CP3XI/p7pL9F9peEYv/zAF8NL/hFP///" - "/t/utrrutN6SQYr0F//7Ff+3////g3/11dJ+l+I/+ld7ey4KP+3//fpX5DOOD/3sb8j+6X/9" - "en1+v/b//dLr//Vuo0rY0ib//aphKGYdtAinbLfROC//Yf/8NKGEmwvaUOwvtK3SX/7DPcUG" - "NjhsUEHhBwwg8JuEGEGEHDCDhhiopiCKcIOKeJHTd8JNuh/+IiIiIsubERxEREREZcNKIiIi" - "IiNDj+En/X/IbQdf/+Cj/9Npd6SXq3WLDSrwSEdigkEGCDrEREf/////////////////////" - "///////4AIAIAA4AAAEDAAEAAABBBAAAAQEDAAEAAAA6AgAAAgEDAAEAAAABAAAAAwEDAAEA" - "AAAEAAAABgEDAAEAAAABAAAAEQEEAAEAAAAIAAAAEgEDAAEAAAABAAAAFQEDAAEAAAABAAAA" - "FgEDAAEAAAA6AgAAFwEEAAEAAADBBgAAGgEFAAEAAAB4BwAAGwEFAAEAAACABwAAHAEDAAEA" - "AAABAAAAKAEDAAEAAAACAAAAAAAAAAAAwBIAAAQAAADAEgAABAA="; - -static const char fontdata_8[] = - "SUkqALIIAAAmoHcGf/////////////////////////////////kMgMsfUgeDaOQLjkHHIZAN" - "T5A8K5AiDQQ0OW7kMqCEHIZthNJkcMwuGQG8g34gYcgo8go4hmwQIDIGIIL1EGOIKO1/wRmG" - "cvBqEX3S3dBGJhUwmlQSpGINF2/9cIxkfa9U+k2Q2OlpNgqaNzWwgWk2k33Veluk2q6STadJ" - "U2jHlzcJtZcGlS4RJOt9f9f9L62GMw+vC0np5HXS/0n/6Vf9dapwxpdj7rr6Wl/f//v9dJLa" - "kG76X/XXpf//v/j62kl4I2i4ZVd8caX8UrS/xEgvV7aVMUP19f615+S7/6BmGXBh70tK21ev" - "60lxefkmGla/8WxVZM9Y31/RDYOEl5uappMV/1sGKhNfYX/1EOuEHiR57DbXfUMOieIxwZgN" - "vjpfrI7a9XQdJF9sSOv+QL+qLzSt//9IW6x6tUg21+Q2qpHnS3Tf5BtTkNSi/06710rYpeDM" - "MuBi6pNq3+QZX6/S0J8DHdUn8f+v3S/Fb9L/63r8hnH9f26/rS0sgXj9fXpV+vuP9X9Igofy" - "DD1el6WQPCR/pL+w7XIZUEGx660nS3V0vSrv/qm0m2UBr61T7S0dAd13XSTdBL+r0l6YYX+t" - "JtK1hhK7CTDCSthJLpeIpIMUGJHaf9rYohsQsQiBhDEIMQtiECCxESCjKESKPdDQqIiIiIig" - "sGhF1Wh16pfbSSrFtKh3odkcHWI/////////////////////////////////////////////" - "////5A7AyfkDqG265DJBRxDKmQanIZWpDKDIOnIaBhB05BQGQwgkcgiCCIIIglxBEEG/kGPI" - "J5DzIN6EG+pDKoQ2akDFCGBBBDkdCCUI5kE8iuRfIPxCwCZBHIYGMFhMI2w8M42COFBnCDIN" - "7JWQz2SsEcKQzwDBENEENkENkQRDRANwQNgwQRthhnDYRthgzZhhGG5cjZQYIGXDOCBhNYYW" - "k2rMBNcu2ECBhptBtAgdoGHQPQdFwTv+l6T4QIGG0Gwi4UOg2gg0777dNXg2gg9Qq+m0g37p" - "eG/8Jf/pd96Cb7Sb9f//1pvbS0vV0rT9L3/0v/0vWCKjV91fdJ//dK/0n1Xx6eXX0vvHGv/0" - "uXTkde9Jv0m//6+/T20rSevIZCggrxpErPFpX+O36j/6C/X2//7/Ecf95dUnSdIUvCsNLCCC" - "I6vvpL+RR8ij//pe3++lfpev+2l1ffdJeQPCOQ0OEEw9Un6+q3/0v/S/S9v/S/q//tfYp1S9" - "NMIIMNKkq1uwS////0vb/b9+t9KZg0fdL3Wm0v/CCDBpdfvF/wwsMLx/pfpff+Evz+ygMr9+" - "ldPdJe00EEQbpww0tV0rmDf8cfNhfxD9/2/8/foEw//f/Y0vEQQQgw6+l3wb/mB5gfoP8wn9" - "pe/+P4bBv90vfvS9Ag2l10lff++//7fv+3/3+Qau/vtK0kXTaX6bq9ePe9L/shZ/+39pfff/" - "th/3S9/+vhhL/SkcJ//HHBr/2/f9v0vS23/vdL0m9LwwwgmRwb20R1SW/f/d//b+0vff2/b/" - "3r70m9LwwyDdOEENsHpHH3+9LIUfv/9vIUff9vuvryGcf9dY2KX1IUfwYMQgnFik0r1b0v2/" - "/2++K+9tLbXbuu+Oum9L8geEchogMMEEQzXbFBb9N6Wvf/7f7+xvX1t6+k0+k/X6ahhhAk2G" - "kt6TZDj4S/b//b0v92GEttLb0tgwvTS3pL/QbQWGDBL7CQYMFTCVhbDBrffbaYW2r3YYSthh" - "K7gwguKr0m9Jfaw8JoMQgQYIMIQgxCQhAhkHQGIRBhBI5BEZBhAYaGCB4IGQSmGIRBugMQiG" - "hDDiiCg4YT+EoZDOhD8aERERERERERxERERDiIMIRERERERH1xb+qQfpJBF2UAZhn9EDUFTK" - "B7xoQYSB7Qjj/////////////////////////////////////////////////kDxf7IHgQOQ" - "VbIH1kCSyCrZA8cEMyCBqHcgYcgYfIHh7IF4TChVCkM1yGhwoVe+loHBwi8gdNMOHS2/tL6H" - "/yGSCkP/6BFOvrtNeE//Sv9cR+v/p1////W6////p1zZkNnZAv2bCDcchsHyLGQ2DmwnZAuO" - "bCBfiBcc3EGochoHNBAjsg3HIQcguOSHLHLHIJMm5LiC7kMocmOWOWOQXciv/62JDZPQZBv5" - "DYhF5z4Zy8yr0yDGEGM1yDGJoMgxyYRiDIEYmQboIYxNF2HPg8lkaH6hMjhDjQ//p0Xb0XmE" - "YmEYcJNhNJj0Xn+gtUXqL3ReaQbVF5ou1qk4TVQwgYQYWDCDoIMIMKXH/9bSbig6CDoIOlyO" - "jAbFVthw+gsG4qwbbSsGKDYQQcMSPJRSBwd6dPbSfpL/6f6tdXqx1YVf6XTCevem168GYDR9" - "fSutLS/9WxeuqrV/9/wl/7pXXXQ/91p7pXjSW5DRhFH+sLuor///6C//33X4P91bl1pjdJKt" - "hovBr4iQPKn/x/X/F////7NAz/v0tavW9aYaXhG3/+YDM2l/zCf///+3+9e3TvSTeglDFegv" - "//bS/9v//+vw3/q3Wt6pf0PpfV3+xX/t//3635DNv9utb0R9t1X4/+vreyOGZ/2//+uvyGx3" - "/16elvVIjH//Xp3/X/2//3X3//WKjjSeNb/+10rtWyMfX/2//7q0rX6u1d2kraSr/3RdYaTD" - "LdsIv2GvJAZ/+w//2GErCCbCLr2EoNiR161b0l/9g0HI6FBimKg2KCB2CBwwQPBA2wQMEDBA" - "4MEDhhiFFBisETgwITTCg2vCTDaQ//ERERERZg2IjiIiIiIzAa8REREREccfwgg/9f6X+v+Q" - "ZK///0x/+m0sF0q9W0sW6XyGSGkOkI7YSr4rYhAkEGCDrFhCI4//////////////////////" - "///////////8AEAEDgAAAQMAAQAAAP8EAAABAQMAAQAAAFUCAAACAQMAAQAAAAEAAAADAQMA" - "AQAAAAQAAAAGAQMAAQAAAAEAAAARAQQAAQAAAAgAAAASAQMAAQAAAAEAAAAVAQMAAQAAAAEA" - "AAAWAQMAAQAAAFUCAAAXAQQAAQAAAKoIAAAaAQUAAQAAAGAJAAAbAQUAAQAAAGgJAAAcAQMA" - "AQAAAAEAAAAoAQMAAQAAAAIAAAAAAAAAAADAEgAABAAAAMASAAAEAA=="; - -static const char fontdata_10[] = - "SUkqAGwKAAAmoFQGz///////////////////////////5DIBocgZg0PkDwy3JvkFdyB4Qchl" - "DkGB7yB5OnZBQ5J8hmckQ0rBNUyDSOkQWnIZXkMqZBrghs0INDkM/kdkDfsLqqhGYKDEHp0k" - "G0HkFEwoQaaqCcWQzzCMMPXfwg0m0gi89KyCgekkYmCpppYQKgjc0m//0Yy8/16VtP0EGwqN" - "to22ugtBBtJv2vpLdJtJJ1SbTpJKwjnoOgg2swGmFLgiStb3+lXf/69v1bYLpuuR1pLVX//X" - "r/S60mwYorKXH/dfS69J/2vX/9UvYyGU699PXXpa/3//4+l1S2EcXqvXHX1qr/8RIMCP17SS" - "pwggnqvj1XpClpf1+3SWlS2l/v6S+btbr/IKbknv62KH2Fel/VJeEGlTDS/1W9tJKiGL8f/1" - "Sri83qxVr/sQ2K1JBpXel/RAuOFXm29On//YMUk/dhf+qEOuEHQtWG2v+w9GEwZuXj1/Uuw1" - "6bnzaSDtF1/wbSI+Sdx/X9IQ6WPCb0YbYr38MvvCMTVv8gqlyGsR/pX/ukkHaS8gqiMOkk2l" - "f/pfpOlvXSTYa/9/b2/yBO9f9cTQMzuu4/RBSgnHpJe2l+KX6Wv6ST1j//7f/2lpdf/pfkM8" - "el+xVr0/pEMofIZV16+v//9tda/pdZAh1vS+sge4/0kv3fyGbBBVeutK126dLtJLuq+ttJuH" - "+FTV/SOR19dJPSWqr6SX2gyx+ur7S0LbS20n/oJf8PS20mwjeNtf0noINYMJBBwwk2kk2kEF" - "texFJBiExCYXXTWwwkCBrEIEDimGEErDCQILERBgsQwgafFRSDEIRDCEMIMUIYhQWQyAaHER" - "bSrERER/0q90tfukqxbWh3odtLbSxH//////////////////////////////////////////" - "////yBTDMpkFsFhyB4YOQyAboILYFByB4hyB4vkMgCIK4iOQsFWQ07IZxyBEeQyQ1PINNLIZ" - "icEDIMeWcgoBkFy4IGQIIIoZByCDhkHIInkMEEDFCGyhBJkFzggyDcYCDINxgQMgwoIIGRDk" - "EIIp0O0MhjrIPyZDCj0GCD4aOEHEN3CPDDaDTQaapp6bwjxByc2EeIOTmGEcbw1TTT7ppJ1U" - "4B46aPGGmQabJeECIJZDPZEmDNhIM2JQIHBggwQMEDBAwSBAwQNo4DdkCHQIGyCiw2gQNkFF" - "htBB5cZwWGCIMOGCBhBglBggdBA6U2Ca5c2EbDvwbSayCZh8Ogg+/6C329JvbSb3SD777/q3" - "TdQq9INoIN/oL2/9J//S7W9IN9pBvv//tJ720m0tL/SbT3X2/9L/9L+XXSvdK90v//1p0nrS" - "+npuXX0vb66X/9Ll0176b/b///eu++1/yGQxyBwOOk63+++ONV/6X8uu3r+l/iOP2t6uk9Cl" - "4WHqR8e7r6SH/Uf/S+19v3/f/96dGF7q0kvCw0qCBAn6vpff//pe9e39/3pX/a9XTaTql5A9" - "wQ2QEmHWgmKer6X8iPkR1/9L7X30vSS///991bpL1TCCDBpKv76Vb/9f+l719+/W+lD/erXW" - "K0v7wggw0qS9K4YIL////QX3+3/pfpMoBq/a9XTTapfWCCIFy4MNL694g/44+P9fdL2/8Jfn" - "mzoGZ96dX+6S92ggsMNLS9bmyD///i/v9v/P/6BMP+/r22KS8RCBCGGl+teDf84POD82DH79" - "1//5HDL+Gw3+6/a/XhBBhpddK+/9PT//N7/r2/8b9yGpT/q1ek2l9BBuvS6vu9f+yDuRj/+3" - "9r7ff/2D/2r16MLpfT9+kh7/X/xf/t+9e39fW2/71q2qV6XsML+qV//jjkCM/9h/a+36+u2/" - "/9dU3peGDCCbdtalw/2/93/9v3r/f2/b/20r71frwwyGWXBBVbaL8JK/+l9//t/a+33X1//7" - "G+levhh4QIXYqKNFX7fWQR9v/9vIO+9e3uu2ltkND/rHUaTekQw/hhiEE2IpK+l6///7elx+" - "33X+313TXX6X5A9uQUQGGEEQa4tKr9vS/b//b/a9jbS20tvX16dJvS/TChgwgk2Gkr6TDILj" - "4S/Yf/7f/+2ltpfdbaX6Tfr90GwgtsJd4JNhcEtLb//b/r3YaWw0tu0uDBJp9fSX/B4WGeNB" - "NNCEGZkghCCGEGGZlCDCDCDwg2GhhN0GE3YYJBBsMEEEGw4YJBBsV00kw0Gh+1QeE0xCCDBB" - "hBMQkCChBsQggwQYQeEG2FBA8IGCBuGIQQYYoINuIQINr8JWCBr4qIiDCERBhCIgygDw1IiI" - "tCLhghBghEGEIMJrxER+hEaERDiIiPpaB/0g/SIGwCcdJFzOgGgr6jEGvGgamgH2EL4j////" - "//////////////////////////////////////////+QP6EDob+QPBoHIElkDw9kCyyBJBA8" - "F7INVkDYDEZDLjyGVCZBXmCqQZPIaUENEAoKlt5A8sTSfV00/S2/6BwdF3D+Dg//pr6Q/+QW" - "wbj//MKvrtNeC/9JN1/iP//+vr//+k3////9r///+k9ZeECzPy+IZY5BuP5AuOXhHhDKHL4g" - "tOXxBowscg3HLjIGByHHIG9CMci+Qzv/+3BEMyeEGQMUCGQLzyBimgwUgRmRewVNBgqDIZXg" - "qYQsFTIEUyGzAUgucuippgmRLIOcuhDFX/pYhPTChGHCNzROBBuKAXpgoLoLBU0wVMIwwwVN" - "Fzgqow2icEgoYIGCDBYMK0EGEDClxP/7YRtvl20YOgg6CDYVBNaMXfQXovNGK6MUIJt0XbCT" - "WqCDhX336B6apJL/0ug3bpB0nSsGbDZZsNghBsHB9BYNhiE2GIQbSbBsNoJwYkergzYN4P1p" - "9pXXX/q3vTaWrr6V1/pf9at02vTX/t7fTaT+l/9Y/rr0370/6XTT0/fr44/6WnuukKpdkFFk" - "K/pN+9DWv//6C//S/rq/7+XVJum9Kt0DXxEF9V///9f/991+ZgY+6Tf8VrQSww0YwaXkDwOE" - "f/H3X/H////sH/+k2k1dJN6SQYrwjj//Ng1dL/m0////9h/t1/tvpN6SQa9Av//ev/b////w" - "3/rpN6ekrelQ+v//sMJf+3///X4N/3t+lt6X4+l6V33hiF/7f/9+t+D/ulr6L70q////+XBp" - "/7f//XX5BQO/9/TdJNvpER//16d1fS/9v/919//1emONK71r//0rtb1/9h//3Wla/XrHWrxS" - "S//YRdbpsijtourZFfT/9v/9+0E2vrZ3hourW0k26X/7aWgwgmGFYaVsMJJzWBDtPTYaaYTt" - "O20oaTYRhUGnUUxV76V0kF/9ioOXQpigxUNiggbYQOGEDwg3CBggwg4MIHDYaCimIWEHDCCa" - "ah9OrDeP/2ENBoNMIQwhbERxkcMgYqbQTCxDEJpoX8RocfxEREUYE4jiOIiIj/2En/r/IG5d" - "J/1/////H69JtLIH9NJf3S6uq9ISh0CxdL8gt46iO2kl6FbYSCQIMIHWGISCTCbWIiI/////" - "/////////////////////////wAQAQ4AAAEDAAEAAACoBQAAAQEDAAEAAABCAgAAAgEDAAEA" - "AAABAAAAAwEDAAEAAAAEAAAABgEDAAEAAAABAAAAEQEEAAEAAAAIAAAAEgEDAAEAAAABAAAA" - "FQEDAAEAAAABAAAAFgEDAAEAAABCAgAAFwEEAAEAAABkCgAAGgEFAAEAAAAaCwAAGwEFAAEA" - "AAAiCwAAHAEDAAEAAAABAAAAKAEDAAEAAAACAAAAAAAAAAAAwBIAAAQAAADAEgAABAA="; - -static const char fontdata_12[] = - "SUkqAFAMAAAmoFsNP/////////////////////////////////kMgNpyBoLGQPBocjfIEkED" - "wU3ILjrkDxwmnkGmKIa+ENfFshpj0Qy5kNIcg0UIHhxyCjCLhDSHIa9kG8yGZPCqpAvBK4YR" - "oCU0km4PTChBkMqgJxhMhnCBBhB6u/QIoBubbpPSb0gjbYKmEH4S0bNo43/rhBpNqjHpKyBh" - "/SDYVNNLCBUkG0EG//0Yi7fdJOqt3S02CzjaPNroLSdJv6qtLDS2qT1TaaVLo5UEDwQb5gGx" - "TAYXdf/ql9PS+t3rVwurp0XXS6SdW+v9f9fpJwxRcUrj7/9JUv/7v1X/Wkl2DGv9aTpel16X" - "v66/6/pbkMyK79/S+tf2///H6tJLbBHv6/4/66Vpf4iQYUfqulXhAioHSrx6S9If//9uq0kk" - "tL/f0v9K0v/v62KHbq9f60vNNdhpX+QJ4JXe6pV7X1+qSXhB0kw0tf6Ye2l0RNFxb1/oEF8W" - "pf0xC/14gwxCSTXv6/yBiiXON4Qattr/sGOmtcL/0oNeEDappMO1+thpIxyIRuOl+kjDdcJ4" - "lzemwwjC/4byL6TbNgp//6ENpY3CDpBG5sV/qQaCEgjc0rfyDKTIbWiX6T+9WqCDbVbkGRRL" - "t6Tav/1/pWl9PShsNL14dJK6b/1X9LXLHf1Scf//bVv8gtRVfpPEX71vXRAnslG6SX2l+K39" - "a/qlrjX/+3/1paX/pb1+Qbj+l+2la/+lkM26/9L1T/+26/Sf1IZg9f6X//0l+xT1/6VrkNDp" - "N0vSWQPOOvX+2/yGlBBkdetLr/WrVLTX+km0m2H+Cp1a6RB3b+0n1eku/9L+0DLHtLpNXrQu" - "0t6tKrUJfXD0knpgwQt/+rSTW0EnYSbpW0kF/weEtsJMTcF/Tqw0iBepYYSIZurDCTDCSsMJ" - "BLa1DEQkgxCYQa0taoMV8QriExVMQiCjsREGFiGEGm8aHaEQYQsIMIQwoWQyA2nER6pIRERH" - "3Vf26pf0kq9v1xbSSHdKFtpDt11WI///////////////////////////////////////////" - "/kC0GD5AzAxBA8DCCGQCoQQMw0yCB4EEEDwYoQyA1YNxDuQ8Hwg2YQ24vIZILHkNQ+QaS4IG" - "QzqyGWkILkwQMhs1ITUg+pB9SD6kJQhjUhmHIGDkMUIZyAgyBgGEGQMBAgZDPQhaEEqIQggm" - "hCoQ1QyBFqQX5MgwGQl1hBgg7hhHyBw/CPkD///vCPEHDCPEHDRxhx/r+CeE6i5wDwxTCPkG" - "pDSmT9GwSQ0TIzkMuZF8homR+EcB2Q2eQI8g38g38g3+cBQfDUaPgoZDZYQIGGQMTJTCBAwy" - "BiZKaBA+QI4hnsGfAgEDBWQe00CbWvRttGwR7CDYQQdhEE9hA0wgaQQdpppppBNPTtIINsIN" - "oINsINpPLhDgmmnaaVyGzkgepgCPwg2EEGHe2k+GHvuk//pdrek3uk3uk//6/t02lSX7aTa+" - "l4f/Sf/0v70m9tJvbX/967SbV60vS0nvdL2/9Kv/S9b0n9J//3+9td0m0tL90m5dfX2/9L/9" - "Ll0+XT9vfb3Sr/3S/ur9J8erX9L7xxX/9L+XXb1/X/f6/+6dJ0q/IZAdyBY+pCQ9X+O/0P/o" - "L7X36v6v8Rx+/RhVbW0hS8LD6BBny1fpL/X/0vevb1f1f/90r/un0vCw0lRyddXr9//+l9r9" - "/f96V/3ule6TaSXkDzggogJMHVIJjdX6/yFfIV//0vf9vS9JL//dL3Suuv00wggw1Vf7wku/" - "+l/6X2l7f//pQ//691bVL1sEEGGlpVpeEFX///6Xv+/vpb6TB/36t7FaSX+EEDDqkv3iv//h" - "hf0vtL2/9L8IKdQ0/uk39U3SXvhBEMomGGgv+rg/44+P9ff+/8JfnOynBp/f1q+qXtMIIFhh" - "paXq84Qf//8X9pe3/nP/BBv961b7Yr8RCCww0vSXvITv58efH5wNH79/2/9hfuG/9ev3S8II" - "QwaX9Je3/CDwg//zif2l7/4/tkNQP9vbXpPS8IINpdfvvf///7fv+339/kNqf+l7a20l8IN1" - "fpJX36/9kGCP/Df6Xt//7Yf+/r0Y//v+lx7/X/3/7f3/fpeltv+9at0lel8MEt/ST9/33chs" - "//2/evb39/b/9f1pvS8MMIJvbRHWpgMfv8cbD/+39r79/f7/t02l6vpeGGQaSYQT3YXX/9L/" - "/9v3r2/r62//X29K9Lww8IIXYrCR4Sv2/9v/9h5Bgftfb3XbXbINx/1/rpX8gw/hg8IKwwmI" - "S76V6WQXf//29divvuvrbuu9uo46vS/DDEIJsWkkr9vS12//2//29tLbrtV+o3dJvS/IHnBA" - "vYMMEEQ04bFLfpvS62//2/39jettLfrdWqpX0v0woYYQSbaS3pNkM4+l+3/+3/Xu2l2lt69p" - "fpXr+tBhhArbCVPhJhhcJft//t67+7DS20tu62GvT030v+G0FsMJLagkygWmRaYLsNdf21BV" - "q12GEsMMJd2EtgwSafX0gv9B4WGfMIEUAgNCgxSEIhlkyC+oZoOQY0IXQhjXIZ9GDQyGEOCI" - "YYKAIsGCRAvoydogX0YcGEiGXoxX0CTBkC+iH7Sh4TQYhJqgQYSBLhiCu/t1vTtwxCsMQrbY" - "hWwunSbv8aERDCERBghEQZIA8GWIiNCLhghBghEGCEGF+IiP0IjQiJA8C+CIiK64QP6pB+kk" - "gf+i4zUBoDN0iBKb0INfCigak4HhI0QMw1IvYQjj////////////////////////////////" - "////////////kD9BA6hrjkM2CGYP5DIDUggeBiyB9hBYsgeGVBDVggbQ2ZiVHkGiCB4rkDfy" - "B4bJqQN5kNdyCiCBEyDVNBbeQPHyqqqqaf/e6aRBYsgeBfEXcgUYnZDRZDUtLb/90hf//9NL" - "1/8gtgsP/8xtfS2mvBf/X/8R//6ptfX+v/Xr///+m1////V////9K0iGb/kMz8g0fkD4fyB4" - "ZxyG3MhmjkDwUp5DMHIYHIHgTj//uwQTycyDTMhl0wnhPLmQy4BcheyBeC5kfgpcwQYKXMg1" - "0M5DZBPAg8FBSBBBM5DCCK5EoQx5C4QcgmcguI/9KxT0wQYQ0bmiQGgwyGBFMhsmQInpZDPN" - "NBkNk00cYZAiaDCGQXmFRttEgHkWbuune7//7hGDeEGEbOEbOEEGwqQfT10C9NNU0EG1QYRs" - "uqQcL4YIGCBgkyFsG0CDBAwUwFX/pXQfRt0EGggg6V6TWjDZBRZDZmlkFFow2jDkFGIw2k5D" - "RiMG0EGiGy1p1Bwd6fp0n6S/+n24hBtXSDpNgzYF84CgQg3voLiEGIQbYhBtJtiEDaTxLuuQ" - "0W76991paX/rdPCdLp/0un/S6rp+6dLhP//WtNq36//TY+366X71/pdNPWr02vjtft72rpdV" - "SXZAxhBx/X66f9v/f8Jf+9X/1Y/62i602lqKXug0/pv9RS1///QX/6/pfD/br3WKbpJBbaDS" - "8RIHgYPv/DC//+v//7/ygDH/dbprVIJYbRuBhLwRmv/x9pf8X//v/7B/6V17vShh4QVBj8I8" - "f/4L6/5tP////Yf7fq2vfTeqQa9Av/5wNS2l/7f///+G/9J66vVK9KgYXpf/+w0v/b///r8G" - "/2+9+26Sf8fX6u/2K/9v/+/W/Iav/6WlaSL71S/H69f7wwv/b//66/D///pb0v//16vouGp/" - "2//3X/yGU7+rdOrGrd9EKP/+vttr6/+3//daTf/36xVJNukkv/66Xe3pf+3///Wv16sfpXGl" - "//aLraTbYRhYZCPp/+3/+2laTYX1u0XWmnV9L/+wl3CbIjsMJbDCXIwG//Yf/7aVoKGEbXus" - "zthLfqm2kl/9iFMwXBhJhhJiFMwzjIMEWQYRBkMEZBghhkEIIYIMRMwwDg2GlDCTELIMaQwS" - "ioqZgY7glB6H/7XL4pimlYVtp3fbV3dp2xCimF6EJ2uq92v/2hoMIMINCGEIbERxDBCIiIhh" - "TeEGsQwmgwhd6EccfsREREIwE4jiOIiIjX+Egf//1f9f8gVq6/6////S1H0vSb8gfo0v90vu" - "v0m4WLrXkFsGsdRHtJL7S2GCCr4rDFEDwUYQyQ0yCCqGlhgqXaxERH//////////////////" - "/////////////////////4AIAIAOAAABAwABAAAAYwYAAAEBAwABAAAAeAIAAAIBAwABAAAA" - "AQAAAAMBAwABAAAABAAAAAYBAwABAAAAAQAAABEBBAABAAAACAAAABIBAwABAAAAAQAAABUB" - "AwABAAAAAQAAABYBAwABAAAAeAIAABcBBAABAAAASAwAABoBBQABAAAA/gwAABsBBQABAAAA" - "Bg0AABwBAwABAAAAAQAAACgBAwABAAAAAgAAAAAAAAAAAMASAAAEAAAAwBIAAAQA"; - -static const char fontdata_14[] = - "SUkqAKINAAAmoCAz/////////////////////////yGQBw/kMgGYcgw5DJBpvIHg1wR3kCuC" - "B4NFhbrIHiwnZAxZFjIafUQ2+BJJshrRkGnyGtBBqmQ05kNqyBcQQ1YINyZBRMhpfhf1CMwz" - "S5hqg9W4aggwoIGCDCWC4QYIPXrwR1BQm6Wkm6pGzYKmn2EFQRsgwjhB/9UjeXg0m1RifVkM" - "t1VBNhUGE1pAtBBtBN//hBYdboJOkk2nVJNgj3R4s8b8JUk6TftfpYfdafV09VbQXCDcEHWX" - "BWCmAIraTf/9eldL0ld1VcLp6bRddKkqff91Vf9fXbDeqtwum0v9L11v/+v+uqSwxR+rx/3S" - "9LS+vfqtf9da7DHr+/pel/79f1/9dKr5Boha9Lr/9L1/a/8fXSqsI/ev/HS9Kkrrv/IZ0n9V" - "aSXYIEU467ePX6j2v+I/tqulSulfX+qX0ldf/e9U6Q9wr1X6pfJ+u2l/kFqyO/tJYr2vr/qv" - "BA9JhpX/XeG0qqtq9f1SS9NIl3DS1/pg8MQlyJWuP/9JfF4QaTFN//EMaVd36/SIZrhNLnCe" - "EGob1/2U4bUJ/cLX/iDXQQb06Ydr0uw6RvZCaePX6V106EwdK2GF38NqQnJOzgE/1/SkcbS2" - "nhBtQjc2JfX6kGrSgjDDW3/r+hDfi3CekEG2v62XmoQTdN/kDgCIKtS/pOl+2qQba/IHCTD0" - "rat//X6Ta/XSuGEl/htaur/0v9et91SbH/+l1evIH0a/pOhJAaf0t/ogtWRY3Wm9v/GutLX/" - "S0sdfpfbS/X9L/0t/r9L9v/pv63r19L8gXH//tL9ddKiDVn9fX19JfbFPXXWkQan+npekv//" - "99df0tLIbHW+vXIHjj11S6bf8hrWQJHp/Sb/rVfS01/rddu/BUH2lpaW2k9JNpJa63pJX3D6" - "6TX9IoZddrf+gvrvS3psIMk7/9N1odpbpOkraQS/70km0mGEcxWvWrpJqwwknDCCbSStJL+o" - "PCW2EmKDXWtUwwkQy06xCINQyKYaWGGEECC2vDEQkgxBMINN/TSsV9bCYhJMUCBYiJBppiGC" - "DC0hxoMIRBghYIMIQwULIZAHDiIvpKIiIj91X7qtfdUvuklXtrS4t0o+lC20h263SxH/////" - "////////////////////////////////////yBlyPyBmCy5A8NUMhkrQgaA6CB4NKCB4ZhyG" - "QBxZCDkHcg8EUcg3cgr35BbB5kGw6kNRQQ1QZAgwQaBogwBkGgGQ0VkPWQxWQxWQxWQShBes" - "g0oINBBDCCDcMhmJyGWrIaichmKwQMhoEyD1kEDIPUQQiPjIMTIaOIL0IKMIEDc8B4WCBggd" - "sMIMMgYZkOCDDQYQaDCDShoNwg7QQMMGEDYYQeGE0GEGg0mGk1uutMIPBnthGYRAzwIGQaMO" - "nIKMPWEZhiQL8DBEMrgYIhldOBlngbcEDZDKgIzEYM8EYRmIyGbhCURwJwZ4C5gFAIGEGCwY" - "QNoEHSr7CMxA03ISYQIgxjkGJ5BiMgvCBB6apqkqtK9AgYbg2gQMPBsIINTAU8FT70/T0G1m" - "A2L5gbRwF34dBB8N/4QT/+gv70E3toJveuv/XT20m6pfSDhBBhp7aT4b/pBV/6Xa3oIN7oIN" - "7aT/+3X7aTpaX02k/ul7f+k//pf+k/aT+v1/+qT1daX/TaML6Xt/6X/6XMJowswnre63vX/7" - "ave2rpaXi6Tffpff///hL/9vSb9Jv1//6/0m168hkA3H0np/r3xxS//S9tL2/f9/xHH/tGF2" - "6ehXwpA/foh7bW/Ue/Uf/S//b0r9K//20vtK0rSS8LDpIEzZ19Vv9f+l9pf+/7//+9e6vpeF" - "hrhHmR/at6r/r/6Xv+3r9L9X+2lq3t1aSXkDyggYgJMHSSCjf+vvIO+Qd//0v0vb6/q9f/79" - "LSbSr00wggw10mtJ9Kt/+v/QXt/t/ev6V//pPtpevqmEEGGlr/eEl//X/0v0vb1fpX6Yf7aT" - "98baSX3ggQYaSSXpPhAv///9L2/2/9L8JSQCr/+vadJL/CCDDS6r7j//+P9L9L//S/CTNYa/" - "3S1dJq+vpoIIg0AQYaWv1yXDZ+OP/0/b/b/wl+ZDIgNP999+6S+00EFhh116vOCB///xf6Xt" - "/5Z/4Jh//pe3el4iEFhhpaql3g3//OAX/ft/t/8L9wb/bSferYpLwghBg0F9aT7f84D5wH//" - "Ob/S9v/H9shr1f/1arpeEEGGvX97f1///t+3///7kFU/7pWr6MJtV4QINpeqST7////7f6Xt" - "9/f2Df9//7r8IPX1xfd6/9kNGn/t+3+39fW2//ulaSTel9+36Xu//7//t/17e/v7b/tpe+k3" - "pfDBf1pf+scchld/7ftr7fr2u7//1ev14MMIJvdUpgGH96/b/+3//919d/71a9U3peGGEE7d" - "yOqSX79e//7ftpe3v/7f/avuqV6+GDINYEEEO2EnCW39/9//t//t91t1t/09aV6vpeGHhArY" - "qKLtL6fSyGd9//28hoftL2/X12yDd69bX/Sb0iGx/DDwQTYaYSW3rel/f/7f7/t7dbdf/f8b" - "1V9fhhiEE2IpL9N6/t//hv+K9vbXtdv/V6qNX0vyB5QQy7DDCCINsWtPq3pft//sPXf/tLet" - "vS26jd0r1/TBIGGEEm2l3pN6X7f/7f9extpbaW3a9r1Svpfrhgwgk20l9JhkNj4S12H/+3+/" - "u2lsNL+uwk19N6S/dBhhBbDCVN4JMMJYIL9h//t6XXuw0ttLbhhLYYS/Svpf8PBYYMIJO0KY" - "MFQhIUmwYVNNPTbQ03TTdhhBJsMJJtwwkmxVNOraaH9JB4TTFEFAZDGqCDEIIIg0AZBisMUQ" - "z1kPWQxXkNlbBhSC+mQlRDGmGKIZVYZQwiGVWwcQiDTW0/QJQZDKrX2sPCaBgvRTg2BIhA0u" - "GS4KP+/te4YLDEL2Fhr+n/xoREGCERIKgYiJBVDERxERxEODBCDBCIMEIMF04iI+oiNCIkDw" - "1bEREfrCB/WEH60gf0qMMH6VIIGU4GoKfSIEsGKCDV9UQNA9IeNA1JAHnhD4j///////////" - "//////////////////////////////+QPkEDMFW+yGQBPBA8NSAmQZ4IHhqQQ2oIEoDFkGuC" - "GlHkDwN4ILMyB4NM1ILMyB4NMyGrNLYeQPF4g14kFC4UgqQQLwFCpbe9pEGbiB4NfIu5As5N" - "Mg34hr9X+qu6Qd1t3Xb+0vUf//9G1/S+vIGYZj//tr67TXhf/S6/xH1//bX///9L/X///bX/" - "//9Lr///9Jtf/////8l/kNTiHwg2f/+k3LhpGgZclMhqeQaJ5Bp/INU9BkGiCBeMgnZDLgIM" - "IMhmwgyDXMg1QSmQ1KE3IF4JYQUHyGbBBdyBGhJBDXchrcQfCC4ZGggwE//xCDwgwQMIYIPJ" - "OCD0wUF1yCj00wVMEDBUGEMFCgg8gY8h+8hjRSEQE1//9JsJ6YUKEcMMIYRsjqBFMhsOC6BY" - "KmmQ0HTRsgwUINSDB1RgcI6BiCgz4OCBnwSDBBtAgz4OCmARf/thGxvTCOFCODoINhJJrRg3" - "+gvRt0YN6MGwgg3phGxVqkGgvvvbh6dqkv/S6D6MDaCDoIHS9J9BByBjCDfNLIGJhtJyBfEE" - "HSbIKMRgVoIHIKMVJ1IaMIJnTrTaTpaX/7e8Qm0mknSbIN8VnAMCn/S6YpuKem4hB0uJdpcg" - "oz3+9tb//9Lq6DpaTr9XV/hBdV1avTaXQff+61S66pL/9t3r/6b1en/S6aenW/Xof/dW/bSd" - "dL8gpD+lj7aTrr//+l//T02vVj/1ownTaV0KSW2QzMv6b/xr///0F//39ff9r1r060luEDXx" - "ELuq///+l/+vv/B/vTa3TFeqWw0DS8hkBoI/+Gv1/xf/+/r7JAZn7+n2m6Sr0bMMJeQyAXmb" - "P/j7X/v////Z1Av90v19UmHhBJBj8I8P/8iAMXr/nE////9h/3tpN03dJN/QYXoL/+cBs2l/" - "7f///+G/3S/W3XfSSBr0vr/2GEv/b///r8H//W6+kr9ofS//9iF/7f///+Q16f39Poum3pfj" - "6X93+GC/9v/+61vwf90m10lb1S//9L+9mA1v+3///X7/39N6T3SX//07r6X/t//v+/kMt3/d" - "LX0rdVId//11u9vS/9v/+0tK//19jikm+q//16bbX1/9v/9/rX69YqnVtvS//tdL0XWyDj6/" - "+3//aVpNr39our/XFJf/6L+GgmGQo7aW2vf/t//t1DSsIwvpWW8NL6pJt0l/9sJcMJMMKwwl" - "sMLyXAv/2H/+2lDCCYaX2lFMVbTurdKl/7EKDiExTFScNAogRrDIMazQMHUGJAjVsg+pDGpt" - "JOCHUQ0DQGEopiFkCKoYSdqThlfBKD0P/60Y07WGFt/+wuv9iFCDXxCaa3pqnf/8MIWgYQME" - "DCEMEIcRHFghEREQwU5BBhYhhNBhDT4jQ4/iIiIhGw7xHEcRERH/0g/9f4Sf//yB+Bf+l/6X" - "/9f/+ra+PVfXWCf/q2uC6r9NoLpuq9RHHS/IGeOltpV9rtpJehWwwSIHg08EDCDrDEKECDIM" - "tVYYIfaxER/////////////////////////////+ACACAA4AAAEDAAEAAAATBwAAAQEDAAEA" - "AABKAgAAAgEDAAEAAAABAAAAAwEDAAEAAAAEAAAABgEDAAEAAAABAAAAEQEEAAEAAAAIAAAA" - "EgEDAAEAAAABAAAAFQEDAAEAAAABAAAAFgEDAAEAAABKAgAAFwEEAAEAAACZDQAAGgEFAAEA" - "AABQDgAAGwEFAAEAAABYDgAAHAEDAAEAAAABAAAAKAEDAAEAAAACAAAAAAAAAAAAwBIAAAQA" - "AADAEgAABAA="; - -static const char fontdata_16[] = - "SUkqAHAPAAAmoCQP/////////////////////////////////IZJx0QyQzjkM45DJA3vIHhr" - "2RbyB9BA8Gy00/IHg8XZDMsiXkGzqIK/Akk2Q2nSINUyG25DVoQ1aEGSCGUoINjkFEyGPIZU" - "yGrPBVXqwQahNUm4PCBhQQYQMFwQcYIGED131IZoaNsOk6SbVII4bBQgwmlhAtHDDCOEH79Y" - "QNINqnrZBoHrQQbCpp+EFSCDYQQb/1wjkXbSekbfSbT9JsFTR82uEFpOk3/+gsOtqk6STadJ" - "LYR9Z4bhBv0FSTdX9fpYf6SeltP6cILhBtBOswCkpsNFdX666S+m1/p7pJbgtJ6bRddBVVNp" - "X++v69LpK2G164XT1/pa/v79a/69dWGKJ2krY+3ul6XS6V/69f9a0uGP/rX/Wkv//9f9fSps" - "Ol/vWl6Wv7/X//1pa6kGu9f/0vS69f+v8fW6S8Izf6/xr/1uu99yGga/qtaSbBH1HS28fS9I" - "atf8R/dVdJLwlf/6S+q9f/fdVpD9PpL9VXkvqmGl//uqxCW2r//18EDVbSv8gerIl3tpVW7C" - "vS/VKvQekw0tevb7SVrx//pBJcXRH9MNBf/yhQxCrIUZXf/0kvahA1Ypv/qIMMJQmv+l+pBp" - "cIOueG8J0w9f1ZLgyJNVuC/9JCDXhB9NWG2v1sNQjnIWvx0v6uug3EwTSu19cMNIh/SsGcF/" - "/6UuNpdaBB8I5hsMI2lv4N4QaTeP6X6iG1xbptJBBtiF/5DU1SCON07//9But61SDtfkFgal" - "29INrf5BZEyDInS/S1/bpINtJf4dJK1b/0v9JuvrVXBhf+303Tf//6Wu+9U2P/ukv3X6pdaT" - "oSGDZ9JXrogerIl79Orf5A8S6/0v/Wtev9Jb3S/FJ/S1/pXrH//2//v0t69fX/0v20v0tdKi" - "Gl36/X0Qyn/+20nr+tIhpj/v16XS/SX8f6X9L5BQ9dL0lr//7Vr7+k2l6V9euQPDx/pJdNv+" - "Q2o7rS62/VdUsJ//trbD/BSBPiWulf6T0k3SXfrpJdWw3rVPetIhiel3V/0gv+9LdWwgyKP/" - "qlfobaW2k6STapa9XpJXTDCH/XulrDSuwk3S6QS3pYelthJibabS10m0kGsMIJOwk2ltpBBd" - "LyjggkgxCaDX9PtpEMwGsUQ0xDEJsJJMNBBBbXgxFYYTCYT/tbFfC4TELDFEMueIiQa0JCGC" - "Bq6FIUgwhEGCEMIMIQwUFkMk3ERdaxEREf60vbVL/qkvbSX9+ku7SS8W0qHekttIdtLbS3ax" - "H//////////////////////////////////////8gMBZD1yBoDQ5A8GXQhkg31IGgFAZA8G0" - "MgeGsQQyQ2oIG45AkvyC2GvMgqoTIa6QhtBCGgbINQqQYFCDWoIbBBBBBDAghgQQwIIOgguI" - "INYZDTIIYIIGKgREA0EwDYRANBMBqgyGgoIYGEMVEHrIY0IYqyC+hAiZBvMhg5DL4gQLMzA8" - "PBAyGsn4MIHIqGZoED//9bwQcGCDgwgf/64J9pcLCYQOyG0kBGgeQboIQgg1AZBQYCMweQLz" - "IGJkMuZDLmQy5o+GWZgqOZgYZDNxHwoZBpORaI+FDINJyKdHhNENlCBjAZoBgEDNAzyGzNHA" - "zuv7CNBA1Z8I0CB2CIMHZ4GEzwLwgQO00001CadJtoIIGHBA2EEDDYIG0EDzYc+HtNNU1dEC" - "9EgdJmwUL5smEfBh24NhIO4N4fDoIP/6Xe+gg27aCDe2k01u+364eg3wkvQQbQQYfugnww9/" - "Sa/9L1vSb20m90n//p/tJ0v+nQTa7aTW3/pP/6Xfek3uk3tpfX7/dNq3Wl+2kG79L2/9L/9L" - "1vSb7Sb///tK1V6tJUvS0nRhd0vf/S//CXcwswnpPuk+6X///tpOlpfugm+/r2/9L/9LmFX3" - "6b+m/3/9unutJv68dbS/X28cV//S+69vW/W/X//XRhdv0tfIZAaQ5A8Ufp9/r/6//QXuv30/" - "q/4jj7/2raTdCl4WHpEH5tb6Ue/Uf/S+69vX+v/+6tf0nVLwsOlBM3dP9b/X/pe6+39/3//q" - "9PTdWkl4WDWkeb/vSX/X/0vuvb1fpX0r/br79XqvIHhYIZdhWHWEE6TaT//kF3yC7//pe6+/" - "XrX//rpatpWkvhNMIIMNUko/vS9v/r/0vuvv7670r/er3punVL7UEEGHXvpegq////S9/2//" - "/ph/3ut+k3SX9hAgw0tKvfCS//r/6X2l7er6SvpQ/9enVjtKvXCCDDSSS9bhggX///+l7/t/" - "6X4SZ1BW+3X/T6++EEQaBMMNL/p4h/668f6C+0vf/S/CTIgGz+ut01aSX00EFhg0tV+4P+OP" - "/0/f+/8JfmIYP96un23SS9poILDDS6rSeeCB///xf6Xt/5ZH8Ew/73XXvS8RCCwYaX6XeDf/" - "88GX+H7f7f+wX7hh/69XVsVXgghDDrX0vb/ngfPA//57f6+3/j+2Q2hH717+6+EEGGl0l77f" - "++//7ft17f/+2QV9f7W19PS8IEGHX6S3v9b7//t/r//9bkFNH709NqjabSXwg2v/T93///+3" - "7de33Xf2G/7/6S9L4Qer1SQvf1/7IN6v/b/X2//9sP+66V9N9fa79V+/X+9/+37de3v7+2//" - "Xvrevwwv6pX/+OOQzJ/+3+vt+l6W//e2ukk+l4YMIJ7fVGwz/vX7D/+37df9/f2/7pdXpN6X" - "hhhArfRdUqf36///b/X29//b/7/f768MMhqiYIJrbS0Et/f+//7ft17fpb1t/7paWqT6+GHh" - "BC22lpU/vpff/7f6+339r/6X33SV6RBRHhh4QVsUxCJ2t9XrkG77f/7eQUPt17e9b1tkC8V/" - "exv76VeDB4QThhMJa9W+v//9vX/77S7S2/73Sr0m9L8MMQgmxGlf70tdv/9v+K99v39vS3X9" - "ikr6/IHhYINEBhhBEFS7S70m9L9v/9v9/b3S20v/umKrV9fwmChhggSbaVP03hLrv/9v+vY7" - "S7S29L136b0v7UMMILYYSW9WGQLvpft//sPS3/bS20tu67S90r0l/oMMIJNtL8JMMJYS1ww/" - "/2//3YaW2lt2lthWqpX0v1w2gsMMElbwSYMElIOfW2Gt3fbarbXuGwgrYaCu7CVsGEv0r6C+" - "6QPC2DCSpoQgxoQkNWDCqq6txrppuwYSUMMElbgwknFe6tpof1h4TQYhEDGpBisIIMIIIg1C" - "hBgQGIRDQIIIIIYEZBuIDBhSC9TRDjCD1OxCIZohiEQzRDBxCINYwNNNUCTBkMsQvtUHhNBh" - "eiXBVClWGrwZCAX/7r/4YWGFuGFhhf1/44iIMEIiDOoZIaDUGQEQiIuIhwYISCmGIgwQhgvx" - "ER9IRHERIHgrwIiI11hGgGwCzroO+qCB+loP9JGCNQGwGXpECYGYPSCBkuBsBt9Q0qBr0ooS" - "GciHjQMJHQDx6IGobv8IRx///////////////////////////////////////yB49PIZIsED" - "wZIIHgxxA8rIHgqWQVrIEsM2yGnZDUvyGQoIM8yB4KnhSB/MgeDZMhtTCWw8geCTIamBIFIH" - "g2IUgzEEFeCGXAKC1t7rXrpp+v9WpA+4geCryMHIHvk0yBfiCp1b7ql6Q/+vf2vr///o4tel" - "015AzBmj/6tf9prwv/q/64j4X/0rS//r/vf9f//0rX///+m////9df///6b////1dL///+rg" - "iGpTIvkG2ZDS/IaX5DUpkpkNOCGXGQf8hmOR+QTyGnBKZDXoQ04I5kNqhJyGVBLiBc+QanIZ" - "4IZ4ISCOCOCDa5BUwgvxBeCJBFciuQz8Qxf/q4gg8EDBAwgeCB4IPCBgoLrkC/BBhBgoQMED" - "BQgYIGFBQoI1gokMzgWOMg9VkKGQwdY44//qwnphQhhHDDR1BQbJnnpkFCCGdGlkM6EGgyDc" - "hMI4QZBuhNDIEIVGx0ageQqAZoGAQMEoMJuCDBBhL/6unphHChGxwgg2FCCY9P9AvCp6aCDe" - "gwjg1qEg0F9pphbCB0mg1MBhf/Vo2K9GyYQQcIIHScKrWjZMgY8go/QWQUejhsI4bIGPRsmk" - "2QL+jZNAg5BR/ThSBHkMe9PbtpPX/+r0H0EG0g6TpXLx4MtPEJ3fS2IJiE7EJ0m3QINhIO6p" - "Pu/6039aX/pXvEJtLSDpNj8+GWn/S6aenVuKDpcS5pXIF+9tJu1dJ1pL/6em6etf9J6b/QXT" - "TdNpPTa9P//61aXWl/63/q6Wl/1/pdV19/XQ//dft039Vv9Nj03/7evv+l7vuk9Nr9j+6ujC" - "aTpaQqvZBp4gQ/q/6Qpa///hL///S6v/q/7SvSrcINP6t9////9Bf/97/3+2vTdN06SSWw0D" - "S8RIHgrU9f///0v/1//ZQDX/3XVj9IILDDQa+QyAatP15OJ+v+L//39fmoMz91dfTdbegpsg" - "0vIZAZlDd/8ff/3////sH+66tpPqkw8JJBivCPj//KgDF0v+eT////2/+nr3fSb0kga8IF//" - "PBt7S/9v//+vwb/bW1bq7SSfqg16X1/7df+3////hv/r9b9K/wwvS//9hhL/2////8g2Eft+" - "l0rdfq0P/93+GIX/t//39X5BUn/pN7ejG9Uvx9L0v/Bgv/b//61/D/39apNvSX//7v3y4bf/" - "b//f6/IZkP+6Wr0t2qX//1d74S/9v//X2//19+1Sf0iGH//XX1vS/9v/+60v//bWK6Stuv//" - "+m219f/b//f1tfrtetjpvVJf/sLpdq3r/7f/9pXTa/+sbWk2xSX/9owtoJttGFhkHfT/9v/9" - "urQTa9pWSHbRftbS+lX/thBcNJhkOOwwlsMJcqwyv+w//20rCUMI2v9pwwl9aTbS//xXDCCb" - "CsQrY1hra6sGtrrbaUNBMQtbSYpit/VvSBf/ak4ZzCFMbUkBsRDPU2QYrlAOawzyBFbkHrIP" - "WauUA5rCFAbGlEINZAhPDCCpqUBmp2gSg9D/9hdNNBrDC2//YXX+xCnkmF8U01vXTtf/hoaB" - "hAwQaEMEIOIjiyOGQCwCwwgYWIMEGgwh/EccfoREREI2CPEcRxEREa/wgg///hJ/6/0n/X/I" - "HiiX///pdfT+n/+tpePX9fhfX1bSyB49NKvptL7/1IHg1wEYA1CxdKvSEdtJLyBmDU/2l/YS" - "2wkl8eGGEEQPDXcJBBhBpYYhMQgQMgUVwsGaAeCsF7WIiI//////////////////////////" - "////////wAQAQA4AAAEDAAEAAADOBwAAAQEDAAEAAAB3AgAAAgEDAAEAAAABAAAAAwEDAAEA" - "AAAEAAAABgEDAAEAAAABAAAAEQEEAAEAAAAIAAAAEgEDAAEAAAABAAAAFQEDAAEAAAABAAAA" - "FgEDAAEAAAB3AgAAFwEEAAEAAABoDwAAGgEFAAEAAAAeEAAAGwEFAAEAAAAmEAAAHAEDAAEA" - "AAABAAAAKAEDAAEAAAACAAAAAAAAAAAAwBIAAAQAAADAEgAABAA="; - -static const char fontdata_18[] = - "SUkqAEARAAAmoCq/////////////////////////////////+QyXe5DJDVchncgthMyB4NFk" - "TMgeJBA8FKE06yB9ad5DbxIgScCpNkFYdSGnQgrOQbKENqhA3ghmWQz2QVRyBxZDMoQbJ4XU" - "g0YQl4IHhBhUm4OggwoIGCBhYwQZBuJggYIHhf1CJwazjaSdJNpqEGFQaYWgSwmg9d6yGanQ" - "Qb10m+gjxMKEGEGlhAtHhhhHyf/4QaVpIw3rZBpelQQbCpp+EFSCDaCDe/XSOMwbSfSDpJN3" - "1TYKqMyraC0nQTfT/pYaW0gv06dKk4Iz8+K4Qb9BUk2k/+ugt9+npbTXVtBcINwnWYAnTNg3" - "77f1+v1aS+k3dVXC6em0YXSqkrSv99UvX/S8N6q3C6dJ/0tLff/r9/S6pJsN0RB6rH2/S9JL" - "XX/r0v//WwxRfqt6XvXpaX1fv9f+lqlThjrf+tfS//////6pbdf910vrS9X+tdf9LSWsKQ1L" - "pfr/9fuv/f/H+1rcIzH+v8aS9LS17/yGwU96S0klsEf2OvePpfj3X+I/2v0l4Svr/S/SStf3" - "/9JwkwqevXf9L+m6/rvdLihW6vpfpL8jmktpf5A8WyEu+6She16/rSXggekwwgr/XvbXr2E3" - "1/SSXgmpHPYaWv+3tpJNEGt/H1/0viHhBpMU3/SyGoYhaZCg/v/0gSS7oINWtf9ifDVCSC6T" - "/X+Qa9Pnx1Tph3/0yEAkpr3Ba/pQaXBBvTUMNtf9hpI4mD+PX9JpdBvRwnV2F/2HhBOQxhFj" - "9f1mBh/TxBB0gjiDYYRxL1wbSIP6Tdj/X9QgbSxdJtQgg2xX/kNVPhA6t/1/SF/unpINwvel" - "DMbSCOG1b/IM4vX6TaW9OqQOGvogzCmD6TaT/5BSEL+k6X7aSTbSX+G0km+/9f+r/r0nDBf+" - "/TpN/9V1paWRjv6VNj/+kt7deQPBe9f0nQj99XS8geWEJe/6t/61fpf+lv8Kv0qb7S/FX9LX" - "9a6x1/X2/+v0v/pXX/0v20v110t69fr6IZcf/vv9daVENXt+vX/9L7bS6/9SGo//S9Ja/1/s" - "Va6t+ldZAu6V6X1/+l9tf11069//pZA9RX6/T/5BWhddK63p0krSSwnr9JOtsP8hteQLMpv0" - "tLtL1fS7/177Yb1qmvWk3X7aXaSWv/SXTcHr0nTa6IGBDXbSvSeku+r0ttJhhFIBO/XXS1tL" - "tJ0u1CXroPSSdWwj3f/ptJNbQSbaTaSTaQQXXw8JbDCCiE1117DSIZsBBlusMJENSAg4YQTD" - "CSsMIIILetkNQgkmKDQYVr00rFRGrEKJrDOKYqmKCC2FqDEVgwmEGnodqmvhbCYShhAgsREh" - "phpCDBBr0hUMIRBhCGCDCEMKCyGScCItpdCIiI/6S+9VX2uvvSX9qlXvWuraSVYtpUO9Jdqw" - "ttIdtLbS2mFiP///////////////////////////////////+QEwate8gaApwQPArqIZINtZ" - "A1DU1IHgpaEDwZCCC3wQPA4jyC2DJMgpIyG0BkFdQQUCZDUKSBAuQ1CZAuDIYBkFwZDAMguD" - "IYDIMAyGpqQ19CGNCGYnINYMg1DRBUBkGsbBEYDchgQQwwQYLlOGCIBc1A5GAxIEVENnkMqZ" - "BihBp4hoK5OB4KJBbBU1IbYxODBEaDORcGpYIH//63hA4MIHBhBxX/+QXDUJrwsIMIPDYRoC" - "MMGHDhGgOGGCDBBggYQYLDBBuEHDCCMw4YYYRmHYYaPBA3DBBhMEGEwSYYV9112EGpBXoQiA" - "gQNEDFCD6EGs5B6EDByCUCBA5AxQhmUIZtCGbQhmUR8Gg0BSonDMZBrwIEDZBqOQiAgQMMg1" - "nIQgIINZBvQg0YDNAzQgYLIKMdHAb0p8C69MIoCB3QdBB6IaEMIhsthEM9oIGmmmmmqaurpB" - "NsIG0E2wgbSDzAOZg9qnp9yGUdIN1BV84NozBh7hsJB3BvfdBP/6W6tukGHhtIMPDaT/7v6a" - "Qbw2lST8INhAgbT3QT7f+En/9LvvSb2wk3vXX/dfuk/pekHSb+0vDf+k//pevpP9P6T9P/uv" - "aVpf/aCDae6Xt/6X/4QX96Te2k3t//7pd09OlpfTpN/r7f/X/6XazCek36Tfpf/3+rat1per" - "03ML9Vv/r/+lzCza7et7re//+62vaTrpeOk636X3/pf/pfaXt9P9P///dPe9XX/pXT+vt44/" - "/6Xv/et+t+uOP/zCpNpNpCl5DIBocgeDj9Ot/Q/6Q/+l917+/7/j+6Wt+rpJeFh6RDH3T9Lt" - "+v/pe6+3pfpf/9/vt1el4WDrCDNzrev/1/6X3Xt/f99f7q2l1pOlXhYapBH2//qv//9L3X2/" - "fpX1v+6+1bStJfCw60E6t0/r+QIfIEOv/oL7r719f6//6fb30vIM9kM2wQQYaSQSf1vS9v//" - "/S919vr+vW/3S1/SbSS9BoMIIMNLqK6fSr/+v/S+/3++u+r/vvum0ukvVMIEGGuvreEF////" - "pe6Xt++kr6TD/enp16bX+8IIMOtL6fBBL+uq/+l9/t6+v6UH/a6+x2kl/hBBg0kl9eIL////" - "S/S+/9L8JMpwZf/03tPSX1hBEGsXDDS6r7lQGX/XXj/S9v9v/S/BBSoDb+6WvVWqXu0EFgw0" - "Fr6Twf8cf/p/pe/+EvzaMH/e36tulX1ggsMNLr+58IH///F+3+3/k9P4IMP966b9ul9oMIIL" - "DDS+kqfIPT/+fBo/f6Xt/8L9oN/669NxpeIhBCGGlqut7f8+GM+GP/z6fv/f+wX7YN/39Wqb" - "SXhAgw6/6fb/w+H//b+69v/H9sgyDP3SbV/0vCCDBpdaS+39f//7fuvt//7kDjT+//Ta/CCD" - "aXpfe////+w/uvfuu67B/3rq6Rvel8IP/0k/d6773/4b919v//bDf+urql6Xwgem/WL3f/9y" - "BhP/7f3X3v7+2/7f1+3pfDC/qkv/X/3/7fuvt+v+7/9Nq3SX18ML71V/f445Bqp/7f3Xt/Xp" - "dv/a/6Tevhhggm71WbBv/1+w//t+6+/f37f+9daq3peGGEFvouqSX79e//7f3+3v7+2/+urf" - "XpeGGEE3bfpd/f///t/pff19bf+3TddJN6XgwZDXFwghd0nCST++l9//t+3+32va/+l+uq3/" - "hh4QVthKIRPO+r/3//byBiPS9vet62yGU9f2vt6T6RDKjww8IJtimkkn1b0sgXjv/9v9v9vt" - "Lutv+9666pvS/DDwQLDCYKv/0v2//2/4r32/ddv911Y2Nb0vwwxCCbEV76b0utv/9v9/vdLt" - "L7S7pivSfr8gz2Qa4DDCCIMsNpJPq3r///t6XXt91t1t/69aV6/pkMzwwYQSDYaXfTelrt//" - "t/v7G2ltpbeltpe1vpV9qDDCCVsJK+kwyGaPpft//t/17tpbaW312rVaTekvrQYYIJNhpVeE" - "mwuEF+w//2/3920ttL7S20t9K9L+8MNBbDCS+kwYSUJa7f/7D16/bS20tu0uDBfaV9Jf0g8F" - "sGEltQSYYLZB0KFTDDVNNPTbUFTdNN2GEk2GEk24YSTYpqtK01C/WHhYYhINNCEGKCEJDTDB" - "gqaaem2hp6abhhhBJsGEEmw0GEk2v1YacfvQeE0GEQzKyGcQEEGEECIahQQIBhiCINwZDAMg" - "uDyCgGwYUgwnaIOIIYTuxCINQMMUQagcOIRDUBhhbuCTBkMwGvsLDwg0GF8hAZQUgQNnDJMM" - "r9b1/uGFhgtwwthNVVf+KQiIMEIiDBCIgynDIBRERGhFwYISBPqIkNGogwX4iI/QiOIiQPBZ" - "cRER9cIzA2gb+qCB/WEH60g/rQNdJJGxlOMjgbQaOpAmg2D1CBj+oaQg1egoogahpaXjQMKa" - "gPBjRA1Dbv4Q1sIcf////////////////////////////////////IFmpA0AkvUhkhlQQPAr" - "gIMgeFsgeBxBAkggTA0rIa9kNe/IZAJBA8vIHgT+FIHjmQPBS8gyeEth5A9HIa2BIgfkEDwU" - "iFIHxBA8FUghtkJbeHtL/IElkMu/S291UgeL5A8Cf0YOQPHNNMhleQZP/+6d0g4dNN3rfpV9" - "If/Xv00vX/yBoGoP/84n+l7+v/q6/7VeC//V167CDXhf/V1/xH//6tf/3/9J69f//9tf///0" - "v////2////9LX////br///9LQIg2UyE8gqTIa08hqzyDZTIsyGu5BofyDU5F8hiZDVgizIbZ" - "yGs5EmQVqEVyGa5F4hlnyGlBDZBA8G1yDJhA8NQC/9W2CBB4IGCBhA8EDwQPCBgpDK7ILzwU" - "IGEGChAwQMFCBhA1BQoI1hlQUFIaE5F0IOnUgmpBc5BjyDFZE0//rEIPCYQYQwnNQGHpkMs5" - "BufXIFzhNMhlnTCDIGDhMIZDYdQuSsgwcg5/77///VtPQYUI2OEeGwoR4iIDRTChdAsKmmFT" - "CPjYVBhHBYVUcFhDBQUgXoQI4hsopBjg2jYFzQCDwNzYLr/6sI4L0wjwsIIHQQbBUEGsJ/oL" - "008JoIMPTQQa1QQcF9qmug6TtV/+k9BvRwdBBoJB0m9J9HBshl+QMfoLIZfo4VHCZDL9HBtB" - "NkC/o4NhBByBj1UOkQ2eQY+6bvbV/S//bp9Ag2gg6TpWQL8k1EJ3fS2IQYhOxCdJ3QIG0ndG" - "3rmI8DTd/Wm60qS/9Lp4p10nSfVngzQg/6XCYTwg6txTpcQnVx/6e90m66//b7ptL3W+rq/0" - "F003V03Ta9Nf+2utWlrpf+l709Ol/77/hL/3Xp66f/+r1bS61//bHq1/SfXr/S6p69utfHH9" - "906em8UktshpYQ2P6X7+v/+/6Xv7pe2vV/2lzH7S1S/CBw/2/0hr7//9Bf/7/X3/9+k2r1SW" - "2g0vS//1///CX//e/8H+2lpXvVqkltoNLxEgeBORvr/9f+v/6//shhp/991iulBAsGDRww18" - "hkArV+vx9//F//7+vynBo+3WldNN6QMPBAkgwYS8ETv/9el/3////sP/W999JJh6SQYrwjMX" - "/8pAy7f/zyf///+w//dLSt+m9Kg16Bf/z4K+0v/b////w3+3X3vapfpA16X1/7aX/t///1+D" - "f+tq9JvSvpYYXpf/+wwgv/b////wb/bp6tbvSv2h9L+7/Yr/2//7/vyCuR/r/oxvSS/H/1/8" - "ML/2//+tL8H/erSel36/9L6/vBgv/b//f/8P/a/apN2kl//+22r6MArf9v//+n8g1O//TpPS" - "Tb6//+l/4S/9v/+0tb//Vtb7VK70iC8f/r1drel/7f//9f/69R/vpJf//q2+3pf+3/+3Wtr9" - "er7T0k231//YXS7X1/9v//StJtf+1j40rikq//RhbQTDbRhWyBA+n/7f/7faCte0rtG1qnSb" - "df/20uGk3YS4YXv/2//20oaTYRtf7Juwwgt3pXpJf/sILgwgmGQsMMILYYXlIGZ/2H/+2lDB" - "BMMJfDSjiuvSt0l/9irMPFMUxVsUnDTtPTYaaYTtO2GEopiFoM0WEmqTenVukP/6jmEnakgC" - "6IaE7DIMDJIDBThokNCdyC6cgunLGSGCEYHJANxChMLIaBEQqakMGn0CTB6/+wtIMINBrYLf" - "/2F/+wp5INegmmFtNPW//4NDQMIMEDQgwhBsRHEMjhkhAsGEDCxBhBoGEP4jQ4/iIiIhHATi" - "OI4iIiP/hBA3/X+Eg///pf9f6T/r/ZA8OLf//+l16T1///a6/S+k9aj//a5Arq0q9JtL//wY" - "YQLdtKvSEcdV8gaIddtKvTS20kvj2GEkvrYYIIgeCsOEggyB6sIOtiFFEFsGRPE6AZgsLDCY" - "XsFkDYDScREcf////////////////////////////////////4AIAIAADgAAAQMAAQAAAIEI" - "AAABAQMAAQAAAIsCAAACAQMAAQAAAAEAAAADAQMAAQAAAAQAAAAGAQMAAQAAAAEAAAARAQQA" - "AQAAAAgAAAASAQMAAQAAAAEAAAAVAQMAAQAAAAEAAAAWAQMAAQAAAIsCAAAXAQQAAQAAADcR" - "AAAaAQUAAQAAAO4RAAAbAQUAAQAAAPYRAAAcAQMAAQAAAAEAAAAoAQMAAQAAAAIAAAAAAAAA" - "AADAEgAABAAAAMASAAAEAA=="; - -static const char fontdata_20[] = - "SUkqABATAAAmoDgf////////////////////////////+QyQy7IGwGXPIZILLkNA/kDwVrIW" - "3IHgvBA8FqE00sgeC9pp5BWhIFSvIHhpOQPDToQK3ILYb01TTINOELmCJwypBY8FVsgy2kQ1" - "6BSCocEDBSDQBEFfCBcWINJwQeF/qDCDSCD0m4eCBhSDZWEGFwTwQMIPC1VKQa6keMPTpJu8" - "IMKEGmuECwg0fIP3dcIGgg2kE9JukkeGwqDQaWECwj42EEG//wiRhpN6ON0lZDSetBBsFTXw" - "gqQQNoJv/9HnJetpIJ1201SSbCpo0JroLQTdP/+EFh6b1ekm060mwRp5mNwQb8JUrat//1uu" - "kk+laeklhBaBA6QdZsCsKcAwqdK/qukv3/pXuvbgum4TaMLpUq3T7u9KltVaS61bfpcLp6/p" - "Kqp1vr1/1/qlcNpJK2wvfdL0tf3//X/S+qsGMjvrHpuuvS6XS//6//SVWw0c6X/q6+lr/f//" - "/pfXhj1b/9L0uv3+mvX/9JJLyGtiX9PX+uvr+/VePpf7YR9f/XGkvqquv/1X+u0klwUi3pL/" - "/66Wvf+Qbi/uqWklVhGaY/Xj6XpD3X+I/37pVuCT/36SX6Vpf1/0lpQl6vS/qv0lbr/720u0" - "h7hX+/6XkWfVpfv+6pRVbXpL+kvggaqw0r/IHgmELd7aWwldq//SSrwg6qGEtfpW9hoJKu2v" - "S/0kvCakWisNL/Xg8MV5DNp43/9IL4h4QaTEJv/4wwSSkEUf6X6gklrhB0+v+yXBmprW//0Q" - "06l5mK0k1DDv/5JgUIJrvBf+lB1wgbwnTDbX/Yejyh1sdL/tLhPo8TW9fqgw0kmyC/Eu//0k" - "cGH9NxBA2kEGwwjaX6hvIYmleP6X6UEG0tp0n0cbYYS+lyGqESCDSd//+hbXF4TaQSBuK/8h" - "tJ8I8w0m/9L9J673VINsL3+YbSQQbSv8gflZA4Cf6TaW9dJBsNJeQPyjD6Te3//+k/+2lTtf" - "+G0ltW/+kv0rS+vScGC/9+nr/1+utcijv6STY1/6S3t1/SX6vEfuu6/7/q3+QPDZi7fpa/0r" - "/RA8PhC56VN7a/il+lr+tdY/+vt1//pf+krr/6X9/6WulvX/1//+2k9f1pUQ1/30vS6ohmv6" - "X22tr/6RBsH//9dV+v9uv0m/SC110vVf/pfsV/9aTrkDELel6X//9tfpfpXX06/pZA8Hj/SS" - "6d+sgy3uul+39JWklhB//utsN/BSB4b4lddNpeler6X1rpJX3DtcKn/pdbaTpVpJaa1f/TcP" - "+k0m0tItXaX16t0l+vpJOkw0GQg71r7+h2l2k2lVqEv+Hpba2EeRrn/S9patpJthJulbSQX/" - "D0km0mdWmKtfdJtJNYYSuGEmwkraQS/4eEttBMQg09de2kQaUiygwiGuTk5ptKmGEEEFtexE" - "JJimgwv+kmDBLWK2ITBgkrBgkCC2qwYWDBBhBp0hSDWGIXwuExCoMQgQLERIauiQhggwr8Ug" - "whEGCEMEGEIYUFkMkMyMgeC/EMu+qwZwMgMo4B4axwDg8B4axwG0LxEW1SxEREfetL9Uv26p" - "L9JfVvpL20kv+qSxbSUd1S26YW2kO2ltpb1tNYj/////////////////////////////////" - "5AWBqr/IGoNMEMgk5DJBaGEDYGKyB4FlZA8CjQgtgYghkJ/IGYMUIHCLIKgkCKsC5QCHQNcl" - "oaCWBqlIGYVAMFWGCoBcqwXKgCDoDBUBrmoFQ1Bg6g1EQGmVYaREwZCrDSIoCqDIGKEDGhDC" - "ghnGEMAyBCchgvZDRWQUYQzaENEyGlMhsBfBAyCuMWRYFYjYaBKcNPQIiYF//9reEHIuG0HB" - "hA4r//IMEZBcOuaAeGnCDBA+wg4Pwgf//94QcMIOGEH/+uE1tetMIOyDIKBFAPIKCwhpi5DK" - "FQigGEQzCCGaQQzCCGaQQzCEaBmkgFNokBlshpzCJwcMhpaEQgInBwyGloRA0fD8g3IINPkD" - "CCDTMgoRkDE7R8NDqvTCJAQNQzMEYQIhsoGZgQ2aBns0BiEaAXtNNNNIJp6baQIG2aAu0CBt" - "mgLsIIPtNO01YaIGEUQMISnwy9do0Ah3g6CD5BQG5BRbkG9oIO00000gmm0naCCDDcNhBBhu" - "G6DzYYNAxap91oNpN1BV84Kwggw120EHwb38JB//S/vSb20m90nrf+vVtJtKkvQQbQQbvcJN" - "Yb/0E//pdrekG9tIN7aT7/v970/pfToIN+2gvb/pL/9L+9Jv0m/S//tLWm1bS/90m19L2/9J" - "//S770n3Sb2+v/fb/SetL0m0E97r7f+l/+l6za9X3X6//3Xat02lpf6TaNr9e3/1/+lzac2u" - "3pN7pN71//rrTaT1pfT0336Xv/r/+l+v3q/q///71362vXjq6T+vt44pf/pe3Xt9X9X6v/9q" - "2u3Tpa/pX39ff//6C/X29X9X/Ecff5tV0m6FLyGQCm5A8ND9Pv0kO/pD/6XvXv9/3//1902l" - "apeFg9SGKE/tL////0vtfb0n9J//7XSfb3pLwsOqCe+3qvfr/6XvXt/fXfX+9P/pWlXhYapB" - "GZv0vqt/r/0vtfv//1v+1vbSbSdV8Fh1oJtb76X///S969vV9Vfr/f0v6bpLyB4eyDVYSDDS" - "0unSfS/yGeMhnhf/S+19+vqvW/+v9tdJL0GEwggw9JJ//S+///9L3r2/v++r/tdJtXSbWvWw" - "gQYaWsfTelW//X/pfa+3/pfpMP96b+rrpL7TCCDBpaXr4QS////oL+vvV/V9KH/f/sU2kv+E" - "EGGlpV7eCC//1/9L2/2/9L9JmoFn7paTf6pf4QQYaSX6XEwGn//4/0v0vf/S/CCkICn//0mm" - "6S/wQRDTKwYaWl/cgQZn8Lhf/X2/2/8JfkKdlICt+1dJvabSr6aCCww6+tJ4P+OP/0/0vb/y" - "xwQX5tWD/er/v196CCww0tf+Zh3///F+3/f+1/BMP+//bbSS9hBoILDDS6VJJvBh//zMGv9/" - "pe3/wX7Qb/dLSbS40vEQgWGDS//w3///37f7/8F+2Df/v/bVeEEIYaWte3hv6mYEZmBH/5nv" - "9fb/x/bIKYT9tbX09Lwggw0v0kvf/ff/9v3r2//9yCwn/66bSV18EEDDrpf+////9v7X77ru" - "tsgtB///zadJfCDaX9JN73r/3/7fvXt//9h/20tL0ndL4Qer0lj7fr/yGZZBI/9v7X3//22/" - "/fb1vr4Qff6T3//3i//b969vf39sP+2trpJN6XwwX/X/////2/tfb9L0t//rp+vX4YX3SSv7" - "6xxshpI/9h+9e339/b//96t6XhhhBPeqSNgY++v4f/2/v+//9v/bSdLpJvrwwYQVu3LrX/fX" - "u//t+6XvvW9bb//e2qXpeGGEE3elqrf+v3/+39/t9r37/7df+3peGGQ2ysIJp20sIKv2+v//" - "2/0vb+vS7/9dL0vXww8ECG2wk4SW+vX2//w37f7e37+3Xpe+2kk3peGHhBOGKiieU/t9ZDLj" - "//28hmR6X36XaW2Qy4+9tLS9W+iDRHhg8IKwwmEq3revW3/+3+3+3t+3W///G/Xpfgw8IJsW" - "Cqn6b0tdv/9vXivfdL/vrdW640m9fwwxCCbFL9X0v7//b639vuttLb17qOqW9L8geHshp2GG" - "CCIG92q31D0v2//2/69vbS20tvr13dJ9L9MFDDCCCbaVPq3pft//t/v8baXpbeu3tV031+mE" - "gwwgVtpLek3hL9v/9v/920tuvtLtL6W9JfvDBhBK2Et9JhkM2NLXb//b12ve0thpbd1tha3S" - "fX/wwwgrYYSSvCTDCWCX7D//b//dhpbYS27CXaTtaV6S/pA6Cwwwl+CQYMElIEB6Ww17/bVd" - "rtw2Ethpd2lsMJf7ekF/w8LYMIJO0ITIwXEJDTDBqmqemw409Ndgwgkwwwgk24MJJs1DTW0k" - "2mh/0HhUGQyQIGCDoMUEECkNCgM1iyGxQQYVkMKMgXUMMJAgbQIiAzg3ZqJEGpQwYJEGpQw5" - "0JENRWxXWCwyDWo/aw8JoMQgQMhsBggQYSBAuGJIAxrrYYUgQIwUhgQ4YhYYhbDQhbX4SYev" - "sJIPBNBgvkmBwCkaBU4ZAgzf+/v7hgsMLcGFhhU71/40IiDCERILIgREgsCBEcREaEQ4MEJA" - "sTiIMEIME9UIiP5BbBq8hkhnmQWy/EgeDIOQUuIZAuEDwZuIHgz0ER9IRHEWZgSgVf0ED9LQ" - "f1QQP0tB/pI5g/SpBEQuSe+iByA3HqEDH6UMKINfUUQNgJXfCoGFNYHhlxhAwvogbAzB/hCO" - "P/////////////////////////////////+QPFrIGoF8IHYG3PIZIbdkMg4CZA8CIIZAb2QJ" - "7IE0NbMg255DIAw5A8PMhkLwpA8H8geBZMgpzCCww/CkDy0IHgVhqQPFDIHgcaEFXRLbyB4L" - "0INvL/kFiyDRnpbe6aXrhNP17+0iB4PMhkL0bKQPB/tMhlTIKc//XfSD67vW/2l6Q/+vfqvr" - "///zyf6W7XkDUo/9LS/6/C//Ta+uGgwvBf+lev8R//+rX/+v/V/1///q1////V////9K0v//" - "/9v////S/////bX///9LnA2ycNKQkyDLMg2/kG2eQ26ZEoQ2oINT+Q0nIkyDEyDa5EoQVzkN" - "qCEmQZTkRyDTBF4hmoyGq5Aw5A8FNyBxf/03BAg8EDIbWoIHggeQ19cIGFBcgwfBQgYQMFCB" - "ggYKCBhA1ChMEU4ZkFBSGwQRKiC4yyD1EGEENHkFxhEqIaBv/1cQg8JhBhDCDwg9Mg1UIGKa" - "4KmmQaKJhAwVBhDIGKKE8g0UIEU9V7X//6sJ6DChQjw2ERAFzxEGGygyBjQho00CyCgMJpkD" - "AaDCPjZAvog1IaNFR4WiMB5BNCBhBDP5BQjIEUtHAUEgGAX/6ujYr0wjwsI8LCCDYSQQfX9B" - "emE9UEG9MI8L1QQcL7TTSYaIGDpMINTYEL/6sJA+jg2gg4QQOk2FSawjxP9BejxtHieEeJpB" - "h6ODaBA16QcF96fp96SS/9XpvhB0EHQQdJvSfQINkM2ZDKnoLIZvoEHQTZDLmgQbSchlzQIN" - "hIOQy5qr5DRMhnn/e2ldVX/1e+kG0nSdLIZXqzMGgU7vpcUxCdinSbdIOk7o2eshl+7uk602" - "k/pf+r08QnWldW9XhP+lwmnhOtxCbS4hPv/ff9aWv/pX9Wk6T19XTf6XTTdN03TpdOv/06aT" - "aT+l/+33ul1rf//oL/9fbX6/9tf20rS6/9LF61169Xr/S6rr2666HH/rTp0nxSS2yGpxAu/7" - "ft/6v/v+l7vul7a/f77c2q33SX4Qafpf9Cl///4QX/+//V/2vXulqqW6DX6b/X///9L///S+" - "H/1q2k2k9JJbaBpeIgu///X/pf/97/sH+2rdfFWtILDDQa+QyQ31/XX//9P/9f/5qDU/1rdN" - "PpJbaPEQzXkMgCsR6/H3/8f/+/r7IgGn7/Tq1dJBh4QVBivBFB///S/7////2H/aVr16qw8J" - "JBrwjQT/8gYZt6/59P////Yf7703tvST6SQNegX/8zBT2l/7f//9fhv+6/Sbtav6DXhBfX/t" - "pf+3////hv/XSvTekn6UGF6X//tpf+3////g3+9N7/6V6tD//d/sMJf+3///35Bk0/tddJNt" - "V/H0v//Yhf+3//daX5AkR+/03o3vpL//0v28ML/2////8H/tK10km7Wv/S/d/wzCBf+3/+/1" - "+/771elvSX//1d/pD/7f//WrfkGuP+0tX1b9IgRH//r63hL/2//7Xr/f1+/SpttJf/9em730" - "v/b//f1v/7V6er0r9L//XSttb1/9v//rptfr+1jY0ntUq/+197X1/9v/+0rS1/9XqqVtiqX/" - "9bQVtowrZDYPr/7D//b7SYa9pW2jCxu6V6//thG/aTdhLbC9/+3/+6VoK1/yKPYS3rSbaS//" - "hhLhhJhkMOwwlw15AgaP+3//aVpMMEc/aVhOGEuulfS//iFoMIJhhWKthhJYd2urBra922lB" - "ggmK1tJimK/eraSBf/asqIpgwVqGwYJBA2GCDhhA8IG2EDBAwQODBBw2GlFMLCKcDDCVVBu1" - "hJh6H/7Cjm0ExQanQMoUQ2F7IEF50DBLhokNghyGC5DBcEDnQHIgEOgFwYIKE1kNgOUGE0wo" - "OtoKw9f/a6DQaDC2Cw//sF/+xCn0gwvimg1vXW0//hhDQMIMEDQgwhDYiOLLhkhlBYMEDCxD" - "BBoMEO+I44/iIkCThILGBHgTiQyQaOxHIHh+EREa/xFBEM58fH/sIJ/6/wk///pP+l/kDwIK" - "////X/3r+uvStf///8ev0nrC+v9pZA8WvSr0g2l//7aXtpfqJTgi4GwGeP16QjtpJeQNAanX" - "tL+0tsJJehWwwgklX2GEgkmg6wxCBIgZiCCB+DrDChfCyBtDOdkcDMMcMLEREf//////////" - "////////////////////4AIAIAAOAAABAwABAAAATAkAAAEBAwABAAAAcwIAAAIBAwABAAAA" - "AQAAAAMBAwABAAAABAAAAAYBAwABAAAAAQAAABEBBAABAAAACAAAABIBAwABAAAAAQAAABUB" - "AwABAAAAAQAAABYBAwABAAAAcwIAABcBBAABAAAABxMAABoBBQABAAAAvhMAABsBBQABAAAA" - "xhMAABwBAwABAAAAAQAAACgBAwABAAAAAgAAAAAAAAAAAMASAAAEAAAAwBIAAAQA"; - -#endif /* LEPTONICA_BMFDATA_H */ - - diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmp.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmp.h deleted file mode 100644 index 568c9901..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmp.h +++ /dev/null @@ -1,124 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_BMP_H -#define LEPTONICA_BMP_H - -/*! - * \file bmp.h - * - *
- * This file is here to describe the fields in the header of - * the BMP file. These fields are not used directly in Leptonica. - * The only thing we use are the sizes of these two headers. - * Furthermore, because of potential namespace conflicts with - * the typedefs and defined sizes, we have changed the names - * to protect anyone who may also need to use the original definitions. - * Thanks to J. D. Bryan for pointing out the potential problems when - * developing on Win32 compatible systems. - *- */ - -/*-------------------------------------------------------------* - * BMP file header * - *-------------------------------------------------------------*/ - -/*! BMP file header - * - * Notes: - * (1) The bfSize field is stored as a 32 bit integer and includes - * the size of the BMP_FileHeader, BMP_InfoHeader, the color - * table (if any), and the size of the DIB bits. - * (2) The bfOffBits field is also stored as a 32 bit integer and - * contains the absolute offset in bytes of the image data - * in this file. Some bmp files have additional data after the - * BMP_InfoHeader and before the color table (if it exists). - * However, enabling reading of these files makes the reader - * vulnerable to various malware attacks. Therefore we do not - * read bmp files with extra data, and require that the size - * of the color table in bytes is - * offset - sizeof(BMP_FileHeader) - sizeof(BMP_InfoHeader) - * (3) Use arrays of l_uint8[] to make an endianness agnostic - * access to the BMP_FileHeader easier. - */ -struct BMP_FileHeader -{ - l_uint8 bfType[2]; /*!< file type; must be "BM" */ - l_uint8 bfSize[4]; /*!< length of the file; - sizeof(BMP_FileHeader) + - sizeof(BMP_InfoHeader) + - size of optional extra data + - size of color table + - size of DIB bits */ - l_uint8 bfReserved1[2]; /*!< don't care (set to 0) */ - l_uint8 bfReserved2[2]; /*!< don't care (set to 0) */ - l_uint8 bfOffBits[4]; /*!< offset from beginning of file */ -}; -typedef struct BMP_FileHeader BMP_FH; - -/*! Number of bytes in a BMP file header */ -#define BMP_FHBYTES sizeof(BMP_FH) - - -/*-------------------------------------------------------------* - * BMP info header * - *-------------------------------------------------------------*/ - -/*! BMP info header */ -struct BMP_InfoHeader -{ - l_int32 biSize; /*!< size of the BMP_InfoHeader struct */ - l_int32 biWidth; /*!< bitmap width in pixels */ - l_int32 biHeight; /*!< bitmap height in pixels */ - l_int16 biPlanes; /*!< number of bitmap planes */ - l_int16 biBitCount; /*!< number of bits per pixel */ - l_int32 biCompression; /*!< compress format (0 == uncompressed) */ - l_int32 biSizeImage; /*!< size of image in bytes */ - l_int32 biXPelsPerMeter; /*!< pixels per meter in x direction */ - l_int32 biYPelsPerMeter; /*!< pixels per meter in y direction */ - l_int32 biClrUsed; /*!< number of colors used */ - l_int32 biClrImportant; /*!< number of important colors used */ -}; -typedef struct BMP_InfoHeader BMP_IH; - -/*! Number of bytes in a BMP info header */ -#define BMP_IHBYTES sizeof(BMP_IH) - - -/*-------------------------------------------------------------* - * Align BMP headers on 4 byte boundaries * - *-------------------------------------------------------------*/ - -/*! BMP_IH is misaligned, causing crashes on some big-endians. - * A packed struct forces alignment. */ -#if defined(__GNUC__) -typedef struct __attribute__((__packed__)) { - BMP_FH bmpfh; - BMP_IH bmpih; -} BMP_HEADER; -#endif - -#endif /* LEPTONICA_BMP_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmpio.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmpio.c deleted file mode 100644 index ef645128..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmpio.c +++ /dev/null @@ -1,639 +0,0 @@ -/*====================================================================* - - 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 bmpio.c - *
- * - * Read bmp - * PIX *pixReadStreamBmp() - * PIX *pixReadMemBmp() - * - * Write bmp - * l_int32 pixWriteStreamBmp() - * l_int32 pixWriteMemBmp() - * - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) Here are references on the bmp file format: - * http://en.wikipedia.org/wiki/BMP_file_format - * http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html - *- */ -PIX * -pixReadStreamBmp(FILE *fp) -{ -l_uint8 *data; -size_t size; -PIX *pix; - - PROCNAME("pixReadStreamBmp"); - - if (!fp) - return (PIX *)ERROR_PTR("fp not defined", procName, NULL); - - /* Read data from file and decode into Y,U,V arrays */ - rewind(fp); - if ((data = l_binaryReadStream(fp, &size)) == NULL) - return (PIX *)ERROR_PTR("data not read", procName, NULL); - - pix = pixReadMemBmp(data, size); - LEPT_FREE(data); - return pix; -} - - -/*! - * \brief pixReadMemBmp() - * - * \param[in] cdata bmp data - * \param[in] size number of bytes of bmp-formatted data - * \return pix, or NULL on error - * - *
- * Notes: - * (1) The BMP file is organized as follows: - * * 14 byte fileheader - * * Variable size infoheader: 40, 108 or 124 bytes. - * We only use data in he first 40 bytes. - * * Optional colormap, with size 4 * ncolors (in bytes) - * * Image data - * (2) 2 bpp bmp files are not valid in the original spec, but they - * are valid in later versions. - *- */ -PIX * -pixReadMemBmp(const l_uint8 *cdata, - size_t size) -{ -l_uint8 pel[4]; -l_uint8 *cmapBuf, *fdata, *data; -l_int16 bftype, depth, d; -l_int32 offset, ihbytes, width, height, height_neg, xres, yres; -l_int32 compression, imagebytes, fdatabytes, cmapbytes, ncolors, maxcolors; -l_int32 fdatabpl, extrabytes, pixWpl, pixBpl, i, j, k; -l_uint32 *line, *pixdata, *pword; -l_int64 npixels; -BMP_FH *bmpfh; -#if defined(__GNUC__) -BMP_HEADER *bmph; -#define bmpih (&bmph->bmpih) -#else -BMP_IH *bmpih; -#endif -PIX *pix, *pix1; -PIXCMAP *cmap; - - PROCNAME("pixReadMemBmp"); - - if (!cdata) - return (PIX *)ERROR_PTR("cdata not defined", procName, NULL); - if (size < sizeof(BMP_FH) + sizeof(BMP_IH)) - return (PIX *)ERROR_PTR("bmf size error", procName, NULL); - - /* Verify this is an uncompressed bmp */ - bmpfh = (BMP_FH *)cdata; - bftype = bmpfh->bfType[0] + ((l_int32)bmpfh->bfType[1] << 8); - if (bftype != BMP_ID) - return (PIX *)ERROR_PTR("not bmf format", procName, NULL); -#if defined(__GNUC__) - bmph = (BMP_HEADER *)bmpfh; -#else - bmpih = (BMP_IH *)(cdata + BMP_FHBYTES); -#endif - compression = convertOnBigEnd32(bmpih->biCompression); - if (compression != 0) - return (PIX *)ERROR_PTR("cannot read compressed BMP files", - procName, NULL); - - /* Find the offset from the beginning of the file to the image data */ - offset = bmpfh->bfOffBits[0]; - offset += (l_int32)bmpfh->bfOffBits[1] << 8; - offset += (l_int32)bmpfh->bfOffBits[2] << 16; - offset += (l_uint32)bmpfh->bfOffBits[3] << 24; - - /* Read the remaining useful data in the infoheader. - * Note that the first 4 bytes give the infoheader size. */ - ihbytes = convertOnBigEnd32(*(l_uint32 *)(bmpih)); - width = convertOnBigEnd32(bmpih->biWidth); - height = convertOnBigEnd32(bmpih->biHeight); - depth = convertOnBigEnd16(bmpih->biBitCount); - imagebytes = convertOnBigEnd32(bmpih->biSizeImage); - xres = convertOnBigEnd32(bmpih->biXPelsPerMeter); - yres = convertOnBigEnd32(bmpih->biYPelsPerMeter); - - /* Some sanity checking. We impose limits on the image - * dimensions, resolution and number of pixels. We make sure the - * file is the correct size to hold the amount of uncompressed data - * that is specified in the header. The number of colormap - * entries is checked: it can be either 0 (no cmap) or some - * number between 2 and 256. - * Note that the imagebytes for uncompressed images is either - * 0 or the size of the file data. (The fact that it can - * be 0 is perhaps some legacy glitch). */ - if (width < 1) - return (PIX *)ERROR_PTR("width < 1", procName, NULL); - if (width > L_MAX_ALLOWED_WIDTH) - return (PIX *)ERROR_PTR("width too large", procName, NULL); - if (height == 0 || height < -L_MAX_ALLOWED_HEIGHT || - height > L_MAX_ALLOWED_HEIGHT) - return (PIX *)ERROR_PTR("invalid height", procName, NULL); - if (xres < 0 || xres > L_MAX_ALLOWED_RES || - yres < 0 || yres > L_MAX_ALLOWED_RES) - return (PIX *)ERROR_PTR("invalid resolution", procName, NULL); - height_neg = 0; - if (height < 0) { - height_neg = 1; - height = -height; - } - if (ihbytes != 40 && ihbytes != 108 && ihbytes != 124) { - L_ERROR("invalid ihbytes = %d; not in {40, 108, 124}\n", - procName, ihbytes); - return NULL; - } - npixels = 1LL * width * height; - if (npixels > L_MAX_ALLOWED_PIXELS) - return (PIX *)ERROR_PTR("npixels too large", procName, NULL); - if (depth != 1 && depth != 2 && depth != 4 && depth != 8 && - depth != 16 && depth != 24 && depth != 32) { - L_ERROR("invalid depth = %d; not in {1, 2, 4, 8, 16, 24, 32}\n", - procName, depth); - return NULL; - } - fdatabpl = 4 * ((1LL * width * depth + 31)/32); - fdatabytes = fdatabpl * height; - if (imagebytes != 0 && imagebytes != fdatabytes) { - L_ERROR("invalid imagebytes = %d; not equal to fdatabytes = %d\n", - procName, imagebytes, fdatabytes); - return NULL; - } - - /* In the original spec, BITMAPINFOHEADER is 40 bytes. - * There have been a number of revisions, to capture more information. - * For example, the fifth version, BITMAPV5HEADER, adds 84 bytes - * of ICC color profiles. We use the size of the infoheader - * to accommodate these newer formats. Knowing the size of the - * infoheader gives more opportunity to sanity check input params. */ - cmapbytes = offset - BMP_FHBYTES - ihbytes; - ncolors = cmapbytes / sizeof(RGBA_QUAD); - if (ncolors < 0 || ncolors == 1) - return (PIX *)ERROR_PTR("invalid: cmap size < 0 or 1", procName, NULL); - if (ncolors > 0 && depth > 8) - return (PIX *)ERROR_PTR("can't have cmap for d > 8", procName, NULL); - maxcolors = (depth <= 8) ? 1 << depth : 0; - if (ncolors > maxcolors) { - L_ERROR("cmap too large for depth %d: ncolors = %d > maxcolors = %d\n", - procName, depth, ncolors, maxcolors); - return NULL; - } - if (size != 1LL * offset + 1LL * fdatabytes) - return (PIX *)ERROR_PTR("size incommensurate with image data", - procName,NULL); - - /* Handle the colormap */ - cmapBuf = NULL; - if (ncolors > 0) { - if ((cmapBuf = (l_uint8 *)LEPT_CALLOC(ncolors, sizeof(RGBA_QUAD))) - == NULL) - return (PIX *)ERROR_PTR("cmapBuf alloc fail", procName, NULL ); - - /* Read the colormap entry data from bmp. The RGBA_QUAD colormap - * entries are used for both bmp and leptonica colormaps. */ - memcpy(cmapBuf, cdata + BMP_FHBYTES + ihbytes, - ncolors * sizeof(RGBA_QUAD)); - } - - /* Make a 32 bpp pix if depth is 24 bpp */ - d = (depth == 24) ? 32 : depth; - if ((pix = pixCreate(width, height, d)) == NULL) { - LEPT_FREE(cmapBuf); - return (PIX *)ERROR_PTR( "pix not made", procName, NULL); - } - pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */ - pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */ - pixSetInputFormat(pix, IFF_BMP); - pixWpl = pixGetWpl(pix); - pixBpl = 4 * pixWpl; - - /* Convert the bmp colormap to a pixcmap */ - cmap = NULL; - if (ncolors > 0) { /* import the colormap to the pix cmap */ - cmap = pixcmapCreate(L_MIN(d, 8)); - LEPT_FREE(cmap->array); /* remove generated cmap array */ - cmap->array = (void *)cmapBuf; /* and replace */ - cmap->n = L_MIN(ncolors, 256); - for (i = 0; i < cmap->n; i++) /* set all colors opaque */ - pixcmapSetAlpha (cmap, i, 255); - } - pixSetColormap(pix, cmap); - - /* Acquire the image data. Image origin for bmp is at lower right. */ - fdata = (l_uint8 *)cdata + offset; /* start of the bmp image data */ - pixdata = pixGetData(pix); - if (depth != 24) { /* typ. 1 or 8 bpp */ - data = (l_uint8 *)pixdata + pixBpl * (height - 1); - for (i = 0; i < height; i++) { - memcpy(data, fdata, fdatabpl); - fdata += fdatabpl; - data -= pixBpl; - } - } else { /* 24 bpp file; 32 bpp pix - * Note: for bmp files, pel[0] is blue, pel[1] is green, - * and pel[2] is red. This is opposite to the storage - * in the pix, which puts the red pixel in the 0 byte, - * the green in the 1 byte and the blue in the 2 byte. - * Note also that all words are endian flipped after - * assignment on L_LITTLE_ENDIAN platforms. - * - * We can then make these assignments for little endians: - * SET_DATA_BYTE(pword, 1, pel[0]); blue - * SET_DATA_BYTE(pword, 2, pel[1]); green - * SET_DATA_BYTE(pword, 3, pel[2]); red - * This looks like: - * 3 (R) 2 (G) 1 (B) 0 - * |-----------|------------|-----------|-----------| - * and after byte flipping: - * 3 2 (B) 1 (G) 0 (R) - * |-----------|------------|-----------|-----------| - * - * For big endians we set: - * SET_DATA_BYTE(pword, 2, pel[0]); blue - * SET_DATA_BYTE(pword, 1, pel[1]); green - * SET_DATA_BYTE(pword, 0, pel[2]); red - * This looks like: - * 0 (R) 1 (G) 2 (B) 3 - * |-----------|------------|-----------|-----------| - * so in both cases we get the correct assignment in the PIX. - * - * Can we do a platform-independent assignment? - * Yes, set the bytes without using macros: - * *((l_uint8 *)pword) = pel[2]; red - * *((l_uint8 *)pword + 1) = pel[1]; green - * *((l_uint8 *)pword + 2) = pel[0]; blue - * For little endians, before flipping, this looks again like: - * 3 (R) 2 (G) 1 (B) 0 - * |-----------|------------|-----------|-----------| - */ - extrabytes = fdatabpl - 3 * width; - line = pixdata + pixWpl * (height - 1); - for (i = 0; i < height; i++) { - for (j = 0; j < width; j++) { - pword = line + j; - memcpy(&pel, fdata, 3); - fdata += 3; - *((l_uint8 *)pword + COLOR_RED) = pel[2]; - *((l_uint8 *)pword + COLOR_GREEN) = pel[1]; - *((l_uint8 *)pword + COLOR_BLUE) = pel[0]; - /* should not use alpha byte, but for buggy readers, - * set it to opaque */ - *((l_uint8 *)pword + L_ALPHA_CHANNEL) = 255; - } - if (extrabytes) { - for (k = 0; k < extrabytes; k++) { - memcpy(&pel, fdata, 1); - fdata++; - } - } - line -= pixWpl; - } - } - - pixEndianByteSwap(pix); - if (height_neg) - pixFlipTB(pix, pix); - - /* ---------------------------------------------- - * The bmp colormap determines the values of black - * and white pixels for binary in the following way: - * (a) white = 0 [255], black = 1 [0] - * 255, 255, 255, 255, 0, 0, 0, 255 - * (b) black = 0 [0], white = 1 [255] - * 0, 0, 0, 255, 255, 255, 255, 255 - * We have no need for a 1 bpp pix with a colormap! - * Note: the alpha component here is 255 (opaque) - * ---------------------------------------------- */ - if (depth == 1 && cmap) { - pix1 = pixRemoveColormap(pix, REMOVE_CMAP_TO_BINARY); - pixDestroy(&pix); - pix = pix1; /* rename */ - } - - return pix; -} - - -/*--------------------------------------------------------------* - * Write bmp * - *--------------------------------------------------------------*/ -/*! - * \brief pixWriteStreamBmp() - * - * \param[in] fp file stream - * \param[in] pix all depths - * \return 0 if OK, 1 on error - */ -l_ok -pixWriteStreamBmp(FILE *fp, - PIX *pix) -{ -l_uint8 *data; -size_t size, nbytes; - - PROCNAME("pixWriteStreamBmp"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - pixWriteMemBmp(&data, &size, pix); - rewind(fp); - nbytes = fwrite(data, 1, size, fp); - free(data); - if (nbytes != size) - return ERROR_INT("Write error", procName, 1); - return 0; -} - - -/*! - * \brief pixWriteMemBmp() - * - * \param[out] pfdata data of bmp formatted image - * \param[out] pfsize size of returned data - * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) 2 bpp bmp files are not valid in the original spec, and are - * written as 8 bpp. - * (2) pix with depth <= 8 bpp are written with a colormap. - * 16 bpp gray and 32 bpp rgb pix are written without a colormap. - * (3) The transparency component in an rgb pix is ignored. - * All 32 bpp pix have the bmp alpha component set to 255 (opaque). - * (4) The bmp colormap entries, RGBA_QUAD, are the same as - * the ones used for colormaps in leptonica. This allows - * a simple memcpy for bmp output. - *- */ -l_ok -pixWriteMemBmp(l_uint8 **pfdata, - size_t *pfsize, - PIX *pixs) -{ -l_uint8 pel[4]; -l_uint8 *cta = NULL; /* address of the bmp color table array */ -l_uint8 *fdata, *data, *fmdata; -l_int32 cmaplen; /* number of bytes in the bmp colormap */ -l_int32 ncolors, val, stepsize; -l_int32 w, h, d, fdepth, xres, yres; -l_int32 pixWpl, pixBpl, extrabytes, fBpl, fWpl, i, j, k; -l_int32 heapcm; /* extra copy of cta on the heap ? 1 : 0 */ -l_uint32 offbytes, fimagebytes; -l_uint32 *line, *pword; -size_t fsize; -BMP_FH *bmpfh; -#if defined(__GNUC__) -BMP_HEADER *bmph; -#define bmpih (&bmph->bmpih) -#else -BMP_IH *bmpih; -#endif -PIX *pix; -PIXCMAP *cmap; -RGBA_QUAD *pquad; - - PROCNAME("pixWriteMemBmp"); - - if (pfdata) *pfdata = NULL; - if (pfsize) *pfsize = 0; - if (!pfdata) - return ERROR_INT("&fdata not defined", procName, 1 ); - if (!pfsize) - return ERROR_INT("&fsize not defined", procName, 1 ); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - - pixGetDimensions(pixs, &w, &h, &d); - if (d == 2) { - L_WARNING("2 bpp files can't be read; converting to 8 bpp\n", procName); - pix = pixConvert2To8(pixs, 0, 85, 170, 255, 1); - d = 8; - } else { - pix = pixCopy(NULL, pixs); - } - fdepth = (d == 32) ? 24 : d; - - /* Resolution is given in pixels/meter */ - xres = (l_int32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); - yres = (l_int32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); - - pixWpl = pixGetWpl(pix); - pixBpl = 4 * pixWpl; - fWpl = (w * fdepth + 31) / 32; - fBpl = 4 * fWpl; - fimagebytes = h * fBpl; - if (fimagebytes > 4LL * L_MAX_ALLOWED_PIXELS) { - pixDestroy(&pix); - return ERROR_INT("image data is too large", procName, 1); - } - - /* If not rgb or 16 bpp, the bmp data is required to have a colormap */ - heapcm = 0; - if (d == 32 || d == 16) { /* 24 bpp rgb or 16 bpp: no colormap */ - ncolors = 0; - cmaplen = 0; - } else if ((cmap = pixGetColormap(pix))) { /* existing colormap */ - ncolors = pixcmapGetCount(cmap); - cmaplen = ncolors * sizeof(RGBA_QUAD); - cta = (l_uint8 *)cmap->array; - } else { /* no existing colormap; d <= 8; make a binary or gray one */ - if (d == 1) { - cmaplen = sizeof(bwmap); - ncolors = 2; - cta = (l_uint8 *)bwmap; - } else { /* d = 2,4,8; use a grayscale output colormap */ - ncolors = 1 << fdepth; - cmaplen = ncolors * sizeof(RGBA_QUAD); - heapcm = 1; - cta = (l_uint8 *)LEPT_CALLOC(cmaplen, 1); - stepsize = 255 / (ncolors - 1); - for (i = 0, val = 0, pquad = (RGBA_QUAD *)cta; - i < ncolors; - i++, val += stepsize, pquad++) { - pquad->blue = pquad->green = pquad->red = val; - pquad->alpha = 255; /* opaque */ - } - } - } - -#if DEBUG - {l_uint8 *pcmptr; - pcmptr = (l_uint8 *)pixGetColormap(pix)->array; - lept_stderr("Pix colormap[0] = %c%c%c%d\n", - pcmptr[0], pcmptr[1], pcmptr[2], pcmptr[3]); - lept_stderr("Pix colormap[1] = %c%c%c%d\n", - pcmptr[4], pcmptr[5], pcmptr[6], pcmptr[7]); - } -#endif /* DEBUG */ - - offbytes = BMP_FHBYTES + BMP_IHBYTES + cmaplen; - fsize = offbytes + fimagebytes; - fdata = (l_uint8 *)LEPT_CALLOC(fsize, 1); - *pfdata = fdata; - *pfsize = fsize; - - /* Write little-endian file header data */ - bmpfh = (BMP_FH *)fdata; - bmpfh->bfType[0] = (l_uint8)(BMP_ID >> 0); - bmpfh->bfType[1] = (l_uint8)(BMP_ID >> 8); - bmpfh->bfSize[0] = (l_uint8)(fsize >> 0); - bmpfh->bfSize[1] = (l_uint8)(fsize >> 8); - bmpfh->bfSize[2] = (l_uint8)(fsize >> 16); - bmpfh->bfSize[3] = (l_uint8)(fsize >> 24); - bmpfh->bfOffBits[0] = (l_uint8)(offbytes >> 0); - bmpfh->bfOffBits[1] = (l_uint8)(offbytes >> 8); - bmpfh->bfOffBits[2] = (l_uint8)(offbytes >> 16); - bmpfh->bfOffBits[3] = (l_uint8)(offbytes >> 24); - - /* Convert to little-endian and write the info header data */ -#if defined(__GNUC__) - bmph = (BMP_HEADER *)bmpfh; -#else - bmpih = (BMP_IH *)(fdata + BMP_FHBYTES); -#endif - bmpih->biSize = convertOnBigEnd32(BMP_IHBYTES); - bmpih->biWidth = convertOnBigEnd32(w); - bmpih->biHeight = convertOnBigEnd32(h); - bmpih->biPlanes = convertOnBigEnd16(1); - bmpih->biBitCount = convertOnBigEnd16(fdepth); - bmpih->biSizeImage = convertOnBigEnd32(fimagebytes); - bmpih->biXPelsPerMeter = convertOnBigEnd32(xres); - bmpih->biYPelsPerMeter = convertOnBigEnd32(yres); - bmpih->biClrUsed = convertOnBigEnd32(ncolors); - bmpih->biClrImportant = convertOnBigEnd32(ncolors); - - /* Copy the colormap data and free the cta if necessary */ - if (ncolors > 0) { - memcpy(fdata + BMP_FHBYTES + BMP_IHBYTES, cta, cmaplen); - if (heapcm) LEPT_FREE(cta); - } - - /* When you write a binary image with a colormap - * that sets BLACK to 0, you must invert the data */ - if (fdepth == 1 && cmap && ((l_uint8 *)(cmap->array))[0] == 0x0) { - pixInvert(pix, pix); - } - - /* An endian byte swap is also required */ - pixEndianByteSwap(pix); - - /* Transfer the image data. Image origin for bmp is at lower right. */ - fmdata = fdata + offbytes; - if (fdepth != 24) { /* typ 1 or 8 bpp */ - data = (l_uint8 *)pixGetData(pix) + pixBpl * (h - 1); - for (i = 0; i < h; i++) { - memcpy(fmdata, data, fBpl); - data -= pixBpl; - fmdata += fBpl; - } - } else { /* 32 bpp pix; 24 bpp file - * See the comments in pixReadStreamBmp() to - * understand the logic behind the pixel ordering below. - * Note that we have again done an endian swap on - * little endian machines before arriving here, so that - * the bytes are ordered on both platforms as: - Red Green Blue -- - |-----------|------------|-----------|-----------| - */ - extrabytes = fBpl - 3 * w; - line = pixGetData(pix) + pixWpl * (h - 1); - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - pword = line + j; - pel[2] = *((l_uint8 *)pword + COLOR_RED); - pel[1] = *((l_uint8 *)pword + COLOR_GREEN); - pel[0] = *((l_uint8 *)pword + COLOR_BLUE); - memcpy(fmdata, &pel, 3); - fmdata += 3; - } - if (extrabytes) { - for (k = 0; k < extrabytes; k++) { - memcpy(fmdata, &pel, 1); - fmdata++; - } - } - line -= pixWpl; - } - } - - pixDestroy(&pix); - return 0; -} - -/* --------------------------------------------*/ -#endif /* USE_BMPIO */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmpiostub.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmpiostub.c deleted file mode 100644 index a861d342..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bmpiostub.c +++ /dev/null @@ -1,72 +0,0 @@ -/*====================================================================* - - 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 bmpiostub.c - *
- * - * Stubs for bmpio.c functions - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * - * Function for generating prog/recog/digits/bootnum1.pa from an - * encoded, gzipped and serialized string. - * - * This was generated using the stringcode utility, slightly edited, - * and then merged into a single file. - * - * The code and encoded strings were made using the stringcode utility: - * - * L_STRCODE *strc; - * strc = strcodeCreate(101); // arbitrary integer - * strcodeGenerate(strc, "recog/digits/bootnum1.pa", "PIXA"); - * strcodeFinalize(&strc, "."); - * - * The two output files, autogen.101.c and autogen.101.h, were - * then slightly edited and merged into this file. - * - * Call this way: - * PIXA *pixa = l_bootnum_gen1(); (C) - * Pixa *pixa = l_bootnum_gen1(); (C++) - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Call this way: - * PIXA *pixa = l_bootnum_gen1(); (C) - * Pixa *pixa = l_bootnum_gen1(); (C++) - *- */ -PIXA * -l_bootnum_gen1(void) -{ -l_uint8 *data1, *data2; -l_int32 size1; -size_t size2; -PIXA *pixa; - - /* Unencode selected string, write to file, and read it */ - data1 = decodeBase64(l_bootnum1, strlen(l_bootnum1), &size1); - data2 = zlibUncompress(data1, size1, &size2); - pixa = pixaReadMem(data2, size2); - lept_free(data1); - lept_free(data2); - return pixa; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bootnumgen2.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bootnumgen2.c deleted file mode 100644 index ffcf6474..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bootnumgen2.c +++ /dev/null @@ -1,291 +0,0 @@ -/*====================================================================* - - 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 bootnumgen2.c - *
- * - * Function for generating prog/recog/digits/bootnum2.pa from an - * encoded, gzipped and serialized string. - * - * This was generated using the stringcode utility, slightly edited, - * and then merged into a single file. - * - * The code and encoded strings were made using the stringcode utility: - * - * L_STRCODE *strc; - * strc = strcodeCreate(102); // arbitrary integer - * strcodeGenerate(strc, "recog/digits/bootnum2.pa", "PIXA"); - * strcodeFinalize(&strc, "."); - * - * The two output files, autogen.102.c and autogen.102.h, were - * then slightly edited and merged into this file. - * - * Call this way: - * PIXA *pixa = l_bootnum_gen2(); (C) - * Pixa *pixa = l_bootnum_gen2(); (C++) - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Call this way: - * PIXA *pixa = l_bootnum_gen2(); (C) - * Pixa *pixa = l_bootnum_gen2(); (C++) - *- */ -PIXA * -l_bootnum_gen2(void) -{ -l_uint8 *data1, *data2; -l_int32 size1; -size_t size2; -PIXA *pixa; - - /* Unencode selected string, write to file, and read it */ - data1 = decodeBase64(l_bootnum2, strlen(l_bootnum2), &size1); - data2 = zlibUncompress(data1, size1, &size2); - pixa = pixaReadMem(data2, size2); - lept_free(data1); - lept_free(data2); - return pixa; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bootnumgen3.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bootnumgen3.c deleted file mode 100644 index 6d45d16a..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bootnumgen3.c +++ /dev/null @@ -1,368 +0,0 @@ -/*====================================================================* - - 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 bootnumgen3.c - *
- * - * Function for generating prog/recog/digits/bootnum3.pa from an - * encoded, gzipped and serialized string. - * - * This was generated using the stringcode utility, slightly edited, - * and then merged into a single file. - * - * The code and encoded strings were made using the stringcode utility: - * - * L_STRCODE *strc; - * strc = strcodeCreate(103); // arbitrary integer - * strcodeGenerate(strc, "recog/digits/bootnum3.pa", "PIXA"); - * strcodeFinalize(&strc, "."); - * - * The two output files, autogen.103.c and autogen.103.h, were - * then slightly edited and merged into this file. - * - * Call this way: - * PIXA *pixa = l_bootnum_gen3(); (C) - * Pixa *pixa = l_bootnum_gen3(); (C++) - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Call this way: - * PIXA *pixa = l_bootnum_gen3(); (C) - * Pixa *pixa = l_bootnum_gen3(); (C++) - *- */ -PIXA * -l_bootnum_gen3(void) -{ -l_uint8 *data1, *data2; -l_int32 size1; -size_t size2; -PIXA *pixa; - - /* Unencode selected string, uncompress it, and read it */ - data1 = decodeBase64(l_strdata_0, strlen(l_strdata_0), &size1); - data2 = zlibUncompress(data1, size1, &size2); - pixa = pixaReadMem(data2, size2); - lept_free(data1); - lept_free(data2); - return pixa; -} - diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bootnumgen4.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bootnumgen4.c deleted file mode 100644 index 731884db..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bootnumgen4.c +++ /dev/null @@ -1,823 +0,0 @@ -/*====================================================================* - - 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 bootnumgen4.c - *
- * - * Function for re-generating prog/recog/digits/bootnum4.pa from an - * encoded, gzipped and serialized string. - * - * Call this way: - * PIXA *pixa = l_bootnum_gen4(nsamp); - * where nsamp is the number of digit templates requested for each - * of the 10 digits. nsamp can be anything from 1 to 100. - - * This file was generated using the stringcode utility, in recog_bootnum3.c, - * slightly edited, and then merged into a single file. That program - * generated a pixa of 100 mosaic'd samples of each digit, - * which was copied to recog/digits/bootnum4.pa. - * - * L_STRCODE *strc; - * strc = strcodeCreate(212); // arbitrary integer - * strcodeGenerate(strc, "recog/digits/bootnum4.pa", "PIXA"); - * strcodeFinalize(&strc, "."); - * - * The two output files, autogen.212.c and autogen.212.h, were - * then slightly edited and merged into this file, and the code - * to generate the pixa of sample templates was added. - * - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) The encoded string and the code to generate pixa1 was - * automatically generated. - * (2) pixa1 is further processed to make the pixa of labelled digits. - *- */ -PIXA * -l_bootnum_gen4(l_int32 nsamp) -{ -l_uint8 *data1, *data2; -l_int32 size1; -size_t size2; -PIXA *pixa1, *pixa2; - - PROCNAME("l_bootnum_gen4"); - - if (nsamp <= 0) - return (PIXA *)ERROR_PTR("invalid nsamp\n", procName, NULL); - - /* Unencode selected string, write to file, and read it */ - data1 = decodeBase64(l_bootnum4, strlen(l_bootnum4), &size1); - data2 = zlibUncompress(data1, size1, &size2); - pixa1 = pixaReadMem(data2, size2); - lept_free(data1); - lept_free(data2); - - /* pixa1 has 10 images of mosaic'd digits. Each of these images - * must be extracted into a pixa of templates, where each template - * is labeled with the digit value, and then selectively - * concatenated into an output pixa. */ - pixa2 = pixaMakeFromTiledPixa(pixa1, 20, 30, nsamp); - pixaDestroy(&pixa1); - return pixa2; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxbasic.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxbasic.c deleted file mode 100644 index c86b81e3..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxbasic.c +++ /dev/null @@ -1,2390 +0,0 @@ -/*====================================================================* - - 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 boxbasic.c - *
- * - * Basic 'class' functions for box, boxa and boxaa, - * including accessors and serialization. - * - * Box creation, copy, clone, destruction - * BOX *boxCreate() - * BOX *boxCreateValid() - * BOX *boxCopy() - * BOX *boxClone() - * void boxDestroy() - * - * Box accessors - * l_int32 boxGetGeometry() - * l_int32 boxSetGeometry() - * l_int32 boxGetSideLocations() - * l_int32 boxSetSideLocations() - * l_int32 boxGetRefcount() - * l_int32 boxChangeRefcount() - * l_int32 boxIsValid() - * - * Boxa creation, copy, destruction - * BOXA *boxaCreate() - * BOXA *boxaCopy() - * void boxaDestroy() - * - * Boxa array extension - * l_int32 boxaAddBox() - * l_int32 boxaExtendArray() - * l_int32 boxaExtendArrayToSize() - * - * Boxa accessors - * l_int32 boxaGetCount() - * l_int32 boxaGetValidCount() - * BOX *boxaGetBox() - * BOX *boxaGetValidBox() - * NUMA *boxaFindInvalidBoxes() - * l_int32 boxaGetBoxGeometry() - * l_int32 boxaIsFull() - * - * Boxa array modifiers - * l_int32 boxaReplaceBox() - * l_int32 boxaInsertBox() - * l_int32 boxaRemoveBox() - * l_int32 boxaRemoveBoxAndSave() - * BOXA *boxaSaveValid() - * l_int32 boxaInitFull() - * l_int32 boxaClear() - * - * Boxaa creation, copy, destruction - * BOXAA *boxaaCreate() - * BOXAA *boxaaCopy() - * void boxaaDestroy() - * - * Boxaa array extension - * l_int32 boxaaAddBoxa() - * l_int32 boxaaExtendArray() - * l_int32 boxaaExtendArrayToSize() - * - * Boxaa accessors - * l_int32 boxaaGetCount() - * l_int32 boxaaGetBoxCount() - * BOXA *boxaaGetBoxa() - * BOX *boxaaGetBox() - * - * Boxaa array modifiers - * l_int32 boxaaInitFull() - * l_int32 boxaaExtendWithInit() - * l_int32 boxaaReplaceBoxa() - * l_int32 boxaaInsertBoxa() - * l_int32 boxaaRemoveBoxa() - * l_int32 boxaaAddBox() - * - * Boxaa serialized I/O - * BOXAA *boxaaReadFromFiles() - * BOXAA *boxaaRead() - * BOXAA *boxaaReadStream() - * BOXAA *boxaaReadMem() - * l_int32 boxaaWrite() - * l_int32 boxaaWriteStream() - * l_int32 boxaaWriteMem() - * - * Boxa serialized I/O - * BOXA *boxaRead() - * BOXA *boxaReadStream() - * BOXA *boxaReadMem() - * l_int32 boxaWriteDebug() - * l_int32 boxaWrite() - * l_int32 boxaWriteStream() - * l_int32 boxaWriteStderr() - * l_int32 boxaWriteMem() - * - * Box print (for debug) - * l_int32 boxPrintStreamInfo() - * - * Most functions use only valid boxes, which are boxes that have both - * width and height > 0. However, a few functions, such as - * boxaGetMedianVals() do not assume that all boxes are valid. For any - * function that can use a boxa with invalid boxes, it is convenient - * to use these accessors: - * boxaGetValidCount() : count of valid boxes - * boxaGetValidBox() : returns NULL for invalid boxes - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This clips the box to the +quad. If no part of the - * box is in the +quad, this returns NULL. - * (2) We allow you to make a box with w = 0 and/or h = 0. - * This does not represent a valid region, but it is useful - * as a placeholder in a boxa for which the index of the - * box in the boxa is important. This is an atypical - * situation; usually you want to put only valid boxes with - * nonzero width and height in a boxa. If you have a boxa - * with invalid boxes, the accessor boxaGetValidBox() - * will return NULL on each invalid box. - * (3) If you want to create only valid boxes, use boxCreateValid(), - * which returns NULL if either w or h is 0. - *- */ -BOX * -boxCreate(l_int32 x, - l_int32 y, - l_int32 w, - l_int32 h) -{ -BOX *box; - - PROCNAME("boxCreate"); - - if (w < 0 || h < 0) - return (BOX *)ERROR_PTR("w and h not both >= 0", procName, NULL); - if (x < 0) { /* take part in +quad */ - w = w + x; - x = 0; - if (w <= 0) - return (BOX *)ERROR_PTR("x < 0 and box off +quad", procName, NULL); - } - if (y < 0) { /* take part in +quad */ - h = h + y; - y = 0; - if (h <= 0) - return (BOX *)ERROR_PTR("y < 0 and box off +quad", procName, NULL); - } - - box = (BOX *)LEPT_CALLOC(1, sizeof(BOX)); - boxSetGeometry(box, x, y, w, h); - box->refcount = 1; - return box; -} - - -/*! - * \brief boxCreateValid() - * - * \param[in] x, y, w, h - * \return box, or NULL on error - * - *
- * Notes: - * (1) This returns NULL if either w = 0 or h = 0. - *- */ -BOX * -boxCreateValid(l_int32 x, - l_int32 y, - l_int32 w, - l_int32 h) -{ - PROCNAME("boxCreateValid"); - - if (w <= 0 || h <= 0) - return (BOX *)ERROR_PTR("w and h not both > 0", procName, NULL); - return boxCreate(x, y, w, h); -} - - -/*! - * \brief boxCopy() - * - * \param[in] box - * \return copy of box, or NULL on error - */ -BOX * -boxCopy(BOX *box) -{ -BOX *boxc; - - PROCNAME("boxCopy"); - - if (!box) - return (BOX *)ERROR_PTR("box not defined", procName, NULL); - - boxc = boxCreate(box->x, box->y, box->w, box->h); - return boxc; -} - - -/*! - * \brief boxClone() - * - * \param[in] box - * \return ptr to same box, or NULL on error - */ -BOX * -boxClone(BOX *box) -{ - - PROCNAME("boxClone"); - - if (!box) - return (BOX *)ERROR_PTR("box not defined", procName, NULL); - - boxChangeRefcount(box, 1); - return box; -} - - -/*! - * \brief boxDestroy() - * - * \param[in,out] pbox will be set to null before returning - * \return void - * - *
- * Notes: - * (1) Decrements the ref count and, if 0, destroys the box. - * (2) Always nulls the input ptr. - *- */ -void -boxDestroy(BOX **pbox) -{ -BOX *box; - - PROCNAME("boxDestroy"); - - if (pbox == NULL) { - L_WARNING("ptr address is null!\n", procName); - return; - } - if ((box = *pbox) == NULL) - return; - - boxChangeRefcount(box, -1); - if (boxGetRefcount(box) <= 0) - LEPT_FREE(box); - *pbox = NULL; - return; -} - - -/*---------------------------------------------------------------------* - * Box accessors * - *---------------------------------------------------------------------*/ -/*! - * \brief boxGetGeometry() - * - * \param[in] box - * \param[out] px, py, pw, ph [optional] each can be null - * \return 0 if OK, 1 on error - */ -l_ok -boxGetGeometry(BOX *box, - l_int32 *px, - l_int32 *py, - l_int32 *pw, - l_int32 *ph) -{ - PROCNAME("boxGetGeometry"); - - if (px) *px = 0; - if (py) *py = 0; - if (pw) *pw = 0; - if (ph) *ph = 0; - if (!box) - return ERROR_INT("box not defined", procName, 1); - if (px) *px = box->x; - if (py) *py = box->y; - if (pw) *pw = box->w; - if (ph) *ph = box->h; - return 0; -} - - -/*! - * \brief boxSetGeometry() - * - * \param[in] box - * \param[in] x, y, w, h [optional] use -1 to leave unchanged - * \return 0 if OK, 1 on error - */ -l_ok -boxSetGeometry(BOX *box, - l_int32 x, - l_int32 y, - l_int32 w, - l_int32 h) -{ - PROCNAME("boxSetGeometry"); - - if (!box) - return ERROR_INT("box not defined", procName, 1); - if (x != -1) box->x = x; - if (y != -1) box->y = y; - if (w != -1) box->w = w; - if (h != -1) box->h = h; - return 0; -} - - -/*! - * \brief boxGetSideLocations() - * - * \param[in] box - * \param[out] pl, pt, pr, pb [optional] each can be null - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) All returned values are within the box. - *- */ -l_ok -boxGetSideLocations(BOX *box, - l_int32 *pl, - l_int32 *pr, - l_int32 *pt, - l_int32 *pb) -{ -l_int32 x, y, w, h; - - PROCNAME("boxGetSideLocations"); - - if (pl) *pl = 0; - if (pr) *pr = 0; - if (pt) *pt = 0; - if (pb) *pb = 0; - if (!box) - return ERROR_INT("box not defined", procName, 1); - - boxGetGeometry(box, &x, &y, &w, &h); - if (pl) *pl = x; - if (pr) *pr = x + w - 1; - if (pt) *pt = y; - if (pb) *pb = y + h - 1; - return 0; -} - - -/*! - * \brief boxSetSideLocations() - * - * \param[in] box - * \param[in] l, r, t, b [optional] use -1 to leave unchanged - * \return 0 if OK, 1 on error - */ -l_ok -boxSetSideLocations(BOX *box, - l_int32 l, - l_int32 r, - l_int32 t, - l_int32 b) -{ -l_int32 x, y, w, h; - - PROCNAME("boxSetSideLocations"); - - if (!box) - return ERROR_INT("box not defined", procName, 1); - x = (l != -1) ? l : box->x; - w = (r != -1) ? r - x + 1 : box->x + box->w - x; - y = (t != -1) ? t : box->y; - h = (b != -1) ? b - y + 1 : box->y + box->h - y; - boxSetGeometry(box, x, y, w, h); - return 0; -} - - -/*! - * \brief Return the current reference count of %box - * - * \param[in] box - * \return refcount - */ -l_int32 -boxGetRefcount(BOX *box) -{ - PROCNAME("boxGetRefcount"); - - if (!box) - return ERROR_INT("box not defined", procName, UNDEF); - - return box->refcount; -} - -/*! - * \brief Adjust the current references count of %box by %delta - * - * \param[in] box ptr to box - * \param[in] delta adjustment, usually -1 or 1 - * \return 0 if OK, 1 on error - */ -l_ok -boxChangeRefcount(BOX *box, - l_int32 delta) -{ - PROCNAME("boxChangeRefcount"); - - if (!box) - return ERROR_INT("box not defined", procName, 1); - - box->refcount += delta; - return 0; -} - - -/*! - * \brief boxIsValid() - * - * \param[in] box - * \param[out] pvalid 1 if valid; 0 otherwise - * \return 0 if OK, 1 on error - */ -l_ok -boxIsValid(BOX *box, - l_int32 *pvalid) -{ - PROCNAME("boxIsValid"); - - if (!pvalid) - return ERROR_INT("&valid not defined", procName, 1); - *pvalid = 0; - if (!box) - return ERROR_INT("box not defined", procName, 1); - - if (box->w > 0 && box->h > 0) - *pvalid = 1; - return 0; -} - - -/*---------------------------------------------------------------------* - * Boxa creation, destruction, copy, extension * - *---------------------------------------------------------------------*/ -/*! - * \brief boxaCreate() - * - * \param[in] n initial number of ptrs; 0 for default - * \return boxa, or NULL on error - */ -BOXA * -boxaCreate(l_int32 n) -{ -BOXA *boxa; - - PROCNAME("boxaCreate"); - - if (n <= 0 || n > MaxPtrArraySize) - n = InitialPtrArraySize; - - boxa = (BOXA *)LEPT_CALLOC(1, sizeof(BOXA)); - boxa->n = 0; - boxa->nalloc = n; - boxa->refcount = 1; - if ((boxa->box = (BOX **)LEPT_CALLOC(n, sizeof(BOX *))) == NULL) { - boxaDestroy(&boxa); - return (BOXA *)ERROR_PTR("boxa ptrs not made", procName, NULL); - } - return boxa; -} - - -/*! - * \brief boxaCopy() - * - * \param[in] boxa - * \param[in] copyflag L_COPY, L_CLONE, L_COPY_CLONE - * \return new boxa, or NULL on error - * - *
- * Notes: - * (1) See pix.h for description of the copyflag. - * (2) The copy-clone makes a new boxa that holds clones of each box. - *- */ -BOXA * -boxaCopy(BOXA *boxa, - l_int32 copyflag) -{ -l_int32 i; -BOX *boxc; -BOXA *boxac; - - PROCNAME("boxaCopy"); - - if (!boxa) - return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL); - - if (copyflag == L_CLONE) { - boxa->refcount++; - return boxa; - } - - if (copyflag != L_COPY && copyflag != L_COPY_CLONE) - return (BOXA *)ERROR_PTR("invalid copyflag", procName, NULL); - - if ((boxac = boxaCreate(boxa->nalloc)) == NULL) - return (BOXA *)ERROR_PTR("boxac not made", procName, NULL); - for (i = 0; i < boxa->n; i++) { - if (copyflag == L_COPY) - boxc = boxaGetBox(boxa, i, L_COPY); - else /* copy-clone */ - boxc = boxaGetBox(boxa, i, L_CLONE); - boxaAddBox(boxac, boxc, L_INSERT); - } - return boxac; -} - - -/*! - * \brief boxaDestroy() - * - * \param[in,out] pboxa will be set to null before returning - * \return void - * - *
- * Notes: - * (1) Decrements the ref count and, if 0, destroys the boxa. - * (2) Always nulls the input ptr. - *- */ -void -boxaDestroy(BOXA **pboxa) -{ -l_int32 i; -BOXA *boxa; - - PROCNAME("boxaDestroy"); - - if (pboxa == NULL) { - L_WARNING("ptr address is null!\n", procName); - return; - } - - if ((boxa = *pboxa) == NULL) - return; - - /* Decrement the ref count. If it is 0, destroy the boxa. */ - boxa->refcount--; - if (boxa->refcount <= 0) { - for (i = 0; i < boxa->n; i++) - boxDestroy(&boxa->box[i]); - LEPT_FREE(boxa->box); - LEPT_FREE(boxa); - } - - *pboxa = NULL; - return; -} - - -/*! - * \brief boxaAddBox() - * - * \param[in] boxa - * \param[in] box to be added - * \param[in] copyflag L_INSERT, L_COPY, L_CLONE - * \return 0 if OK, 1 on error - */ -l_ok -boxaAddBox(BOXA *boxa, - BOX *box, - l_int32 copyflag) -{ -l_int32 n; -BOX *boxc; - - PROCNAME("boxaAddBox"); - - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - - if (copyflag == L_INSERT) - boxc = box; - else if (copyflag == L_COPY) - boxc = boxCopy(box); - else if (copyflag == L_CLONE) - boxc = boxClone(box); - else - return ERROR_INT("invalid copyflag", procName, 1); - if (!boxc) - return ERROR_INT("boxc not made", procName, 1); - - n = boxaGetCount(boxa); - if (n >= boxa->nalloc) - boxaExtendArray(boxa); - boxa->box[n] = boxc; - boxa->n++; - - return 0; -} - - -/*! - * \brief boxaExtendArray() - * - * \param[in] boxa - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) Reallocs with doubled size of ptr array. - *- */ -l_ok -boxaExtendArray(BOXA *boxa) -{ - PROCNAME("boxaExtendArray"); - - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - return boxaExtendArrayToSize(boxa, 2 * boxa->nalloc); -} - - -/*! - * \brief boxaExtendArrayToSize() - * - * \param[in] boxa - * \param[in] size new size of boxa array - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) If necessary, reallocs new boxa ptr array to %size. - *- */ -l_ok -boxaExtendArrayToSize(BOXA *boxa, - l_int32 size) -{ - PROCNAME("boxaExtendArrayToSize"); - - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - if (size > boxa->nalloc) { - if ((boxa->box = (BOX **)reallocNew((void **)&boxa->box, - sizeof(BOX *) * boxa->nalloc, - size * sizeof(BOX *))) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - boxa->nalloc = size; - } - return 0; -} - - -/*---------------------------------------------------------------------* - * Boxa accessors * - *---------------------------------------------------------------------*/ -/*! - * \brief boxaGetCount() - * - * \param[in] boxa - * \return count of all boxes; 0 if no boxes or on error - */ -l_int32 -boxaGetCount(BOXA *boxa) -{ - PROCNAME("boxaGetCount"); - - if (!boxa) - return ERROR_INT("boxa not defined", procName, 0); - return boxa->n; -} - - -/*! - * \brief boxaGetValidCount() - * - * \param[in] boxa - * \return count of valid boxes; 0 if no valid boxes or on error - */ -l_int32 -boxaGetValidCount(BOXA *boxa) -{ -l_int32 n, i, w, h, count; - - PROCNAME("boxaGetValidCount"); - - if (!boxa) - return ERROR_INT("boxa not defined", procName, 0); - - n = boxaGetCount(boxa); - for (i = 0, count = 0; i < n; i++) { - boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); - if (w > 0 && h > 0) - count++; - } - return count; -} - - -/*! - * \brief boxaGetBox() - * - * \param[in] boxa - * \param[in] index to the index-th box - * \param[in] accessflag L_COPY or L_CLONE - * \return box, or NULL on error - */ -BOX * -boxaGetBox(BOXA *boxa, - l_int32 index, - l_int32 accessflag) -{ - PROCNAME("boxaGetBox"); - - if (!boxa) - return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); - if (index < 0 || index >= boxa->n) - return (BOX *)ERROR_PTR("index not valid", procName, NULL); - - if (accessflag == L_COPY) - return boxCopy(boxa->box[index]); - else if (accessflag == L_CLONE) - return boxClone(boxa->box[index]); - else - return (BOX *)ERROR_PTR("invalid accessflag", procName, NULL); -} - - -/*! - * \brief boxaGetValidBox() - * - * \param[in] boxa - * \param[in] index to the index-th box - * \param[in] accessflag L_COPY or L_CLONE - * \return box, or NULL if box is not valid or on error - * - *
- * Notes: - * (1) This returns NULL for an invalid box in a boxa. - * For a box to be valid, both the width and height must be > 0. - * (2) We allow invalid boxes, with w = 0 or h = 0, as placeholders - * in boxa for which the index of the box in the boxa is important. - * This is an atypical situation; usually you want to put only - * valid boxes in a boxa. - *- */ -BOX * -boxaGetValidBox(BOXA *boxa, - l_int32 index, - l_int32 accessflag) -{ -l_int32 w, h; -BOX *box; - - PROCNAME("boxaGetValidBox"); - - if (!boxa) - return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); - - if ((box = boxaGetBox(boxa, index, accessflag)) == NULL) - return (BOX *)ERROR_PTR("box not returned", procName, NULL); - boxGetGeometry(box, NULL, NULL, &w, &h); - if (w <= 0 || h <= 0) /* not valid, but not necessarily an error */ - boxDestroy(&box); - return box; -} - - -/*! - * \brief boxaFindInvalidBoxes() - * - * \param[in] boxa - * \return na numa of invalid boxes; NULL if there are none or on error - */ -NUMA * -boxaFindInvalidBoxes(BOXA *boxa) -{ -l_int32 i, n, w, h; -NUMA *na; - - PROCNAME("boxaFindInvalidBoxes"); - - if (!boxa) - return (NUMA *)ERROR_PTR("boxa not defined", procName, NULL); - - n = boxaGetCount(boxa); - if (boxaGetValidCount(boxa) == n) - return NULL; - - na = numaMakeConstant(0, n); - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); - if (w == 0 || h == 0) - numaSetValue(na, i, 1); - } - return na; -} - - -/*! - * \brief boxaGetBoxGeometry() - * - * \param[in] boxa - * \param[in] index to the index-th box - * \param[out] px, py, pw, ph [optional] each can be null - * \return 0 if OK, 1 on error - */ -l_ok -boxaGetBoxGeometry(BOXA *boxa, - l_int32 index, - l_int32 *px, - l_int32 *py, - l_int32 *pw, - l_int32 *ph) -{ -BOX *box; - - PROCNAME("boxaGetBoxGeometry"); - - if (px) *px = 0; - if (py) *py = 0; - if (pw) *pw = 0; - if (ph) *ph = 0; - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (index < 0 || index >= boxa->n) - return ERROR_INT("index not valid", procName, 1); - - if ((box = boxaGetBox(boxa, index, L_CLONE)) == NULL) - return ERROR_INT("box not found!", procName, 1); - boxGetGeometry(box, px, py, pw, ph); - boxDestroy(&box); - return 0; -} - - -/*! - * \brief boxaIsFull() - * - * \param[in] boxa - * \param[out] pfull 1 if boxa is full; 0 otherwise - * \return 0 if OK, 1 on error - */ -l_ok -boxaIsFull(BOXA *boxa, - l_int32 *pfull) -{ -l_int32 i, n, full; -BOX *box; - - PROCNAME("boxaIsFull"); - - if (!pfull) - return ERROR_INT("&full not defined", procName, 1); - *pfull = 0; - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - n = boxaGetCount(boxa); - full = 1; - for (i = 0; i < n; i++) { - if ((box = boxaGetBox(boxa, i, L_CLONE)) == NULL) { - full = 0; - break; - } - boxDestroy(&box); - } - *pfull = full; - return 0; -} - - -/*---------------------------------------------------------------------* - * Boxa array modifiers * - *---------------------------------------------------------------------*/ -/*! - * \brief boxaReplaceBox() - * - * \param[in] boxa - * \param[in] index to the index-th box - * \param[in] box insert this box to replace existing one - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) In-place replacement of one box; the input %box is now - * owned by the boxa. - * (2) The previous box at that location, if any, is destroyed. - *- */ -l_ok -boxaReplaceBox(BOXA *boxa, - l_int32 index, - BOX *box) -{ - PROCNAME("boxaReplaceBox"); - - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (index < 0 || index >= boxa->n) - return ERROR_INT("index not valid", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - - boxDestroy(&(boxa->box[index])); - boxa->box[index] = box; - return 0; -} - - -/*! - * \brief boxaInsertBox() - * - * \param[in] boxa - * \param[in] index location in boxa to insert new value - * \param[in] box new box to be inserted; the boxa now owns it - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This shifts box[i] --> box[i + 1] for all i >= index, - * and then inserts box as box[index]. - * (2) To insert at the beginning of the array, set index = 0. - * (3) To append to the array, it's easier to use boxaAddBox(). - * (4) This should not be used repeatedly to insert into large arrays, - * because the function is O(n). - *- */ -l_ok -boxaInsertBox(BOXA *boxa, - l_int32 index, - BOX *box) -{ -l_int32 i, n; -BOX **array; - - PROCNAME("boxaInsertBox"); - - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - n = boxaGetCount(boxa); - if (index < 0 || index > n) - return ERROR_INT("index not in {0...n}", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - - if (n >= boxa->nalloc) - boxaExtendArray(boxa); - array = boxa->box; - boxa->n++; - for (i = n; i > index; i--) - array[i] = array[i - 1]; - array[index] = box; - - return 0; -} - - -/*! - * \brief boxaRemoveBox() - * - * \param[in] boxa - * \param[in] index of box to be removed and destroyed - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This removes box[index] and then shifts - * box[i] --> box[i - 1] for all i > index. - * (2) It should not be used repeatedly to remove boxes from - * large arrays, because the function is O(n). - *- */ -l_ok -boxaRemoveBox(BOXA *boxa, - l_int32 index) -{ - return boxaRemoveBoxAndSave(boxa, index, NULL); -} - - -/*! - * \brief boxaRemoveBoxAndSave() - * - * \param[in] boxa - * \param[in] index of box to be removed - * \param[out] pbox [optional] removed box - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This removes box[index] and then shifts - * box[i] --> box[i - 1] for all i > index. - * (2) It should not be used repeatedly to remove boxes from - * large arrays, because the function is O(n). - *- */ -l_ok -boxaRemoveBoxAndSave(BOXA *boxa, - l_int32 index, - BOX **pbox) -{ -l_int32 i, n; -BOX **array; - - PROCNAME("boxaRemoveBoxAndSave"); - - if (pbox) *pbox = NULL; - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - n = boxaGetCount(boxa); - if (index < 0 || index >= n) - return ERROR_INT("index not in {0...n - 1}", procName, 1); - - if (pbox) - *pbox = boxaGetBox(boxa, index, L_CLONE); - array = boxa->box; - boxDestroy(&array[index]); - for (i = index + 1; i < n; i++) - array[i - 1] = array[i]; - array[n - 1] = NULL; - boxa->n--; - - return 0; -} - - -/*! - * \brief boxaSaveValid() - * - * \param[in] boxas - * \param[in] copyflag L_COPY or L_CLONE - * \return boxad if OK, NULL on error - * - *
- * Notes: - * (1) This makes a copy/clone of each valid box. - *- */ -BOXA * -boxaSaveValid(BOXA *boxas, - l_int32 copyflag) -{ -l_int32 i, n; -BOX *box; -BOXA *boxad; - - PROCNAME("boxaSaveValid"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (copyflag != L_COPY && copyflag != L_CLONE) - return (BOXA *)ERROR_PTR("invalid copyflag", procName, NULL); - - n = boxaGetCount(boxas); - boxad = boxaCreate(n); - for (i = 0; i < n; i++) { - if ((box = boxaGetValidBox(boxas, i, copyflag)) != NULL) - boxaAddBox(boxad, box, L_INSERT); - } - - return boxad; -} - - -/*! - * \brief boxaInitFull() - * - * \param[in] boxa typically empty - * \param[in] box [optional] to be replicated into the entire ptr array - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This initializes a boxa by filling up the entire box ptr array - * with copies of %box. If %box == NULL, use a placeholder box - * of zero size. Any existing boxes are destroyed. - * After this opepration, the number of boxes is equal to - * the number of allocated ptrs. - * (2) Note that we use boxaReplaceBox() instead of boxaInsertBox(). - * They both have the same effect when inserting into a NULL ptr - * in the boxa ptr array: - * (3) Example usage. This function is useful to prepare for a - * random insertion (or replacement) of boxes into a boxa. - * To randomly insert boxes into a boxa, up to some index "max": - * Boxa *boxa = boxaCreate(max); - * boxaInitFull(boxa, NULL); - * If you want placeholder boxes of non-zero size: - * Boxa *boxa = boxaCreate(max); - * Box *box = boxCreate(...); - * boxaInitFull(boxa, box); - * boxDestroy(&box); - * If we have an existing boxa with a smaller ptr array, it can - * be reused for up to max boxes: - * boxaExtendArrayToSize(boxa, max); - * boxaInitFull(boxa, NULL); - * The initialization allows the boxa to always be properly - * filled, even if all the boxes are not later replaced. - * If you want to know which boxes have been replaced, - * and you initialized with invalid zero-sized boxes, - * use boxaGetValidBox() to return NULL for the invalid boxes. - *- */ -l_ok -boxaInitFull(BOXA *boxa, - BOX *box) -{ -l_int32 i, n; -BOX *boxt; - - PROCNAME("boxaInitFull"); - - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - n = boxa->nalloc; - boxa->n = n; - for (i = 0; i < n; i++) { - if (box) - boxt = boxCopy(box); - else - boxt = boxCreate(0, 0, 0, 0); - boxaReplaceBox(boxa, i, boxt); - } - return 0; -} - - -/*! - * \brief boxaClear() - * - * \param[in] boxa - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This destroys all boxes in the boxa, setting the ptrs - * to null. The number of allocated boxes, n, is set to 0. - *- */ -l_ok -boxaClear(BOXA *boxa) -{ -l_int32 i, n; - - PROCNAME("boxaClear"); - - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - n = boxaGetCount(boxa); - for (i = 0; i < n; i++) - boxDestroy(&boxa->box[i]); - boxa->n = 0; - return 0; -} - - -/*--------------------------------------------------------------------------* - * Boxaa creation, destruction * - *--------------------------------------------------------------------------*/ -/*! - * \brief boxaaCreate() - * - * \param[in] n size of boxa ptr array to be alloc'd; 0 for default - * \return baa, or NULL on error - */ -BOXAA * -boxaaCreate(l_int32 n) -{ -BOXAA *baa; - - PROCNAME("boxaaCreate"); - - if (n <= 0 || n > MaxPtrArraySize) - n = InitialPtrArraySize; - - baa = (BOXAA *)LEPT_CALLOC(1, sizeof(BOXAA)); - if ((baa->boxa = (BOXA **)LEPT_CALLOC(n, sizeof(BOXA *))) == NULL) { - boxaaDestroy(&baa); - return (BOXAA *)ERROR_PTR("boxa ptr array not made", procName, NULL); - } - baa->nalloc = n; - baa->n = 0; - return baa; -} - - -/*! - * \brief boxaaCopy() - * - * \param[in] baas input boxaa to be copied - * \param[in] copyflag L_COPY, L_CLONE - * \return baad new boxaa, composed of copies or clones of the boxa - * in baas, or NULL on error - * - *
- * Notes: - * (1) L_COPY makes a copy of each boxa in baas. - * L_CLONE makes a clone of each boxa in baas. - *- */ -BOXAA * -boxaaCopy(BOXAA *baas, - l_int32 copyflag) -{ -l_int32 i, n; -BOXA *boxa; -BOXAA *baad; - - PROCNAME("boxaaCopy"); - - if (!baas) - return (BOXAA *)ERROR_PTR("baas not defined", procName, NULL); - if (copyflag != L_COPY && copyflag != L_CLONE) - return (BOXAA *)ERROR_PTR("invalid copyflag", procName, NULL); - - n = boxaaGetCount(baas); - baad = boxaaCreate(n); - for (i = 0; i < n; i++) { - boxa = boxaaGetBoxa(baas, i, copyflag); - boxaaAddBoxa(baad, boxa, L_INSERT); - } - - return baad; -} - - -/*! - * \brief boxaaDestroy() - * - * \param[in,out] pbaa will be set to null before returning - */ -void -boxaaDestroy(BOXAA **pbaa) -{ -l_int32 i; -BOXAA *baa; - - PROCNAME("boxaaDestroy"); - - if (pbaa == NULL) { - L_WARNING("ptr address is NULL!\n", procName); - return; - } - - if ((baa = *pbaa) == NULL) - return; - - for (i = 0; i < baa->n; i++) - boxaDestroy(&baa->boxa[i]); - LEPT_FREE(baa->boxa); - LEPT_FREE(baa); - *pbaa = NULL; - - return; -} - - - -/*--------------------------------------------------------------------------* - * Add Boxa to Boxaa * - *--------------------------------------------------------------------------*/ -/*! - * \brief boxaaAddBoxa() - * - * \param[in] baa - * \param[in] ba to be added - * \param[in] copyflag L_INSERT, L_COPY, L_CLONE - * \return 0 if OK, 1 on error - */ -l_ok -boxaaAddBoxa(BOXAA *baa, - BOXA *ba, - l_int32 copyflag) -{ -l_int32 n; -BOXA *bac; - - PROCNAME("boxaaAddBoxa"); - - if (!baa) - return ERROR_INT("baa not defined", procName, 1); - if (!ba) - return ERROR_INT("ba not defined", procName, 1); - if (copyflag != L_INSERT && copyflag != L_COPY && copyflag != L_CLONE) - return ERROR_INT("invalid copyflag", procName, 1); - - if (copyflag == L_INSERT) - bac = ba; - else - bac = boxaCopy(ba, copyflag); - - n = boxaaGetCount(baa); - if (n >= baa->nalloc) - boxaaExtendArray(baa); - baa->boxa[n] = bac; - baa->n++; - return 0; -} - - -/*! - * \brief boxaaExtendArray() - * - * \param[in] baa - * \return 0 if OK, 1 on error - */ -l_ok -boxaaExtendArray(BOXAA *baa) -{ - - PROCNAME("boxaaExtendArray"); - - if (!baa) - return ERROR_INT("baa not defined", procName, 1); - - if ((baa->boxa = (BOXA **)reallocNew((void **)&baa->boxa, - sizeof(BOXA *) * baa->nalloc, - 2 * sizeof(BOXA *) * baa->nalloc)) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - - baa->nalloc *= 2; - return 0; -} - - -/*! - * \brief boxaaExtendArrayToSize() - * - * \param[in] baa - * \param[in] size new size of boxa array - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) If necessary, reallocs the boxa ptr array to %size. - *- */ -l_ok -boxaaExtendArrayToSize(BOXAA *baa, - l_int32 size) -{ - PROCNAME("boxaaExtendArrayToSize"); - - if (!baa) - return ERROR_INT("baa not defined", procName, 1); - - if (size > baa->nalloc) { - if ((baa->boxa = (BOXA **)reallocNew((void **)&baa->boxa, - sizeof(BOXA *) * baa->nalloc, - size * sizeof(BOXA *))) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - baa->nalloc = size; - } - return 0; -} - - -/*----------------------------------------------------------------------* - * Boxaa accessors * - *----------------------------------------------------------------------*/ -/*! - * \brief boxaaGetCount() - * - * \param[in] baa - * \return count number of boxa, or 0 if no boxa or on error - */ -l_int32 -boxaaGetCount(BOXAA *baa) -{ - PROCNAME("boxaaGetCount"); - - if (!baa) - return ERROR_INT("baa not defined", procName, 0); - return baa->n; -} - - -/*! - * \brief boxaaGetBoxCount() - * - * \param[in] baa - * \return count number of boxes, or 0 if no boxes or on error - */ -l_int32 -boxaaGetBoxCount(BOXAA *baa) -{ -BOXA *boxa; -l_int32 n, sum, i; - - PROCNAME("boxaaGetBoxCount"); - - if (!baa) - return ERROR_INT("baa not defined", procName, 0); - - n = boxaaGetCount(baa); - for (sum = 0, i = 0; i < n; i++) { - boxa = boxaaGetBoxa(baa, i, L_CLONE); - sum += boxaGetCount(boxa); - boxaDestroy(&boxa); - } - - return sum; -} - - -/*! - * \brief boxaaGetBoxa() - * - * \param[in] baa - * \param[in] index to the index-th boxa - * \param[in] accessflag L_COPY or L_CLONE - * \return boxa, or NULL on error - */ -BOXA * -boxaaGetBoxa(BOXAA *baa, - l_int32 index, - l_int32 accessflag) -{ -l_int32 n; - - PROCNAME("boxaaGetBoxa"); - - if (!baa) - return (BOXA *)ERROR_PTR("baa not defined", procName, NULL); - n = boxaaGetCount(baa); - if (index < 0 || index >= n) - return (BOXA *)ERROR_PTR("index not valid", procName, NULL); - if (accessflag != L_COPY && accessflag != L_CLONE) - return (BOXA *)ERROR_PTR("invalid accessflag", procName, NULL); - - return boxaCopy(baa->boxa[index], accessflag); -} - - -/*! - * \brief boxaaGetBox() - * - * \param[in] baa - * \param[in] iboxa index into the boxa array in the boxaa - * \param[in] ibox index into the box array in the boxa - * \param[in] accessflag L_COPY or L_CLONE - * \return box, or NULL on error - */ -BOX * -boxaaGetBox(BOXAA *baa, - l_int32 iboxa, - l_int32 ibox, - l_int32 accessflag) -{ -BOX *box; -BOXA *boxa; - - PROCNAME("boxaaGetBox"); - - if ((boxa = boxaaGetBoxa(baa, iboxa, L_CLONE)) == NULL) - return (BOX *)ERROR_PTR("boxa not retrieved", procName, NULL); - if ((box = boxaGetBox(boxa, ibox, accessflag)) == NULL) - L_ERROR("box not retrieved\n", procName); - boxaDestroy(&boxa); - return box; -} - - -/*----------------------------------------------------------------------* - * Boxaa array modifiers * - *----------------------------------------------------------------------*/ -/*! - * \brief boxaaInitFull() - * - * \param[in] baa typically empty - * \param[in] boxa to be replicated into the entire ptr array - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This initializes a boxaa by filling up the entire boxa ptr array - * with copies of %boxa. Any existing boxa are destroyed. - * After this operation, the number of boxa is equal to - * the number of allocated ptrs. - * (2) Note that we use boxaaReplaceBox() instead of boxaInsertBox(). - * They both have the same effect when inserting into a NULL ptr - * in the boxa ptr array - * (3) Example usage. This function is useful to prepare for a - * random insertion (or replacement) of boxa into a boxaa. - * To randomly insert boxa into a boxaa, up to some index "max": - * Boxaa *baa = boxaaCreate(max); - * // initialize the boxa - * Boxa *boxa = boxaCreate(...); - * ... [optionally fix with boxes] - * boxaaInitFull(baa, boxa); - * A typical use is to initialize the array with empty boxa, - * and to replace only a subset that must be aligned with - * something else, such as a pixa. - *- */ -l_ok -boxaaInitFull(BOXAA *baa, - BOXA *boxa) -{ -l_int32 i, n; -BOXA *boxat; - - PROCNAME("boxaaInitFull"); - - if (!baa) - return ERROR_INT("baa not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - n = baa->nalloc; - baa->n = n; - for (i = 0; i < n; i++) { - boxat = boxaCopy(boxa, L_COPY); - boxaaReplaceBoxa(baa, i, boxat); - } - return 0; -} - - -/*! - * \brief boxaaExtendWithInit() - * - * \param[in] baa - * \param[in] maxindex - * \param[in] boxa to be replicated into the extended ptr array - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This should be used on an existing boxaa that has been - * fully loaded with boxa. It then extends the boxaa, - * loading all the additional ptrs with copies of boxa. - * Typically, boxa will be empty. - *- */ -l_ok -boxaaExtendWithInit(BOXAA *baa, - l_int32 maxindex, - BOXA *boxa) -{ -l_int32 i, n; - - PROCNAME("boxaaExtendWithInit"); - - if (!baa) - return ERROR_INT("baa not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - /* Extend the ptr array if necessary */ - n = boxaaGetCount(baa); - if (maxindex < n) return 0; - boxaaExtendArrayToSize(baa, maxindex + 1); - - /* Fill the new entries with copies of boxa */ - for (i = n; i <= maxindex; i++) - boxaaAddBoxa(baa, boxa, L_COPY); - return 0; -} - - -/*! - * \brief boxaaReplaceBoxa() - * - * \param[in] baa - * \param[in] index to the index-th boxa - * \param[in] boxa insert and replace any existing one - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Any existing boxa is destroyed, and the input one - * is inserted in its place. - * (2) If the index is invalid, return 1 (error) - *- */ -l_ok -boxaaReplaceBoxa(BOXAA *baa, - l_int32 index, - BOXA *boxa) -{ -l_int32 n; - - PROCNAME("boxaaReplaceBoxa"); - - if (!baa) - return ERROR_INT("baa not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - n = boxaaGetCount(baa); - if (index < 0 || index >= n) - return ERROR_INT("index not valid", procName, 1); - - boxaDestroy(&baa->boxa[index]); - baa->boxa[index] = boxa; - return 0; -} - - -/*! - * \brief boxaaInsertBoxa() - * - * \param[in] baa - * \param[in] index location in boxaa to insert new boxa - * \param[in] boxa new boxa to be inserted - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This shifts boxa[i] --> boxa[i + 1] for all i >= index, - * and then inserts boxa as boxa[index]. - * (2) To insert at the beginning of the array, set index = 0. - * (3) To append to the array, it's easier to use boxaaAddBoxa(). - * (4) This should not be used repeatedly to insert into large arrays, - * because the function is O(n). - *- */ -l_ok -boxaaInsertBoxa(BOXAA *baa, - l_int32 index, - BOXA *boxa) -{ -l_int32 i, n; -BOXA **array; - - PROCNAME("boxaaInsertBoxa"); - - if (!baa) - return ERROR_INT("baa not defined", procName, 1); - n = boxaaGetCount(baa); - if (index < 0 || index > n) - return ERROR_INT("index not in {0...n}", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - if (n >= baa->nalloc) - boxaaExtendArray(baa); - array = baa->boxa; - baa->n++; - for (i = n; i > index; i--) - array[i] = array[i - 1]; - array[index] = boxa; - - return 0; -} - - -/*! - * \brief boxaaRemoveBoxa() - * - * \param[in] baa - * \param[in] index of the boxa to be removed and destroyed - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This removes boxa[index] and then shifts - * boxa[i] --> boxa[i - 1] for all i > index. - * (2) The removed boxaa is destroyed. - * (2) This should not be used repeatedly on large arrays, - * because the function is O(n). - *- */ -l_ok -boxaaRemoveBoxa(BOXAA *baa, - l_int32 index) -{ -l_int32 i, n; -BOXA **array; - - PROCNAME("boxaaRemoveBox"); - - if (!baa) - return ERROR_INT("baa not defined", procName, 1); - n = boxaaGetCount(baa); - if (index < 0 || index >= n) - return ERROR_INT("index not valid", procName, 1); - - array = baa->boxa; - boxaDestroy(&array[index]); - for (i = index + 1; i < n; i++) - array[i - 1] = array[i]; - array[n - 1] = NULL; - baa->n--; - - return 0; -} - - -/*! - * \brief boxaaAddBox() - * - * \param[in] baa - * \param[in] index of boxa with boxaa - * \param[in] box to be added - * \param[in] accessflag L_INSERT, L_COPY or L_CLONE - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Adds to an existing boxa only. - *- */ -l_ok -boxaaAddBox(BOXAA *baa, - l_int32 index, - BOX *box, - l_int32 accessflag) -{ -l_int32 n; -BOXA *boxa; - PROCNAME("boxaaAddBox"); - - if (!baa) - return ERROR_INT("baa not defined", procName, 1); - n = boxaaGetCount(baa); - if (index < 0 || index >= n) - return ERROR_INT("index not valid", procName, 1); - if (accessflag != L_INSERT && accessflag != L_COPY && accessflag != L_CLONE) - return ERROR_INT("invalid accessflag", procName, 1); - - boxa = boxaaGetBoxa(baa, index, L_CLONE); - boxaAddBox(boxa, box, accessflag); - boxaDestroy(&boxa); - return 0; -} - - -/*---------------------------------------------------------------------* - * Boxaa serialized I/O * - *---------------------------------------------------------------------*/ -/*! - * \brief boxaaReadFromFiles() - * - * \param[in] dirname directory - * \param[in] substr [optional] substring filter on filenames; can be NULL - * \param[in] first 0-based - * \param[in] nfiles use 0 for everything from %first to the end - * \return baa, or NULL on error or if no boxa files are found. - * - *
- * Notes: - * (1) The files must be serialized boxa files (e.g., *.ba). - * If some files cannot be read, warnings are issued. - * (2) Use %substr to filter filenames in the directory. If - * %substr == NULL, this takes all files. - * (3) After filtering, use %first and %nfiles to select - * a contiguous set of files, that have been lexically - * sorted in increasing order. - *- */ -BOXAA * -boxaaReadFromFiles(const char *dirname, - const char *substr, - l_int32 first, - l_int32 nfiles) -{ -char *fname; -l_int32 i, n; -BOXA *boxa; -BOXAA *baa; -SARRAY *sa; - - PROCNAME("boxaaReadFromFiles"); - - if (!dirname) - return (BOXAA *)ERROR_PTR("dirname not defined", procName, NULL); - - sa = getSortedPathnamesInDirectory(dirname, substr, first, nfiles); - if (!sa || ((n = sarrayGetCount(sa)) == 0)) { - sarrayDestroy(&sa); - return (BOXAA *)ERROR_PTR("no pixa files found", procName, NULL); - } - - baa = boxaaCreate(n); - for (i = 0; i < n; i++) { - fname = sarrayGetString(sa, i, L_NOCOPY); - if ((boxa = boxaRead(fname)) == NULL) { - L_ERROR("boxa not read for %d-th file", procName, i); - continue; - } - boxaaAddBoxa(baa, boxa, L_INSERT); - } - - sarrayDestroy(&sa); - return baa; -} - - -/*! - * \brief boxaaRead() - * - * \param[in] filename - * \return boxaa, or NULL on error - */ -BOXAA * -boxaaRead(const char *filename) -{ -FILE *fp; -BOXAA *baa; - - PROCNAME("boxaaRead"); - - if (!filename) - return (BOXAA *)ERROR_PTR("filename not defined", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (BOXAA *)ERROR_PTR("stream not opened", procName, NULL); - baa = boxaaReadStream(fp); - fclose(fp); - if (!baa) - return (BOXAA *)ERROR_PTR("boxaa not read", procName, NULL); - return baa; -} - - -/*! - * \brief boxaaReadStream() - * - * \param[in] fp input file stream - * \return boxaa, or NULL on error - */ -BOXAA * -boxaaReadStream(FILE *fp) -{ -l_int32 n, i, x, y, w, h, version; -l_int32 ignore; -BOXA *boxa; -BOXAA *baa; - - PROCNAME("boxaaReadStream"); - - if (!fp) - return (BOXAA *)ERROR_PTR("stream not defined", procName, NULL); - - if (fscanf(fp, "\nBoxaa Version %d\n", &version) != 1) - return (BOXAA *)ERROR_PTR("not a boxaa file", procName, NULL); - if (version != BOXAA_VERSION_NUMBER) - return (BOXAA *)ERROR_PTR("invalid boxa version", procName, NULL); - if (fscanf(fp, "Number of boxa = %d\n", &n) != 1) - return (BOXAA *)ERROR_PTR("not a boxaa file", procName, NULL); - - if ((baa = boxaaCreate(n)) == NULL) - return (BOXAA *)ERROR_PTR("boxaa not made", procName, NULL); - for (i = 0; i < n; i++) { - if (fscanf(fp, "\nBoxa[%d] extent: x = %d, y = %d, w = %d, h = %d", - &ignore, &x, &y, &w, &h) != 5) { - boxaaDestroy(&baa); - return (BOXAA *)ERROR_PTR("boxa descr not valid", procName, NULL); - } - if ((boxa = boxaReadStream(fp)) == NULL) { - boxaaDestroy(&baa); - return (BOXAA *)ERROR_PTR("boxa not made", procName, NULL); - } - boxaaAddBoxa(baa, boxa, L_INSERT); - } - return baa; -} - - -/*! - * \brief boxaaReadMem() - * - * \param[in] data serialization of boxaa; in ascii - * \param[in] size of data in bytes; can use strlen to get it - * \return baa, or NULL on error - */ -BOXAA * -boxaaReadMem(const l_uint8 *data, - size_t size) -{ -FILE *fp; -BOXAA *baa; - - PROCNAME("boxaaReadMem"); - - if (!data) - return (BOXAA *)ERROR_PTR("data not defined", procName, NULL); - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return (BOXAA *)ERROR_PTR("stream not opened", procName, NULL); - - baa = boxaaReadStream(fp); - fclose(fp); - if (!baa) L_ERROR("baa not read\n", procName); - return baa; -} - - -/*! - * \brief boxaaWrite() - * - * \param[in] filename - * \param[in] baa - * \return 0 if OK, 1 on error - */ -l_ok -boxaaWrite(const char *filename, - BOXAA *baa) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("boxaaWrite"); - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!baa) - return ERROR_INT("baa not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "w")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = boxaaWriteStream(fp, baa); - fclose(fp); - if (ret) - return ERROR_INT("baa not written to stream", procName, 1); - return 0; -} - - -/*! - * \brief boxaaWriteStream() - * - * \param[in] fp output file stream - * \param[in] baa - * \return 0 if OK, 1 on error - */ -l_ok -boxaaWriteStream(FILE *fp, - BOXAA *baa) -{ -l_int32 n, i, x, y, w, h; -BOX *box; -BOXA *boxa; - - PROCNAME("boxaaWriteStream"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!baa) - return ERROR_INT("baa not defined", procName, 1); - - n = boxaaGetCount(baa); - fprintf(fp, "\nBoxaa Version %d\n", BOXAA_VERSION_NUMBER); - fprintf(fp, "Number of boxa = %d\n", n); - - for (i = 0; i < n; i++) { - if ((boxa = boxaaGetBoxa(baa, i, L_CLONE)) == NULL) - return ERROR_INT("boxa not found", procName, 1); - boxaGetExtent(boxa, NULL, NULL, &box); - boxGetGeometry(box, &x, &y, &w, &h); - fprintf(fp, "\nBoxa[%d] extent: x = %d, y = %d, w = %d, h = %d", - i, x, y, w, h); - boxaWriteStream(fp, boxa); - boxDestroy(&box); - boxaDestroy(&boxa); - } - return 0; -} - - -/*! - * \brief boxaaWriteMem() - * - * \param[out] pdata data of serialized boxaa; ascii - * \param[out] psize size of returned data - * \param[in] baa - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Serializes a boxaa in memory and puts the result in a buffer. - *- */ -l_ok -boxaaWriteMem(l_uint8 **pdata, - size_t *psize, - BOXAA *baa) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("boxaaWriteMem"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - if (!psize) - return ERROR_INT("&size not defined", procName, 1); - if (!baa) - return ERROR_INT("baa not defined", procName, 1); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = boxaaWriteStream(fp, baa); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = boxaaWriteStream(fp, baa); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} - - -/*---------------------------------------------------------------------* - * Boxa serialized I/O * - *---------------------------------------------------------------------*/ -/*! - * \brief boxaRead() - * - * \param[in] filename - * \return boxa, or NULL on error - */ -BOXA * -boxaRead(const char *filename) -{ -FILE *fp; -BOXA *boxa; - - PROCNAME("boxaRead"); - - if (!filename) - return (BOXA *)ERROR_PTR("filename not defined", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (BOXA *)ERROR_PTR("stream not opened", procName, NULL); - boxa = boxaReadStream(fp); - fclose(fp); - if (!boxa) - return (BOXA *)ERROR_PTR("boxa not read", procName, NULL); - return boxa; -} - - -/*! - * \brief boxaReadStream() - * - * \param[in] fp input file stream - * \return boxa, or NULL on error - */ -BOXA * -boxaReadStream(FILE *fp) -{ -l_int32 n, i, x, y, w, h, version; -l_int32 ignore; -BOX *box; -BOXA *boxa; - - PROCNAME("boxaReadStream"); - - if (!fp) - return (BOXA *)ERROR_PTR("stream not defined", procName, NULL); - - if (fscanf(fp, "\nBoxa Version %d\n", &version) != 1) - return (BOXA *)ERROR_PTR("not a boxa file", procName, NULL); - if (version != BOXA_VERSION_NUMBER) - return (BOXA *)ERROR_PTR("invalid boxa version", procName, NULL); - if (fscanf(fp, "Number of boxes = %d\n", &n) != 1) - return (BOXA *)ERROR_PTR("not a boxa file", procName, NULL); - - if ((boxa = boxaCreate(n)) == NULL) - return (BOXA *)ERROR_PTR("boxa not made", procName, NULL); - for (i = 0; i < n; i++) { - if (fscanf(fp, " Box[%d]: x = %d, y = %d, w = %d, h = %d\n", - &ignore, &x, &y, &w, &h) != 5) { - boxaDestroy(&boxa); - return (BOXA *)ERROR_PTR("box descr not valid", procName, NULL); - } - box = boxCreate(x, y, w, h); - boxaAddBox(boxa, box, L_INSERT); - } - - return boxa; -} - - -/*! - * \brief boxaReadMem() - * - * \param[in] data serialization of boxa; in ascii - * \param[in] size of data in bytes; can use strlen to get it - * \return boxa, or NULL on error - */ -BOXA * -boxaReadMem(const l_uint8 *data, - size_t size) -{ -FILE *fp; -BOXA *boxa; - - PROCNAME("boxaReadMem"); - - if (!data) - return (BOXA *)ERROR_PTR("data not defined", procName, NULL); - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return (BOXA *)ERROR_PTR("stream not opened", procName, NULL); - - boxa = boxaReadStream(fp); - fclose(fp); - if (!boxa) L_ERROR("boxa not read\n", procName); - return boxa; -} - - -/*! - * \brief boxaWriteDebug() - * - * \param[in] filename - * \param[in] boxa - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) Debug version, intended for use in the library when writing - * to files in a temp directory with names that are compiled in. - * This is used instead of boxaWrite() for all such library calls. - * (2) The global variable LeptDebugOK defaults to 0, and can be set - * or cleared by the function setLeptDebugOK(). - *- */ -l_ok -boxaWriteDebug(const char *filename, - BOXA *boxa) -{ - PROCNAME("boxaWriteDebug"); - - if (LeptDebugOK) { - return boxaWrite(filename, boxa); - } else { - L_INFO("write to named temp file %s is disabled\n", procName, filename); - return 0; - } -} - - -/*! - * \brief boxaWrite() - * - * \param[in] filename - * \param[in] boxa - * \return 0 if OK, 1 on error - */ -l_ok -boxaWrite(const char *filename, - BOXA *boxa) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("boxaWrite"); - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "w")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = boxaWriteStream(fp, boxa); - fclose(fp); - if (ret) - return ERROR_INT("boxa not written to stream", procName, 1); - - return 0; -} - - -/*! - * \brief boxaWriteStream() - * - * \param[in] fp file stream; use NULL for stderr - * \param[in] boxa - * \return 0 if OK, 1 on error - */ -l_ok -boxaWriteStream(FILE *fp, - BOXA *boxa) -{ -l_int32 n, i; -BOX *box; - - PROCNAME("boxaWriteStream"); - - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (!fp) - return boxaWriteStderr(boxa); - - n = boxaGetCount(boxa); - fprintf(fp, "\nBoxa Version %d\n", BOXA_VERSION_NUMBER); - fprintf(fp, "Number of boxes = %d\n", n); - for (i = 0; i < n; i++) { - if ((box = boxaGetBox(boxa, i, L_CLONE)) == NULL) - return ERROR_INT("box not found", procName, 1); - fprintf(fp, " Box[%d]: x = %d, y = %d, w = %d, h = %d\n", - i, box->x, box->y, box->w, box->h); - boxDestroy(&box); - } - return 0; -} - - -/*! - * \brief boxaWriteStderr() - * - * \param[in] boxa - * \return 0 if OK, 1 on error - */ -l_ok -boxaWriteStderr(BOXA *boxa) -{ -l_int32 n, i; -BOX *box; - - PROCNAME("boxaWriteStderr"); - - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - n = boxaGetCount(boxa); - lept_stderr("\nBoxa Version %d\n", BOXA_VERSION_NUMBER); - lept_stderr("Number of boxes = %d\n", n); - for (i = 0; i < n; i++) { - if ((box = boxaGetBox(boxa, i, L_CLONE)) == NULL) - return ERROR_INT("box not found", procName, 1); - lept_stderr(" Box[%d]: x = %d, y = %d, w = %d, h = %d\n", - i, box->x, box->y, box->w, box->h); - boxDestroy(&box); - } - return 0; -} - - -/*! - * \brief boxaWriteMem() - * - * \param[out] pdata data of serialized boxa; ascii - * \param[out] psize size of returned data - * \param[in] boxa - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Serializes a boxa in memory and puts the result in a buffer. - *- */ -l_ok -boxaWriteMem(l_uint8 **pdata, - size_t *psize, - BOXA *boxa) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("boxaWriteMem"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - if (!psize) - return ERROR_INT("&size not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = boxaWriteStream(fp, boxa); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = boxaWriteStream(fp, boxa); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} - - -/*---------------------------------------------------------------------* - * Debug printing * - *---------------------------------------------------------------------*/ -/*! - * \brief boxPrintStreamInfo() - * - * \param[in] fp file stream; use NULL for stderr - * \param[in] box - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This outputs debug info. Use serialization functions to - * write to file if you want to read the data back. - *- */ -l_ok -boxPrintStreamInfo(FILE *fp, - BOX *box) -{ - PROCNAME("boxPrintStreamInfo"); - - if (!box) - return ERROR_INT("box not defined", procName, 1); - - if (!fp) { /* output to stderr */ - lept_stderr(" Box: x = %d, y = %d, w = %d, h = %d\n", - box->x, box->y, box->w, box->h); - } else { - fprintf(fp, " Box: x = %d, y = %d, w = %d, h = %d\n", - box->x, box->y, box->w, box->h); - } - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxfunc1.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxfunc1.c deleted file mode 100644 index 45e8995d..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxfunc1.c +++ /dev/null @@ -1,2737 +0,0 @@ -/*====================================================================* - - 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 boxfunc1.c - *
- * - * Box geometry - * l_int32 boxContains() - * l_int32 boxIntersects() - * BOXA *boxaContainedInBox() - * l_int32 boxaContainedInBoxCount() - * l_int32 boxaContainedInBoxa() - * BOXA *boxaIntersectsBox() - * l_int32 boxaIntersectsBoxCount() - * BOXA *boxaClipToBox() - * BOXA *boxaCombineOverlaps() - * l_int32 boxaCombineOverlapsInPair() - * BOX *boxOverlapRegion() - * BOX *boxBoundingRegion() - * l_int32 boxOverlapFraction() - * l_int32 boxOverlapArea() - * BOXA *boxaHandleOverlaps() - * l_int32 boxOverlapDistance() - * l_int32 boxSeparationDistance() - * l_int32 boxCompareSize() - * l_int32 boxContainsPt() - * BOX *boxaGetNearestToPt() - * BOX *boxaGetNearestToLine() - * l_int32 boxaFindNearestBoxes() - * l_int32 boxaGetNearestByDirection() - * static l_int32 boxHasOverlapInXorY() - * static l_int32 boxGetDistanceInXorY() - * l_int32 boxIntersectByLine() - * l_int32 boxGetCenter() - * BOX *boxClipToRectangle() - * l_int32 boxClipToRectangleParams() - * BOX *boxRelocateOneSide() - * BOXA *boxaAdjustSides() - * BOXA *boxaAdjustBoxSides() - * BOX *boxAdjustSides() - * BOXA *boxaSetSide() - * l_int32 boxSetSide() - * BOXA *boxaAdjustWidthToTarget() - * BOXA *boxaAdjustHeightToTarget() - * l_int32 boxEqual() - * l_int32 boxaEqual() - * l_int32 boxSimilar() - * l_int32 boxaSimilar() - * - * Boxa combine and split - * l_int32 boxaJoin() - * l_int32 boxaaJoin() - * l_int32 boxaSplitEvenOdd() - * BOXA *boxaMergeEvenOdd() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) All boxes in %boxas that are entirely outside box are removed. - * (2) If %box is not valid, returns an empty boxa. - *- */ -BOXA * -boxaContainedInBox(BOXA *boxas, - BOX *box) -{ -l_int32 i, n, val, valid; -BOX *box1; -BOXA *boxad; - - PROCNAME("boxaContainedInBox"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (!box) - return (BOXA *)ERROR_PTR("box not defined", procName, NULL); - n = boxaGetCount(boxas); - boxIsValid(box, &valid); - if (n == 0 || !valid) - return boxaCreate(1); /* empty */ - - boxad = boxaCreate(0); - for (i = 0; i < n; i++) { - if ((box1 = boxaGetValidBox(boxas, i, L_CLONE)) == NULL) - continue; - boxContains(box, box1, &val); - if (val == 1) - boxaAddBox(boxad, box1, L_COPY); - boxDestroy(&box1); /* destroy the clone */ - } - - return boxad; -} - - -/*! - * \brief boxaContainedInBoxCount() - * - * \param[in] boxa - * \param[in] box for selecting contained boxes in %boxa - * \param[out] pcount number of boxes intersecting the box - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) If %box is not valid, returns a zero count. - *- */ -l_ok -boxaContainedInBoxCount(BOXA *boxa, - BOX *box, - l_int32 *pcount) -{ -l_int32 i, n, val, valid; -BOX *box1; - - PROCNAME("boxaContainedInBoxCount"); - - if (!pcount) - return ERROR_INT("&count not defined", procName, 1); - *pcount = 0; - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - n = boxaGetCount(boxa); - boxIsValid(box, &valid); - if (n == 0 || !valid) - return 0; - - for (i = 0; i < n; i++) { - if ((box1 = boxaGetValidBox(boxa, i, L_CLONE)) == NULL) - continue; - boxContains(box, box1, &val); - if (val == 1) - (*pcount)++; - boxDestroy(&box1); - } - return 0; -} - - -/*! - * \brief boxaContainedInBoxa() - * - * \param[in] boxa1, boxa2 - * \param[out] pcontained 1 if every box in boxa2 is contained in - * some box in boxa1; 0 otherwise - * \return 0 if OK, 1 on error - */ -l_ok -boxaContainedInBoxa(BOXA *boxa1, - BOXA *boxa2, - l_int32 *pcontained) -{ -l_int32 i, j, n1, n2, cont, result; -BOX *box1, *box2; - - PROCNAME("boxaContainedInBoxa"); - - if (!pcontained) - return ERROR_INT("&contained not defined", procName, 1); - *pcontained = 0; - if (!boxa1 || !boxa2) - return ERROR_INT("boxa1 and boxa2 not both defined", procName, 1); - - n1 = boxaGetCount(boxa1); - n2 = boxaGetCount(boxa2); - for (i = 0; i < n2; i++) { - if ((box2 = boxaGetValidBox(boxa2, i, L_CLONE)) == NULL) - continue; - cont = 0; - for (j = 0; j < n1; j++) { - if ((box1 = boxaGetValidBox(boxa1, j, L_CLONE)) == NULL) - continue; - boxContains(box1, box2, &result); - boxDestroy(&box1); - if (result) { - cont = 1; - break; - } - } - boxDestroy(&box2); - if (!cont) return 0; - } - - *pcontained = 1; - return 0; -} - - -/*! - * \brief boxaIntersectsBox() - * - * \param[in] boxas - * \param[in] box for intersecting - * \return boxad boxa with all boxes in boxas that intersect box, - * or NULL on error - * - *
- * Notes: - * (1) All boxes in boxa that intersect with box (i.e., are completely - * or partially contained in box) are retained. - *- */ -BOXA * -boxaIntersectsBox(BOXA *boxas, - BOX *box) -{ -l_int32 i, n, val, valid; -BOX *box1; -BOXA *boxad; - - PROCNAME("boxaIntersectsBox"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (!box) - return (BOXA *)ERROR_PTR("box not defined", procName, NULL); - n = boxaGetCount(boxas); - boxIsValid(box, &valid); - if (n == 0 || !valid) - return boxaCreate(1); /* empty */ - - boxad = boxaCreate(0); - for (i = 0; i < n; i++) { - if ((box1 = boxaGetValidBox(boxas, i, L_CLONE)) == NULL) - continue; - boxIntersects(box, box1, &val); - if (val == 1) - boxaAddBox(boxad, box1, L_COPY); - boxDestroy(&box1); /* destroy the clone */ - } - - return boxad; -} - - -/*! - * \brief boxaIntersectsBoxCount() - * - * \param[in] boxa - * \param[in] box for selecting intersecting boxes in %boxa - * \param[out] pcount number of boxes intersecting the box - * \return 0 if OK, 1 on error - */ -l_ok -boxaIntersectsBoxCount(BOXA *boxa, - BOX *box, - l_int32 *pcount) -{ -l_int32 i, n, val, valid; -BOX *box1; - - PROCNAME("boxaIntersectsBoxCount"); - - if (!pcount) - return ERROR_INT("&count not defined", procName, 1); - *pcount = 0; - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - n = boxaGetCount(boxa); - boxIsValid(box, &valid); - if (n == 0 || !valid) - return 0; - - for (i = 0; i < n; i++) { - if ((box1 = boxaGetValidBox(boxa, i, L_CLONE)) == NULL) - continue; - boxIntersects(box, box1, &val); - if (val == 1) - (*pcount)++; - boxDestroy(&box1); - } - return 0; -} - - -/*! - * \brief boxaClipToBox() - * - * \param[in] boxas - * \param[in] box for clipping - * \return boxad boxa with boxes in boxas clipped to box, or NULL on error - * - *
- * Notes: - * (1) All boxes in boxa not intersecting with box are removed, and - * the remaining boxes are clipped to box. - *- */ -BOXA * -boxaClipToBox(BOXA *boxas, - BOX *box) -{ -l_int32 i, n, valid; -BOX *box1, *boxo; -BOXA *boxad; - - PROCNAME("boxaClipToBox"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (!box) - return (BOXA *)ERROR_PTR("box not defined", procName, NULL); - n = boxaGetCount(boxas); - boxIsValid(box, &valid); - if (n == 0 || !valid) - return boxaCreate(1); /* empty */ - - boxad = boxaCreate(0); - for (i = 0; i < n; i++) { - if ((box1 = boxaGetValidBox(boxas, i, L_CLONE)) == NULL) - continue; - if ((boxo = boxOverlapRegion(box, box1)) != NULL) - boxaAddBox(boxad, boxo, L_INSERT); - boxDestroy(&box1); - } - - return boxad; -} - - -/*! - * \brief boxaCombineOverlaps() - * - * \param[in] boxas - * \param[in,out] pixadb debug output - * \return boxad where each set of boxes in boxas that overlap are combined - * into a single bounding box in boxad, or NULL on error. - * - *
- * Notes: - * (1) If there are no overlapping boxes, it simply returns a copy - * of %boxas. - * (2) Input an empty %pixadb, using pixaCreate(0), for debug output. - * The output gives 2 visualizations of the boxes per iteration; - * boxes in red before, and added boxes in green after. Note that - * all pixels in the red boxes are contained in the green ones. - * (3) The alternative method of painting each rectangle and finding - * the 4-connected components gives a different result in - * general, because two non-overlapping (but touching) - * rectangles, when rendered, are 4-connected and will be joined. - * (4) A bad case computationally is to have n boxes, none of which - * overlap. Then you have one iteration with O(n^2) compares. - * This is still faster than painting each rectangle and finding - * the bounding boxes of the connected components, even for - * thousands of rectangles. - *- */ -BOXA * -boxaCombineOverlaps(BOXA *boxas, - PIXA *pixadb) -{ -l_int32 i, j, w, h, n1, n2, overlap, niters; -BOX *box1, *box2, *box3; -BOXA *boxa1, *boxa2; -PIX *pix1; - - PROCNAME("boxaCombineOverlaps"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - - if (pixadb) boxaGetExtent(boxas, &w, &h, NULL); - - boxa1 = boxaCopy(boxas, L_COPY); - n1 = boxaGetCount(boxa1); - niters = 0; - while (1) { /* loop until no change from previous iteration */ - niters++; - if (pixadb) { - pix1 = pixCreate(w + 5, h + 5, 32); - pixSetAll(pix1); - pixRenderBoxaArb(pix1, boxa1, 2, 255, 0, 0); - pixaAddPix(pixadb, pix1, L_COPY); - } - - /* Combine overlaps for this iteration */ - for (i = 0; i < n1; i++) { - if ((box1 = boxaGetValidBox(boxa1, i, L_COPY)) == NULL) - continue; - for (j = i + 1; j < n1; j++) { - if ((box2 = boxaGetValidBox(boxa1, j, L_COPY)) == NULL) - continue; - boxIntersects(box1, box2, &overlap); - if (overlap) { - box3 = boxBoundingRegion(box1, box2); - boxaReplaceBox(boxa1, i, box3); - boxaReplaceBox(boxa1, j, boxCreate(0, 0, 0, 0)); - boxDestroy(&box1); - box1 = boxCopy(box3); - } - boxDestroy(&box2); - } - boxDestroy(&box1); - } - boxa2 = boxaSaveValid(boxa1, L_COPY); - n2 = boxaGetCount(boxa2); - boxaDestroy(&boxa1); - boxa1 = boxa2; - if (n1 == n2) { - if (pixadb) pixDestroy(&pix1); - break; - } - n1 = n2; - if (pixadb) { - pixRenderBoxaArb(pix1, boxa1, 2, 0, 255, 0); - pixaAddPix(pixadb, pix1, L_INSERT); - } - } - - if (pixadb) - L_INFO("number of iterations: %d\n", procName, niters); - return boxa1; -} - - -/*! - * \brief boxaCombineOverlapsInPair() - * - * \param[in] boxas1 input boxa1 - * \param[in] boxas2 input boxa2 - * \param[out] pboxad1 output boxa1 - * \param[out] pboxad2 output boxa2 - * \param[in,out] pixadb debug output - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) One of three things happens to each box in %boxa1 and %boxa2: - * * it gets absorbed into a larger box that it overlaps with - * * it absorbs a smaller (by area) box that it overlaps with - * and gets larger, using the bounding region of the 2 boxes - * * it is unchanged (including absorbing smaller boxes that - * are contained within it). - * (2) If all the boxes from one of the input boxa are absorbed, this - * returns an empty boxa. - * (3) Input an empty %pixadb, using pixaCreate(0), for debug output - * (4) This is useful if different operations are to be carried out - * on possibly overlapping rectangular regions, and it is desired - * to have only one operation on any rectangular region. - *- */ -l_ok -boxaCombineOverlapsInPair(BOXA *boxas1, - BOXA *boxas2, - BOXA **pboxad1, - BOXA **pboxad2, - PIXA *pixadb) -{ -l_int32 i, j, w, h, w2, h2, n1, n2, n1i, n2i, niters; -l_int32 overlap, bigger, area1, area2; -BOX *box1, *box2, *box3; -BOXA *boxa1, *boxa2, *boxac1, *boxac2; -PIX *pix1; - - PROCNAME("boxaCombineOverlapsInPair"); - - if (pboxad1) *pboxad1 = NULL; - if (pboxad2) *pboxad2 = NULL; - if (!boxas1 || !boxas2) - return ERROR_INT("boxas1 and boxas2 not both defined", procName, 1); - if (!pboxad1 || !pboxad2) - return ERROR_INT("&boxad1 and &boxad2 not both defined", procName, 1); - - if (pixadb) { - boxaGetExtent(boxas1, &w, &h, NULL); - boxaGetExtent(boxas2, &w2, &h2, NULL); - w = L_MAX(w, w2); - h = L_MAX(h, w2); - } - - /* Let the boxa with the largest area have first crack at the other */ - boxaGetArea(boxas1, &area1); - boxaGetArea(boxas2, &area2); - if (area1 >= area2) { - boxac1 = boxaCopy(boxas1, L_COPY); - boxac2 = boxaCopy(boxas2, L_COPY); - } else { - boxac1 = boxaCopy(boxas2, L_COPY); - boxac2 = boxaCopy(boxas1, L_COPY); - } - - n1i = boxaGetCount(boxac1); - n2i = boxaGetCount(boxac2); - niters = 0; - while (1) { - niters++; - if (pixadb) { - pix1 = pixCreate(w + 5, h + 5, 32); - pixSetAll(pix1); - pixRenderBoxaArb(pix1, boxac1, 2, 255, 0, 0); - pixRenderBoxaArb(pix1, boxac2, 2, 0, 255, 0); - pixaAddPix(pixadb, pix1, L_INSERT); - } - - /* First combine boxes in each set */ - boxa1 = boxaCombineOverlaps(boxac1, NULL); - boxa2 = boxaCombineOverlaps(boxac2, NULL); - - /* Now combine boxes between sets */ - n1 = boxaGetCount(boxa1); - n2 = boxaGetCount(boxa2); - for (i = 0; i < n1; i++) { /* 1 eats 2 */ - if ((box1 = boxaGetValidBox(boxa1, i, L_COPY)) == NULL) - continue; - for (j = 0; j < n2; j++) { - if ((box2 = boxaGetValidBox(boxa2, j, L_COPY)) == NULL) - continue; - boxIntersects(box1, box2, &overlap); - boxCompareSize(box1, box2, L_SORT_BY_AREA, &bigger); - if (overlap && (bigger == 1)) { - box3 = boxBoundingRegion(box1, box2); - boxaReplaceBox(boxa1, i, box3); - boxaReplaceBox(boxa2, j, boxCreate(0, 0, 0, 0)); - boxDestroy(&box1); - box1 = boxCopy(box3); - } - boxDestroy(&box2); - } - boxDestroy(&box1); - } - for (i = 0; i < n2; i++) { /* 2 eats 1 */ - if ((box2 = boxaGetValidBox(boxa2, i, L_COPY)) == NULL) - continue; - for (j = 0; j < n1; j++) { - if ((box1 = boxaGetValidBox(boxa1, j, L_COPY)) == NULL) - continue; - boxIntersects(box1, box2, &overlap); - boxCompareSize(box2, box1, L_SORT_BY_AREA, &bigger); - if (overlap && (bigger == 1)) { - box3 = boxBoundingRegion(box1, box2); - boxaReplaceBox(boxa2, i, box3); - boxaReplaceBox(boxa1, j, boxCreate(0, 0, 0, 0)); - boxDestroy(&box2); - box2 = boxCopy(box3); - } - boxDestroy(&box1); - } - boxDestroy(&box2); - } - boxaDestroy(&boxac1); - boxaDestroy(&boxac2); - boxac1 = boxaSaveValid(boxa1, L_COPY); /* remove invalid boxes */ - boxac2 = boxaSaveValid(boxa2, L_COPY); - boxaDestroy(&boxa1); - boxaDestroy(&boxa2); - n1 = boxaGetCount(boxac1); - n2 = boxaGetCount(boxac2); - if (n1 == n1i && n2 == n2i) break; - n1i = n1; - n2i = n2; - if (pixadb) { - pix1 = pixCreate(w + 5, h + 5, 32); - pixSetAll(pix1); - pixRenderBoxaArb(pix1, boxac1, 2, 255, 0, 0); - pixRenderBoxaArb(pix1, boxac2, 2, 0, 255, 0); - pixaAddPix(pixadb, pix1, L_INSERT); - } - } - - if (pixadb) - L_INFO("number of iterations: %d\n", procName, niters); - *pboxad1 = boxac1; - *pboxad2 = boxac2; - return 0; -} - - -/*! - * \brief boxOverlapRegion() - * - * \param[in] box1, box2 - * \return box of overlap region between input boxes; - * NULL if no overlap or on error - * - *
- * Notes: - * (1) This is the geometric intersection of the two rectangles. - *- */ -BOX * -boxOverlapRegion(BOX *box1, - BOX *box2) -{ -l_int32 l1, l2, r1, r2, t1, t2, b1, b2, w1, h1, w2, h2, ld, td, rd, bd; -l_int32 valid1, valid2; - - PROCNAME("boxOverlapRegion"); - - if (!box1 || !box2) - return (BOX *)ERROR_PTR("boxes not both defined", procName, NULL); - boxIsValid(box1, &valid1); - boxIsValid(box2, &valid2); - if (!valid1 || !valid2) { - L_WARNING("at least one box is invalid\n", procName); - return NULL; - } - - boxGetGeometry(box1, &l1, &t1, &w1, &h1); - boxGetGeometry(box2, &l2, &t2, &w2, &h2); - r1 = l1 + w1 - 1; - r2 = l2 + w2 - 1; - b1 = t1 + h1 - 1; - b2 = t2 + h2 - 1; - if (b2 < t1 || b1 < t2 || r1 < l2 || r2 < l1) - return NULL; - - ld = L_MAX(l1, l2); - td = L_MAX(t1, t2); - rd = L_MIN(r1, r2); - bd = L_MIN(b1, b2); - return boxCreate(ld, td, rd - ld + 1, bd - td + 1); -} - - -/*! - * \brief boxBoundingRegion() - * - * \param[in] box1, box2 - * \return box of bounding region containing the input boxes; - * NULL on error - * - *
- * Notes: - * (1) This is the geometric union of the two rectangles. - * (2) Invalid boxes are ignored. This returns an invalid box - * if both input boxes are invalid. - * (3) For the geometric union of a boxa, use boxaGetExtent(). - *- */ -BOX * -boxBoundingRegion(BOX *box1, - BOX *box2) -{ -l_int32 l1, l2, r1, r2, t1, t2, b1, b2, w1, h1, w2, h2, ld, td, rd, bd; -l_int32 valid1, valid2; - - PROCNAME("boxBoundingRegion"); - - if (!box1 || !box2) - return (BOX *)ERROR_PTR("boxes not both defined", procName, NULL); - boxIsValid(box1, &valid1); - boxIsValid(box2, &valid2); - if (!valid1 && !valid2) { - L_WARNING("both boxes are invalid\n", procName); - return boxCreate(0, 0, 0, 0); - } - if (valid1 && !valid2) - return boxCopy(box1); - if (!valid1 && valid2) - return boxCopy(box2); - - boxGetGeometry(box1, &l1, &t1, &w1, &h1); - boxGetGeometry(box2, &l2, &t2, &w2, &h2); - r1 = l1 + w1 - 1; - r2 = l2 + w2 - 1; - b1 = t1 + h1 - 1; - b2 = t2 + h2 - 1; - ld = L_MIN(l1, l2); - td = L_MIN(t1, t2); - rd = L_MAX(r1, r2); - bd = L_MAX(b1, b2); - return boxCreate(ld, td, rd - ld + 1, bd - td + 1); -} - - -/*! - * \brief boxOverlapFraction() - * - * \param[in] box1, box2 - * \param[out] pfract the fraction of box2 overlapped by box1 - * \return 0 if OK, 1 on error. - * - *
- * Notes: - * (1) The result depends on the order of the input boxes, - * because the overlap is taken as a fraction of box2. - * (2) If at least one box is not valid, there is no overlap. - *- */ -l_ok -boxOverlapFraction(BOX *box1, - BOX *box2, - l_float32 *pfract) -{ -l_int32 w2, h2, w, h, valid1, valid2; -BOX *boxo; - - PROCNAME("boxOverlapFraction"); - - if (!pfract) - return ERROR_INT("&fract not defined", procName, 1); - *pfract = 0.0; - if (!box1 || !box2) - return ERROR_INT("boxes not both defined", procName, 1); - boxIsValid(box1, &valid1); - boxIsValid(box2, &valid2); - if (!valid1 || !valid2) { - L_WARNING("boxes not both valid\n", procName); - return 0; - } - - if ((boxo = boxOverlapRegion(box1, box2)) == NULL) /* no overlap */ - return 0; - - boxGetGeometry(box2, NULL, NULL, &w2, &h2); - boxGetGeometry(boxo, NULL, NULL, &w, &h); - *pfract = (l_float32)(w * h) / (l_float32)(w2 * h2); - boxDestroy(&boxo); - return 0; -} - - -/*! - * \brief boxOverlapArea() - * - * \param[in] box1, box2 - * \param[out] parea the number of pixels in the overlap - * \return 0 if OK, 1 on error. - */ -l_ok -boxOverlapArea(BOX *box1, - BOX *box2, - l_int32 *parea) -{ -l_int32 w, h, valid1, valid2; -BOX *box; - - PROCNAME("boxOverlapArea"); - - if (!parea) - return ERROR_INT("&area not defined", procName, 1); - *parea = 0; - if (!box1 || !box2) - return ERROR_INT("boxes not both defined", procName, 1); - boxIsValid(box1, &valid1); - boxIsValid(box2, &valid2); - if (!valid1 || !valid2) - return ERROR_INT("boxes not both valid", procName, 1); - - if ((box = boxOverlapRegion(box1, box2)) == NULL) /* no overlap */ - return 0; - - boxGetGeometry(box, NULL, NULL, &w, &h); - *parea = w * h; - boxDestroy(&box); - return 0; -} - - -/*! - * \brief boxaHandleOverlaps() - * - * \param[in] boxas - * \param[in] op L_COMBINE, L_REMOVE_SMALL - * \param[in] range forward distance over which overlaps - * are checked; > 0 - * \param[in] min_overlap minimum fraction of smaller box required for - * overlap to count; 0.0 to ignore - * \param[in] max_ratio maximum fraction of small/large areas for - * overlap to count; 1.0 to ignore - * \param[out] pnamap [optional] combining map - * \return boxad, or NULL on error. - * - *
- * Notes: - * (1) For all n(n-1)/2 box pairings, if two boxes overlap, either: - * (a) op == L_COMBINE: get the bounding region for the two, - * replace the larger with the bounding region, and remove - * the smaller of the two, or - * (b) op == L_REMOVE_SMALL: just remove the smaller. - * (2) If boxas is 2D sorted, range can be small, but if it is - * not spatially sorted, range should be large to allow all - * pairwise comparisons to be made. - * (3) The %min_overlap parameter allows ignoring small overlaps. - * If %min_overlap == 1.0, only boxes fully contained in larger - * boxes can be considered for removal; if %min_overlap == 0.0, - * this constraint is ignored. - * (4) The %max_ratio parameter allows ignoring overlaps between - * boxes that are not too different in size. If %max_ratio == 0.0, - * no boxes can be removed; if %max_ratio == 1.0, this constraint - * is ignored. - *- */ -BOXA * -boxaHandleOverlaps(BOXA *boxas, - l_int32 op, - l_int32 range, - l_float32 min_overlap, - l_float32 max_ratio, - NUMA **pnamap) -{ -l_int32 i, j, n, w, h, area1, area2, val; -l_int32 overlap_area; -l_float32 overlap_ratio, area_ratio; -BOX *box1, *box2, *box3; -BOXA *boxat, *boxad; -NUMA *namap; - - PROCNAME("boxaHandleOverlaps"); - - if (pnamap) *pnamap = NULL; - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (op != L_COMBINE && op != L_REMOVE_SMALL) - return (BOXA *)ERROR_PTR("invalid op", procName, NULL); - - n = boxaGetCount(boxas); - if (n == 0) - return boxaCreate(1); /* empty */ - if (range == 0) { - L_WARNING("range is 0\n", procName); - return boxaCopy(boxas, L_COPY); - } - - /* Identify smaller boxes in overlap pairs, and mark to eliminate. */ - namap = numaMakeConstant(-1, n); - for (i = 0; i < n; i++) { - if ((box1 = boxaGetValidBox(boxas, i, L_CLONE)) == NULL) - continue; - boxGetGeometry(box1, NULL, NULL, &w, &h); - area1 = w * h; - if (area1 == 0) { - boxDestroy(&box1); - continue; - } - for (j = i + 1; j < i + 1 + range && j < n; j++) { - if ((box2 = boxaGetValidBox(boxas, j, L_CLONE)) == NULL) - continue; - boxOverlapArea(box1, box2, &overlap_area); - if (overlap_area > 0) { - boxGetGeometry(box2, NULL, NULL, &w, &h); - area2 = w * h; - if (area2 == 0) { - /* do nothing */ - } else if (area1 >= area2) { - overlap_ratio = (l_float32)overlap_area / (l_float32)area2; - area_ratio = (l_float32)area2 / (l_float32)area1; - if (overlap_ratio >= min_overlap && - area_ratio <= max_ratio) { - numaSetValue(namap, j, i); - } - } else { - overlap_ratio = (l_float32)overlap_area / (l_float32)area1; - area_ratio = (l_float32)area1 / (l_float32)area2; - if (overlap_ratio >= min_overlap && - area_ratio <= max_ratio) { - numaSetValue(namap, i, j); - } - } - } - boxDestroy(&box2); - } - boxDestroy(&box1); - } - - boxat = boxaCopy(boxas, L_COPY); - if (op == L_COMBINE) { - /* Resize the larger of the pair to the bounding region */ - for (i = 0; i < n; i++) { - numaGetIValue(namap, i, &val); - if (val >= 0) { - box1 = boxaGetBox(boxas, i, L_CLONE); /* smaller */ - box2 = boxaGetBox(boxas, val, L_CLONE); /* larger */ - box3 = boxBoundingRegion(box1, box2); - boxaReplaceBox(boxat, val, box3); - boxDestroy(&box1); - boxDestroy(&box2); - } - } - } - - /* Remove the smaller of the pairs */ - boxad = boxaCreate(n); - for (i = 0; i < n; i++) { - numaGetIValue(namap, i, &val); - if (val == -1) { - box1 = boxaGetBox(boxat, i, L_COPY); - boxaAddBox(boxad, box1, L_INSERT); - } - } - boxaDestroy(&boxat); - if (pnamap) - *pnamap = namap; - else - numaDestroy(&namap); - return boxad; -} - - -/*! - * \brief boxOverlapDistance() - * - * \param[in] box1, box2 two boxes, in any order - * \param[out] ph_ovl [optional] horizontal overlap - * \param[out] pv_ovl [optional] vertical overlap - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This measures horizontal and vertical overlap of the - * two boxes. Horizontal and vertical overlap are measured - * independently. We need to consider several cases to clarify. - * (2) A positive horizontal overlap means that there is at least - * one point on the the %box1 boundary with the same x-component - * as some point on the %box2 boundary. Conversely, with a zero - * or negative horizontal overlap, there are no boundary pixels - * in %box1 that share an x-component with a boundary pixel in %box2. - * (3) For a zero or negative horizontal overlap, o <= 0, the minimum - * difference in the x-component between pixels on the boundaries - * of the two boxes is d = -o + 1. - * (4) Likewise for vertical overlaps. - *- */ -l_ok -boxOverlapDistance(BOX *box1, - BOX *box2, - l_int32 *ph_ovl, - l_int32 *pv_ovl) -{ -l_int32 l1, t1, w1, h1, r1, b1, l2, t2, w2, h2, r2, b2, valid1, valid2; - - PROCNAME("boxOverlapDistance"); - - if (!ph_ovl && !pv_ovl) - return ERROR_INT("nothing to do", procName, 1); - if (ph_ovl) *ph_ovl = 0; - if (pv_ovl) *pv_ovl = 0; - if (!box1 || !box2) - return ERROR_INT("boxes not both defined", procName, 1); - boxIsValid(box1, &valid1); - boxIsValid(box2, &valid2); - if (!valid1 || !valid2) - return ERROR_INT("boxes not both valid", procName, 1); - - if (ph_ovl) { - boxGetGeometry(box1, &l1, NULL, &w1, NULL); - boxGetGeometry(box2, &l2, NULL, &w2, NULL); - r1 = l1 + w1; /* 1 pixel to the right of box 1 */ - r2 = l2 + w2; - if (l2 >= l1) - *ph_ovl = r1 - l2; - else - *ph_ovl = r2 - l1; - } - if (pv_ovl) { - boxGetGeometry(box1, NULL, &t1, NULL, &h1); - boxGetGeometry(box2, NULL, &t2, NULL, &h2); - b1 = t1 + h1; /* 1 pixel below box 1 */ - b2 = t2 + h2; - if (t2 >= t1) - *pv_ovl = b1 - t2; - else - *pv_ovl = b2 - t1; - } - return 0; -} - - -/*! - * \brief boxSeparationDistance() - * - * \param[in] box1, box2 two boxes, in any order - * \param[out] ph_sep horizontal separation - * \param[out] pv_sep vertical separation - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This measures the Manhattan distance between the closest points - * on the boundaries of the two boxes. When the boxes overlap - * (including touching along a line or at a corner), the - * horizontal and vertical distances are 0. - * (2) The distances represent the horizontal and vertical separation - * of the two boxes. The boxes have a nonzero intersection when - * both the horizontal and vertical overlaps are positive, and - * for that case both horizontal and vertical separation - * distances are 0. - * (3) If the horizontal overlap of the boxes is positive, the - * horizontal separation between nearest points on respective - * boundaries is 0, and likewise for the vertical overlap. - * (4) If the horizontal overlap ho <= 0, the horizontal - * separation between nearest points is d = -ho + 1. - * Likewise, if the vertical overlap vo <= 0, the vertical - * separation between nearest points is d = -vo + 1. - *- */ -l_ok -boxSeparationDistance(BOX *box1, - BOX *box2, - l_int32 *ph_sep, - l_int32 *pv_sep) -{ -l_int32 h_ovl, v_ovl, valid1, valid2; - - PROCNAME("boxSeparationDistance"); - - if (ph_sep) *ph_sep = 0; - if (pv_sep) *pv_sep = 0; - if (!ph_sep || !pv_sep) - return ERROR_INT("&h_sep and &v_sep not both defined", procName, 1); - if (!box1 || !box2) - return ERROR_INT("boxes not both defined", procName, 1); - boxIsValid(box1, &valid1); - boxIsValid(box2, &valid2); - if (!valid1 || !valid2) - return ERROR_INT("boxes not both valid", procName, 1); - - boxOverlapDistance(box1, box2, &h_ovl, &v_ovl); - if (h_ovl <= 0) - *ph_sep = -h_ovl + 1; - if (v_ovl <= 0) - *pv_sep = -v_ovl + 1; - return 0; -} - - -/*! - * \brief boxCompareSize() - * - * \param[in] box1, box2 - * \param[in] type L_SORT_BY_WIDTH, L_SORT_BY_HEIGHT, - * L_SORT_BY_MAX_DIMENSION, L_SORT_BY_PERIMETER, - * L_SORT_BY_AREA, - * \param[out] prel 1 if box1 > box2, 0 if the same, -1 if box1 < box2 - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) We're re-using the SORT enum for these comparisons. - *- */ -l_ok -boxCompareSize(BOX *box1, - BOX *box2, - l_int32 type, - l_int32 *prel) -{ -l_int32 w1, h1, w2, h2, size1, size2, valid1, valid2; - - PROCNAME("boxCompareSize"); - - if (!prel) - return ERROR_INT("&rel not defined", procName, 1); - *prel = 0; - if (!box1 || !box2) - return ERROR_INT("boxes not both defined", procName, 1); - boxIsValid(box1, &valid1); - boxIsValid(box2, &valid2); - if (!valid1 || !valid2) - return ERROR_INT("boxes not both valid", procName, 1); - if (type != L_SORT_BY_WIDTH && type != L_SORT_BY_HEIGHT && - type != L_SORT_BY_MAX_DIMENSION && type != L_SORT_BY_PERIMETER && - type != L_SORT_BY_AREA) - return ERROR_INT("invalid compare type", procName, 1); - - boxGetGeometry(box1, NULL, NULL, &w1, &h1); - boxGetGeometry(box2, NULL, NULL, &w2, &h2); - if (type == L_SORT_BY_WIDTH) { - *prel = (w1 > w2) ? 1 : ((w1 == w2) ? 0 : -1); - } else if (type == L_SORT_BY_HEIGHT) { - *prel = (h1 > h2) ? 1 : ((h1 == h2) ? 0 : -1); - } else if (type == L_SORT_BY_MAX_DIMENSION) { - size1 = L_MAX(w1, h1); - size2 = L_MAX(w2, h2); - *prel = (size1 > size2) ? 1 : ((size1 == size2) ? 0 : -1); - } else if (type == L_SORT_BY_PERIMETER) { - size1 = w1 + h1; - size2 = w2 + h2; - *prel = (size1 > size2) ? 1 : ((size1 == size2) ? 0 : -1); - } else if (type == L_SORT_BY_AREA) { - size1 = w1 * h1; - size2 = w2 * h2; - *prel = (size1 > size2) ? 1 : ((size1 == size2) ? 0 : -1); - } - return 0; -} - - -/*! - * \brief boxContainsPt() - * - * \param[in] box - * \param[in] x, y a point - * \param[out] pcontains 1 if box contains point; 0 otherwise - * \return 0 if OK, 1 on error. - */ -l_ok -boxContainsPt(BOX *box, - l_float32 x, - l_float32 y, - l_int32 *pcontains) -{ -l_int32 bx, by, bw, bh; - - PROCNAME("boxContainsPt"); - - if (!pcontains) - return ERROR_INT("&contains not defined", procName, 1); - *pcontains = 0; - if (!box) - return ERROR_INT("&box not defined", procName, 1); - boxGetGeometry(box, &bx, &by, &bw, &bh); - if (x >= bx && x < bx + bw && y >= by && y < by + bh) - *pcontains = 1; - return 0; -} - - -/*! - * \brief boxaGetNearestToPt() - * - * \param[in] boxa - * \param[in] x, y point - * \return box with centroid closest to the given point [x,y], - * or NULL if no boxes in boxa - * - *
- * Notes: - * (1) Uses euclidean distance between centroid and point. - *- */ -BOX * -boxaGetNearestToPt(BOXA *boxa, - l_int32 x, - l_int32 y) -{ -l_int32 i, n, minindex; -l_float32 delx, dely, dist, mindist, cx, cy; -BOX *box; - - PROCNAME("boxaGetNearestToPt"); - - if (!boxa) - return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); - if ((n = boxaGetCount(boxa)) == 0) - return (BOX *)ERROR_PTR("n = 0", procName, NULL); - - mindist = 1000000000.; - minindex = 0; - for (i = 0; i < n; i++) { - if ((box = boxaGetValidBox(boxa, i, L_CLONE)) == NULL) - continue; - boxGetCenter(box, &cx, &cy); - delx = (l_float32)(cx - x); - dely = (l_float32)(cy - y); - dist = delx * delx + dely * dely; - if (dist < mindist) { - minindex = i; - mindist = dist; - } - boxDestroy(&box); - } - - return boxaGetBox(boxa, minindex, L_COPY); -} - - -/*! - * \brief boxaGetNearestToLine() - * - * \param[in] boxa - * \param[in] x, y (y = -1 for vertical line; x = -1 for horiz line) - * \return box with centroid closest to the given line, - * or NULL if no boxes in boxa - * - *
- * Notes: - * (1) For a horizontal line at some value y, get the minimum of the - * distance |yc - y| from the box centroid yc value to y; - * likewise minimize |xc - x| for a vertical line at x. - * (2) Input y < 0, x >= 0 to indicate a vertical line at x, and - * x < 0, y >= 0 for a horizontal line at y. - *- */ -BOX * -boxaGetNearestToLine(BOXA *boxa, - l_int32 x, - l_int32 y) -{ -l_int32 i, n, minindex; -l_float32 dist, mindist, cx, cy; -BOX *box; - - PROCNAME("boxaGetNearestToLine"); - - if (!boxa) - return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); - if ((n = boxaGetCount(boxa)) == 0) - return (BOX *)ERROR_PTR("n = 0", procName, NULL); - if (y >= 0 && x >= 0) - return (BOX *)ERROR_PTR("either x or y must be < 0", procName, NULL); - if (y < 0 && x < 0) - return (BOX *)ERROR_PTR("either x or y must be >= 0", procName, NULL); - - mindist = 1000000000.; - minindex = 0; - for (i = 0; i < n; i++) { - if ((box = boxaGetValidBox(boxa, i, L_CLONE)) == NULL) - continue; - boxGetCenter(box, &cx, &cy); - if (x >= 0) - dist = L_ABS(cx - (l_float32)x); - else /* y >= 0 */ - dist = L_ABS(cy - (l_float32)y); - if (dist < mindist) { - minindex = i; - mindist = dist; - } - boxDestroy(&box); - } - - return boxaGetBox(boxa, minindex, L_COPY); -} - - -/*! - * \brief boxaFindNearestBoxes() - * - * \param[in] boxa either unsorted, or 2D sorted in LR/TB scan order - * \param[in] dist_select L_NON_NEGATIVE, L_ALL - * \param[in] range search distance from box i; use 0 to search - * entire boxa (e.g., if it's not 2D sorted) - * \param[out] pnaaindex for each box in %boxa, contains a numa of 4 - * box indices (per direction) of the nearest box - * \param[out] pnaadist for each box in %boxa, this contains a numa - * \return 0 if OK, 1 on error - *
- * Notes: - * (1) See boxaGetNearestByDirection() for usage of %dist_select - * and %range. - *- */ -l_ok -boxaFindNearestBoxes(BOXA *boxa, - l_int32 dist_select, - l_int32 range, - NUMAA **pnaaindex, - NUMAA **pnaadist) -{ -l_int32 i, n, index, dist; -NUMA *nai, *nad; -NUMAA *naai, *naad; - - PROCNAME("boxaFindNearestBoxes"); - - if (pnaaindex) *pnaaindex = NULL; - if (pnaadist) *pnaadist = NULL; - if (!pnaaindex) - return ERROR_INT("&naaindex not defined", procName, 1); - if (!pnaadist) - return ERROR_INT("&naadist not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - n = boxaGetCount(boxa); - naai = numaaCreate(n); - naad = numaaCreate(n); - *pnaaindex = naai; - *pnaadist = naad; - for (i = 0; i < n; i++) { - nai = numaCreate(4); - nad = numaCreate(4); - boxaGetNearestByDirection(boxa, i, L_FROM_LEFT, dist_select, - range, &index, &dist); - numaAddNumber(nai, index); - numaAddNumber(nad, dist); - boxaGetNearestByDirection(boxa, i, L_FROM_RIGHT, dist_select, - range, &index, &dist); - numaAddNumber(nai, index); - numaAddNumber(nad, dist); - boxaGetNearestByDirection(boxa, i, L_FROM_TOP, dist_select, - range, &index, &dist); - numaAddNumber(nai, index); - numaAddNumber(nad, dist); - boxaGetNearestByDirection(boxa, i, L_FROM_BOT, dist_select, - range, &index, &dist); - numaAddNumber(nai, index); - numaAddNumber(nad, dist); - numaaAddNuma(naai, nai, L_INSERT); - numaaAddNuma(naad, nad, L_INSERT); - } - return 0; -} - - -/*! - * \brief boxaGetNearestByDirection() - * - * \param[in] boxa either unsorted, or 2D sorted in LR/TB scan order - * \param[in] i box we test against - * \param[in] dir direction to look: L_FROM_LEFT, L_FROM_RIGHT, - * L_FROM_TOP, L_FROM_BOT - * \param[in] dist_select L_NON_NEGATIVE, L_ALL - * \param[in] range search distance from box i; use 0 to search - * entire boxa (e.g., if it's not 2D sorted) - * \param[out] pindex index in boxa of nearest box with overlapping - * coordinates in the indicated direction; - * -1 if there is no box - * \param[out] pdist distance of the nearest box in the indicated - * direction; 100000 if no box - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) For efficiency, use a LR/TD sorted %boxa, which can be - * made by flattening a 2D sorted boxaa. In that case, - * %range can be some positive integer like 50. - * (2) If boxes overlap, the distance will be < 0. Use %dist_select - * to determine if these should count or not. If L_ALL, then - * one box will match as the nearest to another in 2 or more - * directions. - *- */ -l_ok -boxaGetNearestByDirection(BOXA *boxa, - l_int32 i, - l_int32 dir, - l_int32 dist_select, - l_int32 range, - l_int32 *pindex, - l_int32 *pdist) -{ -l_int32 j, jmin, jmax, n, mindist, dist, index; -l_int32 x, y, w, h, bx, by, bw, bh; - - PROCNAME("boxaGetNearestByDirection"); - - if (pindex) *pindex = -1; - if (pdist) *pdist = 100000; - if (!pindex) - return ERROR_INT("&index not defined", procName, 1); - if (!pdist) - return ERROR_INT("&dist not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (dir != L_FROM_LEFT && dir != L_FROM_RIGHT && - dir != L_FROM_TOP && dir != L_FROM_BOT) - return ERROR_INT("invalid dir", procName, 1); - if (dist_select != L_NON_NEGATIVE && dist_select != L_ALL) - return ERROR_INT("invalid dist_select", procName, 1); - n = boxaGetCount(boxa); - if (i < 0 || i >= n) - return ERROR_INT("invalid box index", procName, 1); - - jmin = (range <= 0) ? 0 : L_MAX(0, i - range); - jmax = (range <= 0) ? n - 1 : L_MIN(n -1, i + range); - boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); - mindist = 100000; - index = -1; - if (dir == L_FROM_LEFT || dir == L_FROM_RIGHT) { - for (j = jmin; j <= jmax; j++) { - if (j == i) continue; - boxaGetBoxGeometry(boxa, j, &bx, &by, &bw, &bh); - if ((bx >= x && dir == L_FROM_LEFT) || /* not to the left */ - (x >= bx && dir == L_FROM_RIGHT)) /* not to the right */ - continue; - if (boxHasOverlapInXorY(y, h, by, bh) == 1) { - dist = boxGetDistanceInXorY(x, w, bx, bw); - if (dist_select == L_NON_NEGATIVE && dist < 0) continue; - if (dist < mindist) { - mindist = dist; - index = j; - } - } - } - } else if (dir == L_FROM_TOP || dir == L_FROM_BOT) { - for (j = jmin; j <= jmax; j++) { - if (j == i) continue; - boxaGetBoxGeometry(boxa, j, &bx, &by, &bw, &bh); - if ((by >= y && dir == L_FROM_TOP) || /* not above */ - (y >= by && dir == L_FROM_BOT)) /* not below */ - continue; - if (boxHasOverlapInXorY(x, w, bx, bw) == 1) { - dist = boxGetDistanceInXorY(y, h, by, bh); - if (dist_select == L_NON_NEGATIVE && dist < 0) continue; - if (dist < mindist) { - mindist = dist; - index = j; - } - } - } - } - *pindex = index; - *pdist = mindist; - return 0; -} - - -/*! - * \brief boxHasOverlapInXorY() - * - * \param[in] c1 left or top coordinate of box1 - * \param[in] s1 width or height of box1 - * \param[in] c2 left or top coordinate of box2 - * \param[in] s2 width or height of box2 - * \return 0 if no overlap; 1 if any overlap - * - *
- * Notes: - * (1) Like boxGetDistanceInXorY(), this is used for overlaps both in - * x (which projected vertically) and in y (projected horizontally) - *- */ -static l_int32 -boxHasOverlapInXorY(l_int32 c1, - l_int32 s1, - l_int32 c2, - l_int32 s2) -{ -l_int32 ovlp; - - if (c1 > c2) - ovlp = c2 + s2 - 1 - c1; - else - ovlp = c1 + s1 - 1 - c2; - return (ovlp < 0) ? 0 : 1; -} - - -/*! - * \brief boxGetDistanceInXorY() - * - * \param[in] c1 left or top coordinate of box1 - * \param[in] s1 width or height of box1 - * \param[in] c2 left or top coordinate of box2 - * \param[in] s2 width or height of box2 - * \return distance between them (if < 0, box2 overlaps box1 in the - * dimension considered) - */ -static l_int32 -boxGetDistanceInXorY(l_int32 c1, - l_int32 s1, - l_int32 c2, - l_int32 s2) -{ -l_int32 dist; - - if (c1 > c2) - dist = c1 - (c2 + s2 - 1); - else - dist = c2 - (c1 + s1 - 1); - return dist; -} - - -/*! - * \brief boxGetCenter() - * - * \param[in] box - * \param[out] pcx, pcy location of center of box - * \return 0 if OK, 1 on error or if box is not valid - */ -l_ok -boxGetCenter(BOX *box, - l_float32 *pcx, - l_float32 *pcy) -{ -l_int32 x, y, w, h; - - PROCNAME("boxGetCenter"); - - if (pcx) *pcx = 0; - if (pcy) *pcy = 0; - if (!pcx || !pcy) - return ERROR_INT("&cx, &cy not both defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - boxGetGeometry(box, &x, &y, &w, &h); - if (w == 0 || h == 0) return 1; - *pcx = (l_float32)(x + 0.5 * w); - *pcy = (l_float32)(y + 0.5 * h); - - return 0; -} - - -/*! - * \brief boxIntersectByLine() - * - * \param[in] box - * \param[in] x, y point that line goes through - * \param[in] slope of line - * \param[out] px1, py1 1st point of intersection with box - * \param[out] px2, py2 2nd point of intersection with box - * \param[out] pn number of points of intersection - * \return 0 if OK, 1 on error or if box is not valid - * - *
- * Notes: - * (1) If the intersection is at only one point (a corner), the - * coordinates are returned in (x1, y1). - * (2) Represent a vertical line by one with a large but finite slope. - *- */ -l_ok -boxIntersectByLine(BOX *box, - l_int32 x, - l_int32 y, - l_float32 slope, - l_int32 *px1, - l_int32 *py1, - l_int32 *px2, - l_int32 *py2, - l_int32 *pn) -{ -l_int32 bx, by, bw, bh, xp, yp, xt, yt, i, n; -l_float32 invslope; -PTA *pta; - - PROCNAME("boxIntersectByLine"); - - if (px1) *px1 = 0; - if (px2) *px2 = 0; - if (py1) *py1 = 0; - if (py2) *py2 = 0; - if (pn) *pn = 0; - if (!px1 || !py1 || !px2 || !py2) - return ERROR_INT("&x1, &y1, &x2, &y2 not all defined", procName, 1); - if (!pn) - return ERROR_INT("&n not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - boxGetGeometry(box, &bx, &by, &bw, &bh); - if (bw == 0 || bh == 0) return 1; - - if (slope == 0.0) { - if (y >= by && y < by + bh) { - *py1 = *py2 = y; - *px1 = bx; - *px2 = bx + bw - 1; - } - return 0; - } - - if (slope > 1000000.0) { - if (x >= bx && x < bx + bw) { - *px1 = *px2 = x; - *py1 = by; - *py2 = by + bh - 1; - } - return 0; - } - - /* Intersection with top and bottom lines of box */ - pta = ptaCreate(2); - invslope = 1.0 / slope; - xp = (l_int32)(x + invslope * (y - by)); - if (xp >= bx && xp < bx + bw) - ptaAddPt(pta, xp, by); - xp = (l_int32)(x + invslope * (y - by - bh + 1)); - if (xp >= bx && xp < bx + bw) - ptaAddPt(pta, xp, by + bh - 1); - - /* Intersection with left and right lines of box */ - yp = (l_int32)(y + slope * (x - bx)); - if (yp >= by && yp < by + bh) - ptaAddPt(pta, bx, yp); - yp = (l_int32)(y + slope * (x - bx - bw + 1)); - if (yp >= by && yp < by + bh) - ptaAddPt(pta, bx + bw - 1, yp); - - /* There is a maximum of 2 unique points; remove duplicates. */ - n = ptaGetCount(pta); - if (n > 0) { - ptaGetIPt(pta, 0, px1, py1); /* accept the first one */ - *pn = 1; - } - for (i = 1; i < n; i++) { - ptaGetIPt(pta, i, &xt, &yt); - if ((*px1 != xt) || (*py1 != yt)) { - *px2 = xt; - *py2 = yt; - *pn = 2; - break; - } - } - - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief boxClipToRectangle() - * - * \param[in] box - * \param[in] wi, hi rectangle representing image - * \return part of box within given rectangle, or NULL on error - * or if box is entirely outside the rectangle - * - *
- * Notes: - * (1) This can be used to clip a rectangle to an image. - * The clipping rectangle is assumed to have a UL corner at (0, 0), - * and a LR corner at (wi - 1, hi - 1). - *- */ -BOX * -boxClipToRectangle(BOX *box, - l_int32 wi, - l_int32 hi) -{ -BOX *boxd; - - PROCNAME("boxClipToRectangle"); - - if (!box) - return (BOX *)ERROR_PTR("box not defined", procName, NULL); - if (box->x >= wi || box->y >= hi || - box->x + box->w <= 0 || box->y + box->h <= 0) - return (BOX *)ERROR_PTR("box outside rectangle", procName, NULL); - - boxd = boxCopy(box); - if (boxd->x < 0) { - boxd->w += boxd->x; - boxd->x = 0; - } - if (boxd->y < 0) { - boxd->h += boxd->y; - boxd->y = 0; - } - if (boxd->x + boxd->w > wi) - boxd->w = wi - boxd->x; - if (boxd->y + boxd->h > hi) - boxd->h = hi - boxd->y; - return boxd; -} - - -/*! - * \brief boxClipToRectangleParams() - * - * \param[in] box [optional] requested box; can be null - * \param[in] w, h clipping box size; typ. the size of an image - * \param[out] pxstart start x coordinate - * \param[out] pystart start y coordinate - * \param[out] pxend one pixel beyond clipping box - * \param[out] pyend one pixel beyond clipping box - * \param[out] pbw [optional] clipped width - * \param[out] pbh [optional] clipped height - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) The return value should be checked. If it is 1, the - * returned parameter values are bogus. - * (2) This simplifies the selection of pixel locations within - * a given rectangle: - * for (i = ystart; i < yend; i++ { - * ... - * for (j = xstart; j < xend; j++ { - * .... - *- */ -l_ok -boxClipToRectangleParams(BOX *box, - l_int32 w, - l_int32 h, - l_int32 *pxstart, - l_int32 *pystart, - l_int32 *pxend, - l_int32 *pyend, - l_int32 *pbw, - l_int32 *pbh) -{ -l_int32 bw, bh; -BOX *boxc; - - PROCNAME("boxClipToRectangleParams"); - - if (pxstart) *pxstart = 0; - if (pystart) *pystart = 0; - if (pxend) *pxend = w; - if (pyend) *pyend = h; - if (pbw) *pbw = w; - if (pbh) *pbh = h; - if (!pxstart || !pystart || !pxend || !pyend) - return ERROR_INT("invalid ptr input", procName, 1); - if (!box) return 0; - - if ((boxc = boxClipToRectangle(box, w, h)) == NULL) - return ERROR_INT("box outside image", procName, 1); - boxGetGeometry(boxc, pxstart, pystart, &bw, &bh); - boxDestroy(&boxc); - - if (pbw) *pbw = bw; - if (pbh) *pbh = bh; - if (bw == 0 || bh == 0) - return ERROR_INT("invalid clipping box", procName, 1); - *pxend = *pxstart + bw; /* 1 past the end */ - *pyend = *pystart + bh; /* 1 past the end */ - return 0; -} - - -/*! - * \brief boxRelocateOneSide() - * - * \param[in] boxd [optional]; this can be null, equal to boxs, - * or different from boxs; - * \param[in] boxs starting box; to have one side relocated - * \param[in] loc new location of the side that is changing - * \param[in] sideflag L_FROM_LEFT, etc., indicating the side that moves - * \return boxd, or NULL on error or if the computed boxd has - * width or height <= 0. - * - *
- * Notes: - * (1) Set boxd == NULL to get new box; boxd == boxs for in-place; - * or otherwise to resize existing boxd. - * (2) For usage, suggest one of these: - * boxd = boxRelocateOneSide(NULL, boxs, ...); // new - * boxRelocateOneSide(boxs, boxs, ...); // in-place - * boxRelocateOneSide(boxd, boxs, ...); // other - *- */ -BOX * -boxRelocateOneSide(BOX *boxd, - BOX *boxs, - l_int32 loc, - l_int32 sideflag) -{ -l_int32 x, y, w, h; - - PROCNAME("boxRelocateOneSide"); - - if (!boxs) - return (BOX *)ERROR_PTR("boxs not defined", procName, NULL); - if (!boxd) - boxd = boxCopy(boxs); - - boxGetGeometry(boxs, &x, &y, &w, &h); - if (w == 0 || h == 0) - return boxd; - if (sideflag == L_FROM_LEFT) - boxSetGeometry(boxd, loc, -1, w + x - loc, -1); - else if (sideflag == L_FROM_RIGHT) - boxSetGeometry(boxd, -1, -1, loc - x + 1, -1); - else if (sideflag == L_FROM_TOP) - boxSetGeometry(boxd, -1, loc, -1, h + y - loc); - else if (sideflag == L_FROM_BOT) - boxSetGeometry(boxd, -1, -1, -1, loc - y + 1); - return boxd; -} - - -/*! - * \brief boxaAdjustSides() - * - * \param[in] boxas - * \param[in] delleft, delright, deltop, delbot changes in location of - * each side for each box - * \return boxad, or NULL on error - * - *
- * Notes: - * (1) New box dimensions are cropped at left and top to x >= 0 and y >= 0. - * (2) If the width or height of a box goes to 0, we generate a box with - * w == 1 and h == 1, as a placeholder. - * (3) See boxAdjustSides(). - *- */ -BOXA * -boxaAdjustSides(BOXA *boxas, - l_int32 delleft, - l_int32 delright, - l_int32 deltop, - l_int32 delbot) -{ -l_int32 n, i, x, y; -BOX *box1, *box2; -BOXA *boxad; - - PROCNAME("boxaAdjustSides"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - - n = boxaGetCount(boxas); - boxad = boxaCreate(n); - for (i = 0; i < n; i++) { - box1 = boxaGetBox(boxas, i, L_COPY); - box2 = boxAdjustSides(NULL, box1, delleft, delright, deltop, delbot); - if (!box2) { - boxGetGeometry(box1, &x, &y, NULL, NULL); - box2 = boxCreate(x, y, 1, 1); - } - boxaAddBox(boxad, box2, L_INSERT); - boxDestroy(&box1); - } - - return boxad; -} - - -/*! - * \brief boxaAdjustBoxSides() - * - * \param[in] boxas - * \param[in] index - * \param[in] delleft, delright, deltop, delbot changes to box side locs - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) In-place operation on a box in a boxa. - * (2) New box dimensions are cropped at left and top to x >= 0 and y >= 0. - * (3) If a box ends up with no area, an error message is emitted, - * but the box dimensions are not changed. - * (4) See boxaAdjustSides(). - *- */ -l_ok -boxaAdjustBoxSides(BOXA *boxa, - l_int32 index, - l_int32 delleft, - l_int32 delright, - l_int32 deltop, - l_int32 delbot) -{ -BOX *box; - - PROCNAME("boxaAdjustBoxSides"); - - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - if ((box = boxaGetBox(boxa, index, L_CLONE)) == NULL) - return ERROR_INT("invalid index", procName, 1); - - boxAdjustSides(box, box, delleft, delright, deltop, delbot); - boxDestroy(&box); /* the clone */ - return 0; -} - - -/*! - * \brief boxAdjustSides() - * - * \param[in] boxd [optional]; this can be null, equal to boxs, - * or different from boxs - * \param[in] boxs starting box; to have sides adjusted - * \param[in] delleft, delright, deltop, delbot changes in location - * of each side - * \return boxd, or NULL on error or if the computed boxd has - * width or height <= 0. - * - *
- * Notes: - * (1) Set boxd == NULL to get new box; boxd == boxs for in-place; - * or otherwise to resize existing boxd. - * (2) For usage, suggest one of these: - * boxd = boxAdjustSides(NULL, boxs, ...); // new - * boxAdjustSides(boxs, boxs, ...); // in-place - * boxAdjustSides(boxd, boxs, ...); // other - * (3) New box dimensions are cropped at left and top to x >= 0 and y >= 0. - * (4) For example, to expand in-place by 20 pixels on each side, use - * boxAdjustSides(box, box, -20, 20, -20, 20); - *- */ -BOX * -boxAdjustSides(BOX *boxd, - BOX *boxs, - l_int32 delleft, - l_int32 delright, - l_int32 deltop, - l_int32 delbot) -{ -l_int32 x, y, w, h, xl, xr, yt, yb, wnew, hnew; - - PROCNAME("boxAdjustSides"); - - if (!boxs) - return (BOX *)ERROR_PTR("boxs not defined", procName, NULL); - - boxGetGeometry(boxs, &x, &y, &w, &h); - xl = L_MAX(0, x + delleft); - yt = L_MAX(0, y + deltop); - xr = x + w + delright; /* one pixel beyond right edge */ - yb = y + h + delbot; /* one pixel below bottom edge */ - wnew = xr - xl; - hnew = yb - yt; - - if (wnew < 1 || hnew < 1) - return (BOX *)ERROR_PTR("boxd has 0 area", procName, NULL); - if (!boxd) - return boxCreate(xl, yt, wnew, hnew); - - boxSetGeometry(boxd, xl, yt, wnew, hnew); - return boxd; -} - - -/*! - * \brief boxaSetSide() - * - * \param[in] boxad use NULL to get a new one; same as boxas for in-place - * \param[in] boxas - * \param[in] side L_SET_LEFT, L_SET_RIGHT, L_SET_TOP, L_SET_BOT - * \param[in] val location to set for given side, for each box - * \param[in] thresh min abs difference to cause resetting to %val - * \return boxad, or NULL on error - * - *
- * Notes: - * (1) Sets the given side of each box. Use boxad == NULL for a new - * boxa, and boxad == boxas for in-place. - * (2) Use one of these: - * boxad = boxaSetSide(NULL, boxas, ...); // new - * boxaSetSide(boxas, boxas, ...); // in-place - *- */ -BOXA * -boxaSetSide(BOXA *boxad, - BOXA *boxas, - l_int32 side, - l_int32 val, - l_int32 thresh) -{ -l_int32 n, i; -BOX *box; - - PROCNAME("boxaSetSide"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (boxad && (boxas != boxad)) - return (BOXA *)ERROR_PTR("not in-place", procName, NULL); - if (side != L_SET_LEFT && side != L_SET_RIGHT && - side != L_SET_TOP && side != L_SET_BOT) - return (BOXA *)ERROR_PTR("invalid side", procName, NULL); - if (val < 0) - return (BOXA *)ERROR_PTR("val < 0", procName, NULL); - - if (!boxad) - boxad = boxaCopy(boxas, L_COPY); - n = boxaGetCount(boxad); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxad, i, L_CLONE); - boxSetSide(box, side, val, thresh); - boxDestroy(&box); /* the clone */ - } - - return boxad; -} - - -/*! - * \brief boxSetSide() - * - * \param[in] boxs - * \param[in] side L_SET_LEFT, L_SET_RIGHT, L_SET_TOP, L_SET_BOT - * \param[in] val location to set for given side, for each box - * \param[in] thresh min abs difference to cause resetting to %val - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) In-place operation. - * (2) Use %thresh = 0 to definitely set the side to %val. - *- */ -l_ok -boxSetSide(BOX *boxs, - l_int32 side, - l_int32 val, - l_int32 thresh) -{ -l_int32 x, y, w, h, diff; - - PROCNAME("boxSetSide"); - - if (!boxs) - return ERROR_INT("box not defined", procName, 1); - if (side != L_SET_LEFT && side != L_SET_RIGHT && - side != L_SET_TOP && side != L_SET_BOT) - return ERROR_INT("invalid side", procName, 1); - if (val < 0) - return ERROR_INT("val < 0", procName, 1); - - boxGetGeometry(boxs, &x, &y, &w, &h); - if (side == L_SET_LEFT) { - diff = x - val; - if (L_ABS(diff) >= thresh) - boxSetGeometry(boxs, val, y, w + diff, h); - } else if (side == L_SET_RIGHT) { - diff = x + w -1 - val; - if (L_ABS(diff) >= thresh) - boxSetGeometry(boxs, x, y, val - x + 1, h); - } else if (side == L_SET_TOP) { - diff = y - val; - if (L_ABS(diff) >= thresh) - boxSetGeometry(boxs, x, val, w, h + diff); - } else { /* side == L_SET_BOT */ - diff = y + h - 1 - val; - if (L_ABS(diff) >= thresh) - boxSetGeometry(boxs, x, y, w, val - y + 1); - } - - return 0; -} - - -/*! - * \brief boxaAdjustWidthToTarget() - * - * \param[in] boxad use NULL to get a new one; same as boxas for in-place - * \param[in] boxas - * \param[in] sides L_ADJUST_LEFT, L_ADJUST_RIGHT, L_ADJUST_LEFT_AND_RIGHT - * \param[in] target target width if differs by more than thresh - * \param[in] thresh min abs difference in width to cause adjustment - * \return boxad, or NULL on error - * - *
- * Notes: - * (1) Conditionally adjusts the width of each box, by moving - * the indicated edges (left and/or right) if the width differs - * by %thresh or more from %target. - * (2) Use boxad == NULL for a new boxa, and boxad == boxas for in-place. - * Use one of these: - * boxad = boxaAdjustWidthToTarget(NULL, boxas, ...); // new - * boxaAdjustWidthToTarget(boxas, boxas, ...); // in-place - *- */ -BOXA * -boxaAdjustWidthToTarget(BOXA *boxad, - BOXA *boxas, - l_int32 sides, - l_int32 target, - l_int32 thresh) -{ -l_int32 x, y, w, h, n, i, diff; -BOX *box; - - PROCNAME("boxaAdjustWidthToTarget"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (boxad && (boxas != boxad)) - return (BOXA *)ERROR_PTR("not in-place", procName, NULL); - if (sides != L_ADJUST_LEFT && sides != L_ADJUST_RIGHT && - sides != L_ADJUST_LEFT_AND_RIGHT) - return (BOXA *)ERROR_PTR("invalid sides", procName, NULL); - if (target < 1) - return (BOXA *)ERROR_PTR("target < 1", procName, NULL); - - if (!boxad) - boxad = boxaCopy(boxas, L_COPY); - n = boxaGetCount(boxad); - for (i = 0; i < n; i++) { - if ((box = boxaGetValidBox(boxad, i, L_CLONE)) == NULL) - continue; - boxGetGeometry(box, &x, &y, &w, &h); - diff = w - target; - if (sides == L_ADJUST_LEFT) { - if (L_ABS(diff) >= thresh) - boxSetGeometry(box, L_MAX(0, x + diff), y, target, h); - } else if (sides == L_ADJUST_RIGHT) { - if (L_ABS(diff) >= thresh) - boxSetGeometry(box, x, y, target, h); - } else { /* sides == L_ADJUST_LEFT_AND_RIGHT */ - if (L_ABS(diff) >= thresh) - boxSetGeometry(box, L_MAX(0, x + diff/2), y, target, h); - } - boxDestroy(&box); - } - - return boxad; -} - - -/*! - * \brief boxaAdjustHeightToTarget() - * - * \param[in] boxad use NULL to get a new one - * \param[in] boxas - * \param[in] sides L_ADJUST_TOP, L_ADJUST_BOT, L_ADJUST_TOP_AND_BOT - * \param[in] target target height if differs by more than thresh - * \param[in] thresh min abs difference in height to cause adjustment - * \return boxad, or NULL on error - * - *
- * Notes: - * (1) Conditionally adjusts the height of each box, by moving - * the indicated edges (top and/or bot) if the height differs - * by %thresh or more from %target. - * (2) Use boxad == NULL for a new boxa, and boxad == boxas for in-place. - * Use one of these: - * boxad = boxaAdjustHeightToTarget(NULL, boxas, ...); // new - * boxaAdjustHeightToTarget(boxas, boxas, ...); // in-place - *- */ -BOXA * -boxaAdjustHeightToTarget(BOXA *boxad, - BOXA *boxas, - l_int32 sides, - l_int32 target, - l_int32 thresh) -{ -l_int32 x, y, w, h, n, i, diff; -BOX *box; - - PROCNAME("boxaAdjustHeightToTarget"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (boxad && (boxas != boxad)) - return (BOXA *)ERROR_PTR("not in-place", procName, NULL); - if (sides != L_ADJUST_TOP && sides != L_ADJUST_BOT && - sides != L_ADJUST_TOP_AND_BOT) - return (BOXA *)ERROR_PTR("invalid sides", procName, NULL); - if (target < 1) - return (BOXA *)ERROR_PTR("target < 1", procName, NULL); - - if (!boxad) - boxad = boxaCopy(boxas, L_COPY); - n = boxaGetCount(boxad); - for (i = 0; i < n; i++) { - if ((box = boxaGetValidBox(boxad, i, L_CLONE)) == NULL) - continue; - boxGetGeometry(box, &x, &y, &w, &h); - diff = h - target; - if (sides == L_ADJUST_TOP) { - if (L_ABS(diff) >= thresh) - boxSetGeometry(box, x, L_MAX(0, y + diff), w, target); - } else if (sides == L_ADJUST_BOT) { - if (L_ABS(diff) >= thresh) - boxSetGeometry(box, x, y, w, target); - } else { /* sides == L_ADJUST_TOP_AND_BOT */ - if (L_ABS(diff) >= thresh) - boxSetGeometry(box, x, L_MAX(0, y + diff/2), w, target); - } - boxDestroy(&box); - } - - return boxad; -} - - -/*! - * \brief boxEqual() - * - * \param[in] box1 - * \param[in] box2 - * \param[out] psame 1 if equal; 0 otherwise - * \return 0 if OK, 1 on error - */ -l_ok -boxEqual(BOX *box1, - BOX *box2, - l_int32 *psame) -{ - PROCNAME("boxEqual"); - - if (!psame) - return ERROR_INT("&same not defined", procName, 1); - *psame = 0; - if (!box1 || !box2) - return ERROR_INT("boxes not both defined", procName, 1); - if (box1->x == box2->x && box1->y == box2->y && - box1->w == box2->w && box1->h == box2->h) - *psame = 1; - return 0; -} - - -/*! - * \brief boxaEqual() - * - * \param[in] boxa1 - * \param[in] boxa2 - * \param[in] maxdist - * \param[out] pnaindex [optional] index array of correspondences - * \param[out] psame 1 if equal; 0 otherwise - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The two boxa are the "same" if they contain the same - * boxes and each box is within %maxdist of its counterpart - * in their positions within the boxa. This allows for - * small rearrangements. Use 0 for maxdist if the boxa - * must be identical. - * (2) This applies only to geometry and ordering; refcounts - * are not considered. - * (3) %maxdist allows some latitude in the ordering of the boxes. - * For the boxa to be the "same", corresponding boxes must - * be within %maxdist of each other. Note that for large - * %maxdist, we should use a hash function for efficiency. - * (4) naindex[i] gives the position of the box in boxa2 that - * corresponds to box i in boxa1. It is only returned if the - * boxa are equal. - *- */ -l_ok -boxaEqual(BOXA *boxa1, - BOXA *boxa2, - l_int32 maxdist, - NUMA **pnaindex, - l_int32 *psame) -{ -l_int32 i, j, n, jstart, jend, found, samebox; -l_int32 *countarray; -BOX *box1, *box2; -NUMA *na; - - PROCNAME("boxaEqual"); - - if (pnaindex) *pnaindex = NULL; - if (!psame) - return ERROR_INT("&same not defined", procName, 1); - *psame = 0; - if (!boxa1 || !boxa2) - return ERROR_INT("boxa1 and boxa2 not both defined", procName, 1); - n = boxaGetCount(boxa1); - if (n != boxaGetCount(boxa2)) - return 0; - - if ((countarray = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32))) == NULL) - return ERROR_INT("calloc fail for countarray", procName, 1); - na = numaMakeConstant(0.0, n); - - for (i = 0; i < n; i++) { - box1 = boxaGetBox(boxa1, i, L_CLONE); - jstart = L_MAX(0, i - maxdist); - jend = L_MIN(n-1, i + maxdist); - found = FALSE; - for (j = jstart; j <= jend; j++) { - box2 = boxaGetBox(boxa2, j, L_CLONE); - boxEqual(box1, box2, &samebox); - if (samebox && countarray[j] == 0) { - countarray[j] = 1; - numaReplaceNumber(na, i, j); - found = TRUE; - boxDestroy(&box2); - break; - } - boxDestroy(&box2); - } - boxDestroy(&box1); - if (!found) { - numaDestroy(&na); - LEPT_FREE(countarray); - return 0; - } - } - - *psame = 1; - if (pnaindex) - *pnaindex = na; - else - numaDestroy(&na); - LEPT_FREE(countarray); - return 0; -} - - -/*! - * \brief boxSimilar() - * - * \param[in] box1 - * \param[in] box2 - * \param[in] leftdiff, rightdiff, topdiff, botdiff - * \param[out] psimilar 1 if similar; 0 otherwise - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The values of leftdiff (etc) are the maximum allowed deviations - * between the locations of the left (etc) sides. If any side - * pairs differ by more than this amount, the boxes are not similar. - *- */ -l_ok -boxSimilar(BOX *box1, - BOX *box2, - l_int32 leftdiff, - l_int32 rightdiff, - l_int32 topdiff, - l_int32 botdiff, - l_int32 *psimilar) -{ -l_int32 l1, l2, r1, r2, t1, t2, b1, b2, valid1, valid2; - - PROCNAME("boxSimilar"); - - if (!psimilar) - return ERROR_INT("&similar not defined", procName, 1); - *psimilar = 0; - if (!box1 || !box2) - return ERROR_INT("boxes not both defined", procName, 1); - boxIsValid(box1, &valid1); - boxIsValid(box2, &valid2); - if (!valid1 || !valid2) - return ERROR_INT("boxes not both valid", procName, 1); - - boxGetSideLocations(box1, &l1, &r1, &t1, &b1); - boxGetSideLocations(box2, &l2, &r2, &t2, &b2); - if (L_ABS(l1 - l2) > leftdiff) - return 0; - if (L_ABS(r1 - r2) > rightdiff) - return 0; - if (L_ABS(t1 - t2) > topdiff) - return 0; - if (L_ABS(b1 - b2) > botdiff) - return 0; - - *psimilar = 1; - return 0; -} - - -/*! - * \brief boxaSimilar() - * - * \param[in] boxa1 - * \param[in] boxa2 - * \param[in] leftdiff, rightdiff, topdiff, botdiff - * \param[in] debug output details of non-similar boxes - * \param[out] psimilar 1 if similar; 0 otherwise - * \param[out] pnasim [optional] na containing 1 if similar; else 0 - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) See boxSimilar() for parameter usage. - * (2) Corresponding boxes are taken in order in the two boxa. - * (3) %nasim is an indicator array with a (0/1) for each box pair. - * (4) With %nasim or debug == 1, boxes continue to be tested - * after failure. - *- */ -l_ok -boxaSimilar(BOXA *boxa1, - BOXA *boxa2, - l_int32 leftdiff, - l_int32 rightdiff, - l_int32 topdiff, - l_int32 botdiff, - l_int32 debug, - l_int32 *psimilar, - NUMA **pnasim) -{ -l_int32 i, n1, n2, match, mismatch; -BOX *box1, *box2; - - PROCNAME("boxaSimilar"); - - if (psimilar) *psimilar = 0; - if (pnasim) *pnasim = NULL; - if (!boxa1 || !boxa2) - return ERROR_INT("boxa1 and boxa2 not both defined", procName, 1); - if (!psimilar) - return ERROR_INT("&similar not defined", procName, 1); - n1 = boxaGetCount(boxa1); - n2 = boxaGetCount(boxa2); - if (n1 != n2) { - L_ERROR("boxa counts differ: %d vs %d\n", procName, n1, n2); - return 1; - } - if (pnasim) *pnasim = numaCreate(n1); - - mismatch = FALSE; - for (i = 0; i < n1; i++) { - box1 = boxaGetBox(boxa1, i, L_CLONE); - box2 = boxaGetBox(boxa2, i, L_CLONE); - boxSimilar(box1, box2, leftdiff, rightdiff, topdiff, botdiff, - &match); - boxDestroy(&box1); - boxDestroy(&box2); - if (pnasim) - numaAddNumber(*pnasim, match); - if (!match) { - mismatch = TRUE; - if (!debug && pnasim == NULL) - return 0; - else if (debug) - L_INFO("box %d not similar\n", procName, i); - } - } - - if (!mismatch) *psimilar = 1; - return 0; -} - - -/*----------------------------------------------------------------------* - * Boxa combine and split * - *----------------------------------------------------------------------*/ -/*! - * \brief boxaJoin() - * - * \param[in] boxad dest boxa; add to this one - * \param[in] boxas source boxa; add from this one - * \param[in] istart starting index in boxas - * \param[in] iend ending index in boxas; use -1 to cat all - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This appends a clone of each indicated box in boxas to boxad - * (2) istart < 0 is taken to mean 'read from the start' (istart = 0) - * (3) iend < 0 means 'read to the end' - * (4) if boxas == NULL or has no boxes, this is a no-op. - *- */ -l_ok -boxaJoin(BOXA *boxad, - BOXA *boxas, - l_int32 istart, - l_int32 iend) -{ -l_int32 n, i; -BOX *box; - - PROCNAME("boxaJoin"); - - if (!boxad) - return ERROR_INT("boxad not defined", procName, 1); - if (!boxas || ((n = boxaGetCount(boxas)) == 0)) - return 0; - - if (istart < 0) - istart = 0; - if (iend < 0 || iend >= n) - iend = n - 1; - if (istart > iend) - return ERROR_INT("istart > iend; nothing to add", procName, 1); - - for (i = istart; i <= iend; i++) { - box = boxaGetBox(boxas, i, L_CLONE); - boxaAddBox(boxad, box, L_INSERT); - } - - return 0; -} - - -/*! - * \brief boxaaJoin() - * - * \param[in] baad dest boxaa; add to this one - * \param[in] baas source boxaa; add from this one - * \param[in] istart starting index in baas - * \param[in] iend ending index in baas; use -1 to cat all - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This appends a clone of each indicated boxa in baas to baad - * (2) istart < 0 is taken to mean 'read from the start' (istart = 0) - * (3) iend < 0 means 'read to the end' - * (4) if baas == NULL, this is a no-op. - *- */ -l_ok -boxaaJoin(BOXAA *baad, - BOXAA *baas, - l_int32 istart, - l_int32 iend) -{ -l_int32 n, i; -BOXA *boxa; - - PROCNAME("boxaaJoin"); - - if (!baad) - return ERROR_INT("baad not defined", procName, 1); - if (!baas) - return 0; - - if (istart < 0) - istart = 0; - n = boxaaGetCount(baas); - if (iend < 0 || iend >= n) - iend = n - 1; - if (istart > iend) - return ERROR_INT("istart > iend; nothing to add", procName, 1); - - for (i = istart; i <= iend; i++) { - boxa = boxaaGetBoxa(baas, i, L_CLONE); - boxaaAddBoxa(baad, boxa, L_INSERT); - } - - return 0; -} - - -/*! - * \brief boxaSplitEvenOdd() - * - * \param[in] boxa - * \param[in] fillflag 1 to put invalid boxes in place; 0 to omit - * \param[out] pboxae, pboxao save even and odd boxes in their separate - * boxa, setting the other type to invalid boxes. - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) If %fillflag == 1, boxae has copies of the even boxes - * in their original location, and nvalid boxes are placed - * in the odd array locations. And v.v. - * (2) If %fillflag == 0, boxae has only copies of the even boxes. - *- */ -l_ok -boxaSplitEvenOdd(BOXA *boxa, - l_int32 fillflag, - BOXA **pboxae, - BOXA **pboxao) -{ -l_int32 i, n; -BOX *box, *box1; - - PROCNAME("boxaSplitEvenOdd"); - - if (pboxae) *pboxae = NULL; - if (pboxao) *pboxao = NULL; - if (!pboxae || !pboxao) - return ERROR_INT("&boxae and &boxao not both defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - n = boxaGetCount(boxa); - *pboxae = boxaCreate(n); - *pboxao = boxaCreate(n); - if (fillflag == 0) { - /* don't fill with invalid boxes; end up with half-size boxa */ - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_COPY); - if ((i & 1) == 0) - boxaAddBox(*pboxae, box, L_INSERT); - else - boxaAddBox(*pboxao, box, L_INSERT); - } - } else { - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_COPY); - box1 = boxCreate(0, 0, 0, 0); /* empty placeholder */ - if ((i & 1) == 0) { - boxaAddBox(*pboxae, box, L_INSERT); - boxaAddBox(*pboxao, box1, L_INSERT); - } else { - boxaAddBox(*pboxae, box1, L_INSERT); - boxaAddBox(*pboxao, box, L_INSERT); - } - } - } - return 0; -} - - -/*! - * \brief boxaMergeEvenOdd() - * - * \param[in] boxae boxes to go in even positions in merged boxa - * \param[in] boxao boxes to go in odd positions in merged boxa - * \param[in] fillflag 1 if there are invalid boxes in placeholders - * \return boxad merged, or NULL on error - * - *
- * Notes: - * (1) This is essentially the inverse of boxaSplitEvenOdd(). - * Typically, boxae and boxao were generated by boxaSplitEvenOdd(), - * and the value of %fillflag needs to be the same in both calls. - * (2) If %fillflag == 1, both boxae and boxao are of the same size; - * otherwise boxae may have one more box than boxao. - *- */ -BOXA * -boxaMergeEvenOdd(BOXA *boxae, - BOXA *boxao, - l_int32 fillflag) -{ -l_int32 i, n, ne, no; -BOX *box; -BOXA *boxad; - - PROCNAME("boxaMergeEvenOdd"); - - if (!boxae || !boxao) - return (BOXA *)ERROR_PTR("boxae and boxao not defined", procName, NULL); - ne = boxaGetCount(boxae); - no = boxaGetCount(boxao); - if (ne < no || ne > no + 1) - return (BOXA *)ERROR_PTR("boxa sizes invalid", procName, NULL); - - boxad = boxaCreate(ne); - if (fillflag == 0) { /* both are approx. half-sized; all valid boxes */ - n = ne + no; - for (i = 0; i < n; i++) { - if ((i & 1) == 0) - box = boxaGetBox(boxae, i / 2, L_COPY); - else - box = boxaGetBox(boxao, i / 2, L_COPY); - boxaAddBox(boxad, box, L_INSERT); - } - } else { /* both are full size and have invalid placeholders */ - for (i = 0; i < ne; i++) { - if ((i & 1) == 0) - box = boxaGetBox(boxae, i, L_COPY); - else - box = boxaGetBox(boxao, i, L_COPY); - boxaAddBox(boxad, box, L_INSERT); - } - } - return boxad; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxfunc2.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxfunc2.c deleted file mode 100644 index 98f2808a..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxfunc2.c +++ /dev/null @@ -1,1933 +0,0 @@ -/*====================================================================* - - 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 boxfunc2.c - *
- * - * Boxa/Box transform (shift, scale) and orthogonal rotation - * BOXA *boxaTransform() - * BOX *boxTransform() - * BOXA *boxaTransformOrdered() - * BOX *boxTransformOrdered() - * BOXA *boxaRotateOrth() - * BOX *boxRotateOrth() - * BOXA *boxaShiftWithPta() - * - * Boxa sort - * BOXA *boxaSort() - * BOXA *boxaBinSort() - * BOXA *boxaSortByIndex() - * BOXAA *boxaSort2d() - * BOXAA *boxaSort2dByIndex() - * - * Boxa statistics - * l_int32 boxaGetRankVals() - * l_int32 boxaGetMedianVals() - * l_int32 boxaGetAverageSize() - * - * Boxa array extraction - * l_int32 boxaExtractAsNuma() - * l_int32 boxaExtractAsPta() - * PTA *boxaExtractCorners() - * - * Other Boxaa functions - * l_int32 boxaaGetExtent() - * BOXA *boxaaFlattenToBoxa() - * BOXA *boxaaFlattenAligned() - * BOXAA *boxaEncapsulateAligned() - * BOXAA *boxaaTranspose() - * l_int32 boxaaAlignBox() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This is a very simple function that first shifts, then scales. - * (2) The UL corner coordinates of all boxes in the output %boxad - * (3) For the boxes in the output %boxad, the UL corner coordinates - * must be non-negative, and the width and height of valid - * boxes must be at least 1. - *- */ -BOXA * -boxaTransform(BOXA *boxas, - l_int32 shiftx, - l_int32 shifty, - l_float32 scalex, - l_float32 scaley) -{ -l_int32 i, n; -BOX *boxs, *boxd; -BOXA *boxad; - - PROCNAME("boxaTransform"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - n = boxaGetCount(boxas); - if ((boxad = boxaCreate(n)) == NULL) - return (BOXA *)ERROR_PTR("boxad not made", procName, NULL); - for (i = 0; i < n; i++) { - if ((boxs = boxaGetBox(boxas, i, L_CLONE)) == NULL) { - boxaDestroy(&boxad); - return (BOXA *)ERROR_PTR("boxs not found", procName, NULL); - } - boxd = boxTransform(boxs, shiftx, shifty, scalex, scaley); - boxDestroy(&boxs); - boxaAddBox(boxad, boxd, L_INSERT); - } - - return boxad; -} - - -/*! - * \brief boxTransform() - * - * \param[in] box - * \param[in] shiftx - * \param[in] shifty - * \param[in] scalex - * \param[in] scaley - * \return boxd, or NULL on error - * - *
- * Notes: - * (1) This is a very simple function that first shifts, then scales. - * (2) If the box is invalid, a new invalid box is returned. - * (3) The UL corner coordinates must be non-negative, and the - * width and height of valid boxes must be at least 1. - *- */ -BOX * -boxTransform(BOX *box, - l_int32 shiftx, - l_int32 shifty, - l_float32 scalex, - l_float32 scaley) -{ - PROCNAME("boxTransform"); - - if (!box) - return (BOX *)ERROR_PTR("box not defined", procName, NULL); - if (box->w <= 0 || box->h <= 0) - return boxCreate(0, 0, 0, 0); - else - return boxCreate((l_int32)(L_MAX(0, scalex * (box->x + shiftx) + 0.5)), - (l_int32)(L_MAX(0, scaley * (box->y + shifty) + 0.5)), - (l_int32)(L_MAX(1.0, scalex * box->w + 0.5)), - (l_int32)(L_MAX(1.0, scaley * box->h + 0.5))); -} - - -/*! - * \brief boxaTransformOrdered() - * - * \param[in] boxas - * \param[in] shiftx - * \param[in] shifty - * \param[in] scalex - * \param[in] scaley - * \param[in] xcen, ycen center of rotation - * \param[in] angle in radians; clockwise is positive - * \param[in] order one of 6 combinations: L_TR_SC_RO, ... - * \return boxd, or NULL on error - * - *
- * shift, scaling and rotation, and the order of the - * transforms is specified. - * (2) Although these operations appear to be on an infinite - * 2D plane, in practice the region of interest is clipped - * to a finite image. The center of rotation is usually taken - * with respect to the image (either the UL corner or the - * center). A translation can have two very different effects: - * (a) Moves the boxes across the fixed image region. - * (b) Moves the image origin, causing a change in the image - * region and an opposite effective translation of the boxes. - * This function should only be used for (a), where the image - * region is fixed on translation. If the image region is - * changed by the translation, use instead the functions - * in affinecompose.c, where the image region and rotation - * center can be computed from the actual clipping due to - * translation of the image origin. - * (3) See boxTransformOrdered() for usage and implementation details. - *- */ -BOXA * -boxaTransformOrdered(BOXA *boxas, - l_int32 shiftx, - l_int32 shifty, - l_float32 scalex, - l_float32 scaley, - l_int32 xcen, - l_int32 ycen, - l_float32 angle, - l_int32 order) -{ -l_int32 i, n; -BOX *boxs, *boxd; -BOXA *boxad; - - PROCNAME("boxaTransformOrdered"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - n = boxaGetCount(boxas); - if ((boxad = boxaCreate(n)) == NULL) - return (BOXA *)ERROR_PTR("boxad not made", procName, NULL); - for (i = 0; i < n; i++) { - if ((boxs = boxaGetBox(boxas, i, L_CLONE)) == NULL) { - boxaDestroy(&boxad); - return (BOXA *)ERROR_PTR("boxs not found", procName, NULL); - } - boxd = boxTransformOrdered(boxs, shiftx, shifty, scalex, scaley, - xcen, ycen, angle, order); - boxDestroy(&boxs); - boxaAddBox(boxad, boxd, L_INSERT); - } - - return boxad; -} - - -/*! - * \brief boxTransformOrdered() - * - * \param[in] boxs - * \param[in] shiftx - * \param[in] shifty - * \param[in] scalex - * \param[in] scaley - * \param[in] xcen, ycen center of rotation - * \param[in] angle in radians; clockwise is positive - * \param[in] order one of 6 combinations: L_TR_SC_RO, ... - * \return boxd, or NULL on error - * - *
- * Notes: - * (1) This allows a sequence of linear transforms, composed of - * shift, scaling and rotation, where the order of the - * transforms is specified. - * (2) The rotation is taken about a point specified by (xcen, ycen). - * Let the components of the vector from the center of rotation - * to the box center be (xdif, ydif): - * xdif = (bx + 0.5 * bw) - xcen - * ydif = (by + 0.5 * bh) - ycen - * Then the box center after rotation has new components: - * bxcen = xcen + xdif * cosa + ydif * sina - * bycen = ycen + ydif * cosa - xdif * sina - * where cosa and sina are the cos and sin of the angle, - * and the enclosing box for the rotated box has size: - * rw = |bw * cosa| + |bh * sina| - * rh = |bh * cosa| + |bw * sina| - * where bw and bh are the unrotated width and height. - * Then the box UL corner (rx, ry) is - * rx = bxcen - 0.5 * rw - * ry = bycen - 0.5 * rh - * (3) The center of rotation specified by args %xcen and %ycen - * is the point BEFORE any translation or scaling. If the - * rotation is not the first operation, this function finds - * the actual center at the time of rotation. It does this - * by making the following assumptions: - * (1) Any scaling is with respect to the UL corner, so - * that the center location scales accordingly. - * (2) A translation does not affect the center of - * the image; it just moves the boxes. - * We always use assumption (1). However, assumption (2) - * will be incorrect if the apparent translation is due - * to a clipping operation that, in effect, moves the - * origin of the image. In that case, you should NOT use - * these simple functions. Instead, use the functions - * in affinecompose.c, where the rotation center can be - * computed from the actual clipping due to translation - * of the image origin. - *- */ -BOX * -boxTransformOrdered(BOX *boxs, - l_int32 shiftx, - l_int32 shifty, - l_float32 scalex, - l_float32 scaley, - l_int32 xcen, - l_int32 ycen, - l_float32 angle, - l_int32 order) -{ -l_int32 bx, by, bw, bh, tx, ty, tw, th; -l_int32 xcent, ycent; /* transformed center of rotation due to scaling */ -l_float32 sina, cosa, xdif, ydif, rx, ry, rw, rh; -BOX *boxd; - - PROCNAME("boxTransformOrdered"); - - if (!boxs) - return (BOX *)ERROR_PTR("boxs not defined", procName, NULL); - if (order != L_TR_SC_RO && order != L_SC_RO_TR && order != L_RO_TR_SC && - order != L_TR_RO_SC && order != L_RO_SC_TR && order != L_SC_TR_RO) - return (BOX *)ERROR_PTR("order invalid", procName, NULL); - - boxGetGeometry(boxs, &bx, &by, &bw, &bh); - if (bw <= 0 || bh <= 0) /* invalid */ - return boxCreate(0, 0, 0, 0); - if (angle != 0.0) { - sina = sin(angle); - cosa = cos(angle); - } - - if (order == L_TR_SC_RO) { - tx = (l_int32)(scalex * (bx + shiftx) + 0.5); - ty = (l_int32)(scaley * (by + shifty) + 0.5); - tw = (l_int32)(L_MAX(1.0, scalex * bw + 0.5)); - th = (l_int32)(L_MAX(1.0, scaley * bh + 0.5)); - xcent = (l_int32)(scalex * xcen + 0.5); - ycent = (l_int32)(scaley * ycen + 0.5); - if (angle == 0.0) { - boxd = boxCreate(tx, ty, tw, th); - } else { - xdif = tx + 0.5 * tw - xcent; - ydif = ty + 0.5 * th - ycent; - rw = L_ABS(tw * cosa) + L_ABS(th * sina); - rh = L_ABS(th * cosa) + L_ABS(tw * sina); - rx = xcent + xdif * cosa - ydif * sina - 0.5 * rw; - ry = ycent + ydif * cosa + xdif * sina - 0.5 * rh; - boxd = boxCreate((l_int32)rx, (l_int32)ry, (l_int32)rw, - (l_int32)rh); - } - } else if (order == L_SC_TR_RO) { - tx = (l_int32)(scalex * bx + shiftx + 0.5); - ty = (l_int32)(scaley * by + shifty + 0.5); - tw = (l_int32)(L_MAX(1.0, scalex * bw + 0.5)); - th = (l_int32)(L_MAX(1.0, scaley * bh + 0.5)); - xcent = (l_int32)(scalex * xcen + 0.5); - ycent = (l_int32)(scaley * ycen + 0.5); - if (angle == 0.0) { - boxd = boxCreate(tx, ty, tw, th); - } else { - xdif = tx + 0.5 * tw - xcent; - ydif = ty + 0.5 * th - ycent; - rw = L_ABS(tw * cosa) + L_ABS(th * sina); - rh = L_ABS(th * cosa) + L_ABS(tw * sina); - rx = xcent + xdif * cosa - ydif * sina - 0.5 * rw; - ry = ycent + ydif * cosa + xdif * sina - 0.5 * rh; - boxd = boxCreate((l_int32)rx, (l_int32)ry, (l_int32)rw, - (l_int32)rh); - } - } else if (order == L_RO_TR_SC) { - if (angle == 0.0) { - rx = bx; - ry = by; - rw = bw; - rh = bh; - } else { - xdif = bx + 0.5 * bw - xcen; - ydif = by + 0.5 * bh - ycen; - rw = L_ABS(bw * cosa) + L_ABS(bh * sina); - rh = L_ABS(bh * cosa) + L_ABS(bw * sina); - rx = xcen + xdif * cosa - ydif * sina - 0.5 * rw; - ry = ycen + ydif * cosa + xdif * sina - 0.5 * rh; - } - tx = (l_int32)(scalex * (rx + shiftx) + 0.5); - ty = (l_int32)(scaley * (ry + shifty) + 0.5); - tw = (l_int32)(L_MAX(1.0, scalex * rw + 0.5)); - th = (l_int32)(L_MAX(1.0, scaley * rh + 0.5)); - boxd = boxCreate(tx, ty, tw, th); - } else if (order == L_RO_SC_TR) { - if (angle == 0.0) { - rx = bx; - ry = by; - rw = bw; - rh = bh; - } else { - xdif = bx + 0.5 * bw - xcen; - ydif = by + 0.5 * bh - ycen; - rw = L_ABS(bw * cosa) + L_ABS(bh * sina); - rh = L_ABS(bh * cosa) + L_ABS(bw * sina); - rx = xcen + xdif * cosa - ydif * sina - 0.5 * rw; - ry = ycen + ydif * cosa + xdif * sina - 0.5 * rh; - } - tx = (l_int32)(scalex * rx + shiftx + 0.5); - ty = (l_int32)(scaley * ry + shifty + 0.5); - tw = (l_int32)(L_MAX(1.0, scalex * rw + 0.5)); - th = (l_int32)(L_MAX(1.0, scaley * rh + 0.5)); - boxd = boxCreate(tx, ty, tw, th); - } else if (order == L_TR_RO_SC) { - tx = bx + shiftx; - ty = by + shifty; - if (angle == 0.0) { - rx = tx; - ry = ty; - rw = bw; - rh = bh; - } else { - xdif = tx + 0.5 * bw - xcen; - ydif = ty + 0.5 * bh - ycen; - rw = L_ABS(bw * cosa) + L_ABS(bh * sina); - rh = L_ABS(bh * cosa) + L_ABS(bw * sina); - rx = xcen + xdif * cosa - ydif * sina - 0.5 * rw; - ry = ycen + ydif * cosa + xdif * sina - 0.5 * rh; - } - tx = (l_int32)(scalex * rx + 0.5); - ty = (l_int32)(scaley * ry + 0.5); - tw = (l_int32)(L_MAX(1.0, scalex * rw + 0.5)); - th = (l_int32)(L_MAX(1.0, scaley * rh + 0.5)); - boxd = boxCreate(tx, ty, tw, th); - } else { /* order == L_SC_RO_TR) */ - tx = (l_int32)(scalex * bx + 0.5); - ty = (l_int32)(scaley * by + 0.5); - tw = (l_int32)(L_MAX(1.0, scalex * bw + 0.5)); - th = (l_int32)(L_MAX(1.0, scaley * bh + 0.5)); - xcent = (l_int32)(scalex * xcen + 0.5); - ycent = (l_int32)(scaley * ycen + 0.5); - if (angle == 0.0) { - rx = tx; - ry = ty; - rw = tw; - rh = th; - } else { - xdif = tx + 0.5 * tw - xcent; - ydif = ty + 0.5 * th - ycent; - rw = L_ABS(tw * cosa) + L_ABS(th * sina); - rh = L_ABS(th * cosa) + L_ABS(tw * sina); - rx = xcent + xdif * cosa - ydif * sina - 0.5 * rw; - ry = ycent + ydif * cosa + xdif * sina - 0.5 * rh; - } - tx = (l_int32)(rx + shiftx + 0.5); - ty = (l_int32)(ry + shifty + 0.5); - tw = (l_int32)(rw + 0.5); - th = (l_int32)(rh + 0.5); - boxd = boxCreate(tx, ty, tw, th); - } - - return boxd; -} - - -/*! - * \brief boxaRotateOrth() - * - * \param[in] boxas - * \param[in] w, h of image in which the boxa is embedded - * \param[in] rotation 0 = noop, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg; - * all rotations are clockwise - * \return boxad, or NULL on error - * - *
- * Notes: - * (1) See boxRotateOrth() for details. - *- */ -BOXA * -boxaRotateOrth(BOXA *boxas, - l_int32 w, - l_int32 h, - l_int32 rotation) -{ -l_int32 i, n; -BOX *boxs, *boxd; -BOXA *boxad; - - PROCNAME("boxaRotateOrth"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (rotation < 0 || rotation > 3) - return (BOXA *)ERROR_PTR("rotation not in {0,1,2,3}", procName, NULL); - if (rotation == 0) - return boxaCopy(boxas, L_COPY); - - n = boxaGetCount(boxas); - if ((boxad = boxaCreate(n)) == NULL) - return (BOXA *)ERROR_PTR("boxad not made", procName, NULL); - for (i = 0; i < n; i++) { - if ((boxs = boxaGetBox(boxas, i, L_CLONE)) == NULL) { - boxaDestroy(&boxad); - return (BOXA *)ERROR_PTR("boxs not found", procName, NULL); - } - boxd = boxRotateOrth(boxs, w, h, rotation); - boxDestroy(&boxs); - boxaAddBox(boxad, boxd, L_INSERT); - } - - return boxad; -} - - -/*! - * \brief boxRotateOrth() - * - * \param[in] box - * \param[in] w, h of image in which the box is embedded - * \param[in] rotation 0 = noop, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg; - * all rotations are clockwise - * \return boxd, or NULL on error - * - *
- * Notes: - * (1) Rotate the image with the embedded box by the specified amount. - * (2) After rotation, the rotated box is always measured with - * respect to the UL corner of the image. - *- */ -BOX * -boxRotateOrth(BOX *box, - l_int32 w, - l_int32 h, - l_int32 rotation) -{ -l_int32 bx, by, bw, bh, xdist, ydist; - - PROCNAME("boxRotateOrth"); - - if (!box) - return (BOX *)ERROR_PTR("box not defined", procName, NULL); - if (rotation < 0 || rotation > 3) - return (BOX *)ERROR_PTR("rotation not in {0,1,2,3}", procName, NULL); - if (rotation == 0) - return boxCopy(box); - - boxGetGeometry(box, &bx, &by, &bw, &bh); - if (bw <= 0 || bh <= 0) /* invalid */ - return boxCreate(0, 0, 0, 0); - ydist = h - by - bh; /* below box */ - xdist = w - bx - bw; /* to right of box */ - if (rotation == 1) /* 90 deg cw */ - return boxCreate(ydist, bx, bh, bw); - else if (rotation == 2) /* 180 deg cw */ - return boxCreate(xdist, ydist, bw, bh); - else /* rotation == 3, 270 deg cw */ - return boxCreate(by, xdist, bh, bw); -} - - -/*! - * \brief boxaShiftWithPta() - * - * \param[in] boxas - * \param[in] pta aligned with the boxes; determines shift amount - * \param[in] dir +1 to shift by the values in pta; -1 to shift - * by the negative of the values in the pta. - * \return boxad, or NULL on error - * - *
- * Notes: - * (1) In use, %pta may come from the UL corners of of a boxa, each - * of whose boxes contains the corresponding box of %boxas - * within it. The output %boxad is then a boxa in the (global) - * coordinates of the containing boxa. So the input %pta - * could come from boxaExtractCorners(). - * (2) The operations with %dir == 1 and %dir == -1 are inverses if - * called in order (1, -1). Starting with an input boxa and - * calling twice with these values of %dir results in a boxa - * identical to the input. However, because box parameters can - * never be negative, calling in the order (-1, 1) may result - * in clipping at the left side and the top. - *- */ -BOXA * -boxaShiftWithPta(BOXA *boxas, - PTA *pta, - l_int32 dir) -{ -l_int32 i, n, x, y, full; -BOX *box1, *box2; -BOXA *boxad; - - PROCNAME("boxaShiftWithPta"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - boxaIsFull(boxas, &full); - if (!full) - return (BOXA *)ERROR_PTR("boxas not full", procName, NULL); - if (!pta) - return (BOXA *)ERROR_PTR("pta not defined", procName, NULL); - if (dir != 1 && dir != -1) - return (BOXA *)ERROR_PTR("invalid dir", procName, NULL); - n = boxaGetCount(boxas); - if (n != ptaGetCount(pta)) - return (BOXA *)ERROR_PTR("boxas and pta not same size", procName, NULL); - - if ((boxad = boxaCreate(n)) == NULL) - return (BOXA *)ERROR_PTR("boxad not made", procName, NULL); - for (i = 0; i < n; i++) { - box1 = boxaGetBox(boxas, i, L_COPY); - ptaGetIPt(pta, i, &x, &y); - box2 = boxTransform(box1, dir * x, dir * y, 1.0, 1.0); - boxaAddBox(boxad, box2, L_INSERT); - boxDestroy(&box1); - } - return boxad; -} - - -/*---------------------------------------------------------------------* - * Boxa sort * - *---------------------------------------------------------------------*/ -/*! - * \brief boxaSort() - * - * \param[in] boxas - * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y, - * L_SORT_BY_RIGHT, L_SORT_BY_BOT, - * L_SORT_BY_WIDTH, L_SORT_BY_HEIGHT, - * L_SORT_BY_MIN_DIMENSION, L_SORT_BY_MAX_DIMENSION, - * L_SORT_BY_PERIMETER, L_SORT_BY_AREA, - * L_SORT_BY_ASPECT_RATIO - * \param[in] sortorder L_SORT_INCREASING, L_SORT_DECREASING - * \param[out] pnaindex [optional] index of sorted order into - * original array - * \return boxad sorted version of boxas, or NULL on error - * - *
- * Notes: - * (1) An empty boxa returns a copy, with a warning. - *- */ -BOXA * -boxaSort(BOXA *boxas, - l_int32 sorttype, - l_int32 sortorder, - NUMA **pnaindex) -{ -l_int32 i, n, x, y, w, h, size; -BOXA *boxad; -NUMA *na, *naindex; - - PROCNAME("boxaSort"); - - if (pnaindex) *pnaindex = NULL; - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if ((n = boxaGetCount(boxas)) == 0) { - L_WARNING("boxas is empty\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && - sorttype != L_SORT_BY_RIGHT && sorttype != L_SORT_BY_BOT && - sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && - sorttype != L_SORT_BY_MIN_DIMENSION && - sorttype != L_SORT_BY_MAX_DIMENSION && - sorttype != L_SORT_BY_PERIMETER && - sorttype != L_SORT_BY_AREA && - sorttype != L_SORT_BY_ASPECT_RATIO) - return (BOXA *)ERROR_PTR("invalid sort type", procName, NULL); - if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) - return (BOXA *)ERROR_PTR("invalid sort order", procName, NULL); - - /* Use O(n) binsort if possible */ - if (n > MinCompsForBinSort && - ((sorttype == L_SORT_BY_X) || (sorttype == L_SORT_BY_Y) || - (sorttype == L_SORT_BY_WIDTH) || (sorttype == L_SORT_BY_HEIGHT) || - (sorttype == L_SORT_BY_PERIMETER))) - return boxaBinSort(boxas, sorttype, sortorder, pnaindex); - - /* Build up numa of specific data */ - if ((na = numaCreate(n)) == NULL) - return (BOXA *)ERROR_PTR("na not made", procName, NULL); - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxas, i, &x, &y, &w, &h); - switch (sorttype) - { - case L_SORT_BY_X: - numaAddNumber(na, x); - break; - case L_SORT_BY_Y: - numaAddNumber(na, y); - break; - case L_SORT_BY_RIGHT: - numaAddNumber(na, x + w - 1); - break; - case L_SORT_BY_BOT: - numaAddNumber(na, y + h - 1); - break; - case L_SORT_BY_WIDTH: - numaAddNumber(na, w); - break; - case L_SORT_BY_HEIGHT: - numaAddNumber(na, h); - break; - case L_SORT_BY_MIN_DIMENSION: - size = L_MIN(w, h); - numaAddNumber(na, size); - break; - case L_SORT_BY_MAX_DIMENSION: - size = L_MAX(w, h); - numaAddNumber(na, size); - break; - case L_SORT_BY_PERIMETER: - size = w + h; - numaAddNumber(na, size); - break; - case L_SORT_BY_AREA: - size = w * h; - numaAddNumber(na, size); - break; - case L_SORT_BY_ASPECT_RATIO: - numaAddNumber(na, (l_float32)w / (l_float32)h); - break; - default: - L_WARNING("invalid sort type\n", procName); - } - } - - /* Get the sort index for data array */ - naindex = numaGetSortIndex(na, sortorder); - numaDestroy(&na); - if (!naindex) - return (BOXA *)ERROR_PTR("naindex not made", procName, NULL); - - /* Build up sorted boxa using sort index */ - boxad = boxaSortByIndex(boxas, naindex); - - if (pnaindex) - *pnaindex = naindex; - else - numaDestroy(&naindex); - return boxad; -} - - -/*! - * \brief boxaBinSort() - * - * \param[in] boxas - * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH, - * L_SORT_BY_HEIGHT, L_SORT_BY_PERIMETER - * \param[in] sortorder L_SORT_INCREASING, L_SORT_DECREASING - * \param[out] pnaindex [optional] index of sorted order into - * original array - * \return boxad sorted version of boxas, or NULL on error - * - *
- * Notes: - * (1) For a large number of boxes (say, greater than 1000), this - * O(n) binsort is much faster than the O(nlogn) shellsort. - * For 5000 components, this is over 20x faster than boxaSort(). - * (2) Consequently, boxaSort() calls this function if it will - * likely go much faster. - *- */ -BOXA * -boxaBinSort(BOXA *boxas, - l_int32 sorttype, - l_int32 sortorder, - NUMA **pnaindex) -{ -l_int32 i, n, x, y, w, h; -BOXA *boxad; -NUMA *na, *naindex; - - PROCNAME("boxaBinSort"); - - if (pnaindex) *pnaindex = NULL; - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if ((n = boxaGetCount(boxas)) == 0) { - L_WARNING("boxas is empty\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && - sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && - sorttype != L_SORT_BY_PERIMETER) - return (BOXA *)ERROR_PTR("invalid sort type", procName, NULL); - if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) - return (BOXA *)ERROR_PTR("invalid sort order", procName, NULL); - - /* Generate Numa of appropriate box dimensions */ - if ((na = numaCreate(n)) == NULL) - return (BOXA *)ERROR_PTR("na not made", procName, NULL); - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxas, i, &x, &y, &w, &h); - switch (sorttype) - { - case L_SORT_BY_X: - numaAddNumber(na, x); - break; - case L_SORT_BY_Y: - numaAddNumber(na, y); - break; - case L_SORT_BY_WIDTH: - numaAddNumber(na, w); - break; - case L_SORT_BY_HEIGHT: - numaAddNumber(na, h); - break; - case L_SORT_BY_PERIMETER: - numaAddNumber(na, w + h); - break; - default: - L_WARNING("invalid sort type\n", procName); - } - } - - /* Get the sort index for data array */ - naindex = numaGetBinSortIndex(na, sortorder); - numaDestroy(&na); - if (!naindex) - return (BOXA *)ERROR_PTR("naindex not made", procName, NULL); - - /* Build up sorted boxa using the sort index */ - boxad = boxaSortByIndex(boxas, naindex); - - if (pnaindex) - *pnaindex = naindex; - else - numaDestroy(&naindex); - return boxad; -} - - -/*! - * \brief boxaSortByIndex() - * - * \param[in] boxas - * \param[in] naindex na that maps from the new boxa to the input boxa - * \return boxad sorted, or NULL on error - */ -BOXA * -boxaSortByIndex(BOXA *boxas, - NUMA *naindex) -{ -l_int32 i, n, index; -BOX *box; -BOXA *boxad; - - PROCNAME("boxaSortByIndex"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if ((n = boxaGetCount(boxas)) == 0) { - L_WARNING("boxas is empty\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (!naindex) - return (BOXA *)ERROR_PTR("naindex not defined", procName, NULL); - - boxad = boxaCreate(n); - for (i = 0; i < n; i++) { - numaGetIValue(naindex, i, &index); - box = boxaGetBox(boxas, index, L_COPY); - boxaAddBox(boxad, box, L_INSERT); - } - - return boxad; -} - - -/*! - * \brief boxaSort2d() - * - * \param[in] boxas - * \param[out] pnaad [optional] numaa with sorted indices - * whose values are the indices of the input array - * \param[in] delta1 min separation that permits aggregation of a box - * onto a boxa of horizontally-aligned boxes; pass 1 - * \param[in] delta2 min separation that permits aggregation of a box - * onto a boxa of horizontally-aligned boxes; pass 2 - * \param[in] minh1 components less than this height either join an - * existing boxa or are set aside for pass 2 - * \return baa 2d sorted version of boxa, or NULL on error - * - *
- * Notes: - * (1) The final result is a sort where the 'fast scan' direction is - * left to right, and the 'slow scan' direction is from top - * to bottom. Each boxa in the baa represents a sorted set - * of boxes from left to right. - * (2) Three passes are used to aggregate the boxas, which can correspond - * to characters or words in a line of text. In pass 1, only - * taller components, which correspond to xheight or larger, - * are permitted to start a new boxa. In pass 2, the remaining - * vertically-challenged components are allowed to join an - * existing boxa or start a new one. In pass 3, boxa whose extent - * is overlapping are joined. After that, the boxes in each - * boxa are sorted horizontally, and finally the boxa are - * sorted vertically. - * (3) If %delta1 > 0, the first pass allows aggregation when - * boxes in the same boxa do not overlap vertically. In fact, - * %delta1 is the max distance by which they can miss and still - * be aggregated. If %delta1 < 0, the box must have vertical - * overlap of at least abs(%delta1) with the boxa before it - * can be merged. Similar for delta2 on the second pass. - * (4) On the first pass, any component of height less than minh1 - * cannot start a new boxa; it's put aside for later insertion. - * (5) On the second pass, any small component that doesn't align - * with an existing boxa can start a new one. - * (6) This can be used to identify lines of text from - * character or word bounding boxes. - * (7) Typical values for the input parameters on 300 ppi text are: - * delta1 ~ 0 - * delta2 ~ 0 - * minh1 ~ 5 - *- */ -BOXAA * -boxaSort2d(BOXA *boxas, - NUMAA **pnaad, - l_int32 delta1, - l_int32 delta2, - l_int32 minh1) -{ -l_int32 i, index, h, nt, ne, n, m, ival; -BOX *box; -BOXA *boxa, *boxae, *boxan, *boxa1, *boxa2, *boxa3, *boxav, *boxavs; -BOXAA *baa, *baa1, *baad; -NUMA *naindex, *nae, *nan, *nah, *nav, *na1, *na2, *nad, *namap; -NUMAA *naa, *naa1, *naad; - - PROCNAME("boxaSort2d"); - - if (pnaad) *pnaad = NULL; - if (!boxas) - return (BOXAA *)ERROR_PTR("boxas not defined", procName, NULL); - if (boxaGetCount(boxas) == 0) - return (BOXAA *)ERROR_PTR("boxas is empty", procName, NULL); - - /* Sort from left to right */ - if ((boxa = boxaSort(boxas, L_SORT_BY_X, L_SORT_INCREASING, &naindex)) - == NULL) - return (BOXAA *)ERROR_PTR("boxa not made", procName, NULL); - - /* First pass: assign taller boxes to boxa by row */ - nt = boxaGetCount(boxa); - baa = boxaaCreate(0); - naa = numaaCreate(0); - boxae = boxaCreate(0); /* save small height boxes here */ - nae = numaCreate(0); /* keep track of small height boxes */ - for (i = 0; i < nt; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - boxGetGeometry(box, NULL, NULL, NULL, &h); - if (h < minh1) { /* save for 2nd pass */ - boxaAddBox(boxae, box, L_INSERT); - numaAddNumber(nae, i); - } else { - n = boxaaGetCount(baa); - boxaaAlignBox(baa, box, delta1, &index); - if (index < n) { /* append to an existing boxa */ - boxaaAddBox(baa, index, box, L_INSERT); - } else { /* doesn't align, need new boxa */ - boxan = boxaCreate(0); - boxaAddBox(boxan, box, L_INSERT); - boxaaAddBoxa(baa, boxan, L_INSERT); - nan = numaCreate(0); - numaaAddNuma(naa, nan, L_INSERT); - } - numaGetIValue(naindex, i, &ival); - numaaAddNumber(naa, index, ival); - } - } - boxaDestroy(&boxa); - numaDestroy(&naindex); - - /* Second pass: feed in small height boxes */ - ne = boxaGetCount(boxae); - for (i = 0; i < ne; i++) { - box = boxaGetBox(boxae, i, L_CLONE); - n = boxaaGetCount(baa); - boxaaAlignBox(baa, box, delta2, &index); - if (index < n) { /* append to an existing boxa */ - boxaaAddBox(baa, index, box, L_INSERT); - } else { /* doesn't align, need new boxa */ - boxan = boxaCreate(0); - boxaAddBox(boxan, box, L_INSERT); - boxaaAddBoxa(baa, boxan, L_INSERT); - nan = numaCreate(0); - numaaAddNuma(naa, nan, L_INSERT); - } - numaGetIValue(nae, i, &ival); /* location in original boxas */ - numaaAddNumber(naa, index, ival); - } - - /* Third pass: merge some boxa whose extent is overlapping. - * Think of these boxa as text lines, where the bounding boxes - * of the text lines can overlap, but likely won't have - * a huge overlap. - * First do a greedy find of pairs of overlapping boxa, where - * the two boxa overlap by at least 50% of the smaller, and - * the smaller is not more than half the area of the larger. - * For such pairs, call the larger one the primary boxa. The - * boxes in the smaller one are appended to those in the primary - * in pass 3a, and the primaries are extracted in pass 3b. - * In this way, all boxes in the original baa are saved. */ - n = boxaaGetCount(baa); - boxaaGetExtent(baa, NULL, NULL, NULL, &boxa3); - boxa1 = boxaHandleOverlaps(boxa3, L_REMOVE_SMALL, 1000, 0.5, 0.5, &namap); - boxaDestroy(&boxa1); - boxaDestroy(&boxa3); - for (i = 0; i < n; i++) { /* Pass 3a: join selected copies of boxa */ - numaGetIValue(namap, i, &ival); - if (ival >= 0) { /* join current to primary boxa[ival] */ - boxa1 = boxaaGetBoxa(baa, i, L_COPY); - boxa2 = boxaaGetBoxa(baa, ival, L_CLONE); - boxaJoin(boxa2, boxa1, 0, -1); - boxaDestroy(&boxa2); - boxaDestroy(&boxa1); - na1 = numaaGetNuma(naa, i, L_COPY); - na2 = numaaGetNuma(naa, ival, L_CLONE); - numaJoin(na2, na1, 0, -1); - numaDestroy(&na1); - numaDestroy(&na2); - } - } - baa1 = boxaaCreate(n); - naa1 = numaaCreate(n); - for (i = 0; i < n; i++) { /* Pass 3b: save primary boxa */ - numaGetIValue(namap, i, &ival); - if (ival == -1) { - boxa1 = boxaaGetBoxa(baa, i, L_CLONE); - boxaaAddBoxa(baa1, boxa1, L_INSERT); - na1 = numaaGetNuma(naa, i, L_CLONE); - numaaAddNuma(naa1, na1, L_INSERT); - } - } - numaDestroy(&namap); - boxaaDestroy(&baa); - baa = baa1; - numaaDestroy(&naa); - naa = naa1; - - /* Sort the boxes in each boxa horizontally */ - m = boxaaGetCount(baa); - for (i = 0; i < m; i++) { - boxa1 = boxaaGetBoxa(baa, i, L_CLONE); - boxa2 = boxaSort(boxa1, L_SORT_BY_X, L_SORT_INCREASING, &nah); - boxaaReplaceBoxa(baa, i, boxa2); - na1 = numaaGetNuma(naa, i, L_CLONE); - na2 = numaSortByIndex(na1, nah); - numaaReplaceNuma(naa, i, na2); - boxaDestroy(&boxa1); - numaDestroy(&na1); - numaDestroy(&nah); - } - - /* Sort the boxa vertically within boxaa, using the first box - * in each boxa. */ - m = boxaaGetCount(baa); - boxav = boxaCreate(m); /* holds first box in each boxa in baa */ - naad = numaaCreate(m); - if (pnaad) - *pnaad = naad; - baad = boxaaCreate(m); - for (i = 0; i < m; i++) { - boxa1 = boxaaGetBoxa(baa, i, L_CLONE); - box = boxaGetBox(boxa1, 0, L_CLONE); - boxaAddBox(boxav, box, L_INSERT); - boxaDestroy(&boxa1); - } - boxavs = boxaSort(boxav, L_SORT_BY_Y, L_SORT_INCREASING, &nav); - for (i = 0; i < m; i++) { - numaGetIValue(nav, i, &index); - boxa = boxaaGetBoxa(baa, index, L_CLONE); - boxaaAddBoxa(baad, boxa, L_INSERT); - nad = numaaGetNuma(naa, index, L_CLONE); - numaaAddNuma(naad, nad, L_INSERT); - } - - -/* lept_stderr("box count = %d, numaa count = %d\n", nt, - numaaGetNumberCount(naad)); */ - - boxaaDestroy(&baa); - boxaDestroy(&boxav); - boxaDestroy(&boxavs); - boxaDestroy(&boxae); - numaDestroy(&nav); - numaDestroy(&nae); - numaaDestroy(&naa); - if (!pnaad) - numaaDestroy(&naad); - - return baad; -} - - -/*! - * \brief boxaSort2dByIndex() - * - * \param[in] boxas - * \param[in] naa numaa that maps from the new baa to the input boxa - * \return baa sorted boxaa, or NULL on error - */ -BOXAA * -boxaSort2dByIndex(BOXA *boxas, - NUMAA *naa) -{ -l_int32 ntot, boxtot, i, j, n, nn, index; -BOX *box; -BOXA *boxa; -BOXAA *baa; -NUMA *na; - - PROCNAME("boxaSort2dByIndex"); - - if (!boxas) - return (BOXAA *)ERROR_PTR("boxas not defined", procName, NULL); - if ((boxtot = boxaGetCount(boxas)) == 0) - return (BOXAA *)ERROR_PTR("boxas is empty", procName, NULL); - if (!naa) - return (BOXAA *)ERROR_PTR("naindex not defined", procName, NULL); - - /* Check counts */ - ntot = numaaGetNumberCount(naa); - if (ntot != boxtot) - return (BOXAA *)ERROR_PTR("element count mismatch", procName, NULL); - - n = numaaGetCount(naa); - baa = boxaaCreate(n); - for (i = 0; i < n; i++) { - na = numaaGetNuma(naa, i, L_CLONE); - nn = numaGetCount(na); - boxa = boxaCreate(nn); - for (j = 0; j < nn; j++) { - numaGetIValue(na, i, &index); - box = boxaGetBox(boxas, index, L_COPY); - boxaAddBox(boxa, box, L_INSERT); - } - boxaaAddBoxa(baa, boxa, L_INSERT); - numaDestroy(&na); - } - - return baa; -} - - -/*---------------------------------------------------------------------* - * Boxa array extraction * - *---------------------------------------------------------------------*/ -/*! - * \brief boxaExtractAsNuma() - * - * \param[in] boxa - * \param[out] pnal [optional] array of left locations - * \param[out] pnat [optional] array of top locations - * \param[out] pnar [optional] array of right locations - * \param[out] pnab [optional] array of bottom locations - * \param[out] pnaw [optional] array of widths - * \param[out] pnah [optional] array of heights - * \param[in] keepinvalid 1 to keep invalid boxes; 0 to remove them - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) If you are counting or sorting values, such as determining - * rank order, you must remove invalid boxes. - * (2) If you are parametrizing the values, or doing an evaluation - * where the position in the boxa sequence is important, you - * must replace the invalid boxes with valid ones before - * doing the extraction. This is easily done with boxaFillSequence(). - *- */ -l_ok -boxaExtractAsNuma(BOXA *boxa, - NUMA **pnal, - NUMA **pnat, - NUMA **pnar, - NUMA **pnab, - NUMA **pnaw, - NUMA **pnah, - l_int32 keepinvalid) -{ -l_int32 i, n, left, top, right, bot, w, h; - - PROCNAME("boxaExtractAsNuma"); - - if (!pnal && !pnat && !pnar && !pnab && !pnaw && !pnah) - return ERROR_INT("no output requested", procName, 1); - if (pnal) *pnal = NULL; - if (pnat) *pnat = NULL; - if (pnar) *pnar = NULL; - if (pnab) *pnab = NULL; - if (pnaw) *pnaw = NULL; - if (pnah) *pnah = NULL; - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (!keepinvalid && boxaGetValidCount(boxa) == 0) - return ERROR_INT("no valid boxes", procName, 1); - - n = boxaGetCount(boxa); - if (pnal) *pnal = numaCreate(n); - if (pnat) *pnat = numaCreate(n); - if (pnar) *pnar = numaCreate(n); - if (pnab) *pnab = numaCreate(n); - if (pnaw) *pnaw = numaCreate(n); - if (pnah) *pnah = numaCreate(n); - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxa, i, &left, &top, &w, &h); - if (!keepinvalid && (w <= 0 || h <= 0)) - continue; - right = left + w - 1; - bot = top + h - 1; - if (pnal) numaAddNumber(*pnal, left); - if (pnat) numaAddNumber(*pnat, top); - if (pnar) numaAddNumber(*pnar, right); - if (pnab) numaAddNumber(*pnab, bot); - if (pnaw) numaAddNumber(*pnaw, w); - if (pnah) numaAddNumber(*pnah, h); - } - - return 0; -} - - -/*! - * \brief boxaExtractAsPta() - * - * \param[in] boxa - * \param[out] pptal [optional] array of left locations vs. index - * \param[out] pptat [optional] array of top locations vs. index - * \param[out] pptar [optional] array of right locations vs. index - * \param[out] pptab [optional] array of bottom locations vs. index - * \param[out] pptaw [optional] array of widths vs. index - * \param[out] pptah [optional] array of heights vs. index - * \param[in] keepinvalid 1 to keep invalid boxes; 0 to remove them - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) For most applications, such as counting, sorting, fitting - * to some parametrized form, plotting or filtering in general, - * you should remove the invalid boxes. Each pta saves the - * box index in the x array, so replacing invalid boxes by - * filling with boxaFillSequence(), which is required for - * boxaExtractAsNuma(), is not necessary. - * (2) If invalid boxes are retained, each one will result in - * entries (typically 0) in all selected output pta. - * (3) Other boxa --> pta functions are: - * * boxaExtractCorners(): extracts any of the four corners as a pta. - * * boxaConvertToPta(): extracts sufficient number of corners - * to allow reconstruction of the original boxa from the pta. - *- */ -l_ok -boxaExtractAsPta(BOXA *boxa, - PTA **pptal, - PTA **pptat, - PTA **pptar, - PTA **pptab, - PTA **pptaw, - PTA **pptah, - l_int32 keepinvalid) -{ -l_int32 i, n, left, top, right, bot, w, h; - - PROCNAME("boxaExtractAsPta"); - - if (!pptal && !pptar && !pptat && !pptab && !pptaw && !pptah) - return ERROR_INT("no output requested", procName, 1); - if (pptal) *pptal = NULL; - if (pptat) *pptat = NULL; - if (pptar) *pptar = NULL; - if (pptab) *pptab = NULL; - if (pptaw) *pptaw = NULL; - if (pptah) *pptah = NULL; - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (!keepinvalid && boxaGetValidCount(boxa) == 0) - return ERROR_INT("no valid boxes", procName, 1); - - n = boxaGetCount(boxa); - if (pptal) *pptal = ptaCreate(n); - if (pptat) *pptat = ptaCreate(n); - if (pptar) *pptar = ptaCreate(n); - if (pptab) *pptab = ptaCreate(n); - if (pptaw) *pptaw = ptaCreate(n); - if (pptah) *pptah = ptaCreate(n); - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxa, i, &left, &top, &w, &h); - if (!keepinvalid && (w <= 0 || h <= 0)) - continue; - right = left + w - 1; - bot = top + h - 1; - if (pptal) ptaAddPt(*pptal, i, left); - if (pptat) ptaAddPt(*pptat, i, top); - if (pptar) ptaAddPt(*pptar, i, right); - if (pptab) ptaAddPt(*pptab, i, bot); - if (pptaw) ptaAddPt(*pptaw, i, w); - if (pptah) ptaAddPt(*pptah, i, h); - } - - return 0; -} - - -/*! - * \brief boxaExtractCorners() - * - * \param[in] boxa - * \param[in] loc L_UPPER_LEFT, L_UPPER_RIGHT, L_LOWER_LEFT, - * L_LOWER_RIGHT, L_BOX_CENTER - * \return pta of requested coordinates, or NULL on error - * - *
- * Notes: - * (1) Extracts (0,0) for invalid boxes. - * (2) Other boxa --> pta functions are: - * * boxaExtractAsPta(): allows extraction of any dimension - * and/or side location, with each in a separate pta. - * * boxaConvertToPta(): extracts sufficient number of corners - * to allow reconstruction of the original boxa from the pta. - *- */ -PTA * -boxaExtractCorners(BOXA *boxa, - l_int32 loc) -{ -l_int32 i, n, left, top, right, bot, w, h; -PTA *pta; - - PROCNAME("boxaExtractCorners"); - - if (!boxa) - return (PTA *)ERROR_PTR("boxa not defined", procName, NULL); - if (loc != L_UPPER_LEFT && loc != L_UPPER_RIGHT && loc != L_LOWER_LEFT && - loc != L_LOWER_RIGHT && loc != L_BOX_CENTER) - return (PTA *)ERROR_PTR("invalid location", procName, NULL); - - n = boxaGetCount(boxa); - if ((pta = ptaCreate(n)) == NULL) - return (PTA *)ERROR_PTR("pta not made", procName, NULL); - - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxa, i, &left, &top, &w, &h); - right = left + w - 1; - bot = top + h - 1; - if (w == 0 || h == 0) { /* invalid */ - left = 0; - top = 0; - right = 0; - bot = 0; - } - if (loc == L_UPPER_LEFT) - ptaAddPt(pta, left, top); - else if (loc == L_UPPER_RIGHT) - ptaAddPt(pta, right, top); - else if (loc == L_LOWER_LEFT) - ptaAddPt(pta, left, bot); - else if (loc == L_LOWER_RIGHT) - ptaAddPt(pta, right, bot); - else if (loc == L_BOX_CENTER) - ptaAddPt(pta, (left + right) / 2, (top + bot) / 2); - } - - return pta; -} - - -/*---------------------------------------------------------------------* - * Boxa statistics * - *---------------------------------------------------------------------*/ -/*! - * \brief boxaGetRankVals() - * - * \param[in] boxa - * \param[in] fract use 0.0 for smallest, 1.0 for largest width and height - * \param[out] px [optional] rank value of x (left side) - * \param[out] py [optional] rank value of y (top side) - * \param[out] pr [optional] rank value of right side - * \param[out] pb [optional] rank value of bottom side - * \param[out] pw [optional] rank value of width - * \param[out] ph [optional] rank value of height - * \return 0 if OK, 1 on error or if the boxa is empty or has no valid boxes - * - *
- * Notes: - * (1) This function does not assume that all boxes in the boxa are valid - * (2) The six box parameters are sorted independently. - * For rank order, the width and height are sorted in increasing - * order. But what does it mean to sort x and y in "rank order"? - * If the boxes are of comparable size and somewhat - * aligned (e.g., from multiple images), it makes some sense - * to give a "rank order" for x and y by sorting them in - * decreasing order. (By the same argument, we choose to sort - * the r and b sides in increasing order.) In general, the - * interpretation of a rank order on x and y (or on r and b) - * is highly application dependent. In summary: - * ~ x and y are sorted in decreasing order - * ~ r and b are sorted in increasing order - * ~ w and h are sorted in increasing order - *- */ -l_ok -boxaGetRankVals(BOXA *boxa, - l_float32 fract, - l_int32 *px, - l_int32 *py, - l_int32 *pr, - l_int32 *pb, - l_int32 *pw, - l_int32 *ph) -{ -l_float32 xval, yval, rval, bval, wval, hval; -NUMA *nax, *nay, *nar, *nab, *naw, *nah; - - PROCNAME("boxaGetRankVals"); - - if (px) *px = 0; - if (py) *py = 0; - if (pr) *pr = 0; - if (pb) *pb = 0; - if (pw) *pw = 0; - if (ph) *ph = 0; - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (fract < 0.0 || fract > 1.0) - return ERROR_INT("fract not in [0.0 ... 1.0]", procName, 1); - if (boxaGetValidCount(boxa) == 0) - return ERROR_INT("no valid boxes in boxa", procName, 1); - - /* Use only the valid boxes */ - boxaExtractAsNuma(boxa, &nax, &nay, &nar, &nab, &naw, &nah, 0); - - if (px) { - numaGetRankValue(nax, 1.0 - fract, NULL, 1, &xval); - *px = (l_int32)xval; - } - if (py) { - numaGetRankValue(nay, 1.0 - fract, NULL, 1, &yval); - *py = (l_int32)yval; - } - if (pr) { - numaGetRankValue(nar, fract, NULL, 1, &rval); - *pr = (l_int32)rval; - } - if (pb) { - numaGetRankValue(nab, fract, NULL, 1, &bval); - *pb = (l_int32)bval; - } - if (pw) { - numaGetRankValue(naw, fract, NULL, 1, &wval); - *pw = (l_int32)wval; - } - if (ph) { - numaGetRankValue(nah, fract, NULL, 1, &hval); - *ph = (l_int32)hval; - } - numaDestroy(&nax); - numaDestroy(&nay); - numaDestroy(&nar); - numaDestroy(&nab); - numaDestroy(&naw); - numaDestroy(&nah); - return 0; -} - - -/*! - * \brief boxaGetMedianVals() - * - * \param[in] boxa - * \param[out] px [optional] median value of x (left side) - * \param[out] py [optional] median value of y (top side) - * \param[out] pr [optional] median value of right side - * \param[out] pb [optional] median value of bottom side - * \param[out] pw [optional] median value of width - * \param[out] ph [optional] median value of height - * \return 0 if OK, 1 on error or if the boxa is empty or has no valid boxes - * - *
- * Notes: - * (1) See boxaGetRankVals() - *- */ -l_ok -boxaGetMedianVals(BOXA *boxa, - l_int32 *px, - l_int32 *py, - l_int32 *pr, - l_int32 *pb, - l_int32 *pw, - l_int32 *ph) -{ - PROCNAME("boxaGetMedianVals"); - - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (boxaGetValidCount(boxa) == 0) - return ERROR_INT("no valid boxes in boxa", procName, 1); - - return boxaGetRankVals(boxa, 0.5, px, py, pr, pb, pw, ph); -} - - -/*! - * \brief boxaGetAverageSize() - * - * \param[in] boxa - * \param[out] pw [optional] average width - * \param[out] ph [optional] average height - * \return 0 if OK, 1 on error or if the boxa is empty - */ -l_ok -boxaGetAverageSize(BOXA *boxa, - l_float32 *pw, - l_float32 *ph) -{ -l_int32 i, n, bw, bh; -l_float32 sumw, sumh; - - PROCNAME("boxaGetAverageSize"); - - if (pw) *pw = 0.0; - if (ph) *ph = 0.0; - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if ((n = boxaGetCount(boxa)) == 0) - return ERROR_INT("boxa is empty", procName, 1); - - sumw = sumh = 0.0; - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxa, i, NULL, NULL, &bw, &bh); - sumw += bw; - sumh += bh; - } - - if (pw) *pw = sumw / n; - if (ph) *ph = sumh / n; - return 0; -} - - -/*---------------------------------------------------------------------* - * Other Boxaa functions * - *---------------------------------------------------------------------*/ -/*! - * \brief boxaaGetExtent() - * - * \param[in] baa - * \param[out] pw [optional] width - * \param[out] ph [optional] height - * \param[out] pbox [optional] minimum box containing all boxa - * in boxaa - * \param[out] pboxa [optional] boxa containing all boxes in each - * boxa in the boxaa - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The returned w and h are the minimum size image - * that would contain all boxes untranslated. - * (2) Each box in the returned boxa is the minimum box required to - * hold all the boxes in the respective boxa of baa. - * (3) If there are no valid boxes in a boxa, the box corresponding - * to its extent has all fields set to 0 (an invalid box). - *- */ -l_ok -boxaaGetExtent(BOXAA *baa, - l_int32 *pw, - l_int32 *ph, - BOX **pbox, - BOXA **pboxa) -{ -l_int32 i, n, x, y, w, h, xmax, ymax, xmin, ymin, found; -BOX *box1; -BOXA *boxa, *boxa1; - - PROCNAME("boxaaGetExtent"); - - if (!pw && !ph && !pbox && !pboxa) - return ERROR_INT("no ptrs defined", procName, 1); - if (pw) *pw = 0; - if (ph) *ph = 0; - if (pbox) *pbox = NULL; - if (pboxa) *pboxa = NULL; - if (!baa) - return ERROR_INT("baa not defined", procName, 1); - - n = boxaaGetCount(baa); - if (n == 0) - return ERROR_INT("no boxa in baa", procName, 1); - - boxa = boxaCreate(n); - xmax = ymax = 0; - xmin = ymin = 100000000; - found = FALSE; - for (i = 0; i < n; i++) { - boxa1 = boxaaGetBoxa(baa, i, L_CLONE); - boxaGetExtent(boxa1, NULL, NULL, &box1); - boxaDestroy(&boxa1); - boxGetGeometry(box1, &x, &y, &w, &h); - if (w > 0 && h > 0) { /* a valid extent box */ - found = TRUE; /* found at least one valid extent box */ - xmin = L_MIN(xmin, x); - ymin = L_MIN(ymin, y); - xmax = L_MAX(xmax, x + w); - ymax = L_MAX(ymax, y + h); - } - boxaAddBox(boxa, box1, L_INSERT); - } - if (found == FALSE) /* no valid extent boxes */ - xmin = ymin = 0; - - if (pw) *pw = xmax; - if (ph) *ph = ymax; - if (pbox) - *pbox = boxCreate(xmin, ymin, xmax - xmin, ymax - ymin); - if (pboxa) - *pboxa = boxa; - else - boxaDestroy(&boxa); - return 0; -} - - -/*! - * \brief boxaaFlattenToBoxa() - * - * \param[in] baa - * \param[out] pnaindex [optional] the boxa index in the baa - * \param[in] copyflag L_COPY or L_CLONE - * \return boxa, or NULL on error - * - *
- * Notes: - * (1) This 'flattens' the baa to a boxa, taking the boxes in - * order in the first boxa, then the second, etc. - * (2) If a boxa is empty, we generate an invalid, placeholder box - * of zero size. This is useful when converting from a baa - * where each boxa has either 0 or 1 boxes, and it is necessary - * to maintain a 1:1 correspondence between the initial - * boxa array and the resulting box array. - * (3) If &naindex is defined, we generate a Numa that gives, for - * each box in the baa, the index of the boxa to which it belongs. - *- */ -BOXA * -boxaaFlattenToBoxa(BOXAA *baa, - NUMA **pnaindex, - l_int32 copyflag) -{ -l_int32 i, j, m, n; -BOXA *boxa, *boxat; -BOX *box; -NUMA *naindex; - - PROCNAME("boxaaFlattenToBoxa"); - - if (pnaindex) *pnaindex = NULL; - if (!baa) - return (BOXA *)ERROR_PTR("baa not defined", procName, NULL); - if (copyflag != L_COPY && copyflag != L_CLONE) - return (BOXA *)ERROR_PTR("invalid copyflag", procName, NULL); - if (pnaindex) { - naindex = numaCreate(0); - *pnaindex = naindex; - } - - n = boxaaGetCount(baa); - boxa = boxaCreate(n); - for (i = 0; i < n; i++) { - boxat = boxaaGetBoxa(baa, i, L_CLONE); - m = boxaGetCount(boxat); - if (m == 0) { /* placeholder box */ - box = boxCreate(0, 0, 0, 0); - boxaAddBox(boxa, box, L_INSERT); - if (pnaindex) - numaAddNumber(naindex, i); /* save 'row' number */ - } else { - for (j = 0; j < m; j++) { - box = boxaGetBox(boxat, j, copyflag); - boxaAddBox(boxa, box, L_INSERT); - if (pnaindex) - numaAddNumber(naindex, i); /* save 'row' number */ - } - } - boxaDestroy(&boxat); - } - - return boxa; -} - - -/*! - * \brief boxaaFlattenAligned() - * - * \param[in] baa - * \param[in] num number extracted from each - * \param[in] fillerbox [optional] that fills if necessary - * \param[in] copyflag L_COPY or L_CLONE - * \return boxa, or NULL on error - * - *
- * Notes: - * (1) This 'flattens' the baa to a boxa, taking the first %num - * boxes from each boxa. - * (2) In each boxa, if there are less than %num boxes, we preserve - * the alignment between the input baa and the output boxa - * by inserting one or more fillerbox(es) or, if %fillerbox == NULL, - * one or more invalid placeholder boxes. - *- */ -BOXA * -boxaaFlattenAligned(BOXAA *baa, - l_int32 num, - BOX *fillerbox, - l_int32 copyflag) -{ -l_int32 i, j, m, n, mval, nshort; -BOXA *boxat, *boxad; -BOX *box; - - PROCNAME("boxaaFlattenAligned"); - - if (!baa) - return (BOXA *)ERROR_PTR("baa not defined", procName, NULL); - if (copyflag != L_COPY && copyflag != L_CLONE) - return (BOXA *)ERROR_PTR("invalid copyflag", procName, NULL); - - n = boxaaGetCount(baa); - boxad = boxaCreate(n); - for (i = 0; i < n; i++) { - boxat = boxaaGetBoxa(baa, i, L_CLONE); - m = boxaGetCount(boxat); - mval = L_MIN(m, num); - nshort = num - mval; - for (j = 0; j < mval; j++) { /* take the first %num if possible */ - box = boxaGetBox(boxat, j, copyflag); - boxaAddBox(boxad, box, L_INSERT); - } - for (j = 0; j < nshort; j++) { /* add fillers if necessary */ - if (fillerbox) { - boxaAddBox(boxad, fillerbox, L_COPY); - } else { - box = boxCreate(0, 0, 0, 0); /* invalid placeholder box */ - boxaAddBox(boxad, box, L_INSERT); - } - } - boxaDestroy(&boxat); - } - - return boxad; -} - - -/*! - * \brief boxaEncapsulateAligned() - * - * \param[in] boxa - * \param[in] num number put into each boxa in the baa - * \param[in] copyflag L_COPY or L_CLONE - * \return baa, or NULL on error - * - *
- * Notes: - * (1) This puts %num boxes from the input %boxa into each of a - * set of boxa within an output baa. - * (2) This assumes that the boxes in %boxa are in sets of %num each. - *- */ -BOXAA * -boxaEncapsulateAligned(BOXA *boxa, - l_int32 num, - l_int32 copyflag) -{ -l_int32 i, j, n, nbaa, index; -BOX *box; -BOXA *boxat; -BOXAA *baa; - - PROCNAME("boxaEncapsulateAligned"); - - if (!boxa) - return (BOXAA *)ERROR_PTR("boxa not defined", procName, NULL); - if (copyflag != L_COPY && copyflag != L_CLONE) - return (BOXAA *)ERROR_PTR("invalid copyflag", procName, NULL); - - n = boxaGetCount(boxa); - nbaa = n / num; - if (num * nbaa != n) - L_ERROR("inconsistent alignment: num doesn't divide n\n", procName); - baa = boxaaCreate(nbaa); - for (i = 0, index = 0; i < nbaa; i++) { - boxat = boxaCreate(num); - for (j = 0; j < num; j++, index++) { - box = boxaGetBox(boxa, index, copyflag); - boxaAddBox(boxat, box, L_INSERT); - } - boxaaAddBoxa(baa, boxat, L_INSERT); - } - - return baa; -} - - -/*! - * \brief boxaaTranspose() - * - * \param[in] baas - * \return baad, or NULL on error - * - *
- * Notes: - * (1) If you think of a boxaa as a 2D array of boxes that is accessed - * row major, then each row is represented by one of the boxa. - * This function creates a new boxaa related to the input boxaa - * as a column major traversal of the input boxaa. - * (2) For example, if %baas has 2 boxa, each with 10 boxes, then - * %baad will have 10 boxa, each with 2 boxes. - * (3) Require for this transpose operation that each boxa in - * %baas has the same number of boxes. This operation is useful - * when the i-th boxes in each boxa are meaningfully related. - *- */ -BOXAA * -boxaaTranspose(BOXAA *baas) -{ -l_int32 i, j, ny, nb, nbox; -BOX *box; -BOXA *boxa; -BOXAA *baad; - - PROCNAME("boxaaTranspose"); - - if (!baas) - return (BOXAA *)ERROR_PTR("baas not defined", procName, NULL); - if ((ny = boxaaGetCount(baas)) == 0) - return (BOXAA *)ERROR_PTR("baas empty", procName, NULL); - - /* Make sure that each boxa in baas has the same number of boxes */ - for (i = 0; i < ny; i++) { - if ((boxa = boxaaGetBoxa(baas, i, L_CLONE)) == NULL) - return (BOXAA *)ERROR_PTR("baas is missing a boxa", procName, NULL); - nb = boxaGetCount(boxa); - boxaDestroy(&boxa); - if (i == 0) - nbox = nb; - else if (nb != nbox) - return (BOXAA *)ERROR_PTR("boxa are not all the same size", - procName, NULL); - } - - /* baad[i][j] = baas[j][i] */ - baad = boxaaCreate(nbox); - for (i = 0; i < nbox; i++) { - boxa = boxaCreate(ny); - for (j = 0; j < ny; j++) { - box = boxaaGetBox(baas, j, i, L_COPY); - boxaAddBox(boxa, box, L_INSERT); - } - boxaaAddBoxa(baad, boxa, L_INSERT); - } - return baad; -} - - -/*! - * \brief boxaaAlignBox() - * - * \param[in] baa - * \param[in] box to be aligned with bext boxa in the baa, if possible - * \param[in] delta amount by which consecutive components can miss - * in overlap and still be included in the array - * \param[out] pindex index of boxa with best overlap, or if none match, - * this is the index of the next boxa to be generated - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is not greedy. It finds the boxa whose vertical - * extent has the closest overlap with the input box. - *- */ -l_ok -boxaaAlignBox(BOXAA *baa, - BOX *box, - l_int32 delta, - l_int32 *pindex) -{ -l_int32 i, n, m, y, yt, h, ht, ovlp, maxovlp, maxindex; -BOX *boxt; -BOXA *boxa; - - PROCNAME("boxaaAlignBox"); - - if (pindex) *pindex = 0; - if (!baa) - return ERROR_INT("baa not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - if (!pindex) - return ERROR_INT("&index not defined", procName, 1); - - n = boxaaGetCount(baa); - boxGetGeometry(box, NULL, &y, NULL, &h); - maxovlp = -10000000; - for (i = 0; i < n; i++) { - boxa = boxaaGetBoxa(baa, i, L_CLONE); - if ((m = boxaGetCount(boxa)) == 0) { - boxaDestroy(&boxa); - L_WARNING("no boxes in boxa\n", procName); - continue; - } - boxaGetExtent(boxa, NULL, NULL, &boxt); - boxGetGeometry(boxt, NULL, &yt, NULL, &ht); - boxDestroy(&boxt); - boxaDestroy(&boxa); - - /* Overlap < 0 means the components do not overlap vertically */ - if (yt >= y) - ovlp = y + h - 1 - yt; - else - ovlp = yt + ht - 1 - y; - if (ovlp > maxovlp) { - maxovlp = ovlp; - maxindex = i; - } - } - - if (maxovlp + delta >= 0) - *pindex = maxindex; - else - *pindex = n; - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxfunc3.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxfunc3.c deleted file mode 100644 index f0da183f..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxfunc3.c +++ /dev/null @@ -1,1629 +0,0 @@ -/*====================================================================* - - 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 boxfunc3.c - *
- * - * Boxa/Boxaa painting into pix - * PIX *pixMaskConnComp() - * PIX *pixMaskBoxa() - * PIX *pixPaintBoxa() - * PIX *pixSetBlackOrWhiteBoxa() - * PIX *pixPaintBoxaRandom() - * PIX *pixBlendBoxaRandom() - * PIX *pixDrawBoxa() - * PIX *pixDrawBoxaRandom() - * PIX *boxaaDisplay() - * PIXA *pixaDisplayBoxaa() - * - * Split mask components into Boxa - * BOXA *pixSplitIntoBoxa() - * BOXA *pixSplitComponentIntoBoxa() - * static l_int32 pixSearchForRectangle() - * - * Represent horizontal or vertical mosaic strips - * BOXA *makeMosaicStrips() - * - * Comparison between boxa - * l_int32 boxaCompareRegions() - * - * Reliable selection of a single large box - * BOX *pixSelectLargeULComp() - * BOX *boxaSelectLargeULBox() - * - * See summary in pixPaintBoxa() of various ways to paint and draw - * boxes on images. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This generates a mask image with ON pixels over the - * b.b. of the c.c. in pixs. If there are no ON pixels in pixs, - * pixd will also have no ON pixels. - *- */ -PIX * -pixMaskConnComp(PIX *pixs, - l_int32 connectivity, - BOXA **pboxa) -{ -BOXA *boxa; -PIX *pixd; - - PROCNAME("pixMaskConnComp"); - - if (pboxa) *pboxa = NULL; - if (!pixs || pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (connectivity != 4 && connectivity != 8) - return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); - - boxa = pixConnComp(pixs, NULL, connectivity); - pixd = pixCreateTemplate(pixs); - if (boxaGetCount(boxa) != 0) - pixMaskBoxa(pixd, pixd, boxa, L_SET_PIXELS); - if (pboxa) - *pboxa = boxa; - else - boxaDestroy(&boxa); - return pixd; -} - - -/*! - * \brief pixMaskBoxa() - * - * \param[in] pixd [optional] may be NULL - * \param[in] pixs any depth; not cmapped - * \param[in] boxa of boxes, to paint - * \param[in] op L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS - * \return pixd with masking op over the boxes, or NULL on error - * - *
- * Notes: - * (1) This can be used with: - * pixd = NULL (makes a new pixd) - * pixd = pixs (in-place) - * (2) If pixd == NULL, this first makes a copy of pixs, and then - * bit-twiddles over the boxes. Otherwise, it operates directly - * on pixs. - * (3) This simple function is typically used with 1 bpp images. - * It uses the 1-image rasterop function, rasteropUniLow(), - * to set, clear or flip the pixels in pixd. - * (4) If you want to generate a 1 bpp mask of ON pixels from the boxes - * in a Boxa, in a pix of size (w,h): - * pix = pixCreate(w, h, 1); - * pixMaskBoxa(pix, pix, boxa, L_SET_PIXELS); - *- */ -PIX * -pixMaskBoxa(PIX *pixd, - PIX *pixs, - BOXA *boxa, - l_int32 op) -{ -l_int32 i, n, x, y, w, h; -BOX *box; - - PROCNAME("pixMaskBoxa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs is cmapped", procName, NULL); - if (pixd && (pixd != pixs)) - return (PIX *)ERROR_PTR("if pixd, must be in-place", procName, NULL); - if (!boxa) - return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); - if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) - return (PIX *)ERROR_PTR("invalid op", procName, NULL); - - pixd = pixCopy(pixd, pixs); - if ((n = boxaGetCount(boxa)) == 0) { - L_WARNING("no boxes to mask\n", procName); - return pixd; - } - - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - boxGetGeometry(box, &x, &y, &w, &h); - if (op == L_SET_PIXELS) - pixRasterop(pixd, x, y, w, h, PIX_SET, NULL, 0, 0); - else if (op == L_CLEAR_PIXELS) - pixRasterop(pixd, x, y, w, h, PIX_CLR, NULL, 0, 0); - else /* op == L_FLIP_PIXELS */ - pixRasterop(pixd, x, y, w, h, PIX_NOT(PIX_DST), NULL, 0, 0); - boxDestroy(&box); - } - - return pixd; -} - - -/*! - * \brief pixPaintBoxa() - * - * \param[in] pixs any depth, can be cmapped - * \param[in] boxa of boxes, to paint - * \param[in] val rgba color to paint - * \return pixd with painted boxes, or NULL on error - * - *
- * Notes: - * (1) If pixs is 1 bpp or is colormapped, it is converted to 8 bpp - * and the boxa is painted using a colormap; otherwise, - * it is converted to 32 bpp rgb. - * (2) There are several ways to display a box on an image: - * * Paint it as a solid color - * * Draw the outline - * * Blend the outline or region with the existing image - * We provide painting and drawing here; blending is in blend.c. - * When painting or drawing, the result can be either a - * cmapped image or an rgb image. The dest will be cmapped - * if the src is either 1 bpp or has a cmap that is not full. - * To force RGB output, use pixConvertTo8(pixs, FALSE) - * before calling any of these paint and draw functions. - *- */ -PIX * -pixPaintBoxa(PIX *pixs, - BOXA *boxa, - l_uint32 val) -{ -l_int32 i, n, d, rval, gval, bval, newindex; -l_int32 mapvacancy; /* true only if cmap and not full */ -BOX *box; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixPaintBoxa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!boxa) - return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); - - if ((n = boxaGetCount(boxa)) == 0) { - L_WARNING("no boxes to paint; returning a copy\n", procName); - return pixCopy(NULL, pixs); - } - - mapvacancy = FALSE; - if ((cmap = pixGetColormap(pixs)) != NULL) { - if (pixcmapGetCount(cmap) < 256) - mapvacancy = TRUE; - } - if (pixGetDepth(pixs) == 1 || mapvacancy) - pixd = pixConvertTo8(pixs, TRUE); - else - pixd = pixConvertTo32(pixs); - if (!pixd) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - - d = pixGetDepth(pixd); - if (d == 8) { /* colormapped */ - cmap = pixGetColormap(pixd); - extractRGBValues(val, &rval, &gval, &bval); - if (pixcmapAddNewColor(cmap, rval, gval, bval, &newindex)) { - pixDestroy(&pixd); - return (PIX *)ERROR_PTR("cmap full; can't add", procName, NULL); - } - } - - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - if (d == 8) - pixSetInRectArbitrary(pixd, box, newindex); - else - pixSetInRectArbitrary(pixd, box, val); - boxDestroy(&box); - } - - return pixd; -} - - -/*! - * \brief pixSetBlackOrWhiteBoxa() - * - * \param[in] pixs any depth, can be cmapped - * \param[in] boxa [optional] of boxes, to clear or set - * \param[in] op L_SET_BLACK, L_SET_WHITE - * \return pixd with boxes filled with white or black, or NULL on error - */ -PIX * -pixSetBlackOrWhiteBoxa(PIX *pixs, - BOXA *boxa, - l_int32 op) -{ -l_int32 i, n, d, index; -l_uint32 color; -BOX *box; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixSetBlackOrWhiteBoxa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!boxa) - return pixCopy(NULL, pixs); - if ((n = boxaGetCount(boxa)) == 0) - return pixCopy(NULL, pixs); - - pixd = pixCopy(NULL, pixs); - d = pixGetDepth(pixd); - if (d == 1) { - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - if (op == L_SET_WHITE) - pixClearInRect(pixd, box); - else - pixSetInRect(pixd, box); - boxDestroy(&box); - } - return pixd; - } - - cmap = pixGetColormap(pixs); - if (cmap) { - color = (op == L_SET_WHITE) ? 1 : 0; - pixcmapAddBlackOrWhite(cmap, color, &index); - } else if (d == 8) { - color = (op == L_SET_WHITE) ? 0xff : 0x0; - } else if (d == 32) { - color = (op == L_SET_WHITE) ? 0xffffff00 : 0x0; - } else if (d == 2) { - color = (op == L_SET_WHITE) ? 0x3 : 0x0; - } else if (d == 4) { - color = (op == L_SET_WHITE) ? 0xf : 0x0; - } else if (d == 16) { - color = (op == L_SET_WHITE) ? 0xffff : 0x0; - } else { - pixDestroy(&pixd); - return (PIX *)ERROR_PTR("invalid depth", procName, NULL); - } - - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - if (cmap) - pixSetInRectArbitrary(pixd, box, index); - else - pixSetInRectArbitrary(pixd, box, color); - boxDestroy(&box); - } - - return pixd; -} - - -/*! - * \brief pixPaintBoxaRandom() - * - * \param[in] pixs any depth, can be cmapped - * \param[in] boxa of boxes, to paint - * \return pixd with painted boxes, or NULL on error - * - *
- * Notes: - * (1) If pixs is 1 bpp, we paint the boxa using a colormap; - * otherwise, we convert to 32 bpp. - * (2) We use up to 254 different colors for painting the regions. - * (3) If boxes overlap, the later ones paint over earlier ones. - *- */ -PIX * -pixPaintBoxaRandom(PIX *pixs, - BOXA *boxa) -{ -l_int32 i, n, d, rval, gval, bval, index; -l_uint32 val; -BOX *box; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixPaintBoxaRandom"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!boxa) - return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); - - if ((n = boxaGetCount(boxa)) == 0) { - L_WARNING("no boxes to paint; returning a copy\n", procName); - return pixCopy(NULL, pixs); - } - - if (pixGetDepth(pixs) == 1) - pixd = pixConvert1To8(NULL, pixs, 255, 0); - else - pixd = pixConvertTo32(pixs); - if (!pixd) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - - cmap = pixcmapCreateRandom(8, 1, 1); - d = pixGetDepth(pixd); /* either 8 or 32 */ - if (d == 8) /* colormapped */ - pixSetColormap(pixd, cmap); - - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - index = 1 + (i % 254); - if (d == 8) { - pixSetInRectArbitrary(pixd, box, index); - } else { /* d == 32 */ - pixcmapGetColor(cmap, index, &rval, &gval, &bval); - composeRGBPixel(rval, gval, bval, &val); - pixSetInRectArbitrary(pixd, box, val); - } - boxDestroy(&box); - } - - if (d == 32) - pixcmapDestroy(&cmap); - return pixd; -} - - -/*! - * \brief pixBlendBoxaRandom() - * - * \param[in] pixs any depth; can be cmapped - * \param[in] boxa of boxes, to blend/paint - * \param[in] fract of box color to use - * \return pixd 32 bpp, with blend/painted boxes, or NULL on error - * - *
- * Notes: - * (1) pixs is converted to 32 bpp. - * (2) This differs from pixPaintBoxaRandom(), in that the - * colors here are blended with the color of pixs. - * (3) We use up to 254 different colors for painting the regions. - * (4) If boxes overlap, the final color depends only on the last - * rect that is used. - *- */ -PIX * -pixBlendBoxaRandom(PIX *pixs, - BOXA *boxa, - l_float32 fract) -{ -l_int32 i, n, rval, gval, bval, index; -l_uint32 val; -BOX *box; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixBlendBoxaRandom"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!boxa) - return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); - if (fract < 0.0 || fract > 1.0) { - L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName); - fract = 0.5; - } - - if ((n = boxaGetCount(boxa)) == 0) { - L_WARNING("no boxes to paint; returning a copy\n", procName); - return pixCopy(NULL, pixs); - } - - if ((pixd = pixConvertTo32(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not defined", procName, NULL); - - cmap = pixcmapCreateRandom(8, 1, 1); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - index = 1 + (i % 254); - pixcmapGetColor(cmap, index, &rval, &gval, &bval); - composeRGBPixel(rval, gval, bval, &val); - pixBlendInRect(pixd, box, val, fract); - boxDestroy(&box); - } - - pixcmapDestroy(&cmap); - return pixd; -} - - -/*! - * \brief pixDrawBoxa() - * - * \param[in] pixs any depth; can be cmapped - * \param[in] boxa of boxes, to draw - * \param[in] width of lines - * \param[in] val rgba color to draw - * \return pixd with outlines of boxes added, or NULL on error - * - *
- * Notes: - * (1) If pixs is 1 bpp or is colormapped, it is converted to 8 bpp - * and the boxa is drawn using a colormap; otherwise, - * it is converted to 32 bpp rgb. - *- */ -PIX * -pixDrawBoxa(PIX *pixs, - BOXA *boxa, - l_int32 width, - l_uint32 val) -{ -l_int32 rval, gval, bval, newindex; -l_int32 mapvacancy; /* true only if cmap and not full */ -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixDrawBoxa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!boxa) - return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); - if (width < 1) - return (PIX *)ERROR_PTR("width must be >= 1", procName, NULL); - - if (boxaGetCount(boxa) == 0) { - L_WARNING("no boxes to draw; returning a copy\n", procName); - return pixCopy(NULL, pixs); - } - - mapvacancy = FALSE; - if ((cmap = pixGetColormap(pixs)) != NULL) { - if (pixcmapGetCount(cmap) < 256) - mapvacancy = TRUE; - } - if (pixGetDepth(pixs) == 1 || mapvacancy) - pixd = pixConvertTo8(pixs, TRUE); - else - pixd = pixConvertTo32(pixs); - if (!pixd) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - - extractRGBValues(val, &rval, &gval, &bval); - if (pixGetDepth(pixd) == 8) { /* colormapped */ - cmap = pixGetColormap(pixd); - pixcmapAddNewColor(cmap, rval, gval, bval, &newindex); - } - - pixRenderBoxaArb(pixd, boxa, width, rval, gval, bval); - return pixd; -} - - -/*! - * \brief pixDrawBoxaRandom() - * - * \param[in] pixs any depth, can be cmapped - * \param[in] boxa of boxes, to draw - * \param[in] width thickness of line - * \return pixd with box outlines drawn, or NULL on error - * - *
- * Notes: - * (1) If pixs is 1 bpp, we draw the boxa using a colormap; - * otherwise, we convert to 32 bpp. - * (2) We use up to 254 different colors for drawing the boxes. - * (3) If boxes overlap, the later ones draw over earlier ones. - *- */ -PIX * -pixDrawBoxaRandom(PIX *pixs, - BOXA *boxa, - l_int32 width) -{ -l_int32 i, n, rval, gval, bval, index; -BOX *box; -PIX *pixd; -PIXCMAP *cmap; -PTAA *ptaa; - - PROCNAME("pixDrawBoxaRandom"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!boxa) - return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); - if (width < 1) - return (PIX *)ERROR_PTR("width must be >= 1", procName, NULL); - - if ((n = boxaGetCount(boxa)) == 0) { - L_WARNING("no boxes to draw; returning a copy\n", procName); - return pixCopy(NULL, pixs); - } - - /* Input depth = 1 bpp; generate cmapped output */ - if (pixGetDepth(pixs) == 1) { - ptaa = generatePtaaBoxa(boxa); - pixd = pixRenderRandomCmapPtaa(pixs, ptaa, 1, width, 1); - ptaaDestroy(&ptaa); - return pixd; - } - - /* Generate rgb output */ - pixd = pixConvertTo32(pixs); - cmap = pixcmapCreateRandom(8, 1, 1); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - index = 1 + (i % 254); - pixcmapGetColor(cmap, index, &rval, &gval, &bval); - pixRenderBoxArb(pixd, box, width, rval, gval, bval); - boxDestroy(&box); - } - pixcmapDestroy(&cmap); - return pixd; -} - - -/*! - * \brief boxaaDisplay() - * - * \param[in] pixs [optional] 1 bpp - * \param[in] baa boxaa, typically from a 2d sort - * \param[in] linewba line width to display outline of each boxa - * \param[in] linewb line width to display outline of each box - * \param[in] colorba color to display boxa - * \param[in] colorb color to display box - * \param[in] w width of outupt pix; use 0 if determined by %pixs or %baa - * \param[in] h height of outupt pix; use 0 if determined by %pixs or %baa - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) If %pixs exists, this renders the boxes over an 8 bpp version - * of it. Otherwise, it renders the boxes over an empty image - * with a white background. - * (2) If %pixs exists, the dimensions of %pixd are the same, - * and input values of %w and %h are ignored. - * If %pixs is NULL, the dimensions of %pixd are determined by - * - %w and %h if both are > 0, or - * - the minimum size required using all boxes in %baa. - * - *- */ -PIX * -boxaaDisplay(PIX *pixs, - BOXAA *baa, - l_int32 linewba, - l_int32 linewb, - l_uint32 colorba, - l_uint32 colorb, - l_int32 w, - l_int32 h) -{ -l_int32 i, j, n, m, rbox, gbox, bbox, rboxa, gboxa, bboxa; -BOX *box; -BOXA *boxa; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("boxaaDisplay"); - - if (!baa) - return (PIX *)ERROR_PTR("baa not defined", procName, NULL); - - if (w <= 0 || h <= 0) { - if (pixs) - pixGetDimensions(pixs, &w, &h, NULL); - else - boxaaGetExtent(baa, &w, &h, NULL, NULL); - } - - if (pixs) { - pixd = pixConvertTo8(pixs, 1); - cmap = pixGetColormap(pixd); - } else { - pixd = pixCreate(w, h, 8); - cmap = pixcmapCreate(8); - pixSetColormap(pixd, cmap); - pixcmapAddColor(cmap, 255, 255, 255); - } - extractRGBValues(colorb, &rbox, &gbox, &bbox); - extractRGBValues(colorba, &rboxa, &gboxa, &bboxa); - pixcmapAddColor(cmap, rbox, gbox, bbox); - pixcmapAddColor(cmap, rboxa, gboxa, bboxa); - - n = boxaaGetCount(baa); - for (i = 0; i < n; i++) { - boxa = boxaaGetBoxa(baa, i, L_CLONE); - boxaGetExtent(boxa, NULL, NULL, &box); - pixRenderBoxArb(pixd, box, linewba, rboxa, gboxa, bboxa); - boxDestroy(&box); - m = boxaGetCount(boxa); - for (j = 0; j < m; j++) { - box = boxaGetBox(boxa, j, L_CLONE); - pixRenderBoxArb(pixd, box, linewb, rbox, gbox, bbox); - boxDestroy(&box); - } - boxaDestroy(&boxa); - } - - return pixd; -} - - -/*! - * \brief pixaDisplayBoxaa() - * - * \param[in] pixas any depth, can be cmapped - * \param[in] baa boxes to draw on input pixa - * \param[in] colorflag L_DRAW_RED, L_DRAW_GREEN, etc - * \param[in] width thickness of lines - * \return pixa with box outlines drawn on each pix, or NULL on error - * - *
- * Notes: - * (1) All pix in %pixas that are not rgb are converted to rgb. - * (2) Each boxa in %baa contains boxes that will be drawn on - * the corresponding pix in %pixas. - * (3) The color of the boxes drawn on each pix are selected with - * %colorflag: - * * For red, green or blue: use L_DRAW_RED, etc. - * * For sequential r, g, b: use L_DRAW_RGB - * * For random colors: use L_DRAW_RANDOM - *- */ -PIXA * -pixaDisplayBoxaa(PIXA *pixas, - BOXAA *baa, - l_int32 colorflag, - l_int32 width) -{ -l_int32 i, j, nba, n, nbox, rval, gval, bval; -l_uint32 color; -l_uint32 colors[255]; -BOXA *boxa; -BOX *box; -PIX *pix; -PIXA *pixad; - - PROCNAME("pixaDisplayBoxaa"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (!baa) - return (PIXA *)ERROR_PTR("baa not defined", procName, NULL); - if (width < 1) - return (PIXA *)ERROR_PTR("width must be >= 1", procName, NULL); - if ((nba = boxaaGetCount(baa)) < 1) - return (PIXA *)ERROR_PTR("no boxa in baa", procName, NULL); - if ((n = pixaGetCount(pixas)) == 0) - return (PIXA *)ERROR_PTR("no pix in pixas", procName, NULL); - if (n != nba) - return (PIXA *)ERROR_PTR("num pix != num boxa", procName, NULL); - if (colorflag == L_DRAW_RED) - color = 0xff000000; - else if (colorflag == L_DRAW_GREEN) - color = 0x00ff0000; - else if (colorflag == L_DRAW_BLUE) - color = 0x0000ff00; - else if (colorflag == L_DRAW_RGB) - color = 0x000000ff; - else if (colorflag == L_DRAW_RANDOM) - color = 0x00000000; - else - return (PIXA *)ERROR_PTR("invalid colorflag", procName, NULL); - - if (colorflag == L_DRAW_RED || colorflag == L_DRAW_GREEN || - colorflag == L_DRAW_BLUE) { - for (i = 0; i < 255; i++) - colors[i] = color; - } else if (colorflag == L_DRAW_RGB) { - for (i = 0; i < 255; i++) { - if (i % 3 == L_DRAW_RED) - colors[i] = 0xff000000; - else if (i % 3 == L_DRAW_GREEN) - colors[i] = 0x00ff0000; - else /* i % 3 == L_DRAW_BLUE) */ - colors[i] = 0x0000ff00; - } - } else if (colorflag == L_DRAW_RANDOM) { - for (i = 0; i < 255; i++) { - rval = (l_uint32)rand() & 0xff; - gval = (l_uint32)rand() & 0xff; - bval = (l_uint32)rand() & 0xff; - composeRGBPixel(rval, gval, bval, &colors[i]); - } - } - - pixad = pixaCreate(n); - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixas, i, L_COPY); - boxa = boxaaGetBoxa(baa, i, L_CLONE); - nbox = boxaGetCount(boxa); - for (j = 0; j < nbox; j++) { - box = boxaGetBox(boxa, j, L_CLONE); - extractRGBValues(colors[j % 255], &rval, &gval, &bval); - pixRenderBoxArb(pix, box, width, rval, gval, bval); - boxDestroy(&box); - } - boxaDestroy(&boxa); - pixaAddPix(pixad, pix, L_INSERT); - } - - return pixad; -} - - -/*---------------------------------------------------------------------* - * Split mask components into Boxa * - *---------------------------------------------------------------------*/ -/*! - * \brief pixSplitIntoBoxa() - * - * \param[in] pixs 1 bpp - * \param[in] minsum minimum pixels to trigger propagation - * \param[in] skipdist distance before computing sum for propagation - * \param[in] delta difference required to stop propagation - * \param[in] maxbg maximum number of allowed bg pixels in ref scan - * \param[in] maxcomps use 0 for unlimited number of subdivided components - * \param[in] remainder set to 1 to get b.b. of remaining stuff - * \return boxa of rectangles covering the fg of pixs, or NULL on error - * - *
- * Notes: - * (1) This generates a boxa of rectangles that covers - * the fg of a mask. For each 8-connected component in pixs, - * it does a greedy partitioning, choosing the largest - * rectangle found from each of the four directions at each iter. - * See pixSplitComponentIntoBoxa() for details. - * (2) The input parameters give some flexibility for boundary - * noise. The resulting set of rectangles may cover some - * bg pixels. - * (3) This should be used when there are a small number of - * mask components, each of which has sides that are close - * to horizontal and vertical. The input parameters %delta - * and %maxbg determine whether or not holes in the mask are covered. - * (4) The parameter %maxcomps gives the maximum number of allowed - * rectangles extracted from any single connected component. - * Use 0 if no limit is to be applied. - * (5) The flag %remainder specifies whether we take a final bounding - * box for anything left after the maximum number of allowed - * rectangle is extracted. - *- */ -BOXA * -pixSplitIntoBoxa(PIX *pixs, - l_int32 minsum, - l_int32 skipdist, - l_int32 delta, - l_int32 maxbg, - l_int32 maxcomps, - l_int32 remainder) -{ -l_int32 i, n; -BOX *box; -BOXA *boxa, *boxas, *boxad; -PIX *pix; -PIXA *pixas; - - PROCNAME("pixSplitIntoBoxa"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - - boxas = pixConnComp(pixs, &pixas, 8); - n = boxaGetCount(boxas); - boxad = boxaCreate(0); - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixas, i, L_CLONE); - box = boxaGetBox(boxas, i, L_CLONE); - boxa = pixSplitComponentIntoBoxa(pix, box, minsum, skipdist, - delta, maxbg, maxcomps, remainder); - boxaJoin(boxad, boxa, 0, -1); - pixDestroy(&pix); - boxDestroy(&box); - boxaDestroy(&boxa); - } - - pixaDestroy(&pixas); - boxaDestroy(&boxas); - return boxad; -} - - -/*! - * \brief pixSplitComponentIntoBoxa() - * - * \param[in] pix 1 bpp - * \param[in] box [optional] location of pix w/rt an origin - * \param[in] minsum minimum pixels to trigger propagation - * \param[in] skipdist distance before computing sum for propagation - * \param[in] delta difference required to stop propagation - * \param[in] maxbg maximum number of allowed bg pixels in ref scan - * \param[in] maxcomps use 0 for unlimited number of subdivided components - * \param[in] remainder set to 1 to get b.b. of remaining stuff - * \return boxa of rectangles covering the fg of pix, or NULL on error - * - *
- * Notes: - * (1) This generates a boxa of rectangles that covers - * the fg of a mask. It does so by a greedy partitioning of - * the mask, choosing the largest rectangle found from - * each of the four directions at each step. - * (2) The input parameters give some flexibility for boundary - * noise. The resulting set of rectangles must cover all - * the fg pixels and, in addition, may cover some bg pixels. - * Using small input parameters on a noiseless mask (i.e., one - * that has only large vertical and horizontal edges) will - * result in a proper covering of only the fg pixels of the mask. - * (3) The input is assumed to be a single connected component, that - * may have holes. From each side, sweep inward, counting - * the pixels. If the count becomes greater than %minsum, - * and we have moved forward a further amount %skipdist, - * record that count ('countref'), but don't accept if the scan - * contains more than %maxbg bg pixels. Continue the scan - * until we reach a count that differs from countref by at - * least %delta, at which point the propagation stops. The box - * swept out gets a score, which is the sum of fg pixels - * minus a penalty. The penalty is the number of bg pixels - * in the box. This is done from all four sides, and the - * side with the largest score is saved as a rectangle. - * The process repeats until there is either no rectangle - * left, or there is one that can't be captured from any - * direction. For the latter case, we simply accept the - * last rectangle. - * (4) The input box is only used to specify the location of - * the UL corner of pix, with respect to an origin that - * typically represents the UL corner of an underlying image, - * of which pix is one component. If %box is null, - * the UL corner is taken to be (0, 0). - * (5) The parameter %maxcomps gives the maximum number of allowed - * rectangles extracted from any single connected component. - * Use 0 if no limit is to be applied. - * (6) The flag %remainder specifies whether we take a final bounding - * box for anything left after the maximum number of allowed - * rectangle is extracted. - * (7) So if %maxcomps > 0, it specifies that we want no more than - * the first %maxcomps rectangles that satisfy the input - * criteria. After this, we can get a final rectangle that - * bounds everything left over by setting %remainder == 1. - * If %remainder == 0, we only get rectangles that satisfy - * the input criteria. - * (8) It should be noted that the removal of rectangles can - * break the original c.c. into several c.c. - * (9) Summing up: - * * If %maxcomp == 0, the splitting proceeds as far as possible. - * * If %maxcomp > 0, the splitting stops when %maxcomps are - * found, or earlier if no more components can be selected. - * * If %remainder == 1 and components remain that cannot be - * selected, they are returned as a single final rectangle; - * otherwise, they are ignored. - *- */ -BOXA * -pixSplitComponentIntoBoxa(PIX *pix, - BOX *box, - l_int32 minsum, - l_int32 skipdist, - l_int32 delta, - l_int32 maxbg, - l_int32 maxcomps, - l_int32 remainder) -{ -l_int32 i, w, h, boxx, boxy, bx, by, bw, bh, maxdir, maxscore; -l_int32 iter; -BOX *boxs; /* shrinks as rectangular regions are removed */ -BOX *boxt1, *boxt2, *boxt3; -BOXA *boxat; /* stores rectangle data for each side in an iteration */ -BOXA *boxad; -NUMA *nascore, *nas; -PIX *pixs; - - PROCNAME("pixSplitComponentIntoBoxa"); - - if (!pix || pixGetDepth(pix) != 1) - return (BOXA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL); - - pixs = pixCopy(NULL, pix); - pixGetDimensions(pixs, &w, &h, NULL); - if (box) - boxGetGeometry(box, &boxx, &boxy, NULL, NULL); - else - boxx = boxy = 0; - boxs = boxCreate(0, 0, w, h); - boxad = boxaCreate(0); - - iter = 0; - while (boxs != NULL) { - boxGetGeometry(boxs, &bx, &by, &bw, &bh); - boxat = boxaCreate(4); /* potential rectangular regions */ - nascore = numaCreate(4); - for (i = 0; i < 4; i++) { - pixSearchForRectangle(pixs, boxs, minsum, skipdist, delta, maxbg, - i, boxat, nascore); - } - nas = numaGetSortIndex(nascore, L_SORT_DECREASING); - numaGetIValue(nas, 0, &maxdir); - numaGetIValue(nascore, maxdir, &maxscore); -#if DEBUG_SPLIT - lept_stderr("Iteration: %d\n", iter); - boxPrintStreamInfo(stderr, boxs); - boxaWriteStderr(boxat); - lept_stderr("\nmaxdir = %d, maxscore = %d\n\n", maxdir, maxscore); -#endif /* DEBUG_SPLIT */ - if (maxscore > 0) { /* accept this */ - boxt1 = boxaGetBox(boxat, maxdir, L_CLONE); - boxt2 = boxTransform(boxt1, boxx, boxy, 1.0, 1.0); - boxaAddBox(boxad, boxt2, L_INSERT); - pixClearInRect(pixs, boxt1); - boxDestroy(&boxt1); - pixClipBoxToForeground(pixs, boxs, NULL, &boxt3); - boxDestroy(&boxs); - boxs = boxt3; - if (boxs) { - boxGetGeometry(boxs, NULL, NULL, &bw, &bh); - if (bw < 2 || bh < 2) - boxDestroy(&boxs); /* we're done */ - } - } else { /* no more valid rectangles can be found */ - if (remainder == 1) { /* save the last box */ - boxt1 = boxTransform(boxs, boxx, boxy, 1.0, 1.0); - boxaAddBox(boxad, boxt1, L_INSERT); - } - boxDestroy(&boxs); /* we're done */ - } - boxaDestroy(&boxat); - numaDestroy(&nascore); - numaDestroy(&nas); - - iter++; - if ((iter == maxcomps) && boxs) { - if (remainder == 1) { /* save the last box */ - boxt1 = boxTransform(boxs, boxx, boxy, 1.0, 1.0); - boxaAddBox(boxad, boxt1, L_INSERT); - } - boxDestroy(&boxs); /* we're done */ - } - } - - pixDestroy(&pixs); - return boxad; -} - - -/*! - * \brief pixSearchForRectangle() - * - * \param[in] pixs 1 bpp - * \param[in] boxs current region to investigate - * \param[in] minsum minimum pixels to trigger propagation - * \param[in] skipdist distance before computing sum for propagation - * \param[in] delta difference required to stop propagation - * \param[in] maxbg maximum number of allowed bg pixels in ref scan - * \param[in] sideflag side to search from - * \param[in] boxat add result of rectangular region found here - * \param[in] nascore add score for this rectangle here - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) See pixSplitComponentIntoBoxa() for an explanation of the algorithm. - * This does the sweep from a single side. For each iteration - * in pixSplitComponentIntoBoxa(), this will be called 4 times, - * for %sideflag = {0, 1, 2, 3}. - * (2) If a valid rectangle is not found, add a score of 0 and - * input a minimum box. - *- */ -static l_int32 -pixSearchForRectangle(PIX *pixs, - BOX *boxs, - l_int32 minsum, - l_int32 skipdist, - l_int32 delta, - l_int32 maxbg, - l_int32 sideflag, - BOXA *boxat, - NUMA *nascore) -{ -l_int32 bx, by, bw, bh, width, height, setref, atref; -l_int32 minincol, maxincol, mininrow, maxinrow, minval, maxval, bgref; -l_int32 x, y, x0, y0, xref, yref, colsum, rowsum, score, countref, diff; -void **lines1; -BOX *boxr; - - PROCNAME("pixSearchForRectangle"); - - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs undefined or not 1 bpp", procName, 1); - if (!boxs) - return ERROR_INT("boxs not defined", procName, 1); - if (!boxat) - return ERROR_INT("boxat not defined", procName, 1); - if (!nascore) - return ERROR_INT("nascore not defined", procName, 1); - - lines1 = pixGetLinePtrs(pixs, NULL); - boxGetGeometry(boxs, &bx, &by, &bw, &bh); - boxr = NULL; - setref = 0; - atref = 0; - maxval = 0; - minval = 100000; - score = 0; /* sum of all (fg - bg) pixels seen in the scan */ - xref = yref = 100000; /* init to impossibly big number */ - if (sideflag == L_FROM_LEFT) { - for (x = bx; x < bx + bw; x++) { - colsum = 0; - maxincol = 0; - minincol = 100000; - for (y = by; y < by + bh; y++) { - if (GET_DATA_BIT(lines1[y], x)) { - colsum++; - if (y > maxincol) maxincol = y; - if (y < minincol) minincol = y; - } - } - score += colsum; - - /* Enough fg to sweep out a rectangle? */ - if (!setref && colsum >= minsum) { - setref = 1; - xref = x + 10; - if (xref >= bx + bw) - goto failure; - } - - /* Reached the reference line; save the count; - * if there is too much bg, the rectangle is invalid. */ - if (setref && x == xref) { - atref = 1; - countref = colsum; - bgref = maxincol - minincol + 1 - countref; - if (bgref > maxbg) - goto failure; - } - - /* Have we left the rectangle? If so, save it along - * with the score. */ - if (atref) { - diff = L_ABS(colsum - countref); - if (diff >= delta || x == bx + bw - 1) { - height = maxval - minval + 1; - width = x - bx; - if (x == bx + bw - 1) width = x - bx + 1; - boxr = boxCreate(bx, minval, width, height); - score = 2 * score - width * height; - goto success; - } - } - maxval = L_MAX(maxval, maxincol); - minval = L_MIN(minval, minincol); - } - goto failure; - } else if (sideflag == L_FROM_RIGHT) { - for (x = bx + bw - 1; x >= bx; x--) { - colsum = 0; - maxincol = 0; - minincol = 100000; - for (y = by; y < by + bh; y++) { - if (GET_DATA_BIT(lines1[y], x)) { - colsum++; - if (y > maxincol) maxincol = y; - if (y < minincol) minincol = y; - } - } - score += colsum; - if (!setref && colsum >= minsum) { - setref = 1; - xref = x - 10; - if (xref < bx) - goto failure; - } - if (setref && x == xref) { - atref = 1; - countref = colsum; - bgref = maxincol - minincol + 1 - countref; - if (bgref > maxbg) - goto failure; - } - if (atref) { - diff = L_ABS(colsum - countref); - if (diff >= delta || x == bx) { - height = maxval - minval + 1; - x0 = x + 1; - if (x == bx) x0 = x; - width = bx + bw - x0; - boxr = boxCreate(x0, minval, width, height); - score = 2 * score - width * height; - goto success; - } - } - maxval = L_MAX(maxval, maxincol); - minval = L_MIN(minval, minincol); - } - goto failure; - } else if (sideflag == L_FROM_TOP) { - for (y = by; y < by + bh; y++) { - rowsum = 0; - maxinrow = 0; - mininrow = 100000; - for (x = bx; x < bx + bw; x++) { - if (GET_DATA_BIT(lines1[y], x)) { - rowsum++; - if (x > maxinrow) maxinrow = x; - if (x < mininrow) mininrow = x; - } - } - score += rowsum; - if (!setref && rowsum >= minsum) { - setref = 1; - yref = y + 10; - if (yref >= by + bh) - goto failure; - } - if (setref && y == yref) { - atref = 1; - countref = rowsum; - bgref = maxinrow - mininrow + 1 - countref; - if (bgref > maxbg) - goto failure; - } - if (atref) { - diff = L_ABS(rowsum - countref); - if (diff >= delta || y == by + bh - 1) { - width = maxval - minval + 1; - height = y - by; - if (y == by + bh - 1) height = y - by + 1; - boxr = boxCreate(minval, by, width, height); - score = 2 * score - width * height; - goto success; - } - } - maxval = L_MAX(maxval, maxinrow); - minval = L_MIN(minval, mininrow); - } - goto failure; - } else if (sideflag == L_FROM_BOT) { - for (y = by + bh - 1; y >= by; y--) { - rowsum = 0; - maxinrow = 0; - mininrow = 100000; - for (x = bx; x < bx + bw; x++) { - if (GET_DATA_BIT(lines1[y], x)) { - rowsum++; - if (x > maxinrow) maxinrow = x; - if (x < mininrow) mininrow = x; - } - } - score += rowsum; - if (!setref && rowsum >= minsum) { - setref = 1; - yref = y - 10; - if (yref < by) - goto failure; - } - if (setref && y == yref) { - atref = 1; - countref = rowsum; - bgref = maxinrow - mininrow + 1 - countref; - if (bgref > maxbg) - goto failure; - } - if (atref) { - diff = L_ABS(rowsum - countref); - if (diff >= delta || y == by) { - width = maxval - minval + 1; - y0 = y + 1; - if (y == by) y0 = y; - height = by + bh - y0; - boxr = boxCreate(minval, y0, width, height); - score = 2 * score - width * height; - goto success; - } - } - maxval = L_MAX(maxval, maxinrow); - minval = L_MIN(minval, mininrow); - } - goto failure; - } - -failure: - numaAddNumber(nascore, 0); - boxaAddBox(boxat, boxCreate(0, 0, 1, 1), L_INSERT); /* min box */ - LEPT_FREE(lines1); - return 0; - -success: - numaAddNumber(nascore, score); - boxaAddBox(boxat, boxr, L_INSERT); - LEPT_FREE(lines1); - return 0; -} - - -/*---------------------------------------------------------------------* - * Represent horizontal or vertical mosaic strips * - *---------------------------------------------------------------------*/ -/*! - * \brief makeMosaicStrips() - * - * \param[in] w, h - * \param[in] direction L_SCAN_HORIZONTAL or L_SCAN_VERTICAL - * \param[in] size of strips in the scan direction - * \return boxa, or NULL on error - * - *
- * Notes: - * (1) For example, this can be used to generate a pixa of - * vertical strips of width 10 from an image, using: - * pixGetDimensions(pix, &w, &h, NULL); - * boxa = makeMosaicStrips(w, h, L_SCAN_HORIZONTAL, 10); - * pixa = pixClipRectangles(pix, boxa); - * All strips except the last will be the same width. The - * last strip will have width w % 10. - *- */ -BOXA * -makeMosaicStrips(l_int32 w, - l_int32 h, - l_int32 direction, - l_int32 size) -{ -l_int32 i, nstrips, extra; -BOX *box; -BOXA *boxa; - - PROCNAME("makeMosaicStrips"); - - if (w < 1 || h < 1) - return (BOXA *)ERROR_PTR("invalid w or h", procName, NULL); - if (direction != L_SCAN_HORIZONTAL && direction != L_SCAN_VERTICAL) - return (BOXA *)ERROR_PTR("invalid direction", procName, NULL); - if (size < 1) - return (BOXA *)ERROR_PTR("size < 1", procName, NULL); - - boxa = boxaCreate(0); - if (direction == L_SCAN_HORIZONTAL) { - nstrips = w / size; - for (i = 0; i < nstrips; i++) { - box = boxCreate(i * size, 0, size, h); - boxaAddBox(boxa, box, L_INSERT); - } - if ((extra = w % size) > 0) { - box = boxCreate(nstrips * size, 0, extra, h); - boxaAddBox(boxa, box, L_INSERT); - } - } else { - nstrips = h / size; - for (i = 0; i < nstrips; i++) { - box = boxCreate(0, i * size, w, size); - boxaAddBox(boxa, box, L_INSERT); - } - if ((extra = h % size) > 0) { - box = boxCreate(0, nstrips * size, w, extra); - boxaAddBox(boxa, box, L_INSERT); - } - } - return boxa; -} - - -/*---------------------------------------------------------------------* - * Comparison between boxa * - *---------------------------------------------------------------------*/ -/*! - * \brief boxaCompareRegions() - * - * \param[in] boxa1, boxa2 - * \param[in] areathresh minimum area of boxes to be considered - * \param[out] pnsame true if same number of boxes - * \param[out] pdiffarea fractional difference in total area - * \param[out] pdiffxor [optional] fractional difference in xor of regions - * \param[out] ppixdb [optional] debug pix showing two boxa - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This takes 2 boxa, removes all boxes smaller than a given area, - * and compares the remaining boxes between the boxa. - * (2) The area threshold is introduced to help remove noise from - * small components. Any box with a smaller value of w * h - * will be removed from consideration. - * (3) The xor difference is the most stringent test, requiring alignment - * of the corresponding boxes. It is also more computationally - * intensive and is optionally returned. Alignment is to the - * UL corner of each region containing all boxes, as given by - * boxaGetExtent(). - * (4) Both fractional differences are with respect to the total - * area in the two boxa. They range from 0.0 to 1.0. - * A perfect match has value 0.0. If both boxa are empty, - * we return 0.0; if one is empty we return 1.0. - * (5) An example input might be the rectangular regions of a - * segmentation mask for text or images from two pages. - *- */ -l_ok -boxaCompareRegions(BOXA *boxa1, - BOXA *boxa2, - l_int32 areathresh, - l_int32 *pnsame, - l_float32 *pdiffarea, - l_float32 *pdiffxor, - PIX **ppixdb) -{ -l_int32 w, h, x3, y3, w3, h3, x4, y4, w4, h4, n3, n4, area1, area2; -l_int32 count3, count4, countxor; -l_int32 *tab; -BOX *box3, *box4; -BOXA *boxa3, *boxa4, *boxa3t, *boxa4t; -PIX *pix1, *pix2, *pix3, *pix4, *pix5; -PIXA *pixa; - - PROCNAME("boxaCompareRegions"); - - if (pdiffxor) *pdiffxor = 1.0; - if (ppixdb) *ppixdb = NULL; - if (pnsame) *pnsame = FALSE; - if (pdiffarea) *pdiffarea = 1.0; - if (!boxa1 || !boxa2) - return ERROR_INT("boxa1 and boxa2 not both defined", procName, 1); - if (!pnsame) - return ERROR_INT("&nsame not defined", procName, 1); - if (!pdiffarea) - return ERROR_INT("&diffarea not defined", procName, 1); - - boxa3 = boxaSelectByArea(boxa1, areathresh, L_SELECT_IF_GTE, NULL); - boxa4 = boxaSelectByArea(boxa2, areathresh, L_SELECT_IF_GTE, NULL); - n3 = boxaGetCount(boxa3); - n4 = boxaGetCount(boxa4); - if (n3 == n4) - *pnsame = TRUE; - - /* There are no boxes in one or both */ - if (n3 == 0 || n4 == 0) { - boxaDestroy(&boxa3); - boxaDestroy(&boxa4); - if (n3 == 0 && n4 == 0) { /* they are both empty: we say they are the - * same; otherwise, they differ maximally - * and retain the default value. */ - *pdiffarea = 0.0; - if (pdiffxor) *pdiffxor = 0.0; - } - return 0; - } - - /* There are boxes in both */ - boxaGetArea(boxa3, &area1); - boxaGetArea(boxa4, &area2); - *pdiffarea = (l_float32)L_ABS(area1 - area2) / (l_float32)(area1 + area2); - if (!pdiffxor) { - boxaDestroy(&boxa3); - boxaDestroy(&boxa4); - return 0; - } - - /* The easiest way to get the xor of aligned boxes is to work - * with images of each boxa. This is done by translating each - * boxa so that the UL corner of the region that includes all - * boxes in the boxa is placed at the origin of each pix. */ - boxaGetExtent(boxa3, &w, &h, &box3); - boxaGetExtent(boxa4, &w, &h, &box4); - boxGetGeometry(box3, &x3, &y3, &w3, &h3); - boxGetGeometry(box4, &x4, &y4, &w4, &h4); - boxa3t = boxaTransform(boxa3, -x3, -y3, 1.0, 1.0); - boxa4t = boxaTransform(boxa4, -x4, -y4, 1.0, 1.0); - w = L_MAX(x3 + w3, x4 + w4); - h = L_MAX(y3 + h3, y4 + h4); - pix3 = pixCreate(w, h, 1); /* use the max to keep everything in the xor */ - pix4 = pixCreate(w, h, 1); - pixMaskBoxa(pix3, pix3, boxa3t, L_SET_PIXELS); - pixMaskBoxa(pix4, pix4, boxa4t, L_SET_PIXELS); - tab = makePixelSumTab8(); - pixCountPixels(pix3, &count3, tab); - pixCountPixels(pix4, &count4, tab); - pix5 = pixXor(NULL, pix3, pix4); - pixCountPixels(pix5, &countxor, tab); - LEPT_FREE(tab); - *pdiffxor = (l_float32)countxor / (l_float32)(count3 + count4); - - if (ppixdb) { - pixa = pixaCreate(2); - pix1 = pixCreate(w, h, 32); - pixSetAll(pix1); - pixRenderHashBoxaBlend(pix1, boxa3, 5, 1, L_POS_SLOPE_LINE, 2, - 255, 0, 0, 0.5); - pixRenderHashBoxaBlend(pix1, boxa4, 5, 1, L_NEG_SLOPE_LINE, 2, - 0, 255, 0, 0.5); - pixaAddPix(pixa, pix1, L_INSERT); - pix2 = pixCreate(w, h, 32); - pixPaintThroughMask(pix2, pix3, x3, y3, 0xff000000); - pixPaintThroughMask(pix2, pix4, x4, y4, 0x00ff0000); - pixAnd(pix3, pix3, pix4); - pixPaintThroughMask(pix2, pix3, x3, y3, 0x0000ff00); - pixaAddPix(pixa, pix2, L_INSERT); - *ppixdb = pixaDisplayTiledInRows(pixa, 32, 1000, 1.0, 0, 30, 2); - pixaDestroy(&pixa); - } - - boxDestroy(&box3); - boxDestroy(&box4); - boxaDestroy(&boxa3); - boxaDestroy(&boxa3t); - boxaDestroy(&boxa4); - boxaDestroy(&boxa4t); - pixDestroy(&pix3); - pixDestroy(&pix4); - pixDestroy(&pix5); - return 0; -} - - -/*---------------------------------------------------------------------* - * Reliable selection of a single large box * - *---------------------------------------------------------------------*/ -/*! - * \brief pixSelectLargeULComp() - * - * \param[in] pixs 1 bpp - * \param[in] areaslop fraction near but less than 1.0 - * \param[in] yslop number of pixels in y direction - * \param[in] connectivity 4 or 8 - * \return box, or NULL on error - * - *
- * Notes: - * (1) This selects a box near the top (first) and left (second) - * of the image, from the set of all boxes that have - * area >= %areaslop * (area of biggest box), - * where %areaslop is some fraction; say ~ 0.9. - * (2) For all boxes satisfying the above condition, select - * the left-most box that is within %yslop (say, 20) pixels - * of the box nearest the top. - * (3) This can be used to reliably select a specific one of - * the largest regions in an image, for applications where - * there are expected to be small variations in region size - * and location. - * (4) See boxSelectLargeULBox() for implementation details. - *- */ -BOX * -pixSelectLargeULComp(PIX *pixs, - l_float32 areaslop, - l_int32 yslop, - l_int32 connectivity) -{ -BOX *box; -BOXA *boxa1; - - PROCNAME("pixSelectLargeULComp"); - - if (!pixs) - return (BOX *)ERROR_PTR("pixs not defined", procName, NULL); - if (areaslop < 0.0 || areaslop > 1.0) - return (BOX *)ERROR_PTR("invalid value for areaslop", procName, NULL); - yslop = L_MAX(0, yslop); - - boxa1 = pixConnCompBB(pixs, connectivity); - if (boxaGetCount(boxa1) == 0) { - boxaDestroy(&boxa1); - return NULL; - } - box = boxaSelectLargeULBox(boxa1, areaslop, yslop); - boxaDestroy(&boxa1); - return box; -} - - -/*! - * \brief boxaSelectLargeULBox() - * - * \param[in] boxas 1 bpp - * \param[in] areaslop fraction near but less than 1.0 - * \param[in] yslop number of pixels in y direction - * \return box, or NULL on error - * - *
- * Notes: - * (1) See usage notes in pixSelectLargeULComp(). - *- */ -BOX * -boxaSelectLargeULBox(BOXA *boxas, - l_float32 areaslop, - l_int32 yslop) -{ -l_int32 w, h, i, n, x1, y1, x2, y2, select; -l_float32 area, max_area; -BOX *box; -BOXA *boxa1, *boxa2, *boxa3; - - PROCNAME("boxaSelectLargeULBox"); - - if (!boxas) - return (BOX *)ERROR_PTR("boxas not defined", procName, NULL); - if (boxaGetCount(boxas) == 0) - return (BOX *)ERROR_PTR("no boxes in boxas", procName, NULL); - if (areaslop < 0.0 || areaslop > 1.0) - return (BOX *)ERROR_PTR("invalid value for areaslop", procName, NULL); - yslop = L_MAX(0, yslop); - - boxa1 = boxaSort(boxas, L_SORT_BY_AREA, L_SORT_DECREASING, NULL); - boxa2 = boxaSort(boxa1, L_SORT_BY_Y, L_SORT_INCREASING, NULL); - n = boxaGetCount(boxa2); - boxaGetBoxGeometry(boxa1, 0, NULL, NULL, &w, &h); /* biggest box by area */ - max_area = (l_float32)(w * h); - - /* boxa3 collects all boxes eligible by area, sorted top-down */ - boxa3 = boxaCreate(4); - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxa2, i, NULL, NULL, &w, &h); - area = (l_float32)(w * h); - if (area / max_area >= areaslop) { - box = boxaGetBox(boxa2, i, L_COPY); - boxaAddBox(boxa3, box, L_INSERT); - } - } - - /* Take the first (top-most box) unless the second (etc) has - * nearly the same y value but a smaller x value. */ - n = boxaGetCount(boxa3); - boxaGetBoxGeometry(boxa3, 0, &x1, &y1, NULL, NULL); - select = 0; - for (i = 1; i < n; i++) { - boxaGetBoxGeometry(boxa3, i, &x2, &y2, NULL, NULL); - if (y2 - y1 < yslop && x2 < x1) { - select = i; - x1 = x2; /* but always compare against y1 */ - } - } - - box = boxaGetBox(boxa3, select, L_COPY); - boxaDestroy(&boxa1); - boxaDestroy(&boxa2); - boxaDestroy(&boxa3); - return box; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxfunc4.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxfunc4.c deleted file mode 100644 index 9880a51a..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxfunc4.c +++ /dev/null @@ -1,1426 +0,0 @@ -/*====================================================================* - - 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 boxfunc4.c - *
- * - * Boxa and Boxaa range selection - * BOXA *boxaSelectRange() - * BOXAA *boxaaSelectRange() - * - * Boxa size selection - * BOXA *boxaSelectBySize() - * NUMA *boxaMakeSizeIndicator() - * BOXA *boxaSelectByArea() - * NUMA *boxaMakeAreaIndicator() - * BOXA *boxaSelectByWHRatio() - * NUMA *boxaMakeWHRatioIndicator() - * BOXA *boxaSelectWithIndicator() - * - * Boxa permutation - * BOXA *boxaPermutePseudorandom() - * BOXA *boxaPermuteRandom() - * l_int32 boxaSwapBoxes() - * - * Boxa and box conversions - * PTA *boxaConvertToPta() - * BOXA *ptaConvertToBoxa() - * PTA *boxConvertToPta() - * BOX *ptaConvertToBox() - * - * Miscellaneous boxa functions - * l_int32 boxaGetExtent() - * l_int32 boxaGetCoverage() - * l_int32 boxaaSizeRange() - * l_int32 boxaSizeRange() - * l_int32 boxaLocationRange() - * NUMA *boxaGetSizes() - * l_int32 boxaGetArea() - * PIX *boxaDisplayTiled() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) The copyflag specifies what we do with each box from boxas. - * Specifically, L_CLONE inserts a clone into boxad of each - * selected box from boxas. - *- */ -BOXA * -boxaSelectRange(BOXA *boxas, - l_int32 first, - l_int32 last, - l_int32 copyflag) -{ -l_int32 n, nbox, i; -BOX *box; -BOXA *boxad; - - PROCNAME("boxaSelectRange"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (copyflag != L_COPY && copyflag != L_CLONE) - return (BOXA *)ERROR_PTR("invalid copyflag", procName, NULL); - if ((n = boxaGetCount(boxas)) == 0) { - L_WARNING("boxas is empty\n", procName); - return boxaCopy(boxas, copyflag); - } - first = L_MAX(0, first); - if (last < 0) last = n - 1; - if (first >= n) - return (BOXA *)ERROR_PTR("invalid first", procName, NULL); - if (last >= n) { - L_WARNING("last = %d is beyond max index = %d; adjusting\n", - procName, last, n - 1); - last = n - 1; - } - if (first > last) - return (BOXA *)ERROR_PTR("first > last", procName, NULL); - - nbox = last - first + 1; - boxad = boxaCreate(nbox); - for (i = first; i <= last; i++) { - box = boxaGetBox(boxas, i, copyflag); - boxaAddBox(boxad, box, L_INSERT); - } - return boxad; -} - - -/*! - * \brief boxaaSelectRange() - * - * \param[in] baas - * \param[in] first use 0 to select from the beginning - * \param[in] last use -1 to select to the end - * \param[in] copyflag L_COPY, L_CLONE - * \return baad, or NULL on error - * - *
- * Notes: - * (1) The copyflag specifies what we do with each boxa from baas. - * Specifically, L_CLONE inserts a clone into baad of each - * selected boxa from baas. - *- */ -BOXAA * -boxaaSelectRange(BOXAA *baas, - l_int32 first, - l_int32 last, - l_int32 copyflag) -{ -l_int32 n, nboxa, i; -BOXA *boxa; -BOXAA *baad; - - PROCNAME("boxaaSelectRange"); - - if (!baas) - return (BOXAA *)ERROR_PTR("baas not defined", procName, NULL); - if (copyflag != L_COPY && copyflag != L_CLONE) - return (BOXAA *)ERROR_PTR("invalid copyflag", procName, NULL); - if ((n = boxaaGetCount(baas)) == 0) - return (BOXAA *)ERROR_PTR("empty baas", procName, NULL); - first = L_MAX(0, first); - if (last < 0) last = n - 1; - if (first >= n) - return (BOXAA *)ERROR_PTR("invalid first", procName, NULL); - if (last >= n) { - L_WARNING("last = %d is beyond max index = %d; adjusting\n", - procName, last, n - 1); - last = n - 1; - } - if (first > last) - return (BOXAA *)ERROR_PTR("first > last", procName, NULL); - - nboxa = last - first + 1; - baad = boxaaCreate(nboxa); - for (i = first; i <= last; i++) { - boxa = boxaaGetBoxa(baas, i, copyflag); - boxaaAddBoxa(baad, boxa, L_INSERT); - } - return baad; -} - - -/*---------------------------------------------------------------------* - * Boxa size selection * - *---------------------------------------------------------------------*/ -/*! - * \brief boxaSelectBySize() - * - * \param[in] boxas - * \param[in] width, height threshold dimensions - * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT, - * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH - * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \param[out] pchanged [optional] 1 if changed; 0 if clone returned - * \return boxad filtered set, or NULL on error - * - *
- * Notes: - * (1) The args specify constraints on the size of the - * components that are kept. - * (2) Uses box copies in the new boxa. - * (3) If the selection type is L_SELECT_WIDTH, the input - * height is ignored, and v.v. - * (4) To keep small components, use relation = L_SELECT_IF_LT or - * L_SELECT_IF_LTE. - * To keep large components, use relation = L_SELECT_IF_GT or - * L_SELECT_IF_GTE. - *- */ -BOXA * -boxaSelectBySize(BOXA *boxas, - l_int32 width, - l_int32 height, - l_int32 type, - l_int32 relation, - l_int32 *pchanged) -{ -BOXA *boxad; -NUMA *na; - - PROCNAME("boxaSelectBySize"); - - if (pchanged) *pchanged = FALSE; - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (boxaGetCount(boxas) == 0) { - L_WARNING("boxas is empty\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && - type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) - return (BOXA *)ERROR_PTR("invalid type", procName, NULL); - if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && - relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) - return (BOXA *)ERROR_PTR("invalid relation", procName, NULL); - - /* Compute the indicator array for saving components */ - if ((na = - boxaMakeSizeIndicator(boxas, width, height, type, relation)) == NULL) - return (BOXA *)ERROR_PTR("na not made", procName, NULL); - - /* Filter to get output */ - boxad = boxaSelectWithIndicator(boxas, na, pchanged); - - numaDestroy(&na); - return boxad; -} - - -/*! - * \brief boxaMakeSizeIndicator() - * - * \param[in] boxa - * \param[in] width, height threshold dimensions - * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT, - * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH - * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \return na indicator array, or NULL on error - * - *
- * Notes: - * (1) The args specify constraints on the size of the - * components that are kept. - * (2) If the selection type is L_SELECT_WIDTH, the input - * height is ignored, and v.v. - * (3) To keep small components, use relation = L_SELECT_IF_LT or - * L_SELECT_IF_LTE. - * To keep large components, use relation = L_SELECT_IF_GT or - * L_SELECT_IF_GTE. - *- */ -NUMA * -boxaMakeSizeIndicator(BOXA *boxa, - l_int32 width, - l_int32 height, - l_int32 type, - l_int32 relation) -{ -l_int32 i, n, w, h, ival; -NUMA *na; - - PROCNAME("boxaMakeSizeIndicator"); - - if (!boxa) - return (NUMA *)ERROR_PTR("boxa not defined", procName, NULL); - if ((n = boxaGetCount(boxa)) == 0) - return (NUMA *)ERROR_PTR("boxa is empty", procName, NULL); - if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && - type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) - return (NUMA *)ERROR_PTR("invalid type", procName, NULL); - if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && - relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) - return (NUMA *)ERROR_PTR("invalid relation", procName, NULL); - - na = numaCreate(n); - for (i = 0; i < n; i++) { - ival = 0; - boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); - switch (type) - { - case L_SELECT_WIDTH: - if ((relation == L_SELECT_IF_LT && w < width) || - (relation == L_SELECT_IF_GT && w > width) || - (relation == L_SELECT_IF_LTE && w <= width) || - (relation == L_SELECT_IF_GTE && w >= width)) - ival = 1; - break; - case L_SELECT_HEIGHT: - if ((relation == L_SELECT_IF_LT && h < height) || - (relation == L_SELECT_IF_GT && h > height) || - (relation == L_SELECT_IF_LTE && h <= height) || - (relation == L_SELECT_IF_GTE && h >= height)) - ival = 1; - break; - case L_SELECT_IF_EITHER: - if (((relation == L_SELECT_IF_LT) && (w < width || h < height)) || - ((relation == L_SELECT_IF_GT) && (w > width || h > height)) || - ((relation == L_SELECT_IF_LTE) && (w <= width || h <= height)) || - ((relation == L_SELECT_IF_GTE) && (w >= width || h >= height))) - ival = 1; - break; - case L_SELECT_IF_BOTH: - if (((relation == L_SELECT_IF_LT) && (w < width && h < height)) || - ((relation == L_SELECT_IF_GT) && (w > width && h > height)) || - ((relation == L_SELECT_IF_LTE) && (w <= width && h <= height)) || - ((relation == L_SELECT_IF_GTE) && (w >= width && h >= height))) - ival = 1; - break; - default: - L_WARNING("can't get here!\n", procName); - break; - } - numaAddNumber(na, ival); - } - - return na; -} - - -/*! - * \brief boxaSelectByArea() - * - * \param[in] boxas - * \param[in] area threshold value of width * height - * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \param[out] pchanged [optional] 1 if changed; 0 if clone returned - * \return boxad filtered set, or NULL on error - * - *
- * Notes: - * (1) Uses box copies in the new boxa. - * (2) To keep small components, use relation = L_SELECT_IF_LT or - * L_SELECT_IF_LTE. - * To keep large components, use relation = L_SELECT_IF_GT or - * L_SELECT_IF_GTE. - *- */ -BOXA * -boxaSelectByArea(BOXA *boxas, - l_int32 area, - l_int32 relation, - l_int32 *pchanged) -{ -BOXA *boxad; -NUMA *na; - - PROCNAME("boxaSelectByArea"); - - if (pchanged) *pchanged = FALSE; - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (boxaGetCount(boxas) == 0) { - L_WARNING("boxas is empty\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && - relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) - return (BOXA *)ERROR_PTR("invalid relation", procName, NULL); - - /* Compute the indicator array for saving components */ - na = boxaMakeAreaIndicator(boxas, area, relation); - - /* Filter to get output */ - boxad = boxaSelectWithIndicator(boxas, na, pchanged); - - numaDestroy(&na); - return boxad; -} - - -/*! - * \brief boxaMakeAreaIndicator() - * - * \param[in] boxa - * \param[in] area threshold value of width * height - * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \return na indicator array, or NULL on error - * - *
- * Notes: - * (1) To keep small components, use relation = L_SELECT_IF_LT or - * L_SELECT_IF_LTE. - * To keep large components, use relation = L_SELECT_IF_GT or - * L_SELECT_IF_GTE. - *- */ -NUMA * -boxaMakeAreaIndicator(BOXA *boxa, - l_int32 area, - l_int32 relation) -{ -l_int32 i, n, w, h, ival; -NUMA *na; - - PROCNAME("boxaMakeAreaIndicator"); - - if (!boxa) - return (NUMA *)ERROR_PTR("boxa not defined", procName, NULL); - if ((n = boxaGetCount(boxa)) == 0) - return (NUMA *)ERROR_PTR("boxa is empty", procName, NULL); - if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && - relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) - return (NUMA *)ERROR_PTR("invalid relation", procName, NULL); - - na = numaCreate(n); - for (i = 0; i < n; i++) { - ival = 0; - boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); - - if ((relation == L_SELECT_IF_LT && w * h < area) || - (relation == L_SELECT_IF_GT && w * h > area) || - (relation == L_SELECT_IF_LTE && w * h <= area) || - (relation == L_SELECT_IF_GTE && w * h >= area)) - ival = 1; - numaAddNumber(na, ival); - } - - return na; -} - - -/*! - * \brief boxaSelectByWHRatio() - * - * \param[in] boxas - * \param[in] ratio width/height threshold value - * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \param[out] pchanged [optional] 1 if changed; 0 if clone returned - * \return boxad filtered set, or NULL on error - * - *
- * Notes: - * (1) Uses box copies in the new boxa. - * (2) To keep narrow components, use relation = L_SELECT_IF_LT or - * L_SELECT_IF_LTE. - * To keep wide components, use relation = L_SELECT_IF_GT or - * L_SELECT_IF_GTE. - *- */ -BOXA * -boxaSelectByWHRatio(BOXA *boxas, - l_float32 ratio, - l_int32 relation, - l_int32 *pchanged) -{ -BOXA *boxad; -NUMA *na; - - PROCNAME("boxaSelectByWHRatio"); - - if (pchanged) *pchanged = FALSE; - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (boxaGetCount(boxas) == 0) { - L_WARNING("boxas is empty\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && - relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) - return (BOXA *)ERROR_PTR("invalid relation", procName, NULL); - - /* Compute the indicator array for saving components */ - na = boxaMakeWHRatioIndicator(boxas, ratio, relation); - - /* Filter to get output */ - boxad = boxaSelectWithIndicator(boxas, na, pchanged); - - numaDestroy(&na); - return boxad; -} - - -/*! - * \brief boxaMakeWHRatioIndicator() - * - * \param[in] boxa - * \param[in] ratio width/height threshold value - * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \return na indicator array, or NULL on error - * - *
- * Notes: - * (1) To keep narrow components, use relation = L_SELECT_IF_LT or - * L_SELECT_IF_LTE. - * To keep wide components, use relation = L_SELECT_IF_GT or - * L_SELECT_IF_GTE. - *- */ -NUMA * -boxaMakeWHRatioIndicator(BOXA *boxa, - l_float32 ratio, - l_int32 relation) -{ -l_int32 i, n, w, h, ival; -l_float32 whratio; -NUMA *na; - - PROCNAME("boxaMakeWHRatioIndicator"); - - if (!boxa) - return (NUMA *)ERROR_PTR("boxa not defined", procName, NULL); - if ((n = boxaGetCount(boxa)) == 0) - return (NUMA *)ERROR_PTR("boxa is empty", procName, NULL); - if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && - relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) - return (NUMA *)ERROR_PTR("invalid relation", procName, NULL); - - na = numaCreate(n); - for (i = 0; i < n; i++) { - ival = 0; - boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); - whratio = (l_float32)w / (l_float32)h; - - if ((relation == L_SELECT_IF_LT && whratio < ratio) || - (relation == L_SELECT_IF_GT && whratio > ratio) || - (relation == L_SELECT_IF_LTE && whratio <= ratio) || - (relation == L_SELECT_IF_GTE && whratio >= ratio)) - ival = 1; - numaAddNumber(na, ival); - } - - return na; -} - - -/*! - * \brief boxaSelectWithIndicator() - * - * \param[in] boxas - * \param[in] na indicator numa - * \param[out] pchanged [optional] 1 if changed; 0 if clone returned - * \return boxad, or NULL on error - * - *
- * Notes: - * (1) Returns a copy of the boxa if no components are removed. - * (2) Uses box copies in the new boxa. - * (3) The indicator numa has values 0 (ignore) and 1 (accept). - * (4) If all indicator values are 0, the returned boxa is empty. - *- */ -BOXA * -boxaSelectWithIndicator(BOXA *boxas, - NUMA *na, - l_int32 *pchanged) -{ -l_int32 i, n, ival, nsave; -BOX *box; -BOXA *boxad; - - PROCNAME("boxaSelectWithIndicator"); - - if (pchanged) *pchanged = FALSE; - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (!na) - return (BOXA *)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 boxaCopy(boxas, L_COPY); - } - if (pchanged) *pchanged = TRUE; - boxad = boxaCreate(nsave); - for (i = 0; i < n; i++) { - numaGetIValue(na, i, &ival); - if (ival == 0) continue; - box = boxaGetBox(boxas, i, L_COPY); - boxaAddBox(boxad, box, L_INSERT); - } - - return boxad; -} - - -/*---------------------------------------------------------------------* - * Boxa Permutation * - *---------------------------------------------------------------------*/ -/*! - * \brief boxaPermutePseudorandom() - * - * \param[in] boxas input boxa - * \return boxad with boxes permuted, or NULL on error - * - *
- * Notes: - * (1) This does a pseudorandom in-place permutation of the boxes. - * (2) The result is guaranteed not to have any boxes in their - * original position, but it is not very random. If you - * need randomness, use boxaPermuteRandom(). - *- */ -BOXA * -boxaPermutePseudorandom(BOXA *boxas) -{ -l_int32 n; -NUMA *na; -BOXA *boxad; - - PROCNAME("boxaPermutePseudorandom"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL); - - n = boxaGetCount(boxas); - na = numaPseudorandomSequence(n, 0); - boxad = boxaSortByIndex(boxas, na); - numaDestroy(&na); - return boxad; -} - - -/*! - * \brief boxaPermuteRandom() - * - * \param[in] boxad [optional] can be null or equal to boxas - * \param[in] boxas input boxa - * \return boxad with boxes permuted, or NULL on error - * - *
- * Notes: - * (1) If boxad is null, make a copy of boxas and permute the copy. - * Otherwise, boxad must be equal to boxas, and the operation - * is done in-place. - * (2) If boxas is empty, return an empty boxad. - * (3) This does a random in-place permutation of the boxes, - * by swapping each box in turn with a random box. The - * result is almost guaranteed not to have any boxes in their - * original position. - * (4) MSVC rand() has MAX_RAND = 2^15 - 1, so it will not do - * a proper permutation is the number of boxes exceeds this. - *- */ -BOXA * -boxaPermuteRandom(BOXA *boxad, - BOXA *boxas) -{ -l_int32 i, n, index; - - PROCNAME("boxaPermuteRandom"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL); - if (boxad && (boxad != boxas)) - return (BOXA *)ERROR_PTR("boxad defined but in-place", procName, NULL); - - if (!boxad) - boxad = boxaCopy(boxas, L_COPY); - if ((n = boxaGetCount(boxad)) == 0) - return boxad; - index = (l_uint32)rand() % n; - index = L_MAX(1, index); - boxaSwapBoxes(boxad, 0, index); - for (i = 1; i < n; i++) { - index = (l_uint32)rand() % n; - if (index == i) index--; - boxaSwapBoxes(boxad, i, index); - } - - return boxad; -} - - -/*! - * \brief boxaSwapBoxes() - * - * \param[in] boxa - * \param[in] i, j two indices of boxes, that are to be swapped - * \return 0 if OK, 1 on error - */ -l_ok -boxaSwapBoxes(BOXA *boxa, - l_int32 i, - l_int32 j) -{ -l_int32 n; -BOX *box; - - PROCNAME("boxaSwapBoxes"); - - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - n = boxaGetCount(boxa); - if (i < 0 || i >= n) - return ERROR_INT("i invalid", procName, 1); - if (j < 0 || j >= n) - return ERROR_INT("j invalid", procName, 1); - if (i == j) - return ERROR_INT("i == j", procName, 1); - - box = boxa->box[i]; - boxa->box[i] = boxa->box[j]; - boxa->box[j] = box; - return 0; -} - - -/*---------------------------------------------------------------------* - * Boxa and Box Conversions * - *---------------------------------------------------------------------*/ -/*! - * \brief boxaConvertToPta() - * - * \param[in] boxa - * \param[in] ncorners 2 or 4 for the representation of each box - * \return pta with %ncorners points for each box in the boxa, - * or NULL on error - * - *
- * Notes: - * (1) If ncorners == 2, we select the UL and LR corners. - * Otherwise we save all 4 corners in this order: UL, UR, LL, LR. - * (2) Other boxa --> pta functions are: - * * boxaExtractAsPta(): allows extraction of any dimension - * and/or side location, with each in a separate pta. - * * boxaExtractCorners(): extracts any of the four corners as a pta. - *- */ -PTA * -boxaConvertToPta(BOXA *boxa, - l_int32 ncorners) -{ -l_int32 i, n; -BOX *box; -PTA *pta, *pta1; - - PROCNAME("boxaConvertToPta"); - - if (!boxa) - return (PTA *)ERROR_PTR("boxa not defined", procName, NULL); - if (ncorners != 2 && ncorners != 4) - return (PTA *)ERROR_PTR("ncorners not 2 or 4", procName, NULL); - - n = boxaGetCount(boxa); - if ((pta = ptaCreate(n)) == NULL) - return (PTA *)ERROR_PTR("pta not made", procName, NULL); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_COPY); - pta1 = boxConvertToPta(box, ncorners); - ptaJoin(pta, pta1, 0, -1); - boxDestroy(&box); - ptaDestroy(&pta1); - } - - return pta; -} - - -/*! - * \brief ptaConvertToBoxa() - * - * \param[in] pta - * \param[in] ncorners 2 or 4 for the representation of each box - * \return boxa with one box for each 2 or 4 points in the pta, - * or NULL on error - * - *
- * Notes: - * (1) For 2 corners, the order of the 2 points is UL, LR. - * For 4 corners, the order of points is UL, UR, LL, LR. - * (2) Each derived box is the minimum size containing all corners. - *- */ -BOXA * -ptaConvertToBoxa(PTA *pta, - l_int32 ncorners) -{ -l_int32 i, n, nbox, x1, y1, x2, y2, x3, y3, x4, y4, x, y, xmax, ymax; -BOX *box; -BOXA *boxa; - - PROCNAME("ptaConvertToBoxa"); - - if (!pta) - return (BOXA *)ERROR_PTR("pta not defined", procName, NULL); - if (ncorners != 2 && ncorners != 4) - return (BOXA *)ERROR_PTR("ncorners not 2 or 4", procName, NULL); - n = ptaGetCount(pta); - if (n % ncorners != 0) - return (BOXA *)ERROR_PTR("size % ncorners != 0", procName, NULL); - nbox = n / ncorners; - if ((boxa = boxaCreate(nbox)) == NULL) - return (BOXA *)ERROR_PTR("boxa not made", procName, NULL); - for (i = 0; i < n; i += ncorners) { - ptaGetIPt(pta, i, &x1, &y1); - ptaGetIPt(pta, i + 1, &x2, &y2); - if (ncorners == 2) { - box = boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1); - boxaAddBox(boxa, box, L_INSERT); - continue; - } - ptaGetIPt(pta, i + 2, &x3, &y3); - ptaGetIPt(pta, i + 3, &x4, &y4); - x = L_MIN(x1, x3); - y = L_MIN(y1, y2); - xmax = L_MAX(x2, x4); - ymax = L_MAX(y3, y4); - box = boxCreate(x, y, xmax - x + 1, ymax - y + 1); - boxaAddBox(boxa, box, L_INSERT); - } - - return boxa; -} - - -/*! - * \brief boxConvertToPta() - * - * \param[in] box - * \param[in] ncorners 2 or 4 for the representation of the box - * \return pta with %ncorners points, or NULL on error - * - *
- * Notes: - * (1) If ncorners == 2, we select the UL and LR corners. - * Otherwise we save all 4 corners in this order: UL, UR, LL, LR. - *- */ -PTA * -boxConvertToPta(BOX *box, - l_int32 ncorners) -{ -l_int32 x, y, w, h; -PTA *pta; - - PROCNAME("boxConvertToPta"); - - if (!box) - return (PTA *)ERROR_PTR("box not defined", procName, NULL); - if (ncorners != 2 && ncorners != 4) - return (PTA *)ERROR_PTR("ncorners not 2 or 4", procName, NULL); - - if ((pta = ptaCreate(ncorners)) == NULL) - return (PTA *)ERROR_PTR("pta not made", procName, NULL); - boxGetGeometry(box, &x, &y, &w, &h); - ptaAddPt(pta, x, y); - if (ncorners == 2) { - ptaAddPt(pta, x + w - 1, y + h - 1); - } else { - ptaAddPt(pta, x + w - 1, y); - ptaAddPt(pta, x, y + h - 1); - ptaAddPt(pta, x + w - 1, y + h - 1); - } - - return pta; -} - - -/*! - * \brief ptaConvertToBox() - * - * \param[in] pta - * \return box minimum containing all points in the pta, or NULL on error - * - *
- * Notes: - * (1) For 2 corners, the order of the 2 points is UL, LR. - * For 4 corners, the order of points is UL, UR, LL, LR. - *- */ -BOX * -ptaConvertToBox(PTA *pta) -{ -l_int32 n, x1, y1, x2, y2, x3, y3, x4, y4, x, y, xmax, ymax; - - PROCNAME("ptaConvertToBox"); - - if (!pta) - return (BOX *)ERROR_PTR("pta not defined", procName, NULL); - n = ptaGetCount(pta); - ptaGetIPt(pta, 0, &x1, &y1); - ptaGetIPt(pta, 1, &x2, &y2); - if (n == 2) - return boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1); - - /* 4 corners */ - ptaGetIPt(pta, 2, &x3, &y3); - ptaGetIPt(pta, 3, &x4, &y4); - x = L_MIN(x1, x3); - y = L_MIN(y1, y2); - xmax = L_MAX(x2, x4); - ymax = L_MAX(y3, y4); - return boxCreate(x, y, xmax - x + 1, ymax - y + 1); -} - - -/*---------------------------------------------------------------------* - * Miscellaneous Boxa functions * - *---------------------------------------------------------------------*/ -/*! - * \brief boxaGetExtent() - * - * \param[in] boxa - * \param[out] pw [optional] width - * \param[out] ph [optional] height - * \param[out] pbox [optional] minimum box containing all boxes in boxa - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This computes the minimum rectangular bounding region - * that contains all valid boxes in a boxa. - * (2) The returned w and h are the minimum size image - * that would contain all boxes untranslated. - * (3) If there are no valid boxes, returned w and h are 0 and - * all parameters in the returned box are 0. This - * is not an error, because an empty boxa is valid and - * boxaGetExtent() is required for serialization. - *- */ -l_ok -boxaGetExtent(BOXA *boxa, - l_int32 *pw, - l_int32 *ph, - BOX **pbox) -{ -l_int32 i, n, x, y, w, h, xmax, ymax, xmin, ymin, found; - - PROCNAME("boxaGetExtent"); - - if (!pw && !ph && !pbox) - return ERROR_INT("no ptrs defined", procName, 1); - if (pw) *pw = 0; - if (ph) *ph = 0; - if (pbox) *pbox = NULL; - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - n = boxaGetCount(boxa); - xmax = ymax = 0; - xmin = ymin = 100000000; - found = FALSE; - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); - if (w <= 0 || h <= 0) - continue; - found = TRUE; - xmin = L_MIN(xmin, x); - ymin = L_MIN(ymin, y); - xmax = L_MAX(xmax, x + w); - ymax = L_MAX(ymax, y + h); - } - if (found == FALSE) /* no valid boxes in boxa */ - xmin = ymin = 0; - if (pw) *pw = xmax; - if (ph) *ph = ymax; - if (pbox) - *pbox = boxCreate(xmin, ymin, xmax - xmin, ymax - ymin); - - return 0; -} - - -/*! - * \brief boxaGetCoverage() - * - * \param[in] boxa - * \param[in] wc, hc dimensions of overall clipping rectangle with UL - * corner at (0, 0 that is covered by the boxes. - * \param[in] exactflag 1 for guaranteeing an exact result; 0 for getting - * an exact result only if the boxes do not overlap - * \param[out] pfract sum of box area as fraction of w * h - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The boxes in boxa are clipped to the input rectangle. - * (2) * When %exactflag == 1, we generate a 1 bpp pix of size - * wc x hc, paint all the boxes black, and count the fg pixels. - * This can take 1 msec on a large page with many boxes. - * * When %exactflag == 0, we clip each box to the wc x hc region - * and sum the resulting areas. This is faster. - * * The results are the same when none of the boxes overlap - * within the wc x hc region. - *- */ -l_ok -boxaGetCoverage(BOXA *boxa, - l_int32 wc, - l_int32 hc, - l_int32 exactflag, - l_float32 *pfract) -{ -l_int32 i, n, x, y, w, h, sum; -BOX *box, *boxc; -PIX *pixt; - - PROCNAME("boxaGetCoverage"); - - if (!pfract) - return ERROR_INT("&fract not defined", procName, 1); - *pfract = 0.0; - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - n = boxaGetCount(boxa); - if (n == 0) - return ERROR_INT("no boxes in boxa", procName, 1); - - if (exactflag == 0) { /* quick and dirty */ - sum = 0; - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - if ((boxc = boxClipToRectangle(box, wc, hc)) != NULL) { - boxGetGeometry(boxc, NULL, NULL, &w, &h); - sum += w * h; - boxDestroy(&boxc); - } - boxDestroy(&box); - } - } else { /* slower and exact */ - pixt = pixCreate(wc, hc, 1); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - boxGetGeometry(box, &x, &y, &w, &h); - pixRasterop(pixt, x, y, w, h, PIX_SET, NULL, 0, 0); - boxDestroy(&box); - } - pixCountPixels(pixt, &sum, NULL); - pixDestroy(&pixt); - } - - *pfract = (l_float32)sum / (l_float32)(wc * hc); - return 0; -} - - -/*! - * \brief boxaaSizeRange() - * - * \param[in] baa - * \param[out] pminw [optional] min width of all boxes - * \param[out] pmaxw [optional] max width of all boxes - * \param[out] pminh [optional] min height of all boxes - * \param[out] pmaxh [optional] max height of all boxes - * \return 0 if OK, 1 on error - */ -l_ok -boxaaSizeRange(BOXAA *baa, - l_int32 *pminw, - l_int32 *pminh, - l_int32 *pmaxw, - l_int32 *pmaxh) -{ -l_int32 minw, minh, maxw, maxh, minbw, minbh, maxbw, maxbh, i, n; -BOXA *boxa; - - PROCNAME("boxaaSizeRange"); - - if (!pminw && !pmaxw && !pminh && !pmaxh) - return ERROR_INT("no data can be returned", procName, 1); - if (pminw) *pminw = 0; - if (pminh) *pminh = 0; - if (pmaxw) *pmaxw = 0; - if (pmaxh) *pmaxh = 0; - if (!baa) - return ERROR_INT("baa not defined", procName, 1); - - minw = minh = 100000000; - maxw = maxh = 0; - n = boxaaGetCount(baa); - for (i = 0; i < n; i++) { - boxa = boxaaGetBoxa(baa, i, L_CLONE); - boxaSizeRange(boxa, &minbw, &minbh, &maxbw, &maxbh); - if (minbw < minw) - minw = minbw; - if (minbh < minh) - minh = minbh; - if (maxbw > maxw) - maxw = maxbw; - if (maxbh > maxh) - maxh = maxbh; - boxaDestroy(&boxa); - } - - if (pminw) *pminw = minw; - if (pminh) *pminh = minh; - if (pmaxw) *pmaxw = maxw; - if (pmaxh) *pmaxh = maxh; - return 0; -} - - -/*! - * \brief boxaSizeRange() - * - * \param[in] boxa - * \param[out] pminw [optional] min width of all boxes - * \param[out] pmaxw [optional] max width of all boxes - * \param[out] pminh [optional] min height of all boxes - * \param[out] pmaxh [optional] max height of all boxes - * \return 0 if OK, 1 on error - */ -l_ok -boxaSizeRange(BOXA *boxa, - l_int32 *pminw, - l_int32 *pminh, - l_int32 *pmaxw, - l_int32 *pmaxh) -{ -l_int32 minw, minh, maxw, maxh, i, n, w, h; - - PROCNAME("boxaSizeRange"); - - if (!pminw && !pmaxw && !pminh && !pmaxh) - return ERROR_INT("no data can be returned", procName, 1); - if (pminw) *pminw = 0; - if (pminh) *pminh = 0; - if (pmaxw) *pmaxw = 0; - if (pmaxh) *pmaxh = 0; - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - minw = minh = 100000000; - maxw = maxh = 0; - n = boxaGetCount(boxa); - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); - if (w < minw) - minw = w; - if (h < minh) - minh = h; - if (w > maxw) - maxw = w; - if (h > maxh) - maxh = h; - } - - if (pminw) *pminw = minw; - if (pminh) *pminh = minh; - if (pmaxw) *pmaxw = maxw; - if (pmaxh) *pmaxh = maxh; - return 0; -} - - -/*! - * \brief boxaLocationRange() - * - * \param[in] boxa - * \param[out] pminx [optional] min (UL corner) x value of all boxes - * \param[out] pminy [optional] min (UL corner) y value of all boxes - * \param[out] pmaxx [optional] max (UL corner) x value of all boxes - * \param[out] pmaxy [optional] max (UL corner) y value of all boxes - * \return 0 if OK, 1 on error - */ -l_ok -boxaLocationRange(BOXA *boxa, - l_int32 *pminx, - l_int32 *pminy, - l_int32 *pmaxx, - l_int32 *pmaxy) -{ -l_int32 minx, miny, maxx, maxy, i, n, x, y; - - PROCNAME("boxaLocationRange"); - - if (!pminx && !pminy && !pmaxx && !pmaxy) - return ERROR_INT("no data can be returned", procName, 1); - if (pminx) *pminx = 0; - if (pminy) *pminy = 0; - if (pmaxx) *pmaxx = 0; - if (pmaxy) *pmaxy = 0; - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - minx = miny = 100000000; - maxx = maxy = 0; - n = boxaGetCount(boxa); - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxa, i, &x, &y, NULL, NULL); - if (x < minx) - minx = x; - if (y < miny) - miny = y; - if (x > maxx) - maxx = x; - if (y > maxy) - maxy = y; - } - - if (pminx) *pminx = minx; - if (pminy) *pminy = miny; - if (pmaxx) *pmaxx = maxx; - if (pmaxy) *pmaxy = maxy; - - return 0; -} - - -/*! - * \brief boxaGetSizes() - * - * \param[in] boxa - * \param[out] pnaw [optional] widths of valid boxes - * \param[out] pnah [optional] heights of valid boxes - * \return 0 if OK, 1 on error - */ -l_ok -boxaGetSizes(BOXA *boxa, - NUMA **pnaw, - NUMA **pnah) -{ -l_int32 i, n, w, h; -BOX *box; - - PROCNAME("boxaGetSizes"); - - if (pnaw) *pnaw = NULL; - if (pnah) *pnah = NULL; - if (!pnaw && !pnah) - return ERROR_INT("no output requested", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - n = boxaGetValidCount(boxa); - if (pnaw) *pnaw = numaCreate(n); - if (pnah) *pnah = numaCreate(n); - for (i = 0; i < n; i++) { - box = boxaGetValidBox(boxa, i, L_COPY); - if (box) { - boxGetGeometry(box, NULL, NULL, &w, &h); - if (pnaw) numaAddNumber(*pnaw, w); - if (pnah) numaAddNumber(*pnah, h); - boxDestroy(&box); - } - } - - return 0; -} - - -/*! - * \brief boxaGetArea() - * - * \param[in] boxa - * \param[out] parea total area of all boxes - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Measures the total area of the boxes, without regard to overlaps. - *- */ -l_ok -boxaGetArea(BOXA *boxa, - l_int32 *parea) -{ -l_int32 i, n, w, h; - - PROCNAME("boxaGetArea"); - - if (!parea) - return ERROR_INT("&area not defined", procName, 1); - *parea = 0; - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - n = boxaGetCount(boxa); - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h); - *parea += w * h; - } - return 0; -} - - -/*! - * \brief boxaDisplayTiled() - * - * \param[in] boxas - * \param[in] pixa [optional] background for each box - * \param[in] first index of first box - * \param[in] last index of last box; use -1 to go to end - * \param[in] maxwidth of output image - * \param[in] linewidth width of box outlines, before scaling - * \param[in] scalefactor applied to every box; use 1.0 for no scaling - * \param[in] background 0 for white, 1 for black; this is the color - * of the spacing between the images - * \param[in] spacing between images, and on outside - * \param[in] border width of black border added to each image; - * use 0 for no border - * \return pixd of tiled images of boxes, or NULL on error - * - *
- * Notes: - * (1) Displays each box separately in a tiled 32 bpp image. - * (2) If pixa is defined, it must have the same count as the boxa, - * and it will be a background over with each box is rendered. - * If pixa is not defined, the boxes will be rendered over - * blank images of identical size. - * (3) See pixaDisplayTiledInRows() for other parameters. - *- */ -PIX * -boxaDisplayTiled(BOXA *boxas, - PIXA *pixa, - l_int32 first, - l_int32 last, - l_int32 maxwidth, - l_int32 linewidth, - l_float32 scalefactor, - l_int32 background, - l_int32 spacing, - l_int32 border) -{ -char buf[32]; -l_int32 i, n, npix, w, h, fontsize; -L_BMF *bmf; -BOX *box; -BOXA *boxa; -PIX *pix1, *pix2, *pixd; -PIXA *pixat; - - PROCNAME("boxaDisplayTiled"); - - if (!boxas) - return (PIX *)ERROR_PTR("boxas not defined", procName, NULL); - - boxa = boxaSaveValid(boxas, L_COPY); - n = boxaGetCount(boxa); - if (pixa) { - npix = pixaGetCount(pixa); - if (n != npix) { - boxaDestroy(&boxa); - return (PIX *)ERROR_PTR("boxa and pixa counts differ", - procName, NULL); - } - } - first = L_MAX(0, first); - if (last < 0) last = n - 1; - if (first >= n) { - boxaDestroy(&boxa); - return (PIX *)ERROR_PTR("invalid first", procName, NULL); - } - if (last >= n) { - L_WARNING("last = %d is beyond max index = %d; adjusting\n", - procName, last, n - 1); - last = n - 1; - } - if (first > last) { - boxaDestroy(&boxa); - return (PIX *)ERROR_PTR("first > last", procName, NULL); - } - - /* Because the bitmap font will be reduced when tiled, choose the - * font size inversely with the scale factor. */ - if (scalefactor > 0.8) - fontsize = 6; - else if (scalefactor > 0.6) - fontsize = 10; - else if (scalefactor > 0.4) - fontsize = 14; - else if (scalefactor > 0.3) - fontsize = 18; - else fontsize = 20; - bmf = bmfCreate(NULL, fontsize); - - pixat = pixaCreate(n); - boxaGetExtent(boxa, &w, &h, NULL); - for (i = first; i <= last; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - if (!pixa) { - pix1 = pixCreate(w, h, 32); - pixSetAll(pix1); - } else { - pix1 = pixaGetPix(pixa, i, L_COPY); - } - pixSetBorderVal(pix1, 0, 0, 0, 2, 0x0000ff00); - snprintf(buf, sizeof(buf), "%d", i); - pix2 = pixAddSingleTextblock(pix1, bmf, buf, 0x00ff0000, - L_ADD_BELOW, NULL); - pixDestroy(&pix1); - pixRenderBoxArb(pix2, box, linewidth, 255, 0, 0); - pixaAddPix(pixat, pix2, L_INSERT); - boxDestroy(&box); - } - bmfDestroy(&bmf); - boxaDestroy(&boxa); - - pixd = pixaDisplayTiledInRows(pixat, 32, maxwidth, scalefactor, background, - spacing, border); - pixaDestroy(&pixat); - return pixd; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxfunc5.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxfunc5.c deleted file mode 100644 index d0ca60d0..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/boxfunc5.c +++ /dev/null @@ -1,2214 +0,0 @@ -/*====================================================================* - - 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 boxfunc5.c - *
- * - * Boxa sequence fitting - * BOXA *boxaSmoothSequenceMedian() - * BOXA *boxaWindowedMedian() - * BOXA *boxaModifyWithBoxa() - * BOXA *boxaConstrainSize() - * BOXA *boxaReconcileEvenOddHeight() - * static l_int32 boxaTestEvenOddHeight() - * BOXA *boxaReconcilePairWidth() - * l_int32 boxaSizeConsistency1() - * l_int32 boxaSizeConsistency2() - * BOXA *boxaReconcileAllByMedian() - * BOXA *boxaReconcileSidesByMedian() - * static void adjustSidePlotName() -- debug - * BOXA *boxaReconcileSizeByMedian() - * l_int32 boxaPlotSides() [for debugging] - * l_int32 boxaPlotSizes() [for debugging] - * BOXA *boxaFillSequence() - * static l_int32 boxaFillAll() - * l_int32 boxaSizeVariation() - * l_int32 boxaMedianDimensions() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) The target width of the sliding window is 2 * %halfwin + 1. - * If necessary, this will be reduced by boxaWindowedMedian(). - * (2) This returns a modified version of %boxas by constructing - * for each input box a box that has been smoothed with windowed - * median filtering. The filtering is done to each of the - * box sides independently, and it is computed separately for - * sequences of even and odd boxes. The output %boxad is - * constructed from the input boxa and the filtered boxa, - * depending on %subflag. See boxaModifyWithBoxa() for - * details on the use of %subflag, %maxdiff and %extrapixels. - * (3) This is useful for removing noise separately in the even - * and odd sets, where the box edge locations can have - * discontinuities but otherwise vary roughly linearly within - * intervals of size %halfwin or larger. - * (4) If you don't need to handle even and odd sets separately, - * just do this: - * boxam = boxaWindowedMedian(boxas, halfwin, debug); - * boxad = boxaModifyWithBoxa(boxas, boxam, subflag, maxdiff, - * extrapixels); - * boxaDestroy(&boxam); - *- */ -BOXA * -boxaSmoothSequenceMedian(BOXA *boxas, - l_int32 halfwin, - l_int32 subflag, - l_int32 maxdiff, - l_int32 extrapixels, - l_int32 debug) -{ -l_int32 n; -BOXA *boxae, *boxao, *boxamede, *boxamedo, *boxame, *boxamo, *boxad; -PIX *pix1; - - PROCNAME("boxaSmoothSequenceMedian"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (halfwin <= 0) { - L_WARNING("halfwin must be > 0; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (maxdiff < 0) { - L_WARNING("maxdiff must be >= 0; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (subflag != L_USE_MINSIZE && subflag != L_USE_MAXSIZE && - subflag != L_SUB_ON_LOC_DIFF && subflag != L_SUB_ON_SIZE_DIFF && - subflag != L_USE_CAPPED_MIN && subflag != L_USE_CAPPED_MAX) { - L_WARNING("invalid subflag; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - if ((n = boxaGetCount(boxas)) < 6) { - L_WARNING("need at least 6 boxes; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - - boxaSplitEvenOdd(boxas, 0, &boxae, &boxao); - if (debug) { - lept_mkdir("lept/smooth"); - boxaWriteDebug("/tmp/lept/smooth/boxae.ba", boxae); - boxaWriteDebug("/tmp/lept/smooth/boxao.ba", boxao); - } - - boxamede = boxaWindowedMedian(boxae, halfwin, debug); - boxamedo = boxaWindowedMedian(boxao, halfwin, debug); - if (debug) { - boxaWriteDebug("/tmp/lept/smooth/boxamede.ba", boxamede); - boxaWriteDebug("/tmp/lept/smooth/boxamedo.ba", boxamedo); - } - - boxame = boxaModifyWithBoxa(boxae, boxamede, subflag, maxdiff, extrapixels); - boxamo = boxaModifyWithBoxa(boxao, boxamedo, subflag, maxdiff, extrapixels); - if (debug) { - boxaWriteDebug("/tmp/lept/smooth/boxame.ba", boxame); - boxaWriteDebug("/tmp/lept/smooth/boxamo.ba", boxamo); - } - - boxad = boxaMergeEvenOdd(boxame, boxamo, 0); - if (debug) { - boxaPlotSides(boxas, NULL, NULL, NULL, NULL, NULL, &pix1); - pixWrite("/tmp/lept/smooth/plotsides1.png", pix1, IFF_PNG); - pixDestroy(&pix1); - boxaPlotSides(boxad, NULL, NULL, NULL, NULL, NULL, &pix1); - pixWrite("/tmp/lept/smooth/plotsides2.png", pix1, IFF_PNG); - pixDestroy(&pix1); - boxaPlotSizes(boxas, NULL, NULL, NULL, &pix1); - pixWrite("/tmp/lept/smooth/plotsizes1.png", pix1, IFF_PNG); - pixDestroy(&pix1); - boxaPlotSizes(boxad, NULL, NULL, NULL, &pix1); - pixWrite("/tmp/lept/smooth/plotsizes2.png", pix1, IFF_PNG); - pixDestroy(&pix1); - } - - boxaDestroy(&boxae); - boxaDestroy(&boxao); - boxaDestroy(&boxamede); - boxaDestroy(&boxamedo); - boxaDestroy(&boxame); - boxaDestroy(&boxamo); - return boxad; -} - - -/*! - * \brief boxaWindowedMedian() - * - * \param[in] boxas source boxa - * \param[in] halfwin half width of window over which the median is found - * \param[in] debug 1 for debug output - * \return boxad smoothed boxa, or NULL on error - * - *
- * Notes: - * (1) This finds a set of boxes (boxad) where each edge of each box is - * a windowed median smoothed value to the edges of the - * input set of boxes (boxas). - * (2) Invalid input boxes are filled from nearby ones. - * (3) The returned boxad can then be used in boxaModifyWithBoxa() - * to selectively change the boxes in the source boxa. - *- */ -BOXA * -boxaWindowedMedian(BOXA *boxas, - l_int32 halfwin, - l_int32 debug) -{ -l_int32 n, i, left, top, right, bot; -BOX *box; -BOXA *boxaf, *boxad; -NUMA *nal, *nat, *nar, *nab, *naml, *namt, *namr, *namb; -PIX *pix1; - - PROCNAME("boxaWindowedMedian"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if ((n = boxaGetCount(boxas)) < 3) { - L_WARNING("less than 3 boxes; returning a copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (halfwin <= 0) { - L_WARNING("halfwin must be > 0; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - - /* Fill invalid boxes in the input sequence */ - if ((boxaf = boxaFillSequence(boxas, L_USE_ALL_BOXES, debug)) == NULL) - return (BOXA *)ERROR_PTR("filled boxa not made", procName, NULL); - - /* Get the windowed median output from each of the sides */ - boxaExtractAsNuma(boxaf, &nal, &nat, &nar, &nab, NULL, NULL, 0); - naml = numaWindowedMedian(nal, halfwin); - namt = numaWindowedMedian(nat, halfwin); - namr = numaWindowedMedian(nar, halfwin); - namb = numaWindowedMedian(nab, halfwin); - - n = boxaGetCount(boxaf); - boxad = boxaCreate(n); - for (i = 0; i < n; i++) { - numaGetIValue(naml, i, &left); - numaGetIValue(namt, i, &top); - numaGetIValue(namr, i, &right); - numaGetIValue(namb, i, &bot); - box = boxCreate(left, top, right - left + 1, bot - top + 1); - boxaAddBox(boxad, box, L_INSERT); - } - - if (debug) { - lept_mkdir("lept/windowed"); - boxaPlotSides(boxaf, NULL, NULL, NULL, NULL, NULL, &pix1); - pixWrite("/tmp/lept/windowed/plotsides1.png", pix1, IFF_PNG); - pixDestroy(&pix1); - boxaPlotSides(boxad, NULL, NULL, NULL, NULL, NULL, &pix1); - pixWrite("/tmp/lept/windowed/plotsides2.png", pix1, IFF_PNG); - pixDestroy(&pix1); - boxaPlotSizes(boxaf, NULL, NULL, NULL, &pix1); - pixWrite("/tmp/lept/windowed/plotsizes1.png", pix1, IFF_PNG); - pixDestroy(&pix1); - boxaPlotSizes(boxad, NULL, NULL, NULL, &pix1); - pixWrite("/tmp/lept/windowed/plotsizes2.png", pix1, IFF_PNG); - pixDestroy(&pix1); - } - - boxaDestroy(&boxaf); - numaDestroy(&nal); - numaDestroy(&nat); - numaDestroy(&nar); - numaDestroy(&nab); - numaDestroy(&naml); - numaDestroy(&namt); - numaDestroy(&namr); - numaDestroy(&namb); - return boxad; -} - - -/*! - * \brief boxaModifyWithBoxa() - * - * \param[in] boxas - * \param[in] boxam boxa with boxes used to modify those in boxas - * \param[in] subflag L_USE_MINSIZE, L_USE_MAXSIZE, - * L_SUB_ON_LOC_DIFF, L_SUB_ON_SIZE_DIFF, - * L_USE_CAPPED_MIN, L_USE_CAPPED_MAX - * \param[in] maxdiff parameter used with L_SUB_ON_LOC_DIFF, - * L_SUB_ON_SIZE_DIFF, L_USE_CAPPED_MIN, - * L_USE_CAPPED_MAX - * \param[in] extrapixels pixels added on all sides (or subtracted - * if %extrapixels < 0) when using - * L_SUB_ON_LOC_DIFF and L_SUB_ON_SIZE_DIFF - * \return boxad result after adjusting boxes in boxas, or NULL on error. - * - *
- * Notes: - * (1) This takes two input boxa (boxas, boxam) and constructs boxad, - * where each box in boxad is generated from the corresponding - * boxes in boxas and boxam. The rule for constructing each - * output box depends on %subflag and %maxdiff. Let boxs be - * a box from %boxas and boxm be a box from %boxam. - * * If %subflag == L_USE_MINSIZE: the output box is the intersection - * of the two input boxes. - * * If %subflag == L_USE_MAXSIZE: the output box is the union of the - * two input boxes; i.e., the minimum bounding rectangle for the - * two input boxes. - * * If %subflag == L_SUB_ON_LOC_DIFF: each side of the output box - * is found separately from the corresponding side of boxs and boxm. - * Use the boxm side, expanded by %extrapixels, if greater than - * %maxdiff pixels from the boxs side. - * * If %subflag == L_SUB_ON_SIZE_DIFF: the sides of the output box - * are determined in pairs from the width and height of boxs - * and boxm. If the boxm width differs by more than %maxdiff - * pixels from boxs, use the boxm left and right sides, - * expanded by %extrapixels. Ditto for the height difference. - * For the last two flags, each side of the output box is found - * separately from the corresponding side of boxs and boxm, - * according to these rules, where "smaller"("bigger") mean in a - * direction that decreases(increases) the size of the output box: - * * If %subflag == L_USE_CAPPED_MIN: use the Min of boxm - * with the Max of (boxs, boxm +- %maxdiff), where the sign - * is adjusted to make the box smaller (e.g., use "+" on left side). - * * If %subflag == L_USE_CAPPED_MAX: use the Max of boxm - * with the Min of (boxs, boxm +- %maxdiff), where the sign - * is adjusted to make the box bigger (e.g., use "-" on left side). - * Use of the last 2 flags is further explained in (3) and (4). - * (2) boxas and boxam must be the same size. If boxam == NULL, - * this returns a copy of boxas with a warning. - * (3) If %subflag == L_SUB_ON_LOC_DIFF, use boxm for each side - * where the corresponding sides differ by more than %maxdiff. - * Two extreme cases: - * (a) set %maxdiff == 0 to use only values from boxam in boxad. - * (b) set %maxdiff == 10000 to ignore all values from boxam; - * then boxad will be the same as boxas. - * (4) If %subflag == L_USE_CAPPED_MAX: use boxm if boxs is smaller; - * use boxs if boxs is bigger than boxm by an amount up to %maxdiff; - * and use boxm +- %maxdiff (the 'capped' value) if boxs is - * bigger than boxm by an amount larger than %maxdiff. - * Similarly, with interchange of Min/Max and sign of %maxdiff, - * for %subflag == L_USE_CAPPED_MIN. - * (5) If either of corresponding boxes in boxas and boxam is invalid, - * an invalid box is copied to the result. - * (6) Typical input for boxam may be the output of boxaLinearFit(). - * where outliers have been removed and each side is LS fit to a line. - * (7) Unlike boxaAdjustWidthToTarget() and boxaAdjustHeightToTarget(), - * this uses two boxes and does not specify target dimensions. - * Additional constraints on the size of each box can be enforced - * by following this operation with boxaConstrainSize(), taking - * boxad as input. - *- */ -BOXA * -boxaModifyWithBoxa(BOXA *boxas, - BOXA *boxam, - l_int32 subflag, - l_int32 maxdiff, - l_int32 extrapixels) -{ -l_int32 n, i, ls, ts, rs, bs, ws, hs, lm, tm, rm, bm, wm, hm, ld, td, rd, bd; -BOX *boxs, *boxm, *boxd, *boxempty; -BOXA *boxad; - - PROCNAME("boxaModifyWithBoxa"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (!boxam) { - L_WARNING("boxam not defined; returning copy", procName); - return boxaCopy(boxas, L_COPY); - } - if (subflag != L_USE_MINSIZE && subflag != L_USE_MAXSIZE && - subflag != L_SUB_ON_LOC_DIFF && subflag != L_SUB_ON_SIZE_DIFF && - subflag != L_USE_CAPPED_MIN && subflag != L_USE_CAPPED_MAX) { - L_WARNING("invalid subflag; returning copy", procName); - return boxaCopy(boxas, L_COPY); - } - n = boxaGetCount(boxas); - if (n != boxaGetCount(boxam)) { - L_WARNING("boxas and boxam sizes differ; returning copy", procName); - return boxaCopy(boxas, L_COPY); - } - - boxad = boxaCreate(n); - boxempty = boxCreate(0, 0, 0, 0); /* placeholders */ - for (i = 0; i < n; i++) { - boxs = boxaGetValidBox(boxas, i, L_CLONE); - boxm = boxaGetValidBox(boxam, i, L_CLONE); - if (!boxs || !boxm) { - boxaAddBox(boxad, boxempty, L_COPY); - } else { - boxGetGeometry(boxs, &ls, &ts, &ws, &hs); - boxGetGeometry(boxm, &lm, &tm, &wm, &hm); - rs = ls + ws - 1; - bs = ts + hs - 1; - rm = lm + wm - 1; - bm = tm + hm - 1; - if (subflag == L_USE_MINSIZE) { - ld = L_MAX(ls, lm); - rd = L_MIN(rs, rm); - td = L_MAX(ts, tm); - bd = L_MIN(bs, bm); - } else if (subflag == L_USE_MAXSIZE) { - ld = L_MIN(ls, lm); - rd = L_MAX(rs, rm); - td = L_MIN(ts, tm); - bd = L_MAX(bs, bm); - } else if (subflag == L_SUB_ON_LOC_DIFF) { - ld = (L_ABS(lm - ls) <= maxdiff) ? ls : lm - extrapixels; - td = (L_ABS(tm - ts) <= maxdiff) ? ts : tm - extrapixels; - rd = (L_ABS(rm - rs) <= maxdiff) ? rs : rm + extrapixels; - bd = (L_ABS(bm - bs) <= maxdiff) ? bs : bm + extrapixels; - } else if (subflag == L_SUB_ON_SIZE_DIFF) { - ld = (L_ABS(wm - ws) <= maxdiff) ? ls : lm - extrapixels; - td = (L_ABS(hm - hs) <= maxdiff) ? ts : tm - extrapixels; - rd = (L_ABS(wm - ws) <= maxdiff) ? rs : rm + extrapixels; - bd = (L_ABS(hm - hs) <= maxdiff) ? bs : bm + extrapixels; - } else if (subflag == L_USE_CAPPED_MIN) { - ld = L_MAX(lm, L_MIN(ls, lm + maxdiff)); - td = L_MAX(tm, L_MIN(ts, tm + maxdiff)); - rd = L_MIN(rm, L_MAX(rs, rm - maxdiff)); - bd = L_MIN(bm, L_MAX(bs, bm - maxdiff)); - } else { /* subflag == L_USE_CAPPED_MAX */ - ld = L_MIN(lm, L_MAX(ls, lm - maxdiff)); - td = L_MIN(tm, L_MAX(ts, tm - maxdiff)); - rd = L_MAX(rm, L_MIN(rs, rm + maxdiff)); - bd = L_MAX(bm, L_MIN(bs, bm + maxdiff)); - } - boxd = boxCreate(ld, td, rd - ld + 1, bd - td + 1); - boxaAddBox(boxad, boxd, L_INSERT); - } - boxDestroy(&boxs); - boxDestroy(&boxm); - } - boxDestroy(&boxempty); - - return boxad; -} - - -/*! - * \brief boxaConstrainSize() - * - * \param[in] boxas - * \param[in] width force width of all boxes to this size; - * input 0 to use the median width - * \param[in] widthflag L_ADJUST_SKIP, L_ADJUST_LEFT, L_ADJUST_RIGHT, - * or L_ADJUST_LEFT_AND_RIGHT - * \param[in] height force height of all boxes to this size; - * input 0 to use the median height - * \param[in] heightflag L_ADJUST_SKIP, L_ADJUST_TOP, L_ADJUST_BOT, - * or L_ADJUST_TOP_AND_BOT - * \return boxad adjusted so all boxes are the same size - * - *
- * Notes: - * (1) Forces either width or height (or both) of every box in - * the boxa to a specified size, by moving the indicated sides. - * (2) Not all input boxes need to be valid. Median values will be - * used with invalid boxes. - * (3) Typical input might be the output of boxaLinearFit(), - * where each side has been fit. - * (4) Unlike boxaAdjustWidthToTarget() and boxaAdjustHeightToTarget(), - * this is not dependent on a difference threshold to change the size. - * (5) On error, a message is issued and a copy of the input boxa - * is returned. - *- */ -BOXA * -boxaConstrainSize(BOXA *boxas, - l_int32 width, - l_int32 widthflag, - l_int32 height, - l_int32 heightflag) -{ -l_int32 n, i, x, y, w, h, invalid; -l_int32 delw, delh, del_left, del_right, del_top, del_bot; -BOX *medbox, *boxs, *boxd; -BOXA *boxad; - - PROCNAME("boxaConstrainSize"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - - /* Need median values if requested or if there are invalid boxes */ - invalid = boxaGetCount(boxas) - boxaGetValidCount(boxas); - medbox = NULL; - if (width == 0 || height == 0 || invalid > 0) { - if (boxaGetMedianVals(boxas, &x, &y, NULL, NULL, &w, &h)) { - L_ERROR("median vals not returned", procName); - return boxaCopy(boxas, L_COPY); - } - medbox = boxCreate(x, y, w, h); - if (width == 0) width = w; - if (height == 0) height = h; - } - - n = boxaGetCount(boxas); - boxad = boxaCreate(n); - for (i = 0; i < n; i++) { - if ((boxs = boxaGetValidBox(boxas, i, L_COPY)) == NULL) - boxs = boxCopy(medbox); - boxGetGeometry(boxs, NULL, NULL, &w, &h); - delw = width - w; - delh = height - h; - del_left = del_right = del_top = del_bot = 0; - if (widthflag == L_ADJUST_LEFT) { - del_left = -delw; - } else if (widthflag == L_ADJUST_RIGHT) { - del_right = delw; - } else { - del_left = -delw / 2; - del_right = delw / 2 + L_SIGN(delw) * (delw & 1); - } - if (heightflag == L_ADJUST_TOP) { - del_top = -delh; - } else if (heightflag == L_ADJUST_BOT) { - del_bot = delh; - } else { - del_top = -delh / 2; - del_bot = delh / 2 + L_SIGN(delh) * (delh & 1); - } - boxd = boxAdjustSides(NULL, boxs, del_left, del_right, - del_top, del_bot); - boxaAddBox(boxad, boxd, L_INSERT); - boxDestroy(&boxs); - } - - boxDestroy(&medbox); - return boxad; -} - - -/*! - * \brief boxaReconcileEvenOddHeight() - * - * \param[in] boxas containing at least 3 valid boxes in even and odd - * \param[in] sides L_ADJUST_TOP, L_ADJUST_BOT, L_ADJUST_TOP_AND_BOT - * \param[in] delh threshold on median height difference - * \param[in] op L_ADJUST_CHOOSE_MIN, L_ADJUST_CHOOSE_MAX - * \param[in] factor > 0.0, typically near 1.0 - * \param[in] start 0 if pairing (0,1), etc; 1 if pairing (1,2), etc - * \return boxad adjusted, or a copy of boxas on error - * - *
- * Notes: - * (1) The basic idea is to reconcile differences in box height - * in the even and odd boxes, by moving the top and/or bottom - * edges in the even and odd boxes. Choose the edge or edges - * to be moved, whether to adjust the boxes with the min - * or the max of the medians, and the threshold on the median - * difference between even and odd box heights for the operations - * to take place. The same threshold is also used to - * determine if each individual box edge is to be adjusted. - * (2) Boxes are conditionally reset with either the same top (y) - * value or the same bottom value, or both. The value is - * determined by the greater or lesser of the medians of the - * even and odd boxes, with the choice depending on the value - * of %op, which selects for either min or max median height. - * If the median difference between even and odd boxes is - * greater than %dely, then any individual box edge that differs - * from the selected median by more than %dely is set to - * the selected median times a factor typically near 1.0. - * (3) Note that if selecting for minimum height, you will choose - * the largest y-value for the top and the smallest y-value for - * the bottom of the box. - * (4) Typical input might be the output of boxaSmoothSequenceMedian(), - * where even and odd boxa have been independently regulated. - * (5) Require at least 3 valid even boxes and 3 valid odd boxes. - * Median values will be used for invalid boxes. - * (6) If the median height is not representative of the boxes - * in %boxas, this can make things much worse. In that case, - * ignore the value of %op, and force pairwise equality of the - * heights, with pairwise maximal vertical extension. - *- */ -BOXA * -boxaReconcileEvenOddHeight(BOXA *boxas, - l_int32 sides, - l_int32 delh, - l_int32 op, - l_float32 factor, - l_int32 start) -{ -l_int32 n, he, ho, hmed, doeven; -l_float32 del1, del2; -BOXA *boxae, *boxao, *boxa1e, *boxa1o, *boxad; - - PROCNAME("boxaReconcileEvenOddHeight"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (sides != L_ADJUST_TOP && sides != L_ADJUST_BOT && - sides != L_ADJUST_TOP_AND_BOT) { - L_WARNING("no action requested; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - if ((n = boxaGetValidCount(boxas)) < 6) { - L_WARNING("need at least 6 valid boxes; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (factor <= 0.0) { - L_WARNING("invalid factor; setting to 1.0\n", procName); - factor = 1.0; - } - - /* Require at least 3 valid boxes of both types */ - boxaSplitEvenOdd(boxas, 0, &boxae, &boxao); - if (boxaGetValidCount(boxae) < 3 || boxaGetValidCount(boxao) < 3) { - boxaDestroy(&boxae); - boxaDestroy(&boxao); - return boxaCopy(boxas, L_COPY); - } - - /* Get the median heights for each set */ - boxaGetMedianVals(boxae, NULL, NULL, NULL, NULL, NULL, &he); - boxaGetMedianVals(boxao, NULL, NULL, NULL, NULL, NULL, &ho); - L_INFO("median he = %d, median ho = %d\n", procName, he, ho); - - /* If the difference in median height reaches the threshold %delh, - * only adjust the side(s) of one of the sets. If we choose - * the minimum median height as the target, allow the target - * to be scaled by a factor, typically near 1.0, of the - * minimum median height. And similarly if the target is - * the maximum median height. */ - if (L_ABS(he - ho) > delh) { - if (op == L_ADJUST_CHOOSE_MIN) { - doeven = (ho < he) ? TRUE : FALSE; - hmed = (l_int32)(factor * L_MIN(he, ho)); - hmed = L_MIN(hmed, L_MAX(he, ho)); /* don't make it bigger! */ - } else { /* max height */ - doeven = (ho > he) ? TRUE : FALSE; - hmed = (l_int32)(factor * L_MAX(he, ho)); - hmed = L_MAX(hmed, L_MIN(he, ho)); /* don't make it smaller! */ - } - if (doeven) { - boxa1e = boxaAdjustHeightToTarget(NULL, boxae, sides, hmed, delh); - boxa1o = boxaCopy(boxao, L_COPY); - } else { /* !doeven */ - boxa1e = boxaCopy(boxae, L_COPY); - boxa1o = boxaAdjustHeightToTarget(NULL, boxao, sides, hmed, delh); - } - } else { - boxa1e = boxaCopy(boxae, L_CLONE); - boxa1o = boxaCopy(boxao, L_CLONE); - } - boxaDestroy(&boxae); - boxaDestroy(&boxao); - - /* It can happen that the median is not a good measure for an - * entire book. In that case, the reconciliation above can do - * more harm than good. Sanity check by comparing height and y - * differences of adjacent even/odd boxes, before and after - * reconciliation. */ - boxad = boxaMergeEvenOdd(boxa1e, boxa1o, 0); - boxaTestEvenOddHeight(boxas, boxad, start, &del1, &del2); - boxaDestroy(&boxa1e); - boxaDestroy(&boxa1o); - if (del2 < del1 + 10.) - return boxad; - - /* Using the median made it worse. Skip reconciliation: - * forcing all pairs of top and bottom values to have - * maximum extent does not improve the situation either. */ - L_INFO("Got worse: del2 = %f > del1 = %f\n", procName, del2, del1); - boxaDestroy(&boxad); - return boxaCopy(boxas, L_COPY); -} - - -/*! - * \brief boxaTestEvenOddHeight() - * - * \param[in] boxa1 input boxa 1 - * \param[in] boxa2 input boxa 2 - * \param[in] start 0 if pairing (0,1), etc; 1 if pairing (1,2), etc - * \param[out] pdel1 root mean of (dely^2 + delh^2 for boxa1 - * \param[out] pdel2 root mean of (dely^2 + delh^2 for boxa2 - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This compares differences in the y location and height of - * adjacent boxes, in each of the input boxa. - *- */ -static l_int32 -boxaTestEvenOddHeight(BOXA *boxa1, - BOXA *boxa2, - l_int32 start, - l_float32 *pdel1, - l_float32 *pdel2) -{ -l_int32 i, n, npairs, y1a, y1b, y2a, y2b, h1a, h1b, h2a, h2b; -l_float32 del1, del2; - - PROCNAME("boxaTestEvenOddHeight"); - - if (pdel1) *pdel1 = 0.0; - if (pdel2) *pdel2 = 0.0; - if (!pdel1 || !pdel2) - return ERROR_INT("&del1 and &del2 not both defined", procName, 1); - if (!boxa1 || !boxa2) - return ERROR_INT("boxa1 and boxa2 not both defined", procName, 1); - n = L_MIN(boxaGetCount(boxa1), boxaGetCount(boxa2)); - - /* For boxa1 and boxa2 separately, we expect the y and h values - * to be similar for adjacent boxes. Get a measure of similarity - * by finding the sum of squares of differences between - * y values and between h values, and adding them. */ - del1 = del2 = 0.0; - npairs = (n - start) / 2; - for (i = start; i < 2 * npairs; i += 2) { - boxaGetBoxGeometry(boxa1, i, NULL, &y1a, NULL, &h1a); - boxaGetBoxGeometry(boxa1, i + 1, NULL, &y1b, NULL, &h1b); - del1 += (l_float32)(y1a - y1b) * (y1a - y1b) - + (h1a - h1b) * (h1a - h1b); - boxaGetBoxGeometry(boxa2, i, NULL, &y2a, NULL, &h2a); - boxaGetBoxGeometry(boxa2, i + 1, NULL, &y2b, NULL, &h2b); - del2 += (l_float32)(y2a - y2b) * (y2a - y2b) - + (h2a - h2b) * (h2a - h2b); - } - - /* Get the root of the average of the sum of square differences */ - *pdel1 = (l_float32)sqrt((l_float64)del1 / (0.5 * n)); - *pdel2 = (l_float32)sqrt((l_float64)del2 / (0.5 * n)); - return 0; -} - - -/*! - * \brief boxaReconcilePairWidth() - * - * \param[in] boxas - * \param[in] delw threshold on adjacent width difference - * \param[in] op L_ADJUST_CHOOSE_MIN, L_ADJUST_CHOOSE_MAX - * \param[in] factor > 0.0, typically near 1.0 - * \param[in] na [optional] indicator array allowing change - * \return boxad adjusted, or a copy of boxas on error - * - *
- * Notes: - * (1) This reconciles differences in the width of adjacent boxes, - * by moving one side of one of the boxes in each pair. - * If the widths in the pair differ by more than some - * threshold, move either the left side for even boxes or - * the right side for odd boxes, depending on if we're choosing - * the min or max. If choosing min, the width of the max is - * set to factor * (width of min). If choosing max, the width - * of the min is set to factor * (width of max). - * (2) If %na exists, it is an indicator array corresponding to the - * boxes in %boxas. If %na != NULL, only boxes with an - * indicator value of 1 are allowed to adjust; otherwise, - * all boxes can adjust. - * (3) Typical input might be the output of boxaSmoothSequenceMedian(), - * where even and odd boxa have been independently regulated. - *- */ -BOXA * -boxaReconcilePairWidth(BOXA *boxas, - l_int32 delw, - l_int32 op, - l_float32 factor, - NUMA *na) -{ -l_int32 i, ne, no, nmin, xe, we, xo, wo, inde, indo, x, w; -BOX *boxe, *boxo; -BOXA *boxae, *boxao, *boxad; - - PROCNAME("boxaReconcilePairWidth"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (factor <= 0.0) { - L_WARNING("invalid factor; setting to 1.0\n", procName); - factor = 1.0; - } - - /* Taking the boxes in pairs, if the difference in width reaches - * the threshold %delw, adjust the left or right side of one - * of the pair. */ - boxaSplitEvenOdd(boxas, 0, &boxae, &boxao); - ne = boxaGetCount(boxae); - no = boxaGetCount(boxao); - nmin = L_MIN(ne, no); - for (i = 0; i < nmin; i++) { - /* Set indicator values */ - if (na) { - numaGetIValue(na, 2 * i, &inde); - numaGetIValue(na, 2 * i + 1, &indo); - } else { - inde = indo = 1; - } - if (inde == 0 && indo == 0) continue; - - boxe = boxaGetBox(boxae, i, L_CLONE); - boxo = boxaGetBox(boxao, i, L_CLONE); - boxGetGeometry(boxe, &xe, NULL, &we, NULL); - boxGetGeometry(boxo, &xo, NULL, &wo, NULL); - if (we == 0 || wo == 0) { /* if either is invalid; skip */ - boxDestroy(&boxe); - boxDestroy(&boxo); - continue; - } else if (L_ABS(we - wo) > delw) { - if (op == L_ADJUST_CHOOSE_MIN) { - if (we > wo && inde == 1) { - /* move left side of even to the right */ - w = factor * wo; - x = xe + (we - w); - boxSetGeometry(boxe, x, -1, w, -1); - } else if (we < wo && indo == 1) { - /* move right side of odd to the left */ - w = factor * we; - boxSetGeometry(boxo, -1, -1, w, -1); - } - } else { /* maximize width */ - if (we < wo && inde == 1) { - /* move left side of even to the left */ - w = factor * wo; - x = L_MAX(0, xe + (we - w)); - w = we + (xe - x); /* covers both cases for the max */ - boxSetGeometry(boxe, x, -1, w, -1); - } else if (we > wo && indo == 1) { - /* move right side of odd to the right */ - w = factor * we; - boxSetGeometry(boxo, -1, -1, w, -1); - } - } - } - boxDestroy(&boxe); - boxDestroy(&boxo); - } - - boxad = boxaMergeEvenOdd(boxae, boxao, 0); - boxaDestroy(&boxae); - boxaDestroy(&boxao); - return boxad; -} - - -/*! - * \brief boxaSizeConsistency1() - * - * \param[in] boxas of size >= 10 - * \param[in] type L_CHECK_WIDTH, L_CHECK_HEIGHT - * \param[in] threshp threshold for pairwise fractional variation - * \param[in] threshm threshold for fractional variation from median - * \param[out] pfvarp [optional] average fractional pairwise variation - * \param[out] pfvarm [optional] average fractional median variation - * \param[out] psame decision for uniformity of page size (1, 0, -1) - * - *
- * Notes: - * (1) This evaluates a boxa for particular types of dimensional - * variation. Select either width or height variation. Then - * it returns two numbers: one is based on pairwise (even/odd) - * variation; the other is based on the average variation - * from the boxa median. - * (2) For the pairwise variation, get the fraction of the absolute - * difference in dimension of each pair of boxes, and take - * the average value. The median variation is simply the - * the average of the fractional deviation from the median - * of all the boxes. - * (3) Use 0 for default values of %threshp and %threshm. They are - * threshp: 0.02 - * threshm: 0.015 - * (4) The intended application is that the boxes are a sequence of - * page regions in a book scan, and we calculate two numbers - * that can give an indication if the pages are approximately - * the same size. The pairwise variation should be small if - * the boxes are correctly calculated. If there are a - * significant number of random or systematic outliers, the - * pairwise variation will be large, and no decision will be made - * (i.e., return same == -1). Here are the possible outcomes: - * Pairwise Var Median Var Decision - * ------------ ---------- -------- - * small small same size (1) - * small large different size (0) - * large small/large unknown (-1) - *- */ -l_ok -boxaSizeConsistency1(BOXA *boxas, - l_int32 type, - l_float32 threshp, - l_float32 threshm, - l_float32 *pfvarp, - l_float32 *pfvarm, - l_int32 *psame) -{ -l_int32 i, n, bw1, bh1, bw2, bh2, npairs; -l_float32 ave, fdiff, sumdiff, med, fvarp, fvarm; -NUMA *na1; - - PROCNAME("boxaSizeConsistency1"); - - if (pfvarp) *pfvarp = 0.0; - if (pfvarm) *pfvarm = 0.0; - if (!psame) - return ERROR_INT("&same not defined", procName, 1); - *psame = -1; - if (!boxas) - return ERROR_INT("boxas not defined", procName, 1); - if (boxaGetValidCount(boxas) < 6) - return ERROR_INT("need a least 6 valid boxes", procName, 1); - if (type != L_CHECK_WIDTH && type != L_CHECK_HEIGHT) - return ERROR_INT("invalid type", procName, 1); - if (threshp < 0.0 || threshp >= 0.5) - return ERROR_INT("invalid threshp", procName, 1); - if (threshm < 0.0 || threshm >= 0.5) - return ERROR_INT("invalid threshm", procName, 1); - if (threshp == 0.0) threshp = 0.02; - if (threshm == 0.0) threshm = 0.015; - - /* Evaluate pairwise variation */ - n = boxaGetCount(boxas); - na1 = numaCreate(0); - for (i = 0, npairs = 0, sumdiff = 0; i < n - 1; i += 2) { - boxaGetBoxGeometry(boxas, i, NULL, NULL, &bw1, &bh1); - boxaGetBoxGeometry(boxas, i + 1, NULL, NULL, &bw2, &bh2); - if (bw1 == 0 || bh1 == 0 || bw2 == 0 || bh2 == 0) - continue; - npairs++; - if (type == L_CHECK_WIDTH) { - ave = (bw1 + bw2) / 2.0; - fdiff = L_ABS(bw1 - bw2) / ave; - numaAddNumber(na1, bw1); - numaAddNumber(na1, bw2); - } else { /* type == L_CHECK_HEIGHT) */ - ave = (bh1 + bh2) / 2.0; - fdiff = L_ABS(bh1 - bh2) / ave; - numaAddNumber(na1, bh1); - numaAddNumber(na1, bh2); - } - sumdiff += fdiff; - } - fvarp = sumdiff / npairs; - if (pfvarp) *pfvarp = fvarp; - - /* Evaluate the average abs fractional deviation from the median */ - numaGetMedian(na1, &med); - if (med == 0.0) { - L_WARNING("median value is 0\n", procName); - } else { - numaGetMeanDevFromMedian(na1, med, &fvarm); - fvarm /= med; - if (pfvarm) *pfvarm = fvarm; - } - numaDestroy(&na1); - - /* Make decision */ - if (fvarp < threshp && fvarm < threshm) - *psame = 1; - else if (fvarp < threshp && fvarm > threshm) - *psame = 0; - else - *psame = -1; /* unknown */ - return 0; -} - - -/*! - * \brief boxaSizeConsistency2() - * - * \param[in] boxas of size >= 10 - * \param[out] pfdevw average fractional deviation from median width - * \param[out] pfdevh average fractional deviation from median height - * \param[in] debug 1 for debug plot output of input and regularized - * width and height - * - *
- * Notes: - * (1) This evaluates a boxa for consistency of the box sizes. - * The intended application is that the boxes are a sequence of - * page regions in a book scan, and the output is a decision - * about whether the pages should be approximately the same size. - * The determination should be robust to outliers, both random - * and (for many cases) systematic. - * (2) This differs from boxaSizeConsistency1() in that it attempts - * to correct for box dimensional errors before doing the - * evaluation. For this reason, it may be less robust. - * (3) Adjacent even and odd boxes are expected to be the same size. - * Take them pairwise, and assume the minimum height, hmin, - * is correct. Then for (the usual case) wmin/hmin > 0.5, assume - * the minimum width is correct. If wmin/hmin <= 0.5, assume - * the maximum width is correct. - * (4) After correcting each pair so that they are the same size, - * compute the average fractional deviation, from median width and - * height. A deviation of width or height by more than about - * 0.02 is evidence that the boxes may be from a non-homogeneous - * source, such as a book with significantly different page sizes. - *- */ -l_ok -boxaSizeConsistency2(BOXA *boxas, - l_float32 *pfdevw, - l_float32 *pfdevh, - l_int32 debug) -{ -l_int32 i, n, bw1, bh1, bw2, bh2, npairs; -l_float32 medw, medh, devw, devh, minw, maxw, minh, w; -BOX *box; -BOXA *boxa1; -NUMA *naw, *nah; -PIX *pix1, *pix2, *pix3; -PIXA *pixa; - - PROCNAME("boxaSizeConsistency2"); - - if (pfdevw) *pfdevw = 0.0; - if (pfdevh) *pfdevh = 0.0; - if (!boxas) - return ERROR_INT("boxas not defined", procName, 1); - if (!pfdevw || !pfdevh) - return ERROR_INT("&fdevw and &fdevh not both defined", procName, 1); - n = boxaGetCount(boxas); - if (n < 10) { - L_WARNING("small boxa; assuming OK", procName); - return 0; - } - - /* Regularize w and h in pairs; skip last box if n is odd */ - boxa1 = (debug) ? boxaCreate(n) : NULL; - naw = numaCreate(0); - nah = numaCreate(0); - for (i = 0, npairs = 0; i < n - 1; i += 2) { - boxaGetBoxGeometry(boxas, i, NULL, NULL, &bw1, &bh1); - boxaGetBoxGeometry(boxas, i + 1, NULL, NULL, &bw2, &bh2); - if (bw1 == 0 || bh1 == 0 || bw2 == 0 || bh2 == 0) - continue; - npairs++; - minw = (l_float32)L_MIN(bw1, bw2); - maxw = (l_float32)L_MAX(bw1, bw2); - minh = (l_float32)L_MIN(bh1, bh2); - w = (minw / minh > 0.5) ? minw : maxw; - numaAddNumber(naw, w); - numaAddNumber(nah, minh); - if (debug) { - box = boxCreate(0, 0, w, minh); - boxaAddBox(boxa1, box, L_COPY); - boxaAddBox(boxa1, box, L_INSERT); - } - } - if (npairs == 0) { - L_WARNING("no valid box pairs\n", procName); - numaDestroy(&naw); - numaDestroy(&nah); - boxaDestroy(&boxa1); - } - - /* Get the median value of the regularized sizes, and find - * the average absolute fractional deviation from the median. */ - numaGetMedian(naw, &medw); - numaGetMedian(nah, &medh); - numaGetMeanDevFromMedian(naw, medw, &devw); - numaGetMeanDevFromMedian(nah, medh, &devh); - *pfdevw = devw / medw; - *pfdevh = devh / medh; - if (debug) { - lept_stderr("medw = %5.1f, medh = %5.1f\n", medw, medh); - lept_stderr("fdevw = %6.3f, fdevh = %6.3f\n", *pfdevw, *pfdevh); - boxaPlotSizes(boxas, "input_boxa", NULL, NULL, &pix1); - boxaPlotSizes(boxa1, "regularized_boxa", NULL, NULL, &pix2); - pixDisplay(pix1, 500, 0); - pixDisplay(pix2, 500, 1000); - pixa = pixaCreate(2); - pixaAddPix(pixa, pix1, L_INSERT); - pixaAddPix(pixa, pix2, L_INSERT); - pix3 = pixaDisplayTiledInColumns(pixa, 2, 1.0, 3, 2); - lept_mkdir("lept/boxa"); - pixWrite("/tmp/lept/boxa/eval.png", pix3, IFF_PNG); - pixDisplay(pix3, 100, 100); - pixDestroy(&pix3); - pixaDestroy(&pixa); - boxaDestroy(&boxa1); - } - - numaDestroy(&naw); - numaDestroy(&nah); - return 0; -} - - -/*! - * \brief boxaReconcileAllByMedian() - * - * \param[in] boxas containing at least 6 valid boxes - * \param[in] select1 L_ADJUST_LEFT_AND_RIGHT or L_ADJUST_SKIP - * \param[in] select2 L_ADJUST_TOP_AND_BOT or L_ADJUST_SKIP - * \param[in] thresh threshold number of pixels to make adjustment - * \param[in] extra extra pixels to add beyond median value - * \param[in] pixadb use NULL to skip debug output - * \return boxad possibly adjusted from boxas; a copy of boxas on error - * - *
- * Notes: - * (1) This uses boxaReconcileSidesByMedian() to reconcile - * the left-and-right and/or top-and-bottom sides of the - * even and odd boxes, separately. - * (2) See boxaReconcileSidesByMedian() for use of %thresh and %extra. - * (3) If all box sides are within %thresh of the median value, - * the returned box will be identical to %boxas. - *- */ -BOXA * -boxaReconcileAllByMedian(BOXA *boxas, - l_int32 select1, - l_int32 select2, - l_int32 thresh, - l_int32 extra, - PIXA *pixadb) - { -l_int32 i, n, diff, ncols; -l_int32 left, right, top, bot, medleft, medright, medtop, medbot; -BOX *box; -BOXA *boxa1e, *boxa1o, *boxa2e, *boxa2o, *boxa3e, *boxa3o, *boxad; -PIX *pix1; - - PROCNAME("boxaReconcileAllByMedian"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (select1 != L_ADJUST_LEFT_AND_RIGHT && select1 != L_ADJUST_SKIP) { - L_WARNING("invalid select1; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (select2 != L_ADJUST_TOP_AND_BOT && select2 != L_ADJUST_SKIP) { - L_WARNING("invalid select2; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (thresh < 0) { - L_WARNING("thresh must be >= 0; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (boxaGetValidCount(boxas) < 3) { - L_WARNING("need at least 3 valid boxes; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - - /* Adjust even and odd box sides separately */ - boxaSplitEvenOdd(boxas, 0, &boxa1e, &boxa1o); - ncols = 1; - if (select1 == L_ADJUST_LEFT_AND_RIGHT) { - ncols += 2; - boxa2e = boxaReconcileSidesByMedian(boxa1e, select1, thresh, - extra, pixadb); - } else { - boxa2e = boxaCopy(boxa1e, L_COPY); - } - if (select2 == L_ADJUST_TOP_AND_BOT) { - ncols += 2; - boxa3e = boxaReconcileSidesByMedian(boxa2e, select2, thresh, - extra, pixadb); - } else { - boxa3e = boxaCopy(boxa2e, L_COPY); - } - if (select1 == L_ADJUST_LEFT_AND_RIGHT) - boxa2o = boxaReconcileSidesByMedian(boxa1o, select1, thresh, - extra, pixadb); - else - boxa2o = boxaCopy(boxa1o, L_COPY); - if (select2 == L_ADJUST_TOP_AND_BOT) - boxa3o = boxaReconcileSidesByMedian(boxa2o, select2, thresh, - extra, pixadb); - else - boxa3o = boxaCopy(boxa2o, L_COPY); - boxad = boxaMergeEvenOdd(boxa3e, boxa3o, 0); - - /* This generates 2 sets of 3 or 5 plots in a row, depending - * on whether select1 and select2 are true (not skipping). - * The top row is for even boxes; the bottom row is for odd boxes. */ - if (pixadb) { - lept_mkdir("lept/boxa"); - pix1 = pixaDisplayTiledInColumns(pixadb, ncols, 1.0, 30, 2); - pixWrite("/tmp/lept/boxa/recon_sides.png", pix1, IFF_PNG); - pixDestroy(&pix1); - } - - boxaDestroy(&boxa1e); - boxaDestroy(&boxa1o); - boxaDestroy(&boxa2e); - boxaDestroy(&boxa2o); - boxaDestroy(&boxa3e); - boxaDestroy(&boxa3o); - return boxad; -} - - -/*! - * \brief boxaReconcileSidesByMedian() - * - * \param[in] boxas containing at least 3 valid boxes - * \param[in] select L_ADJUST_LEFT, L_ADJUST_RIGHT, etc. - * \param[in] thresh threshold number of pixels to make adjustment - * \param[in] extra extra pixels to add beyond median value - * \param[in] pixadb use NULL to skip debug output - * \return boxad possibly adjusted from boxas; a copy of boxas on error - * - *
- * Notes: - * (1) This modifies individual box sides if their location differs - * significantly (>= %thresh) from the median value. - * (2) %select specifies which sides are to be checked. - * (3) %thresh specifies the tolerance for different side locations. - * Any box side that differs from the median by this much will - * be set to the median value, plus the %extra amount. - * (4) If %extra is positive, the box dimensions are expanded. - * For example, for the left side, a positive %extra results in - * moving the left side farther to the left (i.e., in a negative - * direction). - * (5) If all box sides are within %thresh - 1 of the median value, - * the returned box will be identical to %boxas. - * (6) N.B. If you expect that even and odd box sides should be - * significantly different, this function must be called separately - * on the even and odd boxes in %boxas. Note also that the - * higher level function boxaReconcileAllByMedian() handles the - * even and odd box sides separately. - *- */ -BOXA * -boxaReconcileSidesByMedian(BOXA *boxas, - l_int32 select, - l_int32 thresh, - l_int32 extra, - PIXA *pixadb) - { -char buf[128]; -l_int32 i, n, diff; -l_int32 left, right, top, bot, medleft, medright, medtop, medbot; -BOX *box; -BOXA *boxa1, *boxad; -PIX *pix; - - PROCNAME("boxaReconcileSidesByMedian"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (select != L_ADJUST_LEFT && select != L_ADJUST_RIGHT && - select != L_ADJUST_TOP && select != L_ADJUST_BOT && - select != L_ADJUST_LEFT_AND_RIGHT && select != L_ADJUST_TOP_AND_BOT) { - L_WARNING("invalid select; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (thresh < 0) { - L_WARNING("thresh must be >= 0; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (boxaGetValidCount(boxas) < 3) { - L_WARNING("need at least 3 valid boxes; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - - if (select == L_ADJUST_LEFT_AND_RIGHT) { - boxa1 = boxaReconcileSidesByMedian(boxas, L_ADJUST_LEFT, thresh, extra, - pixadb); - boxad = boxaReconcileSidesByMedian(boxa1, L_ADJUST_RIGHT, thresh, extra, - pixadb); - boxaDestroy(&boxa1); - return boxad; - } - if (select == L_ADJUST_TOP_AND_BOT) { - boxa1 = boxaReconcileSidesByMedian(boxas, L_ADJUST_TOP, thresh, extra, - pixadb); - boxad = boxaReconcileSidesByMedian(boxa1, L_ADJUST_BOT, thresh, extra, - pixadb); - boxaDestroy(&boxa1); - return boxad; - } - - if (pixadb) { - l_int32 ndb = pixaGetCount(pixadb); - if (ndb == 0 || ndb == 5) { /* first of even and odd box sets */ - adjustSidePlotName(buf, sizeof(buf), "init", select); - boxaPlotSides(boxas, buf, NULL, NULL, NULL, NULL, &pix); - pixaAddPix(pixadb, pix, L_INSERT); - } - } - - n = boxaGetCount(boxas); - boxad = boxaCreate(n); - if (select == L_ADJUST_LEFT) { - boxaGetMedianVals(boxas, &medleft, NULL, NULL, NULL, NULL, NULL); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxas, i, L_COPY); - boxGetSideLocations(box, &left, NULL, NULL, NULL); - diff = medleft - left; - if (L_ABS(diff) >= thresh) - boxAdjustSides(box, box, diff - extra, 0, 0, 0); - boxaAddBox(boxad, box, L_INSERT); - } - } else if (select == L_ADJUST_RIGHT) { - boxaGetMedianVals(boxas, NULL, NULL, &medright, NULL, NULL, NULL); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxas, i, L_COPY); - boxGetSideLocations(box, NULL, &right, NULL, NULL); - diff = medright - right; - if (L_ABS(diff) >= thresh) - boxAdjustSides(box, box, 0, diff + extra, 0, 0); - boxaAddBox(boxad, box, L_INSERT); - } - } else if (select == L_ADJUST_TOP) { - boxaGetMedianVals(boxas, NULL, &medtop, NULL, NULL, NULL, NULL); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxas, i, L_COPY); - boxGetSideLocations(box, NULL, NULL, &top, NULL); - diff = medtop - top; - if (L_ABS(diff) >= thresh) - boxAdjustSides(box, box, 0, 0, diff - extra, 0); - boxaAddBox(boxad, box, L_INSERT); - } - } else { /* select == L_ADJUST_BOT */ - boxaGetMedianVals(boxas, NULL, NULL, NULL, &medbot, NULL, NULL); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxas, i, L_COPY); - boxGetSideLocations(box, NULL, NULL, NULL, &bot); - diff = medbot - bot; - if (L_ABS(diff) >= thresh) - boxAdjustSides(box, box, 0, 0, 0, diff + extra); - boxaAddBox(boxad, box, L_INSERT); - } - } - - if (pixadb) { - adjustSidePlotName(buf, sizeof(buf), "final", select); - boxaPlotSides(boxad, buf, NULL, NULL, NULL, NULL, &pix); - pixaAddPix(pixadb, pix, L_INSERT); - } - return boxad; -} - - -static void -adjustSidePlotName(char *buf, - size_t size, - const char *preface, - l_int32 select) -{ - stringCopy(buf, preface, size - 8); - if (select == L_ADJUST_LEFT) - stringCat(buf, size, "-left"); - else if (select == L_ADJUST_RIGHT) - stringCat(buf, size, "-right"); - else if (select == L_ADJUST_TOP) - stringCat(buf, size, "-top"); - else if (select == L_ADJUST_BOT) - stringCat(buf, size, "-bot"); -} - - -/*! - * \brief boxaReconcileSizeByMedian() - * - * \param[in] boxas containing at least 6 valid boxes - * \param[in] type L_CHECK_WIDTH, L_CHECK_HEIGHT, L_CHECK_BOTH - * \param[in] dfract threshold fraction of dimensional variation from - * median; in range (0 ... 1); typ. about 0.05. - * \param[in] sfract threshold fraction of side variation from median; - * in range (0 ... 1); typ. about 0.04. - * \param[in] factor expansion for fixed box beyond median width; - * should be near 1.0. - * \param[out] pnadelw [optional] diff from median width for boxes - * above threshold - * \param[out] pnadelh [optional] diff from median height for boxes - * above threshold - * \param[out] pratiowh [optional] ratio of median width/height of boxas - * \return boxad possibly adjusted from boxas; a copy of boxas on error - * - *
- * Notes: - * (1) The basic idea is to identify significant differences in box - * dimension (either width or height) and modify the outlier boxes. - * (2) %type specifies if we are reconciling the width, height or both. - * (3) %dfract specifies the tolerance for different dimensions. Any - * box with a fractional difference from the median size that - * exceeds %dfract will be altered. - * (4) %sfract specifies the tolerance for different side locations. - * If a box has been marked by (3) for alteration, any side - * location that differs from the median side location by - * more than %sfract of the median dimension (medw or medh) - * will be moved. - * (5) Median width and height are found for all valid boxes (i.e., - * for all boxes with width and height > 0. - * Median side locations are found separately for even and odd boxes, - * using only boxes that are "inliers"; i.e., that have been - * found by (3) to be within tolerance for width or height. - * (6) If all box dimensions are within threshold of the median size, - * just return a copy. Otherwise, box sides of the outliers - * will be adjusted. - * (7) Using %sfract, sides that are sufficiently far from the median - * are first moved to the median value. Then they are moved - * together (in or out) so that the final box dimension - * is %factor times the median dimension. - * (8) The arrays that are the initial deviation from median size - * (width and height) are optionally returned. Also optionally - * returned is the median w/h asperity ratio of the input %boxas. - *- */ -BOXA * -boxaReconcileSizeByMedian(BOXA *boxas, - l_int32 type, - l_float32 dfract, - l_float32 sfract, - l_float32 factor, - NUMA **pnadelw, - NUMA **pnadelh, - l_float32 *pratiowh) -{ -l_int32 i, n, ne, no, outfound, isvalid, ind, del, maxdel; -l_int32 medw, medh, bw, bh, left, right, top, bot; -l_int32 medleft, medlefte, medlefto, medright, medrighte, medrighto; -l_int32 medtop, medtope, medtopo, medbot, medbote, medboto; -l_float32 brat; -BOX *box; -BOXA *boxa1, *boxae, *boxao, *boxad; -NUMA *naind, *nadelw, *nadelh; - - PROCNAME("boxaReconcileSizeByMedian"); - - if (pnadelw) *pnadelw = NULL; - if (pnadelh) *pnadelh = NULL; - if (pratiowh) *pratiowh = 0.0; - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (type != L_CHECK_WIDTH && type != L_CHECK_HEIGHT && - type != L_CHECK_BOTH) { - L_WARNING("invalid type; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (dfract <= 0.0 || dfract >= 0.5) { - L_WARNING("invalid dimensional fract; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (sfract <= 0.0 || sfract >= 0.5) { - L_WARNING("invalid side fract; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - if (factor < 0.8 || factor > 1.25) - L_WARNING("factor %5.3f is typ. closer to 1.0\n", procName, factor); - if (boxaGetValidCount(boxas) < 6) { - L_WARNING("need at least 6 valid boxes; returning copy\n", procName); - return boxaCopy(boxas, L_COPY); - } - - /* If reconciling both width and height, optionally return array of - * median deviations and even/odd ratio for width measurements */ - if (type == L_CHECK_BOTH) { - boxa1 = boxaReconcileSizeByMedian(boxas, L_CHECK_WIDTH, dfract, sfract, - factor, pnadelw, NULL, pratiowh); - boxad = boxaReconcileSizeByMedian(boxa1, L_CHECK_HEIGHT, dfract, sfract, - factor, NULL, pnadelh, NULL); - boxaDestroy(&boxa1); - return boxad; - } - - n = boxaGetCount(boxas); - naind = numaCreate(n); /* outlier indicator array */ - boxae = boxaCreate(0); /* even inliers */ - boxao = boxaCreate(0); /* odd inliers */ - outfound = FALSE; - if (type == L_CHECK_WIDTH) { - boxaMedianDimensions(boxas, &medw, &medh, NULL, NULL, NULL, NULL, - &nadelw, NULL); - if (pratiowh) { - *pratiowh = (l_float32)medw / (l_float32)medh; - L_INFO("median ratio w/h = %5.3f\n", procName, *pratiowh); - } - if (pnadelw) - *pnadelw = nadelw; - else - numaDestroy(&nadelw); - - /* Check for outliers; assemble inliers */ - for (i = 0; i < n; i++) { - if ((box = boxaGetValidBox(boxas, i, L_COPY)) == NULL) { - numaAddNumber(naind, 0); - continue; - } - boxGetGeometry(box, NULL, NULL, &bw, NULL); - brat = (l_float32)bw / (l_float32)medw; - if (brat < 1.0 - dfract || brat > 1.0 + dfract) { - outfound = TRUE; - numaAddNumber(naind, 1); - boxDestroy(&box); - } else { /* add to inliers */ - numaAddNumber(naind, 0); - if (i % 2 == 0) - boxaAddBox(boxae, box, L_INSERT); - else - boxaAddBox(boxao, box, L_INSERT); - } - } - if (!outfound) { /* nothing to do */ - numaDestroy(&naind); - boxaDestroy(&boxae); - boxaDestroy(&boxao); - L_INFO("no width outlier boxes found\n", procName); - return boxaCopy(boxas, L_COPY); - } - - /* Get left/right parameters from inliers. Handle the case - * where there are no inliers for one of the sets. For example, - * when all the even boxes have a different dimension from - * the odd boxes, and the median arbitrarily gets assigned - * to the even boxes, there are no odd inliers; in that case, - * use the even inliers sides to decide whether to adjust - * the left or the right sides of individual outliers. */ - L_INFO("fixing width of outlier boxes\n", procName); - medlefte = medrighte = medlefto = medrighto = 0; - if ((ne = boxaGetValidCount(boxae)) > 0) - boxaGetMedianVals(boxae, &medlefte, NULL, &medrighte, NULL, - NULL, NULL); - if ((no = boxaGetValidCount(boxao)) > 0) - boxaGetMedianVals(boxao, &medlefto, NULL, &medrighto, NULL, - NULL, NULL); - if (ne == 0) { /* use odd inliers values for both */ - medlefte = medlefto; - medrighte = medrighto; - } else if (no == 0) { /* use even inliers values for both */ - medlefto = medlefte; - medrighto = medrighte; - } - - /* Adjust the left and/or right sides of outliers. - * For each box that is a dimensional outlier, consider each side. - * Any side that differs fractionally from the median value - * by more than %sfract times the median width (medw) is set to - * the median value for that side. Then both sides are moved - * an equal distance in or out to make w = %factor * medw. */ - boxad = boxaCreate(n); - maxdel = (l_int32)(sfract * medw + 0.5); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxas, i, L_COPY); - boxIsValid(box, &isvalid); - numaGetIValue(naind, i, &ind); - medleft = (i % 2 == 0) ? medlefte : medlefto; - medright = (i % 2 == 0) ? medrighte : medrighto; - if (ind == 1 && isvalid) { /* adjust sides */ - boxGetSideLocations(box, &left, &right, NULL, NULL); - if (L_ABS(left - medleft) > maxdel) left = medleft; - if (L_ABS(right - medright) > maxdel) right = medright; - del = (l_int32)(factor * medw - (right - left)) / 2; - boxSetSide(box, L_SET_LEFT, left - del, 0); - boxSetSide(box, L_SET_RIGHT, right + del, 0); - } - boxaAddBox(boxad, box, L_INSERT); - } - } else { /* L_CHECK_HEIGHT */ - boxaMedianDimensions(boxas, &medw, &medh, NULL, NULL, NULL, NULL, - NULL, &nadelh); - if (pratiowh) { - *pratiowh = (l_float32)medw / (l_float32)medh; - L_INFO("median ratio w/h = %5.3f\n", procName, *pratiowh); - } - if (pnadelh) - *pnadelh = nadelh; - else - numaDestroy(&nadelh); - - /* Check for outliers; assemble inliers */ - for (i = 0; i < n; i++) { - if ((box = boxaGetValidBox(boxas, i, L_COPY)) == NULL) { - numaAddNumber(naind, 0); - continue; - } - boxGetGeometry(box, NULL, NULL, NULL, &bh); - brat = (l_float32)bh / (l_float32)medh; - if (brat < 1.0 - dfract || brat > 1.0 + dfract) { - outfound = TRUE; - numaAddNumber(naind, 1); - boxDestroy(&box); - } else { /* add to inliers */ - numaAddNumber(naind, 0); - if (i % 2 == 0) - boxaAddBox(boxae, box, L_INSERT); - else - boxaAddBox(boxao, box, L_INSERT); - } - } - if (!outfound) { /* nothing to do */ - numaDestroy(&naind); - boxaDestroy(&boxae); - boxaDestroy(&boxao); - L_INFO("no height outlier boxes found\n", procName); - return boxaCopy(boxas, L_COPY); - } - - /* Get top/bot parameters from inliers. Handle the case - * where there are no inliers for one of the sets. For example, - * when all the even boxes have a different dimension from - * the odd boxes, and the median arbitrarily gets assigned - * to the even boxes, there are no odd inliers; in that case, - * use the even inlier sides to decide whether to adjust - * the top or the bottom sides of individual outliers. */ - L_INFO("fixing height of outlier boxes\n", procName); - medlefte = medtope = medbote = medtopo = medboto = 0; - if ((ne = boxaGetValidCount(boxae)) > 0) - boxaGetMedianVals(boxae, NULL, &medtope, NULL, &medbote, - NULL, NULL); - if ((no = boxaGetValidCount(boxao)) > 0) - boxaGetMedianVals(boxao, NULL, &medtopo, NULL, &medboto, - NULL, NULL); - if (ne == 0) { /* use odd inliers values for both */ - medtope = medtopo; - medbote = medboto; - } else if (no == 0) { /* use even inliers values for both */ - medtopo = medtope; - medboto = medbote; - } - - /* Adjust the top and/or bottom sides of outliers. - * For each box that is a dimensional outlier, consider each side. - * Any side that differs fractionally from the median value - * by more than %sfract times the median height (medh) is - * set to the median value for that that side. Then both - * sides are moved an equal distance in or out to make - * h = %factor * medh). */ - boxad = boxaCreate(n); - maxdel = (l_int32)(sfract * medh + 0.5); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxas, i, L_COPY); - boxIsValid(box, &isvalid); - numaGetIValue(naind, i, &ind); - medtop = (i % 2 == 0) ? medtope : medtopo; - medbot = (i % 2 == 0) ? medbote : medboto; - if (ind == 1 && isvalid) { /* adjust sides */ - boxGetSideLocations(box, NULL, NULL, &top, &bot); - if (L_ABS(top - medtop) > maxdel) top = medtop; - if (L_ABS(bot - medbot) > maxdel) bot = medbot; - del = (l_int32)(factor * medh - (bot - top)) / 2; /* typ > 0 */ - boxSetSide(box, L_SET_TOP, L_MAX(0, top - del), 0); - boxSetSide(box, L_SET_BOT, bot + del, 0); - } - boxaAddBox(boxad, box, L_INSERT); - } - } - numaDestroy(&naind); - boxaDestroy(&boxae); - boxaDestroy(&boxao); - return boxad; -} - - -/*! - * \brief boxaPlotSides() - * - * \param[in] boxa source boxa - * \param[in] plotname [optional], can be NULL - * \param[out] pnal [optional] na of left sides - * \param[out] pnat [optional] na of top sides - * \param[out] pnar [optional] na of right sides - * \param[out] pnab [optional] na of bottom sides - * \param[out] ppixd pix of the output plot - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This debugging function shows the progression of the four - * sides in the boxa. There must be at least 2 boxes. - * (2) If there are invalid boxes (e.g., if only even or odd - * indices have valid boxes), this will fill them with the - * nearest valid box before plotting. - * (3) The plotfiles are put in /tmp/lept/plots/, and are named - * either with %plotname or, if NULL, a default name. If - * %plotname is used, make sure it has no whitespace characters. - *- */ -l_ok -boxaPlotSides(BOXA *boxa, - const char *plotname, - NUMA **pnal, - NUMA **pnat, - NUMA **pnar, - NUMA **pnab, - PIX **ppixd) -{ -char buf[128], titlebuf[128]; -char *dataname; -static l_int32 plotid = 0; -l_int32 n, i, w, h, left, top, right, bot; -l_int32 debugprint = FALSE; /* change to TRUE to spam stderr */ -l_float32 med, dev; -BOXA *boxat; -GPLOT *gplot; -NUMA *nal, *nat, *nar, *nab; - - PROCNAME("boxaPlotSides"); - - if (pnal) *pnal = NULL; - if (pnat) *pnat = NULL; - if (pnar) *pnar = NULL; - if (pnab) *pnab = NULL; - if (ppixd) *ppixd = NULL; - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if ((n = boxaGetCount(boxa)) < 2) - return ERROR_INT("less than 2 boxes", procName, 1); - if (!ppixd) - return ERROR_INT("&pixd not defined", procName, 1); - - boxat = boxaFillSequence(boxa, L_USE_ALL_BOXES, 0); - - /* Build the numas for each side */ - nal = numaCreate(n); - nat = numaCreate(n); - nar = numaCreate(n); - nab = numaCreate(n); - - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxat, i, &left, &top, &w, &h); - right = left + w - 1; - bot = top + h - 1; - numaAddNumber(nal, left); - numaAddNumber(nat, top); - numaAddNumber(nar, right); - numaAddNumber(nab, bot); - } - boxaDestroy(&boxat); - - lept_mkdir("lept/plots"); - if (plotname) { - snprintf(buf, sizeof(buf), "/tmp/lept/plots/sides.%s", plotname); - snprintf(titlebuf, sizeof(titlebuf), "%s: Box sides vs. box index", - plotname); - } else { - snprintf(buf, sizeof(buf), "/tmp/lept/plots/sides.%d", plotid++); - snprintf(titlebuf, sizeof(titlebuf), "Box sides vs. box index"); - } - gplot = gplotCreate(buf, GPLOT_PNG, titlebuf, - "box index", "side location"); - gplotAddPlot(gplot, NULL, nal, GPLOT_LINES, "left side"); - gplotAddPlot(gplot, NULL, nat, GPLOT_LINES, "top side"); - gplotAddPlot(gplot, NULL, nar, GPLOT_LINES, "right side"); - gplotAddPlot(gplot, NULL, nab, GPLOT_LINES, "bottom side"); - *ppixd = gplotMakeOutputPix(gplot); - gplotDestroy(&gplot); - - if (debugprint) { - dataname = (plotname) ? stringNew(plotname) : stringNew("no_name"); - numaGetMedian(nal, &med); - numaGetMeanDevFromMedian(nal, med, &dev); - lept_stderr("%s left: med = %7.3f, meandev = %7.3f\n", - dataname, med, dev); - numaGetMedian(nat, &med); - numaGetMeanDevFromMedian(nat, med, &dev); - lept_stderr("%s top: med = %7.3f, meandev = %7.3f\n", - dataname, med, dev); - numaGetMedian(nar, &med); - numaGetMeanDevFromMedian(nar, med, &dev); - lept_stderr("%s right: med = %7.3f, meandev = %7.3f\n", - dataname, med, dev); - numaGetMedian(nab, &med); - numaGetMeanDevFromMedian(nab, med, &dev); - lept_stderr("%s bot: med = %7.3f, meandev = %7.3f\n", - dataname, med, dev); - LEPT_FREE(dataname); - } - - if (pnal) - *pnal = nal; - else - numaDestroy(&nal); - if (pnat) - *pnat = nat; - else - numaDestroy(&nat); - if (pnar) - *pnar = nar; - else - numaDestroy(&nar); - if (pnab) - *pnab = nab; - else - numaDestroy(&nab); - return 0; -} - - -/*! - * \brief boxaPlotSizes() - * - * \param[in] boxa source boxa - * \param[in] plotname [optional], can be NULL - * \param[out] pnaw [optional] na of widths - * \param[out] pnah [optional] na of heights - * \param[out] ppixd pix of the output plot - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This debugging function shows the progression of box width - * and height in the boxa. There must be at least 2 boxes. - * (2) If there are invalid boxes (e.g., if only even or odd - * indices have valid boxes), this will fill them with the - * nearest valid box before plotting. - * (3) The plotfiles are put in /tmp/lept/plots/, and are named - * either with %plotname or, if NULL, a default name. If - * %plotname is used, make sure it has no whitespace characters. - *- */ -l_ok -boxaPlotSizes(BOXA *boxa, - const char *plotname, - NUMA **pnaw, - NUMA **pnah, - PIX **ppixd) -{ -char buf[128], titlebuf[128]; -static l_int32 plotid = 0; -l_int32 n, i, w, h; -BOXA *boxat; -GPLOT *gplot; -NUMA *naw, *nah; - - PROCNAME("boxaPlotSizes"); - - if (pnaw) *pnaw = NULL; - if (pnah) *pnah = NULL; - if (ppixd) *ppixd = NULL; - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if ((n = boxaGetCount(boxa)) < 2) - return ERROR_INT("less than 2 boxes", procName, 1); - if (!ppixd) - return ERROR_INT("&pixd not defined", procName, 1); - - boxat = boxaFillSequence(boxa, L_USE_ALL_BOXES, 0); - - /* Build the numas for the width and height */ - naw = numaCreate(n); - nah = numaCreate(n); - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxat, i, NULL, NULL, &w, &h); - numaAddNumber(naw, w); - numaAddNumber(nah, h); - } - boxaDestroy(&boxat); - - lept_mkdir("lept/plots"); - if (plotname) { - snprintf(buf, sizeof(buf), "/tmp/lept/plots/size.%s", plotname); - snprintf(titlebuf, sizeof(titlebuf), "%s: Box size vs. box index", - plotname); - } else { - snprintf(buf, sizeof(buf), "/tmp/lept/plots/size.%d", plotid++); - snprintf(titlebuf, sizeof(titlebuf), "Box size vs. box index"); - } - gplot = gplotCreate(buf, GPLOT_PNG, titlebuf, - "box index", "box dimension"); - gplotAddPlot(gplot, NULL, naw, GPLOT_LINES, "width"); - gplotAddPlot(gplot, NULL, nah, GPLOT_LINES, "height"); - *ppixd = gplotMakeOutputPix(gplot); - gplotDestroy(&gplot); - - if (pnaw) - *pnaw = naw; - else - numaDestroy(&naw); - if (pnah) - *pnah = nah; - else - numaDestroy(&nah); - return 0; -} - - -/*! - * \brief boxaFillSequence() - * - * \param[in] boxas with at least 3 boxes - * \param[in] useflag L_USE_ALL_BOXES, L_USE_SAME_PARITY_BOXES - * \param[in] debug 1 for debug output - * \return boxad filled boxa, or NULL on error - * - *
- * Notes: - * (1) This simple function replaces invalid boxes with a copy of - * the nearest valid box, selected from either the entire - * sequence (L_USE_ALL_BOXES) or from the boxes with the - * same parity (L_USE_SAME_PARITY_BOXES). It returns a new boxa. - * (2) This is useful if you expect boxes in the sequence to - * vary slowly with index. - *- */ -BOXA * -boxaFillSequence(BOXA *boxas, - l_int32 useflag, - l_int32 debug) -{ -l_int32 n, nv; -BOXA *boxae, *boxao, *boxad; - - PROCNAME("boxaFillSequence"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (useflag != L_USE_ALL_BOXES && useflag != L_USE_SAME_PARITY_BOXES) - return (BOXA *)ERROR_PTR("invalid useflag", procName, NULL); - - n = boxaGetCount(boxas); - nv = boxaGetValidCount(boxas); - if (n == nv) - return boxaCopy(boxas, L_COPY); /* all valid */ - if (debug) - L_INFO("%d valid boxes, %d invalid boxes\n", procName, nv, n - nv); - if (useflag == L_USE_SAME_PARITY_BOXES && n < 3) { - L_WARNING("n < 3; some invalid\n", procName); - return boxaCopy(boxas, L_COPY); - } - - if (useflag == L_USE_ALL_BOXES) { - boxad = boxaCopy(boxas, L_COPY); - boxaFillAll(boxad); - } else { - boxaSplitEvenOdd(boxas, 0, &boxae, &boxao); - boxaFillAll(boxae); - boxaFillAll(boxao); - boxad = boxaMergeEvenOdd(boxae, boxao, 0); - boxaDestroy(&boxae); - boxaDestroy(&boxao); - } - - nv = boxaGetValidCount(boxad); - if (n != nv) - L_WARNING("there are still %d invalid boxes\n", procName, n - nv); - - return boxad; -} - - -/*! - * \brief boxaFillAll() - * - * \param[in] boxa - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This static function replaces every invalid box with the - * nearest valid box. If there are no valid boxes, it - * issues a warning. - *- */ -static l_int32 -boxaFillAll(BOXA *boxa) -{ -l_int32 n, nv, i, j, spandown, spanup; -l_int32 *indic; -BOX *box, *boxt; - - PROCNAME("boxaFillAll"); - - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - n = boxaGetCount(boxa); - nv = boxaGetValidCount(boxa); - if (n == nv) return 0; - if (nv == 0) { - L_WARNING("no valid boxes out of %d boxes\n", procName, n); - return 0; - } - - /* Make indicator array for valid boxes */ - if ((indic = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32))) == NULL) - return ERROR_INT("indic not made", procName, 1); - for (i = 0; i < n; i++) { - box = boxaGetValidBox(boxa, i, L_CLONE); - if (box) - indic[i] = 1; - boxDestroy(&box); - } - - /* Replace invalid boxes with the nearest valid one */ - for (i = 0; i < n; i++) { - box = boxaGetValidBox(boxa, i, L_CLONE); - if (!box) { - spandown = spanup = 10000000; - for (j = i - 1; j >= 0; j--) { - if (indic[j] == 1) { - spandown = i - j; - break; - } - } - for (j = i + 1; j < n; j++) { - if (indic[j] == 1) { - spanup = j - i; - break; - } - } - if (spandown < spanup) - boxt = boxaGetBox(boxa, i - spandown, L_COPY); - else - boxt = boxaGetBox(boxa, i + spanup, L_COPY); - boxaReplaceBox(boxa, i, boxt); - } - boxDestroy(&box); - } - - LEPT_FREE(indic); - return 0; -} - - -/*! - * \brief boxaSizeVariation() - * - * \param[in] boxa at least 4 boxes - * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT - * \param[out] pdel_evenodd [optional] average absolute value of - * (even - odd) size pairs - * \param[out] prms_even [optional] rms deviation of even boxes - * \param[out] prms_odd [optional] rms deviation of odd boxes - * \param[out] prms_all [optional] rms deviation of all boxes - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This gives several measures of the smoothness of either the - * width or height of a sequence of boxes. - * See boxaMedianDimensions() for some other measures. - * (2) Statistics can be found separately for even and odd boxes. - * Additionally, the average pair-wise difference between - * adjacent even and odd boxes can be returned. - * (3) The use case is bounding boxes for scanned page images, - * where ideally the sizes should have little variance. - *- */ -l_ok -boxaSizeVariation(BOXA *boxa, - l_int32 type, - l_float32 *pdel_evenodd, - l_float32 *prms_even, - l_float32 *prms_odd, - l_float32 *prms_all) -{ -l_int32 n, ne, no, nmin, vale, valo, i; -l_float32 sum; -BOXA *boxae, *boxao; -NUMA *nae, *nao, *na_all; - - PROCNAME("boxaSizeVariation"); - - if (pdel_evenodd) *pdel_evenodd = 0.0; - if (prms_even) *prms_even = 0.0; - if (prms_odd) *prms_odd = 0.0; - if (prms_all) *prms_all = 0.0; - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT) - return ERROR_INT("invalid type", procName, 1); - if (!pdel_evenodd && !prms_even && !prms_odd && !prms_all) - return ERROR_INT("nothing to do", procName, 1); - n = boxaGetCount(boxa); - if (n < 4) - return ERROR_INT("too few boxes", procName, 1); - - boxaSplitEvenOdd(boxa, 0, &boxae, &boxao); - ne = boxaGetCount(boxae); - no = boxaGetCount(boxao); - nmin = L_MIN(ne, no); - if (nmin == 0) { - boxaDestroy(&boxae); - boxaDestroy(&boxao); - return ERROR_INT("either no even or no odd boxes", procName, 1); - } - - if (type == L_SELECT_WIDTH) { - boxaGetSizes(boxae, &nae, NULL); - boxaGetSizes(boxao, &nao, NULL); - boxaGetSizes(boxa, &na_all, NULL); - } else { /* L_SELECT_HEIGHT) */ - boxaGetSizes(boxae, NULL, &nae); - boxaGetSizes(boxao, NULL, &nao); - boxaGetSizes(boxa, NULL, &na_all); - } - - if (pdel_evenodd) { - sum = 0.0; - for (i = 0; i < nmin; i++) { - numaGetIValue(nae, i, &vale); - numaGetIValue(nao, i, &valo); - sum += L_ABS(vale - valo); - } - *pdel_evenodd = sum / nmin; - } - if (prms_even) - numaSimpleStats(nae, 0, -1, NULL, NULL, prms_even); - if (prms_odd) - numaSimpleStats(nao, 0, -1, NULL, NULL, prms_odd); - if (prms_all) - numaSimpleStats(na_all, 0, -1, NULL, NULL, prms_all); - - boxaDestroy(&boxae); - boxaDestroy(&boxao); - numaDestroy(&nae); - numaDestroy(&nao); - numaDestroy(&na_all); - return 0; -} - - -/*! - * \brief boxaMedianDimensions() - * - * \param[in] boxas containing at least 3 valid boxes in even and odd - * \param[out] pmedw [optional] median width of all boxes - * \param[out] pmedh [optional] median height of all boxes - * \param[out] pmedwe [optional] median width of even boxes - * \param[out] pmedwo [optional] median width of odd boxes - * \param[out] pmedhe [optional] median height of even boxes - * \param[out] pmedho [optional] median height of odd boxes - * \param[out] pnadelw [optional] width diff of each box from median - * \param[out] pnadelh [optional] height diff of each box from median - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This provides information that (1) allows identification of - * boxes that have unusual (outlier) width or height, and (2) can - * be used to regularize the sizes of the outlier boxes, assuming - * that the boxes satisfy a fairly regular sequence and should - * mostly have the same width and height. - * (2) This finds the median width and height, as well as separate - * median widths and heights of even and odd boxes. It also - * generates arrays that give the difference in width and height - * of each box from the median, which can be used to correct - * individual boxes. - * (3) All return values are optional. - *- */ -l_ok -boxaMedianDimensions(BOXA *boxas, - l_int32 *pmedw, - l_int32 *pmedh, - l_int32 *pmedwe, - l_int32 *pmedwo, - l_int32 *pmedhe, - l_int32 *pmedho, - NUMA **pnadelw, - NUMA **pnadelh) -{ -l_int32 i, n, bw, bh, medw, medh, medwe, medwo, medhe, medho; -BOXA *boxae, *boxao; -NUMA *nadelw, *nadelh; - - PROCNAME("boxaMedianDimensions"); - - if (pmedw) *pmedw = 0; - if (pmedh) *pmedh = 0; - if (pmedwe) *pmedwe= 0; - if (pmedwo) *pmedwo= 0; - if (pmedhe) *pmedhe= 0; - if (pmedho) *pmedho= 0; - if (pnadelw) *pnadelw = NULL; - if (pnadelh) *pnadelh = NULL; - if (!boxas) - return ERROR_INT("boxas not defined", procName, 1); - if (boxaGetValidCount(boxas) < 6) - return ERROR_INT("need at least 6 valid boxes", procName, 1); - - /* Require at least 3 valid boxes of both types */ - boxaSplitEvenOdd(boxas, 0, &boxae, &boxao); - if (boxaGetValidCount(boxae) < 3 || boxaGetValidCount(boxao) < 3) { - boxaDestroy(&boxae); - boxaDestroy(&boxao); - return ERROR_INT("don't have 3+ valid boxes of each type", procName, 1); - } - - /* Get the relevant median widths and heights */ - boxaGetMedianVals(boxas, NULL, NULL, NULL, NULL, &medw, &medh); - boxaGetMedianVals(boxae, NULL, NULL, NULL, NULL, &medwe, &medhe); - boxaGetMedianVals(boxao, NULL, NULL, NULL, NULL, &medwo, &medho); - if (pmedw) *pmedw = medw; - if (pmedh) *pmedh = medh; - if (pmedwe) *pmedwe = medwe; - if (pmedwo) *pmedwo = medwo; - if (pmedhe) *pmedhe = medhe; - if (pmedho) *pmedho = medho; - - /* Find the variation from median dimension for each box */ - n = boxaGetCount(boxas); - nadelw = numaCreate(n); - nadelh = numaCreate(n); - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxas, i, NULL, NULL, &bw, &bh); - if (bw == 0 || bh == 0) { /* invalid box */ - numaAddNumber(nadelw, 0); - numaAddNumber(nadelh, 0); - } else { - numaAddNumber(nadelw, bw - medw); - numaAddNumber(nadelh, bh - medh); - } - } - if (pnadelw) - *pnadelw = nadelw; - else - numaDestroy(&nadelw); - if (pnadelh) - *pnadelh = nadelh; - else - numaDestroy(&nadelh); - - boxaDestroy(&boxae); - boxaDestroy(&boxao); - return 0; -} - diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bytearray.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bytearray.c deleted file mode 100644 index 39d06212..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/bytearray.c +++ /dev/null @@ -1,640 +0,0 @@ -/*====================================================================* - - 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 bytearray.c - *
- * - * Functions for handling byte arrays, in analogy with C++ 'strings' - * - * Creation, copy, clone, destruction - * L_BYTEA *l_byteaCreate() - * L_BYTEA *l_byteaInitFromMem() - * L_BYTEA *l_byteaInitFromFile() - * L_BYTEA *l_byteaInitFromStream() - * L_BYTEA *l_byteaCopy() - * void l_byteaDestroy() - * - * Accessors - * size_t l_byteaGetSize() - * l_uint8 *l_byteaGetData() - * l_uint8 *l_byteaCopyData() - * - * Appending - * l_int32 l_byteaAppendData() - * l_int32 l_byteaAppendString() - * static l_int32 l_byteaExtendArrayToSize() - * - * Join/Split - * l_int32 l_byteaJoin() - * l_int32 l_byteaSplit() - * - * Search - * l_int32 l_byteaFindEachSequence() - * - * Output to file - * l_int32 l_byteaWrite() - * l_int32 l_byteaWriteStream() - * - * The internal data array is always null-terminated, for ease of use - * in the event that it is an ascii string without null bytes. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) The allocated array is n + 1 bytes. This allows room - * for null termination. - *- */ -L_BYTEA * -l_byteaCreate(size_t nbytes) -{ -L_BYTEA *ba; - - PROCNAME("l_byteaCreate"); - - if (nbytes <= 0 || nbytes > MaxArraySize) - nbytes = InitialArraySize; - ba = (L_BYTEA *)LEPT_CALLOC(1, sizeof(L_BYTEA)); - ba->data = (l_uint8 *)LEPT_CALLOC(nbytes + 1, sizeof(l_uint8)); - if (!ba->data) { - l_byteaDestroy(&ba); - return (L_BYTEA *)ERROR_PTR("ba array not made", procName, NULL); - } - ba->nalloc = nbytes + 1; - ba->refcount = 1; - return ba; -} - - -/*! - * \brief l_byteaInitFromMem() - * - * \param[in] data to be copied to the array - * \param[in] size amount of data - * \return l_bytea, or NULL on error - */ -L_BYTEA * -l_byteaInitFromMem(const l_uint8 *data, - size_t size) -{ -L_BYTEA *ba; - - PROCNAME("l_byteaInitFromMem"); - - if (!data) - return (L_BYTEA *)ERROR_PTR("data not defined", procName, NULL); - if (size <= 0) - return (L_BYTEA *)ERROR_PTR("no bytes to initialize", procName, NULL); - if (size > MaxArraySize) - return (L_BYTEA *)ERROR_PTR("size is too big", procName, NULL); - - if ((ba = l_byteaCreate(size)) == NULL) - return (L_BYTEA *)ERROR_PTR("ba not made", procName, NULL); - memcpy(ba->data, data, size); - ba->size = size; - return ba; -} - - -/*! - * \brief l_byteaInitFromFile() - * - * \param[in] fname - * \return l_bytea, or NULL on error - */ -L_BYTEA * -l_byteaInitFromFile(const char *fname) -{ -FILE *fp; -L_BYTEA *ba; - - PROCNAME("l_byteaInitFromFile"); - - if (!fname) - return (L_BYTEA *)ERROR_PTR("fname not defined", procName, NULL); - - if ((fp = fopenReadStream(fname)) == NULL) - return (L_BYTEA *)ERROR_PTR("file stream not opened", procName, NULL); - ba = l_byteaInitFromStream(fp); - fclose(fp); - if (!ba) - return (L_BYTEA *)ERROR_PTR("ba not made", procName, NULL); - return ba; -} - - -/*! - * \brief l_byteaInitFromStream() - * - * \param[in] fp file stream - * \return l_bytea, or NULL on error - */ -L_BYTEA * -l_byteaInitFromStream(FILE *fp) -{ -l_uint8 *data; -size_t nbytes; -L_BYTEA *ba; - - PROCNAME("l_byteaInitFromStream"); - - if (!fp) - return (L_BYTEA *)ERROR_PTR("stream not defined", procName, NULL); - - if ((data = l_binaryReadStream(fp, &nbytes)) == NULL) - return (L_BYTEA *)ERROR_PTR("data not read", procName, NULL); - if ((ba = l_byteaCreate(nbytes)) == NULL) { - LEPT_FREE(data); - return (L_BYTEA *)ERROR_PTR("ba not made", procName, NULL); - } - memcpy(ba->data, data, nbytes); - ba->size = nbytes; - LEPT_FREE(data); - return ba; -} - - -/*! - * \brief l_byteaCopy() - * - * \param[in] bas source lba - * \param[in] copyflag L_COPY, L_CLONE - * \return clone or copy of bas, or NULL on error - * - *
- * Notes: - * (1) If cloning, up the refcount and return a ptr to %bas. - *- */ -L_BYTEA * -l_byteaCopy(L_BYTEA *bas, - l_int32 copyflag) -{ - PROCNAME("l_byteaCopy"); - - if (!bas) - return (L_BYTEA *)ERROR_PTR("bas not defined", procName, NULL); - - if (copyflag == L_CLONE) { - bas->refcount++; - return bas; - } - - return l_byteaInitFromMem(bas->data, bas->size); -} - - -/*! - * \brief l_byteaDestroy() - * - * \param[in,out] pba will be set to null before returning - * \return void - * - *
- * Notes: - * (1) Decrements the ref count and, if 0, destroys the lba. - * (2) Always nulls the input ptr. - * (3) If the data has been previously removed, the lba will - * have been nulled, so this will do nothing. - *- */ -void -l_byteaDestroy(L_BYTEA **pba) -{ -L_BYTEA *ba; - - PROCNAME("l_byteaDestroy"); - - if (pba == NULL) { - L_WARNING("ptr address is null!\n", procName); - return; - } - - if ((ba = *pba) == NULL) - return; - - /* Decrement the ref count. If it is 0, destroy the lba. */ - ba->refcount--; - if (ba->refcount <= 0) { - if (ba->data) LEPT_FREE(ba->data); - LEPT_FREE(ba); - } - - *pba = NULL; - return; -} - - -/*---------------------------------------------------------------------* - * Accessors * - *---------------------------------------------------------------------*/ -/*! - * \brief l_byteaGetSize() - * - * \param[in] ba - * \return size of stored byte array, or 0 on error - */ -size_t -l_byteaGetSize(L_BYTEA *ba) -{ - PROCNAME("l_byteaGetSize"); - - if (!ba) - return ERROR_INT("ba not defined", procName, 0); - return ba->size; -} - - -/*! - * \brief l_byteaGetData() - * - * \param[in] ba - * \param[out] psize size of data in lba - * \return ptr to existing data array, or NULL on error - * - *
- * Notes: - * (1) The returned ptr is owned by %ba. Do not free it! - *- */ -l_uint8 * -l_byteaGetData(L_BYTEA *ba, - size_t *psize) -{ - PROCNAME("l_byteaGetData"); - - if (!ba) - return (l_uint8 *)ERROR_PTR("ba not defined", procName, NULL); - if (!psize) - return (l_uint8 *)ERROR_PTR("&size not defined", procName, NULL); - - *psize = ba->size; - return ba->data; -} - - -/*! - * \brief l_byteaCopyData() - * - * \param[in] ba - * \param[out] psize size of data in lba - * \return copy of data in use in the data array, or NULL on error. - * - *
- * Notes: - * (1) The returned data is owned by the caller. The input %ba - * still owns the original data array. - *- */ -l_uint8 * -l_byteaCopyData(L_BYTEA *ba, - size_t *psize) -{ -l_uint8 *data; - - PROCNAME("l_byteaCopyData"); - - if (!psize) - return (l_uint8 *)ERROR_PTR("&size not defined", procName, NULL); - *psize = 0; - if (!ba) - return (l_uint8 *)ERROR_PTR("ba not defined", procName, NULL); - - data = l_byteaGetData(ba, psize); - return l_binaryCopy(data, *psize); -} - - -/*---------------------------------------------------------------------* - * Appending * - *---------------------------------------------------------------------*/ -/*! - * \brief l_byteaAppendData() - * - * \param[in] ba - * \param[in] newdata byte array to be appended - * \param[in] newbytes size of data array - * \return 0 if OK, 1 on error - */ -l_ok -l_byteaAppendData(L_BYTEA *ba, - const l_uint8 *newdata, - size_t newbytes) -{ -size_t size, nalloc, reqsize; - - PROCNAME("l_byteaAppendData"); - - if (!ba) - return ERROR_INT("ba not defined", procName, 1); - if (!newdata) - return ERROR_INT("newdata not defined", procName, 1); - - size = l_byteaGetSize(ba); - reqsize = size + newbytes + 1; - nalloc = ba->nalloc; - if (nalloc < reqsize) - l_byteaExtendArrayToSize(ba, 2 * reqsize); - - memcpy(ba->data + size, newdata, newbytes); - ba->size += newbytes; - return 0; -} - - -/*! - * \brief l_byteaAppendString() - * - * \param[in] ba - * \param[in] str null-terminated string to be appended - * \return 0 if OK, 1 on error - */ -l_ok -l_byteaAppendString(L_BYTEA *ba, - const char *str) -{ -size_t size, len, nalloc, reqsize; - - PROCNAME("l_byteaAppendString"); - - if (!ba) - return ERROR_INT("ba not defined", procName, 1); - if (!str) - return ERROR_INT("str not defined", procName, 1); - - size = l_byteaGetSize(ba); - len = strlen(str); - reqsize = size + len + 1; - nalloc = ba->nalloc; - if (nalloc < reqsize) - l_byteaExtendArrayToSize(ba, 2 * reqsize); - - memcpy(ba->data + size, str, len); - ba->size += len; - return 0; -} - - -/*! - * \brief l_byteaExtendArrayToSize() - * - * \param[in] ba - * \param[in] size new size of lba data array - * \return 0 if OK; 1 on error - */ -static l_int32 -l_byteaExtendArrayToSize(L_BYTEA *ba, - size_t size) -{ - PROCNAME("l_byteaExtendArrayToSize"); - - if (!ba) - return ERROR_INT("ba not defined", procName, 1); - - if (size > ba->nalloc) { - if ((ba->data = - (l_uint8 *)reallocNew((void **)&ba->data, ba->nalloc, size)) - == NULL) - return ERROR_INT("new array not returned", procName, 1); - ba->nalloc = size; - } - return 0; -} - - -/*---------------------------------------------------------------------* - * String join/split * - *---------------------------------------------------------------------*/ -/*! - * \brief l_byteaJoin() - * - * \param[in] ba1 - * \param[in,out] pba2 data array is added to the one in ba1; - * then ba2 is destroyed and its pointer is nulled. - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) It is a no-op, not an error, for %ba2 to be null. - *- */ -l_ok -l_byteaJoin(L_BYTEA *ba1, - L_BYTEA **pba2) -{ -l_uint8 *data2; -size_t nbytes2; -L_BYTEA *ba2; - - PROCNAME("l_byteaJoin"); - - if (!ba1) - return ERROR_INT("ba1 not defined", procName, 1); - if (!pba2) - return ERROR_INT("&ba2 not defined", procName, 1); - if ((ba2 = *pba2) == NULL) return 0; - - data2 = l_byteaGetData(ba2, &nbytes2); - l_byteaAppendData(ba1, data2, nbytes2); - - l_byteaDestroy(pba2); - return 0; -} - - -/*! - * \brief l_byteaSplit() - * - * \param[in] ba1 lba to split; array bytes nulled beyond the split loc - * \param[in] splitloc location in ba1 to split; ba2 begins there - * \param[out] pba2 with data starting at splitloc - * \return 0 if OK, 1 on error - */ -l_ok -l_byteaSplit(L_BYTEA *ba1, - size_t splitloc, - L_BYTEA **pba2) -{ -l_uint8 *data1; -size_t nbytes1, nbytes2; - - PROCNAME("l_byteaSplit"); - - if (!pba2) - return ERROR_INT("&ba2 not defined", procName, 1); - *pba2 = NULL; - if (!ba1) - return ERROR_INT("ba1 not defined", procName, 1); - - data1 = l_byteaGetData(ba1, &nbytes1); - if (splitloc >= nbytes1) - return ERROR_INT("splitloc invalid", procName, 1); - nbytes2 = nbytes1 - splitloc; - - /* Make the new lba */ - *pba2 = l_byteaInitFromMem(data1 + splitloc, nbytes2); - - /* Null the removed bytes in the input lba */ - memset(data1 + splitloc, 0, nbytes2); - ba1->size = splitloc; - return 0; -} - - -/*---------------------------------------------------------------------* - * Search * - *---------------------------------------------------------------------*/ -/*! - * \brief l_byteaFindEachSequence() - * - * \param[in] ba - * \param[in] sequence subarray of bytes to find in data - * \param[in] seqlen length of sequence, in bytes - * \param[out] pda byte positions of each occurrence of %sequence - * \return 0 if OK, 1 on error - */ -l_ok -l_byteaFindEachSequence(L_BYTEA *ba, - const l_uint8 *sequence, - size_t seqlen, - L_DNA **pda) -{ -l_uint8 *data; -size_t size; - - PROCNAME("l_byteaFindEachSequence"); - - if (!pda) - return ERROR_INT("&da not defined", procName, 1); - *pda = NULL; - if (!ba) - return ERROR_INT("ba not defined", procName, 1); - if (!sequence) - return ERROR_INT("sequence not defined", procName, 1); - - data = l_byteaGetData(ba, &size); - *pda = arrayFindEachSequence(data, size, sequence, seqlen); - return 0; -} - - -/*---------------------------------------------------------------------* - * Output to file * - *---------------------------------------------------------------------*/ -/*! - * \brief l_byteaWrite() - * - * \param[in] fname output file - * \param[in] ba - * \param[in] startloc first byte to output - * \param[in] nbytes number of bytes to write; use 0 to write to - * the end of the data array - * \return 0 if OK, 1 on error - */ -l_ok -l_byteaWrite(const char *fname, - L_BYTEA *ba, - size_t startloc, - size_t nbytes) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("l_byteaWrite"); - - if (!fname) - return ERROR_INT("fname not defined", procName, 1); - if (!ba) - return ERROR_INT("ba not defined", procName, 1); - - if ((fp = fopenWriteStream(fname, "wb")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = l_byteaWriteStream(fp, ba, startloc, nbytes); - fclose(fp); - return ret; -} - - -/*! - * \brief l_byteaWriteStream() - * - * \param[in] fp file stream opened for binary write - * \param[in] ba - * \param[in] startloc first byte to output - * \param[in] nbytes number of bytes to write; use 0 to write to - * the end of the data array - * \return 0 if OK, 1 on error - */ -l_ok -l_byteaWriteStream(FILE *fp, - L_BYTEA *ba, - size_t startloc, - size_t nbytes) -{ -l_uint8 *data; -size_t size, maxbytes; - - PROCNAME("l_byteaWriteStream"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!ba) - return ERROR_INT("ba not defined", procName, 1); - - data = l_byteaGetData(ba, &size); - if (startloc >= size) - return ERROR_INT("invalid startloc", procName, 1); - maxbytes = size - startloc; - nbytes = (nbytes == 0) ? maxbytes : L_MIN(nbytes, maxbytes); - - fwrite(data + startloc, 1, nbytes, fp); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ccbord.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ccbord.c deleted file mode 100644 index 2f08aab6..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ccbord.c +++ /dev/null @@ -1,2617 +0,0 @@ -/*====================================================================* - - 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 ccbord.c - *
- * - * CCBORDA and CCBORD creation and destruction - * CCBORDA *ccbaCreate() - * void *ccbaDestroy() - * CCBORD *ccbCreate() - * void *ccbDestroy() - * - * CCBORDA addition - * l_int32 ccbaAddCcb() - * static l_int32 ccbaExtendArray() - * - * CCBORDA accessors - * l_int32 ccbaGetCount() - * l_int32 ccbaGetCcb() - * - * Top-level border-finding routines - * CCBORDA *pixGetAllCCBorders() - * static CCBORD *pixGetCCBorders() - * PTAA *pixGetOuterBordersPtaa() - * static PTA *pixGetOuterBorderPta() - * - * Lower-level border location routines - * PTAA *pixGetOuterBorder() - * static l_int32 pixGetHoleBorder() - * static l_int32 findNextBorderPixel() - * static void locateOutsideSeedPixel() - * - * Border conversions - * l_int32 ccbaGenerateGlobalLocs() - * l_int32 ccbaGenerateStepChains() - * l_int32 ccbaStepChainsToPixCoords() - * l_int32 ccbaGenerateSPGlobalLocs() - * - * Conversion to single path - * l_int32 ccbaGenerateSinglePath() - * PTA *getCutPathForHole() - * - * Border and full image rendering - * PIX *ccbaDisplayBorder() - * PIX *ccbaDisplaySPBorder() - * PIX *ccbaDisplayImage1() - * PIX *ccbaDisplayImage2() - * - * Serialize for I/O - * l_int32 ccbaWrite() - * l_int32 ccbaWriteStream() - * l_int32 ccbaRead() - * l_int32 ccbaReadStream() - * - * SVG output - * l_int32 ccbaWriteSVG() - * char *ccbaWriteSVGString() - * - * - * Border finding is tricky because components can have - * holes, which also need to be traced out. The outer - * border can be connected with all the hole borders, - * so that there is a single border for each component. - * [Alternatively, the connecting paths can be eliminated if - * you're willing to have a set of borders for each - * component (an exterior border and some number of - * interior ones), with "line to" operations tracing - * out each border and "move to" operations going from - * one border to the next.] - * - * Here's the plan. We get the pix for each connected - * component, and trace its exterior border. We then - * find the holes (if any) in the pix, and separately - * trace out their borders, all using the same - * border-following rule that has ON pixels on the right - * side of the path. - * - * [For svg, we may want to turn each set of borders for a c.c. - * into a closed path. This can be done by tunnelling - * through the component from the outer border to each of the - * holes, going in and coming out along the same path so - * the connection will be invisible in any rendering - * (display or print) from the outline. The result is a - * closed path, where the outside border is traversed - * cw and each hole is traversed ccw. The svg renderer - * is assumed to handle these closed borders properly.] - * - * Each border is a closed path that is traversed in such - * a way that the stuff inside the c.c. is on the right - * side of the traveller. The border of a singly-connected - * component is thus traversed cw, and the border of the - * holes inside a c.c. are traversed ccw. Suppose we have - * a list of all the borders of each c.c., both the cw and ccw - * traversals. How do we reconstruct the image? - * - * Reconstruction: - * - * Method 1. Topological method using connected components. - * We have closed borders composed of cw border pixels for the - * exterior of c.c. and ccw border pixels for the interior (holes) - * in the c.c. - * (a) Initialize the destination to be OFF. Then, - * in any order: - * (b) Fill the components within and including the cw borders, - * and sequentially XOR them onto the destination. - * (c) Fill the components within but not including the ccw - * borders and sequentially XOR them onto the destination. - * The components that are XOR'd together can be generated as follows: - * (a) For each closed cw path, use pixFillClosedBorders(): - * (1) Turn on the path pixels in a subimage that - * minimally supports the border. - * (2) Do a 4-connected fill from a seed of 1 pixel width - * on the border, using the inverted image in (1) as - * a filling mask. - * (3) Invert the fill result: this gives the component - * including the exterior cw path, with all holes - * filled. - * (b) For each closed ccw path (hole): - * (1) Turn on the path pixels in a subimage that minimally - * supports the path. - * (2) Find a seed pixel on the inside of this path. - * (3) Do a 4-connected fill from this seed pixel, using - * the inverted image of the path in (1) as a filling - * mask. - * - * ------------------------------------------------------ - * - * Method 2. A variant of Method 1. Topological. - * In Method 1, we treat the exterior border differently from - * the interior (hole) borders. Here, all borders in a c.c. - * are treated equally: - * (1) Start with a pix with a 1 pixel OFF boundary - * enclosing all the border pixels of the c.c. - * This is the filling mask. - * (2) Make a seed image of the same size as follows: for - * each border, put one seed pixel OUTSIDE the border - * (where OUTSIDE is determined by the inside/outside - * convention for borders). - * (3) Seedfill into the seed image, filling in the regions - * determined by the filling mask. The fills are clipped - * by the border pixels. - * (4) Inverting this, we get the c.c. properly filled, - * with the holes empty! - * (5) Rasterop using XOR the filled c.c. (but not the 1 - * pixel boundary) into the full dest image. - * - * Method 2 is about 1.2x faster than Method 1 on text images, - * and about 2x faster on complex images (e.g., with halftones). - * - * ------------------------------------------------------ - * - * Method 3. The traditional way to fill components delineated - * by boundaries is through scan line conversion. It's a bit - * tricky, and I have not yet tried to implement it. - * - * ------------------------------------------------------ - * - * Method 4. [Nota Bene: this method probably doesn't work, and - * won't be implemented. If I get a more traditional scan line - * conversion algorithm working, I'll erase these notes.] - * Render all border pixels on a destination image, - * which will be the final result after scan conversion. Assign - * a value 1 to pixels on cw paths, 2 to pixels on ccw paths, - * and 3 to pixels that are on both paths. Each of the paths - * is an 8-connected component. Now scan across each raster - * line. The attempt is to make rules for each scan line - * that are independent of neighboring scanlines. Here are - * a set of rules for writing ON pixels on a destination raster image: - * - * (a) The rasterizer will be in one of two states: ON and OFF. - * (b) Start each line in the OFF state. In the OFF state, - * skip pixels until you hit a path of any type. Turn - * the path pixel ON. - * (c) If the state is ON, each pixel you encounter will - * be turned on, until and including hitting a path pixel. - * (d) When you hit a path pixel, if the path does NOT cut - * through the line, so that there is not an 8-cc path - * pixel (of any type) both above and below, the state - * is unchanged (it stays either ON or OFF). - * (e) If the path does cut through, but with a possible change - * of pixel type, then we decide whether or - * not to toggle the state based on the values of the - * path pixel and the path pixels above and below: - * (1) if a 1 path cuts through, toggle; - * (1) if a 2 path cuts through, toggle; - * (3) if a 3 path cuts through, do not toggle; - * (4) if on one side a 3 touches both a 1 and a 2, use the 2 - * (5) if a 3 has any 1 neighbors, toggle; else if it has - * no 1 neighbors, do not toggle; - * (6) if a 2 has any neighbors that are 1 or 3, - * do not toggle - * (7) if a 1 has neighbors 1 and x (x = 2 or 3), - * toggle - * - * - * To visualize how these rules work, consider the following - * component with border pixels labeled according to the scheme - * above. We also show the values of the interior pixels - * (w=OFF, b=ON), but these of course must be inferred properly - * from the rules above: - * - * 3 - * 3 w 3 1 1 1 - * 1 2 1 1 b 2 b 1 - * 1 b 1 3 w 2 1 - * 3 b 1 1 b 2 b 1 - * 3 w 3 1 1 1 - * 3 w 3 - * 1 b 2 b 1 - * 1 2 w 2 1 - * 1 b 2 w 2 b 1 - * 1 2 w 2 1 - * 1 2 b 1 - * 1 b 1 - * 1 - * - * - * Even if this works, which is unlikely, it will certainly be - * slow because decisions have to be made on a pixel-by-pixel - * basis when encountering borders. - * - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This returns a clone of the ccb; it must be destroyed - *- */ -CCBORD * -ccbaGetCcb(CCBORDA *ccba, - l_int32 index) -{ -CCBORD *ccb; - - PROCNAME("ccbaGetCcb"); - - if (!ccba) - return (CCBORD *)ERROR_PTR("ccba not defined", procName, NULL); - if (index < 0 || index >= ccba->n) - return (CCBORD *)ERROR_PTR("index out of bounds", procName, NULL); - - ccb = ccba->ccb[index]; - ccb->refcount++; - return ccb; -} - - - -/*---------------------------------------------------------------------* - * Top-level border-finding routines * - *---------------------------------------------------------------------*/ -/*! - * \brief pixGetAllCCBorders() - * - * \param[in] pixs 1 bpp - * \return ccborda, or NULL on error - */ -CCBORDA * -pixGetAllCCBorders(PIX *pixs) -{ -l_int32 n, i; -BOX *box; -BOXA *boxa; -CCBORDA *ccba; -CCBORD *ccb; -PIX *pix; -PIXA *pixa; - - PROCNAME("pixGetAllCCBorders"); - - if (!pixs) - return (CCBORDA *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (CCBORDA *)ERROR_PTR("pixs not binary", procName, NULL); - - if ((boxa = pixConnComp(pixs, &pixa, 8)) == NULL) - return (CCBORDA *)ERROR_PTR("boxa not made", procName, NULL); - n = boxaGetCount(boxa); - - if ((ccba = ccbaCreate(pixs, n)) == NULL) { - boxaDestroy(&boxa); - pixaDestroy(&pixa); - return (CCBORDA *)ERROR_PTR("ccba not made", procName, NULL); - } - for (i = 0; i < n; i++) { - if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) { - ccbaDestroy(&ccba); - pixaDestroy(&pixa); - boxaDestroy(&boxa); - return (CCBORDA *)ERROR_PTR("pix not found", procName, NULL); - } - if ((box = pixaGetBox(pixa, i, L_CLONE)) == NULL) { - ccbaDestroy(&ccba); - pixaDestroy(&pixa); - boxaDestroy(&boxa); - pixDestroy(&pix); - return (CCBORDA *)ERROR_PTR("box not found", procName, NULL); - } - ccb = pixGetCCBorders(pix, box); - pixDestroy(&pix); - boxDestroy(&box); - if (!ccb) { - ccbaDestroy(&ccba); - pixaDestroy(&pixa); - boxaDestroy(&boxa); - return (CCBORDA *)ERROR_PTR("ccb not made", procName, NULL); - } -/* ptaWriteStream(stderr, ccb->local, 1); */ - ccbaAddCcb(ccba, ccb); - } - - boxaDestroy(&boxa); - pixaDestroy(&pixa); - return ccba; -} - - -/*! - * \brief pixGetCCBorders() - * - * \param[in] pixs 1 bpp, one 8-connected component - * \param[in] box of %pixs, in global coords - * \return ccbord, or NULL on error - * - *
- * Notes: - * (1) We are finding the exterior and interior borders - * of an 8-connected component. This should be used - * on a pix that has exactly one 8-connected component. - * (2) Typically, pixs is a c.c. in some larger pix. The - * input box gives its location in global coordinates. - * This box is saved, as well as the boxes for the - * borders of any holes within the c.c., but the latter - * are given in relative coords within the c.c. - * (3) The calculations for the exterior border are done - * on a pix with a 1-pixel - * added border, but the saved pixel coordinates - * are the correct (relative) ones for the input pix - * (without a 1-pixel border) - * (4) For the definition of the three tables -- xpostab[], ypostab[] - * and qpostab[] -- see above where they are defined. - *- */ -static CCBORD * -pixGetCCBorders(PIX *pixs, - BOX *box) -{ -l_int32 allzero, i, x, xh, w, nh; -l_int32 xs, ys; /* starting hole border pixel, relative in pixs */ -l_uint32 val; -BOX *boxt, *boxe; -BOXA *boxa; -CCBORD *ccb; -PIX *pixh; /* for hole components */ -PIX *pixt; -PIXA *pixa; - - PROCNAME("pixGetCCBorders"); - - if (!pixs) - return (CCBORD *)ERROR_PTR("pixs not defined", procName, NULL); - if (!box) - return (CCBORD *)ERROR_PTR("box not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (CCBORD *)ERROR_PTR("pixs not binary", procName, NULL); - - pixZero(pixs, &allzero); - if (allzero) - return (CCBORD *)ERROR_PTR("pixs all 0", procName, NULL); - - if ((ccb = ccbCreate(pixs)) == NULL) - return (CCBORD *)ERROR_PTR("ccb not made", procName, NULL); - - /* Get the exterior border */ - pixGetOuterBorder(ccb, pixs, box); - - /* Find the holes, if any */ - if ((pixh = pixHolesByFilling(pixs, 4)) == NULL) { - ccbDestroy(&ccb); - return (CCBORD *)ERROR_PTR("pixh not made", procName, NULL); - } - pixZero(pixh, &allzero); - if (allzero) { /* no holes */ - pixDestroy(&pixh); - return ccb; - } - - /* Get c.c. and locations of the holes */ - if ((boxa = pixConnComp(pixh, &pixa, 4)) == NULL) { - ccbDestroy(&ccb); - pixDestroy(&pixh); - return (CCBORD *)ERROR_PTR("boxa not made", procName, NULL); - } - nh = boxaGetCount(boxa); -/* lept_stderr("%d holes\n", nh); */ - - /* For each hole, find an interior pixel within the hole, - * then march to the right and stop at the first border - * pixel. Save the bounding box of the border, which - * is 1 pixel bigger on each side than the bounding box - * of the hole itself. Note that we use a pix of the - * c.c. of the hole itself to be sure that we start - * with a pixel in the hole of the proper component. - * If we did everything from the parent component, it is - * possible to start in a different hole that is within - * the b.b. of a larger hole. */ - w = pixGetWidth(pixs); - for (i = 0; i < nh; i++) { - boxt = boxaGetBox(boxa, i, L_CLONE); - pixt = pixaGetPix(pixa, i, L_CLONE); - ys = boxt->y; /* there must be a hole pixel on this raster line */ - for (x = 0; x < boxt->w; x++) { /* look for (fg) hole pixel */ - pixGetPixel(pixt, x, 0, &val); - if (val == 1) { - xh = x; - break; - } - } - if (x == boxt->w) { - L_WARNING("no hole pixel found!\n", procName); - continue; - } - for (x = xh + boxt->x; x < w; x++) { /* look for (fg) border pixel */ - pixGetPixel(pixs, x, ys, &val); - if (val == 1) { - xs = x; - break; - } - } - boxe = boxCreate(boxt->x - 1, boxt->y - 1, boxt->w + 2, boxt->h + 2); -#if DEBUG_PRINT - boxPrintStreamInfo(stderr, box); - boxPrintStreamInfo(stderr, boxe); - lept_stderr("xs = %d, ys = %d\n", xs, ys); -#endif /* DEBUG_PRINT */ - pixGetHoleBorder(ccb, pixs, boxe, xs, ys); - boxDestroy(&boxt); - boxDestroy(&boxe); - pixDestroy(&pixt); - } - - boxaDestroy(&boxa); - pixaDestroy(&pixa); - pixDestroy(&pixh); - return ccb; -} - - -/*! - * \brief pixGetOuterBordersPtaa() - * - * \param[in] pixs 1 bpp - * \return ptaa of outer borders, in global coords, or NULL on error - */ -PTAA * -pixGetOuterBordersPtaa(PIX *pixs) -{ -l_int32 i, n; -BOX *box; -BOXA *boxa; -PIX *pix; -PIXA *pixa; -PTA *pta; -PTAA *ptaa; - - PROCNAME("pixGetOuterBordersPtaa"); - - if (!pixs) - return (PTAA *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (PTAA *)ERROR_PTR("pixs not binary", procName, NULL); - - boxa = pixConnComp(pixs, &pixa, 8); - n = boxaGetCount(boxa); - if (n == 0) { - boxaDestroy(&boxa); - pixaDestroy(&pixa); - return (PTAA *)ERROR_PTR("pixs empty", procName, NULL); - } - - ptaa = ptaaCreate(n); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - pix = pixaGetPix(pixa, i, L_CLONE); - pta = pixGetOuterBorderPta(pix, box); - if (pta) - ptaaAddPta(ptaa, pta, L_INSERT); - boxDestroy(&box); - pixDestroy(&pix); - } - - pixaDestroy(&pixa); - boxaDestroy(&boxa); - return ptaa; -} - - -/*! - * \brief pixGetOuterBorderPta() - * - * \param[in] pixs 1 bpp, one 8-connected component - * \param[in] box [optional] of %pixs, in global coordinates - * \return pta of outer border, in global coords, or NULL on error - * - *
- * Notes: - * (1) We are finding the exterior border of a single 8-connected - * component. - * (2) If box is NULL, the outline returned is in the local coords - * of the input pix. Otherwise, box is assumed to give the - * location of the pix in global coordinates, and the returned - * pta will be in those global coordinates. - *- */ -static PTA * -pixGetOuterBorderPta(PIX *pixs, - BOX *box) -{ -l_int32 allzero, x, y; -BOX *boxt; -CCBORD *ccb; -PTA *ptaloc, *ptad; - - PROCNAME("pixGetOuterBorderPta"); - - if (!pixs) - return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (PTA *)ERROR_PTR("pixs not binary", procName, NULL); - - pixZero(pixs, &allzero); - if (allzero) - return (PTA *)ERROR_PTR("pixs all 0", procName, NULL); - - if ((ccb = ccbCreate(pixs)) == NULL) - return (PTA *)ERROR_PTR("ccb not made", procName, NULL); - if (!box) - boxt = boxCreate(0, 0, pixGetWidth(pixs), pixGetHeight(pixs)); - else - boxt = boxClone(box); - - /* Get the exterior border in local coords */ - pixGetOuterBorder(ccb, pixs, boxt); - if ((ptaloc = ptaaGetPta(ccb->local, 0, L_CLONE)) == NULL) { - ccbDestroy(&ccb); - boxDestroy(&boxt); - return (PTA *)ERROR_PTR("ptaloc not made", procName, NULL); - } - - /* Transform to global coordinates, if they are given */ - if (box) { - boxGetGeometry(box, &x, &y, NULL, NULL); - ptad = ptaTransform(ptaloc, x, y, 1.0, 1.0); - } else { - ptad = ptaClone(ptaloc); - } - - ptaDestroy(&ptaloc); - boxDestroy(&boxt); - ccbDestroy(&ccb); - return ptad; -} - - -/*---------------------------------------------------------------------* - * Lower-level border-finding routines * - *---------------------------------------------------------------------*/ -/*! - * \brief pixGetOuterBorder() - * - * \param[in] ccb unfilled - * \param[in] pixs for the component at hand - * \param[in] box for the component, in global coords - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) the border is saved in relative coordinates within - * the c.c. (pixs). Because the calculation is done - * in pixb with added 1 pixel border, we must subtract - * 1 from each pixel value before storing it. - * (2) the stopping condition is that after the first pixel is - * returned to, the next pixel is the second pixel. Having - * these 2 pixels recur in sequence proves the path is closed, - * and we do not store the second pixel again. - *- */ -l_ok -pixGetOuterBorder(CCBORD *ccb, - PIX *pixs, - BOX *box) -{ -l_int32 fpx, fpy, spx, spy, qpos; -l_int32 px, py, npx, npy; -l_int32 w, h, wpl; -l_uint32 *data; -PTA *pta; -PIX *pixb; /* with 1 pixel border */ - - PROCNAME("pixGetOuterBorder"); - - if (!ccb) - return ERROR_INT("ccb not defined", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - - /* Add 1-pixel border all around, and find start pixel */ - if ((pixb = pixAddBorder(pixs, 1, 0)) == NULL) - return ERROR_INT("pixs not made", procName, 1); - if (!nextOnPixelInRaster(pixb, 1, 1, &px, &py)) { - pixDestroy(&pixb); - return ERROR_INT("no start pixel found", procName, 1); - } - qpos = 0; /* relative to p */ - fpx = px; /* save location of first pixel on border */ - fpy = py; - - /* Save box and start pixel in relative coords */ - boxaAddBox(ccb->boxa, box, L_COPY); - ptaAddPt(ccb->start, px - 1, py - 1); - - pta = ptaCreate(0); - ptaaAddPta(ccb->local, pta, L_INSERT); - ptaAddPt(pta, px - 1, py - 1); /* initial point */ - pixGetDimensions(pixb, &w, &h, NULL); - data = pixGetData(pixb); - wpl = pixGetWpl(pixb); - - /* Get the second point; if there is none, return */ - if (findNextBorderPixel(w, h, data, wpl, px, py, &qpos, &npx, &npy)) { - pixDestroy(&pixb); - return 0; - } - - spx = npx; /* save location of second pixel on border */ - spy = npy; - ptaAddPt(pta, npx - 1, npy - 1); /* second point */ - px = npx; - py = npy; - - while (1) { - findNextBorderPixel(w, h, data, wpl, px, py, &qpos, &npx, &npy); - if (px == fpx && py == fpy && npx == spx && npy == spy) - break; - ptaAddPt(pta, npx - 1, npy - 1); - px = npx; - py = npy; - } - - pixDestroy(&pixb); - return 0; -} - - -/*! - * \brief pixGetHoleBorder() - * - * \param[in] ccb the exterior border is already made - * \param[in] pixs for the connected component at hand - * \param[in] box for the specific hole border, in relative - * coordinates to the c.c. - * \param[in] xs, ys first pixel on hole border, relative to c.c. - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) we trace out hole border on pixs without addition - * of single pixel added border to pixs - * (2) therefore all coordinates are relative within the c.c. (pixs) - * (3) same position tables and stopping condition as for - * exterior borders - *- */ -static l_ok -pixGetHoleBorder(CCBORD *ccb, - PIX *pixs, - BOX *box, - l_int32 xs, - l_int32 ys) -{ -l_int32 fpx, fpy, spx, spy, qpos; -l_int32 px, py, npx, npy; -l_int32 w, h, wpl; -l_uint32 *data; -PTA *pta; - - PROCNAME("pixGetHoleBorder"); - - if (!ccb) - return ERROR_INT("ccb not defined", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - - /* Add border and find start pixel */ - qpos = 0; /* orientation of Q relative to P */ - fpx = xs; /* save location of first pixel on border */ - fpy = ys; - - /* Save box and start pixel */ - boxaAddBox(ccb->boxa, box, L_COPY); - ptaAddPt(ccb->start, xs, ys); - - if ((pta = ptaCreate(0)) == NULL) - return ERROR_INT("pta not made", procName, 1); - ptaaAddPta(ccb->local, pta, L_INSERT); - ptaAddPt(pta, xs, ys); /* initial pixel */ - - w = pixGetWidth(pixs); - h = pixGetHeight(pixs); - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - - /* Get the second point; there should always be at least 4 pts - * in a minimal hole border! */ - if (findNextBorderPixel(w, h, data, wpl, xs, ys, &qpos, &npx, &npy)) - return ERROR_INT("isolated hole border point!", procName, 1); - - spx = npx; /* save location of second pixel on border */ - spy = npy; - ptaAddPt(pta, npx, npy); /* second pixel */ - px = npx; - py = npy; - - while (1) { - findNextBorderPixel(w, h, data, wpl, px, py, &qpos, &npx, &npy); - if (px == fpx && py == fpy && npx == spx && npy == spy) - break; - ptaAddPt(pta, npx, npy); - px = npx; - py = npy; - } - - return 0; -} - - -/*! - * \brief findNextBorderPixel() - * - * \param[in] w, h - * \param[in] data, wpl - * \param[in] px, py current P - * \param[in,out] pqpos input current Q; new Q - * \param[out] pnpx, pnpy new P - * \return 0 if next pixel found; 1 otherwise - * - *
- * Notes: - * (1) qpos increases clockwise from 0 to 7, with 0 at - * location with Q to left of P: Q P - * (2) this is a low-level function that does not check input - * parameters. All calling functions should check them. - *- */ -static l_int32 -findNextBorderPixel(l_int32 w, - l_int32 h, - l_uint32 *data, - l_int32 wpl, - l_int32 px, - l_int32 py, - l_int32 *pqpos, - l_int32 *pnpx, - l_int32 *pnpy) -{ -l_int32 qpos, i, pos, npx, npy, val; -l_uint32 *line; - - qpos = *pqpos; - for (i = 1; i < 8; i++) { - pos = (qpos + i) % 8; - npx = px + xpostab[pos]; - npy = py + ypostab[pos]; - line = data + npy * wpl; - val = GET_DATA_BIT(line, npx); - if (val) { - *pnpx = npx; - *pnpy = npy; - *pqpos = qpostab[pos]; - return 0; - } - } - - return 1; -} - - -/*! - * \brief locateOutsideSeedPixel() - * - * \param[in] fpx, fpy location of first pixel - * \param[in] spx, spy location of second pixel - * \param[out] pxs, pys seed pixel to be returned - * - *
- * Notes: - * (1) The first and second pixels must be 8-adjacent, - * so |dx| <= 1 and |dy| <= 1 and both dx and dy - * cannot be 0. There are 8 possible cases. - * (2) The seed pixel is OUTSIDE the foreground of the c.c. - * (3) These rules are for the situation where the INSIDE - * of the c.c. is on the right as you follow the border: - * cw for an exterior border and ccw for a hole border. - *- */ -static void -locateOutsideSeedPixel(l_int32 fpx, - l_int32 fpy, - l_int32 spx, - l_int32 spy, - l_int32 *pxs, - l_int32 *pys) -{ -l_int32 dx, dy; - - dx = spx - fpx; - dy = spy - fpy; - - if (dx * dy == 1) { - *pxs = fpx + dx; - *pys = fpy; - } else if (dx * dy == -1) { - *pxs = fpx; - *pys = fpy + dy; - } else if (dx == 0) { - *pxs = fpx + dy; - *pys = fpy + dy; - } else /* dy == 0 */ { - *pxs = fpx + dx; - *pys = fpy - dx; - } - - return; -} - - - -/*---------------------------------------------------------------------* - * Border conversions * - *---------------------------------------------------------------------*/ -/*! - * \brief ccbaGenerateGlobalLocs() - * - * \param[in] ccba with local chain ptaa of borders computed - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This uses the pixel locs in the local ptaa, which are all - * relative to each c.c., to find the global pixel locations, - * and stores them in the global ptaa. - *- */ -l_ok -ccbaGenerateGlobalLocs(CCBORDA *ccba) -{ -l_int32 ncc, nb, n, i, j, k, xul, yul, x, y; -CCBORD *ccb; -PTAA *ptaal, *ptaag; -PTA *ptal, *ptag; - - PROCNAME("ccbaGenerateGlobalLocs"); - - if (!ccba) - return ERROR_INT("ccba not defined", procName, 1); - - ncc = ccbaGetCount(ccba); /* number of c.c. */ - for (i = 0; i < ncc; i++) { - ccb = ccbaGetCcb(ccba, i); - - /* Get the UL corner in global coords, (xul, yul), of the c.c. */ - boxaGetBoxGeometry(ccb->boxa, 0, &xul, &yul, NULL, NULL); - - /* Make a new global ptaa, removing any old one */ - ptaal = ccb->local; - nb = ptaaGetCount(ptaal); /* number of borders */ - if (ccb->global) /* remove old one */ - ptaaDestroy(&ccb->global); - if ((ptaag = ptaaCreate(nb)) == NULL) - return ERROR_INT("ptaag not made", procName, 1); - ccb->global = ptaag; /* save new one */ - - /* Iterate through the borders for this c.c. */ - for (j = 0; j < nb; j++) { - ptal = ptaaGetPta(ptaal, j, L_CLONE); - n = ptaGetCount(ptal); /* number of pixels in border */ - if ((ptag = ptaCreate(n)) == NULL) - return ERROR_INT("ptag not made", procName, 1); - ptaaAddPta(ptaag, ptag, L_INSERT); - for (k = 0; k < n; k++) { - ptaGetIPt(ptal, k, &x, &y); - ptaAddPt(ptag, x + xul, y + yul); - } - ptaDestroy(&ptal); - } - ccbDestroy(&ccb); - } - - return 0; -} - - -/*! - * \brief ccbaGenerateStepChains() - * - * \param[in] ccba with local chain ptaa of borders computed - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This uses the pixel locs in the local ptaa, - * which are all relative to each c.c., to find - * the step directions for successive pixels in - * the chain, and stores them in the step numaa. - * (2) To get the step direction, use - * 1 2 3 - * 0 P 4 - * 7 6 5 - * where P is the previous pixel at (px, py). The step direction - * is the number (from 0 through 7) for each relative location - * of the current pixel at (cx, cy). It is easily found by - * indexing into a 2-d 3x3 array (dirtab). - *- */ -l_ok -ccbaGenerateStepChains(CCBORDA *ccba) -{ -l_int32 ncc, nb, n, i, j, k; -l_int32 px, py, cx, cy, stepdir; -l_int32 dirtab[][3] = {{1, 2, 3}, {0, -1, 4}, {7, 6, 5}}; -CCBORD *ccb; -NUMA *na; -NUMAA *naa; /* step chain code; to be made */ -PTA *ptal; -PTAA *ptaal; /* local chain code */ - - PROCNAME("ccbaGenerateStepChains"); - - if (!ccba) - return ERROR_INT("ccba not defined", procName, 1); - - ncc = ccbaGetCount(ccba); /* number of c.c. */ - for (i = 0; i < ncc; i++) { - ccb = ccbaGetCcb(ccba, i); - - /* Make a new step numaa, removing any old one */ - ptaal = ccb->local; - nb = ptaaGetCount(ptaal); /* number of borders */ - if (ccb->step) /* remove old one */ - numaaDestroy(&ccb->step); - if ((naa = numaaCreate(nb)) == NULL) - return ERROR_INT("naa not made", procName, 1); - ccb->step = naa; /* save new one */ - - /* Iterate through the borders for this c.c. */ - for (j = 0; j < nb; j++) { - ptal = ptaaGetPta(ptaal, j, L_CLONE); - n = ptaGetCount(ptal); /* number of pixels in border */ - if (n == 1) { /* isolated pixel */ - na = numaCreate(1); /* but leave it empty */ - } else { /* trace out the boundary */ - if ((na = numaCreate(n)) == NULL) - return ERROR_INT("na not made", procName, 1); - ptaGetIPt(ptal, 0, &px, &py); - for (k = 1; k < n; k++) { - ptaGetIPt(ptal, k, &cx, &cy); - stepdir = dirtab[1 + cy - py][1 + cx - px]; - numaAddNumber(na, stepdir); - px = cx; - py = cy; - } - } - numaaAddNuma(naa, na, L_INSERT); - ptaDestroy(&ptal); - } - ccbDestroy(&ccb); /* just decrement refcount */ - } - - return 0; -} - - -/*! - * \brief ccbaStepChainsToPixCoords() - * - * \param[in] ccba with step chains numaa of borders - * \param[in] coordtype CCB_GLOBAL_COORDS or CCB_LOCAL_COORDS - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This uses the step chain data in each ccb to determine - * the pixel locations, either global or local, - * and stores them in the appropriate ptaa, - * either global or local. For the latter, the - * pixel locations are relative to the c.c. - *- */ -l_ok -ccbaStepChainsToPixCoords(CCBORDA *ccba, - l_int32 coordtype) -{ -l_int32 ncc, nb, n, i, j, k; -l_int32 xul, yul, xstart, ystart, x, y, stepdir; -BOXA *boxa; -CCBORD *ccb; -NUMA *na; -NUMAA *naa; -PTAA *ptaan; /* new pix coord ptaa */ -PTA *ptas, *ptan; - - PROCNAME("ccbaStepChainsToPixCoords"); - - if (!ccba) - return ERROR_INT("ccba not defined", procName, 1); - if (coordtype != CCB_GLOBAL_COORDS && coordtype != CCB_LOCAL_COORDS) - return ERROR_INT("coordtype not valid", procName, 1); - - ncc = ccbaGetCount(ccba); /* number of c.c. */ - for (i = 0; i < ncc; i++) { - ccb = ccbaGetCcb(ccba, i); - if ((naa = ccb->step) == NULL) - return ERROR_INT("step numaa not found", procName, 1); - if ((boxa = ccb->boxa) == NULL) - return ERROR_INT("boxa not found", procName, 1); - if ((ptas = ccb->start) == NULL) - return ERROR_INT("start pta not found", procName, 1); - - /* For global coords, get the (xul, yul) of the c.c.; - * otherwise, use relative coords. */ - if (coordtype == CCB_LOCAL_COORDS) { - xul = 0; - yul = 0; - } else { /* coordtype == CCB_GLOBAL_COORDS */ - /* Get UL corner in global coords */ - if (boxaGetBoxGeometry(boxa, 0, &xul, &yul, NULL, NULL)) - return ERROR_INT("bounding rectangle not found", procName, 1); - } - - /* Make a new ptaa, removing any old one */ - nb = numaaGetCount(naa); /* number of borders */ - if ((ptaan = ptaaCreate(nb)) == NULL) - return ERROR_INT("ptaan not made", procName, 1); - if (coordtype == CCB_LOCAL_COORDS) { - if (ccb->local) /* remove old one */ - ptaaDestroy(&ccb->local); - ccb->local = ptaan; /* save new local chain */ - } else { /* coordtype == CCB_GLOBAL_COORDS */ - if (ccb->global) /* remove old one */ - ptaaDestroy(&ccb->global); - ccb->global = ptaan; /* save new global chain */ - } - - /* Iterate through the borders for this c.c. */ - for (j = 0; j < nb; j++) { - na = numaaGetNuma(naa, j, L_CLONE); - n = numaGetCount(na); /* number of steps in border */ - if ((ptan = ptaCreate(n + 1)) == NULL) - return ERROR_INT("ptan not made", procName, 1); - ptaaAddPta(ptaan, ptan, L_INSERT); - ptaGetIPt(ptas, j, &xstart, &ystart); - x = xul + xstart; - y = yul + ystart; - ptaAddPt(ptan, x, y); - for (k = 0; k < n; k++) { - numaGetIValue(na, k, &stepdir); - x += xpostab[stepdir]; - y += ypostab[stepdir]; - ptaAddPt(ptan, x, y); - } - numaDestroy(&na); - } - ccbDestroy(&ccb); - } - - return 0; -} - - -/*! - * \brief ccbaGenerateSPGlobalLocs() - * - * \param[in] ccba - * \param[in] ptsflag CCB_SAVE_ALL_PTS or CCB_SAVE_TURNING_PTS - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This calculates the splocal rep if not yet made. - * (2) It uses the local pixel values in splocal, the single - * path pta, which are all relative to each c.c., to find - * the corresponding global pixel locations, and stores - * them in the spglobal pta. - * (3) This lists only the turning points: it both makes a - * valid svg file and is typically about half the size - * when all border points are listed. - *- */ -l_ok -ccbaGenerateSPGlobalLocs(CCBORDA *ccba, - l_int32 ptsflag) -{ -l_int32 ncc, npt, i, j, xul, yul, x, y, delx, dely; -l_int32 xp, yp, delxp, delyp; /* prev point and increments */ -CCBORD *ccb; -PTA *ptal, *ptag; - - PROCNAME("ccbaGenerateSPGlobalLocs"); - - if (!ccba) - return ERROR_INT("ccba not defined", procName, 1); - - /* Make sure we have a local single path representation */ - if ((ccb = ccbaGetCcb(ccba, 0)) == NULL) - return ERROR_INT("no ccb", procName, 1); - if (!ccb->splocal) - ccbaGenerateSinglePath(ccba); - ccbDestroy(&ccb); /* clone ref */ - - ncc = ccbaGetCount(ccba); /* number of c.c. */ - for (i = 0; i < ncc; i++) { - ccb = ccbaGetCcb(ccba, i); - - /* Get the UL corner in global coords, (xul, yul), of the c.c. */ - if (boxaGetBoxGeometry(ccb->boxa, 0, &xul, &yul, NULL, NULL)) - return ERROR_INT("bounding rectangle not found", procName, 1); - - /* Make a new spglobal pta, removing any old one */ - ptal = ccb->splocal; - npt = ptaGetCount(ptal); /* number of points */ - if (ccb->spglobal) /* remove old one */ - ptaDestroy(&ccb->spglobal); - if ((ptag = ptaCreate(npt)) == NULL) - return ERROR_INT("ptag not made", procName, 1); - ccb->spglobal = ptag; /* save new one */ - - /* Convert local to global */ - if (ptsflag == CCB_SAVE_ALL_PTS) { - for (j = 0; j < npt; j++) { - ptaGetIPt(ptal, j, &x, &y); - ptaAddPt(ptag, x + xul, y + yul); - } - } else { /* ptsflag = CCB_SAVE_TURNING_PTS */ - ptaGetIPt(ptal, 0, &xp, &yp); /* get the 1st pt */ - ptaAddPt(ptag, xp + xul, yp + yul); /* save the 1st pt */ - if (npt == 2) { /* get and save the 2nd pt */ - ptaGetIPt(ptal, 1, &x, &y); - ptaAddPt(ptag, x + xul, y + yul); - } else if (npt > 2) { - ptaGetIPt(ptal, 1, &x, &y); - delxp = x - xp; - delyp = y - yp; - xp = x; - yp = y; - for (j = 2; j < npt; j++) { - ptaGetIPt(ptal, j, &x, &y); - delx = x - xp; - dely = y - yp; - if (delx != delxp || dely != delyp) - ptaAddPt(ptag, xp + xul, yp + yul); - xp = x; - yp = y; - delxp = delx; - delyp = dely; - } - ptaAddPt(ptag, xp + xul, yp + yul); - } - } - - ccbDestroy(&ccb); /* clone ref */ - } - - return 0; -} - - - -/*---------------------------------------------------------------------* - * Conversion to single path * - *---------------------------------------------------------------------*/ -/*! - * \brief ccbaGenerateSinglePath() - * - * \param[in] ccba - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Generates a single border in local pixel coordinates. - * For each c.c., if there is just an outer border, copy it. - * If there are also hole borders, for each hole border, - * determine the smallest horizontal or vertical - * distance from the border to the outside of the c.c., - * and find a path through the c.c. for this cut. - * We do this in a way that guarantees a pixel from the - * hole border is the starting point of the path, and - * we must verify that the path intersects the outer - * border (if it intersects it, then it ends on it). - * One can imagine pathological cases, but they may not - * occur in images of text characters and un-textured - * line graphics. - * (2) Once it is verified that the path through the c.c. - * intersects both the hole and outer borders, we - * generate the full single path for all borders in the - * c.c. Starting at the start point on the outer - * border, when we hit a line on a cut, we take - * the cut, do the hold border, and return on the cut - * to the outer border. We compose a pta of the - * outer border pts that are on cut paths, and for - * every point on the outer border (as we go around), - * we check against this pta. When we find a matching - * point in the pta, we do its cut path and hole border. - * The single path is saved in the ccb. - *- */ -l_ok -ccbaGenerateSinglePath(CCBORDA *ccba) -{ -l_int32 i, j, k, ncc, nb, ncut, npt, dir, len, state, lostholes; -l_int32 x, y, xl, yl, xf, yf; -BOX *boxinner; -BOXA *boxa; -CCBORD *ccb; -PTA *pta, *ptac, *ptah; -PTA *ptahc; /* cyclic permutation of hole border, with end pts at cut */ -PTA *ptas; /* output result: new single path for c.c. */ -PTA *ptaf; /* points on the hole borders that intersect with cuts */ -PTA *ptal; /* points on outer border that intersect with cuts */ -PTA *ptap, *ptarp; /* path and reverse path between borders */ -PTAA *ptaa; -PTAA *ptaap; /* ptaa for all paths between borders */ - - PROCNAME("ccbaGenerateSinglePath"); - - if (!ccba) - return ERROR_INT("ccba not defined", procName, 1); - - ncc = ccbaGetCount(ccba); /* number of c.c. */ - lostholes = 0; - for (i = 0; i < ncc; i++) { - ccb = ccbaGetCcb(ccba, i); - if ((ptaa = ccb->local) == NULL) { - L_WARNING("local pixel loc array not found\n", procName); - continue; - } - nb = ptaaGetCount(ptaa); /* number of borders in the c.c. */ - - /* Prepare the output pta */ - if (ccb->splocal) - ptaDestroy(&ccb->splocal); - ptas = ptaCreate(0); - ccb->splocal = ptas; - - /* If no holes, just concat the outer border */ - pta = ptaaGetPta(ptaa, 0, L_CLONE); - if (nb == 1 || nb > NMAX_HOLES + 1) { - ptaJoin(ptas, pta, 0, -1); - ptaDestroy(&pta); /* remove clone */ - ccbDestroy(&ccb); /* remove clone */ - continue; - } - - /* Find the (nb - 1) cut paths that connect holes - * with outer border */ - boxa = ccb->boxa; - ptaap = ptaaCreate(nb - 1); - ptaf = ptaCreate(nb - 1); - ptal = ptaCreate(nb - 1); - for (j = 1; j < nb; j++) { - boxinner = boxaGetBox(boxa, j, L_CLONE); - - /* Find a short path and store it */ - ptac = getCutPathForHole(ccb->pix, pta, boxinner, &dir, &len); - if (len == 0) { /* bad: we lose the hole! */ - lostholes++; -/* boxPrintStreamInfo(stderr, boxa->box[0]); */ - } - ptaaAddPta(ptaap, ptac, L_INSERT); -/* lept_stderr("dir = %d, length = %d\n", dir, len); */ -/* ptaWriteStream(stderr, ptac, 1); */ - - /* Store the first and last points in the cut path, - * which must be on a hole border and the outer - * border, respectively */ - ncut = ptaGetCount(ptac); - if (ncut == 0) { /* missed hole; neg coords won't match */ - ptaAddPt(ptaf, -1, -1); - ptaAddPt(ptal, -1, -1); - } else { - ptaGetIPt(ptac, 0, &x, &y); - ptaAddPt(ptaf, x, y); - ptaGetIPt(ptac, ncut - 1, &x, &y); - ptaAddPt(ptal, x, y); - } - boxDestroy(&boxinner); - } - - /* Make a single path for the c.c. using these connections */ - npt = ptaGetCount(pta); /* outer border pts */ - for (k = 0; k < npt; k++) { - ptaGetIPt(pta, k, &x, &y); - if (k == 0) { /* if there is a cut at the first point, - * we can wait until the end to take it */ - ptaAddPt(ptas, x, y); - continue; - } - state = L_NOT_FOUND; - for (j = 0; j < nb - 1; j++) { /* iterate over cut end pts */ - ptaGetIPt(ptal, j, &xl, &yl); /* cut point on outer border */ - if (x == xl && y == yl) { /* take this cut to the hole */ - state = L_FOUND; - ptap = ptaaGetPta(ptaap, j, L_CLONE); - ptarp = ptaReverse(ptap, 1); - /* Cut point on hole border: */ - ptaGetIPt(ptaf, j, &xf, &yf); - /* Hole border: */ - ptah = ptaaGetPta(ptaa, j + 1, L_CLONE); - ptahc = ptaCyclicPerm(ptah, xf, yf); -/* ptaWriteStream(stderr, ptahc, 1); */ - ptaJoin(ptas, ptarp, 0, -1); - ptaJoin(ptas, ptahc, 0, -1); - ptaJoin(ptas, ptap, 0, -1); - ptaDestroy(&ptap); - ptaDestroy(&ptarp); - ptaDestroy(&ptah); - ptaDestroy(&ptahc); - break; - } - } - if (state == L_NOT_FOUND) - ptaAddPt(ptas, x, y); - } - -/* ptaWriteStream(stderr, ptas, 1); */ - ptaaDestroy(&ptaap); - ptaDestroy(&ptaf); - ptaDestroy(&ptal); - ptaDestroy(&pta); /* remove clone */ - ccbDestroy(&ccb); /* remove clone */ - } - - if (lostholes > 0) - L_WARNING("***** %d lost holes *****\n", procName, lostholes); - - return 0; -} - - -/*! - * \brief getCutPathForHole() - * - * \param[in] pix 1 bpp, of c.c. - * \param[in] pta of outer border - * \param[in] boxinner bounding box of hole path - * \param[out] pdir direction (0-3), returned; only needed for debug - * \param[out] plen length of path, returned - * \return pta of pts on cut path from the hole border - * to the outer border, including end points on - * both borders; or NULL on error - * - *
- * Notes: - * (1) If we don't find a path, we return a pta with no pts - * in it and len = 0. - * (2) The goal is to get a reasonably short path between the - * inner and outer borders, that goes entirely within the fg of - * the pix. This function is cheap-and-dirty, may fail for some - * holes in complex topologies such as those you might find in a - * moderately dark scanned halftone. If it fails to find a - * path to any particular hole, it gives a warning, and because - * that hole path is not included, the hole will not be rendered. - *- */ -PTA * -getCutPathForHole(PIX *pix, - PTA *pta, - BOX *boxinner, - l_int32 *pdir, - l_int32 *plen) -{ -l_int32 w, h, nc, x, y, xl, yl, xmid, ymid; -l_uint32 val; -PTA *ptac; - - PROCNAME("getCutPathForHole"); - - if (!pix) - return (PTA *)ERROR_PTR("pix not defined", procName, NULL); - if (!pta) - return (PTA *)ERROR_PTR("pta not defined", procName, NULL); - if (!boxinner) - return (PTA *)ERROR_PTR("boxinner not defined", procName, NULL); - - w = pixGetWidth(pix); - h = pixGetHeight(pix); - - if ((ptac = ptaCreate(4)) == NULL) - return (PTA *)ERROR_PTR("ptac not made", procName, NULL); - xmid = boxinner->x + boxinner->w / 2; - ymid = boxinner->y + boxinner->h / 2; - - /* try top first */ - for (y = ymid; y >= 0; y--) { - pixGetPixel(pix, xmid, y, &val); - if (val == 1) { - ptaAddPt(ptac, xmid, y); - break; - } - } - for (y = y - 1; y >= 0; y--) { - pixGetPixel(pix, xmid, y, &val); - if (val == 1) - ptaAddPt(ptac, xmid, y); - else - break; - } - nc = ptaGetCount(ptac); - ptaGetIPt(ptac, nc - 1, &xl, &yl); - if (ptaContainsPt(pta, xl, yl)) { - *pdir = 1; - *plen = nc; - return ptac; - } - - /* Next try bottom */ - ptaEmpty(ptac); - for (y = ymid; y < h; y++) { - pixGetPixel(pix, xmid, y, &val); - if (val == 1) { - ptaAddPt(ptac, xmid, y); - break; - } - } - for (y = y + 1; y < h; y++) { - pixGetPixel(pix, xmid, y, &val); - if (val == 1) - ptaAddPt(ptac, xmid, y); - else - break; - } - nc = ptaGetCount(ptac); - ptaGetIPt(ptac, nc - 1, &xl, &yl); - if (ptaContainsPt(pta, xl, yl)) { - *pdir = 3; - *plen = nc; - return ptac; - } - - /* Next try left */ - ptaEmpty(ptac); - for (x = xmid; x >= 0; x--) { - pixGetPixel(pix, x, ymid, &val); - if (val == 1) { - ptaAddPt(ptac, x, ymid); - break; - } - } - for (x = x - 1; x >= 0; x--) { - pixGetPixel(pix, x, ymid, &val); - if (val == 1) - ptaAddPt(ptac, x, ymid); - else - break; - } - nc = ptaGetCount(ptac); - ptaGetIPt(ptac, nc - 1, &xl, &yl); - if (ptaContainsPt(pta, xl, yl)) { - *pdir = 0; - *plen = nc; - return ptac; - } - - /* Finally try right */ - ptaEmpty(ptac); - for (x = xmid; x < w; x++) { - pixGetPixel(pix, x, ymid, &val); - if (val == 1) { - ptaAddPt(ptac, x, ymid); - break; - } - } - for (x = x + 1; x < w; x++) { - pixGetPixel(pix, x, ymid, &val); - if (val == 1) - ptaAddPt(ptac, x, ymid); - else - break; - } - nc = ptaGetCount(ptac); - ptaGetIPt(ptac, nc - 1, &xl, &yl); - if (ptaContainsPt(pta, xl, yl)) { - *pdir = 2; - *plen = nc; - return ptac; - } - - /* If we get here, we've failed! */ - ptaEmpty(ptac); - L_WARNING("no path found\n", procName); - *plen = 0; - return ptac; -} - - - -/*---------------------------------------------------------------------* - * Border rendering * - *---------------------------------------------------------------------*/ -/*! - * \brief ccbaDisplayBorder() - * - * \param[in] ccba - * \return pix of border pixels, or NULL on error - * - *
- * Notes: - * (1) Uses global ptaa, which gives each border pixel in - * global coordinates, and must be computed in advance - * by calling ccbaGenerateGlobalLocs(). - *- */ -PIX * -ccbaDisplayBorder(CCBORDA *ccba) -{ -l_int32 ncc, nb, n, i, j, k, x, y; -CCBORD *ccb; -PIX *pixd; -PTAA *ptaa; -PTA *pta; - - PROCNAME("ccbaDisplayBorder"); - - if (!ccba) - return (PIX *)ERROR_PTR("ccba not defined", procName, NULL); - - if ((pixd = pixCreate(ccba->w, ccba->h, 1)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - ncc = ccbaGetCount(ccba); /* number of c.c. */ - for (i = 0; i < ncc; i++) { - ccb = ccbaGetCcb(ccba, i); - if ((ptaa = ccb->global) == NULL) { - L_WARNING("global pixel loc array not found", procName); - continue; - } - nb = ptaaGetCount(ptaa); /* number of borders in the c.c. */ - for (j = 0; j < nb; j++) { - pta = ptaaGetPta(ptaa, j, L_CLONE); - n = ptaGetCount(pta); /* number of pixels in the border */ - for (k = 0; k < n; k++) { - ptaGetIPt(pta, k, &x, &y); - pixSetPixel(pixd, x, y, 1); - } - ptaDestroy(&pta); - } - ccbDestroy(&ccb); - } - - return pixd; -} - - -/*! - * \brief ccbaDisplaySPBorder() - * - * \param[in] ccba - * \return pix of border pixels, or NULL on error - * - *
- * Notes: - * (1) Uses spglobal pta, which gives each border pixel in - * global coordinates, one path per c.c., and must - * be computed in advance by calling ccbaGenerateSPGlobalLocs(). - *- */ -PIX * -ccbaDisplaySPBorder(CCBORDA *ccba) -{ -l_int32 ncc, npt, i, j, x, y; -CCBORD *ccb; -PIX *pixd; -PTA *ptag; - - PROCNAME("ccbaDisplaySPBorder"); - - if (!ccba) - return (PIX *)ERROR_PTR("ccba not defined", procName, NULL); - - if ((pixd = pixCreate(ccba->w, ccba->h, 1)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - ncc = ccbaGetCount(ccba); /* number of c.c. */ - for (i = 0; i < ncc; i++) { - ccb = ccbaGetCcb(ccba, i); - if ((ptag = ccb->spglobal) == NULL) { - L_WARNING("spglobal pixel loc array not found\n", procName); - continue; - } - npt = ptaGetCount(ptag); /* number of pixels on path */ - for (j = 0; j < npt; j++) { - ptaGetIPt(ptag, j, &x, &y); - pixSetPixel(pixd, x, y, 1); - } - ccbDestroy(&ccb); /* clone ref */ - } - - return pixd; -} - - -/*! - * \brief ccbaDisplayImage1() - * - * \param[in] ccba - * \return pix of image, or NULL on error - * - *
- * Notes: - * (1) Uses local ptaa, which gives each border pixel in - * local coordinates, so the actual pixel positions must - * be computed using all offsets. - * (2) For the holes, use coordinates relative to the c.c. - * (3) This is slower than Method 2. - * (4) This uses topological properties (Method 1) to do scan - * conversion to raster - * - * This algorithm deserves some commentary. - * - * I first tried the following: - * ~ outer borders: 4-fill from outside, stopping at the - * border, using pixFillClosedBorders() - * ~ inner borders: 4-fill from outside, stopping again - * at the border, XOR with the border, and invert - * to get the hole. This did not work, because if - * you have a hole border that looks like: - * - * x x x x x x - * x x - * x x x x x - * x x o x x - * x x - * x x - * x x x - * - * if you 4-fill from the outside, the pixel 'o' will - * not be filled! XORing with the border leaves it OFF. - * Inverting then gives a single bad ON pixel that is not - * actually part of the hole. - * - * So what you must do instead is 4-fill the holes from inside. - * You can do this from a seedfill, using a pix with the hole - * border as the filling mask. But you need to start with a - * pixel inside the hole. How is this determined? The best - * way is from the contour. We have a right-hand shoulder - * rule for inside (i.e., the filled region). Take the - * first 2 pixels of the hole border, and compute dx and dy - * (second coord minus first coord: dx = sx - fx, dy = sy - fy). - * There are 8 possibilities, depending on the values of dx and - * dy (which can each be -1, 0, and +1, but not both 0). - * These 8 cases can be broken into 4; see the simple algorithm below. - * Once you have an interior seed pixel, you fill from the seed, - * clipping with the hole border pix by filling into its invert. - * - * You then successively XOR these interior filled components, in any order. - *- */ -PIX * -ccbaDisplayImage1(CCBORDA *ccba) -{ -l_int32 ncc, i, nb, n, j, k, x, y, xul, yul, xoff, yoff, w, h; -l_int32 fpx, fpy, spx, spy, xs, ys; -BOX *box; -BOXA *boxa; -CCBORD *ccb; -PIX *pixd, *pixt, *pixh; -PTAA *ptaa; -PTA *pta; - - PROCNAME("ccbaDisplayImage1"); - - if (!ccba) - return (PIX *)ERROR_PTR("ccba not defined", procName, NULL); - - if ((pixd = pixCreate(ccba->w, ccba->h, 1)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - ncc = ccbaGetCount(ccba); - for (i = 0; i < ncc; i++) { - ccb = ccbaGetCcb(ccba, i); - if ((boxa = ccb->boxa) == NULL) { - pixDestroy(&pixd); - return (PIX *)ERROR_PTR("boxa not found", procName, NULL); - } - - /* Render border in pixt */ - if ((ptaa = ccb->local) == NULL) { - L_WARNING("local chain array not found\n", procName); - continue; - } - - nb = ptaaGetCount(ptaa); /* number of borders in the c.c. */ - for (j = 0; j < nb; j++) { - if ((box = boxaGetBox(boxa, j, L_CLONE)) == NULL) { - pixDestroy(&pixd); - return (PIX *)ERROR_PTR("b. box not found", procName, NULL); - } - if (j == 0) { - boxGetGeometry(box, &xul, &yul, &w, &h); - xoff = yoff = 0; - } else { - boxGetGeometry(box, &xoff, &yoff, &w, &h); - } - boxDestroy(&box); - - /* Render the border in a minimum-sized pix; - * subtract xoff and yoff because the pixel - * location is stored relative to the c.c., but - * we need it relative to just the hole border. */ - if ((pixt = pixCreate(w, h, 1)) == NULL) { - pixDestroy(&pixd); - return (PIX *)ERROR_PTR("pixt not made", procName, NULL); - } - pta = ptaaGetPta(ptaa, j, L_CLONE); - n = ptaGetCount(pta); /* number of pixels in the border */ - for (k = 0; k < n; k++) { - ptaGetIPt(pta, k, &x, &y); - pixSetPixel(pixt, x - xoff, y - yoff, 1); - if (j > 0) { /* need this for finding hole border pixel */ - if (k == 0) { - fpx = x - xoff; - fpy = y - yoff; - } - if (k == 1) { - spx = x - xoff; - spy = y - yoff; - } - } - } - ptaDestroy(&pta); - - /* Get the filled component */ - if (j == 0) { /* if outer border, fill from outer boundary */ - if ((pixh = pixFillClosedBorders(pixt, 4)) == NULL) { - pixDestroy(&pixd); - pixDestroy(&pixt); - return (PIX *)ERROR_PTR("pixh not made", procName, NULL); - } - } else { /* fill the hole from inside */ - /* get the location of a seed pixel in the hole */ - locateOutsideSeedPixel(fpx, fpy, spx, spy, &xs, &ys); - - /* Put seed in hole and fill interior of hole, - * using pixt as clipping mask */ - pixh = pixCreateTemplate(pixt); - pixSetPixel(pixh, xs, ys, 1); /* put seed pixel in hole */ - pixInvert(pixt, pixt); /* to make filling mask */ - pixSeedfillBinary(pixh, pixh, pixt, 4); /* 4-fill hole */ - } - - /* XOR into the dest */ - pixRasterop(pixd, xul + xoff, yul + yoff, w, h, PIX_XOR, - pixh, 0, 0); - pixDestroy(&pixt); - pixDestroy(&pixh); - } - ccbDestroy(&ccb); - } - return pixd; -} - - - -/*! - * \brief ccbaDisplayImage2() - * - * \param[in] ccba - * \return pix of image, or NULL on error - * - *
- * Notes: - * (1) Uses local chain ptaa, which gives each border pixel in - * local coordinates, so the actual pixel positions must - * be computed using all offsets. - * (2) Treats exterior and hole borders on equivalent - * footing, and does all calculations on a pix - * that spans the c.c. with a 1 pixel added boundary. - * (3) This uses topological properties (Method 2) to do scan - * conversion to raster - * (4) The algorithm is described at the top of this file (Method 2). - * It is preferred to Method 1 because it is between 1.2x and 2x - * faster than Method 1. - *- */ -PIX * -ccbaDisplayImage2(CCBORDA *ccba) -{ -l_int32 ncc, nb, n, i, j, k, x, y, xul, yul, w, h; -l_int32 fpx, fpy, spx, spy, xs, ys; -BOXA *boxa; -CCBORD *ccb; -PIX *pixd, *pixc, *pixs; -PTAA *ptaa; -PTA *pta; - - PROCNAME("ccbaDisplayImage2"); - - if (!ccba) - return (PIX *)ERROR_PTR("ccba not defined", procName, NULL); - - if ((pixd = pixCreate(ccba->w, ccba->h, 1)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - ncc = ccbaGetCount(ccba); - for (i = 0; i < ncc; i++) { - /* Generate clipping mask from border pixels and seed image - * from one seed for each closed border. */ - ccb = ccbaGetCcb(ccba, i); - if ((boxa = ccb->boxa) == NULL) { - pixDestroy(&pixd); - ccbDestroy(&ccb); - return (PIX *)ERROR_PTR("boxa not found", procName, NULL); - } - if (boxaGetBoxGeometry(boxa, 0, &xul, &yul, &w, &h)) { - pixDestroy(&pixd); - ccbDestroy(&ccb); - return (PIX *)ERROR_PTR("b. box not found", procName, NULL); - } - pixc = pixCreate(w + 2, h + 2, 1); - pixs = pixCreateTemplate(pixc); - - if ((ptaa = ccb->local) == NULL) { - pixDestroy(&pixc); - pixDestroy(&pixs); - ccbDestroy(&ccb); - L_WARNING("local chain array not found\n", procName); - continue; - } - nb = ptaaGetCount(ptaa); /* number of borders in the c.c. */ - for (j = 0; j < nb; j++) { - pta = ptaaGetPta(ptaa, j, L_CLONE); - n = ptaGetCount(pta); /* number of pixels in the border */ - - /* Render border pixels in pixc */ - for (k = 0; k < n; k++) { - ptaGetIPt(pta, k, &x, &y); - pixSetPixel(pixc, x + 1, y + 1, 1); - if (k == 0) { - fpx = x + 1; - fpy = y + 1; - } else if (k == 1) { - spx = x + 1; - spy = y + 1; - } - } - - /* Get and set seed pixel for this border in pixs */ - if (n > 1) - locateOutsideSeedPixel(fpx, fpy, spx, spy, &xs, &ys); - else /* isolated c.c. */ - xs = ys = 0; - pixSetPixel(pixs, xs, ys, 1); - ptaDestroy(&pta); - } - - /* Fill from seeds in pixs, using pixc as the clipping mask, - * to reconstruct the c.c. */ - pixInvert(pixc, pixc); /* to convert clipping -> filling mask */ - pixSeedfillBinary(pixs, pixs, pixc, 4); /* 4-fill */ - pixInvert(pixs, pixs); /* to make the c.c. */ - - /* XOR into the dest */ - pixRasterop(pixd, xul, yul, w, h, PIX_XOR, pixs, 1, 1); - - pixDestroy(&pixc); - pixDestroy(&pixs); - ccbDestroy(&ccb); /* ref-counted */ - } - return pixd; -} - - - -/*---------------------------------------------------------------------* - * Serialize for I/O * - *---------------------------------------------------------------------*/ -/*! - * \brief ccbaWrite() - * - * \param[in] filename - * \param[in] ccba - * \return 0 if OK, 1 on error - */ -l_ok -ccbaWrite(const char *filename, - CCBORDA *ccba) -{ -FILE *fp; - - PROCNAME("ccbaWrite"); - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!ccba) - return ERROR_INT("ccba not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "wb+")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - if (ccbaWriteStream(fp, ccba)) { - fclose(fp); - return ERROR_INT("ccba not written to stream", procName, 1); - } - - fclose(fp); - return 0; -} - - - -/*! - * \brief ccbaWriteStream() - * - * \param[in] fp file stream - * \param[in] ccba - * \return 0 if OK; 1 on error - * - * Format: - * \code - * ccba: %7d cc\n num. c.c.) (ascii) (18B - * pix width 4B - * pix height 4B - * [for i = 1, ncc] - * ulx 4B - * uly 4B - * w 4B -- not req'd for reconstruction - * h 4B -- not req'd for reconstruction - * number of borders 4B - * [for j = 1, nb] - * startx 4B - * starty 4B - * [for k = 1, nb] - * 2 steps 1B - * end in z8 or 88 1B - * \endcode - */ -l_ok -ccbaWriteStream(FILE *fp, - CCBORDA *ccba) -{ -char strbuf[256]; -l_uint8 bval; -l_uint8 *datain, *dataout; -l_int32 i, j, k, bx, by, bw, bh, val, startx, starty; -l_int32 ncc, nb, n; -l_uint32 w, h; -size_t inbytes, outbytes; -L_BBUFFER *bbuf; -CCBORD *ccb; -NUMA *na; -NUMAA *naa; -PTA *pta; - - PROCNAME("ccbaWriteStream"); - -#if !HAVE_LIBZ /* defined in environ.h */ - return ERROR_INT("no libz: can't write data", procName, 1); -#else - - if (!fp) - return ERROR_INT("stream not open", procName, 1); - if (!ccba) - return ERROR_INT("ccba not defined", procName, 1); - - if ((bbuf = bbufferCreate(NULL, 1000)) == NULL) - return ERROR_INT("bbuf not made", procName, 1); - - ncc = ccbaGetCount(ccba); - snprintf(strbuf, sizeof(strbuf), "ccba: %7d cc\n", ncc); - bbufferRead(bbuf, (l_uint8 *)strbuf, 18); - w = pixGetWidth(ccba->pix); - h = pixGetHeight(ccba->pix); - bbufferRead(bbuf, (l_uint8 *)&w, 4); /* width */ - bbufferRead(bbuf, (l_uint8 *)&h, 4); /* height */ - for (i = 0; i < ncc; i++) { - ccb = ccbaGetCcb(ccba, i); - if (boxaGetBoxGeometry(ccb->boxa, 0, &bx, &by, &bw, &bh)) { - bbufferDestroy(&bbuf); - return ERROR_INT("bounding box not found", procName, 1); - } - bbufferRead(bbuf, (l_uint8 *)&bx, 4); /* ulx of c.c. */ - bbufferRead(bbuf, (l_uint8 *)&by, 4); /* uly of c.c. */ - bbufferRead(bbuf, (l_uint8 *)&bw, 4); /* w of c.c. */ - bbufferRead(bbuf, (l_uint8 *)&bh, 4); /* h of c.c. */ - if ((naa = ccb->step) == NULL) { - ccbaGenerateStepChains(ccba); - naa = ccb->step; - } - nb = numaaGetCount(naa); - bbufferRead(bbuf, (l_uint8 *)&nb, 4); /* number of borders in c.c. */ - pta = ccb->start; - for (j = 0; j < nb; j++) { - ptaGetIPt(pta, j, &startx, &starty); - bbufferRead(bbuf, (l_uint8 *)&startx, 4); /* starting x in border */ - bbufferRead(bbuf, (l_uint8 *)&starty, 4); /* starting y in border */ - na = numaaGetNuma(naa, j, L_CLONE); - n = numaGetCount(na); - for (k = 0; k < n; k++) { - numaGetIValue(na, k, &val); - if (k % 2 == 0) - bval = (l_uint8)val << 4; - else - bval |= (l_uint8)val; - if (k % 2 == 1) - bbufferRead(bbuf, (l_uint8 *)&bval, 1); /* 2 border steps */ - } - if (n % 2 == 1) { - bval |= 0x8; - bbufferRead(bbuf, (l_uint8 *)&bval, 1); /* end with 0xz8, */ - /* where z = {0..7} */ - } else { /* n % 2 == 0 */ - bval = 0x88; - bbufferRead(bbuf, (l_uint8 *)&bval, 1); /* end with 0x88 */ - } - numaDestroy(&na); - } - ccbDestroy(&ccb); - } - - datain = bbufferDestroyAndSaveData(&bbuf, &inbytes); - dataout = zlibCompress(datain, inbytes, &outbytes); - fwrite(dataout, 1, outbytes, fp); - - LEPT_FREE(datain); - LEPT_FREE(dataout); - return 0; - -#endif /* !HAVE_LIBZ */ -} - - -/*! - * \brief ccbaRead() - * - * \param[in] filename - * \return ccba, or NULL on error - */ -CCBORDA * -ccbaRead(const char *filename) -{ -FILE *fp; -CCBORDA *ccba; - - PROCNAME("ccbaRead"); - - if (!filename) - return (CCBORDA *)ERROR_PTR("filename not defined", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (CCBORDA *)ERROR_PTR("stream not opened", procName, NULL); - ccba = ccbaReadStream(fp); - fclose(fp); - - if (!ccba) - return (CCBORDA *)ERROR_PTR("ccba not returned", procName, NULL); - return ccba; -} - - -/*! - * \brief ccbaReadStream() - * - * \param[in] fp file stream - * \return ccba, or NULL on error - * - * \code - * Format: ccba: %7d cc\n num. c.c.) (ascii) (17B - * pix width 4B - * pix height 4B - * [for i = 1, ncc] - * ulx 4B - * uly 4B - * w 4B -- not req'd for reconstruction - * h 4B -- not req'd for reconstruction - * number of borders 4B - * [for j = 1, nb] - * startx 4B - * starty 4B - * [for k = 1, nb] - * 2 steps 1B - * end in z8 or 88 1B - * \endcode - */ -CCBORDA * -ccbaReadStream(FILE *fp) -{ -char strbuf[256]; -l_uint8 bval; -l_uint8 *datain, *dataout; -l_int32 i, j, startx, starty; -l_int32 offset, nib1, nib2; -l_int32 ncc, nb; -l_uint32 width, height, w, h, xoff, yoff; -size_t inbytes, outbytes; -BOX *box; -CCBORD *ccb; -CCBORDA *ccba; -NUMA *na; -NUMAA *step; - - PROCNAME("ccbaReadStream"); - -#if !HAVE_LIBZ /* defined in environ.h */ - return (CCBORDA *)ERROR_PTR("no libz: can't read data", procName, NULL); -#else - - if (!fp) - return (CCBORDA *)ERROR_PTR("stream not open", procName, NULL); - - if ((datain = l_binaryReadStream(fp, &inbytes)) == NULL) - return (CCBORDA *)ERROR_PTR("data not read from file", procName, NULL); - dataout = zlibUncompress(datain, inbytes, &outbytes); - LEPT_FREE(datain); - if (!dataout) - return (CCBORDA *)ERROR_PTR("dataout not made", procName, NULL); - - offset = 18; - memcpy(strbuf, dataout, offset); - strbuf[17] = '\0'; - if (memcmp(strbuf, "ccba:", 5) != 0) { - LEPT_FREE(dataout); - return (CCBORDA *)ERROR_PTR("file not type ccba", procName, NULL); - } - sscanf(strbuf, "ccba: %7d cc\n", &ncc); -/* lept_stderr("ncc = %d\n", ncc); */ - if ((ccba = ccbaCreate(NULL, ncc)) == NULL) { - LEPT_FREE(dataout); - return (CCBORDA *)ERROR_PTR("ccba not made", procName, NULL); - } - - memcpy(&width, dataout + offset, 4); - offset += 4; - memcpy(&height, dataout + offset, 4); - offset += 4; - ccba->w = width; - ccba->h = height; -/* lept_stderr("width = %d, height = %d\n", width, height); */ - - for (i = 0; i < ncc; i++) { /* should be ncc */ - ccb = ccbCreate(NULL); - ccbaAddCcb(ccba, ccb); - - memcpy(&xoff, dataout + offset, 4); - offset += 4; - memcpy(&yoff, dataout + offset, 4); - offset += 4; - memcpy(&w, dataout + offset, 4); - offset += 4; - memcpy(&h, dataout + offset, 4); - offset += 4; - box = boxCreate(xoff, yoff, w, h); - boxaAddBox(ccb->boxa, box, L_INSERT); -/* lept_stderr("xoff = %d, yoff = %d, w = %d, h = %d\n", - xoff, yoff, w, h); */ - - memcpy(&nb, dataout + offset, 4); - offset += 4; -/* lept_stderr("num borders = %d\n", nb); */ - step = numaaCreate(nb); - ccb->step = step; - - for (j = 0; j < nb; j++) { /* should be nb */ - memcpy(&startx, dataout + offset, 4); - offset += 4; - memcpy(&starty, dataout + offset, 4); - offset += 4; - ptaAddPt(ccb->start, startx, starty); -/* lept_stderr("startx = %d, starty = %d\n", startx, starty); */ - na = numaCreate(0); - numaaAddNuma(step, na, L_INSERT); - - while(1) { - bval = *(dataout + offset); - offset++; - nib1 = (bval >> 4); - nib2 = bval & 0xf; - if (nib1 != 8) - numaAddNumber(na, nib1); - else - break; - if (nib2 != 8) - numaAddNumber(na, nib2); - else - break; - } - } - } - LEPT_FREE(dataout); - return ccba; - -#endif /* !HAVE_LIBZ */ -} - - -/*---------------------------------------------------------------------* - * SVG Output * - *---------------------------------------------------------------------*/ -/*! - * \brief ccbaWriteSVG() - * - * \param[in] filename - * \param[in] ccba - * \return 0 if OK, 1 on error - */ -l_ok -ccbaWriteSVG(const char *filename, - CCBORDA *ccba) -{ -char *svgstr; - - PROCNAME("ccbaWriteSVG"); - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!ccba) - return ERROR_INT("ccba not defined", procName, 1); - - if ((svgstr = ccbaWriteSVGString(filename, ccba)) == NULL) - return ERROR_INT("svgstr not made", procName, 1); - - l_binaryWrite(filename, "w", svgstr, strlen(svgstr)); - LEPT_FREE(svgstr); - - return 0; -} - - -/*! - * \brief ccbaWriteSVGString() - * - * \param[in] filename - * \param[in] ccba - * \return string in svg-formatted, that can be written to file, - * or NULL on error. - */ -char * -ccbaWriteSVGString(const char *filename, - CCBORDA *ccba) -{ -char *svgstr; -char smallbuf[256]; -char line0[] = ""; -char line1[] = ""; -char line2[] = ""; -char space[] = " "; -l_int32 i, j, ncc, npt, x, y; -CCBORD *ccb; -PTA *pta; -SARRAY *sa; - - PROCNAME("ccbaWriteSVGString"); - - if (!filename) - return (char *)ERROR_PTR("filename not defined", procName, NULL); - if (!ccba) - return (char *)ERROR_PTR("ccba not defined", procName, NULL); - - sa = sarrayCreate(0); - sarrayAddString(sa, line0, L_COPY); - sarrayAddString(sa, line1, L_COPY); - sarrayAddString(sa, line2, L_COPY); - ncc = ccbaGetCount(ccba); - for (i = 0; i < ncc; i++) { - if ((ccb = ccbaGetCcb(ccba, i)) == NULL) { - sarrayDestroy(&sa); - return (char *)ERROR_PTR("ccb not found", procName, NULL); - } - if ((pta = ccb->spglobal) == NULL) { - sarrayDestroy(&sa); - ccbDestroy(&ccb); - return (char *)ERROR_PTR("spglobal not made", procName, NULL); - } - sarrayAddString(sa, line3, L_COPY); - npt = ptaGetCount(pta); - for (j = 0; j < npt; j++) { - ptaGetIPt(pta, j, &x, &y); - snprintf(smallbuf, sizeof(smallbuf), "%0d,%0d", x, y); - sarrayAddString(sa, smallbuf, L_COPY); - } - sarrayAddString(sa, line4, L_COPY); - ccbDestroy(&ccb); - } - sarrayAddString(sa, line5, L_COPY); - sarrayAddString(sa, space, L_COPY); - - svgstr = sarrayToString(sa, 1); -/* lept_stderr("%s", svgstr); */ - - sarrayDestroy(&sa); - return svgstr; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ccbord.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ccbord.h deleted file mode 100644 index cccef6eb..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ccbord.h +++ /dev/null @@ -1,121 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_CCBORD_H -#define LEPTONICA_CCBORD_H - -/*! - * \file ccbord.h - * - *
- * CCBord: represents a single connected component - * CCBorda: an array of CCBord - *- */ - - /*! Use in ccbaStepChainsToPixCoords() */ -/*! CCB Coords */ -enum { - CCB_LOCAL_COORDS = 1, - CCB_GLOBAL_COORDS = 2 -}; - - /*! Use in ccbaGenerateSPGlobalLocs() */ -/*! CCB Points */ -enum { - CCB_SAVE_ALL_PTS = 1, - CCB_SAVE_TURNING_PTS = 2 -}; - - - /*! - *
- * CCBord contains: - * - * (1) a minimally-clipped bitmap of the component (pix), - * (2) a boxa consisting of: - * for the primary component: - * (xul, yul) pixel location in global coords - * (w, h) of the bitmap - * for the hole components: - * (x, y) in relative coordinates in primary component - * (w, h) of the hole border (which is 2 pixels - * larger in each direction than the hole itself) - * (3) a pta ('start') of the initial border pixel location for each - * closed curve, all in relative coordinates of the primary - * component. This is given for the primary component, - * followed by the hole components, if any. - * (4) a refcount of the ccbord; used internally when a ccbord - * is accessed from a ccborda (array of ccbord) - * (5) a ptaa for the chain code for the border in relative - * coordinates, where the first pta is the exterior border - * and all other pta are for interior borders (holes) - * (6) a ptaa for the global pixel loc rendition of the border, - * where the first pta is the exterior border and all other - * pta are for interior borders (holes). - * This is derived from the local or step chain code. - * (7) a numaa for the chain code for the border as orientation - * directions between successive border pixels, where - * the first numa is the exterior border and all other - * numa are for interior borders (holes). This is derived - * from the local chain code. The 8 directions are 0 - 7. - * (8) a pta for a single chain for each c.c., comprised of outer - * and hole borders, plus cut paths between them, all in - * local coords. - * (9) a pta for a single chain for each c.c., comprised of outer - * and hole borders, plus cut paths between them, all in - * global coords. - *- */ -struct CCBord -{ - struct Pix *pix; /*!< component bitmap (min size) */ - struct Boxa *boxa; /*!< regions of each closed curve */ - struct Pta *start; /*!< initial border pixel locations */ - l_int32 refcount; /*!< number of handles; start at 1 */ - struct Ptaa *local; /*!< ptaa of chain pixels (local) */ - struct Ptaa *global; /*!< ptaa of chain pixels (global) */ - struct Numaa *step; /*!< numaa of chain code (step dir) */ - struct Pta *splocal; /*!< pta of single chain (local) */ - struct Pta *spglobal; /*!< pta of single chain (global) */ -}; -typedef struct CCBord CCBORD; - -/*! Array of CCBord */ -struct CCBorda -{ - struct Pix *pix; /*!< input pix (may be null) */ - l_int32 w; /*!< width of pix */ - l_int32 h; /*!< height of pix */ - l_int32 n; /*!< number of ccbord in ptr array */ - l_int32 nalloc; /*!< number of ccbord ptrs allocated */ - struct CCBord **ccb; /*!< ccb ptr array */ -}; -typedef struct CCBorda CCBORDA; - - -#endif /* LEPTONICA_CCBORD_H */ - diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ccthin.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ccthin.c deleted file mode 100644 index 968e8620..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ccthin.c +++ /dev/null @@ -1,476 +0,0 @@ -/*====================================================================* - - 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 ccthin.c - *
- * - * PIXA *pixaThinConnected() - * PIX *pixThinConnected() - * PIX *pixThinConnectedBySet() - * SELA *selaMakeThinSets() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) See notes in pixThinConnected(). - *- */ -PIXA * -pixaThinConnected(PIXA *pixas, - l_int32 type, - l_int32 connectivity, - l_int32 maxiters) -{ -l_int32 i, n, d, same; -PIX *pix1, *pix2; -PIXA *pixad; -SELA *sela; - - PROCNAME("pixaThinConnected"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (type != L_THIN_FG && type != L_THIN_BG) - return (PIXA *)ERROR_PTR("invalid fg/bg type", procName, NULL); - if (connectivity != 4 && connectivity != 8) - return (PIXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); - if (maxiters == 0) maxiters = 10000; - - pixaVerifyDepth(pixas, &same, &d); - if (d != 1) - return (PIXA *)ERROR_PTR("pix are not all 1 bpp", procName, NULL); - - if (connectivity == 4) - sela = selaMakeThinSets(1, 0); - else /* connectivity == 8 */ - sela = selaMakeThinSets(5, 0); - - n = pixaGetCount(pixas); - pixad = pixaCreate(n); - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixas, i, L_CLONE); - pix2 = pixThinConnectedBySet(pix1, type, sela, maxiters); - pixaAddPix(pixad, pix2, L_INSERT); - pixDestroy(&pix1); - } - - selaDestroy(&sela); - return pixad; -} - - -/*! - * \brief pixThinConnected() - * - * \param[in] pixs 1 bpp - * \param[in] type L_THIN_FG, L_THIN_BG - * \param[in] connectivity 4 or 8 - * \param[in] maxiters max number of iters allowed; - * use 0 to iterate until completion - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) See "Connectivity-preserving morphological image transformations," - * Dan S. Bloomberg, in SPIE Visual Communications and Image - * Processing, Conference 1606, pp. 320-334, November 1991, - * Boston, MA. A web version is available at - * http://www.leptonica.com/papers/conn.pdf - * (2) This is a simple interface for two of the best iterative - * morphological thinning algorithms, for 4-c.c and 8-c.c. - * Each iteration uses a mixture of parallel operations - * (using several different 3x3 Sels) and serial operations. - * Specifically, each thinning iteration consists of - * four sequential thinnings from each of four directions. - * Each of these thinnings is a parallel composite - * operation, where the union of a set of HMTs are set - * subtracted from the input. For 4-cc thinning, we - * use 3 HMTs in parallel, and for 8-cc thinning we use 4 HMTs. - * (3) A "good" thinning algorithm is one that generates a skeleton - * that is near the medial axis and has neither pruned - * real branches nor left extra dendritic branches. - * (4) Duality between operations on fg and bg require switching - * the connectivity. To thin the foreground, which is the usual - * situation, use type == L_THIN_FG. Thickening the foreground - * is equivalent to thinning the background (type == L_THIN_BG), - * where the alternate connectivity gets preserved. - * For example, to thicken the fg with 2 rounds of iterations - * using 4-c.c., thin the bg using Sels that preserve 8-connectivity: - * Pix *pix = pixThinConnected(pixs, L_THIN_BG, 8, 2); - * (5) This makes and destroys the sela set each time. It's not a large - * overhead, but if you are calling this thousands of times on - * very small images, you can avoid the overhead; e.g. - * Sela *sela = selaMakeThinSets(1, 0); // for 4-c.c. - * Pix *pix = pixThinConnectedBySet(pixs, L_THIN_FG, sela, 0); - * using set 1 for 4-c.c. and set 5 for 8-c.c operations. - *- */ -PIX * -pixThinConnected(PIX *pixs, - l_int32 type, - l_int32 connectivity, - l_int32 maxiters) -{ -PIX *pixd; -SELA *sela; - - PROCNAME("pixThinConnected"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); - if (type != L_THIN_FG && type != L_THIN_BG) - return (PIX *)ERROR_PTR("invalid fg/bg type", procName, NULL); - if (connectivity != 4 && connectivity != 8) - return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); - if (maxiters == 0) maxiters = 10000; - - if (connectivity == 4) - sela = selaMakeThinSets(1, 0); - else /* connectivity == 8 */ - sela = selaMakeThinSets(5, 0); - - pixd = pixThinConnectedBySet(pixs, type, sela, maxiters); - - selaDestroy(&sela); - return pixd; -} - - -/*! - * \brief pixThinConnectedBySet() - * - * \param[in] pixs 1 bpp - * \param[in] type L_THIN_FG, L_THIN_BG - * \param[in] sela of Sels for parallel composite HMTs - * \param[in] maxiters max number of iters allowed; - * use 0 to iterate until completion - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) See notes in pixThinConnected(). - * (2) This takes a sela representing one of 11 sets of HMT Sels. - * The HMTs from this set are run in parallel and the result - * is OR'd before being subtracted from the source. For each - * iteration, this "parallel" thin is performed four times - * sequentially, for sels rotated by 90 degrees in all four - * directions. - * (3) The "parallel" and "sequential" nomenclature is standard - * in digital filtering. Here, "parallel" operations work on the - * same source (pixd), and accumulate the results in a temp - * image before actually applying them to the source (in this - * case, using an in-place subtraction). "Sequential" operations - * operate directly on the source (pixd) to produce the result - * (in this case, with four sequential thinning operations, one - * from each of four directions). - *- */ -PIX * -pixThinConnectedBySet(PIX *pixs, - l_int32 type, - SELA *sela, - l_int32 maxiters) -{ -l_int32 i, j, r, nsels, same; -PIXA *pixahmt; -PIX **pixhmt; /* array owned by pixahmt; do not destroy! */ -PIX *pix1, *pix2, *pixd; -SEL *sel, *selr; - - PROCNAME("pixThinConnectedBySet"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); - if (type != L_THIN_FG && type != L_THIN_BG) - return (PIX *)ERROR_PTR("invalid fg/bg type", procName, NULL); - if (!sela) - return (PIX *)ERROR_PTR("sela not defined", procName, NULL); - if (maxiters == 0) maxiters = 10000; - - /* Set up array of temp pix to hold hmts */ - nsels = selaGetCount(sela); - pixahmt = pixaCreate(nsels); - for (i = 0; i < nsels; i++) { - pix1 = pixCreateTemplate(pixs); - pixaAddPix(pixahmt, pix1, L_INSERT); - } - pixhmt = pixaGetPixArray(pixahmt); - if (!pixhmt) { - pixaDestroy(&pixahmt); - return (PIX *)ERROR_PTR("pixhmt array not made", procName, NULL); - } - - /* Set up initial image for fg thinning */ - if (type == L_THIN_FG) - pixd = pixCopy(NULL, pixs); - else /* bg thinning */ - pixd = pixInvert(NULL, pixs); - - /* Thin the fg, with up to maxiters iterations */ - for (i = 0; i < maxiters; i++) { - pix1 = pixCopy(NULL, pixd); /* test for completion */ - for (r = 0; r < 4; r++) { /* over 90 degree rotations of Sels */ - for (j = 0; j < nsels; j++) { /* over individual sels in sela */ - sel = selaGetSel(sela, j); /* not a copy */ - selr = selRotateOrth(sel, r); - pixHMT(pixhmt[j], pixd, selr); - selDestroy(&selr); - if (j > 0) - pixOr(pixhmt[0], pixhmt[0], pixhmt[j]); /* accum result */ - } - pixSubtract(pixd, pixd, pixhmt[0]); /* remove result */ - } - pixEqual(pixd, pix1, &same); - pixDestroy(&pix1); - if (same) { -/* L_INFO("%d iterations to completion\n", procName, i); */ - break; - } - } - - /* This is a bit tricky. If we're thickening the foreground, then - * we get a fg border of thickness equal to the number of - * iterations. This border is connected to all components that - * were initially touching the border, but as it grows, it does - * not touch other growing components -- it leaves a 1 pixel wide - * background between it and the growing components, and that - * thin background prevents the components from growing further. - * This border can be entirely removed as follows: - * (1) Subtract the original (unthickened) image pixs from the - * thickened image. This removes the pixels that were originally - * touching the border. - * (2) Get all remaining pixels that are connected to the border. - * (3) Remove those pixels from the thickened image. */ - if (type == L_THIN_BG) { - pixInvert(pixd, pixd); /* finish with duality */ - pix1 = pixSubtract(NULL, pixd, pixs); - pix2 = pixExtractBorderConnComps(pix1, 4); - pixSubtract(pixd, pixd, pix2); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - - pixaDestroy(&pixahmt); - return pixd; -} - - -/*! - * \brief selaMakeThinSets() - * - * \param[in] index into specific sets - * \param[in] debug 1 to output display of sela - * \return sela, or NULL on error - * - *
- * Notes: - * (1) These are specific sets of HMTs to be used in parallel for - * for thinning from each of four directions. - * (2) The sets are indexed as follows: - * For thinning (e.g., run to completion): - * index = 1 sel_4_1, sel_4_2, sel_4_3 - * index = 2 sel_4_1, sel_4_5, sel_4_6 - * index = 3 sel_4_1, sel_4_7, sel_4_7_rot - * index = 4 sel_48_1, sel_48_1_rot, sel_48_2 - * index = 5 sel_8_2, sel_8_3, sel_8_5, sel_8_6 - * index = 6 sel_8_2, sel_8_3, sel_48_2 - * index = 7 sel_8_1, sel_8_5, sel_8_6 - * index = 8 sel_8_2, sel_8_3, sel_8_8, sel_8_9 - * index = 9 sel_8_5, sel_8_6, sel_8_7, sel_8_7_rot - * For thickening (e.g., just a few iterations): - * index = 10 sel_4_2, sel_4_3 - * index = 11 sel_8_4 - * (3) For a very smooth skeleton, use set 1 for 4 connected and - * set 5 for 8 connected thins. - *- */ -SELA * -selaMakeThinSets(l_int32 index, - l_int32 debug) -{ -SEL *sel; -SELA *sela1, *sela2, *sela3; - - PROCNAME("selaMakeThinSets"); - - if (index < 1 || index > 11) - return (SELA *)ERROR_PTR("invalid index", procName, NULL); - - sela2 = selaCreate(4); - switch(index) - { - case 1: - sela1 = sela4ccThin(NULL); - selaFindSelByName(sela1, "sel_4_1", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela1, "sel_4_2", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela1, "sel_4_3", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - break; - case 2: - sela1 = sela4ccThin(NULL); - selaFindSelByName(sela1, "sel_4_1", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela1, "sel_4_5", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela1, "sel_4_6", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - break; - case 3: - sela1 = sela4ccThin(NULL); - selaFindSelByName(sela1, "sel_4_1", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela1, "sel_4_7", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - sel = selRotateOrth(sel, 1); - selaAddSel(sela2, sel, "sel_4_7_rot", L_INSERT); - break; - case 4: - sela1 = sela4and8ccThin(NULL); - selaFindSelByName(sela1, "sel_48_1", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - sel = selRotateOrth(sel, 1); - selaAddSel(sela2, sel, "sel_48_1_rot", L_INSERT); - selaFindSelByName(sela1, "sel_48_2", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - break; - case 5: - sela1 = sela8ccThin(NULL); - selaFindSelByName(sela1, "sel_8_2", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela1, "sel_8_3", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela1, "sel_8_5", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela1, "sel_8_6", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - break; - case 6: - sela1 = sela8ccThin(NULL); - sela3 = sela4and8ccThin(NULL); - selaFindSelByName(sela1, "sel_8_2", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela1, "sel_8_3", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela3, "sel_48_2", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaDestroy(&sela3); - break; - case 7: - sela1 = sela8ccThin(NULL); - selaFindSelByName(sela1, "sel_8_1", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela1, "sel_8_5", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela1, "sel_8_6", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - break; - case 8: - sela1 = sela8ccThin(NULL); - selaFindSelByName(sela1, "sel_8_2", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela1, "sel_8_3", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela1, "sel_8_8", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela1, "sel_8_9", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - break; - case 9: - sela1 = sela8ccThin(NULL); - selaFindSelByName(sela1, "sel_8_5", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela1, "sel_8_6", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela1, "sel_8_7", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - sel = selRotateOrth(sel, 1); - selaAddSel(sela2, sel, "sel_8_7_rot", L_INSERT); - break; - case 10: /* thicken for this one; use just a few iterations */ - sela1 = sela4ccThin(NULL); - selaFindSelByName(sela1, "sel_4_2", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - selaFindSelByName(sela1, "sel_4_3", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - break; - case 11: /* thicken for this one; use just a few iterations */ - sela1 = sela8ccThin(NULL); - selaFindSelByName(sela1, "sel_8_4", NULL, &sel); - selaAddSel(sela2, sel, NULL, L_COPY); - break; - } - - /* Optionally display the sel set */ - if (debug) { - PIX *pix1; - char buf[32]; - lept_mkdir("/lept/sels"); - pix1 = selaDisplayInPix(sela2, 35, 3, 15, 4); - snprintf(buf, sizeof(buf), "/tmp/lept/sels/set%d.png", index); - pixWrite(buf, pix1, IFF_PNG); - pixDisplay(pix1, 100, 100); - pixDestroy(&pix1); - } - - selaDestroy(&sela1); - return sela2; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/checkerboard.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/checkerboard.c deleted file mode 100644 index fac314bb..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/checkerboard.c +++ /dev/null @@ -1,313 +0,0 @@ -/*====================================================================* - - 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 checkerboard.c - *
- * - * Find the checker corners where 4 squares come together - * PIX *pixFindCheckerboardCorners() - * - * Generate the hit-miss sels - * SELA *makeCheckerboardCornerSela() - * static PIXA *makeCheckerboardCornerPixa() - * - * The functions in this file locate the corners where four squares - * in a checkerboard come together. With a perfectly aligned checkerboard, - * the solution is trivial: take the union of two hit-miss transforms (HMTs), - * each having a simple diagonal structuring element (sel). The two - * sels can be generated from strings such as these, using - * selCreateFromString(): - * - * static const char *str1 = "o x" - * " " - * " " - * " C " - * " " - * " " - * "x o"; - * static const char *str2 = "x o" - * " " - * " " - * " C " - * " " - * " " - * "o x"; - * - * A more interesting problem is to consider the checkerboard viewed from - * some arbitrary angle and orientation from the normal. The method - * developed here works for a camera located within a cone with an opening - * half-angle of about 45 degrees, and with its axis along the normal - * to the checkerboard. - * - * See prog/checkerboard_reg.c for usage. - * - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) Use %nsels = 4 if the checkerboard may be rotated by more - * than 20 deg. - * (2) The values of %size and %dilation that can be used depend on - * the square sizes. Nominal values here are for squares of - * size 30 to 50. In general, because of the viewing angle - * of the camera, the "squares" will appear approximately - * as a rotated rectangle. - * (3) The outputs pix_corners and pta_corners are optional. - *- */ -l_ok -pixFindCheckerboardCorners(PIX *pixs, - l_int32 size, - l_int32 dilation, - l_int32 nsels, - PIX **ppix_corners, - PTA **ppta_corners, - PIXA *pixadb) -{ -BOXA *boxa1; -PIX *pix1, *pix2, *pix3; -PTA *pta1; -SEL *sel; -SELA *sela; - - PROCNAME("pixFindCheckerboardCorners"); - - if (ppix_corners) *ppix_corners = NULL; - if (ppta_corners) *ppta_corners = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (size <= 0) size = 7; - if (size < 7) - return ERROR_INT("size too small", procName, 1); - if (dilation < 1 || dilation > 5) - return ERROR_INT("dilation not in [1 ...5]", procName, 1); - if (nsels != 2 && nsels != 4) - return ERROR_INT("nsels not 2 or 4", procName, 1); - - /* Generate the hit-miss sels for finding corners */ - sela = makeCheckerboardCornerSela(size, dilation, nsels, pixadb); - if (!sela) - return ERROR_INT("sela not made", procName, 1); - if (pixadb) { - pix1 = selaDisplayInPix(sela, 15, 3, 15, 2); - pixaAddPix(pixadb, pix1, L_INSERT); - } - - /* Do the hit-miss transform to find corner locations */ - pix1 = pixUnionOfMorphOps(pixs, sela, L_MORPH_HMT); - if (pixadb) pixaAddPix(pixadb, pix1, L_CLONE); - selaDestroy(&sela); - - /* Remove large noise c.c. */ - pix2 = pixSelectBySize(pix1, size, size, 8, L_SELECT_IF_BOTH, - L_SELECT_IF_LTE, NULL); - if (pixadb) pixaAddPix(pixadb, pix2, L_CLONE); - - /* Thin remaining c.c. */ - pix3 = pixThinConnected(pix2, L_THIN_FG, 8, 0); - if (pixadb) pixaAddPix(pixadb, pix3, L_CLONE); - - /* Extract the location of the center of each component */ - boxa1 = pixConnCompBB(pix3, 8); - pta1 = boxaExtractCorners(boxa1, L_BOX_CENTER); - boxaDestroy(&boxa1); - pixDestroy(&pix1); - pixDestroy(&pix2); - if (pixadb) { /* show the result as colored plus signs on the input */ - sel = selMakePlusSign(15, 2); - pix1 = pixDisplaySelectedPixels(pixs, pix3, sel, 0xff000000); - pixaAddPix(pixadb, pix1, L_INSERT); - selDestroy(&sel); - } - - if (ppix_corners) - *ppix_corners = pix3; - else - pixDestroy(&pix3); - if (ppta_corners) - *ppta_corners = pta1; - else - ptaDestroy(&pta1); - return 0; -} - - -/*! - * \brief makeCheckerboardCornerSela() - * - * \param[in] size size of HMT sel; >= 7, typ. 15; 0 for default - * \param[in] dilation size of hit and miss squares; typ. 1 or 3; max 5 - * \param[in] nsels number to use (either 2 or 4) - * \param[in] pixadb [optional] pass in pre-allocated - * \return sela hit-miss sels for finding corners, or NULL on error - * - *
- * Notes: - * (1) Use 4 sels if the checkerboard may be rotated by more than 20 deg. - *- */ -SELA * -makeCheckerboardCornerSela(l_int32 size, - l_int32 dilation, - l_int32 nsels, - PIXA *pixadb) -{ -PIX *pix1; -PIXA *pixa1; -SARRAY *sa; -SELA *sela; - - PROCNAME("makeCheckerboardCornerSela"); - - if (size <= 0) size = 7; - if (size < 7) - return (SELA *)ERROR_PTR("size too small", procName, NULL); - if (dilation < 1 || dilation > 5) - return (SELA *)ERROR_PTR("dilation not in [1 ...5]", procName, NULL); - if (nsels != 2 && nsels != 4) - return (SELA *)ERROR_PTR("nsels not 2 or 4", procName, NULL); - - if ((pixa1 = makeCheckerboardCornerPixa(size, dilation, nsels)) == NULL) - return (SELA *)ERROR_PTR("pixa for sels not made", procName, NULL); - if (pixadb) { - pix1 = pixaDisplayTiledInColumns(pixa1, 4, 8.0, 15, 2); - pixaAddPix(pixadb, pix1, L_INSERT); - } - sa = sarrayCreateWordsFromString(selnames); - sela = selaCreateFromColorPixa(pixa1, sa); - pixaDestroy(&pixa1); - sarrayDestroy(&sa); - if (!sela) - return (SELA *)ERROR_PTR("sela not made", procName, NULL); - return sela; -} - - -/*! - * \brief makeCheckerboardCornerPixa() - * - * \param[in] size size of HMT sel; >= 7, typ. 15; 0 for default - * \param[in] dilation size of hit and miss squares; typ. 1 or 3; max 5 - * \param[in] nsels number to use (either 2 or 4) - * \return pixa representing hit-miss sels for finding corners, or NULL on error - * - *
- * Notes: - * (1) Each pix can be used to generate a hit-miss sel, using the - * function selCreateFromColorPix(). See that function for the - * use of color and gray pixels to encode the hits, misses and - * center in the structuring element. - *- */ -static PIXA * -makeCheckerboardCornerPixa(l_int32 size, - l_int32 dilation, - l_int32 nsels) -{ -PIX *pix1, *pix2, *pix3; -PIXA *pixa1; - - pixa1 = pixaCreate(4); - - /* Represent diagonal neg slope hits and pos slope misses */ - pix1 = pixCreate(size, size, 32); - pixSetAll(pix1); - pix2 = pixCreate(size, size, 1); /* slope -1 line (2 pixel) mask */ - pixSetPixel(pix2, 1, 1, 1); /* UL corner */ - pixSetPixel(pix2, size - 2, size - 2, 1); /* LR corner */ - if (dilation > 1) - pixDilateBrick(pix2, pix2, dilation, dilation); /* dilate each pixel */ - pixSetMasked(pix1, pix2, 0x00ff0000); /* green hit */ - pix3 = pixRotate90(pix2, 1); /* slope +1 line (2 pixel) mask */ - pixSetMasked(pix1, pix3, 0xff000000); /* red miss */ - pixSetRGBPixel(pix1, size / 2, size / 2, 128, 128, 128); /* gray center */ - pixaAddPix(pixa1, pix1, L_INSERT); - - /* Represent diagonal pos slope hits and neg slope misses */ - pix1 = pixCreate(size, size, 32); - pixSetAll(pix1); - pixSetMasked(pix1, pix2, 0xff000000); /* red hit */ - pixSetMasked(pix1, pix3, 0x00ff0000); /* green miss */ - pixSetRGBPixel(pix1, size / 2, size / 2, 128, 128, 128); /* gray center */ - pixaAddPix(pixa1, pix1, L_INSERT); - pixDestroy(&pix2); - pixDestroy(&pix3); - - if (nsels == 2) - return pixa1; - - /* Represent cross: vertical hits and horizontal misses */ - pix1 = pixCreate(size, size, 32); - pixSetAll(pix1); - pix2 = pixCreate(size, size, 1); /* vertical line (2 pixel) mask */ - pixSetPixel(pix2, size / 2, 1, 1); - pixSetPixel(pix2, size / 2, size - 2, 1); - if (dilation > 1) - pixDilateBrick(pix2, pix2, dilation, dilation); /* dilate each pixel */ - pixSetMasked(pix1, pix2, 0x00ff0000); /* green hit */ - pix3 = pixRotate90(pix2, 1); /* horizontal line (2 pixel) mask */ - pixSetMasked(pix1, pix3, 0xff000000); /* red miss */ - pixSetRGBPixel(pix1, size / 2, size / 2, 128, 128, 128); /* gray center */ - pixaAddPix(pixa1, pix1, L_INSERT); - - /* Represent cross: horizontal hits and vertical misses */ - pix1 = pixCreate(size, size, 32); - pixSetAll(pix1); - pixSetMasked(pix1, pix3, 0x00ff0000); /* green hit */ - pixSetMasked(pix1, pix2, 0xff000000); /* red miss */ - pixSetRGBPixel(pix1, size / 2, size / 2, 128, 128, 128); /* gray center */ - pixaAddPix(pixa1, pix1, L_INSERT); - pixDestroy(&pix2); - pixDestroy(&pix3); - - return pixa1; -} - diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/classapp.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/classapp.c deleted file mode 100644 index c383c547..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/classapp.c +++ /dev/null @@ -1,1050 +0,0 @@ -/*====================================================================* - - 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 classapp.c - *
- * - * Top-level jb2 correlation and rank-hausdorff - * l_int32 jbCorrelation() - * l_int32 jbRankHaus() - * - * Extract and classify words in textline order - * JBCLASSER *jbWordsInTextlines() - * l_int32 pixGetWordsInTextlines() - * l_int32 pixGetWordBoxesInTextlines() - * - * Extract word and character bounding boxes - * l_int32 pixFindWordAndCharacterBoxes() - * - * Use word bounding boxes to compare page images - * NUMAA *boxaExtractSortedPattern() - * l_int32 numaaCompareImagesByBoxes() - * static l_int32 testLineAlignmentX() - * static l_int32 countAlignedMatches() - * static void printRowIndices() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * 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_ok -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) - lept_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.%04d", rootname, i); - lept_stderr("filename: %s\n", filename); - pixWrite(filename, pix, IFF_PNG); - pixDestroy(&pix); - } - pixaDestroy(&pixa); - } - - sarrayDestroy(&safiles); - jbClasserDestroy(&classer); - jbDataDestroy(&data); - return 0; -} - - -/*! - * \brief jbRankHaus() - * - * \param[in] dirin directory of input images - * \param[in] size of Sel used for dilation; typ. 2 - * \param[in] rank rank value of match; typ. 0.97 - * \param[in] components JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS - * \param[in] rootname for output files - * \param[in] firstpage 0-based - * \param[in] npages use 0 for all pages in dirin - * \param[in] renderflag 1 to render from templates; 0 to skip - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) See prog/jbrankhaus for generating more output (e.g., - * for debugging) - *- */ -l_ok -jbRankHaus(const char *dirin, - l_int32 size, - l_float32 rank, - 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("jbRankHaus"); - - 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 = jbRankHausInit(components, 0, 0, size, rank); - 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) - lept_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.%04d", rootname, i); - lept_stderr("filename: %s\n", filename); - pixWrite(filename, pix, IFF_PNG); - pixDestroy(&pix); - } - pixaDestroy(&pixa); - } - - sarrayDestroy(&safiles); - jbClasserDestroy(&classer); - jbDataDestroy(&data); - return 0; -} - - - -/*------------------------------------------------------------------* - * Extract and classify words in textline order * - *------------------------------------------------------------------*/ -/*! - * \brief jbWordsInTextlines() - * - * \param[in] dirin directory of input pages - * \param[in] reduction 1 for full res; 2 for half-res - * \param[in] maxwidth of word mask components, to be kept - * \param[in] maxheight of word mask components, to be kept - * \param[in] thresh on correlation; 0.80 is reasonable - * \param[in] weight for handling thick text; 0.6 is reasonable - * \param[out] pnatl numa with textline index for each component - * \param[in] firstpage 0-based - * \param[in] npages use 0 for all pages in dirin - * \return classer for the set of pages - * - *
- * Notes: - * (1) This is a high-level function. See prog/jbwords for example - * of usage. - * (2) Typically, use input of 75 - 150 ppi for finding words. - *- */ -JBCLASSER * -jbWordsInTextlines(const char *dirin, - l_int32 reduction, - l_int32 maxwidth, - l_int32 maxheight, - l_float32 thresh, - l_float32 weight, - NUMA **pnatl, - l_int32 firstpage, - l_int32 npages) -{ -char *fname; -l_int32 nfiles, i, w, h; -BOXA *boxa; -JBCLASSER *classer; -NUMA *nai, *natl; -PIX *pix1, *pix2; -PIXA *pixa; -SARRAY *safiles; - - PROCNAME("jbWordsInTextlines"); - - if (!pnatl) - return (JBCLASSER *)ERROR_PTR("&natl not defined", procName, NULL); - *pnatl = NULL; - if (!dirin) - return (JBCLASSER *)ERROR_PTR("dirin not defined", procName, NULL); - if (reduction != 1 && reduction != 2) - return (JBCLASSER *)ERROR_PTR("reduction not in {1,2}", procName, NULL); - - safiles = getSortedPathnamesInDirectory(dirin, NULL, firstpage, npages); - nfiles = sarrayGetCount(safiles); - - /* Classify components */ - classer = jbCorrelationInit(JB_WORDS, maxwidth, maxheight, thresh, weight); - classer->safiles = sarrayCopy(safiles); - natl = numaCreate(0); - *pnatl = natl; - for (i = 0; i < nfiles; i++) { - fname = sarrayGetString(safiles, i, L_NOCOPY); - if ((pix1 = pixRead(fname)) == NULL) { - L_WARNING("image file %d not read\n", procName, i); - continue; - } - if (reduction == 1) - pix2 = pixClone(pix1); - else /* reduction == 2 */ - pix2 = pixReduceRankBinaryCascade(pix1, 1, 0, 0, 0); - pixGetWordsInTextlines(pix2, JB_WORDS_MIN_WIDTH, - JB_WORDS_MIN_HEIGHT, maxwidth, maxheight, - &boxa, &pixa, &nai); - pixGetDimensions(pix2, &w, &h, NULL); - classer->w = w; - classer->h = h; - jbAddPageComponents(classer, pix2, boxa, pixa); - numaJoin(natl, nai, 0, -1); - pixDestroy(&pix1); - pixDestroy(&pix2); - numaDestroy(&nai); - boxaDestroy(&boxa); - pixaDestroy(&pixa); - } - - sarrayDestroy(&safiles); - return classer; -} - - -/*! - * \brief pixGetWordsInTextlines() - * - * \param[in] pixs 1 bpp, typ. 75 - 150 ppi - * \param[in] minwidth of saved components; smaller are discarded - * \param[in] minheight of saved components; smaller are discarded - * \param[in] maxwidth of saved components; larger are discarded - * \param[in] maxheight of saved components; larger are discarded - * \param[out] pboxad word boxes sorted in textline line order - * \param[out] ppixad word images sorted in textline line order - * \param[out] pnai index of textline for each word - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The input should be at a resolution of between 75 and 150 ppi. - * (2) The four size constraints on saved components are all - * scaled by %reduction. - * (3) The result are word images (and their b.b.), extracted in - * textline order, at either full res or 2x reduction, - * and with a numa giving the textline index for each word. - * (4) The pixa and boxa interfaces should make this type of - * application simple to put together. The steps are: - * ~ generate first estimate of word masks - * ~ get b.b. of these, and remove the small and big ones - * ~ extract pixa of the word images, using the b.b. - * ~ sort actual word images in textline order (2d) - * ~ flatten them to a pixa (1d), saving the textline index - * for each pix - * (5) In an actual application, it may be desirable to pre-filter - * the input image to remove large components, to extract - * single columns of text, and to deskew them. For example, - * to remove both large components and small noisy components - * that can interfere with the statistics used to estimate - * parameters for segmenting by words, but still retain text lines, - * the following image preprocessing can be done: - * Pix *pixt = pixMorphSequence(pixs, "c40.1", 0); - * Pix *pixf = pixSelectBySize(pixt, 0, 60, 8, - * L_SELECT_HEIGHT, L_SELECT_IF_LT, NULL); - * pixAnd(pixf, pixf, pixs); // the filtered image - * The closing turns text lines into long blobs, but does not - * significantly increase their height. But if there are many - * small connected components in a dense texture, this is likely - * to generate tall components that will be eliminated in pixf. - *- */ -l_ok -pixGetWordsInTextlines(PIX *pixs, - l_int32 minwidth, - l_int32 minheight, - l_int32 maxwidth, - l_int32 maxheight, - BOXA **pboxad, - PIXA **ppixad, - NUMA **pnai) -{ -BOXA *boxa1, *boxad; -BOXAA *baa; -NUMA *nai; -NUMAA *naa; -PIXA *pixa1, *pixad; -PIXAA *paa; - - PROCNAME("pixGetWordsInTextlines"); - - if (!pboxad || !ppixad || !pnai) - return ERROR_INT("&boxad, &pixad, &nai not all defined", procName, 1); - *pboxad = NULL; - *ppixad = NULL; - *pnai = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - - /* Get the bounding boxes of the words from the word mask. */ - pixWordBoxesByDilation(pixs, minwidth, minheight, maxwidth, maxheight, - &boxa1, NULL, NULL); - - /* Generate a pixa of the word images */ - pixa1 = pixaCreateFromBoxa(pixs, boxa1, 0, 0, NULL); - - /* Sort the bounding boxes of these words by line. We use the - * index mapping to allow identical sorting of the pixa. */ - baa = boxaSort2d(boxa1, &naa, -1, -1, 4); - paa = pixaSort2dByIndex(pixa1, naa, L_CLONE); - - /* Flatten the word paa */ - pixad = pixaaFlattenToPixa(paa, &nai, L_CLONE); - boxad = pixaGetBoxa(pixad, L_COPY); - - *pnai = nai; - *pboxad = boxad; - *ppixad = pixad; - - pixaDestroy(&pixa1); - boxaDestroy(&boxa1); - boxaaDestroy(&baa); - pixaaDestroy(&paa); - numaaDestroy(&naa); - return 0; -} - - -/*! - * \brief pixGetWordBoxesInTextlines() - * - * \param[in] pixs 1 bpp, typ. 75 - 150 ppi - * \param[in] minwidth of saved components; smaller are discarded - * \param[in] minheight of saved components; smaller are discarded - * \param[in] maxwidth of saved components; larger are discarded - * \param[in] maxheight of saved components; larger are discarded - * \param[out] pboxad word boxes sorted in textline line order - * \param[out] pnai [optional] index of textline for each word - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The input should be at a resolution of between 75 and 150 ppi. - * (2) This is a special version of pixGetWordsInTextlines(), that - * just finds the word boxes in line order, with a numa - * giving the textline index for each word. - * See pixGetWordsInTextlines() for more details. - *- */ -l_ok -pixGetWordBoxesInTextlines(PIX *pixs, - l_int32 minwidth, - l_int32 minheight, - l_int32 maxwidth, - l_int32 maxheight, - BOXA **pboxad, - NUMA **pnai) -{ -BOXA *boxa1; -BOXAA *baa; -NUMA *nai; - - PROCNAME("pixGetWordBoxesInTextlines"); - - if (pnai) *pnai = NULL; - if (!pboxad) - return ERROR_INT("&boxad and &nai not both defined", procName, 1); - *pboxad = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - - /* Get the bounding boxes of the words from the word mask. */ - pixWordBoxesByDilation(pixs, minwidth, minheight, maxwidth, maxheight, - &boxa1, NULL, NULL); - - /* 2D sort the bounding boxes of these words. */ - baa = boxaSort2d(boxa1, NULL, 3, -5, 5); - - /* Flatten the boxaa, saving the boxa index for each box */ - *pboxad = boxaaFlattenToBoxa(baa, &nai, L_CLONE); - - if (pnai) - *pnai = nai; - else - numaDestroy(&nai); - boxaDestroy(&boxa1); - boxaaDestroy(&baa); - return 0; -} - - -/*------------------------------------------------------------------* - * Extract word and character bounding boxes * - *------------------------------------------------------------------*/ -/*! - * \brief pixFindWordAndCharacterBoxes() - * - * \param[in] pixs 2, 4, 8 or 32 bpp; colormap OK; typ. 300 ppi - * \param[in] boxs [optional] region to select in pixs - * \param[in] thresh binarization threshold (typ. 100 - 150) - * \param[out] pboxaw return the word boxes - * \param[out] pboxaac return the character boxes - * \param[in] debugdir [optional] for debug images; use NULL to skip - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) If %boxs == NULL, the entire input image is used. - * (2) Having an input pix that is not 1bpp is necessary to reduce - * touching characters by using a low binarization threshold. - * Suggested thresholds are between 100 and 150. - * (3) The coordinates in the output boxes are global, with respect - * to the input image. - *- */ -l_ok -pixFindWordAndCharacterBoxes(PIX *pixs, - BOX *boxs, - l_int32 thresh, - BOXA **pboxaw, - BOXAA **pboxaac, - const char *debugdir) -{ -char *debugfile, *subdir; -l_int32 i, xs, ys, xb, yb, nb, loc; -l_float32 scalefact; -BOX *box1, *box2; -BOXA *boxa1, *boxa1a, *boxa2, *boxa3, *boxa4, *boxa5, *boxaw; -BOXAA *boxaac; -PIX *pix1, *pix2, *pix3, *pix3a, *pix4, *pix5; - - PROCNAME("pixFindWordAndCharacterBoxes"); - - if (pboxaw) *pboxaw = NULL; - if (pboxaac) *pboxaac = NULL; - if (!pboxaw || !pboxaac) - return ERROR_INT("&boxaw and &boxaac not defined", procName, 1); - if (!pixs || pixGetDepth(pixs) == 1) - return ERROR_INT("pixs not defined or 1 bpp", procName, 1); - if (thresh > 150) - L_WARNING("threshold is %d; may be too high\n", procName, thresh); - - if (boxs) { - if ((pix1 = pixClipRectangle(pixs, boxs, NULL)) == NULL) - return ERROR_INT("pix1 not made", procName, 1); - boxGetGeometry(boxs, &xs, &ys, NULL, NULL); - } else { - pix1 = pixClone(pixs); - xs = ys = 0; - } - - /* Convert pix1 to 8 bpp gray if necessary */ - pix2 = pixConvertTo8(pix1, FALSE); - - /* To find the words and letters, work with 1 bpp images and use - * a low threshold to reduce the number of touching characters. */ - pix3 = pixConvertTo1(pix2, thresh); - - /* Work at about 120 ppi to find the word bounding boxes. */ - pix3a = pixScaleToResolution(pix3, 120.0, 300.0, &scalefact); - - /* First find the words, removing the very small things like - * dots over the 'i' that weren't included in word boxes. */ - pixGetWordBoxesInTextlines(pix3a, 1, 4, 150, 40, &boxa1a, NULL); - boxa1 = boxaTransform(boxa1a, 0, 0, 1.0 / scalefact, 1.0 / scalefact); - if (debugdir) { - loc = 0; - subdir = stringReplaceSubstr(debugdir, "/tmp/", "", &loc, NULL); - lept_mkdir(subdir); - LEPT_FREE(subdir); - pix4 = pixConvertTo32(pix2); - pixRenderBoxaArb(pix4, boxa1, 2, 255, 0, 0); - debugfile = stringJoin(debugdir, "/words.png"); - pixWrite(debugfile, pix4, IFF_PNG); - pixDestroy(&pix4); - LEPT_FREE(debugfile); - } - - /* Now find the letters at 300 ppi */ - nb = boxaGetCount(boxa1); - boxaw = boxaCreate(nb); - boxaac = boxaaCreate(nb); - *pboxaw = boxaw; - *pboxaac = boxaac; - for (i = 0; i < nb; i++) { - box1 = boxaGetBox(boxa1, i, L_COPY); - boxGetGeometry(box1, &xb, &yb, NULL, NULL); - pix4 = pixClipRectangle(pix3, box1, NULL); - /* Join detached parts of characters vertically */ - pix5 = pixMorphSequence(pix4, "c1.10", 0); - /* The connected components should mostly be characters */ - boxa2 = pixConnCompBB(pix5, 4); - /* Remove very small pieces */ - boxa3 = boxaSelectBySize(boxa2, 2, 5, L_SELECT_IF_BOTH, - L_SELECT_IF_GTE, NULL); - /* Order left to right */ - boxa4 = boxaSort(boxa3, L_SORT_BY_X, L_SORT_INCREASING, NULL); - /* Express locations with reference to the full input image */ - boxa5 = boxaTransform(boxa4, xs + xb, ys + yb, 1.0, 1.0); - box2 = boxTransform(box1, xs, ys, 1.0, 1.0); - - /* Ignore any boxa with no boxes after size filtering */ - if (boxaGetCount(boxa5) > 0) { - boxaAddBox(boxaw, box2, L_INSERT); - boxaaAddBoxa(boxaac, boxa5, L_INSERT); - } else { - boxDestroy(&box2); - boxaDestroy(&boxa5); - } - boxDestroy(&box1); - pixDestroy(&pix4); - pixDestroy(&pix5); - boxaDestroy(&boxa2); - boxaDestroy(&boxa3); - boxaDestroy(&boxa4); - } - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - pixDestroy(&pix3a); - boxaDestroy(&boxa1); - boxaDestroy(&boxa1a); - if (debugdir) { - pix4 = pixConvertTo32(pixs); - boxa2 = boxaaFlattenToBoxa(boxaac, NULL, L_COPY); - pixRenderBoxaArb(pix4, boxa2, 2, 255, 0, 0); - boxa3 = boxaAdjustSides(boxaw, -2, 2, -2, 2); - pixRenderBoxaArb(pix4, boxa3, 2, 0, 255, 0); - debugfile = stringJoin(debugdir, "/chars.png"); - pixWrite(debugfile, pix4, IFF_PNG); - pixDestroy(&pix4); - boxaDestroy(&boxa2); - boxaDestroy(&boxa3); - LEPT_FREE(debugfile); - } - return 0; -} - - -/*------------------------------------------------------------------* - * Use word bounding boxes to compare page images * - *------------------------------------------------------------------*/ -/*! - * \brief boxaExtractSortedPattern() - * - * \param[in] boxa typ. of word bounding boxes, in textline order - * \param[in] na index of textline for each box in boxa - * \return naa NUMAA, where each numa represents one textline, - * or NULL on error - * - *
- * Notes: - * (1) The input is expected to come from pixGetWordBoxesInTextlines(). - * (2) Each numa in the output consists of an average y coordinate - * of the first box in the textline, followed by pairs of - * x coordinates representing the left and right edges of each - * of the boxes in the textline. - *- */ -NUMAA * -boxaExtractSortedPattern(BOXA *boxa, - NUMA *na) -{ -l_int32 index, nbox, row, prevrow, x, y, w, h; -BOX *box; -NUMA *nad; -NUMAA *naa; - - PROCNAME("boxaExtractSortedPattern"); - - if (!boxa) - return (NUMAA *)ERROR_PTR("boxa not defined", procName, NULL); - if (!na) - return (NUMAA *)ERROR_PTR("na not defined", procName, NULL); - - naa = numaaCreate(0); - nbox = boxaGetCount(boxa); - if (nbox == 0) - return naa; - - prevrow = -1; - for (index = 0; index < nbox; index++) { - box = boxaGetBox(boxa, index, L_CLONE); - numaGetIValue(na, index, &row); - if (row > prevrow) { - if (index > 0) - numaaAddNuma(naa, nad, L_INSERT); - nad = numaCreate(0); - prevrow = row; - boxGetGeometry(box, NULL, &y, NULL, &h); - numaAddNumber(nad, y + h / 2); - } - boxGetGeometry(box, &x, NULL, &w, NULL); - numaAddNumber(nad, x); - numaAddNumber(nad, x + w - 1); - boxDestroy(&box); - } - numaaAddNuma(naa, nad, L_INSERT); - - return naa; -} - - -/*! - * \brief numaaCompareImagesByBoxes() - * - * \param[in] naa1 for image 1, formatted by boxaExtractSortedPattern() - * \param[in] naa2 for image 2, formatted by boxaExtractSortedPattern() - * \param[in] nperline number of box regions to be used in each textline - * \param[in] nreq number of complete row matches required - * \param[in] maxshiftx max allowed x shift between two patterns, in pixels - * \param[in] maxshifty max allowed y shift between two patterns, in pixels - * \param[in] delx max allowed difference in x data, after alignment - * \param[in] dely max allowed difference in y data, after alignment - * \param[out] psame 1 if %nreq row matches are found; 0 otherwise - * \param[in] debugflag 1 for debug output - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Each input numaa describes a set of sorted bounding boxes - * (sorted by textline and, within each textline, from - * left to right) in the images from which they are derived. - * See boxaExtractSortedPattern() for a description of the data - * format in each of the input numaa. - * (2) This function does an alignment between the input - * descriptions of bounding boxes for two images. The - * input parameter %nperline specifies the number of boxes - * to consider in each line when testing for a match, and - * %nreq is the required number of lines that must be well-aligned - * to get a match. - * (3) Testing by alignment has 3 steps: - * (a) Generating the location of word bounding boxes from the - * images (prior to calling this function). - * (b) Listing all possible pairs of aligned rows, based on - * tolerances in horizontal and vertical positions of - * the boxes. Specifically, all pairs of rows are enumerated - * whose first %nperline boxes can be brought into close - * alignment, based on the delx parameter for boxes in the - * line and within the overall the %maxshiftx and %maxshifty - * constraints. - * (c) Each pair, starting with the first, is used to search - * for a set of %nreq - 1 other pairs that can all be aligned - * with a difference in global translation of not more - * than (%delx, %dely). - *- */ -l_ok -numaaCompareImagesByBoxes(NUMAA *naa1, - NUMAA *naa2, - l_int32 nperline, - l_int32 nreq, - l_int32 maxshiftx, - l_int32 maxshifty, - l_int32 delx, - l_int32 dely, - l_int32 *psame, - l_int32 debugflag) -{ -l_int32 n1, n2, i, j, nbox, y1, y2, xl1, xl2; -l_int32 shiftx, shifty, match; -l_int32 *line1, *line2; /* indicator for sufficient boxes in a line */ -l_int32 *yloc1, *yloc2; /* arrays of y value for first box in a line */ -l_int32 *xleft1, *xleft2; /* arrays of x value for left side of first box */ -NUMA *na1, *na2, *nai1, *nai2, *nasx, *nasy; - - PROCNAME("numaaCompareImagesByBoxes"); - - if (!psame) - return ERROR_INT("&same not defined", procName, 1); - *psame = 0; - if (!naa1) - return ERROR_INT("naa1 not defined", procName, 1); - if (!naa2) - return ERROR_INT("naa2 not defined", procName, 1); - if (nperline < 1) - return ERROR_INT("nperline < 1", procName, 1); - if (nreq < 1) - return ERROR_INT("nreq < 1", procName, 1); - - n1 = numaaGetCount(naa1); - n2 = numaaGetCount(naa2); - if (n1 < nreq || n2 < nreq) - return 0; - - /* Find the lines in naa1 and naa2 with sufficient boxes. - * Also, find the y-values for each of the lines, and the - * LH x-values of the first box in each line. */ - line1 = (l_int32 *)LEPT_CALLOC(n1, sizeof(l_int32)); - line2 = (l_int32 *)LEPT_CALLOC(n2, sizeof(l_int32)); - yloc1 = (l_int32 *)LEPT_CALLOC(n1, sizeof(l_int32)); - yloc2 = (l_int32 *)LEPT_CALLOC(n2, sizeof(l_int32)); - xleft1 = (l_int32 *)LEPT_CALLOC(n1, sizeof(l_int32)); - xleft2 = (l_int32 *)LEPT_CALLOC(n2, sizeof(l_int32)); - if (!line1 || !line2 || !yloc1 || !yloc2 || !xleft1 || !xleft2) - return ERROR_INT("callof failure for an array", procName, 1); - for (i = 0; i < n1; i++) { - na1 = numaaGetNuma(naa1, i, L_CLONE); - numaGetIValue(na1, 0, yloc1 + i); - numaGetIValue(na1, 1, xleft1 + i); - nbox = (numaGetCount(na1) - 1) / 2; - if (nbox >= nperline) - line1[i] = 1; - numaDestroy(&na1); - } - for (i = 0; i < n2; i++) { - na2 = numaaGetNuma(naa2, i, L_CLONE); - numaGetIValue(na2, 0, yloc2 + i); - numaGetIValue(na2, 1, xleft2 + i); - nbox = (numaGetCount(na2) - 1) / 2; - if (nbox >= nperline) - line2[i] = 1; - numaDestroy(&na2); - } - - /* Enumerate all possible line matches. A 'possible' line - * match is one where the x and y shifts for the first box - * in each line are within the maxshiftx and maxshifty - * constraints, and the left and right sides of the remaining - * (nperline - 1) successive boxes are within delx of each other. - * The result is a set of four numas giving parameters of - * each set of matching lines. */ - nai1 = numaCreate(0); /* line index 1 of match */ - nai2 = numaCreate(0); /* line index 2 of match */ - nasx = numaCreate(0); /* shiftx for match */ - nasy = numaCreate(0); /* shifty for match */ - for (i = 0; i < n1; i++) { - if (line1[i] == 0) continue; - y1 = yloc1[i]; - xl1 = xleft1[i]; - na1 = numaaGetNuma(naa1, i, L_CLONE); - for (j = 0; j < n2; j++) { - if (line2[j] == 0) continue; - y2 = yloc2[j]; - if (L_ABS(y1 - y2) > maxshifty) continue; - xl2 = xleft2[j]; - if (L_ABS(xl1 - xl2) > maxshiftx) continue; - shiftx = xl1 - xl2; /* shift to add to x2 values */ - shifty = y1 - y2; /* shift to add to y2 values */ - na2 = numaaGetNuma(naa2, j, L_CLONE); - - /* Now check if 'nperline' boxes in the two lines match */ - match = testLineAlignmentX(na1, na2, shiftx, delx, nperline); - if (match) { - numaAddNumber(nai1, i); - numaAddNumber(nai2, j); - numaAddNumber(nasx, shiftx); - numaAddNumber(nasy, shifty); - } - numaDestroy(&na2); - } - numaDestroy(&na1); - } - - /* Determine if there are a sufficient number of mutually - * aligned matches. Mutually aligned matches place an additional - * constraint on the 'possible' matches, where the relative - * shifts must not exceed the (delx, dely) distances. */ - countAlignedMatches(nai1, nai2, nasx, nasy, n1, n2, delx, dely, - nreq, psame, debugflag); - - LEPT_FREE(line1); - LEPT_FREE(line2); - LEPT_FREE(yloc1); - LEPT_FREE(yloc2); - LEPT_FREE(xleft1); - LEPT_FREE(xleft2); - numaDestroy(&nai1); - numaDestroy(&nai2); - numaDestroy(&nasx); - numaDestroy(&nasy); - return 0; -} - - -static l_int32 -testLineAlignmentX(NUMA *na1, - NUMA *na2, - l_int32 shiftx, - l_int32 delx, - l_int32 nperline) -{ -l_int32 i, xl1, xr1, xl2, xr2, diffl, diffr; - - PROCNAME("testLineAlignmentX"); - - if (!na1) - return ERROR_INT("na1 not defined", procName, 1); - if (!na2) - return ERROR_INT("na2 not defined", procName, 1); - - for (i = 0; i < nperline; i++) { - numaGetIValue(na1, i + 1, &xl1); - numaGetIValue(na1, i + 2, &xr1); - numaGetIValue(na2, i + 1, &xl2); - numaGetIValue(na2, i + 2, &xr2); - diffl = L_ABS(xl1 - xl2 - shiftx); - diffr = L_ABS(xr1 - xr2 - shiftx); - if (diffl > delx || diffr > delx) - return 0; - } - - return 1; -} - - -/* - * \brief countAlignedMatches() - * - * \param[in] nai1, nai2 numas of row pairs for matches - * \param[in] nasx, nasy numas of x and y shifts for the matches - * \param[in] n1, n2 number of rows in images 1 and 2 - * \param[in] delx, dely allowed difference in shifts of the match, - * compared to the reference match - * \param[in] nre1 number of required aligned matches - * \param[out] psame return 1 if %nreq row matches are found; - * 0 otherwise - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This takes 4 input arrays giving parameters of all the - * line matches. It looks for the maximum set of aligned - * matches (matches with approximately the same overall shifts) - * that do not use rows from either image more than once. - *- */ -static l_ok -countAlignedMatches(NUMA *nai1, - NUMA *nai2, - NUMA *nasx, - NUMA *nasy, - l_int32 n1, - l_int32 n2, - l_int32 delx, - l_int32 dely, - l_int32 nreq, - l_int32 *psame, - l_int32 debugflag) -{ -l_int32 i, j, nm, shiftx, shifty, nmatch, diffx, diffy; -l_int32 *ia1, *ia2, *iasx, *iasy, *index1, *index2; - - PROCNAME("countAlignedMatches"); - - if (!nai1 || !nai2 || !nasx || !nasy) - return ERROR_INT("4 input numas not defined", procName, 1); - if (!psame) - return ERROR_INT("&same not defined", procName, 1); - *psame = 0; - - /* Check for sufficient aligned matches, doing a double iteration - * over the set of raw matches. The row index arrays - * are used to verify that the same rows in either image - * are not used in more than one match. Whenever there - * is a match that is properly aligned, those rows are - * marked in the index arrays. */ - nm = numaGetCount(nai1); /* number of matches */ - if (nm < nreq) - return 0; - - ia1 = numaGetIArray(nai1); - ia2 = numaGetIArray(nai2); - iasx = numaGetIArray(nasx); - iasy = numaGetIArray(nasy); - index1 = (l_int32 *)LEPT_CALLOC(n1, sizeof(l_int32)); /* watch rows */ - index2 = (l_int32 *)LEPT_CALLOC(n2, sizeof(l_int32)); - if (!index1 || !index2) - return ERROR_INT("calloc fail for array", procName, 1); - for (i = 0; i < nm; i++) { - if (*psame == 1) - break; - - /* Reset row index arrays */ - memset(index1, 0, 4 * n1); - memset(index2, 0, 4 * n2); - nmatch = 1; - index1[ia1[i]] = nmatch; /* mark these rows as taken */ - index2[ia2[i]] = nmatch; - shiftx = iasx[i]; /* reference shift between two rows */ - shifty = iasy[i]; /* ditto */ - if (nreq == 1) { - *psame = 1; - break; - } - for (j = 0; j < nm; j++) { - if (j == i) continue; - /* Rows must both be different from any previously seen */ - if (index1[ia1[j]] > 0 || index2[ia2[j]] > 0) continue; - /* Check the shift for this match */ - diffx = L_ABS(shiftx - iasx[j]); - diffy = L_ABS(shifty - iasy[j]); - if (diffx > delx || diffy > dely) continue; - /* We have a match */ - nmatch++; - index1[ia1[j]] = nmatch; /* mark the rows */ - index2[ia2[j]] = nmatch; - if (nmatch >= nreq) { - *psame = 1; - if (debugflag) - printRowIndices(index1, n1, index2, n2); - break; - } - } - } - - LEPT_FREE(ia1); - LEPT_FREE(ia2); - LEPT_FREE(iasx); - LEPT_FREE(iasy); - LEPT_FREE(index1); - LEPT_FREE(index2); - return 0; -} - - -static void -printRowIndices(l_int32 *index1, - l_int32 n1, - l_int32 *index2, - l_int32 n2) -{ -l_int32 i; - - lept_stderr("Index1: "); - for (i = 0; i < n1; i++) { - if (i && (i % 20 == 0)) - lept_stderr("\n "); - lept_stderr("%3d", index1[i]); - } - lept_stderr("\n"); - - lept_stderr("Index2: "); - for (i = 0; i < n2; i++) { - if (i && (i % 20 == 0)) - lept_stderr("\n "); - lept_stderr("%3d", index2[i]); - } - lept_stderr("\n"); - return; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colorcontent.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colorcontent.c deleted file mode 100644 index b58c7fb3..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colorcontent.c +++ /dev/null @@ -1,2009 +0,0 @@ -/*====================================================================* - - 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 colorcontent.c - *
- * - * Builds an image of the color content, on a per-pixel basis, - * as a measure of the amount of divergence of each color - * component (R,G,B) from gray. - * l_int32 pixColorContent() - * - * Finds the 'amount' of color in an image, on a per-pixel basis, - * as a measure of the difference of the pixel color from gray. - * PIX *pixColorMagnitude() - * - * Generates a mask over pixels that have sufficient color and - * are not too close to gray pixels. - * PIX *pixMaskOverColorPixels() - * - * Generates a mask over pixels that have little color and - * are not too bright. - * PIX *pixMaskOverGrayPixels() - * - * Generates mask over pixels within a prescribed cube in RGB space - * PIX *pixMaskOverColorRange() - * - * Finds the fraction of pixels with "color" that are not close to black - * l_int32 pixColorFraction() - * - * Determine if there are significant color regions that are - * not background in a page image - * l_int32 pixFindColorRegions() - * - * Finds the number of perceptually significant gray intensities - * in a grayscale image. - * l_int32 pixNumSignificantGrayColors() - * - * Identifies images where color quantization will cause posterization - * due to the existence of many colors in low-gradient regions. - * l_int32 pixColorsForQuantization() - * - * Finds the number of unique colors in an image - * l_int32 pixNumColors() - * - * Lossless conversion of RGB image to colormapped - * PIX *pixConvertRGBToCmap() - * - * Find the most "populated" colors in the image (and quantize) - * l_int32 pixGetMostPopulatedColors() - * PIX *pixSimpleColorQuantize() - * - * Constructs a color histogram based on rgb indices - * NUMA *pixGetRGBHistogram() - * l_int32 makeRGBIndexTables() - * l_int32 getRGBFromIndex() - * - * Identify images that have highlight (red) color - * l_int32 pixHasHighlightRed() - * - * Color is tricky. If we consider gray (r = g = b) to have no color - * content, how should we define the color content in each component - * of an arbitrary pixel, as well as the overall color magnitude? - * - * I can think of three ways to define the color content in each component: - * - * (1) Linear. For each component, take the difference from the average - * of all three. - * (2) Linear. For each component, take the difference from the average - * of the other two. - * (3) Nonlinear. For each component, take the minimum of the differences - * from the other two. - * - * How might one choose from among these? Consider two different situations: - * (a) r = g = 0, b = 255 {255} /255/ - * (b) r = 0, g = 127, b = 255 {191} /128/ - * How much g is in each of these? The three methods above give: - * (a) 1: 85 2: 127 3: 0 [85] - * (b) 1: 0 2: 0 3: 127 [0] - * How much b is in each of these? - * (a) 1: 170 2: 255 3: 255 [255] - * (b) 1: 127 2: 191 3: 127 [191] - * The number I'd "like" to give is in []. (Please don't ask why, it's - * just a feeling. - * - * So my preferences seem to be somewhere between (1) and (2). - * (3) is just too "decisive!" Let's pick (2). - * - * We also allow compensation for white imbalance. For each - * component, we do a linear TRC (gamma = 1.0), where the black - * point remains at 0 and the white point is given by the input - * parameter. This is equivalent to doing a global remapping, - * as with pixGlobalNormRGB(), followed by color content (or magnitude) - * computation, but without the overhead of first creating the - * white point normalized image. - * - * Another useful property is the overall color magnitude in the pixel. - * For this there are again several choices, such as: - * (a) rms deviation from the mean - * (b) the average L1 deviation from the mean - * (c) the maximum (over components) of one of the color - * content measures given above. - * - * For now, we will choose two of the methods in (c): - * L_MAX_DIFF_FROM_AVERAGE_2 - * Define the color magnitude as the maximum over components - * of the difference between the component value and the - * average of the other two. It is easy to show that - * this is equivalent to selecting the two component values - * that are closest to each other, averaging them, and - * using the distance from that average to the third component. - * For (a) and (b) above, this value is in {..}. - * L_MAX_MIN_DIFF_FROM_2 - * Define the color magnitude as the maximum over components - * of the minimum difference between the component value and the - * other two values. It is easy to show that this is equivalent - * to selecting the intermediate value of the three differences - * between the three components. For (a) and (b) above, - * this value is in /../. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This returns the color content in each component, which is - * a measure of the deviation from gray, and is defined - * as the difference between the component and the average of - * the other two components. See the discussion at the - * top of this file. - * (2) The three numbers (rref, gref and bref) can be thought - * of in two ways: - * (a) as the values in the image corresponding to white, - * to compensate for an unbalanced color white point. - * (b) the median or mean values of the background color of - * a scan. - * The gamma TRC transformation is used to modify all colors so that - * these reference values become white. - * These three numbers must either be all 0 or all non-zero. - * To skip the TRC transform, set them all to 0. - * (3) If the maximum component after white point correction, - * max(r,g,b), is less than mingray, all color components - * for that pixel are set to zero. - * Use mingray = 0 to turn off this filtering of dark pixels. - * (4) Therefore, use 0 for all four input parameters if the color - * magnitude is to be calculated without either white balance - * correction or dark filtering. - *- */ -l_ok -pixColorContent(PIX *pixs, - l_int32 rref, - l_int32 gref, - l_int32 bref, - l_int32 mingray, - PIX **ppixr, - PIX **ppixg, - PIX **ppixb) -{ -l_int32 w, h, d, i, j, wplc, wplr, wplg, wplb; -l_int32 rval, gval, bval, rgdiff, rbdiff, gbdiff, maxval, colorval; -l_int32 *rtab, *gtab, *btab; -l_uint32 pixel; -l_uint32 *datac, *datar, *datag, *datab, *linec, *liner, *lineg, *lineb; -NUMA *nar, *nag, *nab; -PIX *pixc; /* rgb */ -PIX *pixr, *pixg, *pixb; /* 8 bpp grayscale */ -PIXCMAP *cmap; - - PROCNAME("pixColorContent"); - - if (!ppixr && !ppixg && !ppixb) - return ERROR_INT("no return val requested", procName, 1); - if (ppixr) *ppixr = NULL; - if (ppixg) *ppixg = NULL; - if (ppixb) *ppixb = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (mingray < 0) mingray = 0; - pixGetDimensions(pixs, &w, &h, &d); - if (mingray > 255) - return ERROR_INT("mingray > 255", procName, 1); - if (rref < 0 || gref < 0 || bref < 0) - return ERROR_INT("some reference vals are negative", procName, 1); - if ((rref || gref || bref) && (rref * gref * bref == 0)) - return ERROR_INT("reference vals not all zero or all nonzero", - procName, 1); - - cmap = pixGetColormap(pixs); - if (!cmap && d != 32) - return ERROR_INT("pixs neither cmapped nor 32 bpp", procName, 1); - if (cmap) - pixc = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); - else - pixc = pixClone(pixs); - - pixr = pixg = pixb = NULL; - pixGetDimensions(pixc, &w, &h, NULL); - if (ppixr) { - pixr = pixCreate(w, h, 8); - datar = pixGetData(pixr); - wplr = pixGetWpl(pixr); - *ppixr = pixr; - } - if (ppixg) { - pixg = pixCreate(w, h, 8); - datag = pixGetData(pixg); - wplg = pixGetWpl(pixg); - *ppixg = pixg; - } - if (ppixb) { - pixb = pixCreate(w, h, 8); - datab = pixGetData(pixb); - wplb = pixGetWpl(pixb); - *ppixb = pixb; - } - - datac = pixGetData(pixc); - wplc = pixGetWpl(pixc); - if (rref) { /* all reference vals are nonzero */ - nar = numaGammaTRC(1.0, 0, rref); - rtab = numaGetIArray(nar); - nag = numaGammaTRC(1.0, 0, gref); - gtab = numaGetIArray(nag); - nab = numaGammaTRC(1.0, 0, bref); - btab = numaGetIArray(nab); - } - for (i = 0; i < h; i++) { - linec = datac + i * wplc; - if (pixr) - liner = datar + i * wplr; - if (pixg) - lineg = datag + i * wplg; - if (pixb) - lineb = datab + i * wplb; - for (j = 0; j < w; j++) { - pixel = linec[j]; - extractRGBValues(pixel, &rval, &gval, &bval); - if (rref) { /* color correct for reference values */ - rval = rtab[rval]; - gval = gtab[gval]; - bval = btab[bval]; - } - if (mingray > 0) { /* dark pixels have no color value */ - maxval = L_MAX(rval, gval); - maxval = L_MAX(maxval, bval); - if (maxval < mingray) - continue; /* colorval = 0 for each component */ - } - rgdiff = L_ABS(rval - gval); - rbdiff = L_ABS(rval - bval); - gbdiff = L_ABS(gval - bval); - if (pixr) { - colorval = (rgdiff + rbdiff) / 2; - SET_DATA_BYTE(liner, j, colorval); - } - if (pixg) { - colorval = (rgdiff + gbdiff) / 2; - SET_DATA_BYTE(lineg, j, colorval); - } - if (pixb) { - colorval = (rbdiff + gbdiff) / 2; - SET_DATA_BYTE(lineb, j, colorval); - } - } - } - - if (rref) { - numaDestroy(&nar); - numaDestroy(&nag); - numaDestroy(&nab); - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - } - pixDestroy(&pixc); - return 0; -} - - -/* ----------------------------------------------------------------------- * - * Finds the 'amount' of color in an image, on a per-pixel basis, * - * as a measure of the difference of the pixel color from gray. * - * ----------------------------------------------------------------------- */ -/*! - * \brief pixColorMagnitude() - * - * \param[in] pixs 32 bpp rgb or 8 bpp colormapped - * \param[in] rref, gref, bref reference color values (e.g. median - * or mean, to compare with the pixel - * component values. - * \param[in] type chooses the method for calculating the color magnitude: - * L_MAX_DIFF_FROM_AVERAGE_2, L_MAX_MIN_DIFF_FROM_2, - * L_MAX_DIFF - * \return pixd 8 bpp, amount of color in each source pixel, - * or NULL on error - * - *
- * Notes: - * (1) For an RGB image, a gray pixel is one where all three components - * are equal. We define the amount of color in an RGB pixel as - * a function depending on the absolute value of the differences - * between the three color components. Consider the two largest - * of these differences. The pixel component in common to these - * two differences is the color farthest from the other two. - * The color magnitude in an RGB pixel can be taken as one - * of these three definitions: - * (a) The average of these two differences. This is the - * average distance from the two components that are - * nearest to each other to the third component. - * (b) The minimum value of these two differences. This is - * the intermediate value of the three distances between - * component values. Stated otherwise, it is the - * maximum over all components of the minimum distance - * from that component to the other two components. - * (c) The maximum difference between component values. - * (2) As an example, suppose that R and G are the closest in - * magnitude. Then the color is determined as either: - * (a) The average distance of B from these two: - * (|B - R| + |B - G|) / 2 - * (b) The minimum distance of B from these two: - * min(|B - R|, |B - G|). - * (c) The maximum distance of B from these two: - * max(|B - R|, |B - G|) - * (3) The three methods for choosing the color magnitude from - * the components are selected with these flags: - * (a) L_MAX_DIFF_FROM_AVERAGE_2 - * (b) L_MAX_MIN_DIFF_FROM_2 - * (c) L_MAX_DIFF - * (4) The three numbers (rref, gref and bref) can be thought - * of in two ways: - * (a) as the values in the image corresponding to white, - * to compensate for an unbalanced color white point. - * (b) the median or mean values of the background color of - * a scan. - * The gamma TRC transformation is used to modify all colors so that - * these reference values become white. - * These three numbers must either be all 0 or all non-zero. - * To skip the TRC transform, set them all to 0. - *- */ -PIX * -pixColorMagnitude(PIX *pixs, - l_int32 rref, - l_int32 gref, - l_int32 bref, - l_int32 type) -{ -l_int32 w, h, d, i, j, wplc, wpld; -l_int32 rval, gval, bval, rdist, gdist, bdist, colorval; -l_int32 rgdist, rbdist, gbdist, mindist, maxdist, minval, maxval; -l_int32 *rtab, *gtab, *btab; -l_uint32 pixel; -l_uint32 *datac, *datad, *linec, *lined; -NUMA *nar, *nag, *nab; -PIX *pixc, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixColorMagnitude"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (type != L_MAX_DIFF_FROM_AVERAGE_2 && type != L_MAX_MIN_DIFF_FROM_2 && - type != L_MAX_DIFF) - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - if (rref < 0 || gref < 0 || bref < 0) - return (PIX *)ERROR_PTR("some reference vals are negative", - procName, NULL); - if ((rref || gref || bref) && (rref * gref * bref == 0)) - return (PIX *)ERROR_PTR("reference vals not all zero or all nonzero", - procName, NULL); - - cmap = pixGetColormap(pixs); - if (!cmap && d != 32) - return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); - if (cmap) - pixc = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); - else - pixc = pixClone(pixs); - - pixd = pixCreate(w, h, 8); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - datac = pixGetData(pixc); - wplc = pixGetWpl(pixc); - if (rref) { /* all ref vals are nonzero */ - nar = numaGammaTRC(1.0, 0, rref); - rtab = numaGetIArray(nar); - nag = numaGammaTRC(1.0, 0, gref); - gtab = numaGetIArray(nag); - nab = numaGammaTRC(1.0, 0, bref); - btab = numaGetIArray(nab); - } - for (i = 0; i < h; i++) { - linec = datac + i * wplc; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - pixel = linec[j]; - extractRGBValues(pixel, &rval, &gval, &bval); - if (rref) { /* color correct for reference values */ - rval = rtab[rval]; - gval = gtab[gval]; - bval = btab[bval]; - } - if (type == L_MAX_DIFF_FROM_AVERAGE_2) { - rdist = ((gval + bval ) / 2 - rval); - rdist = L_ABS(rdist); - gdist = ((rval + bval ) / 2 - gval); - gdist = L_ABS(gdist); - bdist = ((rval + gval ) / 2 - bval); - bdist = L_ABS(bdist); - colorval = L_MAX(rdist, gdist); - colorval = L_MAX(colorval, bdist); - } else if (type == L_MAX_MIN_DIFF_FROM_2) { /* intermediate dist */ - rgdist = L_ABS(rval - gval); - rbdist = L_ABS(rval - bval); - gbdist = L_ABS(gval - bval); - maxdist = L_MAX(rgdist, rbdist); - if (gbdist >= maxdist) { - colorval = maxdist; - } else { /* gbdist is smallest or intermediate */ - mindist = L_MIN(rgdist, rbdist); - colorval = L_MAX(mindist, gbdist); - } - } else { /* type == L_MAX_DIFF */ - minval = L_MIN(rval, gval); - minval = L_MIN(minval, bval); - maxval = L_MAX(rval, gval); - maxval = L_MAX(maxval, bval); - colorval = maxval - minval; - } - SET_DATA_BYTE(lined, j, colorval); - } - } - - if (rref) { - numaDestroy(&nar); - numaDestroy(&nag); - numaDestroy(&nab); - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - } - pixDestroy(&pixc); - return pixd; -} - - -/* ----------------------------------------------------------------------- * - * Generates a mask over pixels that have sufficient color and * - * are not too close to gray pixels. * - * ----------------------------------------------------------------------- */ -/*! - * \brief pixMaskOverColorPixels() - * - * \param[in] pixs 32 bpp rgb or 8 bpp colormapped - * \param[in] threshdiff threshold for minimum of the max difference - * between components - * \param[in] mindist min allowed distance from nearest non-color pixel - * \return pixd 1 bpp, mask over color pixels, or NULL on error - * - *
- * Notes: - * (1) The generated mask identifies each pixel as either color or - * non-color. For a pixel to be color, it must satisfy two - * constraints: - * (a) The max difference between the r,g and b components must - * equal or exceed a threshold %threshdiff. - * (b) It must be at least %mindist (in an 8-connected way) - * from the nearest non-color pixel. - * (2) The distance constraint (b) is only applied if %mindist > 1. - * For example, if %mindist == 2, the color pixels identified - * by (a) are eroded by a 3x3 Sel. In general, the Sel size - * for erosion is 2 * (%mindist - 1) + 1. - * Why have this constraint? In scanned images that are - * essentially gray, color artifacts are typically introduced - * in transition regions near sharp edges that go from dark - * to light, so this allows these transition regions to be removed. - *- */ -PIX * -pixMaskOverColorPixels(PIX *pixs, - l_int32 threshdiff, - l_int32 mindist) -{ -l_int32 w, h, d, i, j, wpls, wpld, size; -l_int32 rval, gval, bval, minval, maxval; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixc, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixMaskOverColorPixels"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - - cmap = pixGetColormap(pixs); - if (!cmap && d != 32) - return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); - if (cmap) - pixc = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); - else - pixc = pixClone(pixs); - - pixd = pixCreate(w, h, 1); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - datas = pixGetData(pixc); - wpls = pixGetWpl(pixc); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - minval = L_MIN(rval, gval); - minval = L_MIN(minval, bval); - maxval = L_MAX(rval, gval); - maxval = L_MAX(maxval, bval); - if (maxval - minval >= threshdiff) - SET_DATA_BIT(lined, j); - } - } - - if (mindist > 1) { - size = 2 * (mindist - 1) + 1; - pixErodeBrick(pixd, pixd, size, size); - } - - pixDestroy(&pixc); - return pixd; -} - - -/* ----------------------------------------------------------------------- * - * Generates a mask over pixels that have little color and * - * are not too bright * - * ----------------------------------------------------------------------- */ -/*! - * \brief pixMaskOverGrayPixels() - * - * \param[in] pixs 32 bpp rgb - * \param[in] maxlimit only consider pixels with max component <= %maxlimit - * \param[in] satlimit only consider pixels with saturation <= %satlimit - * \return pixd (1 bpp), or NULL on error - * - *
- * Notes: - * (1) This generates a mask over rgb pixels that are gray (i.e., - * have low saturation) and are not too bright. For example, if - * we know that the gray pixels in %pixs have saturation - * (max - min) less than 10, and brightness (max) less than 200, - * pixMaskOverGrayPixels(pixs, 220, 10) - * will generate a mask over the gray pixels. Other pixels that - * are not too dark and have a relatively large saturation will - * be little affected. - * (2) The algorithm is related to pixDarkenGray(). - *- */ -PIX * -pixMaskOverGrayPixels(PIX *pixs, - l_int32 maxlimit, - l_int32 satlimit) -{ -l_int32 w, h, i, j, wpls, wpld; -l_int32 rval, gval, bval, minrg, min, maxrg, max, sat; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; - - PROCNAME("pixMaskOverGrayPixels"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); - if (maxlimit < 0 || maxlimit > 255) - return (PIX *)ERROR_PTR("invalid maxlimit", procName, NULL); - if (satlimit < 1) - return (PIX *)ERROR_PTR("invalid satlimit", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if ((pixd = pixCreate(w, h, 1)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - minrg = L_MIN(rval, gval); - min = L_MIN(minrg, bval); - maxrg = L_MAX(rval, gval); - max = L_MAX(maxrg, bval); - sat = max - min; - if (max <= maxlimit && sat <= satlimit) - SET_DATA_BIT(lined, j); - } - } - return pixd; -} - - -/* ----------------------------------------------------------------------- * - * Generates a mask over pixels that have RGB color components * - * within the prescribed range (a cube in RGB color space) * - * ----------------------------------------------------------------------- */ -/*! - * \brief pixMaskOverColorRange() - * - * \param[in] pixs 32 bpp rgb or 8 bpp colormapped - * \param[in] rmin, rmax min and max allowed values for red component - * \param[in] gmin, gmax ditto for green - * \param[in] bmin, bmax ditto for blue - * \return pixd 1 bpp, mask over color pixels, or NULL on error - */ -PIX * -pixMaskOverColorRange(PIX *pixs, - l_int32 rmin, - l_int32 rmax, - l_int32 gmin, - l_int32 gmax, - l_int32 bmin, - l_int32 bmax) -{ -l_int32 w, h, d, i, j, wpls, wpld; -l_int32 rval, gval, bval; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixc, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixMaskOverColorRange"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - - cmap = pixGetColormap(pixs); - if (!cmap && d != 32) - return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); - if (cmap) - pixc = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); - else - pixc = pixClone(pixs); - - pixd = pixCreate(w, h, 1); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - datas = pixGetData(pixc); - wpls = pixGetWpl(pixc); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - if (rval < rmin || rval > rmax) continue; - if (gval < gmin || gval > gmax) continue; - if (bval < bmin || bval > bmax) continue; - SET_DATA_BIT(lined, j); - } - } - - pixDestroy(&pixc); - return pixd; -} - - -/* ----------------------------------------------------------------------- * - * Finds the fraction of pixels with "color" that are not close to black * - * ----------------------------------------------------------------------- */ -/*! - * \brief pixColorFraction() - * - * \param[in] pixs 32 bpp rgb - * \param[in] darkthresh threshold near black; if the lightest component - * is below this, the pixel is not considered in - * the statistics; typ. 20 - * \param[in] lightthresh threshold near white; if the darkest component - * is above this, the pixel is not considered in - * the statistics; typ. 244 - * \param[in] diffthresh thresh for the maximum difference between - * component value; below this the pixel is not - * considered to have sufficient color - * \param[in] factor subsampling factor - * \param[out] ppixfract fraction of pixels in intermediate - * brightness range that were considered - * for color content - * \param[out] pcolorfract fraction of pixels that meet the - * criterion for sufficient color; 0.0 on error - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This function is asking the question: to what extent does the - * image appear to have color? The amount of color a pixel - * appears to have depends on both the deviation of the - * individual components from their average and on the average - * intensity itself. For example, the color will be much more - * obvious with a small deviation from white than the same - * deviation from black. - * (2) Any pixel that meets these three tests is considered a - * colorful pixel: - * (a) the lightest component must equal or exceed %darkthresh - * (b) the darkest component must not exceed %lightthresh - * (c) the max difference between components must equal or - * exceed %diffthresh. - * (3) The dark pixels are removed from consideration because - * they don't appear to have color. - * (4) The very lightest pixels are removed because if an image - * has a lot of "white", the color fraction will be artificially - * low, even if all the other pixels are colorful. - * (5) If pixfract is very small, there are few pixels that are neither - * black nor white. If colorfract is very small, the pixels - * that are neither black nor white have very little color - * content. The product 'pixfract * colorfract' gives the - * fraction of pixels with significant color content. - * (6) One use of this function is as a preprocessing step for median - * cut quantization (colorquant2.c), which does a very poor job - * splitting the color space into rectangular volume elements when - * all the pixels are near the diagonal of the color cube. For - * octree quantization of an image with only gray values, the - * 2^(level) octcubes on the diagonal are the only ones - * that can be occupied. - *- */ -l_ok -pixColorFraction(PIX *pixs, - l_int32 darkthresh, - l_int32 lightthresh, - l_int32 diffthresh, - l_int32 factor, - l_float32 *ppixfract, - l_float32 *pcolorfract) -{ -l_int32 i, j, w, h, wpl, rval, gval, bval, minval, maxval; -l_int32 total, npix, ncolor; -l_uint32 pixel; -l_uint32 *data, *line; - - PROCNAME("pixColorFraction"); - - if (ppixfract) *ppixfract = 0.0; - if (pcolorfract) *pcolorfract = 0.0; - if (!ppixfract || !pcolorfract) - return ERROR_INT("&pixfract and &colorfract not defined", - procName, 1); - if (!pixs || pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); - - pixGetDimensions(pixs, &w, &h, NULL); - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - npix = ncolor = total = 0; - for (i = 0; i < h; i += factor) { - line = data + i * wpl; - for (j = 0; j < w; j += factor) { - total++; - pixel = line[j]; - extractRGBValues(pixel, &rval, &gval, &bval); - minval = L_MIN(rval, gval); - minval = L_MIN(minval, bval); - if (minval > lightthresh) /* near white */ - continue; - maxval = L_MAX(rval, gval); - maxval = L_MAX(maxval, bval); - if (maxval < darkthresh) /* near black */ - continue; - - npix++; - if (maxval - minval >= diffthresh) - ncolor++; - } - } - - if (npix == 0) { - L_WARNING("No pixels found for consideration\n", procName); - return 0; - } - *ppixfract = (l_float32)npix / (l_float32)total; - *pcolorfract = (l_float32)ncolor / (l_float32)npix; - return 0; -} - - -/* ----------------------------------------------------------------------- * - * Determine if there are significant color regions in a page image * - * ----------------------------------------------------------------------- */ -/*! - * \brief pixFindColorRegions() - * - * \param[in] pixs 32 bpp rgb - * \param[in] pixm [optional] 1 bpp mask image - * \param[in] factor subsample factor; integer >= 1 - * \param[in] lightthresh threshold for component average in lightest - * of 10 buckets; typ. 210; -1 for default - * \param[in] darkthresh threshold to eliminate dark pixels (e.g., text) - * from consideration; typ. 70; -1 for default. - * \param[in] mindiff minimum difference (b - r) and (g - r), used to - * find blue or green pixels; typ. 10; -1 for default - * \param[in] colordiff minimum difference in (max - min) component to - * qualify as a color pixel; typ. 90; -1 for default - * \param[in] edgefract fraction of image half-width and half-height - * for which color pixels are ignored; typ. 0.05. - * \param[out] pcolorfract fraction of 'color' pixels found - * \param[out] pcolormask1 [optional] mask over background color, if any - * \param[out] pcolormask2 [optional] filtered mask over background color - * \param[out] pixadb [optional] debug intermediate results - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This function tries to determine if there is a significant - * color or darker region on a scanned page image, where part - * of the image is background that is either white or reddish. - * This also allows extraction of regions of colored pixels that - * have a smaller red component than blue or green components. - * (2) If %pixm exists, pixels under its fg are combined with - * dark pixels to make a mask of pixels not to be considered - * as color candidates. - * (3) There are four thresholds. - * * %lightthresh: compute the average value of each rgb pixel, - * and make 10 buckets by value. If the lightest bucket gray - * value is below %lightthresh, the image is not considered - * to have a light bg, and this returns 0.0 for %colorfract. - * * %darkthresh: ignore pixels darker than this (typ. fg text). - * We make a 1 bpp mask of these pixels, and then dilate it to - * remove all vestiges of fg from their vicinity. - * * %mindiff: consider pixels with either (b - r) or (g - r) - * being at least this value, as having color. - * * %colordiff: consider pixels where the (max - min) difference - * of the pixel components exceeds this value, as having color. - * (4) All components of color pixels that are touching the image - * border are removed. Additionally, all pixels within some - * normalized distance %edgefract from the image border can - * be removed. This insures that dark pixels near the edge - * of the image are not included. - * (5) This returns in %pcolorfract the fraction of pixels that have - * color and are not in the set consisting of an OR between - * %pixm and the dilated dark pixel mask. - * (6) No masks are returned unless light color pixels are found. - * If colorfract > 0.0 and %pcolormask1 is defined, this returns - * a 1 bpp mask with fg pixels over the color background. - * This mask may have some holes in it. - * (7) If colorfract > 0.0 and %pcolormask2 is defined, this returns - * a version of colormask1 where small holes have been filled. - * (8) To generate a boxa of rectangular regions from the overlap - * of components in the filtered mask: - * boxa1 = pixConnCompBB(colormask2, 8); - * boxa2 = boxaCombineOverlaps(boxa1, NULL); - * This is done here in debug mode. - *- */ -l_ok -pixFindColorRegions(PIX *pixs, - PIX *pixm, - l_int32 factor, - l_int32 lightthresh, - l_int32 darkthresh, - l_int32 mindiff, - l_int32 colordiff, - l_float32 edgefract, - l_float32 *pcolorfract, - PIX **pcolormask1, - PIX **pcolormask2, - PIXA *pixadb) -{ -l_int32 w, h, count, rval, gval, bval, aveval, proceed; -l_float32 ratio; -l_uint32 *carray; -BOXA *boxa1, *boxa2; -PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pixm1, *pixm2, *pixm3; - - PROCNAME("pixFindColorRegions"); - - if (pcolormask1) *pcolormask1 = NULL; - if (pcolormask2) *pcolormask2 = NULL; - if (!pcolorfract) - return ERROR_INT("&colorfract not defined", procName, 1); - *pcolorfract = 0.0; - if (!pixs || pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); - if (factor < 1) factor = 1; - if (lightthresh < 0) lightthresh = 210; /* defaults */ - if (darkthresh < 0) darkthresh = 70; - if (mindiff < 0) mindiff = 10; - if (colordiff < 0) colordiff = 90; - if (edgefract < 0.0 || edgefract > 1.0) edgefract = 0.05; - - /* Check if pixm covers most of the image. If so, just return. */ - pixGetDimensions(pixs, &w, &h, NULL); - if (pixm) { - pixCountPixels(pixm, &count, NULL); - ratio = (l_float32)count / ((l_float32)(w) * h); - if (ratio > 0.7) { - if (pixadb) L_INFO("pixm has big fg: %f5.2\n", procName, ratio); - return 0; - } - } - - /* Get the light background color. Use the average component value - * and select the lightest of 10 buckets. Require that it is - * reddish and, using lightthresh, not too dark. */ - pixGetRankColorArray(pixs, 10, L_SELECT_AVERAGE, factor, &carray, NULL, 0); - if (!carray) - return ERROR_INT("rank color array not made", procName, 1); - extractRGBValues(carray[9], &rval, &gval, &bval); - if (pixadb) L_INFO("lightest background color: (r,g,b) = (%d,%d,%d)\n", - procName, rval, gval, bval); - proceed = TRUE; - if ((rval < bval - 2) || (rval < gval - 2)) { - if (pixadb) L_INFO("background not reddish\n", procName); - proceed = FALSE; - } - aveval = (rval + gval + bval) / 3; - if (aveval < lightthresh) { - if (pixadb) L_INFO("background too dark\n", procName); - proceed = FALSE; - } - if (pixadb) { - pix1 = pixDisplayColorArray(carray, 10, 120, 3, 6); - pixaAddPix(pixadb, pix1, L_INSERT); - } - LEPT_FREE(carray); - if (proceed == FALSE) return 0; - - /* Make a mask pixm1 over the dark pixels in the image: - * convert to gray using the average of the components; - * threshold using darkthresh; do a small dilation; - * combine with pixm. */ - pix1 = pixConvertRGBToGray(pixs, 0.33, 0.34, 0.33); - if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); - pixm1 = pixThresholdToBinary(pix1, darkthresh); - pixDilateBrick(pixm1, pixm1, 7, 7); - if (pixadb) pixaAddPix(pixadb, pixm1, L_COPY); - if (pixm) { - pixOr(pixm1, pixm1, pixm); - if (pixadb) pixaAddPix(pixadb, pixm1, L_COPY); - } - pixDestroy(&pix1); - - /* Make masks over pixels that are bluish, or greenish, or - have a very large color saturation (max - min) value. */ - pixm2 = pixConvertRGBToBinaryArb(pixs, -1.0, 0.0, 1.0, mindiff, - L_SELECT_IF_GTE); /* b - r */ - if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY); - pix1 = pixConvertRGBToBinaryArb(pixs, -1.0, 1.0, 0.0, mindiff, - L_SELECT_IF_GTE); /* g - r */ - if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); - pixOr(pixm2, pixm2, pix1); - pixDestroy(&pix1); - pix1 = pixConvertRGBToGrayMinMax(pixs, L_CHOOSE_MAXDIFF); - pix2 = pixThresholdToBinary(pix1, colordiff); - pixInvert(pix2, pix2); - if (pixadb) pixaAddPix(pixadb, pix2, L_COPY); - pixOr(pixm2, pixm2, pix2); - if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY); - pixDestroy(&pix1); - pixDestroy(&pix2); - - /* Subtract the dark pixels represented by pixm1. - * pixm2 now holds all the color pixels of interest */ - pixSubtract(pixm2, pixm2, pixm1); - pixDestroy(&pixm1); - if (pixadb) pixaAddPix(pixadb, pixm2, L_COPY); - - /* But we're not quite finished. Remove pixels from any component - * that is touching the image border. False color pixels can - * sometimes be found there if the image is much darker near - * the border, due to oxidation or reduced illumination. Also - * remove any pixels within the normalized fraction %distfract - * of the image border. */ - pixm3 = pixRemoveBorderConnComps(pixm2, 8); - pixDestroy(&pixm2); - if (edgefract > 0.0) { - pix2 = pixMakeSymmetricMask(w, h, edgefract, edgefract, L_USE_INNER); - pixAnd(pixm3, pixm3, pix2); - pixDestroy(&pix2); - } - if (pixadb) pixaAddPix(pixadb, pixm3, L_COPY); - - /* Get the fraction of light color pixels */ - pixCountPixels(pixm3, &count, NULL); - *pcolorfract = (l_float32)count / ((l_float32)(w) * h); - if (pixadb) { - if (count == 0) - L_INFO("no light color pixels found\n", procName); - else - L_INFO("fraction of light color pixels = %5.3f\n", procName, - *pcolorfract); - } - - /* Debug: extract the color pixels from pixs */ - if (pixadb && count > 0) { - /* Use pixm3 to extract the color pixels */ - pix3 = pixCreateTemplate(pixs); - pixSetAll(pix3); - pixCombineMasked(pix3, pixs, pixm3); - pixaAddPix(pixadb, pix3, L_INSERT); - - /* Use additional filtering to extract the color pixels */ - pix3 = pixCloseSafeBrick(NULL, pixm3, 15, 15); - pixaAddPix(pixadb, pix3, L_INSERT); - pix5 = pixCreateTemplate(pixs); - pixSetAll(pix5); - pixCombineMasked(pix5, pixs, pix3); - pixaAddPix(pixadb, pix5, L_INSERT); - - /* Get the combined bounding boxes of the mask components - * in pix3, and extract those pixels from pixs. */ - boxa1 = pixConnCompBB(pix3, 8); - boxa2 = boxaCombineOverlaps(boxa1, NULL); - pix4 = pixCreateTemplate(pix3); - pixMaskBoxa(pix4, pix4, boxa2, L_SET_PIXELS); - pixaAddPix(pixadb, pix4, L_INSERT); - pix5 = pixCreateTemplate(pixs); - pixSetAll(pix5); - pixCombineMasked(pix5, pixs, pix4); - pixaAddPix(pixadb, pix5, L_INSERT); - boxaDestroy(&boxa1); - boxaDestroy(&boxa2); - } - pixaAddPix(pixadb, pixs, L_COPY); - - /* Optional colormask returns */ - if (pcolormask2 && count > 0) - *pcolormask2 = pixCloseSafeBrick(NULL, pixm3, 15, 15); - if (pcolormask1 && count > 0) - *pcolormask1 = pixm3; - else - pixDestroy(&pixm3); - return 0; -} - - -/* ----------------------------------------------------------------------- * - * Finds the number of perceptually significant gray intensities * - * in a grayscale image. * - * ----------------------------------------------------------------------- */ -/*! - * \brief pixNumSignificantGrayColors() - * - * \param[in] pixs 8 bpp gray - * \param[in] darkthresh dark threshold for minimum intensity to be - * considered; typ. 20 - * \param[in] lightthresh threshold near white, for maximum intensity - * to be considered; typ. 236 - * \param[in] minfract minimum fraction of all pixels to include a level - * as significant; typ. 0.0001; should be < 0.001 - * \param[in] factor subsample factor; integer >= 1 - * \param[out] pncolors number of significant colors; 0 on error - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This function is asking the question: how many perceptually - * significant gray color levels is in this pix? - * A color level must meet 3 criteria to be significant: - * ~ it can't be too close to black - * ~ it can't be too close to white - * ~ it must have at least some minimum fractional population - * (2) Use -1 for default values for darkthresh, lightthresh and minfract. - * (3) Choose default of darkthresh = 20, because variations in very - * dark pixels are not visually significant. - * (4) Choose default of lightthresh = 236, because document images - * that have been jpeg'd typically have near-white pixels in the - * 8x8 jpeg blocks, and these should not be counted. It is desirable - * to obtain a clean image by quantizing this noise away. - *- */ -l_ok -pixNumSignificantGrayColors(PIX *pixs, - l_int32 darkthresh, - l_int32 lightthresh, - l_float32 minfract, - l_int32 factor, - l_int32 *pncolors) -{ -l_int32 i, w, h, count, mincount, ncolors; -NUMA *na; - - PROCNAME("pixNumSignificantGrayColors"); - - if (!pncolors) - return ERROR_INT("&ncolors not defined", procName, 1); - *pncolors = 0; - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); - if (darkthresh < 0) darkthresh = 20; /* defaults */ - if (lightthresh < 0) lightthresh = 236; - if (minfract < 0.0) minfract = 0.0001; - if (minfract > 1.0) - return ERROR_INT("minfract > 1.0", procName, 1); - if (minfract >= 0.001) - L_WARNING("minfract too big; likely to underestimate ncolors\n", - procName); - if (lightthresh > 255 || darkthresh >= lightthresh) - return ERROR_INT("invalid thresholds", procName, 1); - if (factor < 1) factor = 1; - - pixGetDimensions(pixs, &w, &h, NULL); - mincount = (l_int32)(minfract * w * h * factor * factor); - if ((na = pixGetGrayHistogram(pixs, factor)) == NULL) - return ERROR_INT("na not made", procName, 1); - ncolors = 2; /* add in black and white */ - for (i = darkthresh; i <= lightthresh; i++) { - numaGetIValue(na, i, &count); - if (count >= mincount) - ncolors++; - } - - *pncolors = ncolors; - numaDestroy(&na); - return 0; -} - - -/* ----------------------------------------------------------------------- * - * Identifies images where color quantization will cause posterization * - * due to the existence of many colors in low-gradient regions. * - * ----------------------------------------------------------------------- */ -/*! - * \brief pixColorsForQuantization() - * \param[in] pixs 8 bpp gray or 32 bpp rgb; with or without colormap - * \param[in] thresh binary threshold on edge gradient; 0 for default - * \param[out] pncolors the number of colors found - * \param[out] piscolor [optional] 1 if significant color is found; - * 0 otherwise. If pixs is 8 bpp, and does not have - * a colormap with color entries, this is 0 - * \param[in] debug 1 to output masked image that is tested for colors; - * 0 otherwise - * \return 0 if OK, 1 on error. - * - *
- * Notes: - * (1) This function finds a measure of the number of colors that are - * found in low-gradient regions of an image. By its - * magnitude relative to some threshold (not specified in - * this function), it gives a good indication of whether - * quantization will generate posterization. This number - * is larger for images with regions of slowly varying - * intensity (if 8 bpp) or color (if rgb). Such images, if - * quantized, may require dithering to avoid posterization, - * and lossless compression is then expected to be poor. - * (2) If pixs has a colormap, the number of colors returned is - * the number in the colormap. - * (3) It is recommended that document images be reduced to a width - * of 800 pixels before applying this function. Then it can - * be expected that color detection will be fairly accurate - * and the number of colors will reflect both the content and - * the type of compression to be used. For less than 15 colors, - * there is unlikely to be a halftone image, and lossless - * quantization should give both a good visual result and - * better compression. - * (4) When using the default threshold on the gradient (15), - * images (both gray and rgb) where ncolors is greater than - * about 15 will compress poorly with either lossless - * compression or dithered quantization, and they may be - * posterized with non-dithered quantization. - * (5) For grayscale images, or images without significant color, - * this returns the number of significant gray levels in - * the low-gradient regions. The actual number of gray levels - * can be large due to jpeg compression noise in the background. - * (6) Similarly, for color images, the actual number of different - * (r,g,b) colors in the low-gradient regions (rather than the - * number of occupied level 4 octcubes) can be quite large, e.g., - * due to jpeg compression noise, even for regions that appear - * to be of a single color. By quantizing to level 4 octcubes, - * most of these superfluous colors are removed from the counting. - * (7) The image is tested for color. If there is very little color, - * it is thresholded to gray and the number of gray levels in - * the low gradient regions is found. If the image has color, - * the number of occupied level 4 octcubes is found. - * (8) The number of colors in the low-gradient regions increases - * monotonically with the threshold %thresh on the edge gradient. - * (9) Background: grayscale and color quantization is often useful - * to achieve highly compressed images with little visible - * distortion. However, gray or color washes (regions of - * low gradient) can defeat this approach to high compression. - * How can one determine if an image is expected to compress - * well using gray or color quantization? We use the fact that - * * gray washes, when quantized with less than 50 intensities, - * have posterization (visible boundaries between regions - * of uniform 'color') and poor lossless compression - * * color washes, when quantized with level 4 octcubes, - * typically result in both posterization and the occupancy - * of many level 4 octcubes. - * Images can have colors either intrinsically or as jpeg - * compression artifacts. This function reduces but does not - * completely eliminate measurement of jpeg quantization noise - * in the white background of grayscale or color images. - *- */ -l_ok -pixColorsForQuantization(PIX *pixs, - l_int32 thresh, - l_int32 *pncolors, - l_int32 *piscolor, - l_int32 debug) -{ -l_int32 w, h, d, minside, factor; -l_float32 pixfract, colorfract; -PIX *pixt, *pixsc, *pixg, *pixe, *pixb, *pixm; -PIXCMAP *cmap; - - PROCNAME("pixColorsForQuantization"); - - if (piscolor) *piscolor = 0; - if (!pncolors) - return ERROR_INT("&ncolors not defined", procName, 1); - *pncolors = 0; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if ((cmap = pixGetColormap(pixs)) != NULL) { - *pncolors = pixcmapGetCount(cmap); - if (piscolor) - pixcmapHasColor(cmap, piscolor); - return 0; - } - - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8 && d != 32) - return ERROR_INT("pixs not 8 or 32 bpp", procName, 1); - if (thresh <= 0) - thresh = 15; - - /* First test if 32 bpp has any significant color; if not, - * convert it to gray. Colors whose average values are within - * 20 of black or 8 of white are ignored because they're not - * very 'colorful'. If less than 2.5/10000 of the pixels have - * significant color, consider the image to be gray. */ - minside = L_MIN(w, h); - if (d == 8) { - pixt = pixClone(pixs); - } else { /* d == 32 */ - factor = L_MAX(1, minside / 400); - pixColorFraction(pixs, 20, 248, 30, factor, &pixfract, &colorfract); - if (pixfract * colorfract < 0.00025) { - pixt = pixGetRGBComponent(pixs, COLOR_RED); - d = 8; - } else { /* d == 32 */ - pixt = pixClone(pixs); - if (piscolor) - *piscolor = 1; - } - } - - /* If the smallest side is less than 1000, do not downscale. - * If it is in [1000 ... 2000), downscale by 2x. If it is >= 2000, - * downscale by 4x. Factors of 2 are chosen for speed. The - * actual resolution at which subsequent calculations take place - * is not strongly dependent on downscaling. */ - factor = L_MAX(1, minside / 500); - if (factor == 1) - pixsc = pixCopy(NULL, pixt); /* to be sure pixs is unchanged */ - else if (factor == 2 || factor == 3) - pixsc = pixScaleAreaMap2(pixt); - else - pixsc = pixScaleAreaMap(pixt, 0.25, 0.25); - - /* Basic edge mask generation procedure: - * ~ work on a grayscale image - * ~ get a 1 bpp edge mask by using an edge filter and - * thresholding to get fg pixels at the edges - * ~ for gray, dilate with a 3x3 brick Sel to get mask over - * all pixels within a distance of 1 pixel from the nearest - * edge pixel - * ~ for color, dilate with a 7x7 brick Sel to get mask over - * all pixels within a distance of 3 pixels from the nearest - * edge pixel */ - if (d == 8) - pixg = pixClone(pixsc); - else /* d == 32 */ - pixg = pixConvertRGBToLuminance(pixsc); - pixe = pixSobelEdgeFilter(pixg, L_ALL_EDGES); - pixb = pixThresholdToBinary(pixe, thresh); - pixInvert(pixb, pixb); - if (d == 8) - pixm = pixMorphSequence(pixb, "d3.3", 0); - else - pixm = pixMorphSequence(pixb, "d7.7", 0); - - /* Mask the near-edge pixels to white, and count the colors. - * If grayscale, don't count colors within 20 levels of - * black or white, and only count colors with a fraction - * of at least 1/10000 of the image pixels. - * If color, count the number of level 4 octcubes that - * contain at least 20 pixels. These magic numbers are guesses - * as to what might work, based on a small data set. Results - * should not be overly sensitive to their actual values. */ - if (d == 8) { - pixSetMasked(pixg, pixm, 0xff); - if (debug) pixWrite("junkpix8.png", pixg, IFF_PNG); - pixNumSignificantGrayColors(pixg, 20, 236, 0.0001, 1, pncolors); - } else { /* d == 32 */ - pixSetMasked(pixsc, pixm, 0xffffffff); - if (debug) pixWrite("junkpix32.png", pixsc, IFF_PNG); - pixNumberOccupiedOctcubes(pixsc, 4, 20, -1, pncolors); - } - - pixDestroy(&pixt); - pixDestroy(&pixsc); - pixDestroy(&pixg); - pixDestroy(&pixe); - pixDestroy(&pixb); - pixDestroy(&pixm); - return 0; -} - - -/* ----------------------------------------------------------------------- * - * Finds the number of unique colors in an image * - * ----------------------------------------------------------------------- */ -/*! - * \brief pixNumColors() - * \param[in] pixs 2, 4, 8, 32 bpp - * \param[in] factor subsampling factor; integer - * \param[out] pncolors the number of colors found, or 0 if - * there are more than 256 - * \return 0 if OK, 1 on error. - * - *
- * Notes: - * (1) This returns the number of colors found in the image, - * even if there is a colormap. If %factor == 1 and the - * number of colors differs from the number of entries - * in the colormap, a warning is issued. - * (2) Use %factor == 1 to find the actual number of colors. - * Use %factor > 1 to more efficiently find an approximate - * number of colors. - * (3) For d = 2, 4 or 8 bpp grayscale, this returns the number - * of colors found in the image in 'ncolors'. - * (4) For d = 32 bpp (rgb), if the number of colors is greater - * than 256, this uses an ordered set. - *- */ -l_ok -pixNumColors(PIX *pixs, - l_int32 factor, - l_int32 *pncolors) -{ -l_int32 w, h, d, i, j, wpl, hashsize, sum, count, manycolors; -l_int32 rval, gval, bval, val; -l_int32 *inta; -l_uint32 pixel; -l_uint32 *data, *line; -PIXCMAP *cmap; - - PROCNAME("pixNumColors"); - - if (!pncolors) - return ERROR_INT("&ncolors not defined", procName, 1); - *pncolors = 0; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 2 && d != 4 && d != 8 && d != 32) - return ERROR_INT("d not in {2, 4, 8, 32}", procName, 1); - if (factor < 1) factor = 1; - - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - sum = 0; - if (d != 32) { /* grayscale */ - inta = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - for (i = 0; i < h; i += factor) { - line = data + i * wpl; - for (j = 0; j < w; j += factor) { - if (d == 8) - val = GET_DATA_BYTE(line, j); - else if (d == 4) - val = GET_DATA_QBIT(line, j); - else /* d == 2 */ - val = GET_DATA_DIBIT(line, j); - inta[val] = 1; - } - } - for (i = 0; i < 256; i++) - if (inta[i]) sum++; - *pncolors = sum; - LEPT_FREE(inta); - - cmap = pixGetColormap(pixs); - if (cmap && factor == 1) { - count = pixcmapGetCount(cmap); - if (sum != count) - L_WARNING("colormap size %d differs from actual colors\n", - procName, count); - } - return 0; - } - - /* 32 bpp rgb; quit if we get above 256 colors */ - hashsize = 5507; /* big and prime; collisions are not likely */ - inta = (l_int32 *)LEPT_CALLOC(hashsize, sizeof(l_int32)); - manycolors = 0; - for (i = 0; i < h && manycolors == 0; i += factor) { - line = data + i * wpl; - for (j = 0; j < w; j += factor) { - pixel = line[j]; - extractRGBValues(pixel, &rval, &gval, &bval); - val = (137 * rval + 269 * gval + 353 * bval) % hashsize; - if (inta[val] == 0) { - inta[val] = 1; - sum++; - if (sum > 256) { - manycolors = 1; - break; - } - } - } - } - LEPT_FREE(inta); - - if (manycolors == 0) { - *pncolors = sum; - return 0; - } - - /* More than 256 colors in RGB image */ - return pixCountRGBColors(pixs, factor, pncolors); -} - - -/* ----------------------------------------------------------------------- * - * Lossless conversion of RGB image to colormapped * - * ----------------------------------------------------------------------- */ -/*! - * \brief pixConvertRGBToCmap() - * \param[in] pixs 32 bpp RGB - * \return pixd if num colors <= 256; null otherwise or on error - * - *
- * Notes: - * (1) If there are not more than 256 colors, this losslessly - * converts and RGB image to a colormapped one, with the - * smallest pixel depth required to hold all the colors. - *- */ -PIX * -pixConvertRGBToCmap(PIX *pixs) -{ -l_int32 w, h, d, i, j, wpls, wpld, hashsize, hashval, ncolors, index; -l_int32 rval, gval, bval, val; -l_int32 *hasha1, *hasha2; -l_uint32 pixel; -l_uint32 *datas, *lines, *datad, *lined; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixConvertRGBToCmap"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - pixNumColors(pixs, 1, &ncolors); - if (ncolors > 256) { - L_ERROR("too many colors found: %d\n", procName, ncolors); - return NULL; - } - - pixGetDimensions(pixs, &w, &h, NULL); - if (ncolors <= 2) - d = 1; - else if (ncolors <= 4) - d = 2; - else if (ncolors <= 16) - d = 4; - else /* ncolors <= 256 */ - d = 8; - - if ((pixd = pixCreate(w, h, d)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - cmap = pixcmapCreate(d); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - /* hasha1 is a 1/0 indicator array for colors seen. - hasha2 holds the index into the colormap that will be - generated from the colors in the order seen. This is - the value inserted into pixd. */ - hashsize = 5507; /* big and prime; collisions are not likely */ - hasha1 = (l_int32 *)LEPT_CALLOC(hashsize, sizeof(l_int32)); - hasha2 = (l_int32 *)LEPT_CALLOC(hashsize, sizeof(l_int32)); - index = -1; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - pixel = lines[j]; - extractRGBValues(pixel, &rval, &gval, &bval); - hashval = (137 * rval + 269 * gval + 353 * bval) % hashsize; - if (hasha1[hashval] == 0) { /* new color */ - hasha1[hashval] = 1; - index++; - hasha2[hashval] = index; - pixcmapAddColor(cmap, rval, gval, bval); - } - val = hasha2[hashval]; - setLineDataVal(lined, j, d, val); - } - } - pixSetColormap(pixd, cmap); - - LEPT_FREE(hasha1); - LEPT_FREE(hasha2); - return pixd; -} - - -/* ----------------------------------------------------------------------- * - * Find the most "populated" colors in the image (and quantize) * - * ----------------------------------------------------------------------- */ -/*! - * \brief pixGetMostPopulatedColors() - * \param[in] pixs 32 bpp rgb - * \param[in] sigbits 2-6, significant bits retained in the quantizer - * for each component of the input image - * \param[in] factor subsampling factor; use 1 for no subsampling - * \param[in] ncolors the number of most populated colors to select - * \param[out] parray [optional] array of colors, each as 0xrrggbb00 - * \param[out] pcmap [optional] colormap of the colors - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This finds the %ncolors most populated cubes in rgb colorspace, - * where the cube size depends on %sigbits as - * cube side = (256 >> sigbits) - * (2) The rgb color components are found at the center of the cube. - * (3) The output array of colors can be displayed using - * pixDisplayColorArray(array, ncolors, ...); - *- */ -l_ok -pixGetMostPopulatedColors(PIX *pixs, - l_int32 sigbits, - l_int32 factor, - l_int32 ncolors, - l_uint32 **parray, - PIXCMAP **pcmap) -{ -l_int32 n, i, rgbindex, rval, gval, bval; -NUMA *nahisto, *naindex; - - PROCNAME("pixGetMostPopulatedColors"); - - if (!parray && !pcmap) - return ERROR_INT("no return val requested", procName, 1); - if (parray) *parray = NULL; - if (pcmap) *pcmap = NULL; - if (!pixs || pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not defined", procName, 1); - if (sigbits < 2 || sigbits > 6) - return ERROR_INT("sigbits not in [2 ... 6]", procName, 1); - if (factor < 1 || ncolors < 1) - return ERROR_INT("factor < 1 or ncolors < 1", procName, 1); - - if ((nahisto = pixGetRGBHistogram(pixs, sigbits, factor)) == NULL) - return ERROR_INT("nahisto not made", procName, 1); - - /* naindex contains the index into nahisto, which is the rgbindex */ - naindex = numaSortIndexAutoSelect(nahisto, L_SORT_DECREASING); - numaDestroy(&nahisto); - if (!naindex) - return ERROR_INT("naindex not made", procName, 1); - - n = numaGetCount(naindex); - ncolors = L_MIN(n, ncolors); - if (parray) *parray = (l_uint32 *)LEPT_CALLOC(ncolors, sizeof(l_uint32)); - if (pcmap) *pcmap = pixcmapCreate(8); - for (i = 0; i < ncolors; i++) { - numaGetIValue(naindex, i, &rgbindex); /* rgb index */ - getRGBFromIndex(rgbindex, sigbits, &rval, &gval, &bval); - if (parray) composeRGBPixel(rval, gval, bval, *parray + i); - if (pcmap) pixcmapAddColor(*pcmap, rval, gval, bval); - } - - numaDestroy(&naindex); - return 0; -} - - -/*! - * \brief pixSimpleColorQuantize() - * \param[in] pixs 32 bpp rgb - * \param[in] sigbits 2-4, significant bits retained in the quantizer - * for each component of the input image - * \param[in] factor subsampling factor; use 1 for no subsampling - * \param[in] ncolors the number of most populated colors to select - * \return pixd 8 bpp cmapped or NULL on error - * - *
- * Notes: - * (1) If you want to do color quantization for real, use octcube - * or modified median cut. This function shows that it is - * easy to make a simple quantizer based solely on the population - * in cells of a given size in rgb color space. - * (2) The %ncolors most populated cells at the %sigbits level form - * the colormap for quantizing, and this uses octcube indexing - * under the covers to assign each pixel to the nearest color. - * (3) %sigbits is restricted to 2, 3 and 4. At the low end, the - * color discrimination is very crude; at the upper end, a set of - * similar colors can dominate the result. Interesting results - * are generally found for %sigbits = 3 and ncolors ~ 20. - * (4) See also pixColorSegment() for a method of quantizing the - * colors to generate regions of similar color. - * (5) See also pixConvertRGBToCmap() to losslessly convert an - * RGB image with not more than 256 colors. - *- */ -PIX * -pixSimpleColorQuantize(PIX *pixs, - l_int32 sigbits, - l_int32 factor, - l_int32 ncolors) -{ -l_int32 w, h; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixSimpleColorQuantize"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (sigbits < 2 || sigbits > 4) - return (PIX *)ERROR_PTR("sigbits not in {2,3,4}", procName, NULL); - - pixGetMostPopulatedColors(pixs, sigbits, factor, ncolors, NULL, &cmap); - pixGetDimensions(pixs, &w, &h, NULL); - pixd = pixCreate(w, h, 8); - pixSetColormap(pixd, cmap); - pixAssignToNearestColor(pixd, pixs, NULL, 4, NULL); - return pixd; -} - - -/* ----------------------------------------------------------------------- * - * Constructs a color histogram based on rgb indices * - * ----------------------------------------------------------------------- */ -/*! - * \brief pixGetRGBHistogram() - * \param[in] pixs 32 bpp rgb - * \param[in] sigbits 2-6, significant bits retained in the quantizer - * for each component of the input image - * \param[in] factor subsampling factor; use 1 for no subsampling - * \return numa histogram of colors, indexed by RGB - * components, or NULL on error - * - *
- * Notes: - * (1) This uses a simple, fast method of indexing into an rgb image. - * (2) The output is a 1D histogram of count vs. rgb-index, which - * uses red sigbits as the most significant and blue as the least. - * (3) This function produces the same result as pixMedianCutHisto(). - *- */ -NUMA * -pixGetRGBHistogram(PIX *pixs, - l_int32 sigbits, - l_int32 factor) -{ -l_int32 w, h, i, j, size, wpl, rval, gval, bval, npts; -l_uint32 val32, rgbindex; -l_float32 *array; -l_uint32 *data, *line, *rtab, *gtab, *btab; -NUMA *na; - - PROCNAME("pixGetRGBHistogram"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); - if (sigbits < 2 || sigbits > 6) - return (NUMA *)ERROR_PTR("sigbits not in [2 ... 6]", procName, NULL); - if (factor < 1) - return (NUMA *)ERROR_PTR("factor < 1", procName, NULL); - - /* Get histogram size: 2^(3 * sigbits) */ - size = 1 << (3 * sigbits); /* 64, 512, 4096, 32768, 262144 */ - na = numaMakeConstant(0, size); /* init to all 0 */ - array = numaGetFArray(na, L_NOCOPY); - - makeRGBIndexTables(&rtab, >ab, &btab, sigbits); - - /* Check the number of sampled pixels */ - pixGetDimensions(pixs, &w, &h, NULL); - npts = ((w + factor - 1) / factor) * ((h + factor - 1) / factor); - if (npts < 1000) - L_WARNING("only sampling %d pixels\n", procName, npts); - wpl = pixGetWpl(pixs); - data = pixGetData(pixs); - for (i = 0; i < h; i += factor) { - line = data + i * wpl; - for (j = 0; j < w; j += factor) { - val32 = *(line + j); - extractRGBValues(val32, &rval, &gval, &bval); - rgbindex = rtab[rval] | gtab[gval] | btab[bval]; - array[rgbindex]++; - } - } - - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - return na; -} - - -/*! - * \brief makeRGBIndexTables() - * - * \param[out] prtab, pgtab, pbtab 256-entry rgb index tables - * \param[in] sigbits 2-6, significant bits retained in the quantizer - * for each component of the input image - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) These tables are used to map from rgb sample values to - * an rgb index, using - * rgbindex = rtab[rval] | gtab[gval] | btab[bval] - * where, e.g., if sigbits = 3, the index is a 9 bit integer: - * r7 r6 r5 g7 g6 g5 b7 b6 b5 - *- */ -l_ok -makeRGBIndexTables(l_uint32 **prtab, - l_uint32 **pgtab, - l_uint32 **pbtab, - l_int32 sigbits) -{ -l_int32 i; -l_uint32 *rtab, *gtab, *btab; - - PROCNAME("makeRGBIndexTables"); - - if (prtab) *prtab = NULL; - if (pgtab) *pgtab = NULL; - if (pbtab) *pbtab = NULL; - if (!prtab || !pgtab || !pbtab) - return ERROR_INT("not all table ptrs defined", procName, 1); - if (sigbits < 2 || sigbits > 6) - return ERROR_INT("sigbits not in [2 ... 6]", procName, 1); - - rtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); - gtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); - btab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); - if (!rtab || !gtab || !btab) - return ERROR_INT("calloc fail for tab", procName, 1); - *prtab = rtab; - *pgtab = gtab; - *pbtab = btab; - switch (sigbits) { - case 2: - for (i = 0; i < 256; i++) { - rtab[i] = (i & 0xc0) >> 2; - gtab[i] = (i & 0xc0) >> 4; - btab[i] = (i & 0xc0) >> 6; - } - break; - case 3: - for (i = 0; i < 256; i++) { - rtab[i] = (i & 0xe0) << 1; - gtab[i] = (i & 0xe0) >> 2; - btab[i] = (i & 0xe0) >> 5; - } - break; - case 4: - for (i = 0; i < 256; i++) { - rtab[i] = (i & 0xf0) << 4; - gtab[i] = (i & 0xf0); - btab[i] = (i & 0xf0) >> 4; - } - break; - case 5: - for (i = 0; i < 256; i++) { - rtab[i] = (i & 0xf8) << 7; - gtab[i] = (i & 0xf8) << 2; - btab[i] = (i & 0xf8) >> 3; - } - break; - case 6: - for (i = 0; i < 256; i++) { - rtab[i] = (i & 0xfc) << 10; - gtab[i] = (i & 0xfc) << 4; - btab[i] = (i & 0xfc) >> 2; - } - break; - default: - L_ERROR("Illegal sigbits = %d\n", procName, sigbits); - return ERROR_INT("sigbits not in [2 ... 6]", procName, 1); - } - - return 0; -} - - -/*! - * \brief getRGBFromIndex() - * - * \param[in] index rgbindex - * \param[in] sigbits 2-6, significant bits retained in the quantizer - * for each component of the input image - * \param[out] prval, pgval, pbval rgb values - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The %index is expressed in bits, based on the the - * %sigbits of the r, g and b components, as - * r7 r6 ... g7 g6 ... b7 b6 ... - * (2) The computed rgb values are in the center of the quantized cube. - * The extra bit that is OR'd accomplishes this. - *- */ -l_ok -getRGBFromIndex(l_uint32 index, - l_int32 sigbits, - l_int32 *prval, - l_int32 *pgval, - l_int32 *pbval) -{ - PROCNAME("getRGBFromIndex"); - - if (prval) *prval = 0; - if (pgval) *pgval = 0; - if (pbval) *pbval = 0; - if (!prval || !pgval || !pbval) - return ERROR_INT("not all component ptrs defined", procName, 1); - if (sigbits < 2 || sigbits > 6) - return ERROR_INT("sigbits not in [2 ... 6]", procName, 1); - - switch (sigbits) { - case 2: - *prval = ((index << 2) & 0xc0) | 0x20; - *pgval = ((index << 4) & 0xc0) | 0x20; - *pbval = ((index << 6) & 0xc0) | 0x20; - break; - case 3: - *prval = ((index >> 1) & 0xe0) | 0x10; - *pgval = ((index << 2) & 0xe0) | 0x10; - *pbval = ((index << 5) & 0xe0) | 0x10; - break; - case 4: - *prval = ((index >> 4) & 0xf0) | 0x08; - *pgval = (index & 0xf0) | 0x08; - *pbval = ((index << 4) & 0xf0) | 0x08; - break; - case 5: - *prval = ((index >> 7) & 0xf8) | 0x04; - *pgval = ((index >> 2) & 0xf8) | 0x04; - *pbval = ((index << 3) & 0xf8) | 0x04; - break; - case 6: - *prval = ((index >> 10) & 0xfc) | 0x02; - *pgval = ((index >> 4) & 0xfc) | 0x02; - *pbval = ((index << 2) & 0xfc) | 0x02; - break; - default: - L_ERROR("Illegal sigbits = %d\n", procName, sigbits); - return ERROR_INT("sigbits not in [2 ... 6]", procName, 1); - } - - return 0; -} - - -/* ----------------------------------------------------------------------- * - * Identify images that have highlight (red) color * - * ----------------------------------------------------------------------- */ -/*! - * \brief pixHasHighlightRed() - * - * \param[in] pixs 32 bpp rgb - * \param[in] factor subsampling; an integer >= 1; use 1 for all pixels - * \param[in] fract threshold fraction of all image pixels - * \param[in] fthresh threshold on a function of the components; typ. ~2.5 - * \param[out] phasred 1 if red pixels are above threshold - * \param[out] pratio [optional] normalized fraction of threshold - * red pixels that is actually observed - * \param[out] ppixdb [optional] seed pixel mask - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Pixels are identified as red if they satisfy two conditions: - * (a) The components satisfy (R-B)/B > %fthresh (red or dark fg) - * (b) The red component satisfied R > 128 (red or light bg) - * Masks are generated for (a) and (b), and the intersection - * gives the pixels that are red but not either light bg or - * dark fg. - * (2) A typical value for fract = 0.0001, which gives sensitivity - * to an image where a small fraction of the pixels are printed - * in red. - * (3) A typical value for fthresh = 2.5. Higher values give less - * sensitivity to red, and fewer false positives. - *- */ -l_ok -pixHasHighlightRed(PIX *pixs, - l_int32 factor, - l_float32 fract, - l_float32 fthresh, - l_int32 *phasred, - l_float32 *pratio, - PIX **ppixdb) -{ -l_int32 w, h, count; -l_float32 ratio; -PIX *pix1, *pix2, *pix3, *pix4; -FPIX *fpix; - - PROCNAME("pixHasHighlightRed"); - - if (pratio) *pratio = 0.0; - if (ppixdb) *ppixdb = NULL; - if (phasred) *phasred = 0; - if (!pratio && !ppixdb) - return ERROR_INT("no return val requested", procName, 1); - if (!phasred) - return ERROR_INT("&hasred not defined", procName, 1); - if (!pixs || pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); - if (fthresh < 1.5 || fthresh > 3.5) - L_WARNING("fthresh = %f is out of normal bounds\n", procName, fthresh); - - if (factor > 1) - pix1 = pixScaleByIntSampling(pixs, factor); - else - pix1 = pixClone(pixs); - - /* Identify pixels that are either red or dark foreground */ - fpix = pixComponentFunction(pix1, 1.0, 0.0, -1.0, 0.0, 0.0, 1.0); - pix2 = fpixThresholdToPix(fpix, fthresh); - pixInvert(pix2, pix2); - - /* Identify pixels that are either red or light background */ - pix3 = pixGetRGBComponent(pix1, COLOR_RED); - pix4 = pixThresholdToBinary(pix3, 130); - pixInvert(pix4, pix4); - - pixAnd(pix4, pix4, pix2); - pixCountPixels(pix4, &count, NULL); - pixGetDimensions(pix4, &w, &h, NULL); - L_INFO("count = %d, thresh = %d\n", procName, count, - (l_int32)(fract * w * h)); - ratio = (l_float32)count / (fract * w * h); - if (pratio) *pratio = ratio; - if (ratio >= 1.0) - *phasred = 1; - if (ppixdb) - *ppixdb = pix4; - else - pixDestroy(&pix4); - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - fpixDestroy(&fpix); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/coloring.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/coloring.c deleted file mode 100644 index 7624d3d5..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/coloring.c +++ /dev/null @@ -1,1049 +0,0 @@ -/*====================================================================* - - 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 coloring.c - *
- * - * Coloring "gray" pixels - * PIX *pixColorGrayRegions() - * l_int32 pixColorGray() - * PIX *pixColorGrayMasked() - * - * Adjusting one or more colors to a target color - * PIX *pixSnapColor() - * PIX *pixSnapColorCmap() - * - * Piecewise linear color mapping based on a source/target pair - * PIX *pixLinearMapToTargetColor() - * l_int32 pixelLinearMapToTargetColor() - * - * Fractional shift of RGB towards black or white - * PIX *pixShiftByComponent() - * l_int32 pixelShiftByComponent() - * l_int32 pixelFractionalShift() - * - * There are several "coloring" functions in leptonica. - * You can find them in these files: - * coloring.c - * paintcmap.c - * pix2.c - * blend.c - * enhance.c - * - * They fall into the following categories: - * - * (1) Moving either the light or dark pixels toward a - * specified color. (pixColorGray, pixColorGrayMasked) - * (2) Forcing all pixels whose color is within some delta of a - * specified color to move to that color. (pixSnapColor) - * (3) Doing a piecewise linear color shift specified by a source - * and a target color. Each component shifts independently. - * (pixLinearMapToTargetColor) - * (4) Shifting all colors by a given fraction of their distance - * from 0 (if shifting down) or from 255 (if shifting up). - * This is useful for colorizing either the background or - * the foreground of a grayscale image. (pixShiftByComponent) - * (5) Shifting all colors by a component-dependent fraction of - * their distance from 0 (if shifting down) or from 255 (if - * shifting up). This is useful for modifying the color to - * compensate for color shifts in acquisition or printing. - * (enhance.c: pixColorShiftRGB, pixMosaicColorShiftRGB). - * (6) Repainting selected pixels. (paintcmap.c: pixSetSelectMaskedCmap) - * (7) Blending a fraction of a specific color with the existing RGB - * color. (pix2.c: pixBlendInRect()) - * (8) Changing selected colors in a colormap. - * (paintcmap.c: pixSetSelectCmap, pixSetSelectMaskedCmap) - * (9) Shifting all the pixels towards black or white depending on - * the gray value of a second image. (blend.c: pixFadeWithGray) - * (10) Changing the hue, saturation or brightness, by changing the - * appropriate parameter in HSV color space by a fraction of - * the distance toward its end-point. For example, you can change - * the brightness by moving each pixel's v-parameter a specified - * fraction of the distance toward 0 (darkening) or toward 255 - * (brightening). (enhance.c: pixModifySaturation, - * pixModifyHue, pixModifyBrightness) - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This generates a new image, where some of the pixels in each - * box in the boxa are colorized. See pixColorGray() for usage - * with %type and %thresh. Note that %thresh is only used for - * rgb; it is ignored for colormapped images. - * (2) If the input image is colormapped, the new image will be 8 bpp - * colormapped if possible; otherwise, it will be converted - * to 32 bpp rgb. Only pixels that are strictly gray will be - * colorized. - * (3) If the input image is not colormapped, it is converted to rgb. - * A "gray" value for a pixel is determined by averaging the - * components, and the output rgb value is determined from this. - * (4) This can be used in conjunction with pixHasHighlightRed() to - * add highlight color to a grayscale image. - *- */ -PIX * -pixColorGrayRegions(PIX *pixs, - BOXA *boxa, - l_int32 type, - l_int32 thresh, - l_int32 rval, - l_int32 gval, - l_int32 bval) -{ -l_int32 i, n, ncolors, ngray; -BOX *box; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixColorGrayRegions"); - - if (!pixs || pixGetDepth(pixs) == 1) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (!boxa) - return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); - if (type != L_PAINT_LIGHT && type != L_PAINT_DARK) - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - - /* If cmapped and there is room in an 8 bpp colormap for - * expansion, convert pixs to 8 bpp, and colorize. */ - cmap = pixGetColormap(pixs); - if (cmap) { - ncolors = pixcmapGetCount(cmap); - pixcmapCountGrayColors(cmap, &ngray); - if (ncolors + ngray < 255) { - pixd = pixConvertTo8(pixs, 1); /* always new image */ - pixColorGrayRegionsCmap(pixd, boxa, type, rval, gval, bval); - return pixd; - } - } - - /* The output will be rgb. Make sure the thresholds are valid */ - if (type == L_PAINT_LIGHT) { /* thresh should be low */ - if (thresh >= 255) - return (PIX *)ERROR_PTR("thresh must be < 255", procName, NULL); - if (thresh > 127) - L_WARNING("threshold set very high\n", procName); - } else { /* type == L_PAINT_DARK; thresh should be high */ - if (thresh <= 0) - return (PIX *)ERROR_PTR("thresh must be > 0", procName, NULL); - if (thresh < 128) - L_WARNING("threshold set very low\n", procName); - } - - pixd = pixConvertTo32(pixs); /* always new image */ - n = boxaGetCount(boxa); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - pixColorGray(pixd, box, type, thresh, rval, gval, bval); - boxDestroy(&box); - } - - return pixd; -} - - -/*! - * \brief pixColorGray() - * - * \param[in] pixs 8 bpp gray, rgb or colormapped image - * \param[in] box [optional] region in which to apply color; can be NULL - * \param[in] type L_PAINT_LIGHT, L_PAINT_DARK - * \param[in] thresh average value below/above which pixel is unchanged - * \param[in] rval, gval, bval new color to paint - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This is an in-place operation; pixs is modified. - * If pixs is colormapped, the operation will add colors to the - * colormap. Otherwise, pixs will be converted to 32 bpp rgb if - * it is initially 8 bpp gray. - * (2) If type == L_PAINT_LIGHT, it colorizes non-black pixels, - * preserving antialiasing. - * If type == L_PAINT_DARK, it colorizes non-white pixels, - * preserving antialiasing. - * (3) If box is NULL, applies function to the entire image; otherwise, - * clips the operation to the intersection of the box and pix. - * (4) If colormapped, calls pixColorGrayCmap(), which applies the - * coloring algorithm only to pixels that are strictly gray. - * (5) For RGB, determines a "gray" value by averaging; then uses this - * value, plus the input rgb target, to generate the output - * pixel values. - * (6) thresh is only used for rgb; it is ignored for colormapped pix. - * If type == L_PAINT_LIGHT, use thresh = 0 if all pixels are to - * be colored (black pixels will be unaltered). - * In situations where there are a lot of black pixels, - * setting thresh > 0 will make the function considerably - * more efficient without affecting the final result. - * If type == L_PAINT_DARK, use thresh = 255 if all pixels - * are to be colored (white pixels will be unaltered). - * In situations where there are a lot of white pixels, - * setting thresh < 255 will make the function considerably - * more efficient without affecting the final result. - *- */ -l_ok -pixColorGray(PIX *pixs, - BOX *box, - l_int32 type, - l_int32 thresh, - l_int32 rval, - l_int32 gval, - l_int32 bval) -{ -l_int32 i, j, w, h, d, wpl, x1, x2, y1, y2, bw, bh; -l_int32 nrval, ngval, nbval, aveval; -l_float32 factor; -l_uint32 val32; -l_uint32 *line, *data; -PIX *pixt; -PIXCMAP *cmap; - - PROCNAME("pixColorGray"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (type != L_PAINT_LIGHT && type != L_PAINT_DARK) - return ERROR_INT("invalid type", procName, 1); - - cmap = pixGetColormap(pixs); - pixGetDimensions(pixs, &w, &h, &d); - if (!cmap && d != 8 && d != 32) - return ERROR_INT("pixs not cmapped, 8 bpp or rgb", procName, 1); - if (cmap) - return pixColorGrayCmap(pixs, box, type, rval, gval, bval); - - /* rgb or 8 bpp gray image; check the thresh */ - if (type == L_PAINT_LIGHT) { /* thresh should be low */ - if (thresh >= 255) - return ERROR_INT("thresh must be < 255; else this is a no-op", - procName, 1); - if (thresh > 127) - L_WARNING("threshold set very high\n", procName); - } else { /* type == L_PAINT_DARK; thresh should be high */ - if (thresh <= 0) - return ERROR_INT("thresh must be > 0; else this is a no-op", - procName, 1); - if (thresh < 128) - L_WARNING("threshold set very low\n", procName); - } - - /* In-place conversion to 32 bpp if necessary */ - if (d == 8) { - pixt = pixConvertTo32(pixs); - pixTransferAllData(pixs, &pixt, 1, 0); - } - - if (!box) { - x1 = y1 = 0; - x2 = w; - y2 = h; - } else { - boxGetGeometry(box, &x1, &y1, &bw, &bh); - x2 = x1 + bw - 1; - y2 = y1 + bh - 1; - } - - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - factor = 1. / 255.; - for (i = y1; i <= y2; i++) { - if (i < 0 || i >= h) - continue; - line = data + i * wpl; - for (j = x1; j <= x2; j++) { - if (j < 0 || j >= w) - continue; - val32 = *(line + j); - aveval = ((val32 >> 24) + ((val32 >> 16) & 0xff) + - ((val32 >> 8) & 0xff)) / 3; - if (type == L_PAINT_LIGHT) { - if (aveval < thresh) /* skip sufficiently dark pixels */ - continue; - nrval = (l_int32)(rval * aveval * factor); - ngval = (l_int32)(gval * aveval * factor); - nbval = (l_int32)(bval * aveval * factor); - } else { /* type == L_PAINT_DARK */ - if (aveval > thresh) /* skip sufficiently light pixels */ - continue; - nrval = rval + (l_int32)((255. - rval) * aveval * factor); - ngval = gval + (l_int32)((255. - gval) * aveval * factor); - nbval = bval + (l_int32)((255. - bval) * aveval * factor); - } - composeRGBPixel(nrval, ngval, nbval, &val32); - *(line + j) = val32; - } - } - - return 0; -} - - -/*! - * \brief pixColorGrayMasked() - * - * \param[in] pixs 8 bpp gray, rgb or colormapped image - * \param[in] pixm 1 bpp mask, through which to apply color - * \param[in] type L_PAINT_LIGHT, L_PAINT_DARK - * \param[in] thresh average value below/above which pixel is unchanged - * \param[in] rval, gval, bval new color to paint - * \return pixd colorized, or NULL on error - * - *
- * Notes: - * (1) This generates a new image, where some of the pixels under - * FG in the mask are colorized. - * (2) See pixColorGray() for usage with %type and %thresh. Note - * that %thresh is only used for rgb; it is ignored for - * colormapped images. In most cases, the mask will be over - * the darker parts and %type == L_PAINT_DARK. - * (3) If pixs is colormapped this calls pixColorMaskedCmap(), - * which adds colors to the colormap for pixd; it only adds - * colors corresponding to strictly gray colors in the colormap. - * Otherwise, if pixs is 8 bpp gray, pixd will be 32 bpp rgb. - * (4) If pixs is 32 bpp rgb, for each pixel a "gray" value is - * found by averaging. This average is then used with the - * input rgb target to generate the output pixel values. - * (5) This can be used in conjunction with pixHasHighlightRed() to - * add highlight color to a grayscale image. - *- */ -PIX * -pixColorGrayMasked(PIX *pixs, - PIX *pixm, - l_int32 type, - l_int32 thresh, - l_int32 rval, - l_int32 gval, - l_int32 bval) -{ -l_int32 i, j, w, h, d, wm, hm, wmin, hmin, wpl, wplm; -l_int32 nrval, ngval, nbval, aveval; -l_float32 factor; -l_uint32 val32; -l_uint32 *line, *data, *linem, *datam; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixColorGrayMasked"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!pixm || pixGetDepth(pixm) != 1) - return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", procName, NULL); - if (type != L_PAINT_LIGHT && type != L_PAINT_DARK) - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - - cmap = pixGetColormap(pixs); - pixGetDimensions(pixs, &w, &h, &d); - if (!cmap && d != 8 && d != 32) - return (PIX *)ERROR_PTR("pixs not cmapped, 8 bpp gray or 32 bpp", - procName, NULL); - if (cmap) { - pixd = pixCopy(NULL, pixs); - pixColorGrayMaskedCmap(pixd, pixm, type, rval, gval, bval); - return pixd; - } - - /* rgb or 8 bpp gray image; check the thresh */ - if (type == L_PAINT_LIGHT) { /* thresh should be low */ - if (thresh >= 255) - return (PIX *)ERROR_PTR( - "thresh must be < 255; else this is a no-op", procName, NULL); - if (thresh > 127) - L_WARNING("threshold set very high\n", procName); - } else { /* type == L_PAINT_DARK; thresh should be high */ - if (thresh <= 0) - return (PIX *)ERROR_PTR( - "thresh must be > 0; else this is a no-op", procName, NULL); - if (thresh < 128) - L_WARNING("threshold set very low\n", procName); - } - - pixGetDimensions(pixm, &wm, &hm, NULL); - if (wm != w) - L_WARNING("wm = %d differs from w = %d\n", procName, wm, w); - if (hm != h) - L_WARNING("hm = %d differs from h = %d\n", procName, hm, h); - wmin = L_MIN(w, wm); - hmin = L_MIN(h, hm); - if (d == 8) - pixd = pixConvertTo32(pixs); - else - pixd = pixCopy(NULL, pixs); - - data = pixGetData(pixd); - wpl = pixGetWpl(pixd); - datam = pixGetData(pixm); - wplm = pixGetWpl(pixm); - factor = 1. / 255.; - for (i = 0; i < hmin; i++) { - line = data + i * wpl; - linem = datam + i * wplm; - for (j = 0; j < wmin; j++) { - if (GET_DATA_BIT(linem, j) == 0) - continue; - val32 = *(line + j); - aveval = ((val32 >> 24) + ((val32 >> 16) & 0xff) + - ((val32 >> 8) & 0xff)) / 3; - if (type == L_PAINT_LIGHT) { - if (aveval < thresh) /* skip sufficiently dark pixels */ - continue; - nrval = (l_int32)(rval * aveval * factor); - ngval = (l_int32)(gval * aveval * factor); - nbval = (l_int32)(bval * aveval * factor); - } else { /* type == L_PAINT_DARK */ - if (aveval > thresh) /* skip sufficiently light pixels */ - continue; - nrval = rval + (l_int32)((255. - rval) * aveval * factor); - ngval = gval + (l_int32)((255. - gval) * aveval * factor); - nbval = bval + (l_int32)((255. - bval) * aveval * factor); - } - composeRGBPixel(nrval, ngval, nbval, &val32); - *(line + j) = val32; - } - } - - return pixd; -} - - -/*------------------------------------------------------------------* - * Adjusting one or more colors to a target color * - *------------------------------------------------------------------*/ -/*! - * \brief pixSnapColor() - * - * \param[in] pixd [optional]; either NULL or equal to pixs for in-place - * \param[in] pixs colormapped or 8 bpp gray or 32 bpp rgb - * \param[in] srcval color center to be selected for change: 0xrrggbb00 - * \param[in] dstval target color for pixels: 0xrrggbb00 - * \param[in] diff max absolute difference, applied to all components - * \return pixd with all pixels within diff of pixval set to pixval, - * or pixd on error - * - *
- * Notes: - * (1) For inplace operation, call it this way: - * pixSnapColor(pixs, pixs, ... ) - * (2) For generating a new pixd: - * pixd = pixSnapColor(NULL, pixs, ...) - * (3) If pixs has a colormap, it is handled by pixSnapColorCmap(). - * (4) All pixels within 'diff' of 'srcval', componentwise, - * will be changed to 'dstval'. - *- */ -PIX * -pixSnapColor(PIX *pixd, - PIX *pixs, - l_uint32 srcval, - l_uint32 dstval, - l_int32 diff) -{ -l_int32 val, sval, dval; -l_int32 rval, gval, bval, rsval, gsval, bsval; -l_int32 i, j, w, h, d, wpl; -l_uint32 pixel; -l_uint32 *line, *data; - - PROCNAME("pixSnapColor"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixd && (pixd != pixs)) - return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd); - - if (pixGetColormap(pixs)) - return pixSnapColorCmap(pixd, pixs, srcval, dstval, diff); - - /* pixs does not have a colormap; it must be 8 bpp gray or - * 32 bpp rgb. */ - if (pixGetDepth(pixs) < 8) - return (PIX *)ERROR_PTR("pixs is < 8 bpp", procName, pixd); - - /* Do the work on pixd */ - if (!pixd) - pixd = pixCopy(NULL, pixs); - - pixGetDimensions(pixd, &w, &h, &d); - data = pixGetData(pixd); - wpl = pixGetWpl(pixd); - if (d == 8) { - sval = srcval & 0xff; - dval = dstval & 0xff; - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(line, j); - if (L_ABS(val - sval) <= diff) - SET_DATA_BYTE(line, j, dval); - } - } - } else { /* d == 32 */ - extractRGBValues(srcval, &rsval, &gsval, &bsval); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - pixel = *(line + j); - extractRGBValues(pixel, &rval, &gval, &bval); - if ((L_ABS(rval - rsval) <= diff) && - (L_ABS(gval - gsval) <= diff) && - (L_ABS(bval - bsval) <= diff)) - *(line + j) = dstval; /* replace */ - } - } - } - - return pixd; -} - - -/*! - * \brief pixSnapColorCmap() - * - * \param[in] pixd [optional]; either NULL or equal to pixs for in-place - * \param[in] pixs colormapped - * \param[in] srcval color center to be selected for change: 0xrrggbb00 - * \param[in] dstval target color for pixels: 0xrrggbb00 - * \param[in] diff max absolute difference, applied to all components - * \return pixd with all pixels within diff of srcval set to dstval, - * or pixd on error - * - *
- * Notes: - * (1) For inplace operation, call it this way: - * pixSnapCcmap(pixs, pixs, ... ) - * (2) For generating a new pixd: - * pixd = pixSnapCmap(NULL, pixs, ...) - * (3) pixs must have a colormap. - * (4) All colors within 'diff' of 'srcval', componentwise, - * will be changed to 'dstval'. - *- */ -PIX * -pixSnapColorCmap(PIX *pixd, - PIX *pixs, - l_uint32 srcval, - l_uint32 dstval, - l_int32 diff) -{ -l_int32 i, ncolors, index, found; -l_int32 rval, gval, bval, rsval, gsval, bsval, rdval, gdval, bdval; -l_int32 *tab; -PIX *pixm; -PIXCMAP *cmap; - - PROCNAME("pixSnapColorCmap"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (!pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("cmap not found", procName, pixd); - if (pixd && (pixd != pixs)) - return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd); - - if (!pixd) - pixd = pixCopy(NULL, pixs); - - /* If no free colors, look for one close to the target - * that can be commandeered. */ - cmap = pixGetColormap(pixd); - ncolors = pixcmapGetCount(cmap); - extractRGBValues(srcval, &rsval, &gsval, &bsval); - extractRGBValues(dstval, &rdval, &gdval, &bdval); - found = FALSE; - if (pixcmapGetFreeCount(cmap) == 0) { - for (i = 0; i < ncolors; i++) { - pixcmapGetColor(cmap, i, &rval, &gval, &bval); - if ((L_ABS(rval - rsval) <= diff) && - (L_ABS(gval - gsval) <= diff) && - (L_ABS(bval - bsval) <= diff)) { - index = i; - pixcmapResetColor(cmap, index, rdval, gdval, bdval); - found = TRUE; - break; - } - } - } else { /* just add the new color */ - pixcmapAddColor(cmap, rdval, gdval, bdval); - ncolors = pixcmapGetCount(cmap); - index = ncolors - 1; /* index of new destination color */ - found = TRUE; - } - - if (!found) { - L_INFO("nothing to do\n", procName); - return pixd; - } - - /* For each color in cmap that is close enough to srcval, - * set the tab value to 1. Then generate a 1 bpp mask with - * fg pixels for every pixel in pixd that is close enough - * to srcval (i.e., has value 1 in tab). */ - if ((tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32))) == NULL) - return (PIX *)ERROR_PTR("tab not made", procName, pixd); - for (i = 0; i < ncolors; i++) { - pixcmapGetColor(cmap, i, &rval, &gval, &bval); - if ((L_ABS(rval - rsval) <= diff) && - (L_ABS(gval - gsval) <= diff) && - (L_ABS(bval - bsval) <= diff)) - tab[i] = 1; - } - pixm = pixMakeMaskFromLUT(pixd, tab); - LEPT_FREE(tab); - - /* Use the binary mask to set all selected pixels to - * the dest color index. */ - pixSetMasked(pixd, pixm, dstval); - pixDestroy(&pixm); - - /* Remove all unused colors from the colormap. */ - pixRemoveUnusedColors(pixd); - - return pixd; -} - - -/*---------------------------------------------------------------------* - * Piecewise linear color mapping based on a source/target pair * - *---------------------------------------------------------------------*/ -/*! - * \brief pixLinearMapToTargetColor() - * - * \param[in] pixd [optional]; either NULL or equal to pixs for in-place - * \param[in] pixs 32 bpp rgb - * \param[in] srcval source color: 0xrrggbb00 - * \param[in] dstval target color: 0xrrggbb00 - * \return pixd with all pixels mapped based on the srcval/destval mapping, - * or pixd on error - * - *
- * Notes: - * (1) For each component (r, b, g) separately, this does a piecewise - * linear mapping of the colors in pixs to colors in pixd. - * If rs and rd are the red src and dest components in %srcval and - * %dstval, then the range [0 ... rs] in pixs is mapped to - * [0 ... rd] in pixd. Likewise, the range [rs ... 255] in pixs - * is mapped to [rd ... 255] in pixd. And similarly for green - * and blue. - * (2) The mapping will in general change the hue of the pixels. - * However, if the src and dst targets are related by - * a transformation given by pixelFractionalShift(), the hue - * is invariant. - * (3) For inplace operation, call it this way: - * pixLinearMapToTargetColor(pixs, pixs, ... ) - * (4) For generating a new pixd: - * pixd = pixLinearMapToTargetColor(NULL, pixs, ...) - *- */ -PIX * -pixLinearMapToTargetColor(PIX *pixd, - PIX *pixs, - l_uint32 srcval, - l_uint32 dstval) -{ -l_int32 i, j, w, h, wpl; -l_int32 rval, gval, bval, rsval, gsval, bsval, rdval, gdval, bdval; -l_int32 *rtab, *gtab, *btab; -l_uint32 pixel; -l_uint32 *line, *data; - - PROCNAME("pixLinearMapToTargetColor"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixd && (pixd != pixs)) - return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs is not 32 bpp", procName, pixd); - - /* Do the work on pixd */ - if (!pixd) - pixd = pixCopy(NULL, pixs); - - extractRGBValues(srcval, &rsval, &gsval, &bsval); - extractRGBValues(dstval, &rdval, &gdval, &bdval); - rsval = L_MIN(254, L_MAX(1, rsval)); - gsval = L_MIN(254, L_MAX(1, gsval)); - bsval = L_MIN(254, L_MAX(1, bsval)); - rtab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - gtab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - btab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - if (!rtab || !gtab || !btab) - return (PIX *)ERROR_PTR("calloc fail for tab", procName, pixd); - for (i = 0; i < 256; i++) { - if (i <= rsval) - rtab[i] = (i * rdval) / rsval; - else - rtab[i] = rdval + ((255 - rdval) * (i - rsval)) / (255 - rsval); - if (i <= gsval) - gtab[i] = (i * gdval) / gsval; - else - gtab[i] = gdval + ((255 - gdval) * (i - gsval)) / (255 - gsval); - if (i <= bsval) - btab[i] = (i * bdval) / bsval; - else - btab[i] = bdval + ((255 - bdval) * (i - bsval)) / (255 - bsval); - } - pixGetDimensions(pixd, &w, &h, NULL); - data = pixGetData(pixd); - wpl = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - pixel = line[j]; - extractRGBValues(pixel, &rval, &gval, &bval); - composeRGBPixel(rtab[rval], gtab[gval], btab[bval], &pixel); - line[j] = pixel; - } - } - - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - return pixd; -} - - -/*! - * \brief pixelLinearMapToTargetColor() - * - * \param[in] scolor rgb source color: 0xrrggbb00 - * \param[in] srcmap source mapping color: 0xrrggbb00 - * \param[in] dstmap target mapping color: 0xrrggbb00 - * \param[out] pdcolor rgb dest color: 0xrrggbb00 - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This does this does a piecewise linear mapping of each - * component of %scolor to %dcolor, based on the relation - * between the components of %srcmap and %dstmap. It is the - * same transformation, performed on a single color, as mapped - * on every pixel in a pix by pixLinearMapToTargetColor(). - * (2) For each component, if the sval is larger than the smap, - * the dval will be pushed up from dmap towards white. - * Otherwise, dval will be pushed down from dmap towards black. - * This is because you can visualize the transformation as - * a linear stretching where smap moves to dmap, and everything - * else follows linearly with 0 and 255 fixed. - * (3) The mapping will in general change the hue of %scolor. - * However, if the %srcmap and %dstmap targets are related by - * a transformation given by pixelFractionalShift(), the hue - * will be invariant. - *- */ -l_ok -pixelLinearMapToTargetColor(l_uint32 scolor, - l_uint32 srcmap, - l_uint32 dstmap, - l_uint32 *pdcolor) -{ -l_int32 srval, sgval, sbval, drval, dgval, dbval; -l_int32 srmap, sgmap, sbmap, drmap, dgmap, dbmap; - - PROCNAME("pixelLinearMapToTargetColor"); - - if (!pdcolor) - return ERROR_INT("&dcolor not defined", procName, 1); - *pdcolor = 0; - - extractRGBValues(scolor, &srval, &sgval, &sbval); - extractRGBValues(srcmap, &srmap, &sgmap, &sbmap); - extractRGBValues(dstmap, &drmap, &dgmap, &dbmap); - srmap = L_MIN(254, L_MAX(1, srmap)); - sgmap = L_MIN(254, L_MAX(1, sgmap)); - sbmap = L_MIN(254, L_MAX(1, sbmap)); - - if (srval <= srmap) - drval = (srval * drmap) / srmap; - else - drval = drmap + ((255 - drmap) * (srval - srmap)) / (255 - srmap); - if (sgval <= sgmap) - dgval = (sgval * dgmap) / sgmap; - else - dgval = dgmap + ((255 - dgmap) * (sgval - sgmap)) / (255 - sgmap); - if (sbval <= sbmap) - dbval = (sbval * dbmap) / sbmap; - else - dbval = dbmap + ((255 - dbmap) * (sbval - sbmap)) / (255 - sbmap); - - composeRGBPixel(drval, dgval, dbval, pdcolor); - return 0; -} - - -/*------------------------------------------------------------------* - * Fractional shift of RGB towards black or white * - *------------------------------------------------------------------*/ -/*! - * \brief pixShiftByComponent() - * - * \param[in] pixd [optional]; either NULL or equal to pixs for in-place - * \param[in] pixs 32 bpp rgb - * \param[in] srcval source color: 0xrrggbb00 - * \param[in] dstval target color: 0xrrggbb00 - * \return pixd with all pixels mapped based on the srcval/destval mapping, - * or pixd on error - * - *
- * Notes: - * (1) For each component (r, b, g) separately, this does a linear - * mapping of the colors in pixs to colors in pixd. - * Let rs and rd be the red src and dest components in %srcval and - * %dstval, and rval is the red component of the src pixel. - * Then for all pixels in pixs, the mapping for the red - * component from pixs to pixd is: - * if (rd <= rs) (shift toward black) - * rval --> (rd/rs) * rval - * if (rd > rs) (shift toward white) - * (255 - rval) --> ((255 - rs)/(255 - rd)) * (255 - rval) - * Thus if rd <= rs, the red component of all pixels is - * mapped by the same fraction toward white, and if rd > rs, - * they are mapped by the same fraction toward black. - * This is essentially a different linear TRC (gamma = 1) - * for each component. The source and target color inputs are - * just used to generate the three fractions. - * (2) Note that this mapping differs from that in - * pixLinearMapToTargetColor(), which maps rs --> rd and does - * a piecewise stretching in between. - * (3) For inplace operation, call it this way: - * pixFractionalShiftByComponent(pixs, pixs, ... ) - * (4) For generating a new pixd: - * pixd = pixLinearMapToTargetColor(NULL, pixs, ...) - * (5) A simple application is to color a grayscale image. - * A light background can be colored using srcval = 0xffffff00 - * and picking a target background color for dstval. - * A dark foreground can be colored by using srcval = 0x0 - * and choosing a target foreground color for dstval. - *- */ -PIX * -pixShiftByComponent(PIX *pixd, - PIX *pixs, - l_uint32 srcval, - l_uint32 dstval) -{ -l_int32 i, j, w, h, wpl; -l_int32 rval, gval, bval, rsval, gsval, bsval, rdval, gdval, bdval; -l_int32 *rtab, *gtab, *btab; -l_uint32 pixel; -l_uint32 *line, *data; -PIXCMAP *cmap; - - PROCNAME("pixShiftByComponent"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixd && (pixd != pixs)) - return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd); - if (pixGetDepth(pixs) != 32 && !pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, pixd); - - /* Do the work on pixd */ - if (!pixd) - pixd = pixCopy(NULL, pixs); - - /* If colormapped, just modify it */ - if ((cmap = pixGetColormap(pixd)) != NULL) { - pixcmapShiftByComponent(cmap, srcval, dstval); - return pixd; - } - - extractRGBValues(srcval, &rsval, &gsval, &bsval); - extractRGBValues(dstval, &rdval, &gdval, &bdval); - rtab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - gtab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - btab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - if (!rtab || !gtab || !btab) { - L_ERROR("calloc fail for tab\n", procName); - goto cleanup; - } - for (i = 0; i < 256; i++) { - if (rdval == rsval) - rtab[i] = i; - else if (rdval < rsval) - rtab[i] = (i * rdval) / rsval; - else - rtab[i] = 255 - (255 - rdval) * (255 - i) / (255 - rsval); - if (gdval == gsval) - gtab[i] = i; - else if (gdval < gsval) - gtab[i] = (i * gdval) / gsval; - else - gtab[i] = 255 - (255 - gdval) * (255 - i) / (255 - gsval); - if (bdval == bsval) - btab[i] = i; - else if (bdval < bsval) - btab[i] = (i * bdval) / bsval; - else - btab[i] = 255 - (255 - bdval) * (255 - i) / (255 - bsval); - } - pixGetDimensions(pixd, &w, &h, NULL); - data = pixGetData(pixd); - wpl = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - pixel = line[j]; - extractRGBValues(pixel, &rval, &gval, &bval); - composeRGBPixel(rtab[rval], gtab[gval], btab[bval], &pixel); - line[j] = pixel; - } - } - -cleanup: - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - return pixd; -} - - -/*! - * \brief pixelShiftByComponent() - * - * \param[in] rval, gval, bval - * \param[in] srcval source color: 0xrrggbb00 - * \param[in] dstval target color: 0xrrggbb00 - * \param[out] ppixel rgb value - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is a linear transformation that gives the same result - * on a single pixel as pixShiftByComponent() gives - * on a pix. Each component is handled separately. If - * the dest component is larger than the src, then the - * component is pushed toward 255 by the same fraction as - * the src --> dest shift. - *- */ -l_ok -pixelShiftByComponent(l_int32 rval, - l_int32 gval, - l_int32 bval, - l_uint32 srcval, - l_uint32 dstval, - l_uint32 *ppixel) -{ -l_int32 rsval, rdval, gsval, gdval, bsval, bdval, rs, gs, bs; - - PROCNAME("pixelShiftByComponent"); - - if (!ppixel) - return ERROR_INT("&pixel defined", procName, 1); - - extractRGBValues(srcval, &rsval, &gsval, &bsval); - extractRGBValues(dstval, &rdval, &gdval, &bdval); - if (rdval == rsval) - rs = rval; - else if (rdval < rsval) - rs = (rval * rdval) / rsval; - else - rs = 255 - (255 - rdval) * (255 - rval) / (255 - rsval); - if (gdval == gsval) - gs = gval; - else if (gdval < gsval) - gs = (gval * gdval) / gsval; - else - gs = 255 - (255 - gdval) * (255 - gval) / (255 - gsval); - if (bdval == bsval) - bs = bval; - else if (bdval < bsval) - bs = (bval * bdval) / bsval; - else - bs = 255 - (255 - bdval) * (255 - bval) / (255 - bsval); - composeRGBPixel(rs, gs, bs, ppixel); - return 0; -} - - -/*! - * \brief pixelFractionalShift() - * - * \param[in] rval, gval, bval - * \param[in] fraction negative toward black; positive toward white - * \param[out] ppixel rgb value - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This transformation leaves the hue invariant, while changing - * the saturation and intensity. It can be used for that - * purpose in pixLinearMapToTargetColor(). - * (2) %fraction is in the range [-1 .... +1]. If %fraction < 0, - * saturation is increased and brightness is reduced. The - * opposite results if %fraction > 0. If %fraction == -1, - * the resulting pixel is black; %fraction == 1 results in white. - *- */ -l_ok -pixelFractionalShift(l_int32 rval, - l_int32 gval, - l_int32 bval, - l_float32 fraction, - l_uint32 *ppixel) -{ -l_int32 nrval, ngval, nbval; - - PROCNAME("pixelFractionalShift"); - - if (!ppixel) - return ERROR_INT("&pixel defined", procName, 1); - if (fraction < -1.0 || fraction > 1.0) - return ERROR_INT("fraction not in [-1 ... +1]", procName, 1); - - nrval = (fraction < 0) ? (l_int32)((1.0 + fraction) * rval + 0.5) : - rval + (l_int32)(fraction * (255 - rval) + 0.5); - ngval = (fraction < 0) ? (l_int32)((1.0 + fraction) * gval + 0.5) : - gval + (l_int32)(fraction * (255 - gval) + 0.5); - nbval = (fraction < 0) ? (l_int32)((1.0 + fraction) * bval + 0.5) : - bval + (l_int32)(fraction * (255 - bval) + 0.5); - composeRGBPixel(nrval, ngval, nbval, ppixel); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colormap.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colormap.c deleted file mode 100644 index ff0fe755..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colormap.c +++ /dev/null @@ -1,2307 +0,0 @@ -/*====================================================================* - - 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 colormap.c - *
- * - * Colormap creation, copy, destruction, addition - * PIXCMAP *pixcmapCreate() - * PIXCMAP *pixcmapCreateRandom() - * PIXCMAP *pixcmapCreateLinear() - * PIXCMAP *pixcmapCopy() - * void pixcmapDestroy() - * l_int32 pixcmapIsValid() - * l_int32 pixcmapAddColor() - * l_int32 pixcmapAddRGBA() - * l_int32 pixcmapAddNewColor() - * l_int32 pixcmapAddNearestColor() - * l_int32 pixcmapUsableColor() - * l_int32 pixcmapAddBlackOrWhite() - * l_int32 pixcmapSetBlackAndWhite() - * l_int32 pixcmapGetCount() - * l_int32 pixcmapGetDepth() - * l_int32 pixcmapGetMinDepth() - * l_int32 pixcmapGetFreeCount() - * l_int32 pixcmapClear() - * - * Colormap random access and test - * l_int32 pixcmapGetColor() - * l_int32 pixcmapGetColor32() - * l_int32 pixcmapGetRGBA() - * l_int32 pixcmapGetRGBA32() - * l_int32 pixcmapResetColor() - * l_int32 pixcmapSetAlpha() - * l_int32 pixcmapGetIndex() - * l_int32 pixcmapHasColor() - * l_int32 pixcmapIsOpaque() - * l_int32 pixcmapIsBlackAndWhite() - * l_int32 pixcmapCountGrayColors() - * l_int32 pixcmapGetRankIntensity() - * l_int32 pixcmapGetNearestIndex() - * l_int32 pixcmapGetNearestGrayIndex() - * l_int32 pixcmapGetDistanceToColor() - * l_int32 pixcmapGetRangeValues() - * - * Colormap conversion - * PIXCMAP *pixcmapGrayToColor() - * PIXCMAP *pixcmapColorToGray() - * PIXCMAP *pixcmapConvertTo4() - * PIXCMAP *pixcmapConvertTo8() - * - * Colormap I/O - * l_int32 pixcmapRead() - * l_int32 pixcmapReadStream() - * l_int32 pixcmapReadMem() - * l_int32 pixcmapWrite() - * l_int32 pixcmapWriteStream() - * l_int32 pixcmapWriteMem() - * - * Extract colormap arrays and serialization - * l_int32 pixcmapToArrays() - * l_int32 pixcmapToRGBTable() - * l_int32 pixcmapSerializeToMemory() - * PIXCMAP *pixcmapDeserializeFromMemory() - * char *pixcmapConvertToHex() - * - * Colormap transforms - * l_int32 pixcmapGammaTRC() - * l_int32 pixcmapContrastTRC() - * l_int32 pixcmapShiftIntensity() - * l_int32 pixcmapShiftByComponent() - * - * Note: - * (1) colormaps in leptonica have a maximum of 256 entries. - * (2) nalloc, the allocated size of the palette array, is related - * to the depth d of the pixels by: - * nalloc = 2^(d) - * - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This sets up a colormap with random colors, - * where the first color is optionally black, the last color - * is optionally white, and the remaining colors are - * chosen randomly. - * (2) The number of randomly chosen colors is: - * 2^(depth) - haswhite - hasblack - * (3) Because rand() is seeded, it might disrupt otherwise - * deterministic results if also used elsewhere in a program. - * (4) rand() is not threadsafe, and will generate garbage if run - * on multiple threads at once -- though garbage is generally - * what you want from a random number generator! - * (5) Modern rand()s have equal randomness in low and high order - * bits, but older ones don't. Here, we're just using rand() - * to choose colors for output. - *- */ -PIXCMAP * -pixcmapCreateRandom(l_int32 depth, - l_int32 hasblack, - l_int32 haswhite) -{ -l_int32 ncolors, i; -l_int32 red[256], green[256], blue[256]; -PIXCMAP *cmap; - - PROCNAME("pixcmapCreateRandom"); - - if (depth != 2 && depth != 4 && depth != 8) - return (PIXCMAP *)ERROR_PTR("depth not in {2, 4, 8}", procName, NULL); - if (hasblack != 0) hasblack = 1; - if (haswhite != 0) haswhite = 1; - - cmap = pixcmapCreate(depth); - ncolors = 1 << depth; - if (hasblack) /* first color is optionally black */ - pixcmapAddColor(cmap, 0, 0, 0); - for (i = hasblack; i < ncolors - haswhite; i++) { - red[i] = (l_uint32)rand() & 0xff; - green[i] = (l_uint32)rand() & 0xff; - blue[i] = (l_uint32)rand() & 0xff; - pixcmapAddColor(cmap, red[i], green[i], blue[i]); - } - if (haswhite) /* last color is optionally white */ - pixcmapAddColor(cmap, 255, 255, 255); - - return cmap; -} - - -/*! - * \brief pixcmapCreateLinear() - * - * \param[in] d depth of pix for this colormap; 1, 2, 4 or 8 - * \param[in] nlevels valid in range [2, 2^d] - * \return cmap, or NULL on error - * - *
- * Notes: - * (1) Colormap has equally spaced gray color values - * from black (0, 0, 0) to white (255, 255, 255). - *- */ -PIXCMAP * -pixcmapCreateLinear(l_int32 d, - l_int32 nlevels) -{ -l_int32 maxlevels, i, val; -PIXCMAP *cmap; - - PROCNAME("pixcmapCreateLinear"); - - if (d != 1 && d != 2 && d !=4 && d != 8) - return (PIXCMAP *)ERROR_PTR("d not in {1, 2, 4, 8}", procName, NULL); - maxlevels = 1 << d; - if (nlevels < 2 || nlevels > maxlevels) - return (PIXCMAP *)ERROR_PTR("invalid nlevels", procName, NULL); - - cmap = pixcmapCreate(d); - for (i = 0; i < nlevels; i++) { - val = (255 * i) / (nlevels - 1); - pixcmapAddColor(cmap, val, val, val); - } - return cmap; -} - - -/*! - * \brief pixcmapCopy() - * - * \param[in] cmaps - * \return cmapd, or NULL on error - */ -PIXCMAP * -pixcmapCopy(const PIXCMAP *cmaps) -{ -l_int32 nbytes, valid; -PIXCMAP *cmapd; - - PROCNAME("pixcmapCopy"); - - if (!cmaps) - return (PIXCMAP *)ERROR_PTR("cmaps not defined", procName, NULL); - pixcmapIsValid(cmaps, &valid); - if (!valid) - return (PIXCMAP *)ERROR_PTR("invalid cmap", procName, NULL); - - cmapd = (PIXCMAP *)LEPT_CALLOC(1, sizeof(PIXCMAP)); - nbytes = cmaps->nalloc * sizeof(RGBA_QUAD); - cmapd->array = (void *)LEPT_CALLOC(1, nbytes); - memcpy(cmapd->array, cmaps->array, cmaps->n * sizeof(RGBA_QUAD)); - cmapd->n = cmaps->n; - cmapd->nalloc = cmaps->nalloc; - cmapd->depth = cmaps->depth; - return cmapd; -} - - -/*! - * \brief pixcmapDestroy() - * - * \param[in,out] pcmap set to null on return - * \return void - */ -void -pixcmapDestroy(PIXCMAP **pcmap) -{ -PIXCMAP *cmap; - - PROCNAME("pixcmapDestroy"); - - if (pcmap == NULL) { - L_WARNING("ptr address is null!\n", procName); - return; - } - - if ((cmap = *pcmap) == NULL) - return; - - LEPT_FREE(cmap->array); - LEPT_FREE(cmap); - *pcmap = NULL; - return; -} - -/*! - * \brief pixcmapIsValid() - * - * \param[in] cmap - * \param[out] pvalid return 1 if valid; 0 if not - * \return 0 if OK, 1 on error or if cmap is not valid - */ -l_ok -pixcmapIsValid(const PIXCMAP *cmap, - l_int32 *pvalid) -{ -l_int32 d; - - PROCNAME("pixcmapIsValid"); - - if (!pvalid) - return ERROR_INT("&valid not defined", procName, 1); - *pvalid = 0; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - if (!cmap->array) - return ERROR_INT("cmap array not defined", procName, 1); - d = cmap->depth; - if (d !=1 && d != 2 && d != 4 && d != 8) { - L_ERROR("invalid cmap depth: %d\n", procName, d); - return 1; - } - if (cmap->nalloc < 2 || cmap->nalloc > 256) { - L_ERROR("invalid cmap nalloc: %d\n", procName, cmap->nalloc); - return 1; - } - if (cmap->n < 0 || cmap->n > 256 || cmap->n > cmap->nalloc) { - L_ERROR("invalid cmap n: %d (nalloc = %d)\n", procName, - cmap->n, cmap->nalloc); - return 1; - } - *pvalid = 1; - return 0; -} - - -/*! - * \brief pixcmapAddColor() - * - * \param[in] cmap - * \param[in] rval, gval, bval colormap entry to be added; each number - * is in range [0, ... 255] - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This always adds the color if there is room. - * (2) The alpha component is 255 (opaque) - *- */ -l_ok -pixcmapAddColor(PIXCMAP *cmap, - l_int32 rval, - l_int32 gval, - l_int32 bval) -{ -RGBA_QUAD *cta; - - PROCNAME("pixcmapAddColor"); - - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - if (cmap->n >= cmap->nalloc) - return ERROR_INT("no free color entries", procName, 1); - - cta = (RGBA_QUAD *)cmap->array; - cta[cmap->n].red = rval; - cta[cmap->n].green = gval; - cta[cmap->n].blue = bval; - cta[cmap->n].alpha = 255; - cmap->n++; - return 0; -} - - -/*! - * \brief pixcmapAddRGBA() - * - * \param[in] cmap - * \param[in] rval, gval, bval, aval colormap entry to be added; - * each number is in range [0, ... 255] - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This always adds the color if there is room. - *- */ -l_ok -pixcmapAddRGBA(PIXCMAP *cmap, - l_int32 rval, - l_int32 gval, - l_int32 bval, - l_int32 aval) -{ -RGBA_QUAD *cta; - - PROCNAME("pixcmapAddRGBA"); - - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - if (cmap->n >= cmap->nalloc) - return ERROR_INT("no free color entries", procName, 1); - - cta = (RGBA_QUAD *)cmap->array; - cta[cmap->n].red = rval; - cta[cmap->n].green = gval; - cta[cmap->n].blue = bval; - cta[cmap->n].alpha = aval; - cmap->n++; - return 0; -} - - -/*! - * \brief pixcmapAddNewColor() - * - * \param[in] cmap - * \param[in] rval, gval, bval colormap entry to be added; each number - * is in range [0, ... 255] - * \param[out] pindex index of color - * \return 0 if OK, 1 on error; 2 if unable to add color - * - *
- * Notes: - * (1) This only adds color if not already there. - * (2) The alpha component is 255 (opaque) - * (3) This returns the index of the new (or existing) color. - * (4) Returns 2 with a warning if unable to add this color; - * the caller should check the return value. - *- */ -l_ok -pixcmapAddNewColor(PIXCMAP *cmap, - l_int32 rval, - l_int32 gval, - l_int32 bval, - l_int32 *pindex) -{ - PROCNAME("pixcmapAddNewColor"); - - if (!pindex) - return ERROR_INT("&index not defined", procName, 1); - *pindex = 0; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - /* Check if the color is already present. */ - if (!pixcmapGetIndex(cmap, rval, gval, bval, pindex)) /* found */ - return 0; - - /* We need to add the color. Is there room? */ - if (cmap->n >= cmap->nalloc) { - L_WARNING("no free color entries\n", procName); - return 2; - } - - /* There's room. Add it. */ - pixcmapAddColor(cmap, rval, gval, bval); - *pindex = pixcmapGetCount(cmap) - 1; - return 0; -} - - -/*! - * \brief pixcmapAddNearestColor() - * - * \param[in] cmap - * \param[in] rval, gval, bval colormap entry to be added; each number - * is in range [0, ... 255] - * \param[out] pindex index of color - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This only adds color if not already there. - * (2) The alpha component is 255 (opaque) - * (3) If it's not in the colormap and there is no room to add - * another color, this returns the index of the nearest color. - *- */ -l_ok -pixcmapAddNearestColor(PIXCMAP *cmap, - l_int32 rval, - l_int32 gval, - l_int32 bval, - l_int32 *pindex) -{ - PROCNAME("pixcmapAddNearestColor"); - - if (!pindex) - return ERROR_INT("&index not defined", procName, 1); - *pindex = 0; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - /* Check if the color is already present. */ - if (!pixcmapGetIndex(cmap, rval, gval, bval, pindex)) /* found */ - return 0; - - /* We need to add the color. Is there room? */ - if (cmap->n < cmap->nalloc) { - pixcmapAddColor(cmap, rval, gval, bval); - *pindex = pixcmapGetCount(cmap) - 1; - return 0; - } - - /* There's no room. Return the index of the nearest color */ - pixcmapGetNearestIndex(cmap, rval, gval, bval, pindex); - return 0; -} - - -/*! - * \brief pixcmapUsableColor() - * - * \param[in] cmap - * \param[in] rval, gval, bval colormap entry to be added; each number - * is in range [0, ... 255] - * \param[out] pusable 1 if usable; 0 if not - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This checks if the color already exists or if there is - * room to add it. It makes no change in the colormap. - *- */ -l_ok -pixcmapUsableColor(PIXCMAP *cmap, - l_int32 rval, - l_int32 gval, - l_int32 bval, - l_int32 *pusable) -{ -l_int32 index; - - PROCNAME("pixcmapUsableColor"); - - if (!pusable) - return ERROR_INT("&usable not defined", procName, 1); - *pusable = 0; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - /* Is there room to add it? */ - if (cmap->n < cmap->nalloc) { - *pusable = 1; - return 0; - } - - /* No room; check if the color is already present. */ - if (!pixcmapGetIndex(cmap, rval, gval, bval, &index)) /* found */ - *pusable = 1; - return 0; -} - - -/*! - * \brief pixcmapAddBlackOrWhite() - * - * \param[in] cmap - * \param[in] color 0 for black, 1 for white - * \param[out] pindex [optional] index of color; can be null - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This only adds color if not already there. - * (2) The alpha component is 255 (opaque) - * (3) This sets index to the requested color. - * (4) If there is no room in the colormap, returns the index - * of the closest color. - *- */ -l_ok -pixcmapAddBlackOrWhite(PIXCMAP *cmap, - l_int32 color, - l_int32 *pindex) -{ -l_int32 index; - - PROCNAME("pixcmapAddBlackOrWhite"); - - if (pindex) *pindex = 0; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - if (color == 0) { /* black */ - if (pixcmapGetFreeCount(cmap) > 0) - pixcmapAddNewColor(cmap, 0, 0, 0, &index); - else - pixcmapGetRankIntensity(cmap, 0.0, &index); - } else { /* white */ - if (pixcmapGetFreeCount(cmap) > 0) - pixcmapAddNewColor(cmap, 255, 255, 255, &index); - else - pixcmapGetRankIntensity(cmap, 1.0, &index); - } - - if (pindex) - *pindex = index; - return 0; -} - - -/*! - * \brief pixcmapSetBlackAndWhite() - * - * \param[in] cmap - * \param[in] setblack 0 for no operation; 1 to set darkest color to black - * \param[in] setwhite 0 for no operation; 1 to set lightest color to white - * \return 0 if OK, 1 on error - */ -l_ok -pixcmapSetBlackAndWhite(PIXCMAP *cmap, - l_int32 setblack, - l_int32 setwhite) -{ -l_int32 index; - - PROCNAME("pixcmapSetBlackAndWhite"); - - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - if (setblack) { - pixcmapGetRankIntensity(cmap, 0.0, &index); - pixcmapResetColor(cmap, index, 0, 0, 0); - } - if (setwhite) { - pixcmapGetRankIntensity(cmap, 1.0, &index); - pixcmapResetColor(cmap, index, 255, 255, 255); - } - return 0; -} - - -/*! - * \brief pixcmapGetCount() - * - * \param[in] cmap - * \return count, or 0 on error - */ -l_int32 -pixcmapGetCount(const PIXCMAP *cmap) -{ - PROCNAME("pixcmapGetCount"); - - if (!cmap) - return ERROR_INT("cmap not defined", procName, 0); - return cmap->n; -} - - -/*! - * \brief pixcmapGetFreeCount() - * - * \param[in] cmap - * \return free entries, or 0 on error - */ -l_int32 -pixcmapGetFreeCount(PIXCMAP *cmap) -{ - PROCNAME("pixcmapGetFreeCount"); - - if (!cmap) - return ERROR_INT("cmap not defined", procName, 0); - return (cmap->nalloc - cmap->n); -} - - -/*! - * \brief pixcmapGetDepth() - * - * \param[in] cmap - * \return depth, or 0 on error - */ -l_int32 -pixcmapGetDepth(PIXCMAP *cmap) -{ - PROCNAME("pixcmapGetDepth"); - - if (!cmap) - return ERROR_INT("cmap not defined", procName, 0); - return cmap->depth; -} - - -/*! - * \brief pixcmapGetMinDepth() - * - * \param[in] cmap - * \param[out] pmindepth minimum depth to support the colormap - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) On error, &mindepth is returned as 0. - *- */ -l_ok -pixcmapGetMinDepth(PIXCMAP *cmap, - l_int32 *pmindepth) -{ -l_int32 ncolors; - - PROCNAME("pixcmapGetMinDepth"); - - if (!pmindepth) - return ERROR_INT("&mindepth not defined", procName, 1); - *pmindepth = 0; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - ncolors = pixcmapGetCount(cmap); - if (ncolors <= 4) - *pmindepth = 2; - else if (ncolors <= 16) - *pmindepth = 4; - else /* ncolors > 16 */ - *pmindepth = 8; - return 0; -} - - -/*! - * \brief pixcmapClear() - * - * \param[in] cmap - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This removes the colors by setting the count to 0. - *- */ -l_ok -pixcmapClear(PIXCMAP *cmap) -{ - PROCNAME("pixcmapClear"); - - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - cmap->n = 0; - return 0; -} - - -/*-------------------------------------------------------------* - * Colormap random access * - *-------------------------------------------------------------*/ -/*! - * \brief pixcmapGetColor() - * - * \param[in] cmap - * \param[in] index - * \param[out] prval, pgval, pbval each color value - * \return 0 if OK, 1 if not accessible caller should check - */ -l_ok -pixcmapGetColor(PIXCMAP *cmap, - l_int32 index, - l_int32 *prval, - l_int32 *pgval, - l_int32 *pbval) -{ -RGBA_QUAD *cta; - - PROCNAME("pixcmapGetColor"); - - if (!prval || !pgval || !pbval) - return ERROR_INT("&rval, &gval, &bval not all defined", procName, 1); - *prval = *pgval = *pbval = 0; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - if (index < 0 || index >= cmap->n) - return ERROR_INT("index out of bounds", procName, 1); - - cta = (RGBA_QUAD *)cmap->array; - *prval = cta[index].red; - *pgval = cta[index].green; - *pbval = cta[index].blue; - return 0; -} - - -/*! - * \brief pixcmapGetColor32() - * - * \param[in] cmap - * \param[in] index - * \param[out] pval32 32-bit rgb color value - * \return 0 if OK, 1 if not accessible caller should check - * - *
- * Notes: - * (1) The returned alpha channel value is 255. - *- */ -l_ok -pixcmapGetColor32(PIXCMAP *cmap, - l_int32 index, - l_uint32 *pval32) -{ -l_int32 rval, gval, bval; - - PROCNAME("pixcmapGetColor32"); - - if (!pval32) - return ERROR_INT("&val32 not defined", procName, 1); - *pval32 = 0; - - if (pixcmapGetColor(cmap, index, &rval, &gval, &bval) != 0) - return ERROR_INT("rgb values not found", procName, 1); - composeRGBAPixel(rval, gval, bval, 255, pval32); - return 0; -} - - -/*! - * \brief pixcmapGetRGBA() - * - * \param[in] cmap - * \param[in] index - * \param[out] prval, pgval, pbval, paval each color value - * \return 0 if OK, 1 if not accessible caller should check - */ -l_ok -pixcmapGetRGBA(PIXCMAP *cmap, - l_int32 index, - l_int32 *prval, - l_int32 *pgval, - l_int32 *pbval, - l_int32 *paval) -{ -RGBA_QUAD *cta; - - PROCNAME("pixcmapGetRGBA"); - - if (!prval || !pgval || !pbval || !paval) - return ERROR_INT("&rval, &gval, &bval, &aval not all defined", - procName, 1); - *prval = *pgval = *pbval = *paval = 0; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - if (index < 0 || index >= cmap->n) - return ERROR_INT("index out of bounds", procName, 1); - - cta = (RGBA_QUAD *)cmap->array; - *prval = cta[index].red; - *pgval = cta[index].green; - *pbval = cta[index].blue; - *paval = cta[index].alpha; - return 0; -} - - -/*! - * \brief pixcmapGetRGBA32() - * - * \param[in] cmap - * \param[in] index - * \param[out] pval32 32-bit rgba color value - * \return 0 if OK, 1 if not accessible caller should check - */ -l_ok -pixcmapGetRGBA32(PIXCMAP *cmap, - l_int32 index, - l_uint32 *pval32) -{ -l_int32 rval, gval, bval, aval; - - PROCNAME("pixcmapGetRGBA32"); - - if (!pval32) - return ERROR_INT("&val32 not defined", procName, 1); - *pval32 = 0; - - if (pixcmapGetRGBA(cmap, index, &rval, &gval, &bval, &aval) != 0) - return ERROR_INT("rgba values not found", procName, 1); - composeRGBAPixel(rval, gval, bval, aval, pval32); - return 0; -} - - -/*! - * \brief pixcmapResetColor() - * - * \param[in] cmap - * \param[in] index - * \param[in] rval, gval, bval colormap entry to be reset; each number - * is in range [0, ... 255] - * \return 0 if OK, 1 if not accessible caller should check - * - *
- * Notes: - * (1) This resets sets the color of an entry that has already - * been set and included in the count of colors. - * (2) The alpha component is 255 (opaque) - *- */ -l_ok -pixcmapResetColor(PIXCMAP *cmap, - l_int32 index, - l_int32 rval, - l_int32 gval, - l_int32 bval) -{ -RGBA_QUAD *cta; - - PROCNAME("pixcmapResetColor"); - - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - if (index < 0 || index >= cmap->n) - return ERROR_INT("index out of bounds", procName, 1); - - cta = (RGBA_QUAD *)cmap->array; - cta[index].red = rval; - cta[index].green = gval; - cta[index].blue = bval; - cta[index].alpha = 255; - return 0; -} - - -/*! - * \brief pixcmapSetAlpha() - * - * \param[in] cmap - * \param[in] index - * \param[in] aval in range [0, ... 255] - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This modifies the transparency of one entry in a colormap. - * The alpha component by default is 255 (opaque). - * This is used when extracting the colormap from a PNG file - * without decoding the image. - *- */ -l_ok -pixcmapSetAlpha(PIXCMAP *cmap, - l_int32 index, - l_int32 aval) -{ -RGBA_QUAD *cta; - - PROCNAME("pixcmapSetAlpha"); - - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - if (index < 0 || index >= cmap->n) - return ERROR_INT("index out of bounds", procName, 1); - - cta = (RGBA_QUAD *)cmap->array; - cta[index].alpha = aval; - return 0; -} - - -/*! - * \brief pixcmapGetIndex() - * - * \param[in] cmap - * \param[in] rval, gval, bval colormap colors to search for; each number - * is in range [0, ... 255] - * \param[out] pindex value of index found - * \return 0 if found, 1 if not found caller must check - */ -l_int32 -pixcmapGetIndex(PIXCMAP *cmap, - l_int32 rval, - l_int32 gval, - l_int32 bval, - l_int32 *pindex) -{ -l_int32 n, i; -RGBA_QUAD *cta; - - PROCNAME("pixcmapGetIndex"); - - if (!pindex) - return ERROR_INT("&index not defined", procName, 1); - *pindex = 0; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - n = pixcmapGetCount(cmap); - - cta = (RGBA_QUAD *)cmap->array; - for (i = 0; i < n; i++) { - if (rval == cta[i].red && - gval == cta[i].green && - bval == cta[i].blue) { - *pindex = i; - return 0; - } - } - return 1; -} - - -/*! - * \brief pixcmapHasColor() - * - * \param[in] cmap - * \param[out] pcolor TRUE if cmap has color; FALSE otherwise - * \return 0 if OK, 1 on error - */ -l_ok -pixcmapHasColor(PIXCMAP *cmap, - l_int32 *pcolor) -{ -l_int32 n, i; -l_int32 *rmap, *gmap, *bmap; - - PROCNAME("pixcmapHasColor"); - - if (!pcolor) - return ERROR_INT("&color not defined", procName, 1); - *pcolor = FALSE; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - if (pixcmapToArrays(cmap, &rmap, &gmap, &bmap, NULL)) - return ERROR_INT("colormap arrays not made", procName, 1); - n = pixcmapGetCount(cmap); - for (i = 0; i < n; i++) { - if ((rmap[i] != gmap[i]) || (rmap[i] != bmap[i])) { - *pcolor = TRUE; - break; - } - } - - LEPT_FREE(rmap); - LEPT_FREE(gmap); - LEPT_FREE(bmap); - return 0; -} - - -/*! - * \brief pixcmapIsOpaque() - * - * \param[in] cmap - * \param[out] popaque TRUE if fully opaque: all entries are 255 - * \return 0 if OK, 1 on error - */ -l_ok -pixcmapIsOpaque(PIXCMAP *cmap, - l_int32 *popaque) -{ -l_int32 i, n; -RGBA_QUAD *cta; - - PROCNAME("pixcmapIsOpaque"); - - if (!popaque) - return ERROR_INT("&opaque not defined", procName, 1); - *popaque = TRUE; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - n = pixcmapGetCount(cmap); - cta = (RGBA_QUAD *)cmap->array; - for (i = 0; i < n; i++) { - if (cta[i].alpha != 255) { - *popaque = FALSE; - break; - } - } - return 0; -} - - -/*! - * \brief pixcmapIsBlackAndWhite() - * - * \param[in] cmap - * \param[out] pblackwhite TRUE if the cmap has only two colors: - * black (0,0,0) and white (255,255,255) - * \return 0 if OK, 1 on error - */ -l_ok -pixcmapIsBlackAndWhite(PIXCMAP *cmap, - l_int32 *pblackwhite) -{ -l_int32 val0, val1, hascolor; -RGBA_QUAD *cta; - - PROCNAME("pixcmapIsBlackAndWhite"); - - if (!pblackwhite) - return ERROR_INT("&blackwhite not defined", procName, 1); - *pblackwhite = FALSE; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - if (pixcmapGetCount(cmap) != 2) - return 0; - - pixcmapHasColor(cmap, &hascolor); - if (hascolor) return 0; - - cta = (RGBA_QUAD *)cmap->array; - val0 = cta[0].red; - val1 = cta[1].red; - if ((val0 == 0 && val1 == 255) || (val0 == 255 && val1 == 0)) - *pblackwhite = TRUE; - return 0; -} - - -/*! - * \brief pixcmapCountGrayColors() - * - * \param[in] cmap - * \param[out] pngray number of gray colors - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This counts the unique gray colors, including black and white. - *- */ -l_ok -pixcmapCountGrayColors(PIXCMAP *cmap, - l_int32 *pngray) -{ -l_int32 n, i, rval, gval, bval, count; -l_int32 *array; - - PROCNAME("pixcmapCountGrayColors"); - - if (!pngray) - return ERROR_INT("&ngray not defined", procName, 1); - *pngray = 0; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - array = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - n = pixcmapGetCount(cmap); - count = 0; - for (i = 0; i < n; i++) { - pixcmapGetColor(cmap, i, &rval, &gval, &bval); - if ((rval == gval) && (rval == bval) && (array[rval] == 0)) { - array[rval] = 1; - count++; - } - } - - LEPT_FREE(array); - *pngray = count; - return 0; -} - - -/*! - * \brief pixcmapGetRankIntensity() - * - * \param[in] cmap - * \param[in] rankval 0.0 for darkest, 1.0 for lightest color - * \param[out] pindex the index into the colormap that corresponds - * to the rank intensity color - * \return 0 if OK, 1 on error - */ -l_ok -pixcmapGetRankIntensity(PIXCMAP *cmap, - l_float32 rankval, - l_int32 *pindex) -{ -l_int32 n, i, rval, gval, bval, rankindex; -NUMA *na, *nasort; - - PROCNAME("pixcmapGetRankIntensity"); - - if (!pindex) - return ERROR_INT("&index not defined", procName, 1); - *pindex = 0; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - if (rankval < 0.0 || rankval > 1.0) - return ERROR_INT("rankval not in [0.0 ... 1.0]", procName, 1); - - n = pixcmapGetCount(cmap); - na = numaCreate(n); - for (i = 0; i < n; i++) { - pixcmapGetColor(cmap, i, &rval, &gval, &bval); - numaAddNumber(na, rval + gval + bval); - } - nasort = numaGetSortIndex(na, L_SORT_INCREASING); - rankindex = (l_int32)(rankval * (n - 1) + 0.5); - numaGetIValue(nasort, rankindex, pindex); - - numaDestroy(&na); - numaDestroy(&nasort); - return 0; -} - - -/*! - * \brief pixcmapGetNearestIndex() - * - * \param[in] cmap - * \param[in] rval, gval, bval colormap colors to search for; each number - * is in range [0, ... 255] - * \param[out] pindex the index of the nearest color - * \return 0 if OK, 1 on error caller must check - * - *
- * Notes: - * (1) Returns the index of the exact color if possible, otherwise the - * index of the color closest to the target color. - * (2) Nearest color is that which is the least sum-of-squares distance - * from the target color. - *- */ -l_ok -pixcmapGetNearestIndex(PIXCMAP *cmap, - l_int32 rval, - l_int32 gval, - l_int32 bval, - l_int32 *pindex) -{ -l_int32 i, n, delta, dist, mindist; -RGBA_QUAD *cta; - - PROCNAME("pixcmapGetNearestIndex"); - - if (!pindex) - return ERROR_INT("&index not defined", procName, 1); - *pindex = UNDEF; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - if ((cta = (RGBA_QUAD *)cmap->array) == NULL) - return ERROR_INT("cta not defined(!)", procName, 1); - n = pixcmapGetCount(cmap); - - mindist = 3 * 255 * 255 + 1; - for (i = 0; i < n; i++) { - delta = cta[i].red - rval; - dist = delta * delta; - delta = cta[i].green - gval; - dist += delta * delta; - delta = cta[i].blue - bval; - dist += delta * delta; - if (dist < mindist) { - *pindex = i; - if (dist == 0) - break; - mindist = dist; - } - } - - return 0; -} - - -/*! - * \brief pixcmapGetNearestGrayIndex() - * - * \param[in] cmap - * \param[in] val gray value to search for; in range [0, ... 255] - * \param[out] pindex the index of the nearest color - * \return 0 if OK, 1 on error caller must check - * - *
- * Notes: - * (1) This should be used on gray colormaps. It uses only the - * green value of the colormap. - * (2) Returns the index of the exact color if possible, otherwise the - * index of the color closest to the target color. - *- */ -l_ok -pixcmapGetNearestGrayIndex(PIXCMAP *cmap, - l_int32 val, - l_int32 *pindex) -{ -l_int32 i, n, dist, mindist; -RGBA_QUAD *cta; - - PROCNAME("pixcmapGetNearestGrayIndex"); - - if (!pindex) - return ERROR_INT("&index not defined", procName, 1); - *pindex = 0; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - if (val < 0 || val > 255) - return ERROR_INT("val not in [0 ... 255]", procName, 1); - - if ((cta = (RGBA_QUAD *)cmap->array) == NULL) - return ERROR_INT("cta not defined(!)", procName, 1); - n = pixcmapGetCount(cmap); - - mindist = 256; - for (i = 0; i < n; i++) { - dist = cta[i].green - val; - dist = L_ABS(dist); - if (dist < mindist) { - *pindex = i; - if (dist == 0) - break; - mindist = dist; - } - } - - return 0; -} - - -/*! - * \brief pixcmapGetDistanceToColor() - * - * \param[in] cmap - * \param[in] index - * \param[in] rval, gval, bval target color - * \param[out] pdist the distance from the cmap entry to target - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Returns the L2 distance (squared) between the color at index i - * and the target color. - *- */ -l_ok -pixcmapGetDistanceToColor(PIXCMAP *cmap, - l_int32 index, - l_int32 rval, - l_int32 gval, - l_int32 bval, - l_int32 *pdist) -{ -l_int32 n, delta, dist; -RGBA_QUAD *cta; - - PROCNAME("pixcmapGetDistanceToColor"); - - if (!pdist) - return ERROR_INT("&dist not defined", procName, 1); - *pdist = UNDEF; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - n = pixcmapGetCount(cmap); - if (index >= n) - return ERROR_INT("invalid index", procName, 1); - - if ((cta = (RGBA_QUAD *)cmap->array) == NULL) - return ERROR_INT("cta not defined(!)", procName, 1); - - delta = cta[index].red - rval; - dist = delta * delta; - delta = cta[index].green - gval; - dist += delta * delta; - delta = cta[index].blue - bval; - dist += delta * delta; - *pdist = dist; - - return 0; -} - - -/*! - * \brief pixcmapGetRangeValues() - * - * \param[in] cmap - * \param[in] select L_SELECT_RED, L_SELECT_GREEN, L_SELECT_BLUE or - * L_SELECT_AVERAGE - * \param[out] pminval [optional] minimum value of component - * \param[out] pmaxval [optional] maximum value of component - * \param[out] pminindex [optional] index of minimum value - * \param[out] pmaxindex [optional] index of maximum value - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Returns, for selected components (or the average), the - * the extreme values (min and/or max) and their indices - * that are found in the cmap. - *- */ -l_ok -pixcmapGetRangeValues(PIXCMAP *cmap, - l_int32 select, - l_int32 *pminval, - l_int32 *pmaxval, - l_int32 *pminindex, - l_int32 *pmaxindex) -{ -l_int32 i, n, imin, imax, minval, maxval, rval, gval, bval, aveval; - - PROCNAME("pixcmapGetRangeValues"); - - if (pminval) *pminval = UNDEF; - if (pmaxval) *pmaxval = UNDEF; - if (pminindex) *pminindex = UNDEF; - if (pmaxindex) *pmaxindex = UNDEF; - if (!pminval && !pmaxval && !pminindex && !pmaxindex) - return ERROR_INT("no result requested", procName, 1); - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - imin = UNDEF; - imax = UNDEF; - minval = 100000; - maxval = -1; - n = pixcmapGetCount(cmap); - for (i = 0; i < n; i++) { - pixcmapGetColor(cmap, i, &rval, &gval, &bval); - if (select == L_SELECT_RED) { - if (rval < minval) { - minval = rval; - imin = i; - } - if (rval > maxval) { - maxval = rval; - imax = i; - } - } else if (select == L_SELECT_GREEN) { - if (gval < minval) { - minval = gval; - imin = i; - } - if (gval > maxval) { - maxval = gval; - imax = i; - } - } else if (select == L_SELECT_BLUE) { - if (bval < minval) { - minval = bval; - imin = i; - } - if (bval > maxval) { - maxval = bval; - imax = i; - } - } else if (select == L_SELECT_AVERAGE) { - aveval = (rval + gval + bval) / 3; - if (aveval < minval) { - minval = aveval; - imin = i; - } - if (aveval > maxval) { - maxval = aveval; - imax = i; - } - } else { - return ERROR_INT("invalid selection", procName, 1); - } - } - - if (pminval) *pminval = minval; - if (pmaxval) *pmaxval = maxval; - if (pminindex) *pminindex = imin; - if (pmaxindex) *pmaxindex = imax; - return 0; -} - - -/*-------------------------------------------------------------* - * Colormap conversion * - *-------------------------------------------------------------*/ -/*! - * \brief pixcmapGrayToColor() - * - * \param[in] color - * \return cmap, or NULL on error - * - *
- * Notes: - * (1) This creates a colormap that maps from gray to - * a specific color. In the mapping, each component - * is faded to white, depending on the gray value. - * (2) In use, this is simply attached to a grayscale pix - * to give it the input color. - *- */ -PIXCMAP * -pixcmapGrayToColor(l_uint32 color) -{ -l_int32 i, rval, gval, bval; -PIXCMAP *cmap; - - extractRGBValues(color, &rval, &gval, &bval); - cmap = pixcmapCreate(8); - for (i = 0; i < 256; i++) { - pixcmapAddColor(cmap, rval + (i * (255 - rval)) / 255, - gval + (i * (255 - gval)) / 255, - bval + (i * (255 - bval)) / 255); - } - - return cmap; -} - - -/*! - * \brief pixcmapColorToGray() - * - * \param[in] cmaps - * \param[in] rwt, gwt, bwt non-negative; these should add to 1.0 - * \return cmap gray, or NULL on error - * - *
- * Notes: - * (1) This creates a gray colormap from an arbitrary colormap. - * (2) In use, attach the output gray colormap to the pix - * (or a copy of it) that provided the input colormap. - *- */ -PIXCMAP * -pixcmapColorToGray(PIXCMAP *cmaps, - l_float32 rwt, - l_float32 gwt, - l_float32 bwt) -{ -l_int32 i, n, rval, gval, bval, val; -l_float32 sum; -PIXCMAP *cmapd; - - PROCNAME("pixcmapColorToGray"); - - if (!cmaps) - return (PIXCMAP *)ERROR_PTR("cmaps not defined", procName, NULL); - if (rwt < 0.0 || gwt < 0.0 || bwt < 0.0) - return (PIXCMAP *)ERROR_PTR("weights not all >= 0.0", procName, NULL); - - /* Make sure the sum of weights is 1.0; otherwise, you can get - * overflow in the gray value. */ - sum = rwt + gwt + bwt; - if (sum == 0.0) { - L_WARNING("all weights zero; setting equal to 1/3\n", procName); - rwt = gwt = bwt = 0.33333; - sum = 1.0; - } - if (L_ABS(sum - 1.0) > 0.0001) { /* maintain ratios with sum == 1.0 */ - L_WARNING("weights don't sum to 1; maintaining ratios\n", procName); - rwt = rwt / sum; - gwt = gwt / sum; - bwt = bwt / sum; - } - - if ((cmapd = pixcmapCopy(cmaps)) == NULL) - return (PIXCMAP *)ERROR_PTR("cmapd not made", procName, NULL); - n = pixcmapGetCount(cmapd); - for (i = 0; i < n; i++) { - pixcmapGetColor(cmapd, i, &rval, &gval, &bval); - val = (l_int32)(rwt * rval + gwt * gval + bwt * bval + 0.5); - pixcmapResetColor(cmapd, i, val, val, val); - } - - return cmapd; -} - - -/*! - * \brief pixcmapConvertTo4() - * - * \param[in] cmaps colormap for 2 bpp pix - * \return cmapd (4 bpp) - * - *
- * Notes: - * (1) This converts a 2 bpp colormap to 4 bpp. The colors - * are the same; the output colormap entry array has size 16. - *- */ -PIXCMAP * -pixcmapConvertTo4(PIXCMAP *cmaps) -{ -l_int32 i, n, rval, gval, bval; -PIXCMAP *cmapd; - - PROCNAME("pixcmapConvertTo4"); - - if (!cmaps) - return (PIXCMAP *)ERROR_PTR("cmaps not defined", procName, NULL); - if (pixcmapGetDepth(cmaps) != 2) - return (PIXCMAP *)ERROR_PTR("cmaps not for 2 bpp pix", procName, NULL); - - cmapd = pixcmapCreate(4); - n = pixcmapGetCount(cmaps); - for (i = 0; i < n; i++) { - pixcmapGetColor(cmaps, i, &rval, &gval, &bval); - pixcmapAddColor(cmapd, rval, gval, bval); - } - return cmapd; -} - - -/*! - * \brief pixcmapConvertTo8() - * - * \param[in] cmaps colormap for 2 bpp or 4 bpp pix - * \return cmapd (8 bpp) - * - *
- * Notes: - * (1) This converts a 2 bpp or 4 bpp colormap to 8 bpp. The colors - * are the same; the output colormap entry array has size 256. - *- */ -PIXCMAP * -pixcmapConvertTo8(PIXCMAP *cmaps) -{ -l_int32 i, n, depth, rval, gval, bval; -PIXCMAP *cmapd; - - PROCNAME("pixcmapConvertTo8"); - - if (!cmaps) - return (PIXCMAP *)ERROR_PTR("cmaps not defined", procName, NULL); - depth = pixcmapGetDepth(cmaps); - if (depth == 8) return pixcmapCopy(cmaps); - if (depth != 2 && depth != 4) - return (PIXCMAP *)ERROR_PTR("cmaps not 2 or 4 bpp", procName, NULL); - - cmapd = pixcmapCreate(8); - n = pixcmapGetCount(cmaps); - for (i = 0; i < n; i++) { - pixcmapGetColor(cmaps, i, &rval, &gval, &bval); - pixcmapAddColor(cmapd, rval, gval, bval); - } - return cmapd; -} - - -/*-------------------------------------------------------------* - * Colormap I/O * - *-------------------------------------------------------------*/ -/*! - * \brief pixcmapRead() - * - * \param[in] filename - * \return cmap, or NULL on error - */ -PIXCMAP * -pixcmapRead(const char *filename) -{ -FILE *fp; -PIXCMAP *cmap; - - PROCNAME("pixcmapRead"); - - if (!filename) - return (PIXCMAP *)ERROR_PTR("filename not defined", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (PIXCMAP *)ERROR_PTR("stream not opened", procName, NULL); - cmap = pixcmapReadStream(fp); - fclose(fp); - if (!cmap) - return (PIXCMAP *)ERROR_PTR("cmap not read", procName, NULL); - return cmap; -} - - -/*! - * \brief pixcmapReadStream() - * - * \param[in] fp file stream - * \return cmap, or NULL on error - */ -PIXCMAP * -pixcmapReadStream(FILE *fp) -{ -l_int32 rval, gval, bval, aval, ignore; -l_int32 i, index, ret, depth, ncolors; -PIXCMAP *cmap; - - PROCNAME("pixcmapReadStream"); - - if (!fp) - return (PIXCMAP *)ERROR_PTR("stream not defined", procName, NULL); - - ret = fscanf(fp, "\nPixcmap: depth = %d bpp; %d colors\n", - &depth, &ncolors); - if (ret != 2 || - (depth != 1 && depth != 2 && depth != 4 && depth != 8) || - (ncolors < 2 || ncolors > 256)) - return (PIXCMAP *)ERROR_PTR("invalid cmap size", procName, NULL); - ignore = fscanf(fp, "Color R-val G-val B-val Alpha\n"); - ignore = fscanf(fp, "----------------------------------------\n"); - - cmap = pixcmapCreate(depth); - for (i = 0; i < ncolors; i++) { - if (fscanf(fp, "%3d %3d %3d %3d %3d\n", - &index, &rval, &gval, &bval, &aval) != 5) { - pixcmapDestroy(&cmap); - return (PIXCMAP *)ERROR_PTR("invalid entry", procName, NULL); - } - pixcmapAddRGBA(cmap, rval, gval, bval, aval); - } - return cmap; -} - - -/*! - * \brief pixcmapReadMem() - * - * \param[in] data serialization of pixcmap; in ascii - * \param[in] size of data in bytes; can use strlen to get it - * \return cmap, or NULL on error - */ -PIXCMAP * -pixcmapReadMem(const l_uint8 *data, - size_t size) -{ -FILE *fp; -PIXCMAP *cmap; - - PROCNAME("pixcmapReadMem"); - - if (!data) - return (PIXCMAP *)ERROR_PTR("data not defined", procName, NULL); - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return (PIXCMAP *)ERROR_PTR("stream not opened", procName, NULL); - - cmap = pixcmapReadStream(fp); - fclose(fp); - if (!cmap) L_ERROR("cmap not read\n", procName); - return cmap; -} - - -/*! - * \brief pixcmapWrite() - * - * \param[in] filename - * \param[in] cmap - * \return 0 if OK, 1 on error - */ -l_ok -pixcmapWrite(const char *filename, - const PIXCMAP *cmap) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("pixcmapWrite"); - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "w")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = pixcmapWriteStream(fp, cmap); - fclose(fp); - if (ret) - return ERROR_INT("cmap not written to stream", procName, 1); - return 0; -} - - - -/*! - * \brief pixcmapWriteStream() - * - * \param[in] fp file stream - \param[in] cmap - * \return 0 if OK, 1 on error - */ -l_ok -pixcmapWriteStream(FILE *fp, - const PIXCMAP *cmap) -{ -l_int32 *rmap, *gmap, *bmap, *amap; -l_int32 i; - - PROCNAME("pixcmapWriteStream"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - if (pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap)) - return ERROR_INT("colormap arrays not made", procName, 1); - - fprintf(fp, "\nPixcmap: depth = %d bpp; %d colors\n", cmap->depth, cmap->n); - fprintf(fp, "Color R-val G-val B-val Alpha\n"); - fprintf(fp, "----------------------------------------\n"); - for (i = 0; i < cmap->n; i++) - fprintf(fp, "%3d %3d %3d %3d %3d\n", - i, rmap[i], gmap[i], bmap[i], amap[i]); - fprintf(fp, "\n"); - - LEPT_FREE(rmap); - LEPT_FREE(gmap); - LEPT_FREE(bmap); - LEPT_FREE(amap); - return 0; -} - - -/*! - * \brief pixcmapWriteMem() - * - * \param[out] pdata data of serialized pixcmap; ascii - * \param[out] psize size of returned data - * \param[in] cmap - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Serializes a pixcmap in memory and puts the result in a buffer. - *- */ -l_ok -pixcmapWriteMem(l_uint8 **pdata, - size_t *psize, - const PIXCMAP *cmap) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("pixcmapWriteMem"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - if (!psize) - return ERROR_INT("&size not defined", procName, 1); - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = pixcmapWriteStream(fp, cmap); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = pixcmapWriteStream(fp, cmap); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} - - -/*----------------------------------------------------------------------* - * Extract colormap arrays and serialization * - *----------------------------------------------------------------------*/ -/*! - * \brief pixcmapToArrays() - * - * \param[in] cmap colormap - * \param[out] prmap, pgmap, pbmap colormap arrays - * \param[out] pamap [optional] alpha array - * \return 0 if OK; 1 on error - */ -l_ok -pixcmapToArrays(const PIXCMAP *cmap, - l_int32 **prmap, - l_int32 **pgmap, - l_int32 **pbmap, - l_int32 **pamap) -{ -l_int32 *rmap, *gmap, *bmap, *amap; -l_int32 i, ncolors; -RGBA_QUAD *cta; - - PROCNAME("pixcmapToArrays"); - - if (!prmap || !pgmap || !pbmap) - return ERROR_INT("&rmap, &gmap, &bmap not all defined", procName, 1); - *prmap = *pgmap = *pbmap = NULL; - if (pamap) *pamap = NULL; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - ncolors = pixcmapGetCount(cmap); - rmap = (l_int32 *)LEPT_CALLOC(ncolors, sizeof(l_int32)); - gmap = (l_int32 *)LEPT_CALLOC(ncolors, sizeof(l_int32)); - bmap = (l_int32 *)LEPT_CALLOC(ncolors, sizeof(l_int32)); - *prmap = rmap; - *pgmap = gmap; - *pbmap = bmap; - if (pamap) { - amap = (l_int32 *)LEPT_CALLOC(ncolors, sizeof(l_int32)); - *pamap = amap; - } - - cta = (RGBA_QUAD *)cmap->array; - for (i = 0; i < ncolors; i++) { - rmap[i] = cta[i].red; - gmap[i] = cta[i].green; - bmap[i] = cta[i].blue; - if (pamap) - amap[i] = cta[i].alpha; - } - - return 0; -} - - -/*! - * \brief pixcmapToRGBTable() - * - * \param[in] cmap colormap - * \param[out] ptab table of rgba values for the colormap - * \param[out] pncolors [optional] size of table - * \return 0 if OK; 1 on error - */ -l_ok -pixcmapToRGBTable(PIXCMAP *cmap, - l_uint32 **ptab, - l_int32 *pncolors) -{ -l_int32 i, ncolors, rval, gval, bval, aval; -l_uint32 *tab; - - PROCNAME("pixcmapToRGBTable"); - - if (!ptab) - return ERROR_INT("&tab not defined", procName, 1); - *ptab = NULL; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - ncolors = pixcmapGetCount(cmap); - if (pncolors) *pncolors = ncolors; - tab = (l_uint32 *)LEPT_CALLOC(ncolors, sizeof(l_uint32)); - *ptab = tab; - - for (i = 0; i < ncolors; i++) { - pixcmapGetRGBA(cmap, i, &rval, &gval, &bval, &aval); - composeRGBAPixel(rval, gval, bval, aval, &tab[i]); - } - return 0; -} - - -/*! - * \brief pixcmapSerializeToMemory() - * - * \param[in] cmap colormap - * \param[in] cpc components/color: 3 for rgb, 4 for rgba - * \param[out] pncolors number of colors in table - * \param[out] pdata binary string, cpc bytes per color - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) When serializing to store in a pdf, use %cpc = 3. - *- */ -l_ok -pixcmapSerializeToMemory(PIXCMAP *cmap, - l_int32 cpc, - l_int32 *pncolors, - l_uint8 **pdata) -{ -l_int32 i, ncolors, rval, gval, bval, aval; -l_uint8 *data; - - PROCNAME("pixcmapSerializeToMemory"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pncolors) - return ERROR_INT("&ncolors not defined", procName, 1); - *pncolors = 0; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - if (cpc != 3 && cpc != 4) - return ERROR_INT("cpc not 3 or 4", procName, 1); - - ncolors = pixcmapGetCount(cmap); - *pncolors = ncolors; - data = (l_uint8 *)LEPT_CALLOC((size_t)cpc * ncolors, sizeof(l_uint8)); - *pdata = data; - - for (i = 0; i < ncolors; i++) { - pixcmapGetRGBA(cmap, i, &rval, &gval, &bval, &aval); - data[cpc * i] = rval; - data[cpc * i + 1] = gval; - data[cpc * i + 2] = bval; - if (cpc == 4) - data[cpc * i + 3] = aval; - } - return 0; -} - - -/*! - * \brief pixcmapDeserializeFromMemory() - * - * \param[in] data binary string, 3 or 4 bytes per color - * \param[in] cpc components/color: 3 for rgb, 4 for rgba - * \param[in] ncolors - * \return cmap, or NULL on error - */ -PIXCMAP * -pixcmapDeserializeFromMemory(l_uint8 *data, - l_int32 cpc, - l_int32 ncolors) -{ -l_int32 i, d, rval, gval, bval, aval; -PIXCMAP *cmap; - - PROCNAME("pixcmapDeserializeFromMemory"); - - if (!data) - return (PIXCMAP *)ERROR_PTR("data not defined", procName, NULL); - if (cpc != 3 && cpc != 4) - return (PIXCMAP *)ERROR_PTR("cpc not 3 or 4", procName, NULL); - if (ncolors == 0) - return (PIXCMAP *)ERROR_PTR("no entries", procName, NULL); - if (ncolors > 256) - return (PIXCMAP *)ERROR_PTR("ncolors > 256", procName, NULL); - - if (ncolors > 16) - d = 8; - else if (ncolors > 4) - d = 4; - else if (ncolors > 2) - d = 2; - else - d = 1; - cmap = pixcmapCreate(d); - for (i = 0; i < ncolors; i++) { - rval = data[cpc * i]; - gval = data[cpc * i + 1]; - bval = data[cpc * i + 2]; - if (cpc == 4) - aval = data[cpc * i + 3]; - else - aval = 255; /* opaque */ - pixcmapAddRGBA(cmap, rval, gval, bval, aval); - } - - return cmap; -} - - -/*! - * \brief pixcmapConvertToHex() - * - * \param[in] data binary serialized data - * \param[in] ncolors in colormap - * \return hexdata bracketed, space-separated ascii hex string, - * or NULL on error. - * - *
- * Notes: - * (1) The number of bytes in %data is 3 * ncolors. - * (2) Output is in form: - * < r0g0b0 r1g1b1 ... rngnbn > - * where r0, g0, b0 ... are each 2 bytes of hex ascii - * (3) This is used in pdf files to express the colormap as an - * array in ascii (human-readable) format. - *- */ -char * -pixcmapConvertToHex(l_uint8 *data, - l_int32 ncolors) -{ -l_int32 i, j, hexbytes; -char *hexdata = NULL; -char buf[4]; - - PROCNAME("pixcmapConvertToHex"); - - if (!data) - return (char *)ERROR_PTR("data not defined", procName, NULL); - if (ncolors < 1) - return (char *)ERROR_PTR("no colors", procName, NULL); - - hexbytes = 2 + (2 * 3 + 1) * ncolors + 2; - hexdata = (char *)LEPT_CALLOC(hexbytes, sizeof(char)); - hexdata[0] = '<'; - hexdata[1] = ' '; - - for (i = 0; i < ncolors; i++) { - j = 2 + (2 * 3 + 1) * i; - snprintf(buf, sizeof(buf), "%02x", data[3 * i]); - hexdata[j] = buf[0]; - hexdata[j + 1] = buf[1]; - snprintf(buf, sizeof(buf), "%02x", data[3 * i + 1]); - hexdata[j + 2] = buf[0]; - hexdata[j + 3] = buf[1]; - snprintf(buf, sizeof(buf), "%02x", data[3 * i + 2]); - hexdata[j + 4] = buf[0]; - hexdata[j + 5] = buf[1]; - hexdata[j + 6] = ' '; - } - hexdata[j + 7] = '>'; - hexdata[j + 8] = '\0'; - return hexdata; -} - - -/*-------------------------------------------------------------* - * Colormap transforms * - *-------------------------------------------------------------*/ -/*! - * \brief pixcmapGammaTRC() - * - * \param[in] cmap colormap - * \param[in] gamma gamma correction; must be > 0.0 - * \param[in] minval input value that gives 0 for output; can be < 0 - * \param[in] maxval input value that gives 255 for output; can be > 255 - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This is an in-place transform - * (2) See pixGammaTRC() and numaGammaTRC() in enhance.c - * for description and use of transform - *- */ -l_ok -pixcmapGammaTRC(PIXCMAP *cmap, - l_float32 gamma, - l_int32 minval, - l_int32 maxval) -{ -l_int32 rval, gval, bval, trval, tgval, tbval, i, ncolors; -NUMA *nag; - - PROCNAME("pixcmapGammaTRC"); - - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - if (gamma <= 0.0) { - L_WARNING("gamma must be > 0.0; setting to 1.0\n", procName); - gamma = 1.0; - } - if (minval >= maxval) - return ERROR_INT("minval not < maxval", procName, 1); - - if (gamma == 1.0 && minval == 0 && maxval == 255) /* no-op */ - return 0; - - if ((nag = numaGammaTRC(gamma, minval, maxval)) == NULL) - return ERROR_INT("nag not made", procName, 1); - - ncolors = pixcmapGetCount(cmap); - for (i = 0; i < ncolors; i++) { - pixcmapGetColor(cmap, i, &rval, &gval, &bval); - numaGetIValue(nag, rval, &trval); - numaGetIValue(nag, gval, &tgval); - numaGetIValue(nag, bval, &tbval); - pixcmapResetColor(cmap, i, trval, tgval, tbval); - } - - numaDestroy(&nag); - return 0; -} - - -/*! - * \brief pixcmapContrastTRC() - * - * \param[in] cmap colormap - * \param[in] factor generally between 0.0 [no enhancement] - * and 1.0, but can be larger than 1.0 - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This is an in-place transform - * (2) See pixContrastTRC() and numaContrastTRC() in enhance.c - * for description and use of transform - *- */ -l_ok -pixcmapContrastTRC(PIXCMAP *cmap, - l_float32 factor) -{ -l_int32 i, ncolors, rval, gval, bval, trval, tgval, tbval; -NUMA *nac; - - PROCNAME("pixcmapContrastTRC"); - - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - if (factor < 0.0) { - L_WARNING("factor must be >= 0.0; setting to 0.0\n", procName); - factor = 0.0; - } - - if ((nac = numaContrastTRC(factor)) == NULL) - return ERROR_INT("nac not made", procName, 1); - - ncolors = pixcmapGetCount(cmap); - for (i = 0; i < ncolors; i++) { - pixcmapGetColor(cmap, i, &rval, &gval, &bval); - numaGetIValue(nac, rval, &trval); - numaGetIValue(nac, gval, &tgval); - numaGetIValue(nac, bval, &tbval); - pixcmapResetColor(cmap, i, trval, tgval, tbval); - } - - numaDestroy(&nac); - return 0; -} - - -/*! - * \brief pixcmapShiftIntensity() - * - * \param[in] cmap colormap - * \param[in] fraction between -1.0 and +1.0 - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This is an in-place transform - * (2) It does a proportional shift of the intensity for each color. - * (3) If fraction < 0.0, it moves all colors towards (0,0,0). - * This darkens the image. - * If fraction > 0.0, it moves all colors towards (255,255,255) - * This fades the image. - * (4) The equivalent transform can be accomplished with pixcmapGammaTRC(), - * but it is considerably more difficult (see numaGammaTRC()). - *- */ -l_ok -pixcmapShiftIntensity(PIXCMAP *cmap, - l_float32 fraction) -{ -l_int32 i, ncolors, rval, gval, bval; - - PROCNAME("pixcmapShiftIntensity"); - - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - if (fraction < -1.0 || fraction > 1.0) - return ERROR_INT("fraction not in [-1.0, 1.0]", procName, 1); - - ncolors = pixcmapGetCount(cmap); - for (i = 0; i < ncolors; i++) { - pixcmapGetColor(cmap, i, &rval, &gval, &bval); - if (fraction < 0.0) - pixcmapResetColor(cmap, i, - (l_int32)((1.0 + fraction) * rval), - (l_int32)((1.0 + fraction) * gval), - (l_int32)((1.0 + fraction) * bval)); - else - pixcmapResetColor(cmap, i, - rval + (l_int32)(fraction * (255 - rval)), - gval + (l_int32)(fraction * (255 - gval)), - bval + (l_int32)(fraction * (255 - bval))); - } - - return 0; -} - - -/*! - * \brief pixcmapShiftByComponent() - * - * \param[in] cmap colormap - * \param[in] srcval source color: 0xrrggbb00 - * \param[in] dstval target color: 0xrrggbb00 - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This is an in-place transform - * (2) It implements pixelShiftByComponent() for each color. - * The mapping is specified by srcval and dstval. - * (3) If a component decreases, the component in the colormap - * decreases by the same ratio. Likewise for increasing, except - * all ratios are taken with respect to the distance from 255. - *- */ -l_ok -pixcmapShiftByComponent(PIXCMAP *cmap, - l_uint32 srcval, - l_uint32 dstval) -{ -l_int32 i, ncolors, rval, gval, bval; -l_uint32 newval; - - PROCNAME("pixcmapShiftByComponent"); - - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - ncolors = pixcmapGetCount(cmap); - for (i = 0; i < ncolors; i++) { - pixcmapGetColor(cmap, i, &rval, &gval, &bval); - pixelShiftByComponent(rval, gval, bval, srcval, dstval, &newval); - extractRGBValues(newval, &rval, &gval, &bval); - pixcmapResetColor(cmap, i, rval, gval, bval); - } - - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colormorph.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colormorph.c deleted file mode 100644 index e59b7891..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colormorph.c +++ /dev/null @@ -1,128 +0,0 @@ -/*====================================================================* - - 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 colormorph.c - *
- * - * Top-level color morphological operations - * - * PIX *pixColorMorph() - * - * Method: Algorithm by van Herk and Gil and Werman, 1992 - * Apply grayscale morphological operations separately - * to each component. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This does the morph operation on each component separately, - * and recombines the result. - * (2) Sel is a brick with all elements being hits. - * (3) If hsize = vsize = 1, just returns a copy. - *- */ -PIX * -pixColorMorph(PIX *pixs, - l_int32 type, - l_int32 hsize, - l_int32 vsize) -{ -PIX *pixr, *pixg, *pixb, *pixrm, *pixgm, *pixbm, *pixd; - - PROCNAME("pixColorMorph"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (type != L_MORPH_DILATE && type != L_MORPH_ERODE && - type != L_MORPH_OPEN && type != L_MORPH_CLOSE) - return (PIX *)ERROR_PTR("invalid morph type", procName, NULL); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); - if ((hsize & 1) == 0 ) { - L_WARNING("horiz sel size must be odd; increasing by 1\n", procName); - hsize++; - } - if ((vsize & 1) == 0 ) { - L_WARNING("vert sel size must be odd; increasing by 1\n", procName); - vsize++; - } - - if (hsize == 1 && vsize == 1) - return pixCopy(NULL, pixs); - - pixr = pixGetRGBComponent(pixs, COLOR_RED); - pixg = pixGetRGBComponent(pixs, COLOR_GREEN); - pixb = pixGetRGBComponent(pixs, COLOR_BLUE); - if (type == L_MORPH_DILATE) { - pixrm = pixDilateGray(pixr, hsize, vsize); - pixgm = pixDilateGray(pixg, hsize, vsize); - pixbm = pixDilateGray(pixb, hsize, vsize); - } else if (type == L_MORPH_ERODE) { - pixrm = pixErodeGray(pixr, hsize, vsize); - pixgm = pixErodeGray(pixg, hsize, vsize); - pixbm = pixErodeGray(pixb, hsize, vsize); - } else if (type == L_MORPH_OPEN) { - pixrm = pixOpenGray(pixr, hsize, vsize); - pixgm = pixOpenGray(pixg, hsize, vsize); - pixbm = pixOpenGray(pixb, hsize, vsize); - } else { /* type == L_MORPH_CLOSE */ - pixrm = pixCloseGray(pixr, hsize, vsize); - pixgm = pixCloseGray(pixg, hsize, vsize); - pixbm = pixCloseGray(pixb, hsize, vsize); - } - pixd = pixCreateRGBImage(pixrm, pixgm, pixbm); - pixDestroy(&pixr); - pixDestroy(&pixrm); - pixDestroy(&pixg); - pixDestroy(&pixgm); - pixDestroy(&pixb); - pixDestroy(&pixbm); - - return pixd; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colorquant1.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colorquant1.c deleted file mode 100644 index 52ddd386..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colorquant1.c +++ /dev/null @@ -1,4155 +0,0 @@ -/*====================================================================* - - 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 colorquant1.c - *
- * - * Octcube color quantization - * - * There are several different octcube/octree based quantizations. - * These can be classified, in the order in which they appear in this - * file, as follows: - * - * ----------------------------------------------------------------- - * (1) General adaptive octree - * (2) Adaptive octree by population at fixed level - * (3) Adaptive octree using population and with specified number - * of output colors - * (4) Octcube with colormap representation of mixed color/gray - * (5) 256 fixed octcubes covering color space - * (6) Octcubes at fixed level for ncolors <= 256 - * (7) Octcubes at fixed level with RGB output - * (8) Quantizing an rgb image using a specified colormap - * ----------------------------------------------------------------- - * - * (1) Two-pass adaptive octree color quantization - * PIX *pixOctreeColorQuant() - * PIX *pixOctreeColorQuantGeneral() - * - * which calls - * static CQCELL ***octreeGenerateAndPrune() - * static PIX *pixOctreeQuantizePixels() - * - * which calls - * static l_int32 octreeFindColorCell() - * - * Helper cqcell functions - * static CQCELL ***cqcellTreeCreate() - * static void cqcellTreeDestroy() - * - * Helper index functions - * l_int32 makeRGBToIndexTables() - * void getOctcubeIndexFromRGB() - * static void getRGBFromOctcube() - * static l_int32 getOctcubeIndices() - * static l_int32 octcubeGetCount() - * - * (2) Adaptive octree quantization based on population at a fixed level - * PIX *pixOctreeQuantByPopulation() - * static l_int32 pixDitherOctindexWithCmap() - * - * (3) Adaptive octree quantization to 4 and 8 bpp with specified - * number of output colors in colormap - * PIX *pixOctreeQuantNumColors() - * - * (4) Mixed color/gray quantization with specified number of colors - * PIX *pixOctcubeQuantMixedWithGray() - * - * (5) Fixed partition octcube quantization with 256 cells - * PIX *pixFixedOctcubeQuant256() - * - * (6) Fixed partition quantization for images with few colors - * PIX *pixFewColorsOctcubeQuant1() - * PIX *pixFewColorsOctcubeQuant2() - * PIX *pixFewColorsOctcubeQuantMixed() - * - * (7) Fixed partition octcube quantization at specified level - * with quantized output to RGB - * PIX *pixFixedOctcubeQuantGenRGB() - * - * (8) Color quantize RGB image using existing colormap - * PIX *pixQuantFromCmap() [high-level wrapper] - * PIX *pixOctcubeQuantFromCmap() - * static PIX *pixOctcubeQuantFromCmapLUT() - * - * Generation of octcube histogram - * NUMA *pixOctcubeHistogram() - * - * Get filled octcube table from colormap - * l_int32 *pixcmapToOctcubeLUT() - * - * Strip out unused elements in colormap - * l_int32 pixRemoveUnusedColors() - * - * Find number of occupied octcubes at the specified level - * l_int32 pixNumberOccupiedOctcubes() - * - * Notes: - * Leptonica also provides color quantization using a modified - * form of median cut. See colorquant2.c for details. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * This data structure is used for pixOctreeColorQuant(), - * a color octree that adjusts to the color distribution - * in the image that is being quantized. The best settings - * are with CqNLevels = 6 and DITHERING set on. - * - * Notes: - * (1) the CTE (color table entry) index is sequentially - * assigned as the tree is pruned back - * (2) if 'bleaf' == 1, all pixels in that cube have been - * assigned to one or more CTEs. But note that if - * all 8 subcubes have 'bleaf' == 1, it will have no - * pixels left for assignment and will not be a CTE. - * (3) 'nleaves', the number of leaves contained at the next - * lower level is some number between 0 and 8, inclusive. - * If it is zero, it means that all colors within this cube - * are part of a single growing cluster that has not yet - * been set aside as a leaf. If 'nleaves' > 0, 'bleaf' - * will be set to 1 and all pixels not assigned to leaves - * at lower levels will be assigned to a CTE here. - * (However, as described above, if all pixels are already - * assigned, we set 'bleaf' = 1 but do not create a CTE - * at this level.) - * (4) To keep the maximum color error to a minimum, we - * prune the tree back to level 2, and require that - * all 64 level 2 cells are CTEs. - * (5) We reserve an extra set of colors to prevent running out - * of colors during the assignment of the final 64 level 2 cells. - * This is more likely to happen with small images. - * (6) When we run out of colors, the dithered image can be very - * poor, so we additionally prevent dithering if the image - * is small. - * (7) The color content of the image is measured, and if there - * is very little color, it is quantized in grayscale. - *- */ -struct ColorQuantCell -{ - l_int32 rc, gc, bc; /* center values */ - l_int32 n; /* number of samples in this cell */ - l_int32 index; /* CTE (color table entry) index */ - l_int32 nleaves; /* # of leaves contained at next lower level */ - l_int32 bleaf; /* boolean: 0 if not a leaf, 1 if so */ -}; -typedef struct ColorQuantCell CQCELL; - - /* Constants for pixOctreeColorQuant() */ -static const l_int32 CqNLevels = 5; /* only 4, 5 and 6 are allowed */ -static const l_int32 CqReservedColors = 64; /* to allow for level 2 */ - /* remainder CTEs */ -static const l_int32 ExtraReservedColors = 25; /* to avoid running out */ -static const l_int32 TreeGenWidth = 350; /* big enough for good stats */ -static const l_int32 MinDitherSize = 250; /* don't dither if smaller */ - -/* - *
- * This data structure is used for pixOctreeQuantNumColors(), - * a color octree that adjusts in a simple way to the to the color - * distribution in the image that is being quantized. It outputs - * colormapped images, either 4 bpp or 8 bpp, depending on the - * max number of colors and the compression desired. - * - * The number of samples is saved as a float in the first location, - * because this is required to use it as the key that orders the - * cells in the priority queue. - *- * */ -struct OctcubeQuantCell -{ - l_float32 n; /* number of samples in this cell */ - l_int32 octindex; /* octcube index */ - l_int32 rcum, gcum, bcum; /* cumulative values */ - l_int32 rval, gval, bval; /* average values */ -}; -typedef struct OctcubeQuantCell OQCELL; - -/* - *
- * This data structure is using for heap sorting octcubes - * by population. Sort order is decreasing. - *- */ -struct L_OctcubePop -{ - l_float32 npix; /* parameter on which to sort */ - l_int32 index; /* octcube index at assigned level */ - l_int32 rval; /* mean red value of pixels in octcube */ - l_int32 gval; /* mean green value of pixels in octcube */ - l_int32 bval; /* mean blue value of pixels in octcube */ -}; -typedef struct L_OctcubePop L_OCTCUBE_POP; - -/* - *
- * In pixDitherOctindexWithCmap(), we use these default values. - To get the max value of 'dif' in the dithering color transfer, - divide these "DIF_CAP" values by 8. However, a value of - 0 means that there is no cap (infinite cap). A very small - value is used for POP_DIF_CAP because dithering on the population - generated colormap can be unstable without a tight cap. - *- */ - -static const l_int32 FIXED_DIF_CAP = 0; -static const l_int32 POP_DIF_CAP = 40; - - - /* Static octree helper function */ -static l_int32 octreeFindColorCell(l_int32 octindex, CQCELL ***cqcaa, - l_int32 *pindex, l_int32 *prval, - l_int32 *pgval, l_int32 *pbval); - - /* Static cqcell functions */ -static CQCELL ***octreeGenerateAndPrune(PIX *pixs, l_int32 colors, - l_int32 reservedcolors, - PIXCMAP **pcmap); -static PIX *pixOctreeQuantizePixels(PIX *pixs, CQCELL ***cqcaa, - l_int32 ditherflag); -static CQCELL ***cqcellTreeCreate(void); -static void cqcellTreeDestroy(CQCELL ****pcqcaa); - - /* Static helper octcube index functions */ -static void getRGBFromOctcube(l_int32 cubeindex, l_int32 level, - l_int32 *prval, l_int32 *pgval, l_int32 *pbval); -static l_int32 getOctcubeIndices(l_int32 rgbindex, l_int32 level, - l_int32 *pbindex, l_int32 *psindex); -static l_int32 octcubeGetCount(l_int32 level, l_int32 *psize); - - /* Static function to perform octcube-indexed dithering */ -static l_int32 pixDitherOctindexWithCmap(PIX *pixs, PIX *pixd, l_uint32 *rtab, - l_uint32 *gtab, l_uint32 *btab, - l_int32 *carray, l_int32 difcap); - - /* Static function to perform octcube-based quantizing from colormap */ -static PIX *pixOctcubeQuantFromCmapLUT(PIX *pixs, PIXCMAP *cmap, - l_int32 mindepth, l_int32 *cmaptab, - l_uint32 *rtab, l_uint32 *gtab, - l_uint32 *btab); - -#ifndef NO_CONSOLE_IO -#define DEBUG_COLORQUANT 0 -#define DEBUG_OCTINDEX 0 -#define DEBUG_OCTCUBE_CMAP 0 -#define DEBUG_POP 0 -#define DEBUG_FEW_COLORS 0 -#define PRINT_OCTCUBE_STATS 0 -#endif /* ~NO_CONSOLE_IO */ - -/*-------------------------------------------------------------------------* - * Two-pass adaptive octree color quantization * - *-------------------------------------------------------------------------*/ -/*! - * \brief pixOctreeColorQuant() - * - * \param[in] pixs 32 bpp; 24-bit color - * \param[in] colors in colormap; some number in range [128 ... 256]; - * the actual number of colors used will be smaller - * \param[in] ditherflag 1 to dither, 0 otherwise - * \return pixd 8 bpp with colormap, or NULL on error - * - *
- * I found one description in the literature of octree color - * quantization, using progressive truncation of the octree, - * by M. Gervautz and W. Purgathofer in Graphics Gems, pp. - * 287-293, ed. A. Glassner, Academic Press, 1990. - * Rather than setting up a fixed partitioning of the color - * space ab initio, as we do here, they allow the octree to be - * progressively truncated as new pixels are added. They - * need to set up some data structures that are traversed - * with the addition of each 24 bit pixel, in order to decide - * either 1) in which cluster (sub-branch of the octree to put - * the pixel, or 2 whether to truncate the octree further - * to place the pixel in an existing cluster, or 3 which - * two existing clusters should be merged so that the pixel - * can be left to start a truncated leaf of the octree. Such dynamic - * truncation is considerably more complicated, and Gervautz et - * al. did not explain how they did it in anywhere near the - * detail required to check their implementation. - * - * The simple method in pixFixedOctcubeQuant256 is very - * fast, and with dithering the results are good, but you - * can do better if the color clusters are selected adaptively - * from the image. We want a method that makes much better - * use of color samples in regions of color space with high - * pixel density, while also fairly representing small numbers - * of color pixels in low density regions. Such adaptation - * requires two passes through the image: the first for generating - * the pruned tree of color cubes and the second for computing the index - * into the color table for each pixel. - * - * A relatively simple adaptive method is pixOctreeQuantByPopulation. - * That function first determines if the image has very few colors, - * and, if so, quantizes to those colors. If there are more than - * 256 colors, it generates a histogram of octcube leaf occupancy - * at level 4, chooses the 192 most populated such leaves as - * the first 192 colors, and sets the remaining 64 colors to the - * residual average pixel values in each of the 64 level 2 octcubes. - * This is a bit faster than pixOctreeColorQuant, and does very - * well without dithering, but for most images with dithering it - * is clearly inferior. - * - * We now describe pixOctreeColorQuant. The first pass is done - * on a subsampled image, because we do not need to use all the - * pixels in the image to generate the tree. Subsampling - * down to 0.25 1/16 of the pixels makes the program run - * about 1.3 times faster. - * - * Instead of dividing the color space into 256 equal-sized - * regions, we initially divide it into 2^12 or 2^15 or 2^18 - * equal-sized octcubes. Suppose we choose to use 2^18 octcubes. - * This gives us 6 octree levels. We then prune back, - * starting from level 6. For every cube at level 6, there - * are 8 cubes at level 5. Call the operation of putting a - * cube aside as a color table entry CTE a "saving." - * We use a in general level-dependent threshold, and save - * those level 6 cubes that are above threshold. - * The rest are combined into the containing level 5 cube. - * If between 1 and 7 level 6 cubes within a level 5 - * cube have been saved by thresholding, then the remaining - * level 6 cubes in that level 5 cube are automatically - * saved as well, without applying a threshold. This greatly - * simplifies both the description of the CTEs and the later - * classification of each pixel as belonging to a CTE. - * This procedure is iterated through every cube, starting at - * level 5, and then 4, 3, and 2, successively. The result is that - * each CTE contains the entirety of a set of from 1 to 7 cubes - * from a given level that all belong to a single cube at the - * level above. We classify the CTEs in terms of the - * condition in which they are made as either being "threshold" - * or "residual." They are "threshold" CTEs if no subcubes - * are CTEs that is, they contain every pixel within the cube - * and the number of pixels exceeds the threshold for making - * a CTE. They are "residual" CTEs if at least one but not more - * than 7 of the subcubes have already been determined to be CTEs; - * this happens automatically -- no threshold is applied. - * If all 8 subcubes are determined to be CTEs, the cube is - * marked as having all pixels accounted for 'bleaf' = 1 but - * is not saved as a CTE. - * - * We stop the pruning at level 2, at which there are 64 - * sub-cubes. Any pixels not already claimed in a CTE are - * put in these cubes. - * - * As the cubes are saved as color samples in the color table, - * the number of remaining pixels P and the number of - * remaining colors in the color table N are recomputed, - * along with the average number of pixels P/N ppc to go in - * each of the remaining colors. This running average number is - * used to set the threshold at the current level. - * - * Because we are going to very small cubes at levels 6 or 5, - * and will dither the colors for errors, it is not necessary - * to compute the color center of each cluster; we can simply - * use the center of the cube. This gives us a minimax error - * condition: the maximum error is half the width of the - * level 2 cubes -- 32 color values out of 256 -- for each color - * sample. In practice, most of the pixels will be very much - * closer to the center of their cells. And with dithering, - * the average pixel color in a small region will be closer still. - * Thus with the octree quantizer, we are able to capture - * regions of high color pdf probability density function in small - * but accurate CTEs, and to have only a small number of pixels - * that end up a significant distance with a guaranteed maximum - * from their true color. - * - * How should the threshold factor vary? Threshold factors - * are required for levels 2, 3, 4 and 5 in the pruning stage. - * The threshold for level 5 is actually applied to cubes at - * level 6, etc. From various experiments, it appears that - * the results do not vary appreciably for threshold values near 1.0. - * If you want more colors in smaller cubes, the threshold - * factors can be set lower than 1.0 for cubes at levels 4 and 5. - * However, if the factor is set much lower than 1.0 for - * levels 2 and 3, we can easily run out of colors. - * We put aside 64 colors in the calculation of the threshold - * values, because we must have 64 color centers at level 2, - * that will have very few pixels in most of them. - * If we reduce the factor for level 5 to 0.4, this will - * generate many level 6 CTEs, and consequently - * many residual cells will be formed up from those leaves, - * resulting in the possibility of running out of colors. - * Remember, the residual CTEs are mandatory, and are formed - * without using the threshold, regardless of the number of - * pixels that are absorbed. - * - * The implementation logically has four parts: - * - * 1 accumulation into small, fixed cells - * 2 pruning back into selected CTE cubes - * 3 organizing the CTEs for fast search to find - * the CTE to which any image pixel belongs - * 4 doing a second scan to code the image pixels by CTE - * - * Step 1 is straightforward; we use 2^15 cells. - * - * We've already discussed how the pruning step 2 will be performed. - * - * Steps 3) and (4 are related, in that the organization - * used by step 3 determines how the search actually - * takes place for each pixel in step 4. - * - * There are many ways to do step 3. Let's explore a few. - * - * a The simplest is to order the cubes from highest occupancy - * to lowest, and traverse the list looking for the deepest - * match. To make this more efficient, so that we know when - * to stop looking, any cube that has separate CTE subcubes - * would be marked as such, so that we know when we hit a - * true leaf. - * - * b Alternatively, we can order the cubes by highest - * occupancy separately each level, and work upward, - * starting at level 5, so that when we find a match we - * know that it will be correct. - * - * c Another approach would be to order the cubes by - * "address" and use a hash table to find the cube - * corresponding to a pixel color. I don't know how to - * do this with a variable length address, as each CTE - * will have 3*n bits, where n is the level. - * - * d Another approach entirely is to put the CTE cubes into - * a tree, in such a way that starting from the root, and - * using 3 bits of address at a time, the correct branch of - * each octree can be taken until a leaf is found. Because - * a given cube can be both a leaf and also have branches - * going to sub-cubes, the search stops only when no - * marked subcubes have addresses that match the given pixel. - * - * In the tree method, we can start with a dense infrastructure, - * and place the leaves corresponding to the N colors - * in the tree, or we can grow from the root only those - * branches that end directly on leaves. - * - * What we do here is to take approach d, and implement the tree - * "virtually", as a set of arrays, one array for each level - * of the tree. Initially we start at level 5, an array with - * 2^15 cubes, each with 8 subcubes. We then build nodes at - * levels closer to the root; at level 4 there are 2^12 nodes - * each with 8 subcubes; etc. Using these arrays has - * several advantages: - * - * ~ We don't need to keep track of links between cubes - * and subcubes, because we can use the canonical - * addressing on the cell arrays directly to determine - * which nodes are parent cubes and which are sub-cubes. - * - * ~ We can prune directly on this tree - * - * ~ We can navigate the pruned tree quickly to classify - * each pixel in the image. - * - * Canonical addressing guarantees that the i-th node at level k - * has 8 subnodes given by the 8*i ... 8*i+7 nodes at level k+1. - * - * The pruning step works as follows. We go from the lowest - * level up. At each level, the threshold is found from the - * product of a factor near 1.0 and the ratio of unmarked pixels - * to remaining colors minus the 64. We march through - * the space, sequentially considering a cube and its 8 subcubes. - * We first check those subcubes that are not already - * marked as CTE to see if any are above threshold, and if so, - * generate a CTE and mark them as such. - * We then determine if any of the subcubes have been marked. - * If so, and there are subcubes that are not marked, - * we generate a CTE for the cube from the remaining unmarked - * subcubes; this is mandatory and does not depend on how many - * pixels are in the set of subcubes. If none of the subcubes - * are marked, we aggregate their pixels into the cube - * containing them, but do not mark it as a CTE; that - * will be determined when iterating through the next level up. - * - * When all the pixels in a cube are accounted for in one or more - * colors, we set the boolean 'bleaf' to true. This is the - * flag used to mark the cubes in the pruning step. If a cube - * is marked, and all 8 subcubes are marked, then it is not - * itself given a CTE because all pixels have already been - * accounted for. - * - * Note that the pruning of the tree and labelling of the CTEs - * step 2 accomplishes step 3 implicitly, because the marked - * and pruned tree is ready for use in labelling each pixel - * in step 4. We now, for every pixel in the image, traverse - * the tree from the root, looking for the lowest cube that is a leaf. - * At each level we have a cube and subcube. If we reach a subcube - * leaf that is marked 0, we know that the color is stored in the - * cube above, and we've found the CTE. Otherwise, the subcube - * leaf is marked 1. If we're at the last level, we've reached - * the final leaf and must use it. Otherwise, continue the - * process at the next level down. - * - * For robustness, efficiency and high quality output, we do the following: - * - * (1) Measure the color content of the image. If there is very little - * color, quantize in grayscale. - * (2) For efficiency, build the octree with a subsampled image if the - * image is larger than some threshold size. - * (3) Reserve an extra set of colors to prevent running out of colors - * when pruning the octree; specifically, during the assignment - * of those level 2 cells out of the 64 that have unassigned - * pixels. The problem of running out is more likely to happen - * with small images, because the estimation we use for the - * number of pixels available is not accurate. - * (4) In the unlikely event that we run out of colors, the dithered - * image can be very poor. As this would only happen with very - * small images, and dithering is not particularly noticeable with - * such images, turn it off. - *- */ -PIX * -pixOctreeColorQuant(PIX *pixs, - l_int32 colors, - l_int32 ditherflag) -{ - PROCNAME("pixOctreeColorQuant"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (colors < 128 || colors > 240) /* further restricted */ - return (PIX *)ERROR_PTR("colors must be in [128, 240]", procName, NULL); - - return pixOctreeColorQuantGeneral(pixs, colors, ditherflag, 0.01, 0.01); -} - - -/*! - * \brief pixOctreeColorQuantGeneral() - * - * \param[in] pixs 32 bpp; 24-bit color - * \param[in] colors in colormap; some number in range [128 ... 240]; - * the actual number of colors used will be smaller - * \param[in] ditherflag 1 to dither, 0 otherwise - * \param[in] validthresh minimum fraction of pixels neither near white - * nor black, required for color quantization; - * typically ~0.01, but smaller for images that have - * color but are nearly all white - * \param[in] colorthresh minimum fraction of pixels with color that are - * not near white or black, that are required - * for color quantization; typ. ~0.01, but smaller - * for images that have color along with a - * significant fraction of gray - * \return pixd 8 bit with colormap, or NULL on error - * - *
- * Notes: - * (1) The parameters %validthresh and %colorthresh are used to - * determine if color quantization should be used on an image, - * or whether, instead, it should be quantized in grayscale. - * If the image has very few non-white and non-black pixels, or - * if those pixels that are non-white and non-black are all - * very close to either white or black, it is usually better - * to treat the color as accidental and to quantize the image - * to gray only. These parameters are useful if you know - * something a priori about the image. Perhaps you know that - * there is only a very small fraction of color pixels, but they're - * important to preserve; then you want to use a smaller value for - * these parameters. To disable conversion to gray and force - * color quantization, use %validthresh = 0.0 and %colorthresh = 0.0. - * (2) See pixOctreeColorQuant() for algorithmic and implementation - * details. This function has a more general interface. - * (3) See pixColorFraction() for computing the fraction of pixels - * that are neither white nor black, and the fraction of those - * pixels that have little color. From the documentation there: - * If pixfract is very small, there are few pixels that are - * neither black nor white. If colorfract is very small, - * the pixels that are neither black nor white have very - * little color content. The product 'pixfract * colorfract' - * gives the fraction of pixels with significant color content. - * We test against the product %validthresh * %colorthresh - * to find color in images that have either very few - * intermediate gray pixels or that have many such gray pixels. - *- */ -PIX * -pixOctreeColorQuantGeneral(PIX *pixs, - l_int32 colors, - l_int32 ditherflag, - l_float32 validthresh, - l_float32 colorthresh) -{ -l_int32 w, h, minside, factor, index, rval, gval, bval; -l_float32 scalefactor; -l_float32 pixfract; /* fraction neither near white nor black */ -l_float32 colorfract; /* fraction with color of the pixfract population */ -CQCELL ***cqcaa; -PIX *pixd, *pixsub; -PIXCMAP *cmap; - - PROCNAME("pixOctreeColorQuantGeneral"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (colors < 128 || colors > 240) - return (PIX *)ERROR_PTR("colors must be in [128, 240]", procName, NULL); - - /* Determine if the image has sufficient color content for - * octree quantization, based on the input thresholds. - * If pixfract << 1, most pixels are close to black or white. - * If colorfract << 1, the pixels that are not near - * black or white have very little color. - * If with insufficient color, quantize with a grayscale colormap. */ - pixGetDimensions(pixs, &w, &h, NULL); - if (validthresh > 0.0 && colorthresh > 0.0) { - minside = L_MIN(w, h); - factor = L_MAX(1, minside / 400); - pixColorFraction(pixs, 20, 244, 20, factor, &pixfract, &colorfract); - if (pixfract * colorfract < validthresh * colorthresh) { - L_INFO("\n Pixel fraction neither white nor black = %6.3f" - "\n Color fraction of those pixels = %6.3f" - "\n Quantizing to 8 bpp gray\n", - procName, pixfract, colorfract); - return pixConvertTo8(pixs, 1); - } - } else { - L_INFO("\n Process in color by default\n", procName); - } - - /* Conditionally subsample to speed up the first pass */ - if (w > TreeGenWidth) { - scalefactor = (l_float32)TreeGenWidth / (l_float32)w; - pixsub = pixScaleBySampling(pixs, scalefactor, scalefactor); - } else { - pixsub = pixClone(pixs); - } - - /* Drop the number of requested colors if image is very small */ - if (w < MinDitherSize && h < MinDitherSize) - colors = L_MIN(colors, 220); - - /* Make the pruned octree */ - cqcaa = octreeGenerateAndPrune(pixsub, colors, CqReservedColors, &cmap); - if (!cqcaa) { - pixDestroy(&pixsub); - return (PIX *)ERROR_PTR("tree not made", procName, NULL); - } -#if DEBUG_COLORQUANT - L_INFO(" Colors requested = %d\n", procName, colors); - L_INFO(" Actual colors = %d\n", procName, cmap->n); -#endif /* DEBUG_COLORQUANT */ - - /* Do not dither if image is very small */ - if (w < MinDitherSize && h < MinDitherSize && ditherflag == 1) { - L_INFO("Small image: dithering turned off\n", procName); - ditherflag = 0; - } - - /* Traverse tree from root, looking for lowest cube - * that is a leaf, and set dest pix value to its - * colortable index */ - if ((pixd = pixOctreeQuantizePixels(pixs, cqcaa, ditherflag)) == NULL) { - pixDestroy(&pixsub); - cqcellTreeDestroy(&cqcaa); - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - - /* Attach colormap and copy res */ - pixSetColormap(pixd, cmap); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - - /* Force darkest color to black if each component <= 4 */ - pixcmapGetRankIntensity(cmap, 0.0, &index); - pixcmapGetColor(cmap, index, &rval, &gval, &bval); - if (rval < 5 && gval < 5 && bval < 5) - pixcmapResetColor(cmap, index, 0, 0, 0); - - /* Force lightest color to white if each component >= 252 */ - pixcmapGetRankIntensity(cmap, 1.0, &index); - pixcmapGetColor(cmap, index, &rval, &gval, &bval); - if (rval > 251 && gval > 251 && bval > 251) - pixcmapResetColor(cmap, index, 255, 255, 255); - - cqcellTreeDestroy(&cqcaa); - pixDestroy(&pixsub); - return pixd; -} - - -/*! - * \brief octreeGenerateAndPrune() - * - * \param[in] pixs - * \param[in] colors number of colors to use between 128 and 256 - * \param[in] reservedcolors number of reserved colors - * \param[out] pcmap colormap returned - * \return octree, colormap and number of colors used, or NULL - * on error - * - *
- * Notes: - * (1) The number of colors in the cmap may differ from the number - * of colors requested, but it will not be larger than 256 - *- */ -static CQCELL *** -octreeGenerateAndPrune(PIX *pixs, - l_int32 colors, - l_int32 reservedcolors, - PIXCMAP **pcmap) -{ -l_int32 rval, gval, bval, cindex; -l_int32 level, ncells, octindex; -l_int32 w, h, wpls; -l_int32 i, j, isub; -l_int32 npix; /* number of remaining pixels to be assigned */ -l_int32 ncolor; /* number of remaining color cells to be used */ -l_int32 ppc; /* ave number of pixels left for each color cell */ -l_int32 rv, gv, bv; -l_float32 thresholdFactor[] = {0.01f, 0.01f, 1.0f, 1.0f, 1.0f, 1.0f}; -l_float32 thresh; /* factor of ppc for this level */ -l_uint32 *datas, *lines; -l_uint32 *rtab, *gtab, *btab; -CQCELL ***cqcaa; /* one array for each octree level */ -CQCELL **cqca, **cqcasub; -CQCELL *cqc, *cqcsub; -PIXCMAP *cmap; -NUMA *nat; /* accumulates levels for threshold cells */ -NUMA *nar; /* accumulates levels for residual cells */ - - PROCNAME("octreeGenerateAndPrune"); - - if (!pixs) - return (CQCELL ***)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (CQCELL ***)ERROR_PTR("pixs must be 32 bpp", procName, NULL); - if (colors < 128 || colors > 256) - return (CQCELL ***)ERROR_PTR("colors not in [128,256]", procName, NULL); - if (!pcmap) - return (CQCELL ***)ERROR_PTR("&cmap not defined", procName, NULL); - - if ((cqcaa = cqcellTreeCreate()) == NULL) - return (CQCELL ***)ERROR_PTR("cqcaa not made", procName, NULL); - - /* Make the canonical index tables */ - rtab = gtab = btab = NULL; - makeRGBToIndexTables(CqNLevels, &rtab, >ab, &btab); - - /* Generate an 8 bpp cmap (max size 256) */ - cmap = pixcmapCreate(8); - *pcmap = cmap; - - pixGetDimensions(pixs, &w, &h, NULL); - npix = w * h; /* initialize to all pixels */ - ncolor = colors - reservedcolors - ExtraReservedColors; - ppc = npix / ncolor; - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - - /* Accumulate the centers of each cluster at level CqNLevels */ - ncells = 1 << (3 * CqNLevels); - cqca = cqcaa[CqNLevels]; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - octindex = rtab[rval] | gtab[gval] | btab[bval]; - cqc = cqca[octindex]; - cqc->n++; - } - } - - /* Arrays for storing statistics */ - nat = numaCreate(0); - nar = numaCreate(0); - - /* Prune back from the lowest level and generate the colormap */ - for (level = CqNLevels - 1; level >= 2; level--) { - thresh = thresholdFactor[level]; - cqca = cqcaa[level]; - cqcasub = cqcaa[level + 1]; - ncells = 1 << (3 * level); - for (i = 0; i < ncells; i++) { /* i is octindex at level */ - cqc = cqca[i]; - for (j = 0; j < 8; j++) { /* check all subnodes */ - isub = 8 * i + j; /* isub is octindex at level+1 */ - cqcsub = cqcasub[isub]; - if (cqcsub->bleaf == 1) { /* already a leaf? */ - cqc->nleaves++; /* count the subcube leaves */ - continue; - } - if (cqcsub->n >= thresh * ppc) { /* make it a true leaf? */ - cqcsub->bleaf = 1; - if (cmap->n < 256) { - cqcsub->index = cmap->n; /* assign the color index */ - getRGBFromOctcube(isub, level + 1, &rv, &gv, &bv); - pixcmapAddColor(cmap, rv, gv, bv); -#if 1 /* save values */ - cqcsub->rc = rv; - cqcsub->gc = gv; - cqcsub->bc = bv; -#endif - } else { - /* This doesn't seem to happen. Do something. */ - L_ERROR("assigning pixels to wrong color\n", procName); - pixcmapGetNearestIndex(cmap, 128, 128, 128, &cindex); - cqcsub->index = cindex; /* assign to the nearest */ - pixcmapGetColor(cmap, cindex, &rval, &gval, &bval); - cqcsub->rc = rval; - cqcsub->gc = gval; - cqcsub->bc = bval; - } - cqc->nleaves++; - npix -= cqcsub->n; - ncolor--; - if (ncolor > 0) - ppc = npix / ncolor; - else if (ncolor + reservedcolors > 0) - ppc = npix / (ncolor + reservedcolors); - else - ppc = 1000000; /* make it big */ - numaAddNumber(nat, level + 1); - -#if DEBUG_OCTCUBE_CMAP - lept_stderr("Exceeds threshold: colors used = %d, colors remaining = %d\n", - cmap->n, ncolor + reservedcolors); - lept_stderr(" cell with %d pixels, npix = %d, ppc = %d\n", - cqcsub->n, npix, ppc); - lept_stderr(" index = %d, level = %d, subindex = %d\n", - i, level, j); - lept_stderr(" rv = %d, gv = %d, bv = %d\n", rv, gv, bv); -#endif /* DEBUG_OCTCUBE_CMAP */ - - } - } - if (cqc->nleaves > 0 || level == 2) { /* make the cube a leaf now */ - cqc->bleaf = 1; - if (cqc->nleaves < 8) { /* residual CTE cube: acquire the - * remaining pixels */ - for (j = 0; j < 8; j++) { /* check all subnodes */ - isub = 8 * i + j; - cqcsub = cqcasub[isub]; - if (cqcsub->bleaf == 0) /* absorb */ - cqc->n += cqcsub->n; - } - if (cmap->n < 256) { - cqc->index = cmap->n; /* assign the color index */ - getRGBFromOctcube(i, level, &rv, &gv, &bv); - pixcmapAddColor(cmap, rv, gv, bv); -#if 1 /* save values */ - cqc->rc = rv; - cqc->gc = gv; - cqc->bc = bv; -#endif - } else { - L_WARNING("possibly assigned pixels to wrong color\n", - procName); - /* This is very bad. It will only cause trouble - * with dithering, and we try to avoid it with - * ExtraReservedColors. */ - pixcmapGetNearestIndex(cmap, rv, gv, bv, &cindex); - cqc->index = cindex; /* assign to the nearest */ - pixcmapGetColor(cmap, cindex, &rval, &gval, &bval); - cqc->rc = rval; - cqc->gc = gval; - cqc->bc = bval; - } - npix -= cqc->n; - ncolor--; - if (ncolor > 0) - ppc = npix / ncolor; - else if (ncolor + reservedcolors > 0) - ppc = npix / (ncolor + reservedcolors); - else - ppc = 1000000; /* make it big */ - numaAddNumber(nar, level); - -#if DEBUG_OCTCUBE_CMAP - lept_stderr("By remainder: colors used = %d, colors remaining = %d\n", - cmap->n, ncolor + reservedcolors); - lept_stderr(" cell with %d pixels, npix = %d, ppc = %d\n", - cqc->n, npix, ppc); - lept_stderr(" index = %d, level = %d\n", i, level); - lept_stderr(" rv = %d, gv = %d, bv = %d\n", rv, gv, bv); -#endif /* DEBUG_OCTCUBE_CMAP */ - - } - } else { /* absorb all the subpixels but don't make it a leaf */ - for (j = 0; j < 8; j++) { /* absorb from all subnodes */ - isub = 8 * i + j; - cqcsub = cqcasub[isub]; - cqc->n += cqcsub->n; - } - } - } - } - -#if PRINT_OCTCUBE_STATS -{ -l_int32 tc[] = {0, 0, 0, 0, 0, 0, 0}; -l_int32 rc[] = {0, 0, 0, 0, 0, 0, 0}; -l_int32 nt, nr, ival; - - nt = numaGetCount(nat); - nr = numaGetCount(nar); - for (i = 0; i < nt; i++) { - numaGetIValue(nat, i, &ival); - tc[ival]++; - } - for (i = 0; i < nr; i++) { - numaGetIValue(nar, i, &ival); - rc[ival]++; - } - lept_stderr(" Threshold cells formed: %d\n", nt); - for (i = 1; i < CqNLevels + 1; i++) - lept_stderr(" level %d: %d\n", i, tc[i]); - lept_stderr("\n Residual cells formed: %d\n", nr); - for (i = 0; i < CqNLevels ; i++) - lept_stderr(" level %d: %d\n", i, rc[i]); -} -#endif /* PRINT_OCTCUBE_STATS */ - - numaDestroy(&nat); - numaDestroy(&nar); - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - - return cqcaa; -} - - -/*! - * \brief pixOctreeQuantizePixels() - * - * \param[in] pixs 32 bpp - * \param[in] cqcaa octree in array format - * \param[in] ditherflag 1 for dithering, 0 for no dithering - * \return pixd or NULL on error - * - *
- * Notes: - * (1) This routine doesn't need to use the CTEs (colormap - * table entries) because the color indices are embedded - * in the octree. Thus, the calling program must make - * and attach the colormap to pixd after it is returned. - * (2) Dithering is performed in integers, effectively rounding - * to 1/8 sample increment. The data in the integer buffers is - * 64 times the sample values. The 'dif' is 8 times the - * sample values, and this spread, multiplied by 8, to the - * integer buffers. Because the dif is truncated to an - * integer, the dither is accurate to 1/8 of a sample increment, - * or 1/2048 of the color range. - *- */ -static PIX * -pixOctreeQuantizePixels(PIX *pixs, - CQCELL ***cqcaa, - l_int32 ditherflag) -{ -l_uint8 *bufu8r, *bufu8g, *bufu8b; -l_int32 rval, gval, bval; -l_int32 octindex, index; -l_int32 val1, val2, val3, dif; -l_int32 w, h, wpls, wpld, i, j, success; -l_int32 rc, gc, bc; -l_int32 *buf1r, *buf1g, *buf1b, *buf2r, *buf2g, *buf2b; -l_uint32 *rtab, *gtab, *btab; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; - - PROCNAME("pixOctreeQuantizePixels"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); - if (!cqcaa) - return (PIX *)ERROR_PTR("cqcaa not defined", procName, NULL); - - /* Make output 8 bpp palette image */ - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if ((pixd = pixCreate(w, h, 8)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - /* Make the canonical index tables */ - rtab = gtab = btab = NULL; - makeRGBToIndexTables(CqNLevels, &rtab, >ab, &btab); - - /* Traverse tree from root, looking for lowest cube - * that is a leaf, and set dest pix to its - * colortable index value. The results are far - * better when dithering to get a more accurate - * average color. */ - if (ditherflag == 0) { /* no dithering */ - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - octindex = rtab[rval] | gtab[gval] | btab[bval]; - octreeFindColorCell(octindex, cqcaa, &index, &rc, &gc, &bc); - SET_DATA_BYTE(lined, j, index); - } - } - } else { /* Dither */ - success = TRUE; - bufu8r = bufu8g = bufu8b = NULL; - buf1r = buf1g = buf1b = buf2r = buf2g = buf2b = NULL; - bufu8r = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); - bufu8g = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); - bufu8b = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); - buf1r = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - buf1g = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - buf1b = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - buf2r = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - buf2g = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - buf2b = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - if (!bufu8r || !bufu8g || !bufu8b || !buf1r || !buf1g || - !buf1b || !buf2r || !buf2g || !buf2b) { - L_ERROR("buffer not made\n", procName); - success = FALSE; - goto buffer_cleanup; - } - - /* Start by priming buf2; line 1 is above line 2 */ - pixGetRGBLine(pixs, 0, bufu8r, bufu8g, bufu8b); - for (j = 0; j < w; j++) { - buf2r[j] = 64 * bufu8r[j]; - buf2g[j] = 64 * bufu8g[j]; - buf2b[j] = 64 * bufu8b[j]; - } - - for (i = 0; i < h - 1; i++) { - /* Swap data 2 --> 1, and read in new line 2 */ - memcpy(buf1r, buf2r, 4 * w); - memcpy(buf1g, buf2g, 4 * w); - memcpy(buf1b, buf2b, 4 * w); - pixGetRGBLine(pixs, i + 1, bufu8r, bufu8g, bufu8b); - for (j = 0; j < w; j++) { - buf2r[j] = 64 * bufu8r[j]; - buf2g[j] = 64 * bufu8g[j]; - buf2b[j] = 64 * bufu8b[j]; - } - - /* Dither */ - lined = datad + i * wpld; - for (j = 0; j < w - 1; j++) { - rval = buf1r[j] / 64; - gval = buf1g[j] / 64; - bval = buf1b[j] / 64; - octindex = rtab[rval] | gtab[gval] | btab[bval]; - octreeFindColorCell(octindex, cqcaa, &index, &rc, &gc, &bc); - SET_DATA_BYTE(lined, j, index); - - dif = buf1r[j] / 8 - 8 * rc; - if (dif != 0) { - val1 = buf1r[j + 1] + 3 * dif; - val2 = buf2r[j] + 3 * dif; - val3 = buf2r[j + 1] + 2 * dif; - if (dif > 0) { - buf1r[j + 1] = L_MIN(16383, val1); - buf2r[j] = L_MIN(16383, val2); - buf2r[j + 1] = L_MIN(16383, val3); - } else { - buf1r[j + 1] = L_MAX(0, val1); - buf2r[j] = L_MAX(0, val2); - buf2r[j + 1] = L_MAX(0, val3); - } - } - - dif = buf1g[j] / 8 - 8 * gc; - if (dif != 0) { - val1 = buf1g[j + 1] + 3 * dif; - val2 = buf2g[j] + 3 * dif; - val3 = buf2g[j + 1] + 2 * dif; - if (dif > 0) { - buf1g[j + 1] = L_MIN(16383, val1); - buf2g[j] = L_MIN(16383, val2); - buf2g[j + 1] = L_MIN(16383, val3); - } else { - buf1g[j + 1] = L_MAX(0, val1); - buf2g[j] = L_MAX(0, val2); - buf2g[j + 1] = L_MAX(0, val3); - } - } - - dif = buf1b[j] / 8 - 8 * bc; - if (dif != 0) { - val1 = buf1b[j + 1] + 3 * dif; - val2 = buf2b[j] + 3 * dif; - val3 = buf2b[j + 1] + 2 * dif; - if (dif > 0) { - buf1b[j + 1] = L_MIN(16383, val1); - buf2b[j] = L_MIN(16383, val2); - buf2b[j + 1] = L_MIN(16383, val3); - } else { - buf1b[j + 1] = L_MAX(0, val1); - buf2b[j] = L_MAX(0, val2); - buf2b[j + 1] = L_MAX(0, val3); - } - } - } - - /* Get last pixel in row; no downward propagation */ - rval = buf1r[w - 1] / 64; - gval = buf1g[w - 1] / 64; - bval = buf1b[w - 1] / 64; - octindex = rtab[rval] | gtab[gval] | btab[bval]; - octreeFindColorCell(octindex, cqcaa, &index, &rc, &gc, &bc); - SET_DATA_BYTE(lined, w - 1, index); - } - - /* Get last row of pixels; no leftward propagation */ - lined = datad + (h - 1) * wpld; - for (j = 0; j < w; j++) { - rval = buf2r[j] / 64; - gval = buf2g[j] / 64; - bval = buf2b[j] / 64; - octindex = rtab[rval] | gtab[gval] | btab[bval]; - octreeFindColorCell(octindex, cqcaa, &index, &rc, &gc, &bc); - SET_DATA_BYTE(lined, j, index); - } - -buffer_cleanup: - LEPT_FREE(bufu8r); - LEPT_FREE(bufu8g); - LEPT_FREE(bufu8b); - LEPT_FREE(buf1r); - LEPT_FREE(buf1g); - LEPT_FREE(buf1b); - LEPT_FREE(buf2r); - LEPT_FREE(buf2g); - LEPT_FREE(buf2b); - if (!success) pixDestroy(&pixd); - } - - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - return pixd; -} - - -/*! - * \brief octreeFindColorCell() - * - * \param[in] octindex - * \param[in] cqcaa - * \param[out] pindex index of CTE; returned to set pixel value - * \param[out] prval of CTE - * \param[out] pgval of CTE - * \param[out] pbval of CTE - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) As this is in inner loop, we don't check input pointers! - * (2) This traverses from the root (well, actually from level 2, - * because the level 2 cubes are the largest CTE cubes), - * and finds the index number of the cell and the color values, - * which can be used either directly or in a (Floyd-Steinberg) - * error-diffusion dithering algorithm. - *- */ -static l_int32 -octreeFindColorCell(l_int32 octindex, - CQCELL ***cqcaa, - l_int32 *pindex, - l_int32 *prval, - l_int32 *pgval, - l_int32 *pbval) -{ -l_int32 level; -l_int32 baseindex, subindex; -CQCELL *cqc, *cqcsub; - - /* Use rgb values stored in the cubes; a little faster */ - for (level = 2; level < CqNLevels; level++) { - getOctcubeIndices(octindex, level, &baseindex, &subindex); - cqc = cqcaa[level][baseindex]; - cqcsub = cqcaa[level + 1][subindex]; - if (cqcsub->bleaf == 0) { /* use cell at level above */ - *pindex = cqc->index; - *prval = cqc->rc; - *pgval = cqc->gc; - *pbval = cqc->bc; - break; - } else if (level == CqNLevels - 1) { /* reached the bottom */ - *pindex = cqcsub->index; - *prval = cqcsub->rc; - *pgval = cqcsub->gc; - *pbval = cqcsub->bc; - break; - } - } - -#if 0 - /* Generate rgb values for each cube on the fly; slower */ - for (level = 2; level < CqNLevels; level++) { - l_int32 rv, gv, bv; - getOctcubeIndices(octindex, level, &baseindex, &subindex); - cqc = cqcaa[level][baseindex]; - cqcsub = cqcaa[level + 1][subindex]; - if (cqcsub->bleaf == 0) { /* use cell at level above */ - getRGBFromOctcube(baseindex, level, &rv, &gv, &bv); - *pindex = cqc->index; - *prval = rv; - *pgval = gv; - *pbval = bv; - break; - } else if (level == CqNLevels - 1) { /* reached the bottom */ - getRGBFromOctcube(subindex, level + 1, &rv, &gv, &bv); - *pindex = cqcsub->index; - *prval = rv; - *pgval = gv; - *pbval = bv; - break; - } - } -#endif - - return 0; -} - - - -/*------------------------------------------------------------------* - * Helper cqcell functions * - *------------------------------------------------------------------*/ -/*! - * \brief cqcellTreeCreate() - * - * \return cqcell array tree - */ -static CQCELL *** -cqcellTreeCreate(void) -{ -l_int32 level, ncells, i; -CQCELL ***cqcaa; -CQCELL **cqca; /* one array for each octree level */ - - PROCNAME("cqcellTreeCreate"); - - /* Make array of accumulation cell arrays from levels 1 to 5 */ - if ((cqcaa = (CQCELL ***)LEPT_CALLOC(CqNLevels + 1, sizeof(CQCELL **))) - == NULL) - return (CQCELL ***)ERROR_PTR("cqcaa not made", procName, NULL); - for (level = 0; level <= CqNLevels; level++) { - ncells = 1 << (3 * level); - if ((cqca = (CQCELL **)LEPT_CALLOC(ncells, sizeof(CQCELL *))) == NULL) { - cqcellTreeDestroy(&cqcaa); - return (CQCELL ***)ERROR_PTR("cqca not made", procName, NULL); - } - cqcaa[level] = cqca; - for (i = 0; i < ncells; i++) { - if ((cqca[i] = (CQCELL *)LEPT_CALLOC(1, sizeof(CQCELL))) == NULL) { - cqcellTreeDestroy(&cqcaa); - return (CQCELL ***)ERROR_PTR("cqc not made", procName, NULL); - } - } - } - - return cqcaa; -} - - -/*! - * \brief cqcellTreeDestroy() - * - * \param[in,out] pcqcaa will be set to null before returning - */ -static void -cqcellTreeDestroy(CQCELL ****pcqcaa) -{ -l_int32 level, ncells, i; -CQCELL ***cqcaa; -CQCELL **cqca; - - PROCNAME("cqcellTreeDestroy"); - - if (pcqcaa == NULL) { - L_WARNING("ptr address is NULL\n", procName); - return; - } - - if ((cqcaa = *pcqcaa) == NULL) - return; - - for (level = 0; level <= CqNLevels; level++) { - cqca = cqcaa[level]; - ncells = 1 << (3 * level); - for (i = 0; i < ncells; i++) - LEPT_FREE(cqca[i]); - LEPT_FREE(cqca); - } - LEPT_FREE(cqcaa); - *pcqcaa = NULL; - - return; -} - - - -/*------------------------------------------------------------------* - * Helper index functions * - *------------------------------------------------------------------*/ -/*! - * \brief makeRGBToIndexTables() - * - * \param[in] cqlevels can be 1, 2, 3, 4, 5 or 6 - * \param[out] prtab, pgtab, pbtab tables - * \return 0 if OK; 1 on error - * - *
- * Set up tables. e.g., for cqlevels = 5, we need an integer 0 < i < 2^15: - * rtab = 0 i7 0 0 i6 0 0 i5 0 0 i4 0 0 i3 0 0 - * gtab = 0 0 i7 0 0 i6 0 0 i5 0 0 i4 0 0 i3 0 - * btab = 0 0 0 i7 0 0 i6 0 0 i5 0 0 i4 0 0 i3 - * - * The tables are then used to map from rbg --> index as follows: - * index = 0 r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4 r3 g3 b3 - * - * e.g., for cqlevels = 4, we map to - * index = 0 0 0 0 r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4 - * - * This may look a bit strange. The notation 'r7' means the MSBit of - * the r value which has 8 bits, going down from r7 to r0. - * Keep in mind that r7 is actually the r component bit for level 1 of - * the octtree. Level 1 is composed of 8 octcubes, represented by - * the bits r7 g7 b7, which divide the entire color space into - * 8 cubes. At level 2, each of these 8 octcubes is further divided into - * 8 cubes, each labeled by the second most significant bits r6 g6 b6 - * of the rgb color. - *- */ -l_ok -makeRGBToIndexTables(l_int32 cqlevels, - l_uint32 **prtab, - l_uint32 **pgtab, - l_uint32 **pbtab) -{ -l_int32 i; -l_uint32 *rtab, *gtab, *btab; - - PROCNAME("makeRGBToIndexTables"); - - if (cqlevels < 1 || cqlevels > 6) - return ERROR_INT("cqlevels must be in {1,...6}", procName, 1); - if (!prtab || !pgtab || !pbtab) - return ERROR_INT("not all &tabs defined", procName, 1); - - rtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); - gtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); - btab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); - if (!rtab || !gtab || !btab) - return ERROR_INT("calloc fail for tab", procName, 1); - *prtab = rtab; - *pgtab = gtab; - *pbtab = btab; - - switch (cqlevels) - { - case 1: - for (i = 0; i < 256; i++) { - rtab[i] = (i >> 5) & 0x0004; - gtab[i] = (i >> 6) & 0x0002; - btab[i] = (i >> 7); - } - break; - case 2: - for (i = 0; i < 256; i++) { - rtab[i] = ((i >> 2) & 0x0020) | ((i >> 4) & 0x0004); - gtab[i] = ((i >> 3) & 0x0010) | ((i >> 5) & 0x0002); - btab[i] = ((i >> 4) & 0x0008) | ((i >> 6) & 0x0001); - } - break; - case 3: - for (i = 0; i < 256; i++) { - rtab[i] = ((i << 1) & 0x0100) | ((i >> 1) & 0x0020) | - ((i >> 3) & 0x0004); - gtab[i] = (i & 0x0080) | ((i >> 2) & 0x0010) | - ((i >> 4) & 0x0002); - btab[i] = ((i >> 1) & 0x0040) | ((i >> 3) & 0x0008) | - ((i >> 5) & 0x0001); - } - break; - case 4: - for (i = 0; i < 256; i++) { - rtab[i] = ((i << 4) & 0x0800) | ((i << 2) & 0x0100) | - (i & 0x0020) | ((i >> 2) & 0x0004); - gtab[i] = ((i << 3) & 0x0400) | ((i << 1) & 0x0080) | - ((i >> 1) & 0x0010) | ((i >> 3) & 0x0002); - btab[i] = ((i << 2) & 0x0200) | (i & 0x0040) | - ((i >> 2) & 0x0008) | ((i >> 4) & 0x0001); - } - break; - case 5: - for (i = 0; i < 256; i++) { - rtab[i] = ((i << 7) & 0x4000) | ((i << 5) & 0x0800) | - ((i << 3) & 0x0100) | ((i << 1) & 0x0020) | - ((i >> 1) & 0x0004); - gtab[i] = ((i << 6) & 0x2000) | ((i << 4) & 0x0400) | - ((i << 2) & 0x0080) | (i & 0x0010) | - ((i >> 2) & 0x0002); - btab[i] = ((i << 5) & 0x1000) | ((i << 3) & 0x0200) | - ((i << 1) & 0x0040) | ((i >> 1) & 0x0008) | - ((i >> 3) & 0x0001); - } - break; - case 6: - for (i = 0; i < 256; i++) { - rtab[i] = ((i << 10) & 0x20000) | ((i << 8) & 0x4000) | - ((i << 6) & 0x0800) | ((i << 4) & 0x0100) | - ((i << 2) & 0x0020) | (i & 0x0004); - gtab[i] = ((i << 9) & 0x10000) | ((i << 7) & 0x2000) | - ((i << 5) & 0x0400) | ((i << 3) & 0x0080) | - ((i << 1) & 0x0010) | ((i >> 1) & 0x0002); - btab[i] = ((i << 8) & 0x8000) | ((i << 6) & 0x1000) | - ((i << 4) & 0x0200) | ((i << 2) & 0x0040) | - (i & 0x0008) | ((i >> 2) & 0x0001); - } - break; - default: - ERROR_INT("cqlevels not in [1...6]", procName, 1); - break; - } - - return 0; -} - - -/*! - * \brief getOctcubeIndexFromRGB() - * - * \param[in] rval, gval, bval - * \param[in] rtab, gtab, btab generated with makeRGBToIndexTables() - * \param[out] pindex found index - * \return void - * - *
- * Notes: - * No error checking! - *- */ -void -getOctcubeIndexFromRGB(l_int32 rval, - l_int32 gval, - l_int32 bval, - l_uint32 *rtab, - l_uint32 *gtab, - l_uint32 *btab, - l_uint32 *pindex) -{ - *pindex = rtab[rval] | gtab[gval] | btab[bval]; - return; -} - - -/*! - * \brief getRGBFromOctcube() - * - * \param[in] cubeindex octcube index - * \param[in] level at which index is expressed - * \param[out] prval r val of this cube - * \param[out] pgval g val of this cube - * \param[out] pbval b val of this cube - * \return void - * - *
- * Notes: - * (1) We can consider all octcube indices to represent a - * specific point in color space: namely, the location - * of the 'upper-left' corner of the cube, where indices - * increase down and to the right. The upper left corner - * of the color space is then 00000.... - * (2) The 'rgbindex' is a 24-bit representation of the location, - * in octcube notation, at the center of the octcube. - * To get to the center of an octcube, you choose the 111 - * octcube at the next lower level. - * (3) For example, if the octcube index = 110101 (binary), - * which is a level 2 expression, then the rgbindex - * is the 24-bit representation of 110101111 (at level 3); - * namely, 000110101111000000000000. The number is padded - * with 3 leading 0s (because the representation uses - * only 21 bits) and 12 trailing 0s (the default for - * levels 4-7, which are contained within each of the level3 - * octcubes. Then the rgb values for the center of the - * octcube are: rval = 11100000, gval = 10100000, bval = 01100000 - *- */ -static void -getRGBFromOctcube(l_int32 cubeindex, - l_int32 level, - l_int32 *prval, - l_int32 *pgval, - l_int32 *pbval) -{ -l_int32 rgbindex; - - /* Bring to format in 21 bits: (r7 g7 b7 r6 g6 b6 ...) */ - /* This is valid for levels from 0 to 6 */ - rgbindex = cubeindex << (3 * (7 - level)); /* upper corner of cube */ - rgbindex |= (0x7 << (3 * (6 - level))); /* index to center of cube */ - - /* Extract separate pieces */ - *prval = ((rgbindex >> 13) & 0x80) | - ((rgbindex >> 11) & 0x40) | - ((rgbindex >> 9) & 0x20) | - ((rgbindex >> 7) & 0x10) | - ((rgbindex >> 5) & 0x08) | - ((rgbindex >> 3) & 0x04) | - ((rgbindex >> 1) & 0x02); - *pgval = ((rgbindex >> 12) & 0x80) | - ((rgbindex >> 10) & 0x40) | - ((rgbindex >> 8) & 0x20) | - ((rgbindex >> 6) & 0x10) | - ((rgbindex >> 4) & 0x08) | - ((rgbindex >> 2) & 0x04) | - (rgbindex & 0x02); - *pbval = ((rgbindex >> 11) & 0x80) | - ((rgbindex >> 9) & 0x40) | - ((rgbindex >> 7) & 0x20) | - ((rgbindex >> 5) & 0x10) | - ((rgbindex >> 3) & 0x08) | - ((rgbindex >> 1) & 0x04) | - ((rgbindex << 1) & 0x02); - - return; -} - - -/*! - * \brief getOctcubeIndices() - * - * \param[in] rgbindex - * \param[in] level octree level 0, 1, 2, 3, 4, 5 - * \param[out] pbindex base index index at the octree level - * \param[out] psindex sub index index at the next lower level - * \return 0 if OK, 1 on error - * - *
- * Notes: - * for CqNLevels = 6, the full RGB index is in the form: - * index = (0[13] 0 r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4 r3 g3 b3 r2 g2 b2) - * for CqNLevels = 5, the full RGB index is in the form: - * index = (0[16] 0 r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4 r3 g3 b3) - * for CqNLevels = 4, the full RGB index is in the form: - * index = (0[19] 0 r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4) - * - * The base index is the index of the octcube at the level given, - * whereas the sub index is the index at the next level down. - * - * For level 0: base index = 0 - * sub index is the 3 bit number (r7 g7 b7) - * For level 1: base index = (r7 g7 b7) - * sub index = (r7 g7 b7 r6 g6 b6) - * For level 2: base index = (r7 g7 b7 r6 g6 b6) - * sub index = (r7 g7 b7 r6 g6 b6 r5 g5 b5) - * For level 3: base index = (r7 g7 b7 r6 g6 b6 r5 g5 b5) - * sub index = (r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4) - * For level 4: base index = (r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4) - * sub index = (r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4 r3 g3 b3) - * For level 5: base index = (r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4 r3 g3 b3) - * sub index = (r7 g7 b7 r6 g6 b6 r5 g5 b5 r4 g4 b4 r3 g3 b3 - * r2 g2 b2) - *- */ -static l_int32 -getOctcubeIndices(l_int32 rgbindex, - l_int32 level, - l_int32 *pbindex, - l_int32 *psindex) -{ - PROCNAME("getOctcubeIndex"); - - if (level < 0 || level > CqNLevels - 1) - return ERROR_INT("level must be in e.g., [0 ... 5]", procName, 1); - if (!pbindex) - return ERROR_INT("&bindex not defined", procName, 1); - if (!psindex) - return ERROR_INT("&sindex not defined", procName, 1); - - *pbindex = rgbindex >> (3 * (CqNLevels - level)); - *psindex = rgbindex >> (3 * (CqNLevels - 1 - level)); - return 0; -} - - -/*! - * \brief octcubeGetCount() - * - * \param[in] level valid values are in [1,...6]; there are 2^level - * cubes along each side of the rgb cube - * \param[out] psize 2^(3 * level) cubes in the entire rgb cube - * \return 0 if OK, 1 on error. Caller must check! - * - *
- * level: 1 2 3 4 5 6 - * size: 8 64 512 4098 32784 262272 - *- */ -static l_int32 -octcubeGetCount(l_int32 level, - l_int32 *psize) -{ - PROCNAME("octcubeGetCount"); - - if (!psize) - return ERROR_INT("&size not defined", procName, 1); - if (level < 1 || level > 6) - return ERROR_INT("invalid level", procName, 1); - - *psize = 1 << (3 * level); - return 0; -} - - -/*---------------------------------------------------------------------------* - * Adaptive octree quantization based on population at a fixed level * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixOctreeQuantByPopulation() - * - * \param[in] pixs 32 bpp rgb - * \param[in] level significant bits for each of RGB; valid for {3,4}. - * Use 0 for default (level 4; recommended - * \param[in] ditherflag 1 to dither, 0 otherwise - * \return pixd quantized to octcubes or NULL on error - * - *
- * Notes: - * (1) This color quantization method works very well without - * dithering, using octcubes at two different levels: - * (a) the input %level, which is either 3 or 4 - * (b) level 2 (64 octcubes to cover the entire color space) - * (2) For best results, using %level = 4 is recommended. - * Why do we provide an option for using level 3? Because - * there are 512 octcubes at level 3, and for many images - * not more than 256 are filled. As a result, on some images - * a very accurate quantized representation is possible using - * %level = 3. - * (3) This first breaks up the color space into octcubes at the - * input %level, and computes, for each octcube, the average - * value of the pixels that are in it. - * (4) Then there are two possible situations: - * (a) If there are not more than 256 populated octcubes, - * it returns a cmapped pix with those values assigned. - * (b) Otherwise, it selects 192 octcubes containing the largest - * number of pixels and quantizes pixels within those octcubes - * to their average. Then, to handle the residual pixels - * that are not in those 192 octcubes, it generates a - * level 2 octree consisting of 64 octcubes, and within - * each octcube it quantizes the residual pixels to their - * average within each of those level 2 octcubes. - * (5) Unpopulated level 2 octcubes are represented in the colormap - * by their centers. This, of course, has no effect unless - * dithering is used for the output image. - * (6) The depth of pixd is the minimum required to support the - * number of colors found at %level; namely, 2, 4 or 8. - * (7) This function works particularly well on images such as maps, - * where there are a relatively small number of well-populated - * colors, but due to antialiasing and compression artifacts - * there may be a large number of different colors. This will - * pull out and represent accurately the highly populated colors, - * while still making a reasonable approximation for the others. - * (8) The highest level of octcubes allowed is 4. Use of higher - * levels typically results in having a small fraction of - * pixels in the most populated 192 octcubes. As a result, - * most of the pixels are represented at level 2, which is - * not sufficiently accurate. - * (9) Dithering shows artifacts on some images. If you plan to - * dither, pixOctreeColorQuant() and pixFixedOctcubeQuant256() - * usually give better results. - *- */ -PIX * -pixOctreeQuantByPopulation(PIX *pixs, - l_int32 level, - l_int32 ditherflag) -{ -l_int32 w, h, wpls, wpld, i, j, depth, size, ncolors, index; -l_int32 rval, gval, bval; -l_int32 *rarray, *garray, *barray, *narray, *iarray; -l_uint32 octindex, octindex2; -l_uint32 *rtab, *gtab, *btab, *rtab2, *gtab2, *btab2; -l_uint32 *lines, *lined, *datas, *datad; -L_OCTCUBE_POP *opop; -L_HEAP *lh; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixOctreeQuantByPopulation"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (level == 0) level = 4; - if (level < 3 || level > 4) - return (PIX *)ERROR_PTR("level not in {3,4}", procName, NULL); - - /* Do not dither if image is very small */ - pixGetDimensions(pixs, &w, &h, NULL); - if (w < MinDitherSize && h < MinDitherSize && ditherflag == 1) { - L_INFO("Small image: dithering turned off\n", procName); - ditherflag = 0; - } - - if (octcubeGetCount(level, &size)) /* array size = 2 ** (3 * level) */ - return (PIX *)ERROR_PTR("size not returned", procName, NULL); - rtab = gtab = btab = NULL; - makeRGBToIndexTables(level, &rtab, >ab, &btab); - - pixd = NULL; - narray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); - rarray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); - garray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); - barray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); - if (!narray || !rarray || !garray || !barray) - goto array_cleanup; - - /* Place the pixels in octcube leaves. */ - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - octindex = rtab[rval] | gtab[gval] | btab[bval]; - narray[octindex]++; - rarray[octindex] += rval; - garray[octindex] += gval; - barray[octindex] += bval; - } - } - - /* Find the number of different colors */ - for (i = 0, ncolors = 0; i < size; i++) { - if (narray[i] > 0) - ncolors++; - } - if (ncolors <= 4) - depth = 2; - else if (ncolors <= 16) - depth = 4; - else - depth = 8; - pixd = pixCreate(w, h, depth); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - cmap = pixcmapCreate(depth); - pixSetColormap(pixd, cmap); - - /* Average the colors in each octcube leaf. */ - for (i = 0; i < size; i++) { - if (narray[i] > 0) { - rarray[i] /= narray[i]; - garray[i] /= narray[i]; - barray[i] /= narray[i]; - } - } - - /* If ncolors <= 256, finish immediately. Do not dither. - * Re-use narray to hold the colormap index + 1 */ - if (ncolors <= 256) { - for (i = 0, index = 0; i < size; i++) { - if (narray[i] > 0) { - pixcmapAddColor(cmap, rarray[i], garray[i], barray[i]); - narray[i] = index + 1; /* to avoid storing 0 */ - index++; - } - } - - /* Set the cmap indices for each pixel */ - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - octindex = rtab[rval] | gtab[gval] | btab[bval]; - switch (depth) - { - case 8: - SET_DATA_BYTE(lined, j, narray[octindex] - 1); - break; - case 4: - SET_DATA_QBIT(lined, j, narray[octindex] - 1); - break; - case 2: - SET_DATA_DIBIT(lined, j, narray[octindex] - 1); - break; - default: - L_WARNING("shouldn't get here\n", procName); - } - } - } - goto array_cleanup; - } - - /* More complicated. Sort by decreasing population */ - lh = lheapCreate(500, L_SORT_DECREASING); - for (i = 0; i < size; i++) { - if (narray[i] > 0) { - opop = (L_OCTCUBE_POP *)LEPT_CALLOC(1, sizeof(L_OCTCUBE_POP)); - opop->npix = (l_float32)narray[i]; - opop->index = i; - opop->rval = rarray[i]; - opop->gval = garray[i]; - opop->bval = barray[i]; - lheapAdd(lh, opop); - } - } - - /* Take the top 192. These will form the first 192 colors - * in the cmap. iarray[i] holds the index into the cmap. */ - iarray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); - for (i = 0; i < 192; i++) { - opop = (L_OCTCUBE_POP*)lheapRemove(lh); - if (!opop) break; - pixcmapAddColor(cmap, opop->rval, opop->gval, opop->bval); - iarray[opop->index] = i + 1; /* +1 to avoid storing 0 */ - -#if DEBUG_POP - lept_stderr("i = %d, n = %6.0f, (r,g,b) = (%d %d %d)\n", - i, opop->npix, opop->rval, opop->gval, opop->bval); -#endif /* DEBUG_POP */ - - LEPT_FREE(opop); - } - - /* Make the octindex tables for level 2, and reuse rarray, etc. */ - rtab2 = gtab2 = btab2 = NULL; - makeRGBToIndexTables(2, &rtab2, >ab2, &btab2); - for (i = 0; i < 64; i++) { - narray[i] = 0; - rarray[i] = 0; - garray[i] = 0; - barray[i] = 0; - } - - /* Take the rest of the occupied octcubes, assigning the pixels - * to these new colormap indices. iarray[] is addressed - * by %level octcube indices, and it now holds the - * colormap indices for all pixels in pixs. */ - for (i = 192; i < size; i++) { - opop = (L_OCTCUBE_POP*)lheapRemove(lh); - if (!opop) break; - rval = opop->rval; - gval = opop->gval; - bval = opop->bval; - octindex2 = rtab2[rval] | gtab2[gval] | btab2[bval]; - narray[octindex2] += (l_int32)opop->npix; - rarray[octindex2] += (l_int32)opop->npix * rval; - garray[octindex2] += (l_int32)opop->npix * gval; - barray[octindex2] += (l_int32)opop->npix * bval; - iarray[opop->index] = 192 + octindex2 + 1; /* +1 to avoid storing 0 */ - LEPT_FREE(opop); - } - lheapDestroy(&lh, TRUE); - - /* To span the full color space, which is necessary for dithering, - * set each iarray element whose value is still 0 at the input - * level octcube leaves (because there were no pixels in those - * octcubes) to the colormap index corresponding to its level 2 - * octcube. */ - if (ditherflag) { - for (i = 0; i < size; i++) { - if (iarray[i] == 0) { - getRGBFromOctcube(i, level, &rval, &gval, &bval); - octindex2 = rtab2[rval] | gtab2[gval] | btab2[bval]; - iarray[i] = 192 + octindex2 + 1; - } - } - } - LEPT_FREE(rtab2); - LEPT_FREE(gtab2); - LEPT_FREE(btab2); - - /* Average the colors from the residuals in each level 2 octcube, - * and add these 64 values to the colormap. */ - for (i = 0; i < 64; i++) { - if (narray[i] > 0) { - rarray[i] /= narray[i]; - garray[i] /= narray[i]; - barray[i] /= narray[i]; - } else { /* no pixels in this octcube; use center value */ - getRGBFromOctcube(i, 2, &rarray[i], &garray[i], &barray[i]); - } - pixcmapAddColor(cmap, rarray[i], garray[i], barray[i]); - } - - /* Set the cmap indices for each pixel. Subtract 1 from - * the value in iarray[] because we added 1 earlier. */ - if (ditherflag == 0) { - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - octindex = rtab[rval] | gtab[gval] | btab[bval]; - SET_DATA_BYTE(lined, j, iarray[octindex] - 1); - } - } - } else { /* dither */ - pixDitherOctindexWithCmap(pixs, pixd, rtab, gtab, btab, - iarray, POP_DIF_CAP); - } - -#if DEBUG_POP - for (i = 0; i < size / 16; i++) { - l_int32 j; - for (j = 0; j < 16; j++) - lept_stderr("%d ", iarray[16 * i + j]); - lept_stderr("\n"); - } -#endif /* DEBUG_POP */ - - LEPT_FREE(iarray); - -array_cleanup: - LEPT_FREE(narray); - LEPT_FREE(rarray); - LEPT_FREE(garray); - LEPT_FREE(barray); - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - - return pixd; -} - - -/*! - * \brief pixDitherOctindexWithCmap() - * - * \param[in] pixs 32 bpp rgb - * \param[in] pixd 8 bpp cmapped - * \param[in] rtab, gtab, btab tables from rval to octindex - * \param[in] indexmap array mapping octindex to cmap index - * \param[in] difcap max allowed dither transfer; - * use 0 for infinite cap - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This performs dithering to generate the colormap indices - * in pixd. The colormap has been calculated, along with - * four input LUTs that together give the inverse colormapping - * from RGB to colormap index. - * (2) For pixOctreeQuantByPopulation(), %indexmap maps from the - * standard octindex to colormap index (after subtracting 1). - * The basic pixel-level function, without dithering, is: - * extractRGBValues(lines[j], &rval, &gval, &bval); - * octindex = rtab[rval] | gtab[gval] | btab[bval]; - * SET_DATA_BYTE(lined, j, indexmap[octindex] - 1); - * (3) This can be used in any situation where the general - * prescription for finding the colormap index from the rgb - * value is precisely this: - * cmapindex = indexmap[rtab[rval] | gtab[gval] | btab[bval]] - 1 - * For example, in pixFixedOctcubeQuant256(), we don't use - * standard octcube indexing, the rtab (etc) LUTs map directly - * to the colormap index, and %indexmap just compensates for - * the 1-off indexing assumed to be in that table. - *- */ -static l_int32 -pixDitherOctindexWithCmap(PIX *pixs, - PIX *pixd, - l_uint32 *rtab, - l_uint32 *gtab, - l_uint32 *btab, - l_int32 *indexmap, - l_int32 difcap) -{ -l_uint8 *bufu8r, *bufu8g, *bufu8b; -l_int32 i, j, w, h, wpld, octindex, cmapindex, success; -l_int32 rval, gval, bval, rc, gc, bc; -l_int32 dif, val1, val2, val3; -l_int32 *buf1r, *buf1g, *buf1b, *buf2r, *buf2g, *buf2b; -l_uint32 *datad, *lined; -PIXCMAP *cmap; - - PROCNAME("pixDitherOctindexWithCmap"); - - if (!pixs || pixGetDepth(pixs) != 32) - return ERROR_INT("pixs undefined or not 32 bpp", procName, 1); - if (!pixd || pixGetDepth(pixd) != 8) - return ERROR_INT("pixd undefined or not 8 bpp", procName, 1); - if ((cmap = pixGetColormap(pixd)) == NULL) - return ERROR_INT("pixd not cmapped", procName, 1); - if (!rtab || !gtab || !btab || !indexmap) - return ERROR_INT("not all 4 tables defined", procName, 1); - pixGetDimensions(pixs, &w, &h, NULL); - if (pixGetWidth(pixd) != w || pixGetHeight(pixd) != h) - return ERROR_INT("pixs and pixd not same size", procName, 1); - - success = TRUE; - bufu8r = bufu8g = bufu8b = NULL; - buf1r = buf1g = buf1b = buf2r = buf2g = buf2b = NULL; - bufu8r = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); - bufu8g = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); - bufu8b = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); - buf1r = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - buf1g = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - buf1b = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - buf2r = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - buf2g = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - buf2b = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - if (!bufu8r || !bufu8g || !bufu8b || !buf1r || !buf1g || - !buf1b || !buf2r || !buf2g || !buf2b) { - L_ERROR("buffer not made\n", procName); - success = FALSE; - goto buffer_cleanup; - } - - /* Start by priming buf2; line 1 is above line 2 */ - pixGetRGBLine(pixs, 0, bufu8r, bufu8g, bufu8b); - for (j = 0; j < w; j++) { - buf2r[j] = 64 * bufu8r[j]; - buf2g[j] = 64 * bufu8g[j]; - buf2b[j] = 64 * bufu8b[j]; - } - - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h - 1; i++) { - /* Swap data 2 --> 1, and read in new line 2 */ - memcpy(buf1r, buf2r, 4 * w); - memcpy(buf1g, buf2g, 4 * w); - memcpy(buf1b, buf2b, 4 * w); - pixGetRGBLine(pixs, i + 1, bufu8r, bufu8g, bufu8b); - for (j = 0; j < w; j++) { - buf2r[j] = 64 * bufu8r[j]; - buf2g[j] = 64 * bufu8g[j]; - buf2b[j] = 64 * bufu8b[j]; - } - - /* Dither */ - lined = datad + i * wpld; - for (j = 0; j < w - 1; j++) { - rval = buf1r[j] / 64; - gval = buf1g[j] / 64; - bval = buf1b[j] / 64; - octindex = rtab[rval] | gtab[gval] | btab[bval]; - cmapindex = indexmap[octindex] - 1; - SET_DATA_BYTE(lined, j, cmapindex); - pixcmapGetColor(cmap, cmapindex, &rc, &gc, &bc); - - dif = buf1r[j] / 8 - 8 * rc; - if (difcap > 0) { - if (dif > difcap) dif = difcap; - if (dif < -difcap) dif = -difcap; - } - if (dif != 0) { - val1 = buf1r[j + 1] + 3 * dif; - val2 = buf2r[j] + 3 * dif; - val3 = buf2r[j + 1] + 2 * dif; - if (dif > 0) { - buf1r[j + 1] = L_MIN(16383, val1); - buf2r[j] = L_MIN(16383, val2); - buf2r[j + 1] = L_MIN(16383, val3); - } else { - buf1r[j + 1] = L_MAX(0, val1); - buf2r[j] = L_MAX(0, val2); - buf2r[j + 1] = L_MAX(0, val3); - } - } - - dif = buf1g[j] / 8 - 8 * gc; - if (difcap > 0) { - if (dif > difcap) dif = difcap; - if (dif < -difcap) dif = -difcap; - } - if (dif != 0) { - val1 = buf1g[j + 1] + 3 * dif; - val2 = buf2g[j] + 3 * dif; - val3 = buf2g[j + 1] + 2 * dif; - if (dif > 0) { - buf1g[j + 1] = L_MIN(16383, val1); - buf2g[j] = L_MIN(16383, val2); - buf2g[j + 1] = L_MIN(16383, val3); - } else { - buf1g[j + 1] = L_MAX(0, val1); - buf2g[j] = L_MAX(0, val2); - buf2g[j + 1] = L_MAX(0, val3); - } - } - - dif = buf1b[j] / 8 - 8 * bc; - if (difcap > 0) { - if (dif > difcap) dif = difcap; - if (dif < -difcap) dif = -difcap; - } - if (dif != 0) { - val1 = buf1b[j + 1] + 3 * dif; - val2 = buf2b[j] + 3 * dif; - val3 = buf2b[j + 1] + 2 * dif; - if (dif > 0) { - buf1b[j + 1] = L_MIN(16383, val1); - buf2b[j] = L_MIN(16383, val2); - buf2b[j + 1] = L_MIN(16383, val3); - } else { - buf1b[j + 1] = L_MAX(0, val1); - buf2b[j] = L_MAX(0, val2); - buf2b[j + 1] = L_MAX(0, val3); - } - } - } - - /* Get last pixel in row; no downward propagation */ - rval = buf1r[w - 1] / 64; - gval = buf1g[w - 1] / 64; - bval = buf1b[w - 1] / 64; - octindex = rtab[rval] | gtab[gval] | btab[bval]; - cmapindex = indexmap[octindex] - 1; - SET_DATA_BYTE(lined, w - 1, cmapindex); - } - - /* Get last row of pixels; no leftward propagation */ - lined = datad + (h - 1) * wpld; - for (j = 0; j < w; j++) { - rval = buf2r[j] / 64; - gval = buf2g[j] / 64; - bval = buf2b[j] / 64; - octindex = rtab[rval] | gtab[gval] | btab[bval]; - cmapindex = indexmap[octindex] - 1; - SET_DATA_BYTE(lined, j, cmapindex); - } - -buffer_cleanup: - LEPT_FREE(bufu8r); - LEPT_FREE(bufu8g); - LEPT_FREE(bufu8b); - LEPT_FREE(buf1r); - LEPT_FREE(buf1g); - LEPT_FREE(buf1b); - LEPT_FREE(buf2r); - LEPT_FREE(buf2g); - LEPT_FREE(buf2b); - - return (success) ? 0 : 1; -} - - -/*---------------------------------------------------------------------------* - * Adaptive octree quantization to 4 and 8 bpp with max colors * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixOctreeQuantNumColors() - * - * \param[in] pixs 32 bpp rgb - * \param[in] maxcolors 8 to 256; the actual number of colors used - * may be less than this - * \param[in] subsample factor for computing color distribution; - * use 0 for default - * \return pixd 4 or 8 bpp, colormapped, or NULL on error - * - *
- * pixOctreeColorQuant is very flexible in terms of the relative - * depth of different cubes of the octree. By contrast, this function, - * pixOctreeQuantNumColors is also adaptive, but it supports octcube - * leaves at only two depths: a smaller depth that guarantees - * full coverage of the color space and octcubes at one level - * deeper for more accurate colors. Its main virutes are simplicity - * and speed, which are both derived from the natural indexing of - * the octcubes from the RGB values. - * - * Before describing pixOctreeQuantNumColors, consider an even simpler - * approach for 4 bpp with either 8 or 16 colors. With 8 colors, - * you simply go to level 1 octcubes and use the average color - * found in each cube. For 16 colors, you find which of the three - * colors has the largest variance at the second level, and use two - * indices for that color. The result is quite poor, because 1 some - * of the cubes are nearly empty and 2 you don't get much color - * differentiation for the extra 8 colors. Trust me, this method may - * be simple, but it isn't worth anything. - * - * In pixOctreeQuantNumColors, we generate colormapped images at - * either 4 bpp or 8 bpp. For 4 bpp, we have a minimum of 8 colors - * for the level 1 octcubes, plus up to 8 additional colors that - * are determined from the level 2 popularity. If the number of colors - * is between 8 and 16, the output is a 4 bpp image. If the number of - * colors is greater than 16, the output is a 8 bpp image. - * - * We use a priority queue, implemented with a heap, to select the - * requisite number of most populated octcubes at the deepest level - * level 2 for 64 or fewer colors; level 3 for more than 64 colors. - * These are combined with one color for each octcube one level above, - * which is used to span the color space of octcubes that were not - * included at the deeper level. - * - * If the deepest level is 2, we combine the popular level 2 octcubes - * out of a total of 64 with the 8 level 1 octcubes. If the deepest - * level is 3, we combine the popular level 3 octcubes out of a - * total 512 with the 64 level 2 octcubes that span the color space. - * In the latter case, we require a minimum of 64 colors for the level 2 - * octcubes, plus up to 192 additional colors determined from level 3 - * popularity. - * - * The parameter 'maxlevel' is the deepest octcube level that is used. - * The implementation also uses two LUTs, which are employed in - * two successive traversals of the dest image. The first maps - * from the src octindex at 'maxlevel' to the color table index, - * which is the value that is stored in the 4 or 8 bpp dest pixel. - * The second LUT maps from that colormap value in the dest to a - * new colormap value for a minimum sized colormap, stored back in - * the dest. It is used to remove any color map entries that - * correspond to color space regions that have no pixels in the - * source image. These regions can be either from the higher level - * e.g., level 1 for 4 bpp, or from octcubes at 'maxlevel' that - * are unoccupied. This remapping results in the minimum number - * of colors used according to the constraints induced by the - * input 'maxcolors'. We also compute the average R, G and B color - * values in each region of the color space represented by a - * colormap entry, and store them in the colormap. - * - * The maximum number of colors is input, which determines the - * following properties of the dest image and octcube regions used: - * - * Number of colors dest image depth maxlevel - * ---------------- ---------------- -------- - * 8 to 16 4 bpp 2 - * 17 to 64 8 bpp 2 - * 65 to 256 8 bpp 3 - * - * It may turn out that the number of extra colors, beyond the - * minimum 8 and 64 for maxlevel 2 and 3, respectively, is larger - * than the actual number of occupied cubes at these levels - * In that case, all the pixels are contained in this - * subset of cubes at maxlevel, and no colormap colors are needed - * to represent the remainder pixels one level above. Thus, for - * example, in use one often finds that the pixels in an image - * occupy less than 192 octcubes at level 3, so they can be represented - * by a colormap for octcubes at level 3 only. - *- */ -PIX * -pixOctreeQuantNumColors(PIX *pixs, - l_int32 maxcolors, - l_int32 subsample) -{ -l_int32 w, h, minside, bpp, wpls, wpld, i, j, actualcolors; -l_int32 rval, gval, bval, nbase, nextra, maxlevel, ncubes, val; -l_int32 *lut1, *lut2; -l_uint32 index; -l_uint32 *lines, *lined, *datas, *datad, *pspixel; -l_uint32 *rtab, *gtab, *btab; -OQCELL *oqc; -OQCELL **oqca; -L_HEAP *lh; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixOctreeQuantNumColors"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (maxcolors < 8) { - L_WARNING("max colors < 8; setting to 8\n", procName); - maxcolors = 8; - } - if (maxcolors > 256) { - L_WARNING("max colors > 256; setting to 256\n", procName); - maxcolors = 256; - } - - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - minside = L_MIN(w, h); - if (subsample <= 0) { - subsample = L_MAX(1, minside / 200); - } - - if (maxcolors <= 16) { - bpp = 4; - pixd = pixCreate(w, h, bpp); - maxlevel = 2; - ncubes = 64; /* 2^6 */ - nbase = 8; - nextra = maxcolors - nbase; - } else if (maxcolors <= 64) { - bpp = 8; - pixd = pixCreate(w, h, bpp); - maxlevel = 2; - ncubes = 64; /* 2^6 */ - nbase = 8; - nextra = maxcolors - nbase; - } else { /* maxcolors <= 256 */ - bpp = 8; - pixd = pixCreate(w, h, bpp); - maxlevel = 3; - ncubes = 512; /* 2^9 */ - nbase = 64; - nextra = maxcolors - nbase; - } - - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - - /*----------------------------------------------------------* - * If we're using the minimum number of colors, it is * - * much simpler. We just use 'nbase' octcubes. * - * For this case, we don't eliminate any extra colors. * - *----------------------------------------------------------*/ - if (nextra == 0) { - /* prepare the OctcubeQuantCell array */ - if ((oqca = (OQCELL **)LEPT_CALLOC(nbase, sizeof(OQCELL *))) == NULL) { - pixDestroy(&pixd); - return (PIX *)ERROR_PTR("oqca not made", procName, NULL); - } - for (i = 0; i < nbase; i++) { - oqca[i] = (OQCELL *)LEPT_CALLOC(1, sizeof(OQCELL)); - oqca[i]->n = 0.0; - } - - rtab = gtab = btab = NULL; - makeRGBToIndexTables(maxlevel - 1, &rtab, >ab, &btab); - - /* Go through the entire image, gathering statistics and - * assigning pixels to their quantized value */ - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - pspixel = lines + j; - extractRGBValues(*pspixel, &rval, &gval, &bval); - getOctcubeIndexFromRGB(rval, gval, bval, - rtab, gtab, btab, &index); -/* lept_stderr("rval = %d, gval = %d, bval = %d," - " index = %d\n", rval, gval, bval, index); */ - if (bpp == 4) - SET_DATA_QBIT(lined, j, index); - else /* bpp == 8 */ - SET_DATA_BYTE(lined, j, index); - oqca[index]->n += 1.0; - oqca[index]->rcum += rval; - oqca[index]->gcum += gval; - oqca[index]->bcum += bval; - } - } - - /* Compute average color values in each octcube, and - * generate colormap */ - cmap = pixcmapCreate(bpp); - pixSetColormap(pixd, cmap); - for (i = 0; i < nbase; i++) { - oqc = oqca[i]; - if (oqc->n != 0) { - oqc->rval = (l_int32)(oqc->rcum / oqc->n); - oqc->gval = (l_int32)(oqc->gcum / oqc->n); - oqc->bval = (l_int32)(oqc->bcum / oqc->n); - } else { - getRGBFromOctcube(i, maxlevel - 1, &oqc->rval, - &oqc->gval, &oqc->bval); - } - pixcmapAddColor(cmap, oqc->rval, oqc->gval, oqc->bval); - } - - for (i = 0; i < nbase; i++) - LEPT_FREE(oqca[i]); - LEPT_FREE(oqca); - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - return pixd; - } - - /*------------------------------------------------------------* - * General case: we will use colors in octcubes at maxlevel. * - * We also remove any colors that are not populated from * - * the colormap. * - *------------------------------------------------------------*/ - /* Prepare the OctcubeQuantCell array */ - if ((oqca = (OQCELL **)LEPT_CALLOC(ncubes, sizeof(OQCELL *))) == NULL) { - pixDestroy(&pixd); - return (PIX *)ERROR_PTR("oqca not made", procName, NULL); - } - for (i = 0; i < ncubes; i++) { - oqca[i] = (OQCELL *)LEPT_CALLOC(1, sizeof(OQCELL)); - oqca[i]->n = 0.0; - } - - /* Make the tables to map color to the octindex, - * of which there are 'ncubes' at 'maxlevel' */ - rtab = gtab = btab = NULL; - makeRGBToIndexTables(maxlevel, &rtab, >ab, &btab); - - /* Estimate the color distribution; we want to find the - * most popular nextra colors at 'maxlevel' */ - for (i = 0; i < h; i += subsample) { - lines = datas + i * wpls; - for (j = 0; j < w; j += subsample) { - pspixel = lines + j; - extractRGBValues(*pspixel, &rval, &gval, &bval); - getOctcubeIndexFromRGB(rval, gval, bval, rtab, gtab, btab, &index); - oqca[index]->n += 1.0; - oqca[index]->octindex = index; - oqca[index]->rcum += rval; - oqca[index]->gcum += gval; - oqca[index]->bcum += bval; - } - } - - /* Transfer the OQCELL from the array, and order in a heap */ - lh = lheapCreate(512, L_SORT_DECREASING); - for (i = 0; i < ncubes; i++) - lheapAdd(lh, oqca[i]); - LEPT_FREE(oqca); /* don't need this array */ - - /* Prepare a new OctcubeQuantCell array, with maxcolors cells */ - oqca = (OQCELL **)LEPT_CALLOC(maxcolors, sizeof(OQCELL *)); - for (i = 0; i < nbase; i++) { /* make nbase cells */ - oqca[i] = (OQCELL *)LEPT_CALLOC(1, sizeof(OQCELL)); - oqca[i]->n = 0.0; - } - - /* Remove the nextra most populated ones, and put them in the array */ - for (i = 0; i < nextra; i++) { - oqc = (OQCELL *)lheapRemove(lh); - oqc->n = 0.0; /* reinit */ - oqc->rcum = 0; - oqc->gcum = 0; - oqc->bcum = 0; - oqca[nbase + i] = oqc; /* store it in the array */ - } - - /* Destroy the heap and its remaining contents */ - lheapDestroy(&lh, TRUE); - - /* Generate a lookup table from octindex at maxlevel - * to color table index */ - lut1 = (l_int32 *)LEPT_CALLOC(ncubes, sizeof(l_int32)); - for (i = 0; i < nextra; i++) - lut1[oqca[nbase + i]->octindex] = nbase + i; - for (index = 0; index < ncubes; index++) { - if (lut1[index] == 0) /* not one of the extras; need to assign */ - lut1[index] = index >> 3; /* remove the least significant bits */ -/* lept_stderr("lut1[%d] = %d\n", index, lut1[index]); */ - } - - /* Go through the entire image, gathering statistics and - * assigning pixels to their quantized value */ - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - pspixel = lines + j; - extractRGBValues(*pspixel, &rval, &gval, &bval); - getOctcubeIndexFromRGB(rval, gval, bval, rtab, gtab, btab, &index); -/* lept_stderr("rval = %d, gval = %d, bval = %d, index = %d\n", - rval, gval, bval, index); */ - val = lut1[index]; - switch (bpp) { - case 4: - SET_DATA_QBIT(lined, j, val); - break; - case 8: - SET_DATA_BYTE(lined, j, val); - break; - default: - LEPT_FREE(oqca); - LEPT_FREE(lut1); - return (PIX *)ERROR_PTR("bpp not 4 or 8!", procName, NULL); - break; - } - oqca[val]->n += 1.0; - oqca[val]->rcum += rval; - oqca[val]->gcum += gval; - oqca[val]->bcum += bval; - } - } - - /* Compute averages, set up a colormap, and make a second - * lut that converts from the color values currently in - * the image to a minimal set */ - lut2 = (l_int32 *)LEPT_CALLOC(ncubes, sizeof(l_int32)); - cmap = pixcmapCreate(bpp); - pixSetColormap(pixd, cmap); - for (i = 0, index = 0; i < maxcolors; i++) { - oqc = oqca[i]; - lut2[i] = index; - if (oqc->n == 0) /* no occupancy; don't bump up index */ - continue; - oqc->rval = (l_int32)(oqc->rcum / oqc->n); - oqc->gval = (l_int32)(oqc->gcum / oqc->n); - oqc->bval = (l_int32)(oqc->bcum / oqc->n); - pixcmapAddColor(cmap, oqc->rval, oqc->gval, oqc->bval); - index++; - } -/* pixcmapWriteStream(stderr, cmap); */ - actualcolors = pixcmapGetCount(cmap); -/* lept_stderr("Number of different colors = %d\n", actualcolors); */ - - /* Last time through the image; use the lookup table to - * remap the pixel value to the minimal colormap */ - if (actualcolors < maxcolors) { - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - switch (bpp) { - case 4: - val = GET_DATA_QBIT(lined, j); - SET_DATA_QBIT(lined, j, lut2[val]); - break; - case 8: - val = GET_DATA_BYTE(lined, j); - SET_DATA_BYTE(lined, j, lut2[val]); - break; - } - } - } - } - - if (oqca) { - for (i = 0; i < maxcolors; i++) - LEPT_FREE(oqca[i]); - } - LEPT_FREE(oqca); - LEPT_FREE(lut1); - LEPT_FREE(lut2); - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - return pixd; -} - - -/*-------------------------------------------------------------------------* - * Mixed color/gray quantization with specified number of colors * - *-------------------------------------------------------------------------*/ -/*! - * \brief pixOctcubeQuantMixedWithGray() - * - * \param[in] pixs 32 bpp rgb - * \param[in] depth of output pix - * \param[in] graylevels graylevels (must be > 1) - * \param[in] delta threshold for deciding if a pix is color or gray - * \return pixd quantized to octcube and gray levels or NULL on error - * - *
- * Notes: - * (1) Generates a colormapped image, where the colormap table values - * have two components: octcube values representing pixels with - * color content, and grayscale values for the rest. - * (2) The threshold (delta) is the maximum allowable difference of - * the max abs value of | r - g |, | r - b | and | g - b |. - * (3) The octcube values are the averages of all pixels that are - * found in the octcube, and that are far enough from gray to - * be considered color. This can roughly be visualized as all - * the points in the rgb color cube that are not within a "cylinder" - * of diameter approximately 'delta' along the main diagonal. - * (4) We want to guarantee full coverage of the rgb color space; thus, - * if the output depth is 4, the octlevel is 1 (2 x 2 x 2 = 8 cubes) - * and if the output depth is 8, the octlevel is 2 (4 x 4 x 4 - * = 64 cubes). - * (5) Consequently, we have the following constraint on the number - * of allowed gray levels: for 4 bpp, 8; for 8 bpp, 192. - *- */ -PIX * -pixOctcubeQuantMixedWithGray(PIX *pixs, - l_int32 depth, - l_int32 graylevels, - l_int32 delta) -{ -l_int32 w, h, wpls, wpld, i, j, size, octlevels; -l_int32 rval, gval, bval, del, val, midval; -l_int32 *carray, *rarray, *garray, *barray; -l_int32 *tabval; -l_uint32 octindex; -l_uint32 *rtab, *gtab, *btab; -l_uint32 *lines, *lined, *datas, *datad; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixOctcubeQuantMixedWithGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (graylevels < 2) - return (PIX *)ERROR_PTR("invalid graylevels", procName, NULL); - if (depth == 4) { - octlevels = 1; - size = 8; /* 2 ** 3 */ - if (graylevels > 8) - return (PIX *)ERROR_PTR("max 8 gray levels", procName, NULL); - } else if (depth == 8) { - octlevels = 2; - size = 64; /* 2 ** 6 */ - if (graylevels > 192) - return (PIX *)ERROR_PTR("max 192 gray levels", procName, NULL); - } else { - return (PIX *)ERROR_PTR("output depth not 4 or 8 bpp", procName, NULL); - } - - pixd = NULL; - - /* Make octcube index tables */ - rtab = gtab = btab = NULL; - makeRGBToIndexTables(octlevels, &rtab, >ab, &btab); - - /* Make octcube arrays for storing points in each cube */ - carray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); - rarray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); - garray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); - barray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); - - /* Make lookup table, using computed thresholds */ - tabval = makeGrayQuantIndexTable(graylevels); - if (!rtab || !gtab || !btab || - !carray || !rarray || !garray || !barray || !tabval) { - L_ERROR("calloc fail for an array\n", procName); - goto array_cleanup; - } - - /* Make colormapped output pixd */ - pixGetDimensions(pixs, &w, &h, NULL); - if ((pixd = pixCreate(w, h, depth)) == NULL) { - L_ERROR("pixd not made\n", procName); - goto array_cleanup; - } - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - cmap = pixcmapCreate(depth); - for (j = 0; j < size; j++) /* reserve octcube colors */ - pixcmapAddColor(cmap, 1, 1, 1); /* a color that won't be used */ - for (j = 0; j < graylevels; j++) { /* set grayscale colors */ - val = (255 * j) / (graylevels - 1); - pixcmapAddColor(cmap, val, val, val); - } - pixSetColormap(pixd, cmap); - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - - /* Go through src image: assign dest pixels to colormap values - * and compute average colors in each occupied octcube */ - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - if (rval > gval) { - if (gval > bval) { /* r > g > b */ - del = rval - bval; - midval = gval; - } else if (rval > bval) { /* r > b > g */ - del = rval - gval; - midval = bval; - } else { /* b > r > g */ - del = bval - gval; - midval = rval; - } - } else { /* gval >= rval */ - if (rval > bval) { /* g > r > b */ - del = gval - bval; - midval = rval; - } else if (gval > bval) { /* g > b > r */ - del = gval - rval; - midval = bval; - } else { /* b > g > r */ - del = bval - rval; - midval = gval; - } - } - if (del > delta) { /* assign to color */ - octindex = rtab[rval] | gtab[gval] | btab[bval]; - carray[octindex]++; - rarray[octindex] += rval; - garray[octindex] += gval; - barray[octindex] += bval; - if (depth == 4) - SET_DATA_QBIT(lined, j, octindex); - else /* depth == 8 */ - SET_DATA_BYTE(lined, j, octindex); - } else { /* assign to grayscale */ - val = size + tabval[midval]; - if (depth == 4) - SET_DATA_QBIT(lined, j, val); - else /* depth == 8 */ - SET_DATA_BYTE(lined, j, val); - } - } - } - - /* Average the colors in each bin and reset the colormap */ - for (i = 0; i < size; i++) { - if (carray[i] > 0) { - rarray[i] /= carray[i]; - garray[i] /= carray[i]; - barray[i] /= carray[i]; - pixcmapResetColor(cmap, i, rarray[i], garray[i], barray[i]); - } - } - -array_cleanup: - LEPT_FREE(carray); - LEPT_FREE(rarray); - LEPT_FREE(garray); - LEPT_FREE(barray); - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - LEPT_FREE(tabval); - - return pixd; -} - - -/*-------------------------------------------------------------------------* - * Fixed partition octcube quantization with 256 cells * - *-------------------------------------------------------------------------*/ -/*! - * \brief pixFixedOctcubeQuant256() - * - * \param[in] pixs 32 bpp; 24-bit color - * \param[in] ditherflag 1 for dithering; 0 for no dithering - * \return pixd 8 bit with colormap, or NULL on error - * - *
- * Notes: - * This simple 1-pass color quantization works by breaking the - * color space into 256 pieces, with 3 bits quantized for each of - * red and green, and 2 bits quantized for blue. We shortchange - * blue because the eye is least sensitive to blue. This - * division of the color space is into two levels of octrees, - * followed by a further division by 4 not 8, where both - * blue octrees have been combined in the third level. - * - * The color map is generated from the 256 color centers by - * taking the representative color to be the center of the - * cell volume. This gives a maximum error in the red and - * green values of 16 levels, and a maximum error in the - * blue sample of 32 levels. - * - * Each pixel in the 24-bit color image is placed in its containing - * cell, given by the relevant MSbits of the red, green and blue - * samples. An error-diffusion dithering is performed on each - * color sample to give the appearance of good average local color. - * Dithering is required; without it, the contouring and visible - * color errors are very bad. - * - * I originally implemented this algorithm in two passes, - * where the first pass was used to compute the weighted average - * of each sample in each pre-allocated region of color space. - * The idea was to use these centroids in the dithering algorithm - * of the second pass, to reduce the average error that was - * being dithered. However, with dithering, there is - * virtually no difference, so there is no reason to make the - * first pass. Consequently, this 1-pass version just assigns - * the pixels to the centers of the pre-allocated cells. - * We use dithering to spread the difference between the sample - * value and the location of the center of the cell. For speed - * and simplicity, we use integer dithering and propagate only - * to the right, down, and diagonally down-right, with ratios - * 3/8, 3/8 and 1/4, respectively. The results should be nearly - * as good, and a bit faster, with propagation only to the right - * and down. - * - * The algorithm is very fast, because there is no search, - * only fast generation of the cell index for each pixel. - * We use a simple mapping from the three 8 bit rgb samples - * to the 8 bit cell index; namely, r7 r6 r5 g7 g6 g5 b7 b6. - * This is not in an octcube format, but it doesn't matter. - * There are no storage requirements. We could keep a - * running average of the center of each sample in each - * cluster, rather than using the center of the cell, but - * this is just extra work, esp. with dithering. - * - * This method gives surprisingly good results with dithering. - * However, without dithering, the loss of color accuracy is - * evident in regions that are very light or that have subtle - * blending of colors. - *- */ -PIX * -pixFixedOctcubeQuant256(PIX *pixs, - l_int32 ditherflag) -{ -l_uint8 index; -l_int32 rval, gval, bval; -l_int32 w, h, wpls, wpld, i, j, cindex; -l_uint32 *rtab, *gtab, *btab; -l_int32 *itab; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixFixedOctcubeQuant256"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - - /* Do not dither if image is very small */ - pixGetDimensions(pixs, &w, &h, NULL); - if (w < MinDitherSize && h < MinDitherSize && ditherflag == 1) { - L_INFO("Small image: dithering turned off\n", procName); - ditherflag = 0; - } - - /* Find the centers of the 256 cells, each of which represents - * the 3 MSBits of the red and green components, and the - * 2 MSBits of the blue component. This gives a mapping - * from a "cube index" to the rgb values. Save all 256 - * rgb values of these centers in a colormap. - * For example, to get the red color of the cell center, - * you take the 3 MSBits of to the index and add the - * offset to the center of the cell, which is 0x10. */ - cmap = pixcmapCreate(8); - for (cindex = 0; cindex < 256; cindex++) { - rval = (cindex & 0xe0) | 0x10; - gval = ((cindex << 3) & 0xe0) | 0x10; - bval = ((cindex << 6) & 0xc0) | 0x20; - pixcmapAddColor(cmap, rval, gval, bval); - } - - /* Make output 8 bpp palette image */ - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if ((pixd = pixCreate(w, h, 8)) == NULL) { - pixcmapDestroy(&cmap); - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - pixSetColormap(pixd, cmap); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - /* Set dest pix values to colortable indices */ - if (ditherflag == 0) { /* no dithering */ - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - index = (rval & 0xe0) | ((gval >> 3) & 0x1c) | (bval >> 6); - SET_DATA_BYTE(lined, j, index); - } - } - } else { /* ditherflag == 1 */ - /* Set up conversion tables from rgb directly to the colormap - * index. However, the dithering function expects these tables - * to generate an octcube index (+1), and the table itab[] to - * convert to the colormap index. So we make a trivial - * itab[], that simply compensates for the -1 in - * pixDitherOctindexWithCmap(). No cap is required on - * the propagated difference. */ - rtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); - gtab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); - btab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); - itab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - if (!rtab || !gtab || !btab || !itab) { - pixDestroy(&pixd); - return (PIX *)ERROR_PTR("calloc fail for table", procName, NULL); - } - for (i = 0; i < 256; i++) { - rtab[i] = i & 0xe0; - gtab[i] = (i >> 3) & 0x1c; - btab[i] = i >> 6; - itab[i] = i + 1; - } - pixDitherOctindexWithCmap(pixs, pixd, rtab, gtab, btab, itab, - FIXED_DIF_CAP); - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - LEPT_FREE(itab); - } - - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Nearly exact quantization for images with few colors * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixFewColorsOctcubeQuant1() - * - * \param[in] pixs 32 bpp rgb - * \param[in] level significant bits for each of RGB; valid in [1...6] - * \return pixd quantized to octcube or NULL on error - * - *
- * Notes: - * (1) Generates a colormapped image, where the colormap table values - * are the averages of all pixels that are found in the octcube. - * (2) This fails if there are more than 256 colors (i.e., more - * than 256 occupied octcubes). - * (3) Often level 3 (512 octcubes) will succeed because not more - * than half of them are occupied with 1 or more pixels. - * (4) The depth of the result, which is either 2, 4 or 8 bpp, - * is the minimum required to hold the number of colors that - * are found. - * (5) This can be useful for quantizing orthographically generated - * images such as color maps, where there may be more than 256 colors - * because of aliasing or jpeg artifacts on text or lines, but - * there are a relatively small number of solid colors. Then, - * use with level = 3 can often generate a compact and accurate - * representation of the original RGB image. For this purpose, - * it is better than pixFewColorsOctcubeQuant2(), because it - * uses the average value of pixels in the octcube rather - * than the first found pixel. It is also simpler to use, - * because it generates the histogram internally. - *- */ -PIX * -pixFewColorsOctcubeQuant1(PIX *pixs, - l_int32 level) -{ -l_int32 w, h, wpls, wpld, i, j, depth, size, ncolors, index; -l_int32 rval, gval, bval; -l_int32 *carray, *rarray, *garray, *barray; -l_uint32 octindex; -l_uint32 *rtab, *gtab, *btab; -l_uint32 *lines, *lined, *datas, *datad, *pspixel; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixFewColorsOctcubeQuant1"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (level < 1 || level > 6) - return (PIX *)ERROR_PTR("invalid level", procName, NULL); - - pixd = NULL; - - if (octcubeGetCount(level, &size)) /* array size = 2 ** (3 * level) */ - return (PIX *)ERROR_PTR("size not returned", procName, NULL); - rtab = gtab = btab = NULL; - makeRGBToIndexTables(level, &rtab, >ab, &btab); - - carray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); - rarray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); - garray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); - barray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32)); - if (!carray || !rarray || !garray || !barray) { - L_ERROR("calloc fail for an array\n", procName); - goto array_cleanup; - } - - /* Place the pixels in octcube leaves. */ - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) { - pspixel = lines + j; - extractRGBValues(*pspixel, &rval, &gval, &bval); - octindex = rtab[rval] | gtab[gval] | btab[bval]; - carray[octindex]++; - rarray[octindex] += rval; - garray[octindex] += gval; - barray[octindex] += bval; - } - } - - /* Find the number of different colors */ - for (i = 0, ncolors = 0; i < size; i++) { - if (carray[i] > 0) - ncolors++; - } - if (ncolors > 256) { - L_WARNING("%d colors found; more than 256\n", procName, ncolors); - goto array_cleanup; - } - if (ncolors <= 4) - depth = 2; - else if (ncolors <= 16) - depth = 4; - else - depth = 8; - - /* Average the colors in each octcube leaf and add to colormap table; - * then use carray to hold the colormap index + 1 */ - cmap = pixcmapCreate(depth); - for (i = 0, index = 0; i < size; i++) { - if (carray[i] > 0) { - rarray[i] /= carray[i]; - garray[i] /= carray[i]; - barray[i] /= carray[i]; - pixcmapAddColor(cmap, rarray[i], garray[i], barray[i]); - carray[i] = index + 1; /* to avoid storing 0 */ - index++; - } - } - - pixd = pixCreate(w, h, depth); - pixSetColormap(pixd, cmap); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - pspixel = lines + j; - extractRGBValues(*pspixel, &rval, &gval, &bval); - octindex = rtab[rval] | gtab[gval] | btab[bval]; - switch (depth) - { - case 2: - SET_DATA_DIBIT(lined, j, carray[octindex] - 1); - break; - case 4: - SET_DATA_QBIT(lined, j, carray[octindex] - 1); - break; - case 8: - SET_DATA_BYTE(lined, j, carray[octindex] - 1); - break; - default: - L_WARNING("shouldn't get here\n", procName); - } - } - } - -array_cleanup: - LEPT_FREE(carray); - LEPT_FREE(rarray); - LEPT_FREE(garray); - LEPT_FREE(barray); - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - return pixd; -} - - -/*! - * \brief pixFewColorsOctcubeQuant2() - * - * \param[in] pixs 32 bpp rgb - * \param[in] level of octcube indexing, for histogram: 3, 4, 5, 6 - * \param[in] na histogram of pixel occupation in octree leaves - * at given level - * \param[in] ncolors number of occupied octree leaves at given level - * \param[out] pnerrors [optional] num of pixels not exactly - * represented in the colormap - * \return pixd 2, 4 or 8 bpp with colormap, or NULL on error - * - *
- * Notes: - * (1) Generates a colormapped image, where the colormap table values - * are the averages of all pixels that are found in the octcube. - * (2) This fails if there are more than 256 colors (i.e., more - * than 256 occupied octcubes). - * (3) Often level 3 (512 octcubes) will succeed because not more - * than half of them are occupied with 1 or more pixels. - * (4) For an image with not more than 256 colors, it is unlikely - * that two pixels of different color will fall in the same - * octcube at level = 4. However it is possible, and this - * function optionally returns %nerrors, the number of pixels - * where, because more than one color is in the same octcube, - * the pixel color is not exactly reproduced in the colormap. - * The colormap for an occupied leaf of the octree contains - * the color of the first pixel encountered in that octcube. - * (5) This differs from pixFewColorsOctcubeQuant1(), which also - * requires not more than 256 occupied leaves, but represents - * the color of each leaf by an average over the pixels in - * that leaf. This also requires precomputing the histogram - * of occupied octree leaves, which is generated using - * pixOctcubeHistogram(). - * (6) This is used in pixConvertRGBToColormap() for images that - * are determined, by their histogram, to have relatively few - * colors. This typically happens with orthographically - * produced images (as oppopsed to natural images), where - * it is expected that most of the pixels within a leaf - * octcube have exactly the same color, and quantization to - * that color is lossless. - *- */ -PIX * -pixFewColorsOctcubeQuant2(PIX *pixs, - l_int32 level, - NUMA *na, - l_int32 ncolors, - l_int32 *pnerrors) -{ -l_int32 w, h, wpls, wpld, i, j, nerrors; -l_int32 ncubes, depth, cindex, oval; -l_int32 rval, gval, bval; -l_int32 *octarray; -l_uint32 octindex; -l_uint32 *rtab, *gtab, *btab; -l_uint32 *lines, *lined, *datas, *datad, *ppixel; -l_uint32 *colorarray; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixFewColorsOctcubeQuant2"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (level < 3 || level > 6) - return (PIX *)ERROR_PTR("level not in {4, 5, 6}", procName, NULL); - if (ncolors > 256) - return (PIX *)ERROR_PTR("ncolors > 256", procName, NULL); - if (pnerrors) - *pnerrors = UNDEF; - - pixd = NULL; - - /* Represent the image with a set of leaf octcubes - * at 'level', one for each color. */ - rtab = gtab = btab = NULL; - makeRGBToIndexTables(level, &rtab, >ab, &btab); - - /* The octarray will give a ptr from the octcube to the colorarray */ - ncubes = numaGetCount(na); - octarray = (l_int32 *)LEPT_CALLOC(ncubes, sizeof(l_int32)); - - /* The colorarray will hold the colors of the first pixel - * that lands in the leaf octcube. After filling, it is - * used to generate the colormap. */ - colorarray = (l_uint32 *)LEPT_CALLOC(ncolors + 1, sizeof(l_uint32)); - if (!octarray || !colorarray) { - L_ERROR("octarray or colorarray not made\n", procName); - goto cleanup_arrays; - } - - /* Determine the output depth from the number of colors */ - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if (ncolors <= 4) - depth = 2; - else if (ncolors <= 16) - depth = 4; - else /* ncolors <= 256 */ - depth = 8; - - if ((pixd = pixCreate(w, h, depth)) == NULL) { - L_ERROR("pixd not made\n", procName); - goto cleanup_arrays; - } - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - /* For each pixel, get the octree index for its leaf octcube. - * Check if a pixel has already been found in this octcube. - * ~ If not yet found, save that color in the colorarray - * and save the cindex in the octarray. - * ~ If already found, compare the pixel color with the - * color in the colorarray, and note if it differs. - * Then set the dest pixel value to the cindex - 1, which - * will be the cmap index for this color. */ - cindex = 1; /* start with 1 */ - nerrors = 0; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - ppixel = lines + j; - extractRGBValues(*ppixel, &rval, &gval, &bval); - octindex = rtab[rval] | gtab[gval] | btab[bval]; - oval = octarray[octindex]; - if (oval == 0) { - octarray[octindex] = cindex; - colorarray[cindex] = *ppixel; - setPixelLow(lined, j, depth, cindex - 1); - cindex++; - } else { /* already have seen this color; is it unique? */ - setPixelLow(lined, j, depth, oval - 1); - if (colorarray[oval] != *ppixel) - nerrors++; - } - } - } - if (pnerrors) - *pnerrors = nerrors; - -#if DEBUG_FEW_COLORS - lept_stderr("ncubes = %d, ncolors = %d\n", ncubes, ncolors); - for (i = 0; i < ncolors; i++) - lept_stderr("color[%d] = %x\n", i, colorarray[i + 1]); -#endif /* DEBUG_FEW_COLORS */ - - /* Make the colormap. */ - cmap = pixcmapCreate(depth); - for (i = 0; i < ncolors; i++) { - ppixel = colorarray + i + 1; - extractRGBValues(*ppixel, &rval, &gval, &bval); - pixcmapAddColor(cmap, rval, gval, bval); - } - pixSetColormap(pixd, cmap); - -cleanup_arrays: - LEPT_FREE(octarray); - LEPT_FREE(colorarray); - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - - return pixd; -} - - -/*! - * \brief pixFewColorsOctcubeQuantMixed() - * - * \param[in] pixs 32 bpp rgb - * \param[in] level significant octcube bits for each of RGB; - * valid in [1...6]; use 0 for default - * \param[in] darkthresh threshold near black; if the lightest component - * is below this, the pixel is not considered to - * be gray or color; uses 0 for default - * \param[in] lightthresh threshold near white; if the darkest component - * is above this, the pixel is not considered to - * be gray or color; use 0 for default - * \param[in] diffthresh thresh for the max difference between component - * values; for differences below this, the pixel - * is considered to be gray; use 0 for default - * \param[in] minfract min fraction of pixels for gray histo bin; - * use 0.0 for default - * \param[in] maxspan max size of gray histo bin; use 0 for default - * \return pixd 8 bpp, quantized to octcube for pixels that are - * not gray; gray pixels are quantized separately - * over the full gray range, or NULL on error - * - *
- * Notes: - * (1) First runs pixFewColorsOctcubeQuant1(). If this succeeds, - * it separates the color from gray(ish) entries in the cmap, - * and re-quantizes the gray pixels. The result has some pixels - * in color and others in gray. - * (2) This fails if there are more than 256 colors (i.e., more - * than 256 occupied octcubes in the color quantization). - * (3) Level 3 (512 octcubes) will usually succeed because not more - * than half of them are occupied with 1 or more pixels. - * (4) This uses the criterion from pixColorFraction() for deciding - * if a colormap entry is color; namely, if the color components - * are not too close to either black or white, and the maximum - * difference between component values equals or exceeds a threshold. - * (5) For quantizing the gray pixels, it uses a histogram-based - * method where input parameters determining the buckets are - * the minimum population fraction and the maximum allowed size. - * (6) Recommended input parameters are: - * %level: 3 or 4 (3 is default) - * %darkthresh: 20 - * %lightthresh: 244 - * %diffthresh: 20 - * %minfract: 0.05 - * %maxspan: 15 - * These numbers are intended to be conservative (somewhat over- - * sensitive) in color detection, It's usually better to pay - * extra with octcube quantization of a grayscale image than - * to use grayscale quantization on an image that has some - * actual color. Input 0 on any of these to get the default. - * (7) This can be useful for quantizing orthographically generated - * images such as color maps, where there may be more than 256 colors - * because of aliasing or jpeg artifacts on text or lines, but - * there are a relatively small number of solid colors. It usually - * gives results that are better than pixOctcubeQuantMixedWithGray(), - * both in size and appearance. But it is a bit slower. - *- */ -PIX * -pixFewColorsOctcubeQuantMixed(PIX *pixs, - l_int32 level, - l_int32 darkthresh, - l_int32 lightthresh, - l_int32 diffthresh, - l_float32 minfract, - l_int32 maxspan) -{ -l_int32 i, j, w, h, wplc, wplm, wpld, ncolors, index; -l_int32 rval, gval, bval, val, minval, maxval; -l_int32 *lut; -l_uint32 *datac, *datam, *datad, *linec, *linem, *lined; -PIX *pixc, *pixm, *pixg, *pixd; -PIXCMAP *cmap, *cmapd; - - PROCNAME("pixFewColorsOctcubeQuantMixed"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); - if (level <= 0) level = 3; - if (level > 6) - return (PIX *)ERROR_PTR("invalid level", procName, NULL); - if (darkthresh <= 0) darkthresh = 20; - if (lightthresh <= 0) lightthresh = 244; - if (diffthresh <= 0) diffthresh = 20; - if (minfract <= 0.0) minfract = 0.05; - if (maxspan <= 2) maxspan = 15; - - /* Start with a simple fixed octcube quantizer. */ - if ((pixc = pixFewColorsOctcubeQuant1(pixs, level)) == NULL) - return (PIX *)ERROR_PTR("too many colors", procName, NULL); - - /* Identify and save color entries in the colormap. Set up a LUT - * that returns -1 for any gray pixel. */ - cmap = pixGetColormap(pixc); - ncolors = pixcmapGetCount(cmap); - cmapd = pixcmapCreate(8); - lut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - for (i = 0; i < 256; i++) - lut[i] = -1; - for (i = 0, index = 0; i < ncolors; i++) { - pixcmapGetColor(cmap, i, &rval, &gval, &bval); - minval = L_MIN(rval, gval); - minval = L_MIN(minval, bval); - if (minval > lightthresh) /* near white */ - continue; - maxval = L_MAX(rval, gval); - maxval = L_MAX(maxval, bval); - if (maxval < darkthresh) /* near black */ - continue; - - /* Use the max diff between components to test for color */ - if (maxval - minval >= diffthresh) { - pixcmapAddColor(cmapd, rval, gval, bval); - lut[i] = index; - index++; - } - } - - /* Generate dest pix with just the color pixels set to their - * colormap indices. At the same time, make a 1 bpp mask - * of the non-color pixels */ - pixGetDimensions(pixs, &w, &h, NULL); - pixd = pixCreate(w, h, 8); - pixSetColormap(pixd, cmapd); - pixm = pixCreate(w, h, 1); - datac = pixGetData(pixc); - datam = pixGetData(pixm); - datad = pixGetData(pixd); - wplc = pixGetWpl(pixc); - wplm = pixGetWpl(pixm); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - linec = datac + i * wplc; - linem = datam + i * wplm; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(linec, j); - if (lut[val] == -1) - SET_DATA_BIT(linem, j); - else - SET_DATA_BYTE(lined, j, lut[val]); - } - } - - /* Fill in the gray values. Use a grayscale version of pixs - * as input, along with the mask over the actual gray pixels. */ - pixg = pixConvertTo8(pixs, 0); - pixGrayQuantFromHisto(pixd, pixg, pixm, minfract, maxspan); - - LEPT_FREE(lut); - pixDestroy(&pixc); - pixDestroy(&pixm); - pixDestroy(&pixg); - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Fixed partition octcube quantization with RGB output * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixFixedOctcubeQuantGenRGB() - * - * \param[in] pixs 32 bpp rgb - * \param[in] level significant bits for each of r,g,b - * \return pixd rgb; quantized to octcube centers, or NULL on error - * - *
- * Notes: - * (1) Unlike the other color quantization functions, this one - * generates an rgb image. - * (2) The pixel values are quantized to the center of each octcube - * (at the specified level) containing the pixel. They are - * not quantized to the average of the pixels in that octcube. - *- */ -PIX * -pixFixedOctcubeQuantGenRGB(PIX *pixs, - l_int32 level) -{ -l_int32 w, h, wpls, wpld, i, j; -l_int32 rval, gval, bval; -l_uint32 octindex; -l_uint32 *rtab, *gtab, *btab; -l_uint32 *lines, *lined, *datas, *datad; -PIX *pixd; - - PROCNAME("pixFixedOctcubeQuantGenRGB"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (level < 1 || level > 6) - return (PIX *)ERROR_PTR("level not in {1,...6}", procName, NULL); - - if (makeRGBToIndexTables(level, &rtab, >ab, &btab)) - return (PIX *)ERROR_PTR("tables not made", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - pixd = pixCreate(w, h, 32); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - octindex = rtab[rval] | gtab[gval] | btab[bval]; - getRGBFromOctcube(octindex, level, &rval, &gval, &bval); - composeRGBPixel(rval, gval, bval, lined + j); - } - } - - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - return pixd; -} - - -/*------------------------------------------------------------------* - * Color quantize RGB image using existing colormap * - *------------------------------------------------------------------*/ -/*! - * \brief pixQuantFromCmap() - * - * \param[in] pixs 8 bpp grayscale without cmap, or 32 bpp rgb - * \param[in] cmap to quantize to; insert copy into dest pix - * \param[in] mindepth minimum depth of pixd: can be 2, 4 or 8 bpp - * \param[in] level of octcube used for finding nearest color in cmap - * \param[in] metric L_MANHATTAN_DISTANCE, L_EUCLIDEAN_DISTANCE - * \return pixd 2, 4 or 8 bpp, colormapped, or NULL on error - * - *
- * Notes: - * (1) This is a top-level wrapper for quantizing either grayscale - * or rgb images to a specified colormap. - * (2) The actual output depth is constrained by %mindepth and - * by the number of colors in %cmap. - * (3) For grayscale, %level and %metric are ignored. - * (4) If the cmap has color and pixs is grayscale, the color is - * removed from the cmap before quantizing pixs. - *- */ -PIX * -pixQuantFromCmap(PIX *pixs, - PIXCMAP *cmap, - l_int32 mindepth, - l_int32 level, - l_int32 metric) -{ -l_int32 d; - - PROCNAME("pixQuantFromCmap"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (mindepth != 2 && mindepth != 4 && mindepth != 8) - return (PIX *)ERROR_PTR("invalid mindepth", procName, NULL); - d = pixGetDepth(pixs); - if (d == 8) - return pixGrayQuantFromCmap(pixs, cmap, mindepth); - else if (d == 32) - return pixOctcubeQuantFromCmap(pixs, cmap, mindepth, - level, metric); - else - return (PIX *)ERROR_PTR("d not 8 or 32 bpp", procName, NULL); -} - - - -/*! - * \brief pixOctcubeQuantFromCmap() - * - * \param[in] pixs 32 bpp rgb - * \param[in] cmap to quantize to; insert copy into dest pix - * \param[in] mindepth minimum depth of pixd: can be 2, 4 or 8 bpp - * \param[in] level of octcube used for finding nearest color in cmap - * \param[in] metric L_MANHATTAN_DISTANCE, L_EUCLIDEAN_DISTANCE - * \return pixd 2, 4 or 8 bpp, colormapped, or NULL on error - * - *
- * Notes: - * (1) In typical use, we are doing an operation, such as - * interpolative scaling, on a colormapped pix, where it is - * necessary to remove the colormap before the operation. - * We then want to re-quantize the RGB result using the same - * colormap. - * (2) The level is used to divide the color space into octcubes. - * Each input pixel is, in effect, placed at the center of an - * octcube at the given level, and it is mapped into the - * exact color (given in the colormap) that is the closest - * to that location. We need to know that distance, for each color - * in the colormap. The higher the level of the octtree, the smaller - * the octcubes in the color space, and hence the more accurately - * we can determine the closest color in the colormap; however, - * the size of the LUT, which is the total number of octcubes, - * increases by a factor of 8 for each increase of 1 level. - * The time required to acquire a level 4 mapping table, which has - * about 4K entries, is less than 1 msec, so that is the - * recommended minimum size to be used. At that size, the - * octcubes have their centers 16 units apart in each (r,g,b) - * direction. If two colors are in the same octcube, the one - * closest to the center will always be chosen. The maximum - * error for any component occurs when the correct color is - * at a cube corner and there is an incorrect color just inside - * the cube next to the opposite corner, giving an error of - * 14 units (out of 256) for each component. Using a level 5 - * mapping table reduces the maximum error to 6 units. - * (3) Typically you should use the Euclidean metric, because the - * resulting voronoi cells (which are generated using the actual - * colormap values as seeds) are convex for Euclidean distance - * but not for Manhattan distance. In terms of the octcubes, - * convexity of the voronoi cells means that if the 8 corners - * of any cube (of which the octcubes are special cases) - * are all within a cell, then every point in the cube will - * lie within the cell. - * (4) The depth of the output pixd is equal to the maximum of - * (a) %mindepth and (b) the minimum (2, 4 or 8 bpp) necessary - * to hold the indices in the colormap. - * (5) We build a mapping table from octcube to colormap index so - * that this function can run in a time (otherwise) independent - * of the number of colors in the colormap. This avoids a - * brute-force search for the closest colormap color to each - * pixel in the image. - * (6) This is similar to the function pixAssignToNearestColor() - * used for color segmentation. - * (7) Except for very small images or when using level > 4, - * it takes very little time to generate the tables, - * compared to the generation of the colormapped dest pix, - * so one would not typically use the low-level version. - *- */ -PIX * -pixOctcubeQuantFromCmap(PIX *pixs, - PIXCMAP *cmap, - l_int32 mindepth, - l_int32 level, - l_int32 metric) -{ -l_int32 *cmaptab; -l_uint32 *rtab, *gtab, *btab; -PIX *pixd; - - PROCNAME("pixOctcubeQuantFromCmap"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (!cmap) - return (PIX *)ERROR_PTR("cmap not defined", procName, NULL); - if (mindepth != 2 && mindepth != 4 && mindepth != 8) - return (PIX *)ERROR_PTR("invalid mindepth", procName, NULL); - if (level < 1 || level > 6) - return (PIX *)ERROR_PTR("level not in {1...6}", procName, NULL); - if (metric != L_MANHATTAN_DISTANCE && metric != L_EUCLIDEAN_DISTANCE) - return (PIX *)ERROR_PTR("invalid metric", procName, NULL); - - /* Set up the tables to map rgb to the nearest colormap index */ - rtab = gtab = btab = NULL; - makeRGBToIndexTables(level, &rtab, >ab, &btab); - cmaptab = pixcmapToOctcubeLUT(cmap, level, metric); - - pixd = pixOctcubeQuantFromCmapLUT(pixs, cmap, mindepth, - cmaptab, rtab, gtab, btab); - - LEPT_FREE(cmaptab); - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - return pixd; -} - - -/*! - * \brief pixOctcubeQuantFromCmapLUT() - * - * \param[in] pixs 32 bpp rgb - * \param[in] cmap to quantize to; insert copy into dest pix - * \param[in] mindepth minimum depth of pixd: can be 2, 4 or 8 bpp - * \param[in] cmaptab table mapping from octindex to colormap index - * \param[in] rtab, gtab, btab tables mapping from RGB to octindex - * \return pixd 2, 4 or 8 bpp, colormapped, or NULL on error - * - *
- * Notes: - * (1) See the notes in the higher-level function - * pixOctcubeQuantFromCmap(). The octcube level for - * the generated octree is specified there, along with - * the distance metric for determining the closest - * color in the colormap to each octcube. - * (2) If the colormap, level and metric information have already - * been used to construct the set of mapping tables, - * this low-level function can be used directly (i.e., - * independently of pixOctcubeQuantFromCmap()) to build - * a colormapped pix that uses the specified colormap. - *- */ -static PIX * -pixOctcubeQuantFromCmapLUT(PIX *pixs, - PIXCMAP *cmap, - l_int32 mindepth, - l_int32 *cmaptab, - l_uint32 *rtab, - l_uint32 *gtab, - l_uint32 *btab) -{ -l_int32 i, j, w, h, depth, wpls, wpld; -l_int32 rval, gval, bval, index; -l_uint32 octindex; -l_uint32 *lines, *lined, *datas, *datad; -PIX *pixd; -PIXCMAP *cmapc; - - PROCNAME("pixOctcubeQuantFromCmapLUT"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (!cmap) - return (PIX *)ERROR_PTR("cmap not defined", procName, NULL); - if (mindepth != 2 && mindepth != 4 && mindepth != 8) - return (PIX *)ERROR_PTR("invalid mindepth", procName, NULL); - if (!rtab || !gtab || !btab || !cmaptab) - return (PIX *)ERROR_PTR("tables not all defined", procName, NULL); - - /* Init dest pix (with minimum bpp depending on cmap) */ - pixcmapGetMinDepth(cmap, &depth); - depth = L_MAX(depth, mindepth); - pixGetDimensions(pixs, &w, &h, NULL); - if ((pixd = pixCreate(w, h, depth)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - cmapc = pixcmapCopy(cmap); - pixSetColormap(pixd, cmapc); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - - /* Insert the colormap index of the color nearest to the input pixel */ - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - /* Map from rgb to octcube index */ - getOctcubeIndexFromRGB(rval, gval, bval, rtab, gtab, btab, - &octindex); - /* Map from octcube index to nearest colormap index */ - index = cmaptab[octindex]; - if (depth == 2) - SET_DATA_DIBIT(lined, j, index); - else if (depth == 4) - SET_DATA_QBIT(lined, j, index); - else /* depth == 8 */ - SET_DATA_BYTE(lined, j, index); - } - } - - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Generation of octcube histogram * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixOctcubeHistogram() - * - * \param[in] pixs 32 bpp rgb - * \param[in] level significant bits for each of RGB; valid in [1...6] - * \param[out] pncolors [optional] number of occupied cubes - * \return numa histogram of color pixels, or NULL on error - * - *
- * Notes: - * (1) Input NULL for &ncolors to prevent computation and return value. - *- */ -NUMA * -pixOctcubeHistogram(PIX *pixs, - l_int32 level, - l_int32 *pncolors) -{ -l_int32 size, i, j, w, h, wpl, ncolors, val; -l_int32 rval, gval, bval; -l_uint32 octindex; -l_uint32 *rtab, *gtab, *btab; -l_uint32 *data, *line; -l_float32 *array; -NUMA *na; - - PROCNAME("pixOctcubeHistogram"); - - if (pncolors) *pncolors = 0; - if (!pixs) - return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (NUMA *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - wpl = pixGetWpl(pixs); - data = pixGetData(pixs); - - if (octcubeGetCount(level, &size)) /* array size = 2 ** (3 * level) */ - return (NUMA *)ERROR_PTR("size not returned", procName, NULL); - rtab = gtab = btab = NULL; - makeRGBToIndexTables(level, &rtab, >ab, &btab); - - if ((na = numaCreate(size)) == NULL) { - L_ERROR("na not made\n", procName); - goto cleanup_arrays; - } - numaSetCount(na, size); - array = numaGetFArray(na, L_NOCOPY); - - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - extractRGBValues(line[j], &rval, &gval, &bval); - octindex = rtab[rval] | gtab[gval] | btab[bval]; -#if DEBUG_OCTINDEX - if ((level == 1 && octindex > 7) || - (level == 2 && octindex > 63) || - (level == 3 && octindex > 511) || - (level == 4 && octindex > 4097) || - (level == 5 && octindex > 32783) || - (level == 6 && octindex > 262271)) { - lept_stderr("level = %d, octindex = %d, index error!\n", - level, octindex); - continue; - } -#endif /* DEBUG_OCTINDEX */ - array[octindex] += 1.0; - } - } - - if (pncolors) { - for (i = 0, ncolors = 0; i < size; i++) { - numaGetIValue(na, i, &val); - if (val > 0) - ncolors++; - } - *pncolors = ncolors; - } - -cleanup_arrays: - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - return na; -} - - -/*------------------------------------------------------------------* - * Get filled octcube table from colormap * - *------------------------------------------------------------------*/ -/*! - * \brief pixcmapToOctcubeLUT() - * - * \param[in] cmap - * \param[in] level significant bits for each of RGB; valid in [1...6] - * \param[in] metric L_MANHATTAN_DISTANCE, L_EUCLIDEAN_DISTANCE - * \return tab[2**3 * level] - * - *
- * Notes: - * (1) This function is used to quickly find the colormap color - * that is closest to any rgb color. It is used to assign - * rgb colors to an existing colormap. It can be very expensive - * to search through the entire colormap for the closest color - * to each pixel. Instead, we first set up this table, which is - * populated by the colormap index nearest to each octcube - * color. Then we go through the image; for each pixel, - * do two table lookups: first to generate the octcube index - * from rgb and second to use this table to read out the - * colormap index. - * (2) Do a slight modification for white and black. For level = 4, - * each octcube size is 16. The center of the whitest octcube - * is at (248, 248, 248), which is closer to 242 than 255. - * Consequently, any gray color between 242 and 254 will - * be selected, even if white (255, 255, 255) exists. This is - * typically not optimal, because the original color was - * likely white. Therefore, if white exists in the colormap, - * use it for any rgb color that falls into the most white octcube. - * Do the similar thing for black. - * (3) Here are the actual function calls for quantizing to a - * specified colormap: - * ~ first make the tables that map from rgb --> octcube index - * makeRGBToIndexTables() - * ~ then for each pixel: - * * use the tables to get the octcube index - * getOctcubeIndexFromRGB() - * * use this table to get the nearest color in the colormap - * cmap_index = tab[index] - * (4) Distance can be either manhattan or euclidean. - * (5) In typical use, level = 4 gives reasonable results, and - * level = 5 is slightly better. When this function is used - * for color segmentation, there are typically a small number - * of colors and the number of levels can be small (e.g., level = 3). - *- */ -l_int32 * -pixcmapToOctcubeLUT(PIXCMAP *cmap, - l_int32 level, - l_int32 metric) -{ -l_int32 i, k, size, ncolors, mindist, dist, mincolor, index; -l_int32 rval, gval, bval; /* color at center of the octcube */ -l_int32 *rmap, *gmap, *bmap, *tab; - - PROCNAME("pixcmapToOctcubeLUT"); - - if (!cmap) - return (l_int32 *)ERROR_PTR("cmap not defined", procName, NULL); - if (level < 1 || level > 6) - return (l_int32 *)ERROR_PTR("level not in {1...6}", procName, NULL); - if (metric != L_MANHATTAN_DISTANCE && metric != L_EUCLIDEAN_DISTANCE) - return (l_int32 *)ERROR_PTR("invalid metric", procName, NULL); - - if (octcubeGetCount(level, &size)) /* array size = 2 ** (3 * level) */ - return (l_int32 *)ERROR_PTR("size not returned", procName, NULL); - if ((tab = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32))) == NULL) - return (l_int32 *)ERROR_PTR("tab not allocated", procName, NULL); - - ncolors = pixcmapGetCount(cmap); - pixcmapToArrays(cmap, &rmap, &gmap, &bmap, NULL); - - /* Assign based on the closest octcube center to the cmap color */ - for (i = 0; i < size; i++) { - getRGBFromOctcube(i, level, &rval, &gval, &bval); - mindist = 1000000; - mincolor = 0; /* irrelevant init */ - for (k = 0; k < ncolors; k++) { - if (metric == L_MANHATTAN_DISTANCE) { - dist = L_ABS(rval - rmap[k]) + L_ABS(gval - gmap[k]) + - L_ABS(bval - bmap[k]); - } else { /* L_EUCLIDEAN_DISTANCE */ - dist = (rval - rmap[k]) * (rval - rmap[k]) + - (gval - gmap[k]) * (gval - gmap[k]) + - (bval - bmap[k]) * (bval - bmap[k]); - } - if (dist < mindist) { - mindist = dist; - mincolor = k; - } - } - tab[i] = mincolor; - } - - /* Reset black and white if available in the colormap. - * The darkest octcube is at octindex 0. - * The lightest octcube is at the max octindex. */ - pixcmapGetNearestIndex(cmap, 0, 0, 0, &index); - pixcmapGetColor(cmap, index, &rval, &gval, &bval); - if (rval < 7 && gval < 7 && bval < 7) { - tab[0] = index; - } - pixcmapGetNearestIndex(cmap, 255, 255, 255, &index); - pixcmapGetColor(cmap, index, &rval, &gval, &bval); - if (rval > 248 && gval > 248 && bval > 248) { - tab[(1 << (3 * level)) - 1] = index; - } - - LEPT_FREE(rmap); - LEPT_FREE(gmap); - LEPT_FREE(bmap); - return tab; -} - - -/*------------------------------------------------------------------* - * Strip out unused elements in colormap * - *------------------------------------------------------------------*/ -/*! - * \brief pixRemoveUnusedColors() - * - * \param[in] pixs colormapped - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is an in-place operation. - * (2) If the image doesn't have a colormap, returns without error. - * (3) Unusued colors are removed from the colormap, and the - * image pixels are re-numbered. - *- */ -l_ok -pixRemoveUnusedColors(PIX *pixs) -{ -l_int32 i, j, w, h, d, nc, wpls, val, newval, index, zerofound; -l_int32 rval, gval, bval; -l_uint32 *datas, *lines; -l_int32 *histo, *map1, *map2; -PIXCMAP *cmap, *cmapd; - - PROCNAME("pixRemoveUnusedColors"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if ((cmap = pixGetColormap(pixs)) == NULL) - return 0; - - d = pixGetDepth(pixs); - if (d != 2 && d != 4 && d != 8) - return ERROR_INT("d not in {2, 4, 8}", procName, 1); - - /* Find which indices are actually used */ - nc = pixcmapGetCount(cmap); - if ((histo = (l_int32 *)LEPT_CALLOC(nc, sizeof(l_int32))) == NULL) - return ERROR_INT("histo not made", procName, 1); - pixGetDimensions(pixs, &w, &h, NULL); - wpls = pixGetWpl(pixs); - datas = pixGetData(pixs); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) { - switch (d) - { - case 2: - val = GET_DATA_DIBIT(lines, j); - break; - case 4: - val = GET_DATA_QBIT(lines, j); - break; - case 8: - val = GET_DATA_BYTE(lines, j); - break; - default: - LEPT_FREE(histo); - return ERROR_INT("switch ran off end!", procName, 1); - } - if (val >= nc) { - L_WARNING("cmap index out of bounds!\n", procName); - continue; - } - histo[val]++; - } - } - - /* Check if there are any zeroes. If none, quit. */ - zerofound = FALSE; - for (i = 0; i < nc; i++) { - if (histo[i] == 0) { - zerofound = TRUE; - break; - } - } - if (!zerofound) { - LEPT_FREE(histo); - return 0; - } - - /* Generate mapping tables between indices */ - map1 = (l_int32 *)LEPT_CALLOC(nc, sizeof(l_int32)); - map2 = (l_int32 *)LEPT_CALLOC(nc, sizeof(l_int32)); - index = 0; - for (i = 0; i < nc; i++) { - if (histo[i] != 0) { - map1[index] = i; /* get old index from new */ - map2[i] = index; /* get new index from old */ - index++; - } - } - - /* Generate new colormap and attach to pixs */ - cmapd = pixcmapCreate(d); - for (i = 0; i < index; i++) { - pixcmapGetColor(cmap, map1[i], &rval, &gval, &bval); - pixcmapAddColor(cmapd, rval, gval, bval); - } - pixSetColormap(pixs, cmapd); - - /* Map pixel (index) values to new cmap */ - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) { - switch (d) - { - case 2: - val = GET_DATA_DIBIT(lines, j); - newval = map2[val]; - SET_DATA_DIBIT(lines, j, newval); - break; - case 4: - val = GET_DATA_QBIT(lines, j); - newval = map2[val]; - SET_DATA_QBIT(lines, j, newval); - break; - case 8: - val = GET_DATA_BYTE(lines, j); - newval = map2[val]; - SET_DATA_BYTE(lines, j, newval); - break; - default: - LEPT_FREE(histo); - LEPT_FREE(map1); - LEPT_FREE(map2); - return ERROR_INT("switch ran off end!", procName, 1); - } - } - } - - LEPT_FREE(histo); - LEPT_FREE(map1); - LEPT_FREE(map2); - return 0; -} - - -/*------------------------------------------------------------------* - * Find number of occupied octcubes at the specified level * - *------------------------------------------------------------------*/ -/*! - * \brief pixNumberOccupiedOctcubes() - * - * \param[in] pix 32 bpp - * \param[in] level of octcube - * \param[in] mincount minimum num pixels in an octcube to be counted; - * -1 to not use - * \param[in] minfract minimum fract of pixels in an octcube to be - * counted; -1 to not use - * \param[out] pncolors number of occupied octcubes - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Exactly one of (%mincount, %minfract) must be -1, so, e.g., - * if %mincount == -1, then we use %minfract. - * (2) If all occupied octcubes are to count, set %mincount == 1. - * Setting %minfract == 0.0 is taken to mean the same thing. - *- */ -l_ok -pixNumberOccupiedOctcubes(PIX *pix, - l_int32 level, - l_int32 mincount, - l_float32 minfract, - l_int32 *pncolors) -{ -l_int32 i, j, w, h, d, wpl, ncolors, size, octindex; -l_int32 rval, gval, bval; -l_int32 *carray; -l_uint32 *data, *line, *rtab, *gtab, *btab; - - PROCNAME("pixNumberOccupiedOctcubes"); - - if (!pncolors) - return ERROR_INT("&ncolors not defined", procName, 1); - *pncolors = 0; - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - pixGetDimensions(pix, &w, &h, &d); - if (d != 32) - return ERROR_INT("pix not 32 bpp", procName, 1); - if (level < 1 || level > 6) - return ERROR_INT("invalid level", procName, 1); - if ((mincount < 0 && minfract < 0) || (mincount >= 0.0 && minfract >= 0.0)) - return ERROR_INT("invalid mincount/minfract", procName, 1); - if (mincount == 0 || minfract == 0.0) - mincount = 1; - else if (minfract > 0.0) - mincount = L_MIN(1, (l_int32)(minfract * w * h)); - - if (octcubeGetCount(level, &size)) /* array size = 2 ** (3 * level) */ - return ERROR_INT("size not returned", procName, 1); - rtab = gtab = btab = NULL; - makeRGBToIndexTables(level, &rtab, >ab, &btab); - if ((carray = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32))) == NULL) { - L_ERROR("carray not made\n", procName); - goto cleanup_arrays; - } - - /* Mark the occupied octcube leaves */ - data = pixGetData(pix); - wpl = pixGetWpl(pix); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - extractRGBValues(line[j], &rval, &gval, &bval); - octindex = rtab[rval] | gtab[gval] | btab[bval]; - carray[octindex]++; - } - } - - /* Count them */ - for (i = 0, ncolors = 0; i < size; i++) { - if (carray[i] >= mincount) - ncolors++; - } - *pncolors = ncolors; - -cleanup_arrays: - LEPT_FREE(carray); - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colorquant2.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colorquant2.c deleted file mode 100644 index 98ab8b0a..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colorquant2.c +++ /dev/null @@ -1,1692 +0,0 @@ -/*====================================================================* - - 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 colorquant2.c - *
- * - * Modified median cut color quantization - * - * High level - * PIX *pixMedianCutQuant() - * PIX *pixMedianCutQuantGeneral() - * PIX *pixMedianCutQuantMixed() - * PIX *pixFewColorsMedianCutQuantMixed() - * - * Median cut indexed histogram - * l_int32 *pixMedianCutHisto() - * - * Static helpers - * static PIXCMAP *pixcmapGenerateFromHisto() - * static PIX *pixQuantizeWithColormap() - * static void getColorIndexMedianCut() - * static L_BOX3D *pixGetColorRegion() - * static l_int32 medianCutApply() - * static PIXCMAP *pixcmapGenerateFromMedianCuts() - * static l_int32 vboxGetAverageColor() - * static l_int32 vboxGetCount() - * static l_int32 vboxGetVolume() - * static L_BOX3D *box3dCreate(); - * static L_BOX3D *box3dCopy(); - * - * Paul Heckbert published the median cut algorithm, "Color Image - * Quantization for Frame Buffer Display," in Proc. SIGGRAPH '82, - * Boston, July 1982, pp. 297-307. See: - * http://delivery.acm.org/10.1145/810000/801294/p297-heckbert.pdf - * - * Median cut starts with either the full color space or the occupied - * region of color space. If you're not dithering, the occupied region - * can be used, but with dithering, pixels can end up in any place - * in the color space, so you must represent the entire color space in - * the final colormap. - * - * Color components are quantized to typically 5 or 6 significant - * bits (for each of r, g and b). Call a 3D region of color - * space a 'vbox'. Any color in this quantized space is represented - * by an element of a linear histogram array, indexed by rgb value. - * The initial region is then divided into two regions that have roughly - * equal pixel occupancy (hence the name "median cut"). Subdivision - * continues until the requisite number of vboxes has been generated. - * - * But the devil is in the details of the subdivision process. - * Here are some choices that you must make: - * (1) Along which axis to subdivide? - * (2) Which box to put the bin with the median pixel? - * (3) How to order the boxes for subdivision? - * (4) How to adequately handle boxes with very small numbers of pixels? - * (5) How to prevent a little-represented but highly visible color - * from being masked out by other colors in its vbox. - * - * Taking these in order: - * (1) Heckbert suggests using either the largest vbox side, or the vbox - * side with the largest variance in pixel occupancy. We choose - * to divide based on the largest vbox side. - * (2) Suppose you've chosen a side. Then you have a histogram - * of pixel occupancy in 2D slices of the vbox. One of those - * slices includes the median pixel. Suppose there are L bins - * to the left (smaller index) and R bins to the right. Then - * this slice (or bin) should be assigned to the box containing - * the smaller of L and R. This both shortens the larger - * of the subdivided dimensions and helps a low-count color - * far from the subdivision boundary to better express itself. - * (2a) One can also ask if the boundary should be moved even - * farther into the longer side. This is feasible if we have - * a method for doing extra subdivisions on the high count - * vboxes. And we do (see (3)). - * (3) To make sure that the boxes are subdivided toward equal - * occupancy, use an occupancy-sorted priority queue, rather - * than a simple queue. - * (4) With a priority queue, boxes with small number of pixels - * won't be repeatedly subdivided. This is good. - * (5) Use of a priority queue allows tricks such as in (2a) to let - * small occupancy clusters be better expressed. In addition, - * rather than splitting near the median, small occupancy colors - * are best reproduced by cutting half-way into the longer side. - * - * However, serious problems can arise with dithering if a priority - * queue is used based on population alone. If the picture has - * large regions of nearly constant color, some vboxes can be very - * large and have a sizeable population (but not big enough to get to - * the head of the queue). If one of these large, occupied vboxes - * is near in color to a nearly constant color region of the - * image, dithering can inject pixels from the large vbox into - * the nearly uniform region. These pixels can be very far away - * in color, and the oscillations are highly visible. To prevent - * this, we can take either or both of these actions: - * - * (1) Subdivide a fraction (< 1.0) based on population, and - * do the rest of the subdivision based on the product of - * the vbox volume and its population. By using the product, - * we avoid further subdivision of nearly empty vboxes, and - * directly target large vboxes with significant population. - * - * (2) Threshold the excess color transferred in dithering to - * neighboring pixels. - * - * Doing either of these will stop the most annoying oscillations - * in dithering. Furthermore, by doing (1), we also improve the - * rendering of regions of nearly constant color, both with and - * without dithering. It turns out that the image quality is - * not sensitive to the value of the parameter in (1); values - * between 0.3 and 0.9 give very good results. - * - * Here's the lesson: subdivide the color space into vboxes such - * that (1) the most populated vboxes that can be further - * subdivided (i.e., that occupy more than one quantum volume - * in color space) all have approximately the same population, - * and (2) all large vboxes have no significant population. - * If these conditions are met, the quantization will be excellent. - * - * Once the subdivision has been made, the colormap is generated, - * with one color for each vbox and using the average color in the vbox. - * At the same time, the histogram array is converted to an inverse - * colormap table, storing the colormap index in every cell in the - * vbox. Finally, using both the colormap and the inverse colormap, - * a colormapped pix is quickly generated from the original rgb pix. - * - * In the present implementation, subdivided regions of colorspace - * that are not occupied are retained, but not further subdivided. - * This is required for our inverse colormap lookup table for - * dithering, because dithered pixels may fall into these unoccupied - * regions. For such empty regions, we use the center as the rgb - * colormap value. - * - * This variation on median cut can be referred to as "Modified Median - * Cut" quantization, or MMCQ. Overall, the undithered MMCQ gives - * comparable results to the two-pass Octcube Quantizer (OQ). - * Comparing the two methods on the test24.jpg painting, we see: - * - * (1) For rendering spot color (the various reds and pinks in - * the image), MMCQ is not as good as OQ. - * - * (2) For rendering majority color regions, MMCQ does a better - * job of avoiding posterization. That is, it does better - * dividing the color space up in the most heavily populated regions. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) Simple interface. See pixMedianCutQuantGeneral() for - * use of defaulted parameters. - *- */ -PIX * -pixMedianCutQuant(PIX *pixs, - l_int32 ditherflag) -{ - return pixMedianCutQuantGeneral(pixs, ditherflag, - 0, 256, DefaultSigBits, 1, 1); -} - - -/*! - * \brief pixMedianCutQuantGeneral() - * - * \param[in] pixs 32 bpp; rgb color - * \param[in] ditherflag 1 for dither; 0 for no dither - * \param[in] outdepth output depth; valid: 0, 1, 2, 4, 8 - * \param[in] maxcolors between 2 and 256 - * \param[in] sigbits valid: 5 or 6; use 0 for default - * \param[in] maxsub max subsampling, integer; use 0 for default; - * 1 for no subsampling - * \param[in] checkbw 1 to check if color content is very small, - * 0 to assume there is sufficient color - * \return pixd 8 bit with colormap, or NULL on error - * - *
- * Notes: - * (1) %maxcolors must be in the range [2 ... 256]. - * (2) Use %outdepth = 0 to have the output depth computed as the - * minimum required to hold the actual colors found, given - * the %maxcolors constraint. - * (3) Use %outdepth = 1, 2, 4 or 8 to specify the output depth. - * In that case, %maxcolors must not exceed 2^(outdepth). - * (4) If there are fewer quantized colors in the image than %maxcolors, - * the colormap is simply generated from those colors. - * (5) %maxsub is the maximum allowed subsampling to be used in the - * computation of the color histogram and region of occupied - * color space. The subsampling is chosen internally for - * efficiency, based on the image size, but this parameter - * limits it. Use %maxsub = 0 for the internal default, which is the - * maximum allowed subsampling. Use %maxsub = 1 to prevent - * subsampling. In general use %maxsub >= 1 to specify the - * maximum subsampling to be allowed, where the actual subsampling - * will be the minimum of this value and the internally - * determined default value. - * (6) %sigbits can be 5 or 6. There are 2^24 colors in the color space. - * sigbits # of volume elems # of colors in a volume elem - * -------------------------------------------------------------- - * 5 2^15 2^9 = 512 - * 6 2^18 2^6 = 64 - * Volume in color space is measured in the number of volume elements. - * (7) If the image appears gray because either most of the pixels - * are gray or most of the pixels are essentially black or white, - * the image is trivially quantized with a grayscale colormap. The - * reason is that median cut divides the color space into rectangular - * regions, and it does a very poor job if all the pixels are - * near the diagonal of the color space cube. - *- */ -PIX * -pixMedianCutQuantGeneral(PIX *pixs, - l_int32 ditherflag, - l_int32 outdepth, - l_int32 maxcolors, - l_int32 sigbits, - l_int32 maxsub, - l_int32 checkbw) -{ -l_int32 i, subsample, histosize, smalln, ncolors, niters, popcolors; -l_int32 w, h, minside, factor, index, rval, gval, bval; -l_int32 *histo; -l_float32 maxprod, prod, norm, pixfract, colorfract; -L_BOX3D *vbox, *vbox1, *vbox2; -L_HEAP *lh, *lhs; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixMedianCutQuantGeneral"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); - if (maxcolors < 2 || maxcolors > 256) - return (PIX *)ERROR_PTR("maxcolors not in [2...256]", procName, NULL); - if (outdepth != 0 && outdepth != 1 && outdepth != 2 && outdepth != 4 && - outdepth != 8) - return (PIX *)ERROR_PTR("outdepth not in {0,1,2,4,8}", procName, NULL); - if (outdepth > 0 && (maxcolors > (1 << outdepth))) - return (PIX *)ERROR_PTR("maxcolors > 2^(outdepth)", procName, NULL); - if (sigbits == 0) - sigbits = DefaultSigBits; - else if (sigbits < 5 || sigbits > 6) - return (PIX *)ERROR_PTR("sigbits not 5 or 6", procName, NULL); - if (maxsub <= 0) - maxsub = 10; /* default will prevail for 10^7 pixels or less */ - - /* Determine if the image has sufficient color content. - * If pixfract << 1, most pixels are close to black or white. - * If colorfract << 1, the pixels that are not near - * black or white have very little color. - * If with little color, quantize with a grayscale colormap. */ - pixGetDimensions(pixs, &w, &h, NULL); - if (checkbw) { - minside = L_MIN(w, h); - factor = L_MAX(1, minside / 400); - pixColorFraction(pixs, 20, 244, 20, factor, &pixfract, &colorfract); - if (pixfract * colorfract < 0.00025) { - L_INFO("\n Pixel fraction neither white nor black = %6.3f" - "\n Color fraction of those pixels = %6.3f" - "\n Quantizing in gray\n", - procName, pixfract, colorfract); - return pixConvertTo8(pixs, 1); - } - } - - /* Compute the color space histogram. Default sampling - * is about 10^5 sampled pixels. */ - if (maxsub == 1) { - subsample = 1; - } else { - subsample = (l_int32)(sqrt((l_float64)(w * h) / 100000.)); - subsample = L_MAX(1, L_MIN(maxsub, subsample)); - } - histo = pixMedianCutHisto(pixs, sigbits, subsample); - histosize = 1 << (3 * sigbits); - - /* See if the number of quantized colors is less than maxcolors */ - ncolors = 0; - smalln = TRUE; - for (i = 0; i < histosize; i++) { - if (histo[i]) - ncolors++; - if (ncolors > maxcolors) { - smalln = FALSE; - break; - } - } - if (smalln) { /* finish up now */ - if (outdepth == 0) { - if (ncolors <= 2) - outdepth = 1; - else if (ncolors <= 4) - outdepth = 2; - else if (ncolors <= 16) - outdepth = 4; - else - outdepth = 8; - } - cmap = pixcmapGenerateFromHisto(pixs, outdepth, - histo, histosize, sigbits); - pixd = pixQuantizeWithColormap(pixs, ditherflag, outdepth, cmap, - histo, histosize, sigbits); - LEPT_FREE(histo); - return pixd; - } - - /* Initial vbox: minimum region in colorspace occupied by pixels */ - if (ditherflag || subsample > 1) /* use full color space */ - vbox = box3dCreate(0, (1 << sigbits) - 1, - 0, (1 << sigbits) - 1, - 0, (1 << sigbits) - 1); - else - vbox = pixGetColorRegion(pixs, sigbits, subsample); - vbox->npix = vboxGetCount(vbox, histo, sigbits); - vbox->vol = vboxGetVolume(vbox); - - /* For a fraction 'popcolors' of the desired 'maxcolors', - * generate median cuts based on population, putting - * everything on a priority queue sorted by population. */ - lh = lheapCreate(0, L_SORT_DECREASING); - lheapAdd(lh, vbox); - ncolors = 1; - niters = 0; - popcolors = (l_int32)(FractByPopulation * maxcolors); - while (1) { - vbox = (L_BOX3D *)lheapRemove(lh); - if (vboxGetCount(vbox, histo, sigbits) == 0) { /* just put it back */ - lheapAdd(lh, vbox); - continue; - } - medianCutApply(histo, sigbits, vbox, &vbox1, &vbox2); - if (!vbox1) { - L_WARNING("vbox1 not defined; shouldn't happen!\n", procName); - break; - } - if (vbox1->vol > 1) - vbox1->sortparam = vbox1->npix; - LEPT_FREE(vbox); - lheapAdd(lh, vbox1); - if (vbox2) { /* vbox2 can be NULL */ - if (vbox2->vol > 1) - vbox2->sortparam = vbox2->npix; - lheapAdd(lh, vbox2); - ncolors++; - } - if (ncolors >= popcolors) - break; - if (niters++ > MaxItersAllowed) { - L_WARNING("infinite loop; perhaps too few pixels!\n", procName); - break; - } - } - - /* Re-sort by the product of pixel occupancy times the size - * in color space. Normalize to the largest product to avoid - * integer overflow. */ - maxprod = 0.0; - for (i = 0; i < lh->n; i++) { - if ((vbox = (L_BOX3D *)lheapGetElement(lh, i)) == NULL) - continue; - prod = (l_float32)vbox->npix * (l_float32)vbox->vol; - if (prod > maxprod) maxprod = prod; - } - norm = (maxprod == 0) ? 1.0 : 1000000.0 / maxprod; - lhs = lheapCreate(0, L_SORT_DECREASING); - while ((vbox = (L_BOX3D *)lheapRemove(lh))) { - vbox->sortparam = norm * vbox->npix * vbox->vol; - lheapAdd(lhs, vbox); - } - lheapDestroy(&lh, TRUE); - - /* For the remaining (maxcolors - popcolors), generate the - * median cuts using the (npix * vol) sorting. */ - while (1) { - vbox = (L_BOX3D *)lheapRemove(lhs); - if (vboxGetCount(vbox, histo, sigbits) == 0) { /* just put it back */ - lheapAdd(lhs, vbox); - continue; - } - medianCutApply(histo, sigbits, vbox, &vbox1, &vbox2); - if (!vbox1) { - L_WARNING("vbox1 not defined; shouldn't happen!\n", procName); - break; - } - if (vbox1->vol > 1) - vbox1->sortparam = norm * vbox1->npix * vbox1->vol; - LEPT_FREE(vbox); - lheapAdd(lhs, vbox1); - if (vbox2) { /* vbox2 can be NULL */ - if (vbox2->vol > 1) - vbox2->sortparam = norm * vbox2->npix * vbox2->vol; - lheapAdd(lhs, vbox2); - ncolors++; - } - if (ncolors >= maxcolors) - break; - if (niters++ > MaxItersAllowed) { - L_WARNING("infinite loop; perhaps too few pixels!\n", procName); - break; - } - } - - /* Re-sort by pixel occupancy. This is not necessary, - * but it makes a more useful listing. */ - lh = lheapCreate(0, L_SORT_DECREASING); - while ((vbox = (L_BOX3D *)lheapRemove(lhs))) { - vbox->sortparam = vbox->npix; -/* vbox->sortparam = vbox->npix * vbox->vol; */ - lheapAdd(lh, vbox); - } - lheapDestroy(&lhs, TRUE); - - /* Generate colormap from median cuts and quantize pixd */ - cmap = pixcmapGenerateFromMedianCuts(lh, histo, sigbits); - if (outdepth == 0) { - ncolors = pixcmapGetCount(cmap); - if (ncolors <= 2) - outdepth = 1; - else if (ncolors <= 4) - outdepth = 2; - else if (ncolors <= 16) - outdepth = 4; - else - outdepth = 8; - } - pixd = pixQuantizeWithColormap(pixs, ditherflag, outdepth, cmap, - histo, histosize, sigbits); - - /* Force darkest color to black if each component <= 4 */ - pixcmapGetRankIntensity(cmap, 0.0, &index); - pixcmapGetColor(cmap, index, &rval, &gval, &bval); - if (rval < 5 && gval < 5 && bval < 5) - pixcmapResetColor(cmap, index, 0, 0, 0); - - /* Force lightest color to white if each component >= 252 */ - pixcmapGetRankIntensity(cmap, 1.0, &index); - pixcmapGetColor(cmap, index, &rval, &gval, &bval); - if (rval > 251 && gval > 251 && bval > 251) - pixcmapResetColor(cmap, index, 255, 255, 255); - - lheapDestroy(&lh, TRUE); - LEPT_FREE(histo); - return pixd; -} - - -/*! - * \brief pixMedianCutQuantMixed() - * - * \param[in] pixs 32 bpp; rgb color - * \param[in] ncolor maximum number of colors assigned to - * pixels with significant color - * \param[in] ngray number of gray colors to be used; must be >= 2 - * \param[in] darkthresh threshold near black; if the lightest component - * is below this, the pixel is not considered to - * be gray or color; uses 0 for default - * \param[in] lightthresh threshold near white; if the darkest component - * is above this, the pixel is not considered to - * be gray or color; use 0 for default - * \param[in] diffthresh thresh for the max difference between component - * values; for differences below this, the pixel - * is considered to be gray; use 0 for default - * \return pixd 8 bpp cmapped, or NULL on error - * - *
- * Notes: - * (1) ncolor + ngray must not exceed 255. - * (2) The method makes use of pixMedianCutQuantGeneral() with - * minimal addition. - * (a) Preprocess the image, setting all pixels with little color - * to black, and populating an auxiliary 8 bpp image with the - * expected colormap values corresponding to the set of - * quantized gray values. - * (b) Color quantize the altered input image to n + 1 colors. - * (c) Augment the colormap with the gray indices, and - * substitute the gray quantized values from the auxiliary - * image for those in the color quantized output that had - * been quantized as black. - * (3) Median cut color quantization is relatively poor for grayscale - * images with many colors, when compared to octcube quantization. - * Thus, for images with both gray and color, it is important - * to quantize the gray pixels by another method. Here, we - * are conservative in detecting color, preferring to use - * a few extra bits to encode colorful pixels that push them - * to gray. This is particularly reasonable with this function, - * because it handles the gray and color pixels separately, - * using median cut color quantization for the color pixels - * and equal-bin grayscale quantization for the non-color pixels. - *- */ -PIX * -pixMedianCutQuantMixed(PIX *pixs, - l_int32 ncolor, - l_int32 ngray, - l_int32 darkthresh, - l_int32 lightthresh, - l_int32 diffthresh) -{ -l_int32 i, j, w, h, wplc, wplg, wpld, nc, unused, iscolor, factor, minside; -l_int32 rval, gval, bval, minval, maxval, val, grayval; -l_float32 pixfract, colorfract; -l_int32 *lut; -l_uint32 *datac, *datag, *datad, *linec, *lineg, *lined; -PIX *pixc, *pixg, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixMedianCutQuantMixed"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); - if (ngray < 2) - return (PIX *)ERROR_PTR("ngray < 2", procName, NULL); - if (ncolor + ngray > 255) - return (PIX *)ERROR_PTR("ncolor + ngray > 255", procName, NULL); - if (darkthresh <= 0) darkthresh = 20; - if (lightthresh <= 0) lightthresh = 244; - if (diffthresh <= 0) diffthresh = 20; - - /* First check if this should be quantized in gray. - * Use a more sensitive parameter for detecting color than with - * pixMedianCutQuantGeneral(), because this function can handle - * gray pixels well. */ - pixGetDimensions(pixs, &w, &h, NULL); - minside = L_MIN(w, h); - factor = L_MAX(1, minside / 400); - pixColorFraction(pixs, darkthresh, lightthresh, diffthresh, factor, - &pixfract, &colorfract); - if (pixfract * colorfract < 0.0001) { - L_INFO("\n Pixel fraction neither white nor black = %6.3f" - "\n Color fraction of those pixels = %6.3f" - "\n Quantizing in gray\n", - procName, pixfract, colorfract); - pixg = pixConvertTo8(pixs, 0); - pixd = pixThresholdOn8bpp(pixg, ngray, 1); - pixDestroy(&pixg); - return pixd; - } - - /* OK, there is color in the image. - * Preprocess to handle the gray pixels. Set the color pixels in pixc - * to black, and store their (eventual) colormap indices in pixg.*/ - pixc = pixCopy(NULL, pixs); - pixg = pixCreate(w, h, 8); /* color pixels will remain 0 here */ - datac = pixGetData(pixc); - datag = pixGetData(pixg); - wplc = pixGetWpl(pixc); - wplg = pixGetWpl(pixg); - lut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - for (i = 0; i < 256; i++) - lut[i] = ncolor + 1 + (i * (ngray - 1) + 128) / 255; - for (i = 0; i < h; i++) { - linec = datac + i * wplc; - lineg = datag + i * wplg; - for (j = 0; j < w; j++) { - iscolor = FALSE; - extractRGBValues(linec[j], &rval, &gval, &bval); - minval = L_MIN(rval, gval); - minval = L_MIN(minval, bval); - maxval = L_MAX(rval, gval); - maxval = L_MAX(maxval, bval); - if (maxval >= darkthresh && - minval <= lightthresh && - maxval - minval >= diffthresh) { - iscolor = TRUE; - } - if (!iscolor) { - linec[j] = 0x0; /* set to black */ - grayval = (maxval + minval) / 2; - SET_DATA_BYTE(lineg, j, lut[grayval]); - } - } - } - - /* Median cut on color pixels plus black */ - pixd = pixMedianCutQuantGeneral(pixc, FALSE, 8, ncolor + 1, - DefaultSigBits, 1, 0); - - /* Augment the colormap with gray values. The new cmap - * indices should agree with the values previously stored in pixg. */ - cmap = pixGetColormap(pixd); - nc = pixcmapGetCount(cmap); - unused = ncolor + 1 - nc; - if (unused < 0) - L_ERROR("Too many colors: extra = %d\n", procName, -unused); - if (unused > 0) { /* fill in with black; these won't be used */ - L_INFO("%d unused colors\n", procName, unused); - for (i = 0; i < unused; i++) - pixcmapAddColor(cmap, 0, 0, 0); - } - for (i = 0; i < ngray; i++) { - grayval = (255 * i) / (ngray - 1); - pixcmapAddColor(cmap, grayval, grayval, grayval); - } - - /* Substitute cmap indices for the gray pixels into pixd */ - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - lineg = datag + i * wplg; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(lineg, j); /* if 0, it's a color pixel */ - if (val) - SET_DATA_BYTE(lined, j, val); - } - } - - pixDestroy(&pixc); - pixDestroy(&pixg); - LEPT_FREE(lut); - return pixd; -} - - -/*! - * \brief pixFewColorsMedianCutQuantMixed() - * - * \param[in] pixs 32 bpp rgb - * \param[in] ncolor number of colors to be assigned to pixels - * with significant color - * \param[in] ngray number of gray colors to be used; must be >= 2 - * \param[in] maxncolors maximum number of colors to be returned from - * pixColorsForQuantization(); use 0 for default - * \param[in] darkthresh threshold near black; if the lightest component - * is below this, the pixel is not considered to - * be gray or color; use 0 for default - * \param[in] lightthresh threshold near white; if the darkest component - * is above this, the pixel is not considered to - * be gray or color; use 0 for default - * \param[in] diffthresh thresh for the max difference between component - * values; for differences below this, the pixel - * is considered to be gray; use 0 for default - * \return pixd 8 bpp, median cut quantized for pixels that are - * not gray; gray pixels are quantized separately over - * the full gray range; null if too many colors or on error - * - *
- * Notes: - * (1) This is the "few colors" version of pixMedianCutQuantMixed(). - * It fails (returns NULL) if it finds more than maxncolors, but - * otherwise it gives the same result. - * (2) Recommended input parameters are: - * %maxncolors: 20 - * %darkthresh: 20 - * %lightthresh: 244 - * %diffthresh: 15 (any higher can miss colors differing - * slightly from gray) - * (3) Both ncolor and ngray should be at least equal to maxncolors. - * If they're not, they are automatically increased, and a - * warning is given. - * (4) If very little color content is found, the input is - * converted to gray and quantized in equal intervals. - * (5) This can be useful for quantizing orthographically generated - * images such as color maps, where there may be more than 256 colors - * because of aliasing or jpeg artifacts on text or lines, but - * there are a relatively small number of solid colors. - * (6) Example of usage: - * // Try to quantize, using default values for mixed med cut - * Pix *pixq = pixFewColorsMedianCutQuantMixed(pixs, 100, 20, - * 0, 0, 0, 0); - * if (!pixq) // too many colors; don't quantize - * pixq = pixClone(pixs); - *- */ -PIX * -pixFewColorsMedianCutQuantMixed(PIX *pixs, - l_int32 ncolor, - l_int32 ngray, - l_int32 maxncolors, - l_int32 darkthresh, - l_int32 lightthresh, - l_int32 diffthresh) -{ -l_int32 ncolors, iscolor; -PIX *pixg, *pixd; - - PROCNAME("pixFewColorsMedianCutQuantMixed"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); - if (maxncolors <= 0) maxncolors = 20; - if (darkthresh <= 0) darkthresh = 20; - if (lightthresh <= 0) lightthresh = 244; - if (diffthresh <= 0) diffthresh = 15; - if (ncolor < maxncolors) { - L_WARNING("ncolor too small; setting to %d\n", procName, maxncolors); - ncolor = maxncolors; - } - if (ngray < maxncolors) { - L_WARNING("ngray too small; setting to %d\n", procName, maxncolors); - ngray = maxncolors; - } - - /* Estimate the color content and the number of colors required */ - pixColorsForQuantization(pixs, 15, &ncolors, &iscolor, 0); - - /* Note that maxncolors applies to all colors required to quantize, - * both gray and colorful */ - if (ncolors > maxncolors) - return (PIX *)ERROR_PTR("too many colors", procName, NULL); - - /* If no color, return quantized gray pix */ - if (!iscolor) { - pixg = pixConvertTo8(pixs, 0); - pixd = pixThresholdOn8bpp(pixg, ngray, 1); - pixDestroy(&pixg); - return pixd; - } - - /* Use the mixed gray/color quantizer */ - return pixMedianCutQuantMixed(pixs, ncolor, ngray, darkthresh, - lightthresh, diffthresh); -} - - - -/*------------------------------------------------------------------------* - * Median cut indexed histogram * - *------------------------------------------------------------------------*/ -/*! - * \brief pixMedianCutHisto() - * - * \param[in] pixs 32 bpp; rgb color - * \param[in] sigbits valid: 5 or 6 - * \param[in] subsample integer > 0 - * \return histo 1-d array, giving the number of pixels in each - * quantized region of color space, or NULL on error - * - *
- * Notes: - * (1) Array is indexed by (3 * sigbits) bits. The array size - * is 2^(3 * sigbits). - * (2) Indexing into the array from rgb uses red sigbits as - * most significant and blue as least. - *- */ -l_int32 * -pixMedianCutHisto(PIX *pixs, - l_int32 sigbits, - l_int32 subsample) -{ -l_int32 i, j, w, h, wpl, rshift, index, histosize; -l_int32 *histo; -l_uint32 mask, pixel; -l_uint32 *data, *line; - - PROCNAME("pixMedianCutHisto"); - - if (!pixs) - return (l_int32 *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (l_int32 *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (sigbits < 5 || sigbits > 6) - return (l_int32 *)ERROR_PTR("sigbits not 5 or 6", procName, NULL); - if (subsample <= 0) - return (l_int32 *)ERROR_PTR("subsample not > 0", procName, NULL); - - histosize = 1 << (3 * sigbits); - if ((histo = (l_int32 *)LEPT_CALLOC(histosize, sizeof(l_int32))) == NULL) - return (l_int32 *)ERROR_PTR("histo not made", procName, NULL); - - rshift = 8 - sigbits; - mask = 0xff >> rshift; - pixGetDimensions(pixs, &w, &h, NULL); - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - for (i = 0; i < h; i += subsample) { - line = data + i * wpl; - for (j = 0; j < w; j += subsample) { - pixel = line[j]; - getColorIndexMedianCut(pixel, rshift, mask, sigbits, &index); - histo[index]++; - } - } - - return histo; -} - - -/*------------------------------------------------------------------------* - * Static helpers * - *------------------------------------------------------------------------*/ -/*! - * \brief pixcmapGenerateFromHisto() - * - * \param[in] pixs 32 bpp; rgb color - * \param[in] depth of colormap - * \param[in] histo - * \param[in] histosize - * \param[in] sigbits - * \return colormap, or NULL on error - * - *
- * Notes: - * (1) This is used when the number of colors in the histo - * is not greater than maxcolors. - * (2) As a side-effect, the histo becomes an inverse colormap, - * labeling the cmap indices for each existing color. - *- */ -static PIXCMAP * -pixcmapGenerateFromHisto(PIX *pixs, - l_int32 depth, - l_int32 *histo, - l_int32 histosize, - l_int32 sigbits) -{ -l_int32 i, index, shift, rval, gval, bval; -l_uint32 mask; -PIXCMAP *cmap; - - PROCNAME("pixcmapGenerateFromHisto"); - - if (!pixs) - return (PIXCMAP *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIXCMAP *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (!histo) - return (PIXCMAP *)ERROR_PTR("histo not defined", procName, NULL); - - /* Capture the rgb values of each occupied cube in the histo, - * and re-label the histo value with the colormap index. */ - cmap = pixcmapCreate(depth); - shift = 8 - sigbits; - mask = 0xff >> shift; - for (i = 0, index = 0; i < histosize; i++) { - if (histo[i]) { - rval = (i >> (2 * sigbits)) << shift; - gval = ((i >> sigbits) & mask) << shift; - bval = (i & mask) << shift; - pixcmapAddColor(cmap, rval, gval, bval); - histo[i] = index++; - } - } - - return cmap; -} - - -/*! - * \brief pixQuantizeWithColormap() - * - * \param[in] pixs 32 bpp; rgb color - * \param[in] ditherflag 1 for dither; 0 for no dither - * \param[in] outdepth depth of the returned pixd - * \param[in] cmap colormap - * \param[in] indexmap lookup table - * \param[in] mapsize size of the lookup table - * \param[in] sigbits significant bits in output - * \return pixd quantized to colormap, or NULL on error - * - *
- * Notes: - * (1) The indexmap is a LUT that takes the rgb indices of the - * pixel and returns the index into the colormap. - * (2) If ditherflag is 1, %outdepth is ignored and the output - * depth is set to 8. - *- */ -static PIX * -pixQuantizeWithColormap(PIX *pixs, - l_int32 ditherflag, - l_int32 outdepth, - PIXCMAP *cmap, - l_int32 *indexmap, - l_int32 mapsize, - l_int32 sigbits) -{ -l_uint8 *bufu8r, *bufu8g, *bufu8b; -l_int32 i, j, w, h, wpls, wpld, rshift, index, cmapindex, success; -l_int32 rval, gval, bval, rc, gc, bc; -l_int32 dif, val1, val2, val3; -l_int32 *buf1r, *buf1g, *buf1b, *buf2r, *buf2g, *buf2b; -l_uint32 *datas, *datad, *lines, *lined; -l_uint32 mask, pixel; -PIX *pixd; - - PROCNAME("pixQuantizeWithColormap"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (!cmap) - return (PIX *)ERROR_PTR("cmap not defined", procName, NULL); - if (!indexmap) - return (PIX *)ERROR_PTR("indexmap not defined", procName, NULL); - if (ditherflag) - outdepth = 8; - - pixGetDimensions(pixs, &w, &h, NULL); - pixd = pixCreate(w, h, outdepth); - pixSetColormap(pixd, cmap); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - - rshift = 8 - sigbits; - mask = 0xff >> rshift; - if (ditherflag == 0) { - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - if (outdepth == 1) { - for (j = 0; j < w; j++) { - pixel = lines[j]; - getColorIndexMedianCut(pixel, rshift, mask, - sigbits, &index); - if (indexmap[index]) - SET_DATA_BIT(lined, j); - } - } else if (outdepth == 2) { - for (j = 0; j < w; j++) { - pixel = lines[j]; - getColorIndexMedianCut(pixel, rshift, mask, - sigbits, &index); - SET_DATA_DIBIT(lined, j, indexmap[index]); - } - } else if (outdepth == 4) { - for (j = 0; j < w; j++) { - pixel = lines[j]; - getColorIndexMedianCut(pixel, rshift, mask, - sigbits, &index); - SET_DATA_QBIT(lined, j, indexmap[index]); - } - } else { /* outdepth == 8 */ - for (j = 0; j < w; j++) { - pixel = lines[j]; - getColorIndexMedianCut(pixel, rshift, mask, - sigbits, &index); - SET_DATA_BYTE(lined, j, indexmap[index]); - } - } - } - } else { /* ditherflag == 1 */ - success = TRUE; - bufu8r = bufu8g = bufu8b = NULL; - buf1r = buf1g = buf1b = buf2r = buf2g = buf2b = NULL; - bufu8r = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); - bufu8g = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); - bufu8b = (l_uint8 *)LEPT_CALLOC(w, sizeof(l_uint8)); - buf1r = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - buf1g = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - buf1b = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - buf2r = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - buf2g = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - buf2b = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - if (!bufu8r || !bufu8g || !bufu8b || !buf1r || !buf1g || - !buf1b || !buf2r || !buf2g || !buf2b) { - L_ERROR("buffer not made\n", procName); - success = FALSE; - goto buffer_cleanup; - } - - /* Start by priming buf2; line 1 is above line 2 */ - pixGetRGBLine(pixs, 0, bufu8r, bufu8g, bufu8b); - for (j = 0; j < w; j++) { - buf2r[j] = 64 * bufu8r[j]; - buf2g[j] = 64 * bufu8g[j]; - buf2b[j] = 64 * bufu8b[j]; - } - - for (i = 0; i < h - 1; i++) { - /* Swap data 2 --> 1, and read in new line 2 */ - memcpy(buf1r, buf2r, 4 * w); - memcpy(buf1g, buf2g, 4 * w); - memcpy(buf1b, buf2b, 4 * w); - pixGetRGBLine(pixs, i + 1, bufu8r, bufu8g, bufu8b); - for (j = 0; j < w; j++) { - buf2r[j] = 64 * bufu8r[j]; - buf2g[j] = 64 * bufu8g[j]; - buf2b[j] = 64 * bufu8b[j]; - } - - /* Dither */ - lined = datad + i * wpld; - for (j = 0; j < w - 1; j++) { - rval = buf1r[j] / 64; - gval = buf1g[j] / 64; - bval = buf1b[j] / 64; - index = ((rval >> rshift) << (2 * sigbits)) + - ((gval >> rshift) << sigbits) + (bval >> rshift); - cmapindex = indexmap[index]; - SET_DATA_BYTE(lined, j, cmapindex); - pixcmapGetColor(cmap, cmapindex, &rc, &gc, &bc); - - dif = buf1r[j] / 8 - 8 * rc; - if (dif > DifCap) dif = DifCap; - if (dif < -DifCap) dif = -DifCap; - if (dif != 0) { - val1 = buf1r[j + 1] + 3 * dif; - val2 = buf2r[j] + 3 * dif; - val3 = buf2r[j + 1] + 2 * dif; - if (dif > 0) { - buf1r[j + 1] = L_MIN(16383, val1); - buf2r[j] = L_MIN(16383, val2); - buf2r[j + 1] = L_MIN(16383, val3); - } else { - buf1r[j + 1] = L_MAX(0, val1); - buf2r[j] = L_MAX(0, val2); - buf2r[j + 1] = L_MAX(0, val3); - } - } - - dif = buf1g[j] / 8 - 8 * gc; - if (dif > DifCap) dif = DifCap; - if (dif < -DifCap) dif = -DifCap; - if (dif != 0) { - val1 = buf1g[j + 1] + 3 * dif; - val2 = buf2g[j] + 3 * dif; - val3 = buf2g[j + 1] + 2 * dif; - if (dif > 0) { - buf1g[j + 1] = L_MIN(16383, val1); - buf2g[j] = L_MIN(16383, val2); - buf2g[j + 1] = L_MIN(16383, val3); - } else { - buf1g[j + 1] = L_MAX(0, val1); - buf2g[j] = L_MAX(0, val2); - buf2g[j + 1] = L_MAX(0, val3); - } - } - - dif = buf1b[j] / 8 - 8 * bc; - if (dif > DifCap) dif = DifCap; - if (dif < -DifCap) dif = -DifCap; - if (dif != 0) { - val1 = buf1b[j + 1] + 3 * dif; - val2 = buf2b[j] + 3 * dif; - val3 = buf2b[j + 1] + 2 * dif; - if (dif > 0) { - buf1b[j + 1] = L_MIN(16383, val1); - buf2b[j] = L_MIN(16383, val2); - buf2b[j + 1] = L_MIN(16383, val3); - } else { - buf1b[j + 1] = L_MAX(0, val1); - buf2b[j] = L_MAX(0, val2); - buf2b[j + 1] = L_MAX(0, val3); - } - } - } - - /* Get last pixel in row; no downward propagation */ - rval = buf1r[w - 1] / 64; - gval = buf1g[w - 1] / 64; - bval = buf1b[w - 1] / 64; - index = ((rval >> rshift) << (2 * sigbits)) + - ((gval >> rshift) << sigbits) + (bval >> rshift); - SET_DATA_BYTE(lined, w - 1, indexmap[index]); - } - - /* Get last row of pixels; no leftward propagation */ - lined = datad + (h - 1) * wpld; - for (j = 0; j < w; j++) { - rval = buf2r[j] / 64; - gval = buf2g[j] / 64; - bval = buf2b[j] / 64; - index = ((rval >> rshift) << (2 * sigbits)) + - ((gval >> rshift) << sigbits) + (bval >> rshift); - SET_DATA_BYTE(lined, j, indexmap[index]); - } - -buffer_cleanup: - LEPT_FREE(bufu8r); - LEPT_FREE(bufu8g); - LEPT_FREE(bufu8b); - LEPT_FREE(buf1r); - LEPT_FREE(buf1g); - LEPT_FREE(buf1b); - LEPT_FREE(buf2r); - LEPT_FREE(buf2g); - LEPT_FREE(buf2b); - if (!success) pixDestroy(&pixd); - } - - return pixd; -} - - -/*! - * \brief getColorIndexMedianCut() - * - * \param[in] pixel 32 bit rgb - * \param[in] rshift of component: 8 - sigbits - * \param[in] mask over sigbits - * \param[in] sigbits - * \param[out] pindex rgb index value - * \return void - * - *
- * Notes: - * (1) This is used on each pixel in the source image. No checking - * is done on input values. - *- */ -static void -getColorIndexMedianCut(l_uint32 pixel, - l_int32 rshift, - l_uint32 mask, - l_int32 sigbits, - l_int32 *pindex) -{ -l_int32 rval, gval, bval; - - rval = pixel >> (24 + rshift); - gval = (pixel >> (16 + rshift)) & mask; - bval = (pixel >> (8 + rshift)) & mask; - *pindex = (rval << (2 * sigbits)) + (gval << sigbits) + bval; - return; -} - - -/*! - * \brief pixGetColorRegion() - * - * \param[in] pixs 32 bpp; rgb color - * \param[in] sigbits valid: 5, 6 - * \param[in] subsample integer > 0 - * \return vbox minimum 3D box in color space enclosing all pixels, - * or NULL on error - * - *
- * Notes: - * (1) Computes the minimum 3D box in color space enclosing all - * pixels in the image. - *- */ -static L_BOX3D * -pixGetColorRegion(PIX *pixs, - l_int32 sigbits, - l_int32 subsample) -{ -l_int32 rmin, rmax, gmin, gmax, bmin, bmax, rval, gval, bval; -l_int32 w, h, wpl, i, j, rshift; -l_uint32 mask, pixel; -l_uint32 *data, *line; - - PROCNAME("pixGetColorRegion"); - - if (!pixs) - return (L_BOX3D *)ERROR_PTR("pixs not defined", procName, NULL); - - rmin = gmin = bmin = 1000000; - rmax = gmax = bmax = 0; - rshift = 8 - sigbits; - mask = 0xff >> rshift; - pixGetDimensions(pixs, &w, &h, NULL); - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - for (i = 0; i < h; i += subsample) { - line = data + i * wpl; - for (j = 0; j < w; j += subsample) { - pixel = line[j]; - rval = pixel >> (24 + rshift); - gval = (pixel >> (16 + rshift)) & mask; - bval = (pixel >> (8 + rshift)) & mask; - if (rval < rmin) - rmin = rval; - else if (rval > rmax) - rmax = rval; - if (gval < gmin) - gmin = gval; - else if (gval > gmax) - gmax = gval; - if (bval < bmin) - bmin = bval; - else if (bval > bmax) - bmax = bval; - } - } - - return box3dCreate(rmin, rmax, gmin, gmax, bmin, bmax); -} - - -/*! - * \brief medianCutApply() - * - * \param[in] histo array; in rgb colorspace - * \param[in] sigbits - * \param[in] vbox input 3D box - * \param[out] pvbox1, pvbox2 vbox split in two parts - * \return 0 if OK, 1 on error - */ -static l_int32 -medianCutApply(l_int32 *histo, - l_int32 sigbits, - L_BOX3D *vbox, - L_BOX3D **pvbox1, - L_BOX3D **pvbox2) -{ -l_int32 i, j, k, sum, rw, gw, bw, maxw, index; -l_int32 total, left, right; -l_int32 partialsum[128]; -L_BOX3D *vbox1, *vbox2; - - PROCNAME("medianCutApply"); - - if (pvbox1) *pvbox1 = NULL; - if (pvbox2) *pvbox2 = NULL; - if (!histo) - return ERROR_INT("histo not defined", procName, 1); - if (!vbox) - return ERROR_INT("vbox not defined", procName, 1); - if (!pvbox1 || !pvbox2) - return ERROR_INT("&vbox1 and &vbox2 not both defined", procName, 1); - - if (vboxGetCount(vbox, histo, sigbits) == 0) - return ERROR_INT("no pixels in vbox", procName, 1); - - /* If the vbox occupies just one element in color space, it can't - * be split. Leave the 'sortparam' field at 0, so that it goes to - * the tail of the priority queue and stays there, thereby avoiding - * an infinite loop (take off, put back on the head) if it - * happens to be the most populous box! */ - rw = vbox->r2 - vbox->r1 + 1; - gw = vbox->g2 - vbox->g1 + 1; - bw = vbox->b2 - vbox->b1 + 1; - if (rw == 1 && gw == 1 && bw == 1) { - *pvbox1 = box3dCopy(vbox); - return 0; - } - - /* Select the longest axis for splitting */ - maxw = L_MAX(rw, gw); - maxw = L_MAX(maxw, bw); -#if DEBUG_SPLIT_AXES - if (rw == maxw) - lept_stderr("red split\n"); - else if (gw == maxw) - lept_stderr("green split\n"); - else - lept_stderr("blue split\n"); -#endif /* DEBUG_SPLIT_AXES */ - - /* Find the partial sum arrays along the selected axis. */ - total = 0; - if (maxw == rw) { - for (i = vbox->r1; i <= vbox->r2; i++) { - sum = 0; - for (j = vbox->g1; j <= vbox->g2; j++) { - for (k = vbox->b1; k <= vbox->b2; k++) { - index = (i << (2 * sigbits)) + (j << sigbits) + k; - sum += histo[index]; - } - } - total += sum; - partialsum[i] = total; - } - } else if (maxw == gw) { - for (i = vbox->g1; i <= vbox->g2; i++) { - sum = 0; - for (j = vbox->r1; j <= vbox->r2; j++) { - for (k = vbox->b1; k <= vbox->b2; k++) { - index = (i << sigbits) + (j << (2 * sigbits)) + k; - sum += histo[index]; - } - } - total += sum; - partialsum[i] = total; - } - } else { /* maxw == bw */ - for (i = vbox->b1; i <= vbox->b2; i++) { - sum = 0; - for (j = vbox->r1; j <= vbox->r2; j++) { - for (k = vbox->g1; k <= vbox->g2; k++) { - index = i + (j << (2 * sigbits)) + (k << sigbits); - sum += histo[index]; - } - } - total += sum; - partialsum[i] = total; - } - } - - /* Determine the cut planes, making sure that two vboxes - * are always produced. Generate the two vboxes and compute - * the sum in each of them. Choose the cut plane within - * the greater of the (left, right) sides of the bin in which - * the median pixel resides. Here's the surprise: go halfway - * into that side. By doing that, you technically move away - * from "median cut," but in the process a significant number - * of low-count vboxes are produced, allowing much better - * reproduction of low-count spot colors. */ - vbox1 = vbox2 = NULL; - if (maxw == rw) { - for (i = vbox->r1; i <= vbox->r2; i++) { - if (partialsum[i] > total / 2) { - vbox1 = box3dCopy(vbox); - vbox2 = box3dCopy(vbox); - left = i - vbox->r1; - right = vbox->r2 - i; - if (left <= right) - vbox1->r2 = L_MIN(vbox->r2 - 1, i + right / 2); - else /* left > right */ - vbox1->r2 = L_MAX(vbox->r1, i - 1 - left / 2); - vbox2->r1 = vbox1->r2 + 1; - break; - } - } - } else if (maxw == gw) { - for (i = vbox->g1; i <= vbox->g2; i++) { - if (partialsum[i] > total / 2) { - vbox1 = box3dCopy(vbox); - vbox2 = box3dCopy(vbox); - left = i - vbox->g1; - right = vbox->g2 - i; - if (left <= right) - vbox1->g2 = L_MIN(vbox->g2 - 1, i + right / 2); - else /* left > right */ - vbox1->g2 = L_MAX(vbox->g1, i - 1 - left / 2); - vbox2->g1 = vbox1->g2 + 1; - break; - } - } - } else { /* maxw == bw */ - for (i = vbox->b1; i <= vbox->b2; i++) { - if (partialsum[i] > total / 2) { - vbox1 = box3dCopy(vbox); - vbox2 = box3dCopy(vbox); - left = i - vbox->b1; - right = vbox->b2 - i; - if (left <= right) - vbox1->b2 = L_MIN(vbox->b2 - 1, i + right / 2); - else /* left > right */ - vbox1->b2 = L_MAX(vbox->b1, i - 1 - left / 2); - vbox2->b1 = vbox1->b2 + 1; - break; - } - } - } - *pvbox1 = vbox1; - *pvbox2 = vbox2; - if (!vbox1) - return ERROR_INT("vbox1 not made; shouldn't happen", procName, 1); - if (!vbox2) - return ERROR_INT("vbox2 not made; shouldn't happen", procName, 1); - vbox1->npix = vboxGetCount(vbox1, histo, sigbits); - vbox2->npix = vboxGetCount(vbox2, histo, sigbits); - vbox1->vol = vboxGetVolume(vbox1); - vbox2->vol = vboxGetVolume(vbox2); - - return 0; -} - - -/*! - * \brief pixcmapGenerateFromMedianCuts() - * - * \param[in] lh priority queue of pointers to vboxes - * \param[in] histo - * \param[in] sigbits valid: 5 or 6 - * \return cmap, or NULL on error - * - *
- * Notes: - * (1) Each vbox in the heap represents a color in the colormap. - * (2) As a side-effect, the histo becomes an inverse colormap, - * where the part of the array correpsonding to each vbox - * is labeled with the cmap index for that vbox. Then - * for each rgb pixel, the colormap index is found directly - * by mapping the rgb value to the histo array index. - *- */ -static PIXCMAP * -pixcmapGenerateFromMedianCuts(L_HEAP *lh, - l_int32 *histo, - l_int32 sigbits) -{ -l_int32 index, rval, gval, bval; -L_BOX3D *vbox; -PIXCMAP *cmap; - - PROCNAME("pixcmapGenerateFromMedianCuts"); - - if (!lh) - return (PIXCMAP *)ERROR_PTR("lh not defined", procName, NULL); - if (!histo) - return (PIXCMAP *)ERROR_PTR("histo not defined", procName, NULL); - - rval = gval = bval = 0; /* make compiler happy */ - cmap = pixcmapCreate(8); - index = 0; - while (lheapGetCount(lh) > 0) { - vbox = (L_BOX3D *)lheapRemove(lh); - vboxGetAverageColor(vbox, histo, sigbits, index, &rval, &gval, &bval); - pixcmapAddColor(cmap, rval, gval, bval); - LEPT_FREE(vbox); - index++; - } - - return cmap; -} - - -/*! - * \brief vboxGetAverageColor() - * - * \param[in] vbox 3d region of color space for one quantized color - * \param[in] histo - * \param[in] sigbits valid: 5 or 6 - * \param[in] index if >= 0, assign to all colors in histo in this vbox - * \param[out] prval, pgval, pbval average color - * \return cmap, or NULL on error - * - *
- * Notes: - * (1) The vbox represents one color in the colormap. - * (2) If index >= 0, as a side-effect, all array elements in - * the histo corresponding to the vbox are labeled with this - * cmap index for that vbox. Otherwise, the histo array - * is not changed. - * (3) The vbox is quantized in sigbits. So the actual 8-bit color - * components are found by multiplying the quantized value - * by either 4 or 8. We must add 0.5 to the quantized index - * before multiplying to get the approximate 8-bit color in - * the center of the vbox; otherwise we get values on - * the lower corner. - *- */ -static l_int32 -vboxGetAverageColor(L_BOX3D *vbox, - l_int32 *histo, - l_int32 sigbits, - l_int32 index, - l_int32 *prval, - l_int32 *pgval, - l_int32 *pbval) -{ -l_int32 i, j, k, ntot, mult, histoindex, rsum, gsum, bsum; - - PROCNAME("vboxGetAverageColor"); - - if (!vbox) - return ERROR_INT("vbox not defined", procName, 1); - if (!histo) - return ERROR_INT("histo not defined", procName, 1); - if (!prval || !pgval || !pbval) - return ERROR_INT("&p*val not all defined", procName, 1); - - *prval = *pgval = *pbval = 0; - ntot = 0; - mult = 1 << (8 - sigbits); - rsum = gsum = bsum = 0; - for (i = vbox->r1; i <= vbox->r2; i++) { - for (j = vbox->g1; j <= vbox->g2; j++) { - for (k = vbox->b1; k <= vbox->b2; k++) { - histoindex = (i << (2 * sigbits)) + (j << sigbits) + k; - ntot += histo[histoindex]; - rsum += (l_int32)(histo[histoindex] * (i + 0.5) * mult); - gsum += (l_int32)(histo[histoindex] * (j + 0.5) * mult); - bsum += (l_int32)(histo[histoindex] * (k + 0.5) * mult); - if (index >= 0) - histo[histoindex] = index; - } - } - } - - if (ntot == 0) { - *prval = mult * (vbox->r1 + vbox->r2 + 1) / 2; - *pgval = mult * (vbox->g1 + vbox->g2 + 1) / 2; - *pbval = mult * (vbox->b1 + vbox->b2 + 1) / 2; - } else { - *prval = rsum / ntot; - *pgval = gsum / ntot; - *pbval = bsum / ntot; - } - -#if DEBUG_MC_COLORS - lept_stderr("ntot[%d] = %d: [%d, %d, %d], (%d, %d, %d)\n", - index, ntot, vbox->r2 - vbox->r1 + 1, - vbox->g2 - vbox->g1 + 1, vbox->b2 - vbox->b1 + 1, - *prval, *pgval, *pbval); -#endif /* DEBUG_MC_COLORS */ - - return 0; -} - - -/*! - * \brief vboxGetCount() - * - * \param[in] vbox 3d region of color space for one quantized color - * \param[in] histo - * \param[in] sigbits valid: 5 or 6 - * \return number of image pixels in this region, or 0 on error - */ -static l_int32 -vboxGetCount(L_BOX3D *vbox, - l_int32 *histo, - l_int32 sigbits) -{ -l_int32 i, j, k, npix, index; - - PROCNAME("vboxGetCount"); - - if (!vbox) - return ERROR_INT("vbox not defined", procName, 0); - if (!histo) - return ERROR_INT("histo not defined", procName, 0); - - npix = 0; - for (i = vbox->r1; i <= vbox->r2; i++) { - for (j = vbox->g1; j <= vbox->g2; j++) { - for (k = vbox->b1; k <= vbox->b2; k++) { - index = (i << (2 * sigbits)) + (j << sigbits) + k; - npix += histo[index]; - } - } - } - - return npix; -} - - -/*! - * \brief vboxGetVolume() - * - * \param[in] vbox 3d region of color space for one quantized color - * \return quantized volume of vbox, or 0 on error - */ -static l_int32 -vboxGetVolume(L_BOX3D *vbox) -{ - PROCNAME("vboxGetVolume"); - - if (!vbox) - return ERROR_INT("vbox not defined", procName, 0); - - return ((vbox->r2 - vbox->r1 + 1) * (vbox->g2 - vbox->g1 + 1) * - (vbox->b2 - vbox->b1 + 1)); -} - -/*! - * \brief box3dCreate() - * - * \param[in] r1, r2, g1, g2, b1, b2 initial values - * \return vbox - */ -static L_BOX3D * -box3dCreate(l_int32 r1, - l_int32 r2, - l_int32 g1, - l_int32 g2, - l_int32 b1, - l_int32 b2) -{ -L_BOX3D *vbox; - - vbox = (L_BOX3D *)LEPT_CALLOC(1, sizeof(L_BOX3D)); - vbox->r1 = r1; - vbox->r2 = r2; - vbox->g1 = g1; - vbox->g2 = g2; - vbox->b1 = b1; - vbox->b2 = b2; - return vbox; -} - - -/*! - * \brief box3dCopy() - * - * \param[in] vbox - * \return vboxc copy of vbox - * - *
- * Notes: - * Don't copy the sortparam. - *- */ -static L_BOX3D * -box3dCopy(L_BOX3D *vbox) -{ -L_BOX3D *vboxc; - - PROCNAME("box3dCopy"); - - if (!vbox) - return (L_BOX3D *)ERROR_PTR("vbox not defined", procName, NULL); - - vboxc = box3dCreate(vbox->r1, vbox->r2, vbox->g1, vbox->g2, - vbox->b1, vbox->b2); - vboxc->npix = vbox->npix; - vboxc->vol = vbox->vol; - return vboxc; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colorseg.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colorseg.c deleted file mode 100644 index c3f3c3d8..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colorseg.c +++ /dev/null @@ -1,658 +0,0 @@ -/*====================================================================* - - 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 colorseg.c - *
- * - * Unsupervised color segmentation - * - * PIX *pixColorSegment() - * PIX *pixColorSegmentCluster() - * static l_int32 pixColorSegmentTryCluster() - * l_int32 pixAssignToNearestColor() - * l_int32 pixColorSegmentClean() - * l_int32 pixColorSegmentRemoveColors() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Color segmentation proceeds in four phases: - * - * Phase 1: pixColorSegmentCluster() - * The image is traversed in raster order. Each pixel either - * becomes the representative for a new cluster or is assigned to an - * existing cluster. Assignment is greedy. The data is stored in - * a colormapped image. Three auxiliary arrays are used to hold - * the colors of the representative pixels, for fast lookup. - * The average color in each cluster is computed. - * - * Phase 2. pixAssignToNearestColor() - * A second non-greedy clustering pass is performed, where each pixel - * is assigned to the nearest cluster average. We also keep track - * of how many pixels are assigned to each cluster. - * - * Phase 3. pixColorSegmentClean() - * For each cluster, starting with the largest, do a morphological - * closing to eliminate small components within larger ones. - * - * Phase 4. pixColorSegmentRemoveColors() - * Eliminate all colors except the most populated 'finalcolors'. - * Then remove unused colors from the colormap, and reassign those - * pixels to the nearest remaining cluster, using the original pixel values. - * - * Notes: - * (1) The goal is to generate a small number of colors. - * Typically this would be specified by 'finalcolors', - * a number that would be somewhere between 3 and 6. - * The parameter 'maxcolors' specifies the maximum number of - * colors generated in the first phase. This should be - * larger than finalcolors, perhaps twice as large. - * If more than 'maxcolors' are generated in the first phase - * using the input 'maxdist', the distance is repeatedly - * increased by a multiplicative factor until the condition - * is satisfied. The implicit relation between 'maxdist' - * and 'maxcolors' is thus adjusted programmatically. - * (2) As a very rough guideline, given a target value of 'finalcolors', - * here are approximate values of 'maxdist' and 'maxcolors' - * to start with: - * - * finalcolors maxcolors maxdist - * ----------- --------- ------- - * 3 6 100 - * 4 8 90 - * 5 10 75 - * 6 12 60 - * - * For a given number of finalcolors, if you use too many - * maxcolors, the result will be noisy. If you use too few, - * the result will be a relatively poor assignment of colors. - *- */ -PIX * -pixColorSegment(PIX *pixs, - l_int32 maxdist, - l_int32 maxcolors, - l_int32 selsize, - l_int32 finalcolors, - l_int32 debugflag) -{ -l_int32 *countarray; -PIX *pixd; - - PROCNAME("pixColorSegment"); - - 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); - - /* Phase 1; original segmentation */ - pixd = pixColorSegmentCluster(pixs, maxdist, maxcolors, debugflag); - if (!pixd) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - if (debugflag) { - lept_mkdir("lept/segment"); - pixWriteDebug("/tmp/lept/segment/colorseg1.png", pixd, IFF_PNG); - } - - /* Phase 2; refinement in pixel assignment */ - if ((countarray = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32))) == NULL) { - pixDestroy(&pixd); - return (PIX *)ERROR_PTR("countarray not made", procName, NULL); - } - pixAssignToNearestColor(pixd, pixs, NULL, LEVEL_IN_OCTCUBE, countarray); - if (debugflag) - pixWriteDebug("/tmp/lept/segment/colorseg2.png", pixd, IFF_PNG); - - /* Phase 3: noise removal by separately closing each color */ - pixColorSegmentClean(pixd, selsize, countarray); - LEPT_FREE(countarray); - if (debugflag) - pixWriteDebug("/tmp/lept/segment/colorseg3.png", pixd, IFF_PNG); - - /* Phase 4: removal of colors with small population and - * reassignment of pixels to remaining colors */ - pixColorSegmentRemoveColors(pixd, pixs, finalcolors); - return pixd; -} - - -/*! - * \brief pixColorSegmentCluster() - * - * \param[in] pixs 32 bpp; 24-bit color - * \param[in] maxdist max euclidean dist to existing cluster - * \param[in] maxcolors max number of colors allowed in first pass - * \param[in] debugflag 1 for debug output; 0 otherwise - * \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 debugflag) -{ -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); - - pixGetDimensions(pixs, &w, &h, NULL); - 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, debugflag); - niters++; - if (!ret) { - ncolors = pixcmapGetCount(cmap); - if (debugflag) - L_INFO("Success with %d colors after %d iters\n", procName, - ncolors, niters); - break; - } - if (niters == MAX_ALLOWED_ITERATIONS) { - L_WARNING("too many iters; newmaxdist = %d\n", - 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; -} - - -/*! - * \brief pixColorSegmentTryCluster() - * - * \param[in] pixd - * \param[in] pixs - * \param[in] maxdist - * \param[in] maxcolors - * \param[in] debugflag 1 for debug output; 0 otherwise - * \return 0 if OK, 1 on error - * - *
- * Notes: - * This function should only be called from pixColorSegCluster() - *- */ -static l_int32 -pixColorSegmentTryCluster(PIX *pixd, - PIX *pixs, - l_int32 maxdist, - l_int32 maxcolors, - l_int32 debugflag) -{ -l_int32 rmap[256], gmap[256], bmap[256]; -l_int32 w, h, wpls, wpld, i, j, k, found, ret, index, ncolors; -l_int32 rval, gval, bval, dist2, maxdist2; -l_int32 countarray[256]; -l_int32 rsum[256], gsum[256], bsum[256]; -l_uint32 *ppixel; -l_uint32 *datas, *datad, *lines, *lined; -PIXCMAP *cmap; - - PROCNAME("pixColorSegmentTryCluster"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - - w = pixGetWidth(pixs); - h = pixGetHeight(pixs); - maxdist2 = maxdist * maxdist; - cmap = pixGetColormap(pixd); - pixcmapClear(cmap); - for (k = 0; k < 256; k++) { - rsum[k] = gsum[k] = bsum[k] = 0; - rmap[k] = gmap[k] = bmap[k] = 0; - } - - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - ncolors = 0; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - ppixel = lines + j; - rval = GET_DATA_BYTE(ppixel, COLOR_RED); - gval = GET_DATA_BYTE(ppixel, COLOR_GREEN); - bval = GET_DATA_BYTE(ppixel, COLOR_BLUE); - ncolors = pixcmapGetCount(cmap); - found = FALSE; - for (k = 0; k < ncolors; k++) { - dist2 = (rval - rmap[k]) * (rval - rmap[k]) + - (gval - gmap[k]) * (gval - gmap[k]) + - (bval - bmap[k]) * (bval - bmap[k]); - if (dist2 <= maxdist2) { /* take it; greedy */ - found = TRUE; - SET_DATA_BYTE(lined, j, k); - countarray[k]++; - rsum[k] += rval; - gsum[k] += gval; - bsum[k] += bval; - break; - } - } - if (!found) { /* Add a new color */ - ret = pixcmapAddNewColor(cmap, rval, gval, bval, &index); -/* lept_stderr( - "index = %d, (i,j) = (%d,%d), rgb = (%d, %d, %d)\n", - index, i, j, rval, gval, bval); */ - if (ret == 0 && index < maxcolors) { - countarray[index] = 1; - SET_DATA_BYTE(lined, j, index); - rmap[index] = rval; - gmap[index] = gval; - bmap[index] = bval; - rsum[index] = rval; - gsum[index] = gval; - bsum[index] = bval; - } else { - if (debugflag) { - L_INFO("maxcolors exceeded for maxdist = %d\n", - procName, maxdist); - } - return 1; - } - } - } - } - - /* Replace the colors in the colormap by the averages */ - for (k = 0; k < ncolors; k++) { - rval = rsum[k] / countarray[k]; - gval = gsum[k] / countarray[k]; - bval = bsum[k] / countarray[k]; - pixcmapResetColor(cmap, k, rval, gval, bval); - } - - return 0; -} - - -/*! - * \brief pixAssignToNearestColor() - * - * \param[in] pixd 8 bpp, colormapped - * \param[in] pixs 32 bpp; 24-bit color - * \param[in] pixm [optional] 1 bpp - * \param[in] level of octcube used for finding nearest color in cmap - * \param[in] countarray [optional] ptr to array, in which we can store - * the number of pixels found in each color in - * the colormap in pixd - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is used in phase 2 of color segmentation, where pixs - * is the original input image to pixColorSegment(), and - * pixd is the colormapped image returned from - * pixColorSegmentCluster(). It is also used, with a mask, - * in phase 4. - * (2) This is an in-place operation. - * (3) The colormap in pixd is unchanged. - * (4) pixs and pixd must be the same size (w, h). - * (5) The selection mask pixm can be null. If it exists, it must - * be the same size as pixs and pixd, and only pixels - * corresponding to fg in pixm are assigned. Set to - * NULL if all pixels in pixd are to be assigned. - * (6) The countarray can be null. If it exists, it is pre-allocated - * and of a size at least equal to the size of the colormap in pixd. - * (7) This does a best-fit (non-greedy) assignment of pixels to - * existing clusters. Specifically, it assigns each pixel - * in pixd to the color index in the pixd colormap that has a - * color closest to the corresponding rgb pixel in pixs. - * (8) 'level' is the octcube level used to quickly find the nearest - * color in the colormap for each pixel. For color segmentation, - * this parameter is set to LEVEL_IN_OCTCUBE. - * (9) We build a mapping table from octcube to colormap index so - * that this function can run in a time (otherwise) independent - * of the number of colors in the colormap. This avoids a - * brute-force search for the closest colormap color to each - * pixel in the image. - *- */ -l_ok -pixAssignToNearestColor(PIX *pixd, - PIX *pixs, - PIX *pixm, - l_int32 level, - l_int32 *countarray) -{ -l_int32 w, h, wpls, wpld, wplm, i, j, success; -l_int32 rval, gval, bval, index; -l_int32 *cmaptab; -l_uint32 octindex; -l_uint32 *rtab, *gtab, *btab; -l_uint32 *ppixel; -l_uint32 *datas, *datad, *datam, *lines, *lined, *linem; -PIXCMAP *cmap; - - PROCNAME("pixAssignToNearestColor"); - - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - if ((cmap = pixGetColormap(pixd)) == NULL) - return ERROR_INT("cmap not found", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not 32 bpp", procName, 1); - if (level < 1 || level > 6) - return ERROR_INT("level not in [1 ... 6]", procName, 1); - - /* Set up the tables to map rgb to the nearest colormap index */ - success = TRUE; - makeRGBToIndexTables(level, &rtab, >ab, &btab); - cmaptab = pixcmapToOctcubeLUT(cmap, level, L_MANHATTAN_DISTANCE); - if (!rtab || !gtab || !btab || !cmaptab) { - L_ERROR("failure to make a table\n", procName); - success = FALSE; - goto cleanup_arrays; - } - - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - if (pixm) { - datam = pixGetData(pixm); - wplm = pixGetWpl(pixm); - } - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - if (pixm) - linem = datam + i * wplm; - for (j = 0; j < w; j++) { - if (pixm) { - if (!GET_DATA_BIT(linem, j)) - continue; - } - ppixel = lines + j; - rval = GET_DATA_BYTE(ppixel, COLOR_RED); - gval = GET_DATA_BYTE(ppixel, COLOR_GREEN); - bval = GET_DATA_BYTE(ppixel, COLOR_BLUE); - /* Map from rgb to octcube index */ - getOctcubeIndexFromRGB(rval, gval, bval, rtab, gtab, btab, - &octindex); - /* Map from octcube index to nearest colormap index */ - index = cmaptab[octindex]; - if (countarray) - countarray[index]++; - SET_DATA_BYTE(lined, j, index); - } - } - -cleanup_arrays: - LEPT_FREE(cmaptab); - LEPT_FREE(rtab); - LEPT_FREE(gtab); - LEPT_FREE(btab); - return (success) ? 0 : 1; -} - - -/*! - * \brief pixColorSegmentClean() - * - * \param[in] pixs 8 bpp, colormapped - * \param[in] selsize for closing - * \param[in] countarray ptr to array containing the number of pixels - * found in each color in the colormap - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This operation is in-place. - * (2) This is phase 3 of color segmentation. It is the first - * part of a two-step noise removal process. Colors with a - * large population are closed first; this operation absorbs - * small sets of intercolated pixels of a different color. - *- */ -l_ok -pixColorSegmentClean(PIX *pixs, - l_int32 selsize, - l_int32 *countarray) -{ -l_int32 i, ncolors, val; -l_uint32 val32; -NUMA *na, *nasi; -PIX *pixt1, *pixt2; -PIXCMAP *cmap; - - PROCNAME("pixColorSegmentClean"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not 8 bpp", procName, 1); - if ((cmap = pixGetColormap(pixs)) == NULL) - return ERROR_INT("cmap not found", procName, 1); - if (!countarray) - return ERROR_INT("countarray not defined", procName, 1); - if (selsize <= 1) - return 0; /* nothing to do */ - - /* Sort colormap indices in decreasing order of pixel population */ - ncolors = pixcmapGetCount(cmap); - na = numaCreate(ncolors); - for (i = 0; i < ncolors; i++) - numaAddNumber(na, countarray[i]); - nasi = numaGetSortIndex(na, L_SORT_DECREASING); - numaDestroy(&na); - if (!nasi) - return ERROR_INT("nasi not made", procName, 1); - - /* For each color, in order of decreasing population, - * do a closing and absorb the added pixels. Note that - * if the closing removes pixels at the border, they'll - * still appear in the xor and will be properly (re)set. */ - for (i = 0; i < ncolors; i++) { - numaGetIValue(nasi, i, &val); - pixt1 = pixGenerateMaskByValue(pixs, val, 1); - pixt2 = pixCloseSafeCompBrick(NULL, pixt1, selsize, selsize); - pixXor(pixt2, pixt2, pixt1); /* pixels to be added to type 'val' */ - pixcmapGetColor32(cmap, val, &val32); - pixSetMasked(pixs, pixt2, val32); /* add them */ - pixDestroy(&pixt1); - pixDestroy(&pixt2); - } - numaDestroy(&nasi); - return 0; -} - - -/*! - * \brief pixColorSegmentRemoveColors() - * - * \param[in] pixd 8 bpp, colormapped - * \param[in] pixs 32 bpp rgb, with initial pixel values - * \param[in] finalcolors max number of colors to retain - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This operation is in-place. - * (2) This is phase 4 of color segmentation, and the second part - * of the 2-step noise removal. Only 'finalcolors' different - * colors are retained, with colors with smaller populations - * being replaced by the nearest color of the remaining colors. - * For highest accuracy, for pixels that are being replaced, - * we find the nearest colormap color to the original rgb color. - *- */ -l_ok -pixColorSegmentRemoveColors(PIX *pixd, - PIX *pixs, - l_int32 finalcolors) -{ -l_int32 i, ncolors, index, tempindex; -l_int32 *tab; -l_uint32 tempcolor; -NUMA *na, *nasi; -PIX *pixm; -PIXCMAP *cmap; - - PROCNAME("pixColorSegmentRemoveColors"); - - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - if (pixGetDepth(pixd) != 8) - return ERROR_INT("pixd not 8 bpp", procName, 1); - if ((cmap = pixGetColormap(pixd)) == NULL) - return ERROR_INT("cmap not found", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - ncolors = pixcmapGetCount(cmap); - if (finalcolors >= ncolors) /* few enough colors already; nothing to do */ - return 0; - - /* Generate a mask over all pixels that are not in the - * 'finalcolors' most populated colors. Save the colormap - * index of any one of the retained colors in 'tempindex'. - * The LUT has values 0 for the 'finalcolors' most populated colors, - * which will be retained; and 1 for the rest, which are marked - * by fg pixels in pixm and will be removed. */ - na = pixGetCmapHistogram(pixd, 1); - if ((nasi = numaGetSortIndex(na, L_SORT_DECREASING)) == NULL) { - numaDestroy(&na); - return ERROR_INT("nasi not made", procName, 1); - } - numaGetIValue(nasi, finalcolors - 1, &tempindex); /* retain down to this */ - pixcmapGetColor32(cmap, tempindex, &tempcolor); /* use this color */ - tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - for (i = finalcolors; i < ncolors; i++) { - numaGetIValue(nasi, i, &index); - tab[index] = 1; - } - - pixm = pixMakeMaskFromLUT(pixd, tab); - LEPT_FREE(tab); - - /* Reassign the masked pixels temporarily to the saved index - * (tempindex). This guarantees that no pixels are labeled by - * a colormap index of any colors that will be removed. - * The actual value doesn't matter, as long as it's one - * of the retained colors, because these pixels will later - * be reassigned based on the full set of colors retained - * in the colormap. */ - pixSetMasked(pixd, pixm, tempcolor); - - /* Now remove unused colors from the colormap. This reassigns - * image pixels as required. */ - pixRemoveUnusedColors(pixd); - - /* Finally, reassign the pixels under the mask (those that were - * given a 'tempindex' value) to the nearest color in the colormap. - * This is the function used in phase 2 on all image pixels; here - * it is only used on the masked pixels given by pixm. */ - pixAssignToNearestColor(pixd, pixs, pixm, LEVEL_IN_OCTCUBE, NULL); - - pixDestroy(&pixm); - numaDestroy(&na); - numaDestroy(&nasi); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colorspace.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colorspace.c deleted file mode 100644 index 18a57d10..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/colorspace.c +++ /dev/null @@ -1,2419 +0,0 @@ -/*====================================================================* - - 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 colorspace.c - *
- * - * Colorspace conversion between RGB and HSV - * PIX *pixConvertRGBToHSV() - * PIX *pixConvertHSVToRGB() - * l_int32 convertRGBToHSV() - * l_int32 convertHSVToRGB() - * l_int32 pixcmapConvertRGBToHSV() - * l_int32 pixcmapConvertHSVToRGB() - * PIX *pixConvertRGBToHue() - * PIX *pixConvertRGBToSaturation() - * PIX *pixConvertRGBToValue() - * - * Selection and display of range of colors in HSV space - * PIX *pixMakeRangeMaskHS() - * PIX *pixMakeRangeMaskHV() - * PIX *pixMakeRangeMaskSV() - * PIX *pixMakeHistoHS() - * PIX *pixMakeHistoHV() - * PIX *pixMakeHistoSV() - * PIX *pixFindHistoPeaksHSV() - * PIX *displayHSVColorRange() - * - * Colorspace conversion between RGB and YUV - * PIX *pixConvertRGBToYUV() - * PIX *pixConvertYUVToRGB() - * l_int32 convertRGBToYUV() - * l_int32 convertYUVToRGB() - * l_int32 pixcmapConvertRGBToYUV() - * l_int32 pixcmapConvertYUVToRGB() - * - * Colorspace conversion between RGB and XYZ - * FPIXA *pixConvertRGBToXYZ() - * PIX *fpixaConvertXYZToRGB() - * l_int32 convertRGBToXYZ() - * l_int32 convertXYZToRGB() - * - * Colorspace conversion between XYZ and LAB - * FPIXA *fpixaConvertXYZToLAB() - * PIX *fpixaConvertLABToXYZ() - * l_int32 convertXYZToLAB() - * l_int32 convertLABToXYZ() - * static l_float32 lab_forward() - * static l_float32 lab_reverse() - * - * Colorspace conversion between RGB and LAB - * FPIXA *pixConvertRGBToLAB() - * PIX *fpixaConvertLABToRGB() - * l_int32 convertRGBToLAB() - * l_int32 convertLABToRGB() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) For pixs = pixd, this is in-place; otherwise pixd must be NULL. - * (2) The definition of our HSV space is given in convertRGBToHSV(). - * (3) The h, s and v values are stored in the same places as - * the r, g and b values, respectively. Here, they are explicitly - * placed in the 3 MS bytes in the pixel. - * (4) Normalizing to 1 and considering the r,g,b components, - * a simple way to understand the HSV space is: - * ~ v = max(r,g,b) - * ~ s = (max - min) / max - * ~ h ~ (mid - min) / (max - min) [apart from signs and constants] - * (5) Normalizing to 1, some properties of the HSV space are: - * ~ For gray values (r = g = b) along the continuum between - * black and white: - * s = 0 (becoming undefined as you approach black) - * h is undefined everywhere - * ~ Where one component is saturated and the others are zero: - * v = 1 - * s = 1 - * h = 0 (r = max), 1/3 (g = max), 2/3 (b = max) - * ~ Where two components are saturated and the other is zero: - * v = 1 - * s = 1 - * h = 1/2 (if r = 0), 5/6 (if g = 0), 1/6 (if b = 0) - * (6) Dividing each component by a constant c > 1 reduces the - * brightness v, but leaves the saturation and hue invariant. - *- */ -PIX * -pixConvertRGBToHSV(PIX *pixd, - PIX *pixs) -{ -l_int32 w, h, d, wpl, i, j, rval, gval, bval, hval, sval, vval; -l_uint32 *line, *data; -PIXCMAP *cmap; - - PROCNAME("pixConvertRGBToHSV"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixd && pixd != pixs) - return (PIX *)ERROR_PTR("pixd defined and not inplace", procName, pixd); - - d = pixGetDepth(pixs); - cmap = pixGetColormap(pixs); - if (!cmap && d != 32) - return (PIX *)ERROR_PTR("not cmapped or rgb", procName, pixd); - - if (!pixd) - pixd = pixCopy(NULL, pixs); - - cmap = pixGetColormap(pixd); - if (cmap) { /* just convert the colormap */ - pixcmapConvertRGBToHSV(cmap); - return pixd; - } - - /* Convert RGB image */ - pixGetDimensions(pixd, &w, &h, NULL); - wpl = pixGetWpl(pixd); - data = pixGetData(pixd); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - extractRGBValues(line[j], &rval, &gval, &bval); - convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval); - line[j] = (hval << 24) | (sval << 16) | (vval << 8); - } - } - - return pixd; -} - - -/*! - * \brief pixConvertHSVToRGB() - * - * \param[in] pixd can be NULL; if not NULL, must == pixs - * \param[in] pixs - * \return pixd always - * - *
- * Notes: - * (1) For pixs = pixd, this is in-place; otherwise pixd must be NULL. - * (2) The user takes responsibility for making sure that pixs is - * in our HSV space. The definition of our HSV space is given - * in convertRGBToHSV(). - * (3) The h, s and v values are stored in the same places as - * the r, g and b values, respectively. Here, they are explicitly - * placed in the 3 MS bytes in the pixel. - *- */ -PIX * -pixConvertHSVToRGB(PIX *pixd, - PIX *pixs) -{ -l_int32 w, h, d, wpl, i, j, rval, gval, bval, hval, sval, vval; -l_uint32 pixel; -l_uint32 *line, *data; -PIXCMAP *cmap; - - PROCNAME("pixConvertHSVToRGB"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixd && pixd != pixs) - return (PIX *)ERROR_PTR("pixd defined and not inplace", procName, pixd); - - d = pixGetDepth(pixs); - cmap = pixGetColormap(pixs); - if (!cmap && d != 32) - return (PIX *)ERROR_PTR("not cmapped or hsv", procName, pixd); - - if (!pixd) - pixd = pixCopy(NULL, pixs); - - cmap = pixGetColormap(pixd); - if (cmap) { /* just convert the colormap */ - pixcmapConvertHSVToRGB(cmap); - return pixd; - } - - /* Convert HSV image */ - pixGetDimensions(pixd, &w, &h, NULL); - wpl = pixGetWpl(pixd); - data = pixGetData(pixd); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - pixel = line[j]; - hval = pixel >> 24; - sval = (pixel >> 16) & 0xff; - vval = (pixel >> 8) & 0xff; - convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval); - composeRGBPixel(rval, gval, bval, line + j); - } - } - - return pixd; -} - - -/*! - * \brief convertRGBToHSV() - * - * \param[in] rval, gval, bval RGB input - * \param[out] phval, psval, pvval comparable HSV values - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The range of returned values is: - * h [0 ... 239] - * s [0 ... 255] - * v [0 ... 255] - * (2) If r = g = b, the pixel is gray (s = 0), and we define h = 0. - * (3) h wraps around, so that h = 0 and h = 240 are equivalent - * in hue space. - * (4) h has the following correspondence to color: - * h = 0 magenta - * h = 40 red - * h = 80 yellow - * h = 120 green - * h = 160 cyan - * h = 200 blue - *- */ -l_ok -convertRGBToHSV(l_int32 rval, - l_int32 gval, - l_int32 bval, - l_int32 *phval, - l_int32 *psval, - l_int32 *pvval) -{ -l_int32 minrg, maxrg, min, max, delta; -l_float32 h; - - PROCNAME("convertRGBToHSV"); - - if (phval) *phval = 0; - if (psval) *psval = 0; - if (pvval) *pvval = 0; - if (!phval || !psval || !pvval) - return ERROR_INT("&hval, &sval, &vval not all defined", procName, 1); - - minrg = L_MIN(rval, gval); - min = L_MIN(minrg, bval); - maxrg = L_MAX(rval, gval); - max = L_MAX(maxrg, bval); - delta = max - min; - - *pvval = max; - if (delta == 0) { /* gray; no chroma */ - *phval = 0; - *psval = 0; - } else { - *psval = (l_int32)(255. * (l_float32)delta / (l_float32)max + 0.5); - if (rval == max) /* between magenta and yellow */ - h = (l_float32)(gval - bval) / (l_float32)delta; - else if (gval == max) /* between yellow and cyan */ - h = 2. + (l_float32)(bval - rval) / (l_float32)delta; - else /* between cyan and magenta */ - h = 4. + (l_float32)(rval - gval) / (l_float32)delta; - h *= 40.0; - if (h < 0.0) - h += 240.0; - if (h >= 239.5) - h = 0.0; - *phval = (l_int32)(h + 0.5); - } - - return 0; -} - - -/*! - * \brief convertHSVToRGB() - * - * \param[in] hval, sval, vval HSV input - * \param[out] prval, pgval, pbval comparable RGB values - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) See convertRGBToHSV() for valid input range of HSV values - * and their interpretation in color space. - *- */ -l_ok -convertHSVToRGB(l_int32 hval, - l_int32 sval, - l_int32 vval, - l_int32 *prval, - l_int32 *pgval, - l_int32 *pbval) -{ -l_int32 i, x, y, z; -l_float32 h, f, s; - - PROCNAME("convertHSVToRGB"); - - if (prval) *prval = 0; - if (pgval) *pgval = 0; - if (pbval) *pbval = 0; - if (!prval || !pgval || !pbval) - return ERROR_INT("&rval, &gval, &bval not all defined", procName, 1); - - if (sval == 0) { /* gray */ - *prval = vval; - *pgval = vval; - *pbval = vval; - } else { - if (hval < 0 || hval > 240) - return ERROR_INT("invalid hval", procName, 1); - if (hval == 240) - hval = 0; - h = (l_float32)hval / 40.; - i = (l_int32)h; - f = h - i; - s = (l_float32)sval / 255.; - x = (l_int32)(vval * (1. - s) + 0.5); - y = (l_int32)(vval * (1. - s * f) + 0.5); - z = (l_int32)(vval * (1. - s * (1. - f)) + 0.5); - switch (i) - { - case 0: - *prval = vval; - *pgval = z; - *pbval = x; - break; - case 1: - *prval = y; - *pgval = vval; - *pbval = x; - break; - case 2: - *prval = x; - *pgval = vval; - *pbval = z; - break; - case 3: - *prval = x; - *pgval = y; - *pbval = vval; - break; - case 4: - *prval = z; - *pgval = x; - *pbval = vval; - break; - case 5: - *prval = vval; - *pgval = x; - *pbval = y; - break; - default: /* none possible */ - return 1; - } - } - - return 0; -} - - -/*! - * \brief pixcmapConvertRGBToHSV() - * - * \param[in] cmap - * \return 0 if OK; 1 on error - * - *
- * Notes: - * ~ in-place transform - * ~ See convertRGBToHSV() for def'n of HSV space. - * ~ replaces: r --> h, g --> s, b --> v - *- */ -l_ok -pixcmapConvertRGBToHSV(PIXCMAP *cmap) -{ -l_int32 i, ncolors, rval, gval, bval, hval, sval, vval; - - PROCNAME("pixcmapConvertRGBToHSV"); - - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - ncolors = pixcmapGetCount(cmap); - for (i = 0; i < ncolors; i++) { - pixcmapGetColor(cmap, i, &rval, &gval, &bval); - convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval); - pixcmapResetColor(cmap, i, hval, sval, vval); - } - return 0; -} - - -/*! - * \brief pixcmapConvertHSVToRGB() - * - * \param[in] cmap - * \return 0 if OK; 1 on error - * - *
- * Notes: - * ~ in-place transform - * ~ See convertRGBToHSV() for def'n of HSV space. - * ~ replaces: h --> r, s --> g, v --> b - *- */ -l_ok -pixcmapConvertHSVToRGB(PIXCMAP *cmap) -{ -l_int32 i, ncolors, rval, gval, bval, hval, sval, vval; - - PROCNAME("pixcmapConvertHSVToRGB"); - - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - ncolors = pixcmapGetCount(cmap); - for (i = 0; i < ncolors; i++) { - pixcmapGetColor(cmap, i, &hval, &sval, &vval); - convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval); - pixcmapResetColor(cmap, i, rval, gval, bval); - } - return 0; -} - - -/*! - * \brief pixConvertRGBToHue() - * - * \param[in] pixs 32 bpp RGB, or 8 bpp with colormap - * \return pixd 8 bpp hue of HSV, or NULL on error - * - *
- * Notes: - * (1) The conversion to HSV hue is in-lined here. - * (2) If there is a colormap, it is removed. - * (3) If you just want the hue component, this does it - * at about 10 Mpixels/sec/GHz, which is about - * 2x faster than using pixConvertRGBToHSV() - *- */ -PIX * -pixConvertRGBToHue(PIX *pixs) -{ -l_int32 w, h, d, wplt, wpld; -l_int32 i, j, rval, gval, bval, hval, minrg, min, maxrg, max, delta; -l_float32 fh; -l_uint32 pixel; -l_uint32 *linet, *lined, *datat, *datad; -PIX *pixt, *pixd; - - PROCNAME("pixConvertRGBToHue"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - pixGetDimensions(pixs, &w, &h, &d); - if (d != 32 && !pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("not cmapped or rgb", procName, NULL); - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); - - /* Convert RGB image */ - pixd = pixCreate(w, h, 8); - pixCopyResolution(pixd, pixs); - wplt = pixGetWpl(pixt); - datat = pixGetData(pixt); - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - for (i = 0; i < h; i++) { - linet = datat + i * wplt; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - pixel = linet[j]; - extractRGBValues(pixel, &rval, &gval, &bval); - minrg = L_MIN(rval, gval); - min = L_MIN(minrg, bval); - maxrg = L_MAX(rval, gval); - max = L_MAX(maxrg, bval); - delta = max - min; - if (delta == 0) { /* gray; no chroma */ - hval = 0; - } else { - if (rval == max) /* between magenta and yellow */ - fh = (l_float32)(gval - bval) / (l_float32)delta; - else if (gval == max) /* between yellow and cyan */ - fh = 2. + (l_float32)(bval - rval) / (l_float32)delta; - else /* between cyan and magenta */ - fh = 4. + (l_float32)(rval - gval) / (l_float32)delta; - fh *= 40.0; - if (fh < 0.0) - fh += 240.0; - hval = (l_int32)(fh + 0.5); - } - SET_DATA_BYTE(lined, j, hval); - } - } - pixDestroy(&pixt); - - return pixd; -} - - - -/*! - * \brief pixConvertRGBToSaturation() - * - * \param[in] pixs 32 bpp RGB, or 8 bpp with colormap - * \return pixd 8 bpp sat of HSV, or NULL on error - * - *
- * Notes: - * (1) The conversion to HSV sat is in-lined here. - * (2) If there is a colormap, it is removed. - * (3) If you just want the saturation component, this does it - * at about 12 Mpixels/sec/GHz. - *- */ -PIX * -pixConvertRGBToSaturation(PIX *pixs) -{ -l_int32 w, h, d, wplt, wpld; -l_int32 i, j, rval, gval, bval, sval, minrg, min, maxrg, max, delta; -l_uint32 pixel; -l_uint32 *linet, *lined, *datat, *datad; -PIX *pixt, *pixd; - - PROCNAME("pixConvertRGBToSaturation"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - pixGetDimensions(pixs, &w, &h, &d); - if (d != 32 && !pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("not cmapped or rgb", procName, NULL); - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); - - /* Convert RGB image */ - pixd = pixCreate(w, h, 8); - pixCopyResolution(pixd, pixs); - wplt = pixGetWpl(pixt); - datat = pixGetData(pixt); - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - for (i = 0; i < h; i++) { - linet = datat + i * wplt; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - pixel = linet[j]; - extractRGBValues(pixel, &rval, &gval, &bval); - minrg = L_MIN(rval, gval); - min = L_MIN(minrg, bval); - maxrg = L_MAX(rval, gval); - max = L_MAX(maxrg, bval); - delta = max - min; - if (delta == 0) /* gray; no chroma */ - sval = 0; - else - sval = (l_int32)(255. * - (l_float32)delta / (l_float32)max + 0.5); - SET_DATA_BYTE(lined, j, sval); - } - } - - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixConvertRGBToValue() - * - * \param[in] pixs 32 bpp RGB,or 8 bpp with colormap - * \return pixd 8 bpp max component intensity of HSV, or NULL on error - * - *
- * Notes: - * (1) The conversion to HSV sat is in-lined here. - * (2) If there is a colormap, it is removed. - * (3) If you just want the value component, this does it - * at about 35 Mpixels/sec/GHz. - *- */ -PIX * -pixConvertRGBToValue(PIX *pixs) -{ -l_int32 w, h, d, wplt, wpld; -l_int32 i, j, rval, gval, bval, maxrg, max; -l_uint32 pixel; -l_uint32 *linet, *lined, *datat, *datad; -PIX *pixt, *pixd; - - PROCNAME("pixConvertRGBToValue"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - pixGetDimensions(pixs, &w, &h, &d); - if (d != 32 && !pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("not cmapped or rgb", procName, NULL); - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); - - /* Convert RGB image */ - pixd = pixCreate(w, h, 8); - pixCopyResolution(pixd, pixs); - wplt = pixGetWpl(pixt); - datat = pixGetData(pixt); - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - for (i = 0; i < h; i++) { - linet = datat + i * wplt; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - pixel = linet[j]; - extractRGBValues(pixel, &rval, &gval, &bval); - maxrg = L_MAX(rval, gval); - max = L_MAX(maxrg, bval); - SET_DATA_BYTE(lined, j, max); - } - } - - pixDestroy(&pixt); - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Selection and display of range of colors in HSV space * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixMakeRangeMaskHS() - * - * \param[in] pixs 32 bpp rgb - * \param[in] huecenter center value of hue range - * \param[in] huehw half-width of hue range - * \param[in] satcenter center value of saturation range - * \param[in] sathw half-width of saturation range - * \param[in] regionflag L_INCLUDE_REGION, L_EXCLUDE_REGION - * \return pixd 1 bpp mask over selected pixels, or NULL on error - * - *
- * Notes: - * (1) The pixels are selected based on the specified ranges of - * hue and saturation. For selection or exclusion, the pixel - * HS component values must be within both ranges. Care must - * be taken in finding the hue range because of wrap-around. - * (2) Use %regionflag == L_INCLUDE_REGION to take only those - * pixels within the rectangular region specified in HS space. - * Use %regionflag == L_EXCLUDE_REGION to take all pixels except - * those within the rectangular region specified in HS space. - *- */ -PIX * -pixMakeRangeMaskHS(PIX *pixs, - l_int32 huecenter, - l_int32 huehw, - l_int32 satcenter, - l_int32 sathw, - l_int32 regionflag) -{ -l_int32 i, j, w, h, wplt, wpld, hstart, hend, sstart, send, hval, sval; -l_int32 *hlut, *slut; -l_uint32 pixel; -l_uint32 *datat, *datad, *linet, *lined; -PIX *pixt, *pixd; - - PROCNAME("pixMakeRangeMaskHS"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); - if (regionflag != L_INCLUDE_REGION && regionflag != L_EXCLUDE_REGION) - return (PIX *)ERROR_PTR("invalid regionflag", procName, NULL); - - /* Set up LUTs for hue and saturation. These have the value 1 - * within the specified intervals of hue and saturation. */ - hlut = (l_int32 *)LEPT_CALLOC(240, sizeof(l_int32)); - slut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - sstart = L_MAX(0, satcenter - sathw); - send = L_MIN(255, satcenter + sathw); - for (i = sstart; i <= send; i++) - slut[i] = 1; - hstart = (huecenter - huehw + 240) % 240; - hend = (huecenter + huehw + 240) % 240; - if (hstart < hend) { - for (i = hstart; i <= hend; i++) - hlut[i] = 1; - } else { /* wrap */ - for (i = hstart; i < 240; i++) - hlut[i] = 1; - for (i = 0; i <= hend; i++) - hlut[i] = 1; - } - - /* Generate the mask */ - pixt = pixConvertRGBToHSV(NULL, pixs); - pixGetDimensions(pixs, &w, &h, NULL); - pixd = pixCreateNoInit(w, h, 1); - if (regionflag == L_INCLUDE_REGION) - pixClearAll(pixd); - else /* L_EXCLUDE_REGION */ - pixSetAll(pixd); - datat = pixGetData(pixt); - datad = pixGetData(pixd); - wplt = pixGetWpl(pixt); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - linet = datat + i * wplt; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - pixel = linet[j]; - hval = (pixel >> L_RED_SHIFT) & 0xff; - sval = (pixel >> L_GREEN_SHIFT) & 0xff; - if (hlut[hval] == 1 && slut[sval] == 1) { - if (regionflag == L_INCLUDE_REGION) - SET_DATA_BIT(lined, j); - else /* L_EXCLUDE_REGION */ - CLEAR_DATA_BIT(lined, j); - } - } - } - - LEPT_FREE(hlut); - LEPT_FREE(slut); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixMakeRangeMaskHV() - * - * \param[in] pixs 32 bpp rgb - * \param[in] huecenter center value of hue range - * \param[in] huehw half-width of hue range - * \param[in] valcenter center value of max intensity range - * \param[in] valhw half-width of max intensity range - * \param[in] regionflag L_INCLUDE_REGION, L_EXCLUDE_REGION - * \return pixd 1 bpp mask over selected pixels, or NULL on error - * - *
- * Notes: - * (1) The pixels are selected based on the specified ranges of - * hue and max intensity values. For selection or exclusion, - * the pixel HV component values must be within both ranges. - * Care must be taken in finding the hue range because of wrap-around. - * (2) Use %regionflag == L_INCLUDE_REGION to take only those - * pixels within the rectangular region specified in HV space. - * Use %regionflag == L_EXCLUDE_REGION to take all pixels except - * those within the rectangular region specified in HV space. - *- */ -PIX * -pixMakeRangeMaskHV(PIX *pixs, - l_int32 huecenter, - l_int32 huehw, - l_int32 valcenter, - l_int32 valhw, - l_int32 regionflag) -{ -l_int32 i, j, w, h, wplt, wpld, hstart, hend, vstart, vend, hval, vval; -l_int32 *hlut, *vlut; -l_uint32 pixel; -l_uint32 *datat, *datad, *linet, *lined; -PIX *pixt, *pixd; - - PROCNAME("pixMakeRangeMaskHV"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); - if (regionflag != L_INCLUDE_REGION && regionflag != L_EXCLUDE_REGION) - return (PIX *)ERROR_PTR("invalid regionflag", procName, NULL); - - /* Set up LUTs for hue and maximum intensity (val). These have - * the value 1 within the specified intervals of hue and value. */ - hlut = (l_int32 *)LEPT_CALLOC(240, sizeof(l_int32)); - vlut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - vstart = L_MAX(0, valcenter - valhw); - vend = L_MIN(255, valcenter + valhw); - for (i = vstart; i <= vend; i++) - vlut[i] = 1; - hstart = (huecenter - huehw + 240) % 240; - hend = (huecenter + huehw + 240) % 240; - if (hstart < hend) { - for (i = hstart; i <= hend; i++) - hlut[i] = 1; - } else { - for (i = hstart; i < 240; i++) - hlut[i] = 1; - for (i = 0; i <= hend; i++) - hlut[i] = 1; - } - - /* Generate the mask */ - pixt = pixConvertRGBToHSV(NULL, pixs); - pixGetDimensions(pixs, &w, &h, NULL); - pixd = pixCreateNoInit(w, h, 1); - if (regionflag == L_INCLUDE_REGION) - pixClearAll(pixd); - else /* L_EXCLUDE_REGION */ - pixSetAll(pixd); - datat = pixGetData(pixt); - datad = pixGetData(pixd); - wplt = pixGetWpl(pixt); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - linet = datat + i * wplt; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - pixel = linet[j]; - hval = (pixel >> L_RED_SHIFT) & 0xff; - vval = (pixel >> L_BLUE_SHIFT) & 0xff; - if (hlut[hval] == 1 && vlut[vval] == 1) { - if (regionflag == L_INCLUDE_REGION) - SET_DATA_BIT(lined, j); - else /* L_EXCLUDE_REGION */ - CLEAR_DATA_BIT(lined, j); - } - } - } - - LEPT_FREE(hlut); - LEPT_FREE(vlut); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixMakeRangeMaskSV() - * - * \param[in] pixs 32 bpp rgb - * \param[in] satcenter center value of saturation range - * \param[in] sathw half-width of saturation range - * \param[in] valcenter center value of max intensity range - * \param[in] valhw half-width of max intensity range - * \param[in] regionflag L_INCLUDE_REGION, L_EXCLUDE_REGION - * \return pixd 1 bpp mask over selected pixels, or NULL on error - * - *
- * Notes: - * (1) The pixels are selected based on the specified ranges of - * saturation and max intensity (val). For selection or - * exclusion, the pixel SV component values must be within both ranges. - * (2) Use %regionflag == L_INCLUDE_REGION to take only those - * pixels within the rectangular region specified in SV space. - * Use %regionflag == L_EXCLUDE_REGION to take all pixels except - * those within the rectangular region specified in SV space. - *- */ -PIX * -pixMakeRangeMaskSV(PIX *pixs, - l_int32 satcenter, - l_int32 sathw, - l_int32 valcenter, - l_int32 valhw, - l_int32 regionflag) -{ -l_int32 i, j, w, h, wplt, wpld, sval, vval, sstart, send, vstart, vend; -l_int32 *slut, *vlut; -l_uint32 pixel; -l_uint32 *datat, *datad, *linet, *lined; -PIX *pixt, *pixd; - - PROCNAME("pixMakeRangeMaskSV"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); - if (regionflag != L_INCLUDE_REGION && regionflag != L_EXCLUDE_REGION) - return (PIX *)ERROR_PTR("invalid regionflag", procName, NULL); - - /* Set up LUTs for saturation and max intensity (val). - * These have the value 1 within the specified intervals of - * saturation and max intensity. */ - slut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - vlut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - sstart = L_MAX(0, satcenter - sathw); - send = L_MIN(255, satcenter + sathw); - vstart = L_MAX(0, valcenter - valhw); - vend = L_MIN(255, valcenter + valhw); - for (i = sstart; i <= send; i++) - slut[i] = 1; - for (i = vstart; i <= vend; i++) - vlut[i] = 1; - - /* Generate the mask */ - pixt = pixConvertRGBToHSV(NULL, pixs); - pixGetDimensions(pixs, &w, &h, NULL); - pixd = pixCreateNoInit(w, h, 1); - if (regionflag == L_INCLUDE_REGION) - pixClearAll(pixd); - else /* L_EXCLUDE_REGION */ - pixSetAll(pixd); - datat = pixGetData(pixt); - datad = pixGetData(pixd); - wplt = pixGetWpl(pixt); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - linet = datat + i * wplt; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - pixel = linet[j]; - sval = (pixel >> L_GREEN_SHIFT) & 0xff; - vval = (pixel >> L_BLUE_SHIFT) & 0xff; - if (slut[sval] == 1 && vlut[vval] == 1) { - if (regionflag == L_INCLUDE_REGION) - SET_DATA_BIT(lined, j); - else /* L_EXCLUDE_REGION */ - CLEAR_DATA_BIT(lined, j); - } - } - } - - LEPT_FREE(slut); - LEPT_FREE(vlut); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixMakeHistoHS() - * - * \param[in] pixs HSV colorspace - * \param[in] factor subsampling factor; integer - * \param[out] pnahue [optional] hue histogram - * \param[out] pnasat [optional] saturation histogram - * \return pixd 32 bpp histogram in hue and saturation, or NULL on error - * - *
- * Notes: - * (1) pixs is a 32 bpp image in HSV colorspace; hue is in the "red" - * byte, saturation is in the "green" byte. - * (2) In pixd, hue is displayed vertically; saturation horizontally. - * The dimensions of pixd are w = 256, h = 240, and the depth - * is 32 bpp. The value at each point is simply the number - * of pixels found at that value of hue and saturation. - *- */ -PIX * -pixMakeHistoHS(PIX *pixs, - l_int32 factor, - NUMA **pnahue, - NUMA **pnasat) -{ -l_int32 i, j, w, h, wplt, hval, sval, nd; -l_uint32 pixel; -l_uint32 *datat, *linet; -void **lined32; -NUMA *nahue, *nasat; -PIX *pixt, *pixd; - - PROCNAME("pixMakeHistoHS"); - - if (pnahue) *pnahue = NULL; - if (pnasat) *pnasat = NULL; - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); - - if (pnahue) { - nahue = numaCreate(240); - numaSetCount(nahue, 240); - *pnahue = nahue; - } - if (pnasat) { - nasat = numaCreate(256); - numaSetCount(nasat, 256); - *pnasat = nasat; - } - - if (factor <= 1) - pixt = pixClone(pixs); - else - pixt = pixScaleBySampling(pixs, 1.0 / (l_float32)factor, - 1.0 / (l_float32)factor); - - /* Create the hue-saturation histogram */ - pixd = pixCreate(256, 240, 32); - lined32 = pixGetLinePtrs(pixd, NULL); - pixGetDimensions(pixt, &w, &h, NULL); - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - for (i = 0; i < h; i++) { - linet = datat + i * wplt; - for (j = 0; j < w; j++) { - pixel = linet[j]; - hval = (pixel >> L_RED_SHIFT) & 0xff; - -#if DEBUG_HISTO - if (hval > 239) { - lept_stderr("hval = %d for (%d,%d)\n", hval, i, j); - continue; - } -#endif /* DEBUG_HISTO */ - - sval = (pixel >> L_GREEN_SHIFT) & 0xff; - if (pnahue) - numaShiftValue(nahue, hval, 1.0); - if (pnasat) - numaShiftValue(nasat, sval, 1.0); - nd = GET_DATA_FOUR_BYTES(lined32[hval], sval); - SET_DATA_FOUR_BYTES(lined32[hval], sval, nd + 1); - } - } - - LEPT_FREE(lined32); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixMakeHistoHV() - * - * \param[in] pixs HSV colorspace - * \param[in] factor subsampling factor; integer - * \param[out] pnahue [optional] hue histogram - * \param[out] pnaval [optional] max intensity (value) histogram - * \return pixd 32 bpp histogram in hue and value, or NULL on error - * - *
- * Notes: - * (1) %pixs is a 32 bpp image in HSV colorspace; hue is in the "red" - * byte, max intensity ("value") is in the "blue" byte. - * (2) In %pixd, hue is displayed vertically; intensity horizontally. - * The dimensions of %pixd are w = 256, h = 240, and the depth - * is 32 bpp. The value at each point is simply the number - * of pixels found at that value of hue and intensity. - *- */ -PIX * -pixMakeHistoHV(PIX *pixs, - l_int32 factor, - NUMA **pnahue, - NUMA **pnaval) -{ -l_int32 i, j, w, h, wplt, hval, vval, nd; -l_uint32 pixel; -l_uint32 *datat, *linet; -void **lined32; -NUMA *nahue, *naval; -PIX *pixt, *pixd; - - PROCNAME("pixMakeHistoHV"); - - if (pnahue) *pnahue = NULL; - if (pnaval) *pnaval = NULL; - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); - - if (pnahue) { - nahue = numaCreate(240); - numaSetCount(nahue, 240); - *pnahue = nahue; - } - if (pnaval) { - naval = numaCreate(256); - numaSetCount(naval, 256); - *pnaval = naval; - } - - if (factor <= 1) - pixt = pixClone(pixs); - else - pixt = pixScaleBySampling(pixs, 1.0 / (l_float32)factor, - 1.0 / (l_float32)factor); - - /* Create the hue-value histogram */ - pixd = pixCreate(256, 240, 32); - lined32 = pixGetLinePtrs(pixd, NULL); - pixGetDimensions(pixt, &w, &h, NULL); - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - for (i = 0; i < h; i++) { - linet = datat + i * wplt; - for (j = 0; j < w; j++) { - pixel = linet[j]; - hval = (pixel >> L_RED_SHIFT) & 0xff; - vval = (pixel >> L_BLUE_SHIFT) & 0xff; - if (pnahue) - numaShiftValue(nahue, hval, 1.0); - if (pnaval) - numaShiftValue(naval, vval, 1.0); - nd = GET_DATA_FOUR_BYTES(lined32[hval], vval); - SET_DATA_FOUR_BYTES(lined32[hval], vval, nd + 1); - } - } - - LEPT_FREE(lined32); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixMakeHistoSV() - * - * \param[in] pixs HSV colorspace - * \param[in] factor subsampling factor; integer - * \param[out] pnasat [optional] sat histogram - * \param[out] pnaval [optional] max intensity (value) histogram - * \return pixd 32 bpp histogram in sat and value, or NULL on error - * - *
- * Notes: - * (1) %pixs is a 32 bpp image in HSV colorspace; sat is in the "green" - * byte, max intensity ("value") is in the "blue" byte. - * (2) In %pixd, sat is displayed vertically; intensity horizontally. - * The dimensions of %pixd are w = 256, h = 256, and the depth - * is 32 bpp. The value at each point is simply the number - * of pixels found at that value of saturation and intensity. - *- */ -PIX * -pixMakeHistoSV(PIX *pixs, - l_int32 factor, - NUMA **pnasat, - NUMA **pnaval) -{ -l_int32 i, j, w, h, wplt, sval, vval, nd; -l_uint32 pixel; -l_uint32 *datat, *linet; -void **lined32; -NUMA *nasat, *naval; -PIX *pixt, *pixd; - - PROCNAME("pixMakeHistoSV"); - - if (pnasat) *pnasat = NULL; - if (pnaval) *pnaval = NULL; - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); - - if (pnasat) { - nasat = numaCreate(256); - numaSetCount(nasat, 256); - *pnasat = nasat; - } - if (pnaval) { - naval = numaCreate(256); - numaSetCount(naval, 256); - *pnaval = naval; - } - - if (factor <= 1) - pixt = pixClone(pixs); - else - pixt = pixScaleBySampling(pixs, 1.0 / (l_float32)factor, - 1.0 / (l_float32)factor); - - /* Create the hue-value histogram */ - pixd = pixCreate(256, 256, 32); - lined32 = pixGetLinePtrs(pixd, NULL); - pixGetDimensions(pixt, &w, &h, NULL); - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - for (i = 0; i < h; i++) { - linet = datat + i * wplt; - for (j = 0; j < w; j++) { - pixel = linet[j]; - sval = (pixel >> L_GREEN_SHIFT) & 0xff; - vval = (pixel >> L_BLUE_SHIFT) & 0xff; - if (pnasat) - numaShiftValue(nasat, sval, 1.0); - if (pnaval) - numaShiftValue(naval, vval, 1.0); - nd = GET_DATA_FOUR_BYTES(lined32[sval], vval); - SET_DATA_FOUR_BYTES(lined32[sval], vval, nd + 1); - } - } - - LEPT_FREE(lined32); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixFindHistoPeaksHSV() - * - * \param[in] pixs 32 bpp; HS, HV or SV histogram; not changed - * \param[in] type L_HS_HISTO, L_HV_HISTO or L_SV_HISTO - * \param[in] width half width of sliding window - * \param[in] height half height of sliding window - * \param[in] npeaks number of peaks to look for - * \param[in] erasefactor ratio of erase window size to sliding window size - * \param[out] ppta locations of max for each integrated peak area - * \param[out] pnatot integrated peak areas - * \param[out] ppixa [optional] pixa for debugging; NULL to skip - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) %pixs is a 32 bpp histogram in a pair of HSV colorspace. It - * should be thought of as a single sample with 32 bps (bits/sample). - * (2) After each peak is found, the peak is erased with a window - * that is centered on the peak and scaled from the sliding - * window by %erasefactor. Typically, %erasefactor is chosen - * to be > 1.0. - * (3) Data for a maximum of %npeaks is returned in %pta and %natot. - * (4) For debugging, after the pixa is returned, display with: - * pixd = pixaDisplayTiledInRows(pixa, 32, 1000, 1.0, 0, 30, 2); - *- */ -l_ok -pixFindHistoPeaksHSV(PIX *pixs, - l_int32 type, - l_int32 width, - l_int32 height, - l_int32 npeaks, - l_float32 erasefactor, - PTA **ppta, - NUMA **pnatot, - PIXA **ppixa) -{ -l_int32 i, xmax, ymax, ewidth, eheight; -l_uint32 maxval; -BOX *box; -NUMA *natot; -PIX *pixh, *pixw, *pix1, *pix2, *pix3; -PTA *pta; - - PROCNAME("pixFindHistoPeaksHSV"); - - if (ppixa) *ppixa = NULL; - if (ppta) *ppta = NULL; - if (pnatot) *pnatot = NULL; - if (!pixs || pixGetDepth(pixs) != 32) - return ERROR_INT("pixs undefined or not 32 bpp", procName, 1); - if (!ppta || !pnatot) - return ERROR_INT("&pta and &natot not both defined", procName, 1); - if (type != L_HS_HISTO && type != L_HV_HISTO && type != L_SV_HISTO) - return ERROR_INT("invalid HSV histo type", procName, 1); - - if ((pta = ptaCreate(npeaks)) == NULL) - return ERROR_INT("pta not made", procName, 1); - *ppta = pta; - if ((natot = numaCreate(npeaks)) == NULL) - return ERROR_INT("natot not made", procName, 1); - *pnatot = natot; - - *ppta = pta; - if (type == L_SV_HISTO) - pixh = pixAddMirroredBorder(pixs, width + 1, width + 1, height + 1, - height + 1); - else /* type == L_HS_HISTO or type == L_HV_HISTO */ - pixh = pixAddMixedBorder(pixs, width + 1, width + 1, height + 1, - height + 1); - - /* Get the total count in the sliding window. If the window - * fully covers the peak, this will be the integrated - * volume under the peak. */ - pixw = pixWindowedMean(pixh, width, height, 1, 0); - pixDestroy(&pixh); - - /* Sequentially identify and erase peaks in the histogram. - * If requested for debugging, save a pixa of the sequence of - * false color histograms. */ - if (ppixa) - *ppixa = pixaCreate(0); - for (i = 0; i < npeaks; i++) { - pixGetMaxValueInRect(pixw, NULL, &maxval, &xmax, &ymax); - if (maxval == 0) break; - numaAddNumber(natot, maxval); - ptaAddPt(pta, xmax, ymax); - ewidth = (l_int32)(width * erasefactor); - eheight = (l_int32)(height * erasefactor); - box = boxCreate(xmax - ewidth, ymax - eheight, 2 * ewidth + 1, - 2 * eheight + 1); - - if (ppixa) { - pix1 = pixMaxDynamicRange(pixw, L_LINEAR_SCALE); - pixaAddPix(*ppixa, pix1, L_INSERT); - pix2 = pixConvertGrayToFalseColor(pix1, 1.0); - pixaAddPix(*ppixa, pix2, L_INSERT); - pix1 = pixMaxDynamicRange(pixw, L_LOG_SCALE); - pix2 = pixConvertGrayToFalseColor(pix1, 1.0); - pixaAddPix(*ppixa, pix2, L_INSERT); - pix3 = pixConvertTo32(pix1); - pixRenderHashBoxArb(pix3, box, 6, 2, L_NEG_SLOPE_LINE, - 1, 255, 100, 100); - pixaAddPix(*ppixa, pix3, L_INSERT); - pixDestroy(&pix1); - } - - pixClearInRect(pixw, box); - boxDestroy(&box); - if (type == L_HS_HISTO || type == L_HV_HISTO) { - /* clear wraps at bottom and top */ - if (ymax - eheight < 0) { /* overlap to bottom */ - box = boxCreate(xmax - ewidth, 240 + ymax - eheight, - 2 * ewidth + 1, eheight - ymax); - } else if (ymax + eheight > 239) { /* overlap to top */ - box = boxCreate(xmax - ewidth, 0, 2 * ewidth + 1, - ymax + eheight - 239); - } else { - box = NULL; - } - if (box) { - pixClearInRect(pixw, box); - boxDestroy(&box); - } - } - } - - pixDestroy(&pixw); - return 0; -} - - -/*! - * \brief displayHSVColorRange() - * - * \param[in] hval hue center value; in range [0 ... 240] - * \param[in] sval saturation center value; in range [0 ... 255] - * \param[in] vval max intensity value; in range [0 ... 255] - * \param[in] huehw half-width of hue range; > 0 - * \param[in] sathw half-width of saturation range; > 0 - * \param[in] nsamp number of samplings in each half-width in hue and sat - * \param[in] factor linear size of each color square, in pixels; > 3 - * \return pixd 32 bpp set of color squares over input range; NULL on error - * - *
- * Notes: - * (1) The total number of color samplings in each of the hue - * and saturation directions is 2 * nsamp + 1. - *- */ -PIX * -displayHSVColorRange(l_int32 hval, - l_int32 sval, - l_int32 vval, - l_int32 huehw, - l_int32 sathw, - l_int32 nsamp, - l_int32 factor) -{ -l_int32 i, j, w, huedelta, satdelta, hue, sat, rval, gval, bval; -PIX *pixt, *pixd; - - PROCNAME("displayHSVColorRange"); - - if (hval < 0 || hval > 240) - return (PIX *)ERROR_PTR("invalid hval", procName, NULL); - if (huehw < 5 || huehw > 120) - return (PIX *)ERROR_PTR("invalid huehw", procName, NULL); - if (sval - sathw < 0 || sval + sathw > 255) - return (PIX *)ERROR_PTR("invalid sval/sathw", procName, NULL); - if (nsamp < 1 || factor < 3) - return (PIX *)ERROR_PTR("invalid nsamp or rep. factor", procName, NULL); - if (vval < 0 || vval > 255) - return (PIX *)ERROR_PTR("invalid vval", procName, NULL); - - w = (2 * nsamp + 1); - huedelta = (l_int32)((l_float32)huehw / (l_float32)nsamp); - satdelta = (l_int32)((l_float32)sathw / (l_float32)nsamp); - pixt = pixCreate(w, w, 32); - for (i = 0; i < w; i++) { - hue = hval + huedelta * (i - nsamp); - if (hue < 0) hue += 240; - if (hue >= 240) hue -= 240; - for (j = 0; j < w; j++) { - sat = sval + satdelta * (j - nsamp); - convertHSVToRGB(hue, sat, vval, &rval, &gval, &bval); - pixSetRGBPixel(pixt, j, i, rval, gval, bval); - } - } - - pixd = pixExpandReplicate(pixt, factor); - pixDestroy(&pixt); - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Colorspace conversion between RGB and YUV * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvertRGBToYUV() - * - * \param[in] pixd can be NULL; if not NULL, must == pixs - * \param[in] pixs - * \return pixd always - * - *
- * Notes: - * (1) For pixs = pixd, this is in-place; otherwise pixd must be NULL. - * (2) The Y, U and V values are stored in the same places as - * the r, g and b values, respectively. Here, they are explicitly - * placed in the 3 MS bytes in the pixel. - * (3) Normalizing to 1 and considering the r,g,b components, - * a simple way to understand the YUV space is: - * ~ Y = weighted sum of (r,g,b) - * ~ U = weighted difference between Y and B - * ~ V = weighted difference between Y and R - * (4) Following video conventions, Y, U and V are in the range: - * Y: [16, 235] - * U: [16, 240] - * V: [16, 240] - * (5) For the coefficients in the transform matrices, see eq. 4 in - * "Frequently Asked Questions about Color" by Charles Poynton, - * //http://user.engineering.uiowa.edu/~aip/Misc/ColorFAQ.html - *- */ -PIX * -pixConvertRGBToYUV(PIX *pixd, - PIX *pixs) -{ -l_int32 w, h, d, wpl, i, j, rval, gval, bval, yval, uval, vval; -l_uint32 *line, *data; -PIXCMAP *cmap; - - PROCNAME("pixConvertRGBToYUV"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixd && pixd != pixs) - return (PIX *)ERROR_PTR("pixd defined and not inplace", procName, pixd); - - d = pixGetDepth(pixs); - cmap = pixGetColormap(pixs); - if (!cmap && d != 32) - return (PIX *)ERROR_PTR("not cmapped or rgb", procName, pixd); - - if (!pixd) - pixd = pixCopy(NULL, pixs); - - cmap = pixGetColormap(pixd); - if (cmap) { /* just convert the colormap */ - pixcmapConvertRGBToYUV(cmap); - return pixd; - } - - /* Convert RGB image */ - pixGetDimensions(pixd, &w, &h, NULL); - wpl = pixGetWpl(pixd); - data = pixGetData(pixd); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - extractRGBValues(line[j], &rval, &gval, &bval); - convertRGBToYUV(rval, gval, bval, &yval, &uval, &vval); - line[j] = (yval << 24) | (uval << 16) | (vval << 8); - } - } - - return pixd; -} - - -/*! - * \brief pixConvertYUVToRGB() - * - * \param[in] pixd can be NULL; if not NULL, must == pixs - * \param[in] pixs - * \return pixd always - * - *
- * Notes: - * (1) For pixs = pixd, this is in-place; otherwise pixd must be NULL. - * (2) The user takes responsibility for making sure that pixs is - * in YUV space. - * (3) The Y, U and V values are stored in the same places as - * the r, g and b values, respectively. Here, they are explicitly - * placed in the 3 MS bytes in the pixel. - *- */ -PIX * -pixConvertYUVToRGB(PIX *pixd, - PIX *pixs) -{ -l_int32 w, h, d, wpl, i, j, rval, gval, bval, yval, uval, vval; -l_uint32 pixel; -l_uint32 *line, *data; -PIXCMAP *cmap; - - PROCNAME("pixConvertYUVToRGB"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixd && pixd != pixs) - return (PIX *)ERROR_PTR("pixd defined and not inplace", procName, pixd); - - d = pixGetDepth(pixs); - cmap = pixGetColormap(pixs); - if (!cmap && d != 32) - return (PIX *)ERROR_PTR("not cmapped or hsv", procName, pixd); - - if (!pixd) - pixd = pixCopy(NULL, pixs); - - cmap = pixGetColormap(pixd); - if (cmap) { /* just convert the colormap */ - pixcmapConvertYUVToRGB(cmap); - return pixd; - } - - /* Convert YUV image */ - pixGetDimensions(pixd, &w, &h, NULL); - wpl = pixGetWpl(pixd); - data = pixGetData(pixd); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - pixel = line[j]; - yval = pixel >> 24; - uval = (pixel >> 16) & 0xff; - vval = (pixel >> 8) & 0xff; - convertYUVToRGB(yval, uval, vval, &rval, &gval, &bval); - composeRGBPixel(rval, gval, bval, line + j); - } - } - - return pixd; -} - - -/*! - * \brief convertRGBToYUV() - * - * \param[in] rval, gval, bval RGB input - * \param[out] pyval, puval, pvval equivalent YUV values - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The range of returned values is: - * Y [16 ... 235] - * U [16 ... 240] - * V [16 ... 240] - *- */ -l_ok -convertRGBToYUV(l_int32 rval, - l_int32 gval, - l_int32 bval, - l_int32 *pyval, - l_int32 *puval, - l_int32 *pvval) -{ -l_float32 norm; - - PROCNAME("convertRGBToYUV"); - - if (pyval) *pyval = 0; - if (puval) *puval = 0; - if (pvval) *pvval = 0; - if (!pyval || !puval || !pvval) - return ERROR_INT("&yval, &uval, &vval not all defined", procName, 1); - - norm = 1.0 / 256.; - *pyval = (l_int32)(16.0 + - norm * (65.738 * rval + 129.057 * gval + 25.064 * bval) + 0.5); - *puval = (l_int32)(128.0 + - norm * (-37.945 * rval -74.494 * gval + 112.439 * bval) + 0.5); - *pvval = (l_int32)(128.0 + - norm * (112.439 * rval - 94.154 * gval - 18.285 * bval) + 0.5); - return 0; -} - - -/*! - * \brief convertYUVToRGB() - * - * \param[in] yval, uval, vval YUV input - * \param[out] prval, pgval, pbval equivalent RGB values - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The range of valid input values is: - * Y [16 ... 235] - * U [16 ... 240] - * V [16 ... 240] - * (2) Conversion of RGB --> YUV --> RGB leaves the image unchanged. - * (3) The YUV gamut is larger than the RBG gamut; many YUV values - * will result in an invalid RGB value. We clip individual - * r,g,b components to the range [0, 255], and do not test input. - *- */ -l_ok -convertYUVToRGB(l_int32 yval, - l_int32 uval, - l_int32 vval, - l_int32 *prval, - l_int32 *pgval, - l_int32 *pbval) -{ -l_int32 rval, gval, bval; -l_float32 norm, ym, um, vm; - - PROCNAME("convertYUVToRGB"); - - if (prval) *prval = 0; - if (pgval) *pgval = 0; - if (pbval) *pbval = 0; - if (!prval || !pgval || !pbval) - return ERROR_INT("&rval, &gval, &bval not all defined", procName, 1); - - norm = 1.0 / 256.; - ym = yval - 16.0; - um = uval - 128.0; - vm = vval - 128.0; - rval = (l_int32)(norm * (298.082 * ym + 408.583 * vm) + 0.5); - gval = (l_int32)(norm * (298.082 * ym - 100.291 * um - 208.120 * vm) + - 0.5); - bval = (l_int32)(norm * (298.082 * ym + 516.411 * um) + 0.5); - *prval = L_MIN(255, L_MAX(0, rval)); - *pgval = L_MIN(255, L_MAX(0, gval)); - *pbval = L_MIN(255, L_MAX(0, bval)); - - return 0; -} - - -/*! - * \brief pixcmapConvertRGBToYUV() - * - * \param[in] cmap - * \return 0 if OK; 1 on error - * - *
- * Notes: - * ~ in-place transform - * ~ See convertRGBToYUV() for def'n of YUV space. - * ~ replaces: r --> y, g --> u, b --> v - *- */ -l_ok -pixcmapConvertRGBToYUV(PIXCMAP *cmap) -{ -l_int32 i, ncolors, rval, gval, bval, yval, uval, vval; - - PROCNAME("pixcmapConvertRGBToYUV"); - - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - ncolors = pixcmapGetCount(cmap); - for (i = 0; i < ncolors; i++) { - pixcmapGetColor(cmap, i, &rval, &gval, &bval); - convertRGBToYUV(rval, gval, bval, &yval, &uval, &vval); - pixcmapResetColor(cmap, i, yval, uval, vval); - } - return 0; -} - - -/*! - * \brief pixcmapConvertYUVToRGB() - * - * \param[in] cmap - * \return 0 if OK; 1 on error - * - *
- * Notes: - * ~ in-place transform - * ~ See convertRGBToYUV() for def'n of YUV space. - * ~ replaces: y --> r, u --> g, v --> b - *- */ -l_ok -pixcmapConvertYUVToRGB(PIXCMAP *cmap) -{ -l_int32 i, ncolors, rval, gval, bval, yval, uval, vval; - - PROCNAME("pixcmapConvertYUVToRGB"); - - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - ncolors = pixcmapGetCount(cmap); - for (i = 0; i < ncolors; i++) { - pixcmapGetColor(cmap, i, &yval, &uval, &vval); - convertYUVToRGB(yval, uval, vval, &rval, &gval, &bval); - pixcmapResetColor(cmap, i, rval, gval, bval); - } - return 0; -} - - -/*---------------------------------------------------------------------------* - * Colorspace conversion between RGB and XYZ * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvertRGBToXYZ() - * - * \param[in] pixs 32 bpp rgb - * \return fpixa xyz - * - *
- * Notes: - * (1) The [x,y,z] values are stored as float values in three fpix - * that are returned in a fpixa. - * (2) The XYZ color space was defined in 1931 as a reference model that - * simulates human color perception. When Y is taken as luminance, - * the values of X and Z constitute a color plane representing - * all the hues that can be perceived. This gamut of colors - * is larger than the gamuts that can be displayed or printed. - * For example, although all rgb values map to XYZ, the converse - * is not true. - * (3) The value of the coefficients depends on the illuminant. We use - * coefficients for converting sRGB under D65 (the spectrum from - * a 6500 degree K black body; an approximation to daylight color). - * See, e.g., - * http://www.cs.rit.edu/~ncs/color/t_convert.html - * For more general information on color transforms, see: - * http://www.brucelindbloom.com/ - * http://user.engineering.uiowa.edu/~aip/Misc/ColorFAQ.html - * http://en.wikipedia.org/wiki/CIE_1931_color_space - *- */ -FPIXA * -pixConvertRGBToXYZ(PIX *pixs) -{ -l_int32 w, h, wpls, wpld, i, j, rval, gval, bval; -l_uint32 *lines, *datas; -l_float32 fxval, fyval, fzval; -l_float32 *linex, *liney, *linez, *datax, *datay, *dataz; -FPIX *fpix; -FPIXA *fpixa; - - PROCNAME("pixConvertRGBToXYZ"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (FPIXA *)ERROR_PTR("pixs undefined or not rgb", procName, NULL); - - /* Convert RGB image */ - pixGetDimensions(pixs, &w, &h, NULL); - fpixa = fpixaCreate(3); - for (i = 0; i < 3; i++) { - fpix = fpixCreate(w, h); - fpixaAddFPix(fpixa, fpix, L_INSERT); - } - wpls = pixGetWpl(pixs); - wpld = fpixGetWpl(fpix); - datas = pixGetData(pixs); - datax = fpixaGetData(fpixa, 0); - datay = fpixaGetData(fpixa, 1); - dataz = fpixaGetData(fpixa, 2); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - linex = datax + i * wpld; - liney = datay + i * wpld; - linez = dataz + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - convertRGBToXYZ(rval, gval, bval, &fxval, &fyval, &fzval); - *(linex + j) = fxval; - *(liney + j) = fyval; - *(linez + j) = fzval; - } - } - - return fpixa; -} - - -/*! - * \brief fpixaConvertXYZToRGB() - * - * \param[in] fpixa three fpix: x,y,z - * \return pixd 32 bpp rgb - * - *
- * Notes: - * (1) The xyz image is stored in three fpix. - * (2) For values of xyz that are out of gamut for rgb, the rgb - * components are set to the closest valid color. - *- */ -PIX * -fpixaConvertXYZToRGB(FPIXA *fpixa) -{ -l_int32 w, h, wpls, wpld, i, j, rval, gval, bval; -l_float32 fxval, fyval, fzval; -l_float32 *linex, *liney, *linez, *datax, *datay, *dataz; -l_uint32 *lined, *datad; -PIX *pixd; -FPIX *fpix; - - PROCNAME("fpixaConvertXYZToRGB"); - - if (!fpixa || fpixaGetCount(fpixa) != 3) - return (PIX *)ERROR_PTR("fpixa undefined or invalid", procName, NULL); - - /* Convert XYZ image */ - if (fpixaGetFPixDimensions(fpixa, 0, &w, &h)) - return (PIX *)ERROR_PTR("fpixa dimensions not found", procName, NULL); - pixd = pixCreate(w, h, 32); - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - datax = fpixaGetData(fpixa, 0); - datay = fpixaGetData(fpixa, 1); - dataz = fpixaGetData(fpixa, 2); - fpix = fpixaGetFPix(fpixa, 0, L_CLONE); - wpls = fpixGetWpl(fpix); - fpixDestroy(&fpix); - for (i = 0; i < h; i++) { - linex = datax + i * wpls; - liney = datay + i * wpls; - linez = dataz + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - fxval = linex[j]; - fyval = liney[j]; - fzval = linez[j]; - convertXYZToRGB(fxval, fyval, fzval, 0, &rval, &gval, &bval); - composeRGBPixel(rval, gval, bval, lined + j); - } - } - - return pixd; -} - - -/*! - * \brief convertRGBToXYZ() - * - * \param[in] rval, gval, bval rgb input - * \param[out] pfxval, pfyval, pfzval equivalent xyz values - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) These conversions are for illuminant D65 acting on linear sRGB - * values. - *- */ -l_ok -convertRGBToXYZ(l_int32 rval, - l_int32 gval, - l_int32 bval, - l_float32 *pfxval, - l_float32 *pfyval, - l_float32 *pfzval) -{ - PROCNAME("convertRGBToXYZ"); - - if (pfxval) *pfxval = 0.0; - if (pfyval) *pfyval = 0.0; - if (pfzval) *pfzval = 0.0; - if (!pfxval || !pfyval || !pfzval) - return ERROR_INT("&xval, &yval, &zval not all defined", procName, 1); - - *pfxval = 0.4125 * rval + 0.3576 * gval + 0.1804 * bval; - *pfyval = 0.2127 * rval + 0.7152 * gval + 0.0722 * bval; - *pfzval = 0.0193 * rval + 0.1192 * gval + 0.9502 * bval; - return 0; -} - - -/*! - * \brief convertXYZToRGB() - * - * \param[in] fxval, fyval, fzval - * \param[in] blackout 0 to output nearest color if out of gamut; - * 1 to output black - * \param[out] prval, pgval, pbval 32 bpp rgb values - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) For values of xyz that are out of gamut for rgb, at least - * one of the r, g or b components will be either less than 0 - * or greater than 255. For that situation: - * * if %blackout == 0, the individual component(s) that are out - * of gamut will be set to 0 or 255, respectively. - * * if %blackout == 1, the output color will be set to black - *- */ -l_ok -convertXYZToRGB(l_float32 fxval, - l_float32 fyval, - l_float32 fzval, - l_int32 blackout, - l_int32 *prval, - l_int32 *pgval, - l_int32 *pbval) -{ -l_int32 rval, gval, bval; - - PROCNAME("convertXYZToRGB"); - - if (prval) *prval = 0; - if (pgval) *pgval = 0; - if (pbval) *pbval = 0; - if (!prval || !pgval ||!pbval) - return ERROR_INT("&rval, &gval, &bval not all defined", procName, 1); - *prval = *pgval = *pbval = 0; - - rval = (l_int32)(3.2405 * fxval - 1.5372 * fyval - 0.4985 * fzval + 0.5); - gval = (l_int32)(-0.9693 * fxval + 1.8760 * fyval + 0.0416 * fzval + 0.5); - bval = (l_int32)(0.0556 * fxval - 0.2040 * fyval + 1.0573 * fzval + 0.5); - if (blackout == 0) { /* the usual situation; use nearest rgb color */ - *prval = L_MAX(0, L_MIN(rval, 255)); - *pgval = L_MAX(0, L_MIN(gval, 255)); - *pbval = L_MAX(0, L_MIN(bval, 255)); - } else { /* use black for out of gamut */ - if (rval >= 0 && rval < 256 && gval >= 0 && gval < 256 && - bval >= 0 && bval < 256) { /* in gamut */ - *prval = rval; - *pgval = gval; - *pbval = bval; - } - } - return 0; -} - - -/*---------------------------------------------------------------------------* - * Colorspace conversion between XYZ and LAB * - *---------------------------------------------------------------------------*/ -/*! - * \brief fpixaConvertXYZToLAB() - * - * \param[in] fpixas xyz - * \return fpixa lab - * - *
- * Notes: - * (1) The input [x,y,z] and output [l,a,b] values are stored as - * float values, each set in three fpix. - * (2) The CIE LAB color space was invented in 1976, as an - * absolute reference for specifying colors that we can - * perceive, independently of the rendering device. It was - * invented to align color display and print images. - * For information, see: - * http://www.brucelindbloom.com/ - * http://en.wikipedia.org/wiki/Lab_color_space - *- */ -FPIXA * -fpixaConvertXYZToLAB(FPIXA *fpixas) -{ -l_int32 w, h, wpl, i, j; -l_float32 fxval, fyval, fzval, flval, faval, fbval; -l_float32 *linex, *liney, *linez, *datax, *datay, *dataz; -l_float32 *linel, *linea, *lineb, *datal, *dataa, *datab; -FPIX *fpix; -FPIXA *fpixad; - - PROCNAME("fpixaConvertXYZToLAB"); - - if (!fpixas || fpixaGetCount(fpixas) != 3) - return (FPIXA *)ERROR_PTR("fpixas undefined/invalid", procName, NULL); - - /* Convert XYZ image */ - if (fpixaGetFPixDimensions(fpixas, 0, &w, &h)) - return (FPIXA *)ERROR_PTR("fpixas sizes not found", procName, NULL); - fpixad = fpixaCreate(3); - for (i = 0; i < 3; i++) { - fpix = fpixCreate(w, h); - fpixaAddFPix(fpixad, fpix, L_INSERT); - } - wpl = fpixGetWpl(fpix); - datax = fpixaGetData(fpixas, 0); - datay = fpixaGetData(fpixas, 1); - dataz = fpixaGetData(fpixas, 2); - datal = fpixaGetData(fpixad, 0); - dataa = fpixaGetData(fpixad, 1); - datab = fpixaGetData(fpixad, 2); - - /* Convert XYZ image */ - for (i = 0; i < h; i++) { - linex = datax + i * wpl; - liney = datay + i * wpl; - linez = dataz + i * wpl; - linel = datal + i * wpl; - linea = dataa + i * wpl; - lineb = datab + i * wpl; - for (j = 0; j < w; j++) { - fxval = *(linex + j); - fyval = *(liney + j); - fzval = *(linez + j); - convertXYZToLAB(fxval, fyval, fzval, &flval, &faval, &fbval); - *(linel + j) = flval; - *(linea + j) = faval; - *(lineb + j) = fbval; - } - } - - return fpixad; -} - - -/*! - * \brief fpixaConvertLABToXYZ() - * - * \param[in] fpixas lab - * \return fpixa xyz - * - *
- * Notes: - * (1) The input [l,a,b] and output [x,y,z] values are stored as - * float values, each set in three fpix. - *- */ -FPIXA * -fpixaConvertLABToXYZ(FPIXA *fpixas) -{ -l_int32 w, h, wpl, i, j; -l_float32 fxval, fyval, fzval, flval, faval, fbval; -l_float32 *linel, *linea, *lineb, *datal, *dataa, *datab; -l_float32 *linex, *liney, *linez, *datax, *datay, *dataz; -FPIX *fpix; -FPIXA *fpixad; - - PROCNAME("fpixaConvertLABToXYZ"); - - if (!fpixas || fpixaGetCount(fpixas) != 3) - return (FPIXA *)ERROR_PTR("fpixas undefined/invalid", procName, NULL); - - /* Convert LAB image */ - if (fpixaGetFPixDimensions(fpixas, 0, &w, &h)) - return (FPIXA *)ERROR_PTR("fpixas sizes not found", procName, NULL); - fpixad = fpixaCreate(3); - for (i = 0; i < 3; i++) { - fpix = fpixCreate(w, h); - fpixaAddFPix(fpixad, fpix, L_INSERT); - } - wpl = fpixGetWpl(fpix); - datal = fpixaGetData(fpixas, 0); - dataa = fpixaGetData(fpixas, 1); - datab = fpixaGetData(fpixas, 2); - datax = fpixaGetData(fpixad, 0); - datay = fpixaGetData(fpixad, 1); - dataz = fpixaGetData(fpixad, 2); - - /* Convert XYZ image */ - for (i = 0; i < h; i++) { - linel = datal + i * wpl; - linea = dataa + i * wpl; - lineb = datab + i * wpl; - linex = datax + i * wpl; - liney = datay + i * wpl; - linez = dataz + i * wpl; - for (j = 0; j < w; j++) { - flval = *(linel + j); - faval = *(linea + j); - fbval = *(lineb + j); - convertLABToXYZ(flval, faval, fbval, &fxval, &fyval, &fzval); - *(linex + j) = fxval; - *(liney + j) = fyval; - *(linez + j) = fzval; - } - } - - return fpixad; -} - - -/*! - * \brief convertXYZToLAB() - * - * \param[in] xval, yval, zval input xyz - * \param[out] plval, paval, pbval equivalent lab values - * \return 0 if OK, 1 on error - */ -l_ok -convertXYZToLAB(l_float32 xval, - l_float32 yval, - l_float32 zval, - l_float32 *plval, - l_float32 *paval, - l_float32 *pbval) -{ -l_float32 xn, yn, zn, fx, fy, fz; - - PROCNAME("convertXYZToLAB"); - - if (plval) *plval = 0.0; - if (paval) *paval = 0.0; - if (pbval) *pbval = 0.0; - if (!plval || !paval || !pbval) - return ERROR_INT("&lval, &aval, &bval not all defined", procName, 1); - - /* First normalize to the corresponding white values */ - xn = 0.0041259 * xval; - yn = 0.0039216 * yval; - zn = 0.0036012 * zval; - /* Then apply the lab_forward function */ - fx = lab_forward(xn); - fy = lab_forward(yn); - fz = lab_forward(zn); - *plval = 116.0 * fy - 16.0; - *paval = 500.0 * (fx - fy); - *pbval = 200.0 * (fy - fz); - return 0; -} - - -/*! - * \brief convertLABToXYZ() - * - * \param[in] lval, aval, bval input lab - * \param[out] pxval, pyval, pzval equivalent xyz values - * \return 0 if OK, 1 on error - */ -l_ok -convertLABToXYZ(l_float32 lval, - l_float32 aval, - l_float32 bval, - l_float32 *pxval, - l_float32 *pyval, - l_float32 *pzval) -{ -l_float32 fx, fy, fz; -l_float32 xw = 242.37; /* x component corresponding to rgb white */ -l_float32 yw = 255.0; /* y component corresponding to rgb white */ -l_float32 zw = 277.69; /* z component corresponding to rgb white */ - - PROCNAME("convertLABToXYZ"); - - if (pxval) *pxval = 0.0; - if (pyval) *pyval = 0.0; - if (pzval) *pzval = 0.0; - if (!pxval || !pyval || !pzval) - return ERROR_INT("&xval, &yval, &zval not all defined", procName, 1); - - fy = 0.0086207 * (16.0 + lval); - fx = fy + 0.002 * aval; - fz = fy - 0.005 * bval; - *pxval = xw * lab_reverse(fx); - *pyval = yw * lab_reverse(fy); - *pzval = zw * lab_reverse(fz); - return 0; -} - - -/* - * See http://en.wikipedia.org/wiki/Lab_color_space for formulas. - * This is the forward function: from xyz to lab. It includes a rational - * function approximation over [0.008856 ... 1] to the cube root, from - * "Fast Color Space Transformations Using Minimax Approximations", - * M. Celebi et al, http://arxiv.org/pdf/1009.0854v1.pdf. - */ -static l_float32 -lab_forward(l_float32 v) -{ -const l_float32 f_thresh = 0.008856; /* (6/29)^3 */ -const l_float32 f_factor = 7.787; /* (1/3) * (29/6)^2) */ -const l_float32 f_offset = 0.13793; /* 4/29 */ - - if (v > f_thresh) { -#if SLOW_CUBE_ROOT - return powf(v, 0.333333); -#else - l_float32 num, den; - num = 4.37089e-04 + v * (9.52695e-02 + v * (1.25201 + v * 1.30273)); - den = 3.91236e-03 + v * (2.95408e-01 + v * (1.71714 + v * 6.34341e-01)); - return num / den; -#endif - } else { - return f_factor * v + f_offset; - } -} - - -/* - * See http://en.wikipedia.org/wiki/Lab_color_space for formulas. - * This is the reverse (inverse) function: from lab to xyz. - */ -static l_float32 -lab_reverse(l_float32 v) -{ -const l_float32 r_thresh = 0.20690; /* 6/29 */ -const l_float32 r_factor = 0.12842; /* 3 * (6/29)^2 */ -const l_float32 r_offset = 0.13793; /* 4/29 */ - - if (v > r_thresh) { - return v * v * v; - } else { - return r_factor * (v - r_offset); - } -} - - -/*---------------------------------------------------------------------------* - * Colorspace conversion between RGB and LAB * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvertRGBToLAB() - * - * \param[in] pixs 32 bpp rgb - * \return fpixa lab - * - *
- * Notes: - * (1) The [l,a,b] values are stored as float values in three fpix - * that are returned in a fpixa. - *- */ -FPIXA * -pixConvertRGBToLAB(PIX *pixs) -{ -l_int32 w, h, wpls, wpld, i, j, rval, gval, bval; -l_uint32 *lines, *datas; -l_float32 flval, faval, fbval; -l_float32 *linel, *linea, *lineb, *datal, *dataa, *datab; -FPIX *fpix; -FPIXA *fpixa; - - PROCNAME("pixConvertRGBToLAB"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (FPIXA *)ERROR_PTR("pixs undefined or not rgb", procName, NULL); - - /* Convert RGB image */ - pixGetDimensions(pixs, &w, &h, NULL); - fpixa = fpixaCreate(3); - for (i = 0; i < 3; i++) { - fpix = fpixCreate(w, h); - fpixaAddFPix(fpixa, fpix, L_INSERT); - } - wpls = pixGetWpl(pixs); - wpld = fpixGetWpl(fpix); - datas = pixGetData(pixs); - datal = fpixaGetData(fpixa, 0); - dataa = fpixaGetData(fpixa, 1); - datab = fpixaGetData(fpixa, 2); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - linel = datal + i * wpld; - linea = dataa + i * wpld; - lineb = datab + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - convertRGBToLAB(rval, gval, bval, &flval, &faval, &fbval); - *(linel + j) = flval; - *(linea + j) = faval; - *(lineb + j) = fbval; - } - } - - return fpixa; -} - - -/*! - * \brief fpixaConvertLABToRGB() - * - * \param[in] fpixa three fpix: l,a,b - * \return pixd 32 bpp rgb - * - *
- * Notes: - * (1) The lab image is stored in three fpix. - *- */ -PIX * -fpixaConvertLABToRGB(FPIXA *fpixa) -{ -l_int32 w, h, wpls, wpld, i, j, rval, gval, bval; -l_float32 flval, faval, fbval; -l_float32 *linel, *linea, *lineb, *datal, *dataa, *datab; -l_uint32 *lined, *datad; -PIX *pixd; -FPIX *fpix; - - PROCNAME("fpixaConvertLABToRGB"); - - if (!fpixa || fpixaGetCount(fpixa) != 3) - return (PIX *)ERROR_PTR("fpixa undefined or invalid", procName, NULL); - - /* Convert LAB image */ - if (fpixaGetFPixDimensions(fpixa, 0, &w, &h)) - return (PIX *)ERROR_PTR("fpixa dimensions not found", procName, NULL); - pixd = pixCreate(w, h, 32); - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - datal = fpixaGetData(fpixa, 0); - dataa = fpixaGetData(fpixa, 1); - datab = fpixaGetData(fpixa, 2); - fpix = fpixaGetFPix(fpixa, 0, L_CLONE); - wpls = fpixGetWpl(fpix); - fpixDestroy(&fpix); - for (i = 0; i < h; i++) { - linel = datal + i * wpls; - linea = dataa + i * wpls; - lineb = datab + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - flval = linel[j]; - faval = linea[j]; - fbval = lineb[j]; - convertLABToRGB(flval, faval, fbval, &rval, &gval, &bval); - composeRGBPixel(rval, gval, bval, lined + j); - } - } - - return pixd; -} - - -/*! - * \brief convertRGBToLAB() - * - * \param[in] rval, gval, bval rgb input - * \param[out] pflval, pfaval, pfbval equivalent lab values - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) These conversions are for illuminant D65 acting on linear sRGB - * values. - *- */ -l_ok -convertRGBToLAB(l_int32 rval, - l_int32 gval, - l_int32 bval, - l_float32 *pflval, - l_float32 *pfaval, - l_float32 *pfbval) -{ -l_float32 fxval, fyval, fzval; - - PROCNAME("convertRGBToLAB"); - - if (pflval) *pflval = 0.0; - if (pfaval) *pfaval = 0.0; - if (pfbval) *pfbval = 0.0; - if (!pflval || !pfaval || !pfbval) - return ERROR_INT("&flval, &faval, &fbval not all defined", procName, 1); - - convertRGBToXYZ(rval, gval, bval, &fxval, &fyval, &fzval); - convertXYZToLAB(fxval, fyval, fzval, pflval, pfaval, pfbval); - return 0; -} - - -/*! - * \brief convertLABToRGB() - * - * \param[in] flval, faval, fbval input lab - * \param[out] prval, pgval, pbval equivalent rgb values - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) For values of lab that are out of gamut for rgb, the rgb - * components are set to the closest valid color. - *- */ -l_ok -convertLABToRGB(l_float32 flval, - l_float32 faval, - l_float32 fbval, - l_int32 *prval, - l_int32 *pgval, - l_int32 *pbval) -{ -l_float32 fxval, fyval, fzval; - - PROCNAME("convertLABToRGB"); - - if (prval) *prval = 0; - if (pgval) *pgval = 0; - if (pbval) *pbval = 0; - if (!prval || !pgval || !pbval) - return ERROR_INT("&rval, &gval, &bval not all defined", procName, 1); - - convertLABToXYZ(flval, faval, fbval, &fxval, &fyval, &fzval); - convertXYZToRGB(fxval, fyval, fzval, 0, prval, pgval, pbval); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/compare.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/compare.c deleted file mode 100644 index d0800719..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/compare.c +++ /dev/null @@ -1,3607 +0,0 @@ -/*====================================================================* - - 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 compare.c - *
- * - * Test for pix equality - * l_int32 pixEqual() - * l_int32 pixEqualWithAlpha() - * l_int32 pixEqualWithCmap() - * l_int32 cmapEqual() - * l_int32 pixUsesCmapColor() - * - * Binary correlation - * l_int32 pixCorrelationBinary() - * - * Difference of two images of same size - * l_int32 pixDisplayDiffBinary() - * l_int32 pixCompareBinary() - * l_int32 pixCompareGrayOrRGB() - * l_int32 pixCompareGray() - * l_int32 pixCompareRGB() - * l_int32 pixCompareTiled() - * - * Other measures of the difference of two images of the same size - * NUMA *pixCompareRankDifference() - * l_int32 pixTestForSimilarity() - * l_int32 pixGetDifferenceStats() - * NUMA *pixGetDifferenceHistogram() - * l_int32 pixGetPerceptualDiff() - * l_int32 pixGetPSNR() - * - * Comparison of photo regions by histogram - * l_int32 pixaComparePhotoRegionsByHisto() -- top-level - * l_int32 pixComparePhotoRegionsByHisto() -- top-level for 2 - * l_int32 pixGenPhotoHistos() - * PIX *pixPadToCenterCentroid() - * l_int32 pixCentroid8() - * l_int32 pixDecideIfPhotoImage() - * static l_int32 findHistoGridDimensions() - * l_int32 compareTilesByHisto() - * - * l_int32 pixCompareGrayByHisto() -- top-level for 2 - * static l_int32 pixCompareTilesByHisto() - * l_int32 pixCropAlignedToCentroid() - * - * l_uint8 *l_compressGrayHistograms() - * NUMAA *l_uncompressGrayHistograms() - * - * Translated images at the same resolution - * l_int32 pixCompareWithTranslation() - * l_int32 pixBestCorrelation() - * - * For comparing images using tiled histograms, essentially all the - * computation goes into deciding if a region of an image is a photo, - * whether that photo region is amenable to similarity measurements - * using histograms, and finally the calculation of the gray histograms - * for each of the tiled regions. The actual comparison is essentially - * instantaneous. Therefore, with a large number of images to compare - * with each other, it is important to first calculate the histograms - * for each image. Then the comparisons, which go as the square of the - * number of images, actually takes no time. - * - * A high level function that takes a pixa of images and does - * all comparisons, pixaComparePhotosByHisto(), uses this split - * approach. It pads the images so that the centroid is in the center, - * which will allow the tiles to be better aligned. - * - * For testing purposes, two functions are given that do all the work - * to compare just two photo regions: - * * pixComparePhotoRegionsByHisto() uses the split approach, qualifying - * the images first with pixGenPhotoHistos(), and then comparing - * with compareTilesByHisto(). - * * pixCompareGrayByHisto() aligns the two images by centroid - * and calls pixCompareTilesByHisto() to generate the histograms - * and do the comparison. - * - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) Equality is defined as having the same pixel values for - * each respective image pixel. - * (2) This works on two pix of any depth. If one or both pix - * have a colormap, the depths can be different and the - * two pix can still be equal. - * (3) This ignores the alpha component for 32 bpp images. - * (4) If both pix have colormaps and the depths are equal, - * use the pixEqualWithCmap() function, which does a fast - * comparison if the colormaps are identical and a relatively - * slow comparison otherwise. - * (5) In all other cases, any existing colormaps must first be - * removed before doing pixel comparison. After the colormaps - * are removed, the resulting two images must have the same depth. - * The "lowest common denominator" is RGB, but this is only - * chosen when necessary, or when both have colormaps but - * different depths. - * (6) For images without colormaps that are not 32 bpp, all bits - * in the image part of the data array must be identical. - *- */ -l_ok -pixEqual(PIX *pix1, - PIX *pix2, - l_int32 *psame) -{ - return pixEqualWithAlpha(pix1, pix2, 0, psame); -} - - -/*! - * \brief pixEqualWithAlpha() - * - * \param[in] pix1 - * \param[in] pix2 - * \param[in] use_alpha 1 to compare alpha in RGBA; 0 to ignore - * \param[out] psame 1 if same; 0 if different - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) See notes in pixEqual(). - * (2) This is more general than pixEqual(), in that for 32 bpp - * RGBA images, where spp = 4, you can optionally include - * the alpha component in the comparison. - *- */ -l_ok -pixEqualWithAlpha(PIX *pix1, - PIX *pix2, - l_int32 use_alpha, - l_int32 *psame) -{ -l_int32 w1, h1, d1, w2, h2, d2, wpl1, wpl2; -l_int32 spp1, spp2, i, j, color, mismatch, opaque; -l_int32 fullwords, linebits, endbits; -l_uint32 endmask, wordmask; -l_uint32 *data1, *data2, *line1, *line2; -PIX *pixs1, *pixs2, *pixt1, *pixt2, *pixalpha; -PIXCMAP *cmap1, *cmap2; - - PROCNAME("pixEqualWithAlpha"); - - if (!psame) - return ERROR_INT("psame not defined", procName, 1); - *psame = 0; /* init to not equal */ - if (!pix1 || !pix2) - return ERROR_INT("pix1 and pix2 not both defined", procName, 1); - pixGetDimensions(pix1, &w1, &h1, &d1); - pixGetDimensions(pix2, &w2, &h2, &d2); - if (w1 != w2 || h1 != h2) { - L_INFO("pix sizes differ\n", procName); - return 0; - } - - /* Suppose the use_alpha flag is true. - * If only one of two 32 bpp images has spp == 4, we call that - * a "mismatch" of the alpha component. In the case of a mismatch, - * if the 4 bpp pix does not have all alpha components opaque (255), - * the images are not-equal. However if they are all opaque, - * this image is equivalent to spp == 3, so we allow the - * comparison to go forward, testing only for the RGB equality. */ - spp1 = pixGetSpp(pix1); - spp2 = pixGetSpp(pix2); - mismatch = 0; - if (use_alpha && d1 == 32 && d2 == 32) { - mismatch = ((spp1 == 4 && spp2 != 4) || (spp1 != 4 && spp2 == 4)); - if (mismatch) { - pixalpha = (spp1 == 4) ? pix1 : pix2; - pixAlphaIsOpaque(pixalpha, &opaque); - if (!opaque) { - L_INFO("just one pix has a non-opaque alpha layer\n", procName); - return 0; - } - } - } - - cmap1 = pixGetColormap(pix1); - cmap2 = pixGetColormap(pix2); - if (!cmap1 && !cmap2 && (d1 != d2) && (d1 == 32 || d2 == 32)) { - L_INFO("no colormaps, pix depths unequal, and one of them is RGB\n", - procName); - return 0; - } - - if (cmap1 && cmap2 && (d1 == d2)) /* use special function */ - return pixEqualWithCmap(pix1, pix2, psame); - - /* Must remove colormaps if they exist, and in the process - * end up with the resulting images having the same depth. */ - if (cmap1 && !cmap2) { - pixUsesCmapColor(pix1, &color); - if (color && d2 <= 8) /* can't be equal */ - return 0; - if (d2 < 8) - pixs2 = pixConvertTo8(pix2, FALSE); - else - pixs2 = pixClone(pix2); - if (d2 <= 8) - pixs1 = pixRemoveColormap(pix1, REMOVE_CMAP_TO_GRAYSCALE); - else - pixs1 = pixRemoveColormap(pix1, REMOVE_CMAP_TO_FULL_COLOR); - } else if (!cmap1 && cmap2) { - pixUsesCmapColor(pix2, &color); - if (color && d1 <= 8) /* can't be equal */ - return 0; - if (d1 < 8) - pixs1 = pixConvertTo8(pix1, FALSE); - else - pixs1 = pixClone(pix1); - if (d1 <= 8) - pixs2 = pixRemoveColormap(pix2, REMOVE_CMAP_TO_GRAYSCALE); - else - pixs2 = pixRemoveColormap(pix2, REMOVE_CMAP_TO_FULL_COLOR); - } else if (cmap1 && cmap2) { /* depths not equal; use rgb */ - pixs1 = pixRemoveColormap(pix1, REMOVE_CMAP_TO_FULL_COLOR); - pixs2 = pixRemoveColormap(pix2, REMOVE_CMAP_TO_FULL_COLOR); - } else { /* no colormaps */ - pixs1 = pixClone(pix1); - pixs2 = pixClone(pix2); - } - - /* OK, we have no colormaps, but the depths may still be different */ - d1 = pixGetDepth(pixs1); - d2 = pixGetDepth(pixs2); - if (d1 != d2) { - if (d1 == 16 || d2 == 16) { - L_INFO("one pix is 16 bpp\n", procName); - pixDestroy(&pixs1); - pixDestroy(&pixs2); - return 0; - } - pixt1 = pixConvertLossless(pixs1, 8); - pixt2 = pixConvertLossless(pixs2, 8); - if (!pixt1 || !pixt2) { - L_INFO("failure to convert to 8 bpp\n", procName); - pixDestroy(&pixs1); - pixDestroy(&pixs2); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - return 0; - } - } else { - pixt1 = pixClone(pixs1); - pixt2 = pixClone(pixs2); - } - pixDestroy(&pixs1); - pixDestroy(&pixs2); - - /* No colormaps, equal depths; do pixel comparisons */ - d1 = pixGetDepth(pixt1); - d2 = pixGetDepth(pixt2); - wpl1 = pixGetWpl(pixt1); - wpl2 = pixGetWpl(pixt2); - data1 = pixGetData(pixt1); - data2 = pixGetData(pixt2); - - if (d1 == 32) { /* test either RGB or RGBA pixels */ - if (use_alpha && !mismatch) - wordmask = (spp1 == 3) ? 0xffffff00 : 0xffffffff; - else - wordmask = 0xffffff00; - for (i = 0; i < h1; i++) { - line1 = data1 + wpl1 * i; - line2 = data2 + wpl2 * i; - for (j = 0; j < wpl1; j++) { - if ((*line1 ^ *line2) & wordmask) { - pixDestroy(&pixt1); - pixDestroy(&pixt2); - return 0; - } - line1++; - line2++; - } - } - } else { /* all bits count */ - linebits = d1 * w1; - fullwords = linebits / 32; - endbits = linebits & 31; - endmask = (endbits == 0) ? 0 : (0xffffffff << (32 - endbits)); - for (i = 0; i < h1; i++) { - line1 = data1 + wpl1 * i; - line2 = data2 + wpl2 * i; - for (j = 0; j < fullwords; j++) { - if (*line1 ^ *line2) { - pixDestroy(&pixt1); - pixDestroy(&pixt2); - return 0; - } - line1++; - line2++; - } - if (endbits) { - if ((*line1 ^ *line2) & endmask) { - pixDestroy(&pixt1); - pixDestroy(&pixt2); - return 0; - } - } - } - } - - pixDestroy(&pixt1); - pixDestroy(&pixt2); - *psame = 1; - return 0; -} - - -/*! - * \brief pixEqualWithCmap() - * - * \param[in] pix1 - * \param[in] pix2 - * \param[out] psame - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This returns same = TRUE if the images have identical content. - * (2) Both pix must have a colormap, and be of equal size and depth. - * If these conditions are not satisfied, it is not an error; - * the returned result is same = FALSE. - * (3) We then check whether the colormaps are the same; if so, - * the comparison proceeds 32 bits at a time. - * (4) If the colormaps are different, the comparison is done by - * slow brute force. - *- */ -l_ok -pixEqualWithCmap(PIX *pix1, - PIX *pix2, - l_int32 *psame) -{ -l_int32 d, w, h, wpl1, wpl2, i, j, linebits, fullwords, endbits; -l_int32 rval1, rval2, gval1, gval2, bval1, bval2, samecmaps; -l_uint32 endmask, val1, val2; -l_uint32 *data1, *data2, *line1, *line2; -PIXCMAP *cmap1, *cmap2; - - PROCNAME("pixEqualWithCmap"); - - if (!psame) - return ERROR_INT("&same not defined", procName, 1); - *psame = 0; - if (!pix1) - return ERROR_INT("pix1 not defined", procName, 1); - if (!pix2) - return ERROR_INT("pix2 not defined", procName, 1); - - if (pixSizesEqual(pix1, pix2) == 0) - return 0; - cmap1 = pixGetColormap(pix1); - cmap2 = pixGetColormap(pix2); - if (!cmap1 || !cmap2) { - L_INFO("both images don't have colormap\n", procName); - return 0; - } - pixGetDimensions(pix1, &w, &h, &d); - if (d != 1 && d != 2 && d != 4 && d != 8) { - L_INFO("pix depth not in {1, 2, 4, 8}\n", procName); - return 0; - } - - cmapEqual(cmap1, cmap2, 3, &samecmaps); - if (samecmaps == TRUE) { /* colormaps are identical; compare by words */ - linebits = d * w; - wpl1 = pixGetWpl(pix1); - wpl2 = pixGetWpl(pix2); - data1 = pixGetData(pix1); - data2 = pixGetData(pix2); - fullwords = linebits / 32; - endbits = linebits & 31; - endmask = (endbits == 0) ? 0 : (0xffffffff << (32 - endbits)); - for (i = 0; i < h; i++) { - line1 = data1 + wpl1 * i; - line2 = data2 + wpl2 * i; - for (j = 0; j < fullwords; j++) { - if (*line1 ^ *line2) - return 0; - line1++; - line2++; - } - if (endbits) { - if ((*line1 ^ *line2) & endmask) - return 0; - } - } - *psame = 1; - return 0; - } - - /* Colormaps aren't identical; compare pixel by pixel */ - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - pixGetPixel(pix1, j, i, &val1); - pixGetPixel(pix2, j, i, &val2); - pixcmapGetColor(cmap1, val1, &rval1, &gval1, &bval1); - pixcmapGetColor(cmap2, val2, &rval2, &gval2, &bval2); - if (rval1 != rval2 || gval1 != gval2 || bval1 != bval2) - return 0; - } - } - - *psame = 1; - return 0; -} - - -/*! - * \brief cmapEqual() - * - * \param[in] cmap1 - * \param[in] cmap2 - * \param[in] ncomps 3 for RGB, 4 for RGBA - * \param[out] psame - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This returns %same = TRUE if the colormaps have identical entries. - * (2) If %ncomps == 4, the alpha components of the colormaps are also - * compared. - *- */ -l_ok -cmapEqual(PIXCMAP *cmap1, - PIXCMAP *cmap2, - l_int32 ncomps, - l_int32 *psame) -{ -l_int32 n1, n2, i, rval1, rval2, gval1, gval2, bval1, bval2, aval1, aval2; - - PROCNAME("cmapEqual"); - - if (!psame) - return ERROR_INT("&same not defined", procName, 1); - *psame = FALSE; - if (!cmap1) - return ERROR_INT("cmap1 not defined", procName, 1); - if (!cmap2) - return ERROR_INT("cmap2 not defined", procName, 1); - if (ncomps != 3 && ncomps != 4) - return ERROR_INT("ncomps not 3 or 4", procName, 1); - - n1 = pixcmapGetCount(cmap1); - n2 = pixcmapGetCount(cmap2); - if (n1 != n2) { - L_INFO("colormap sizes are different\n", procName); - return 0; - } - - for (i = 0; i < n1; i++) { - pixcmapGetRGBA(cmap1, i, &rval1, &gval1, &bval1, &aval1); - pixcmapGetRGBA(cmap2, i, &rval2, &gval2, &bval2, &aval2); - if (rval1 != rval2 || gval1 != gval2 || bval1 != bval2) - return 0; - if (ncomps == 4 && aval1 != aval2) - return 0; - } - *psame = TRUE; - return 0; -} - - -/*! - * \brief pixUsesCmapColor() - * - * \param[in] pixs any depth, colormap - * \param[out] pcolor TRUE if color found - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This returns color = TRUE if three things are obtained: - * (a) the pix has a colormap - * (b) the colormap has at least one color entry - * (c) a color entry is actually used - * (2) It is used in pixEqual() for comparing two images, in a - * situation where it is required to know if the colormap - * has color entries that are actually used in the image. - *- */ -l_ok -pixUsesCmapColor(PIX *pixs, - l_int32 *pcolor) -{ -l_int32 n, i, rval, gval, bval, numpix; -NUMA *na; -PIXCMAP *cmap; - - PROCNAME("pixUsesCmapColor"); - - if (!pcolor) - return ERROR_INT("&color not defined", procName, 1); - *pcolor = 0; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - - if ((cmap = pixGetColormap(pixs)) == NULL) - return 0; - - pixcmapHasColor(cmap, pcolor); - if (*pcolor == 0) /* no color */ - return 0; - - /* The cmap has color entries. Are they used? */ - na = pixGetGrayHistogram(pixs, 1); - n = pixcmapGetCount(cmap); - for (i = 0; i < n; i++) { - pixcmapGetColor(cmap, i, &rval, &gval, &bval); - numaGetIValue(na, i, &numpix); - if ((rval != gval || rval != bval) && numpix) { /* color found! */ - *pcolor = 1; - break; - } - } - numaDestroy(&na); - - return 0; -} - - -/*------------------------------------------------------------------* - * Binary correlation * - *------------------------------------------------------------------*/ -/*! - * \brief pixCorrelationBinary() - * - * \param[in] pix1 1 bpp - * \param[in] pix2 1 bpp - * \param[out] pval correlation - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) The correlation is a number between 0.0 and 1.0, - * based on foreground similarity: - * (|1 AND 2|)**2 - * correlation = -------------- - * |1| * |2| - * where |x| is the count of foreground pixels in image x. - * If the images are identical, this is 1.0. - * If they have no fg pixels in common, this is 0.0. - * If one or both images have no fg pixels, the correlation is 0.0. - * (2) Typically the two images are of equal size, but this - * is not enforced. Instead, the UL corners are aligned. - *- */ -l_ok -pixCorrelationBinary(PIX *pix1, - PIX *pix2, - l_float32 *pval) -{ -l_int32 count1, count2, countn; -l_int32 *tab8; -PIX *pixn; - - PROCNAME("pixCorrelationBinary"); - - if (!pval) - return ERROR_INT("&pval not defined", procName, 1); - *pval = 0.0; - if (!pix1) - return ERROR_INT("pix1 not defined", procName, 1); - if (!pix2) - return ERROR_INT("pix2 not defined", procName, 1); - - tab8 = makePixelSumTab8(); - pixCountPixels(pix1, &count1, tab8); - pixCountPixels(pix2, &count2, tab8); - if (count1 == 0 || count2 == 0) { - LEPT_FREE(tab8); - return 0; - } - pixn = pixAnd(NULL, pix1, pix2); - pixCountPixels(pixn, &countn, tab8); - *pval = (l_float32)countn * (l_float32)countn / - ((l_float32)count1 * (l_float32)count2); - LEPT_FREE(tab8); - pixDestroy(&pixn); - return 0; -} - - -/*------------------------------------------------------------------* - * Difference of two images * - *------------------------------------------------------------------*/ -/*! - * \brief pixDisplayDiffBinary() - * - * \param[in] pix1 1 bpp - * \param[in] pix2 1 bpp - * \return pixd 4 bpp cmapped, or NULL on error - * - *
- * Notes: - * (1) This gives a color representation of the difference between - * pix1 and pix2. The color difference depends on the order. - * The pixels in pixd have 4 colors: - * * unchanged: black (on), white (off) - * * on in pix1, off in pix2: red - * * on in pix2, off in pix1: green - * (2) This aligns the UL corners of pix1 and pix2, and crops - * to the overlapping pixels. - *- */ -PIX * -pixDisplayDiffBinary(PIX *pix1, - PIX *pix2) -{ -l_int32 w1, h1, d1, w2, h2, d2, minw, minh; -PIX *pixt, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixDisplayDiffBinary"); - - if (!pix1 || !pix2) - return (PIX *)ERROR_PTR("pix1, pix2 not both defined", procName, NULL); - pixGetDimensions(pix1, &w1, &h1, &d1); - pixGetDimensions(pix2, &w2, &h2, &d2); - if (d1 != 1 || d2 != 1) - return (PIX *)ERROR_PTR("pix1 and pix2 not 1 bpp", procName, NULL); - minw = L_MIN(w1, w2); - minh = L_MIN(h1, h2); - - pixd = pixCreate(minw, minh, 4); - cmap = pixcmapCreate(4); - pixcmapAddColor(cmap, 255, 255, 255); /* initialized to white */ - pixcmapAddColor(cmap, 0, 0, 0); - pixcmapAddColor(cmap, 255, 0, 0); - pixcmapAddColor(cmap, 0, 255, 0); - pixSetColormap(pixd, cmap); - - pixt = pixAnd(NULL, pix1, pix2); - pixPaintThroughMask(pixd, pixt, 0, 0, 0x0); /* black */ - pixSubtract(pixt, pix1, pix2); - pixPaintThroughMask(pixd, pixt, 0, 0, 0xff000000); /* red */ - pixSubtract(pixt, pix2, pix1); - pixPaintThroughMask(pixd, pixt, 0, 0, 0x00ff0000); /* green */ - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixCompareBinary() - * - * \param[in] pix1 1 bpp - * \param[in] pix2 1 bpp - * \param[in] comptype L_COMPARE_XOR, L_COMPARE_SUBTRACT - * \param[out] pfract fraction of pixels that are different - * \param[out] ppixdiff [optional] pix of difference - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) The two images are aligned at the UL corner, and do not - * need to be the same size. - * (2) If using L_COMPARE_SUBTRACT, pix2 is subtracted from pix1. - * (3) The total number of pixels is determined by pix1. - * (4) On error, the returned fraction is 1.0. - *- */ -l_ok -pixCompareBinary(PIX *pix1, - PIX *pix2, - l_int32 comptype, - l_float32 *pfract, - PIX **ppixdiff) -{ -l_int32 w, h, count; -PIX *pixt; - - PROCNAME("pixCompareBinary"); - - if (ppixdiff) *ppixdiff = NULL; - if (!pfract) - return ERROR_INT("&pfract not defined", procName, 1); - *pfract = 1.0; /* initialize to max difference */ - if (!pix1 || pixGetDepth(pix1) != 1) - return ERROR_INT("pix1 not defined or not 1 bpp", procName, 1); - if (!pix2 || pixGetDepth(pix2) != 1) - return ERROR_INT("pix2 not defined or not 1 bpp", procName, 1); - if (comptype != L_COMPARE_XOR && comptype != L_COMPARE_SUBTRACT) - return ERROR_INT("invalid comptype", procName, 1); - - if (comptype == L_COMPARE_XOR) - pixt = pixXor(NULL, pix1, pix2); - else /* comptype == L_COMPARE_SUBTRACT) */ - pixt = pixSubtract(NULL, pix1, pix2); - pixCountPixels(pixt, &count, NULL); - pixGetDimensions(pix1, &w, &h, NULL); - *pfract = (l_float32)(count) / (l_float32)(w * h); - - if (ppixdiff) - *ppixdiff = pixt; - else - pixDestroy(&pixt); - return 0; -} - - -/*! - * \brief pixCompareGrayOrRGB() - * - * \param[in] pix1 2,4,8,16 bpp gray, 32 bpp rgb, or colormapped - * \param[in] pix2 2,4,8,16 bpp gray, 32 bpp rgb, or colormapped - * \param[in] comptype L_COMPARE_SUBTRACT, L_COMPARE_ABS_DIFF - * \param[in] plottype gplot plot output type, or 0 for no plot - * \param[out] psame [optional] 1 if pixel values are identical - * \param[out] pdiff [optional] average difference - * \param[out] prmsdiff [optional] rms of difference - * \param[out] ppixdiff [optional] pix of difference - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) The two images are aligned at the UL corner, and do not - * need to be the same size. If they are not the same size, - * the comparison will be made over overlapping pixels. - * (2) If there is a colormap, it is removed and the result - * is either gray or RGB depending on the colormap. - * (3) If RGB, each component is compared separately. - * (4) If type is L_COMPARE_ABS_DIFF, pix2 is subtracted from pix1 - * and the absolute value is taken. - * (5) If type is L_COMPARE_SUBTRACT, pix2 is subtracted from pix1 - * and the result is clipped to 0. - * (6) The plot output types are specified in gplot.h. - * Use 0 if no difference plot is to be made. - * (7) If the images are pixelwise identical, no difference - * plot is made, even if requested. The result (TRUE or FALSE) - * is optionally returned in the parameter 'same'. - * (8) The average difference (either subtracting or absolute value) - * is optionally returned in the parameter 'diff'. - * (9) The RMS difference is optionally returned in the - * parameter 'rmsdiff'. For RGB, we return the average of - * the RMS differences for each of the components. - * (10) Because pixel values are compared, pix1 and pix2 can be equal when: - * * they are both gray with different depth - * * one is colormapped and the other is not - * * they are both colormapped and have different size colormaps - *- */ -l_ok -pixCompareGrayOrRGB(PIX *pix1, - PIX *pix2, - l_int32 comptype, - l_int32 plottype, - l_int32 *psame, - l_float32 *pdiff, - l_float32 *prmsdiff, - PIX **ppixdiff) -{ -l_int32 retval, d1, d2; -PIX *pixt1, *pixt2, *pixs1, *pixs2; - - PROCNAME("pixCompareGrayOrRGB"); - - if (psame) *psame = 0; - if (pdiff) *pdiff = 255.0; - if (prmsdiff) *prmsdiff = 255.0; - if (ppixdiff) *ppixdiff = NULL; - if (!pix1 || pixGetDepth(pix1) == 1) - return ERROR_INT("pix1 not defined or 1 bpp", procName, 1); - if (!pix2 || pixGetDepth(pix2) == 1) - return ERROR_INT("pix2 not defined or 1 bpp", procName, 1); - if (comptype != L_COMPARE_SUBTRACT && comptype != L_COMPARE_ABS_DIFF) - return ERROR_INT("invalid comptype", procName, 1); - if (plottype < 0 || plottype >= NUM_GPLOT_OUTPUTS) - return ERROR_INT("invalid plottype", procName, 1); - - pixt1 = pixRemoveColormap(pix1, REMOVE_CMAP_BASED_ON_SRC); - pixt2 = pixRemoveColormap(pix2, REMOVE_CMAP_BASED_ON_SRC); - d1 = pixGetDepth(pixt1); - d2 = pixGetDepth(pixt2); - if (d1 < 8) - pixs1 = pixConvertTo8(pixt1, FALSE); - else - pixs1 = pixClone(pixt1); - if (d2 < 8) - pixs2 = pixConvertTo8(pixt2, FALSE); - else - pixs2 = pixClone(pixt2); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - d1 = pixGetDepth(pixs1); - d2 = pixGetDepth(pixs2); - if (d1 != d2) { - pixDestroy(&pixs1); - pixDestroy(&pixs2); - return ERROR_INT("intrinsic depths are not equal", procName, 1); - } - - if (d1 == 8 || d1 == 16) - retval = pixCompareGray(pixs1, pixs2, comptype, plottype, psame, - pdiff, prmsdiff, ppixdiff); - else /* d1 == 32 */ - retval = pixCompareRGB(pixs1, pixs2, comptype, plottype, psame, - pdiff, prmsdiff, ppixdiff); - pixDestroy(&pixs1); - pixDestroy(&pixs2); - return retval; -} - - -/*! - * \brief pixCompareGray() - * - * \param[in] pix1 8 or 16 bpp, not cmapped - * \param[in] pix2 8 or 16 bpp, not cmapped - * \param[in] comptype L_COMPARE_SUBTRACT, L_COMPARE_ABS_DIFF - * \param[in] plottype gplot plot output type, or 0 for no plot - * \param[out] psame [optional] 1 if pixel values are identical - * \param[out] pdiff [optional] average difference - * \param[out] prmsdiff [optional] rms of difference - * \param[out] ppixdiff [optional] pix of difference - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) See pixCompareGrayOrRGB() for details. - * (2) Use pixCompareGrayOrRGB() if the input pix are colormapped. - * (3) Note: setting %plottype > 0 can result in writing named - * output files. - *- */ -l_ok -pixCompareGray(PIX *pix1, - PIX *pix2, - l_int32 comptype, - l_int32 plottype, - l_int32 *psame, - l_float32 *pdiff, - l_float32 *prmsdiff, - PIX **ppixdiff) -{ -char buf[64]; -static l_int32 index = 0; -l_int32 d1, d2, same, first, last; -GPLOT *gplot; -NUMA *na, *nac; -PIX *pixt; - - PROCNAME("pixCompareGray"); - - if (psame) *psame = 0; - if (pdiff) *pdiff = 255.0; - if (prmsdiff) *prmsdiff = 255.0; - if (ppixdiff) *ppixdiff = NULL; - if (!pix1) - return ERROR_INT("pix1 not defined", procName, 1); - if (!pix2) - return ERROR_INT("pix2 not defined", procName, 1); - d1 = pixGetDepth(pix1); - d2 = pixGetDepth(pix2); - if ((d1 != d2) || (d1 != 8 && d1 != 16)) - return ERROR_INT("depths unequal or not 8 or 16 bpp", procName, 1); - if (pixGetColormap(pix1) || pixGetColormap(pix2)) - return ERROR_INT("pix1 and/or pix2 are colormapped", procName, 1); - if (comptype != L_COMPARE_SUBTRACT && comptype != L_COMPARE_ABS_DIFF) - return ERROR_INT("invalid comptype", procName, 1); - if (plottype < 0 || plottype >= NUM_GPLOT_OUTPUTS) - return ERROR_INT("invalid plottype", procName, 1); - - lept_mkdir("lept/comp"); - - if (comptype == L_COMPARE_SUBTRACT) - pixt = pixSubtractGray(NULL, pix1, pix2); - else /* comptype == L_COMPARE_ABS_DIFF) */ - pixt = pixAbsDifference(pix1, pix2); - - pixZero(pixt, &same); - if (same) - L_INFO("Images are pixel-wise identical\n", procName); - if (psame) *psame = same; - - if (pdiff) - pixGetAverageMasked(pixt, NULL, 0, 0, 1, L_MEAN_ABSVAL, pdiff); - - /* Don't bother to plot if the images are the same */ - if (plottype && !same) { - L_INFO("Images differ: output plots will be generated\n", procName); - na = pixGetGrayHistogram(pixt, 1); - numaGetNonzeroRange(na, TINY, &first, &last); - nac = numaClipToInterval(na, 0, last); - snprintf(buf, sizeof(buf), "/tmp/lept/comp/compare_gray%d", index); - gplot = gplotCreate(buf, plottype, - "Pixel Difference Histogram", "diff val", - "number of pixels"); - gplotAddPlot(gplot, NULL, nac, GPLOT_LINES, "gray"); - gplotMakeOutput(gplot); - gplotDestroy(&gplot); - snprintf(buf, sizeof(buf), "/tmp/lept/comp/compare_gray%d.png", - index++); - l_fileDisplay(buf, 100, 100, 1.0); - numaDestroy(&na); - numaDestroy(&nac); - } - - if (ppixdiff) - *ppixdiff = pixCopy(NULL, pixt); - - if (prmsdiff) { - if (comptype == L_COMPARE_SUBTRACT) { /* wrong type for rms diff */ - pixDestroy(&pixt); - pixt = pixAbsDifference(pix1, pix2); - } - pixGetAverageMasked(pixt, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, prmsdiff); - } - - pixDestroy(&pixt); - return 0; -} - - -/*! - * \brief pixCompareRGB() - * - * \param[in] pix1 32 bpp rgb - * \param[in] pix2 32 bpp rgb - * \param[in] comptype L_COMPARE_SUBTRACT, L_COMPARE_ABS_DIFF - * \param[in] plottype gplot plot output type, or 0 for no plot - * \param[out] psame [optional] 1 if pixel values are identical - * \param[out] pdiff [optional] average difference - * \param[out] prmsdiff [optional] rms of difference - * \param[out] ppixdiff [optional] pix of difference - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) See pixCompareGrayOrRGB() for details. - * (2) Note: setting %plottype > 0 can result in writing named - * output files. - *- */ -l_ok -pixCompareRGB(PIX *pix1, - PIX *pix2, - l_int32 comptype, - l_int32 plottype, - l_int32 *psame, - l_float32 *pdiff, - l_float32 *prmsdiff, - PIX **ppixdiff) -{ -char buf[64]; -static l_int32 index = 0; -l_int32 rsame, gsame, bsame, same, first, rlast, glast, blast, last; -l_float32 rdiff, gdiff, bdiff; -GPLOT *gplot; -NUMA *nar, *nag, *nab, *narc, *nagc, *nabc; -PIX *pixr1, *pixr2, *pixg1, *pixg2, *pixb1, *pixb2; -PIX *pixr, *pixg, *pixb; - - PROCNAME("pixCompareRGB"); - - if (psame) *psame = 0; - if (pdiff) *pdiff = 0.0; - if (prmsdiff) *prmsdiff = 0.0; - if (ppixdiff) *ppixdiff = NULL; - if (!pix1 || pixGetDepth(pix1) != 32) - return ERROR_INT("pix1 not defined or not 32 bpp", procName, 1); - if (!pix2 || pixGetDepth(pix2) != 32) - return ERROR_INT("pix2 not defined or not ew bpp", procName, 1); - if (comptype != L_COMPARE_SUBTRACT && comptype != L_COMPARE_ABS_DIFF) - return ERROR_INT("invalid comptype", procName, 1); - if (plottype < 0 || plottype >= NUM_GPLOT_OUTPUTS) - return ERROR_INT("invalid plottype", procName, 1); - - lept_mkdir("lept/comp"); - - pixr1 = pixGetRGBComponent(pix1, COLOR_RED); - pixr2 = pixGetRGBComponent(pix2, COLOR_RED); - pixg1 = pixGetRGBComponent(pix1, COLOR_GREEN); - pixg2 = pixGetRGBComponent(pix2, COLOR_GREEN); - pixb1 = pixGetRGBComponent(pix1, COLOR_BLUE); - pixb2 = pixGetRGBComponent(pix2, COLOR_BLUE); - if (comptype == L_COMPARE_SUBTRACT) { - pixr = pixSubtractGray(NULL, pixr1, pixr2); - pixg = pixSubtractGray(NULL, pixg1, pixg2); - pixb = pixSubtractGray(NULL, pixb1, pixb2); - } else { /* comptype == L_COMPARE_ABS_DIFF) */ - pixr = pixAbsDifference(pixr1, pixr2); - pixg = pixAbsDifference(pixg1, pixg2); - pixb = pixAbsDifference(pixb1, pixb2); - } - - pixZero(pixr, &rsame); - pixZero(pixg, &gsame); - pixZero(pixb, &bsame); - same = rsame && gsame && bsame; - if (same) - L_INFO("Images are pixel-wise identical\n", procName); - if (psame) *psame = same; - - if (pdiff) { - pixGetAverageMasked(pixr, NULL, 0, 0, 1, L_MEAN_ABSVAL, &rdiff); - pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_MEAN_ABSVAL, &gdiff); - pixGetAverageMasked(pixb, NULL, 0, 0, 1, L_MEAN_ABSVAL, &bdiff); - *pdiff = (rdiff + gdiff + bdiff) / 3.0; - } - - /* Don't bother to plot if the images are the same */ - if (plottype && !same) { - L_INFO("Images differ: output plots will be generated\n", procName); - nar = pixGetGrayHistogram(pixr, 1); - nag = pixGetGrayHistogram(pixg, 1); - nab = pixGetGrayHistogram(pixb, 1); - numaGetNonzeroRange(nar, TINY, &first, &rlast); - numaGetNonzeroRange(nag, TINY, &first, &glast); - numaGetNonzeroRange(nab, TINY, &first, &blast); - last = L_MAX(rlast, glast); - last = L_MAX(last, blast); - narc = numaClipToInterval(nar, 0, last); - nagc = numaClipToInterval(nag, 0, last); - nabc = numaClipToInterval(nab, 0, last); - snprintf(buf, sizeof(buf), "/tmp/lept/comp/compare_rgb%d", index); - gplot = gplotCreate(buf, plottype, - "Pixel Difference Histogram", "diff val", - "number of pixels"); - gplotAddPlot(gplot, NULL, narc, GPLOT_LINES, "red"); - gplotAddPlot(gplot, NULL, nagc, GPLOT_LINES, "green"); - gplotAddPlot(gplot, NULL, nabc, GPLOT_LINES, "blue"); - gplotMakeOutput(gplot); - gplotDestroy(&gplot); - snprintf(buf, sizeof(buf), "/tmp/lept/comp/compare_rgb%d.png", - index++); - l_fileDisplay(buf, 100, 100, 1.0); - numaDestroy(&nar); - numaDestroy(&nag); - numaDestroy(&nab); - numaDestroy(&narc); - numaDestroy(&nagc); - numaDestroy(&nabc); - } - - if (ppixdiff) - *ppixdiff = pixCreateRGBImage(pixr, pixg, pixb); - - if (prmsdiff) { - if (comptype == L_COMPARE_SUBTRACT) { - pixDestroy(&pixr); - pixDestroy(&pixg); - pixDestroy(&pixb); - pixr = pixAbsDifference(pixr1, pixr2); - pixg = pixAbsDifference(pixg1, pixg2); - pixb = pixAbsDifference(pixb1, pixb2); - } - pixGetAverageMasked(pixr, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, &rdiff); - pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, &gdiff); - pixGetAverageMasked(pixb, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, &bdiff); - *prmsdiff = (rdiff + gdiff + bdiff) / 3.0; - } - - pixDestroy(&pixr1); - pixDestroy(&pixr2); - pixDestroy(&pixg1); - pixDestroy(&pixg2); - pixDestroy(&pixb1); - pixDestroy(&pixb2); - pixDestroy(&pixr); - pixDestroy(&pixg); - pixDestroy(&pixb); - return 0; -} - - -/*! - * \brief pixCompareTiled() - * - * \param[in] pix1 8 bpp or 32 bpp rgb - * \param[in] pix2 8 bpp 32 bpp rgb - * \param[in] sx, sy tile size; must be > 1 in each dimension - * \param[in] type L_MEAN_ABSVAL or L_ROOT_MEAN_SQUARE - * \param[out] ppixdiff pix of difference - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) With L_MEAN_ABSVAL, we compute for each tile the - * average abs value of the pixel component difference between - * the two (aligned) images. With L_ROOT_MEAN_SQUARE, we - * compute instead the rms difference over all components. - * (2) The two input pix must be the same depth. Comparison is made - * using UL corner alignment. - * (3) For 32 bpp, the distance between corresponding tiles - * is found by averaging the measured difference over all three - * components of each pixel in the tile. - * (4) The result, pixdiff, contains one pixel for each source tile. - *- */ -l_ok -pixCompareTiled(PIX *pix1, - PIX *pix2, - l_int32 sx, - l_int32 sy, - l_int32 type, - PIX **ppixdiff) -{ -l_int32 d1, d2, w, h; -PIX *pixt, *pixr, *pixg, *pixb; -PIX *pixrdiff, *pixgdiff, *pixbdiff; -PIXACC *pixacc; - - PROCNAME("pixCompareTiled"); - - if (!ppixdiff) - return ERROR_INT("&pixdiff not defined", procName, 1); - *ppixdiff = NULL; - if (!pix1) - return ERROR_INT("pix1 not defined", procName, 1); - if (!pix2) - return ERROR_INT("pix2 not defined", procName, 1); - d1 = pixGetDepth(pix1); - d2 = pixGetDepth(pix2); - if (d1 != d2) - return ERROR_INT("depths not equal", procName, 1); - if (d1 != 8 && d1 != 32) - return ERROR_INT("pix1 not 8 or 32 bpp", procName, 1); - if (d2 != 8 && d2 != 32) - return ERROR_INT("pix2 not 8 or 32 bpp", procName, 1); - if (sx < 2 || sy < 2) - return ERROR_INT("sx and sy not both > 1", procName, 1); - if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE) - return ERROR_INT("invalid type", procName, 1); - - pixt = pixAbsDifference(pix1, pix2); - if (d1 == 8) { - *ppixdiff = pixGetAverageTiled(pixt, sx, sy, type); - } else { /* d1 == 32 */ - pixr = pixGetRGBComponent(pixt, COLOR_RED); - pixg = pixGetRGBComponent(pixt, COLOR_GREEN); - pixb = pixGetRGBComponent(pixt, COLOR_BLUE); - pixrdiff = pixGetAverageTiled(pixr, sx, sy, type); - pixgdiff = pixGetAverageTiled(pixg, sx, sy, type); - pixbdiff = pixGetAverageTiled(pixb, sx, sy, type); - pixGetDimensions(pixrdiff, &w, &h, NULL); - pixacc = pixaccCreate(w, h, 0); - pixaccAdd(pixacc, pixrdiff); - pixaccAdd(pixacc, pixgdiff); - pixaccAdd(pixacc, pixbdiff); - pixaccMultConst(pixacc, 1. / 3.); - *ppixdiff = pixaccFinal(pixacc, 8); - pixDestroy(&pixr); - pixDestroy(&pixg); - pixDestroy(&pixb); - pixDestroy(&pixrdiff); - pixDestroy(&pixgdiff); - pixDestroy(&pixbdiff); - pixaccDestroy(&pixacc); - } - pixDestroy(&pixt); - return 0; -} - - -/*------------------------------------------------------------------* - * Other measures of the difference of two images * - *------------------------------------------------------------------*/ -/*! - * \brief pixCompareRankDifference() - * - * \param[in] pix1 8 bpp gray or 32 bpp rgb, or colormapped - * \param[in] pix2 8 bpp gray or 32 bpp rgb, or colormapped - * \param[in] factor subsampling factor; use 0 or 1 for no subsampling - * \return narank numa of rank difference, or NULL on error - * - *
- * Notes: - * (1) This answers the question: if the pixel values in each - * component are compared by absolute difference, for - * any value of difference, what is the fraction of - * pixel pairs that have a difference of this magnitude - * or greater. For a difference of 0, the fraction is 1.0. - * In this sense, it is a mapping from pixel difference to - * rank order of difference. - * (2) The two images are aligned at the UL corner, and do not - * need to be the same size. If they are not the same size, - * the comparison will be made over overlapping pixels. - * (3) If there is a colormap, it is removed and the result - * is either gray or RGB depending on the colormap. - * (4) If RGB, pixel differences for each component are aggregated - * into a single histogram. - *- */ -NUMA * -pixCompareRankDifference(PIX *pix1, - PIX *pix2, - l_int32 factor) -{ -l_int32 i; -l_float32 *array1, *array2; -NUMA *nah, *nan, *nad; - - PROCNAME("pixCompareRankDifference"); - - if (!pix1) - return (NUMA *)ERROR_PTR("pix1 not defined", procName, NULL); - if (!pix2) - return (NUMA *)ERROR_PTR("pix2 not defined", procName, NULL); - - if ((nah = pixGetDifferenceHistogram(pix1, pix2, factor)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - - nan = numaNormalizeHistogram(nah, 1.0); - array1 = numaGetFArray(nan, L_NOCOPY); - - nad = numaCreate(256); - numaSetCount(nad, 256); /* all initialized to 0.0 */ - array2 = numaGetFArray(nad, L_NOCOPY); - - /* Do rank accumulation on normalized histo of diffs */ - array2[0] = 1.0; - for (i = 1; i < 256; i++) - array2[i] = array2[i - 1] - array1[i - 1]; - - numaDestroy(&nah); - numaDestroy(&nan); - return nad; -} - - -/*! - * \brief pixTestForSimilarity() - * - * \param[in] pix1 8 bpp gray or 32 bpp rgb, or colormapped - * \param[in] pix2 8 bpp gray or 32 bpp rgb, or colormapped - * \param[in] factor subsampling factor; use 0 or 1 for no subsampling - * \param[in] mindiff minimum pixel difference to be counted; > 0 - * \param[in] maxfract maximum fraction of pixels allowed to have - * diff greater than or equal to mindiff - * \param[in] maxave maximum average difference of pixels allowed for - * pixels with diff greater than or equal to - * mindiff, after subtracting mindiff - * \param[out] psimilar 1 if similar, 0 otherwise - * \param[in] details use 1 to give normalized histogram and other data - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This takes 2 pix that are the same size and determines using - * 3 input parameters if they are "similar". The first parameter - * %mindiff establishes a criterion of pixel-to-pixel similarity: - * two pixels are not similar if their difference in value is - * at least mindiff. Then %maxfract and %maxave are thresholds - * on the number and distribution of dissimilar pixels - * allowed for the two pix to be similar. If the pix are - * to be similar, neither threshold can be exceeded. - * (2) In setting the %maxfract and %maxave thresholds, you have - * these options: - * (a) Base the comparison only on %maxfract. Then set - * %maxave = 0.0 or 256.0. (If 0, we always ignore it.) - * (b) Base the comparison only on %maxave. Then set - * %maxfract = 1.0. - * (c) Base the comparison on both thresholds. - * (3) Example of values that can be expected at mindiff = 15 when - * comparing lossless png encoding with jpeg encoding, q=75: - * (smoothish bg) fractdiff = 0.01, avediff = 2.5 - * (natural scene) fractdiff = 0.13, avediff = 3.5 - * To identify these images as 'similar', select maxfract - * and maxave to be upper bounds of what you expect. - * (4) See pixGetDifferenceStats() for a discussion of why we subtract - * mindiff from the computed average diff of the nonsimilar pixels - * to get the 'avediff' returned by that function. - * (5) If there is a colormap, it is removed and the result - * is either gray or RGB depending on the colormap. - * (6) If RGB, the maximum difference between pixel components is - * saved in the histogram. - *- */ -l_ok -pixTestForSimilarity(PIX *pix1, - PIX *pix2, - l_int32 factor, - l_int32 mindiff, - l_float32 maxfract, - l_float32 maxave, - l_int32 *psimilar, - l_int32 details) -{ -l_float32 fractdiff, avediff; - - PROCNAME("pixTestForSimilarity"); - - if (!psimilar) - return ERROR_INT("&similar not defined", procName, 1); - *psimilar = 0; - if (!pix1) - return ERROR_INT("pix1 not defined", procName, 1); - if (!pix2) - return ERROR_INT("pix2 not defined", procName, 1); - if (pixSizesEqual(pix1, pix2) == 0) - return ERROR_INT("pix sizes not equal", procName, 1); - if (mindiff <= 0) - return ERROR_INT("mindiff must be > 0", procName, 1); - - if (pixGetDifferenceStats(pix1, pix2, factor, mindiff, - &fractdiff, &avediff, details)) - return ERROR_INT("diff stats not found", procName, 1); - - if (maxave <= 0.0) maxave = 256.0; - if (fractdiff <= maxfract && avediff <= maxave) - *psimilar = 1; - return 0; -} - - -/*! - * \brief pixGetDifferenceStats() - * - * \param[in] pix1 8 bpp gray or 32 bpp rgb, or colormapped - * \param[in] pix2 8 bpp gray or 32 bpp rgb, or colormapped - * \param[in] factor subsampling factor; use 0 or 1 for no subsampling - * \param[in] mindiff minimum pixel difference to be counted; > 0 - * \param[out] pfractdiff fraction of pixels with diff greater than or - * equal to mindiff - * \param[out] pavediff average difference of pixels with diff greater - * than or equal to mindiff, less mindiff - * \param[in] details use 1 to give normalized histogram and other data - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This takes a threshold %mindiff and describes the difference - * between two images in terms of two numbers: - * (a) the fraction of pixels, %fractdiff, whose difference - * equals or exceeds the threshold %mindiff, and - * (b) the average value %avediff of the difference in pixel value - * for the pixels in the set given by (a), after you subtract - * %mindiff. The reason for subtracting %mindiff is that - * you then get a useful measure for the rate of falloff - * of the distribution for larger differences. For example, - * if %mindiff = 10 and you find that %avediff = 2.5, it - * says that of the pixels with diff > 10, the average of - * their diffs is just mindiff + 2.5 = 12.5. This is a - * fast falloff in the histogram with increasing difference. - * (2) The two images are aligned at the UL corner, and do not - * need to be the same size. If they are not the same size, - * the comparison will be made over overlapping pixels. - * (3) If there is a colormap, it is removed and the result - * is either gray or RGB depending on the colormap. - * (4) If RGB, the maximum difference between pixel components is - * saved in the histogram. - * (5) Set %details == 1 to see the difference histogram and get - * an output that shows for each value of %mindiff, what are the - * minimum values required for fractdiff and avediff in order - * that the two pix will be considered similar. - *- */ -l_ok -pixGetDifferenceStats(PIX *pix1, - PIX *pix2, - l_int32 factor, - l_int32 mindiff, - l_float32 *pfractdiff, - l_float32 *pavediff, - l_int32 details) -{ -l_int32 i, first, last, diff; -l_float32 fract, ave; -l_float32 *array; -NUMA *nah, *nan, *nac; - - PROCNAME("pixGetDifferenceStats"); - - if (pfractdiff) *pfractdiff = 0.0; - if (pavediff) *pavediff = 0.0; - if (!pfractdiff) - return ERROR_INT("&fractdiff not defined", procName, 1); - if (!pavediff) - return ERROR_INT("&avediff not defined", procName, 1); - if (!pix1) - return ERROR_INT("pix1 not defined", procName, 1); - if (!pix2) - return ERROR_INT("pix2 not defined", procName, 1); - if (mindiff <= 0) - return ERROR_INT("mindiff must be > 0", procName, 1); - - if ((nah = pixGetDifferenceHistogram(pix1, pix2, factor)) == NULL) - return ERROR_INT("na not made", procName, 1); - - if ((nan = numaNormalizeHistogram(nah, 1.0)) == NULL) { - numaDestroy(&nah); - return ERROR_INT("nan not made", procName, 1); - } - array = numaGetFArray(nan, L_NOCOPY); - - if (details) { - lept_mkdir("lept/comp"); - numaGetNonzeroRange(nan, 0.0, &first, &last); - nac = numaClipToInterval(nan, first, last); - gplotSimple1(nac, GPLOT_PNG, "/tmp/lept/comp/histo", - "Difference histogram"); - l_fileDisplay("/tmp/lept/comp/histo.png", 500, 0, 1.0); - lept_stderr("\nNonzero values in normalized histogram:"); - numaWriteStderr(nac); - numaDestroy(&nac); - lept_stderr(" Mindiff fractdiff avediff\n"); - lept_stderr(" -----------------------------------\n"); - for (diff = 1; diff < L_MIN(2 * mindiff, last); diff++) { - fract = 0.0; - ave = 0.0; - for (i = diff; i <= last; i++) { - fract += array[i]; - ave += (l_float32)i * array[i]; - } - ave = (fract == 0.0) ? 0.0 : ave / fract; - ave -= diff; - lept_stderr("%5d %7.4f %7.4f\n", - diff, fract, ave); - } - lept_stderr(" -----------------------------------\n"); - } - - fract = 0.0; - ave = 0.0; - for (i = mindiff; i < 256; i++) { - fract += array[i]; - ave += (l_float32)i * array[i]; - } - ave = (fract == 0.0) ? 0.0 : ave / fract; - ave -= mindiff; - - *pfractdiff = fract; - *pavediff = ave; - - numaDestroy(&nah); - numaDestroy(&nan); - return 0; -} - - -/*! - * \brief pixGetDifferenceHistogram() - * - * \param[in] pix1 8 bpp gray or 32 bpp rgb, or colormapped - * \param[in] pix2 8 bpp gray or 32 bpp rgb, or colormapped - * \param[in] factor subsampling factor; use 0 or 1 for no subsampling - * \return na Numa of histogram of differences, or NULL on error - * - *
- * Notes: - * (1) The two images are aligned at the UL corner, and do not - * need to be the same size. If they are not the same size, - * the comparison will be made over overlapping pixels. - * (2) If there is a colormap, it is removed and the result - * is either gray or RGB depending on the colormap. - * (3) If RGB, the maximum difference between pixel components is - * saved in the histogram. - *- */ -NUMA * -pixGetDifferenceHistogram(PIX *pix1, - PIX *pix2, - l_int32 factor) -{ -l_int32 w1, h1, d1, w2, h2, d2, w, h, wpl1, wpl2; -l_int32 i, j, val, val1, val2; -l_int32 rval1, rval2, gval1, gval2, bval1, bval2; -l_int32 rdiff, gdiff, bdiff, maxdiff; -l_uint32 *data1, *data2, *line1, *line2; -l_float32 *array; -NUMA *na; -PIX *pixt1, *pixt2; - - PROCNAME("pixGetDifferenceHistogram"); - - if (!pix1) - return (NUMA *)ERROR_PTR("pix1 not defined", procName, NULL); - if (!pix2) - return (NUMA *)ERROR_PTR("pix2 not defined", procName, NULL); - d1 = pixGetDepth(pix1); - d2 = pixGetDepth(pix2); - if (d1 == 16 || d2 == 16) - return (NUMA *)ERROR_PTR("d == 16 not supported", procName, NULL); - if (d1 < 8 && !pixGetColormap(pix1)) - return (NUMA *)ERROR_PTR("pix1 depth < 8 bpp and not cmapped", - procName, NULL); - if (d2 < 8 && !pixGetColormap(pix2)) - return (NUMA *)ERROR_PTR("pix2 depth < 8 bpp and not cmapped", - procName, NULL); - pixt1 = pixRemoveColormap(pix1, REMOVE_CMAP_BASED_ON_SRC); - pixt2 = pixRemoveColormap(pix2, REMOVE_CMAP_BASED_ON_SRC); - pixGetDimensions(pixt1, &w1, &h1, &d1); - pixGetDimensions(pixt2, &w2, &h2, &d2); - if (d1 != d2) { - pixDestroy(&pixt1); - pixDestroy(&pixt2); - return (NUMA *)ERROR_PTR("pix depths not equal", procName, NULL); - } - if (factor < 1) factor = 1; - - na = numaCreate(256); - numaSetCount(na, 256); /* all initialized to 0.0 */ - array = numaGetFArray(na, L_NOCOPY); - w = L_MIN(w1, w2); - h = L_MIN(h1, h2); - data1 = pixGetData(pixt1); - data2 = pixGetData(pixt2); - wpl1 = pixGetWpl(pixt1); - wpl2 = pixGetWpl(pixt2); - if (d1 == 8) { - for (i = 0; i < h; i += factor) { - line1 = data1 + i * wpl1; - line2 = data2 + i * wpl2; - for (j = 0; j < w; j += factor) { - val1 = GET_DATA_BYTE(line1, j); - val2 = GET_DATA_BYTE(line2, j); - val = L_ABS(val1 - val2); - array[val]++; - } - } - } else { /* d1 == 32 */ - for (i = 0; i < h; i += factor) { - line1 = data1 + i * wpl1; - line2 = data2 + i * wpl2; - for (j = 0; j < w; j += factor) { - extractRGBValues(line1[j], &rval1, &gval1, &bval1); - extractRGBValues(line2[j], &rval2, &gval2, &bval2); - rdiff = L_ABS(rval1 - rval2); - gdiff = L_ABS(gval1 - gval2); - bdiff = L_ABS(bval1 - bval2); - maxdiff = L_MAX(rdiff, gdiff); - maxdiff = L_MAX(maxdiff, bdiff); - array[maxdiff]++; - } - } - } - - pixDestroy(&pixt1); - pixDestroy(&pixt2); - return na; -} - - -/*! - * \brief pixGetPerceptualDiff() - * - * \param[in] pixs1 8 bpp gray or 32 bpp rgb, or colormapped - * \param[in] pixs2 8 bpp gray or 32 bpp rgb, or colormapped - * \param[in] sampling subsampling factor; use 0 or 1 for no subsampling - * \param[in] dilation size of grayscale or color Sel; odd - * \param[in] mindiff minimum pixel difference to be counted; > 0 - * \param[out] pfract fraction of pixels with diff greater than mindiff - * \param[out] ppixdiff1 [optional] showing difference (gray or color) - * \param[out] ppixdiff2 [optional] showing pixels of sufficient diff - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This takes 2 pix and determines, using 2 input parameters: - * * %dilation specifies the amount of grayscale or color - * dilation to apply to the images, to compensate for - * a small amount of misregistration. A typical number might - * be 5, which uses a 5x5 Sel. Grayscale dilation expands - * lighter pixels into darker pixel regions. - * * %mindiff determines the threshold on the difference in - * pixel values to be counted -- two pixels are not similar - * if their difference in value is at least %mindiff. For - * color pixels, we use the maximum component difference. - * (2) The pixelwise comparison is always done with the UL corners - * aligned. The sizes of pix1 and pix2 need not be the same, - * although in practice it can be useful to scale to the same size. - * (3) If there is a colormap, it is removed and the result - * is either gray or RGB depending on the colormap. - * (4) Two optional diff images can be retrieved (typ. for debugging): - * pixdiff1: the gray or color difference - * pixdiff2: thresholded to 1 bpp for pixels exceeding %mindiff - * (5) The returned value of fract can be compared to some threshold, - * which is application dependent. - * (6) This method is in analogy to the two-sided hausdorff transform, - * except here it is for d > 1. For d == 1 (see pixRankHaustest()), - * we verify that when one pix1 is dilated, it covers at least a - * given fraction of the pixels in pix2, and v.v.; in that - * case, the two pix are sufficiently similar. Here, we - * do an analogous thing: subtract the dilated pix1 from pix2 to - * get a 1-sided hausdorff-like transform. Then do it the - * other way. Take the component-wise max of the two results, - * and threshold to get the fraction of pixels with a difference - * below the threshold. - *- */ -l_ok -pixGetPerceptualDiff(PIX *pixs1, - PIX *pixs2, - l_int32 sampling, - l_int32 dilation, - l_int32 mindiff, - l_float32 *pfract, - PIX **ppixdiff1, - PIX **ppixdiff2) -{ -l_int32 d1, d2, w, h, count; -PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7, *pix8, *pix9; -PIX *pix10, *pix11; - - PROCNAME("pixGetPerceptualDiff"); - - if (ppixdiff1) *ppixdiff1 = NULL; - if (ppixdiff2) *ppixdiff2 = NULL; - if (!pfract) - return ERROR_INT("&fract not defined", procName, 1); - *pfract = 1.0; /* init to completely different */ - if ((dilation & 1) == 0) - return ERROR_INT("dilation must be odd", procName, 1); - if (!pixs1) - return ERROR_INT("pixs1 not defined", procName, 1); - if (!pixs2) - return ERROR_INT("pixs2 not defined", procName, 1); - d1 = pixGetDepth(pixs1); - d2 = pixGetDepth(pixs2); - if (!pixGetColormap(pixs1) && d1 < 8) - return ERROR_INT("pixs1 not cmapped or >=8 bpp", procName, 1); - if (!pixGetColormap(pixs2) && d2 < 8) - return ERROR_INT("pixs2 not cmapped or >=8 bpp", procName, 1); - - /* Integer downsample if requested */ - if (sampling > 1) { - pix1 = pixScaleByIntSampling(pixs1, sampling); - pix2 = pixScaleByIntSampling(pixs2, sampling); - } else { - pix1 = pixClone(pixs1); - pix2 = pixClone(pixs2); - } - - /* Remove colormaps */ - if (pixGetColormap(pix1)) { - pix3 = pixRemoveColormap(pix1, REMOVE_CMAP_BASED_ON_SRC); - d1 = pixGetDepth(pix3); - } else { - pix3 = pixClone(pix1); - } - if (pixGetColormap(pix2)) { - pix4 = pixRemoveColormap(pix2, REMOVE_CMAP_BASED_ON_SRC); - d2 = pixGetDepth(pix4); - } else { - pix4 = pixClone(pix2); - } - pixDestroy(&pix1); - pixDestroy(&pix2); - if (d1 != d2) { - pixDestroy(&pix3); - pixDestroy(&pix4); - return ERROR_INT("pix3 and pix4 depths not equal", procName, 1); - } - - /* In each direction, do a small dilation and subtract the dilated - * image from the other image to get a one-sided difference. - * Then take the max of the differences for each direction - * and clipping each component to 255 if necessary. Note that - * for RGB images, the dilations and max selection are done - * component-wise, and the conversion to grayscale also uses the - * maximum component. The resulting grayscale images are - * thresholded using %mindiff. */ - if (d1 == 8) { - pix5 = pixDilateGray(pix3, dilation, dilation); - pixCompareGray(pix4, pix5, L_COMPARE_SUBTRACT, 0, NULL, NULL, NULL, - &pix7); - pix6 = pixDilateGray(pix4, dilation, dilation); - pixCompareGray(pix3, pix6, L_COMPARE_SUBTRACT, 0, NULL, NULL, NULL, - &pix8); - pix9 = pixMinOrMax(NULL, pix7, pix8, L_CHOOSE_MAX); - pix10 = pixThresholdToBinary(pix9, mindiff); - pixInvert(pix10, pix10); - pixCountPixels(pix10, &count, NULL); - pixGetDimensions(pix10, &w, &h, NULL); - *pfract = (l_float32)count / (l_float32)(w * h); - pixDestroy(&pix5); - pixDestroy(&pix6); - pixDestroy(&pix7); - pixDestroy(&pix8); - if (ppixdiff1) - *ppixdiff1 = pix9; - else - pixDestroy(&pix9); - if (ppixdiff2) - *ppixdiff2 = pix10; - else - pixDestroy(&pix10); - } else { /* d1 == 32 */ - pix5 = pixColorMorph(pix3, L_MORPH_DILATE, dilation, dilation); - pixCompareRGB(pix4, pix5, L_COMPARE_SUBTRACT, 0, NULL, NULL, NULL, - &pix7); - pix6 = pixColorMorph(pix4, L_MORPH_DILATE, dilation, dilation); - pixCompareRGB(pix3, pix6, L_COMPARE_SUBTRACT, 0, NULL, NULL, NULL, - &pix8); - pix9 = pixMinOrMax(NULL, pix7, pix8, L_CHOOSE_MAX); - pix10 = pixConvertRGBToGrayMinMax(pix9, L_CHOOSE_MAX); - pix11 = pixThresholdToBinary(pix10, mindiff); - pixInvert(pix11, pix11); - pixCountPixels(pix11, &count, NULL); - pixGetDimensions(pix11, &w, &h, NULL); - *pfract = (l_float32)count / (l_float32)(w * h); - pixDestroy(&pix5); - pixDestroy(&pix6); - pixDestroy(&pix7); - pixDestroy(&pix8); - pixDestroy(&pix10); - if (ppixdiff1) - *ppixdiff1 = pix9; - else - pixDestroy(&pix9); - if (ppixdiff2) - *ppixdiff2 = pix11; - else - pixDestroy(&pix11); - - } - pixDestroy(&pix3); - pixDestroy(&pix4); - return 0; -} - - -/*! - * \brief pixGetPSNR() - * - * \param[in] pix1, pix2 8 or 32 bpp; no colormap - * \param[in] factor sampling factor; >= 1 - * \param[out] ppsnr power signal/noise ratio difference - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This computes the power S/N ratio, in dB, for the difference - * between two images. By convention, the power S/N - * for a grayscale image is ('log' == log base 10, - * and 'ln == log base e): - * PSNR = 10 * log((255/MSE)^2) - * = 4.3429 * ln((255/MSE)^2) - * = -4.3429 * ln((MSE/255)^2) - * where MSE is the mean squared error. - * Here are some examples: - * MSE PSNR - * --- ---- - * 10 28.1 - * 3 38.6 - * 1 48.1 - * 0.1 68.1 - * (2) If pix1 and pix2 have the same pixel values, the MSE = 0.0 - * and the PSNR is infinity. For that case, this returns - * PSNR = 1000, which corresponds to the very small MSE of - * about 10^(-48). - *- */ -l_ok -pixGetPSNR(PIX *pix1, - PIX *pix2, - l_int32 factor, - l_float32 *ppsnr) -{ -l_int32 same, i, j, w, h, d, wpl1, wpl2, v1, v2, r1, g1, b1, r2, g2, b2; -l_uint32 *data1, *data2, *line1, *line2; -l_float32 mse; /* mean squared error */ - - PROCNAME("pixGetPSNR"); - - if (!ppsnr) - return ERROR_INT("&psnr not defined", procName, 1); - *ppsnr = 0.0; - if (!pix1 || !pix2) - return ERROR_INT("empty input pix", procName, 1); - if (!pixSizesEqual(pix1, pix2)) - return ERROR_INT("pix sizes unequal", procName, 1); - if (pixGetColormap(pix1)) - return ERROR_INT("pix1 has colormap", procName, 1); - if (pixGetColormap(pix2)) - return ERROR_INT("pix2 has colormap", procName, 1); - pixGetDimensions(pix1, &w, &h, &d); - if (d != 8 && d != 32) - return ERROR_INT("pix not 8 or 32 bpp", procName, 1); - if (factor < 1) - return ERROR_INT("invalid sampling factor", procName, 1); - - pixEqual(pix1, pix2, &same); - if (same) { - *ppsnr = 1000.0; /* crazy big exponent */ - return 0; - } - - data1 = pixGetData(pix1); - data2 = pixGetData(pix2); - wpl1 = pixGetWpl(pix1); - wpl2 = pixGetWpl(pix2); - mse = 0.0; - if (d == 8) { - for (i = 0; i < h; i += factor) { - line1 = data1 + i * wpl1; - line2 = data2 + i * wpl2; - for (j = 0; j < w; j += factor) { - v1 = GET_DATA_BYTE(line1, j); - v2 = GET_DATA_BYTE(line2, j); - mse += (l_float32)(v1 - v2) * (v1 - v2); - } - } - } else { /* d == 32 */ - for (i = 0; i < h; i += factor) { - line1 = data1 + i * wpl1; - line2 = data2 + i * wpl2; - for (j = 0; j < w; j += factor) { - extractRGBValues(line1[j], &r1, &g1, &b1); - extractRGBValues(line2[j], &r2, &g2, &b2); - mse += ((l_float32)(r1 - r2) * (r1 - r2) + - (g1 - g2) * (g1 - g2) + - (b1 - b2) * (b1 - b2)) / 3.0; - } - } - } - mse = mse / ((l_float32)(w) * h); - - *ppsnr = -4.3429448 * log(mse / (255 * 255)); - return 0; -} - - -/*------------------------------------------------------------------* - * Comparison of photo regions by histogram * - *------------------------------------------------------------------*/ -/*! - * \brief pixaComparePhotoRegionsByHisto() - * - * \param[in] pixa any depth; colormap OK - * \param[in] minratio requiring sizes be compatible; < 1.0 - * \param[in] textthresh threshold for text/photo; use 0 for default - * \param[in] factor subsampling; >= 1 - * \param[in] n in range {1, ... 7}. n^2 is the maximum number - * of subregions for histograms; typ. n = 3. - * \param[in] simthresh threshold for similarity; use 0 for default - * \param[out] pnai array giving similarity class indices - * \param[out] pscores [optional] score matrix as 1-D array of size N^2 - * \param[out] ppixd [optional] pix of similarity classes - * \param[in] debug 1 to output histograms; 0 otherwise - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This function takes a pixa of cropped photo images and - * compares each one to the others for similarity. - * Each image is first tested to see if it is a photo that can - * be compared by tiled histograms. If so, it is padded to put - * the centroid in the center of the image, and the histograms - * are generated. The final step of comparing each histogram - * with all the others is very fast. - * (2) To make the histograms, each image is subdivided in a maximum - * of n^2 subimages. The parameter %n specifies the "side" of - * an n x n grid of such subimages. If the subimages have an - * aspect ratio larger than 2, the grid will change, again using n^2 - * as a maximum for the number of subimages. For example, - * if n == 3, but the image is 600 x 200 pixels, a 3x3 grid - * would have subimages of 200 x 67 pixels, which is more - * than 2:1, so we change to a 4x2 grid where each subimage - * has 150 x 100 pixels. - * (3) An initial filter gives %score = 0 if the ratio of widths - * and heights (smallest / largest) does not exceed a - * threshold %minratio. If set at 1.0, both images must be - * exactly the same size. A typical value for %minratio is 0.9. - * (4) The comparison score between two images is a value in [0.0 .. 1.0]. - * If the comparison score >= %simthresh, the images are placed in - * the same similarity class. Default value for %simthresh is 0.25. - * (5) An array %nai of similarity class indices for pix in the - * input pixa is returned. - * (6) There are two debugging options: - * * An optional 2D matrix of scores is returned as a 1D array. - * A visualization of this is written to a temp file. - * * An optional pix showing the similarity classes can be - * returned. Text in each input pix is reproduced. - * (7) See the notes in pixComparePhotoRegionsByHisto() for details - * on the implementation. - *- */ -l_ok -pixaComparePhotoRegionsByHisto(PIXA *pixa, - l_float32 minratio, - l_float32 textthresh, - l_int32 factor, - l_int32 n, - l_float32 simthresh, - NUMA **pnai, - l_float32 **pscores, - PIX **ppixd, - l_int32 debug) -{ -char *text; -l_int32 i, j, nim, w, h, w1, h1, w2, h2, ival, index, classid; -l_float32 score; -l_float32 *scores; -NUMA *nai, *naw, *nah; -NUMAA *naa; -NUMAA **n3a; /* array of naa */ -PIX *pix; - - PROCNAME("pixaComparePhotoRegionsByHisto"); - - if (pscores) *pscores = NULL; - if (ppixd) *ppixd = NULL; - if (!pnai) - return ERROR_INT("&na not defined", procName, 1); - *pnai = NULL; - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - if (minratio < 0.0 || minratio > 1.0) - return ERROR_INT("minratio not in [0.0 ... 1.0]", procName, 1); - if (textthresh <= 0.0) textthresh = 1.3; - if (factor < 1) - return ERROR_INT("subsampling factor must be >= 1", procName, 1); - if (n < 1 || n > 7) { - L_WARNING("n = %d is invalid; setting to 4\n", procName, n); - n = 4; - } - if (simthresh <= 0.0) simthresh = 0.25; - if (simthresh > 1.0) - return ERROR_INT("simthresh invalid; should be near 0.25", procName, 1); - - /* Prepare the histograms */ - nim = pixaGetCount(pixa); - if ((n3a = (NUMAA **)LEPT_CALLOC(nim, sizeof(NUMAA *))) == NULL) - return ERROR_INT("calloc fail for n3a", procName, 1); - naw = numaCreate(0); - nah = numaCreate(0); - for (i = 0; i < nim; i++) { - pix = pixaGetPix(pixa, i, L_CLONE); - text = pixGetText(pix); - pixSetResolution(pix, 150, 150); - index = (debug) ? i : 0; - pixGenPhotoHistos(pix, NULL, factor, textthresh, n, - &naa, &w, &h, index); - n3a[i] = naa; - numaAddNumber(naw, w); - numaAddNumber(nah, h); - if (naa) - lept_stderr("Image %s is photo\n", text); - else - lept_stderr("Image %s is NOT photo\n", text); - pixDestroy(&pix); - } - - /* Do the comparisons. We are making a set of classes, where - * all similar images are placed in the same class. There are - * 'nim' input images. The classes are labeled by 'classid' (all - * similar images get the same 'classid' value), and 'nai' maps - * the classid of the image in the input array to the classid - * of the similarity class. */ - if ((scores = - (l_float32 *)LEPT_CALLOC((size_t)nim * nim, sizeof(l_float32))) - == NULL) { - L_ERROR("calloc fail for scores\n", procName); - goto cleanup; - } - nai = numaMakeConstant(-1, nim); /* classid array */ - for (i = 0, classid = 0; i < nim; i++) { - scores[nim * i + i] = 1.0; - numaGetIValue(nai, i, &ival); - if (ival != -1) /* already set */ - continue; - numaSetValue(nai, i, classid); - if (n3a[i] == NULL) { /* not a photo */ - classid++; - continue; - } - numaGetIValue(naw, i, &w1); - numaGetIValue(nah, i, &h1); - for (j = i + 1; j < nim; j++) { - numaGetIValue(nai, j, &ival); - if (ival != -1) /* already set */ - continue; - if (n3a[j] == NULL) /* not a photo */ - continue; - numaGetIValue(naw, j, &w2); - numaGetIValue(nah, j, &h2); - compareTilesByHisto(n3a[i], n3a[j], minratio, w1, h1, w2, h2, - &score, NULL); - scores[nim * i + j] = score; - scores[nim * j + i] = score; /* the score array is symmetric */ -/* lept_stderr("score = %5.3f\n", score); */ - if (score > simthresh) { - numaSetValue(nai, j, classid); - lept_stderr( - "Setting %d similar to %d, in class %d; score %5.3f\n", - j, i, classid, score); - } - } - classid++; - } - *pnai = nai; - - /* Debug: optionally save and display the score array. - * All images that are photos are represented by a point on - * the diagonal. Other images in the same similarity class - * are on the same horizontal raster line to the right. - * The array has been symmetrized, so images in the same - * same similarity class also appear on the same column below. */ - if (pscores) { - l_int32 wpl, fact; - l_uint32 *line, *data; - PIX *pix2, *pix3; - pix2 = pixCreate(nim, nim, 8); - data = pixGetData(pix2); - wpl = pixGetWpl(pix2); - for (i = 0; i < nim; i++) { - line = data + i * wpl; - for (j = 0; j < nim; j++) { - SET_DATA_BYTE(line, j, - L_MIN(255, 4.0 * 255 * scores[nim * i + j])); - } - } - fact = L_MAX(2, 1000 / nim); - pix3 = pixExpandReplicate(pix2, fact); - lept_stderr("Writing to /tmp/lept/comp/scorearray.png\n"); - lept_mkdir("lept/comp"); - pixWrite("/tmp/lept/comp/scorearray.png", pix3, IFF_PNG); - pixDestroy(&pix2); - pixDestroy(&pix3); - *pscores = scores; - } else { - LEPT_FREE(scores); - } - - /* Debug: optionally display and save the image comparisons. - * Image similarity classes are displayed by column; similar - * images are displayed in the same column. */ - if (ppixd) - *ppixd = pixaDisplayTiledByIndex(pixa, nai, 200, 20, 2, 6, 0x0000ff00); - -cleanup: - numaDestroy(&naw); - numaDestroy(&nah); - for (i = 0; i < nim; i++) - numaaDestroy(&n3a[i]); - LEPT_FREE(n3a); - return 0; -} - - -/*! - * \brief pixComparePhotoRegionsByHisto() - * - * \param[in] pix1, pix2 any depth; colormap OK - * \param[in] box1, box2 [optional] photo regions from each; can be null - * \param[in] minratio requiring sizes be compatible; < 1.0 - * \param[in] factor subsampling factor; >= 1 - * \param[in] n in range {1, ... 7}. n^2 is the maximum number - * of subregions for histograms; typ. n = 3. - * \param[out] pscore similarity score of histograms - * \param[in] debugflag 1 for debug output; 0 for no debugging - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This function compares two grayscale photo regions. If a - * box is given, the region is clipped; otherwise assume - * the entire images are photo regions. This is done with a - * set of not more than n^2 spatially aligned histograms, which are - * aligned using the centroid of the inverse image. - * (2) The parameter %n specifies the "side" of an n x n grid - * of subimages. If the subimages have an aspect ratio larger - * than 2, the grid will change, using n^2 as a maximum for - * the number of subimages. For example, if n == 3, but the - * image is 600 x 200 pixels, a 3x3 grid would have subimages - * of 200 x 67 pixels, which is more than 2:1, so we change - * to a 4x2 grid where each subimage has 150 x 100 pixels. - * (3) An initial filter gives %score = 0 if the ratio of widths - * and heights (smallest / largest) does not exceed a - * threshold %minratio. This must be between 0.5 and 1.0. - * If set at 1.0, both images must be exactly the same size. - * A typical value for %minratio is 0.9. - * (4) Because this function should not be used on text or - * line graphics, which can give false positive results - * (i.e., high scores for different images), filter the images - * using pixGenPhotoHistos(), which returns tiled histograms - * only if an image is not text and comparison is expected - * to work with histograms. If either image fails the test, - * the comparison returns a score of 0.0. - * (5) The white value counts in the histograms are removed; they - * are typically pixels that were padded to achieve alignment. - * (6) For an efficient representation of the histogram, normalize - * using a multiplicative factor so that the number in the - * maximum bucket is 255. It then takes 256 bytes to store. - * (7) When comparing the histograms of two regions, use the - * Earth Mover distance (EMD), with the histograms normalized - * so that the sum over bins is the same. Further normalize - * by dividing by 255, so that the result is in [0.0 ... 1.0]. - * (8) Get a similarity score S = 1.0 - k * D, where - * k is a constant, say in the range 5-10 - * D = normalized EMD - * and for multiple tiles, take the Min(S) to be the final score. - * Using aligned tiles gives protection against accidental - * similarity of the overall grayscale histograms. - * A small number of aligned tiles works well. - * (9) With debug on, you get a pdf that shows, for each tile, - * the images, histograms and score. - *- */ -l_ok -pixComparePhotoRegionsByHisto(PIX *pix1, - PIX *pix2, - BOX *box1, - BOX *box2, - l_float32 minratio, - l_int32 factor, - l_int32 n, - l_float32 *pscore, - l_int32 debugflag) -{ -l_int32 w1, h1, w2, h2, w1c, h1c, w2c, h2c, debugindex; -l_float32 wratio, hratio; -NUMAA *naa1, *naa2; -PIX *pix3, *pix4; -PIXA *pixa; - - PROCNAME("pixComparePhotoRegionsByHisto"); - - if (!pscore) - return ERROR_INT("&score not defined", procName, 1); - *pscore = 0.0; - if (!pix1 || !pix2) - return ERROR_INT("pix1 and pix2 not both defined", procName, 1); - if (minratio < 0.5 || minratio > 1.0) - return ERROR_INT("minratio not in [0.5 ... 1.0]", procName, 1); - if (factor < 1) - return ERROR_INT("subsampling factor must be >= 1", procName, 1); - if (n < 1 || n > 7) { - L_WARNING("n = %d is invalid; setting to 4\n", procName, n); - n = 4; - } - - debugindex = 0; - if (debugflag) { - lept_mkdir("lept/comp"); - debugindex = 666; /* arbitrary number used for naming output */ - } - - /* Initial filter by size */ - if (box1) - boxGetGeometry(box1, NULL, NULL, &w1, &h1); - else - pixGetDimensions(pix1, &w1, &h1, NULL); - if (box2) - boxGetGeometry(box2, NULL, NULL, &w2, &h2); - else - pixGetDimensions(pix1, &w2, &h2, NULL); - wratio = (w1 < w2) ? (l_float32)w1 / (l_float32)w2 : - (l_float32)w2 / (l_float32)w1; - hratio = (h1 < h2) ? (l_float32)h1 / (l_float32)h2 : - (l_float32)h2 / (l_float32)h1; - if (wratio < minratio || hratio < minratio) - return 0; - - /* Initial crop, if necessary, and make histos */ - if (box1) - pix3 = pixClipRectangle(pix1, box1, NULL); - else - pix3 = pixClone(pix1); - pixGenPhotoHistos(pix3, NULL, factor, 0, n, &naa1, &w1c, &h1c, debugindex); - pixDestroy(&pix3); - if (!naa1) return 0; - if (box2) - pix4 = pixClipRectangle(pix2, box2, NULL); - else - pix4 = pixClone(pix2); - pixGenPhotoHistos(pix4, NULL, factor, 0, n, &naa2, &w2c, &h2c, debugindex); - pixDestroy(&pix4); - if (!naa2) return 0; - - /* Compare histograms */ - pixa = (debugflag) ? pixaCreate(0) : NULL; - compareTilesByHisto(naa1, naa2, minratio, w1c, h1c, w2c, h2c, pscore, pixa); - pixaDestroy(&pixa); - return 0; -} - - -/*! - * \brief pixGenPhotoHistos() - * - * \param[in] pixs depth > 1 bpp; colormap OK - * \param[in] box [optional] region to be selected; can be null - * \param[in] factor subsampling; >= 1 - * \param[in] thresh threshold for photo/text; use 0 for default - * \param[in] n in range {1, ... 7}. n^2 is the maximum number - * of subregions for histograms; typ. n = 3. - * \param[out] pnaa nx * ny 256-entry gray histograms - * \param[out] pw width of image used to make histograms - * \param[out] ph height of image used to make histograms - * \param[in] debugindex 0 for no debugging; positive integer otherwise - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This crops and converts to 8 bpp if necessary. It adds a - * minimal white boundary such that the centroid of the - * photo-inverted image is in the center. This allows - * automatic alignment with histograms of other image regions. - * (2) The parameter %n specifies the "side" of the n x n grid - * of subimages. If the subimages have an aspect ratio larger - * than 2, the grid will change, using n^2 as a maximum for - * the number of subimages. For example, if n == 3, but the - * image is 600 x 200 pixels, a 3x3 grid would have subimages - * of 200 x 67 pixels, which is more than 2:1, so we change - * to a 4x2 grid where each subimage has 150 x 100 pixels. - * (3) The white value in the histogram is removed, because of - * the padding. - * (4) Use 0 for conservative default (1.3) for thresh. - * (5) For an efficient representation of the histogram, normalize - * using a multiplicative factor so that the number in the - * maximum bucket is 255. It then takes 256 bytes to store. - * (6) With %debugindex > 0, this makes a pdf that shows, for each tile, - * the images and histograms. - *- */ -l_ok -pixGenPhotoHistos(PIX *pixs, - BOX *box, - l_int32 factor, - l_float32 thresh, - l_int32 n, - NUMAA **pnaa, - l_int32 *pw, - l_int32 *ph, - l_int32 debugindex) -{ -char buf[64]; -NUMAA *naa; -PIX *pix1, *pix2, *pix3, *pixm; -PIXA *pixa; - - PROCNAME("pixGenPhotoHistos"); - - if (pnaa) *pnaa = NULL; - if (pw) *pw = 0; - if (ph) *ph = 0; - if (!pnaa) - return ERROR_INT("&naa not defined", procName, 1); - if (!pw || !ph) - return ERROR_INT("&w and &h not both defined", procName, 1); - if (!pixs || pixGetDepth(pixs) == 1) - return ERROR_INT("pixs not defined or 1 bpp", procName, 1); - if (factor < 1) - return ERROR_INT("subsampling factor must be >= 1", procName, 1); - if (thresh <= 0.0) thresh = 1.3; /* default */ - if (n < 1 || n > 7) { - L_WARNING("n = %d is invalid; setting to 4\n", procName, n); - n = 4; - } - - pixa = NULL; - if (debugindex > 0) { - pixa = pixaCreate(0); - lept_mkdir("lept/comp"); - } - - /* Initial crop, if necessary */ - if (box) - pix1 = pixClipRectangle(pixs, box, NULL); - else - pix1 = pixClone(pixs); - - /* Convert to 8 bpp and pad to center the centroid */ - pix2 = pixConvertTo8(pix1, FALSE); - pix3 = pixPadToCenterCentroid(pix2, factor); - - /* Set to 255 all pixels above 230. Do this so that light gray - * pixels do not enter into the comparison. */ - pixm = pixThresholdToBinary(pix3, 230); - pixInvert(pixm, pixm); - pixSetMaskedGeneral(pix3, pixm, 255, 0, 0); - pixDestroy(&pixm); - - if (debugindex > 0) { - PIX *pix4, *pix5, *pix6, *pix7, *pix8; - PIXA *pixa2; - pix4 = pixConvertTo32(pix2); - pix5 = pixConvertTo32(pix3); - pix6 = pixScaleToSize(pix4, 400, 0); - pix7 = pixScaleToSize(pix5, 400, 0); - pixa2 = pixaCreate(2); - pixaAddPix(pixa2, pix6, L_INSERT); - pixaAddPix(pixa2, pix7, L_INSERT); - pix8 = pixaDisplayTiledInRows(pixa2, 32, 1000, 1.0, 0, 50, 3); - pixaAddPix(pixa, pix8, L_INSERT); - pixDestroy(&pix4); - pixDestroy(&pix5); - pixaDestroy(&pixa2); - } - pixDestroy(&pix1); - pixDestroy(&pix2); - - /* Test if this is a photoimage */ - pixDecideIfPhotoImage(pix3, factor, thresh, n, &naa, pixa); - if (naa) { - *pnaa = naa; - *pw = pixGetWidth(pix3); - *ph = pixGetHeight(pix3); - } - - if (pixa) { - snprintf(buf, sizeof(buf), "/tmp/lept/comp/tiledhistos.%d.pdf", - debugindex); - lept_stderr("Writing to %s\n", buf); - pixaConvertToPdf(pixa, 300, 1.0, L_FLATE_ENCODE, 0, NULL, buf); - pixaDestroy(&pixa); - } - - pixDestroy(&pix3); - return 0; -} - - -/*! - * \brief pixPadToCenterCentroid() - * - * \param[in] pixs any depth, colormap OK - * \param[in] factor subsampling for centroid; >= 1 - * \return pixd padded with white pixels, or NULL on error. - * - *
- * Notes: - * (1) This add minimum white padding to an 8 bpp pix, such that - * the centroid of the photometric inverse is in the center of - * the resulting image. Thus in computing the centroid, - * black pixels have weight 255, and white pixels have weight 0. - *- */ -PIX * -pixPadToCenterCentroid(PIX *pixs, - l_int32 factor) - -{ -l_float32 cx, cy; -l_int32 xs, ys, delx, dely, icx, icy, ws, hs, wd, hd; -PIX *pix1, *pixd; - - PROCNAME("pixPadToCenterCentroid"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (factor < 1) - return (PIX *)ERROR_PTR("invalid sampling factor", procName, NULL); - - pix1 = pixConvertTo8(pixs, FALSE); - pixCentroid8(pix1, factor, &cx, &cy); - icx = (l_int32)(cx + 0.5); - icy = (l_int32)(cy + 0.5); - pixGetDimensions(pix1, &ws, &hs, NULL); - delx = ws - 2 * icx; - dely = hs - 2 * icy; - xs = L_MAX(0, delx); - ys = L_MAX(0, dely); - wd = 2 * L_MAX(icx, ws - icx); - hd = 2 * L_MAX(icy, hs - icy); - pixd = pixCreate(wd, hd, 8); - pixSetAll(pixd); /* to white */ - pixCopyResolution(pixd, pixs); - pixRasterop(pixd, xs, ys, ws, hs, PIX_SRC, pix1, 0, 0); - pixDestroy(&pix1); - return pixd; -} - - -/*! - * \brief pixCentroid8() - * - * \param[in] pixs 8 bpp - * \param[in] factor subsampling factor; >= 1 - * \param[out] pcx x value of centroid - * \param[out] pcy y value of centroid - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This first does a photometric inversion (black = 255, white = 0). - * It then finds the centroid of the result. The inversion is - * done because white is usually background, so the centroid - * is computed based on the "foreground" gray pixels, and the - * darker the pixel, the more weight it is given. - *- */ -l_ok -pixCentroid8(PIX *pixs, - l_int32 factor, - l_float32 *pcx, - l_float32 *pcy) -{ -l_int32 i, j, w, h, wpl, val; -l_float32 sumx, sumy, sumv; -l_uint32 *data, *line; -PIX *pix1; - - PROCNAME("pixCentroid8"); - - if (pcx) *pcx = 0.0; - if (pcy) *pcy = 0.0; - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs undefined or not 8 bpp", procName, 1); - if (factor < 1) - return ERROR_INT("subsampling factor must be >= 1", procName, 1); - if (!pcx || !pcy) - return ERROR_INT("&cx and &cy not both defined", procName, 1); - - pix1 = pixInvert(NULL, pixs); - pixGetDimensions(pix1, &w, &h, NULL); - data = pixGetData(pix1); - wpl = pixGetWpl(pix1); - sumx = sumy = sumv = 0.0; - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(line, j); - sumx += val * j; - sumy += val * i; - sumv += val; - } - } - pixDestroy(&pix1); - - if (sumv == 0) { - L_INFO("input image is white\n", procName); - *pcx = (l_float32)(w) / 2; - *pcy = (l_float32)(h) / 2; - } else { - *pcx = sumx / sumv; - *pcy = sumy / sumv; - } - - return 0; -} - - -/*! - * \brief pixDecideIfPhotoImage() - * - * \param[in] pix 8 bpp, centroid in center - * \param[in] factor subsampling for histograms; >= 1 - * \param[in] thresh threshold for photo/text; use 0 for default - * \param[in] n in range {1, ... 7}. n^2 is the maximum number - * of subregions for histograms; typ. n = 3. - * \param[out] pnaa array of normalized histograms - * \param[in] pixadebug [optional] use only for debug output - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The input image must be 8 bpp (no colormap), and padded with - * white pixels so the centroid of photo-inverted pixels is at - * the center of the image. - * (2) The parameter %n specifies the "side" of the n x n grid - * of subimages. If the subimages have an aspect ratio larger - * than 2, the grid will change, using n^2 as a maximum for - * the number of subimages. For example, if n == 3, but the - * image is 600 x 200 pixels, a 3x3 grid would have subimages - * of 200 x 67 pixels, which is more than 2:1, so we change - * to a 4x2 grid where each subimage has 150 x 100 pixels. - * (3) If the pix is not almost certainly a photoimage, the returned - * histograms (%naa) are null. - * (4) If histograms are generated, the white (255) count is set - * to 0. This removes all pixels values above 230, including - * white padding from the centroid matching operation, from - * consideration. The resulting histograms are then normalized - * so the maximum count is 255. - * (5) Default for %thresh is 1.3; this seems sufficiently conservative. - * (6) Use %pixadebug == NULL unless debug output is requested. - *- */ -l_ok -pixDecideIfPhotoImage(PIX *pix, - l_int32 factor, - l_float32 thresh, - l_int32 n, - NUMAA **pnaa, - PIXA *pixadebug) -{ -char buf[64]; -l_int32 i, w, h, nx, ny, ngrids, istext, isphoto; -l_float32 maxval, sum1, sum2, ratio; -L_BMF *bmf; -NUMA *na1, *na2, *na3, *narv; -NUMAA *naa; -PIX *pix1; -PIXA *pixa1, *pixa2, *pixa3; - - PROCNAME("pixDecideIfPhotoImage"); - - if (!pnaa) - return ERROR_INT("&naa not defined", procName, 1); - *pnaa = NULL; - if (!pix || pixGetDepth(pix) != 8 || pixGetColormap(pix)) - return ERROR_INT("pix undefined or invalid", procName, 1); - if (n < 1 || n > 7) { - L_WARNING("n = %d is invalid; setting to 4\n", procName, n); - n = 4; - } - if (thresh <= 0.0) thresh = 1.3; /* default */ - - /* Look for text lines */ - pixDecideIfText(pix, NULL, &istext, pixadebug); - if (istext) { - L_INFO("Image is text\n", procName); - return 0; - } - - /* Determine grid from n */ - pixGetDimensions(pix, &w, &h, NULL); - if (w == 0 || h == 0) - return ERROR_INT("invalid pix dimension", procName, 1); - findHistoGridDimensions(n, w, h, &nx, &ny, 1); - - /* Evaluate histograms in each tile */ - pixa1 = pixaSplitPix(pix, nx, ny, 0, 0); - ngrids = nx * ny; - bmf = (pixadebug) ? bmfCreate(NULL, 6) : NULL; - naa = numaaCreate(ngrids); - if (pixadebug) { - lept_rmdir("lept/compplot"); - lept_mkdir("lept/compplot"); - } - for (i = 0; i < ngrids; i++) { - pix1 = pixaGetPix(pixa1, i, L_CLONE); - - /* Get histograms, set white count to 0, normalize max to 255 */ - na1 = pixGetGrayHistogram(pix1, factor); - numaSetValue(na1, 255, 0); - na2 = numaWindowedMean(na1, 5); /* do some smoothing */ - numaGetMax(na2, &maxval, NULL); - na3 = numaTransform(na2, 0, 255.0 / maxval); - if (pixadebug) { - snprintf(buf, sizeof(buf), "/tmp/lept/compplot/plot.%d", i); - gplotSimple1(na3, GPLOT_PNG, buf, "Histos"); - } - - numaaAddNuma(naa, na3, L_INSERT); - numaDestroy(&na1); - numaDestroy(&na2); - pixDestroy(&pix1); - } - if (pixadebug) { - pix1 = pixaDisplayTiledInColumns(pixa1, nx, 1.0, 30, 2); - pixaAddPix(pixadebug, pix1, L_INSERT); - pixa2 = pixaReadFiles("/tmp/lept/compplot", ".png"); - pixa3 = pixaScale(pixa2, 0.4, 0.4); - pix1 = pixaDisplayTiledInColumns(pixa3, nx, 1.0, 30, 2); - pixaAddPix(pixadebug, pix1, L_INSERT); - pixaDestroy(&pixa2); - pixaDestroy(&pixa3); - } - - /* Compute the standard deviation between these histos to decide - * if the image is photo or something more like line art, - * which does not support good comparison by tiled histograms. */ - grayInterHistogramStats(naa, 5, NULL, NULL, NULL, &narv); - - /* For photos, the root variance has a larger weight of - * values in the range [50 ... 150] compared to [200 ... 230], - * than text or line art. For the latter, most of the variance - * between tiles is in the lightest parts of the image, well - * above 150. */ - numaGetSumOnInterval(narv, 50, 150, &sum1); - numaGetSumOnInterval(narv, 200, 230, &sum2); - if (sum2 == 0.0) { /* shouldn't happen */ - ratio = 0.001; /* anything very small for debug output */ - isphoto = 0; /* be conservative */ - } else { - ratio = sum1 / sum2; - isphoto = (ratio > thresh) ? 1 : 0; - } - if (pixadebug) { - if (isphoto) - L_INFO("ratio %f > %f; isphoto is true\n", - procName, ratio, thresh); - else - L_INFO("ratio %f < %f; isphoto is false\n", - procName, ratio, thresh); - } - if (isphoto) - *pnaa = naa; - else - numaaDestroy(&naa); - bmfDestroy(&bmf); - numaDestroy(&narv); - pixaDestroy(&pixa1); - return 0; -} - - -/*! - * \brief findHistoGridDimensions() - * - * \param[in] n max number of grid elements is n^2; typ. n = 3 - * \param[in] w width of image to be subdivided - * \param[in] h height of image to be subdivided - * \param[out] pnx number of grid elements in x direction - * \param[out] pny number of grid elements in y direction - * \param[in] debug 1 for debug output to stderr - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This determines the number of subdivisions to be used on - * the image in each direction. A histogram will be built - * for each subimage. - * (2) The parameter %n specifies the "side" of the n x n grid - * of subimages. If the subimages have an aspect ratio larger - * than 2, the grid will change, using n^2 as a maximum for - * the number of subimages. For example, if n == 3, but the - * image is 600 x 200 pixels, a 3x3 grid would have subimages - * of 200 x 67 pixels, which is more than 2:1, so we change - * to a 4x2 grid where each subimage has 150 x 100 pixels. - *- */ -static l_ok -findHistoGridDimensions(l_int32 n, - l_int32 w, - l_int32 h, - l_int32 *pnx, - l_int32 *pny, - l_int32 debug) -{ -l_int32 nx, ny, max; -l_float32 ratio; - - ratio = (l_float32)w / (l_float32)h; - max = n * n; - nx = ny = n; - while (nx > 1 && ny > 1) { - if (ratio > 2.0) { /* reduce ny */ - ny--; - nx = max / ny; - if (debug) - lept_stderr("nx = %d, ny = %d, ratio w/h = %4.2f\n", - nx, ny, ratio); - } else if (ratio < 0.5) { /* reduce nx */ - nx--; - ny = max / nx; - if (debug) - lept_stderr("nx = %d, ny = %d, ratio w/h = %4.2f\n", - nx, ny, ratio); - } else { /* we're ok */ - if (debug) - lept_stderr("nx = %d, ny = %d, ratio w/h = %4.2f\n", - nx, ny, ratio); - break; - } - ratio = (l_float32)(ny * w) / (l_float32)(nx * h); - } - *pnx = nx; - *pny = ny; - return 0; -} - - -/*! - * \brief compareTilesByHisto() - * - * \param[in] naa1, naa2 each is a set of 256 entry histograms - * \param[in] minratio requiring image sizes be compatible; < 1.0 - * \param[in] w1, h1, w2, h2 image sizes from which histograms were made - * \param[out] pscore similarity score of histograms - * \param[in] pixadebug [optional] use only for debug output - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) naa1 and naa2 must be generated using pixGenPhotoHistos(), - * using the same tile sizes. - * (2) The image dimensions must be similar. The score is 0.0 - * if the ratio of widths and heights (smallest / largest) - * exceeds a threshold %minratio, which must be between - * 0.5 and 1.0. If set at 1.0, both images must be exactly - * the same size. A typical value for %minratio is 0.9. - * (3) The input pixadebug is null unless debug output is requested. - *- */ -l_ok -compareTilesByHisto(NUMAA *naa1, - NUMAA *naa2, - l_float32 minratio, - l_int32 w1, - l_int32 h1, - l_int32 w2, - l_int32 h2, - l_float32 *pscore, - PIXA *pixadebug) -{ -char buf1[128], buf2[128]; -l_int32 i, n; -l_float32 wratio, hratio, score, minscore, dist; -L_BMF *bmf; -NUMA *na1, *na2, *nadist, *nascore; - - PROCNAME("compareTilesByHisto"); - - if (!pscore) - return ERROR_INT("&score not defined", procName, 1); - *pscore = 0.0; - if (!naa1 || !naa2) - return ERROR_INT("naa1 and naa2 not both defined", procName, 1); - - /* Filter for different sizes */ - wratio = (w1 < w2) ? (l_float32)w1 / (l_float32)w2 : - (l_float32)w2 / (l_float32)w1; - hratio = (h1 < h2) ? (l_float32)h1 / (l_float32)h2 : - (l_float32)h2 / (l_float32)h1; - if (wratio < minratio || hratio < minratio) { - if (pixadebug) - L_INFO("Sizes differ: wratio = %f, hratio = %f\n", - procName, wratio, hratio); - return 0; - } - n = numaaGetCount(naa1); - if (n != numaaGetCount(naa2)) { /* due to differing w/h ratio */ - L_INFO("naa1 and naa2 sizes are different\n", procName); - return 0; - } - - if (pixadebug) { - lept_rmdir("lept/comptile"); - lept_mkdir("lept/comptile"); - } - - - /* Evaluate histograms in each tile. Remove white before - * computing EMD, because there are may be a lot of white - * pixels due to padding, and we don't want to include them. - * This also makes the debug histo plots more informative. */ - minscore = 1.0; - nadist = numaCreate(n); - nascore = numaCreate(n); - bmf = (pixadebug) ? bmfCreate(NULL, 6) : NULL; - for (i = 0; i < n; i++) { - na1 = numaaGetNuma(naa1, i, L_CLONE); - na2 = numaaGetNuma(naa2, i, L_CLONE); - numaSetValue(na1, 255, 0.0); - numaSetValue(na2, 255, 0.0); - - /* To compare histograms, use the normalized earthmover distance. - * Further normalize to get the EM distance as a fraction of the - * maximum distance in the histogram (255). Finally, scale this - * up by 10.0, and subtract from 1.0 to get a similarity score. */ - numaEarthMoverDistance(na1, na2, &dist); - score = L_MAX(0.0, 1.0 - 10.0 * (dist / 255.)); - numaAddNumber(nadist, dist); - numaAddNumber(nascore, score); - minscore = L_MIN(minscore, score); - if (pixadebug) { - snprintf(buf1, sizeof(buf1), "/tmp/lept/comptile/plot.%d", i); - gplotSimple2(na1, na2, GPLOT_PNG, buf1, "Histos"); - } - numaDestroy(&na1); - numaDestroy(&na2); - } - *pscore = minscore; - - if (pixadebug) { - for (i = 0; i < n; i++) { - PIX *pix1, *pix2; - snprintf(buf1, sizeof(buf1), "/tmp/lept/comptile/plot.%d.png", i); - pix1 = pixRead(buf1); - numaGetFValue(nadist, i, &dist); - numaGetFValue(nascore, i, &score); - snprintf(buf2, sizeof(buf2), - "Image %d\ndist = %5.3f, score = %5.3f", i, dist, score); - pix2 = pixAddTextlines(pix1, bmf, buf2, 0x0000ff00, L_ADD_BELOW); - pixaAddPix(pixadebug, pix2, L_INSERT); - pixDestroy(&pix1); - } - lept_stderr("Writing to /tmp/lept/comptile/comparegray.pdf\n"); - pixaConvertToPdf(pixadebug, 300, 1.0, L_FLATE_ENCODE, 0, NULL, - "/tmp/lept/comptile/comparegray.pdf"); - numaWriteDebug("/tmp/lept/comptile/scores.na", nascore); - numaWriteDebug("/tmp/lept/comptile/dists.na", nadist); - } - - bmfDestroy(&bmf); - numaDestroy(&nadist); - numaDestroy(&nascore); - return 0; -} - - -/*! - * \brief pixCompareGrayByHisto() - * - * \param[in] pix1, pix2 any depth; colormap OK - * \param[in] box1, box2 [optional] region selected from each; can be null - * \param[in] minratio requiring sizes be compatible; < 1.0 - * \param[in] maxgray max value to keep in histo; >= 200, 255 to keep all - * \param[in] factor subsampling factor; >= 1 - * \param[in] n in range {1, ... 7}. n^2 is the maximum number - * of subregions for histograms; typ. n = 3. - * \param[out] pscore similarity score of histograms - * \param[in] debugflag 1 for debug output; 0 for no debugging - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This function compares two grayscale photo regions. It can - * do it with a single histogram from each region, or with a - * set of spatially aligned histograms. For both cases, - * align the regions using the centroid of the inverse image, - * and crop to the smallest of the two. - * (2) The parameter %n specifies the "side" of an n x n grid - * of subimages. If the subimages have an aspect ratio larger - * than 2, the grid will change, using n^2 as a maximum for - * the number of subimages. For example, if n == 3, but the - * image is 600 x 200 pixels, a 3x3 grid would have subimages - * of 200 x 67 pixels, which is more than 2:1, so we change - * to a 4x2 grid where each subimage has 150 x 100 pixels. - * (3) An initial filter gives %score = 0 if the ratio of widths - * and heights (smallest / largest) does not exceed a - * threshold %minratio. This must be between 0.5 and 1.0. - * If set at 1.0, both images must be exactly the same size. - * A typical value for %minratio is 0.9. - * (4) The lightest values in the histogram can be disregarded. - * Set %maxgray to the lightest value to be kept. For example, - * to eliminate white (255), set %maxgray = 254. %maxgray must - * be >= 200. - * (5) For an efficient representation of the histogram, normalize - * using a multiplicative factor so that the number in the - * maximum bucket is 255. It then takes 256 bytes to store. - * (6) When comparing the histograms of two regions: - * ~ Use %maxgray = 254 to ignore the white pixels, the number - * of which may be sensitive to the crop region if the pixels - * outside that region are white. - * ~ Use the Earth Mover distance (EMD), with the histograms - * normalized so that the sum over bins is the same. - * Further normalize by dividing by 255, so that the result - * is in [0.0 ... 1.0]. - * (7) Get a similarity score S = 1.0 - k * D, where - * k is a constant, say in the range 5-10 - * D = normalized EMD - * and for multiple tiles, take the Min(S) to be the final score. - * Using aligned tiles gives protection against accidental - * similarity of the overall grayscale histograms. - * A small number of aligned tiles works well. - * (8) With debug on, you get a pdf that shows, for each tile, - * the images, histograms and score. - * (9) When to use: - * (a) Because this function should not be used on text or - * line graphics, which can give false positive results - * (i.e., high scores for different images), the input - * images should be filtered. - * (b) To filter, first use pixDecideIfText(). If that function - * says the image is text, do not use it. If the function - * says it is not text, it still may be line graphics, and - * in that case, use: - * pixGetGrayHistogramTiled() - * grayInterHistogramStats() - * to determine whether it is photo or line graphics. - *- */ -l_ok -pixCompareGrayByHisto(PIX *pix1, - PIX *pix2, - BOX *box1, - BOX *box2, - l_float32 minratio, - l_int32 maxgray, - l_int32 factor, - l_int32 n, - l_float32 *pscore, - l_int32 debugflag) -{ -l_int32 w1, h1, w2, h2; -l_float32 wratio, hratio; -BOX *box3, *box4; -PIX *pix3, *pix4, *pix5, *pix6, *pix7, *pix8; -PIXA *pixa; - - PROCNAME("pixCompareGrayByHisto"); - - if (!pscore) - return ERROR_INT("&score not defined", procName, 1); - *pscore = 0.0; - if (!pix1 || !pix2) - return ERROR_INT("pix1 and pix2 not both defined", procName, 1); - if (minratio < 0.5 || minratio > 1.0) - return ERROR_INT("minratio not in [0.5 ... 1.0]", procName, 1); - if (maxgray < 200) - return ERROR_INT("invalid maxgray; should be >= 200", procName, 1); - maxgray = L_MIN(255, maxgray); - if (factor < 1) - return ERROR_INT("subsampling factor must be >= 1", procName, 1); - if (n < 1 || n > 7) { - L_WARNING("n = %d is invalid; setting to 4\n", procName, n); - n = 4; - } - - if (debugflag) - lept_mkdir("lept/comp"); - - /* Initial filter by size */ - if (box1) - boxGetGeometry(box1, NULL, NULL, &w1, &h1); - else - pixGetDimensions(pix1, &w1, &h1, NULL); - if (box2) - boxGetGeometry(box2, NULL, NULL, &w2, &h2); - else - pixGetDimensions(pix1, &w2, &h2, NULL); - wratio = (w1 < w2) ? (l_float32)w1 / (l_float32)w2 : - (l_float32)w2 / (l_float32)w1; - hratio = (h1 < h2) ? (l_float32)h1 / (l_float32)h2 : - (l_float32)h2 / (l_float32)h1; - if (wratio < minratio || hratio < minratio) - return 0; - - /* Initial crop, if necessary */ - if (box1) - pix3 = pixClipRectangle(pix1, box1, NULL); - else - pix3 = pixClone(pix1); - if (box2) - pix4 = pixClipRectangle(pix2, box2, NULL); - else - pix4 = pixClone(pix2); - - /* Convert to 8 bpp, align centroids and do maximal crop */ - pix5 = pixConvertTo8(pix3, FALSE); - pix6 = pixConvertTo8(pix4, FALSE); - pixCropAlignedToCentroid(pix5, pix6, factor, &box3, &box4); - pix7 = pixClipRectangle(pix5, box3, NULL); - pix8 = pixClipRectangle(pix6, box4, NULL); - pixa = (debugflag) ? pixaCreate(0) : NULL; - if (debugflag) { - PIX *pix9, *pix10, *pix11, *pix12, *pix13; - PIXA *pixa2; - pix9 = pixConvertTo32(pix5); - pix10 = pixConvertTo32(pix6); - pixRenderBoxArb(pix9, box3, 2, 255, 0, 0); - pixRenderBoxArb(pix10, box4, 2, 255, 0, 0); - pix11 = pixScaleToSize(pix9, 400, 0); - pix12 = pixScaleToSize(pix10, 400, 0); - pixa2 = pixaCreate(2); - pixaAddPix(pixa2, pix11, L_INSERT); - pixaAddPix(pixa2, pix12, L_INSERT); - pix13 = pixaDisplayTiledInRows(pixa2, 32, 1000, 1.0, 0, 50, 0); - pixaAddPix(pixa, pix13, L_INSERT); - pixDestroy(&pix9); - pixDestroy(&pix10); - pixaDestroy(&pixa2); - } - pixDestroy(&pix3); - pixDestroy(&pix4); - pixDestroy(&pix5); - pixDestroy(&pix6); - boxDestroy(&box3); - boxDestroy(&box4); - - /* Tile and compare histograms */ - pixCompareTilesByHisto(pix7, pix8, maxgray, factor, n, pscore, pixa); - pixaDestroy(&pixa); - pixDestroy(&pix7); - pixDestroy(&pix8); - return 0; -} - - -/*! - * \brief pixCompareTilesByHisto() - * - * \param[in] pix1, pix2 8 bpp - * \param[in] maxgray max value to keep in histo; 255 to keep all - * \param[in] factor subsampling factor; >= 1 - * \param[in] n see pixCompareGrayByHisto() - * \param[out] pscore similarity score of histograms - * \param[in] pixadebug [optional] use only for debug output - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This static function is only called from pixCompareGrayByHisto(). - * The input images have been converted to 8 bpp if necessary, - * aligned and cropped. - * (2) The input pixadebug is null unless debug output is requested. - * (3) See pixCompareGrayByHisto() for details. - *- */ -static l_ok -pixCompareTilesByHisto(PIX *pix1, - PIX *pix2, - l_int32 maxgray, - l_int32 factor, - l_int32 n, - l_float32 *pscore, - PIXA *pixadebug) -{ -char buf[64]; -l_int32 w, h, i, j, nx, ny, ngr; -l_float32 score, minscore, maxval1, maxval2, dist; -L_BMF *bmf; -NUMA *na1, *na2, *na3, *na4, *na5, *na6, *na7; -PIX *pix3, *pix4; -PIXA *pixa1, *pixa2; - - PROCNAME("pixCompareTilesByHisto"); - - if (!pscore) - return ERROR_INT("&score not defined", procName, 1); - *pscore = 0.0; - if (!pix1 || !pix2) - return ERROR_INT("pix1 and pix2 not both defined", procName, 1); - - /* Determine grid from n */ - pixGetDimensions(pix1, &w, &h, NULL); - findHistoGridDimensions(n, w, h, &nx, &ny, 1); - ngr = nx * ny; - - /* Evaluate histograms in each tile */ - pixa1 = pixaSplitPix(pix1, nx, ny, 0, 0); - pixa2 = pixaSplitPix(pix2, nx, ny, 0, 0); - na7 = (pixadebug) ? numaCreate(ngr) : NULL; - bmf = (pixadebug) ? bmfCreate(NULL, 6) : NULL; - minscore = 1.0; - for (i = 0; i < ngr; i++) { - pix3 = pixaGetPix(pixa1, i, L_CLONE); - pix4 = pixaGetPix(pixa2, i, L_CLONE); - - /* Get histograms, set white count to 0, normalize max to 255 */ - na1 = pixGetGrayHistogram(pix3, factor); - na2 = pixGetGrayHistogram(pix4, factor); - if (maxgray < 255) { - for (j = maxgray + 1; j <= 255; j++) { - numaSetValue(na1, j, 0); - numaSetValue(na2, j, 0); - } - } - na3 = numaWindowedMean(na1, 5); - na4 = numaWindowedMean(na2, 5); - numaGetMax(na3, &maxval1, NULL); - numaGetMax(na4, &maxval2, NULL); - na5 = numaTransform(na3, 0, 255.0 / maxval1); - na6 = numaTransform(na4, 0, 255.0 / maxval2); - if (pixadebug) { - gplotSimple2(na5, na6, GPLOT_PNG, "/tmp/lept/comp/plot1", "Histos"); - } - - /* To compare histograms, use the normalized earthmover distance. - * Further normalize to get the EM distance as a fraction of the - * maximum distance in the histogram (255). Finally, scale this - * up by 10.0, and subtract from 1.0 to get a similarity score. */ - numaEarthMoverDistance(na5, na6, &dist); - score = L_MAX(0.0, 1.0 - 8.0 * (dist / 255.)); - if (pixadebug) numaAddNumber(na7, score); - minscore = L_MIN(minscore, score); - if (pixadebug) { - PIX *pix5, *pix6, *pix7, *pix8, *pix9, *pix10; - PIXA *pixa3; - l_int32 w, h, wscale; - pixa3 = pixaCreate(3); - pixGetDimensions(pix3, &w, &h, NULL); - wscale = (w > h) ? 700 : 400; - pix5 = pixScaleToSize(pix3, wscale, 0); - pix6 = pixScaleToSize(pix4, wscale, 0); - pixaAddPix(pixa3, pix5, L_INSERT); - pixaAddPix(pixa3, pix6, L_INSERT); - pix7 = pixRead("/tmp/lept/comp/plot1.png"); - pix8 = pixScaleToSize(pix7, 700, 0); - snprintf(buf, sizeof(buf), "%5.3f", score); - pix9 = pixAddTextlines(pix8, bmf, buf, 0x0000ff00, L_ADD_RIGHT); - pixaAddPix(pixa3, pix9, L_INSERT); - pix10 = pixaDisplayTiledInRows(pixa3, 32, 1000, 1.0, 0, 50, 0); - pixaAddPix(pixadebug, pix10, L_INSERT); - pixDestroy(&pix7); - pixDestroy(&pix8); - pixaDestroy(&pixa3); - } - numaDestroy(&na1); - numaDestroy(&na2); - numaDestroy(&na3); - numaDestroy(&na4); - numaDestroy(&na5); - numaDestroy(&na6); - pixDestroy(&pix3); - pixDestroy(&pix4); - } - *pscore = minscore; - - if (pixadebug) { - pixaConvertToPdf(pixadebug, 300, 1.0, L_FLATE_ENCODE, 0, NULL, - "/tmp/lept/comp/comparegray.pdf"); - numaWriteDebug("/tmp/lept/comp/tilescores.na", na7); - } - - bmfDestroy(&bmf); - numaDestroy(&na7); - pixaDestroy(&pixa1); - pixaDestroy(&pixa2); - return 0; -} - - -/*! - * \brief pixCropAlignedToCentroid() - * - * \param[in] pix1, pix2 any depth; colormap OK - * \param[in] factor subsampling; >= 1 - * \param[out] pbox1 crop box for pix1 - * \param[out] pbox2 crop box for pix2 - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This finds the maximum crop boxes for two 8 bpp images when - * their centroids of their photometric inverses are aligned. - * Black pixels have weight 255; white pixels have weight 0. - *- */ -l_ok -pixCropAlignedToCentroid(PIX *pix1, - PIX *pix2, - l_int32 factor, - BOX **pbox1, - BOX **pbox2) -{ -l_float32 cx1, cy1, cx2, cy2; -l_int32 w1, h1, w2, h2, icx1, icy1, icx2, icy2; -l_int32 xm, xm1, xm2, xp, xp1, xp2, ym, ym1, ym2, yp, yp1, yp2; -PIX *pix3, *pix4; - - PROCNAME("pixCropAlignedToCentroid"); - - if (pbox1) *pbox1 = NULL; - if (pbox2) *pbox2 = NULL; - if (!pix1 || !pix2) - return ERROR_INT("pix1 and pix2 not both defined", procName, 1); - if (factor < 1) - return ERROR_INT("subsampling factor must be >= 1", procName, 1); - if (!pbox1 || !pbox2) - return ERROR_INT("&box1 and &box2 not both defined", procName, 1); - - pix3 = pixConvertTo8(pix1, FALSE); - pix4 = pixConvertTo8(pix2, FALSE); - pixCentroid8(pix3, factor, &cx1, &cy1); - pixCentroid8(pix4, factor, &cx2, &cy2); - pixGetDimensions(pix3, &w1, &h1, NULL); - pixGetDimensions(pix4, &w2, &h2, NULL); - pixDestroy(&pix3); - pixDestroy(&pix4); - - icx1 = (l_int32)(cx1 + 0.5); - icy1 = (l_int32)(cy1 + 0.5); - icx2 = (l_int32)(cx2 + 0.5); - icy2 = (l_int32)(cy2 + 0.5); - xm = L_MIN(icx1, icx2); - xm1 = icx1 - xm; - xm2 = icx2 - xm; - xp = L_MIN(w1 - icx1, w2 - icx2); /* one pixel beyond to the right */ - xp1 = icx1 + xp; - xp2 = icx2 + xp; - ym = L_MIN(icy1, icy2); - ym1 = icy1 - ym; - ym2 = icy2 - ym; - yp = L_MIN(h1 - icy1, h2 - icy2); /* one pixel below the bottom */ - yp1 = icy1 + yp; - yp2 = icy2 + yp; - *pbox1 = boxCreate(xm1, ym1, xp1 - xm1, yp1 - ym1); - *pbox2 = boxCreate(xm2, ym2, xp2 - xm2, yp2 - ym2); - return 0; -} - - -/*! - * \brief l_compressGrayHistograms() - * - * \param[in] naa set of 256-entry histograms - * \param[in] w, h size of image - * \param[out] psize size of byte array - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This first writes w and h to the byte array as 4 byte ints. - * (2) Then it normalizes each histogram to a max value of 255, - * and saves each value as a byte. If there are - * N histograms, the output bytearray has 8 + 256 * N bytes. - * (3) Further compression of the array with zlib yields only about - * a 25% decrease in size, so we don't bother. If size reduction - * were important, a lossy transform using a 1-dimensional DCT - * would be effective, because we don't care about the fine - * details of these histograms. - *- */ -l_uint8 * -l_compressGrayHistograms(NUMAA *naa, - l_int32 w, - l_int32 h, - size_t *psize) -{ -l_uint8 *bytea; -l_int32 i, j, n, nn, ival; -l_float32 maxval; -NUMA *na1, *na2; - - PROCNAME("l_compressGrayHistograms"); - - if (!psize) - return (l_uint8 *)ERROR_PTR("&size not defined", procName, NULL); - *psize = 0; - if (!naa) - return (l_uint8 *)ERROR_PTR("naa not defined", procName, NULL); - n = numaaGetCount(naa); - for (i = 0; i < n; i++) { - nn = numaaGetNumaCount(naa, i); - if (nn != 256) { - L_ERROR("%d numbers in numa[%d]\n", procName, nn, i); - return NULL; - } - } - - if ((bytea = (l_uint8 *)LEPT_CALLOC(8 + 256 * n, sizeof(l_uint8))) == NULL) - return (l_uint8 *)ERROR_PTR("bytea not made", procName, NULL); - *psize = 8 + 256 * n; - l_setDataFourBytes(bytea, 0, w); - l_setDataFourBytes(bytea, 1, h); - for (i = 0; i < n; i++) { - na1 = numaaGetNuma(naa, i, L_COPY); - numaGetMax(na1, &maxval, NULL); - na2 = numaTransform(na1, 0, 255.0 / maxval); - for (j = 0; j < 256; j++) { - numaGetIValue(na2, j, &ival); - bytea[8 + 256 * i + j] = ival; - } - numaDestroy(&na1); - numaDestroy(&na2); - } - - return bytea; -} - - -/*! - * \brief l_uncompressGrayHistograms() - * - * \param[in] bytea byte array of size 8 + 256 * N, N an integer - * \param[in] size size of byte array - * \param[out] pw width of the image that generated the histograms - * \param[out] ph height of the image - * \return numaa representing N histograms, each with 256 bins, - * or NULL on error. - * - *
- * Notes: - * (1) The first 8 bytes are read as two 32-bit ints. - * (2) Then this constructs a numaa representing some number of - * gray histograms that are normalized such that the max value - * in each histogram is 255. The data is stored as a byte - * array, with 256 bytes holding the data for each histogram. - * Each gray histogram was computed from a tile of a grayscale image. - *- */ -NUMAA * -l_uncompressGrayHistograms(l_uint8 *bytea, - size_t size, - l_int32 *pw, - l_int32 *ph) -{ -l_int32 i, j, n; -NUMA *na; -NUMAA *naa; - - PROCNAME("l_uncompressGrayHistograms"); - - if (pw) *pw = 0; - if (ph) *ph = 0; - if (!pw || !ph) - return (NUMAA *)ERROR_PTR("&w and &h not both defined", procName, NULL); - if (!bytea) - return (NUMAA *)ERROR_PTR("bytea not defined", procName, NULL); - n = (size - 8) / 256; - if ((size - 8) % 256 != 0) - return (NUMAA *)ERROR_PTR("bytea size is invalid", procName, NULL); - - *pw = l_getDataFourBytes(bytea, 0); - *ph = l_getDataFourBytes(bytea, 1); - naa = numaaCreate(n); - for (i = 0; i < n; i++) { - na = numaCreate(256); - for (j = 0; j < 256; j++) - numaAddNumber(na, bytea[8 + 256 * i + j]); - numaaAddNuma(naa, na, L_INSERT); - } - - return naa; -} - - -/*------------------------------------------------------------------* - * Translated images at the same resolution * - *------------------------------------------------------------------*/ -/*! - * \brief pixCompareWithTranslation() - * - * \param[in] pix1, pix2 any depth; colormap OK - * \param[in] thresh threshold for converting to 1 bpp - * \param[out] pdelx x translation on pix2 to align with pix1 - * \param[out] pdely y translation on pix2 to align with pix1 - * \param[out] pscore correlation score at best alignment - * \param[in] debugflag 1 for debug output; 0 for no debugging - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This does a coarse-to-fine search for best translational - * alignment of two images, measured by a scoring function - * that is the correlation between the fg pixels. - * (2) The threshold is used if the images aren't 1 bpp. - * (3) With debug on, you get a pdf that shows, as a grayscale - * image, the score as a function of shift from the initial - * estimate, for each of the four levels. The shift is 0 at - * the center of the image. - * (4) With debug on, you also get a pdf that shows the - * difference at the best alignment between the two images, - * at each of the four levels. The red and green pixels - * show locations where one image has a fg pixel and the - * other doesn't. The black pixels are where both images - * have fg pixels, and white pixels are where neither image - * has fg pixels. - *- */ -l_ok -pixCompareWithTranslation(PIX *pix1, - PIX *pix2, - l_int32 thresh, - l_int32 *pdelx, - l_int32 *pdely, - l_float32 *pscore, - l_int32 debugflag) -{ -l_uint8 *subtab; -l_int32 i, level, area1, area2, delx, dely; -l_int32 etransx, etransy, maxshift, dbint; -l_int32 *stab, *ctab; -l_float32 cx1, cx2, cy1, cy2, score; -PIX *pixb1, *pixb2, *pixt1, *pixt2, *pixt3, *pixt4; -PIXA *pixa1, *pixa2, *pixadb; - - PROCNAME("pixCompareWithTranslation"); - - if (pdelx) *pdelx = 0; - if (pdely) *pdely = 0; - if (pscore) *pscore = 0.0; - if (!pdelx || !pdely) - return ERROR_INT("&delx and &dely not defined", procName, 1); - if (!pscore) - return ERROR_INT("&score not defined", procName, 1); - if (!pix1) - return ERROR_INT("pix1 not defined", procName, 1); - if (!pix2) - return ERROR_INT("pix2 not defined", procName, 1); - - /* Make tables */ - subtab = makeSubsampleTab2x(); - stab = makePixelSumTab8(); - ctab = makePixelCentroidTab8(); - - /* Binarize each image */ - pixb1 = pixConvertTo1(pix1, thresh); - pixb2 = pixConvertTo1(pix2, thresh); - - /* Make a cascade of 2x reduced images for each, thresholding - * with level 2 (neutral), down to 8x reduction */ - pixa1 = pixaCreate(4); - pixa2 = pixaCreate(4); - if (debugflag) - pixadb = pixaCreate(4); - pixaAddPix(pixa1, pixb1, L_INSERT); - pixaAddPix(pixa2, pixb2, L_INSERT); - for (i = 0; i < 3; i++) { - pixt1 = pixReduceRankBinary2(pixb1, 2, subtab); - pixt2 = pixReduceRankBinary2(pixb2, 2, subtab); - pixaAddPix(pixa1, pixt1, L_INSERT); - pixaAddPix(pixa2, pixt2, L_INSERT); - pixb1 = pixt1; - pixb2 = pixt2; - } - - /* At the lowest level, use the centroids with a maxshift of 6 - * to search for the best alignment. Then at higher levels, - * use the result from the level below as the initial approximation - * for the alignment, and search with a maxshift of 2. */ - for (level = 3; level >= 0; level--) { - pixt1 = pixaGetPix(pixa1, level, L_CLONE); - pixt2 = pixaGetPix(pixa2, level, L_CLONE); - pixCountPixels(pixt1, &area1, stab); - pixCountPixels(pixt2, &area2, stab); - if (level == 3) { - pixCentroid(pixt1, ctab, stab, &cx1, &cy1); - pixCentroid(pixt2, ctab, stab, &cx2, &cy2); - etransx = lept_roundftoi(cx1 - cx2); - etransy = lept_roundftoi(cy1 - cy2); - maxshift = 6; - } else { - etransx = 2 * delx; - etransy = 2 * dely; - maxshift = 2; - } - dbint = (debugflag) ? level + 1 : 0; - pixBestCorrelation(pixt1, pixt2, area1, area2, etransx, etransy, - maxshift, stab, &delx, &dely, &score, dbint); - if (debugflag) { - lept_stderr("Level %d: delx = %d, dely = %d, score = %7.4f\n", - level, delx, dely, score); - pixRasteropIP(pixt2, delx, dely, L_BRING_IN_WHITE); - pixt3 = pixDisplayDiffBinary(pixt1, pixt2); - pixt4 = pixExpandReplicate(pixt3, 8 / (1 << (3 - level))); - pixaAddPix(pixadb, pixt4, L_INSERT); - pixDestroy(&pixt3); - } - pixDestroy(&pixt1); - pixDestroy(&pixt2); - } - - if (debugflag) { - pixaConvertToPdf(pixadb, 300, 1.0, L_FLATE_ENCODE, 0, NULL, - "/tmp/lept/comp/compare.pdf"); - convertFilesToPdf("/tmp/lept/comp", "correl_", 30, 1.0, L_FLATE_ENCODE, - 0, "Correlation scores at levels 1 through 5", - "/tmp/lept/comp/correl.pdf"); - pixaDestroy(&pixadb); - } - - *pdelx = delx; - *pdely = dely; - *pscore = score; - pixaDestroy(&pixa1); - pixaDestroy(&pixa2); - LEPT_FREE(subtab); - LEPT_FREE(stab); - LEPT_FREE(ctab); - return 0; -} - - -/*! - * \brief pixBestCorrelation() - * - * \param[in] pix1 1 bpp - * \param[in] pix2 1 bpp - * \param[in] area1 number of on pixels in pix1 - * \param[in] area2 number of on pixels in pix2 - * \param[in] etransx estimated x translation of pix2 to align with pix1 - * \param[in] etransy estimated y translation of pix2 to align with pix1 - * \param[in] maxshift max x and y shift of pix2, around the estimated - * alignment location, relative to pix1 - * \param[in] tab8 [optional] sum tab for ON pixels in byte; can be NULL - * \param[out] pdelx [optional] best x shift of pix2 relative to pix1 - * \param[out] pdely [optional] best y shift of pix2 relative to pix1 - * \param[out] pscore [optional] maximum score found; can be NULL - * \param[in] debugflag <= 0 to skip; positive to generate output. - * The integer is used to label the debug image. - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This maximizes the correlation score between two 1 bpp images, - * by starting with an estimate of the alignment - * (%etransx, %etransy) and computing the correlation around this. - * It optionally returns the shift (%delx, %dely) that maximizes - * the correlation score when pix2 is shifted by this amount - * relative to pix1. - * (2) Get the centroids of pix1 and pix2, using pixCentroid(), - * to compute (%etransx, %etransy). Get the areas using - * pixCountPixels(). - * (3) The centroid of pix2 is shifted with respect to the centroid - * of pix1 by all values between -maxshiftx and maxshiftx, - * and likewise for the y shifts. Therefore, the number of - * correlations computed is: - * (2 * maxshiftx + 1) * (2 * maxshifty + 1) - * Consequently, if pix1 and pix2 are large, you should do this - * in a coarse-to-fine sequence. See the use of this function - * in pixCompareWithTranslation(). - *- */ -l_ok -pixBestCorrelation(PIX *pix1, - PIX *pix2, - l_int32 area1, - l_int32 area2, - l_int32 etransx, - l_int32 etransy, - l_int32 maxshift, - l_int32 *tab8, - l_int32 *pdelx, - l_int32 *pdely, - l_float32 *pscore, - l_int32 debugflag) -{ -l_int32 shiftx, shifty, delx, dely; -l_int32 *tab; -l_float32 maxscore, score; -FPIX *fpix; -PIX *pix3, *pix4; - - PROCNAME("pixBestCorrelation"); - - if (pdelx) *pdelx = 0; - if (pdely) *pdely = 0; - if (pscore) *pscore = 0.0; - if (!pix1 || pixGetDepth(pix1) != 1) - return ERROR_INT("pix1 not defined or not 1 bpp", procName, 1); - if (!pix2 || pixGetDepth(pix2) != 1) - return ERROR_INT("pix2 not defined or not 1 bpp", procName, 1); - if (!area1 || !area2) - return ERROR_INT("areas must be > 0", procName, 1); - - if (debugflag > 0) - fpix = fpixCreate(2 * maxshift + 1, 2 * maxshift + 1); - - if (!tab8) - tab = makePixelSumTab8(); - else - tab = tab8; - - /* Search over a set of {shiftx, shifty} for the max */ - maxscore = 0; - delx = etransx; - dely = etransy; - for (shifty = -maxshift; shifty <= maxshift; shifty++) { - for (shiftx = -maxshift; shiftx <= maxshift; shiftx++) { - pixCorrelationScoreShifted(pix1, pix2, area1, area2, - etransx + shiftx, - etransy + shifty, tab, &score); - if (debugflag > 0) { - fpixSetPixel(fpix, maxshift + shiftx, maxshift + shifty, - 1000.0 * score); -/* lept_stderr("(sx, sy) = (%d, %d): score = %6.4f\n", - shiftx, shifty, score); */ - } - if (score > maxscore) { - maxscore = score; - delx = etransx + shiftx; - dely = etransy + shifty; - } - } - } - - if (debugflag > 0) { - lept_mkdir("lept/comp"); - char buf[128]; - pix3 = fpixDisplayMaxDynamicRange(fpix); - pix4 = pixExpandReplicate(pix3, 20); - snprintf(buf, sizeof(buf), "/tmp/lept/comp/correl_%d.png", - debugflag); - pixWrite(buf, pix4, IFF_PNG); - pixDestroy(&pix3); - pixDestroy(&pix4); - fpixDestroy(&fpix); - } - - if (pdelx) *pdelx = delx; - if (pdely) *pdely = dely; - if (pscore) *pscore = maxscore; - if (!tab8) LEPT_FREE(tab); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/conncomp.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/conncomp.c deleted file mode 100644 index 82cc0adf..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/conncomp.c +++ /dev/null @@ -1,1246 +0,0 @@ -/*====================================================================* - - 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 conncomp.c - *
- * - * Connected component counting and extraction, using Heckbert's - * stack-based filling algorithm. - * - * 4- and 8-connected components: counts, bounding boxes and images - * - * Top-level calls: - * BOXA *pixConnComp() - * BOXA *pixConnCompPixa() - * BOXA *pixConnCompBB() - * l_int32 pixCountConnComp() - * - * Identify the next c.c. to be erased: - * l_int32 nextOnPixelInRaster() - * static l_int32 nextOnPixelInRasterLow() - * - * Erase the c.c., saving the b.b.: - * BOX *pixSeedfillBB() - * BOX *pixSeedfill4BB() - * BOX *pixSeedfill8BB() - * - * Just erase the c.c.: - * l_int32 pixSeedfill() - * l_int32 pixSeedfill4() - * l_int32 pixSeedfill8() - * - * Static stack helper functions for single raster line seedfill: - * static void pushFillsegBB() - * static void pushFillseg() - * static void popFillseg() - * - * The basic method in pixConnCompBB() is very simple. We scan the - * image in raster order, looking for the next ON pixel. When it - * is found, we erase it and every pixel of the 4- or 8-connected - * component to which it belongs, using Heckbert's seedfill - * algorithm. As pixels are erased, we keep track of the - * minimum rectangle that encloses all erased pixels; after - * the connected component has been erased, we save its - * bounding box in an array of boxes. When all pixels in the - * image have been erased, we have an array that describes every - * 4- or 8-connected component in terms of its bounding box. - * - * pixConnCompPixa() is a slight variation on pixConnCompBB(), - * where we additionally save an array of images (in a Pixa) - * of each of the 4- or 8-connected components. This is done trivially - * by maintaining two temporary images. We erase a component from one, - * and use the bounding box to extract the pixels within the b.b. - * from each of the two images. An XOR between these subimages - * gives the erased component. Then we erase the component from the - * second image using the XOR again, with the extracted component - * placed on the second image at the location of the bounding box. - * Rasterop does all the work. At the end, we have an array - * of the 4- or 8-connected components, as well as an array of the - * bounding boxes that describe where they came from in the original image. - * - * If you just want the number of connected components, pixCountConnComp() - * is a bit faster than pixConnCompBB(), because it doesn't have to - * keep track of the bounding rectangles for each c.c. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This is the top-level call for getting bounding boxes or - * a pixa of the components, and it can be used instead - * of either pixConnCompBB() or pixConnCompPixa(), rsp. - *- */ -BOXA * -pixConnComp(PIX *pixs, - PIXA **ppixa, - l_int32 connectivity) -{ - - PROCNAME("pixConnComp"); - - if (ppixa) *ppixa = NULL; - if (!pixs || pixGetDepth(pixs) != 1) - return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (connectivity != 4 && connectivity != 8) - return (BOXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); - - if (!ppixa) - return pixConnCompBB(pixs, connectivity); - else - return pixConnCompPixa(pixs, ppixa, connectivity); -} - - -/*! - * \brief pixConnCompPixa() - * - * \param[in] pixs 1 bpp - * \param[out] ppixa pixa of each c.c. - * \param[in] connectivity 4 or 8 - * \return boxa, or NULL on error - * - *
- * Notes: - * (1) This finds bounding boxes of 4- or 8-connected components - * in a binary image, and saves images of each c.c - * in a pixa array. - * (2) It sets up 2 temporary pix, and for each c.c. that is - * located in raster order, it erases the c.c. from one pix, - * then uses the b.b. to extract the c.c. from the two pix using - * an XOR, and finally erases the c.c. from the second pix. - * (3) A clone of the returned boxa (where all boxes in the array - * are clones) is inserted into the pixa. - * (4) If the input is valid, this always returns a boxa and a pixa. - * If pixs is empty, the boxa and pixa will be empty. - *- */ -BOXA * -pixConnCompPixa(PIX *pixs, - PIXA **ppixa, - l_int32 connectivity) -{ -l_int32 h, iszero; -l_int32 x, y, xstart, ystart; -PIX *pix1, *pix2, *pix3, *pix4; -PIXA *pixa; -BOX *box; -BOXA *boxa; -L_STACK *stack, *auxstack; - - PROCNAME("pixConnCompPixa"); - - if (!ppixa) - return (BOXA *)ERROR_PTR("&pixa not defined", procName, NULL); - *ppixa = NULL; - if (!pixs || pixGetDepth(pixs) != 1) - return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (connectivity != 4 && connectivity != 8) - return (BOXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); - - pix1 = pix2 = pix3 = pix4 = NULL; - stack = NULL; - pixa = pixaCreate(0); - boxa = NULL; - *ppixa = pixa; - pixZero(pixs, &iszero); - if (iszero) - return boxaCreate(1); /* return empty boxa and empty pixa */ - - pixSetPadBits(pixs, 0); - pix1 = pixCopy(NULL, pixs); - pix2 = pixCopy(NULL, pixs); - if (!pix1 || !pix2) { - L_ERROR("pix1 or pix2 not made\n", procName); - pixaDestroy(ppixa); - goto cleanup; - } - - h = pixGetHeight(pixs); - if ((stack = lstackCreate(h)) == NULL) { - L_ERROR("stack not made\n", procName); - pixaDestroy(ppixa); - goto cleanup; - } - auxstack = lstackCreate(0); - stack->auxstack = auxstack; - boxa = boxaCreate(0); - - xstart = 0; - ystart = 0; - while (1) { - if (!nextOnPixelInRaster(pix1, xstart, ystart, &x, &y)) - break; - - if ((box = pixSeedfillBB(pix1, stack, x, y, connectivity)) == NULL) { - boxaDestroy(&boxa); - pixaDestroy(ppixa); - L_ERROR("box not made\n", procName); - goto cleanup; - } - boxaAddBox(boxa, box, L_INSERT); - - /* Save the c.c. and remove from pix2 as well */ - pix3 = pixClipRectangle(pix1, box, NULL); - pix4 = pixClipRectangle(pix2, box, NULL); - pixXor(pix3, pix3, pix4); - pixRasterop(pix2, box->x, box->y, box->w, box->h, PIX_SRC ^ PIX_DST, - pix3, 0, 0); - pixaAddPix(pixa, pix3, L_INSERT); - pixDestroy(&pix4); - - xstart = x; - ystart = y; - } - -#if DEBUG - pixCountPixels(pix1, &iszero, NULL); - lept_stderr("Number of remaining pixels = %d\n", iszero); - lept_mkdir("lept/cc"); - pixWriteDebug("/tmp/lept/cc/remain.png", pix1, IFF_PNG); -#endif /* DEBUG */ - - /* Remove old boxa of pixa and replace with a copy */ - boxaDestroy(&pixa->boxa); - pixa->boxa = boxaCopy(boxa, L_COPY); - *ppixa = pixa; - - /* Cleanup, freeing the fillsegs on each stack */ -cleanup: - lstackDestroy(&stack, TRUE); - pixDestroy(&pix1); - pixDestroy(&pix2); - return boxa; -} - - -/*! - * \brief pixConnCompBB() - * - * \param[in] pixs 1 bpp - * \param[in] connectivity 4 or 8 - * \return boxa, or NULL on error - * - *
- * Notes: - * (1) Finds bounding boxes of 4- or 8-connected components - * in a binary image. - * (2) This works on a copy of the input pix. The c.c. are located - * in raster order and erased one at a time. In the process, - * the b.b. is computed and saved. - *- */ -BOXA * -pixConnCompBB(PIX *pixs, - l_int32 connectivity) -{ -l_int32 h, iszero; -l_int32 x, y, xstart, ystart; -PIX *pix1; -BOX *box; -BOXA *boxa; -L_STACK *stack, *auxstack; - - PROCNAME("pixConnCompBB"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (connectivity != 4 && connectivity != 8) - return (BOXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); - - boxa = NULL; - pix1 = NULL; - stack = NULL; - pixZero(pixs, &iszero); - if (iszero) - return boxaCreate(1); /* return empty boxa */ - - pixSetPadBits(pixs, 0); - if ((pix1 = pixCopy(NULL, pixs)) == NULL) - return (BOXA *)ERROR_PTR("pix1 not made", procName, NULL); - - h = pixGetHeight(pixs); - if ((stack = lstackCreate(h)) == NULL) { - L_ERROR("stack not made\n", procName); - goto cleanup; - } - auxstack = lstackCreate(0); - stack->auxstack = auxstack; - boxa = boxaCreate(0); - - xstart = 0; - ystart = 0; - while (1) { - if (!nextOnPixelInRaster(pix1, xstart, ystart, &x, &y)) - break; - - if ((box = pixSeedfillBB(pix1, stack, x, y, connectivity)) == NULL) { - L_ERROR("box not made\n", procName); - boxaDestroy(&boxa); - goto cleanup; - } - boxaAddBox(boxa, box, L_INSERT); - - xstart = x; - ystart = y; - } - -#if DEBUG - pixCountPixels(pix1, &iszero, NULL); - lept_stderr("Number of remaining pixels = %d\n", iszero); - lept_mkdir("lept/cc"); - pixWriteDebug("/tmp/lept/cc/remain.png", pix1, IFF_PNG); -#endif /* DEBUG */ - - /* Cleanup, freeing the fillsegs on each stack */ -cleanup: - lstackDestroy(&stack, TRUE); - pixDestroy(&pix1); - return boxa; -} - - -/*! - * \brief pixCountConnComp() - * - * \param[in] pixs 1 bpp - * \param[in] connectivity 4 or 8 - * \param[out] pcount - * \return 0 if OK, 1 on error - * - * Notes: - * (1 This is the top-level call for getting the number of - * 4- or 8-connected components in a 1 bpp image. - * 2 It works on a copy of the input pix. The c.c. are located - * in raster order and erased one at a time. - */ -l_ok -pixCountConnComp(PIX *pixs, - l_int32 connectivity, - l_int32 *pcount) -{ -l_int32 h, iszero; -l_int32 x, y, xstart, ystart; -PIX *pix1; -L_STACK *stack, *auxstack; - - PROCNAME("pixCountConnComp"); - - if (!pcount) - return ERROR_INT("&count not defined", procName, 1); - *pcount = 0; /* initialize the count to 0 */ - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (connectivity != 4 && connectivity != 8) - return ERROR_INT("connectivity not 4 or 8", procName, 1); - - stack = NULL; - pixZero(pixs, &iszero); - if (iszero) - return 0; - - pixSetPadBits(pixs, 0); - if ((pix1 = pixCopy(NULL, pixs)) == NULL) - return ERROR_INT("pix1 not made", procName, 1); - h = pixGetHeight(pixs); - if ((stack = lstackCreate(h)) == NULL) { - pixDestroy(&pix1); - return ERROR_INT("stack not made\n", procName, 1); - } - auxstack = lstackCreate(0); - stack->auxstack = auxstack; - - xstart = 0; - ystart = 0; - while (1) { - if (!nextOnPixelInRaster(pix1, xstart, ystart, &x, &y)) - break; - - pixSeedfill(pix1, stack, x, y, connectivity); - (*pcount)++; - xstart = x; - ystart = y; - } - - /* Cleanup, freeing the fillsegs on each stack */ - lstackDestroy(&stack, TRUE); - pixDestroy(&pix1); - return 0; -} - - -/*! - * \brief nextOnPixelInRaster() - * - * \param[in] pixs 1 bpp - * \param[in] xstart, ystart starting point for search - * \param[out] px, py coord value of next ON pixel - * \return 1 if a pixel is found; 0 otherwise or on error - */ -l_int32 -nextOnPixelInRaster(PIX *pixs, - l_int32 xstart, - l_int32 ystart, - l_int32 *px, - l_int32 *py) -{ -l_int32 w, h, d, wpl; -l_uint32 *data; - - PROCNAME("nextOnPixelInRaster"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 0); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 1) - return ERROR_INT("pixs not 1 bpp", procName, 0); - - wpl = pixGetWpl(pixs); - data = pixGetData(pixs); - return nextOnPixelInRasterLow(data, w, h, wpl, xstart, ystart, px, py); -} - - -/*! - * \brief nextOnPixelInRasterLow() - * - * \param[in] data pix data - * \param[in] w, h width and height - * \param[in] wpl words per line - * \param[in] xstart, ystart starting point for search - * \param[out] px, py coord value of next ON pixel - * \return 1 if a pixel is found; 0 otherwise or on error - */ -static l_int32 -nextOnPixelInRasterLow(l_uint32 *data, - l_int32 w, - l_int32 h, - l_int32 wpl, - l_int32 xstart, - l_int32 ystart, - l_int32 *px, - l_int32 *py) -{ -l_int32 i, x, y, xend, startword; -l_uint32 *line, *pword; - - /* Look at the first word */ - line = data + ystart * wpl; - pword = line + (xstart / 32); - if (*pword) { - xend = xstart - (xstart % 32) + 31; - for (x = xstart; x <= xend && x < w; x++) { - if (GET_DATA_BIT(line, x)) { - *px = x; - *py = ystart; - return 1; - } - } - } - - /* Continue with the rest of the line */ - startword = (xstart / 32) + 1; - x = 32 * startword; - for (pword = line + startword; x < w; pword++, x += 32) { - if (*pword) { - for (i = 0; i < 32 && x < w; i++, x++) { - if (GET_DATA_BIT(line, x)) { - *px = x; - *py = ystart; - return 1; - } - } - } - } - - /* Continue with following lines */ - for (y = ystart + 1; y < h; y++) { - line = data + y * wpl; - for (pword = line, x = 0; x < w; pword++, x += 32) { - if (*pword) { - for (i = 0; i < 32 && x < w; i++, x++) { - if (GET_DATA_BIT(line, x)) { - *px = x; - *py = y; - return 1; - } - } - } - } - } - - return 0; -} - - -/*! - * \brief pixSeedfillBB() - * - * \param[in] pixs 1 bpp - * \param[in] stack for holding fillsegs - * \param[in] x,y location of seed pixel - * \param[in] connectivity 4 or 8 - * \return box or NULL on error - * - *
- * Notes: - * (1) This is the high-level interface to Paul Heckbert's - * stack-based seedfill algorithm. - *- */ -BOX * -pixSeedfillBB(PIX *pixs, - L_STACK *stack, - l_int32 x, - l_int32 y, - l_int32 connectivity) -{ -BOX *box; - - PROCNAME("pixSeedfillBB"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (BOX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (!stack) - return (BOX *)ERROR_PTR("stack not defined", procName, NULL); - if (connectivity != 4 && connectivity != 8) - return (BOX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); - - if (connectivity == 4) { - if ((box = pixSeedfill4BB(pixs, stack, x, y)) == NULL) - return (BOX *)ERROR_PTR("box not made", procName, NULL); - } else if (connectivity == 8) { - if ((box = pixSeedfill8BB(pixs, stack, x, y)) == NULL) - return (BOX *)ERROR_PTR("box not made", procName, NULL); - } else { - return (BOX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); - } - - return box; -} - - -/*! - * \brief pixSeedfill4BB() - * - * \param[in] pixs 1 bpp - * \param[in] stack for holding fillsegs - * \param[in] x,y location of seed pixel - * \return box or NULL on error. - * - *
- * Notes: - * (1) This is Paul Heckbert's stack-based 4-cc seedfill algorithm. - * (2) This operates on the input 1 bpp pix to remove the fg seed - * pixel, at (x,y), and all pixels that are 4-connected to it. - * The seed pixel at (x,y) must initially be ON. - * (3) Returns the bounding box of the erased 4-cc component. - * (4) Reference: see Paul Heckbert's stack-based seed fill algorithm - * in "Graphic Gems", ed. Andrew Glassner, Academic - * Press, 1990. The algorithm description is given - * on pp. 275-277; working C code is on pp. 721-722.) - * The code here follows Heckbert's exactly, except - * we use function calls instead of macros for - * pushing data on and popping data off the stack. - * This makes sense to do because Heckbert's fixed-size - * stack with macros is dangerous: images exist that - * will overrun the stack and crash. The stack utility - * here grows dynamically as needed, and the fillseg - * structures that are not in use are stored in another - * stack for reuse. It should be noted that the - * overhead in the function calls (vs. macros) is negligible. - *- */ -BOX * -pixSeedfill4BB(PIX *pixs, - L_STACK *stack, - l_int32 x, - l_int32 y) -{ -l_int32 w, h, xstart, wpl, x1, x2, dy; -l_int32 xmax, ymax; -l_int32 minx, maxx, miny, maxy; /* for bounding box of this c.c. */ -l_uint32 *data, *line; -BOX *box; - - PROCNAME("pixSeedfill4BB"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (BOX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (!stack) - return (BOX *)ERROR_PTR("stack not defined", procName, NULL); - if (!stack->auxstack) - stack->auxstack = lstackCreate(0); - - pixGetDimensions(pixs, &w, &h, NULL); - xmax = w - 1; - ymax = h - 1; - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - line = data + y * wpl; - - /* Check pix value of seed; must be within the image and ON */ - if (x < 0 || x > xmax || y < 0 || y > ymax || (GET_DATA_BIT(line, x) == 0)) - return NULL; - - /* Init stack to seed: - * Must first init b.b. values to prevent valgrind from complaining; - * then init b.b. boundaries correctly to seed. */ - minx = miny = 100000; - maxx = maxy = 0; - pushFillsegBB(stack, x, x, y, 1, ymax, &minx, &maxx, &miny, &maxy); - pushFillsegBB(stack, x, x, y + 1, -1, ymax, &minx, &maxx, &miny, &maxy); - minx = maxx = x; - miny = maxy = y; - - while (lstackGetCount(stack) > 0) { - /* Pop segment off stack and fill a neighboring scan line */ - popFillseg(stack, &x1, &x2, &y, &dy); - line = data + y * wpl; - - /* A segment of scanline y - dy for x1 <= x <= x2 was - * previously filled. We now explore adjacent pixels - * in scan line y. There are three regions: to the - * left of x1 - 1, between x1 and x2, and to the right of x2. - * These regions are handled differently. Leaks are - * possible expansions beyond the previous segment and - * going back in the -dy direction. These can happen - * for x < x1 - 1 and for x > x2 + 1. Any "leak" segments - * are plugged with a push in the -dy (opposite) direction. - * And any segments found anywhere are always extended - * in the +dy direction. */ - for (x = x1; x >= 0 && (GET_DATA_BIT(line, x) == 1); x--) - CLEAR_DATA_BIT(line,x); - if (x >= x1) /* pix at x1 was off and was not cleared */ - goto skip; - xstart = x + 1; - if (xstart < x1 - 1) /* leak on left? */ - pushFillsegBB(stack, xstart, x1 - 1, y, -dy, - ymax, &minx, &maxx, &miny, &maxy); - - x = x1 + 1; - do { - for (; x <= xmax && (GET_DATA_BIT(line, x) == 1); x++) - CLEAR_DATA_BIT(line, x); - pushFillsegBB(stack, xstart, x - 1, y, dy, - ymax, &minx, &maxx, &miny, &maxy); - if (x > x2 + 1) /* leak on right? */ - pushFillsegBB(stack, x2 + 1, x - 1, y, -dy, - ymax, &minx, &maxx, &miny, &maxy); - skip: for (x++; x <= x2 && - x <= xmax && - (GET_DATA_BIT(line, x) == 0); x++) - ; - xstart = x; - } while (x <= x2 && x <= xmax); - } - - if ((box = boxCreate(minx, miny, maxx - minx + 1, maxy - miny + 1)) - == NULL) - return (BOX *)ERROR_PTR("box not made", procName, NULL); - return box; -} - - -/*! - * \brief pixSeedfill8BB() - * - * \param[in] pixs 1 bpp - * \param[in] stack for holding fillsegs - * \param[in] x,y location of seed pixel - * \return box or NULL on error. - * - *
- * Notes: - * (1) This is Paul Heckbert's stack-based 8-cc seedfill algorithm. - * (2) This operates on the input 1 bpp pix to remove the fg seed - * pixel, at (x,y), and all pixels that are 8-connected to it. - * The seed pixel at (x,y) must initially be ON. - * (3) Returns the bounding box of the erased 8-cc component. - * (4) Reference: see Paul Heckbert's stack-based seed fill algorithm - * in "Graphic Gems", ed. Andrew Glassner, Academic - * Press, 1990. The algorithm description is given - * on pp. 275-277; working C code is on pp. 721-722.) - * The code here follows Heckbert's closely, except - * the leak checks are changed for 8 connectivity. - * See comments on pixSeedfill4BB() for more details. - *- */ -BOX * -pixSeedfill8BB(PIX *pixs, - L_STACK *stack, - l_int32 x, - l_int32 y) -{ -l_int32 w, h, xstart, wpl, x1, x2, dy; -l_int32 xmax, ymax; -l_int32 minx, maxx, miny, maxy; /* for bounding box of this c.c. */ -l_uint32 *data, *line; -BOX *box; - - PROCNAME("pixSeedfill8BB"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (BOX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (!stack) - return (BOX *)ERROR_PTR("stack not defined", procName, NULL); - if (!stack->auxstack) - stack->auxstack = lstackCreate(0); - - pixGetDimensions(pixs, &w, &h, NULL); - xmax = w - 1; - ymax = h - 1; - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - line = data + y * wpl; - - /* Check pix value of seed; must be ON */ - if (x < 0 || x > xmax || y < 0 || y > ymax || (GET_DATA_BIT(line, x) == 0)) - return NULL; - - /* Init stack to seed: - * Must first init b.b. values to prevent valgrind from complaining; - * then init b.b. boundaries correctly to seed. */ - minx = miny = 100000; - maxx = maxy = 0; - pushFillsegBB(stack, x, x, y, 1, ymax, &minx, &maxx, &miny, &maxy); - pushFillsegBB(stack, x, x, y + 1, -1, ymax, &minx, &maxx, &miny, &maxy); - minx = maxx = x; - miny = maxy = y; - - while (lstackGetCount(stack) > 0) { - /* Pop segment off stack and fill a neighboring scan line */ - popFillseg(stack, &x1, &x2, &y, &dy); - line = data + y * wpl; - - /* A segment of scanline y - dy for x1 <= x <= x2 was - * previously filled. We now explore adjacent pixels - * in scan line y. There are three regions: to the - * left of x1, between x1 and x2, and to the right of x2. - * These regions are handled differently. Leaks are - * possible expansions beyond the previous segment and - * going back in the -dy direction. These can happen - * for x < x1 and for x > x2. Any "leak" segments - * are plugged with a push in the -dy (opposite) direction. - * And any segments found anywhere are always extended - * in the +dy direction. */ - for (x = x1 - 1; x >= 0 && (GET_DATA_BIT(line, x) == 1); x--) - CLEAR_DATA_BIT(line,x); - if (x >= x1 - 1) /* pix at x1 - 1 was off and was not cleared */ - goto skip; - xstart = x + 1; - if (xstart < x1) /* leak on left? */ - pushFillsegBB(stack, xstart, x1 - 1, y, -dy, - ymax, &minx, &maxx, &miny, &maxy); - - x = x1; - do { - for (; x <= xmax && (GET_DATA_BIT(line, x) == 1); x++) - CLEAR_DATA_BIT(line, x); - pushFillsegBB(stack, xstart, x - 1, y, dy, - ymax, &minx, &maxx, &miny, &maxy); - if (x > x2) /* leak on right? */ - pushFillsegBB(stack, x2 + 1, x - 1, y, -dy, - ymax, &minx, &maxx, &miny, &maxy); - skip: for (x++; x <= x2 + 1 && - x <= xmax && - (GET_DATA_BIT(line, x) == 0); x++) - ; - xstart = x; - } while (x <= x2 + 1 && x <= xmax); - } - - if ((box = boxCreate(minx, miny, maxx - minx + 1, maxy - miny + 1)) - == NULL) - return (BOX *)ERROR_PTR("box not made", procName, NULL); - return box; -} - - -/*! - * \brief pixSeedfill() - * - * \param[in] pixs 1 bpp - * \param[in] stack for holding fillsegs - * \param[in] x,y location of seed pixel - * \param[in] connectivity 4 or 8 - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This removes the component from pixs with a fg pixel at (x,y). - * (2) See pixSeedfill4() and pixSeedfill8() for details. - *- */ -l_ok -pixSeedfill(PIX *pixs, - L_STACK *stack, - l_int32 x, - l_int32 y, - l_int32 connectivity) -{ -l_int32 retval; - - PROCNAME("pixSeedfill"); - - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (!stack) - return ERROR_INT("stack not defined", procName, 1); - if (connectivity != 4 && connectivity != 8) - return ERROR_INT("connectivity not 4 or 8", procName, 1); - - if (connectivity == 4) - retval = pixSeedfill4(pixs, stack, x, y); - else /* connectivity == 8 */ - retval = pixSeedfill8(pixs, stack, x, y); - - return retval; -} - - -/*! - * \brief pixSeedfill4() - * - * \param[in] pixs 1 bpp - * \param[in] stack for holding fillsegs - * \param[in] x,y location of seed pixel - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is Paul Heckbert's stack-based 4-cc seedfill algorithm. - * (2) This operates on the input 1 bpp pix to remove the fg seed - * pixel, at (x,y), and all pixels that are 4-connected to it. - * The seed pixel at (x,y) must initially be ON. - * (3) Reference: see pixSeedFill4BB() - *- */ -l_ok -pixSeedfill4(PIX *pixs, - L_STACK *stack, - l_int32 x, - l_int32 y) -{ -l_int32 w, h, xstart, wpl, x1, x2, dy; -l_int32 xmax, ymax; -l_uint32 *data, *line; - - PROCNAME("pixSeedfill4"); - - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (!stack) - return ERROR_INT("stack not defined", procName, 1); - if (!stack->auxstack) - stack->auxstack = lstackCreate(0); - - pixGetDimensions(pixs, &w, &h, NULL); - xmax = w - 1; - ymax = h - 1; - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - line = data + y * wpl; - - /* Check pix value of seed; must be within the image and ON */ - if (x < 0 || x > xmax || y < 0 || y > ymax || (GET_DATA_BIT(line, x) == 0)) - return 0; - - /* Init stack to seed */ - pushFillseg(stack, x, x, y, 1, ymax); - pushFillseg(stack, x, x, y + 1, -1, ymax); - - while (lstackGetCount(stack) > 0) { - /* Pop segment off stack and fill a neighboring scan line */ - popFillseg(stack, &x1, &x2, &y, &dy); - line = data + y * wpl; - - /* A segment of scanline y - dy for x1 <= x <= x2 was - * previously filled. We now explore adjacent pixels - * in scan line y. There are three regions: to the - * left of x1 - 1, between x1 and x2, and to the right of x2. - * These regions are handled differently. Leaks are - * possible expansions beyond the previous segment and - * going back in the -dy direction. These can happen - * for x < x1 - 1 and for x > x2 + 1. Any "leak" segments - * are plugged with a push in the -dy (opposite) direction. - * And any segments found anywhere are always extended - * in the +dy direction. */ - for (x = x1; x >= 0 && (GET_DATA_BIT(line, x) == 1); x--) - CLEAR_DATA_BIT(line,x); - if (x >= x1) /* pix at x1 was off and was not cleared */ - goto skip; - xstart = x + 1; - if (xstart < x1 - 1) /* leak on left? */ - pushFillseg(stack, xstart, x1 - 1, y, -dy, ymax); - - x = x1 + 1; - do { - for (; x <= xmax && (GET_DATA_BIT(line, x) == 1); x++) - CLEAR_DATA_BIT(line, x); - pushFillseg(stack, xstart, x - 1, y, dy, ymax); - if (x > x2 + 1) /* leak on right? */ - pushFillseg(stack, x2 + 1, x - 1, y, -dy, ymax); - skip: for (x++; x <= x2 && - x <= xmax && - (GET_DATA_BIT(line, x) == 0); x++) - ; - xstart = x; - } while (x <= x2 && x <= xmax); - } - - return 0; -} - - -/*! - * \brief pixSeedfill8() - * - * \param[in] pixs 1 bpp - * \param[in] stack for holding fillsegs - * \param[in] x,y location of seed pixel - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is Paul Heckbert's stack-based 8-cc seedfill algorithm. - * (2) This operates on the input 1 bpp pix to remove the fg seed - * pixel, at (x,y), and all pixels that are 8-connected to it. - * The seed pixel at (x,y) must initially be ON. - * (3) Reference: see pixSeedFill8BB() - *- */ -l_ok -pixSeedfill8(PIX *pixs, - L_STACK *stack, - l_int32 x, - l_int32 y) -{ -l_int32 w, h, xstart, wpl, x1, x2, dy; -l_int32 xmax, ymax; -l_uint32 *data, *line; - - PROCNAME("pixSeedfill8"); - - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (!stack) - return ERROR_INT("stack not defined", procName, 1); - if (!stack->auxstack) - stack->auxstack = lstackCreate(0); - - pixGetDimensions(pixs, &w, &h, NULL); - xmax = w - 1; - ymax = h - 1; - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - line = data + y * wpl; - - /* Check pix value of seed; must be ON */ - if (x < 0 || x > xmax || y < 0 || y > ymax || (GET_DATA_BIT(line, x) == 0)) - return 0; - - /* Init stack to seed */ - pushFillseg(stack, x, x, y, 1, ymax); - pushFillseg(stack, x, x, y + 1, -1, ymax); - - while (lstackGetCount(stack) > 0) { - /* Pop segment off stack and fill a neighboring scan line */ - popFillseg(stack, &x1, &x2, &y, &dy); - line = data + y * wpl; - - /* A segment of scanline y - dy for x1 <= x <= x2 was - * previously filled. We now explore adjacent pixels - * in scan line y. There are three regions: to the - * left of x1, between x1 and x2, and to the right of x2. - * These regions are handled differently. Leaks are - * possible expansions beyond the previous segment and - * going back in the -dy direction. These can happen - * for x < x1 and for x > x2. Any "leak" segments - * are plugged with a push in the -dy (opposite) direction. - * And any segments found anywhere are always extended - * in the +dy direction. */ - for (x = x1 - 1; x >= 0 && (GET_DATA_BIT(line, x) == 1); x--) - CLEAR_DATA_BIT(line,x); - if (x >= x1 - 1) /* pix at x1 - 1 was off and was not cleared */ - goto skip; - xstart = x + 1; - if (xstart < x1) /* leak on left? */ - pushFillseg(stack, xstart, x1 - 1, y, -dy, ymax); - - x = x1; - do { - for (; x <= xmax && (GET_DATA_BIT(line, x) == 1); x++) - CLEAR_DATA_BIT(line, x); - pushFillseg(stack, xstart, x - 1, y, dy, ymax); - if (x > x2) /* leak on right? */ - pushFillseg(stack, x2 + 1, x - 1, y, -dy, ymax); - skip: for (x++; x <= x2 + 1 && - x <= xmax && - (GET_DATA_BIT(line, x) == 0); x++) - ; - xstart = x; - } while (x <= x2 + 1 && x <= xmax); - } - - return 0; -} - - - -/*-----------------------------------------------------------------------* - * Static stack helper functions: push and pop fillsegs * - *-----------------------------------------------------------------------*/ -/*! - * \brief pushFillsegBB() - * - * \param[in] stack - * \param[in] xleft, xright - * \param[in] y - * \param[in] dy - * \param[in] ymax - * \param[out] pminx minimum x - * \param[out] pmaxx maximum x - * \param[out] pminy minimum y - * \param[out] pmaxy maximum y - * \return void - * - *
- * Notes: - * (1) This adds a line segment to the stack, and returns its size. - * (2) The auxiliary stack is used as a storage area to recycle - * fillsegs that are no longer in use. We only calloc new - * fillsegs if the auxiliary stack is empty. - *- */ -static void -pushFillsegBB(L_STACK *stack, - l_int32 xleft, - l_int32 xright, - l_int32 y, - l_int32 dy, - l_int32 ymax, - l_int32 *pminx, - l_int32 *pmaxx, - l_int32 *pminy, - l_int32 *pmaxy) -{ -FILLSEG *fseg; -L_STACK *auxstack; - - PROCNAME("pushFillsegBB"); - - if (!stack) { - L_ERROR("stack not defined\n", procName); - return; - } - - *pminx = L_MIN(*pminx, xleft); - *pmaxx = L_MAX(*pmaxx, xright); - *pminy = L_MIN(*pminy, y); - *pmaxy = L_MAX(*pmaxy, y); - - if (y + dy >= 0 && y + dy <= ymax) { - if ((auxstack = stack->auxstack) == NULL) { - L_ERROR("auxstack not defined\n", procName); - return; - } - - /* Get a fillseg to use */ - if (lstackGetCount(auxstack) > 0) - fseg = (FILLSEG *)lstackRemove(auxstack); - else - fseg = (FILLSEG *)LEPT_CALLOC(1, sizeof(FILLSEG)); - fseg->xleft = xleft; - fseg->xright = xright; - fseg->y = y; - fseg->dy = dy; - lstackAdd(stack, fseg); - } - return; -} - - -/*! - * \brief pushFillseg() - * - * \param[in] stack - * \param[in] xleft, xright - * \param[in] y - * \param[in] dy - * \param[in] ymax - * \return void - * - *
- * Notes: - * (1) This adds a line segment to the stack. - * (2) The auxiliary stack is used as a storage area to recycle - * fillsegs that are no longer in use. We only calloc new - * fillsegs if the auxiliary stack is empty. - *- */ -static void -pushFillseg(L_STACK *stack, - l_int32 xleft, - l_int32 xright, - l_int32 y, - l_int32 dy, - l_int32 ymax) -{ -FILLSEG *fseg; -L_STACK *auxstack; - - PROCNAME("pushFillseg"); - - if (!stack) { - L_ERROR("stack not defined\n", procName); - return; - } - - if (y + dy >= 0 && y + dy <= ymax) { - if ((auxstack = stack->auxstack) == NULL) { - L_ERROR("auxstack not defined\n", procName); - return; - } - - /* Get a fillseg to use */ - if (lstackGetCount(auxstack) > 0) - fseg = (FILLSEG *)lstackRemove(auxstack); - else - fseg = (FILLSEG *)LEPT_CALLOC(1, sizeof(FILLSEG)); - fseg->xleft = xleft; - fseg->xright = xright; - fseg->y = y; - fseg->dy = dy; - lstackAdd(stack, fseg); - } - return; -} - - -/*! - * \brief popFillseg() - * - * \param[in] stack - * \param[out] pxleft left x - * \param[out] pxright right x - * \param[out] py y coordinate - * \param[out] pdy delta y - * \return void - * - *
- * Notes: - * (1) This removes a line segment from the stack, and returns its size. - * (2) The surplussed fillseg is placed on the auxiliary stack - * for future use. - *- */ -static void -popFillseg(L_STACK *stack, - l_int32 *pxleft, - l_int32 *pxright, - l_int32 *py, - l_int32 *pdy) -{ -FILLSEG *fseg; -L_STACK *auxstack; - - PROCNAME("popFillseg"); - - if (!stack) { - L_ERROR("stack not defined\n", procName); - return; - } - if ((auxstack = stack->auxstack) == NULL) { - L_ERROR("auxstack not defined\n", procName); - return; - } - - if ((fseg = (FILLSEG *)lstackRemove(stack)) == NULL) - return; - - *pxleft = fseg->xleft; - *pxright = fseg->xright; - *py = fseg->y + fseg->dy; /* this now points to the new line */ - *pdy = fseg->dy; - - /* Save it for re-use */ - lstackAdd(auxstack, fseg); - return; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/convertfiles.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/convertfiles.c deleted file mode 100644 index 7c229e07..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/convertfiles.c +++ /dev/null @@ -1,149 +0,0 @@ -/*====================================================================* - - 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 convertfiles.c - *
- * - * Conversion to 1 bpp - * l_int32 convertFilesTo1bpp() - * - * These are utility functions that will perform depth conversion - * on selected files, writing the results to a specified directory. - * We start with conversion to 1 bpp. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) Images are sorted lexicographically, and the names in the - * output directory are retained except for the extension. - *- */ -l_ok -convertFilesTo1bpp(const char *dirin, - const char *substr, - l_int32 upscaling, - l_int32 thresh, - l_int32 firstpage, - l_int32 npages, - const char *dirout, - l_int32 outformat) -{ -l_int32 i, nfiles; -char buf[512]; -char *fname, *tail, *basename; -PIX *pixs, *pixg1, *pixg2, *pixb; -SARRAY *safiles; - - PROCNAME("convertFilesTo1bpp"); - - if (!dirin) - return ERROR_INT("dirin", procName, 1); - if (!dirout) - return ERROR_INT("dirout", procName, 1); - if (upscaling != 1 && upscaling != 2 && upscaling != 4) - return ERROR_INT("invalid upscaling factor", procName, 1); - if (thresh <= 0) thresh = 180; - if (firstpage < 0) firstpage = 0; - if (npages < 0) npages = 0; - if (outformat != IFF_TIFF_G4) - outformat = IFF_PNG; - - safiles = getSortedPathnamesInDirectory(dirin, substr, firstpage, npages); - if (!safiles) - return ERROR_INT("safiles not made", procName, 1); - if ((nfiles = sarrayGetCount(safiles)) == 0) { - sarrayDestroy(&safiles); - return ERROR_INT("no matching files in the directory", procName, 1); - } - - for (i = 0; i < nfiles; i++) { - fname = sarrayGetString(safiles, i, L_NOCOPY); - if ((pixs = pixRead(fname)) == NULL) { - L_WARNING("Couldn't read file %s\n", procName, fname); - continue; - } - if (pixGetDepth(pixs) == 32) - pixg1 = pixConvertRGBToLuminance(pixs); - else - pixg1 = pixClone(pixs); - pixg2 = pixRemoveColormap(pixg1, REMOVE_CMAP_TO_GRAYSCALE); - if (pixGetDepth(pixg2) == 1) { - pixb = pixClone(pixg2); - } else { - if (upscaling == 1) - pixb = pixThresholdToBinary(pixg2, thresh); - else if (upscaling == 2) - pixb = pixScaleGray2xLIThresh(pixg2, thresh); - else /* upscaling == 4 */ - pixb = pixScaleGray4xLIThresh(pixg2, thresh); - } - pixDestroy(&pixs); - pixDestroy(&pixg1); - pixDestroy(&pixg2); - - splitPathAtDirectory(fname, NULL, &tail); - splitPathAtExtension(tail, &basename, NULL); - if (outformat == IFF_TIFF_G4) { - snprintf(buf, sizeof(buf), "%s/%s.tif", dirout, basename); - pixWrite(buf, pixb, IFF_TIFF_G4); - } else { - snprintf(buf, sizeof(buf), "%s/%s.png", dirout, basename); - pixWrite(buf, pixb, IFF_PNG); - } - pixDestroy(&pixb); - LEPT_FREE(tail); - LEPT_FREE(basename); - } - - sarrayDestroy(&safiles); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/convolve.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/convolve.c deleted file mode 100644 index 0d118795..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/convolve.c +++ /dev/null @@ -1,2580 +0,0 @@ -/*====================================================================* - - 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 convolve.c - *
- * - * Top level grayscale or color block convolution - * PIX *pixBlockconv() - * - * Grayscale block convolution - * PIX *pixBlockconvGray() - * static void blockconvLow() - * - * Accumulator for 1, 8 and 32 bpp convolution - * PIX *pixBlockconvAccum() - * static void blockconvAccumLow() - * - * Un-normalized grayscale block convolution - * PIX *pixBlockconvGrayUnnormalized() - * - * Tiled grayscale or color block convolution - * PIX *pixBlockconvTiled() - * PIX *pixBlockconvGrayTile() - * - * Convolution for mean, mean square, variance and rms deviation - * in specified window - * l_int32 pixWindowedStats() - * PIX *pixWindowedMean() - * PIX *pixWindowedMeanSquare() - * l_int32 pixWindowedVariance() - * DPIX *pixMeanSquareAccum() - * - * Binary block sum and rank filter - * PIX *pixBlockrank() - * PIX *pixBlocksum() - * static void blocksumLow() - * - * Census transform - * PIX *pixCensusTransform() - * - * Generic convolution (with Pix) - * PIX *pixConvolve() - * PIX *pixConvolveSep() - * PIX *pixConvolveRGB() - * PIX *pixConvolveRGBSep() - * - * Generic convolution (with float arrays) - * FPIX *fpixConvolve() - * FPIX *fpixConvolveSep() - * - * Convolution with bias (for non-negative output) - * PIX *pixConvolveWithBias() - * - * Set parameter for convolution subsampling - * void l_setConvolveSampling() - * - * Additive gaussian noise - * PIX *pixAddGaussNoise() - * l_float32 gaussDistribSampling() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) The full width and height of the convolution kernel - * are (2 * wc + 1) and (2 * hc + 1) - * (2) Returns a copy if both wc and hc are 0 - * (3) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1, - * where (w,h) are the dimensions of pixs. - *- */ -PIX * -pixBlockconv(PIX *pix, - l_int32 wc, - l_int32 hc) -{ -l_int32 w, h, d; -PIX *pixs, *pixd, *pixr, *pixrc, *pixg, *pixgc, *pixb, *pixbc; - - PROCNAME("pixBlockconv"); - - if (!pix) - return (PIX *)ERROR_PTR("pix not defined", procName, NULL); - if (wc < 0) wc = 0; - if (hc < 0) hc = 0; - pixGetDimensions(pix, &w, &h, &d); - if (w < 2 * wc + 1 || h < 2 * hc + 1) { - wc = L_MIN(wc, (w - 1) / 2); - hc = L_MIN(hc, (h - 1) / 2); - L_WARNING("kernel too large; reducing!\n", procName); - L_INFO("wc = %d, hc = %d\n", procName, wc, hc); - } - if (wc == 0 && hc == 0) /* no-op */ - return pixCopy(NULL, pix); - - /* Remove colormap if necessary */ - if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) { - L_WARNING("pix has colormap; removing\n", procName); - pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); - d = pixGetDepth(pixs); - } else { - pixs = pixClone(pix); - } - - if (d != 8 && d != 32) { - pixDestroy(&pixs); - return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, NULL); - } - - if (d == 8) { - pixd = pixBlockconvGray(pixs, NULL, wc, hc); - } else { /* d == 32 */ - pixr = pixGetRGBComponent(pixs, COLOR_RED); - pixrc = pixBlockconvGray(pixr, NULL, wc, hc); - pixDestroy(&pixr); - pixg = pixGetRGBComponent(pixs, COLOR_GREEN); - pixgc = pixBlockconvGray(pixg, NULL, wc, hc); - pixDestroy(&pixg); - pixb = pixGetRGBComponent(pixs, COLOR_BLUE); - pixbc = pixBlockconvGray(pixb, NULL, wc, hc); - pixDestroy(&pixb); - pixd = pixCreateRGBImage(pixrc, pixgc, pixbc); - pixDestroy(&pixrc); - pixDestroy(&pixgc); - pixDestroy(&pixbc); - } - - pixDestroy(&pixs); - return pixd; -} - - -/*----------------------------------------------------------------------* - * Grayscale block convolution * - *----------------------------------------------------------------------*/ -/*! - * \brief pixBlockconvGray() - * - * \param[in] pixs 8 bpp - * \param[in] pixacc pix 32 bpp; can be null - * \param[in] wc, hc half width/height of convolution kernel - * \return pix 8 bpp, or NULL on error - * - *
- * Notes: - * (1) If accum pix is null, make one and destroy it before - * returning; otherwise, just use the input accum pix. - * (2) The full width and height of the convolution kernel - * are (2 * wc + 1) and (2 * hc + 1). - * (3) Returns a copy if both wc and hc are 0. - * (4) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1, - * where (w,h) are the dimensions of pixs. - *- */ -PIX * -pixBlockconvGray(PIX *pixs, - PIX *pixacc, - l_int32 wc, - l_int32 hc) -{ -l_int32 w, h, d, wpl, wpla; -l_uint32 *datad, *dataa; -PIX *pixd, *pixt; - - PROCNAME("pixBlockconvGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (wc < 0) wc = 0; - if (hc < 0) hc = 0; - if (w < 2 * wc + 1 || h < 2 * hc + 1) { - wc = L_MIN(wc, (w - 1) / 2); - hc = L_MIN(hc, (h - 1) / 2); - L_WARNING("kernel too large; reducing!\n", procName); - L_INFO("wc = %d, hc = %d\n", procName, wc, hc); - } - if (wc == 0 && hc == 0) /* no-op */ - return pixCopy(NULL, pixs); - - if (pixacc) { - if (pixGetDepth(pixacc) == 32) { - pixt = pixClone(pixacc); - } else { - L_WARNING("pixacc not 32 bpp; making new one\n", procName); - if ((pixt = pixBlockconvAccum(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, NULL); - } - } else { - if ((pixt = pixBlockconvAccum(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, NULL); - } - - if ((pixd = pixCreateTemplate(pixs)) == NULL) { - pixDestroy(&pixt); - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - - wpl = pixGetWpl(pixs); - wpla = pixGetWpl(pixt); - datad = pixGetData(pixd); - dataa = pixGetData(pixt); - blockconvLow(datad, w, h, wpl, dataa, wpla, wc, hc); - - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief blockconvLow() - * - * \param[in] data data of input image, to be convolved - * \param[in] w, h, wpl - * \param[in] dataa data of 32 bpp accumulator - * \param[in] wpla accumulator - * \param[in] wc convolution "half-width" - * \param[in] hc convolution "half-height" - * \return void - * - *
- * Notes: - * (1) The full width and height of the convolution kernel - * are (2 * wc + 1) and (2 * hc + 1). - * (2) The lack of symmetry between the handling of the - * first (hc + 1) lines and the last (hc) lines, - * and similarly with the columns, is due to fact that - * for the pixel at (x,y), the accumulator values are - * taken at (x + wc, y + hc), (x - wc - 1, y + hc), - * (x + wc, y - hc - 1) and (x - wc - 1, y - hc - 1). - * (3) We compute sums, normalized as if there were no reduced - * area at the boundary. This under-estimates the value - * of the boundary pixels, so we multiply them by another - * normalization factor that is greater than 1. - * (4) This second normalization is done first for the first - * hc + 1 lines; then for the last hc lines; and finally - * for the first wc + 1 and last wc columns in the intermediate - * lines. - * (5) The caller should verify that wc < w and hc < h. - * Under those conditions, illegal reads and writes can occur. - * (6) Implementation note: to get the same results in the interior - * between this function and pixConvolve(), it is necessary to - * add 0.5 for roundoff in the main loop that runs over all pixels. - * However, if we do that and have white (255) pixels near the - * image boundary, some overflow occurs for pixels very close - * to the boundary. We can't fix this by subtracting from the - * normalized values for the boundary pixels, because this results - * in underflow if the boundary pixels are black (0). Empirically, - * adding 0.25 (instead of 0.5) before truncating in the main - * loop will not cause overflow, but this gives some - * off-by-1-level errors in interior pixel values. So we add - * 0.5 for roundoff in the main loop, and for pixels within a - * half filter width of the boundary, use a L_MIN of the - * computed value and 255 to avoid overflow during normalization. - *- */ -static void -blockconvLow(l_uint32 *data, - l_int32 w, - l_int32 h, - l_int32 wpl, - l_uint32 *dataa, - l_int32 wpla, - l_int32 wc, - l_int32 hc) -{ -l_int32 i, j, imax, imin, jmax, jmin; -l_int32 wn, hn, fwc, fhc, wmwc, hmhc; -l_float32 norm, normh, normw; -l_uint32 val; -l_uint32 *linemina, *linemaxa, *line; - - PROCNAME("blockconvLow"); - - wmwc = w - wc; - hmhc = h - hc; - if (wmwc <= 0 || hmhc <= 0) { - L_ERROR("wc >= w || hc >=h\n", procName); - return; - } - fwc = 2 * wc + 1; - fhc = 2 * hc + 1; - norm = 1.0 / ((l_float32)(fwc) * fhc); - - /*------------------------------------------------------------* - * Compute, using b.c. only to set limits on the accum image * - *------------------------------------------------------------*/ - for (i = 0; i < h; i++) { - imin = L_MAX(i - 1 - hc, 0); - imax = L_MIN(i + hc, h - 1); - line = data + wpl * i; - linemina = dataa + wpla * imin; - linemaxa = dataa + wpla * imax; - for (j = 0; j < w; j++) { - jmin = L_MAX(j - 1 - wc, 0); - jmax = L_MIN(j + wc, w - 1); - val = linemaxa[jmax] - linemaxa[jmin] - + linemina[jmin] - linemina[jmax]; - val = (l_uint8)(norm * val + 0.5); /* see comment above */ - SET_DATA_BYTE(line, j, val); - } - } - - /*------------------------------------------------------------* - * Fix normalization for boundary pixels * - *------------------------------------------------------------*/ - for (i = 0; i <= hc; i++) { /* first hc + 1 lines */ - hn = hc + i; - normh = (l_float32)fhc / (l_float32)hn; /* > 1 */ - line = data + wpl * i; - for (j = 0; j <= wc; j++) { - wn = wc + j; - normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ - val = GET_DATA_BYTE(line, j); - val = (l_uint8)L_MIN(val * normh * normw, 255); - SET_DATA_BYTE(line, j, val); - } - for (j = wc + 1; j < wmwc; j++) { - val = GET_DATA_BYTE(line, j); - val = (l_uint8)L_MIN(val * normh, 255); - SET_DATA_BYTE(line, j, val); - } - for (j = wmwc; j < w; j++) { - wn = wc + w - j; - normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ - val = GET_DATA_BYTE(line, j); - val = (l_uint8)L_MIN(val * normh * normw, 255); - SET_DATA_BYTE(line, j, val); - } - } - - for (i = hmhc; i < h; i++) { /* last hc lines */ - hn = hc + h - i; - normh = (l_float32)fhc / (l_float32)hn; /* > 1 */ - line = data + wpl * i; - for (j = 0; j <= wc; j++) { - wn = wc + j; - normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ - val = GET_DATA_BYTE(line, j); - val = (l_uint8)L_MIN(val * normh * normw, 255); - SET_DATA_BYTE(line, j, val); - } - for (j = wc + 1; j < wmwc; j++) { - val = GET_DATA_BYTE(line, j); - val = (l_uint8)L_MIN(val * normh, 255); - SET_DATA_BYTE(line, j, val); - } - for (j = wmwc; j < w; j++) { - wn = wc + w - j; - normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ - val = GET_DATA_BYTE(line, j); - val = (l_uint8)L_MIN(val * normh * normw, 255); - SET_DATA_BYTE(line, j, val); - } - } - - for (i = hc + 1; i < hmhc; i++) { /* intermediate lines */ - line = data + wpl * i; - for (j = 0; j <= wc; j++) { /* first wc + 1 columns */ - wn = wc + j; - normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ - val = GET_DATA_BYTE(line, j); - val = (l_uint8)L_MIN(val * normw, 255); - SET_DATA_BYTE(line, j, val); - } - for (j = wmwc; j < w; j++) { /* last wc columns */ - wn = wc + w - j; - normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ - val = GET_DATA_BYTE(line, j); - val = (l_uint8)L_MIN(val * normw, 255); - SET_DATA_BYTE(line, j, val); - } - } - - return; -} - - -/*----------------------------------------------------------------------* - * Accumulator for 1, 8 and 32 bpp convolution * - *----------------------------------------------------------------------*/ -/*! - * \brief pixBlockconvAccum() - * - * \param[in] pixs 1, 8 or 32 bpp - * \return accum pix 32 bpp, or NULL on error. - * - *
- * Notes: - * (1) The general recursion relation is - * a(i,j) = v(i,j) + a(i-1, j) + a(i, j-1) - a(i-1, j-1) - * For the first line, this reduces to the special case - * a(i,j) = v(i,j) + a(i, j-1) - * For the first column, the special case is - * a(i,j) = v(i,j) + a(i-1, j) - *- */ -PIX * -pixBlockconvAccum(PIX *pixs) -{ -l_int32 w, h, d, wpls, wpld; -l_uint32 *datas, *datad; -PIX *pixd; - - PROCNAME("pixBlockconvAccum"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - pixGetDimensions(pixs, &w, &h, &d); - if (d != 1 && d != 8 && d != 32) - return (PIX *)ERROR_PTR("pixs not 1, 8 or 32 bpp", procName, NULL); - if ((pixd = pixCreate(w, h, 32)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - blockconvAccumLow(datad, w, h, wpld, datas, d, wpls); - - return pixd; -} - - -/* - * \brief blockconvAccumLow() - * - * \param[in] datad 32 bpp dest - * \param[in] w, h, wpld of 32 bpp dest - * \param[in] datas 1, 8 or 32 bpp src - * \param[in] d bpp of src - * \param[in] wpls of src - * \return void - * - *
- * Notes: - * (1) The general recursion relation is - * a(i,j) = v(i,j) + a(i-1, j) + a(i, j-1) - a(i-1, j-1) - * For the first line, this reduces to the special case - * a(0,j) = v(0,j) + a(0, j-1), j > 0 - * For the first column, the special case is - * a(i,0) = v(i,0) + a(i-1, 0), i > 0 - *- */ -static void -blockconvAccumLow(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 d, - l_int32 wpls) -{ -l_uint8 val; -l_int32 i, j; -l_uint32 val32; -l_uint32 *lines, *lined, *linedp; - - PROCNAME("blockconvAccumLow"); - - lines = datas; - lined = datad; - - if (d == 1) { - /* Do the first line */ - for (j = 0; j < w; j++) { - val = GET_DATA_BIT(lines, j); - if (j == 0) - lined[0] = val; - else - lined[j] = lined[j - 1] + val; - } - - /* Do the other lines */ - for (i = 1; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; /* curr dest line */ - linedp = lined - wpld; /* prev dest line */ - for (j = 0; j < w; j++) { - val = GET_DATA_BIT(lines, j); - if (j == 0) - lined[0] = val + linedp[0]; - else - lined[j] = val + lined[j - 1] + linedp[j] - linedp[j - 1]; - } - } - } else if (d == 8) { - /* Do the first line */ - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(lines, j); - if (j == 0) - lined[0] = val; - else - lined[j] = lined[j - 1] + val; - } - - /* Do the other lines */ - for (i = 1; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; /* curr dest line */ - linedp = lined - wpld; /* prev dest line */ - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(lines, j); - if (j == 0) - lined[0] = val + linedp[0]; - else - lined[j] = val + lined[j - 1] + linedp[j] - linedp[j - 1]; - } - } - } else if (d == 32) { - /* Do the first line */ - for (j = 0; j < w; j++) { - val32 = lines[j]; - if (j == 0) - lined[0] = val32; - else - lined[j] = lined[j - 1] + val32; - } - - /* Do the other lines */ - for (i = 1; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; /* curr dest line */ - linedp = lined - wpld; /* prev dest line */ - for (j = 0; j < w; j++) { - val32 = lines[j]; - if (j == 0) - lined[0] = val32 + linedp[0]; - else - lined[j] = val32 + lined[j - 1] + linedp[j] - linedp[j - 1]; - } - } - } else { - L_ERROR("depth not 1, 8 or 32 bpp\n", procName); - } - - return; -} - - -/*----------------------------------------------------------------------* - * Un-normalized grayscale block convolution * - *----------------------------------------------------------------------*/ -/*! - * \brief pixBlockconvGrayUnnormalized() - * - * \param[in] pixs 8 bpp - * \param[in] wc, hc half width/height of convolution kernel - * \return pix 32 bpp; containing the convolution without normalizing - * for the window size, or NULL on error - * - *
- * Notes: - * (1) The full width and height of the convolution kernel - * are (2 * wc + 1) and (2 * hc + 1). - * (2) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1, - * where (w,h) are the dimensions of pixs. - * (3) Returns a copy if both wc and hc are 0. - * (3) Adds mirrored border to avoid treating the boundary pixels - * specially. Note that we add wc + 1 pixels to the left - * and wc to the right. The added width is 2 * wc + 1 pixels, - * and the particular choice simplifies the indexing in the loop. - * Likewise, add hc + 1 pixels to the top and hc to the bottom. - * (4) To get the normalized result, divide by the area of the - * convolution kernel: (2 * wc + 1) * (2 * hc + 1) - * Specifically, do this: - * pixc = pixBlockconvGrayUnnormalized(pixs, wc, hc); - * fract = 1. / ((2 * wc + 1) * (2 * hc + 1)); - * pixMultConstantGray(pixc, fract); - * pixd = pixGetRGBComponent(pixc, L_ALPHA_CHANNEL); - * (5) Unlike pixBlockconvGray(), this always computes the accumulation - * pix because its size is tied to wc and hc. - * (6) Compare this implementation with pixBlockconvGray(), where - * most of the code in blockconvLow() is special casing for - * efficiently handling the boundary. Here, the use of - * mirrored borders and destination indexing makes the - * implementation very simple. - *- */ -PIX * -pixBlockconvGrayUnnormalized(PIX *pixs, - l_int32 wc, - l_int32 hc) -{ -l_int32 i, j, w, h, d, wpla, wpld, jmax; -l_uint32 *linemina, *linemaxa, *lined, *dataa, *datad; -PIX *pixsb, *pixacc, *pixd; - - PROCNAME("pixBlockconvGrayUnnormalized"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (wc < 0) wc = 0; - if (hc < 0) hc = 0; - if (w < 2 * wc + 1 || h < 2 * hc + 1) { - wc = L_MIN(wc, (w - 1) / 2); - hc = L_MIN(hc, (h - 1) / 2); - L_WARNING("kernel too large; reducing!\n", procName); - L_INFO("wc = %d, hc = %d\n", procName, wc, hc); - } - if (wc == 0 && hc == 0) /* no-op */ - return pixCopy(NULL, pixs); - - if ((pixsb = pixAddMirroredBorder(pixs, wc + 1, wc, hc + 1, hc)) == NULL) - return (PIX *)ERROR_PTR("pixsb not made", procName, NULL); - pixacc = pixBlockconvAccum(pixsb); - pixDestroy(&pixsb); - if (!pixacc) - return (PIX *)ERROR_PTR("pixacc not made", procName, NULL); - if ((pixd = pixCreate(w, h, 32)) == NULL) { - pixDestroy(&pixacc); - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - - wpla = pixGetWpl(pixacc); - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - dataa = pixGetData(pixacc); - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - linemina = dataa + i * wpla; - linemaxa = dataa + (i + 2 * hc + 1) * wpla; - for (j = 0; j < w; j++) { - jmax = j + 2 * wc + 1; - lined[j] = linemaxa[jmax] - linemaxa[j] - - linemina[jmax] + linemina[j]; - } - } - - pixDestroy(&pixacc); - return pixd; -} - - -/*----------------------------------------------------------------------* - * Tiled grayscale or color block convolution * - *----------------------------------------------------------------------*/ -/*! - * \brief pixBlockconvTiled() - * - * \param[in] pix 8 or 32 bpp; or 2, 4 or 8 bpp with colormap - * \param[in] wc, hc half width/height of convolution kernel - * \param[in] nx, ny subdivision into tiles - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) The full width and height of the convolution kernel - * are (2 * wc + 1) and (2 * hc + 1) - * (2) Returns a copy if both wc and hc are 0 - * (3) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1, - * where (w,h) are the dimensions of pixs. - * (4) For nx == ny == 1, this defaults to pixBlockconv(), which - * is typically about twice as fast, and gives nearly - * identical results as pixBlockconvGrayTile(). - * (5) If the tiles are too small, nx and/or ny are reduced - * a minimum amount so that the tiles are expanded to the - * smallest workable size in the problematic direction(s). - * (6) Why a tiled version? Three reasons: - * (a) Because the accumulator is a uint32, overflow can occur - * for an image with more than 16M pixels. - * (b) The accumulator array for 16M pixels is 64 MB; using - * tiles reduces the size of this array. - * (c) Each tile can be processed independently, in parallel, - * on a multicore processor. - *- */ -PIX * -pixBlockconvTiled(PIX *pix, - l_int32 wc, - l_int32 hc, - l_int32 nx, - l_int32 ny) -{ -l_int32 i, j, w, h, d, xrat, yrat; -PIX *pixs, *pixd, *pixc, *pixt; -PIX *pixr, *pixrc, *pixg, *pixgc, *pixb, *pixbc; -PIXTILING *pt; - - PROCNAME("pixBlockconvTiled"); - - if (!pix) - return (PIX *)ERROR_PTR("pix not defined", procName, NULL); - if (wc < 0) wc = 0; - if (hc < 0) hc = 0; - pixGetDimensions(pix, &w, &h, &d); - if (w < 2 * wc + 3 || h < 2 * hc + 3) { - wc = L_MAX(0, L_MIN(wc, (w - 3) / 2)); - hc = L_MAX(0, L_MIN(hc, (h - 3) / 2)); - L_WARNING("kernel too large; reducing!\n", procName); - L_INFO("wc = %d, hc = %d\n", procName, wc, hc); - } - if (wc == 0 && hc == 0) /* no-op */ - return pixCopy(NULL, pix); - if (nx <= 1 && ny <= 1) - return pixBlockconv(pix, wc, hc); - - /* Test to see if the tiles are too small. The required - * condition is that the tile dimensions must be at least - * (wc + 2) x (hc + 2). */ - xrat = w / nx; - yrat = h / ny; - if (xrat < wc + 2) { - nx = w / (wc + 2); - L_WARNING("tile width too small; nx reduced to %d\n", procName, nx); - } - if (yrat < hc + 2) { - ny = h / (hc + 2); - L_WARNING("tile height too small; ny reduced to %d\n", procName, ny); - } - - /* Remove colormap if necessary */ - if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) { - L_WARNING("pix has colormap; removing\n", procName); - pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); - d = pixGetDepth(pixs); - } else { - pixs = pixClone(pix); - } - - if (d != 8 && d != 32) { - pixDestroy(&pixs); - return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, NULL); - } - - /* Note that the overlaps in the width and height that - * are added to the tile are (wc + 2) and (hc + 2). - * These overlaps are removed by pixTilingPaintTile(). - * They are larger than the extent of the filter because - * although the filter is symmetric with respect to its origin, - * the implementation is asymmetric -- see the implementation in - * pixBlockconvGrayTile(). */ - if ((pixd = pixCreateTemplate(pixs)) == NULL) { - pixDestroy(&pixs); - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - pt = pixTilingCreate(pixs, nx, ny, 0, 0, wc + 2, hc + 2); - for (i = 0; i < ny; i++) { - for (j = 0; j < nx; j++) { - pixt = pixTilingGetTile(pt, i, j); - - /* Convolve over the tile */ - if (d == 8) { - pixc = pixBlockconvGrayTile(pixt, NULL, wc, hc); - } else { /* d == 32 */ - pixr = pixGetRGBComponent(pixt, COLOR_RED); - pixrc = pixBlockconvGrayTile(pixr, NULL, wc, hc); - pixDestroy(&pixr); - pixg = pixGetRGBComponent(pixt, COLOR_GREEN); - pixgc = pixBlockconvGrayTile(pixg, NULL, wc, hc); - pixDestroy(&pixg); - pixb = pixGetRGBComponent(pixt, COLOR_BLUE); - pixbc = pixBlockconvGrayTile(pixb, NULL, wc, hc); - pixDestroy(&pixb); - pixc = pixCreateRGBImage(pixrc, pixgc, pixbc); - pixDestroy(&pixrc); - pixDestroy(&pixgc); - pixDestroy(&pixbc); - } - - pixTilingPaintTile(pixd, i, j, pixc, pt); - pixDestroy(&pixt); - pixDestroy(&pixc); - } - } - - pixDestroy(&pixs); - pixTilingDestroy(&pt); - return pixd; -} - - -/*! - * \brief pixBlockconvGrayTile() - * - * \param[in] pixs 8 bpp gray - * \param[in] pixacc 32 bpp accum pix - * \param[in] wc, hc half width/height of convolution kernel - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) The full width and height of the convolution kernel - * are (2 * wc + 1) and (2 * hc + 1) - * (2) Assumes that the input pixs is padded with (wc + 1) pixels on - * left and right, and with (hc + 1) pixels on top and bottom. - * The returned pix has these stripped off; they are only used - * for computation. - * (3) Returns a copy if both wc and hc are 0 - * (4) Require that w > 2 * wc + 1 and h > 2 * hc + 1, - * where (w,h) are the dimensions of pixs. - *- */ -PIX * -pixBlockconvGrayTile(PIX *pixs, - PIX *pixacc, - l_int32 wc, - l_int32 hc) -{ -l_int32 w, h, d, wd, hd, i, j, imin, imax, jmin, jmax, wplt, wpld; -l_float32 norm; -l_uint32 val; -l_uint32 *datat, *datad, *lined, *linemint, *linemaxt; -PIX *pixt, *pixd; - - PROCNAME("pixBlockconvGrayTile"); - - if (!pixs) - return (PIX *)ERROR_PTR("pix not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (wc < 0) wc = 0; - if (hc < 0) hc = 0; - if (w < 2 * wc + 3 || h < 2 * hc + 3) { - wc = L_MAX(0, L_MIN(wc, (w - 3) / 2)); - hc = L_MAX(0, L_MIN(hc, (h - 3) / 2)); - L_WARNING("kernel too large; reducing!\n", procName); - L_INFO("wc = %d, hc = %d\n", procName, wc, hc); - } - if (wc == 0 && hc == 0) - return pixCopy(NULL, pixs); - wd = w - 2 * wc; - hd = h - 2 * hc; - - if (pixacc) { - if (pixGetDepth(pixacc) == 32) { - pixt = pixClone(pixacc); - } else { - L_WARNING("pixacc not 32 bpp; making new one\n", procName); - if ((pixt = pixBlockconvAccum(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, NULL); - } - } else { - if ((pixt = pixBlockconvAccum(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, NULL); - } - - if ((pixd = pixCreateTemplate(pixs)) == NULL) { - pixDestroy(&pixt); - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - norm = 1. / (l_float32)((2 * wc + 1) * (2 * hc + 1)); - - /* Do the convolution over the subregion of size (wd - 2, hd - 2), - * which exactly corresponds to the size of the subregion that - * will be extracted by pixTilingPaintTile(). Note that the - * region in which points are computed is not symmetric about - * the center of the images; instead the computation in - * the accumulator image is shifted up and to the left by 1, - * relative to the center, because the 4 accumulator sampling - * points are taken at the LL corner of the filter and at 3 other - * points that are shifted -wc and -hc to the left and above. */ - for (i = hc; i < hc + hd - 2; i++) { - imin = L_MAX(i - hc - 1, 0); - imax = L_MIN(i + hc, h - 1); - lined = datad + i * wpld; - linemint = datat + imin * wplt; - linemaxt = datat + imax * wplt; - for (j = wc; j < wc + wd - 2; j++) { - jmin = L_MAX(j - wc - 1, 0); - jmax = L_MIN(j + wc, w - 1); - val = linemaxt[jmax] - linemaxt[jmin] - + linemint[jmin] - linemint[jmax]; - val = (l_uint8)(norm * val + 0.5); - SET_DATA_BYTE(lined, j, val); - } - } - - pixDestroy(&pixt); - return pixd; -} - - -/*----------------------------------------------------------------------* - * Convolution for mean, mean square, variance and rms deviation * - *----------------------------------------------------------------------*/ -/*! - * \brief pixWindowedStats() - * - * \param[in] pixs 8 bpp grayscale - * \param[in] wc, hc half width/height of convolution kernel - * \param[in] hasborder use 1 if it already has (wc + 1 border pixels - * on left and right, and hc + 1 on top and bottom; - * use 0 to add kernel-dependent border) - * \param[out] ppixm [optional] 8 bpp mean value in window - * \param[out] ppixms [optional] 32 bpp mean square value in window - * \param[out] pfpixv [optional] float variance in window - * \param[out] pfpixrv [optional] float rms deviation from the mean - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is a high-level convenience function for calculating - * any or all of these derived images. - * (2) If %hasborder = 0, a border is added and the result is - * computed over all pixels in pixs. Otherwise, no border is - * added and the border pixels are removed from the output images. - * (3) These statistical measures over the pixels in the - * rectangular window are: - * ~ average value:- */ -l_ok -pixWindowedStats(PIX *pixs, - l_int32 wc, - l_int32 hc, - l_int32 hasborder, - PIX **ppixm, - PIX **ppixms, - FPIX **pfpixv, - FPIX **pfpixrv) -{ -PIX *pixb, *pixm, *pixms; - - PROCNAME("pixWindowedStats"); - - if (!ppixm && !ppixms && !pfpixv && !pfpixrv) - return ERROR_INT("no output requested", procName, 1); - if (ppixm) *ppixm = NULL; - if (ppixms) *ppixms = NULL; - if (pfpixv) *pfpixv = NULL; - if (pfpixrv) *pfpixrv = NULL; - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); - if (wc < 2 || hc < 2) - return ERROR_INT("wc and hc not >= 2", procName, 1); - - /* Add border if requested */ - if (!hasborder) - pixb = pixAddBorderGeneral(pixs, wc + 1, wc + 1, hc + 1, hc + 1, 0); - else - pixb = pixClone(pixs); - - if (!pfpixv && !pfpixrv) { - if (ppixm) *ppixm = pixWindowedMean(pixb, wc, hc, 1, 1); - if (ppixms) *ppixms = pixWindowedMeanSquare(pixb, wc, hc, 1); - pixDestroy(&pixb); - return 0; - } - - pixm = pixWindowedMean(pixb, wc, hc, 1, 1); - pixms = pixWindowedMeanSquare(pixb, wc, hc, 1); - pixWindowedVariance(pixm, pixms, pfpixv, pfpixrv); - if (ppixm) - *ppixm = pixm; - else - pixDestroy(&pixm); - if (ppixms) - *ppixms = pixms; - else - pixDestroy(&pixms); - pixDestroy(&pixb); - return 0; -} - - -/*! - * \brief pixWindowedMean() - * - * \param[in] pixs 8 or 32 bpp grayscale - * \param[in] wc, hc half width/height of convolution kernel - * \param[in] hasborder use 1 if it already has (wc + 1 border pixels - * on left and right, and hc + 1 on top and bottom; - * use 0 to add kernel-dependent border) - * \param[in] normflag 1 for normalization to get average in window; - * 0 for the sum in the window (un-normalized) - * \return pixd 8 or 32 bpp, average over kernel window - * - *(pixm) - * ~ average squared value:
(pixms) - * ~ variance: <(p -
)*(p -
)> =
-
*
(pixv) - * ~ square-root of variance: (pixrv) - * where the brackets < .. > indicate that the average value is - * to be taken over the window. - * (4) Note that the variance is just the mean square difference from - * the mean value; and the square root of the variance is the - * root mean square difference from the mean, sometimes also - * called the 'standard deviation'. - * (5) The added border, along with the use of an accumulator array, - * allows computation without special treatment of pixels near - * the image boundary, and runs in a time that is independent - * of the size of the convolution kernel. - *
- * Notes: - * (1) The input and output depths are the same. - * (2) A set of border pixels of width (wc + 1) on left and right, - * and of height (hc + 1) on top and bottom, must be on the - * pix before the accumulator is found. The output pixd - * (after convolution) has this border removed. - * If %hasborder = 0, the required border is added. - * (3) Typically, %normflag == 1. However, if you want the sum - * within the window, rather than a normalized convolution, - * use %normflag == 0. - * (4) This builds a block accumulator pix, uses it here, and - * destroys it. - * (5) The added border, along with the use of an accumulator array, - * allows computation without special treatment of pixels near - * the image boundary, and runs in a time that is independent - * of the size of the convolution kernel. - *- */ -PIX * -pixWindowedMean(PIX *pixs, - l_int32 wc, - l_int32 hc, - l_int32 hasborder, - l_int32 normflag) -{ -l_int32 i, j, w, h, d, wd, hd, wplc, wpld, wincr, hincr; -l_uint32 val; -l_uint32 *datac, *datad, *linec1, *linec2, *lined; -l_float32 norm; -PIX *pixb, *pixc, *pixd; - - PROCNAME("pixWindowedMean"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - if (d != 8 && d != 32) - return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); - if (wc < 2 || hc < 2) - return (PIX *)ERROR_PTR("wc and hc not >= 2", procName, NULL); - - pixb = pixc = pixd = NULL; - - /* Add border if requested */ - if (!hasborder) - pixb = pixAddBorderGeneral(pixs, wc + 1, wc + 1, hc + 1, hc + 1, 0); - else - pixb = pixClone(pixs); - - /* Make the accumulator pix from pixb */ - if ((pixc = pixBlockconvAccum(pixb)) == NULL) { - L_ERROR("pixc not made\n", procName); - goto cleanup; - } - wplc = pixGetWpl(pixc); - datac = pixGetData(pixc); - - /* The output has wc + 1 border pixels stripped from each side - * of pixb, and hc + 1 border pixels stripped from top and bottom. */ - pixGetDimensions(pixb, &w, &h, NULL); - wd = w - 2 * (wc + 1); - hd = h - 2 * (hc + 1); - if (wd < 2 || hd < 2) { - L_ERROR("w or h is too small for the kernel\n", procName); - goto cleanup; - } - if ((pixd = pixCreate(wd, hd, d)) == NULL) { - L_ERROR("pixd not made\n", procName); - goto cleanup; - } - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - - wincr = 2 * wc + 1; - hincr = 2 * hc + 1; - norm = 1.0; /* use this for sum-in-window */ - if (normflag) - norm = 1.0 / ((l_float32)(wincr) * hincr); - for (i = 0; i < hd; i++) { - linec1 = datac + i * wplc; - linec2 = datac + (i + hincr) * wplc; - lined = datad + i * wpld; - for (j = 0; j < wd; j++) { - val = linec2[j + wincr] - linec2[j] - linec1[j + wincr] + linec1[j]; - if (d == 8) { - val = (l_uint8)(norm * val); - SET_DATA_BYTE(lined, j, val); - } else { /* d == 32 */ - val = (l_uint32)(norm * val); - lined[j] = val; - } - } - } - -cleanup: - pixDestroy(&pixb); - pixDestroy(&pixc); - return pixd; -} - - -/*! - * \brief pixWindowedMeanSquare() - * - * \param[in] pixs 8 bpp grayscale - * \param[in] wc, hc half width/height of convolution kernel - * \param[in] hasborder use 1 if it already has (wc + 1 border pixels - * on left and right, and hc + 1 on top and bottom; - * use 0 to add kernel-dependent border) - * \return pixd 32 bpp, average over rectangular window of - * width = 2 * wc + 1 and height = 2 * hc + 1 - * - *
- * Notes: - * (1) A set of border pixels of width (wc + 1) on left and right, - * and of height (hc + 1) on top and bottom, must be on the - * pix before the accumulator is found. The output pixd - * (after convolution) has this border removed. - * If %hasborder = 0, the required border is added. - * (2) The advantage is that we are unaffected by the boundary, and - * it is not necessary to treat pixels within %wc and %hc of the - * border differently. This is because processing for pixd - * only takes place for pixels in pixs for which the - * kernel is entirely contained in pixs. - * (3) Why do we have an added border of width (%wc + 1) and - * height (%hc + 1), when we only need %wc and %hc pixels - * to satisfy this condition? Answer: the accumulators - * are asymmetric, requiring an extra row and column of - * pixels at top and left to work accurately. - * (4) The added border, along with the use of an accumulator array, - * allows computation without special treatment of pixels near - * the image boundary, and runs in a time that is independent - * of the size of the convolution kernel. - *- */ -PIX * -pixWindowedMeanSquare(PIX *pixs, - l_int32 wc, - l_int32 hc, - l_int32 hasborder) -{ -l_int32 i, j, w, h, wd, hd, wpl, wpld, wincr, hincr; -l_uint32 ival; -l_uint32 *datad, *lined; -l_float64 norm; -l_float64 val; -l_float64 *data, *line1, *line2; -DPIX *dpix; -PIX *pixb, *pixd; - - PROCNAME("pixWindowedMeanSquare"); - - if (!pixs || (pixGetDepth(pixs) != 8)) - return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - if (wc < 2 || hc < 2) - return (PIX *)ERROR_PTR("wc and hc not >= 2", procName, NULL); - - pixd = NULL; - - /* Add border if requested */ - if (!hasborder) - pixb = pixAddBorderGeneral(pixs, wc + 1, wc + 1, hc + 1, hc + 1, 0); - else - pixb = pixClone(pixs); - - if ((dpix = pixMeanSquareAccum(pixb)) == NULL) { - L_ERROR("dpix not made\n", procName); - goto cleanup; - } - wpl = dpixGetWpl(dpix); - data = dpixGetData(dpix); - - /* The output has wc + 1 border pixels stripped from each side - * of pixb, and hc + 1 border pixels stripped from top and bottom. */ - pixGetDimensions(pixb, &w, &h, NULL); - wd = w - 2 * (wc + 1); - hd = h - 2 * (hc + 1); - if (wd < 2 || hd < 2) { - L_ERROR("w or h too small for kernel\n", procName); - goto cleanup; - } - if ((pixd = pixCreate(wd, hd, 32)) == NULL) { - L_ERROR("pixd not made\n", procName); - goto cleanup; - } - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - - wincr = 2 * wc + 1; - hincr = 2 * hc + 1; - norm = 1.0 / ((l_float32)(wincr) * hincr); - for (i = 0; i < hd; i++) { - line1 = data + i * wpl; - line2 = data + (i + hincr) * wpl; - lined = datad + i * wpld; - for (j = 0; j < wd; j++) { - val = line2[j + wincr] - line2[j] - line1[j + wincr] + line1[j]; - ival = (l_uint32)(norm * val + 0.5); /* to round up */ - lined[j] = ival; - } - } - -cleanup: - dpixDestroy(&dpix); - pixDestroy(&pixb); - return pixd; -} - - -/*! - * \brief pixWindowedVariance() - * - * \param[in] pixm mean over window; 8 or 32 bpp grayscale - * \param[in] pixms mean square over window; 32 bpp - * \param[out] pfpixv [optional] float variance -- the ms deviation - * from the mean - * \param[out] pfpixrv [optional] float rms deviation from the mean - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The mean and mean square values are precomputed, using - * pixWindowedMean() and pixWindowedMeanSquare(). - * (2) Either or both of the variance and square-root of variance - * are returned as an fpix, where the variance is the - * average over the window of the mean square difference of - * the pixel value from the mean: - * <(p -- */ -l_ok -pixWindowedVariance(PIX *pixm, - PIX *pixms, - FPIX **pfpixv, - FPIX **pfpixrv) -{ -l_int32 i, j, w, h, ws, hs, ds, wplm, wplms, wplv, wplrv, valm, valms; -l_float32 var; -l_uint32 *linem, *linems, *datam, *datams; -l_float32 *linev, *linerv, *datav, *datarv; -FPIX *fpixv, *fpixrv; /* variance and square root of variance */ - - PROCNAME("pixWindowedVariance"); - - if (!pfpixv && !pfpixrv) - return ERROR_INT("no output requested", procName, 1); - if (pfpixv) *pfpixv = NULL; - if (pfpixrv) *pfpixrv = NULL; - if (!pixm || pixGetDepth(pixm) != 8) - return ERROR_INT("pixm undefined or not 8 bpp", procName, 1); - if (!pixms || pixGetDepth(pixms) != 32) - return ERROR_INT("pixms undefined or not 32 bpp", procName, 1); - pixGetDimensions(pixm, &w, &h, NULL); - pixGetDimensions(pixms, &ws, &hs, &ds); - if (w != ws || h != hs) - return ERROR_INT("pixm and pixms sizes differ", procName, 1); - - if (pfpixv) { - fpixv = fpixCreate(w, h); - *pfpixv = fpixv; - wplv = fpixGetWpl(fpixv); - datav = fpixGetData(fpixv); - } - if (pfpixrv) { - fpixrv = fpixCreate(w, h); - *pfpixrv = fpixrv; - wplrv = fpixGetWpl(fpixrv); - datarv = fpixGetData(fpixrv); - } - - wplm = pixGetWpl(pixm); - wplms = pixGetWpl(pixms); - datam = pixGetData(pixm); - datams = pixGetData(pixms); - for (i = 0; i < h; i++) { - linem = datam + i * wplm; - linems = datams + i * wplms; - if (pfpixv) - linev = datav + i * wplv; - if (pfpixrv) - linerv = datarv + i * wplrv; - for (j = 0; j < w; j++) { - valm = GET_DATA_BYTE(linem, j); - if (ds == 8) - valms = GET_DATA_BYTE(linems, j); - else /* ds == 32 */ - valms = (l_int32)linems[j]; - var = (l_float32)valms - (l_float32)valm * valm; - if (pfpixv) - linev[j] = var; - if (pfpixrv) - linerv[j] = (l_float32)sqrt(var); - } - } - - return 0; -} - - -/*! - * \brief pixMeanSquareAccum() - * - * \param[in] pixs 8 bpp grayscale - * \return dpix 64 bit array, or NULL on error - * - *)*(p -
)> =
-
*
- * (3) To visualize the results: - * ~ for both, use fpixDisplayMaxDynamicRange(). - * ~ for rms deviation, simply convert the output fpix to pix, - *
- * Notes: - * (1) Similar to pixBlockconvAccum(), this computes the - * sum of the squares of the pixel values in such a way - * that the value at (i,j) is the sum of all squares in - * the rectangle from the origin to (i,j). - * (2) The general recursion relation (v are squared pixel values) is - * a(i,j) = v(i,j) + a(i-1, j) + a(i, j-1) - a(i-1, j-1) - * For the first line, this reduces to the special case - * a(i,j) = v(i,j) + a(i, j-1) - * For the first column, the special case is - * a(i,j) = v(i,j) + a(i-1, j) - *- */ -DPIX * -pixMeanSquareAccum(PIX *pixs) -{ -l_int32 i, j, w, h, wpl, wpls, val; -l_uint32 *datas, *lines; -l_float64 *data, *line, *linep; -DPIX *dpix; - - PROCNAME("pixMeanSquareAccum"); - - - if (!pixs || (pixGetDepth(pixs) != 8)) - return (DPIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - if ((dpix = dpixCreate(w, h)) == NULL) - return (DPIX *)ERROR_PTR("dpix not made", procName, NULL); - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - data = dpixGetData(dpix); - wpl = dpixGetWpl(dpix); - - lines = datas; - line = data; - for (j = 0; j < w; j++) { /* first line */ - val = GET_DATA_BYTE(lines, j); - if (j == 0) - line[0] = (l_float64)(val) * val; - else - line[j] = line[j - 1] + (l_float64)(val) * val; - } - - /* Do the other lines */ - for (i = 1; i < h; i++) { - lines = datas + i * wpls; - line = data + i * wpl; /* current dest line */ - linep = line - wpl;; /* prev dest line */ - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(lines, j); - if (j == 0) - line[0] = linep[0] + (l_float64)(val) * val; - else - line[j] = line[j - 1] + linep[j] - linep[j - 1] - + (l_float64)(val) * val; - } - } - - return dpix; -} - - -/*----------------------------------------------------------------------* - * Binary block sum/rank * - *----------------------------------------------------------------------*/ -/*! - * \brief pixBlockrank() - * - * \param[in] pixs 1 bpp - * \param[in] pixacc pix [optional] 32 bpp - * \param[in] wc, hc half width/height of block sum/rank kernel - * \param[in] rank between 0.0 and 1.0; 0.5 is median filter - * \return pixd 1 bpp - * - *
- * Notes: - * (1) The full width and height of the convolution kernel - * are (2 * wc + 1) and (2 * hc + 1) - * (2) This returns a pixd where each pixel is a 1 if the - * neighborhood (2 * wc + 1) x (2 * hc + 1)) pixels - * contains the rank fraction of 1 pixels. Otherwise, - * the returned pixel is 0. Note that the special case - * of rank = 0.0 is always satisfied, so the returned - * pixd has all pixels with value 1. - * (3) If accum pix is null, make one, use it, and destroy it - * before returning; otherwise, just use the input accum pix - * (4) If both wc and hc are 0, returns a copy unless rank == 0.0, - * in which case this returns an all-ones image. - * (5) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1, - * where (w,h) are the dimensions of pixs. - *- */ -PIX * -pixBlockrank(PIX *pixs, - PIX *pixacc, - l_int32 wc, - l_int32 hc, - l_float32 rank) -{ -l_int32 w, h, d, thresh; -PIX *pixt, *pixd; - - PROCNAME("pixBlockrank"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); - if (rank < 0.0 || rank > 1.0) - return (PIX *)ERROR_PTR("rank must be in [0.0, 1.0]", procName, NULL); - - if (rank == 0.0) { - pixd = pixCreateTemplate(pixs); - pixSetAll(pixd); - return pixd; - } - - if (wc < 0) wc = 0; - if (hc < 0) hc = 0; - if (w < 2 * wc + 1 || h < 2 * hc + 1) { - wc = L_MIN(wc, (w - 1) / 2); - hc = L_MIN(hc, (h - 1) / 2); - L_WARNING("kernel too large; reducing!\n", procName); - L_INFO("wc = %d, hc = %d\n", procName, wc, hc); - } - if (wc == 0 && hc == 0) - return pixCopy(NULL, pixs); - - if ((pixt = pixBlocksum(pixs, pixacc, wc, hc)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, NULL); - - /* 1 bpp block rank filter output. - * Must invert because threshold gives 1 for values < thresh, - * but we need a 1 if the value is >= thresh. */ - thresh = (l_int32)(255. * rank); - pixd = pixThresholdToBinary(pixt, thresh); - pixInvert(pixd, pixd); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixBlocksum() - * - * \param[in] pixs 1 bpp - * \param[in] pixacc pix [optional] 32 bpp - * \param[in] wc, hc half width/height of block sum/rank kernel - * \return pixd 8 bpp - * - *
- * Notes: - * (1) If accum pix is null, make one and destroy it before - * returning; otherwise, just use the input accum pix - * (2) The full width and height of the convolution kernel - * are (2 * wc + 1) and (2 * hc + 1) - * (3) Use of wc = hc = 1, followed by pixInvert() on the - * 8 bpp result, gives a nice anti-aliased, and somewhat - * darkened, result on text. - * (4) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1, - * where (w,h) are the dimensions of pixs. - * (5) Returns in each dest pixel the sum of all src pixels - * that are within a block of size of the kernel, centered - * on the dest pixel. This sum is the number of src ON - * pixels in the block at each location, normalized to 255 - * for a block containing all ON pixels. For pixels near - * the boundary, where the block is not entirely contained - * within the image, we then multiply by a second normalization - * factor that is greater than one, so that all results - * are normalized by the number of participating pixels - * within the block. - *- */ -PIX * -pixBlocksum(PIX *pixs, - PIX *pixacc, - l_int32 wc, - l_int32 hc) -{ -l_int32 w, h, d, wplt, wpld; -l_uint32 *datat, *datad; -PIX *pixt, *pixd; - - PROCNAME("pixBlocksum"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); - if (wc < 0) wc = 0; - if (hc < 0) hc = 0; - if (w < 2 * wc + 1 || h < 2 * hc + 1) { - wc = L_MIN(wc, (w - 1) / 2); - hc = L_MIN(hc, (h - 1) / 2); - L_WARNING("kernel too large; reducing!\n", procName); - L_INFO("wc = %d, hc = %d\n", procName, wc, hc); - } - if (wc == 0 && hc == 0) - return pixCopy(NULL, pixs); - - if (pixacc) { - if (pixGetDepth(pixacc) != 32) - return (PIX *)ERROR_PTR("pixacc not 32 bpp", procName, NULL); - pixt = pixClone(pixacc); - } else { - if ((pixt = pixBlockconvAccum(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, NULL); - } - - /* 8 bpp block sum output */ - if ((pixd = pixCreate(w, h, 8)) == NULL) { - pixDestroy(&pixt); - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - pixCopyResolution(pixd, pixs); - - wpld = pixGetWpl(pixd); - wplt = pixGetWpl(pixt); - datad = pixGetData(pixd); - datat = pixGetData(pixt); - blocksumLow(datad, w, h, wpld, datat, wplt, wc, hc); - - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief blocksumLow() - * - * \param[in] datad of 8 bpp dest - * \param[in] w, h, wpl of 8 bpp dest - * \param[in] dataa of 32 bpp accum - * \param[in] wpla of 32 bpp accum - * \param[in] wc, hc convolution "half-width" and "half-height" - * \return void - * - *
- * Notes: - * (1) The full width and height of the convolution kernel - * are (2 * wc + 1) and (2 * hc + 1). - * (2) The lack of symmetry between the handling of the - * first (hc + 1) lines and the last (hc) lines, - * and similarly with the columns, is due to fact that - * for the pixel at (x,y), the accumulator values are - * taken at (x + wc, y + hc), (x - wc - 1, y + hc), - * (x + wc, y - hc - 1) and (x - wc - 1, y - hc - 1). - * (3) Compute sums of ON pixels within the block filter size, - * normalized between 0 and 255, as if there were no reduced - * area at the boundary. This under-estimates the value - * of the boundary pixels, so we multiply them by another - * normalization factor that is greater than 1. - * (4) This second normalization is done first for the first - * hc + 1 lines; then for the last hc lines; and finally - * for the first wc + 1 and last wc columns in the intermediate - * lines. - * (5) Required constraints are: wc < w and hc < h. - *- */ -static void -blocksumLow(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpl, - l_uint32 *dataa, - l_int32 wpla, - l_int32 wc, - l_int32 hc) -{ -l_int32 i, j, imax, imin, jmax, jmin; -l_int32 wn, hn, fwc, fhc, wmwc, hmhc; -l_float32 norm, normh, normw; -l_uint32 val; -l_uint32 *linemina, *linemaxa, *lined; - - PROCNAME("blocksumLow"); - - wmwc = w - wc; - hmhc = h - hc; - if (wmwc <= 0 || hmhc <= 0) { - L_ERROR("wc >= w || hc >=h\n", procName); - return; - } - fwc = 2 * wc + 1; - fhc = 2 * hc + 1; - norm = 255. / ((l_float32)(fwc) * fhc); - - /*------------------------------------------------------------* - * Compute, using b.c. only to set limits on the accum image * - *------------------------------------------------------------*/ - for (i = 0; i < h; i++) { - imin = L_MAX(i - 1 - hc, 0); - imax = L_MIN(i + hc, h - 1); - lined = datad + wpl * i; - linemina = dataa + wpla * imin; - linemaxa = dataa + wpla * imax; - for (j = 0; j < w; j++) { - jmin = L_MAX(j - 1 - wc, 0); - jmax = L_MIN(j + wc, w - 1); - val = linemaxa[jmax] - linemaxa[jmin] - - linemina[jmax] + linemina[jmin]; - val = (l_uint8)(norm * val); - SET_DATA_BYTE(lined, j, val); - } - } - - /*------------------------------------------------------------* - * Fix normalization for boundary pixels * - *------------------------------------------------------------*/ - for (i = 0; i <= hc; i++) { /* first hc + 1 lines */ - hn = hc + i; - normh = (l_float32)fhc / (l_float32)hn; /* > 1 */ - lined = datad + wpl * i; - for (j = 0; j <= wc; j++) { - wn = wc + j; - normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ - val = GET_DATA_BYTE(lined, j); - val = (l_uint8)(val * normh * normw); - SET_DATA_BYTE(lined, j, val); - } - for (j = wc + 1; j < wmwc; j++) { - val = GET_DATA_BYTE(lined, j); - val = (l_uint8)(val * normh); - SET_DATA_BYTE(lined, j, val); - } - for (j = wmwc; j < w; j++) { - wn = wc + w - j; - normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ - val = GET_DATA_BYTE(lined, j); - val = (l_uint8)(val * normh * normw); - SET_DATA_BYTE(lined, j, val); - } - } - - for (i = hmhc; i < h; i++) { /* last hc lines */ - hn = hc + h - i; - normh = (l_float32)fhc / (l_float32)hn; /* > 1 */ - lined = datad + wpl * i; - for (j = 0; j <= wc; j++) { - wn = wc + j; - normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ - val = GET_DATA_BYTE(lined, j); - val = (l_uint8)(val * normh * normw); - SET_DATA_BYTE(lined, j, val); - } - for (j = wc + 1; j < wmwc; j++) { - val = GET_DATA_BYTE(lined, j); - val = (l_uint8)(val * normh); - SET_DATA_BYTE(lined, j, val); - } - for (j = wmwc; j < w; j++) { - wn = wc + w - j; - normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ - val = GET_DATA_BYTE(lined, j); - val = (l_uint8)(val * normh * normw); - SET_DATA_BYTE(lined, j, val); - } - } - - for (i = hc + 1; i < hmhc; i++) { /* intermediate lines */ - lined = datad + wpl * i; - for (j = 0; j <= wc; j++) { /* first wc + 1 columns */ - wn = wc + j; - normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ - val = GET_DATA_BYTE(lined, j); - val = (l_uint8)(val * normw); - SET_DATA_BYTE(lined, j, val); - } - for (j = wmwc; j < w; j++) { /* last wc columns */ - wn = wc + w - j; - normw = (l_float32)fwc / (l_float32)wn; /* > 1 */ - val = GET_DATA_BYTE(lined, j); - val = (l_uint8)(val * normw); - SET_DATA_BYTE(lined, j, val); - } - } - - return; -} - - -/*----------------------------------------------------------------------* - * Census transform * - *----------------------------------------------------------------------*/ -/*! - * \brief pixCensusTransform() - * - * \param[in] pixs 8 bpp - * \param[in] halfsize of square over which neighbors are averaged - * \param[in] pixacc [optional] 32 bpp pix - * \return pixd 1 bpp - * - *
- * Notes: - * (1) The Census transform was invented by Ramin Zabih and John Woodfill - * ("Non-parametric local transforms for computing visual - * correspondence", Third European Conference on Computer Vision, - * Stockholm, Sweden, May 1994); see publications at - * http://www.cs.cornell.edu/~rdz/index.htm - * This compares each pixel against the average of its neighbors, - * in a square of odd dimension centered on the pixel. - * If the pixel is greater than the average of its neighbors, - * the output pixel value is 1; otherwise it is 0. - * (2) This can be used as an encoding for an image that is - * fairly robust against slow illumination changes, with - * applications in image comparison and mosaicing. - * (3) The size of the convolution kernel is (2 * halfsize + 1) - * on a side. The halfsize parameter must be >= 1. - * (4) If accum pix is null, make one, use it, and destroy it - * before returning; otherwise, just use the input accum pix - *- */ -PIX * -pixCensusTransform(PIX *pixs, - l_int32 halfsize, - PIX *pixacc) -{ -l_int32 i, j, w, h, wpls, wplv, wpld; -l_int32 vals, valv; -l_uint32 *datas, *datav, *datad, *lines, *linev, *lined; -PIX *pixav, *pixd; - - PROCNAME("pixCensusTransform"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (halfsize < 1) - return (PIX *)ERROR_PTR("halfsize must be >= 1", procName, NULL); - - /* Get the average of each pixel with its neighbors */ - if ((pixav = pixBlockconvGray(pixs, pixacc, halfsize, halfsize)) - == NULL) - return (PIX *)ERROR_PTR("pixav not made", procName, NULL); - - /* Subtract the pixel from the average, and then compare - * the pixel value with the remaining average */ - pixGetDimensions(pixs, &w, &h, NULL); - if ((pixd = pixCreate(w, h, 1)) == NULL) { - pixDestroy(&pixav); - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - datas = pixGetData(pixs); - datav = pixGetData(pixav); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wplv = pixGetWpl(pixav); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - linev = datav + i * wplv; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - vals = GET_DATA_BYTE(lines, j); - valv = GET_DATA_BYTE(linev, j); - if (vals > valv) - SET_DATA_BIT(lined, j); - } - } - - pixDestroy(&pixav); - return pixd; -} - - -/*----------------------------------------------------------------------* - * Generic convolution * - *----------------------------------------------------------------------*/ -/*! - * \brief pixConvolve() - * - * \param[in] pixs 8, 16, 32 bpp; no colormap - * \param[in] kel kernel - * \param[in] outdepth of pixd: 8, 16 or 32 - * \param[in] normflag 1 to normalize kernel to unit sum; 0 otherwise - * \return pixd 8, 16 or 32 bpp - * - *
- * Notes: - * (1) This gives a convolution with an arbitrary kernel. - * (2) The input pixs must have only one sample/pixel. - * To do a convolution on an RGB image, use pixConvolveRGB(). - * (3) The parameter %outdepth determines the depth of the result. - * If the kernel is normalized to unit sum, the output values - * can never exceed 255, so an output depth of 8 bpp is sufficient. - * If the kernel is not normalized, it may be necessary to use - * 16 or 32 bpp output to avoid overflow. - * (4) If normflag == 1, the result is normalized by scaling all - * kernel values for a unit sum. If the sum of kernel values - * is very close to zero, the kernel can not be normalized and - * the convolution will not be performed. A warning is issued. - * (5) The kernel values can be positive or negative, but the - * result for the convolution can only be stored as a positive - * number. Consequently, if it goes negative, the choices are - * to clip to 0 or take the absolute value. We're choosing - * to take the absolute value. (Another possibility would be - * to output a second unsigned image for the negative values.) - * If you want to get a clipped result, or to keep the negative - * values in the result, use fpixConvolve(), with the - * converters in fpix2.c between pix and fpix. - * (6) This uses a mirrored border to avoid special casing on - * the boundaries. - * (7) To get a subsampled output, call l_setConvolveSampling(). - * The time to make a subsampled output is reduced by the - * product of the sampling factors. - * (8) The function is slow, running at about 12 machine cycles for - * each pixel-op in the convolution. For example, with a 3 GHz - * cpu, a 1 Mpixel grayscale image, and a kernel with - * (sx * sy) = 25 elements, the convolution takes about 100 msec. - *- */ -PIX * -pixConvolve(PIX *pixs, - L_KERNEL *kel, - l_int32 outdepth, - l_int32 normflag) -{ -l_int32 i, j, id, jd, k, m, w, h, d, wd, hd, sx, sy, cx, cy, wplt, wpld; -l_int32 val; -l_uint32 *datat, *datad, *linet, *lined; -l_float32 sum; -L_KERNEL *keli, *keln; -PIX *pixt, *pixd; - - PROCNAME("pixConvolve"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8 && d != 16 && d != 32) - return (PIX *)ERROR_PTR("pixs not 8, 16, or 32 bpp", procName, NULL); - if (!kel) - return (PIX *)ERROR_PTR("kel not defined", procName, NULL); - - pixd = NULL; - - keli = kernelInvert(kel); - kernelGetParameters(keli, &sy, &sx, &cy, &cx); - if (normflag) - keln = kernelNormalize(keli, 1.0); - else - keln = kernelCopy(keli); - - if ((pixt = pixAddMirroredBorder(pixs, cx, sx - cx, cy, sy - cy)) == NULL) { - L_ERROR("pixt not made\n", procName); - goto cleanup; - } - - wd = (w + ConvolveSamplingFactX - 1) / ConvolveSamplingFactX; - hd = (h + ConvolveSamplingFactY - 1) / ConvolveSamplingFactY; - pixd = pixCreate(wd, hd, outdepth); - datat = pixGetData(pixt); - datad = pixGetData(pixd); - wplt = pixGetWpl(pixt); - wpld = pixGetWpl(pixd); - for (i = 0, id = 0; id < hd; i += ConvolveSamplingFactY, id++) { - lined = datad + id * wpld; - for (j = 0, jd = 0; jd < wd; j += ConvolveSamplingFactX, jd++) { - sum = 0.0; - for (k = 0; k < sy; k++) { - linet = datat + (i + k) * wplt; - if (d == 8) { - for (m = 0; m < sx; m++) { - val = GET_DATA_BYTE(linet, j + m); - sum += val * keln->data[k][m]; - } - } else if (d == 16) { - for (m = 0; m < sx; m++) { - val = GET_DATA_TWO_BYTES(linet, j + m); - sum += val * keln->data[k][m]; - } - } else { /* d == 32 */ - for (m = 0; m < sx; m++) { - val = *(linet + j + m); - sum += val * keln->data[k][m]; - } - } - } - if (sum < 0.0) sum = -sum; /* make it non-negative */ - if (outdepth == 8) - SET_DATA_BYTE(lined, jd, (l_int32)(sum + 0.5)); - else if (outdepth == 16) - SET_DATA_TWO_BYTES(lined, jd, (l_int32)(sum + 0.5)); - else /* outdepth == 32 */ - *(lined + jd) = (l_uint32)(sum + 0.5); - } - } - -cleanup: - kernelDestroy(&keli); - kernelDestroy(&keln); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixConvolveSep() - * - * \param[in] pixs 8, 16, 32 bpp; no colormap - * \param[in] kelx x-dependent kernel - * \param[in] kely y-dependent kernel - * \param[in] outdepth of pixd: 8, 16 or 32 - * \param[in] normflag 1 to normalize kernel to unit sum; 0 otherwise - * \return pixd 8, 16 or 32 bpp - * - *
- * Notes: - * (1) This does a convolution with a separable kernel that is - * is a sequence of convolutions in x and y. The two - * one-dimensional kernel components must be input separately; - * the full kernel is the product of these components. - * The support for the full kernel is thus a rectangular region. - * (2) The input pixs must have only one sample/pixel. - * To do a convolution on an RGB image, use pixConvolveSepRGB(). - * (3) The parameter %outdepth determines the depth of the result. - * If the kernel is normalized to unit sum, the output values - * can never exceed 255, so an output depth of 8 bpp is sufficient. - * If the kernel is not normalized, it may be necessary to use - * 16 or 32 bpp output to avoid overflow. - * (2) The %normflag parameter is used as in pixConvolve(). - * (4) The kernel values can be positive or negative, but the - * result for the convolution can only be stored as a positive - * number. Consequently, if it goes negative, the choices are - * to clip to 0 or take the absolute value. We're choosing - * the former for now. Another possibility would be to output - * a second unsigned image for the negative values. - * (5) Warning: if you use l_setConvolveSampling() to get a - * subsampled output, and the sampling factor is larger than - * the kernel half-width, it is faster to use the non-separable - * version pixConvolve(). This is because the first convolution - * here must be done on every raster line, regardless of the - * vertical sampling factor. If the sampling factor is smaller - * than kernel half-width, it's faster to use the separable - * convolution. - * (6) This uses mirrored borders to avoid special casing on - * the boundaries. - *- */ -PIX * -pixConvolveSep(PIX *pixs, - L_KERNEL *kelx, - L_KERNEL *kely, - l_int32 outdepth, - l_int32 normflag) -{ -l_int32 d, xfact, yfact; -L_KERNEL *kelxn, *kelyn; -PIX *pixt, *pixd; - - PROCNAME("pixConvolveSep"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - if (d != 8 && d != 16 && d != 32) - return (PIX *)ERROR_PTR("pixs not 8, 16, or 32 bpp", procName, NULL); - if (!kelx) - return (PIX *)ERROR_PTR("kelx not defined", procName, NULL); - if (!kely) - return (PIX *)ERROR_PTR("kely not defined", procName, NULL); - - xfact = ConvolveSamplingFactX; - yfact = ConvolveSamplingFactY; - if (normflag) { - kelxn = kernelNormalize(kelx, 1000.0); - kelyn = kernelNormalize(kely, 0.001); - l_setConvolveSampling(xfact, 1); - pixt = pixConvolve(pixs, kelxn, 32, 0); - l_setConvolveSampling(1, yfact); - pixd = pixConvolve(pixt, kelyn, outdepth, 0); - l_setConvolveSampling(xfact, yfact); /* restore */ - kernelDestroy(&kelxn); - kernelDestroy(&kelyn); - } else { /* don't normalize */ - l_setConvolveSampling(xfact, 1); - pixt = pixConvolve(pixs, kelx, 32, 0); - l_setConvolveSampling(1, yfact); - pixd = pixConvolve(pixt, kely, outdepth, 0); - l_setConvolveSampling(xfact, yfact); - } - - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixConvolveRGB() - * - * \param[in] pixs 32 bpp rgb - * \param[in] kel kernel - * \return pixd 32 bpp rgb - * - *
- * Notes: - * (1) This gives a convolution on an RGB image using an - * arbitrary kernel (which we normalize to keep each - * component within the range [0 ... 255]. - * (2) The input pixs must be RGB. - * (3) The kernel values can be positive or negative, but the - * result for the convolution can only be stored as a positive - * number. Consequently, if it goes negative, we clip the - * result to 0. - * (4) To get a subsampled output, call l_setConvolveSampling(). - * The time to make a subsampled output is reduced by the - * product of the sampling factors. - * (5) This uses a mirrored border to avoid special casing on - * the boundaries. - *- */ -PIX * -pixConvolveRGB(PIX *pixs, - L_KERNEL *kel) -{ -PIX *pixt, *pixr, *pixg, *pixb, *pixd; - - PROCNAME("pixConvolveRGB"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs is not 32 bpp", procName, NULL); - if (!kel) - return (PIX *)ERROR_PTR("kel not defined", procName, NULL); - - pixt = pixGetRGBComponent(pixs, COLOR_RED); - pixr = pixConvolve(pixt, kel, 8, 1); - pixDestroy(&pixt); - pixt = pixGetRGBComponent(pixs, COLOR_GREEN); - pixg = pixConvolve(pixt, kel, 8, 1); - pixDestroy(&pixt); - pixt = pixGetRGBComponent(pixs, COLOR_BLUE); - pixb = pixConvolve(pixt, kel, 8, 1); - pixDestroy(&pixt); - pixd = pixCreateRGBImage(pixr, pixg, pixb); - - pixDestroy(&pixr); - pixDestroy(&pixg); - pixDestroy(&pixb); - return pixd; -} - - -/*! - * \brief pixConvolveRGBSep() - * - * \param[in] pixs 32 bpp rgb - * \param[in] kelx x-dependent kernel - * \param[in] kely y-dependent kernel - * \return pixd 32 bpp rgb - * - *
- * Notes: - * (1) This does a convolution on an RGB image using a separable - * kernel that is a sequence of convolutions in x and y. The two - * one-dimensional kernel components must be input separately; - * the full kernel is the product of these components. - * The support for the full kernel is thus a rectangular region. - * (2) The kernel values can be positive or negative, but the - * result for the convolution can only be stored as a positive - * number. Consequently, if it goes negative, we clip the - * result to 0. - * (3) To get a subsampled output, call l_setConvolveSampling(). - * The time to make a subsampled output is reduced by the - * product of the sampling factors. - * (4) This uses a mirrored border to avoid special casing on - * the boundaries. - *- */ -PIX * -pixConvolveRGBSep(PIX *pixs, - L_KERNEL *kelx, - L_KERNEL *kely) -{ -PIX *pixt, *pixr, *pixg, *pixb, *pixd; - - PROCNAME("pixConvolveRGBSep"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs is not 32 bpp", procName, NULL); - if (!kelx || !kely) - return (PIX *)ERROR_PTR("kelx, kely not both defined", procName, NULL); - - pixt = pixGetRGBComponent(pixs, COLOR_RED); - pixr = pixConvolveSep(pixt, kelx, kely, 8, 1); - pixDestroy(&pixt); - pixt = pixGetRGBComponent(pixs, COLOR_GREEN); - pixg = pixConvolveSep(pixt, kelx, kely, 8, 1); - pixDestroy(&pixt); - pixt = pixGetRGBComponent(pixs, COLOR_BLUE); - pixb = pixConvolveSep(pixt, kelx, kely, 8, 1); - pixDestroy(&pixt); - pixd = pixCreateRGBImage(pixr, pixg, pixb); - - pixDestroy(&pixr); - pixDestroy(&pixg); - pixDestroy(&pixb); - return pixd; -} - - -/*----------------------------------------------------------------------* - * Generic convolution with float array * - *----------------------------------------------------------------------*/ -/*! - * \brief fpixConvolve() - * - * \param[in] fpixs 32 bit float array - * \param[in] kel kernel - * \param[in] normflag 1 to normalize kernel to unit sum; 0 otherwise - * \return fpixd 32 bit float array - * - *
- * Notes: - * (1) This gives a float convolution with an arbitrary kernel. - * (2) If normflag == 1, the result is normalized by scaling all - * kernel values for a unit sum. If the sum of kernel values - * is very close to zero, the kernel can not be normalized and - * the convolution will not be performed. A warning is issued. - * (3) With the FPix, there are no issues about negative - * array or kernel values. The convolution is performed - * with single precision arithmetic. - * (4) To get a subsampled output, call l_setConvolveSampling(). - * The time to make a subsampled output is reduced by the - * product of the sampling factors. - * (5) This uses a mirrored border to avoid special casing on - * the boundaries. - *- */ -FPIX * -fpixConvolve(FPIX *fpixs, - L_KERNEL *kel, - l_int32 normflag) -{ -l_int32 i, j, id, jd, k, m, w, h, wd, hd, sx, sy, cx, cy, wplt, wpld; -l_float32 val; -l_float32 *datat, *datad, *linet, *lined; -l_float32 sum; -L_KERNEL *keli, *keln; -FPIX *fpixt, *fpixd; - - PROCNAME("fpixConvolve"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - if (!kel) - return (FPIX *)ERROR_PTR("kel not defined", procName, NULL); - - fpixd = NULL; - - keli = kernelInvert(kel); - kernelGetParameters(keli, &sy, &sx, &cy, &cx); - if (normflag) - keln = kernelNormalize(keli, 1.0); - else - keln = kernelCopy(keli); - - fpixGetDimensions(fpixs, &w, &h); - fpixt = fpixAddMirroredBorder(fpixs, cx, sx - cx, cy, sy - cy); - if (!fpixt) { - L_ERROR("fpixt not made\n", procName); - goto cleanup; - } - - wd = (w + ConvolveSamplingFactX - 1) / ConvolveSamplingFactX; - hd = (h + ConvolveSamplingFactY - 1) / ConvolveSamplingFactY; - fpixd = fpixCreate(wd, hd); - datat = fpixGetData(fpixt); - datad = fpixGetData(fpixd); - wplt = fpixGetWpl(fpixt); - wpld = fpixGetWpl(fpixd); - for (i = 0, id = 0; id < hd; i += ConvolveSamplingFactY, id++) { - lined = datad + id * wpld; - for (j = 0, jd = 0; jd < wd; j += ConvolveSamplingFactX, jd++) { - sum = 0.0; - for (k = 0; k < sy; k++) { - linet = datat + (i + k) * wplt; - for (m = 0; m < sx; m++) { - val = *(linet + j + m); - sum += val * keln->data[k][m]; - } - } - *(lined + jd) = sum; - } - } - -cleanup: - kernelDestroy(&keli); - kernelDestroy(&keln); - fpixDestroy(&fpixt); - return fpixd; -} - - -/*! - * \brief fpixConvolveSep() - * - * \param[in] fpixs 32 bit float array - * \param[in] kelx x-dependent kernel - * \param[in] kely y-dependent kernel - * \param[in] normflag 1 to normalize kernel to unit sum; 0 otherwise - * \return fpixd 32 bit float array - * - *
- * Notes: - * (1) This does a convolution with a separable kernel that is - * is a sequence of convolutions in x and y. The two - * one-dimensional kernel components must be input separately; - * the full kernel is the product of these components. - * The support for the full kernel is thus a rectangular region. - * (2) The normflag parameter is used as in fpixConvolve(). - * (3) Warning: if you use l_setConvolveSampling() to get a - * subsampled output, and the sampling factor is larger than - * the kernel half-width, it is faster to use the non-separable - * version pixConvolve(). This is because the first convolution - * here must be done on every raster line, regardless of the - * vertical sampling factor. If the sampling factor is smaller - * than kernel half-width, it's faster to use the separable - * convolution. - * (4) This uses mirrored borders to avoid special casing on - * the boundaries. - *- */ -FPIX * -fpixConvolveSep(FPIX *fpixs, - L_KERNEL *kelx, - L_KERNEL *kely, - l_int32 normflag) -{ -l_int32 xfact, yfact; -L_KERNEL *kelxn, *kelyn; -FPIX *fpixt, *fpixd; - - PROCNAME("fpixConvolveSep"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!kelx) - return (FPIX *)ERROR_PTR("kelx not defined", procName, NULL); - if (!kely) - return (FPIX *)ERROR_PTR("kely not defined", procName, NULL); - - xfact = ConvolveSamplingFactX; - yfact = ConvolveSamplingFactY; - if (normflag) { - kelxn = kernelNormalize(kelx, 1.0); - kelyn = kernelNormalize(kely, 1.0); - l_setConvolveSampling(xfact, 1); - fpixt = fpixConvolve(fpixs, kelxn, 0); - l_setConvolveSampling(1, yfact); - fpixd = fpixConvolve(fpixt, kelyn, 0); - l_setConvolveSampling(xfact, yfact); /* restore */ - kernelDestroy(&kelxn); - kernelDestroy(&kelyn); - } else { /* don't normalize */ - l_setConvolveSampling(xfact, 1); - fpixt = fpixConvolve(fpixs, kelx, 0); - l_setConvolveSampling(1, yfact); - fpixd = fpixConvolve(fpixt, kely, 0); - l_setConvolveSampling(xfact, yfact); - } - - fpixDestroy(&fpixt); - return fpixd; -} - - -/*------------------------------------------------------------------------* - * Convolution with bias (for non-negative output) * - *------------------------------------------------------------------------*/ -/*! - * \brief pixConvolveWithBias() - * - * \param[in] pixs 8 bpp; no colormap - * \param[in] kel1 - * \param[in] kel2 can be null; use if separable - * \param[in] force8 if 1, force output to 8 bpp; otherwise, determine - * output depth by the dynamic range of pixel values - * \param[out] pbias applied bias - * \return pixd 8 or 16 bpp - * - *
- * Notes: - * (1) This does a convolution with either a single kernel or - * a pair of separable kernels, and automatically applies whatever - * bias (shift) is required so that the resulting pixel values - * are non-negative. - * (2) The kernel is always normalized. If there are no negative - * values in the kernel, a standard normalized convolution is - * performed, with 8 bpp output. If the sum of kernel values is - * very close to zero, the kernel can not be normalized and - * the convolution will not be performed. An error message results. - * (3) If there are negative values in the kernel, the pix is - * converted to an fpix, the convolution is done on the fpix, and - * a bias (shift) may need to be applied. - * (4) If force8 == TRUE and the range of values after the convolution - * is > 255, the output values will be scaled to fit in [0 ... 255]. - * If force8 == FALSE, the output will be either 8 or 16 bpp, - * to accommodate the dynamic range of output values without scaling. - *- */ -PIX * -pixConvolveWithBias(PIX *pixs, - L_KERNEL *kel1, - L_KERNEL *kel2, - l_int32 force8, - l_int32 *pbias) -{ -l_int32 outdepth; -l_float32 min1, min2, min, minval, maxval, range; -FPIX *fpix1, *fpix2; -PIX *pixd; - - PROCNAME("pixConvolveWithBias"); - - if (!pbias) - return (PIX *)ERROR_PTR("&bias not defined", procName, NULL); - *pbias = 0; - if (!pixs || pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); - if (!kel1) - return (PIX *)ERROR_PTR("kel1 not defined", procName, NULL); - - /* Determine if negative values can be produced in the convolution */ - kernelGetMinMax(kel1, &min1, NULL); - min2 = 0.0; - if (kel2) - kernelGetMinMax(kel2, &min2, NULL); - min = L_MIN(min1, min2); - - if (min >= 0.0) { - if (!kel2) - return pixConvolve(pixs, kel1, 8, 1); - else - return pixConvolveSep(pixs, kel1, kel2, 8, 1); - } - - /* Bias may need to be applied; convert to fpix and convolve */ - fpix1 = pixConvertToFPix(pixs, 1); - if (!kel2) - fpix2 = fpixConvolve(fpix1, kel1, 1); - else - fpix2 = fpixConvolveSep(fpix1, kel1, kel2, 1); - fpixDestroy(&fpix1); - - /* Determine the bias and the dynamic range. - * If the dynamic range is <= 255, just shift the values by the - * bias, if any. - * If the dynamic range is > 255, there are two cases: - * (1) the output depth is not forced to 8 bpp - * ==> apply the bias without scaling; outdepth = 16 - * (2) the output depth is forced to 8 - * ==> linearly map the pixel values to [0 ... 255]. */ - fpixGetMin(fpix2, &minval, NULL, NULL); - fpixGetMax(fpix2, &maxval, NULL, NULL); - range = maxval - minval; - *pbias = (minval < 0.0) ? -minval : 0.0; - fpixAddMultConstant(fpix2, *pbias, 1.0); /* shift: min val ==> 0 */ - if (range <= 255 || !force8) { /* no scaling of output values */ - outdepth = (range > 255) ? 16 : 8; - } else { /* scale output values to fit in 8 bpp */ - fpixAddMultConstant(fpix2, 0.0, (255.0 / range)); - outdepth = 8; - } - - /* Convert back to pix; it won't do any clipping */ - pixd = fpixConvertToPix(fpix2, outdepth, L_CLIP_TO_ZERO, 0); - fpixDestroy(&fpix2); - - return pixd; -} - - -/*------------------------------------------------------------------------* - * Set parameter for convolution subsampling * - *------------------------------------------------------------------------*/ -/*! - * \brief l_setConvolveSampling() - - * - * \param[in] xfact, yfact integer >= 1 - * \return void - * - *
- * Notes: - * (1) This sets the x and y output subsampling factors for generic pix - * and fpix convolution. The default values are 1 (no subsampling). - *- */ -void -l_setConvolveSampling(l_int32 xfact, - l_int32 yfact) -{ - if (xfact < 1) xfact = 1; - if (yfact < 1) yfact = 1; - ConvolveSamplingFactX = xfact; - ConvolveSamplingFactY = yfact; -} - - -/*------------------------------------------------------------------------* - * Additive gaussian noise * - *------------------------------------------------------------------------*/ -/*! - * \brief pixAddGaussianNoise() - * - * \param[in] pixs 8 bpp gray or 32 bpp rgb; no colormap - * \param[in] stdev of noise - * \return pixd 8 or 32 bpp, or NULL on error - * - *
- * Notes: - * (1) This adds noise to each pixel, taken from a normal - * distribution with zero mean and specified standard deviation. - *- */ -PIX * -pixAddGaussianNoise(PIX *pixs, - l_float32 stdev) -{ -l_int32 i, j, w, h, d, wpls, wpld, val, rval, gval, bval; -l_uint32 pixel; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; - - PROCNAME("pixAddGaussianNoise"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8 && d != 32) - return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); - - pixd = pixCreateTemplateNoInit(pixs); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - if (d == 8) { - val = GET_DATA_BYTE(lines, j); - val += (l_int32)(stdev * gaussDistribSampling() + 0.5); - val = L_MIN(255, L_MAX(0, val)); - SET_DATA_BYTE(lined, j, val); - } else { /* d = 32 */ - pixel = *(lines + j); - extractRGBValues(pixel, &rval, &gval, &bval); - rval += (l_int32)(stdev * gaussDistribSampling() + 0.5); - rval = L_MIN(255, L_MAX(0, rval)); - gval += (l_int32)(stdev * gaussDistribSampling() + 0.5); - gval = L_MIN(255, L_MAX(0, gval)); - bval += (l_int32)(stdev * gaussDistribSampling() + 0.5); - bval = L_MIN(255, L_MAX(0, bval)); - composeRGBPixel(rval, gval, bval, lined + j); - } - } - } - return pixd; -} - - -/*! - * \brief gaussDistribSampling() - * - * \return gaussian distributed variable with zero mean and unit stdev - * - *
- * Notes: - * (1) For an explanation of the Box-Muller method for generating - * a normally distributed random variable with zero mean and - * unit standard deviation, see Numerical Recipes in C, - * 2nd edition, p. 288ff. - * (2) This can be called sequentially to get samples that can be - * used for adding noise to each pixel of an image, for example. - *- */ -l_float32 -gaussDistribSampling(void) -{ -static l_int32 select = 0; /* flips between 0 and 1 on successive calls */ -static l_float32 saveval; -l_float32 frand, xval, yval, rsq, factor; - - if (select == 0) { - while (1) { /* choose a point in a 2x2 square, centered at origin */ - frand = (l_float32)rand() / (l_float32)RAND_MAX; - xval = 2.0 * frand - 1.0; - frand = (l_float32)rand() / (l_float32)RAND_MAX; - yval = 2.0 * frand - 1.0; - rsq = xval * xval + yval * yval; - if (rsq > 0.0 && rsq < 1.0) /* point is inside the unit circle */ - break; - } - factor = sqrt(-2.0 * log(rsq) / rsq); - saveval = xval * factor; - select = 1; - return yval * factor; - } - else { - select = 0; - return saveval; - } -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/correlscore.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/correlscore.c deleted file mode 100644 index c5b0e06b..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/correlscore.c +++ /dev/null @@ -1,883 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -/* - * correlscore.c - * - * These are functions for computing correlation between - * pairs of 1 bpp images. - * - * Optimized 2 pix correlators (for jbig2 clustering) - * l_int32 pixCorrelationScore() - * l_int32 pixCorrelationScoreThresholded() - * - * Simple 2 pix correlators - * l_int32 pixCorrelationScoreSimple() - * l_int32 pixCorrelationScoreShifted() - * - * There are other, more application-oriented functions, that - * compute the correlation between two binary images, taking into - * account small translational shifts, between two binary images. - * These are: - * compare.c: pixBestCorrelation() - * Uses coarse-to-fine translations of full image - * recogident.c: pixCorrelationBestShift() - * Uses small shifts between c.c. centroids. - */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * We check first that the two pix are roughly the same size. - * For jbclass (jbig2) applications at roughly 300 ppi, maxdiffw and - * maxdiffh should be at least 2. - * - * Only if they meet that criterion do we compare the bitmaps. - * The centroid difference is used to align the two images to the - * nearest integer for the correlation. - * - * The correlation score is the ratio of the square of the number of - * pixels in the AND of the two bitmaps to the product of the number - * of ON pixels in each. Denote the number of ON pixels in pix1 - * by |1|, the number in pix2 by |2|, and the number in the AND - * of pix1 and pix2 by |1 & 2|. The correlation score is then - * (|1 & 2|)**2 / (|1|*|2|). - * - * This score is compared with an input threshold, which can - * be modified depending on the weight of the template. - * The modified threshold is - * thresh + (1.0 - thresh) * weight * R - * where - * weight is a fixed input factor between 0.0 and 1.0 - * R = |2| / area(2) - * and area(2) is the total number of pixels in 2 (i.e., width x height). - * - * To understand why a weight factor is useful, consider what happens - * with thick, sans-serif characters that look similar and have a value - * of R near 1. Different characters can have a high correlation value, - * and the classifier will make incorrect substitutions. The weight - * factor raises the threshold for these characters. - * - * Yet another approach to reduce such substitutions is to run the classifier - * in a non-greedy way, matching to the template with the highest - * score, not the first template with a score satisfying the matching - * constraint. However, this is not particularly effective. - * - * The implementation here gives the same result as in - * pixCorrelationScoreSimple(), where a temporary Pix is made to hold - * the AND and implementation uses rasterop: - * pixt = pixCreateTemplate(pix1); - * pixRasterop(pixt, idelx, idely, wt, ht, PIX_SRC, pix2, 0, 0); - * pixRasterop(pixt, 0, 0, wi, hi, PIX_SRC & PIX_DST, pix1, 0, 0); - * pixCountPixels(pixt, &count, tab); - * pixDestroy(&pixt); - * However, here it is done in a streaming fashion, counting as it goes, - * and touching memory exactly once, giving a 3-4x speedup over the - * simple implementation. This very fast correlation matcher was - * contributed by William Rucklidge. - *- */ -l_ok -pixCorrelationScore(PIX *pix1, - PIX *pix2, - l_int32 area1, - l_int32 area2, - l_float32 delx, /* x(1) - x(3) */ - l_float32 dely, /* y(1) - y(3) */ - l_int32 maxdiffw, - l_int32 maxdiffh, - l_int32 *tab, - l_float32 *pscore) -{ -l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, count; -l_int32 wpl1, wpl2, lorow, hirow, locol, hicol; -l_int32 x, y, pix1lskip, pix2lskip, rowwords1, rowwords2; -l_uint32 word1, word2, andw; -l_uint32 *row1, *row2; - - PROCNAME("pixCorrelationScore"); - - if (!pscore) - return ERROR_INT("&score not defined", procName, 1); - *pscore = 0.0; - if (!pix1 || pixGetDepth(pix1) != 1) - return ERROR_INT("pix1 undefined or not 1 bpp", procName, 1); - if (!pix2 || pixGetDepth(pix2) != 1) - return ERROR_INT("pix2 undefined or not 1 bpp", procName, 1); - if (!tab) - return ERROR_INT("tab not defined", procName, 1); - if (area1 <= 0 || area2 <= 0) - return ERROR_INT("areas must be > 0", procName, 1); - - /* Eliminate based on size difference */ - pixGetDimensions(pix1, &wi, &hi, NULL); - pixGetDimensions(pix2, &wt, &ht, NULL); - delw = L_ABS(wi - wt); - if (delw > maxdiffw) - return 0; - delh = L_ABS(hi - ht); - if (delh > maxdiffh) - return 0; - - /* Round difference to nearest integer */ - if (delx >= 0) - idelx = (l_int32)(delx + 0.5); - else - idelx = (l_int32)(delx - 0.5); - if (dely >= 0) - idely = (l_int32)(dely + 0.5); - else - idely = (l_int32)(dely - 0.5); - - count = 0; - wpl1 = pixGetWpl(pix1); - wpl2 = pixGetWpl(pix2); - rowwords2 = wpl2; - - /* What rows of pix1 need to be considered? Only those underlying the - * shifted pix2. */ - lorow = L_MAX(idely, 0); - hirow = L_MIN(ht + idely, hi); - - /* Get the pointer to the first row of each image that will be - * considered. */ - row1 = pixGetData(pix1) + wpl1 * lorow; - row2 = pixGetData(pix2) + wpl2 * (lorow - idely); - - /* Similarly, figure out which columns of pix1 will be considered. */ - locol = L_MAX(idelx, 0); - hicol = L_MIN(wt + idelx, wi); - - if (idelx >= 32) { - /* pix2 is shifted far enough to the right that pix1's first - * word(s) won't contribute to the count. Increment its - * pointer to point to the first word that will contribute, - * and adjust other values accordingly. */ - pix1lskip = idelx >> 5; /* # of words to skip on left */ - row1 += pix1lskip; - locol -= pix1lskip << 5; - hicol -= pix1lskip << 5; - idelx &= 31; - } else if (idelx <= -32) { - /* pix2 is shifted far enough to the left that its first word(s) - * won't contribute to the count. Increment its pointer - * to point to the first word that will contribute, - * and adjust other values accordingly. */ - pix2lskip = -((idelx + 31) >> 5); /* # of words to skip on left */ - row2 += pix2lskip; - rowwords2 -= pix2lskip; - idelx += pix2lskip << 5; - } - - if ((locol >= hicol) || (lorow >= hirow)) { /* there is no overlap */ - count = 0; - } else { - /* How many words of each row of pix1 need to be considered? */ - rowwords1 = (hicol + 31) >> 5; - - if (idelx == 0) { - /* There's no lateral offset; simple case. */ - for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { - for (x = 0; x < rowwords1; x++) { - andw = row1[x] & row2[x]; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - } - } - } else if (idelx > 0) { - /* pix2 is shifted to the right. word 0 of pix1 is touched by - * word 0 of pix2; word 1 of pix1 is touched by word 0 and word - * 1 of pix2, and so on up to the last word of pix1 (word N), - * which is touched by words N-1 and N of pix1... if there is a - * word N. Handle the two cases (pix2 has N-1 words and pix2 - * has at least N words) separately. - * - * Note: we know that pix2 has at least N-1 words (i.e., - * rowwords2 >= rowwords1 - 1) by the following logic. - * We can pretend that idelx <= 31 because the >= 32 logic - * above adjusted everything appropriately. Then - * hicol <= wt + idelx <= wt + 31, so - * hicol + 31 <= wt + 62 - * rowwords1 = (hicol + 31) >> 5 <= (wt + 62) >> 5 - * rowwords2 == (wt + 31) >> 5, so - * rowwords1 <= rowwords2 + 1 */ - if (rowwords2 < rowwords1) { - for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { - /* Do the first iteration so the loop can be - * branch-free. */ - word1 = row1[0]; - word2 = row2[0] >> idelx; - andw = word1 & word2; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - - for (x = 1; x < rowwords2; x++) { - word1 = row1[x]; - word2 = (row2[x] >> idelx) | - (row2[x - 1] << (32 - idelx)); - andw = word1 & word2; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - } - - /* Now the last iteration - we know that this is safe - * (i.e. rowwords1 >= 2) because rowwords1 > rowwords2 - * > 0 (if it was 0, we'd have taken the "count = 0" - * fast-path out of here). */ - word1 = row1[x]; - word2 = row2[x - 1] << (32 - idelx); - andw = word1 & word2; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - } - } else { - for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { - /* Do the first iteration so the loop can be - * branch-free. This section is the same as above - * except for the different limit on the loop, since - * the last iteration is the same as all the other - * iterations (beyond the first). */ - word1 = row1[0]; - word2 = row2[0] >> idelx; - andw = word1 & word2; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - - for (x = 1; x < rowwords1; x++) { - word1 = row1[x]; - word2 = (row2[x] >> idelx) | - (row2[x - 1] << (32 - idelx)); - andw = word1 & word2; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - } - } - } - } else { - /* pix2 is shifted to the left. word 0 of pix1 is touched by - * word 0 and word 1 of pix2, and so on up to the last word of - * pix1 (word N), which is touched by words N and N+1 of - * pix2... if there is a word N+1. Handle the two cases (pix2 - * has N words and pix2 has at least N+1 words) separately. */ - if (rowwords1 < rowwords2) { - /* pix2 has at least N+1 words, so every iteration through - * the loop can be the same. */ - for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { - for (x = 0; x < rowwords1; x++) { - word1 = row1[x]; - word2 = row2[x] << -idelx; - word2 |= row2[x + 1] >> (32 + idelx); - andw = word1 & word2; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - } - } - } else { - /* pix2 has only N words, so the last iteration is broken - * out. */ - for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { - for (x = 0; x < rowwords1 - 1; x++) { - word1 = row1[x]; - word2 = row2[x] << -idelx; - word2 |= row2[x + 1] >> (32 + idelx); - andw = word1 & word2; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - } - - word1 = row1[x]; - word2 = row2[x] << -idelx; - andw = word1 & word2; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - } - } - } - } - - *pscore = (l_float32)count * (l_float32)count / - ((l_float32)area1 * (l_float32)area2); -/* lept_stderr("score = %5.3f, count = %d, area1 = %d, area2 = %d\n", - *pscore, count, area1, area2); */ - return 0; -} - - -/*! - * \brief pixCorrelationScoreThresholded() - * - * \param[in] pix1 test pix, 1 bpp - * \param[in] pix2 exemplar pix, 1 bpp - * \param[in] area1 number of on pixels in pix1 - * \param[in] area2 number of on pixels in pix2 - * \param[in] delx x comp of centroid difference - * \param[in] dely y comp of centroid difference - * \param[in] maxdiffw max width difference of pix1 and pix2 - * \param[in] maxdiffh max height difference of pix1 and pix2 - * \param[in] tab sum tab for byte - * \param[in] downcount count of 1 pixels below each row of pix1 - * \param[in] score_threshold - * \return whether the correlation score is >= score_threshold - * - * - *
- * Notes: - * We check first that the two pix are roughly the same size. - * Only if they meet that criterion do we compare the bitmaps. - * The centroid difference is used to align the two images to the - * nearest integer for the correlation. - * - * The correlation score is the ratio of the square of the number of - * pixels in the AND of the two bitmaps to the product of the number - * of ON pixels in each. Denote the number of ON pixels in pix1 - * by |1|, the number in pix2 by |2|, and the number in the AND - * of pix1 and pix2 by |1 & 2|. The correlation score is then - * (|1 & 2|)**2 / (|1|*|2|). - * - * This score is compared with an input threshold, which can - * be modified depending on the weight of the template. - * The modified threshold is - * thresh + (1.0 - thresh) * weight * R - * where - * weight is a fixed input factor between 0.0 and 1.0 - * R = |2| / area(2) - * and area(2) is the total number of pixels in 2 (i.e., width x height). - * - * To understand why a weight factor is useful, consider what happens - * with thick, sans-serif characters that look similar and have a value - * of R near 1. Different characters can have a high correlation value, - * and the classifier will make incorrect substitutions. The weight - * factor raises the threshold for these characters. - * - * Yet another approach to reduce such substitutions is to run the classifier - * in a non-greedy way, matching to the template with the highest - * score, not the first template with a score satisfying the matching - * constraint. However, this is not particularly effective. - * - * This very fast correlation matcher was contributed by William Rucklidge. - *- */ -l_int32 -pixCorrelationScoreThresholded(PIX *pix1, - PIX *pix2, - l_int32 area1, - l_int32 area2, - l_float32 delx, /* x(1) - x(3) */ - l_float32 dely, /* y(1) - y(3) */ - l_int32 maxdiffw, - l_int32 maxdiffh, - l_int32 *tab, - l_int32 *downcount, - l_float32 score_threshold) -{ -l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, count; -l_int32 wpl1, wpl2, lorow, hirow, locol, hicol, untouchable; -l_int32 x, y, pix1lskip, pix2lskip, rowwords1, rowwords2; -l_uint32 word1, word2, andw; -l_uint32 *row1, *row2; -l_float32 score; -l_int32 threshold; - - PROCNAME("pixCorrelationScoreThresholded"); - - if (!pix1 || pixGetDepth(pix1) != 1) - return ERROR_INT("pix1 undefined or not 1 bpp", procName, 0); - if (!pix2 || pixGetDepth(pix2) != 1) - return ERROR_INT("pix2 undefined or not 1 bpp", procName, 0); - if (!tab) - return ERROR_INT("tab not defined", procName, 0); - if (area1 <= 0 || area2 <= 0) - return ERROR_INT("areas must be > 0", procName, 0); - - /* Eliminate based on size difference */ - pixGetDimensions(pix1, &wi, &hi, NULL); - pixGetDimensions(pix2, &wt, &ht, NULL); - delw = L_ABS(wi - wt); - if (delw > maxdiffw) - return FALSE; - delh = L_ABS(hi - ht); - if (delh > maxdiffh) - return FALSE; - - /* Round difference to nearest integer */ - if (delx >= 0) - idelx = (l_int32)(delx + 0.5); - else - idelx = (l_int32)(delx - 0.5); - if (dely >= 0) - idely = (l_int32)(dely + 0.5); - else - idely = (l_int32)(dely - 0.5); - - /* Compute the correlation count that is needed so that - * count * count / (area1 * area2) >= score_threshold */ - threshold = (l_int32)ceil(sqrt((l_float64)score_threshold * area1 * area2)); - - count = 0; - wpl1 = pixGetWpl(pix1); - wpl2 = pixGetWpl(pix2); - rowwords2 = wpl2; - - /* What rows of pix1 need to be considered? Only those underlying the - * shifted pix2. */ - lorow = L_MAX(idely, 0); - hirow = L_MIN(ht + idely, hi); - - /* Get the pointer to the first row of each image that will be - * considered. */ - row1 = pixGetData(pix1) + wpl1 * lorow; - row2 = pixGetData(pix2) + wpl2 * (lorow - idely); - if (hirow <= hi) { - /* Some rows of pix1 will never contribute to count */ - untouchable = downcount[hirow - 1]; - } - - /* Similarly, figure out which columns of pix1 will be considered. */ - locol = L_MAX(idelx, 0); - hicol = L_MIN(wt + idelx, wi); - - if (idelx >= 32) { - /* pix2 is shifted far enough to the right that pix1's first - * word(s) won't contribute to the count. Increment its - * pointer to point to the first word that will contribute, - * and adjust other values accordingly. */ - pix1lskip = idelx >> 5; /* # of words to skip on left */ - row1 += pix1lskip; - locol -= pix1lskip << 5; - hicol -= pix1lskip << 5; - idelx &= 31; - } else if (idelx <= -32) { - /* pix2 is shifted far enough to the left that its first word(s) - * won't contribute to the count. Increment its pointer - * to point to the first word that will contribute, - * and adjust other values accordingly. */ - pix2lskip = -((idelx + 31) >> 5); /* # of words to skip on left */ - row2 += pix2lskip; - rowwords2 -= pix2lskip; - idelx += pix2lskip << 5; - } - - if ((locol >= hicol) || (lorow >= hirow)) { /* there is no overlap */ - count = 0; - } else { - /* How many words of each row of pix1 need to be considered? */ - rowwords1 = (hicol + 31) >> 5; - - if (idelx == 0) { - /* There's no lateral offset; simple case. */ - for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { - for (x = 0; x < rowwords1; x++) { - andw = row1[x] & row2[x]; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - } - - /* If the count is over the threshold, no need to - * calculate any further. Likewise, return early if the - * count plus the maximum count attainable from further - * rows is below the threshold. */ - if (count >= threshold) return TRUE; - if (count + downcount[y] - untouchable < threshold) { - return FALSE; - } - } - } else if (idelx > 0) { - /* pix2 is shifted to the right. word 0 of pix1 is touched by - * word 0 of pix2; word 1 of pix1 is touched by word 0 and word - * 1 of pix2, and so on up to the last word of pix1 (word N), - * which is touched by words N-1 and N of pix1... if there is a - * word N. Handle the two cases (pix2 has N-1 words and pix2 - * has at least N words) separately. - * - * Note: we know that pix2 has at least N-1 words (i.e., - * rowwords2 >= rowwords1 - 1) by the following logic. - * We can pretend that idelx <= 31 because the >= 32 logic - * above adjusted everything appropriately. Then - * hicol <= wt + idelx <= wt + 31, so - * hicol + 31 <= wt + 62 - * rowwords1 = (hicol + 31) >> 5 <= (wt + 62) >> 5 - * rowwords2 == (wt + 31) >> 5, so - * rowwords1 <= rowwords2 + 1 */ - if (rowwords2 < rowwords1) { - for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { - /* Do the first iteration so the loop can be - * branch-free. */ - word1 = row1[0]; - word2 = row2[0] >> idelx; - andw = word1 & word2; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - - for (x = 1; x < rowwords2; x++) { - word1 = row1[x]; - word2 = (row2[x] >> idelx) | - (row2[x - 1] << (32 - idelx)); - andw = word1 & word2; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - } - - /* Now the last iteration - we know that this is safe - * (i.e. rowwords1 >= 2) because rowwords1 > rowwords2 - * > 0 (if it was 0, we'd have taken the "count = 0" - * fast-path out of here). */ - word1 = row1[x]; - word2 = row2[x - 1] << (32 - idelx); - andw = word1 & word2; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - - if (count >= threshold) return TRUE; - if (count + downcount[y] - untouchable < threshold) { - return FALSE; - } - } - } else { - for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { - /* Do the first iteration so the loop can be - * branch-free. This section is the same as above - * except for the different limit on the loop, since - * the last iteration is the same as all the other - * iterations (beyond the first). */ - word1 = row1[0]; - word2 = row2[0] >> idelx; - andw = word1 & word2; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - - for (x = 1; x < rowwords1; x++) { - word1 = row1[x]; - word2 = (row2[x] >> idelx) | - (row2[x - 1] << (32 - idelx)); - andw = word1 & word2; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - } - - if (count >= threshold) return TRUE; - if (count + downcount[y] - untouchable < threshold) { - return FALSE; - } - } - } - } else { - /* pix2 is shifted to the left. word 0 of pix1 is touched by - * word 0 and word 1 of pix2, and so on up to the last word of - * pix1 (word N), which is touched by words N and N+1 of - * pix2... if there is a word N+1. Handle the two cases (pix2 - * has N words and pix2 has at least N+1 words) separately. */ - if (rowwords1 < rowwords2) { - /* pix2 has at least N+1 words, so every iteration through - * the loop can be the same. */ - for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { - for (x = 0; x < rowwords1; x++) { - word1 = row1[x]; - word2 = row2[x] << -idelx; - word2 |= row2[x + 1] >> (32 + idelx); - andw = word1 & word2; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - } - - if (count >= threshold) return TRUE; - if (count + downcount[y] - untouchable < threshold) { - return FALSE; - } - } - } else { - /* pix2 has only N words, so the last iteration is broken - * out. */ - for (y = lorow; y < hirow; y++, row1 += wpl1, row2 += wpl2) { - for (x = 0; x < rowwords1 - 1; x++) { - word1 = row1[x]; - word2 = row2[x] << -idelx; - word2 |= row2[x + 1] >> (32 + idelx); - andw = word1 & word2; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - } - - word1 = row1[x]; - word2 = row2[x] << -idelx; - andw = word1 & word2; - count += tab[andw & 0xff] + - tab[(andw >> 8) & 0xff] + - tab[(andw >> 16) & 0xff] + - tab[andw >> 24]; - - if (count >= threshold) return TRUE; - if (count + downcount[y] - untouchable < threshold) { - return FALSE; - } - } - } - } - } - - score = (l_float32)count * (l_float32)count / - ((l_float32)area1 * (l_float32)area2); - if (score >= score_threshold) { - lept_stderr( - "count %d < threshold %d but score %g >= score_threshold %g\n", - count, threshold, score, score_threshold); - } - return FALSE; -} - - -/* -------------------------------------------------------------------- * - * Simple 2 pix correlators (for jbig2 clustering) * - * -------------------------------------------------------------------- */ -/*! - * \brief pixCorrelationScoreSimple() - * - * \param[in] pix1 test pix, 1 bpp - * \param[in] pix2 exemplar pix, 1 bpp - * \param[in] area1 number of on pixels in pix1 - * \param[in] area2 number of on pixels in pix2 - * \param[in] delx x comp of centroid difference - * \param[in] dely y comp of centroid difference - * \param[in] maxdiffw max width difference of pix1 and pix2 - * \param[in] maxdiffh max height difference of pix1 and pix2 - * \param[in] tab sum tab for byte - * \param[out] pscore correlation score, in range [0.0 ... 1.0] - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This calculates exactly the same value as pixCorrelationScore(). - * It is 2-3x slower, but much simpler to understand. - * (2) The returned correlation score is 0.0 if the width or height - * exceed %maxdiffw or %maxdiffh. - *- */ -l_ok -pixCorrelationScoreSimple(PIX *pix1, - PIX *pix2, - l_int32 area1, - l_int32 area2, - l_float32 delx, /* x(1) - x(3) */ - l_float32 dely, /* y(1) - y(3) */ - l_int32 maxdiffw, - l_int32 maxdiffh, - l_int32 *tab, - l_float32 *pscore) -{ -l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, count; -PIX *pixt; - - PROCNAME("pixCorrelationScoreSimple"); - - if (!pscore) - return ERROR_INT("&score not defined", procName, 1); - *pscore = 0.0; - if (!pix1 || pixGetDepth(pix1) != 1) - return ERROR_INT("pix1 undefined or not 1 bpp", procName, 1); - if (!pix2 || pixGetDepth(pix2) != 1) - return ERROR_INT("pix2 undefined or not 1 bpp", procName, 1); - if (!tab) - return ERROR_INT("tab not defined", procName, 1); - if (!area1 || !area2) - return ERROR_INT("areas must be > 0", procName, 1); - - /* Eliminate based on size difference */ - pixGetDimensions(pix1, &wi, &hi, NULL); - pixGetDimensions(pix2, &wt, &ht, NULL); - delw = L_ABS(wi - wt); - if (delw > maxdiffw) - return 0; - delh = L_ABS(hi - ht); - if (delh > maxdiffh) - return 0; - - /* Round difference to nearest integer */ - if (delx >= 0) - idelx = (l_int32)(delx + 0.5); - else - idelx = (l_int32)(delx - 0.5); - if (dely >= 0) - idely = (l_int32)(dely + 0.5); - else - idely = (l_int32)(dely - 0.5); - - /* pixt = pixAnd(NULL, pix1, pix2), including shift. - * To insure that pixels are ON only within the - * intersection of pix1 and the shifted pix2: - * (1) Start with pixt cleared and equal in size to pix1. - * (2) Blit the shifted pix2 onto pixt. Then all ON pixels - * are within the intersection of pix1 and the shifted pix2. - * (3) AND pix1 with pixt. */ - pixt = pixCreateTemplate(pix1); - pixRasterop(pixt, idelx, idely, wt, ht, PIX_SRC, pix2, 0, 0); - pixRasterop(pixt, 0, 0, wi, hi, PIX_SRC & PIX_DST, pix1, 0, 0); - pixCountPixels(pixt, &count, tab); - pixDestroy(&pixt); - - *pscore = (l_float32)count * (l_float32)count / - ((l_float32)area1 * (l_float32)area2); -/* lept_stderr("score = %5.3f, count = %d, area1 = %d, area2 = %d\n", - *pscore, count, area1, area2); */ - return 0; -} - - -/*! - * \brief pixCorrelationScoreShifted() - * - * \param[in] pix1 1 bpp - * \param[in] pix2 1 bpp - * \param[in] area1 number of on pixels in pix1 - * \param[in] area2 number of on pixels in pix2 - * \param[in] delx x translation of pix2 relative to pix1 - * \param[in] dely y translation of pix2 relative to pix1 - * \param[in] tab sum tab for byte - * \param[out] pscore correlation score - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This finds the correlation between two 1 bpp images, - * when pix2 is shifted by (delx, dely) with respect - * to each other. - * (2) This is implemented by starting with a copy of pix1 and - * ANDing its pixels with those of a shifted pix2. - * (3) Get the pixel counts for area1 and area2 using piCountPixels(). - * (4) A good estimate for a shift that would maximize the correlation - * is to align the centroids (cx1, cy1; cx2, cy2), giving the - * relative translations etransx and etransy: - * etransx = cx1 - cx2 - * etransy = cy1 - cy2 - * Typically delx is chosen to be near etransx; ditto for dely. - * This function is used in pixBestCorrelation(), where the - * translations delx and dely are varied to find the best alignment. - * (5) We do not check the sizes of pix1 and pix2, because they should - * be comparable. - *- */ -l_ok -pixCorrelationScoreShifted(PIX *pix1, - PIX *pix2, - l_int32 area1, - l_int32 area2, - l_int32 delx, - l_int32 dely, - l_int32 *tab, - l_float32 *pscore) -{ -l_int32 w1, h1, w2, h2, count; -PIX *pixt; - - PROCNAME("pixCorrelationScoreShifted"); - - if (!pscore) - return ERROR_INT("&score not defined", procName, 1); - *pscore = 0.0; - if (!pix1 || pixGetDepth(pix1) != 1) - return ERROR_INT("pix1 undefined or not 1 bpp", procName, 1); - if (!pix2 || pixGetDepth(pix2) != 1) - return ERROR_INT("pix2 undefined or not 1 bpp", procName, 1); - if (!tab) - return ERROR_INT("tab not defined", procName, 1); - if (!area1 || !area2) - return ERROR_INT("areas must be > 0", procName, 1); - - pixGetDimensions(pix1, &w1, &h1, NULL); - pixGetDimensions(pix2, &w2, &h2, NULL); - - /* To insure that pixels are ON only within the - * intersection of pix1 and the shifted pix2: - * (1) Start with pixt cleared and equal in size to pix1. - * (2) Blit the shifted pix2 onto pixt. Then all ON pixels - * are within the intersection of pix1 and the shifted pix2. - * (3) AND pix1 with pixt. */ - pixt = pixCreateTemplate(pix1); - pixRasterop(pixt, delx, dely, w2, h2, PIX_SRC, pix2, 0, 0); - pixRasterop(pixt, 0, 0, w1, h1, PIX_SRC & PIX_DST, pix1, 0, 0); - pixCountPixels(pixt, &count, tab); - pixDestroy(&pixt); - - *pscore = (l_float32)count * (l_float32)count / - ((l_float32)area1 * (l_float32)area2); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dewarp.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dewarp.h deleted file mode 100644 index 37bfb632..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dewarp.h +++ /dev/null @@ -1,191 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_DEWARP_H -#define LEPTONICA_DEWARP_H - -/*! - * \file dewarp.h - * - *
- * Data structure to hold arrays and results for generating - * horizontal and vertical disparity arrays based on textlines. - * Each disparity array is two-dimensional. The vertical disparity - * array gives a vertical displacement, relative to the lowest point - * in the textlines. The horizontal disparty array gives a horizontal - * displacement, relative to the minimum values (for even pages) - * or maximum values (for odd pages) of the left and right ends of - * full textlines. Horizontal alignment always involves translations - * away from the book gutter. - * - * We have intentionally separated the process of building models - * from the rendering process that uses the models. For any page, - * the building operation either creates an actual model (that is, - * a model with at least the vertical disparity being computed, and - * for which the 'success' flag is set) or fails to create a model. - * However, at rendering time, a page can have one of two different - * types of models. - * (1) A valid model is an actual model that meets the rendering - * constraints, which are limits on model curvature parameters. - * See dewarpaTestForValidModel() for details. - * Valid models are identified by dewarpaInsertRefModels(), - * which sets the 'vvalid' and 'hvalid' fields. Only valid - * models are used for rendering. - * (2) A reference model is used by a page that doesn't have - * a valid model, but has a nearby valid model of the same - * parity (even/odd page) that it can use. The range in pages - * to search for a valid model is given by the 'maxdist' field. - * - * At the rendering stage, vertical and horizontal disparities are - * treated differently. It is somewhat more robust to generate - * vertical disparity models (VDM) than horizontal disparity - * models (HDM). A valid VDM is required for any correction to - * be made; if a valid VDM is not available, just use the input - * image. Otherwise, assuming it is available, the use of the - * HDM is controlled by two fields: 'useboth' and 'check_columns'. - * (a) With useboth == 0, we use only the VDM. - * (b) With useboth == 1, we require using the VDM and, if a valid - * horizontal disparity model (HDM) is available, we also use it. - * (c) With check_columns == 1, check for multiple columns and if - * true, only use the VDM, even if a valid HDM is available. - * Note that 'check_columns' takes precedence over 'useboth' - * when there is more than 1 column of text. By default, - * check_columns == 0. - * - * The 'maxdist' parameter is input when the dewarpa is created. - * The other rendering parameters have default values given in dewarp1.c. - * All parameters used by rendering can be set (or reset) using accessors. - * - * After dewarping, use of the VDM will cause all points on each - * altered curve to have a y-value equal to the minimum. Use of - * the HDA will cause the left and right edges of the textlines - * to be vertically aligned if they had been typeset flush-left - * and flush-right, respectively. - * - * The sampled disparity arrays are expanded to full resolution, - * using linear interpolation, and this is further expanded - * by slope continuation to the right and below if the image - * is larger than the full resolution disparity arrays. Then - * the disparity correction can be applied to the input image. - * If the input pix are 2x reduced, the expansion from sampled - * to full res uses the product of (sampling) * (redfactor). - * - * The most accurate results are produced at full resolution, and - * this is generally recommended. - *- */ - - /*! Dewarp version for serialization - *
- * Note on versioning of the serialization of this data structure: - * The dewarping utility and the stored data can be expected to change. - * In most situations, the serialized version is ephemeral -- it is - * not needed after being used. No functions will be provided to - * convert between different versions. - *- */ -#define DEWARP_VERSION_NUMBER 4 - -/*! Data structure to hold a number of Dewarp */ -struct L_Dewarpa -{ - l_int32 nalloc; /*!< size of dewarp ptr array */ - l_int32 maxpage; /*!< maximum page number in array */ - struct L_Dewarp **dewarp; /*!< array of ptrs to page dewarp */ - struct L_Dewarp **dewarpcache; /*!< array of ptrs to cached dewarps */ - struct Numa *namodels; /*!< list of page numbers for pages */ - /*!< with page models */ - struct Numa *napages; /*!< list of page numbers with either */ - /*!< page models or ref page models */ - l_int32 redfactor; /*!< reduction factor of input: 1 or 2 */ - l_int32 sampling; /*!< disparity arrays sampling factor */ - l_int32 minlines; /*!< min number of long lines required */ - l_int32 maxdist; /*!< max distance for getting ref page */ - l_int32 max_linecurv; /*!< maximum abs line curvature, */ - /*!< in micro-units */ - l_int32 min_diff_linecurv; /*!< minimum abs diff line */ - /*!< curvature in micro-units */ - l_int32 max_diff_linecurv; /*!< maximum abs diff line */ - /*!< curvature in micro-units */ - l_int32 max_edgeslope; /*!< maximum abs left or right edge */ - /*!< slope, in milli-units */ - l_int32 max_edgecurv; /*!< maximum abs left or right edge */ - /*!< curvature, in micro-units */ - l_int32 max_diff_edgecurv; /*!< maximum abs diff left-right */ - /*!< edge curvature, in micro-units */ - l_int32 useboth; /*!< use both disparity arrays if */ - /*!< available; only vertical otherwise */ - l_int32 check_columns; /*!< if there are multiple columns, */ - /*!< only use the vertical disparity */ - /*!< array */ - l_int32 modelsready; /*!< invalid models have been removed */ - /*!< and refs built against valid set */ -}; -typedef struct L_Dewarpa L_DEWARPA; - - -/*! Data structure for a single dewarp */ -struct L_Dewarp -{ - struct L_Dewarpa *dewa; /*!< ptr to parent (not owned) */ - struct Pix *pixs; /*!< source pix, 1 bpp */ - struct FPix *sampvdispar; /*!< sampled vert disparity array */ - struct FPix *samphdispar; /*!< sampled horiz disparity array */ - struct FPix *sampydispar; /*!< sampled slope h-disparity array */ - struct FPix *fullvdispar; /*!< full vert disparity array */ - struct FPix *fullhdispar; /*!< full horiz disparity array */ - struct FPix *fullydispar; /*!< full slope h-disparity array */ - struct Numa *namidys; /*!< sorted y val of midpoint each line */ - struct Numa *nacurves; /*!< sorted curvature of each line */ - l_int32 w; /*!< width of source image */ - l_int32 h; /*!< height of source image */ - l_int32 pageno; /*!< page number; important for reuse */ - l_int32 sampling; /*!< sampling factor of disparity arrays */ - l_int32 redfactor; /*!< reduction factor of pixs: 1 or 2 */ - l_int32 minlines; /*!< min number of long lines required */ - l_int32 nlines; /*!< number of long lines found */ - l_int32 mincurv; /*!< min line curvature in micro-units */ - l_int32 maxcurv; /*!< max line curvature in micro-units */ - l_int32 leftslope; /*!< left edge slope in milli-units */ - l_int32 rightslope; /*!< right edge slope in milli-units */ - l_int32 leftcurv; /*!< left edge curvature in micro-units */ - l_int32 rightcurv; /*!< right edge curvature in micro-units*/ - l_int32 nx; /*!< number of sampling pts in x-dir */ - l_int32 ny; /*!< number of sampling pts in y-dir */ - l_int32 hasref; /*!< 0 if normal; 1 if has a refpage */ - l_int32 refpage; /*!< page with disparity model to use */ - l_int32 vsuccess; /*!< sets to 1 if vert disparity builds */ - l_int32 hsuccess; /*!< sets to 1 if horiz disparity builds */ - l_int32 ysuccess; /*!< sets to 1 if slope disparity builds */ - l_int32 vvalid; /*!< sets to 1 if valid vert disparity */ - l_int32 hvalid; /*!< sets to 1 if valid horiz disparity */ - l_int32 skip_horiz; /*!< if 1, skip horiz disparity */ - /*!< correction */ - l_int32 debug; /*!< set to 1 if debug output requested */ -}; -typedef struct L_Dewarp L_DEWARP; - -#endif /* LEPTONICA_DEWARP_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dewarp1.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dewarp1.c deleted file mode 100644 index 11633d53..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dewarp1.c +++ /dev/null @@ -1,1701 +0,0 @@ -/*====================================================================* - - 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 dewarp1.c - *
- * - * Basic operations and serialization - * - * Create/destroy dewarp - * L_DEWARP *dewarpCreate() - * L_DEWARP *dewarpCreateRef() - * void dewarpDestroy() - * - * Create/destroy dewarpa - * L_DEWARPA *dewarpaCreate() - * L_DEWARPA *dewarpaCreateFromPixacomp() - * void dewarpaDestroy() - * l_int32 dewarpaDestroyDewarp() - * - * Dewarpa insertion/extraction - * l_int32 dewarpaInsertDewarp() - * static l_int32 dewarpaExtendArraysToSize() - * L_DEWARP *dewarpaGetDewarp() - * - * Setting parameters to control rendering from the model - * l_int32 dewarpaSetCurvatures() - * l_int32 dewarpaUseBothArrays() - * l_int32 dewarpaSetCheckColumns() - * l_int32 dewarpaSetMaxDistance() - * - * Dewarp serialized I/O - * L_DEWARP *dewarpRead() - * L_DEWARP *dewarpReadStream() - * L_DEWARP *dewarpReadMem() - * l_int32 dewarpWrite() - * l_int32 dewarpWriteStream() - * l_int32 dewarpWriteMem() - * - * Dewarpa serialized I/O - * L_DEWARPA *dewarpaRead() - * L_DEWARPA *dewarpaReadStream() - * L_DEWARPA *dewarpaReadMem() - * l_int32 dewarpaWrite() - * l_int32 dewarpaWriteStream() - * l_int32 dewarpaWriteMem() - * - * - * Examples of usage - * ================= - * - * See dewarpaCreateFromPixacomp() for an example of the basic - * operations, starting from a set of 1 bpp images. - * - * Basic functioning to dewarp a specific single page: - * \code - * // Make the Dewarpa for the pages - * L_Dewarpa *dewa = dewarpaCreate(1, 30, 1, 15, 50); - * dewarpaSetCurvatures(dewa, -1, 5, -1, -1, -1, -1); - * dewarpaUseBothArrays(dewa, 1); // try to use both disparity - * // arrays for this example - * - * // Do the page: start with a binarized image - * Pix *pixb = "binarize"(pixs); - * // Initialize a Dewarp for this page (say, page 214) - * L_Dewarp *dew = dewarpCreate(pixb, 214); - * // Insert in Dewarpa and obtain parameters for building the model - * dewarpaInsertDewarp(dewa, dew); - * // Do the work - * dewarpBuildPageModel(dew, NULL); // no debugging - * // Optionally set rendering parameters - * // Apply model to the input pixs - * Pix *pixd; - * dewarpaApplyDisparity(dewa, 214, pixs, 255, 0, 0, &pixd, NULL); - * pixDestroy(&pixb); - * \endcode - * - * Basic functioning to dewarp many pages: - * \code - * // Make the Dewarpa for the set of pages; use fullres 1 bpp - * L_Dewarpa *dewa = dewarpaCreate(10, 30, 1, 15, 50); - * // Optionally set rendering parameters - * dewarpaSetCurvatures(dewa, -1, 10, -1, -1, -1, -1); - * dewarpaUseBothArrays(dewa, 0); // just use the vertical disparity - * // array for this example - * - * // Do first page: start with a binarized image - * Pix *pixb = "binarize"(pixs); - * // Initialize a Dewarp for this page (say, page 1) - * L_Dewarp *dew = dewarpCreate(pixb, 1); - * // Insert in Dewarpa and obtain parameters for building the model - * dewarpaInsertDewarp(dewa, dew); - * // Do the work - * dewarpBuildPageModel(dew, NULL); // no debugging - * dewarpMinimze(dew); // remove most heap storage - * pixDestroy(&pixb); - * - * // Do the other pages the same way - * ... - * - * // Apply models to each page; if the page model is invalid, - * // try to use a valid neighboring model. Note that the call - * // to dewarpaInsertRefModels() is optional, because it is called - * // by dewarpaApplyDisparity() on the first page it acts on. - * dewarpaInsertRefModels(dewa, 0, 1); // use debug flag to get more - * // detailed information about the page models - * [For each page, where pixs is the fullres image to be dewarped] { - * L_Dewarp *dew = dewarpaGetDewarp(dewa, pageno); - * if (dew) { // disparity model exists - * Pix *pixd; - * dewarpaApplyDisparity(dewa, pageno, pixs, 255, - * 0, 0, &pixd, NULL); - * dewarpMinimize(dew); // clean out the pix and fpix arrays - * // Squirrel pixd away somewhere ...) - * } - * } - * \endcode - * - * Basic functioning to dewarp a small set of pages, potentially - * using models from nearby pages: - * \code - * // (1) Generate a set of binarized images in the vicinity of the - * // pages to be dewarped. We will attempt to compute models - * // for pages from 'firstpage' to 'lastpage'. - * // Store the binarized images in a compressed array of - * // size 'n', where 'n' is the number of images to be stored, - * // and where the offset is the first page. - * PixaComp *pixac = pixacompCreateInitialized(n, firstpage, NULL, - * IFF_TIFF_G4); - * for (i = firstpage; i <= lastpage; i++) { - * Pix *pixb = "binarize"(pixs); - * pixacompReplacePix(pixac, i, pixb, IFF_TIFF_G4); - * pixDestroy(&pixb); - * } - * - * // (2) Make the Dewarpa for the pages. - * L_Dewarpa *dewa = - * dewarpaCreateFromPixacomp(pixac, 30, 15, 20); - * dewarpaUseBothArrays(dewa, 1); // try to use both disparity arrays - * // in this example - * - * // (3) Finally, apply the models. For page 'firstpage' with image pixs: - * L_Dewarp *dew = dewarpaGetDewarp(dewa, firstpage); - * if (dew) { // disparity model exists - * Pix *pixd; - * dewarpaApplyDisparity(dewa, firstpage, pixs, 255, 0, 0, &pixd, NULL); - * dewarpMinimize(dew); - * } - * \endcode - * - * Because in general some pages will not have enough text to build a - * model, we fill in for those pages with a reference to the page - * model to use. Both the target page and the reference page must - * have the same parity. We can also choose to use either a partial model - * (with only vertical disparity) or the full model of a nearby page. - * - * Minimizing the data in a model by stripping out images, - * numas, and full resolution disparity arrays: - * dewarpMinimize(dew); - * This can be done at any time to save memory. Serialization does - * not use the data that is stripped. - * - * You can apply any model (in a dew), stripped or not, to another image: - * \code - * // For all pages with invalid models, assign the nearest valid - * // page model with same parity. - * dewarpaInsertRefModels(dewa, 0, 0); - * // You can then apply to 'newpix' the page model that was assigned - * // to 'pageno', giving the result in pixd: - * Pix *pixd; - * dewarpaApplyDisparity(dewa, pageno, newpix, 255, 0, 0, &pixd, NULL); - * \endcode - * - * You can apply the disparity arrays to a deliberately undercropped - * image. Suppose that you undercrop by (left, right, top, bot), so - * that the disparity arrays are aligned with their origin at (left, top). - * Dewarp the undercropped image with: - * \code - * Pix *pixd; - * dewarpaApplyDisparity(dewa, pageno, undercropped_pix, 255, - * left, top, &pixd, NULL); - * \endcode - * - * Description of the approach to analyzing page image distortion - * ============================================================== - * - * When a book page is scanned, there are several possible causes - * for the text lines to appear to be curved: - * (1) A barrel (fish-eye) effect because the camera is at - * a finite distance from the page. Take the normal from - * the camera to the page (the 'optic axis'). Lines on - * the page "below" this point will appear to curve upward - * (negative curvature); lines "above" this will curve downward. - * (2) Radial distortion from the camera lens. Probably not - * a big factor. - * (3) Local curvature of the page in to (or out of) the image - * plane (which is perpendicular to the optic axis). - * This has no effect if the page is flat. - * - * In the following, the optic axis is in the z direction and is - * perpendicular to the xy plane;, the book is assumed to be aligned - * so that y is approximately along the binding. - * The goal is to compute the "disparity" field, D(x,y), which - * is actually a vector composed of the horizontal and vertical - * disparity fields H(x,y) and V(x,y). Each of these is a local - * function that gives the amount each point in the image is - * required to move in order to rectify the horizontal and vertical - * lines. It would also be nice to "flatten" the page to compensate - * for effect (3), foreshortening due to bending of the page into - * the z direction, but that is more difficult. - * - * Effects (1) and (2) can be directly compensated by calibrating - * the scene, using a flat page with horizontal and vertical lines. - * Then H(x,y) and V(x,y) can be found as two (non-parametric) arrays - * of values. Suppose this has been done. Then the remaining - * distortion is due to (3). - * - * We consider the simple situation where the page bending is independent - * of y, and is described by alpha(x), where alpha is the angle between - * the normal to the page and the optic axis. cos(alpha(x)) is the local - * compression factor of the page image in the horizontal direction, at x. - * Thus, if we know alpha(x), we can compute the disparity H(x) required - * to flatten the image by simply integrating 1/cos(alpha), and we could - * compute the remaining disparities, H(x,y) and V(x,y), from the - * page content, as described below. Unfortunately, we don't know - * alpha. What do we know? If there are horizontal text lines - * on the page, we can compute the vertical disparity, V(x,y), which - * is the local translation required to make the text lines parallel - * to the rasters. If the margins are left and right aligned, we can - * also estimate the horizontal disparity, H(x,y), required to have - * uniform margins. All that can be done from the image alone, - * assuming we have text lines covering a sufficient part of the page. - * - * What about alpha(x)? The basic question relating to (3) is this: - * - * Is it possible, using the shape of the text lines alone, - * to compute both the vertical and horizontal disparity fields? - * - * The underlying problem is to separate the line curvature effects due - * to the camera view from those due to actual bending of the page. - * I believe the proper way to do this is to make some measurements - * based on the camera setup, which will depend mostly on the distance - * of the camera from the page, and to a smaller extent on the location - * of the optic axis with respect to the page. - * - * Here is the procedure. Photograph a page with a fine 2D line grid - * several times, each with a different slope near the binding. - * This can be done by placing the grid page on books that have - * different shapes z(x) near the binding. For each one you can - * measure, near the binding: - * (1) ds/dy, the vertical rate of change of slope of the horizontal lines - * (2) the local horizontal compression of the vertical lines due - * to the page angle dz/dx. - * As mentioned above, the local horizontal compression is simply - * cos(dz/dx). But the measurement you can make on an actual book - * page is (1). The difficulty is to generate (2) from (1). - * - * Back to the procedure. The function in (1), ds/dy, likely needs - * to be measured at a few y locations, because the relation - * between (1) and (2) may weakly depend on the y-location with - * respect to the y-coordinate of the optic axis of the camera. - * From these measurements you can determine, for the camera setup - * that you have, the local horizontal compression, cos(dz/dx), as a - * function of the both vertical location (y) and your measured vertical - * derivative of the text line slope there, ds/dy. Then with - * appropriate smoothing of your measured values, you can set up a - * horizontal disparity array to correct for the compression due - * to dz/dx. - * - * Now consider V(x,0) and V(x,h), the vertical disparity along - * the top and bottom of the image. With a little thought you - * can convince yourself that the local foreshortening, - * as a function of x, is proportional to the difference - * between the slope of V(x,0) and V(x,h). The horizontal - * disparity can then be computed by integrating the local foreshortening - * over x. Integration of the slope of V(x,0) and V(x,h) gives - * the vertical disparity itself. We have to normalize to h, the - * height of the page. So the very simple result is that - * - * H(x) ~ (V(x,0) - V(x,h)) / h [1] - * - * which is easily computed. There is a proportionality constant - * that depends on the ratio of h to the distance to the camera. - * Can we actually believe this for the case where the bending - * is independent of y? I believe the answer is yes, - * as long as you first remove the apparent distortion due - * to the camera being at a finite distance. - * - * If you know the intersection of the optical axis with the page - * and the distance to the camera, and if the page is perpendicular - * to the optic axis, you can compute the horizontal and vertical - * disparities due to (1) and (2) and remove them. The resulting - * distortion should be entirely due to bending (3), for which - * the relation - * - * Hx(x) dx = C * ((Vx(x,0) - Vx(x, h))/h) dx [2] - * - * holds for each point in x (Hx and Vx are partial derivatives w/rt x). - * Integrating over x, and using H(0) = 0, we get the result [1]. - * - * I believe this result holds differentially for each value of y, so - * that in the case where the bending is not independent of y, - * the expression (V(x,0) - V(x,h)) / h goes over to Vy(x,y). Then - * - * H(x,y) = Integral(0,x) (Vyx(x,y) dx) [3] - * - * where Vyx() is the partial derivative of V w/rt both x and y. - * - * It would be nice if there were a simple mathematical relation between - * the horizontal and vertical disparities for the situation - * where the paper bends without stretching or kinking. - * I had hoped to get a relation between H and V, such as - * Hx(x,y) ~ Vy(x,y), which would imply that H and V are real - * and imaginary parts of a complex potential, each of which - * satisfy the laplace equation. But then the gradients of the - * two potentials would be normal, and that does not appear to be the case. - * Thus, the questions of proving the relations above (for small bending), - * or finding a simpler relation between H and V than those equations, - * remain open. So far, we have only used [1] for the horizontal - * disparity H(x). - * - * In the version of the code that follows, we first use text lines - * to find V(x,y). Then, we try to compute H(x,y) that will align - * the text vertically on the left and right margins. This is not - * always possible -- sometimes the right margin is not right justified. - * By default, we don't require the horizontal disparity to have a - * valid page model for dewarping a page, but this requirement can - * be forced using dewarpaUseFullModel(). - * - * As described above, one can add a y-independent component of - * the horizontal disparity H(x) to counter the foreshortening - * effect due to the bending of the page near the binding. - * This requires widening the image on the side near the binding, - * and we do not provide this option here. However, we do provide - * a function that will generate this disparity field: - * fpixExtraHorizDisparity() - * - * Here is the basic outline for building the disparity arrays. - * - * (1) Find lines going approximately through the center of the - * text in each text line. Accept only lines that are - * close in length to the longest line. - * (2) Use these lines to generate a regular and highly subsampled - * vertical disparity field V(x,y). - * (3) Interpolate this to generate a full resolution vertical - * disparity field. - * (4) For lines that are sufficiently long, assume they are approximately - * left and right-justified, and construct a highly subsampled - * horizontal disparity field H(x,y) that will bring them into alignment. - * (5) Interpolate this to generate a full resolution horizontal - * disparity field. - * (6) Apply the vertical dewarping, followed by the horizontal dewarping. - * - * Step (1) is clearly described by the code in pixGetTextlineCenters(). - * - * Steps (2) and (3) follow directly from the data in step (1), - * and constitute the bulk of the work done in dewarpBuildPageModel(). - * Virtually all the noise in the data is smoothed out by doing - * least-square quadratic fits, first horizontally to the data - * points representing the text line centers, and then vertically. - * The trick is to sample these lines on a regular grid. - * First each horizontal line is sampled at equally spaced - * intervals horizontally. We thus get a set of points, - * one in each line, that are vertically aligned, and - * the data we represent is the vertical distance of each point - * from the min or max value on the curve, depending on the - * sign of the curvature component. Each of these vertically - * aligned sets of points constitutes a sampled vertical disparity, - * and we do a LS quartic fit to each of them, followed by - * vertical sampling at regular intervals. We now have a subsampled - * grid of points, all equally spaced, giving at each point the local - * vertical disparity. Finally, the full resolution vertical disparity - * is formed by interpolation. All the least square fits do a - * great job of smoothing everything out, as can be observed by - * the contour maps that are generated for the vertical disparity field. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) The input pixs is either full resolution or 2x reduced. - * (2) The page number is typically 0-based. If scanned from a book, - * the even pages are usually on the left. Disparity arrays - * built for even pages should only be applied to even pages. - *- */ -L_DEWARP * -dewarpCreate(PIX *pixs, - l_int32 pageno) -{ -L_DEWARP *dew; - - PROCNAME("dewarpCreate"); - - if (!pixs) - return (L_DEWARP *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (L_DEWARP *)ERROR_PTR("pixs not 1 bpp", procName, NULL); - - dew = (L_DEWARP *)LEPT_CALLOC(1, sizeof(L_DEWARP)); - dew->pixs = pixClone(pixs); - dew->pageno = pageno; - dew->w = pixGetWidth(pixs); - dew->h = pixGetHeight(pixs); - return dew; -} - - -/*! - * \brief dewarpCreateRef() - * - * \param[in] pageno this page number - * \param[in] refpage page number of dewarp disparity arrays to be used - * \return dew or NULL on error - * - *
- * Notes: - * (1) This specifies which dewarp struct should be used for - * the given page. It is placed in dewarpa for pages - * for which no model can be built. - * (2) This page and the reference page have the same parity and - * the reference page is the closest page with a disparity model - * to this page. - *- */ -L_DEWARP * -dewarpCreateRef(l_int32 pageno, - l_int32 refpage) -{ -L_DEWARP *dew; - - PROCNAME("dewarpCreateRef"); - - dew = (L_DEWARP *)LEPT_CALLOC(1, sizeof(L_DEWARP)); - dew->pageno = pageno; - dew->hasref = 1; - dew->refpage = refpage; - return dew; -} - - -/*! - * \brief dewarpDestroy() - * - * \param[in,out] pdew will be set to null before returning - * \return void - */ -void -dewarpDestroy(L_DEWARP **pdew) -{ -L_DEWARP *dew; - - PROCNAME("dewarpDestroy"); - - if (pdew == NULL) { - L_WARNING("ptr address is null!\n", procName); - return; - } - if ((dew = *pdew) == NULL) - return; - - pixDestroy(&dew->pixs); - fpixDestroy(&dew->sampvdispar); - fpixDestroy(&dew->samphdispar); - fpixDestroy(&dew->sampydispar); - fpixDestroy(&dew->fullvdispar); - fpixDestroy(&dew->fullhdispar); - fpixDestroy(&dew->fullydispar); - numaDestroy(&dew->namidys); - numaDestroy(&dew->nacurves); - LEPT_FREE(dew); - *pdew = NULL; - return; -} - - -/*----------------------------------------------------------------------* - * Create/destroy Dewarpa * - *----------------------------------------------------------------------*/ -/*! - * \brief dewarpaCreate() - * - * \param[in] nptrs number of dewarp page ptrs; typ. the number of pages - * \param[in] sampling use 0 for default value; the minimum allowed is 8 - * \param[in] redfactor of input images: 1 is full res; 2 is 2x reduced - * \param[in] minlines minimum number of lines to accept; use 0 for default - * \param[in] maxdist for locating reference disparity; use -1 for default - * \return dewa or NULL on error - * - *
- * Notes: - * (1) The sampling, minlines and maxdist parameters will be - * applied to all images. - * (2) The sampling factor is used for generating the disparity arrays - * from the input image. For 2x reduced input, use a sampling - * factor that is half the sampling you want on the full resolution - * images. - * (3) Use %redfactor = 1 for full resolution; 2 for 2x reduction. - * All input images must be at one of these two resolutions. - * (4) %minlines is the minimum number of nearly full-length lines - * required to generate a vertical disparity array. The default - * number is 15. Use a smaller number to accept a questionable - * array, but not smaller than 4. - * (5) When a model can't be built for a page, it looks up to %maxdist - * in either direction for a valid model with the same page parity. - * Use -1 for the default value of %maxdist; use 0 to avoid using - * a ref model. - * (6) The ptr array is expanded as necessary to accommodate page images. - *- */ -L_DEWARPA * -dewarpaCreate(l_int32 nptrs, - l_int32 sampling, - l_int32 redfactor, - l_int32 minlines, - l_int32 maxdist) -{ -L_DEWARPA *dewa; - - PROCNAME("dewarpaCreate"); - - if (nptrs <= 0) - nptrs = InitialPtrArraySize; - if (nptrs > MaxPtrArraySize) - return (L_DEWARPA *)ERROR_PTR("too many pages", procName, NULL); - if (redfactor != 1 && redfactor != 2) - return (L_DEWARPA *)ERROR_PTR("redfactor not in {1,2}", - procName, NULL); - if (sampling == 0) { - sampling = DefaultArraySampling; - } else if (sampling < MinArraySampling) { - L_WARNING("sampling too small; setting to %d\n", procName, - MinArraySampling); - sampling = MinArraySampling; - } - if (minlines == 0) { - minlines = DefaultMinLines; - } else if (minlines < MinMinLines) { - L_WARNING("minlines too small; setting to %d\n", procName, - MinMinLines); - minlines = DefaultMinLines; - } - if (maxdist < 0) - maxdist = DefaultMaxRefDist; - - dewa = (L_DEWARPA *)LEPT_CALLOC(1, sizeof(L_DEWARPA)); - dewa->dewarp = (L_DEWARP **)LEPT_CALLOC(nptrs, sizeof(L_DEWARPA *)); - dewa->dewarpcache = (L_DEWARP **)LEPT_CALLOC(nptrs, sizeof(L_DEWARPA *)); - if (!dewa->dewarp || !dewa->dewarpcache) { - dewarpaDestroy(&dewa); - return (L_DEWARPA *)ERROR_PTR("dewarp ptrs not made", procName, NULL); - } - dewa->nalloc = nptrs; - dewa->sampling = sampling; - dewa->redfactor = redfactor; - dewa->minlines = minlines; - dewa->maxdist = maxdist; - dewa->max_linecurv = DefaultMaxLineCurv; - dewa->min_diff_linecurv = DefaultMinDiffLineCurv; - dewa->max_diff_linecurv = DefaultMaxDiffLineCurv; - dewa->max_edgeslope = DefaultMaxEdgeSlope; - dewa->max_edgecurv = DefaultMaxEdgeCurv; - dewa->max_diff_edgecurv = DefaultMaxDiffEdgeCurv; - dewa->check_columns = DefaultCheckColumns; - dewa->useboth = DefaultUseBoth; - return dewa; -} - - -/*! - * \brief dewarpaCreateFromPixacomp() - * - * \param[in] pixac pixacomp of G4, 1 bpp images; with 1x1x1 placeholders - * \param[in] useboth 0 for only vert disparity; 1 for both vert and horiz - * \param[in] sampling use -1 or 0 for default value; otherwise minimum of 5 - * \param[in] minlines minimum number of lines to accept; e.g., 10 - * \param[in] maxdist for locating reference disparity; use -1 for default - * \return dewa or NULL on error - * - *
- * Notes: - * (1) The returned dewa has disparity arrays calculated and - * is ready for serialization or for use in dewarping. - * (2) The sampling, minlines and maxdist parameters are - * applied to all images. See notes in dewarpaCreate() for details. - * (3) The pixac is full. Placeholders, if any, are w=h=d=1 images, - * and the real input images are 1 bpp at full resolution. - * They are assumed to be cropped to the actual page regions, - * and may be arbitrarily sparse in the array. - * (4) The output dewarpa is indexed by the page number. - * The offset in the pixac gives the mapping between the - * array index in the pixac and the page number. - * (5) This adds the ref page models. - * (6) This can be used to make models for any desired set of pages. - * The direct models are only made for pages with images in - * the pixacomp; the ref models are made for pages of the - * same parity within %maxdist of the nearest direct model. - *- */ -L_DEWARPA * -dewarpaCreateFromPixacomp(PIXAC *pixac, - l_int32 useboth, - l_int32 sampling, - l_int32 minlines, - l_int32 maxdist) -{ -l_int32 i, nptrs, pageno; -L_DEWARP *dew; -L_DEWARPA *dewa; -PIX *pixt; - - PROCNAME("dewarpaCreateFromPixacomp"); - - if (!pixac) - return (L_DEWARPA *)ERROR_PTR("pixac not defined", procName, NULL); - - nptrs = pixacompGetCount(pixac); - if ((dewa = dewarpaCreate(pixacompGetOffset(pixac) + nptrs, - sampling, 1, minlines, maxdist)) == NULL) - return (L_DEWARPA *)ERROR_PTR("dewa not made", procName, NULL); - dewarpaUseBothArrays(dewa, useboth); - - for (i = 0; i < nptrs; i++) { - pageno = pixacompGetOffset(pixac) + i; /* index into pixacomp */ - pixt = pixacompGetPix(pixac, pageno); - if (pixt && (pixGetWidth(pixt) > 1)) { - dew = dewarpCreate(pixt, pageno); - pixDestroy(&pixt); - if (!dew) { - ERROR_INT("unable to make dew!", procName, 1); - continue; - } - - /* Insert into dewa for this page */ - dewarpaInsertDewarp(dewa, dew); - - /* Build disparity arrays for this page */ - dewarpBuildPageModel(dew, NULL); - if (!dew->vsuccess) { /* will need to use model from nearby page */ - dewarpaDestroyDewarp(dewa, pageno); - L_ERROR("unable to build model for page %d\n", procName, i); - continue; - } - /* Remove all extraneous data */ - dewarpMinimize(dew); - } - pixDestroy(&pixt); - } - dewarpaInsertRefModels(dewa, 0, 0); - - return dewa; -} - - -/*! - * \brief dewarpaDestroy() - * - * \param[in,out] pdewa will be set to null before returning - * \return void - */ -void -dewarpaDestroy(L_DEWARPA **pdewa) -{ -l_int32 i; -L_DEWARP *dew; -L_DEWARPA *dewa; - - PROCNAME("dewarpaDestroy"); - - if (pdewa == NULL) { - L_WARNING("ptr address is null!\n", procName); - return; - } - if ((dewa = *pdewa) == NULL) - return; - - for (i = 0; i < dewa->nalloc; i++) { - if ((dew = dewa->dewarp[i]) != NULL) - dewarpDestroy(&dew); - if ((dew = dewa->dewarpcache[i]) != NULL) - dewarpDestroy(&dew); - } - numaDestroy(&dewa->namodels); - numaDestroy(&dewa->napages); - - LEPT_FREE(dewa->dewarp); - LEPT_FREE(dewa->dewarpcache); - LEPT_FREE(dewa); - *pdewa = NULL; - return; -} - - -/*! - * \brief dewarpaDestroyDewarp() - * - * \param[in] dewa - * \param[in] pageno of dew to be destroyed - * \return 0 if OK, 1 on error - */ -l_ok -dewarpaDestroyDewarp(L_DEWARPA *dewa, - l_int32 pageno) -{ -L_DEWARP *dew; - - PROCNAME("dewarpaDestroyDewarp"); - - if (!dewa) - return ERROR_INT("dewa or dew not defined", procName, 1); - if (pageno < 0 || pageno > dewa->maxpage) - return ERROR_INT("page out of bounds", procName, 1); - if ((dew = dewa->dewarp[pageno]) == NULL) - return ERROR_INT("dew not defined", procName, 1); - - dewarpDestroy(&dew); - dewa->dewarp[pageno] = NULL; - return 0; -} - - -/*----------------------------------------------------------------------* - * Dewarpa insertion/extraction * - *----------------------------------------------------------------------*/ -/*! - * \brief dewarpaInsertDewarp() - * - * \param[in] dewa - * \param[in] dew to be added - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This inserts the dewarp into the array, which now owns it. - * It also keeps track of the largest page number stored. - * It must be done before the disparity model is built. - * (2) Note that this differs from the usual method of filling out - * arrays in leptonica, where the arrays are compact and - * new elements are typically added to the end. Here, - * the dewarp can be added anywhere, even beyond the initial - * allocation. - *- */ -l_ok -dewarpaInsertDewarp(L_DEWARPA *dewa, - L_DEWARP *dew) -{ -l_int32 pageno, n, newsize; -L_DEWARP *prevdew; - - PROCNAME("dewarpaInsertDewarp"); - - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - if (!dew) - return ERROR_INT("dew not defined", procName, 1); - - dew->dewa = dewa; - pageno = dew->pageno; - if (pageno > MaxPtrArraySize) - return ERROR_INT("too many pages", procName, 1); - if (pageno > dewa->maxpage) - dewa->maxpage = pageno; - dewa->modelsready = 0; /* force re-evaluation at application time */ - - /* Extend ptr array if necessary */ - n = dewa->nalloc; - newsize = n; - if (pageno >= 2 * n) - newsize = 2 * pageno; - else if (pageno >= n) - newsize = 2 * n; - if (newsize > n) - dewarpaExtendArraysToSize(dewa, newsize); - - if ((prevdew = dewarpaGetDewarp(dewa, pageno)) != NULL) - dewarpDestroy(&prevdew); - dewa->dewarp[pageno] = dew; - - dew->sampling = dewa->sampling; - dew->redfactor = dewa->redfactor; - dew->minlines = dewa->minlines; - - /* Get the dimensions of the sampled array. This will be - * stored in an fpix, and the input resolution version is - * guaranteed to be larger than pixs. However, if you - * want to apply the disparity to an image with a width - * w > nx * s - 2 * s + 2 - * you will need to extend the input res fpix. - * And similarly for h. */ - dew->nx = (dew->w + 2 * dew->sampling - 2) / dew->sampling; - dew->ny = (dew->h + 2 * dew->sampling - 2) / dew->sampling; - return 0; -} - - -/*! - * \brief dewarpaExtendArraysToSize() - * - * \param[in] dewa - * \param[in] size new size of dewarpa array - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) If necessary, reallocs main and cache dewarpa ptr arrays to %size. - *- */ -static l_int32 -dewarpaExtendArraysToSize(L_DEWARPA *dewa, - l_int32 size) -{ - PROCNAME("dewarpaExtendArraysToSize"); - - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - - if (size > dewa->nalloc) { - if ((dewa->dewarp = (L_DEWARP **)reallocNew((void **)&dewa->dewarp, - sizeof(L_DEWARP *) * dewa->nalloc, - size * sizeof(L_DEWARP *))) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - if ((dewa->dewarpcache = - (L_DEWARP **)reallocNew((void **)&dewa->dewarpcache, - sizeof(L_DEWARP *) * dewa->nalloc, - size * sizeof(L_DEWARP *))) == NULL) - return ERROR_INT("new ptr cache array not returned", procName, 1); - dewa->nalloc = size; - } - return 0; -} - - -/*! - * \brief dewarpaGetDewarp() - * - * \param[in] dewa populated with dewarp structs for pages - * \param[in] index into dewa: this is the pageno - * \return dew handle; still owned by dewa, or NULL on error - */ -L_DEWARP * -dewarpaGetDewarp(L_DEWARPA *dewa, - l_int32 index) -{ - PROCNAME("dewarpaGetDewarp"); - - if (!dewa) - return (L_DEWARP *)ERROR_PTR("dewa not defined", procName, NULL); - if (index < 0 || index > dewa->maxpage) { - L_ERROR("index = %d is invalid; max index = %d\n", - procName, index, dewa->maxpage); - return NULL; - } - - return dewa->dewarp[index]; -} - - -/*----------------------------------------------------------------------* - * Setting parameters to control rendering from the model * - *----------------------------------------------------------------------*/ -/*! - * \brief dewarpaSetCurvatures() - * - * \param[in] dewa - * \param[in] max_linecurv -1 for default - * \param[in] min_diff_linecurv -1 for default; 0 to accept all models - * \param[in] max_diff_linecurv -1 for default - * \param[in] max_edgecurv -1 for default - * \param[in] max_diff_edgecurv -1 for default - * \param[in] max_edgeslope -1 for default - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Approximating the line by a quadratic, the coefficient - * of the quadratic term is the curvature, and distance - * units are in pixels (of course). The curvature is very - * small, so we multiply by 10^6 and express the constraints - * on the model curvatures in micro-units. - * (2) This sets five curvature thresholds and a slope threshold: - * * the maximum absolute value of the vertical disparity - * line curvatures - * * the minimum absolute value of the largest difference in - * vertical disparity line curvatures (Use a value of 0 - * to accept all models.) - * * the maximum absolute value of the largest difference in - * vertical disparity line curvatures - * * the maximum absolute value of the left and right edge - * curvature for the horizontal disparity - * * the maximum absolute value of the difference between - * left and right edge curvature for the horizontal disparity - * all in micro-units, for dewarping to take place. - * Use -1 for default values. - * (3) An image with a line curvature less than about 0.00001 - * has fairly straight textlines. This is 10 micro-units. - * (4) For example, if %max_linecurv == 100, this would prevent dewarping - * if any of the lines has a curvature exceeding 100 micro-units. - * A model having maximum line curvature larger than about 150 - * micro-units should probably not be used. - * (5) A model having a left or right edge curvature larger than - * about 50 micro-units should probably not be used. - *- */ -l_ok -dewarpaSetCurvatures(L_DEWARPA *dewa, - l_int32 max_linecurv, - l_int32 min_diff_linecurv, - l_int32 max_diff_linecurv, - l_int32 max_edgecurv, - l_int32 max_diff_edgecurv, - l_int32 max_edgeslope) -{ - PROCNAME("dewarpaSetCurvatures"); - - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - - if (max_linecurv == -1) - dewa->max_linecurv = DefaultMaxLineCurv; - else - dewa->max_linecurv = L_ABS(max_linecurv); - - if (min_diff_linecurv == -1) - dewa->min_diff_linecurv = DefaultMinDiffLineCurv; - else - dewa->min_diff_linecurv = L_ABS(min_diff_linecurv); - - if (max_diff_linecurv == -1) - dewa->max_diff_linecurv = DefaultMaxDiffLineCurv; - else - dewa->max_diff_linecurv = L_ABS(max_diff_linecurv); - - if (max_edgecurv == -1) - dewa->max_edgecurv = DefaultMaxEdgeCurv; - else - dewa->max_edgecurv = L_ABS(max_edgecurv); - - if (max_diff_edgecurv == -1) - dewa->max_diff_edgecurv = DefaultMaxDiffEdgeCurv; - else - dewa->max_diff_edgecurv = L_ABS(max_diff_edgecurv); - - if (max_edgeslope == -1) - dewa->max_edgeslope = DefaultMaxEdgeSlope; - else - dewa->max_edgeslope = L_ABS(max_edgeslope); - - dewa->modelsready = 0; /* force validation */ - return 0; -} - - -/*! - * \brief dewarpaUseBothArrays() - * - * \param[in] dewa - * \param[in] useboth 0 for false, 1 for true - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This sets the useboth field. If set, this will attempt - * to apply both vertical and horizontal disparity arrays. - * Note that a model with only a vertical disparity array will - * always be valid. - *- */ -l_ok -dewarpaUseBothArrays(L_DEWARPA *dewa, - l_int32 useboth) -{ - PROCNAME("dewarpaUseBothArrays"); - - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - - dewa->useboth = useboth; - dewa->modelsready = 0; /* force validation */ - return 0; -} - - -/*! - * \brief dewarpaSetCheckColumns() - * - * \param[in] dewa - * \param[in] check_columns 0 for false, 1 for true - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This sets the 'check_columns" field. If set, and if - * 'useboth' is set, this will count the number of text - * columns. If the number is larger than 1, this will - * prevent the application of horizontal disparity arrays - * if they exist. Note that the default value of check_columns - * if 0 (FALSE). - * (2) This field is set to 0 by default. For horizontal disparity - * correction to take place on a single column of text, you must have: - * - a valid horizontal disparity array - * - useboth = 1 (TRUE) - * If there are multiple columns, additionally - * - check_columns = 0 (FALSE) - * - *- */ -l_ok -dewarpaSetCheckColumns(L_DEWARPA *dewa, - l_int32 check_columns) -{ - PROCNAME("dewarpaSetCheckColumns"); - - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - - dewa->check_columns = check_columns; - return 0; -} - - -/*! - * \brief dewarpaSetMaxDistance() - * - * \param[in] dewa - * \param[in] maxdist for using ref models - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This sets the maxdist field. - *- */ -l_ok -dewarpaSetMaxDistance(L_DEWARPA *dewa, - l_int32 maxdist) -{ - PROCNAME("dewarpaSetMaxDistance"); - - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - - dewa->maxdist = maxdist; - dewa->modelsready = 0; /* force validation */ - return 0; -} - - -/*----------------------------------------------------------------------* - * Dewarp serialized I/O * - *----------------------------------------------------------------------*/ -/*! - * \brief dewarpRead() - * - * \param[in] filename - * \return dew, or NULL on error - */ -L_DEWARP * -dewarpRead(const char *filename) -{ -FILE *fp; -L_DEWARP *dew; - - PROCNAME("dewarpRead"); - - if (!filename) - return (L_DEWARP *)ERROR_PTR("filename not defined", procName, NULL); - if ((fp = fopenReadStream(filename)) == NULL) - return (L_DEWARP *)ERROR_PTR("stream not opened", procName, NULL); - - if ((dew = dewarpReadStream(fp)) == NULL) { - fclose(fp); - return (L_DEWARP *)ERROR_PTR("dew not read", procName, NULL); - } - - fclose(fp); - return dew; -} - - -/*! - * \brief dewarpReadStream() - * - * \param[in] fp file stream - * \return dew dewarp, or NULL on error - * - *
- * Notes: - * (1) The dewarp struct is stored in minimized format, with only - * subsampled disparity arrays. - * (2) The sampling and extra horizontal disparity parameters are - * stored here. During generation of the dewarp struct, they - * are passed in from the dewarpa. In readback, it is assumed - * that they are (a) the same for each page and (b) the same - * as the values used to create the dewarpa. - *- */ -L_DEWARP * -dewarpReadStream(FILE *fp) -{ -l_int32 version, sampling, redfactor, minlines, pageno, hasref, refpage; -l_int32 w, h, nx, ny, vdispar, hdispar, nlines; -l_int32 mincurv, maxcurv, leftslope, rightslope, leftcurv, rightcurv; -L_DEWARP *dew; -FPIX *fpixv, *fpixh; - - PROCNAME("dewarpReadStream"); - - if (!fp) - return (L_DEWARP *)ERROR_PTR("stream not defined", procName, NULL); - - if (fscanf(fp, "\nDewarp Version %d\n", &version) != 1) - return (L_DEWARP *)ERROR_PTR("not a dewarp file", procName, NULL); - if (version != DEWARP_VERSION_NUMBER) - return (L_DEWARP *)ERROR_PTR("invalid dewarp version", procName, NULL); - if (fscanf(fp, "pageno = %d\n", &pageno) != 1) - return (L_DEWARP *)ERROR_PTR("read fail for pageno", procName, NULL); - if (fscanf(fp, "hasref = %d, refpage = %d\n", &hasref, &refpage) != 2) - return (L_DEWARP *)ERROR_PTR("read fail for hasref, refpage", - procName, NULL); - if (fscanf(fp, "sampling = %d, redfactor = %d\n", &sampling, &redfactor) - != 2) - return (L_DEWARP *)ERROR_PTR("read fail for sampling/redfactor", - procName, NULL); - if (fscanf(fp, "nlines = %d, minlines = %d\n", &nlines, &minlines) != 2) - return (L_DEWARP *)ERROR_PTR("read fail for nlines/minlines", - procName, NULL); - if (fscanf(fp, "w = %d, h = %d\n", &w, &h) != 2) - return (L_DEWARP *)ERROR_PTR("read fail for w, h", procName, NULL); - if (fscanf(fp, "nx = %d, ny = %d\n", &nx, &ny) != 2) - return (L_DEWARP *)ERROR_PTR("read fail for nx, ny", procName, NULL); - if (fscanf(fp, "vert_dispar = %d, horiz_dispar = %d\n", &vdispar, &hdispar) - != 2) - return (L_DEWARP *)ERROR_PTR("read fail for flags", procName, NULL); - if (vdispar) { - if (fscanf(fp, "min line curvature = %d, max line curvature = %d\n", - &mincurv, &maxcurv) != 2) - return (L_DEWARP *)ERROR_PTR("read fail for mincurv & maxcurv", - procName, NULL); - } - if (hdispar) { - if (fscanf(fp, "left edge slope = %d, right edge slope = %d\n", - &leftslope, &rightslope) != 2) - return (L_DEWARP *)ERROR_PTR("read fail for leftslope & rightslope", - procName, NULL); - if (fscanf(fp, "left edge curvature = %d, right edge curvature = %d\n", - &leftcurv, &rightcurv) != 2) - return (L_DEWARP *)ERROR_PTR("read fail for leftcurv & rightcurv", - procName, NULL); - } - if (vdispar) { - if ((fpixv = fpixReadStream(fp)) == NULL) - return (L_DEWARP *)ERROR_PTR("read fail for vdispar", - procName, NULL); - } - if (hdispar) { - if ((fpixh = fpixReadStream(fp)) == NULL) - return (L_DEWARP *)ERROR_PTR("read fail for hdispar", - procName, NULL); - } - getc(fp); - - dew = (L_DEWARP *)LEPT_CALLOC(1, sizeof(L_DEWARP)); - dew->w = w; - dew->h = h; - dew->pageno = pageno; - dew->sampling = sampling; - dew->redfactor = redfactor; - dew->minlines = minlines; - dew->nlines = nlines; - dew->hasref = hasref; - dew->refpage = refpage; - if (hasref == 0) /* any dew without a ref has an actual model */ - dew->vsuccess = 1; - dew->nx = nx; - dew->ny = ny; - if (vdispar) { - dew->mincurv = mincurv; - dew->maxcurv = maxcurv; - dew->vsuccess = 1; - dew->sampvdispar = fpixv; - } - if (hdispar) { - dew->leftslope = leftslope; - dew->rightslope = rightslope; - dew->leftcurv = leftcurv; - dew->rightcurv = rightcurv; - dew->hsuccess = 1; - dew->samphdispar = fpixh; - } - - return dew; -} - - -/*! - * \brief dewarpReadMem() - * - * \param[in] data serialization of dewarp - * \param[in] size of data in bytes - * \return dew dewarp, or NULL on error - */ -L_DEWARP * -dewarpReadMem(const l_uint8 *data, - size_t size) -{ -FILE *fp; -L_DEWARP *dew; - - PROCNAME("dewarpReadMem"); - - if (!data) - return (L_DEWARP *)ERROR_PTR("data not defined", procName, NULL); - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return (L_DEWARP *)ERROR_PTR("stream not opened", procName, NULL); - - dew = dewarpReadStream(fp); - fclose(fp); - if (!dew) L_ERROR("dew not read\n", procName); - return dew; -} - - -/*! - * \brief dewarpWrite() - * - * \param[in] filename - * \param[in] dew - * \return 0 if OK, 1 on error - */ -l_ok -dewarpWrite(const char *filename, - L_DEWARP *dew) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("dewarpWrite"); - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!dew) - return ERROR_INT("dew not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "wb")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = dewarpWriteStream(fp, dew); - fclose(fp); - if (ret) - return ERROR_INT("dew not written to stream", procName, 1); - return 0; -} - - -/*! - * \brief dewarpWriteStream() - * - * \param[in] fp file stream opened for "wb" - * \param[in] dew - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This should not be written if there is no sampled - * vertical disparity array, which means that no model has - * been built for this page. - *- */ -l_ok -dewarpWriteStream(FILE *fp, - L_DEWARP *dew) -{ -l_int32 vdispar, hdispar; - - PROCNAME("dewarpWriteStream"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!dew) - return ERROR_INT("dew not defined", procName, 1); - - fprintf(fp, "\nDewarp Version %d\n", DEWARP_VERSION_NUMBER); - fprintf(fp, "pageno = %d\n", dew->pageno); - fprintf(fp, "hasref = %d, refpage = %d\n", dew->hasref, dew->refpage); - fprintf(fp, "sampling = %d, redfactor = %d\n", - dew->sampling, dew->redfactor); - fprintf(fp, "nlines = %d, minlines = %d\n", dew->nlines, dew->minlines); - fprintf(fp, "w = %d, h = %d\n", dew->w, dew->h); - fprintf(fp, "nx = %d, ny = %d\n", dew->nx, dew->ny); - vdispar = (dew->sampvdispar) ? 1 : 0; - hdispar = (dew->samphdispar) ? 1 : 0; - fprintf(fp, "vert_dispar = %d, horiz_dispar = %d\n", vdispar, hdispar); - if (vdispar) - fprintf(fp, "min line curvature = %d, max line curvature = %d\n", - dew->mincurv, dew->maxcurv); - if (hdispar) { - fprintf(fp, "left edge slope = %d, right edge slope = %d\n", - dew->leftslope, dew->rightslope); - fprintf(fp, "left edge curvature = %d, right edge curvature = %d\n", - dew->leftcurv, dew->rightcurv); - } - if (vdispar) fpixWriteStream(fp, dew->sampvdispar); - if (hdispar) fpixWriteStream(fp, dew->samphdispar); - fprintf(fp, "\n"); - - if (!vdispar) - L_WARNING("no disparity arrays!\n", procName); - return 0; -} - - -/*! - * \brief dewarpWriteMem() - * - * \param[out] pdata data of serialized dewarp (not ascii) - * \param[out] psize size of returned data - * \param[in] dew - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Serializes a dewarp in memory and puts the result in a buffer. - *- */ -l_ok -dewarpWriteMem(l_uint8 **pdata, - size_t *psize, - L_DEWARP *dew) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("dewarpWriteMem"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - if (!psize) - return ERROR_INT("&size not defined", procName, 1); - if (!dew) - return ERROR_INT("dew not defined", procName, 1); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = dewarpWriteStream(fp, dew); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = dewarpWriteStream(fp, dew); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} - - -/*----------------------------------------------------------------------* - * Dewarpa serialized I/O * - *----------------------------------------------------------------------*/ -/*! - * \brief dewarpaRead() - * - * \param[in] filename - * \return dewa, or NULL on error - */ -L_DEWARPA * -dewarpaRead(const char *filename) -{ -FILE *fp; -L_DEWARPA *dewa; - - PROCNAME("dewarpaRead"); - - if (!filename) - return (L_DEWARPA *)ERROR_PTR("filename not defined", procName, NULL); - if ((fp = fopenReadStream(filename)) == NULL) - return (L_DEWARPA *)ERROR_PTR("stream not opened", procName, NULL); - - if ((dewa = dewarpaReadStream(fp)) == NULL) { - fclose(fp); - return (L_DEWARPA *)ERROR_PTR("dewa not read", procName, NULL); - } - - fclose(fp); - return dewa; -} - - -/*! - * \brief dewarpaReadStream() - * - * \param[in] fp file stream - * \return dewa, or NULL on error - * - *
- * Notes: - * (1) The serialized dewarp contains a Numa that gives the - * (increasing) page number of the dewarp structs that are - * contained. - * (2) Reference pages are added in after readback. - *- */ -L_DEWARPA * -dewarpaReadStream(FILE *fp) -{ -l_int32 i, version, ndewarp, maxpage; -l_int32 sampling, redfactor, minlines, maxdist, useboth; -l_int32 max_linecurv, min_diff_linecurv, max_diff_linecurv; -l_int32 max_edgeslope, max_edgecurv, max_diff_edgecurv; -L_DEWARP *dew; -L_DEWARPA *dewa; -NUMA *namodels; - - PROCNAME("dewarpaReadStream"); - - if (!fp) - return (L_DEWARPA *)ERROR_PTR("stream not defined", procName, NULL); - - if (fscanf(fp, "\nDewarpa Version %d\n", &version) != 1) - return (L_DEWARPA *)ERROR_PTR("not a dewarpa file", procName, NULL); - if (version != DEWARP_VERSION_NUMBER) - return (L_DEWARPA *)ERROR_PTR("invalid dewarp version", procName, NULL); - - if (fscanf(fp, "ndewarp = %d, maxpage = %d\n", &ndewarp, &maxpage) != 2) - return (L_DEWARPA *)ERROR_PTR("read fail for maxpage+", procName, NULL); - if (fscanf(fp, - "sampling = %d, redfactor = %d, minlines = %d, maxdist = %d\n", - &sampling, &redfactor, &minlines, &maxdist) != 4) - return (L_DEWARPA *)ERROR_PTR("read fail for 4 params", procName, NULL); - if (fscanf(fp, - "max_linecurv = %d, min_diff_linecurv = %d, max_diff_linecurv = %d\n", - &max_linecurv, &min_diff_linecurv, &max_diff_linecurv) != 3) - return (L_DEWARPA *)ERROR_PTR("read fail for linecurv", procName, NULL); - if (fscanf(fp, - "max_edgeslope = %d, max_edgecurv = %d, max_diff_edgecurv = %d\n", - &max_edgeslope, &max_edgecurv, &max_diff_edgecurv) != 3) - return (L_DEWARPA *)ERROR_PTR("read fail for edgecurv", procName, NULL); - if (fscanf(fp, "fullmodel = %d\n", &useboth) != 1) - return (L_DEWARPA *)ERROR_PTR("read fail for useboth", procName, NULL); - - if (ndewarp > MaxPtrArraySize) - return (L_DEWARPA *)ERROR_PTR("too many pages", procName, NULL); - - dewa = dewarpaCreate(maxpage + 1, sampling, redfactor, minlines, maxdist); - dewa->maxpage = maxpage; - dewa->max_linecurv = max_linecurv; - dewa->min_diff_linecurv = min_diff_linecurv; - dewa->max_diff_linecurv = max_diff_linecurv; - dewa->max_edgeslope = max_edgeslope; - dewa->max_edgecurv = max_edgecurv; - dewa->max_diff_edgecurv = max_diff_edgecurv; - dewa->useboth = useboth; - namodels = numaCreate(ndewarp); - dewa->namodels = namodels; - for (i = 0; i < ndewarp; i++) { - if ((dew = dewarpReadStream(fp)) == NULL) { - L_ERROR("read fail for dew[%d]\n", procName, i); - dewarpaDestroy(&dewa); - return NULL; - } - dewarpaInsertDewarp(dewa, dew); - numaAddNumber(namodels, dew->pageno); - } - - /* Validate the models and insert reference models */ - dewarpaInsertRefModels(dewa, 0, 0); - return dewa; -} - - -/*! - * \brief dewarpaReadMem() - * - * \param[in] data serialization of dewarpa - * \param[in] size of data in bytes - * \return dewa dewarpa, or NULL on error - */ -L_DEWARPA * -dewarpaReadMem(const l_uint8 *data, - size_t size) -{ -FILE *fp; -L_DEWARPA *dewa; - - PROCNAME("dewarpaReadMem"); - - if (!data) - return (L_DEWARPA *)ERROR_PTR("data not defined", procName, NULL); - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return (L_DEWARPA *)ERROR_PTR("stream not opened", procName, NULL); - - dewa = dewarpaReadStream(fp); - fclose(fp); - if (!dewa) L_ERROR("dewa not read\n", procName); - return dewa; -} - - -/*! - * \brief dewarpaWrite() - * - * \param[in] filename - * \param[in] dewa - * \return 0 if OK, 1 on error - */ -l_ok -dewarpaWrite(const char *filename, - L_DEWARPA *dewa) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("dewarpaWrite"); - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "wb")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = dewarpaWriteStream(fp, dewa); - fclose(fp); - if (ret) - return ERROR_INT("dewa not written to stream", procName, 1); - return 0; -} - - -/*! - * \brief dewarpaWriteStream() - * - * \param[in] fp file stream opened for "wb" - * \param[in] dewa - * \return 0 if OK, 1 on error - */ -l_ok -dewarpaWriteStream(FILE *fp, - L_DEWARPA *dewa) -{ -l_int32 ndewarp, i, pageno; - - PROCNAME("dewarpaWriteStream"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - - /* Generate the list of page numbers for which a model exists. - * Note that no attempt is made to determine if the model is - * valid, because that determination is associated with - * using the model to remove the warping, which typically - * can happen later, after all the models have been built. */ - dewarpaListPages(dewa); - if (!dewa->namodels) - return ERROR_INT("dewa->namodels not made", procName, 1); - ndewarp = numaGetCount(dewa->namodels); /* with actual page models */ - - fprintf(fp, "\nDewarpa Version %d\n", DEWARP_VERSION_NUMBER); - fprintf(fp, "ndewarp = %d, maxpage = %d\n", ndewarp, dewa->maxpage); - fprintf(fp, "sampling = %d, redfactor = %d, minlines = %d, maxdist = %d\n", - dewa->sampling, dewa->redfactor, dewa->minlines, dewa->maxdist); - fprintf(fp, - "max_linecurv = %d, min_diff_linecurv = %d, max_diff_linecurv = %d\n", - dewa->max_linecurv, dewa->min_diff_linecurv, dewa->max_diff_linecurv); - fprintf(fp, - "max_edgeslope = %d, max_edgecurv = %d, max_diff_edgecurv = %d\n", - dewa->max_edgeslope, dewa->max_edgecurv, dewa->max_diff_edgecurv); - fprintf(fp, "fullmodel = %d\n", dewa->useboth); - for (i = 0; i < ndewarp; i++) { - numaGetIValue(dewa->namodels, i, &pageno); - dewarpWriteStream(fp, dewarpaGetDewarp(dewa, pageno)); - } - - return 0; -} - - -/*! - * \brief dewarpaWriteMem() - * - * \param[out] pdata data of serialized dewarpa (not ascii) - * \param[out] psize size of returned data - * \param[in] dewa - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Serializes a dewarpa in memory and puts the result in a buffer. - *- */ -l_ok -dewarpaWriteMem(l_uint8 **pdata, - size_t *psize, - L_DEWARPA *dewa) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("dewarpaWriteMem"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - if (!psize) - return ERROR_INT("&size not defined", procName, 1); - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = dewarpaWriteStream(fp, dewa); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = dewarpaWriteStream(fp, dewa); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dewarp2.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dewarp2.c deleted file mode 100644 index a5f9e3e2..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dewarp2.c +++ /dev/null @@ -1,1918 +0,0 @@ -/*====================================================================* - - 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 dewarp2.c - *
- * - * Build the page disparity model - * - * Build basic page disparity model - * l_int32 dewarpBuildPageModel() - * l_int32 dewarpFindVertDisparity() - * l_int32 dewarpFindHorizDisparity() - * PTAA *dewarpGetTextlineCenters() - * static PTA *dewarpGetMeanVerticals() - * PTAA *dewarpRemoveShortLines() - * static l_int32 dewarpGetLineEndPoints() - * static l_int32 dewarpFilterLineEndPoints() - * static PTA *dewarpRemoveBadEndPoints() - * static l_int32 dewarpIsLineCoverageValid() - * static l_int32 dewarpQuadraticLSF() - * - * Build disparity model for slope near binding - * l_int32 dewarpFindHorizSlopeDisparity() - * - * Build the line disparity model - * l_int32 dewarpBuildLineModel() - * - * Query model status - * l_int32 dewarpaModelStatus() - * - * Rendering helpers - * static l_int32 pixRenderMidYs() - * static l_int32 pixRenderHorizEndPoints - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This is the basic function that builds the horizontal and - * vertical disparity arrays, which allow determination of the - * src pixel in the input image corresponding to each - * dest pixel in the dewarped image. - * (2) Sets vsuccess = 1 if the vertical disparity array builds. - * Always attempts to build the horizontal disparity array, - * even if it will not be requested (useboth == 0). - * Sets hsuccess = 1 if horizontal disparity builds. - * (3) The method is as follows: - * (a) Estimate the points along the centers of all the - * long textlines. If there are too few lines, no - * disparity models are built. - * (b) From the vertical deviation of the lines, estimate - * the vertical disparity. - * (c) From the ends of the lines, estimate the horizontal - * disparity, assuming that the text is made of lines - * that are close to left and right justified. - * (d) One can also compute an additional contribution to the - * horizontal disparity, inferred from slopes of the top - * and bottom lines. We do not do this. - * (4) In more detail for the vertical disparity: - * (a) Fit a LS quadratic to center locations along each line. - * This smooths the curves. - * (b) Sample each curve at a regular interval, find the y-value - * of the mid-point on each curve, and subtract the sampled - * curve value from this value. This is the vertical - * disparity at sampled points along each curve. - * (c) Fit a LS quadratic to each set of vertically aligned - * disparity samples. This smooths the disparity values - * in the vertical direction. Then resample at the same - * regular interval. We now have a regular grid of smoothed - * vertical disparity valuels. - * (5) Once the sampled vertical disparity array is found, it can be - * interpolated to get a full resolution vertical disparity map. - * This can be applied directly to the src image pixels - * to dewarp the image in the vertical direction, making - * all textlines horizontal. Likewise, the horizontal - * disparity array is used to left- and right-align the - * longest textlines. - *- */ -l_ok -dewarpBuildPageModel(L_DEWARP *dew, - const char *debugfile) -{ -l_int32 linecount, ntop, nbot, ytop, ybot, ret; -PIX *pixs, *pix1, *pix2, *pix3; -PTA *pta; -PTAA *ptaa1, *ptaa2; - - PROCNAME("dewarpBuildPageModel"); - - if (!dew) - return ERROR_INT("dew not defined", procName, 1); - - dew->debug = (debugfile) ? 1 : 0; - dew->vsuccess = dew->hsuccess = 0; - pixs = dew->pixs; - if (debugfile) { - lept_rmdir("lept/dewmod"); /* erase previous images */ - lept_mkdir("lept/dewmod"); - pixDisplayWithTitle(pixs, 0, 0, "pixs", 1); - pixWriteDebug("/tmp/lept/dewmod/0010.png", pixs, IFF_PNG); - } - - /* Make initial estimate of centers of textlines */ - ptaa1 = dewarpGetTextlineCenters(pixs, debugfile || DEBUG_TEXTLINE_CENTERS); - if (!ptaa1) { - L_WARNING("textline centers not found; model not built\n", procName); - return 1; - } - if (debugfile) { - pix1 = pixConvertTo32(pixs); - pta = generatePtaFilledCircle(1); - pix2 = pixGenerateFromPta(pta, 5, 5); - pix3 = pixDisplayPtaaPattern(NULL, pix1, ptaa1, pix2, 2, 2); - pixWriteDebug("/tmp/lept/dewmod/0020.png", pix3, IFF_PNG); - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - ptaDestroy(&pta); - } - - /* Remove all lines that are not at least 0.8 times the length - * of the longest line. */ - ptaa2 = dewarpRemoveShortLines(pixs, ptaa1, 0.8, - debugfile || DEBUG_SHORT_LINES); - if (debugfile) { - pix1 = pixConvertTo32(pixs); - pta = generatePtaFilledCircle(1); - pix2 = pixGenerateFromPta(pta, 5, 5); - pix3 = pixDisplayPtaaPattern(NULL, pix1, ptaa2, pix2, 2, 2); - pixWriteDebug("/tmp/lept/dewmod/0030.png", pix3, IFF_PNG); - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - ptaDestroy(&pta); - } - ptaaDestroy(&ptaa1); - - /* Verify that there are sufficient "long" lines */ - linecount = ptaaGetCount(ptaa2); - if (linecount < dew->minlines) { - ptaaDestroy(&ptaa2); - L_WARNING("linecount %d < min req'd number of lines (%d) for model\n", - procName, linecount, dew->minlines); - return 1; - } - - /* Verify that the lines have a reasonable coverage of the - * vertical extent of the page. */ - if (dewarpIsLineCoverageValid(ptaa2, pixGetHeight(pixs), - &ntop, &nbot, &ytop, &ybot) == FALSE) { - ptaaDestroy(&ptaa2); - L_WARNING("invalid line coverage: ntop = %d, nbot = %d;" - " spanning [%d ... %d] in height %d\n", procName, - ntop, nbot, ytop, ybot, pixGetHeight(pixs)); - return 1; - } - - /* Get the sampled vertical disparity from the textline centers. - * The disparity array will push pixels vertically so that each - * textline is flat and centered at the y-position of the mid-point. */ - if (dewarpFindVertDisparity(dew, ptaa2, 0) != 0) { - L_WARNING("vertical disparity not built\n", procName); - ptaaDestroy(&ptaa2); - return 1; - } - - /* Get the sampled horizontal disparity from the left and right - * edges of the text. The disparity array will expand the image - * linearly outward to align the text edges vertically. - * Do this even if useboth == 0; we still calculate it even - * if we don't plan to use it. */ - if ((ret = dewarpFindHorizDisparity(dew, ptaa2)) == 0) - L_INFO("hsuccess = 1\n", procName); - - /* Debug output */ - if (debugfile) { - dewarpPopulateFullRes(dew, NULL, 0, 0); - pix1 = fpixRenderContours(dew->fullvdispar, 3.0, 0.15); - pixWriteDebug("/tmp/lept/dewmod/0060.png", pix1, IFF_PNG); - pixDisplay(pix1, 1000, 0); - pixDestroy(&pix1); - if (ret == 0) { - pix1 = fpixRenderContours(dew->fullhdispar, 3.0, 0.15); - pixWriteDebug("/tmp/lept/dewmod/0070.png", pix1, IFF_PNG); - pixDisplay(pix1, 1000, 0); - pixDestroy(&pix1); - } - convertFilesToPdf("/tmp/lept/dewmod", NULL, 135, 1.0, 0, 0, - "Dewarp Build Model", debugfile); - lept_stderr("pdf file: %s\n", debugfile); - } - - ptaaDestroy(&ptaa2); - return 0; -} - - -/*! - * \brief dewarpFindVertDisparity() - * - * \param[in] dew - * \param[in] ptaa unsmoothed lines, not vertically ordered - * \param[in] rotflag 0 if using dew->pixs; 1 if rotated by 90 degrees cw - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This starts with points along the centers of textlines. - * It does quadratic fitting (and smoothing), first along the - * lines and then in the vertical direction, to generate - * the sampled vertical disparity map. This can then be - * interpolated to full resolution and used to remove - * the vertical line warping. - * (2) Use %rotflag == 1 if you are dewarping vertical lines, as - * is done in dewarpBuildLineModel(). The usual case is for - * %rotflag == 0. - * (3) Note that this builds a vertical disparity model (VDM), but - * does not check it against constraints for validity. - * Constraint checking is done after building the models, - * and before inserting reference models. - * (4) This sets the vsuccess flag to 1 on success. - * (5) Pix debug output goes to /tmp/dewvert/ for collection into - * a pdf. Non-pix debug output goes to /tmp. - *- */ -l_ok -dewarpFindVertDisparity(L_DEWARP *dew, - PTAA *ptaa, - l_int32 rotflag) -{ -l_int32 i, j, nlines, npts, nx, ny, sampling; -l_float32 c0, c1, c2, x, y, midy, val, medval, meddev, minval, maxval; -l_float32 *famidys; -NUMA *nax, *nafit, *nacurve0, *nacurve1, *nacurves; -NUMA *namidy, *namidys, *namidysi; -PIX *pix1, *pix2, *pixcirc, *pixdb; -PTA *pta, *ptad, *ptacirc; -PTAA *ptaa0, *ptaa1, *ptaa2, *ptaa3, *ptaa4, *ptaa5, *ptaat; -FPIX *fpix; - - PROCNAME("dewarpFindVertDisparity"); - - if (!dew) - return ERROR_INT("dew not defined", procName, 1); - dew->vsuccess = 0; - if (!ptaa) - return ERROR_INT("ptaa not defined", procName, 1); - - if (dew->debug) L_INFO("finding vertical disparity\n", procName); - - /* Do quadratic fit to smooth each line. A single quadratic - * over the entire width of the line appears to be sufficient. - * Quartics tend to overfit to noise. Each line is thus - * represented by three coefficients: y(x) = c2 * x^2 + c1 * x + c0. - * Using the coefficients, sample each fitted curve uniformly - * across the full width of the image. The result is in ptaa0. */ - sampling = dew->sampling; - nx = (rotflag) ? dew->ny : dew->nx; - ny = (rotflag) ? dew->nx : dew->ny; - nlines = ptaaGetCount(ptaa); - dew->nlines = nlines; - ptaa0 = ptaaCreate(nlines); - nacurve0 = numaCreate(nlines); /* stores curvature coeff c2 */ - pixdb = (rotflag) ? pixRotateOrth(dew->pixs, 1) : pixClone(dew->pixs); - for (i = 0; i < nlines; i++) { /* for each line */ - pta = ptaaGetPta(ptaa, i, L_CLONE); - ptaGetQuadraticLSF(pta, &c2, &c1, &c0, NULL); - numaAddNumber(nacurve0, c2); - ptad = ptaCreate(nx); - for (j = 0; j < nx; j++) { /* uniformly sampled in x */ - x = j * sampling; - applyQuadraticFit(c2, c1, c0, x, &y); - ptaAddPt(ptad, x, y); - } - ptaaAddPta(ptaa0, ptad, L_INSERT); - ptaDestroy(&pta); - } - if (dew->debug) { - lept_mkdir("lept/dewarp"); - lept_mkdir("lept/dewdebug"); - lept_mkdir("lept/dewmod"); - ptaat = ptaaCreate(nlines); - for (i = 0; i < nlines; i++) { - pta = ptaaGetPta(ptaa, i, L_CLONE); - ptaGetArrays(pta, &nax, NULL); - ptaGetQuadraticLSF(pta, NULL, NULL, NULL, &nafit); - ptad = ptaCreateFromNuma(nax, nafit); - ptaaAddPta(ptaat, ptad, L_INSERT); - ptaDestroy(&pta); - numaDestroy(&nax); - numaDestroy(&nafit); - } - pix1 = pixConvertTo32(pixdb); - pta = generatePtaFilledCircle(1); - pixcirc = pixGenerateFromPta(pta, 5, 5); - pix2 = pixDisplayPtaaPattern(NULL, pix1, ptaat, pixcirc, 2, 2); - pixWriteDebug("/tmp/lept/dewmod/0041.png", pix2, IFF_PNG); - pixDestroy(&pix1); - pixDestroy(&pix2); - ptaDestroy(&pta); - pixDestroy(&pixcirc); - ptaaDestroy(&ptaat); - } - - /* Remove lines with outlier curvatures. - * Note that this is just looking for internal consistency in - * the line curvatures. It is not rejecting lines based on - * the magnitude of the curvature. That is done when constraints - * are applied for valid models. */ - numaGetMedianDevFromMedian(nacurve0, &medval, &meddev); - L_INFO("\nPage %d\n", procName, dew->pageno); - L_INFO("Pass 1: Curvature: medval = %f, meddev = %f\n", - procName, medval, meddev); - ptaa1 = ptaaCreate(nlines); - nacurve1 = numaCreate(nlines); - for (i = 0; i < nlines; i++) { /* for each line */ - numaGetFValue(nacurve0, i, &val); - if (L_ABS(val - medval) > 7.0 * meddev) /* TODO: reduce to ~ 3.0 */ - continue; - pta = ptaaGetPta(ptaa0, i, L_CLONE); - ptaaAddPta(ptaa1, pta, L_INSERT); - numaAddNumber(nacurve1, val); - } - nlines = ptaaGetCount(ptaa1); - numaDestroy(&nacurve0); - - /* Save the min and max curvature (in micro-units) */ - numaGetMin(nacurve1, &minval, NULL); - numaGetMax(nacurve1, &maxval, NULL); - dew->mincurv = lept_roundftoi(1000000. * minval); - dew->maxcurv = lept_roundftoi(1000000. * maxval); - L_INFO("Pass 2: Min/max curvature = (%d, %d)\n", procName, - dew->mincurv, dew->maxcurv); - - /* Find and save the y values at the mid-points in each curve. - * If the slope is zero anywhere, it will typically be here. */ - namidy = numaCreate(nlines); - for (i = 0; i < nlines; i++) { - pta = ptaaGetPta(ptaa1, i, L_CLONE); - npts = ptaGetCount(pta); - ptaGetPt(pta, npts / 2, NULL, &midy); - numaAddNumber(namidy, midy); - ptaDestroy(&pta); - } - - /* Sort the lines in ptaa1c by their vertical position, going down */ - namidysi = numaGetSortIndex(namidy, L_SORT_INCREASING); - namidys = numaSortByIndex(namidy, namidysi); - nacurves = numaSortByIndex(nacurve1, namidysi); - numaDestroy(&dew->namidys); /* in case previously made */ - numaDestroy(&dew->nacurves); - dew->namidys = namidys; - dew->nacurves = nacurves; - ptaa2 = ptaaSortByIndex(ptaa1, namidysi); - numaDestroy(&namidy); - numaDestroy(&nacurve1); - numaDestroy(&namidysi); - if (dew->debug) { - numaWriteDebug("/tmp/lept/dewdebug/midys.na", namidys); - numaWriteDebug("/tmp/lept/dewdebug/curves.na", nacurves); - pix1 = pixConvertTo32(pixdb); - ptacirc = generatePtaFilledCircle(5); - pixcirc = pixGenerateFromPta(ptacirc, 11, 11); - srand(3); - pixDisplayPtaaPattern(pix1, pix1, ptaa2, pixcirc, 5, 5); - srand(3); /* use the same colors for text and reference lines */ - pixRenderMidYs(pix1, namidys, 2); - pix2 = (rotflag) ? pixRotateOrth(pix1, 3) : pixClone(pix1); - pixWriteDebug("/tmp/lept/dewmod/0042.png", pix2, IFF_PNG); - pixDisplay(pix2, 0, 0); - ptaDestroy(&ptacirc); - pixDestroy(&pixcirc); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - pixDestroy(&pixdb); - - /* Convert the sampled points in ptaa2 to a sampled disparity with - * with respect to the y value at the mid point in the curve. - * The disparity is the distance the point needs to move; - * plus is downward. */ - ptaa3 = ptaaCreate(nlines); - for (i = 0; i < nlines; i++) { - pta = ptaaGetPta(ptaa2, i, L_CLONE); - numaGetFValue(namidys, i, &midy); - ptad = ptaCreate(nx); - for (j = 0; j < nx; j++) { - ptaGetPt(pta, j, &x, &y); - ptaAddPt(ptad, x, midy - y); - } - ptaaAddPta(ptaa3, ptad, L_INSERT); - ptaDestroy(&pta); - } - if (dew->debug) { - ptaaWriteDebug("/tmp/lept/dewdebug/ptaa3.ptaa", ptaa3, 0); - } - - /* Generate ptaa4 by taking vertical 'columns' from ptaa3. - * We want to fit the vertical disparity on the column to the - * vertical position of the line, which we call 'y' here and - * obtain from namidys. So each pta in ptaa4 is the set of - * vertical disparities down a column of points. The columns - * in ptaa4 are equally spaced in x. */ - ptaa4 = ptaaCreate(nx); - famidys = numaGetFArray(namidys, L_NOCOPY); - for (j = 0; j < nx; j++) { - pta = ptaCreate(nlines); - for (i = 0; i < nlines; i++) { - y = famidys[i]; - ptaaGetPt(ptaa3, i, j, NULL, &val); /* disparity value */ - ptaAddPt(pta, y, val); - } - ptaaAddPta(ptaa4, pta, L_INSERT); - } - if (dew->debug) { - ptaaWriteDebug("/tmp/lept/dewdebug/ptaa4.ptaa", ptaa4, 0); - } - - /* Do quadratic fit vertically on each of the pixel columns - * in ptaa4, for the vertical displacement (which identifies the - * src pixel(s) for each dest pixel) as a function of y (the - * y value of the mid-points for each line). Then generate - * ptaa5 by sampling the fitted vertical displacement on a - * regular grid in the vertical direction. Each pta in ptaa5 - * gives the vertical displacement for regularly sampled y values - * at a fixed x. */ - ptaa5 = ptaaCreate(nx); /* uniformly sampled across full height of image */ - for (j = 0; j < nx; j++) { /* for each column */ - pta = ptaaGetPta(ptaa4, j, L_CLONE); - ptaGetQuadraticLSF(pta, &c2, &c1, &c0, NULL); - ptad = ptaCreate(ny); - for (i = 0; i < ny; i++) { /* uniformly sampled in y */ - y = i * sampling; - applyQuadraticFit(c2, c1, c0, y, &val); - ptaAddPt(ptad, y, val); - } - ptaaAddPta(ptaa5, ptad, L_INSERT); - ptaDestroy(&pta); - } - if (dew->debug) { - ptaaWriteDebug("/tmp/lept/dewdebug/ptaa5.ptaa", ptaa5, 0); - convertFilesToPdf("/tmp/lept/dewmod", "004", 135, 1.0, 0, 0, - "Dewarp Vert Disparity", - "/tmp/lept/dewarp/vert_disparity.pdf"); - lept_stderr("pdf file: /tmp/lept/dewarp/vert_disparity.pdf\n"); - } - - /* Save the result in a fpix at the specified subsampling */ - fpix = fpixCreate(nx, ny); - for (i = 0; i < ny; i++) { - for (j = 0; j < nx; j++) { - ptaaGetPt(ptaa5, j, i, NULL, &val); - fpixSetPixel(fpix, j, i, val); - } - } - dew->sampvdispar = fpix; - dew->vsuccess = 1; - - ptaaDestroy(&ptaa0); - ptaaDestroy(&ptaa1); - ptaaDestroy(&ptaa2); - ptaaDestroy(&ptaa3); - ptaaDestroy(&ptaa4); - ptaaDestroy(&ptaa5); - return 0; -} - - -/*! - * \brief dewarpFindHorizDisparity() - * - * \param[in] dew - * \param[in] ptaa unsmoothed lines, not vertically ordered - * \return 0 if OK, 1 if horizontal disparity array is not built, or on error - * - *
- * Notes: - * (1) This builds a horizontal disparity model (HDM), but - * does not check it against constraints for validity. - * Constraint checking is done at rendering time. - * (2) Horizontal disparity is not required for a successful model; - * only the vertical disparity is required. This will not be - * called if the function to build the vertical disparity fails. - * (3) This sets the hsuccess flag to 1 on success. - * (4) Internally in ptal1, ptar1, ptal2, ptar2: x and y are reversed, - * so the 'y' value is horizontal distance across the image width. - * (5) Debug output goes to /tmp/lept/dewmod/ for collection into a pdf. - *- */ -l_ok -dewarpFindHorizDisparity(L_DEWARP *dew, - PTAA *ptaa) -{ -l_int32 i, j, h, nx, ny, sampling, ret; -l_float32 c0, c1, cl0, cl1, cl2, cr0, cr1, cr2; -l_float32 x, y, refl, refr; -l_float32 val, mederr; -NUMA *nald, *nard; -PIX *pix1; -PTA *ptal1, *ptar1; /* left/right end points of lines; initial */ -PTA *ptal2, *ptar2; /* left/right end points; after filtering */ -PTA *ptal3, *ptar3; /* left and right block, fitted, uniform spacing */ -PTA *pta, *ptat, *pta1, *pta2; -PTAA *ptaah; -FPIX *fpix; - - PROCNAME("dewarpFindHorizDisparity"); - - if (!dew) - return ERROR_INT("dew not defined", procName, 1); - dew->hsuccess = 0; - if (!ptaa) - return ERROR_INT("ptaa not defined", procName, 1); - - if (dew->debug) L_INFO("finding horizontal disparity\n", procName); - - /* Get the endpoints of the lines, and sort from top to bottom */ - h = pixGetHeight(dew->pixs); - ret = dewarpGetLineEndPoints(h, ptaa, &ptal1, &ptar1); - if (ret) { - L_INFO("Horiz disparity not built\n", procName); - return 1; - } - if (dew->debug) { - lept_mkdir("lept/dewdebug"); - lept_mkdir("lept/dewarp"); - ptaWriteDebug("/tmp/lept/dewdebug/endpts_left1.pta", ptal1, 1); - ptaWriteDebug("/tmp/lept/dewdebug/endpts_right1.pta", ptar1, 1); - } - - /* Filter the points by x-location to prevent 2-column images - * from getting confused about left and right endpoints. We - * require valid left points to not be farther than - * 0.20 * (remaining distance to the right edge of the image) - * to the right of the leftmost endpoint, and similarly for - * the right endpoints. (Note: x and y are reversed in the pta.) - * Also require end points to be near the medians in the - * upper and lower halves. */ - ret = dewarpFilterLineEndPoints(dew, ptal1, ptar1, &ptal2, &ptar2); - ptaDestroy(&ptal1); - ptaDestroy(&ptar1); - if (ret) { - L_INFO("Not enough filtered end points\n", procName); - return 1; - } - - /* Do a quadratic fit to the left and right endpoints of the - * longest lines. Each line is represented by 3 coefficients: - * x(y) = c2 * y^2 + c1 * y + c0. - * Using the coefficients, sample each fitted curve uniformly - * along the full height of the image. */ - sampling = dew->sampling; - nx = dew->nx; - ny = dew->ny; - - /* Fit the left side, using quadratic LSF on the set of long - * lines. It is not necessary to use the noisy LSF fit - * function, because we've removed outlier end points by - * selecting the long lines. Then uniformly sample along - * this fitted curve. */ - dewarpQuadraticLSF(ptal2, &cl2, &cl1, &cl0, &mederr); - dew->leftslope = lept_roundftoi(1000. * cl1); /* milli-units */ - dew->leftcurv = lept_roundftoi(1000000. * cl2); /* micro-units */ - L_INFO("Left quad LSF median error = %5.2f\n", procName, mederr); - L_INFO("Left edge slope = %d\n", procName, dew->leftslope); - L_INFO("Left edge curvature = %d\n", procName, dew->leftcurv); - ptal3 = ptaCreate(ny); - for (i = 0; i < ny; i++) { /* uniformly sampled in y */ - y = i * sampling; - applyQuadraticFit(cl2, cl1, cl0, y, &x); - ptaAddPt(ptal3, x, y); - } - - /* Fit the right side in the same way. */ - dewarpQuadraticLSF(ptar2, &cr2, &cr1, &cr0, &mederr); - dew->rightslope = lept_roundftoi(1000.0 * cr1); /* milli-units */ - dew->rightcurv = lept_roundftoi(1000000. * cr2); /* micro-units */ - L_INFO("Right quad LSF median error = %5.2f\n", procName, mederr); - L_INFO("Right edge slope = %d\n", procName, dew->rightslope); - L_INFO("Right edge curvature = %d\n", procName, dew->rightcurv); - ptar3 = ptaCreate(ny); - for (i = 0; i < ny; i++) { /* uniformly sampled in y */ - y = i * sampling; - applyQuadraticFit(cr2, cr1, cr0, y, &x); - ptaAddPt(ptar3, x, y); - } - - if (dew->debug) { - PTA *ptalft, *ptarft; - h = pixGetHeight(dew->pixs); - pta1 = ptaCreate(h); - pta2 = ptaCreate(h); - for (i = 0; i < h; i++) { - applyQuadraticFit(cl2, cl1, cl0, i, &x); - ptaAddPt(pta1, x, i); - applyQuadraticFit(cr2, cr1, cr0, i, &x); - ptaAddPt(pta2, x, i); - } - pix1 = pixDisplayPta(NULL, dew->pixs, pta1); - pixDisplayPta(pix1, pix1, pta2); - pixRenderHorizEndPoints(pix1, ptal2, ptar2, 0xff000000); - pixDisplay(pix1, 600, 800); - pixWriteDebug("/tmp/lept/dewmod/0051.png", pix1, IFF_PNG); - pixDestroy(&pix1); - - pix1 = pixDisplayPta(NULL, dew->pixs, pta1); - pixDisplayPta(pix1, pix1, pta2); - ptalft = ptaTranspose(ptal3); - ptarft = ptaTranspose(ptar3); - pixRenderHorizEndPoints(pix1, ptalft, ptarft, 0x0000ff00); - pixDisplay(pix1, 800, 800); - pixWriteDebug("/tmp/lept/dewmod/0052.png", pix1, IFF_PNG); - convertFilesToPdf("/tmp/lept/dewmod", "005", 135, 1.0, 0, 0, - "Dewarp Horiz Disparity", - "/tmp/lept/dewarp/horiz_disparity.pdf"); - lept_stderr("pdf file: /tmp/lept/dewarp/horiz_disparity.pdf\n"); - pixDestroy(&pix1); - ptaDestroy(&pta1); - ptaDestroy(&pta2); - ptaDestroy(&ptalft); - ptaDestroy(&ptarft); - } - - /* Find the x value at the midpoints (in y) of the two vertical lines, - * ptal3 and ptar3. These are the reference values for each of the - * lines. Then use the difference between the these midpoint - * values and the actual x coordinates of the lines to represent - * the horizontal disparity (nald, nard) on the vertical lines - * for the sampled y values. */ - ptaGetPt(ptal3, ny / 2, &refl, NULL); - ptaGetPt(ptar3, ny / 2, &refr, NULL); - nald = numaCreate(ny); - nard = numaCreate(ny); - for (i = 0; i < ny; i++) { - ptaGetPt(ptal3, i, &x, NULL); - numaAddNumber(nald, refl - x); - ptaGetPt(ptar3, i, &x, NULL); - numaAddNumber(nard, refr - x); - } - - /* Now for each pair of sampled values of the two lines (at the - * same value of y), do a linear interpolation to generate - * the horizontal disparity on all sampled points between them. */ - ptaah = ptaaCreate(ny); - for (i = 0; i < ny; i++) { - pta = ptaCreate(2); - numaGetFValue(nald, i, &val); - ptaAddPt(pta, refl, val); - numaGetFValue(nard, i, &val); - ptaAddPt(pta, refr, val); - ptaGetLinearLSF(pta, &c1, &c0, NULL); /* horiz disparity along line */ - ptat = ptaCreate(nx); - for (j = 0; j < nx; j++) { - x = j * sampling; - applyLinearFit(c1, c0, x, &val); - ptaAddPt(ptat, x, val); - } - ptaaAddPta(ptaah, ptat, L_INSERT); - ptaDestroy(&pta); - } - numaDestroy(&nald); - numaDestroy(&nard); - - /* Save the result in a fpix at the specified subsampling */ - fpix = fpixCreate(nx, ny); - for (i = 0; i < ny; i++) { - for (j = 0; j < nx; j++) { - ptaaGetPt(ptaah, i, j, NULL, &val); - fpixSetPixel(fpix, j, i, val); - } - } - dew->samphdispar = fpix; - dew->hsuccess = 1; - ptaDestroy(&ptal2); - ptaDestroy(&ptar2); - ptaDestroy(&ptal3); - ptaDestroy(&ptar3); - ptaaDestroy(&ptaah); - return 0; -} - - -/*! - * \brief dewarpGetTextlineCenters() - * - * \param[in] pixs 1 bpp - * \param[in] debugflag 1 for debug output - * \return ptaa of center values of textlines - * - *
- * Notes: - * (1) This in general does not have a point for each value - * of x, because there will be gaps between words. - * It doesn't matter because we will fit a quadratic to the - * points that we do have. - *- */ -PTAA * -dewarpGetTextlineCenters(PIX *pixs, - l_int32 debugflag) -{ -char buf[64]; -l_int32 i, w, h, bx, by, nsegs, csize1, csize2; -BOXA *boxa; -PIX *pix1, *pix2; -PIXA *pixa1, *pixa2; -PTA *pta; -PTAA *ptaa; - - PROCNAME("dewarpGetTextlineCenters"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - - if (debugflag) L_INFO("finding text line centers\n", procName); - - /* Filter to solidify the text lines within the x-height region, - * and to remove most of the ascenders and descenders. - * We start with a small vertical opening to remove noise beyond - * the line that can cause an error in the line end points. - * The small closing (csize1) is used to bridge the gaps between - * letters. The large closing (csize2) bridges the gaps between - * words; using 1/30 of the page width usually suffices. */ - csize1 = L_MAX(15, w / 80); - csize2 = L_MAX(40, w / 30); - snprintf(buf, sizeof(buf), "o1.3 + c%d.1 + o%d.1 + c%d.1", - csize1, csize1, csize2); - pix1 = pixMorphSequence(pixs, buf, 0); - - /* Remove the components (e.g., embedded images) that have - * long vertical runs (>= 50 pixels). You can't use bounding - * boxes because connected component b.b. of lines can be quite - * tall due to slope and curvature. */ - pix2 = pixMorphSequence(pix1, "e1.50", 0); /* seed */ - pixSeedfillBinary(pix2, pix2, pix1, 8); /* tall components */ - pixXor(pix2, pix2, pix1); /* remove tall */ - - if (debugflag) { - lept_mkdir("lept/dewmod"); - pixWriteDebug("/tmp/lept/dewmod/0011.tif", pix1, IFF_TIFF_G4); - pixDisplayWithTitle(pix1, 0, 600, "pix1", 1); - pixWriteDebug("/tmp/lept/dewmod/0012.tif", pix2, IFF_TIFF_G4); - pixDisplayWithTitle(pix2, 0, 800, "pix2", 1); - } - pixDestroy(&pix1); - - /* Get the 8-connected components ... */ - boxa = pixConnComp(pix2, &pixa1, 8); - pixDestroy(&pix2); - boxaDestroy(&boxa); - if (pixaGetCount(pixa1) == 0) { - pixaDestroy(&pixa1); - return NULL; - } - - /* ... and remove the short width and very short height c.c */ - pixa2 = pixaSelectBySize(pixa1, 100, 4, L_SELECT_IF_BOTH, - L_SELECT_IF_GT, NULL); - if ((nsegs = pixaGetCount(pixa2)) == 0) { - pixaDestroy(&pixa1); - pixaDestroy(&pixa2); - return NULL; - } - if (debugflag) { - pix2 = pixaDisplay(pixa2, w, h); - pixWriteDebug("/tmp/lept/dewmod/0013.tif", pix2, IFF_TIFF_G4); - pixDisplayWithTitle(pix2, 0, 1000, "pix2", 1); - pixDestroy(&pix2); - } - - /* For each c.c., get the weighted center of each vertical column. - * The result is a set of points going approximately through - * the center of the x-height part of the text line. */ - ptaa = ptaaCreate(nsegs); - for (i = 0; i < nsegs; i++) { - pixaGetBoxGeometry(pixa2, i, &bx, &by, NULL, NULL); - pix2 = pixaGetPix(pixa2, i, L_CLONE); - pta = dewarpGetMeanVerticals(pix2, bx, by); - ptaaAddPta(ptaa, pta, L_INSERT); - pixDestroy(&pix2); - } - if (debugflag) { - pix1 = pixCreateTemplate(pixs); - pix2 = pixDisplayPtaa(pix1, ptaa); - pixWriteDebug("/tmp/lept/dewmod/0014.tif", pix2, IFF_PNG); - pixDisplayWithTitle(pix2, 0, 1200, "pix3", 1); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - - pixaDestroy(&pixa1); - pixaDestroy(&pixa2); - return ptaa; -} - - -/*! - * \brief dewarpGetMeanVerticals() - * - * \param[in] pixs 1 bpp, single c.c. - * \param[in] x,y location of UL corner of pixs, relative to page image - * \return pta (mean y-values in component for each x-value, - * both translated by (x,y - */ -static PTA * -dewarpGetMeanVerticals(PIX *pixs, - l_int32 x, - l_int32 y) -{ -l_int32 w, h, i, j, wpl, sum, count; -l_uint32 *line, *data; -PTA *pta; - - PROCNAME("pixGetMeanVerticals"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - pta = ptaCreate(w); - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - for (j = 0; j < w; j++) { - line = data; - sum = count = 0; - for (i = 0; i < h; i++) { - if (GET_DATA_BIT(line, j) == 1) { - sum += i; - count += 1; - } - line += wpl; - } - if (count == 0) continue; - ptaAddPt(pta, x + j, y + (sum / count)); - } - - return pta; -} - - -/*! - * \brief dewarpRemoveShortLines() - * - * \param[in] pixs 1 bpp - * \param[in] ptaas input lines - * \param[in] fract minimum fraction of longest line to keep - * \param[in] debugflag - * \return ptaad containing only lines of sufficient length, - * or NULL on error - */ -PTAA * -dewarpRemoveShortLines(PIX *pixs, - PTAA *ptaas, - l_float32 fract, - l_int32 debugflag) -{ -l_int32 w, n, i, index, maxlen, len; -l_float32 minx, maxx; -NUMA *na, *naindex; -PIX *pix1, *pix2; -PTA *pta; -PTAA *ptaad; - - PROCNAME("dewarpRemoveShortLines"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (!ptaas) - return (PTAA *)ERROR_PTR("ptaas undefined", procName, NULL); - - pixGetDimensions(pixs, &w, NULL, NULL); - n = ptaaGetCount(ptaas); - ptaad = ptaaCreate(n); - na = numaCreate(n); - for (i = 0; i < n; i++) { - pta = ptaaGetPta(ptaas, i, L_CLONE); - ptaGetRange(pta, &minx, &maxx, NULL, NULL); - numaAddNumber(na, maxx - minx + 1); - ptaDestroy(&pta); - } - - /* Sort by length and find all that are long enough */ - naindex = numaGetSortIndex(na, L_SORT_DECREASING); - numaGetIValue(naindex, 0, &index); - numaGetIValue(na, index, &maxlen); - if (maxlen < 0.5 * w) - L_WARNING("lines are relatively short\n", procName); - pta = ptaaGetPta(ptaas, index, L_CLONE); - ptaaAddPta(ptaad, pta, L_INSERT); - for (i = 1; i < n; i++) { - numaGetIValue(naindex, i, &index); - numaGetIValue(na, index, &len); - if (len < fract * maxlen) break; - pta = ptaaGetPta(ptaas, index, L_CLONE); - ptaaAddPta(ptaad, pta, L_INSERT); - } - - if (debugflag) { - pix1 = pixCopy(NULL, pixs); - pix2 = pixDisplayPtaa(pix1, ptaad); - pixDisplayWithTitle(pix2, 0, 200, "pix4", 1); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - - numaDestroy(&na); - numaDestroy(&naindex); - return ptaad; -} - - -/*! - * \brief dewarpGetLineEndPoints() - * - * \param[in] h height of pixs - * \param[in] ptaa lines - * \param[out] pptal left end points of each line - * \param[out] pptar right end points of each line - * \return 0 if OK, 1 on error. - * - *
- * Notes: - * (1) We require that the set of end points extends over 45% of the - * height of the input image, to insure good coverage and - * avoid extrapolating the curvature too far beyond the - * actual textlines. Large extrapolations are particularly - * dangerous if used as a reference model. We also require - * at least 10 lines of text. - * (2) We sort the lines from top to bottom (sort by x in the ptas). - * (3) For fitting the endpoints, x = f(y), we transpose x and y. - * Thus all these ptas have x and y swapped! - *- */ -static l_int32 -dewarpGetLineEndPoints(l_int32 h, - PTAA *ptaa, - PTA **pptal, - PTA **pptar) -{ -l_int32 i, n, npt, x, y; -l_float32 miny, maxy, ratio; -PTA *pta, *ptal1, *ptar1; - - PROCNAME("dewarpGetLineEndPoints"); - - if (!pptal || !pptar) - return ERROR_INT("&ptal and &ptar not both defined", procName, 1); - *pptal = *pptar = NULL; - if (!ptaa) - return ERROR_INT("ptaa undefined", procName, 1); - - /* Are there at least 10 lines? */ - n = ptaaGetCount(ptaa); - if (n < MinLinesForHoriz1) { - L_INFO("only %d lines; too few\n", procName, n); - return 1; - } - - /* Extract the line end points, and transpose x and y values */ - ptal1 = ptaCreate(n); - ptar1 = ptaCreate(n); - for (i = 0; i < n; i++) { - pta = ptaaGetPta(ptaa, i, L_CLONE); - ptaGetIPt(pta, 0, &x, &y); - ptaAddPt(ptal1, y, x); /* transpose */ - npt = ptaGetCount(pta); - ptaGetIPt(pta, npt - 1, &x, &y); - ptaAddPt(ptar1, y, x); /* transpose */ - ptaDestroy(&pta); - } - - /* Use the min and max of the y value on the left side. */ - ptaGetRange(ptal1, &miny, &maxy, NULL, NULL); - ratio = (maxy - miny) / (l_float32)h; - if (ratio < MinRatioLinesToHeight) { - L_INFO("ratio lines to height, %f, too small\n", procName, ratio); - ptaDestroy(&ptal1); - ptaDestroy(&ptar1); - return 1; - } - - /* Sort from top to bottom */ - *pptal = ptaSort(ptal1, L_SORT_BY_X, L_SORT_INCREASING, NULL); - *pptar = ptaSort(ptar1, L_SORT_BY_X, L_SORT_INCREASING, NULL); - ptaDestroy(&ptal1); - ptaDestroy(&ptar1); - return 0; -} - - -/*! - * \brief dewarpFilterLineEndPoints() - * - * \param[in] dew - * \param[in] ptal input left end points of each line - * \param[in] ptar input right end points of each line - * \param[out] pptalf filtered left end points - * \param[out] pptarf filtered right end points - * \return 0 if OK, 1 on error. - * - *
- * Notes: - * (1) Avoid confusion with multiple columns by requiring that line - * end points be close enough to leftmost and rightmost end points. - * Must have at least 8 points on left and right after this step. - * (2) Apply second filtering step, find the median positions in - * top and bottom halves, and removing end points that are - * displaced too much from these in the x direction. - * Must have at least 6 points on left and right after this step. - * (3) Reminder: x and y in the pta are transposed; think x = f(y). - *- */ -static l_int32 -dewarpFilterLineEndPoints(L_DEWARP *dew, - PTA *ptal, - PTA *ptar, - PTA **pptalf, - PTA **pptarf) -{ -l_int32 w, i, n; -l_float32 ymin, ymax, xvall, xvalr, yvall, yvalr; -PTA *ptal1, *ptar1, *ptal2, *ptar2; - - PROCNAME("dewarpFilterLineEndPoints"); - if (!ptal || !ptar) - return ERROR_INT("ptal or ptar not defined", procName, 1); - *pptalf = *pptarf = NULL; - - /* First filter for lines near left and right margins */ - w = pixGetWidth(dew->pixs); - ptaGetMinMax(ptal, NULL, &ymin, NULL, NULL); - ptaGetMinMax(ptar, NULL, NULL, NULL, &ymax); - n = ptaGetCount(ptal); /* ptar is the same size; at least 10 */ - ptal1 = ptaCreate(n); - ptar1 = ptaCreate(n); - for (i = 0; i < n; i++) { - ptaGetPt(ptal, i, &xvall, &yvall); - ptaGetPt(ptar, i, &xvalr, &yvalr); - if (yvall < ymin + 0.20 * (w - ymin) && - yvalr > 0.80 * ymax) { - ptaAddPt(ptal1, xvall, yvall); - ptaAddPt(ptar1, xvalr, yvalr); - } - } - if (dew->debug) { - ptaWriteDebug("/tmp/lept/dewdebug/endpts_left2.pta", ptal1, 1); - ptaWriteDebug("/tmp/lept/dewdebug/endpts_right2.pta", ptar1, 1); - } - - n = L_MIN(ptaGetCount(ptal1), ptaGetCount(ptar1)); - if (n < MinLinesForHoriz1 - 2) { - ptaDestroy(&ptal1); - ptaDestroy(&ptar1); - L_INFO("First filter: only %d endpoints; needed 8\n", procName, n); - return 1; - } - - /* Remove outlier points */ - ptal2 = dewarpRemoveBadEndPoints(w, ptal1); - ptar2 = dewarpRemoveBadEndPoints(w, ptar1); - ptaDestroy(&ptal1); - ptaDestroy(&ptar1); - if (!ptal2 || !ptar2) { - ptaDestroy(&ptal2); - ptaDestroy(&ptar2); - L_INFO("Second filter: too few endpoints left after outliers removed\n", - procName); - return 1; - } - if (dew->debug) { - ptaWriteDebug("/tmp/lept/dewdebug/endpts_left3.pta", ptal2, 1); - ptaWriteDebug("/tmp/lept/dewdebug/endpts_right3.pta", ptar2, 1); - } - - *pptalf = ptal2; - *pptarf = ptar2; - return 0; -} - - -/*! - * \brief dewarpRemoveBadEndPoints() - * - * \param[in] w width of input image - * \param[in] ptas left or right line end points - * \return ptad filtered left or right end points, or NULL on error. - * - *
- * Notes: - * (1) The input set is sorted by line position (x value). - * Break into two (upper and lower); for each find the median - * horizontal (y value), and remove all points farther than - * a fraction of the image width from this. Make sure each - * part still has at least 3 points, and join the two sections - * before returning. - * (2) Reminder: x and y in the pta are transposed; think x = f(y). - *- */ -static PTA * -dewarpRemoveBadEndPoints(l_int32 w, - PTA *ptas) -{ -l_int32 i, n, nu, nd; -l_float32 rval, xval, yval, delta; -PTA *ptau1, *ptau2, *ptad1, *ptad2; - - PROCNAME("dewarpRemoveBadEndPoints"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - - delta = AllowedWidthFract * w; - n = ptaGetCount(ptas); /* will be at least 8 */ - - /* Check the upper half */ - ptau1 = ptaSelectRange(ptas, 0, n / 2); - ptaGetRankValue(ptau1, 0.5, NULL, L_SORT_BY_Y, &rval); - nu = ptaGetCount(ptau1); - ptau2 = ptaCreate(nu); - for (i = 0; i < nu; i++) { - ptaGetPt(ptau1, i, &xval, &yval); /* transposed */ - if (L_ABS(rval - yval) <= delta) - ptaAddPt(ptau2, xval, yval); - } - ptaDestroy(&ptau1); - if (ptaGetCount(ptau2) < MinLinesForHoriz2) { - ptaDestroy(&ptau2); - L_INFO("Second filter: upper set is too small after outliers removed\n", - procName); - return NULL; - } - - /* Check the lower half */ - ptad1 = ptaSelectRange(ptas, n / 2 + 1, -1); - ptaGetRankValue(ptad1, 0.5, NULL, L_SORT_BY_Y, &rval); - nd = ptaGetCount(ptad1); - ptad2 = ptaCreate(nd); - for (i = 0; i < nd; i++) { - ptaGetPt(ptad1, i, &xval, &yval); /* transposed */ - if (L_ABS(rval - yval) <= delta) - ptaAddPt(ptad2, xval, yval); - } - ptaDestroy(&ptad1); - if (ptaGetCount(ptad2) < MinLinesForHoriz2) { - ptaDestroy(&ptau2); - ptaDestroy(&ptad2); - L_INFO("Second filter: lower set is too small after outliers removed\n", - procName); - return NULL; - } - - ptaJoin(ptau2, ptad2, 0, -1); - ptaDestroy(&ptad2); - return ptau2; -} - - -/*! - * \brief dewarpIsLineCoverageValid() - * - * \param[in] ptaa of validated lines - * \param[in] h height of pix - * \param[out] pntop number of lines in top half - * \param[out] pnbot number of lines in bottom half - * \param[out] pytop location of top line - * \param[out] pybot location of bottom line - * \return 1 if coverage is valid, 0 if not or on error. - * - *
- * Notes: - * (1) The criterion for valid coverage is: - * (a) there must be at least 4 lines in each half (top and bottom) - * of the image. - * (b) the coverage must be at least 50% of the image height - *- */ -static l_int32 -dewarpIsLineCoverageValid(PTAA *ptaa, - l_int32 h, - l_int32 *pntop, - l_int32 *pnbot, - l_int32 *pytop, - l_int32 *pybot) -{ -l_int32 i, n, iy, both_halves, ntop, nbot, ytop, ybot, nmin; -l_float32 y, fraction; -NUMA *na; - - PROCNAME("dewarpIsLineCoverageValid"); - - if (!ptaa) - return ERROR_INT("ptaa not defined", procName, 0); - if ((n = ptaaGetCount(ptaa)) == 0) - return ERROR_INT("ptaa empty", procName, 0); - if (h <= 0) - return ERROR_INT("invalid h", procName, 0); - if (!pntop || !pnbot) - return ERROR_INT("&ntop and &nbot not defined", procName, 0); - if (!pytop || !pybot) - return ERROR_INT("&ytop and &ybot not defined", procName, 0); - - na = numaCreate(n); - for (i = 0; i < n; i++) { - ptaaGetPt(ptaa, i, 0, NULL, &y); - numaAddNumber(na, y); - } - numaSort(na, na, L_SORT_INCREASING); - for (i = 0, ntop = 0; i < n; i++) { - numaGetIValue(na, i, &iy); - if (i == 0) ytop = iy; - if (i == n - 1) ybot = iy; - if (iy < 0.5 * h) - ntop++; - } - numaDestroy(&na); - nbot = n - ntop; - *pntop = ntop; - *pnbot = nbot; - *pytop = ytop; - *pybot = ybot; - nmin = 4; /* minimum number of lines required in each half */ - both_halves = (ntop >= nmin) && (nbot >= nmin); - fraction = (l_float32)(ybot - ytop) / (l_float32)h; - if (both_halves && fraction > 0.50) - return 1; - return 0; -} - - -/*! - * \brief dewarpQuadraticLSF() - * - * \param[in] ptad left or right end points of longest lines - * \param[out] pa coeff a of LSF: y = ax^2 + bx + c - * \param[out] pb coeff b of LSF: y = ax^2 + bx + c - * \param[out] pc coeff c of LSF: y = ax^2 + bx + c - * \param[out] pmederr [optional] median error - * \return 0 if OK, 1 on error. - * - *
- * Notes: - * (1) This is used for finding the left or right sides of - * the text block, computed as a quadratic curve. - * Only the longest lines are input, so there are - * no outliers. - * (2) The ptas for the end points all have x and y swapped. - *- */ -static l_int32 -dewarpQuadraticLSF(PTA *ptad, - l_float32 *pa, - l_float32 *pb, - l_float32 *pc, - l_float32 *pmederr) -{ -l_int32 i, n; -l_float32 x, y, xp, c0, c1, c2; -NUMA *naerr; - - PROCNAME("dewarpQuadraticLSF"); - - if (pmederr) *pmederr = 0.0; - if (!pa || !pb || !pc) - return ERROR_INT("not all ptrs are defined", procName, 1); - *pa = *pb = *pc = 0.0; - if (!ptad) - return ERROR_INT("ptad not defined", procName, 1); - - /* Fit to the longest lines */ - ptaGetQuadraticLSF(ptad, &c2, &c1, &c0, NULL); - *pa = c2; - *pb = c1; - *pc = c0; - - /* Optionally, find the median error */ - if (pmederr) { - n = ptaGetCount(ptad); - naerr = numaCreate(n); - for (i = 0; i < n; i++) { - ptaGetPt(ptad, i, &y, &xp); - applyQuadraticFit(c2, c1, c0, y, &x); - numaAddNumber(naerr, L_ABS(x - xp)); - } - numaGetMedian(naerr, pmederr); - numaDestroy(&naerr); - } - return 0; -} - -/*----------------------------------------------------------------------* - * Build disparity model for slope near binding * - *----------------------------------------------------------------------*/ -/*! - * \brief dewarpFindHorizSlopeDisparity() - * - * \param[in] dew - * \param[in] pixb 1 bpp, with vert and horiz disparity removed - * \param[in] fractthresh threshold fractional difference in density - * \param[in] parity 0 if even page, 1 if odd page - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) %fractthresh is a threshold on the fractional difference in stroke - * density between between left and right sides. Process this - * disparity only if the absolute value of the fractional - * difference equals or exceeds this threshold. - * (2) %parity indicates where the binding is: on the left for - * %parity == 0 and on the right for %parity == 1. - * (3) This takes a 1 bpp %pixb where both vertical and horizontal - * disparity have been applied, so the text lines are straight and, - * more importantly, the line end points are vertically aligned. - * It estimates the foreshortening of the characters on the - * binding side, and if significant, computes a one-dimensional - * horizontal disparity function to compensate. - * (4) The first attempt was to use the average width of the - * connected components (c.c.) in vertical slices. This does not work - * reliably, because the horizontal compression of the text is - * often accompanied by horizontal joining of c.c. - * (5) We use the density of vertical strokes, measured by first using - * a vertical opening, which improves the signal. The result - * is relatively insensitive to the size of the opening; we use - * a 10-pixel opening. The relative density is measured by - * finding the number of c.c. in a full height sliding window - * of width 50 pixels, and compute every 25 pixels. Similar results - * are obtained counting c.c. that either intersect the window - * or are fully contained within it. - * (6) Debug output goes to /tmp/lept/dewmod/ for collection into a pdf. - *- */ -l_ok -dewarpFindHorizSlopeDisparity(L_DEWARP *dew, - PIX *pixb, - l_float32 fractthresh, - l_int32 parity) -{ -l_int32 i, j, x, n1, n2, nb, ne, count, w, h, ival, prev; -l_int32 istart, iend, first, last, x0, x1, nx, ny; -l_float32 fract, delta, sum, aveval, fval, del, denom; -l_float32 ca, cb, cc, cd, ce, y; -BOX *box; -BOXA *boxa1, *boxa2; -GPLOT *gplot; -NUMA *na1, *na2, *na3, *na4, *nasum; -PIX *pix1; -PTA *pta1; -FPIX *fpix; - - PROCNAME("dewarpFindHorizSlopeDisparity"); - - if (!dew) - return ERROR_INT("dew not defined", procName, 1); - if (!dew->vvalid || !dew->hvalid) - return ERROR_INT("invalid vert or horiz disparity model", procName, 1); - if (!pixb || pixGetDepth(pixb) != 1) - return ERROR_INT("pixb not defined or not 1 bpp", procName, 1); - - if (dew->debug) L_INFO("finding slope horizontal disparity\n", procName); - - /* Find the bounding boxes of the vertical strokes; remove noise */ - pix1 = pixMorphSequence(pixb, "o1.10", 0); - pixDisplay(pix1, 100, 100); - boxa1 = pixConnCompBB(pix1, 4); - boxa2 = boxaSelectBySize(boxa1, 0, 5, L_SELECT_HEIGHT, L_SELECT_IF_GT, - NULL); - nb = boxaGetCount(boxa2); - lept_stderr("number of components: %d\n", nb); - boxaDestroy(&boxa1); - - /* Estimate the horizontal density of vertical strokes */ - na1 = numaCreate(0); - numaSetParameters(na1, 0, 25); - pixGetDimensions(pixb, &w, &h, NULL); - for (x = 0; x + 50 < w; x += 25) { - box = boxCreate(x, 0, 50, h); - boxaContainedInBoxCount(boxa2, box, &count); - numaAddNumber(na1, count); - boxDestroy(&box); - } - if (dew->debug) { - lept_mkdir("lept/dew"); - gplotSimple1(na1, GPLOT_PNG, "/tmp/lept/dew/0091", NULL); - lept_mv("/tmp/lept/dew/0091.png", "lept/dewmod", NULL, NULL); - pixWriteDebug("/tmp/lept/dewmod/0090.png", pix1, IFF_PNG); - } - pixDestroy(&pix1); - boxaDestroy(&boxa2); - - /* Find the left and right end local maxima; if the difference - * is small, quit. */ - n1 = numaGetCount(na1); - prev = 0; - istart = 0; - first = 0; - for (i = 0; i < n1; i++) { - numaGetIValue(na1, i, &ival); - if (ival >= prev) { - prev = ival; - continue; - } else { - first = prev; - istart = i - 1; - break; - } - } - prev = 0; - last = 0; - iend = n1 - 1; - for (i = n1 - 1; i >= 0; i--) { - numaGetIValue(na1, i, &ival); - if (ival >= prev) { - prev = ival; - continue; - } else { - last = prev; - iend = i + 1; - break; - } - } - na2 = numaClipToInterval(na1, istart, iend); - numaDestroy(&na1); - n2 = numaGetCount(na2); - delta = (parity == 0) ? last - first : first - last; - denom = L_MAX(1.0, (l_float32)(L_MIN(first, last))); - fract = (l_float32)delta / denom; - if (dew->debug) { - L_INFO("Slope-disparity: first = %d, last = %d, fract = %7.3f\n", - procName, first, last, fract); - gplotSimple1(na2, GPLOT_PNG, "/tmp/lept/dew/0092", NULL); - lept_mv("/tmp/lept/dew/0092.png", "lept/dewmod", NULL, NULL); - } - if (fract < fractthresh) { - L_INFO("Small slope-disparity: first = %d, last = %d, fract = %7.3f\n", - procName, first, last, fract); - numaDestroy(&na2); - return 0; - } - - /* Find the density far from the binding, and normalize to 1. */ - ne = n2 - n2 % 2; - if (parity == 0) - numaGetSumOnInterval(na2, 0, ne / 2 - 1, &sum); - else /* parity == 1 */ - numaGetSumOnInterval(na2, ne / 2, ne - 1, &sum); - denom = L_MAX(1.0, (l_float32)(ne / 2)); - aveval = sum / denom; - na3 = numaMakeConstant(aveval, n2); - numaArithOp(na2, na2, na3, L_ARITH_DIVIDE); - numaDestroy(&na3); - if (dew->debug) { - L_INFO("Average background density: %5.1f\n", procName, aveval); - gplotSimple1(na2, GPLOT_PNG, "/tmp/lept/dew/0093", NULL); - lept_mv("/tmp/lept/dew/0093.png", "lept/dewmod", NULL, NULL); - } - - /* Fit the normalized density curve to a quartic */ - pta1 = numaConvertToPta1(na2); - ptaWriteStream(stderr, pta1, 0); -/* ptaGetQuadraticLSF(pta1, NULL, NULL, NULL, &na3); */ - ptaGetQuarticLSF(pta1, &ca, &cb, &cc, &cd, &ce, &na3); - ptaGetArrays(pta1, &na4, NULL); - if (dew->debug) { - gplot = gplotSimpleXY1(na4, na3, GPLOT_LINES, GPLOT_PNG, - "/tmp/lept/dew/0094", NULL); - gplotDestroy(&gplot); - lept_mv("/tmp/lept/dew/0094.png", "lept/dewmod", NULL, NULL); - } - ptaDestroy(&pta1); - - /* Integrate from the high point down to 1 (or v.v) to get the - * disparity needed to make the density constant. */ - nasum = numaMakeConstant(0, w); /* area under the curve above 1.0 */ - if (parity == 0) { - for (i = n2 - 1; i >= 0; i--) { - numaGetFValue(na3, i, &fval); - if (fval < 1.0) break; - } - numaGetIValue(na4, i + 1, &x0); - numaGetIValue(na4, n2 - 1, &x1); - numaSetParameters(nasum, x0, 1); - sum = 0.0; - for (x = x0; x < x1; x++) { - applyQuarticFit(ca, cb, cc, cd, ce, (l_float32)x, &y); - sum += (y - 1.0); - numaReplaceNumber(nasum, x, sum); - } - for (x = x1; x < w; x++) - numaReplaceNumber(nasum, x, sum); - } else { /* parity == 1 */ - for (i = 0; i < n2; i++) { - numaGetFValue(na3, i, &fval); - if (fval < 1.0) break; - } - numaGetIValue(na4, 0, &x0); - numaGetIValue(na4, i - 1, &x1); - numaSetParameters(nasum, x0, 1); - sum = 0.0; - for (x = x1; x >= x0; x--) { - applyQuarticFit(ca, cb, cc, cd, ce, (l_float32)x, &y); - sum += (y - 1.0); - numaReplaceNumber(nasum, x, sum); - } - for (x = x0; x >= 0; x--) - numaReplaceNumber(nasum, x, sum); - } - - /* Save the result in a fpix at the specified subsampling */ - nx = dew->nx; - ny = dew->ny; - fpix = fpixCreate(nx, ny); - del = (l_float32)w / (l_float32)nx; - for (i = 0; i < ny; i++) { - for (j = 0; j < nx; j++) { - x = del * j; - numaGetFValue(nasum, x, &fval); - fpixSetPixel(fpix, j, i, fval); - } - } - dew->sampydispar = fpix; - dew->ysuccess = 1; - - numaDestroy(&na2); - numaDestroy(&na3); - numaDestroy(&na4); - numaDestroy(&nasum); - return 0; -} - - -/*----------------------------------------------------------------------* - * Build line disparity model * - *----------------------------------------------------------------------*/ -/*! - * \brief dewarpBuildLineModel() - * - * \param[in] dew - * \param[in] opensize size of opening to remove perpendicular lines - * \param[in] debugfile use NULL to skip writing this - * \return 0 if OK, 1 if unable to build the model or on error - * - *
- * Notes: - * (1) This builds the horizontal and vertical disparity arrays - * for an input of ruled lines, typically for calibration. - * In book scanning, you could lay the ruled paper over a page. - * Then for that page and several below it, you can use the - * disparity correction of the line model to dewarp the pages. - * (2) The dew has been initialized with the image of ruled lines. - * These lines must be continuous, but we do a small amount - * of pre-processing here to insure that. - * (3) %opensize is typically about 8. It must be larger than - * the thickness of the lines to be extracted. This is the - * default value, which is applied if %opensize < 3. - * (4) Sets vsuccess = 1 and hsuccess = 1 if the vertical and/or - * horizontal disparity arrays build. - * (5) Similar to dewarpBuildPageModel(), except here the vertical - * and horizontal disparity arrays are both built from ruled lines. - * See notes there. - *- */ -l_ok -dewarpBuildLineModel(L_DEWARP *dew, - l_int32 opensize, - const char *debugfile) -{ -char buf[64]; -l_int32 i, j, bx, by, ret, nlines; -BOXA *boxa; -PIX *pixs, *pixh, *pixv, *pix, *pix1, *pix2; -PIXA *pixa1, *pixa2; -PTA *pta; -PTAA *ptaa1, *ptaa2; - - PROCNAME("dewarpBuildLineModel"); - - if (!dew) - return ERROR_INT("dew not defined", procName, 1); - if (opensize < 3) { - L_WARNING("opensize should be >= 3; setting to 8\n", procName); - opensize = 8; /* default */ - } - - dew->debug = (debugfile) ? 1 : 0; - dew->vsuccess = dew->hsuccess = 0; - pixs = dew->pixs; - if (debugfile) { - lept_rmdir("lept/dewline"); /* erase previous images */ - lept_mkdir("lept/dewline"); - lept_rmdir("lept/dewmod"); /* erase previous images */ - lept_mkdir("lept/dewmod"); - lept_mkdir("lept/dewarp"); - pixDisplayWithTitle(pixs, 0, 0, "pixs", 1); - pixWriteDebug("/tmp/lept/dewline/001.png", pixs, IFF_PNG); - } - - /* Extract and solidify the horizontal and vertical lines. We use - * the horizontal lines to derive the vertical disparity, and v.v. - * Both disparities are computed using the vertical disparity - * algorithm; the horizontal disparity is found from the - * vertical lines by rotating them clockwise by 90 degrees. - * On the first pass, we compute the horizontal disparity, from - * the vertical lines, by rotating them by 90 degrees (so they - * are horizontal) and computing the vertical disparity on them; - * we rotate the resulting fpix array for the horizontal disparity - * back by -90 degrees. On the second pass, we compute the vertical - * disparity from the horizontal lines in the usual fashion. */ - snprintf(buf, sizeof(buf), "d1.3 + c%d.1 + o%d.1", opensize - 2, opensize); - pixh = pixMorphSequence(pixs, buf, 0); /* horiz */ - snprintf(buf, sizeof(buf), "d3.1 + c1.%d + o1.%d", opensize - 2, opensize); - pix1 = pixMorphSequence(pixs, buf, 0); /* vert */ - pixv = pixRotateOrth(pix1, 1); /* vert rotated to horizontal */ - pixa1 = pixaCreate(2); - pixaAddPix(pixa1, pixv, L_INSERT); /* get horizontal disparity first */ - pixaAddPix(pixa1, pixh, L_INSERT); - pixDestroy(&pix1); - - /*--------------------------------------------------------------*/ - /* Process twice: first for horiz disparity, then for vert */ - /*--------------------------------------------------------------*/ - for (i = 0; i < 2; i++) { - pix = pixaGetPix(pixa1, i, L_CLONE); - pixDisplay(pix, 0, 900); - boxa = pixConnComp(pix, &pixa2, 8); - nlines = boxaGetCount(boxa); - boxaDestroy(&boxa); - if (nlines < dew->minlines) { - L_WARNING("only found %d lines\n", procName, nlines); - pixDestroy(&pix); - pixaDestroy(&pixa1); - continue; - } - - /* Identify the pixels along the skeleton of each line */ - ptaa1 = ptaaCreate(nlines); - for (j = 0; j < nlines; j++) { - pixaGetBoxGeometry(pixa2, j, &bx, &by, NULL, NULL); - pix1 = pixaGetPix(pixa2, j, L_CLONE); - pta = dewarpGetMeanVerticals(pix1, bx, by); - ptaaAddPta(ptaa1, pta, L_INSERT); - pixDestroy(&pix1); - } - pixaDestroy(&pixa2); - if (debugfile) { - pix1 = pixConvertTo32(pix); - pix2 = pixDisplayPtaa(pix1, ptaa1); - snprintf(buf, sizeof(buf), "/tmp/lept/dewline/%03d.png", 2 + 2 * i); - pixWriteDebug(buf, pix2, IFF_PNG); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - - /* Remove all lines that are not at least 0.75 times the length - * of the longest line. */ - ptaa2 = dewarpRemoveShortLines(pix, ptaa1, 0.75, DEBUG_SHORT_LINES); - if (debugfile) { - pix1 = pixConvertTo32(pix); - pix2 = pixDisplayPtaa(pix1, ptaa2); - snprintf(buf, sizeof(buf), "/tmp/lept/dewline/%03d.png", 3 + 2 * i); - pixWriteDebug(buf, pix2, IFF_PNG); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - ptaaDestroy(&ptaa1); - nlines = ptaaGetCount(ptaa2); - if (nlines < dew->minlines) { - pixDestroy(&pix); - ptaaDestroy(&ptaa2); - L_WARNING("%d lines: too few to build model\n", procName, nlines); - continue; - } - - /* Get the sampled 'vertical' disparity from the textline - * centers. The disparity array will push pixels vertically - * so that each line is flat and centered at the y-position - * of the mid-point. */ - ret = dewarpFindVertDisparity(dew, ptaa2, 1 - i); - - /* If i == 0, move the result to the horizontal disparity, - * rotating it back by -90 degrees. */ - if (i == 0) { /* horizontal disparity, really */ - if (ret) { - L_WARNING("horizontal disparity not built\n", procName); - } else { - L_INFO("hsuccess = 1\n", procName); - dew->samphdispar = fpixRotateOrth(dew->sampvdispar, 3); - fpixDestroy(&dew->sampvdispar); - if (debugfile) - lept_mv("/tmp/lept/dewarp/vert_disparity.pdf", - "lept/dewarp", "horiz_disparity.pdf", NULL); - } - dew->hsuccess = dew->vsuccess; - dew->vsuccess = 0; - } else { /* i == 1 */ - if (ret) - L_WARNING("vertical disparity not built\n", procName); - else - L_INFO("vsuccess = 1\n", procName); - } - ptaaDestroy(&ptaa2); - pixDestroy(&pix); - } - pixaDestroy(&pixa1); - - /* Debug output */ - if (debugfile) { - if (dew->vsuccess == 1) { - dewarpPopulateFullRes(dew, NULL, 0, 0); - pix1 = fpixRenderContours(dew->fullvdispar, 3.0, 0.15); - pixWriteDebug("/tmp/lept/dewline/006.png", pix1, IFF_PNG); - pixDisplay(pix1, 1000, 0); - pixDestroy(&pix1); - } - if (dew->hsuccess == 1) { - pix1 = fpixRenderContours(dew->fullhdispar, 3.0, 0.15); - pixWriteDebug("/tmp/lept/dewline/007.png", pix1, IFF_PNG); - pixDisplay(pix1, 1000, 0); - pixDestroy(&pix1); - } - convertFilesToPdf("/tmp/lept/dewline", NULL, 135, 1.0, 0, 0, - "Dewarp Build Line Model", debugfile); - lept_stderr("pdf file: %s\n", debugfile); - } - - return 0; -} - - -/*----------------------------------------------------------------------* - * Query model status * - *----------------------------------------------------------------------*/ -/*! - * \brief dewarpaModelStatus() - * - * \param[in] dewa - * \param[in] pageno - * \param[out] pvsuccess [optional] 1 on success - * \param[out] phsuccess [optional] 1 on success - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This tests if a model has been built, not if it is valid. - *- */ -l_ok -dewarpaModelStatus(L_DEWARPA *dewa, - l_int32 pageno, - l_int32 *pvsuccess, - l_int32 *phsuccess) -{ -L_DEWARP *dew; - - PROCNAME("dewarpaModelStatus"); - - if (pvsuccess) *pvsuccess = 0; - if (phsuccess) *phsuccess = 0; - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - - if ((dew = dewarpaGetDewarp(dewa, pageno)) == NULL) - return ERROR_INT("dew not retrieved", procName, 1); - if (pvsuccess) *pvsuccess = dew->vsuccess; - if (phsuccess) *phsuccess = dew->hsuccess; - return 0; -} - - -/*----------------------------------------------------------------------* - * Rendering helpers * - *----------------------------------------------------------------------*/ -/*! - * \brief pixRenderMidYs() - * - * \param[in] pixs 32 bpp - * \param[in] namidys y location of reference lines for vertical disparity - * \param[in] linew width of rendered line; typ 2 - * \return 0 if OK, 1 on error - */ -static l_int32 -pixRenderMidYs(PIX *pixs, - NUMA *namidys, - l_int32 linew) -{ -l_int32 i, n, w, yval, rval, gval, bval; -PIXCMAP *cmap; - - PROCNAME("pixRenderMidYs"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!namidys) - return ERROR_INT("namidys not defined", procName, 1); - - w = pixGetWidth(pixs); - n = numaGetCount(namidys); - cmap = pixcmapCreateRandom(8, 0, 0); - for (i = 0; i < n; i++) { - pixcmapGetColor(cmap, i % 256, &rval, &gval, &bval); - numaGetIValue(namidys, i, &yval); - pixRenderLineArb(pixs, 0, yval, w, yval, linew, rval, gval, bval); - } - pixcmapDestroy(&cmap); - return 0; -} - - -/*! - * \brief pixRenderHorizEndPoints() - * - * \param[in] pixs 32 bpp - * \param[in] ptal left side line end points - * \param[in] ptar right side line end points - * \param[in] color 0xrrggbb00 - * \return 0 if OK, 1 on error - */ -static l_int32 -pixRenderHorizEndPoints(PIX *pixs, - PTA *ptal, - PTA *ptar, - l_uint32 color) -{ -PIX *pixcirc; -PTA *ptalt, *ptart, *ptacirc; - - PROCNAME("pixRenderHorizEndPoints"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!ptal || !ptar) - return ERROR_INT("ptal and ptar not both defined", procName, 1); - - ptacirc = generatePtaFilledCircle(5); - pixcirc = pixGenerateFromPta(ptacirc, 11, 11); - ptalt = ptaTranspose(ptal); - ptart = ptaTranspose(ptar); - - pixDisplayPtaPattern(pixs, pixs, ptalt, pixcirc, 5, 5, color); - pixDisplayPtaPattern(pixs, pixs, ptart, pixcirc, 5, 5, color); - ptaDestroy(&ptacirc); - ptaDestroy(&ptalt); - ptaDestroy(&ptart); - pixDestroy(&pixcirc); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dewarp3.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dewarp3.c deleted file mode 100644 index 1b195c96..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dewarp3.c +++ /dev/null @@ -1,1016 +0,0 @@ -/*====================================================================* - - 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 dewarp3.c - *
- * - * Applying and stripping the page disparity model - * - * Apply disparity array to pix - * l_int32 dewarpaApplyDisparity() - * static l_int32 dewarpaApplyInit() - * static PIX *pixApplyVertDisparity() - * static PIX *pixApplyHorizDisparity() - * - * Apply disparity array to boxa - * l_int32 dewarpaApplyDisparityBoxa() - * static BOXA *boxaApplyDisparity() - * - * Stripping out data and populating full res disparity - * l_int32 dewarpMinimize() - * l_int32 dewarpPopulateFullRes() - * - * Static functions not presently in use - * static FPIX *fpixSampledDisparity() - * static FPIX *fpixExtraHorizDisparity() - * - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This applies the disparity arrays to the specified image. - * (2) Specify gray color for pixels brought in from the outside: - * 0 is black, 255 is white. Use -1 to select pixels from the - * boundary of the source image. - * (3) If the models and ref models have not been validated, this - * will do so by calling dewarpaInsertRefModels(). - * (4) This works with both stripped and full resolution page models. - * If the full res disparity array(s) are missing, they are remade. - * (5) The caller must handle errors that are returned because there - * are no valid models or ref models for the page -- typically - * by using the input pixs. - * (6) If there is no model for %pageno, this will use the model for - * 'refpage' and put the result in the dew for %pageno. - * (7) This populates the full resolution disparity arrays if - * necessary. If x and/or y are positive, they are used, - * in conjunction with pixs, to determine the required - * slope-based extension of the full resolution disparity - * arrays in each direction. When (x,y) == (0,0), all - * extension is to the right and down. Nonzero values of (x,y) - * are useful for dewarping when pixs is deliberately undercropped. - * (8) Important: when applying disparity to a number of images, - * after calling this function and saving the resulting pixd, - * you should call dewarpMinimize(dew) on the dew for %pageno. - * This will remove pixs and pixd (or their clones) stored in dew, - * as well as the full resolution disparity arrays. Together, - * these hold approximately 16 bytes for each pixel in pixs. - *- */ -l_ok -dewarpaApplyDisparity(L_DEWARPA *dewa, - l_int32 pageno, - PIX *pixs, - l_int32 grayin, - l_int32 x, - l_int32 y, - PIX **ppixd, - const char *debugfile) -{ -L_DEWARP *dew1, *dew; -PIX *pixv, *pixh; - - PROCNAME("dewarpaApplyDisparity"); - - /* Initialize the output with the input, so we'll have that - * in case we can't apply the page model. */ - if (!ppixd) - return ERROR_INT("&pixd not defined", procName, 1); - *ppixd = pixClone(pixs); - if (grayin > 255) { - L_WARNING("invalid grayin = %d; clipping at 255\n", procName, grayin); - grayin = 255; - } - - /* Find the appropriate dew to use and fully populate its array(s) */ - if (dewarpaApplyInit(dewa, pageno, pixs, x, y, &dew, debugfile)) - return ERROR_INT("no model available", procName, 1); - - /* Correct for vertical disparity and save the result */ - if ((pixv = pixApplyVertDisparity(dew, pixs, grayin)) == NULL) { - dewarpMinimize(dew); - return ERROR_INT("pixv not made", procName, 1); - } - pixDestroy(ppixd); - *ppixd = pixv; - if (debugfile) { - pixDisplayWithTitle(pixv, 300, 0, "pixv", 1); - lept_rmdir("lept/dewapply"); /* remove previous images */ - lept_mkdir("lept/dewapply"); - pixWriteDebug("/tmp/lept/dewapply/001.png", pixs, IFF_PNG); - pixWriteDebug("/tmp/lept/dewapply/002.png", pixv, IFF_PNG); - } - - /* Optionally, correct for horizontal disparity */ - if (dewa->useboth && dew->hsuccess && !dew->skip_horiz) { - if (dew->hvalid == FALSE) { - L_INFO("invalid horiz model for page %d\n", procName, pageno); - } else { - if ((pixh = pixApplyHorizDisparity(dew, pixv, grayin)) != NULL) { - pixDestroy(ppixd); - *ppixd = pixh; - if (debugfile) { - pixDisplayWithTitle(pixh, 600, 0, "pixh", 1); - pixWriteDebug("/tmp/lept/dewapply/003.png", pixh, IFF_PNG); - } - } else { - L_ERROR("horiz disparity failed on page %d\n", - procName, pageno); - } - } - } - - if (debugfile) { - dew1 = dewarpaGetDewarp(dewa, pageno); - dewarpDebug(dew1, "lept/dewapply", 0); - convertFilesToPdf("/tmp/lept/dewapply", NULL, 250, 1.0, 0, 0, - "Dewarp Apply Disparity", debugfile); - lept_stderr("pdf file: %s\n", debugfile); - } - - /* Get rid of the large full res disparity arrays */ - dewarpMinimize(dew); - - return 0; -} - - -/*! - * \brief dewarpaApplyInit() - * - * \param[in] dewa - * \param[in] pageno of page model to be used; may be a ref model - * \param[in] pixs image to be modified; can be 1, 8 or 32 bpp - * \param[in] x, y origin for generation of disparity arrays - * \param[out] pdew dewarp to be used for this page - * \param[in] debugfile use NULL to skip writing this - * \return 0 if OK, 1 on error no models or ref models available - * - *
- * Notes: - * (1) This prepares pixs for being dewarped. It returns 1 if - * no dewarping model exists. - * (2) The returned %dew contains the model to be used for this page - * image. The %dew is owned by dewa; do not destroy. - * (3) If both the 'useboth' and 'check_columns' fields are true, - * this checks for multiple text columns and if found, sets - * the 'skip_horiz' field in the %dew for this page. - *- */ -static l_int32 -dewarpaApplyInit(L_DEWARPA *dewa, - l_int32 pageno, - PIX *pixs, - l_int32 x, - l_int32 y, - L_DEWARP **pdew, - const char *debugfile) -{ -l_int32 ncols, debug; -L_DEWARP *dew1, *dew2; -PIX *pix1; - - PROCNAME("dewarpaApplyInit"); - - if (!pdew) - return ERROR_INT("&dew not defined", procName, 1); - *pdew = NULL; - - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - if (pageno < 0 || pageno > dewa->maxpage) - return ERROR_INT("invalid pageno", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (x < 0) x = 0; - if (y < 0) y = 0; - debug = (debugfile) ? 1 : 0; - - /* Make sure all models are valid and all refmodels have - * been added to dewa */ - if (dewa->modelsready == FALSE) - dewarpaInsertRefModels(dewa, 0, debug); - - /* Check for the existence of a valid model; we don't expect - * all pages to have them. */ - if ((dew1 = dewarpaGetDewarp(dewa, pageno)) == NULL) { - L_INFO("no valid dew model for page %d\n", procName, pageno); - return 1; - } - - /* Get the page model that we will use and sanity-check that - * it is valid. The ultimate result will be put in dew1->pixd. */ - if (dew1->hasref) /* point to another page with a model */ - dew2 = dewarpaGetDewarp(dewa, dew1->refpage); - else - dew2 = dew1; - if (dew2->vvalid == FALSE) - return ERROR_INT("no model; shouldn't happen", procName, 1); - *pdew = dew2; - - /* If check_columns is TRUE and useboth is TRUE, check for - * multiple columns. If there is more than one column, we - * only apply vertical disparity. */ - if (dewa->useboth && dewa->check_columns) { - pix1 = pixConvertTo1(pixs, 140); - pixCountTextColumns(pix1, 0.3, 0.5, 0.1, &ncols, NULL); - pixDestroy(&pix1); - if (ncols > 1) { - L_INFO("found %d columns; not correcting horiz disparity\n", - procName, ncols); - dew2->skip_horiz = TRUE; - } else { - dew2->skip_horiz = FALSE; - } - } - - /* Generate the full res disparity arrays if they don't exist - * (e.g., if they've been minimized or read from file), or if - * they are too small for the current image. */ - dewarpPopulateFullRes(dew2, pixs, x, y); - return 0; -} - - -/*! - * \brief pixApplyVertDisparity() - * - * \param[in] dew - * \param[in] pixs 1, 8 or 32 bpp - * \param[in] grayin gray value, from 0 to 255, for pixels brought in; - * use -1 to use pixels on the boundary of pixs - * \return pixd modified to remove vertical disparity, or NULL on error - * - *
- * Notes: - * (1) This applies the vertical disparity array to the specified - * image. For src pixels above the image, we use the pixels - * in the first raster line. - * (2) Specify gray color for pixels brought in from the outside: - * 0 is black, 255 is white. Use -1 to select pixels from the - * boundary of the source image. - *- */ -static PIX * -pixApplyVertDisparity(L_DEWARP *dew, - PIX *pixs, - l_int32 grayin) -{ -l_int32 i, j, w, h, d, fw, fh, wpld, wplf, isrc, val8; -l_uint32 *datad, *lined; -l_float32 *dataf, *linef; -void **lineptrs; -FPIX *fpix; -PIX *pixd; - - PROCNAME("pixApplyVertDisparity"); - - if (!dew) - return (PIX *)ERROR_PTR("dew not defined", procName, NULL); - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 1 && d != 8 && d != 32) - return (PIX *)ERROR_PTR("pix not 1, 8 or 32 bpp", procName, NULL); - if ((fpix = dew->fullvdispar) == NULL) - return (PIX *)ERROR_PTR("fullvdispar not defined", procName, NULL); - fpixGetDimensions(fpix, &fw, &fh); - if (fw < w || fh < h) { - lept_stderr("fw = %d, w = %d, fh = %d, h = %d\n", fw, w, fh, h); - return (PIX *)ERROR_PTR("invalid fpix size", procName, NULL); - } - - /* Two choices for requested pixels outside pixs: (1) use pixels' - * from the boundary of pixs; use white or light gray pixels. */ - pixd = pixCreateTemplate(pixs); - if (grayin >= 0) - pixSetAllGray(pixd, grayin); - datad = pixGetData(pixd); - dataf = fpixGetData(fpix); - wpld = pixGetWpl(pixd); - wplf = fpixGetWpl(fpix); - if (d == 1) { - lineptrs = pixGetLinePtrs(pixs, NULL); - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - linef = dataf + i * wplf; - for (j = 0; j < w; j++) { - isrc = (l_int32)(i - linef[j] + 0.5); - if (grayin < 0) /* use value at boundary if outside */ - isrc = L_MIN(L_MAX(isrc, 0), h - 1); - if (isrc >= 0 && isrc < h) { /* remains gray if outside */ - if (GET_DATA_BIT(lineptrs[isrc], j)) - SET_DATA_BIT(lined, j); - } - } - } - } else if (d == 8) { - lineptrs = pixGetLinePtrs(pixs, NULL); - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - linef = dataf + i * wplf; - for (j = 0; j < w; j++) { - isrc = (l_int32)(i - linef[j] + 0.5); - if (grayin < 0) - isrc = L_MIN(L_MAX(isrc, 0), h - 1); - if (isrc >= 0 && isrc < h) { - val8 = GET_DATA_BYTE(lineptrs[isrc], j); - SET_DATA_BYTE(lined, j, val8); - } - } - } - } else { /* d == 32 */ - lineptrs = pixGetLinePtrs(pixs, NULL); - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - linef = dataf + i * wplf; - for (j = 0; j < w; j++) { - isrc = (l_int32)(i - linef[j] + 0.5); - if (grayin < 0) - isrc = L_MIN(L_MAX(isrc, 0), h - 1); - if (isrc >= 0 && isrc < h) - lined[j] = GET_DATA_FOUR_BYTES(lineptrs[isrc], j); - } - } - } - - LEPT_FREE(lineptrs); - return pixd; -} - - -/*! - * \brief pixApplyHorizDisparity() - * - * \param[in] dew - * \param[in] pixs 1, 8 or 32 bpp - * \param[in] grayin gray value, from 0 to 255, for pixels brought in; - * use -1 to use pixels on the boundary of pixs - * \return pixd modified to remove horizontal disparity if possible, - * or NULL on error. - * - *
- * Notes: - * (1) This applies the horizontal disparity array to the specified - * image. - * (2) Specify gray color for pixels brought in from the outside: - * 0 is black, 255 is white. Use -1 to select pixels from the - * boundary of the source image. - * (3) The input pixs has already been corrected for vertical disparity. - * If the horizontal disparity array doesn't exist, this returns - * a clone of %pixs. - *- */ -static PIX * -pixApplyHorizDisparity(L_DEWARP *dew, - PIX *pixs, - l_int32 grayin) -{ -l_int32 i, j, w, h, d, fw, fh, wpls, wpld, wplf, jsrc, val8; -l_uint32 *datas, *lines, *datad, *lined; -l_float32 *dataf, *linef; -FPIX *fpix; -PIX *pixd; - - PROCNAME("pixApplyHorizDisparity"); - - if (!dew) - return (PIX *)ERROR_PTR("dew not defined", procName, pixs); - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 1 && d != 8 && d != 32) - return (PIX *)ERROR_PTR("pix not 1, 8 or 32 bpp", procName, NULL); - if ((fpix = dew->fullhdispar) == NULL) - return (PIX *)ERROR_PTR("fullhdispar not defined", procName, NULL); - fpixGetDimensions(fpix, &fw, &fh); - if (fw < w || fh < h) { - lept_stderr("fw = %d, w = %d, fh = %d, h = %d\n", fw, w, fh, h); - return (PIX *)ERROR_PTR("invalid fpix size", procName, NULL); - } - - /* Two choices for requested pixels outside pixs: (1) use pixels' - * from the boundary of pixs; use white or light gray pixels. */ - pixd = pixCreateTemplate(pixs); - if (grayin >= 0) - pixSetAllGray(pixd, grayin); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - dataf = fpixGetData(fpix); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - wplf = fpixGetWpl(fpix); - if (d == 1) { - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - linef = dataf + i * wplf; - for (j = 0; j < w; j++) { - jsrc = (l_int32)(j - linef[j] + 0.5); - if (grayin < 0) /* use value at boundary if outside */ - jsrc = L_MIN(L_MAX(jsrc, 0), w - 1); - if (jsrc >= 0 && jsrc < w) { /* remains gray if outside */ - if (GET_DATA_BIT(lines, jsrc)) - SET_DATA_BIT(lined, j); - } - } - } - } else if (d == 8) { - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - linef = dataf + i * wplf; - for (j = 0; j < w; j++) { - jsrc = (l_int32)(j - linef[j] + 0.5); - if (grayin < 0) - jsrc = L_MIN(L_MAX(jsrc, 0), w - 1); - if (jsrc >= 0 && jsrc < w) { - val8 = GET_DATA_BYTE(lines, jsrc); - SET_DATA_BYTE(lined, j, val8); - } - } - } - } else { /* d == 32 */ - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - linef = dataf + i * wplf; - for (j = 0; j < w; j++) { - jsrc = (l_int32)(j - linef[j] + 0.5); - if (grayin < 0) - jsrc = L_MIN(L_MAX(jsrc, 0), w - 1); - if (jsrc >= 0 && jsrc < w) - lined[j] = lines[jsrc]; - } - } - } - - return pixd; -} - - -/*----------------------------------------------------------------------* - * Apply warping disparity array to boxa * - *----------------------------------------------------------------------*/ -/*! - * \brief dewarpaApplyDisparityBoxa() - * - * \param[in] dewa - * \param[in] pageno of page model to be used; may be a ref model - * \param[in] pixs initial pix reference; for alignment and debugging - * \param[in] boxas boxa to be mapped - * \param[in] mapdir 1 if mapping forward from original to dewarped; - * 0 if backward - * \param[in] x, y origin for generation of disparity arrays with - * respect to the source region - * \param[out] pboxad disparity corrected boxa - * \param[in] debugfile use NULL to skip writing this - * \return 0 if OK, 1 on error no models or ref models available - * - *
- * Notes: - * (1) This applies the disparity arrays in one of two mapping directions - * to the specified boxa. It can be used in the backward direction - * to locate a box in the original coordinates that would have - * been dewarped to to the specified image. - * (2) If there is no model for %pageno, this will use the model for - * 'refpage' and put the result in the dew for %pageno. - * (3) This works with both stripped and full resolution page models. - * If the full res disparity array(s) are missing, they are remade. - * (4) If an error occurs, a copy of the input boxa is returned. - *- */ -l_ok -dewarpaApplyDisparityBoxa(L_DEWARPA *dewa, - l_int32 pageno, - PIX *pixs, - BOXA *boxas, - l_int32 mapdir, - l_int32 x, - l_int32 y, - BOXA **pboxad, - const char *debugfile) -{ -l_int32 debug_out; -L_DEWARP *dew1, *dew; -BOXA *boxav, *boxah; -PIX *pixv, *pixh; - - PROCNAME("dewarpaApplyDisparityBoxa"); - - /* Initialize the output with the input, so we'll have that - * in case we can't apply the page model. */ - if (!pboxad) - return ERROR_INT("&boxad not defined", procName, 1); - *pboxad = boxaCopy(boxas, L_CLONE); - - /* Find the appropriate dew to use and fully populate its array(s) */ - if (dewarpaApplyInit(dewa, pageno, pixs, x, y, &dew, debugfile)) - return ERROR_INT("no model available", procName, 1); - - /* Correct for vertical disparity and save the result */ - if ((boxav = boxaApplyDisparity(dew, boxas, L_VERT, mapdir)) == NULL) { - dewarpMinimize(dew); - return ERROR_INT("boxa1 not made", procName, 1); - } - boxaDestroy(pboxad); - *pboxad = boxav; - pixv = NULL; - pixh = NULL; - if (debugfile && mapdir != 1) - L_INFO("Reverse map direction; no debug output\n", procName); - debug_out = debugfile && (mapdir == 1); - if (debug_out) { - PIX *pix1; - lept_rmdir("lept/dewboxa"); /* remove previous images */ - lept_mkdir("lept/dewboxa"); - pix1 = pixConvertTo32(pixs); - pixRenderBoxaArb(pix1, boxas, 2, 255, 0, 0); - pixWriteDebug("/tmp/lept/dewboxa/01.png", pix1, IFF_PNG); - pixDestroy(&pix1); - pixv = pixApplyVertDisparity(dew, pixs, 255); - pix1 = pixConvertTo32(pixv); - pixRenderBoxaArb(pix1, boxav, 2, 0, 255, 0); - pixWriteDebug("/tmp/lept/dewboxa/02.png", pix1, IFF_PNG); - pixDestroy(&pix1); - } - - /* Optionally, correct for horizontal disparity */ - if (dewa->useboth && dew->hsuccess && !dew->skip_horiz) { - if (dew->hvalid == FALSE) { - L_INFO("invalid horiz model for page %d\n", procName, pageno); - } else { - boxah = boxaApplyDisparity(dew, boxav, L_HORIZ, mapdir); - if (!boxah) { - L_ERROR("horiz disparity fails on page %d\n", procName, pageno); - } else { - boxaDestroy(pboxad); - *pboxad = boxah; - if (debug_out) { - PIX *pix1; - pixh = pixApplyHorizDisparity(dew, pixv, 255); - pix1 = pixConvertTo32(pixh); - pixRenderBoxaArb(pix1, boxah, 2, 0, 0, 255); - pixWriteDebug("/tmp/lept/dewboxa/03.png", pix1, IFF_PNG); - pixDestroy(&pixh); - pixDestroy(&pix1); - } - } - } - } - - if (debug_out) { - pixDestroy(&pixv); - dew1 = dewarpaGetDewarp(dewa, pageno); - dewarpDebug(dew1, "lept/dewapply", 0); - convertFilesToPdf("/tmp/lept/dewboxa", NULL, 135, 1.0, 0, 0, - "Dewarp Apply Disparity Boxa", debugfile); - lept_stderr("Dewarp Apply Disparity Boxa pdf file: %s\n", - debugfile); - } - - /* Get rid of the large full res disparity arrays */ - dewarpMinimize(dew); - - return 0; -} - - -/*! - * \brief boxaApplyDisparity() - * - * \param[in] dew - * \param[in] boxa - * \param[in] direction L_HORIZ or L_VERT - * \param[in] mapdir 1 if mapping forward from original to dewarped; - * 0 if backward - * \return boxad modified by the disparity, or NULL on error - */ -static BOXA * -boxaApplyDisparity(L_DEWARP *dew, - BOXA *boxa, - l_int32 direction, - l_int32 mapdir) -{ -l_int32 x, y, w, h, ib, ip, nbox, wpl; -l_float32 xn, yn; -l_float32 *data, *line; -BOX *boxs, *boxd; -BOXA *boxad; -FPIX *fpix; -PTA *ptas, *ptad; - - PROCNAME("boxaApplyDisparity"); - - if (!dew) - return (BOXA *)ERROR_PTR("dew not defined", procName, NULL); - if (!boxa) - return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL); - if (direction == L_VERT) - fpix = dew->fullvdispar; - else if (direction == L_HORIZ) - fpix = dew->fullhdispar; - else - return (BOXA *)ERROR_PTR("invalid direction", procName, NULL); - if (!fpix) - return (BOXA *)ERROR_PTR("full disparity not defined", procName, NULL); - fpixGetDimensions(fpix, &w, &h); - - /* Clip the output to the positive quadrant because all box - * coordinates must be non-negative. */ - data = fpixGetData(fpix); - wpl = fpixGetWpl(fpix); - nbox = boxaGetCount(boxa); - boxad = boxaCreate(nbox); - for (ib = 0; ib < nbox; ib++) { - boxs = boxaGetBox(boxa, ib, L_COPY); - ptas = boxConvertToPta(boxs, 4); - ptad = ptaCreate(4); - for (ip = 0; ip < 4; ip++) { - ptaGetIPt(ptas, ip, &x, &y); - line = data + y * wpl; - if (direction == L_VERT) { - if (mapdir == 0) - yn = y - line[x]; - else - yn = y + line[x]; - yn = L_MAX(0, yn); - ptaAddPt(ptad, x, yn); - } else { /* direction == L_HORIZ */ - if (mapdir == 0) - xn = x - line[x]; - else - xn = x + line[x]; - xn = L_MAX(0, xn); - ptaAddPt(ptad, xn, y); - } - } - boxd = ptaConvertToBox(ptad); - boxaAddBox(boxad, boxd, L_INSERT); - boxDestroy(&boxs); - ptaDestroy(&ptas); - ptaDestroy(&ptad); - } - - return boxad; -} - - -/*----------------------------------------------------------------------* - * Stripping out data and populating full res disparity * - *----------------------------------------------------------------------*/ -/*! - * \brief dewarpMinimize() - * - * \param[in] dew - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This removes all data that is not needed for serialization. - * It keeps the subsampled disparity array(s), so the full - * resolution arrays can be reconstructed. - *- */ -l_ok -dewarpMinimize(L_DEWARP *dew) -{ -L_DEWARP *dewt; - - PROCNAME("dewarpMinimize"); - - if (!dew) - return ERROR_INT("dew not defined", procName, 1); - - /* If dew is a ref, minimize the actual dewarp */ - if (dew->hasref) - dewt = dewarpaGetDewarp(dew->dewa, dew->refpage); - else - dewt = dew; - if (!dewt) - return ERROR_INT("dewt not found", procName, 1); - - pixDestroy(&dewt->pixs); - fpixDestroy(&dewt->fullvdispar); - fpixDestroy(&dewt->fullhdispar); - numaDestroy(&dewt->namidys); - numaDestroy(&dewt->nacurves); - return 0; -} - - -/*! - * \brief dewarpPopulateFullRes() - * - * \param[in] dew - * \param[in] pix [optional], to give size of actual image - * \param[in] x, y origin for generation of disparity arrays - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) If the full resolution vertical and horizontal disparity - * arrays do not exist, they are built from the subsampled ones. - * (2) If pixs is not given, the size of the arrays is determined - * by the original image from which the sampled version was - * generated. Any values of (x,y) are ignored. - * (3) If pixs is given, the full resolution disparity arrays must - * be large enough to accommodate it. - * (a) If the arrays do not exist, the value of (x,y) determines - * the origin of the full resolution arrays without extension, - * relative to pixs. Thus, (x,y) gives the amount of - * slope extension in (left, top). The (right, bottom) - * extension is then determined by the size of pixs and - * (x,y); the values should never be < 0. - * (b) If the arrays exist and pixs is too large, the existing - * full res arrays are destroyed and new ones are made, - * again using (x,y) to determine the extension in the - * four directions. - *- */ -l_ok -dewarpPopulateFullRes(L_DEWARP *dew, - PIX *pix, - l_int32 x, - l_int32 y) -{ -l_int32 width, height, fw, fh, deltaw, deltah, redfactor; -FPIX *fpixt1, *fpixt2; - - PROCNAME("dewarpPopulateFullRes"); - - if (!dew) - return ERROR_INT("dew not defined", procName, 1); - if (!dew->sampvdispar) - return ERROR_INT("no sampled vert disparity", procName, 1); - if (x < 0) x = 0; - if (y < 0) y = 0; - - /* Establish the target size for the full res arrays */ - if (pix) - pixGetDimensions(pix, &width, &height, NULL); - else { - width = dew->w; - height = dew->h; - } - - /* Destroy the existing arrays if they are too small */ - if (dew->fullvdispar) { - fpixGetDimensions(dew->fullvdispar, &fw, &fh); - if (width > fw || height > fw) - fpixDestroy(&dew->fullvdispar); - } - if (dew->fullhdispar) { - fpixGetDimensions(dew->fullhdispar, &fw, &fh); - if (width > fw || height > fw) - fpixDestroy(&dew->fullhdispar); - } - - /* Find the required width and height expansion deltas */ - deltaw = width - dew->sampling * (dew->nx - 1) + 2; - deltah = height - dew->sampling * (dew->ny - 1) + 2; - redfactor = dew->redfactor; - deltaw = redfactor * L_MAX(0, deltaw); - deltah = redfactor * L_MAX(0, deltah); - - /* Generate the full res vertical array if it doesn't exist, - * extending it as required to make it big enough. Use x,y - * to determine the amounts on each side. */ - if (!dew->fullvdispar) { - fpixt1 = fpixCopy(NULL, dew->sampvdispar); - if (redfactor == 2) - fpixAddMultConstant(fpixt1, 0.0, (l_float32)redfactor); - fpixt2 = fpixScaleByInteger(fpixt1, dew->sampling * redfactor); - fpixDestroy(&fpixt1); - if (deltah == 0 && deltaw == 0) { - dew->fullvdispar = fpixt2; - } - else { - dew->fullvdispar = fpixAddSlopeBorder(fpixt2, x, deltaw - x, - y, deltah - y); - fpixDestroy(&fpixt2); - } - } - - /* Similarly, generate the full res horizontal array if it - * doesn't exist. Do this even if useboth == 1, but - * not if required to skip running horizontal disparity. */ - if (!dew->fullhdispar && dew->samphdispar && !dew->skip_horiz) { - fpixt1 = fpixCopy(NULL, dew->samphdispar); - if (redfactor == 2) - fpixAddMultConstant(fpixt1, 0.0, (l_float32)redfactor); - fpixt2 = fpixScaleByInteger(fpixt1, dew->sampling * redfactor); - fpixDestroy(&fpixt1); - if (deltah == 0 && deltaw == 0) { - dew->fullhdispar = fpixt2; - } - else { - dew->fullhdispar = fpixAddSlopeBorder(fpixt2, x, deltaw - x, - y, deltah - y); - fpixDestroy(&fpixt2); - } - } - - return 0; -} - - -#if 0 -/*----------------------------------------------------------------------* - * Static functions not presently in use * - *----------------------------------------------------------------------*/ -/*! - * \brief fpixSampledDisparity() - * - * \param[in] fpixs full resolution disparity model - * \param[in] sampling sampling factor - * \return fpixd sampled disparity model, or NULL on error - * - *
- * Notes: - * (1) This converts full to sampled disparity. - * (2) The input array is sampled at the right and top edges, and - * at every %sampling pixels horizontally and vertically. - * (3) The sampled array may not extend to the right and bottom - * pixels in fpixs. This will occur if fpixs was generated - * with slope extension because the image on that page was - * larger than normal. This is fine, because in use the - * sampled array will be interpolated back to full resolution - * and then extended as required. So the operations of - * sampling and interpolation will be idempotent. - * (4) There must be at least 3 sampled points horizontally and - * vertically. - *- */ -static FPIX * -fpixSampledDisparity(FPIX *fpixs, - l_int32 sampling) -{ -l_int32 w, h, wd, hd, i, j, is, js; -l_float32 val; -FPIX *fpixd; - - PROCNAME("fpixSampledDisparity"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - if (sampling < 1) - return (FPIX *)ERROR_PTR("sampling < 1", procName, NULL); - - fpixGetDimensions(fpixs, &w, &h); - wd = 1 + (w + sampling - 2) / sampling; - hd = 1 + (h + sampling - 2) / sampling; - if (wd < 3 || hd < 3) - return (FPIX *)ERROR_PTR("wd < 3 or hd < 3", procName, NULL); - fpixd = fpixCreate(wd, hd); - for (i = 0; i < hd; i++) { - is = sampling * i; - if (is >= h) continue; - for (j = 0; j < wd; j++) { - js = sampling * j; - if (js >= w) continue; - fpixGetPixel(fpixs, js, is, &val); - fpixSetPixel(fpixd, j, i, val); - } - } - - return fpixd; -} - -static const l_float32 DefaultSlopeFactor = 0.1; /* just a guess; fix it */ - -/*! - * \brief fpixExtraHorizDisparity() - * - * \param[in] fpixv vertical disparity model - * \param[in] factor conversion factor for vertical disparity slope; - * use 0 for default - * \param[out] pxwid extra width to be added to dewarped pix - * \return fpixh, or NULL on error - * - *
- * Notes: - * (1) This takes the difference in vertical disparity at top - * and bottom of the image, and converts it to an assumed - * horizontal disparity. In use, we add this to the - * horizontal disparity determined by the left and right - * ends of textlines. - * (2) Usage: - * l_int32 xwid = [extra width to be added to fpix and image] - * FPix *fpix = fpixExtraHorizDisparity(dew->fullvdispar, 0, &xwid); - * fpixLinearCombination(dew->fullhdispar, dew->fullhdispar, - * fpix, 1.0, 1.0); - *- */ -static FPIX * -fpixExtraHorizDisparity(FPIX *fpixv, - l_float32 factor, - l_int32 *pxwid) -{ -l_int32 w, h, i, j, fw, wpl, maxloc; -l_float32 val1, val2, vdisp, vdisp0, maxval; -l_float32 *data, *line, *fadiff; -NUMA *nadiff; -FPIX *fpixh; - - PROCNAME("fpixExtraHorizDisparity"); - - if (!fpixv) - return (FPIX *)ERROR_PTR("fpixv not defined", procName, NULL); - if (!pxwid) - return (FPIX *)ERROR_PTR("&xwid not defined", procName, NULL); - if (factor == 0.0) - factor = DefaultSlopeFactor; - - /* Estimate horizontal disparity from the vertical disparity - * difference between the top and bottom, normalized to the - * image height. Add the maximum value to the width of the - * output image, so that all src pixels can be mapped - * into the dest. */ - fpixGetDimensions(fpixv, &w, &h); - nadiff = numaCreate(w); - for (j = 0; j < w; j++) { - fpixGetPixel(fpixv, j, 0, &val1); - fpixGetPixel(fpixv, j, h - 1, &val2); - vdisp = factor * (val2 - val1) / (l_float32)h; - if (j == 0) vdisp0 = vdisp; - vdisp = vdisp0 - vdisp; - numaAddNumber(nadiff, vdisp); - } - numaGetMax(nadiff, &maxval, &maxloc); - *pxwid = (l_int32)(maxval + 0.5); - - fw = w + *pxwid; - fpixh = fpixCreate(fw, h); - data = fpixGetData(fpixh); - wpl = fpixGetWpl(fpixh); - fadiff = numaGetFArray(nadiff, L_NOCOPY); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < fw; j++) { - if (j < maxloc) /* this may not work for even pages */ - line[j] = fadiff[j]; - else /* keep it at the max value the rest of the way across */ - line[j] = maxval; - } - } - - numaDestroy(&nadiff); - return fpixh; -} -#endif diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dewarp4.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dewarp4.c deleted file mode 100644 index 728c7415..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dewarp4.c +++ /dev/null @@ -1,1175 +0,0 @@ -/*====================================================================* - - 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 dewarp4.c - *
- * - * Single page dewarper - * - * Reference model (book-level, dewarpa) operations and debugging output - * - * Top-level single page dewarper - * l_int32 dewarpSinglePage() - * l_int32 dewarpSinglePageInit() - * l_int32 dewarpSinglePageRun() - * - * Operations on dewarpa - * l_int32 dewarpaListPages() - * l_int32 dewarpaSetValidModels() - * l_int32 dewarpaInsertRefModels() - * l_int32 dewarpaStripRefModels() - * l_int32 dewarpaRestoreModels() - * - * Dewarp debugging output - * l_int32 dewarpaInfo() - * l_int32 dewarpaModelStats() - * static l_int32 dewarpaTestForValidModel() - * l_int32 dewarpaShowArrays() - * l_int32 dewarpDebug() - * l_int32 dewarpShowResults() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) Dewarps pixs and returns the result in &pixd. - * (2) This uses default values for all model parameters. - * (3) If pixs is 1 bpp, the parameters %adaptive and %thresh are ignored. - * (4) If it can't build a model, returns a copy of pixs in &pixd. - *- */ -l_ok -dewarpSinglePage(PIX *pixs, - l_int32 thresh, - l_int32 adaptive, - l_int32 useboth, - l_int32 check_columns, - PIX **ppixd, - L_DEWARPA **pdewa, - l_int32 debug) -{ -L_DEWARPA *dewa; -PIX *pixb; - - PROCNAME("dewarpSinglePage"); - - if (!ppixd) - return ERROR_INT("&pixd not defined", procName, 1); - *ppixd = NULL; - if (pdewa) *pdewa = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - - dewarpSinglePageInit(pixs, thresh, adaptive, useboth, - check_columns, &pixb, &dewa); - if (!pixb) { - dewarpaDestroy(&dewa); - return ERROR_INT("pixb not made", procName, 1); - } - - dewarpSinglePageRun(pixs, pixb, dewa, ppixd, debug); - - if (pdewa) - *pdewa = dewa; - else - dewarpaDestroy(&dewa); - pixDestroy(&pixb); - return 0; -} - - -/*! - * \brief dewarpSinglePageInit() - * - * \param[in] pixs with text, any depth - * \param[in] thresh for global thresh to 1 bpp; ignore otherwise - * \param[in] adaptive 1 for adaptive thresh; 0 for global threshold - * \param[in] useboth 1 for both horiz and vert; 0 for vertical only - * \param[in] check_columns 1 to skip horizontal if multiple columns; - * 0 otherwise; default is to skip - * \param[out] ppixb 1 bpp debug image - * \param[out] pdewa initialized dewa - * \return 0 if OK, 1 on error list of page numbers, or NULL on error - * - *
- * Notes: - * (1) This binarizes the input pixs if necessary, returning the - * binarized image. It also initializes the dewa to default values - * for the model parameters. - * (2) If pixs is 1 bpp, the parameters %adaptive and %thresh are ignored. - * (3) To change the model parameters, call dewarpaSetCurvatures() - * before running dewarpSinglePageRun(). For example: - * dewarpSinglePageInit(pixs, 0, 1, 1, 1, &pixb, &dewa); - * dewarpaSetCurvatures(dewa, 250, -1, -1, 80, 70, 150); - * dewarpSinglePageRun(pixs, pixb, dewa, &pixd, 0); - * dewarpaDestroy(&dewa); - * pixDestroy(&pixb); - *- */ -l_ok -dewarpSinglePageInit(PIX *pixs, - l_int32 thresh, - l_int32 adaptive, - l_int32 useboth, - l_int32 check_columns, - PIX **ppixb, - L_DEWARPA **pdewa) -{ -PIX *pix1; - - PROCNAME("dewarpSinglePageInit"); - - if (ppixb) *ppixb = NULL; - if (pdewa) *pdewa = NULL; - if (!ppixb || !pdewa) - return ERROR_INT("&pixb and &dewa not both defined", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - - *pdewa = dewarpaCreate(1, 0, 1, 0, -1); - dewarpaUseBothArrays(*pdewa, useboth); - dewarpaSetCheckColumns(*pdewa, check_columns); - - /* Generate a binary image, if necessary */ - if (pixGetDepth(pixs) > 1) { - pix1 = pixConvertTo8(pixs, 0); - if (adaptive) - *ppixb = pixAdaptThresholdToBinary(pix1, NULL, 1.0); - else - *ppixb = pixThresholdToBinary(pix1, thresh); - pixDestroy(&pix1); - } else { - *ppixb = pixClone(pixs); - } - return 0; -} - - -/*! - * \brief dewarpSinglePageRun() - * - * \param[in] pixs any depth - * \param[in] pixb 1 bpp - * \param[in] dewa initialized - * \param[out] ppixd dewarped result - * \param[in] debug 1 for debugging output, 0 otherwise - * \return 0 if OK, 1 on error list of page numbers, or NULL on error - * - *
- * Notes: - * (1) Dewarps pixs and returns the result in &pixd. - * (2) The 1 bpp version %pixb and %dewa are conveniently generated by - * dewarpSinglePageInit(). - * (3) Non-default model parameters must be set before calling this. - * (4) If a model cannot be built, this returns a copy of pixs in &pixd. - *- */ -l_ok -dewarpSinglePageRun(PIX *pixs, - PIX *pixb, - L_DEWARPA *dewa, - PIX **ppixd, - l_int32 debug) -{ -const char *debugfile; -l_int32 vsuccess, ret; -L_DEWARP *dew; - - PROCNAME("dewarpSinglePageRun"); - - if (!ppixd) - return ERROR_INT("&pixd not defined", procName, 1); - *ppixd = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!pixb) - return ERROR_INT("pixb not defined", procName, 1); - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - - if (debug) - lept_mkdir("lept/dewarp"); - - /* Generate the page model */ - dew = dewarpCreate(pixb, 0); - dewarpaInsertDewarp(dewa, dew); - debugfile = (debug) ? "/tmp/lept/dewarp/singlepage_model.pdf" : NULL; - dewarpBuildPageModel(dew, debugfile); - dewarpaModelStatus(dewa, 0, &vsuccess, NULL); - if (vsuccess == 0) { - L_ERROR("failure to build model for vertical disparity\n", procName); - *ppixd = pixCopy(NULL, pixs); - return 0; - } - - /* Apply the page model */ - debugfile = (debug) ? "/tmp/lept/dewarp/singlepage_apply.pdf" : NULL; - ret = dewarpaApplyDisparity(dewa, 0, pixs, 255, 0, 0, ppixd, debugfile); - if (ret) - L_ERROR("invalid model; failure to apply disparity\n", procName); - return 0; -} - - -/*----------------------------------------------------------------------* - * Operations on dewarpa * - *----------------------------------------------------------------------*/ -/*! - * \brief dewarpaListPages() - * - * \param[in] dewa populated with dewarp structs for pages - * \return 0 if OK, 1 on error list of page numbers, or NULL on error - * - *
- * Notes: - * (1) This generates two numas, stored in the dewarpa, that give: - * (a) the page number for each dew that has a page model. - * (b) the page number for each dew that has either a page - * model or a reference model. - * It can be called at any time. - * (2) It is called by the dewarpa serializer before writing. - *- */ -l_ok -dewarpaListPages(L_DEWARPA *dewa) -{ -l_int32 i; -L_DEWARP *dew; -NUMA *namodels, *napages; - - PROCNAME("dewarpaListPages"); - - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - - numaDestroy(&dewa->namodels); - numaDestroy(&dewa->napages); - namodels = numaCreate(dewa->maxpage + 1); - napages = numaCreate(dewa->maxpage + 1); - dewa->namodels = namodels; - dewa->napages = napages; - for (i = 0; i <= dewa->maxpage; i++) { - if ((dew = dewarpaGetDewarp(dewa, i)) != NULL) { - if (dew->hasref == 0) - numaAddNumber(namodels, dew->pageno); - numaAddNumber(napages, dew->pageno); - } - } - return 0; -} - - -/*! - * \brief dewarpaSetValidModels() - * - * \param[in] dewa - * \param[in] notests - * \param[in] debug 1 to output information on invalid page models - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) A valid model must meet the rendering requirements, which - * include whether or not a vertical disparity model exists - * and conditions on curvatures for vertical and horizontal - * disparity models. - * (2) If %notests == 1, this ignores the curvature constraints - * and assumes that all successfully built models are valid. - * (3) This function does not need to be called by the application. - * It is called by dewarpaInsertRefModels(), which - * will destroy all invalid dewarps. Consequently, to inspect - * an invalid dewarp model, it must be done before calling - * dewarpaInsertRefModels(). - *- */ -l_ok -dewarpaSetValidModels(L_DEWARPA *dewa, - l_int32 notests, - l_int32 debug) -{ -l_int32 i, n, maxcurv, diffcurv, diffedge; -L_DEWARP *dew; - - PROCNAME("dewarpaSetValidModels"); - - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - - n = dewa->maxpage + 1; - for (i = 0; i < n; i++) { - if ((dew = dewarpaGetDewarp(dewa, i)) == NULL) - continue; - - if (debug) { - if (dew->hasref == 1) { - L_INFO("page %d: has only a ref model\n", procName, i); - } else if (dew->vsuccess == 0) { - L_INFO("page %d: no model successfully built\n", - procName, i); - } else if (!notests) { - maxcurv = L_MAX(L_ABS(dew->mincurv), L_ABS(dew->maxcurv)); - diffcurv = dew->maxcurv - dew->mincurv; - if (dewa->useboth && !dew->hsuccess) - L_INFO("page %d: useboth, but no horiz disparity\n", - procName, i); - if (maxcurv > dewa->max_linecurv) - L_INFO("page %d: max curvature %d > max_linecurv\n", - procName, i, diffcurv); - if (diffcurv < dewa->min_diff_linecurv) - L_INFO("page %d: diff curv %d < min_diff_linecurv\n", - procName, i, diffcurv); - if (diffcurv > dewa->max_diff_linecurv) - L_INFO("page %d: abs diff curv %d > max_diff_linecurv\n", - procName, i, diffcurv); - if (dew->hsuccess) { - if (L_ABS(dew->leftslope) > dewa->max_edgeslope) - L_INFO("page %d: abs left slope %d > max_edgeslope\n", - procName, i, dew->leftslope); - if (L_ABS(dew->rightslope) > dewa->max_edgeslope) - L_INFO("page %d: abs right slope %d > max_edgeslope\n", - procName, i, dew->rightslope); - diffedge = L_ABS(dew->leftcurv - dew->rightcurv); - if (L_ABS(dew->leftcurv) > dewa->max_edgecurv) - L_INFO("page %d: left curvature %d > max_edgecurv\n", - procName, i, dew->leftcurv); - if (L_ABS(dew->rightcurv) > dewa->max_edgecurv) - L_INFO("page %d: right curvature %d > max_edgecurv\n", - procName, i, dew->rightcurv); - if (diffedge > dewa->max_diff_edgecurv) - L_INFO("page %d: abs diff left-right curv %d > " - "max_diff_edgecurv\n", procName, i, diffedge); - } - } - } - - dewarpaTestForValidModel(dewa, dew, notests); - } - - return 0; -} - - -/*! - * \brief dewarpaInsertRefModels() - * - * \param[in] dewa - * \param[in] notests if 1, ignore curvature constraints on model - * \param[in] debug 1 to output information on invalid page models - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This destroys all dewarp models that are invalid, and then - * inserts reference models where possible. - * (2) If %notests == 1, this ignores the curvature constraints - * and assumes that all successfully built models are valid. - * (3) If useboth == 0, it uses the closest valid model within the - * distance and parity constraints. If useboth == 1, it tries - * to use the closest allowed hvalid model; if it doesn't find - * an hvalid model, it uses the closest valid model. - * (4) For all pages without a model, this clears out any existing - * invalid and reference dewarps, finds the nearest valid model - * with the same parity, and inserts an empty dewarp with the - * reference page. - * (5) Then if it is requested to use both vertical and horizontal - * disparity arrays (useboth == 1), it tries to replace any - * hvalid == 0 model or reference with an hvalid == 1 reference. - * (6) The distance constraint is that any reference model must - * be within maxdist. Note that with the parity constraint, - * no reference models will be used if maxdist < 2. - * (7) This function must be called, even if reference models will - * not be used. It should be called after building models on all - * available pages, and after setting the rendering parameters. - * (8) If the dewa has been serialized, this function is called by - * dewarpaRead() when it is read back. It is also called - * any time the rendering parameters are changed. - * (9) Note: if this has been called with useboth == 1, and useboth - * is reset to 0, you should first call dewarpaRestoreModels() - * to bring real models from the cache back to the primary array. - *- */ -l_ok -dewarpaInsertRefModels(L_DEWARPA *dewa, - l_int32 notests, - l_int32 debug) -{ -l_int32 i, j, n, val, min, distdown, distup; -L_DEWARP *dew; -NUMA *na, *nah; - - PROCNAME("dewarpaInsertRefModels"); - - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - if (dewa->maxdist < 2) - L_INFO("maxdist < 2; no ref models can be used\n", procName); - - /* Make an indicator numa for pages with valid models. */ - dewarpaSetValidModels(dewa, notests, debug); - n = dewa->maxpage + 1; - na = numaMakeConstant(0, n); - for (i = 0; i < n; i++) { - dew = dewarpaGetDewarp(dewa, i); - if (dew && dew->vvalid) - numaReplaceNumber(na, i, 1); - } - - /* Remove all existing ref models and restore models from cache */ - dewarpaRestoreModels(dewa); - - /* Move invalid models to the cache, and insert reference dewarps - * for pages that need to borrow a model. - * First, try to find a valid model for each page. */ - for (i = 0; i < n; i++) { - numaGetIValue(na, i, &val); - if (val == 1) continue; /* already has a valid model */ - if ((dew = dewa->dewarp[i]) != NULL) { /* exists but is not valid; */ - dewa->dewarpcache[i] = dew; /* move it to the cache */ - dewa->dewarp[i] = NULL; - } - if (dewa->maxdist < 2) continue; /* can't use a ref model */ - /* Look back for nearest model */ - distdown = distup = dewa->maxdist + 1; - for (j = i - 2; j >= 0 && distdown > dewa->maxdist; j -= 2) { - numaGetIValue(na, j, &val); - if (val == 1) distdown = i - j; - } - /* Look ahead for nearest model */ - for (j = i + 2; j < n && distup > dewa->maxdist; j += 2) { - numaGetIValue(na, j, &val); - if (val == 1) distup = j - i; - } - min = L_MIN(distdown, distup); - if (min > dewa->maxdist) continue; /* no valid model in range */ - if (distdown <= distup) - dewarpaInsertDewarp(dewa, dewarpCreateRef(i, i - distdown)); - else - dewarpaInsertDewarp(dewa, dewarpCreateRef(i, i + distup)); - } - numaDestroy(&na); - - /* If a valid model will do, we're finished. */ - if (dewa->useboth == 0) { - dewa->modelsready = 1; /* validated */ - return 0; - } - - /* The request is useboth == 1. Now try to find an hvalid model */ - nah = numaMakeConstant(0, n); - for (i = 0; i < n; i++) { - dew = dewarpaGetDewarp(dewa, i); - if (dew && dew->hvalid) - numaReplaceNumber(nah, i, 1); - } - for (i = 0; i < n; i++) { - numaGetIValue(nah, i, &val); - if (val == 1) continue; /* already has a hvalid model */ - if (dewa->maxdist < 2) continue; /* can't use a ref model */ - distdown = distup = 100000; - for (j = i - 2; j >= 0; j -= 2) { /* look back for nearest model */ - numaGetIValue(nah, j, &val); - if (val == 1) { - distdown = i - j; - break; - } - } - for (j = i + 2; j < n; j += 2) { /* look ahead for nearest model */ - numaGetIValue(nah, j, &val); - if (val == 1) { - distup = j - i; - break; - } - } - min = L_MIN(distdown, distup); - if (min > dewa->maxdist) continue; /* no hvalid model within range */ - - /* We can replace the existing valid model with an hvalid model. - * If it's not a reference, save it in the cache. */ - if ((dew = dewarpaGetDewarp(dewa, i)) == NULL) { - L_ERROR("dew is null for page %d!\n", procName, i); - } else { - if (dew->hasref == 0) { /* not a ref model */ - dewa->dewarpcache[i] = dew; /* move it to the cache */ - dewa->dewarp[i] = NULL; /* must null the ptr */ - } - } - if (distdown <= distup) /* insert the hvalid ref model */ - dewarpaInsertDewarp(dewa, dewarpCreateRef(i, i - distdown)); - else - dewarpaInsertDewarp(dewa, dewarpCreateRef(i, i + distup)); - } - numaDestroy(&nah); - - dewa->modelsready = 1; /* validated */ - return 0; -} - - -/*! - * \brief dewarpaStripRefModels() - * - * \param[in] dewa populated with dewarp structs for pages - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This examines each dew in a dewarpa, and removes - * all that don't have their own page model (i.e., all - * that have "references" to nearby pages with valid models). - * These references were generated by dewarpaInsertRefModels(dewa). - *- */ -l_ok -dewarpaStripRefModels(L_DEWARPA *dewa) -{ -l_int32 i; -L_DEWARP *dew; - - PROCNAME("dewarpaStripRefModels"); - - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - - for (i = 0; i <= dewa->maxpage; i++) { - if ((dew = dewarpaGetDewarp(dewa, i)) != NULL) { - if (dew->hasref) - dewarpDestroy(&dewa->dewarp[i]); - } - } - dewa->modelsready = 0; - - /* Regenerate the page lists */ - dewarpaListPages(dewa); - return 0; -} - - -/*! - * \brief dewarpaRestoreModels() - * - * \param[in] dewa populated with dewarp structs for pages - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This puts all real models (and only real models) in the - * primary dewarpa array. First remove all dewarps that are - * only references to other page models. Then move all models - * that had been cached back into the primary dewarp array. - * (2) After this is done, we still need to recompute and insert - * the reference models before dewa->modelsready is true. - *- */ -l_ok -dewarpaRestoreModels(L_DEWARPA *dewa) -{ -l_int32 i; -L_DEWARP *dew; - - PROCNAME("dewarpaRestoreModels"); - - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - - /* Strip out ref models. Then only real models will be in the - * primary dewarp array. */ - dewarpaStripRefModels(dewa); - - /* The cache holds only real models, which are not necessarily valid. */ - for (i = 0; i <= dewa->maxpage; i++) { - if ((dew = dewa->dewarpcache[i]) != NULL) { - if (dewa->dewarp[i]) { - L_ERROR("dew in both cache and main array!: page %d\n", - procName, i); - } else { - dewa->dewarp[i] = dew; - dewa->dewarpcache[i] = NULL; - } - } - } - dewa->modelsready = 0; /* new ref models not yet inserted */ - - /* Regenerate the page lists */ - dewarpaListPages(dewa); - return 0; -} - - -/*----------------------------------------------------------------------* - * Dewarp debugging output * - *----------------------------------------------------------------------*/ -/*! - * \brief dewarpaInfo() - * - * \param[in] fp - * \param[in] dewa - * \return 0 if OK, 1 on error - */ -l_ok -dewarpaInfo(FILE *fp, - L_DEWARPA *dewa) -{ -l_int32 i, n, pageno, nnone, nvsuccess, nvvalid, nhsuccess, nhvalid, nref; -L_DEWARP *dew; - - PROCNAME("dewarpaInfo"); - - if (!fp) - return ERROR_INT("dewa not defined", procName, 1); - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - - fprintf(fp, "\nDewarpaInfo: %p\n", dewa); - fprintf(fp, "nalloc = %d, maxpage = %d\n", dewa->nalloc, dewa->maxpage); - fprintf(fp, "sampling = %d, redfactor = %d, minlines = %d\n", - dewa->sampling, dewa->redfactor, dewa->minlines); - fprintf(fp, "maxdist = %d, useboth = %d\n", - dewa->maxdist, dewa->useboth); - - dewarpaModelStats(dewa, &nnone, &nvsuccess, &nvvalid, - &nhsuccess, &nhvalid, &nref); - n = numaGetCount(dewa->napages); - lept_stderr("Total number of pages with a dew = %d\n", n); - lept_stderr("Number of pages without any models = %d\n", nnone); - lept_stderr("Number of pages with a vert model = %d\n", nvsuccess); - lept_stderr("Number of pages with a valid vert model = %d\n", nvvalid); - lept_stderr("Number of pages with both models = %d\n", nhsuccess); - lept_stderr("Number of pages with both models valid = %d\n", nhvalid); - lept_stderr("Number of pages with a ref model = %d\n", nref); - - for (i = 0; i < n; i++) { - numaGetIValue(dewa->napages, i, &pageno); - if ((dew = dewarpaGetDewarp(dewa, pageno)) == NULL) - continue; - lept_stderr("Page: %d\n", dew->pageno); - lept_stderr(" hasref = %d, refpage = %d\n", - dew->hasref, dew->refpage); - lept_stderr(" nlines = %d\n", dew->nlines); - lept_stderr(" w = %d, h = %d, nx = %d, ny = %d\n", - dew->w, dew->h, dew->nx, dew->ny); - if (dew->sampvdispar) - lept_stderr(" Vertical disparity builds:\n" - " (min,max,abs-diff) line curvature = (%d,%d,%d)\n", - dew->mincurv, dew->maxcurv, dew->maxcurv - dew->mincurv); - if (dew->samphdispar) - lept_stderr(" Horizontal disparity builds:\n" - " left edge slope = %d, right edge slope = %d\n" - " (left,right,abs-diff) edge curvature = (%d,%d,%d)\n", - dew->leftslope, dew->rightslope, dew->leftcurv, - dew->rightcurv, L_ABS(dew->leftcurv - dew->rightcurv)); - } - return 0; -} - - -/*! - * \brief dewarpaModelStats() - * - * \param[in] dewa - * \param[out] pnnone [optional] number without any model - * \param[out] pnvsuccess [optional] number with a vert model - * \param[out] pnvvalid [optional] number with a valid vert model - * \param[out] pnhsuccess [optional] number with both models - * \param[out] pnhvalid [optional] number with both models valid - * \param[out] pnref [optional] number with a reference model - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) A page without a model has no dew. It most likely failed to - * generate a vertical model, and has not been assigned a ref - * model from a neighboring page with a valid vertical model. - * (2) A page has vsuccess == 1 if there is at least a model of the - * vertical disparity. The model may be invalid, in which case - * dewarpaInsertRefModels() will stash it in the cache and - * attempt to replace it by a valid ref model. - * (3) A vvvalid model is a vertical disparity model whose parameters - * satisfy the constraints given in dewarpaSetValidModels(). - * (4) A page has hsuccess == 1 if both the vertical and horizontal - * disparity arrays have been constructed. - * (5) An hvalid model has vertical and horizontal disparity - * models whose parameters satisfy the constraints given - * in dewarpaSetValidModels(). - * (6) A page has a ref model if it failed to generate a valid - * model but was assigned a vvalid or hvalid model on another - * page (within maxdist) by dewarpaInsertRefModel(). - * (7) This calls dewarpaTestForValidModel(); it ignores the vvalid - * and hvalid fields. - *- */ -l_ok -dewarpaModelStats(L_DEWARPA *dewa, - l_int32 *pnnone, - l_int32 *pnvsuccess, - l_int32 *pnvvalid, - l_int32 *pnhsuccess, - l_int32 *pnhvalid, - l_int32 *pnref) -{ -l_int32 i, n, pageno, nnone, nvsuccess, nvvalid, nhsuccess, nhvalid, nref; -L_DEWARP *dew; - - PROCNAME("dewarpaModelStats"); - - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - - dewarpaListPages(dewa); - n = numaGetCount(dewa->napages); - nnone = nref = nvsuccess = nvvalid = nhsuccess = nhvalid = 0; - for (i = 0; i < n; i++) { - numaGetIValue(dewa->napages, i, &pageno); - dew = dewarpaGetDewarp(dewa, pageno); - if (!dew) { - nnone++; - continue; - } - if (dew->hasref == 1) - nref++; - if (dew->vsuccess == 1) - nvsuccess++; - if (dew->hsuccess == 1) - nhsuccess++; - dewarpaTestForValidModel(dewa, dew, 0); - if (dew->vvalid == 1) - nvvalid++; - if (dew->hvalid == 1) - nhvalid++; - } - - if (pnnone) *pnnone = nnone; - if (pnref) *pnref = nref; - if (pnvsuccess) *pnvsuccess = nvsuccess; - if (pnvvalid) *pnvvalid = nvvalid; - if (pnhsuccess) *pnhsuccess = nhsuccess; - if (pnhvalid) *pnhvalid = nhvalid; - return 0; -} - - -/*! - * \brief dewarpaTestForValidModel() - * - * \param[in] dewa - * \param[in] dew - * \param[in] notests - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Computes validity of vertical (vvalid) model and both - * vertical and horizontal (hvalid) models. - * (2) If %notests == 1, this ignores the curvature constraints - * and assumes that all successfully built models are valid. - * (3) This is just about the models, not the rendering process, - * so the value of useboth is not considered here. - *- */ -static l_int32 -dewarpaTestForValidModel(L_DEWARPA *dewa, - L_DEWARP *dew, - l_int32 notests) -{ -l_int32 maxcurv, diffcurv, diffedge; - - PROCNAME("dewarpaTestForValidModel"); - - if (!dewa || !dew) - return ERROR_INT("dewa and dew not both defined", procName, 1); - - if (notests) { - dew->vvalid = dew->vsuccess; - dew->hvalid = dew->hsuccess; - return 0; - } - - /* No actual model was built */ - if (dew->vsuccess == 0) return 0; - - /* Was previously found not to have a valid model */ - if (dew->hasref == 1) return 0; - - /* vsuccess == 1; a vertical (line) model exists. - * First test that the vertical curvatures are within allowed - * bounds. Note that all curvatures are signed.*/ - maxcurv = L_MAX(L_ABS(dew->mincurv), L_ABS(dew->maxcurv)); - diffcurv = dew->maxcurv - dew->mincurv; - if (maxcurv <= dewa->max_linecurv && - diffcurv >= dewa->min_diff_linecurv && - diffcurv <= dewa->max_diff_linecurv) { - dew->vvalid = 1; - } else { - L_INFO("invalid vert model for page %d:\n", procName, dew->pageno); -#if DEBUG_INVALID_MODELS - lept_stderr(" max line curv = %d, max allowed = %d\n", - maxcurv, dewa->max_linecurv); - lept_stderr(" diff line curv = %d, max allowed = %d\n", - diffcurv, dewa->max_diff_linecurv); -#endif /* DEBUG_INVALID_MODELS */ - } - - /* If a horizontal (edge) model exists, test for validity. */ - if (dew->hsuccess) { - diffedge = L_ABS(dew->leftcurv - dew->rightcurv); - if (L_ABS(dew->leftslope) <= dewa->max_edgeslope && - L_ABS(dew->rightslope) <= dewa->max_edgeslope && - L_ABS(dew->leftcurv) <= dewa->max_edgecurv && - L_ABS(dew->rightcurv) <= dewa->max_edgecurv && - diffedge <= dewa->max_diff_edgecurv) { - dew->hvalid = 1; - } else { - L_INFO("invalid horiz model for page %d:\n", procName, dew->pageno); -#if DEBUG_INVALID_MODELS - lept_stderr(" left edge slope = %d, max allowed = %d\n", - dew->leftslope, dewa->max_edgeslope); - lept_stderr(" right edge slope = %d, max allowed = %d\n", - dew->rightslope, dewa->max_edgeslope); - lept_stderr(" left edge curv = %d, max allowed = %d\n", - dew->leftcurv, dewa->max_edgecurv); - lept_stderr(" right edge curv = %d, max allowed = %d\n", - dew->rightcurv, dewa->max_edgecurv); - lept_stderr(" diff edge curv = %d, max allowed = %d\n", - diffedge, dewa->max_diff_edgecurv); -#endif /* DEBUG_INVALID_MODELS */ - } - } - - return 0; -} - - -/*! - * \brief dewarpaShowArrays() - * - * \param[in] dewa - * \param[in] scalefact on contour images; typ. 0.5 - * \param[in] first first page model to render - * \param[in] last last page model to render; use 0 to go to end - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Generates a pdf of contour plots of the disparity arrays. - * (2) This only shows actual models; not ref models - *- */ -l_ok -dewarpaShowArrays(L_DEWARPA *dewa, - l_float32 scalefact, - l_int32 first, - l_int32 last) -{ -char buf[256]; -l_int32 i, svd, shd; -L_BMF *bmf; -L_DEWARP *dew; -PIX *pixv, *pixvs, *pixh, *pixhs, *pixt, *pixd; -PIXA *pixa; - - PROCNAME("dewarpaShowArrays"); - - if (!dewa) - return ERROR_INT("dew not defined", procName, 1); - if (first < 0 || first > dewa->maxpage) - return ERROR_INT("first out of bounds", procName, 1); - if (last <= 0 || last > dewa->maxpage) last = dewa->maxpage; - if (last < first) - return ERROR_INT("last < first", procName, 1); - - lept_rmdir("lept/dewarp1"); /* temp directory for contour plots */ - lept_mkdir("lept/dewarp1"); - if ((bmf = bmfCreate(NULL, 8)) == NULL) - L_ERROR("bmf not made; page info not displayed", procName); - - lept_stderr("Generating contour plots\n"); - for (i = first; i <= last; i++) { - if (i && ((i % 10) == 0)) - lept_stderr(" .. %d", i); - dew = dewarpaGetDewarp(dewa, i); - if (!dew) continue; - if (dew->hasref == 1) continue; - svd = shd = 0; - if (dew->sampvdispar) svd = 1; - if (dew->samphdispar) shd = 1; - if (!svd) { - L_ERROR("sampvdispar not made for page %d!\n", procName, i); - continue; - } - - /* Generate contour plots at reduced resolution */ - dewarpPopulateFullRes(dew, NULL, 0, 0); - pixv = fpixRenderContours(dew->fullvdispar, 3.0, 0.15); - pixvs = pixScaleBySampling(pixv, scalefact, scalefact); - pixDestroy(&pixv); - if (shd) { - pixh = fpixRenderContours(dew->fullhdispar, 3.0, 0.15); - pixhs = pixScaleBySampling(pixh, scalefact, scalefact); - pixDestroy(&pixh); - } - dewarpMinimize(dew); - - /* Save side-by-side */ - pixa = pixaCreate(2); - pixaAddPix(pixa, pixvs, L_INSERT); - if (shd) - pixaAddPix(pixa, pixhs, L_INSERT); - pixt = pixaDisplayTiledInRows(pixa, 32, 1500, 1.0, 0, 30, 2); - snprintf(buf, sizeof(buf), "Page %d", i); - pixd = pixAddSingleTextblock(pixt, bmf, buf, 0x0000ff00, - L_ADD_BELOW, NULL); - snprintf(buf, sizeof(buf), "/tmp/lept/dewarp1/arrays_%04d.png", i); - pixWriteDebug(buf, pixd, IFF_PNG); - pixaDestroy(&pixa); - pixDestroy(&pixt); - pixDestroy(&pixd); - } - bmfDestroy(&bmf); - lept_stderr("\n"); - - lept_stderr("Generating pdf of contour plots\n"); - convertFilesToPdf("/tmp/lept/dewarp1", "arrays_", 90, 1.0, L_FLATE_ENCODE, - 0, "Disparity arrays", "/tmp/lept/disparity_arrays.pdf"); - lept_stderr("Output written to: /tmp/lept/disparity_arrays.pdf\n"); - return 0; -} - - -/*! - * \brief dewarpDebug() - * - * \param[in] dew - * \param[in] subdirs one or more subdirectories of /tmp; e.g., "dew1" - * \param[in] index to help label output images; e.g., the page number - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Prints dewarp fields and generates disparity array contour images. - * The contour images are written to file: - * /tmp/[subdirs]/pixv_[index].png - *- */ -l_ok -dewarpDebug(L_DEWARP *dew, - const char *subdirs, - l_int32 index) -{ -char fname[256]; -char *outdir; -l_int32 svd, shd; -PIX *pixv, *pixh; - - PROCNAME("dewarpDebug"); - - if (!dew) - return ERROR_INT("dew not defined", procName, 1); - if (!subdirs) - return ERROR_INT("subdirs not defined", procName, 1); - - lept_stderr("pageno = %d, hasref = %d, refpage = %d\n", - dew->pageno, dew->hasref, dew->refpage); - lept_stderr("sampling = %d, redfactor = %d, minlines = %d\n", - dew->sampling, dew->redfactor, dew->minlines); - svd = shd = 0; - if (!dew->hasref) { - if (dew->sampvdispar) svd = 1; - if (dew->samphdispar) shd = 1; - lept_stderr("sampv = %d, samph = %d\n", svd, shd); - lept_stderr("w = %d, h = %d\n", dew->w, dew->h); - lept_stderr("nx = %d, ny = %d\n", dew->nx, dew->ny); - lept_stderr("nlines = %d\n", dew->nlines); - if (svd) { - lept_stderr("(min,max,abs-diff) line curvature = (%d,%d,%d)\n", - dew->mincurv, dew->maxcurv, dew->maxcurv - dew->mincurv); - } - if (shd) { - lept_stderr("(left edge slope = %d, right edge slope = %d\n", - dew->leftslope, dew->rightslope); - lept_stderr("(left,right,abs-diff) edge curvature = " - "(%d,%d,%d)\n", dew->leftcurv, dew->rightcurv, - L_ABS(dew->leftcurv - dew->rightcurv)); - } - } - if (!svd && !shd) { - lept_stderr("No disparity arrays\n"); - return 0; - } - - dewarpPopulateFullRes(dew, NULL, 0, 0); - lept_mkdir(subdirs); - outdir = pathJoin("/tmp", subdirs); - if (svd) { - pixv = fpixRenderContours(dew->fullvdispar, 3.0, 0.15); - snprintf(fname, sizeof(fname), "%s/pixv_%d.png", outdir, index); - pixWriteDebug(fname, pixv, IFF_PNG); - pixDestroy(&pixv); - } - if (shd) { - pixh = fpixRenderContours(dew->fullhdispar, 3.0, 0.15); - snprintf(fname, sizeof(fname), "%s/pixh_%d.png", outdir, index); - pixWriteDebug(fname, pixh, IFF_PNG); - pixDestroy(&pixh); - } - LEPT_FREE(outdir); - return 0; -} - - -/*! - * \brief dewarpShowResults() - * - * \param[in] dewa - * \param[in] sa of indexed input images - * \param[in] boxa crop boxes for input images; can be null - * \param[in] firstpage - * \param[in] lastpage - * \param[in] pdfout filename - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This generates a pdf of image pairs (before, after) for - * the designated set of input pages. - * (2) If the boxa exists, its elements are aligned with numbers - * in the filenames in %sa. It is used to crop the input images. - * It is assumed that the dewa was generated from the cropped - * images. No undercropping is applied before rendering. - *- */ -l_ok -dewarpShowResults(L_DEWARPA *dewa, - SARRAY *sa, - BOXA *boxa, - l_int32 firstpage, - l_int32 lastpage, - const char *pdfout) -{ -char bufstr[256]; -l_int32 i, modelpage; -L_BMF *bmf; -BOX *box; -L_DEWARP *dew; -PIX *pixs, *pixc, *pixd, *pixt1, *pixt2; -PIXA *pixa; - - PROCNAME("dewarpShowResults"); - - if (!dewa) - return ERROR_INT("dewa not defined", procName, 1); - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - if (!pdfout) - return ERROR_INT("pdfout not defined", procName, 1); - if (firstpage > lastpage) - return ERROR_INT("invalid first/last page numbers", procName, 1); - - lept_rmdir("lept/dewarp_pdfout"); - lept_mkdir("lept/dewarp_pdfout"); - bmf = bmfCreate(NULL, 6); - - lept_stderr("Dewarping and generating s/by/s view\n"); - for (i = firstpage; i <= lastpage; i++) { - if (i && (i % 10 == 0)) lept_stderr(".. %d ", i); - pixs = pixReadIndexed(sa, i); - if (boxa) { - box = boxaGetBox(boxa, i, L_CLONE); - pixc = pixClipRectangle(pixs, box, NULL); - boxDestroy(&box); - } - else - pixc = pixClone(pixs); - dew = dewarpaGetDewarp(dewa, i); - pixd = NULL; - if (dew) { - dewarpaApplyDisparity(dewa, dew->pageno, pixc, - GrayInValue, 0, 0, &pixd, NULL); - dewarpMinimize(dew); - } - pixa = pixaCreate(2); - pixaAddPix(pixa, pixc, L_INSERT); - if (pixd) - pixaAddPix(pixa, pixd, L_INSERT); - pixt1 = pixaDisplayTiledAndScaled(pixa, 32, 500, 2, 0, 35, 2); - if (dew) { - modelpage = (dew->hasref) ? dew->refpage : dew->pageno; - snprintf(bufstr, sizeof(bufstr), "Page %d; using %d\n", - i, modelpage); - } - else - snprintf(bufstr, sizeof(bufstr), "Page %d; no dewarp\n", i); - pixt2 = pixAddSingleTextblock(pixt1, bmf, bufstr, 0x0000ff00, - L_ADD_BELOW, 0); - snprintf(bufstr, sizeof(bufstr), "/tmp/lept/dewarp_pdfout/%05d", i); - pixWriteDebug(bufstr, pixt2, IFF_JFIF_JPEG); - pixaDestroy(&pixa); - pixDestroy(&pixs); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - } - lept_stderr("\n"); - - lept_stderr("Generating pdf of result\n"); - convertFilesToPdf("/tmp/lept/dewarp_pdfout", NULL, 100, 1.0, L_JPEG_ENCODE, - 0, "Dewarp sequence", pdfout); - lept_stderr("Output written to: %s\n", pdfout); - bmfDestroy(&bmf); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dnabasic.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dnabasic.c deleted file mode 100644 index 9c9dba5f..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dnabasic.c +++ /dev/null @@ -1,1685 +0,0 @@ -/*====================================================================* - - 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 dnabasic.c - *
- * - * Dna creation, destruction, copy, clone, etc. - * L_DNA *l_dnaCreate() - * L_DNA *l_dnaCreateFromIArray() - * L_DNA *l_dnaCreateFromDArray() - * L_DNA *l_dnaMakeSequence() - * void *l_dnaDestroy() - * L_DNA *l_dnaCopy() - * L_DNA *l_dnaClone() - * l_int32 l_dnaEmpty() - * - * Dna: add/remove number and extend array - * l_int32 l_dnaAddNumber() - * static l_int32 l_dnaExtendArray() - * l_int32 l_dnaInsertNumber() - * l_int32 l_dnaRemoveNumber() - * l_int32 l_dnaReplaceNumber() - * - * Dna accessors - * l_int32 l_dnaGetCount() - * l_int32 l_dnaSetCount() - * l_int32 l_dnaGetIValue() - * l_int32 l_dnaGetDValue() - * l_int32 l_dnaSetValue() - * l_int32 l_dnaShiftValue() - * l_int32 *l_dnaGetIArray() - * l_float64 *l_dnaGetDArray() - * l_int32 l_dnaGetRefcount() - * l_int32 l_dnaChangeRefcount() - * l_int32 l_dnaGetParameters() - * l_int32 l_dnaSetParameters() - * l_int32 l_dnaCopyParameters() - * - * Serialize Dna for I/O - * L_DNA *l_dnaRead() - * L_DNA *l_dnaReadStream() - * l_int32 l_dnaWrite() - * l_int32 l_dnaWriteStream() - * - * Dnaa creation, destruction - * L_DNAA *l_dnaaCreate() - * L_DNAA *l_dnaaCreateFull() - * l_int32 l_dnaaTruncate() - * void *l_dnaaDestroy() - * - * Add Dna to Dnaa - * l_int32 l_dnaaAddDna() - * static l_int32 l_dnaaExtendArray() - * - * Dnaa accessors - * l_int32 l_dnaaGetCount() - * l_int32 l_dnaaGetDnaCount() - * l_int32 l_dnaaGetNumberCount() - * L_DNA *l_dnaaGetDna() - * L_DNA *l_dnaaReplaceDna() - * l_int32 l_dnaaGetValue() - * l_int32 l_dnaaAddNumber() - * - * Serialize Dnaa for I/O - * L_DNAA *l_dnaaRead() - * L_DNAA *l_dnaaReadStream() - * l_int32 l_dnaaWrite() - * l_int32 l_dnaaWriteStream() - * - * (1) The Dna is a struct holding an array of doubles. It can also - * be used to store l_int32 values, up to the full precision - * of int32. Always use it whenever integers larger than a - * few million need to be stored. - * - * (2) Always use the accessors in this file, never the fields directly. - * - * (3) Storing and retrieving numbers: - * - * * to append a new number to the array, use l_dnaAddNumber(). If - * the number is an int, it will will automatically be converted - * to l_float64 and stored. - * - * * to reset a value stored in the array, use l_dnaSetValue(). - * - * * to increment or decrement a value stored in the array, - * use l_dnaShiftValue(). - * - * * to obtain a value from the array, use either l_dnaGetIValue() - * or l_dnaGetDValue(), depending on whether you are retrieving - * an integer or a float64. This avoids doing an explicit cast, - * such as - * (a) return a l_float64 and cast it to an l_int32 - * (b) cast the return directly to (l_float64 *) to - * satisfy the function prototype, as in - * l_dnaGetDValue(da, index, (l_float64 *)&ival); [ugly!] - * - * (4) int <--> double conversions: - * - * Conversions go automatically from l_int32 --> l_float64, - * without loss of precision. You must cast (l_int32) - * to go from l_float64 --> l_int32 because you're truncating - * to the integer value. - * - * (5) As with other arrays in leptonica, the l_dna has both an allocated - * size and a count of the stored numbers. When you add a number, it - * goes on the end of the array, and causes a realloc if the array - * is already filled. However, in situations where you want to - * add numbers randomly into an array, such as when you build a - * histogram, you must set the count of stored numbers in advance. - * This is done with l_dnaSetCount(). If you set a count larger - * than the allocated array, it does a realloc to the size requested. - * - * (6) In situations where the data in a l_dna correspond to a function - * y(x), the values can be either at equal spacings in x or at - * arbitrary spacings. For the former, we can represent all x values - * by two parameters: startx (corresponding to y[0]) and delx - * for the change in x for adjacent values y[i] and y[i+1]. - * startx and delx are initialized to 0.0 and 1.0, rsp. - * For arbitrary spacings, we use a second l_dna, and the two - * l_dnas are typically denoted dnay and dnax. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) We can't insert this int array into the l_dna, because a l_dna - * takes a double array. So this just copies the data from the - * input array into the l_dna. The input array continues to be - * owned by the caller. - *- */ -L_DNA * -l_dnaCreateFromIArray(l_int32 *iarray, - l_int32 size) -{ -l_int32 i; -L_DNA *da; - - PROCNAME("l_dnaCreateFromIArray"); - - if (!iarray) - return (L_DNA *)ERROR_PTR("iarray not defined", procName, NULL); - if (size <= 0) - return (L_DNA *)ERROR_PTR("size must be > 0", procName, NULL); - - da = l_dnaCreate(size); - for (i = 0; i < size; i++) - l_dnaAddNumber(da, iarray[i]); - - return da; -} - - -/*! - * \brief l_dnaCreateFromDArray() - * - * \param[in] darray float - * \param[in] size of the array - * \param[in] copyflag L_INSERT or L_COPY - * \return da, or NULL on error - * - *
- * Notes: - * (1) With L_INSERT, ownership of the input array is transferred - * to the returned l_dna, and all %size elements are considered - * to be valid. - *- */ -L_DNA * -l_dnaCreateFromDArray(l_float64 *darray, - l_int32 size, - l_int32 copyflag) -{ -l_int32 i; -L_DNA *da; - - PROCNAME("l_dnaCreateFromDArray"); - - if (!darray) - return (L_DNA *)ERROR_PTR("darray not defined", procName, NULL); - if (size <= 0) - return (L_DNA *)ERROR_PTR("size must be > 0", procName, NULL); - if (copyflag != L_INSERT && copyflag != L_COPY) - return (L_DNA *)ERROR_PTR("invalid copyflag", procName, NULL); - - da = l_dnaCreate(size); - if (copyflag == L_INSERT) { - if (da->array) LEPT_FREE(da->array); - da->array = darray; - da->n = size; - } else { /* just copy the contents */ - for (i = 0; i < size; i++) - l_dnaAddNumber(da, darray[i]); - } - - return da; -} - - -/*! - * \brief l_dnaMakeSequence() - * - * \param[in] startval - * \param[in] increment - * \param[in] size of sequence - * \return l_dna of sequence of evenly spaced values, or NULL on error - */ -L_DNA * -l_dnaMakeSequence(l_float64 startval, - l_float64 increment, - l_int32 size) -{ -l_int32 i; -l_float64 val; -L_DNA *da; - - PROCNAME("l_dnaMakeSequence"); - - if ((da = l_dnaCreate(size)) == NULL) - return (L_DNA *)ERROR_PTR("da not made", procName, NULL); - - for (i = 0; i < size; i++) { - val = startval + i * increment; - l_dnaAddNumber(da, val); - } - - return da; -} - - -/*! - * \brief l_dnaDestroy() - * - * \param[in,out] pda will be set to null before returning - * \return void - * - *
- * Notes: - * (1) Decrements the ref count and, if 0, destroys the l_dna. - * (2) Always nulls the input ptr. - *- */ -void -l_dnaDestroy(L_DNA **pda) -{ -L_DNA *da; - - PROCNAME("l_dnaDestroy"); - - if (pda == NULL) { - L_WARNING("ptr address is NULL\n", procName); - return; - } - - if ((da = *pda) == NULL) - return; - - /* Decrement the ref count. If it is 0, destroy the l_dna. */ - l_dnaChangeRefcount(da, -1); - if (l_dnaGetRefcount(da) <= 0) { - if (da->array) - LEPT_FREE(da->array); - LEPT_FREE(da); - } - - *pda = NULL; - return; -} - - -/*! - * \brief l_dnaCopy() - * - * \param[in] da - * \return copy of da, or NULL on error - * - *
- * Notes: - * (1) This removes unused ptrs above da->n. - *- */ -L_DNA * -l_dnaCopy(L_DNA *da) -{ -l_int32 i; -L_DNA *dac; - - PROCNAME("l_dnaCopy"); - - if (!da) - return (L_DNA *)ERROR_PTR("da not defined", procName, NULL); - - if ((dac = l_dnaCreate(da->n)) == NULL) - return (L_DNA *)ERROR_PTR("dac not made", procName, NULL); - dac->startx = da->startx; - dac->delx = da->delx; - - for (i = 0; i < da->n; i++) - l_dnaAddNumber(dac, da->array[i]); - - return dac; -} - - -/*! - * \brief l_dnaClone() - * - * \param[in] da - * \return ptr to same da, or NULL on error - */ -L_DNA * -l_dnaClone(L_DNA *da) -{ - PROCNAME("l_dnaClone"); - - if (!da) - return (L_DNA *)ERROR_PTR("da not defined", procName, NULL); - - l_dnaChangeRefcount(da, 1); - return da; -} - - -/*! - * \brief l_dnaEmpty() - * - * \param[in] da - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This does not change the allocation of the array. - * It just clears the number of stored numbers, so that - * the array appears to be empty. - *- */ -l_ok -l_dnaEmpty(L_DNA *da) -{ - PROCNAME("l_dnaEmpty"); - - if (!da) - return ERROR_INT("da not defined", procName, 1); - - da->n = 0; - return 0; -} - - - -/*--------------------------------------------------------------------------* - * Dna: add/remove number and extend array * - *--------------------------------------------------------------------------*/ -/*! - * \brief l_dnaAddNumber() - * - * \param[in] da - * \param[in] val float or int to be added; stored as a float - * \return 0 if OK, 1 on error - */ -l_ok -l_dnaAddNumber(L_DNA *da, - l_float64 val) -{ -l_int32 n; - - PROCNAME("l_dnaAddNumber"); - - if (!da) - return ERROR_INT("da not defined", procName, 1); - - n = l_dnaGetCount(da); - if (n >= da->nalloc) - l_dnaExtendArray(da); - da->array[n] = val; - da->n++; - return 0; -} - - -/*! - * \brief l_dnaExtendArray() - * - * \param[in] da - * \return 0 if OK, 1 on error - */ -static l_int32 -l_dnaExtendArray(L_DNA *da) -{ - PROCNAME("l_dnaExtendArray"); - - if (!da) - return ERROR_INT("da not defined", procName, 1); - - if ((da->array = (l_float64 *)reallocNew((void **)&da->array, - sizeof(l_float64) * da->nalloc, - 2 * sizeof(l_float64) * da->nalloc)) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - - da->nalloc *= 2; - return 0; -} - - -/*! - * \brief l_dnaInsertNumber() - * - * \param[in] da - * \param[in] index location in da to insert new value - * \param[in] val float64 or integer to be added - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This shifts da[i] --> da[i + 1] for all i >= %index, - * and then inserts %val as da[%index]. - * (2) It should not be used repeatedly on large arrays, - * because the function is O(n). - * - *- */ -l_ok -l_dnaInsertNumber(L_DNA *da, - l_int32 index, - l_float64 val) -{ -l_int32 i, n; - - PROCNAME("l_dnaInsertNumber"); - - if (!da) - return ERROR_INT("da not defined", procName, 1); - n = l_dnaGetCount(da); - if (index < 0 || index > n) - return ERROR_INT("index not in {0...n}", procName, 1); - - if (n >= da->nalloc) - l_dnaExtendArray(da); - for (i = n; i > index; i--) - da->array[i] = da->array[i - 1]; - da->array[index] = val; - da->n++; - return 0; -} - - -/*! - * \brief l_dnaRemoveNumber() - * - * \param[in] da - * \param[in] index element to be removed - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This shifts da[i] --> da[i - 1] for all i > %index. - * (2) It should not be used repeatedly on large arrays, - * because the function is O(n). - *- */ -l_ok -l_dnaRemoveNumber(L_DNA *da, - l_int32 index) -{ -l_int32 i, n; - - PROCNAME("l_dnaRemoveNumber"); - - if (!da) - return ERROR_INT("da not defined", procName, 1); - n = l_dnaGetCount(da); - if (index < 0 || index >= n) - return ERROR_INT("index not in {0...n - 1}", procName, 1); - - for (i = index + 1; i < n; i++) - da->array[i - 1] = da->array[i]; - da->n--; - return 0; -} - - -/*! - * \brief l_dnaReplaceNumber() - * - * \param[in] da - * \param[in] index element to be replaced - * \param[in] val new value to replace old one - * \return 0 if OK, 1 on error - */ -l_ok -l_dnaReplaceNumber(L_DNA *da, - l_int32 index, - l_float64 val) -{ -l_int32 n; - - PROCNAME("l_dnaReplaceNumber"); - - if (!da) - return ERROR_INT("da not defined", procName, 1); - n = l_dnaGetCount(da); - if (index < 0 || index >= n) - return ERROR_INT("index not in {0...n - 1}", procName, 1); - - da->array[index] = val; - return 0; -} - - -/*----------------------------------------------------------------------* - * Dna accessors * - *----------------------------------------------------------------------*/ -/*! - * \brief l_dnaGetCount() - * - * \param[in] da - * \return count, or 0 if no numbers or on error - */ -l_int32 -l_dnaGetCount(L_DNA *da) -{ - PROCNAME("l_dnaGetCount"); - - if (!da) - return ERROR_INT("da not defined", procName, 0); - return da->n; -} - - -/*! - * \brief l_dnaSetCount() - * - * \param[in] da - * \param[in] newcount - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) If %newcount <= da->nalloc, this resets da->n. - * Using %newcount = 0 is equivalent to l_dnaEmpty(). - * (2) If %newcount > da->nalloc, this causes a realloc - * to a size da->nalloc = %newcount. - * (3) All the previously unused values in da are set to 0.0. - *- */ -l_ok -l_dnaSetCount(L_DNA *da, - l_int32 newcount) -{ - PROCNAME("l_dnaSetCount"); - - if (!da) - return ERROR_INT("da not defined", procName, 1); - if (newcount > da->nalloc) { - if ((da->array = (l_float64 *)reallocNew((void **)&da->array, - sizeof(l_float64) * da->nalloc, - sizeof(l_float64) * newcount)) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - da->nalloc = newcount; - } - da->n = newcount; - return 0; -} - - -/*! - * \brief l_dnaGetDValue() - * - * \param[in] da - * \param[in] index into l_dna - * \param[out] pval double value; 0.0 on error - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) Caller may need to check the function return value to - * decide if a 0.0 in the returned ival is valid. - *- */ -l_ok -l_dnaGetDValue(L_DNA *da, - l_int32 index, - l_float64 *pval) -{ - PROCNAME("l_dnaGetDValue"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0.0; - if (!da) - return ERROR_INT("da not defined", procName, 1); - - if (index < 0 || index >= da->n) - return ERROR_INT("index not valid", procName, 1); - - *pval = da->array[index]; - return 0; -} - - -/*! - * \brief l_dnaGetIValue() - * - * \param[in] da - * \param[in] index into l_dna - * \param[out] pival integer value; 0 on error - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) Caller may need to check the function return value to - * decide if a 0 in the returned ival is valid. - *- */ -l_ok -l_dnaGetIValue(L_DNA *da, - l_int32 index, - l_int32 *pival) -{ -l_float64 val; - - PROCNAME("l_dnaGetIValue"); - - if (!pival) - return ERROR_INT("&ival not defined", procName, 1); - *pival = 0; - if (!da) - return ERROR_INT("da not defined", procName, 1); - - if (index < 0 || index >= da->n) - return ERROR_INT("index not valid", procName, 1); - - val = da->array[index]; - *pival = (l_int32)(val + L_SIGN(val) * 0.5); - return 0; -} - - -/*! - * \brief l_dnaSetValue() - * - * \param[in] da - * \param[in] index to element to be set - * \param[in] val to set element - * \return 0 if OK; 1 on error - */ -l_ok -l_dnaSetValue(L_DNA *da, - l_int32 index, - l_float64 val) -{ - PROCNAME("l_dnaSetValue"); - - if (!da) - return ERROR_INT("da not defined", procName, 1); - if (index < 0 || index >= da->n) - return ERROR_INT("index not valid", procName, 1); - - da->array[index] = val; - return 0; -} - - -/*! - * \brief l_dnaShiftValue() - * - * \param[in] da - * \param[in] index to element to change relative to the current value - * \param[in] diff increment if diff > 0 or decrement if diff < 0 - * \return 0 if OK; 1 on error - */ -l_ok -l_dnaShiftValue(L_DNA *da, - l_int32 index, - l_float64 diff) -{ - PROCNAME("l_dnaShiftValue"); - - if (!da) - return ERROR_INT("da not defined", procName, 1); - if (index < 0 || index >= da->n) - return ERROR_INT("index not valid", procName, 1); - - da->array[index] += diff; - return 0; -} - - -/*! - * \brief l_dnaGetIArray() - * - * \param[in] da - * \return a copy of the bare internal array, integerized - * by rounding, or NULL on error - *
- * Notes: - * (1) A copy of the array is made, because we need to - * generate an integer array from the bare double array. - * The caller is responsible for freeing the array. - * (2) The array size is determined by the number of stored numbers, - * not by the size of the allocated array in the l_dna. - * (3) This function is provided to simplify calculations - * using the bare internal array, rather than continually - * calling accessors on the l_dna. It is typically used - * on an array of size 256. - *- */ -l_int32 * -l_dnaGetIArray(L_DNA *da) -{ -l_int32 i, n, ival; -l_int32 *array; - - PROCNAME("l_dnaGetIArray"); - - if (!da) - return (l_int32 *)ERROR_PTR("da not defined", procName, NULL); - - n = l_dnaGetCount(da); - if ((array = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32))) == NULL) - return (l_int32 *)ERROR_PTR("array not made", procName, NULL); - for (i = 0; i < n; i++) { - l_dnaGetIValue(da, i, &ival); - array[i] = ival; - } - - return array; -} - - -/*! - * \brief l_dnaGetDArray() - * - * \param[in] da - * \param[in] copyflag L_NOCOPY or L_COPY - * \return either the bare internal array or a copy of it, or NULL on error - * - *
- * Notes: - * (1) If %copyflag == L_COPY, it makes a copy which the caller - * is responsible for freeing. Otherwise, it operates - * directly on the bare array of the l_dna. - * (2) Very important: for L_NOCOPY, any writes to the array - * will be in the l_dna. Do not write beyond the size of - * the count field, because it will not be accessible - * from the l_dna! If necessary, be sure to set the count - * field to a larger number (such as the alloc size) - * BEFORE calling this function. Creating with l_dnaMakeConstant() - * is another way to insure full initialization. - *- */ -l_float64 * -l_dnaGetDArray(L_DNA *da, - l_int32 copyflag) -{ -l_int32 i, n; -l_float64 *array; - - PROCNAME("l_dnaGetDArray"); - - if (!da) - return (l_float64 *)ERROR_PTR("da not defined", procName, NULL); - - if (copyflag == L_NOCOPY) { - array = da->array; - } else { /* copyflag == L_COPY */ - n = l_dnaGetCount(da); - if ((array = (l_float64 *)LEPT_CALLOC(n, sizeof(l_float64))) == NULL) - return (l_float64 *)ERROR_PTR("array not made", procName, NULL); - for (i = 0; i < n; i++) - array[i] = da->array[i]; - } - - return array; -} - - -/*! - * \brief l_dnaGetRefCount() - * - * \param[in] da - * \return refcount, or UNDEF on error - */ -l_int32 -l_dnaGetRefcount(L_DNA *da) -{ - PROCNAME("l_dnaGetRefcount"); - - if (!da) - return ERROR_INT("da not defined", procName, UNDEF); - return da->refcount; -} - - -/*! - * \brief l_dnaChangeRefCount() - * - * \param[in] da - * \param[in] delta change to be applied - * \return 0 if OK, 1 on error - */ -l_ok -l_dnaChangeRefcount(L_DNA *da, - l_int32 delta) -{ - PROCNAME("l_dnaChangeRefcount"); - - if (!da) - return ERROR_INT("da not defined", procName, 1); - da->refcount += delta; - return 0; -} - - -/*! - * \brief l_dnaGetParameters() - * - * \param[in] da - * \param[out] pstartx [optional] startx - * \param[out] pdelx [optional] delx - * \return 0 if OK, 1 on error - */ -l_ok -l_dnaGetParameters(L_DNA *da, - l_float64 *pstartx, - l_float64 *pdelx) -{ - PROCNAME("l_dnaGetParameters"); - - if (pstartx) *pstartx = 0.0; - if (pdelx) *pdelx = 1.0; - if (!pstartx && !pdelx) - return ERROR_INT("neither &startx nor &delx are defined", procName, 1); - if (!da) - return ERROR_INT("da not defined", procName, 1); - - if (pstartx) *pstartx = da->startx; - if (pdelx) *pdelx = da->delx; - return 0; -} - - -/*! - * \brief l_dnaSetParameters() - * - * \param[in] da - * \param[in] startx x value corresponding to da[0] - * \param[in] delx difference in x values for the situation where the - * elements of da correspond to the evaulation of a - * function at equal intervals of size %delx - * \return 0 if OK, 1 on error - */ -l_ok -l_dnaSetParameters(L_DNA *da, - l_float64 startx, - l_float64 delx) -{ - PROCNAME("l_dnaSetParameters"); - - if (!da) - return ERROR_INT("da not defined", procName, 1); - - da->startx = startx; - da->delx = delx; - return 0; -} - - -/*! - * \brief l_dnaCopyParameters() - * - * \param[in] dad destination DNuma - * \param[in] das source DNuma - * \return 0 if OK, 1 on error - */ -l_ok -l_dnaCopyParameters(L_DNA *dad, - L_DNA *das) -{ -l_float64 start, binsize; - - PROCNAME("l_dnaCopyParameters"); - - if (!das || !dad) - return ERROR_INT("das and dad not both defined", procName, 1); - - l_dnaGetParameters(das, &start, &binsize); - l_dnaSetParameters(dad, start, binsize); - return 0; -} - - -/*----------------------------------------------------------------------* - * Serialize Dna for I/O * - *----------------------------------------------------------------------*/ -/*! - * \brief l_dnaRead() - * - * \param[in] filename - * \return da, or NULL on error - */ -L_DNA * -l_dnaRead(const char *filename) -{ -FILE *fp; -L_DNA *da; - - PROCNAME("l_dnaRead"); - - if (!filename) - return (L_DNA *)ERROR_PTR("filename not defined", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (L_DNA *)ERROR_PTR("stream not opened", procName, NULL); - da = l_dnaReadStream(fp); - fclose(fp); - if (!da) - return (L_DNA *)ERROR_PTR("da not read", procName, NULL); - return da; -} - - -/*! - * \brief l_dnaReadStream() - * - * \param[in] fp file stream - * \return da, or NULL on error - * - *
- * Notes: - * (1) fscanf takes %lf to read a double; fprintf takes %f to write it. - *- */ -L_DNA * -l_dnaReadStream(FILE *fp) -{ -l_int32 i, n, index, ret, version; -l_float64 val, startx, delx; -L_DNA *da; - - PROCNAME("l_dnaReadStream"); - - if (!fp) - return (L_DNA *)ERROR_PTR("stream not defined", procName, NULL); - - ret = fscanf(fp, "\nL_Dna Version %d\n", &version); - if (ret != 1) - return (L_DNA *)ERROR_PTR("not a l_dna file", procName, NULL); - if (version != DNA_VERSION_NUMBER) - return (L_DNA *)ERROR_PTR("invalid l_dna version", procName, NULL); - if (fscanf(fp, "Number of numbers = %d\n", &n) != 1) - return (L_DNA *)ERROR_PTR("invalid number of numbers", procName, NULL); - - if (n > MaxArraySize) { - L_ERROR("n = %d > %d\n", procName, n, MaxArraySize); - return NULL; - } - if ((da = l_dnaCreate(n)) == NULL) - return (L_DNA *)ERROR_PTR("da not made", procName, NULL); - for (i = 0; i < n; i++) { - if (fscanf(fp, " [%d] = %lf\n", &index, &val) != 2) { - l_dnaDestroy(&da); - return (L_DNA *)ERROR_PTR("bad input data", procName, NULL); - } - l_dnaAddNumber(da, val); - } - - /* Optional data */ - if (fscanf(fp, "startx = %lf, delx = %lf\n", &startx, &delx) == 2) - l_dnaSetParameters(da, startx, delx); - return da; -} - - -/*! - * \brief l_dnaWrite() - * - * \param[in] filename - * \param[in] da - * \return 0 if OK, 1 on error - */ -l_ok -l_dnaWrite(const char *filename, - L_DNA *da) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("l_dnaWrite"); - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!da) - return ERROR_INT("da not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "w")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = l_dnaWriteStream(fp, da); - fclose(fp); - if (ret) - return ERROR_INT("da not written to stream", procName, 1); - return 0; -} - - -/*! - * \brief l_dnaWriteStream() - * - * \param[in] fp file stream - * \param[in] da - * \return 0 if OK, 1 on error - */ -l_ok -l_dnaWriteStream(FILE *fp, - L_DNA *da) -{ -l_int32 i, n; -l_float64 startx, delx; - - PROCNAME("l_dnaWriteStream"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!da) - return ERROR_INT("da not defined", procName, 1); - - n = l_dnaGetCount(da); - fprintf(fp, "\nL_Dna Version %d\n", DNA_VERSION_NUMBER); - fprintf(fp, "Number of numbers = %d\n", n); - for (i = 0; i < n; i++) - fprintf(fp, " [%d] = %f\n", i, da->array[i]); - fprintf(fp, "\n"); - - /* Optional data */ - l_dnaGetParameters(da, &startx, &delx); - if (startx != 0.0 || delx != 1.0) - fprintf(fp, "startx = %f, delx = %f\n", startx, delx); - - return 0; -} - - -/*--------------------------------------------------------------------------* - * Dnaa creation, destruction * - *--------------------------------------------------------------------------*/ -/*! - * \brief l_dnaaCreate() - * - * \param[in] n size of l_dna ptr array to be alloc'd 0 for default - * \return daa, or NULL on error - * - */ -L_DNAA * -l_dnaaCreate(l_int32 n) -{ -L_DNAA *daa; - - PROCNAME("l_dnaaCreate"); - - if (n <= 0 || n > MaxPtrArraySize) - n = InitialArraySize; - - daa = (L_DNAA *)LEPT_CALLOC(1, sizeof(L_DNAA)); - if ((daa->dna = (L_DNA **)LEPT_CALLOC(n, sizeof(L_DNA *))) == NULL) { - l_dnaaDestroy(&daa); - return (L_DNAA *)ERROR_PTR("l_dna ptr array not made", procName, NULL); - } - daa->nalloc = n; - daa->n = 0; - return daa; -} - - -/*! - * \brief l_dnaaCreateFull() - * - * \param[in] nptr size of dna ptr array to be alloc'd - * \param[in] n size of individual dna arrays to be alloc'd 0 for default - * \return daa, or NULL on error - * - *
- * Notes: - * (1) This allocates a dnaa and fills the array with allocated dnas. - * In use, after calling this function, use - * l_dnaaAddNumber(dnaa, index, val); - * to add val to the index-th dna in dnaa. - *- */ -L_DNAA * -l_dnaaCreateFull(l_int32 nptr, - l_int32 n) -{ -l_int32 i; -L_DNAA *daa; -L_DNA *da; - - daa = l_dnaaCreate(nptr); - for (i = 0; i < nptr; i++) { - da = l_dnaCreate(n); - l_dnaaAddDna(daa, da, L_INSERT); - } - - return daa; -} - - -/*! - * \brief l_dnaaTruncate() - * - * \param[in] daa - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This identifies the largest index containing a dna that - * has any numbers within it, destroys all dna beyond that - * index, and resets the count. - *- */ -l_ok -l_dnaaTruncate(L_DNAA *daa) -{ -l_int32 i, n, nn; -L_DNA *da; - - PROCNAME("l_dnaaTruncate"); - - if (!daa) - return ERROR_INT("daa not defined", procName, 1); - - n = l_dnaaGetCount(daa); - for (i = n - 1; i >= 0; i--) { - da = l_dnaaGetDna(daa, i, L_CLONE); - if (!da) - continue; - nn = l_dnaGetCount(da); - l_dnaDestroy(&da); /* the clone */ - if (nn == 0) - l_dnaDestroy(&daa->dna[i]); - else - break; - } - daa->n = i + 1; - return 0; -} - - -/*! - * \brief l_dnaaDestroy() - * - * \param[in,out] pdaa will be set to null before returning - * \return void - */ -void -l_dnaaDestroy(L_DNAA **pdaa) -{ -l_int32 i; -L_DNAA *daa; - - PROCNAME("l_dnaaDestroy"); - - if (pdaa == NULL) { - L_WARNING("ptr address is NULL!\n", procName); - return; - } - - if ((daa = *pdaa) == NULL) - return; - - for (i = 0; i < daa->n; i++) - l_dnaDestroy(&daa->dna[i]); - LEPT_FREE(daa->dna); - LEPT_FREE(daa); - *pdaa = NULL; - - return; -} - - -/*--------------------------------------------------------------------------* - * Add Dna to Dnaa * - *--------------------------------------------------------------------------*/ -/*! - * \brief l_dnaaAddDna() - * - * \param[in] daa - * \param[in] da to be added - * \param[in] copyflag L_INSERT, L_COPY, L_CLONE - * \return 0 if OK, 1 on error - */ -l_ok -l_dnaaAddDna(L_DNAA *daa, - L_DNA *da, - l_int32 copyflag) -{ -l_int32 n; -L_DNA *dac; - - PROCNAME("l_dnaaAddDna"); - - if (!daa) - return ERROR_INT("daa not defined", procName, 1); - if (!da) - return ERROR_INT("da not defined", procName, 1); - - if (copyflag == L_INSERT) { - dac = da; - } else if (copyflag == L_COPY) { - if ((dac = l_dnaCopy(da)) == NULL) - return ERROR_INT("dac not made", procName, 1); - } else if (copyflag == L_CLONE) { - dac = l_dnaClone(da); - } else { - return ERROR_INT("invalid copyflag", procName, 1); - } - - n = l_dnaaGetCount(daa); - if (n >= daa->nalloc) - l_dnaaExtendArray(daa); - daa->dna[n] = dac; - daa->n++; - return 0; -} - - -/*! - * \brief l_dnaaExtendArray() - * - * \param[in] daa - * \return 0 if OK, 1 on error - */ -static l_int32 -l_dnaaExtendArray(L_DNAA *daa) -{ - PROCNAME("l_dnaaExtendArray"); - - if (!daa) - return ERROR_INT("daa not defined", procName, 1); - - if ((daa->dna = (L_DNA **)reallocNew((void **)&daa->dna, - sizeof(L_DNA *) * daa->nalloc, - 2 * sizeof(L_DNA *) * daa->nalloc)) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - - daa->nalloc *= 2; - return 0; -} - - -/*----------------------------------------------------------------------* - * DNumaa accessors * - *----------------------------------------------------------------------*/ -/*! - * \brief l_dnaaGetCount() - * - * \param[in] daa - * \return count number of l_dna, or 0 if no l_dna or on error - */ -l_int32 -l_dnaaGetCount(L_DNAA *daa) -{ - PROCNAME("l_dnaaGetCount"); - - if (!daa) - return ERROR_INT("daa not defined", procName, 0); - return daa->n; -} - - -/*! - * \brief l_dnaaGetDnaCount() - * - * \param[in] daa - * \param[in] index of l_dna in daa - * \return count of numbers in the referenced l_dna, or 0 on error. - */ -l_int32 -l_dnaaGetDnaCount(L_DNAA *daa, - l_int32 index) -{ - PROCNAME("l_dnaaGetDnaCount"); - - if (!daa) - return ERROR_INT("daa not defined", procName, 0); - if (index < 0 || index >= daa->n) - return ERROR_INT("invalid index into daa", procName, 0); - return l_dnaGetCount(daa->dna[index]); -} - - -/*! - * \brief l_dnaaGetNumberCount() - * - * \param[in] daa - * \return count total number of numbers in the l_dnaa, - * or 0 if no numbers or on error - */ -l_int32 -l_dnaaGetNumberCount(L_DNAA *daa) -{ -L_DNA *da; -l_int32 n, sum, i; - - PROCNAME("l_dnaaGetNumberCount"); - - if (!daa) - return ERROR_INT("daa not defined", procName, 0); - - n = l_dnaaGetCount(daa); - for (sum = 0, i = 0; i < n; i++) { - da = l_dnaaGetDna(daa, i, L_CLONE); - sum += l_dnaGetCount(da); - l_dnaDestroy(&da); - } - - return sum; -} - - -/*! - * \brief l_dnaaGetDna() - * - * \param[in] daa - * \param[in] index to the index-th l_dna - * \param[in] accessflag L_COPY or L_CLONE - * \return l_dna, or NULL on error - */ -L_DNA * -l_dnaaGetDna(L_DNAA *daa, - l_int32 index, - l_int32 accessflag) -{ - PROCNAME("l_dnaaGetDna"); - - if (!daa) - return (L_DNA *)ERROR_PTR("daa not defined", procName, NULL); - if (index < 0 || index >= daa->n) - return (L_DNA *)ERROR_PTR("index not valid", procName, NULL); - - if (accessflag == L_COPY) - return l_dnaCopy(daa->dna[index]); - else if (accessflag == L_CLONE) - return l_dnaClone(daa->dna[index]); - else - return (L_DNA *)ERROR_PTR("invalid accessflag", procName, NULL); -} - - -/*! - * \brief l_dnaaReplaceDna() - * - * \param[in] daa - * \param[in] index to the index-th l_dna - * \param[in] da insert and replace any existing one - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Any existing l_dna is destroyed, and the input one - * is inserted in its place. - * (2) If %index is invalid, return 1 (error) - *- */ -l_ok -l_dnaaReplaceDna(L_DNAA *daa, - l_int32 index, - L_DNA *da) -{ -l_int32 n; - - PROCNAME("l_dnaaReplaceDna"); - - if (!daa) - return ERROR_INT("daa not defined", procName, 1); - if (!da) - return ERROR_INT("da not defined", procName, 1); - n = l_dnaaGetCount(daa); - if (index < 0 || index >= n) - return ERROR_INT("index not valid", procName, 1); - - l_dnaDestroy(&daa->dna[index]); - daa->dna[index] = da; - return 0; -} - - -/*! - * \brief l_dnaaGetValue() - * - * \param[in] daa - * \param[in] i index of l_dna within l_dnaa - * \param[in] j index into l_dna - * \param[out] pval double value - * \return 0 if OK, 1 on error - */ -l_ok -l_dnaaGetValue(L_DNAA *daa, - l_int32 i, - l_int32 j, - l_float64 *pval) -{ -l_int32 n; -L_DNA *da; - - PROCNAME("l_dnaaGetValue"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0.0; - if (!daa) - return ERROR_INT("daa not defined", procName, 1); - n = l_dnaaGetCount(daa); - if (i < 0 || i >= n) - return ERROR_INT("invalid index into daa", procName, 1); - da = daa->dna[i]; - if (j < 0 || j >= da->n) - return ERROR_INT("invalid index into da", procName, 1); - *pval = da->array[j]; - return 0; -} - - -/*! - * \brief l_dnaaAddNumber() - * - * \param[in] daa - * \param[in] index of l_dna within l_dnaa - * \param[in] val number to be added; stored as a double - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Adds to an existing l_dna only. - *- */ -l_ok -l_dnaaAddNumber(L_DNAA *daa, - l_int32 index, - l_float64 val) -{ -l_int32 n; -L_DNA *da; - - PROCNAME("l_dnaaAddNumber"); - - if (!daa) - return ERROR_INT("daa not defined", procName, 1); - n = l_dnaaGetCount(daa); - if (index < 0 || index >= n) - return ERROR_INT("invalid index in daa", procName, 1); - - da = l_dnaaGetDna(daa, index, L_CLONE); - l_dnaAddNumber(da, val); - l_dnaDestroy(&da); - return 0; -} - - -/*----------------------------------------------------------------------* - * Serialize Dna for I/O * - *----------------------------------------------------------------------*/ -/*! - * \brief l_dnaaRead() - * - * \param[in] filename - * \return daa, or NULL on error - */ -L_DNAA * -l_dnaaRead(const char *filename) -{ -FILE *fp; -L_DNAA *daa; - - PROCNAME("l_dnaaRead"); - - if (!filename) - return (L_DNAA *)ERROR_PTR("filename not defined", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (L_DNAA *)ERROR_PTR("stream not opened", procName, NULL); - daa = l_dnaaReadStream(fp); - fclose(fp); - if (!daa) - return (L_DNAA *)ERROR_PTR("daa not read", procName, NULL); - return daa; -} - - -/*! - * \brief l_dnaaReadStream() - * - * \param[in] fp file stream - * \return daa, or NULL on error - */ -L_DNAA * -l_dnaaReadStream(FILE *fp) -{ -l_int32 i, n, index, ret, version; -L_DNA *da; -L_DNAA *daa; - - PROCNAME("l_dnaaReadStream"); - - if (!fp) - return (L_DNAA *)ERROR_PTR("stream not defined", procName, NULL); - - ret = fscanf(fp, "\nL_Dnaa Version %d\n", &version); - if (ret != 1) - return (L_DNAA *)ERROR_PTR("not a l_dna file", procName, NULL); - if (version != DNA_VERSION_NUMBER) - return (L_DNAA *)ERROR_PTR("invalid l_dnaa version", procName, NULL); - if (fscanf(fp, "Number of L_Dna = %d\n\n", &n) != 1) - return (L_DNAA *)ERROR_PTR("invalid number of l_dna", procName, NULL); - - if (n > MaxPtrArraySize) { - L_ERROR("n = %d > %d\n", procName, n, MaxPtrArraySize); - return NULL; - } - if ((daa = l_dnaaCreate(n)) == NULL) - return (L_DNAA *)ERROR_PTR("daa not made", procName, NULL); - - for (i = 0; i < n; i++) { - if (fscanf(fp, "L_Dna[%d]:", &index) != 1) { - l_dnaaDestroy(&daa); - return (L_DNAA *)ERROR_PTR("invalid l_dna header", procName, NULL); - } - if ((da = l_dnaReadStream(fp)) == NULL) { - l_dnaaDestroy(&daa); - return (L_DNAA *)ERROR_PTR("da not made", procName, NULL); - } - l_dnaaAddDna(daa, da, L_INSERT); - } - - return daa; -} - - -/*! - * \brief l_dnaaWrite() - * - * \param[in] filename - * \param[in] daa - * \return 0 if OK, 1 on error - */ -l_ok -l_dnaaWrite(const char *filename, - L_DNAA *daa) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("l_dnaaWrite"); - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!daa) - return ERROR_INT("daa not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "w")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = l_dnaaWriteStream(fp, daa); - fclose(fp); - if (ret) - return ERROR_INT("daa not written to stream", procName, 1); - return 0; -} - - -/*! - * \brief l_dnaaWriteStream() - * - * \param[in] fp file stream - * \param[in] daa - * \return 0 if OK, 1 on error - */ -l_ok -l_dnaaWriteStream(FILE *fp, - L_DNAA *daa) -{ -l_int32 i, n; -L_DNA *da; - - PROCNAME("l_dnaaWriteStream"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!daa) - return ERROR_INT("daa not defined", procName, 1); - - n = l_dnaaGetCount(daa); - fprintf(fp, "\nL_Dnaa Version %d\n", DNA_VERSION_NUMBER); - fprintf(fp, "Number of L_Dna = %d\n\n", n); - for (i = 0; i < n; i++) { - if ((da = l_dnaaGetDna(daa, i, L_CLONE)) == NULL) - return ERROR_INT("da not found", procName, 1); - fprintf(fp, "L_Dna[%d]:", i); - l_dnaWriteStream(fp, da); - l_dnaDestroy(&da); - } - - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dnafunc1.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dnafunc1.c deleted file mode 100644 index aba2528d..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dnafunc1.c +++ /dev/null @@ -1,408 +0,0 @@ -/*====================================================================* - - 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 dnafunc1.c - *
- * - * Rearrangements - * l_int32 *l_dnaJoin() - * l_int32 *l_dnaaFlattenToDna() - * - * Conversion between numa and dna - * NUMA *l_dnaConvertToNuma() - * L_DNA *numaConvertToDna() - * - * Set operations using aset (rbtree) - * L_DNA *l_dnaUnionByAset() - * L_DNA *l_dnaRemoveDupsByAset() - * L_DNA *l_dnaIntersectionByAset() - * L_ASET *l_asetCreateFromDna() - * - * Miscellaneous operations - * L_DNA *l_dnaDiffAdjValues() - * - * - * This file contains an implementation on sets of doubles (or integers) - * that uses an underlying tree (rbtree). The keys stored in the tree - * are simply the double array values in the dna. Use of a DnaHash - * is typically more efficient, with O(1) in lookup and insertion. - * - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) istart < 0 is taken to mean 'read from the start' (istart = 0) - * (2) iend < 0 means 'read to the end' - * (3) if das == NULL, this is a no-op - *- */ -l_ok -l_dnaJoin(L_DNA *dad, - L_DNA *das, - l_int32 istart, - l_int32 iend) -{ -l_int32 n, i; -l_float64 val; - - PROCNAME("l_dnaJoin"); - - if (!dad) - return ERROR_INT("dad not defined", procName, 1); - if (!das) - return 0; - - if (istart < 0) - istart = 0; - n = l_dnaGetCount(das); - if (iend < 0 || iend >= n) - iend = n - 1; - if (istart > iend) - return ERROR_INT("istart > iend; nothing to add", procName, 1); - - for (i = istart; i <= iend; i++) { - l_dnaGetDValue(das, i, &val); - l_dnaAddNumber(dad, val); - } - - return 0; -} - - -/*! - * \brief l_dnaaFlattenToDna() - * - * \param[in] daa - * \return dad, or NULL on error - * - *
- * Notes: - * (1) This 'flattens' the dnaa to a dna, by joining successively - * each dna in the dnaa. - * (2) It leaves the input dnaa unchanged. - *- */ -L_DNA * -l_dnaaFlattenToDna(L_DNAA *daa) -{ -l_int32 i, nalloc; -L_DNA *da, *dad; -L_DNA **array; - - PROCNAME("l_dnaaFlattenToDna"); - - if (!daa) - return (L_DNA *)ERROR_PTR("daa not defined", procName, NULL); - - nalloc = daa->nalloc; - array = daa->dna; - dad = l_dnaCreate(0); - for (i = 0; i < nalloc; i++) { - da = array[i]; - if (!da) continue; - l_dnaJoin(dad, da, 0, -1); - } - - return dad; -} - - -/*----------------------------------------------------------------------* - * Conversion between numa and dna * - *----------------------------------------------------------------------*/ -/*! - * \brief l_dnaConvertToNuma() - * - * \param[in] da - * \return na, or NULL on error - */ -NUMA * -l_dnaConvertToNuma(L_DNA *da) -{ -l_int32 i, n; -l_float64 val; -NUMA *na; - - PROCNAME("l_dnaConvertToNuma"); - - if (!da) - return (NUMA *)ERROR_PTR("da not defined", procName, NULL); - - n = l_dnaGetCount(da); - na = numaCreate(n); - for (i = 0; i < n; i++) { - l_dnaGetDValue(da, i, &val); - numaAddNumber(na, val); - } - return na; -} - - -/*! - * \brief numaConvertToDna - * - * \param[in] na - * \return da, or NULL on error - */ -L_DNA * -numaConvertToDna(NUMA *na) -{ -l_int32 i, n; -l_float32 val; -L_DNA *da; - - PROCNAME("numaConvertToDna"); - - if (!na) - return (L_DNA *)ERROR_PTR("na not defined", procName, NULL); - - n = numaGetCount(na); - da = l_dnaCreate(n); - for (i = 0; i < n; i++) { - numaGetFValue(na, i, &val); - l_dnaAddNumber(da, val); - } - return da; -} - - -/*----------------------------------------------------------------------* - * Set operations using aset (rbtree) * - *----------------------------------------------------------------------*/ -/*! - * \brief l_dnaUnionByAset() - * - * \param[in] da1, da2 - * \return dad with the union of the set of numbers, or NULL on error - * - *
- * Notes: - * (1) See sarrayUnionByAset() for the approach. - * (2) Here, the key in building the sorted tree is the number itself. - * (3) Operations using an underlying tree are O(nlogn), which is - * typically less efficient than hashing, which is O(n). - *- */ -L_DNA * -l_dnaUnionByAset(L_DNA *da1, - L_DNA *da2) -{ -L_DNA *da3, *dad; - - PROCNAME("l_dnaUnionByAset"); - - if (!da1) - return (L_DNA *)ERROR_PTR("da1 not defined", procName, NULL); - if (!da2) - return (L_DNA *)ERROR_PTR("da2 not defined", procName, NULL); - - /* Join */ - da3 = l_dnaCopy(da1); - l_dnaJoin(da3, da2, 0, -1); - - /* Eliminate duplicates */ - dad = l_dnaRemoveDupsByAset(da3); - l_dnaDestroy(&da3); - return dad; -} - - -/*! - * \brief l_dnaRemoveDupsByAset() - * - * \param[in] das - * \return dad with duplicates removed, or NULL on error - */ -L_DNA * -l_dnaRemoveDupsByAset(L_DNA *das) -{ -l_int32 i, n; -l_float64 val; -L_DNA *dad; -L_ASET *set; -RB_TYPE key; - - PROCNAME("l_dnaRemoveDupsByAset"); - - if (!das) - return (L_DNA *)ERROR_PTR("das not defined", procName, NULL); - - set = l_asetCreate(L_FLOAT_TYPE); - dad = l_dnaCreate(0); - n = l_dnaGetCount(das); - for (i = 0; i < n; i++) { - l_dnaGetDValue(das, i, &val); - key.ftype = val; - if (!l_asetFind(set, key)) { - l_dnaAddNumber(dad, val); - l_asetInsert(set, key); - } - } - - l_asetDestroy(&set); - return dad; -} - - -/*! - * \brief l_dnaIntersectionByAset() - * - * \param[in] da1, da2 - * \return dad with the intersection of the two arrays, or NULL on error - * - *
- * Notes: - * (1) See sarrayIntersection() for the approach. - * (2) Here, the key in building the sorted tree is the number itself. - * (3) Operations using an underlying tree are O(nlogn), which is - * typically less efficient than hashing, which is O(n). - *- */ -L_DNA * -l_dnaIntersectionByAset(L_DNA *da1, - L_DNA *da2) -{ -l_int32 n1, n2, i, n; -l_float64 val; -L_ASET *set1, *set2; -RB_TYPE key; -L_DNA *da_small, *da_big, *dad; - - PROCNAME("l_dnaIntersectionByAset"); - - if (!da1) - return (L_DNA *)ERROR_PTR("da1 not defined", procName, NULL); - if (!da2) - return (L_DNA *)ERROR_PTR("da2 not defined", procName, NULL); - - /* Put the elements of the largest array into a set */ - n1 = l_dnaGetCount(da1); - n2 = l_dnaGetCount(da2); - da_small = (n1 < n2) ? da1 : da2; /* do not destroy da_small */ - da_big = (n1 < n2) ? da2 : da1; /* do not destroy da_big */ - set1 = l_asetCreateFromDna(da_big); - - /* Build up the intersection of floats */ - dad = l_dnaCreate(0); - n = l_dnaGetCount(da_small); - set2 = l_asetCreate(L_FLOAT_TYPE); - for (i = 0; i < n; i++) { - l_dnaGetDValue(da_small, i, &val); - key.ftype = val; - if (l_asetFind(set1, key) && !l_asetFind(set2, key)) { - l_dnaAddNumber(dad, val); - l_asetInsert(set2, key); - } - } - - l_asetDestroy(&set1); - l_asetDestroy(&set2); - return dad; -} - - -/*! - * \brief l_asetCreateFromDna() - * - * \param[in] da source dna - * \return set using the doubles in %da as keys - */ -L_ASET * -l_asetCreateFromDna(L_DNA *da) -{ -l_int32 i, n; -l_float64 val; -L_ASET *set; -RB_TYPE key; - - PROCNAME("l_asetCreateFromDna"); - - if (!da) - return (L_ASET *)ERROR_PTR("da not defined", procName, NULL); - - set = l_asetCreate(L_FLOAT_TYPE); - n = l_dnaGetCount(da); - for (i = 0; i < n; i++) { - l_dnaGetDValue(da, i, &val); - key.ftype = val; - l_asetInsert(set, key); - } - - return set; -} - - -/*----------------------------------------------------------------------* - * Miscellaneous operations * - *----------------------------------------------------------------------*/ -/*! - * \brief l_dnaDiffAdjValues() - * - * \param[in] das input l_dna - * \return dad of difference values val[i+1] - val[i], - * or NULL on error - */ -L_DNA * -l_dnaDiffAdjValues(L_DNA *das) -{ -l_int32 i, n, prev, cur; -L_DNA *dad; - - PROCNAME("l_dnaDiffAdjValues"); - - if (!das) - return (L_DNA *)ERROR_PTR("das not defined", procName, NULL); - n = l_dnaGetCount(das); - dad = l_dnaCreate(n - 1); - prev = 0; - for (i = 1; i < n; i++) { - l_dnaGetIValue(das, i, &cur); - l_dnaAddNumber(dad, cur - prev); - prev = cur; - } - return dad; -} - diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dnahash.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dnahash.c deleted file mode 100644 index a1c5a452..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dnahash.c +++ /dev/null @@ -1,593 +0,0 @@ -/*====================================================================* - - 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 dnahash.c - *
- * - * DnaHash creation, destruction - * L_DNAHASH *l_dnaHashCreate() - * void l_dnaHashDestroy() - * - * DnaHash: Accessors and modifiers * - * l_int32 l_dnaHashGetCount() - * l_int32 l_dnaHashGetTotalCount() - * L_DNA *l_dnaHashGetDna() - * void l_dnaHashAdd() - * - * DnaHash: Operations on Dna - * L_DNAHASH *l_dnaHashCreateFromDna() - * l_int32 l_dnaRemoveDupsByHash() - * l_int32 l_dnaMakeHistoByHash() - * L_DNA *l_dnaIntersectionByHash() - * l_int32 l_dnaFindValByHash() - * - * (1) The DnaHash is an array of Dna. It is useful for fast - * storage and lookup for sets and maps. If the set or map - * is on a Dna itself, the hash is a simple function that - * maps a double to a l_uint64; otherwise the function will - * map a string or a (x,y) point to a l_uint64. The result of - * the map is the "key", which is then used with the mod - * function to select which Dna array is to be used. The - * number of arrays in a DnaHash should be a prime number. - * If there are N items, we set up the DnaHash array to have - * approximately N/20 Dna, so the average size of these arrays - * will be about 20 when fully populated. The number 20 was - * found empirically to be in a broad maximum of efficiency. - * (2) Note that the word "hash" is overloaded. There are actually - * two hashing steps: the first hashes the object to a l_uint64, - * called the "key", and the second uses the mod function to - * "hash" the "key" to the index of a particular Dna in the - * DnaHash array. - * (3) Insertion and lookup time for DnaHash is O(1). Hash collisions - * are easily handled (we expect an average of 20 for each key), - * so we can use simple (fast) hash functions: we deal with - * collisions by storing an array for each hash key. - * This can be contrasted with using rbtree for sets and - * maps, where insertion and lookup are O(logN) and hash functions - * are slower because they must be good enough (i.e, random - * enough with arbitrary input) to avoid collisions. - * (4) Hash functions that map points, strings and floats to l_uint64 - * are given in utils.c. - * (5) The use of the DnaHash (and RBTree) with strings and - * (x,y) points can be found in string2.c and ptafunc2.c, rsp. - * This file has similar hash set functions, using DnaHash on - * two input Dna, for removing duplicates and finding the - * intersection. It also uses DnaHash as a hash map to find - * a histogram of counts from an input Dna. - * (6) Comparisons in running time, between DnaHash and RBTree, for - * large sets of strings and points, are given in prog/hashtest.c. - * (7) This is a very simple implementation, that expects that you - * know approximately (i.e., within a factor of 2 or 3) how many - * items are to be stored when you initialize the DnaHash. - * (It would be nice to modify the l_dnaHashAdd() function - * to increase the number of bins when the average occupation - * exceeds 40 or so.) - * (8) Useful rule of thumb for hashing collisions: - * For a random hashing function (say, from strings to l_uint64), - * the probability of a collision increases as N^2 for N much - * less than 2^32. The quadratic behavior switches over to - * approaching 1.0 around 2^32, which is the square root of 2^64. - * So, for example, if you have 10^7 strings, the probability - * of a single collision using an l_uint64 key is on the order of - * (10^7/10^9)^2 ~ 10^-4. - * For a million strings you don't need to worry about collisons - * (~10-6 probability), and for most applications can use the - * RBTree (sorting) implementation with confidence. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) Actual dna are created only as required by l_dnaHashAdd() - *- */ -L_DNAHASH * -l_dnaHashCreate(l_int32 nbuckets, - l_int32 initsize) -{ -L_DNAHASH *dahash; - - PROCNAME("l_dnaHashCreate"); - - if (nbuckets <= 0) - return (L_DNAHASH *)ERROR_PTR("negative hash size", procName, NULL); - dahash = (L_DNAHASH *)LEPT_CALLOC(1, sizeof(L_DNAHASH)); - if ((dahash->dna = (L_DNA **)LEPT_CALLOC(nbuckets, sizeof(L_DNA *))) - == NULL) { - LEPT_FREE(dahash); - return (L_DNAHASH *)ERROR_PTR("dna ptr array not made", procName, NULL); - } - - dahash->nbuckets = nbuckets; - dahash->initsize = initsize; - return dahash; -} - - -/*! - * \brief l_dnaHashDestroy() - * - * \param[in,out] pdahash will be set to null before returning - * \return void - */ -void -l_dnaHashDestroy(L_DNAHASH **pdahash) -{ -L_DNAHASH *dahash; -l_int32 i; - - PROCNAME("l_dnaHashDestroy"); - - if (pdahash == NULL) { - L_WARNING("ptr address is NULL!\n", procName); - return; - } - - if ((dahash = *pdahash) == NULL) - return; - - for (i = 0; i < dahash->nbuckets; i++) - l_dnaDestroy(&dahash->dna[i]); - LEPT_FREE(dahash->dna); - LEPT_FREE(dahash); - *pdahash = NULL; -} - - -/*--------------------------------------------------------------------------* - * Dna hash: Accessors and modifiers * - *--------------------------------------------------------------------------*/ -/*! - * \brief l_dnaHashGetCount() - * - * \param[in] dahash - * \return nbuckets allocated, or 0 on error - */ -l_int32 -l_dnaHashGetCount(L_DNAHASH *dahash) -{ - - PROCNAME("l_dnaHashGetCount"); - - if (!dahash) - return ERROR_INT("dahash not defined", procName, 0); - return dahash->nbuckets; -} - - -/*! - * \brief l_dnaHashGetTotalCount() - * - * \param[in] dahash - * \return n number of numbers in all dna, or 0 on error - */ -l_int32 -l_dnaHashGetTotalCount(L_DNAHASH *dahash) -{ -l_int32 i, n; -L_DNA *da; - - PROCNAME("l_dnaHashGetTotalCount"); - - if (!dahash) - return ERROR_INT("dahash not defined", procName, 0); - - for (i = 0, n = 0; i < dahash->nbuckets; i++) { - da = l_dnaHashGetDna(dahash, i, L_NOCOPY); - if (da) - n += l_dnaGetCount(da); - } - - return n; -} - - -/*! - * \brief l_dnaHashGetDna() - * - * \param[in] dahash - * \param[in] key key to be hashed into a bucket number - * \param[in] copyflag L_NOCOPY, L_COPY, L_CLONE - * \return ptr to dna - */ -L_DNA * -l_dnaHashGetDna(L_DNAHASH *dahash, - l_uint64 key, - l_int32 copyflag) -{ -l_int32 bucket; -L_DNA *da; - - PROCNAME("l_dnaHashGetDna"); - - if (!dahash) - return (L_DNA *)ERROR_PTR("dahash not defined", procName, NULL); - bucket = key % dahash->nbuckets; - da = dahash->dna[bucket]; - if (da) { - if (copyflag == L_NOCOPY) - return da; - else if (copyflag == L_COPY) - return l_dnaCopy(da); - else - return l_dnaClone(da); - } - else - return NULL; -} - - -/*! - * \brief l_dnaHashAdd() - * - * \param[in] dahash - * \param[in] key key to be hashed into a bucket number - * \param[in] value float value to be appended to the specific dna - * \return 0 if OK; 1 on error - */ -l_ok -l_dnaHashAdd(L_DNAHASH *dahash, - l_uint64 key, - l_float64 value) -{ -l_int32 bucket; -L_DNA *da; - - PROCNAME("l_dnaHashAdd"); - - if (!dahash) - return ERROR_INT("dahash not defined", procName, 1); - bucket = key % dahash->nbuckets; - da = dahash->dna[bucket]; - if (!da) { - if ((da = l_dnaCreate(dahash->initsize)) == NULL) - return ERROR_INT("da not made", procName, 1); - dahash->dna[bucket] = da; - } - l_dnaAddNumber(da, value); - return 0; -} - - -/*--------------------------------------------------------------------------* - * DnaHash: Operations on Dna * - *--------------------------------------------------------------------------*/ -/*! - * \brief l_dnaHashCreateFromDna() - * - * \param[in] da - * \return dahash if OK; 1 on error - * - *
- * Notes: - * (1) The values stored in the %dahash are indices into %da; - * %dahash has no use without %da. - *- */ -L_DNAHASH * -l_dnaHashCreateFromDna(L_DNA *da) -{ -l_int32 i, n; -l_uint32 nsize; -l_uint64 key; -l_float64 val; -L_DNAHASH *dahash; - - PROCNAME("l_dnaHashCreateFromDna"); - - if (!da) - return (L_DNAHASH *)ERROR_PTR("da not defined", procName, NULL); - - n = l_dnaGetCount(da); - findNextLargerPrime(n / 20, &nsize); /* buckets in hash table */ - - dahash = l_dnaHashCreate(nsize, 8); - for (i = 0; i < n; i++) { - l_dnaGetDValue(da, i, &val); - l_hashFloat64ToUint64(nsize, val, &key); - l_dnaHashAdd(dahash, key, (l_float64)i); - } - - return dahash; -} - - -/*! - * \brief l_dnaRemoveDupsByHash() - * - * \param[in] das - * \param[out] pdad hash set - * \param[out] pdahash [optional] dnahash used for lookup - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) Generates a dna with unique values. - * (2) The dnahash is built up with dad to assure uniqueness. - * It can be used to find if an element is in the set: - * l_dnaFindValByHash(dad, dahash, val, &index) - *- */ -l_ok -l_dnaRemoveDupsByHash(L_DNA *das, - L_DNA **pdad, - L_DNAHASH **pdahash) -{ -l_int32 i, n, index, items; -l_uint32 nsize; -l_uint64 key; -l_float64 val; -L_DNA *dad; -L_DNAHASH *dahash; - - PROCNAME("l_dnaRemoveDupsByHash"); - - if (pdahash) *pdahash = NULL; - if (!pdad) - return ERROR_INT("&dad not defined", procName, 1); - *pdad = NULL; - if (!das) - return ERROR_INT("das not defined", procName, 1); - - n = l_dnaGetCount(das); - findNextLargerPrime(n / 20, &nsize); /* buckets in hash table */ - dahash = l_dnaHashCreate(nsize, 8); - dad = l_dnaCreate(n); - *pdad = dad; - for (i = 0, items = 0; i < n; i++) { - l_dnaGetDValue(das, i, &val); - l_dnaFindValByHash(dad, dahash, val, &index); - if (index < 0) { /* not found */ - l_hashFloat64ToUint64(nsize, val, &key); - l_dnaHashAdd(dahash, key, (l_float64)items); - l_dnaAddNumber(dad, val); - items++; - } - } - - if (pdahash) - *pdahash = dahash; - else - l_dnaHashDestroy(&dahash); - return 0; -} - - -/*! - * \brief l_dnaMakeHistoByHash() - * - * \param[in] das - * \param[out] pdahash hash map: val --> index - * \param[out] pdav array of values: index --> val - * \param[out] pdac histo array of counts: index --> count - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) Generates and returns a dna of occurrences (histogram), - * an aligned dna of values, and an associated hashmap. - * The hashmap takes %dav and a value, and points into the - * histogram in %dac. - * (2) The dna of values, %dav, is aligned with the histogram %dac, - * and is needed for fast lookup. It is a hash set, because - * the values are unique. - * (3) Lookup is simple: - * l_dnaFindValByHash(dav, dahash, val, &index); - * if (index >= 0) - * l_dnaGetIValue(dac, index, &icount); - * else - * icount = 0; - *- */ -l_ok -l_dnaMakeHistoByHash(L_DNA *das, - L_DNAHASH **pdahash, - L_DNA **pdav, - L_DNA **pdac) -{ -l_int32 i, n, nitems, index, count; -l_uint32 nsize; -l_uint64 key; -l_float64 val; -L_DNA *dac, *dav; -L_DNAHASH *dahash; - - PROCNAME("l_dnaMakeHistoByHash"); - - if (pdahash) *pdahash = NULL; - if (pdac) *pdac = NULL; - if (pdav) *pdav = NULL; - if (!pdahash || !pdac || !pdav) - return ERROR_INT("&dahash, &dac, &dav not all defined", procName, 1); - if (!das) - return ERROR_INT("das not defined", procName, 1); - if ((n = l_dnaGetCount(das)) == 0) - return ERROR_INT("no data in das", procName, 1); - - findNextLargerPrime(n / 20, &nsize); /* buckets in hash table */ - dahash = l_dnaHashCreate(nsize, 8); - dac = l_dnaCreate(n); /* histogram */ - dav = l_dnaCreate(n); /* the values */ - for (i = 0, nitems = 0; i < n; i++) { - l_dnaGetDValue(das, i, &val); - /* Is this value already stored in dav? */ - l_dnaFindValByHash(dav, dahash, val, &index); - if (index >= 0) { /* found */ - l_dnaGetIValue(dac, (l_float64)index, &count); - l_dnaSetValue(dac, (l_float64)index, count + 1); - } else { /* not found */ - l_hashFloat64ToUint64(nsize, val, &key); - l_dnaHashAdd(dahash, key, (l_float64)nitems); - l_dnaAddNumber(dav, val); - l_dnaAddNumber(dac, 1); - nitems++; - } - } - - *pdahash = dahash; - *pdac = dac; - *pdav = dav; - return 0; -} - - -/*! - * \brief l_dnaIntersectionByHash() - * - * \param[in] da1, da2 - * \return dad intersection of the number arrays, or NULL on error - * - *
- * Notes: - * (1) This uses the same method for building the intersection set - * as ptaIntersectionByHash() and sarrayIntersectionByHash(). - *- */ -L_DNA * -l_dnaIntersectionByHash(L_DNA *da1, - L_DNA *da2) -{ -l_int32 n1, n2, nsmall, nbuckets, i, index1, index2; -l_uint32 nsize2; -l_uint64 key; -l_float64 val; -L_DNAHASH *dahash1, *dahash2; -L_DNA *da_small, *da_big, *dad; - - PROCNAME("l_dnaIntersectionByHash"); - - if (!da1) - return (L_DNA *)ERROR_PTR("da1 not defined", procName, NULL); - if (!da2) - return (L_DNA *)ERROR_PTR("da2 not defined", procName, NULL); - - /* Put the elements of the biggest array into a dnahash */ - n1 = l_dnaGetCount(da1); - n2 = l_dnaGetCount(da2); - da_small = (n1 < n2) ? da1 : da2; /* do not destroy da_small */ - da_big = (n1 < n2) ? da2 : da1; /* do not destroy da_big */ - dahash1 = l_dnaHashCreateFromDna(da_big); - - /* Build up the intersection of numbers. Add to %dad - * if the number is in da_big (using dahash1) but hasn't - * yet been seen in the traversal of da_small (using dahash2). */ - dad = l_dnaCreate(0); - nsmall = l_dnaGetCount(da_small); - findNextLargerPrime(nsmall / 20, &nsize2); /* buckets in hash table */ - dahash2 = l_dnaHashCreate(nsize2, 0); - nbuckets = l_dnaHashGetCount(dahash2); - for (i = 0; i < nsmall; i++) { - l_dnaGetDValue(da_small, i, &val); - l_dnaFindValByHash(da_big, dahash1, val, &index1); - if (index1 >= 0) { /* found */ - l_dnaFindValByHash(da_small, dahash2, val, &index2); - if (index2 == -1) { /* not found */ - l_dnaAddNumber(dad, val); - l_hashFloat64ToUint64(nbuckets, val, &key); - l_dnaHashAdd(dahash2, key, (l_float64)i); - } - } - } - - l_dnaHashDestroy(&dahash1); - l_dnaHashDestroy(&dahash2); - return dad; -} - - -/*! - * \brief l_dnaFindValByHash() - * - * \param[in] da - * \param[in] dahash containing indices into %da - * \param[in] val searching for this number in %da - * \param[out] pindex index into da if found; -1 otherwise - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) Algo: hash %val into a key; hash the key to get the dna - * in %dahash (that holds indices into %da); traverse - * the dna of indices looking for %val in %da. - *- */ -l_ok -l_dnaFindValByHash(L_DNA *da, - L_DNAHASH *dahash, - l_float64 val, - l_int32 *pindex) -{ -l_int32 i, nbuckets, nvals, indexval; -l_float64 vali; -l_uint64 key; -L_DNA *da1; - - PROCNAME("l_dnaFindValByHash"); - - if (!pindex) - return ERROR_INT("&index not defined", procName, 1); - *pindex = -1; - if (!da) - return ERROR_INT("da not defined", procName, 1); - if (!dahash) - return ERROR_INT("dahash not defined", procName, 1); - - nbuckets = l_dnaHashGetCount(dahash); - l_hashFloat64ToUint64(nbuckets, val, &key); - da1 = l_dnaHashGetDna(dahash, key, L_NOCOPY); - if (!da1) return 0; - - /* Run through da1, looking for this %val */ - nvals = l_dnaGetCount(da1); - for (i = 0; i < nvals; i++) { - l_dnaGetIValue(da1, i, &indexval); - l_dnaGetDValue(da, indexval, &vali); - if (val == vali) { - *pindex = indexval; - return 0; - } - } - - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dwacomb.2.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dwacomb.2.c deleted file mode 100644 index 4f48897d..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dwacomb.2.c +++ /dev/null @@ -1,299 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -/*! - * Top-level fast binary morphology with auto-generated sels - * - * PIX *pixMorphDwa_2() - * PIX *pixFMorphopGen_2() - */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This simply adds a border, calls the appropriate - * pixFMorphopGen_*(), and removes the border. - * See the notes for that function. - * (2) The size of the border depends on the operation - * and the boundary conditions. - *- */ -PIX * -pixMorphDwa_2(PIX *pixd, - PIX *pixs, - l_int32 operation, - char *selname) -{ -l_int32 bordercolor, bordersize; -PIX *pixt1, *pixt2, *pixt3; - - PROCNAME("pixMorphDwa_2"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, pixd); - - /* Set the border size */ - bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); - bordersize = 32; - if (bordercolor == 0 && operation == L_MORPH_CLOSE) - bordersize += 32; - - pixt1 = pixAddBorder(pixs, bordersize, 0); - pixt2 = pixFMorphopGen_2(NULL, pixt1, operation, selname); - pixt3 = pixRemoveBorder(pixt2, bordersize); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - - if (!pixd) - return pixt3; - - pixCopy(pixd, pixt3); - pixDestroy(&pixt3); - return pixd; -} - - -/*! - * \brief pixFMorphopGen_2() - * - * \param[in] pixd usual 3 choices: null, == pixs, != pixs - * \param[in] pixs 1 bpp - * \param[in] operation L_MORPH_DILATE, L_MORPH_ERODE, - * L_MORPH_OPEN, L_MORPH_CLOSE - * \param[in] sel name - * \return pixd - * - *
- * Notes: - * (1) This is a dwa operation, and the Sels must be limited in - * size to not more than 31 pixels about the origin. - * (2) A border of appropriate size (32 pixels, or 64 pixels - * for safe closing with asymmetric b.c.) must be added before - * this function is called. - * (3) This handles all required setting of the border pixels - * before erosion and dilation. - * (4) The closing operation is safe; no pixels can be removed - * near the boundary. - *- */ -PIX * -pixFMorphopGen_2(PIX *pixd, - PIX *pixs, - l_int32 operation, - char *selname) -{ -l_int32 i, index, found, w, h, wpls, wpld, bordercolor, erodeop, borderop; -l_uint32 *datad, *datas, *datat; -PIX *pixt; - - PROCNAME("pixFMorphopGen_2"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, pixd); - - /* Get boundary colors to use */ - bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); - if (bordercolor == 1) - erodeop = PIX_SET; - else - erodeop = PIX_CLR; - - found = FALSE; - for (i = 0; i < NUM_SELS_GENERATED; i++) { - if (strcmp(selname, SEL_NAMES[i]) == 0) { - found = TRUE; - index = 2 * i; - break; - } - } - if (found == FALSE) - return (PIX *)ERROR_PTR("sel index not found", procName, pixd); - - if (!pixd) { - if ((pixd = pixCreateTemplate(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - else /* for in-place or pre-allocated */ - pixResizeImageData(pixd, pixs); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - - /* The images must be surrounded, in advance, with a border of - * size 32 pixels (or 64, for closing), that we'll read from. - * Fabricate a "proper" image as the subimage within the 32 - * pixel border, having the following parameters: */ - w = pixGetWidth(pixs) - 64; - h = pixGetHeight(pixs) - 64; - datas = pixGetData(pixs) + 32 * wpls + 1; - datad = pixGetData(pixd) + 32 * wpld + 1; - - if (operation == L_MORPH_DILATE || operation == L_MORPH_ERODE) { - borderop = PIX_CLR; - if (operation == L_MORPH_ERODE) { - borderop = erodeop; - index++; - } - if (pixd == pixs) { /* in-place; generate a temp image */ - if ((pixt = pixCopy(NULL, pixs)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, pixd); - datat = pixGetData(pixt) + 32 * wpls + 1; - pixSetOrClearBorder(pixt, 32, 32, 32, 32, borderop); - fmorphopgen_low_2(datad, w, h, wpld, datat, wpls, index); - pixDestroy(&pixt); - } - else { /* not in-place */ - pixSetOrClearBorder(pixs, 32, 32, 32, 32, borderop); - fmorphopgen_low_2(datad, w, h, wpld, datas, wpls, index); - } - } - else { /* opening or closing; generate a temp image */ - if ((pixt = pixCreateTemplate(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, pixd); - datat = pixGetData(pixt) + 32 * wpls + 1; - if (operation == L_MORPH_OPEN) { - pixSetOrClearBorder(pixs, 32, 32, 32, 32, erodeop); - fmorphopgen_low_2(datat, w, h, wpls, datas, wpls, index+1); - pixSetOrClearBorder(pixt, 32, 32, 32, 32, PIX_CLR); - fmorphopgen_low_2(datad, w, h, wpld, datat, wpls, index); - } - else { /* closing */ - pixSetOrClearBorder(pixs, 32, 32, 32, 32, PIX_CLR); - fmorphopgen_low_2(datat, w, h, wpls, datas, wpls, index); - pixSetOrClearBorder(pixt, 32, 32, 32, 32, erodeop); - fmorphopgen_low_2(datad, w, h, wpld, datat, wpls, index+1); - } - pixDestroy(&pixt); - } - - return pixd; -} - diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dwacomblow.2.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dwacomblow.2.c deleted file mode 100644 index e47684ff..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/dwacomblow.2.c +++ /dev/null @@ -1,4970 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -/*! - * Low-level fast binary morphology with auto-generated sels - * - * Dispatcher: - * l_int32 fmorphopgen_low_2() - * - * Static Low-level: - * void fdilate_2_*() - * void ferode_2_*() - */ - -#ifdef HAVE_CONFIG_H -#include
- * - * Sobel edge detecting filter - * PIX *pixSobelEdgeFilter() - * - * Two-sided edge gradient filter - * PIX *pixTwoSidedEdgeFilter() - * - * Measurement of edge smoothness - * l_int32 pixMeasureEdgeSmoothness() - * NUMA *pixGetEdgeProfile() - * l_int32 pixGetLastOffPixelInRun() - * l_int32 pixGetLastOnPixelInRun() - * - * - * The Sobel edge detector uses these two simple gradient filters. - * - * 1 2 1 1 0 -1 - * 0 0 0 2 0 -2 - * -1 -2 -1 1 0 -1 - * - * (horizontal) (vertical) - * - * To use both the vertical and horizontal filters, set the orientation - * flag to L_ALL_EDGES; this sums the abs. value of their outputs, - * clipped to 255. - * - * See comments below for displaying the resulting image with - * the edges dark, both for 8 bpp and 1 bpp. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) Invert pixd to see larger gradients as darker (grayscale). - * (2) To generate a binary image of the edges, threshold - * the result using pixThresholdToBinary(). If the high - * edge values are to be fg (1), invert after running - * pixThresholdToBinary(). - * (3) Label the pixels as follows: - * 1 4 7 - * 2 5 8 - * 3 6 9 - * Read the data incrementally across the image and unroll - * the loop. - * (4) This runs at about 45 Mpix/sec on a 3 GHz processor. - *- */ -PIX * -pixSobelEdgeFilter(PIX *pixs, - l_int32 orientflag) -{ -l_int32 w, h, d, i, j, wplt, wpld, gx, gy, vald; -l_int32 val1, val2, val3, val4, val5, val6, val7, val8, val9; -l_uint32 *datat, *linet, *datad, *lined; -PIX *pixt, *pixd; - - PROCNAME("pixSobelEdgeFilter"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (orientflag != L_HORIZONTAL_EDGES && orientflag != L_VERTICAL_EDGES && - orientflag != L_ALL_EDGES) - return (PIX *)ERROR_PTR("invalid orientflag", procName, NULL); - - /* Add 1 pixel (mirrored) to each side of the image. */ - if ((pixt = pixAddMirroredBorder(pixs, 1, 1, 1, 1)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, NULL); - - /* Compute filter output at each location. */ - pixd = pixCreateTemplate(pixs); - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - linet = datat + i * wplt; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - if (j == 0) { /* start a new row */ - val1 = GET_DATA_BYTE(linet, j); - val2 = GET_DATA_BYTE(linet + wplt, j); - val3 = GET_DATA_BYTE(linet + 2 * wplt, j); - val4 = GET_DATA_BYTE(linet, j + 1); - val5 = GET_DATA_BYTE(linet + wplt, j + 1); - val6 = GET_DATA_BYTE(linet + 2 * wplt, j + 1); - val7 = GET_DATA_BYTE(linet, j + 2); - val8 = GET_DATA_BYTE(linet + wplt, j + 2); - val9 = GET_DATA_BYTE(linet + 2 * wplt, j + 2); - } else { /* shift right by 1 pixel; update incrementally */ - val1 = val4; - val2 = val5; - val3 = val6; - val4 = val7; - val5 = val8; - val6 = val9; - val7 = GET_DATA_BYTE(linet, j + 2); - val8 = GET_DATA_BYTE(linet + wplt, j + 2); - val9 = GET_DATA_BYTE(linet + 2 * wplt, j + 2); - } - if (orientflag == L_HORIZONTAL_EDGES) - vald = L_ABS(val1 + 2 * val4 + val7 - - val3 - 2 * val6 - val9) >> 3; - else if (orientflag == L_VERTICAL_EDGES) - vald = L_ABS(val1 + 2 * val2 + val3 - val7 - - 2 * val8 - val9) >> 3; - else { /* L_ALL_EDGES */ - gx = L_ABS(val1 + 2 * val2 + val3 - val7 - - 2 * val8 - val9) >> 3; - gy = L_ABS(val1 + 2 * val4 + val7 - - val3 - 2 * val6 - val9) >> 3; - vald = L_MIN(255, gx + gy); - } - SET_DATA_BYTE(lined, j, vald); - } - } - - pixDestroy(&pixt); - return pixd; -} - - -/*----------------------------------------------------------------------* - * Two-sided edge gradient filter * - *----------------------------------------------------------------------*/ -/*! - * \brief pixTwoSidedEdgeFilter() - * - * \param[in] pixs 8 bpp; no colormap - * \param[in] orientflag L_HORIZONTAL_EDGES, L_VERTICAL_EDGES - * \return pixd 8 bpp, edges are brighter, or NULL on error - * - *
- * Notes: - * (1) For detecting vertical edges, this considers the - * difference of the central pixel from those on the left - * and right. For situations where the gradient is the same - * sign on both sides, this computes and stores the minimum - * (absolute value of the) difference. The reason for - * checking the sign is that we are looking for pixels within - * a transition. By contrast, for single pixel noise, the pixel - * value is either larger than or smaller than its neighbors, - * so the gradient would change direction on each side. Horizontal - * edges are handled similarly, looking for vertical gradients. - * (2) To generate a binary image of the edges, threshold - * the result using pixThresholdToBinary(). If the high - * edge values are to be fg (1), invert after running - * pixThresholdToBinary(). - * (3) This runs at about 60 Mpix/sec on a 3 GHz processor. - * It is about 30% faster than Sobel, and the results are - * similar. - *- */ -PIX * -pixTwoSidedEdgeFilter(PIX *pixs, - l_int32 orientflag) -{ -l_int32 w, h, d, i, j, wpls, wpld; -l_int32 cval, rval, bval, val, lgrad, rgrad, tgrad, bgrad; -l_uint32 *datas, *lines, *datad, *lined; -PIX *pixd; - - PROCNAME("pixTwoSidedEdgeFilter"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (orientflag != L_HORIZONTAL_EDGES && orientflag != L_VERTICAL_EDGES) - return (PIX *)ERROR_PTR("invalid orientflag", procName, NULL); - - pixd = pixCreateTemplate(pixs); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - if (orientflag == L_VERTICAL_EDGES) { - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - cval = GET_DATA_BYTE(lines, 1); - lgrad = cval - GET_DATA_BYTE(lines, 0); - for (j = 1; j < w - 1; j++) { - rval = GET_DATA_BYTE(lines, j + 1); - rgrad = rval - cval; - if (lgrad * rgrad > 0) { - if (lgrad < 0) - val = -L_MAX(lgrad, rgrad); - else - val = L_MIN(lgrad, rgrad); - SET_DATA_BYTE(lined, j, val); - } - lgrad = rgrad; - cval = rval; - } - } - } - else { /* L_HORIZONTAL_EDGES) */ - for (j = 0; j < w; j++) { - lines = datas + wpls; - cval = GET_DATA_BYTE(lines, j); /* for line 1 */ - tgrad = cval - GET_DATA_BYTE(datas, j); - for (i = 1; i < h - 1; i++) { - lines += wpls; /* for line i + 1 */ - lined = datad + i * wpld; - bval = GET_DATA_BYTE(lines, j); - bgrad = bval - cval; - if (tgrad * bgrad > 0) { - if (tgrad < 0) - val = -L_MAX(tgrad, bgrad); - else - val = L_MIN(tgrad, bgrad); - SET_DATA_BYTE(lined, j, val); - } - tgrad = bgrad; - cval = bval; - } - } - } - - return pixd; -} - - -/*----------------------------------------------------------------------* - * Measurement of edge smoothness * - *----------------------------------------------------------------------*/ -/*! - * \brief pixMeasureEdgeSmoothness() - * - * \param[in] pixs 1 bpp - * \param[in] side L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOT - * \param[in] minjump minimum jump to be counted; >= 1 - * \param[in] minreversal minimum reversal size for new peak or valley - * \param[out] pjpl [optional] jumps/length: number of jumps, - * normalized to length of component side - * \param[out] pjspl [optional] jumpsum/length: sum of all - * sufficiently large jumps, normalized to length - * of component side - * \param[out] prpl [optional] reversals/length: number of - * peak-to-valley or valley-to-peak reversals, - * normalized to length of component side - * \param[in] debugfile [optional] displays constructed edge; use NULL - * for no output - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This computes three measures of smoothness of the edge of a - * connected component: - * * jumps/length: (jpl) the number of jumps of size >= %minjump, - * normalized to the length of the side - * * jump sum/length: (jspl) the sum of all jump lengths of - * size >= %minjump, normalized to the length of the side - * * reversals/length: (rpl) the number of peak <--> valley - * reversals, using %minreverse as a minimum deviation of - * the peak or valley from its preceding extremum, - * normalized to the length of the side - * (2) The input pix should be a single connected component, but - * this is not required. - *- */ -l_ok -pixMeasureEdgeSmoothness(PIX *pixs, - l_int32 side, - l_int32 minjump, - l_int32 minreversal, - l_float32 *pjpl, - l_float32 *pjspl, - l_float32 *prpl, - const char *debugfile) -{ -l_int32 i, n, val, nval, diff, njumps, jumpsum, nreversal; -NUMA *na, *nae; - - PROCNAME("pixMeasureEdgeSmoothness"); - - if (pjpl) *pjpl = 0.0; - if (pjspl) *pjspl = 0.0; - if (prpl) *prpl = 0.0; - if (!pjpl && !pjspl && !prpl && !debugfile) - return ERROR_INT("no output requested", procName, 1); - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (side != L_FROM_LEFT && side != L_FROM_RIGHT && - side != L_FROM_TOP && side != L_FROM_BOT) - return ERROR_INT("invalid side", procName, 1); - if (minjump < 1) - return ERROR_INT("invalid minjump; must be >= 1", procName, 1); - if (minreversal < 1) - return ERROR_INT("invalid minreversal; must be >= 1", procName, 1); - - if ((na = pixGetEdgeProfile(pixs, side, debugfile)) == NULL) - return ERROR_INT("edge profile not made", procName, 1); - if ((n = numaGetCount(na)) < 2) { - numaDestroy(&na); - return 0; - } - - if (pjpl || pjspl) { - jumpsum = 0; - njumps = 0; - numaGetIValue(na, 0, &val); - for (i = 1; i < n; i++) { - numaGetIValue(na, i, &nval); - diff = L_ABS(nval - val); - if (diff >= minjump) { - njumps++; - jumpsum += diff; - } - val = nval; - } - if (pjpl) - *pjpl = (l_float32)njumps / (l_float32)(n - 1); - if (pjspl) - *pjspl = (l_float32)jumpsum / (l_float32)(n - 1); - } - - if (prpl) { - nae = numaFindExtrema(na, minreversal, NULL); - nreversal = numaGetCount(nae) - 1; - *prpl = (l_float32)nreversal / (l_float32)(n - 1); - numaDestroy(&nae); - } - - numaDestroy(&na); - return 0; -} - - -/*! - * \brief pixGetEdgeProfile() - * - * \param[in] pixs 1 bpp - * \param[in] side L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOT - * \param[in] debugfile [optional] displays constructed edge; use NULL - * for no output - * \return na of fg edge pixel locations, or NULL on error - */ -NUMA * -pixGetEdgeProfile(PIX *pixs, - l_int32 side, - const char *debugfile) -{ -l_int32 x, y, w, h, loc, index, ival; -l_uint32 val; -NUMA *na; -PIX *pixt; -PIXCMAP *cmap; - - PROCNAME("pixGetEdgeProfile"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (NUMA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (side != L_FROM_LEFT && side != L_FROM_RIGHT && - side != L_FROM_TOP && side != L_FROM_BOT) - return (NUMA *)ERROR_PTR("invalid side", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - if (side == L_FROM_LEFT || side == L_FROM_RIGHT) - na = numaCreate(h); - else - na = numaCreate(w); - if (side == L_FROM_LEFT) { - pixGetLastOffPixelInRun(pixs, 0, 0, L_FROM_LEFT, &loc); - loc = (loc == w - 1) ? 0 : loc + 1; /* back to the left edge */ - numaAddNumber(na, loc); - for (y = 1; y < h; y++) { - pixGetPixel(pixs, loc, y, &val); - if (val == 1) { - pixGetLastOnPixelInRun(pixs, loc, y, L_FROM_RIGHT, &loc); - } else { - pixGetLastOffPixelInRun(pixs, loc, y, L_FROM_LEFT, &loc); - loc = (loc == w - 1) ? 0 : loc + 1; - } - numaAddNumber(na, loc); - } - } - else if (side == L_FROM_RIGHT) { - pixGetLastOffPixelInRun(pixs, w - 1, 0, L_FROM_RIGHT, &loc); - loc = (loc == 0) ? w - 1 : loc - 1; /* back to the right edge */ - numaAddNumber(na, loc); - for (y = 1; y < h; y++) { - pixGetPixel(pixs, loc, y, &val); - if (val == 1) { - pixGetLastOnPixelInRun(pixs, loc, y, L_FROM_LEFT, &loc); - } else { - pixGetLastOffPixelInRun(pixs, loc, y, L_FROM_RIGHT, &loc); - loc = (loc == 0) ? w - 1 : loc - 1; - } - numaAddNumber(na, loc); - } - } - else if (side == L_FROM_TOP) { - pixGetLastOffPixelInRun(pixs, 0, 0, L_FROM_TOP, &loc); - loc = (loc == h - 1) ? 0 : loc + 1; /* back to the top edge */ - numaAddNumber(na, loc); - for (x = 1; x < w; x++) { - pixGetPixel(pixs, x, loc, &val); - if (val == 1) { - pixGetLastOnPixelInRun(pixs, x, loc, L_FROM_BOT, &loc); - } else { - pixGetLastOffPixelInRun(pixs, x, loc, L_FROM_TOP, &loc); - loc = (loc == h - 1) ? 0 : loc + 1; - } - numaAddNumber(na, loc); - } - } - else { /* side == L_FROM_BOT */ - pixGetLastOffPixelInRun(pixs, 0, h - 1, L_FROM_BOT, &loc); - loc = (loc == 0) ? h - 1 : loc - 1; /* back to the bottom edge */ - numaAddNumber(na, loc); - for (x = 1; x < w; x++) { - pixGetPixel(pixs, x, loc, &val); - if (val == 1) { - pixGetLastOnPixelInRun(pixs, x, loc, L_FROM_TOP, &loc); - } else { - pixGetLastOffPixelInRun(pixs, x, loc, L_FROM_BOT, &loc); - loc = (loc == 0) ? h - 1 : loc - 1; - } - numaAddNumber(na, loc); - } - } - - if (debugfile) { - pixt = pixConvertTo8(pixs, TRUE); - cmap = pixGetColormap(pixt); - pixcmapAddColor(cmap, 255, 0, 0); - index = pixcmapGetCount(cmap) - 1; - if (side == L_FROM_LEFT || side == L_FROM_RIGHT) { - for (y = 0; y < h; y++) { - numaGetIValue(na, y, &ival); - pixSetPixel(pixt, ival, y, index); - } - } else { /* L_FROM_TOP or L_FROM_BOT */ - for (x = 0; x < w; x++) { - numaGetIValue(na, x, &ival); - pixSetPixel(pixt, x, ival, index); - } - } - pixWrite(debugfile, pixt, IFF_PNG); - pixDestroy(&pixt); - } - - return na; -} - - -/* - * \brief pixGetLastOffPixelInRun() - * - * \param[in] pixs 1 bpp - * \param[in] x, y starting location - * \param[in] direction L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOT - * \param[out] ploc location in scan direction coordinate - * of last OFF pixel found - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Search starts from the pixel at (x, y), which is OFF. - * (2) It returns the location in the scan direction of the last - * pixel in the current run that is OFF. - * (3) The interface for these pixel run functions is cleaner when - * you ask for the last pixel in the current run, rather than the - * first pixel of opposite polarity that is found, because the - * current run may go to the edge of the image, in which case - * no pixel of opposite polarity is found. - *- */ -l_ok -pixGetLastOffPixelInRun(PIX *pixs, - l_int32 x, - l_int32 y, - l_int32 direction, - l_int32 *ploc) -{ -l_int32 loc, w, h; -l_uint32 val; - - PROCNAME("pixGetLastOffPixelInRun"); - - if (!ploc) - return ERROR_INT("&loc not defined", procName, 1); - *ploc = 0; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs undefined or not 1 bpp", procName, 1); - if (direction != L_FROM_LEFT && direction != L_FROM_RIGHT && - direction != L_FROM_TOP && direction != L_FROM_BOT) - return ERROR_INT("invalid side", procName, 1); - - pixGetDimensions(pixs, &w, &h, NULL); - if (direction == L_FROM_LEFT) { - for (loc = x; loc < w; loc++) { - pixGetPixel(pixs, loc, y, &val); - if (val == 1) - break; - } - *ploc = loc - 1; - } else if (direction == L_FROM_RIGHT) { - for (loc = x; loc >= 0; loc--) { - pixGetPixel(pixs, loc, y, &val); - if (val == 1) - break; - } - *ploc = loc + 1; - } - else if (direction == L_FROM_TOP) { - for (loc = y; loc < h; loc++) { - pixGetPixel(pixs, x, loc, &val); - if (val == 1) - break; - } - *ploc = loc - 1; - } - else if (direction == L_FROM_BOT) { - for (loc = y; loc >= 0; loc--) { - pixGetPixel(pixs, x, loc, &val); - if (val == 1) - break; - } - *ploc = loc + 1; - } - return 0; -} - - -/* - * \brief pixGetLastOnPixelInRun() - * - * \param[in] pixs 1 bpp - * \param[in] x, y starting location - * \param[in] direction L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOT - * \param[out] ploc location in scan direction coordinate - * of first ON pixel found - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Search starts from the pixel at (x, y), which is ON. - * (2) It returns the location in the scan direction of the last - * pixel in the current run that is ON. - *- */ -l_int32 -pixGetLastOnPixelInRun(PIX *pixs, - l_int32 x, - l_int32 y, - l_int32 direction, - l_int32 *ploc) -{ -l_int32 loc, w, h; -l_uint32 val; - - PROCNAME("pixLastOnPixelInRun"); - - if (!ploc) - return ERROR_INT("&loc not defined", procName, 1); - *ploc = 0; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs undefined or not 1 bpp", procName, 1); - if (direction != L_FROM_LEFT && direction != L_FROM_RIGHT && - direction != L_FROM_TOP && direction != L_FROM_BOT) - return ERROR_INT("invalid side", procName, 1); - - pixGetDimensions(pixs, &w, &h, NULL); - if (direction == L_FROM_LEFT) { - for (loc = x; loc < w; loc++) { - pixGetPixel(pixs, loc, y, &val); - if (val == 0) - break; - } - *ploc = loc - 1; - } else if (direction == L_FROM_RIGHT) { - for (loc = x; loc >= 0; loc--) { - pixGetPixel(pixs, loc, y, &val); - if (val == 0) - break; - } - *ploc = loc + 1; - } - else if (direction == L_FROM_TOP) { - for (loc = y; loc < h; loc++) { - pixGetPixel(pixs, x, loc, &val); - if (val == 0) - break; - } - *ploc = loc - 1; - } - else if (direction == L_FROM_BOT) { - for (loc = y; loc >= 0; loc--) { - pixGetPixel(pixs, x, loc, &val); - if (val == 0) - break; - } - *ploc = loc + 1; - } - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/encoding.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/encoding.c deleted file mode 100644 index 4705bde4..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/encoding.c +++ /dev/null @@ -1,652 +0,0 @@ -/*====================================================================* - - Copyright (C) 2001 Leptonica. All rights reserved. - - This software is distributed in the hope that it will be - - useful, but with NO WARRANTY OF ANY KIND. - - No author or distributor accepts responsibility to anyone for the - - consequences of using this software, or for whether it serves any - - particular purpose or works at all, unless he or she says so in - - writing. Everyone is granted permission to copy, modify and - - redistribute this source code, for commercial or non-commercial - - purposes, with the following restrictions: (1) the origin of this - - source code must not be misrepresented; (2) modified versions must - - be plainly marked as such; and (3) this notice may not be removed - - or altered from any source or modified source distribution. - *====================================================================*/ - -/* - * encodings.c - * - * Base64 - * char *encodeBase64() - * l_uint8 *decodeBase64() - * static l_int32 isBase64() - * static l_int32 *genReverseTab64() - * static void byteConvert3to4() - * static void byteConvert4to3() - * - * Ascii85 - * char *encodeAscii85() - * l_uint8 *decodeAscii85() - * static l_int32 convertChunkToAscii85() - * - * String reformatting for base 64 encoded data - * char *reformatPacked64() - * - * Base64 encoding is useful for encding binary data in a restricted set of - * 64 printable ascii symbols, that includes the 62 alphanumerics and '+' - * and '/'. Notably it does not include quotes, so that base64 encoded - * strings can be used in situations where quotes are used for formatting. - * 64 symbols was chosen because it is the smallest number that can be used - * in 4-for-3 byte encoding of binary data: - * log2(64) / log2(256) = 0.75 = 3/4 - * - * Ascii85 encoding is used in PostScript and some pdf files for - * representing binary data (for example, a compressed image) in printable - * ascii symbols. It has a dictionary of 85 symbols; 85 was chosen because - * it is the smallest number that can be used in 5-for-4 byte encoding - * of binary data (256 possible input values). This can be seen from - * the max information content in such a sequence: - * log2(84) / log2(256) = 0.799 < 4/5 - * log2(85) / log2(256) = 0.801 > 4/5 - */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) The input character data is unrestricted binary. - * The output encoded data consists of the 64 characters - * in the base64 set, plus newlines and the pad character '='. - *- */ -char * -encodeBase64(const l_uint8 *inarray, - l_int32 insize, - l_int32 *poutsize) -{ -char *chara; -const l_uint8 *bytea; -l_uint8 array3[3], array4[4]; -l_int32 outsize, i, j, index, linecount; - - PROCNAME("encodeBase64"); - - if (!poutsize) - return (char *)ERROR_PTR("&outsize not defined", procName, NULL); - *poutsize = 0; - if (!inarray) - return (char *)ERROR_PTR("inarray not defined", procName, NULL); - if (insize <= 0) - return (char *)ERROR_PTR("insize not > 0", procName, NULL); - - /* The output array is padded to a multiple of 4 bytes, not - * counting the newlines. We just need to allocate a large - * enough array, and add 4 bytes to make sure it is big enough. */ - outsize = 4 * ((insize + 2) / 3); /* without newlines */ - outsize += outsize / MAX_BASE64_LINE + 4; /* with the newlines */ - if ((chara = (char *)LEPT_CALLOC(outsize, sizeof(char))) == NULL) - return (char *)ERROR_PTR("chara not made", procName, NULL); - - /* Read all the input data, and convert in sets of 3 input - * bytes --> 4 output bytes. */ - i = index = linecount = 0; - bytea = inarray; - while (insize--) { - if (linecount == MAX_BASE64_LINE) { - chara[index++] = '\n'; - linecount = 0; - } - array3[i++] = *bytea++; - if (i == 3) { /* convert 3 to 4 and save */ - byteConvert3to4(array3, array4); - for (j = 0; j < 4; j++) - chara[index++] = tablechar64[array4[j]]; - i = 0; - linecount += 4; - } - } - - /* Suppose 1 or 2 bytes has been read but not yet processed. - * If 1 byte has been read, this will generate 2 bytes of - * output, with 6 bits to the first byte and 2 bits to the second. - * We will add two bytes of '=' for padding. - * If 2 bytes has been read, this will generate 3 bytes of output, - * with 6 bits to the first 2 bytes and 4 bits to the third, and - * we add a fourth padding byte ('='). */ - if (i > 0) { /* left-over 1 or 2 input bytes */ - for (j = i; j < 3; j++) - array3[j] = '\0'; /* zero the remaining input bytes */ - byteConvert3to4(array3, array4); - for (j = 0; j <= i; j++) - chara[index++] = tablechar64[array4[j]]; - for (j = i + 1; j < 4; j++) - chara[index++] = '='; - } - *poutsize = index; - - return chara; -} - - -/*! - * \brief decodeBase64() - * - * \param[in] inarray input encoded char data, with 72 chars/line) - * \param[in] insize number of bytes in input array - * \param[out] poutsize number of bytes in output byte array - * \return bytea decoded byte data, or NULL on error - * - *
- * Notes: - * (1) The input character data should have only 66 different characters: - * The 64 character set for base64 encoding, plus the pad - * character '=' and newlines for formatting with fixed line - * lengths. If there are any other characters, the decoder - * will declare the input data to be invalid and return NULL. - * (2) The decoder ignores newlines and, for a valid input string, - * stops reading input when a pad byte is found. - *- */ -l_uint8 * -decodeBase64(const char *inarray, - l_int32 insize, - l_int32 *poutsize) -{ -char inchar; -l_uint8 *bytea; -l_uint8 array3[3], array4[4]; -l_int32 *rtable64; -l_int32 i, j, outsize, in_index, out_index; - - PROCNAME("decodeBase64"); - - if (!poutsize) - return (l_uint8 *)ERROR_PTR("&outsize not defined", procName, NULL); - *poutsize = 0; - if (!inarray) - return (l_uint8 *)ERROR_PTR("inarray not defined", procName, NULL); - if (insize <= 0) - return (l_uint8 *)ERROR_PTR("insize not > 0", procName, NULL); - - /* Validate the input data */ - for (i = 0; i < insize; i++) { - inchar = inarray[i]; - if (inchar == '\n') continue; - if (isBase64(inchar) == 0 && inchar != '=') - return (l_uint8 *)ERROR_PTR("invalid char in inarray", - procName, NULL); - } - - /* The input array typically is made with a newline every - * MAX_BASE64_LINE input bytes. However, as a printed string, the - * newlines would be stripped. So when we allocate the output - * array, assume the input array is all data, but strip - * out the newlines during decoding. This guarantees that - * the allocated array is large enough. */ - outsize = 3 * ((insize + 3) / 4) + 4; - if ((bytea = (l_uint8 *)LEPT_CALLOC(outsize, sizeof(l_uint8))) == NULL) - return (l_uint8 *)ERROR_PTR("bytea not made", procName, NULL); - - /* The number of encoded input data bytes is always a multiple of 4. - * Read all the data, until you reach either the end or - * the first pad character '='. The data is processed in - * units of 4 input bytes, generating 3 output decoded bytes - * of binary data. Newlines are ignored. If there are no - * pad bytes, i == 0 at the end of this section. */ - rtable64 = genReverseTab64(); - i = in_index = out_index = 0; - for (in_index = 0; in_index < insize; in_index++) { - inchar = inarray[in_index]; - if (inchar == '\n') continue; - if (inchar == '=') break; - array4[i++] = rtable64[(unsigned char)inchar]; - if (i < 4) { - continue; - } else { /* i == 4; convert 4 to 3 and save */ - byteConvert4to3(array4, array3); - for (j = 0; j < 3; j++) - bytea[out_index++] = array3[j]; - i = 0; - } - } - - /* If i > 0, we ran into pad bytes ('='). If i == 2, there are - * two input pad bytes and one output data byte. If i == 3, - * there is one input pad byte and two output data bytes. */ - if (i > 0) { - for (j = i; j < 4; j++) - array4[j] = '\0'; /* zero the remaining input bytes */ - byteConvert4to3(array4, array3); - for (j = 0; j < i - 1; j++) - bytea[out_index++] = array3[j]; - } - *poutsize = out_index; - - LEPT_FREE(rtable64); - return bytea; -} - - -/*! - * \brief isBase64() - */ -static l_int32 -isBase64(char c) -{ - return (isalnum(((int)c)) || ((c) == '+') || ((c) == '/')) ? 1 : 0; -} - -/*! - * \brief genReverseTab64() - */ -static l_int32 * -genReverseTab64() -{ -l_int32 i; -l_int32 *rtable64; - - rtable64 = (l_int32 *)LEPT_CALLOC(128, sizeof(l_int32)); - for (i = 0; i < 64; i++) { - rtable64[(unsigned char)tablechar64[i]] = i; - } - return rtable64; -} - -/*! - * \brief byteConvert3to4() - */ -static void -byteConvert3to4(l_uint8 *in3, - l_uint8 *out4) -{ - out4[0] = in3[0] >> 2; - out4[1] = ((in3[0] & 0x03) << 4) | (in3[1] >> 4); - out4[2] = ((in3[1] & 0x0f) << 2) | (in3[2] >> 6); - out4[3] = in3[2] & 0x3f; - return; -} - -/*! - * \brief byteConvert4to3() - */ -static void -byteConvert4to3(l_uint8 *in4, - l_uint8 *out3) -{ - out3[0] = (in4[0] << 2) | (in4[1] >> 4); - out3[1] = ((in4[1] & 0x0f) << 4) | (in4[2] >> 2); - out3[2] = ((in4[2] & 0x03) << 6) | in4[3]; - return; -} - - -/*-------------------------------------------------------------* - * Utility for encoding and decoding data with ascii85 * - *-------------------------------------------------------------*/ -/*! - * \brief encodeAscii85() - * - * \param[in] inarray input data - * \param[in] insize number of bytes in input array - * \param[out] poutsize number of bytes in output char array - * \return chara with 64 characters + \n in each line - * - *
- * Notes: - * (1) Ghostscript has a stack break if the last line of - * data only has a '>', so we avoid the problem by - * always putting '~>' on the last line. - *- */ -char * -encodeAscii85(const l_uint8 *inarray, - l_int32 insize, - l_int32 *poutsize) -{ -char *chara; -char outbuf[8]; -l_int32 maxsize, i, index, outindex, linecount, nbout, eof; - - PROCNAME("encodeAscii85"); - - if (!poutsize) - return (char *)ERROR_PTR("&outsize not defined", procName, NULL); - *poutsize = 0; - if (!inarray) - return (char *)ERROR_PTR("inarray not defined", procName, NULL); - if (insize <= 0) - return (char *)ERROR_PTR("insize not > 0", procName, NULL); - - /* Accumulate results in char array */ - maxsize = (l_int32)(80. + (insize * 5. / 4.) * - (1. + 2. / MAX_ASCII85_LINE)); - if ((chara = (char *)LEPT_CALLOC(maxsize, sizeof(char))) == NULL) - return (char *)ERROR_PTR("chara not made", procName, NULL); - - linecount = 0; - index = 0; - outindex = 0; - while (1) { - eof = convertChunkToAscii85(inarray, insize, &index, outbuf, &nbout); - for (i = 0; i < nbout; i++) { - chara[outindex++] = outbuf[i]; - linecount++; - if (linecount >= MAX_ASCII85_LINE) { - chara[outindex++] = '\n'; - linecount = 0; - } - } - if (eof == TRUE) { - if (linecount != 0) - chara[outindex++] = '\n'; - chara[outindex++] = '~'; - chara[outindex++] = '>'; - chara[outindex++] = '\n'; - break; - } - } - - *poutsize = outindex; - return chara; -} - - -/*! - * \brief convertChunkToAscii85() - * - * \param[in] inarray input data - * \param[in] insize number of bytes in input array - * \param[out] pindex use and -- ptr - * \param[in] outbuf holds 8 ascii chars; we use no more than 7 - * \param[out] pnbsout number of bytes written to outbuf - * \return boolean for eof 0 if more data, 1 if end of file - * - *
- * Notes: - * (1) Attempts to read 4 bytes and write 5. - * (2) Writes 1 byte if the value is 0. - *- */ -static l_int32 -convertChunkToAscii85(const l_uint8 *inarray, - l_int32 insize, - l_int32 *pindex, - char *outbuf, - l_int32 *pnbout) -{ -l_uint8 inbyte; -l_uint32 inword, val; -l_int32 eof, index, nread, nbout, i; - - eof = FALSE; - index = *pindex; - nread = L_MIN(4, (insize - index)); - if (insize == index + nread) - eof = TRUE; - *pindex += nread; /* save new index */ - - /* Read input data and save in l_uint32 */ - inword = 0; - for (i = 0; i < nread; i++) { - inbyte = inarray[index + i]; - inword += inbyte << (8 * (3 - i)); - } - -#if 0 - lept_stderr("index = %d, nread = %d\n", index, nread); - lept_stderr("inword = %x\n", inword); - lept_stderr("eof = %d\n", eof); -#endif - - /* Special case: output 1 byte only */ - if (inword == 0) { - outbuf[0] = 'z'; - nbout = 1; - } else { /* output nread + 1 bytes */ - for (i = 4; i >= 4 - nread; i--) { - val = inword / power85[i]; - outbuf[4 - i] = (l_uint8)(val + '!'); - inword -= val * power85[i]; - } - nbout = nread + 1; - } - *pnbout = nbout; - - return eof; -} - - -/*! - * \brief decodeAscii85() - * - * \param[in] inarray ascii85 input data - * \param[in] insize number of bytes in input array - * \param[out] poutsize number of bytes in output l_uint8 array - * \return outarray binary - * - *
- * Notes: - * (1) We assume the data is properly encoded, so we do not check - * for invalid characters or the final '>' character. - * (2) We permit whitespace to be added to the encoding in an - * arbitrary way. - *- */ -l_uint8 * -decodeAscii85(const char *inarray, - l_int32 insize, - l_int32 *poutsize) -{ -char inc; -const char *pin; -l_uint8 val; -l_uint8 *outa; -l_int32 maxsize, ocount, bytecount, index; -l_uint32 oword; - - PROCNAME("decodeAscii85"); - - if (!poutsize) - return (l_uint8 *)ERROR_PTR("&outsize not defined", procName, NULL); - *poutsize = 0; - if (!inarray) - return (l_uint8 *)ERROR_PTR("inarray not defined", procName, NULL); - if (insize <= 0) - return (l_uint8 *)ERROR_PTR("insize not > 0", procName, NULL); - - /* Accumulate results in outa */ - maxsize = (l_int32)(80. + (insize * 4. / 5.)); /* plenty big */ - if ((outa = (l_uint8 *)LEPT_CALLOC(maxsize, sizeof(l_uint8))) == NULL) - return (l_uint8 *)ERROR_PTR("outa not made", procName, NULL); - - pin = inarray; - ocount = 0; /* byte index into outa */ - oword = 0; - for (index = 0, bytecount = 0; index < insize; index++, pin++) { - inc = *pin; - - if (inc == ' ' || inc == '\t' || inc == '\n' || - inc == '\f' || inc == '\r' || inc == '\v') /* ignore white space */ - continue; - - val = inc - '!'; - if (val < 85) { - oword = oword * 85 + val; - if (bytecount < 4) { - bytecount++; - } else { /* we have all 5 input chars for the oword */ - outa[ocount] = (oword >> 24) & 0xff; - outa[ocount + 1] = (oword >> 16) & 0xff; - outa[ocount + 2] = (oword >> 8) & 0xff; - outa[ocount + 3] = oword & 0xff; - ocount += 4; - bytecount = 0; - oword = 0; - } - } else if (inc == 'z' && bytecount == 0) { - outa[ocount] = 0; - outa[ocount + 1] = 0; - outa[ocount + 2] = 0; - outa[ocount + 3] = 0; - ocount += 4; - } else if (inc == '~') { /* end of data */ - L_INFO(" %d extra bytes output\n", procName, bytecount - 1); - switch (bytecount) { - case 0: /* normal eof */ - case 1: /* error */ - break; - case 2: /* 1 extra byte */ - oword = oword * power85[3] + 0xffffff; - outa[ocount] = (oword >> 24) & 0xff; - break; - case 3: /* 2 extra bytes */ - oword = oword * power85[2] + 0xffff; - outa[ocount] = (oword >> 24) & 0xff; - outa[ocount + 1] = (oword >> 16) & 0xff; - break; - case 4: /* 3 extra bytes */ - oword = oword * 85 + 0xff; - outa[ocount] = (oword >> 24) & 0xff; - outa[ocount + 1] = (oword >> 16) & 0xff; - outa[ocount + 2] = (oword >> 8) & 0xff; - break; - } - if (bytecount > 1) - ocount += (bytecount - 1); - break; - } - } - *poutsize = ocount; - - return outa; -} - - -/*-------------------------------------------------------------* - * String reformatting for base 64 encoded data * - *-------------------------------------------------------------*/ -/*! - * \brief reformatPacked64() - * - * \param[in] inarray base64 encoded string with newlines - * \param[in] insize number of bytes in input array - * \param[in] leadspace number of spaces in each line before the data - * \param[in] linechars number of bytes of data in each line; multiple of 4 - * \param[in] addquotes 1 to add quotes to each line of data; 0 to skip - * \param[out] poutsize number of bytes in output char array - * \return outarray ascii - * - *
- * Notes: - * (1) Each line in the output array has %leadspace space characters, - * followed optionally by a double-quote, followed by %linechars - * bytes of base64 data, followed optionally by a double-quote, - * followed by a newline. - * (2) This can be used to convert a base64 encoded string to a - * string formatted for inclusion in a C source file. - *- */ -char * -reformatPacked64(const char *inarray, - l_int32 insize, - l_int32 leadspace, - l_int32 linechars, - l_int32 addquotes, - l_int32 *poutsize) -{ -char *flata, *outa; -l_int32 i, j, flatindex, flatsize, outindex, nlines, linewithpad, linecount; - - PROCNAME("reformatPacked64"); - - if (!poutsize) - return (char *)ERROR_PTR("&outsize not defined", procName, NULL); - *poutsize = 0; - if (!inarray) - return (char *)ERROR_PTR("inarray not defined", procName, NULL); - if (insize <= 0) - return (char *)ERROR_PTR("insize not > 0", procName, NULL); - if (leadspace < 0) - return (char *)ERROR_PTR("leadspace must be >= 0", procName, NULL); - if (linechars % 4) - return (char *)ERROR_PTR("linechars % 4 must be 0", procName, NULL); - - /* Remove all white space */ - if ((flata = (char *)LEPT_CALLOC(insize, sizeof(char))) == NULL) - return (char *)ERROR_PTR("flata not made", procName, NULL); - for (i = 0, flatindex = 0; i < insize; i++) { - if (isBase64(inarray[i]) || inarray[i] == '=') - flata[flatindex++] = inarray[i]; - } - - /* Generate output string */ - flatsize = flatindex; - nlines = (flatsize + linechars - 1) / linechars; - linewithpad = leadspace + linechars + 1; /* including newline */ - if (addquotes) linewithpad += 2; - if ((outa = (char *)LEPT_CALLOC((size_t)nlines * linewithpad, - sizeof(char))) == NULL) { - LEPT_FREE(flata); - return (char *)ERROR_PTR("outa not made", procName, NULL); - } - for (j = 0, outindex = 0; j < leadspace; j++) - outa[outindex++] = ' '; - if (addquotes) outa[outindex++] = '"'; - for (i = 0, linecount = 0; i < flatsize; i++) { - if (linecount == linechars) { - if (addquotes) outa[outindex++] = '"'; - outa[outindex++] = '\n'; - for (j = 0; j < leadspace; j++) - outa[outindex++] = ' '; - if (addquotes) outa[outindex++] = '"'; - linecount = 0; - } - outa[outindex++] = flata[i]; - linecount++; - } - if (addquotes) outa[outindex++] = '"'; - *poutsize = outindex; - - LEPT_FREE(flata); - return outa; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/endianness.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/endianness.h deleted file mode 100644 index 8cdc060d..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/endianness.h +++ /dev/null @@ -1,11 +0,0 @@ -#if !defined (L_BIG_ENDIAN) && !defined (L_LITTLE_ENDIAN) -# if defined (__APPLE_CC__) -# ifdef __BIG_ENDIAN__ -# define L_BIG_ENDIAN -# else -# define L_LITTLE_ENDIAN -# endif -# else -# define L_LITTLE_ENDIAN -# endif -#endif diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/enhance.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/enhance.c deleted file mode 100644 index 4033800a..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/enhance.c +++ /dev/null @@ -1,2356 +0,0 @@ -/*====================================================================* - - 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 enhance.c - *
- * - * Gamma TRC (tone reproduction curve) mapping - * PIX *pixGammaTRC() - * PIX *pixGammaTRCMasked() - * PIX *pixGammaTRCWithAlpha() - * NUMA *numaGammaTRC() - * - * Contrast enhancement - * PIX *pixContrastTRC() - * PIX *pixContrastTRCMasked() - * NUMA *numaContrastTRC() - * - * Histogram equalization - * PIX *pixEqualizeTRC() - * NUMA *numaEqualizeTRC() - * - * Generic TRC mapper - * PIX *pixTRCMap() - * PIX *pixTRCMapGeneral() - * - * Unsharp-masking - * PIX *pixUnsharpMasking() - * PIX *pixUnsharpMaskingGray() - * PIX *pixUnsharpMaskingFast() - * PIX *pixUnsharpMaskingGrayFast() - * PIX *pixUnsharpMaskingGray1D() - * PIX *pixUnsharpMaskingGray2D() - * - * Hue and saturation modification - * PIX *pixModifyHue() - * PIX *pixModifySaturation() - * l_int32 pixMeasureSaturation() - * PIX *pixModifyBrightness() - * - * Color shifting - * PIX *pixMosaicColorShiftRGB() - * PIX *pixColorShiftRGB() - * - * Darken gray (unsaturated) pixels - * PIX *pixDarkenGray() - * - * General multiplicative constant color transform - * PIX *pixMultConstantColor() - * PIX *pixMultMatrixColor() - * - * Edge by bandpass - * PIX *pixHalfEdgeByBandpass() - * - * Gamma correction, contrast enhancement and histogram equalization - * apply a simple mapping function to each pixel (or, for color - * images, to each sample (i.e., r,g,b) of the pixel). - * - * ~ Gamma correction either lightens the image or darkens - * it, depending on whether the gamma factor is greater - * or less than 1.0, respectively. - * - * ~ Contrast enhancement darkens the pixels that are already - * darker than the middle of the dynamic range (128) - * and lightens pixels that are lighter than 128. - * - * ~ Histogram equalization remaps to have the same number - * of image pixels at each of 256 intensity values. This is - * a quick and dirty method of adjusting contrast and brightness - * to bring out details in both light and dark regions. - * - * Unsharp masking is a more complicated enhancement. - * A "high frequency" image, generated by subtracting - * the smoothed ("low frequency") part of the image from - * itself, has all the energy at the edges. This "edge image" - * has 0 average value. A fraction of the edge image is - * then added to the original, enhancing the differences - * between pixel values at edges. Because we represent - * images as l_uint8 arrays, we preserve dynamic range and - * handle negative values by doing all the arithmetic on - * shifted l_uint16 arrays; the l_uint8 values are recovered - * at the end. - * - * Hue and saturation modification work in HSV space. Because - * this is too large for efficient table lookup, each pixel value - * is transformed to HSV, modified, and transformed back. - * It's not the fastest way to do this, but the method is - * easily understood. - * - * Unsharp masking is never in-place, and returns a clone if no - * operation is to be performed. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) pixd must either be null or equal to pixs. - * For in-place operation, set pixd == pixs: - * pixGammaTRC(pixs, pixs, ...); - * To get a new image, set pixd == null: - * pixd = pixGammaTRC(NULL, pixs, ...); - * (2) If pixs is colormapped, the colormap is transformed, - * either in-place or in a copy of pixs. - * (3) We use a gamma mapping between minval and maxval. - * (4) If gamma < 1.0, the image will appear darker; - * if gamma > 1.0, the image will appear lighter; - * (5) If gamma = 1.0 and minval = 0 and maxval = 255, no - * enhancement is performed; return a copy unless in-place, - * in which case this is a no-op. - * (6) For color images that are not colormapped, the mapping - * is applied to each component. - * (7) minval and maxval are not restricted to the interval [0, 255]. - * If minval < 0, an input value of 0 is mapped to a - * nonzero output. This will turn black to gray. - * If maxval > 255, an input value of 255 is mapped to - * an output value less than 255. This will turn - * white (e.g., in the background) to gray. - * (8) Increasing minval darkens the image. - * (9) Decreasing maxval bleaches the image. - * (10) Simultaneously increasing minval and decreasing maxval - * will darken the image and make the colors more intense; - * e.g., minval = 50, maxval = 200. - * (11) See numaGammaTRC() for further examples of use. - * (12) Use pixTRCMapGeneral() if applying different mappings - * to each channel in an RGB image. - *- */ -PIX * -pixGammaTRC(PIX *pixd, - PIX *pixs, - l_float32 gamma, - l_int32 minval, - l_int32 maxval) -{ -l_int32 d; -NUMA *nag; -PIXCMAP *cmap; - - PROCNAME("pixGammaTRC"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixd && (pixd != pixs)) - return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); - if (gamma <= 0.0) { - L_WARNING("gamma must be > 0.0; setting to 1.0\n", procName); - gamma = 1.0; - } - if (minval >= maxval) - return (PIX *)ERROR_PTR("minval not < maxval", procName, pixd); - cmap = pixGetColormap(pixs); - d = pixGetDepth(pixs); - if (!cmap && d != 8 && d != 32) - return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, pixd); - - if (gamma == 1.0 && minval == 0 && maxval == 255) /* no-op */ - return pixCopy(pixd, pixs); - - if (!pixd) /* start with a copy if not in-place */ - pixd = pixCopy(NULL, pixs); - - if (cmap) { - pixcmapGammaTRC(pixGetColormap(pixd), gamma, minval, maxval); - return pixd; - } - - /* pixd is 8 or 32 bpp */ - if ((nag = numaGammaTRC(gamma, minval, maxval)) == NULL) - return (PIX *)ERROR_PTR("nag not made", procName, pixd); - pixTRCMap(pixd, NULL, nag); - numaDestroy(&nag); - - return pixd; -} - - -/*! - * \brief pixGammaTRCMasked() - * - * \param[in] pixd [optional] null or equal to pixs - * \param[in] pixs 8 or 32 bpp; not colormapped - * \param[in] pixm [optional] null or 1 bpp - * \param[in] gamma gamma correction; must be > 0.0 - * \param[in] minval input value that gives 0 for output; can be < 0 - * \param[in] maxval input value that gives 255 for output; can be > 255 - * \return pixd always - * - *
- * Notes: - * (1) Same as pixGammaTRC() except mapping is optionally over - * a subset of pixels described by pixm. - * (2) Masking does not work for colormapped images. - * (3) See pixGammaTRC() for details on how to use the parameters. - *- */ -PIX * -pixGammaTRCMasked(PIX *pixd, - PIX *pixs, - PIX *pixm, - l_float32 gamma, - l_int32 minval, - l_int32 maxval) -{ -l_int32 d; -NUMA *nag; - - PROCNAME("pixGammaTRCMasked"); - - if (!pixm) - return pixGammaTRC(pixd, pixs, gamma, minval, maxval); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("invalid: pixs has a colormap", procName, pixd); - if (pixd && (pixd != pixs)) - return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); - d = pixGetDepth(pixs); - if (d != 8 && d != 32) - return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, pixd); - if (minval >= maxval) - return (PIX *)ERROR_PTR("minval not < maxval", procName, pixd); - if (gamma <= 0.0) { - L_WARNING("gamma must be > 0.0; setting to 1.0\n", procName); - gamma = 1.0; - } - - if (gamma == 1.0 && minval == 0 && maxval == 255) - return pixCopy(pixd, pixs); - - if (!pixd) /* start with a copy if not in-place */ - pixd = pixCopy(NULL, pixs); - - if ((nag = numaGammaTRC(gamma, minval, maxval)) == NULL) - return (PIX *)ERROR_PTR("nag not made", procName, pixd); - pixTRCMap(pixd, pixm, nag); - numaDestroy(&nag); - - return pixd; -} - - -/*! - * \brief pixGammaTRCWithAlpha() - * - * \param[in] pixd [optional] null or equal to pixs - * \param[in] pixs 32 bpp - * \param[in] gamma gamma correction; must be > 0.0 - * \param[in] minval input value that gives 0 for output; can be < 0 - * \param[in] maxval input value that gives 255 for output; can be > 255 - * \return pixd always - * - *
- * Notes: - * (1) See usage notes in pixGammaTRC(). - * (2) This version saves the alpha channel. It is only valid - * for 32 bpp (no colormap), and is a bit slower. - *- */ -PIX * -pixGammaTRCWithAlpha(PIX *pixd, - PIX *pixs, - l_float32 gamma, - l_int32 minval, - l_int32 maxval) -{ -NUMA *nag; -PIX *pixalpha; - - PROCNAME("pixGammaTRCWithAlpha"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, pixd); - if (pixd && (pixd != pixs)) - return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); - if (gamma <= 0.0) { - L_WARNING("gamma must be > 0.0; setting to 1.0\n", procName); - gamma = 1.0; - } - if (minval >= maxval) - return (PIX *)ERROR_PTR("minval not < maxval", procName, pixd); - - if (gamma == 1.0 && minval == 0 && maxval == 255) - return pixCopy(pixd, pixs); - if (!pixd) /* start with a copy if not in-place */ - pixd = pixCopy(NULL, pixs); - - pixalpha = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); /* save */ - if ((nag = numaGammaTRC(gamma, minval, maxval)) == NULL) - return (PIX *)ERROR_PTR("nag not made", procName, pixd); - pixTRCMap(pixd, NULL, nag); - pixSetRGBComponent(pixd, pixalpha, L_ALPHA_CHANNEL); /* restore */ - pixSetSpp(pixd, 4); - - numaDestroy(&nag); - pixDestroy(&pixalpha); - return pixd; -} - - -/*! - * \brief numaGammaTRC() - * - * \param[in] gamma gamma factor; must be > 0.0 - * \param[in] minval input value that gives 0 for output - * \param[in] maxval input value that gives 255 for output - * \return na, or NULL on error - * - *
- * Notes: - * (1) The map is returned as a numa; values are clipped to [0, 255]. - * (2) To force all intensities into a range within fraction delta - * of white, use: minval = -256 * (1 - delta) / delta - * maxval = 255 - * (3) To force all intensities into a range within fraction delta - * of black, use: minval = 0 - * maxval = 256 * (1 - delta) / delta - *- */ -NUMA * -numaGammaTRC(l_float32 gamma, - l_int32 minval, - l_int32 maxval) -{ -l_int32 i, val; -l_float32 x, invgamma; -NUMA *na; - - PROCNAME("numaGammaTRC"); - - if (minval >= maxval) - return (NUMA *)ERROR_PTR("minval not < maxval", procName, NULL); - if (gamma <= 0.0) { - L_WARNING("gamma must be > 0.0; setting to 1.0\n", procName); - gamma = 1.0; - } - - invgamma = 1. / gamma; - na = numaCreate(256); - for (i = 0; i < minval; i++) - numaAddNumber(na, 0); - for (i = minval; i <= maxval; i++) { - if (i < 0) continue; - if (i > 255) continue; - x = (l_float32)(i - minval) / (l_float32)(maxval - minval); - val = (l_int32)(255. * powf(x, invgamma) + 0.5); - val = L_MAX(val, 0); - val = L_MIN(val, 255); - numaAddNumber(na, val); - } - for (i = maxval + 1; i < 256; i++) - numaAddNumber(na, 255); - - return na; -} - - -/*-------------------------------------------------------------* - * Contrast enhancement * - *-------------------------------------------------------------*/ -/*! - * \brief pixContrastTRC() - * - * \param[in] pixd [optional] null or equal to pixs - * \param[in] pixs 8 or 32 bpp; or 2, 4 or 8 bpp with colormap - * \param[in] factor 0.0 is no enhancement - * \return pixd always - * - *
- * Notes: - * (1) pixd must either be null or equal to pixs. - * For in-place operation, set pixd == pixs: - * pixContrastTRC(pixs, pixs, ...); - * To get a new image, set pixd == null: - * pixd = pixContrastTRC(NULL, pixs, ...); - * (2) If pixs is colormapped, the colormap is transformed, - * either in-place or in a copy of pixs. - * (3) Contrast is enhanced by mapping each color component - * using an atan function with maximum slope at 127. - * Pixels below 127 are lowered in intensity and pixels - * above 127 are increased. - * (4) The useful range for the contrast factor is scaled to - * be in (0.0 to 1.0), but larger values can also be used. - * (5) If factor == 0.0, no enhancement is performed; return a copy - * unless in-place, in which case this is a no-op. - * (6) For color images that are not colormapped, the mapping - * is applied to each component. - *- */ -PIX * -pixContrastTRC(PIX *pixd, - PIX *pixs, - l_float32 factor) -{ -l_int32 d; -NUMA *nac; -PIXCMAP *cmap; - - PROCNAME("pixContrastTRC"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixd && (pixd != pixs)) - return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); - if (factor < 0.0) { - L_WARNING("factor must be >= 0.0; using 0.0\n", procName); - factor = 0.0; - } - if (factor == 0.0) - return pixCopy(pixd, pixs); - - cmap = pixGetColormap(pixs); - d = pixGetDepth(pixs); - if (!cmap && d != 8 && d != 32) - return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, pixd); - - if (!pixd) /* start with a copy if not in-place */ - pixd = pixCopy(NULL, pixs); - - if (cmap) { - pixcmapContrastTRC(pixGetColormap(pixd), factor); - return pixd; - } - - /* pixd is 8 or 32 bpp */ - if ((nac = numaContrastTRC(factor)) == NULL) - return (PIX *)ERROR_PTR("nac not made", procName, pixd); - pixTRCMap(pixd, NULL, nac); - numaDestroy(&nac); - - return pixd; -} - - -/*! - * \brief pixContrastTRCMasked() - * - * \param[in] pixd [optional] null or equal to pixs - * \param[in] pixs 8 or 32 bpp; or 2, 4 or 8 bpp with colormap - * \param[in] pixm [optional] null or 1 bpp - * \param[in] factor 0.0 is no enhancement - * \return pixd always - * - *
- * Notes: - * (1) Same as pixContrastTRC() except mapping is optionally over - * a subset of pixels described by pixm. - * (2) Masking does not work for colormapped images. - * (3) See pixContrastTRC() for details on how to use the parameters. - *- */ -PIX * -pixContrastTRCMasked(PIX *pixd, - PIX *pixs, - PIX *pixm, - l_float32 factor) -{ -l_int32 d; -NUMA *nac; - - PROCNAME("pixContrastTRCMasked"); - - if (!pixm) - return pixContrastTRC(pixd, pixs, factor); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("invalid: pixs has a colormap", procName, pixd); - if (pixd && (pixd != pixs)) - return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); - d = pixGetDepth(pixs); - if (d != 8 && d != 32) - return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, pixd); - - if (factor < 0.0) { - L_WARNING("factor must be >= 0.0; using 0.0\n", procName); - factor = 0.0; - } - if (factor == 0.0) - return pixCopy(pixd, pixs); - - if (!pixd) /* start with a copy if not in-place */ - pixd = pixCopy(NULL, pixs); - - if ((nac = numaContrastTRC(factor)) == NULL) - return (PIX *)ERROR_PTR("nac not made", procName, pixd); - pixTRCMap(pixd, pixm, nac); - numaDestroy(&nac); - - return pixd; -} - - -/*! - * \brief numaContrastTRC() - * - * \param[in] factor generally between 0.0 [no enhancement] - * and 1.0, but can be larger than 1.0 - * \return na, or NULL on error - * - *
- * Notes: - * (1) The mapping is monotonic increasing, where 0 is mapped - * to 0 and 255 is mapped to 255. - * (2) As 'factor' is increased from 0.0 (where the mapping is linear), - * the map gets closer to its limit as a step function that - * jumps from 0 to 255 at the center (input value = 127). - *- */ -NUMA * -numaContrastTRC(l_float32 factor) -{ -l_int32 i, val; -l_float64 x, ymax, ymin, dely, scale; -NUMA *na; - - PROCNAME("numaContrastTRC"); - - if (factor < 0.0) { - L_WARNING("factor must be >= 0.0; using 0.0; no enhancement\n", - procName); - factor = 0.0; - } - if (factor == 0.0) - return numaMakeSequence(0, 1, 256); /* linear map */ - - scale = EnhanceScaleFactor; - ymax = atan((l_float64)(1.0 * factor * scale)); - ymin = atan((l_float64)(-127. * factor * scale / 128.)); - dely = ymax - ymin; - na = numaCreate(256); - for (i = 0; i < 256; i++) { - x = (l_float64)i; - val = (l_int32)((255. / dely) * - (-ymin + atan((l_float64)(factor * scale * (x - 127.) / 128.))) + - 0.5); - numaAddNumber(na, val); - } - - return na; -} - - -/*-------------------------------------------------------------* - * Histogram equalization * - *-------------------------------------------------------------*/ -/*! - * \brief pixEqualizeTRC() - * - * \param[in] pixd [optional] null or equal to pixs - * \param[in] pixs 8 bpp gray, 32 bpp rgb, or colormapped - * \param[in] fract fraction of equalization movement of pixel values - * \param[in] factor subsampling factor; integer >= 1 - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) pixd must either be null or equal to pixs. - * For in-place operation, set pixd == pixs: - * pixEqualizeTRC(pixs, pixs, ...); - * To get a new image, set pixd == null: - * pixd = pixEqualizeTRC(NULL, pixs, ...); - * (2) In histogram equalization, a tone reproduction curve - * mapping is used to make the number of pixels at each - * intensity equal. - * (3) If fract == 0.0, no equalization is performed; return a copy - * unless in-place, in which case this is a no-op. - * If fract == 1.0, equalization is complete. - * (4) Set the subsampling factor > 1 to reduce the amount of computation. - * (5) If pixs is colormapped, the colormap is removed and - * converted to rgb or grayscale. - * (6) If pixs has color, equalization is done in each channel - * separately. - * (7) Note that even if there is a colormap, we can get an - * in-place operation because the intermediate image pixt - * is copied back to pixs (which for in-place is the same - * as pixd). - *- */ -PIX * -pixEqualizeTRC(PIX *pixd, - PIX *pixs, - l_float32 fract, - l_int32 factor) -{ -l_int32 d; -NUMA *na; -PIX *pixt, *pix8; -PIXCMAP *cmap; - - PROCNAME("pixEqualizeTRC"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixd && (pixd != pixs)) - return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); - cmap = pixGetColormap(pixs); - d = pixGetDepth(pixs); - if (d != 8 && d != 32 && !cmap) - return (PIX *)ERROR_PTR("pixs not 8/32 bpp or cmapped", procName, NULL); - if (fract < 0.0 || fract > 1.0) - return (PIX *)ERROR_PTR("fract not in [0.0 ... 1.0]", procName, NULL); - if (factor < 1) - return (PIX *)ERROR_PTR("sampling factor < 1", procName, NULL); - - if (fract == 0.0) - return pixCopy(pixd, pixs); - - /* If there is a colormap, remove it. */ - if (cmap) - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - else - pixt = pixClone(pixs); - - /* Make a copy if necessary */ - pixd = pixCopy(pixd, pixt); - pixDestroy(&pixt); - - d = pixGetDepth(pixd); - if (d == 8) { - na = numaEqualizeTRC(pixd, fract, factor); - pixTRCMap(pixd, NULL, na); - numaDestroy(&na); - } else { /* 32 bpp */ - pix8 = pixGetRGBComponent(pixd, COLOR_RED); - na = numaEqualizeTRC(pix8, fract, factor); - pixTRCMap(pix8, NULL, na); - pixSetRGBComponent(pixd, pix8, COLOR_RED); - numaDestroy(&na); - pixDestroy(&pix8); - pix8 = pixGetRGBComponent(pixd, COLOR_GREEN); - na = numaEqualizeTRC(pix8, fract, factor); - pixTRCMap(pix8, NULL, na); - pixSetRGBComponent(pixd, pix8, COLOR_GREEN); - numaDestroy(&na); - pixDestroy(&pix8); - pix8 = pixGetRGBComponent(pixd, COLOR_BLUE); - na = numaEqualizeTRC(pix8, fract, factor); - pixTRCMap(pix8, NULL, na); - pixSetRGBComponent(pixd, pix8, COLOR_BLUE); - numaDestroy(&na); - pixDestroy(&pix8); - } - - return pixd; -} - - -/*! - * \brief numaEqualizeTRC() - * - * \param[in] pix 8 bpp, no colormap - * \param[in] fract fraction of equalization movement of pixel values - * \param[in] factor subsampling factor; integer >= 1 - * \return nad, or NULL on error - * - *
- * Notes: - * (1) If fract == 0.0, no equalization will be performed. - * If fract == 1.0, equalization is complete. - * (2) Set the subsampling factor > 1 to reduce the amount of computation. - * (3) The map is returned as a numa with 256 values, specifying - * the equalized value (array value) for every input value - * (the array index). - *- */ -NUMA * -numaEqualizeTRC(PIX *pix, - l_float32 fract, - l_int32 factor) -{ -l_int32 iin, iout, itarg; -l_float32 val, sum; -NUMA *nah, *nasum, *nad; - - PROCNAME("numaEqualizeTRC"); - - if (!pix) - return (NUMA *)ERROR_PTR("pix not defined", procName, NULL); - if (pixGetDepth(pix) != 8) - return (NUMA *)ERROR_PTR("pix not 8 bpp", procName, NULL); - if (fract < 0.0 || fract > 1.0) - return (NUMA *)ERROR_PTR("fract not in [0.0 ... 1.0]", procName, NULL); - if (factor < 1) - return (NUMA *)ERROR_PTR("sampling factor < 1", procName, NULL); - - if (fract == 0.0) - L_WARNING("fract = 0.0; no equalization requested\n", procName); - - if ((nah = pixGetGrayHistogram(pix, factor)) == NULL) - return (NUMA *)ERROR_PTR("histogram not made", procName, NULL); - numaGetSum(nah, &sum); - nasum = numaGetPartialSums(nah); - - nad = numaCreate(256); - for (iin = 0; iin < 256; iin++) { - numaGetFValue(nasum, iin, &val); - itarg = (l_int32)(255. * val / sum + 0.5); - iout = iin + (l_int32)(fract * (itarg - iin)); - iout = L_MIN(iout, 255); /* to be safe */ - numaAddNumber(nad, iout); - } - - numaDestroy(&nah); - numaDestroy(&nasum); - return nad; -} - - -/*-------------------------------------------------------------* - * Generic TRC mapping * - *-------------------------------------------------------------*/ -/*! - * \brief pixTRCMap() - * - * \param[in] pixs 8 grayscale or 32 bpp rgb; not colormapped - * \param[in] pixm [optional] 1 bpp mask - * \param[in] na mapping array - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) This operation is in-place on pixs. - * (2) For 32 bpp, this applies the same map to each of the r,g,b - * components. - * (3) The mapping array is of size 256, and it maps the input - * index into values in the range [0, 255]. - * (4) If defined, the optional 1 bpp mask pixm has its origin - * aligned with pixs, and the map function is applied only - * to pixels in pixs under the fg of pixm. - * (5) For 32 bpp, this does not save the alpha channel. - *- */ -l_int32 -pixTRCMap(PIX *pixs, - PIX *pixm, - NUMA *na) -{ -l_int32 w, h, d, wm, hm, wpl, wplm, i, j, sval8, dval8; -l_uint32 sval32, dval32; -l_uint32 *data, *datam, *line, *linem, *tab; - - PROCNAME("pixTRCMap"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetColormap(pixs)) - return ERROR_INT("pixs is colormapped", procName, 1); - if (!na) - return ERROR_INT("na not defined", procName, 1); - if (numaGetCount(na) != 256) - return ERROR_INT("na not of size 256", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8 && d != 32) - return ERROR_INT("pixs not 8 or 32 bpp", procName, 1); - if (pixm) { - if (pixGetDepth(pixm) != 1) - return ERROR_INT("pixm not 1 bpp", procName, 1); - } - - tab = (l_uint32 *)numaGetIArray(na); /* get the array for efficiency */ - wpl = pixGetWpl(pixs); - data = pixGetData(pixs); - if (!pixm) { - if (d == 8) { - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - sval8 = GET_DATA_BYTE(line, j); - dval8 = tab[sval8]; - SET_DATA_BYTE(line, j, dval8); - } - } - } else { /* d == 32 */ - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - sval32 = *(line + j); - dval32 = - tab[(sval32 >> L_RED_SHIFT) & 0xff] << L_RED_SHIFT | - tab[(sval32 >> L_GREEN_SHIFT) & 0xff] << L_GREEN_SHIFT | - tab[(sval32 >> L_BLUE_SHIFT) & 0xff] << L_BLUE_SHIFT; - *(line + j) = dval32; - } - } - } - } else { - datam = pixGetData(pixm); - wplm = pixGetWpl(pixm); - pixGetDimensions(pixm, &wm, &hm, NULL); - if (d == 8) { - for (i = 0; i < h; i++) { - if (i >= hm) - break; - line = data + i * wpl; - linem = datam + i * wplm; - for (j = 0; j < w; j++) { - if (j >= wm) - break; - if (GET_DATA_BIT(linem, j) == 0) - continue; - sval8 = GET_DATA_BYTE(line, j); - dval8 = tab[sval8]; - SET_DATA_BYTE(line, j, dval8); - } - } - } else { /* d == 32 */ - for (i = 0; i < h; i++) { - if (i >= hm) - break; - line = data + i * wpl; - linem = datam + i * wplm; - for (j = 0; j < w; j++) { - if (j >= wm) - break; - if (GET_DATA_BIT(linem, j) == 0) - continue; - sval32 = *(line + j); - dval32 = - tab[(sval32 >> L_RED_SHIFT) & 0xff] << L_RED_SHIFT | - tab[(sval32 >> L_GREEN_SHIFT) & 0xff] << L_GREEN_SHIFT | - tab[(sval32 >> L_BLUE_SHIFT) & 0xff] << L_BLUE_SHIFT; - *(line + j) = dval32; - } - } - } - } - - LEPT_FREE(tab); - return 0; -} - - -/*! - * \brief pixTRCMapGeneral() - * - * \param[in] pixs 32 bpp rgb; not colormapped - * \param[in] pixm [optional] 1 bpp mask - * \param[in] nar, nag, nab mapping arrays - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) This operation is in-place on %pixs. - * (2) Each of the r,g,b mapping arrays is of size 256. They map the - * input value for that color component into values in the - * range [0, 255]. - * (3) In the special case where the r, g and b mapping arrays are - * all the same, call pixTRCMap() instead. - * (4) If defined, the optional 1 bpp mask %pixm has its origin - * aligned with %pixs, and the map function is applied only - * to pixels in %pixs under the fg of pixm. - * (5) The alpha channel is not saved. - *- */ -l_int32 -pixTRCMapGeneral(PIX *pixs, - PIX *pixm, - NUMA *nar, - NUMA *nag, - NUMA *nab) -{ -l_int32 w, h, wm, hm, wpl, wplm, i, j; -l_uint32 sval32, dval32; -l_uint32 *data, *datam, *line, *linem, *tabr, *tabg, *tabb; - - PROCNAME("pixTRCMapGeneral"); - - if (!pixs || pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); - if (pixm && pixGetDepth(pixm) != 1) - return ERROR_INT("pixm defined and not 1 bpp", procName, 1); - if (!nar || !nag || !nab) - return ERROR_INT("na{r,g,b} not all defined", procName, 1); - if (numaGetCount(nar) != 256 || numaGetCount(nag) != 256 || - numaGetCount(nab) != 256) - return ERROR_INT("na{r,g,b} not all of size 256", procName, 1); - - /* Get the arrays for efficiency */ - tabr = (l_uint32 *)numaGetIArray(nar); - tabg = (l_uint32 *)numaGetIArray(nag); - tabb = (l_uint32 *)numaGetIArray(nab); - pixGetDimensions(pixs, &w, &h, NULL); - wpl = pixGetWpl(pixs); - data = pixGetData(pixs); - if (!pixm) { - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - sval32 = *(line + j); - dval32 = - tabr[(sval32 >> L_RED_SHIFT) & 0xff] << L_RED_SHIFT | - tabg[(sval32 >> L_GREEN_SHIFT) & 0xff] << L_GREEN_SHIFT | - tabb[(sval32 >> L_BLUE_SHIFT) & 0xff] << L_BLUE_SHIFT; - *(line + j) = dval32; - } - } - } else { - datam = pixGetData(pixm); - wplm = pixGetWpl(pixm); - pixGetDimensions(pixm, &wm, &hm, NULL); - for (i = 0; i < h; i++) { - if (i >= hm) - break; - line = data + i * wpl; - linem = datam + i * wplm; - for (j = 0; j < w; j++) { - if (j >= wm) - break; - if (GET_DATA_BIT(linem, j) == 0) - continue; - sval32 = *(line + j); - dval32 = - tabr[(sval32 >> L_RED_SHIFT) & 0xff] << L_RED_SHIFT | - tabg[(sval32 >> L_GREEN_SHIFT) & 0xff] << L_GREEN_SHIFT | - tabb[(sval32 >> L_BLUE_SHIFT) & 0xff] << L_BLUE_SHIFT; - *(line + j) = dval32; - } - } - } - - LEPT_FREE(tabr); - LEPT_FREE(tabg); - LEPT_FREE(tabb); - return 0; -} - - - -/*-----------------------------------------------------------------------* - * Unsharp masking * - *-----------------------------------------------------------------------*/ -/*! - * \brief pixUnsharpMasking() - * - * \param[in] pixs all depths except 1 bpp; with or without colormaps - * \param[in] halfwidth "half-width" of smoothing filter - * \param[in] fract fraction of edge added back into image - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) We use symmetric smoothing filters of odd dimension, - * typically use sizes of 3, 5, 7, etc. The %halfwidth parameter - * for these is (size - 1)/2; i.e., 1, 2, 3, etc. - * (2) The fract parameter is typically taken in the - * range: 0.2 < fract < 0.7 - * (3) Returns a clone if no sharpening is requested. - *- */ -PIX * -pixUnsharpMasking(PIX *pixs, - l_int32 halfwidth, - l_float32 fract) -{ -l_int32 d; -PIX *pixt, *pixd, *pixr, *pixrs, *pixg, *pixgs, *pixb, *pixbs; - - PROCNAME("pixUnsharpMasking"); - - if (!pixs || (pixGetDepth(pixs) == 1)) - return (PIX *)ERROR_PTR("pixs not defined or 1 bpp", procName, NULL); - if (fract <= 0.0 || halfwidth <= 0) { - L_WARNING("no sharpening requested; clone returned\n", procName); - return pixClone(pixs); - } - - if (halfwidth == 1 || halfwidth == 2) - return pixUnsharpMaskingFast(pixs, halfwidth, fract, L_BOTH_DIRECTIONS); - - /* Remove colormap; clone if possible; result is either 8 or 32 bpp */ - if ((pixt = pixConvertTo8Or32(pixs, L_CLONE, 0)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, NULL); - - /* Sharpen */ - d = pixGetDepth(pixt); - if (d == 8) { - pixd = pixUnsharpMaskingGray(pixt, halfwidth, fract); - } else { /* d == 32 */ - pixr = pixGetRGBComponent(pixs, COLOR_RED); - pixrs = pixUnsharpMaskingGray(pixr, halfwidth, fract); - pixDestroy(&pixr); - pixg = pixGetRGBComponent(pixs, COLOR_GREEN); - pixgs = pixUnsharpMaskingGray(pixg, halfwidth, fract); - pixDestroy(&pixg); - pixb = pixGetRGBComponent(pixs, COLOR_BLUE); - pixbs = pixUnsharpMaskingGray(pixb, halfwidth, fract); - pixDestroy(&pixb); - pixd = pixCreateRGBImage(pixrs, pixgs, pixbs); - pixDestroy(&pixrs); - pixDestroy(&pixgs); - pixDestroy(&pixbs); - if (pixGetSpp(pixs) == 4) - pixScaleAndTransferAlpha(pixd, pixs, 1.0, 1.0); - } - - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixUnsharpMaskingGray() - * - * \param[in] pixs 8 bpp; no colormap - * \param[in] halfwidth "half-width" of smoothing filter - * \param[in] fract fraction of edge added back into image - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) We use symmetric smoothing filters of odd dimension, - * typically use sizes of 3, 5, 7, etc. The %halfwidth parameter - * for these is (size - 1)/2; i.e., 1, 2, 3, etc. - * (2) The fract parameter is typically taken in the range: - * 0.2 < fract < 0.7 - * (3) Returns a clone if no sharpening is requested. - *- */ -PIX * -pixUnsharpMaskingGray(PIX *pixs, - l_int32 halfwidth, - l_float32 fract) -{ -l_int32 w, h, d; -PIX *pixc, *pixd; -PIXACC *pixacc; - - PROCNAME("pixUnsharpMaskingGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8 || pixGetColormap(pixs) != NULL) - return (PIX *)ERROR_PTR("pixs not 8 bpp or has cmap", procName, NULL); - if (fract <= 0.0 || halfwidth <= 0) { - L_WARNING("no sharpening requested; clone returned\n", procName); - return pixClone(pixs); - } - if (halfwidth == 1 || halfwidth == 2) - return pixUnsharpMaskingGrayFast(pixs, halfwidth, fract, - L_BOTH_DIRECTIONS); - - if ((pixc = pixBlockconvGray(pixs, NULL, halfwidth, halfwidth)) == NULL) - return (PIX *)ERROR_PTR("pixc not made", procName, NULL); - - /* Steps: - * (1) edge image is pixs - pixc (this is highpass part) - * (2) multiply edge image by fract - * (3) add fraction of edge to pixs - * - * To show how this is done with both interfaces to arithmetic - * on integer Pix, here is the implementation in the lower-level - * function calls: - * pixt = pixInitAccumulate(w, h, 0x10000000)) == NULL) - * pixAccumulate(pixt, pixs, L_ARITH_ADD); - * pixAccumulate(pixt, pixc, L_ARITH_SUBTRACT); - * pixMultConstAccumulate(pixt, fract, 0x10000000); - * pixAccumulate(pixt, pixs, L_ARITH_ADD); - * pixd = pixFinalAccumulate(pixt, 0x10000000, 8)) == NULL) - * pixDestroy(&pixt); - * - * The code below does the same thing using the Pixacc accumulator, - * hiding the details of the offset that is needed for subtraction. - */ - pixacc = pixaccCreate(w, h, 1); - pixaccAdd(pixacc, pixs); - pixaccSubtract(pixacc, pixc); - pixaccMultConst(pixacc, fract); - pixaccAdd(pixacc, pixs); - pixd = pixaccFinal(pixacc, 8); - pixaccDestroy(&pixacc); - - pixDestroy(&pixc); - return pixd; -} - - -/*! - * \brief pixUnsharpMaskingFast() - * - * \param[in] pixs all depths except 1 bpp; with or without colormaps - * \param[in] halfwidth "half-width" of smoothing filter; 1 and 2 only - * \param[in] fract fraction of high frequency added to image - * \param[in] direction L_HORIZ, L_VERT, L_BOTH_DIRECTIONS - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) The fast version uses separable 1-D filters directly on - * the input image. The halfwidth is either 1 (full width = 3) - * or 2 (full width = 5). - * (2) The fract parameter is typically taken in the - * range: 0.2 < fract < 0.7 - * (3) To skip horizontal sharpening, use %fracth = 0.0; ditto for %fractv - * (4) For one dimensional filtering (as an example): - * For %halfwidth = 1, the low-pass filter is - * L: 1/3 1/3 1/3 - * and the high-pass filter is - * H = I - L: -1/3 2/3 -1/3 - * For %halfwidth = 2, the low-pass filter is - * L: 1/5 1/5 1/5 1/5 1/5 - * and the high-pass filter is - * H = I - L: -1/5 -1/5 4/5 -1/5 -1/5 - * The new sharpened pixel value is found by adding some fraction - * of the high-pass filter value (which sums to 0) to the - * initial pixel value: - * N = I + fract * H - * (5) For 2D, the sharpening filter is not separable, because the - * vertical filter depends on the horizontal location relative - * to the filter origin, and v.v. So we either do the full - * 2D filter (for %halfwidth == 1) or do the low-pass - * convolution separably and then compose with the original pix. - * (6) Returns a clone if no sharpening is requested. - *- */ -PIX * -pixUnsharpMaskingFast(PIX *pixs, - l_int32 halfwidth, - l_float32 fract, - l_int32 direction) -{ -l_int32 d; -PIX *pixt, *pixd, *pixr, *pixrs, *pixg, *pixgs, *pixb, *pixbs; - - PROCNAME("pixUnsharpMaskingFast"); - - if (!pixs || (pixGetDepth(pixs) == 1)) - return (PIX *)ERROR_PTR("pixs not defined or 1 bpp", procName, NULL); - if (fract <= 0.0 || halfwidth <= 0) { - L_WARNING("no sharpening requested; clone returned\n", procName); - return pixClone(pixs); - } - if (halfwidth != 1 && halfwidth != 2) - return (PIX *)ERROR_PTR("halfwidth must be 1 or 2", procName, NULL); - if (direction != L_HORIZ && direction != L_VERT && - direction != L_BOTH_DIRECTIONS) - return (PIX *)ERROR_PTR("invalid direction", procName, NULL); - - /* Remove colormap; clone if possible; result is either 8 or 32 bpp */ - if ((pixt = pixConvertTo8Or32(pixs, L_CLONE, 0)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, NULL); - - /* Sharpen */ - d = pixGetDepth(pixt); - if (d == 8) { - pixd = pixUnsharpMaskingGrayFast(pixt, halfwidth, fract, direction); - } else { /* d == 32 */ - pixr = pixGetRGBComponent(pixs, COLOR_RED); - pixrs = pixUnsharpMaskingGrayFast(pixr, halfwidth, fract, direction); - pixDestroy(&pixr); - pixg = pixGetRGBComponent(pixs, COLOR_GREEN); - pixgs = pixUnsharpMaskingGrayFast(pixg, halfwidth, fract, direction); - pixDestroy(&pixg); - pixb = pixGetRGBComponent(pixs, COLOR_BLUE); - pixbs = pixUnsharpMaskingGrayFast(pixb, halfwidth, fract, direction); - pixDestroy(&pixb); - pixd = pixCreateRGBImage(pixrs, pixgs, pixbs); - if (pixGetSpp(pixs) == 4) - pixScaleAndTransferAlpha(pixd, pixs, 1.0, 1.0); - pixDestroy(&pixrs); - pixDestroy(&pixgs); - pixDestroy(&pixbs); - } - - pixDestroy(&pixt); - return pixd; -} - - - -/*! - * \brief pixUnsharpMaskingGrayFast() - * - * \param[in] pixs 8 bpp; no colormap - * \param[in] halfwidth "half-width" of smoothing filter: 1 or 2 - * \param[in] fract fraction of high frequency added to image - * \param[in] direction L_HORIZ, L_VERT, L_BOTH_DIRECTIONS - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) For usage and explanation of the algorithm, see notes - * in pixUnsharpMaskingFast(). - * (2) Returns a clone if no sharpening is requested. - *- */ -PIX * -pixUnsharpMaskingGrayFast(PIX *pixs, - l_int32 halfwidth, - l_float32 fract, - l_int32 direction) -{ -PIX *pixd; - - PROCNAME("pixUnsharpMaskingGrayFast"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8 || pixGetColormap(pixs) != NULL) - return (PIX *)ERROR_PTR("pixs not 8 bpp or has cmap", procName, NULL); - if (fract <= 0.0 || halfwidth <= 0) { - L_WARNING("no sharpening requested; clone returned\n", procName); - return pixClone(pixs); - } - if (halfwidth != 1 && halfwidth != 2) - return (PIX *)ERROR_PTR("halfwidth must be 1 or 2", procName, NULL); - if (direction != L_HORIZ && direction != L_VERT && - direction != L_BOTH_DIRECTIONS) - return (PIX *)ERROR_PTR("invalid direction", procName, NULL); - - if (direction != L_BOTH_DIRECTIONS) - pixd = pixUnsharpMaskingGray1D(pixs, halfwidth, fract, direction); - else /* 2D sharpening */ - pixd = pixUnsharpMaskingGray2D(pixs, halfwidth, fract); - - return pixd; -} - - -/*! - * \brief pixUnsharpMaskingGray1D() - * - * \param[in] pixs 8 bpp; no colormap - * \param[in] halfwidth "half-width" of smoothing filter: 1 or 2 - * \param[in] fract fraction of high frequency added to image - * \param[in] direction filtering direction; use L_HORIZ or L_VERT - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) For usage and explanation of the algorithm, see notes - * in pixUnsharpMaskingFast(). - * (2) Returns a clone if no sharpening is requested. - *- */ -PIX * -pixUnsharpMaskingGray1D(PIX *pixs, - l_int32 halfwidth, - l_float32 fract, - l_int32 direction) -{ -l_int32 w, h, d, wpls, wpld, i, j, ival; -l_uint32 *datas, *datad; -l_uint32 *lines, *lines0, *lines1, *lines2, *lines3, *lines4, *lined; -l_float32 val, a[5]; -PIX *pixd; - - PROCNAME("pixUnsharpMaskingGray1D"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8 || pixGetColormap(pixs) != NULL) - return (PIX *)ERROR_PTR("pixs not 8 bpp or has cmap", procName, NULL); - if (fract <= 0.0 || halfwidth <= 0) { - L_WARNING("no sharpening requested; clone returned\n", procName); - return pixClone(pixs); - } - if (halfwidth != 1 && halfwidth != 2) - return (PIX *)ERROR_PTR("halfwidth must be 1 or 2", procName, NULL); - - /* Initialize pixd with pixels from pixs that will not be - * set when computing the sharpened values. */ - pixd = pixCopyBorder(NULL, pixs, halfwidth, halfwidth, - halfwidth, halfwidth); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - - if (halfwidth == 1) { - a[0] = -fract / 3.0; - a[1] = 1.0 + fract * 2.0 / 3.0; - a[2] = a[0]; - } else { /* halfwidth == 2 */ - a[0] = -fract / 5.0; - a[1] = a[0]; - a[2] = 1.0 + fract * 4.0 / 5.0; - a[3] = a[0]; - a[4] = a[0]; - } - - if (direction == L_HORIZ) { - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - if (halfwidth == 1) { - for (j = 1; j < w - 1; j++) { - val = a[0] * GET_DATA_BYTE(lines, j - 1) + - a[1] * GET_DATA_BYTE(lines, j) + - a[2] * GET_DATA_BYTE(lines, j + 1); - ival = (l_int32)val; - ival = L_MAX(0, ival); - ival = L_MIN(255, ival); - SET_DATA_BYTE(lined, j, ival); - } - } else { /* halfwidth == 2 */ - for (j = 2; j < w - 2; j++) { - val = a[0] * GET_DATA_BYTE(lines, j - 2) + - a[1] * GET_DATA_BYTE(lines, j - 1) + - a[2] * GET_DATA_BYTE(lines, j) + - a[3] * GET_DATA_BYTE(lines, j + 1) + - a[4] * GET_DATA_BYTE(lines, j + 2); - ival = (l_int32)val; - ival = L_MAX(0, ival); - ival = L_MIN(255, ival); - SET_DATA_BYTE(lined, j, ival); - } - } - } - } else { /* direction == L_VERT */ - if (halfwidth == 1) { - for (i = 1; i < h - 1; i++) { - lines0 = datas + (i - 1) * wpls; - lines1 = datas + i * wpls; - lines2 = datas + (i + 1) * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = a[0] * GET_DATA_BYTE(lines0, j) + - a[1] * GET_DATA_BYTE(lines1, j) + - a[2] * GET_DATA_BYTE(lines2, j); - ival = (l_int32)val; - ival = L_MAX(0, ival); - ival = L_MIN(255, ival); - SET_DATA_BYTE(lined, j, ival); - } - } - } else { /* halfwidth == 2 */ - for (i = 2; i < h - 2; i++) { - lines0 = datas + (i - 2) * wpls; - lines1 = datas + (i - 1) * wpls; - lines2 = datas + i * wpls; - lines3 = datas + (i + 1) * wpls; - lines4 = datas + (i + 2) * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = a[0] * GET_DATA_BYTE(lines0, j) + - a[1] * GET_DATA_BYTE(lines1, j) + - a[2] * GET_DATA_BYTE(lines2, j) + - a[3] * GET_DATA_BYTE(lines3, j) + - a[4] * GET_DATA_BYTE(lines4, j); - ival = (l_int32)val; - ival = L_MAX(0, ival); - ival = L_MIN(255, ival); - SET_DATA_BYTE(lined, j, ival); - } - } - } - } - - return pixd; -} - - -/*! - * \brief pixUnsharpMaskingGray2D() - * - * \param[in] pixs 8 bpp; no colormap - * \param[in] halfwidth "half-width" of smoothing filter: 1 or 2 - * \param[in] fract fraction of high frequency added to image - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) This is for %halfwidth == 1, 2. - * (2) The lowpass filter is implemented separably. - * (3) Returns a clone if no sharpening is requested. - *- */ -PIX * -pixUnsharpMaskingGray2D(PIX *pixs, - l_int32 halfwidth, - l_float32 fract) -{ -l_int32 w, h, d, wpls, wpld, wplf, i, j, ival, sval; -l_uint32 *datas, *datad, *lines, *lined; -l_float32 val, norm; -l_float32 *dataf, *linef, *linef0, *linef1, *linef2, *linef3, *linef4; -PIX *pixd; -FPIX *fpix; - - PROCNAME("pixUnsharpMaskingGray2D"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8 || pixGetColormap(pixs) != NULL) - return (PIX *)ERROR_PTR("pixs not 8 bpp or has cmap", procName, NULL); - if (fract <= 0.0 || halfwidth <= 0) { - L_WARNING("no sharpening requested; clone returned\n", procName); - return pixClone(pixs); - } - if (halfwidth != 1 && halfwidth != 2) - return (PIX *)ERROR_PTR("halfwidth must be 1 or 2", procName, NULL); - - if ((pixd = pixCopyBorder(NULL, pixs, halfwidth, halfwidth, - halfwidth, halfwidth)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - - /* Do the low pass separably. Store the result of horizontal - * smoothing in an intermediate fpix. */ - if ((fpix = fpixCreate(w, h)) == NULL) { - pixDestroy(&pixd); - return (PIX *)ERROR_PTR("fpix not made", procName, NULL); - } - dataf = fpixGetData(fpix); - wplf = fpixGetWpl(fpix); - if (halfwidth == 1) { - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - linef = dataf + i * wplf; - for (j = 1; j < w - 1; j++) { - val = GET_DATA_BYTE(lines, j - 1) + - GET_DATA_BYTE(lines, j) + - GET_DATA_BYTE(lines, j + 1); - linef[j] = val; - } - } - } else { - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - linef = dataf + i * wplf; - for (j = 2; j < w - 2; j++) { - val = GET_DATA_BYTE(lines, j - 2) + - GET_DATA_BYTE(lines, j - 1) + - GET_DATA_BYTE(lines, j) + - GET_DATA_BYTE(lines, j + 1) + - GET_DATA_BYTE(lines, j + 2); - linef[j] = val; - } - } - } - - /* Do vertical smoothing to finish the low-pass filter. - * At each pixel, if L is the lowpass value, I is the - * src pixel value and f is the fraction of highpass to - * be added to I, then the highpass filter value is - * H = I - L - * and the new sharpened value is - * N = I + f * H. */ - if (halfwidth == 1) { - for (i = 1; i < h - 1; i++) { - linef0 = dataf + (i - 1) * wplf; - linef1 = dataf + i * wplf; - linef2 = dataf + (i + 1) * wplf; - lined = datad + i * wpld; - lines = datas + i * wpls; - norm = 1.0 / 9.0; - for (j = 1; j < w - 1; j++) { - val = norm * (linef0[j] + linef1[j] + - linef2[j]); /* L: lowpass filter value */ - sval = GET_DATA_BYTE(lines, j); /* I: source pixel */ - ival = (l_int32)(sval + fract * (sval - val) + 0.5); - ival = L_MAX(0, ival); - ival = L_MIN(255, ival); - SET_DATA_BYTE(lined, j, ival); - } - } - } else { - for (i = 2; i < h - 2; i++) { - linef0 = dataf + (i - 2) * wplf; - linef1 = dataf + (i - 1) * wplf; - linef2 = dataf + i * wplf; - linef3 = dataf + (i + 1) * wplf; - linef4 = dataf + (i + 2) * wplf; - lined = datad + i * wpld; - lines = datas + i * wpls; - norm = 1.0 / 25.0; - for (j = 2; j < w - 2; j++) { - val = norm * (linef0[j] + linef1[j] + linef2[j] + linef3[j] + - linef4[j]); /* L: lowpass filter value */ - sval = GET_DATA_BYTE(lines, j); /* I: source pixel */ - ival = (l_int32)(sval + fract * (sval - val) + 0.5); - ival = L_MAX(0, ival); - ival = L_MIN(255, ival); - SET_DATA_BYTE(lined, j, ival); - } - } - } - - fpixDestroy(&fpix); - return pixd; -} - - -/*-----------------------------------------------------------------------* - * Hue and saturation modification * - *-----------------------------------------------------------------------*/ -/*! - * \brief pixModifyHue() - * - * \param[in] pixd [optional] can be null or equal to pixs - * \param[in] pixs 32 bpp rgb - * \param[in] fract between -1.0 and 1.0 - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) pixd must either be null or equal to pixs. - * For in-place operation, set pixd == pixs: - * pixEqualizeTRC(pixs, pixs, ...); - * To get a new image, set pixd == null: - * pixd = pixEqualizeTRC(NULL, pixs, ...); - * (1) Use fract > 0.0 to increase hue value; < 0.0 to decrease it. - * 1.0 (or -1.0) represents a 360 degree rotation; i.e., no change. - * (2) If no modification is requested (fract = -1.0 or 0 or 1.0), - * return a copy unless in-place, in which case this is a no-op. - * (3) See discussion of color-modification methods, in coloring.c. - *- */ -PIX * -pixModifyHue(PIX *pixd, - PIX *pixs, - l_float32 fract) -{ -l_int32 w, h, d, i, j, wpl, delhue; -l_int32 rval, gval, bval, hval, sval, vval; -l_uint32 *data, *line; - - PROCNAME("pixModifyHue"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetColormap(pixs) != NULL) - return (PIX *)ERROR_PTR("pixs colormapped", procName, NULL); - if (pixd && (pixd != pixs)) - return (PIX *)ERROR_PTR("pixd not null or pixs", procName, pixd); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (L_ABS(fract) > 1.0) - return (PIX *)ERROR_PTR("fract not in [-1.0 ... 1.0]", procName, NULL); - - pixd = pixCopy(pixd, pixs); - - delhue = (l_int32)(240 * fract); - if (delhue == 0 || delhue == 240 || delhue == -240) { - L_WARNING("no change requested in hue\n", procName); - return pixd; - } - if (delhue < 0) - delhue += 240; - - data = pixGetData(pixd); - wpl = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - extractRGBValues(line[j], &rval, &gval, &bval); - convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval); - hval = (hval + delhue) % 240; - convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval); - composeRGBPixel(rval, gval, bval, line + j); - } - } - if (pixGetSpp(pixs) == 4) - pixScaleAndTransferAlpha(pixd, pixs, 1.0, 1.0); - - return pixd; -} - - -/*! - * \brief pixModifySaturation() - * - * \param[in] pixd [optional] can be null, existing or equal to pixs - * \param[in] pixs 32 bpp rgb - * \param[in] fract between -1.0 and 1.0 - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) If fract > 0.0, it gives the fraction that the pixel - * saturation is moved from its initial value toward 255. - * If fract < 0.0, it gives the fraction that the pixel - * saturation is moved from its initial value toward 0. - * The limiting values for fract = -1.0 (1.0) thus set the - * saturation to 0 (255). - * (2) If fract = 0, no modification is requested; return a copy - * unless in-place, in which case this is a no-op. - * (3) See discussion of color-modification methods, in coloring.c. - *- */ -PIX * -pixModifySaturation(PIX *pixd, - PIX *pixs, - l_float32 fract) -{ -l_int32 w, h, d, i, j, wpl; -l_int32 rval, gval, bval, hval, sval, vval; -l_uint32 *data, *line; - - PROCNAME("pixModifySaturation"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (L_ABS(fract) > 1.0) - return (PIX *)ERROR_PTR("fract not in [-1.0 ... 1.0]", procName, NULL); - - pixd = pixCopy(pixd, pixs); - if (fract == 0.0) { - L_WARNING("no change requested in saturation\n", procName); - return pixd; - } - - data = pixGetData(pixd); - wpl = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - extractRGBValues(line[j], &rval, &gval, &bval); - convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval); - if (fract < 0.0) - sval = (l_int32)(sval * (1.0 + fract)); - else - sval = (l_int32)(sval + fract * (255 - sval)); - convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval); - composeRGBPixel(rval, gval, bval, line + j); - } - } - if (pixGetSpp(pixs) == 4) - pixScaleAndTransferAlpha(pixd, pixs, 1.0, 1.0); - - return pixd; -} - - -/*! - * \brief pixMeasureSaturation() - * - * \param[in] pixs 32 bpp rgb - * \param[in] factor subsampling factor; integer >= 1 - * \param[out] psat average saturation - * \return 0 if OK, 1 on error - */ -l_int32 -pixMeasureSaturation(PIX *pixs, - l_int32 factor, - l_float32 *psat) -{ -l_int32 w, h, d, i, j, wpl, sum, count; -l_int32 rval, gval, bval, hval, sval, vval; -l_uint32 *data, *line; - - PROCNAME("pixMeasureSaturation"); - - if (!psat) - return ERROR_INT("pixs not defined", procName, 1); - *psat = 0.0; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 32) - return ERROR_INT("pixs not 32 bpp", procName, 1); - if (factor < 1) - return ERROR_INT("subsampling factor < 1", procName, 1); - - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - for (i = 0, sum = 0, count = 0; i < h; i += factor) { - line = data + i * wpl; - for (j = 0; j < w; j += factor) { - extractRGBValues(line[j], &rval, &gval, &bval); - convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval); - sum += sval; - count++; - } - } - - if (count > 0) - *psat = (l_float32)sum / (l_float32)count; - return 0; -} - - -/*! - * \brief pixModifyBrightness() - * - * \param[in] pixd [optional] can be null, existing or equal to pixs - * \param[in] pixs 32 bpp rgb - * \param[in] fract between -1.0 and 1.0 - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) If fract > 0.0, it gives the fraction that the v-parameter, - * which is max(r,g,b), is moved from its initial value toward 255. - * If fract < 0.0, it gives the fraction that the v-parameter - * is moved from its initial value toward 0. - * The limiting values for fract = -1.0 (1.0) thus set the - * v-parameter to 0 (255). - * (2) If fract = 0, no modification is requested; return a copy - * unless in-place, in which case this is a no-op. - * (3) See discussion of color-modification methods, in coloring.c. - *- */ -PIX * -pixModifyBrightness(PIX *pixd, - PIX *pixs, - l_float32 fract) -{ -l_int32 w, h, d, i, j, wpl; -l_int32 rval, gval, bval, hval, sval, vval; -l_uint32 *data, *line; - - PROCNAME("pixModifyBrightness"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (L_ABS(fract) > 1.0) - return (PIX *)ERROR_PTR("fract not in [-1.0 ... 1.0]", procName, NULL); - - pixd = pixCopy(pixd, pixs); - if (fract == 0.0) { - L_WARNING("no change requested in brightness\n", procName); - return pixd; - } - - data = pixGetData(pixd); - wpl = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - extractRGBValues(line[j], &rval, &gval, &bval); - convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval); - if (fract > 0.0) - vval = (l_int32)(vval + fract * (255.0 - vval)); - else - vval = (l_int32)(vval * (1.0 + fract)); - convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval); - composeRGBPixel(rval, gval, bval, line + j); - } - } - if (pixGetSpp(pixs) == 4) - pixScaleAndTransferAlpha(pixd, pixs, 1.0, 1.0); - - return pixd; -} - - -/*-----------------------------------------------------------------------* - * Color shifting * - *-----------------------------------------------------------------------*/ -/*! - * \brief pixMosaicColorShiftRGB() - * - * \param[in] pixs 32 bpp rgb - * \param[in] roff center offset of red component - * \param[in] goff center offset of green component - * \param[in] boff center offset of blue component - * \param[in] delta increments from center offsets [0.0 - 0.1]; - * use 0.0 to get the default (0.04) - * \param[in] nincr number of increments in each (positive and negative) - * direction; use 0 to get the default (2). - * \return pix, or NULL on error - * - *
- * Notes: - * (1) This generates a mosaic view of the effect of shifting the RGB - * components. See pixColorShiftRGB() for details on the shifting. - * (2) The offsets (%roff, %goff, %boff) set the color center point, - * and the deviations from this are shown separately for deltas - * in r, g and b. For each component, we show 2 * %nincr + 1 - * images. - * (3) Usage: color prints differ from the original due to three factors: - * illumination, calibration of the camera in acquisition, - * and calibration of the printer. This function can be used - * to iteratively match a color print to the original. On each - * iteration, the center offsets are set to the best match so - * far, and the %delta increments are typically reduced. - *- */ -PIX * -pixMosaicColorShiftRGB(PIX *pixs, - l_float32 roff, - l_float32 goff, - l_float32 boff, - l_float32 delta, - l_int32 nincr) -{ -char buf[64]; -l_int32 i; -l_float32 del; -L_BMF *bmf; -PIX *pix1, *pix2, *pix3; -PIXA *pixa; - - PROCNAME("pixMosaicColorShiftRGB"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not rgb", procName, NULL); - if (roff < -1.0 || roff > 1.0) - return (PIX *)ERROR_PTR("roff not in [-1.0, 1.0]", procName, NULL); - if (goff < -1.0 || goff > 1.0) - return (PIX *)ERROR_PTR("goff not in [-1.0, 1.0]", procName, NULL); - if (boff < -1.0 || boff > 1.0) - return (PIX *)ERROR_PTR("boff not in [-1.0, 1.0]", procName, NULL); - if (delta < 0.0 || delta > 0.1) - return (PIX *)ERROR_PTR("delta not in [0.0, 0.1]", procName, NULL); - if (delta == 0.0) delta = 0.04; - if (nincr < 0 || nincr > 6) - return (PIX *)ERROR_PTR("nincr not in [0, 6]", procName, NULL); - if (nincr == 0) nincr = 2; - - pixa = pixaCreate(3 * (2 * nincr + 1)); - bmf = bmfCreate(NULL, 8); - pix1 = pixScaleToSize(pixs, 400, 0); - for (i = 0, del = - nincr * delta; i < 2 * nincr + 1; i++, del += delta) { - pix2 = pixColorShiftRGB(pix1, roff + del, goff, boff); - snprintf(buf, sizeof(buf), "%4.2f, %4.2f, %4.2f", - roff + del, goff, boff); - pix3 = pixAddSingleTextblock(pix2, bmf, buf, 0xff000000, - L_ADD_BELOW, 0); - pixaAddPix(pixa, pix3, L_INSERT); - pixDestroy(&pix2); - } - for (i = 0, del = - nincr * delta; i < 2 * nincr + 1; i++, del += delta) { - pix2 = pixColorShiftRGB(pix1, roff, goff + del, boff); - snprintf(buf, sizeof(buf), "%4.2f, %4.2f, %4.2f", - roff, goff + del, boff); - pix3 = pixAddSingleTextblock(pix2, bmf, buf, 0xff000000, - L_ADD_BELOW, 0); - pixaAddPix(pixa, pix3, L_INSERT); - pixDestroy(&pix2); - } - for (i = 0, del = - nincr * delta; i < 2 * nincr + 1; i++, del += delta) { - pix2 = pixColorShiftRGB(pix1, roff, goff, boff + del); - snprintf(buf, sizeof(buf), "%4.2f, %4.2f, %4.2f", - roff, goff, boff + del); - pix3 = pixAddSingleTextblock(pix2, bmf, buf, 0xff000000, - L_ADD_BELOW, 0); - pixaAddPix(pixa, pix3, L_INSERT); - pixDestroy(&pix2); - } - pixDestroy(&pix1); - - pix1 = pixaDisplayTiledAndScaled(pixa, 32, 300, 2 * nincr + 1, 0, 30, 2); - pixaDestroy(&pixa); - bmfDestroy(&bmf); - return pix1; -} - - -/*! - * \brief pixColorShiftRGB() - * - * \param[in] pixs 32 bpp rgb - * \param[in] rfract fractional shift in red component - * \param[in] gfract fractional shift in green component - * \param[in] bfract fractional shift in blue component - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) This allows independent fractional shifts of the r,g and b - * components. A positive shift pushes to saturation (255); - * a negative shift pushes toward 0 (black). - * (2) The effect can be imagined using a color wheel that consists - * (for our purposes) of these 6 colors, separated by 60 degrees: - * red, magenta, blue, cyan, green, yellow - * (3) So, for example, a negative shift of the blue component - * (bfract < 0) could be accompanied by positive shifts - * of red and green to make an image more yellow. - * (4) Examples of limiting cases: - * rfract = 1 ==> r = 255 - * rfract = -1 ==> r = 0 - *- */ -PIX * -pixColorShiftRGB(PIX *pixs, - l_float32 rfract, - l_float32 gfract, - l_float32 bfract) -{ -l_int32 w, h, i, j, wpls, wpld, rval, gval, bval; -l_int32 *rlut, *glut, *blut; -l_uint32 *datas, *datad, *lines, *lined; -l_float32 fi; -PIX *pixd; - - PROCNAME("pixColorShiftRGB"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (rfract < -1.0 || rfract > 1.0) - return (PIX *)ERROR_PTR("rfract not in [-1.0, 1.0]", procName, NULL); - if (gfract < -1.0 || gfract > 1.0) - return (PIX *)ERROR_PTR("gfract not in [-1.0, 1.0]", procName, NULL); - if (bfract < -1.0 || bfract > 1.0) - return (PIX *)ERROR_PTR("bfract not in [-1.0, 1.0]", procName, NULL); - if (rfract == 0.0 && gfract == 0.0 && bfract == 0.0) - return pixCopy(NULL, pixs); - - rlut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - glut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - blut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - for (i = 0; i < 256; i++) { - fi = i; - if (rfract >= 0) { - rlut[i] = (l_int32)(fi + (255.0 - fi) * rfract); - } else { - rlut[i] = (l_int32)(fi * (1.0 + rfract)); - } - if (gfract >= 0) { - glut[i] = (l_int32)(fi + (255.0 - fi) * gfract); - } else { - glut[i] = (l_int32)(fi * (1.0 + gfract)); - } - if (bfract >= 0) { - blut[i] = (l_int32)(fi + (255.0 - fi) * bfract); - } else { - blut[i] = (l_int32)(fi * (1.0 + bfract)); - } - } - - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - pixd = pixCreate(w, h, 32); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - composeRGBPixel(rlut[rval], glut[gval], blut[bval], lined + j); - } - } - - LEPT_FREE(rlut); - LEPT_FREE(glut); - LEPT_FREE(blut); - return pixd; -} - -/*-----------------------------------------------------------------------* - * Darken gray (unsaturated) pixels - *-----------------------------------------------------------------------*/ -/*! - * \brief pixDarkenGray() - * - * \param[in] pixd [optional] can be null or equal to pixs - * \param[in] pixs 32 bpp rgb - * \param[in] thresh pixels with max component >= %thresh are unchanged - * \param[in] satlimit pixels with saturation >= %satlimit are unchanged - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) This darkens gray pixels, by a fraction (sat/%satlimit), where - * the saturation, sat, is the component difference (max - min). - * The pixel value is unchanged if sat >= %satlimit. A typical - * value of %satlimit might be 40; the larger the value, the - * more that pixels with a smaller saturation will be darkened. - * (2) Pixels with max component >= %thresh are unchanged. This can be - * used to prevent bright pixels with low saturation from being - * darkened. Setting thresh == 0 is a no-op; setting %thresh == 255 - * causes the darkening to be applied to all pixels. - * (3) This function is useful to enhance pixels relative to a - * gray background. - * (4) A related function that builds a 1 bpp mask over the gray - * pixels is pixMaskOverGrayPixels(). - *- */ -PIX * -pixDarkenGray(PIX *pixd, - PIX *pixs, - l_int32 thresh, - l_int32 satlimit) -{ -l_int32 w, h, i, j, wpls, wpld; -l_int32 rval, gval, bval, minrg, min, maxrg, max, sat; -l_uint32 *datas, *datad, *lines, *lined; -l_float32 ratio; - - PROCNAME("pixDarkenGray"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); - if (thresh < 0 || thresh > 255) - return (PIX *)ERROR_PTR("invalid thresh", procName, NULL); - if (satlimit < 1) - return (PIX *)ERROR_PTR("invalid satlimit", procName, NULL); - if (pixd && (pixs != pixd)) - return (PIX *)ERROR_PTR("not new or in-place", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if ((pixd = pixCopy(pixd, pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - minrg = L_MIN(rval, gval); - min = L_MIN(minrg, bval); - maxrg = L_MAX(rval, gval); - max = L_MAX(maxrg, bval); - sat = max - min; - if (max >= thresh || sat >= satlimit) - continue; - ratio = (l_float32)sat / (l_float32)satlimit; - composeRGBPixel((l_int32)(ratio * rval), (l_int32)(ratio * gval), - (l_int32)(ratio * bval), &lined[j]); - } - } - return pixd; -} - - -/*-----------------------------------------------------------------------* - * General multiplicative constant color transform * - *-----------------------------------------------------------------------*/ -/*! - * \brief pixMultConstantColor() - * - * \param[in] pixs colormapped or rgb - * \param[in] rfact red multiplicative factor - * \param[in] gfact green multiplicative factor - * \param[in] bfact blue multiplicative factor - * \return pixd colormapped or rgb, with colors scaled, or NULL on error - * - *
- * Notes: - * (1) rfact, gfact and bfact can only have non-negative values. - * They can be greater than 1.0. All transformed component - * values are clipped to the interval [0, 255]. - * (2) For multiplication with a general 3x3 matrix of constants, - * use pixMultMatrixColor(). - *- */ -PIX * -pixMultConstantColor(PIX *pixs, - l_float32 rfact, - l_float32 gfact, - l_float32 bfact) -{ -l_int32 i, j, w, h, d, wpls, wpld; -l_int32 ncolors, rval, gval, bval, nrval, ngval, nbval; -l_uint32 nval; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixMultConstantColor"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - cmap = pixGetColormap(pixs); - if (!cmap && d != 32) - return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); - rfact = L_MAX(0.0, rfact); - gfact = L_MAX(0.0, gfact); - bfact = L_MAX(0.0, bfact); - - if (cmap) { - if ((pixd = pixCopy(NULL, pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - cmap = pixGetColormap(pixd); - ncolors = pixcmapGetCount(cmap); - for (i = 0; i < ncolors; i++) { - pixcmapGetColor(cmap, i, &rval, &gval, &bval); - nrval = (l_int32)(rfact * rval); - ngval = (l_int32)(gfact * gval); - nbval = (l_int32)(bfact * bval); - nrval = L_MIN(255, nrval); - ngval = L_MIN(255, ngval); - nbval = L_MIN(255, nbval); - pixcmapResetColor(cmap, i, nrval, ngval, nbval); - } - return pixd; - } - - if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - nrval = (l_int32)(rfact * rval); - ngval = (l_int32)(gfact * gval); - nbval = (l_int32)(bfact * bval); - nrval = L_MIN(255, nrval); - ngval = L_MIN(255, ngval); - nbval = L_MIN(255, nbval); - composeRGBPixel(nrval, ngval, nbval, &nval); - *(lined + j) = nval; - } - } - - return pixd; -} - - -/*! - * \brief pixMultMatrixColor() - * - * \param[in] pixs colormapped or rgb - * \param[in] kel kernel 3x3 matrix of floats - * \return pixd colormapped or rgb, or NULL on error - * - *
- * Notes: - * (1) The kernel is a data structure used mostly for floating point - * convolution. Here it is a 3x3 matrix of floats that are used - * to transform the pixel values by matrix multiplication: - * nrval = a[0,0] * rval + a[0,1] * gval + a[0,2] * bval - * ngval = a[1,0] * rval + a[1,1] * gval + a[1,2] * bval - * nbval = a[2,0] * rval + a[2,1] * gval + a[2,2] * bval - * (2) The matrix can be generated in several ways. - * See kernel.c for details. Here are two of them: - * (a) kel = kernelCreate(3, 3); - * kernelSetElement(kel, 0, 0, val00); - * kernelSetElement(kel, 0, 1, val01); - * ... - * (b) from a static string; e.g.,: - * const char *kdata = " 0.6 0.3 -0.2 " - * " 0.1 1.2 0.4 " - * " -0.4 0.2 0.9 "; - * kel = kernelCreateFromString(3, 3, 0, 0, kdata); - * (3) For the special case where the matrix is diagonal, it is easier - * to use pixMultConstantColor(). - * (4) Matrix entries can have positive and negative values, and can - * be larger than 1.0. All transformed component values - * are clipped to [0, 255]. - *- */ -PIX * -pixMultMatrixColor(PIX *pixs, - L_KERNEL *kel) -{ -l_int32 i, j, index, kw, kh, w, h, d, wpls, wpld; -l_int32 ncolors, rval, gval, bval, nrval, ngval, nbval; -l_uint32 nval; -l_uint32 *datas, *datad, *lines, *lined; -l_float32 v[9]; /* use linear array for convenience */ -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixMultMatrixColor"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!kel) - return (PIX *)ERROR_PTR("kel not defined", procName, NULL); - kernelGetParameters(kel, &kw, &kh, NULL, NULL); - if (kw != 3 || kh != 3) - return (PIX *)ERROR_PTR("matrix not 3x3", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - cmap = pixGetColormap(pixs); - if (!cmap && d != 32) - return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); - - for (i = 0, index = 0; i < 3; i++) - for (j = 0; j < 3; j++, index++) - kernelGetElement(kel, i, j, v + index); - - if (cmap) { - if ((pixd = pixCopy(NULL, pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - cmap = pixGetColormap(pixd); - ncolors = pixcmapGetCount(cmap); - for (i = 0; i < ncolors; i++) { - pixcmapGetColor(cmap, i, &rval, &gval, &bval); - nrval = (l_int32)(v[0] * rval + v[1] * gval + v[2] * bval); - ngval = (l_int32)(v[3] * rval + v[4] * gval + v[5] * bval); - nbval = (l_int32)(v[6] * rval + v[7] * gval + v[8] * bval); - nrval = L_MAX(0, L_MIN(255, nrval)); - ngval = L_MAX(0, L_MIN(255, ngval)); - nbval = L_MAX(0, L_MIN(255, nbval)); - pixcmapResetColor(cmap, i, nrval, ngval, nbval); - } - return pixd; - } - - if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - nrval = (l_int32)(v[0] * rval + v[1] * gval + v[2] * bval); - ngval = (l_int32)(v[3] * rval + v[4] * gval + v[5] * bval); - nbval = (l_int32)(v[6] * rval + v[7] * gval + v[8] * bval); - nrval = L_MAX(0, L_MIN(255, nrval)); - ngval = L_MAX(0, L_MIN(255, ngval)); - nbval = L_MAX(0, L_MIN(255, nbval)); - composeRGBPixel(nrval, ngval, nbval, &nval); - *(lined + j) = nval; - } - } - - return pixd; -} - - -/*-------------------------------------------------------------* - * Half-edge by bandpass * - *-------------------------------------------------------------*/ -/*! - * \brief pixHalfEdgeByBandpass() - * - * \param[in] pixs 8 bpp gray or 32 bpp rgb - * \param[in] sm1h, sm1v "half-widths" of smoothing filter sm1 - * \param[in] sm2h, sm2v "half-widths" of smoothing filter sm2; - * require sm2 != sm1 - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) We use symmetric smoothing filters of odd dimension, - * typically use 3, 5, 7, etc. The smoothing parameters - * for these are 1, 2, 3, etc. The filter size is related - * to the smoothing parameter by - * size = 2 * smoothing + 1 - * (2) Because we take the difference of two lowpass filters, - * this is actually a bandpass filter. - * (3) We allow both filters to be anisotropic. - * (4) Consider either the h or v component of the 2 filters. - * Depending on whether sm1 > sm2 or sm2 > sm1, we get - * different halves of the smoothed gradients (or "edges"). - * This difference of smoothed signals looks more like - * a second derivative of a transition, which we rectify - * by not allowing the signal to go below zero. If sm1 < sm2, - * the sm2 transition is broader, so the difference between - * sm1 and sm2 signals is positive on the upper half of - * the transition. Likewise, if sm1 > sm2, the sm1 - sm2 - * signal difference is positive on the lower half of - * the transition. - *- */ -PIX * -pixHalfEdgeByBandpass(PIX *pixs, - l_int32 sm1h, - l_int32 sm1v, - l_int32 sm2h, - l_int32 sm2v) -{ -l_int32 d; -PIX *pixg, *pixacc, *pixc1, *pixc2; - - PROCNAME("pixHalfEdgeByBandpass"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (sm1h == sm2h && sm1v == sm2v) - return (PIX *)ERROR_PTR("sm2 = sm1", procName, NULL); - d = pixGetDepth(pixs); - if (d != 8 && d != 32) - return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); - if (d == 32) - pixg = pixConvertRGBToLuminance(pixs); - else /* d == 8 */ - pixg = pixClone(pixs); - - /* Make a convolution accumulator and use it twice */ - if ((pixacc = pixBlockconvAccum(pixg)) == NULL) { - pixDestroy(&pixg); - return (PIX *)ERROR_PTR("pixacc not made", procName, NULL); - } - if ((pixc1 = pixBlockconvGray(pixg, pixacc, sm1h, sm1v)) == NULL) { - pixDestroy(&pixg); - pixDestroy(&pixacc); - return (PIX *)ERROR_PTR("pixc1 not made", procName, NULL); - } - pixc2 = pixBlockconvGray(pixg, pixacc, sm2h, sm2v); - pixDestroy(&pixg); - pixDestroy(&pixacc); - if (!pixc2) { - pixDestroy(&pixc1); - return (PIX *)ERROR_PTR("pixc2 not made", procName, NULL); - } - - /* Compute the half-edge using pixc1 - pixc2. */ - pixSubtractGray(pixc1, pixc1, pixc2); - pixDestroy(&pixc2); - return pixc1; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/environ.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/environ.h deleted file mode 100644 index 5edb182e..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/environ.h +++ /dev/null @@ -1,560 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_ENVIRON_H -#define LEPTONICA_ENVIRON_H - -/*------------------------------------------------------------------------* - * Defines and includes differ for Unix and Windows. Also for Windows, * - * differentiate between conditionals based on platform and compiler. * - * For platforms: * - * _WIN32 => Windows, 32- or 64-bit * - * _WIN64 => Windows, 64-bit only * - * __CYGWIN__ => Cygwin * - * For compilers: * - * __GNUC__ => gcc * - * _MSC_VER => msvc * - *------------------------------------------------------------------------*/ - -/* MS VC++ does not provide stdint.h, so define the missing types here */ - - -#ifndef _MSC_VER -#include
- * Usage - * ===== - * Messages are of two types. - * - * (1) The messages - * ERROR_INT(a,b,c) : returns l_int32 - * ERROR_FLOAT(a,b,c) : returns l_float32 - * ERROR_PTR(a,b,c) : returns void* - * are used to return from functions and take a fixed set of parameters: - * a :- */ - -#ifdef NO_CONSOLE_IO - - #define PROCNAME(name) - #define ERROR_INT(a, b, c) ((l_int32)(c)) - #define ERROR_FLOAT(a, b, c) ((l_float32)(c)) - #define ERROR_PTR(a, b, c) ((void *)(c)) - #define L_ERROR(a, ...) - #define L_WARNING(a, ...) - #define L_INFO(a, ...) - -#else - - #define PROCNAME(name) static const char procName[] = name - #define IF_SEV(l, t, f) \ - ((l) >= MINIMUM_SEVERITY && (l) >= LeptMsgSeverity ? (t) : (f)) - - #define ERROR_INT(a, b, c) \ - IF_SEV(L_SEVERITY_ERROR, returnErrorInt((a), (b), (c)), (l_int32)(c)) - #define ERROR_FLOAT(a, b, c) \ - IF_SEV(L_SEVERITY_ERROR, returnErrorFloat((a), (b), (c)), (l_float32)(c)) - #define ERROR_PTR(a, b, c) \ - IF_SEV(L_SEVERITY_ERROR, returnErrorPtr((a), (b), (c)), (void *)(c)) - - #define L_ERROR(a, ...) \ - IF_SEV(L_SEVERITY_ERROR, \ - (void)lept_stderr("Error in %s: " a, __VA_ARGS__), \ - (void)0) - #define L_WARNING(a, ...) \ - IF_SEV(L_SEVERITY_WARNING, \ - (void)lept_stderr("Warning in %s: " a, __VA_ARGS__), \ - (void)0) - #define L_INFO(a, ...) \ - IF_SEV(L_SEVERITY_INFO, \ - (void)lept_stderr("Info in %s: " a, __VA_ARGS__), \ - (void)0) - -#if 0 /* Alternative method for controlling L_* message output */ - #define L_ERROR(a, ...) \ - { if (L_SEVERITY_ERROR >= MINIMUM_SEVERITY && \ - L_SEVERITY_ERROR >= LeptMsgSeverity) \ - lept_stderr("Error in %s: " a, __VA_ARGS__) \ - } - #define L_WARNING(a, ...) \ - { if (L_SEVERITY_WARNING >= MINIMUM_SEVERITY && \ - L_SEVERITY_WARNING >= LeptMsgSeverity) \ - lept_stderr("Warning in %s: " a, __VA_ARGS__) \ - } - #define L_INFO(a, ...) \ - { if (L_SEVERITY_INFO >= MINIMUM_SEVERITY && \ - L_SEVERITY_INFO >= LeptMsgSeverity) \ - lept_stderr("Info in %s: " a, __VA_ARGS__) \ - } -#endif - -#endif /* NO_CONSOLE_IO */ - - -/*------------------------------------------------------------------------* - * snprintf() renamed in MSVC (pre-VS2015) * - *------------------------------------------------------------------------*/ -#if defined _MSC_VER && _MSC_VER < 1900 -#define snprintf(buf, size, ...) _snprintf_s(buf, size, _TRUNCATE, __VA_ARGS__) -#endif - - -#endif /* LEPTONICA_ENVIRON_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fhmtauto.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fhmtauto.c deleted file mode 100644 index 8f38b0fe..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fhmtauto.c +++ /dev/null @@ -1,821 +0,0 @@ -/*====================================================================* - - 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 fhmtauto.c - *- * b : procName - * c : - * where procName is the name of the local variable naming the function. - * - * (2) The purely informational L_* messages - * L_ERROR(a,...) - * L_WARNING(a,...) - * L_INFO(a,...) - * do not take a return value, but they take at least two parameters: - * a : with optional format conversions - * v1 : procName (this must be included as the first vararg) - * v2, ... : optional varargs to match format converters in the message - * - * To return an error from a function that returns void, use: - * L_ERROR( , procName, [...]) - * return; - * - * Implementation details - * ====================== - * Messages are defined with the IF_SEV macro. The first parameter is - * the message severity, the second is the function to call if the - * message is to be printed, and the third is the return value if the - * message is to be suppressed. For example, we might have an - * informational message defined as: - * - * IF_SEV(L_SEVERITY_INFO, fprintf(.......), 0) - * - * The macro expands into a conditional. Because the first comparison - * is between two constants, an optimizing compiler will remove either - * the comparison (if it's true) or the entire macro expansion (if it - * is false). This means that there is no run-time overhead for - * messages whose severity falls below the minimum specified at compile - * time, and for others the overhead is one (not two) comparisons. - * - * The L_nnn() macros below do not return a value, but because the - * conditional operator requires one for the false condition, we - * specify a void expression. - *
- * - * Main function calls: - * l_int32 fhmtautogen() - * l_int32 fhmtautogen1() - * l_int32 fhmtautogen2() - * - * Static helpers: - * static SARRAY *sarrayMakeWplsCode() - * static SARRAY *sarrayMakeInnerLoopDWACode() - * static char *makeBarrelshiftString() - * - * This automatically generates dwa code for the hit-miss transform. - * Here's a road map for how it all works. - * - * (1) You generate an array (a SELA) of hit-miss transform SELs. - * This can be done in several ways, including - * (a) calling the function selaAddHitMiss() for - * pre-compiled SELs - * (b) generating the SELA in code in line - * (c) reading in a SELA from file, using selaRead() - * or various other formats. - * - * (2) You call fhmtautogen1() and fhmtautogen2() on this SELA. - * This uses the text files hmttemplate1.txt and - * hmttemplate2.txt for building up the source code. See the file - * prog/fhmtautogen.c for an example of how this is done. - * The output is written to files named fhmtgen.*.c - * and fhmtgenlow.*.c, where "*" is an integer that you - * input to this function. That integer labels both - * the output files, as well as all the functions that - * are generated. That way, using different integers, - * you can invoke fhmtautogen() any number of times - * to get functions that all have different names so that - * they can be linked into one program. - * - * (3) You copy the generated source code back to your src - * directory for compilation. Put their names in the - * Makefile, regnerate the prototypes, and recompile - * the libraries. Look at the Makefile to see how I've - * included fhmtgen.1.c and fhmtgenlow.1.c. These files - * provide the high-level interfaces for the hmt, and - * the low-level interfaces to do the actual work. - * - * (4) In an application, you now use this interface. Again - * for the example files generated, using integer "1": - * - * PIX *pixHMTDwa_1(PIX *pixd, PIX *pixs, const char *selname); - * - * or - * - * PIX *pixFHMTGen_1(PIX *pixd, PIX *pixs, const char *selname); - * - * where the selname is one of the set that were defined - * as the name field of sels. This set is listed at the - * beginning of the file fhmtgen.1.c. - * As an example, see the file prog/fmtauto_reg.c, which - * verifies the correctness of the implementation by - * comparing the dwa result with that of full-image - * rasterops. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This function generates all the code for implementing - * dwa morphological operations using all the sels in the sela. - * (2) See fhmtautogen1() and fhmtautogen2() for details. - *- */ -l_ok -fhmtautogen(SELA *sela, - l_int32 fileindex, - const char *filename) -{ -l_int32 ret1, ret2; - - PROCNAME("fhmtautogen"); - - if (!sela) - return ERROR_INT("sela not defined", procName, 1); - ret1 = fhmtautogen1(sela, fileindex, filename); - ret2 = fhmtautogen2(sela, fileindex, filename); - if (ret1 || ret2) - return ERROR_INT("code generation problem", procName, 1); - return 0; -} - - -/*! - * \brief fhmtautogen1() - * - * \param[in] sela array - * \param[in] fileindex - * \param[in] filename [optional]; can be null - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This function uses hmttemplate1.txt to create a - * top-level file that contains two functions that carry - * out the hit-miss transform for any of the sels in - * the input sela. - * (2) The fileindex parameter is inserted into the output - * filename, as described below. - * (3) If filename == NULL, the output file is fhmtgen.[n].c, - * where [n] is equal to the 'fileindex' parameter. - * (4) If filename != NULL, the output file is [filename].[n].c. - * (5) Each sel must have at least one hit. A sel with only misses - * generates code that will abort the operation if it is called. - *- */ -l_ok -fhmtautogen1(SELA *sela, - l_int32 fileindex, - const char *filename) -{ -char *filestr; -char *str_proto1, *str_proto2, *str_proto3; -char *str_doc1, *str_doc2, *str_doc3, *str_doc4; -char *str_def1, *str_def2, *str_proc1, *str_proc2; -char *str_dwa1, *str_low_dt, *str_low_ds; -char bigbuf[L_BUF_SIZE]; -l_int32 i, nsels, nbytes, actstart, end, newstart; -size_t size; -SARRAY *sa1, *sa2, *sa3; - - PROCNAME("fhmtautogen1"); - - if (!sela) - return ERROR_INT("sela not defined", procName, 1); - if (fileindex < 0) - fileindex = 0; - if ((nsels = selaGetCount(sela)) == 0) - return ERROR_INT("no sels in sela", procName, 1); - - /* Make array of textlines from from hmttemplate1.txt */ - if ((filestr = (char *)l_binaryRead(TEMPLATE1, &size)) == NULL) - return ERROR_INT("filestr not made", procName, 1); - sa2 = sarrayCreateLinesFromString(filestr, 1); - LEPT_FREE(filestr); - if (!sa2) - return ERROR_INT("sa2 not made", procName, 1); - - /* Make array of sel names */ - sa1 = selaGetSelnames(sela); - - /* Make strings containing function call names */ - sprintf(bigbuf, "PIX *pixHMTDwa_%d(PIX *pixd, PIX *pixs, " - "const char *selname);", fileindex); - str_proto1 = stringNew(bigbuf); - sprintf(bigbuf, "PIX *pixFHMTGen_%d(PIX *pixd, PIX *pixs, " - "const char *selname);", fileindex); - str_proto2 = stringNew(bigbuf); - sprintf(bigbuf, "l_int32 fhmtgen_low_%d(l_uint32 *datad, l_int32 w,\n" - " l_int32 h, l_int32 wpld,\n" - " l_uint32 *datas, l_int32 wpls,\n" - " l_int32 index);", fileindex); - str_proto3 = stringNew(bigbuf); - sprintf(bigbuf, " * PIX *pixHMTDwa_%d()", fileindex); - str_doc1 = stringNew(bigbuf); - sprintf(bigbuf, " * PIX *pixFHMTGen_%d()", fileindex); - str_doc2 = stringNew(bigbuf); - sprintf(bigbuf, " * \\brief pixHMTDwa_%d()", fileindex); - str_doc3 = stringNew(bigbuf); - sprintf(bigbuf, " * \\brief pixFHMTGen_%d()", fileindex); - str_doc4 = stringNew(bigbuf); - sprintf(bigbuf, "pixHMTDwa_%d(PIX *pixd,", fileindex); - str_def1 = stringNew(bigbuf); - sprintf(bigbuf, "pixFHMTGen_%d(PIX *pixd,", fileindex); - str_def2 = stringNew(bigbuf); - sprintf(bigbuf, " PROCNAME(\"pixHMTDwa_%d\");", fileindex); - str_proc1 = stringNew(bigbuf); - sprintf(bigbuf, " PROCNAME(\"pixFHMTGen_%d\");", fileindex); - str_proc2 = stringNew(bigbuf); - sprintf(bigbuf, " pixt2 = pixFHMTGen_%d(NULL, pixt1, selname);", - fileindex); - str_dwa1 = stringNew(bigbuf); - sprintf(bigbuf, - " fhmtgen_low_%d(datad, w, h, wpld, datat, wpls, index);", - fileindex); - str_low_dt = stringNew(bigbuf); - sprintf(bigbuf, - " fhmtgen_low_%d(datad, w, h, wpld, datas, wpls, index);", - fileindex); - str_low_ds = stringNew(bigbuf); - - /* Make the output sa */ - sa3 = sarrayCreate(0); - - /* Copyright notice and info header */ - sarrayParseRange(sa2, 0, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - - /* Insert function names as documentation */ - sarrayAddString(sa3, str_doc1, L_INSERT); - sarrayAddString(sa3, str_doc2, L_INSERT); - - /* Add '#include's */ - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - - /* Insert function prototypes */ - sarrayAddString(sa3, str_proto1, L_INSERT); - sarrayAddString(sa3, str_proto2, L_INSERT); - sarrayAddString(sa3, str_proto3, L_INSERT); - - /* Add static globals */ - sprintf(bigbuf, "\nstatic l_int32 NUM_SELS_GENERATED = %d;", nsels); - sarrayAddString(sa3, bigbuf, L_COPY); - sprintf(bigbuf, "static char SEL_NAMES[][80] = {"); - sarrayAddString(sa3, bigbuf, L_COPY); - for (i = 0; i < nsels - 1; i++) { - sprintf(bigbuf, " \"%s\",", - sarrayGetString(sa1, i, L_NOCOPY)); - sarrayAddString(sa3, bigbuf, L_COPY); - } - sprintf(bigbuf, " \"%s\"};", - sarrayGetString(sa1, i, L_NOCOPY)); - sarrayAddString(sa3, bigbuf, L_COPY); - - /* Start pixHMTDwa_*() function description */ - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - sarrayAddString(sa3, str_doc3, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - - /* Finish pixHMTDwa_*() function definition */ - sarrayAddString(sa3, str_def1, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - sarrayAddString(sa3, str_proc1, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - sarrayAddString(sa3, str_dwa1, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - - /* Start pixFHMTGen_*() function description */ - sarrayAddString(sa3, str_doc4, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - - /* Finish pixFHMTGen_*() function description */ - sarrayAddString(sa3, str_def2, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - sarrayAddString(sa3, str_proc2, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - sarrayAddString(sa3, str_low_dt, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - sarrayAddString(sa3, str_low_ds, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - - filestr = sarrayToString(sa3, 1); - nbytes = strlen(filestr); - if (filename) - snprintf(bigbuf, L_BUF_SIZE, "%s.%d.c", filename, fileindex); - else - sprintf(bigbuf, "%s.%d.c", OUTROOT, fileindex); - l_binaryWrite(bigbuf, "w", filestr, nbytes); - sarrayDestroy(&sa1); - sarrayDestroy(&sa2); - sarrayDestroy(&sa3); - LEPT_FREE(filestr); - return 0; -} - - -/*! - * \brief fhmtautogen2() - * - * \param[in] sela array - * \param[in] fileindex - * \param[in] filename [optional]; can be null - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This function uses hmttemplate2.txt to create a - * low-level file that contains the low-level functions for - * implementing the hit-miss transform for every sel - * in the input sela. - * (2) The fileindex parameter is inserted into the output - * filename, as described below. - * (3) If filename == NULL, the output file is fhmtgenlow.[n].c, - * where [n] is equal to the %fileindex parameter. - * (4) If filename != NULL, the output file is [filename]low.[n].c. - *- */ -l_ok -fhmtautogen2(SELA *sela, - l_int32 fileindex, - const char *filename) -{ -char *filestr, *fname, *linestr; -char *str_doc1, *str_doc2, *str_doc3, *str_def1; -char bigbuf[L_BUF_SIZE]; -char breakstring[] = " break;"; -char staticstring[] = "static void"; -l_int32 i, k, l, nsels, nbytes, nhits, nmisses; -l_int32 actstart, end, newstart; -l_int32 argstart, argend, loopstart, loopend, finalstart, finalend; -size_t size; -SARRAY *sa1, *sa2, *sa3, *sa4, *sa5, *sa6; -SEL *sel; - - PROCNAME("fhmtautogen2"); - - if (!sela) - return ERROR_INT("sela not defined", procName, 1); - if (fileindex < 0) - fileindex = 0; - if ((nsels = selaGetCount(sela)) == 0) - return ERROR_INT("no sels in sela", procName, 1); - - /* Make the array of textlines from hmttemplate2.txt */ - if ((filestr = (char *)l_binaryRead(TEMPLATE2, &size)) == NULL) - return ERROR_INT("filestr not made", procName, 1); - sa1 = sarrayCreateLinesFromString(filestr, 1); - LEPT_FREE(filestr); - if (!sa1) - return ERROR_INT("sa1 not made", procName, 1); - - /* Make the array of static function names */ - if ((sa2 = sarrayCreate(nsels)) == NULL) { - sarrayDestroy(&sa1); - return ERROR_INT("sa2 not made", procName, 1); - } - for (i = 0; i < nsels; i++) { - sprintf(bigbuf, "fhmt_%d_%d", fileindex, i); - sarrayAddString(sa2, bigbuf, L_COPY); - } - - /* Make the static prototype strings */ - sa3 = sarrayCreate(2 * nsels); /* should be ok */ - for (i = 0; i < nsels; i++) { - fname = sarrayGetString(sa2, i, L_NOCOPY); - sprintf(bigbuf, "static void %s%s", fname, PROTOARGS); - sarrayAddString(sa3, bigbuf, L_COPY); - } - - /* Make strings containing function names */ - sprintf(bigbuf, " * l_int32 fhmtgen_low_%d()", - fileindex); - str_doc1 = stringNew(bigbuf); - sprintf(bigbuf, " * void fhmt_%d_*()", fileindex); - str_doc2 = stringNew(bigbuf); - - /* Output to this sa */ - sa4 = sarrayCreate(0); - - /* Copyright notice and info header */ - sarrayParseRange(sa1, 0, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa4, sa1, actstart, end); - - /* Insert function names as documentation */ - sarrayAddString(sa4, str_doc1, L_INSERT); - sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa4, sa1, actstart, end); - sarrayAddString(sa4, str_doc2, L_INSERT); - sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa4, sa1, actstart, end); - - /* Insert static protos */ - for (i = 0; i < nsels; i++) { - if ((linestr = sarrayGetString(sa3, i, L_COPY)) == NULL) { - sarrayDestroy(&sa1); - sarrayDestroy(&sa2); - sarrayDestroy(&sa3); - sarrayDestroy(&sa4); - return ERROR_INT("linestr not retrieved", procName, 1); - } - sarrayAddString(sa4, linestr, L_INSERT); - } - - /* Make more strings containing function names */ - sprintf(bigbuf, " * fhmtgen_low_%d()", fileindex); - str_doc3 = stringNew(bigbuf); - sprintf(bigbuf, "fhmtgen_low_%d(l_uint32 *datad,", fileindex); - str_def1 = stringNew(bigbuf); - - /* Insert function header */ - sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa4, sa1, actstart, end); - sarrayAddString(sa4, str_doc3, L_INSERT); - sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa4, sa1, actstart, end); - sarrayAddString(sa4, str_def1, L_INSERT); - sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa4, sa1, actstart, end); - - /* Generate and insert the dispatcher code */ - for (i = 0; i < nsels; i++) { - sprintf(bigbuf, " case %d:", i); - sarrayAddString(sa4, bigbuf, L_COPY); - sprintf(bigbuf, " %s(datad, w, h, wpld, datas, wpls);", - sarrayGetString(sa2, i, L_NOCOPY)); - sarrayAddString(sa4, bigbuf, L_COPY); - sarrayAddString(sa4, breakstring, L_COPY); - } - - /* Finish the dispatcher and introduce the low-level code */ - sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa4, sa1, actstart, end); - - /* Get the range for the args common to all functions */ - sarrayParseRange(sa1, newstart, &argstart, &argend, &newstart, "--", 0); - - /* Get the range for the loop code common to all functions */ - sarrayParseRange(sa1, newstart, &loopstart, &loopend, &newstart, "--", 0); - - /* Get the range for the ending code common to all functions */ - sarrayParseRange(sa1, newstart, &finalstart, &finalend, &newstart, "--", 0); - - /* Do all the static functions */ - for (i = 0; i < nsels; i++) { - /* Generate the function header and add the common args */ - sarrayAddString(sa4, staticstring, L_COPY); - fname = sarrayGetString(sa2, i, L_NOCOPY); - sprintf(bigbuf, "%s(l_uint32 *datad,", fname); - sarrayAddString(sa4, bigbuf, L_COPY); - sarrayAppendRange(sa4, sa1, argstart, argend); - - /* Declare and define wplsN args, as necessary */ - if ((sel = selaGetSel(sela, i)) == NULL) { - sarrayDestroy(&sa1); - sarrayDestroy(&sa2); - sarrayDestroy(&sa3); - sarrayDestroy(&sa4); - return ERROR_INT("sel not returned", procName, 1); - } - sa5 = sarrayMakeWplsCode(sel); - sarrayJoin(sa4, sa5); - sarrayDestroy(&sa5); - - /* Make sure sel has at least one hit */ - nhits = 0; - nmisses = 0; - for (k = 0; k < sel->sy; k++) { - for (l = 0; l < sel->sx; l++) { - if (sel->data[k][l] == 1) - nhits++; - else if (sel->data[k][l] == 2) - nmisses++; - } - } - if (nhits == 0) { - linestr = stringNew(" " - "lept_stderr(\"Error in HMT: no hits in sel!\\n\");\n}\n\n"); - sarrayAddString(sa4, linestr, L_INSERT); - continue; - } - - /* Add the function loop code */ - sarrayAppendRange(sa4, sa1, loopstart, loopend); - - /* Insert barrel-op code for *dptr */ - if ((sa6 = sarrayMakeInnerLoopDWACode(sel, nhits, nmisses)) == NULL) { - sarrayDestroy(&sa1); - sarrayDestroy(&sa2); - sarrayDestroy(&sa3); - sarrayDestroy(&sa4); - return ERROR_INT("sa6 not made", procName, 1); - } - sarrayJoin(sa4, sa6); - sarrayDestroy(&sa6); - - /* Finish the function code */ - sarrayAppendRange(sa4, sa1, finalstart, finalend); - } - - /* Output to file */ - filestr = sarrayToString(sa4, 1); - nbytes = strlen(filestr); - if (filename) - snprintf(bigbuf, L_BUF_SIZE, "%slow.%d.c", filename, fileindex); - else - sprintf(bigbuf, "%slow.%d.c", OUTROOT, fileindex); - l_binaryWrite(bigbuf, "w", filestr, nbytes); - sarrayDestroy(&sa1); - sarrayDestroy(&sa2); - sarrayDestroy(&sa3); - sarrayDestroy(&sa4); - LEPT_FREE(filestr); - return 0; -} - - - -/*--------------------------------------------------------------------------* - * Helper code for sel * - *--------------------------------------------------------------------------*/ -/*! - * \brief sarrayMakeWplsCode() - */ -static SARRAY * -sarrayMakeWplsCode(SEL *sel) -{ -char emptystring[] = ""; -l_int32 i, j, ymax, dely; -SARRAY *sa; - - PROCNAME("sarrayMakeWplsCode"); - - if (!sel) - return (SARRAY *)ERROR_PTR("sel not defined", procName, NULL); - - ymax = 0; - for (i = 0; i < sel->sy; i++) { - for (j = 0; j < sel->sx; j++) { - if (sel->data[i][j] == 1 || sel->data[i][j] == 2) { - dely = L_ABS(i - sel->cy); - ymax = L_MAX(ymax, dely); - } - } - } - if (ymax > 31) { - L_WARNING("ymax > 31; truncating to 31\n", procName); - ymax = 31; - } - - sa = sarrayCreate(0); - - /* Declarations */ - if (ymax > 4) - sarrayAddString(sa, wpldecls[2], L_COPY); - if (ymax > 8) - sarrayAddString(sa, wpldecls[6], L_COPY); - if (ymax > 12) - sarrayAddString(sa, wpldecls[10], L_COPY); - if (ymax > 16) - sarrayAddString(sa, wpldecls[14], L_COPY); - if (ymax > 20) - sarrayAddString(sa, wpldecls[18], L_COPY); - if (ymax > 24) - sarrayAddString(sa, wpldecls[22], L_COPY); - if (ymax > 28) - sarrayAddString(sa, wpldecls[26], L_COPY); - if (ymax > 1) - sarrayAddString(sa, wpldecls[ymax - 2], L_COPY); - - sarrayAddString(sa, emptystring, L_COPY); - - /* Definitions */ - for (i = 2; i <= ymax; i++) - sarrayAddString(sa, wpldefs[i - 2], L_COPY); - - return sa; -} - - -/*! - * \brief sarrayMakeInnerLoopDWACode() - */ -static SARRAY * -sarrayMakeInnerLoopDWACode(SEL *sel, - l_int32 nhits, - l_int32 nmisses) -{ -char *string; -char land[] = "&"; -char bigbuf[L_BUF_SIZE]; -l_int32 i, j, ntot, nfound, type, delx, dely; -SARRAY *sa; - - PROCNAME("sarrayMakeInnerLoopDWACode"); - - if (!sel) - return (SARRAY *)ERROR_PTR("sel not defined", procName, NULL); - - sa = sarrayCreate(0); - ntot = nhits + nmisses; - nfound = 0; - for (i = 0; i < sel->sy; i++) { - for (j = 0; j < sel->sx; j++) { - type = sel->data[i][j]; - if (type == SEL_HIT || type == SEL_MISS) { - nfound++; - dely = i - sel->cy; - delx = j - sel->cx; - if ((string = makeBarrelshiftString(delx, dely, type)) - == NULL) { - L_WARNING("barrel shift string not made\n", procName); - continue; - } - if (ntot == 1) /* just one item */ - sprintf(bigbuf, " *dptr = %s;", string); - else if (nfound == 1) - sprintf(bigbuf, " *dptr = %s %s", string, land); - else if (nfound < ntot) - sprintf(bigbuf, " %s %s", string, land); - else /* nfound == ntot */ - sprintf(bigbuf, " %s;", string); - sarrayAddString(sa, bigbuf, L_COPY); - LEPT_FREE(string); - } - } - } - - return sa; -} - - -/*! - * \brief makeBarrelshiftString() - */ -static char * -makeBarrelshiftString(l_int32 delx, /* j - cx */ - l_int32 dely, /* i - cy */ - l_int32 type) /* SEL_HIT or SEL_MISS */ -{ -l_int32 absx, absy; -char bigbuf[L_BUF_SIZE]; - - PROCNAME("makeBarrelshiftString"); - - if (delx < -31 || delx > 31) - return (char *)ERROR_PTR("delx out of bounds", procName, NULL); - if (dely < -31 || dely > 31) - return (char *)ERROR_PTR("dely out of bounds", procName, NULL); - absx = L_ABS(delx); - absy = L_ABS(dely); - - if (type == SEL_HIT) { - if ((delx == 0) && (dely == 0)) - sprintf(bigbuf, "(*sptr)"); - else if ((delx == 0) && (dely < 0)) - sprintf(bigbuf, "(*(sptr %s))", wplstrm[absy - 1]); - else if ((delx == 0) && (dely > 0)) - sprintf(bigbuf, "(*(sptr %s))", wplstrp[absy - 1]); - else if ((delx < 0) && (dely == 0)) - sprintf(bigbuf, "((*(sptr) >> %d) | (*(sptr - 1) << %d))", - absx, 32 - absx); - else if ((delx > 0) && (dely == 0)) - sprintf(bigbuf, "((*(sptr) << %d) | (*(sptr + 1) >> %d))", - absx, 32 - absx); - else if ((delx < 0) && (dely < 0)) - sprintf(bigbuf, "((*(sptr %s) >> %d) | (*(sptr %s - 1) << %d))", - wplstrm[absy - 1], absx, wplstrm[absy - 1], 32 - absx); - else if ((delx > 0) && (dely < 0)) - sprintf(bigbuf, "((*(sptr %s) << %d) | (*(sptr %s + 1) >> %d))", - wplstrm[absy - 1], absx, wplstrm[absy - 1], 32 - absx); - else if ((delx < 0) && (dely > 0)) - sprintf(bigbuf, "((*(sptr %s) >> %d) | (*(sptr %s - 1) << %d))", - wplstrp[absy - 1], absx, wplstrp[absy - 1], 32 - absx); - else /* ((delx > 0) && (dely > 0)) */ - sprintf(bigbuf, "((*(sptr %s) << %d) | (*(sptr %s + 1) >> %d))", - wplstrp[absy - 1], absx, wplstrp[absy - 1], 32 - absx); - } else { /* type == SEL_MISS */ - if ((delx == 0) && (dely == 0)) - sprintf(bigbuf, "(~*sptr)"); - else if ((delx == 0) && (dely < 0)) - sprintf(bigbuf, "(~*(sptr %s))", wplstrm[absy - 1]); - else if ((delx == 0) && (dely > 0)) - sprintf(bigbuf, "(~*(sptr %s))", wplstrp[absy - 1]); - else if ((delx < 0) && (dely == 0)) - sprintf(bigbuf, "((~*(sptr) >> %d) | (~*(sptr - 1) << %d))", - absx, 32 - absx); - else if ((delx > 0) && (dely == 0)) - sprintf(bigbuf, "((~*(sptr) << %d) | (~*(sptr + 1) >> %d))", - absx, 32 - absx); - else if ((delx < 0) && (dely < 0)) - sprintf(bigbuf, "((~*(sptr %s) >> %d) | (~*(sptr %s - 1) << %d))", - wplstrm[absy - 1], absx, wplstrm[absy - 1], 32 - absx); - else if ((delx > 0) && (dely < 0)) - sprintf(bigbuf, "((~*(sptr %s) << %d) | (~*(sptr %s + 1) >> %d))", - wplstrm[absy - 1], absx, wplstrm[absy - 1], 32 - absx); - else if ((delx < 0) && (dely > 0)) - sprintf(bigbuf, "((~*(sptr %s) >> %d) | (~*(sptr %s - 1) << %d))", - wplstrp[absy - 1], absx, wplstrp[absy - 1], 32 - absx); - else /* ((delx > 0) && (dely > 0)) */ - sprintf(bigbuf, "((~*(sptr %s) << %d) | (~*(sptr %s + 1) >> %d))", - wplstrp[absy - 1], absx, wplstrp[absy - 1], 32 - absx); - } - - return stringNew(bigbuf); -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fhmtgen.1.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fhmtgen.1.c deleted file mode 100644 index 8a1fcab8..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fhmtgen.1.c +++ /dev/null @@ -1,177 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -/*! - * Top-level fast hit-miss transform with auto-generated sels - * - * PIX *pixHMTDwa_1() - * PIX *pixFHMTGen_1() - */ - -#include
- * Notes: - * (1) This simply adds a 32 pixel border, calls the appropriate - * pixFHMTGen_*(), and removes the border. - * See notes below for that function. - *- */ -PIX * -pixHMTDwa_1(PIX *pixd, - PIX *pixs, - const char *selname) -{ -PIX *pixt1, *pixt2, *pixt3; - - PROCNAME("pixHMTDwa_1"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, pixd); - - pixt1 = pixAddBorder(pixs, 32, 0); - pixt2 = pixFHMTGen_1(NULL, pixt1, selname); - pixt3 = pixRemoveBorder(pixt2, 32); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - - if (!pixd) - return pixt3; - - pixCopy(pixd, pixt3); - pixDestroy(&pixt3); - return pixd; -} - - -/*! - * \brief pixFHMTGen_1() - * - * \param[in] pixd usual 3 choices: null, == pixs, != pixs - * \param[in] pixs 1 bpp - * \param[in] sel name - * \return pixd - * - *
- * Notes: - * (1) This is a dwa implementation of the hit-miss transform - * on pixs by the sel. - * (2) The sel must be limited in size to not more than 31 pixels - * about the origin. It must have at least one hit, and it - * can have any number of misses. - * (3) This handles all required setting of the border pixels - * before erosion and dilation. - *- */ -PIX * -pixFHMTGen_1(PIX *pixd, - PIX *pixs, - const char *selname) -{ -l_int32 i, index, found, w, h, wpls, wpld; -l_uint32 *datad, *datas, *datat; -PIX *pixt; - - PROCNAME("pixFHMTGen_1"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, pixd); - - found = FALSE; - for (i = 0; i < NUM_SELS_GENERATED; i++) { - if (strcmp(selname, SEL_NAMES[i]) == 0) { - found = TRUE; - index = i; - break; - } - } - if (found == FALSE) - return (PIX *)ERROR_PTR("sel index not found", procName, pixd); - - if (!pixd) { - if ((pixd = pixCreateTemplate(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - else /* for in-place or pre-allocated */ - pixResizeImageData(pixd, pixs); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - - /* The images must be surrounded with 32 additional border - * pixels, that we'll read from. We fabricate a "proper" - * image as the subimage within the border, having the - * following parameters: */ - w = pixGetWidth(pixs) - 64; - h = pixGetHeight(pixs) - 64; - datas = pixGetData(pixs) + 32 * wpls + 1; - datad = pixGetData(pixd) + 32 * wpld + 1; - - if (pixd == pixs) { /* need temp image if in-place */ - if ((pixt = pixCopy(NULL, pixs)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, pixd); - datat = pixGetData(pixt) + 32 * wpls + 1; - fhmtgen_low_1(datad, w, h, wpld, datat, wpls, index); - pixDestroy(&pixt); - } - else { /* not in-place */ - fhmtgen_low_1(datad, w, h, wpld, datas, wpls, index); - } - - return pixd; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fhmtgenlow.1.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fhmtgenlow.1.c deleted file mode 100644 index b1c863cf..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fhmtgenlow.1.c +++ /dev/null @@ -1,445 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -/*! - * Low-level fast hit-miss transform with auto-generated sels - * - * Dispatcher: - * l_int32 fhmtgen_low_1() - * - * Static Low-level: - * void fhmt_1_*() - */ - -#include "allheaders.h" - -static void fhmt_1_0(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fhmt_1_1(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fhmt_1_2(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fhmt_1_3(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fhmt_1_4(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fhmt_1_5(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fhmt_1_6(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fhmt_1_7(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fhmt_1_8(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fhmt_1_9(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); - - -/*---------------------------------------------------------------------* - * Fast hmt dispatcher * - *---------------------------------------------------------------------*/ -/*! - * fhmtgen_low_1() - * - * a dispatcher to appropriate low-level code - */ -l_int32 -fhmtgen_low_1(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls, - l_int32 index) -{ - - switch (index) - { - case 0: - fhmt_1_0(datad, w, h, wpld, datas, wpls); - break; - case 1: - fhmt_1_1(datad, w, h, wpld, datas, wpls); - break; - case 2: - fhmt_1_2(datad, w, h, wpld, datas, wpls); - break; - case 3: - fhmt_1_3(datad, w, h, wpld, datas, wpls); - break; - case 4: - fhmt_1_4(datad, w, h, wpld, datas, wpls); - break; - case 5: - fhmt_1_5(datad, w, h, wpld, datas, wpls); - break; - case 6: - fhmt_1_6(datad, w, h, wpld, datas, wpls); - break; - case 7: - fhmt_1_7(datad, w, h, wpld, datas, wpls); - break; - case 8: - fhmt_1_8(datad, w, h, wpld, datas, wpls); - break; - case 9: - fhmt_1_9(datad, w, h, wpld, datas, wpls); - break; - } - - return 0; -} - - -/*--------------------------------------------------------------------------* - * Low-level auto-generated static routines * - *--------------------------------------------------------------------------*/ -/* - * N.B. In all the low-level routines, the part of the image - * that is accessed has been clipped by 32 pixels on - * all four sides. This is done in the higher level - * code by redefining w and h smaller and by moving the - * start-of-image pointers up to the beginning of this - * interior rectangle. - */ -static void -fhmt_1_0(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((~*(sptr - wpls) >> 1) | (~*(sptr - wpls - 1) << 31)) & - (~*(sptr - wpls)) & - ((~*(sptr - wpls) << 1) | (~*(sptr - wpls + 1) >> 31)) & - ((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) & - (*sptr) & - ((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) & - ((~*(sptr + wpls) >> 1) | (~*(sptr + wpls - 1) << 31)) & - (~*(sptr + wpls)) & - ((~*(sptr + wpls) << 1) | (~*(sptr + wpls + 1) >> 31)); - } - } -} - -static void -fhmt_1_1(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((~*(sptr + wpls) >> 1) | (~*(sptr + wpls - 1) << 31)) & - (~*(sptr + wpls)) & - ((~*(sptr + wpls) << 1) | (~*(sptr + wpls + 1) >> 31)); - } - } -} - -static void -fhmt_1_2(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((~*(sptr - wpls) >> 1) | (~*(sptr - wpls - 1) << 31)) & - (~*(sptr - wpls)) & - ((~*(sptr - wpls) << 1) | (~*(sptr - wpls + 1) >> 31)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)); - } - } -} - -static void -fhmt_1_3(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls)) & - ((~*(sptr - wpls) << 1) | (~*(sptr - wpls + 1) >> 31)) & - (*sptr) & - ((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) & - (*(sptr + wpls)) & - ((~*(sptr + wpls) << 1) | (~*(sptr + wpls + 1) >> 31)); - } - } -} - -static void -fhmt_1_4(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((~*(sptr - wpls) >> 1) | (~*(sptr - wpls - 1) << 31)) & - (*(sptr - wpls)) & - ((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) & - (*sptr) & - ((~*(sptr + wpls) >> 1) | (~*(sptr + wpls - 1) << 31)) & - (*(sptr + wpls)); - } - } -} - -static void -fhmt_1_5(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((~*(sptr - wpls6) << 1) | (~*(sptr - wpls6 + 1) >> 31)) & - ((*(sptr - wpls6) << 3) | (*(sptr - wpls6 + 1) >> 29)) & - (~*(sptr - wpls2)) & - ((*(sptr - wpls2) << 2) | (*(sptr - wpls2 + 1) >> 30)) & - ((~*(sptr + wpls2) >> 1) | (~*(sptr + wpls2 - 1) << 31)) & - ((*(sptr + wpls2) << 1) | (*(sptr + wpls2 + 1) >> 31)) & - ((~*(sptr + wpls6) >> 2) | (~*(sptr + wpls6 - 1) << 30)) & - (*(sptr + wpls6)); - } - } -} - -static void -fhmt_1_6(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2; - - wpls2 = 2 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((~*(sptr - wpls) >> 1) | (~*(sptr - wpls - 1) << 31)) & - (~*(sptr - wpls)) & - ((~*(sptr - wpls) << 1) | (~*(sptr - wpls + 1) >> 31)) & - ((~*(sptr - wpls) << 2) | (~*(sptr - wpls + 1) >> 30)) & - ((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((~*(sptr + wpls) >> 1) | (~*(sptr + wpls - 1) << 31)) & - ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)) & - ((*(sptr + wpls) << 2) | (*(sptr + wpls + 1) >> 30)) & - ((~*(sptr + wpls2) >> 1) | (~*(sptr + wpls2 - 1) << 31)) & - (*(sptr + wpls2)) & - ((*(sptr + wpls2) << 1) | (*(sptr + wpls2 + 1) >> 31)) & - ((*(sptr + wpls2) << 2) | (*(sptr + wpls2 + 1) >> 30)); - } - } -} - -static void -fhmt_1_7(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2; - - wpls2 = 2 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((~*(sptr - wpls) >> 2) | (~*(sptr - wpls - 1) << 30)) & - ((~*(sptr - wpls) >> 1) | (~*(sptr - wpls - 1) << 31)) & - (~*(sptr - wpls)) & - ((~*(sptr - wpls) << 1) | (~*(sptr - wpls + 1) >> 31)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) & - ((*(sptr + wpls) >> 2) | (*(sptr + wpls - 1) << 30)) & - ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) & - ((~*(sptr + wpls) << 1) | (~*(sptr + wpls + 1) >> 31)) & - ((*(sptr + wpls2) >> 2) | (*(sptr + wpls2 - 1) << 30)) & - ((*(sptr + wpls2) >> 1) | (*(sptr + wpls2 - 1) << 31)) & - (*(sptr + wpls2)) & - ((~*(sptr + wpls2) << 1) | (~*(sptr + wpls2 + 1) >> 31)); - } - } -} - -static void -fhmt_1_8(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2; - - wpls2 = 2 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((~*(sptr - wpls2) >> 1) | (~*(sptr - wpls2 - 1) << 31)) & - (*(sptr - wpls2)) & - ((*(sptr - wpls2) << 1) | (*(sptr - wpls2 + 1) >> 31)) & - ((*(sptr - wpls2) << 2) | (*(sptr - wpls2 + 1) >> 30)) & - ((~*(sptr - wpls) >> 1) | (~*(sptr - wpls - 1) << 31)) & - ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) & - ((*(sptr - wpls) << 2) | (*(sptr - wpls + 1) >> 30)) & - ((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((~*(sptr + wpls) >> 1) | (~*(sptr + wpls - 1) << 31)) & - (~*(sptr + wpls)) & - ((~*(sptr + wpls) << 1) | (~*(sptr + wpls + 1) >> 31)) & - ((~*(sptr + wpls) << 2) | (~*(sptr + wpls + 1) >> 30)); - } - } -} - -static void -fhmt_1_9(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2; - - wpls2 = 2 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr - wpls2) >> 2) | (*(sptr - wpls2 - 1) << 30)) & - ((*(sptr - wpls2) >> 1) | (*(sptr - wpls2 - 1) << 31)) & - (*(sptr - wpls2)) & - ((~*(sptr - wpls2) << 1) | (~*(sptr - wpls2 + 1) >> 31)) & - ((*(sptr - wpls) >> 2) | (*(sptr - wpls - 1) << 30)) & - ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)) & - ((~*(sptr - wpls) << 1) | (~*(sptr - wpls + 1) >> 31)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) & - ((~*(sptr + wpls) >> 2) | (~*(sptr + wpls - 1) << 30)) & - ((~*(sptr + wpls) >> 1) | (~*(sptr + wpls - 1) << 31)) & - (~*(sptr + wpls)) & - ((~*(sptr + wpls) << 1) | (~*(sptr + wpls + 1) >> 31)); - } - } -} - diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/finditalic.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/finditalic.c deleted file mode 100644 index 54589075..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/finditalic.c +++ /dev/null @@ -1,240 +0,0 @@ -/*====================================================================* - - 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 finditalic.c - *
- * - * l_int32 pixItalicWords() - * - * Locate italic words. This is an example of the use of - * hit-miss binary morphology with binary reconstruction - * (filling from a seed into a mask). - * - * To see how this works, run with prog/italic.png. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) You can input the bounding boxes for the words in one of - * two forms: as bounding boxes (%boxaw) or as a word mask with - * the word bounding boxes filled (%pixw). For example, - * to compute %pixw, you can use pixWordMaskByDilation(). - * (2) Alternatively, you can set both of these inputs to NULL, - * in which case the word mask is generated here. This is - * done by dilating and closing the input image to connect - * letters within a word, while leaving the words separated. - * The parameters are chosen under the assumption that the - * input is 10 to 12 pt text, scanned at about 300 ppi. - * (3) sel_ital1 and sel_ital2 detect the right edges that are - * nearly vertical, at approximately the angle of italic - * strokes. We use the right edge to avoid getting seeds - * from lower-case 'y'. The typical italic slant has a smaller - * angle with the vertical than the 'W', so in most cases we - * will not trigger on the slanted lines in the 'W'. - * (4) Note that sel_ital2 is shorter than sel_ital1. It is - * more appropriate for a typical font scanned at 200 ppi. - *- */ -l_ok -pixItalicWords(PIX *pixs, - BOXA *boxaw, - PIX *pixw, - BOXA **pboxa, - l_int32 debugflag) -{ -char opstring[32]; -l_int32 size; -BOXA *boxa; -PIX *pixsd, *pixm, *pixd; -SEL *sel_ital1, *sel_ital2, *sel_ital3; - - PROCNAME("pixItalicWords"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!pboxa) - return ERROR_INT("&boxa not defined", procName, 1); - if (boxaw && pixw) - return ERROR_INT("both boxaw and pixw are defined", procName, 1); - - sel_ital1 = selCreateFromString(str_ital1, 13, 6, NULL); - sel_ital2 = selCreateFromString(str_ital2, 10, 6, NULL); - sel_ital3 = selCreateFromString(str_ital3, 4, 2, NULL); - - /* Make the italic seed: extract with HMT; remove noise. - * The noise removal close/open is important to exclude - * situations where a small slanted line accidentally - * matches sel_ital1. */ - pixsd = pixHMT(NULL, pixs, sel_ital1); - pixClose(pixsd, pixsd, sel_ital3); - pixOpen(pixsd, pixsd, sel_ital3); - - /* Make the word mask. Use input boxes or mask if given. */ - size = 0; /* init */ - if (boxaw) { - pixm = pixCreateTemplate(pixs); - pixMaskBoxa(pixm, pixm, boxaw, L_SET_PIXELS); - } else if (pixw) { - pixm = pixClone(pixw); - } else { - pixWordMaskByDilation(pixs, NULL, &size, NULL); - L_INFO("dilation size = %d\n", procName, size); - snprintf(opstring, sizeof(opstring), "d1.5 + c%d.1", size); - pixm = pixMorphSequence(pixs, opstring, 0); - } - - /* Binary reconstruction to fill in those word mask - * components for which there is at least one seed pixel. */ - pixd = pixSeedfillBinary(NULL, pixsd, pixm, 8); - boxa = pixConnComp(pixd, NULL, 8); - *pboxa = boxa; - - if (debugflag) { - /* Save results at at 2x reduction */ - lept_mkdir("lept/ital"); - l_int32 res, upper; - BOXA *boxat; - GPLOT *gplot; - NUMA *na; - PIXA *pixa1; - PIX *pix1, *pix2, *pix3; - pixa1 = pixaCreate(0); - boxat = pixConnComp(pixm, NULL, 8); - boxaWriteDebug("/tmp/lept/ital/ital.ba", boxat); - pixaAddPix(pixa1, pixs, L_COPY); /* orig */ - pixaAddPix(pixa1, pixsd, L_COPY); /* seed */ - pix1 = pixConvertTo32(pixm); - pixRenderBoxaArb(pix1, boxat, 3, 255, 0, 0); - pixaAddPix(pixa1, pix1, L_INSERT); /* mask + outline */ - pixaAddPix(pixa1, pixd, L_COPY); /* ital mask */ - pix1 = pixConvertTo32(pixs); - pixRenderBoxaArb(pix1, boxa, 3, 255, 0, 0); - pixaAddPix(pixa1, pix1, L_INSERT); /* orig + outline */ - pix1 = pixCreateTemplate(pixs); - pix2 = pixSetBlackOrWhiteBoxa(pix1, boxa, L_SET_BLACK); - pixCopy(pix1, pixs); - pix3 = pixDilateBrick(NULL, pixs, 3, 3); - pixCombineMasked(pix1, pix3, pix2); - pixaAddPix(pixa1, pix1, L_INSERT); /* ital bolded */ - pixDestroy(&pix2); - pixDestroy(&pix3); - pix2 = pixaDisplayTiledInColumns(pixa1, 1, 0.5, 20, 2); - pixWriteDebug("/tmp/lept/ital/ital.png", pix2, IFF_PNG); - pixDestroy(&pix2); - - /* Assuming the image represents 6 inches of actual page width, - * the pixs resolution is approximately - * (width of pixs in pixels) / 6 - * and the images have been saved at half this resolution. */ - res = pixGetWidth(pixs) / 12; - L_INFO("resolution = %d\n", procName, res); - l_pdfSetDateAndVersion(0); - pixaConvertToPdf(pixa1, res, 1.0, L_FLATE_ENCODE, 75, "Italic Finder", - "/tmp/lept/ital/ital.pdf"); - l_pdfSetDateAndVersion(1); - pixaDestroy(&pixa1); - boxaDestroy(&boxat); - - /* Plot histogram of horizontal white run sizes. A small - * initial vertical dilation removes most runs that are neither - * inter-character nor inter-word. The larger first peak is - * from inter-character runs, and the smaller second peak is - * from inter-word runs. */ - pix1 = pixDilateBrick(NULL, pixs, 1, 15); - upper = L_MAX(30, 3 * size); - na = pixRunHistogramMorph(pix1, L_RUN_OFF, L_HORIZ, upper); - pixDestroy(&pix1); - gplot = gplotCreate("/tmp/lept/ital/runhisto", GPLOT_PNG, - "Histogram of horizontal runs of white pixels, vs length", - "run length", "number of runs"); - gplotAddPlot(gplot, NULL, na, GPLOT_LINES, "plot1"); - gplotMakeOutput(gplot); - gplotDestroy(&gplot); - numaDestroy(&na); - } - - selDestroy(&sel_ital1); - selDestroy(&sel_ital2); - selDestroy(&sel_ital3); - pixDestroy(&pixsd); - pixDestroy(&pixm); - pixDestroy(&pixd); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/flipdetect.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/flipdetect.c deleted file mode 100644 index 49f14f78..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/flipdetect.c +++ /dev/null @@ -1,1167 +0,0 @@ -/*====================================================================* - - 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 flipdetect.c - *
- * - * High-level interface for detection and correction - * l_int32 pixOrientCorrect() - * - * Page orientation detection (pure rotation by 90 degree increments): - * l_int32 pixOrientDetect() - * l_int32 makeOrientDecision() - * l_int32 pixUpDownDetect() - * l_int32 pixUpDownDetectGeneral() - * l_int32 pixOrientDetectDwa() - * l_int32 pixUpDownDetectDwa() - * l_int32 pixUpDownDetectGeneralDwa() - * - * Page mirror detection (flip 180 degrees about line in plane of image): - * l_int32 pixMirrorDetect() - * l_int32 pixMirrorDetectDwa() - * - * Static debug helper - * void pixDebugFlipDetect() - * - * =================================================================== - * - * Page transformation detection: - * - * Once a page is deskewed, there are 8 possible states that it - * can be in, shown symbolically below. Suppose state 0 is correct. - * - * 0: correct 1 2 3 - * +------+ +------+ +------+ +------+ - * | **** | | * | | **** | | * | - * | * | | * | | * | | * | - * | * | | **** | | * | | **** | - * +------+ +------+ +------+ +------+ - * - * 4 5 6 7 - * +-----+ +-----+ +-----+ +-----+ - * | *** | | * | | *** | | * | - * | * | | * | | * | | * | - * | * | | * | | * | | * | - * | * | | *** | | * | | *** | - * +-----+ +-----+ +-----+ +-----+ - * - * Each of the other seven can be derived from state 0 by applying some - * combination of a 90 degree clockwise rotation, a flip about - * a horizontal line, and a flip about a vertical line, - * all abbreviated as: - * R = Rotation (about a line perpendicular to the image) - * H = Horizontal flip (about a vertical line in the plane of the image) - * V = Vertical flip (about a horizontal line in the plane of the image) - * - * We get these transformations: - * RHV - * 000 -> 0 - * 001 -> 1 - * 010 -> 2 - * 011 -> 3 - * 100 -> 4 - * 101 -> 5 - * 110 -> 6 - * 111 -> 7 - * - * Note that in four of these, the sum of H and V is 1 (odd). - * For these four, we have a change in parity (handedness) of - * the image, and the transformation cannot be performed by - * rotation about a vertical line out of the page. Under - * rotation R, the set of 8 transformations decomposes into - * two subgroups linking {0, 3, 4, 7} and {1, 2, 5, 6} independently. - * - * pixOrientDetect*() tests for a pure rotation (0, 90, 180, 270 degrees). - * It doesn't change parity. - * - * pixMirrorDetect*() tests for a horizontal flip about the vertical axis. - * It changes parity. - * - * The landscape/portrait rotation can be detected in two ways: - * - * (1) Compute the deskew confidence for an image segment, - * both as is and rotated 90 degrees (see skew.c). - * - * (2) Compute the ascender/descender signal for the image, - * both as is and rotated 90 degrees (implemented here). - * - * The ascender/descender signal is useful for determining text - * orientation in Roman alphabets because the incidence of letters - * with straight-line ascenders (b, d, h, k, l, 't') outnumber - * those with descenders ('g', p, q). The letters 't' and 'g' - * will respond variably to the filter, depending on the type face. - * - * What about the mirror image situations? These aren't common - * unless you're dealing with film, for example. - * But you can reliably test if the image has undergone a - * parity-changing flip once about some axis in the plane - * of the image, using pixMirrorDetect*(). This works ostensibly by - * counting the number of characters with ascenders that - * stick out to the left and right of the ascender. Characters - * that are not mirror flipped are more likely to extend to the - * right (b, h, k) than to the left (d). Of course, that is for - * text that is rightside-up. So before you apply the mirror - * test, it is necessary to insure that the text has the ascenders - * going up, and not down or to the left or right. But here's - * what *really* happens. It turns out that the pre-filtering before - * the hit-miss transform (HMT) is crucial, and surprisingly, when - * the pre-filtering is chosen to generate a large signal, the majority - * of the signal comes from open regions of common lower-case - * letters such as 'e', 'c' and 'f'. - * - * All operations are given in two implementations whose results are - * identical: rasterop morphology and dwa morphology. The dwa - * implementations are between 2x and 3x faster. - * - * The set of operations you actually use depends on your prior knowledge: - * - * (1) If the page is known to be either rightside-up or upside-down, use - * either pixOrientDetect*() with pleftconf = NULL, or - * pixUpDownDetect*(). [The '*' refers to either the rasterop - * or dwa versions.] - * - * (2) If any of the four orientations are possible, use pixOrientDetect*(). - * - * (3) If the text is horizontal and rightside-up, the only remaining - * degree of freedom is a left-right mirror flip: use - * pixMirrorDetect*(). - * - * (4) If you have a relatively large amount of numbers on the page, - * us the slower pixUpDownDetectGeneral(). - * - * We summarize the full orientation and mirror flip detection process: - * - * (1) First determine which of the four 90 degree rotations - * causes the text to be rightside-up. This can be done - * with either skew confidence or the pixOrientDetect*() - * signals. For the latter, see the table for pixOrientDetect(). - * - * (2) Then, with ascenders pointing up, apply pixMirrorDetect*(). - * In the normal situation the confidence confidence will be - * large and positive. However, if mirror flipped, the - * confidence will be large and negative. - * - * A high-level interface, pixOrientCorrect() combines the detection - * of the orientation with the rotation decision and the rotation itself. - * - * Finally, use can be made of programs such as exiftool and convert to - * read exif camera orientation data in jpeg files and conditionally rotate. - * Here is an example shell script, made by Dan9er: - * ================================================================== - * #!/bin/sh - * # orientByExif.sh - * # Dependencies: exiftool (exiflib) and convert (ImageMagick) - * # Note: if there is no exif orientation data in the jpeg file, - * # this simply copies the input file. - * # - * if [[ -z $(command -v exiftool) || -z $(command -v convert) ]]; then - * echo "You need to install dependencies; e.g.:" - * echo " sudo apt install libimage-exiftool-perl" - * echo " sudo apt install imagemagick" - * exit 1 - * fi - * if [[ $# != 2 ]]; then - * echo "Syntax: orientByExif infile outfile" - * exit 2 - * fi - * if [[ ${1: -4} != ".jpg" ]]; then - * echo "File is not a jpeg" - * exit 3 - * fi - * if [[ $(exiftool -s3 -n -Orientation "$1") = 1 ]]; then - * echo "Image is already upright" - * exit 0 - * fi - * convert "$1" -auto-orient "$2" - * echo "Done" - * exit 0 - * ================================================================== - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) Simple top-level function to detect if Roman text is in - * reading orientation, and to rotate the image accordingly if not. - * (2) Returns a copy if no rotation is needed. - * (3) See notes for pixOrientDetect() and pixOrientDecision(). - * Use 0.0 for default values for %minupconf and %minratio - * (4) Optional output of intermediate confidence results and - * the rotation performed on pixs. - *- */ -PIX * -pixOrientCorrect(PIX *pixs, - l_float32 minupconf, - l_float32 minratio, - l_float32 *pupconf, - l_float32 *pleftconf, - l_int32 *protation, - l_int32 debug) -{ -l_int32 orient; -l_float32 upconf, leftconf; -PIX *pix1; - - PROCNAME("pixOrientCorrect"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - - /* Get confidences for orientation */ - pixUpDownDetectDwa(pixs, &upconf, 0, debug); - pix1 = pixRotate90(pixs, 1); - pixUpDownDetectDwa(pix1, &leftconf, 0, debug); - pixDestroy(&pix1); - if (pupconf) *pupconf = upconf; - if (pleftconf) *pleftconf = leftconf; - - /* Decide what to do */ - makeOrientDecision(upconf,leftconf, minupconf, minratio, &orient, debug); - - /* Do it */ - switch (orient) - { - case L_TEXT_ORIENT_UNKNOWN: - L_INFO("text orientation not determined; no rotation\n", procName); - if (protation) *protation = 0; - return pixCopy(NULL, pixs); - break; - case L_TEXT_ORIENT_UP: - L_INFO("text is oriented up; no rotation\n", procName); - if (protation) *protation = 0; - return pixCopy(NULL, pixs); - break; - case L_TEXT_ORIENT_LEFT: - L_INFO("landscape; text oriented left; 90 cw rotation\n", procName); - if (protation) *protation = 90; - return pixRotateOrth(pixs, 1); - break; - case L_TEXT_ORIENT_DOWN: - L_INFO("text oriented down; 180 cw rotation\n", procName); - if (protation) *protation = 180; - return pixRotateOrth(pixs, 2); - break; - case L_TEXT_ORIENT_RIGHT: - L_INFO("landscape; text oriented right; 270 cw rotation\n", procName); - if (protation) *protation = 270; - return pixRotateOrth(pixs, 3); - break; - default: - L_ERROR("invalid orient flag!\n", procName); - return pixCopy(NULL, pixs); - } -} - - -/*----------------------------------------------------------------* - * Orientation detection (four 90 degree angles) * - * Rasterop implementation * - *----------------------------------------------------------------*/ -/*! - * \brief pixOrientDetect() - * - * \param[in] pixs 1 bpp, deskewed, English text, 150 - 300 ppi - * \param[out] pupconf [optional] ; may be NULL - * \param[out] pleftconf [optional] ; may be NULL - * \param[in] mincount min number of up + down; use 0 for default - * \param[in] debug 1 for debug output; 0 otherwise - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) See "Measuring document image skew and orientation" - * Dan S. Bloomberg, Gary E. Kopec and Lakshmi Dasari - * IS&T/SPIE EI'95, Conference 2422: Document Recognition II - * pp 302-316, Feb 6-7, 1995, San Jose, CA - * (2) upconf is the normalized difference between up ascenders - * and down ascenders. The image is analyzed without rotation - * for being rightside-up or upside-down. Set &upconf to null - * to skip this operation. - * (3) leftconf is the normalized difference between up ascenders - * and down ascenders in the image after it has been - * rotated 90 degrees clockwise. With that rotation, ascenders - * projecting to the left in the source image will project up - * in the rotated image. We compute this by rotating 90 degrees - * clockwise and testing for up and down ascenders. Set - * &leftconf to null to skip this operation. - * (4) Note that upconf and leftconf are not linear measures of - * confidence, e.g., in a range between 0 and 100. They - * measure how far you are out on the tail of a (presumably) - * normal distribution. For example, a confidence of 10 means - * that it is nearly certain that the difference did not - * happen at random. However, these values must be interpreted - * cautiously, taking into consideration the estimated prior - * for a particular orientation or mirror flip. The up-down - * signal is very strong if applied to text with ascenders - * up and down, and relatively weak for text at 90 degrees, - * but even at 90 degrees, the difference can look significant. - * For example, suppose the ascenders are oriented horizontally, - * but the test is done vertically. Then upconf can - * be < -MIN_CONF_FOR_UP_DOWN, suggesting the text may be - * upside-down. However, if instead the test were done - * horizontally, leftconf will be very much larger - * (in absolute value), giving the correct orientation. - * (5) If you compute both upconf and leftconf, and there is - * sufficient signal, the following table determines the - * cw angle necessary to rotate pixs so that the text is - * rightside-up: - * 0 deg : upconf >> 1, abs(upconf) >> abs(leftconf) - * 90 deg : leftconf >> 1, abs(leftconf) >> abs(upconf) - * 180 deg : upconf << -1, abs(upconf) >> abs(leftconf) - * 270 deg : leftconf << -1, abs(leftconf) >> abs(upconf) - * (6) One should probably not interpret the direction unless - * there are a sufficient number of counts for both orientations, - * in which case neither upconf nor leftconf will be 0.0. - * (7) This algorithm will fail on some images, such as tables, - * where most of the characters are numbers and appear as - * uppercase, but there are some repeated words that give a - * biased signal. It may be advisable to run a table detector - * first (e.g., pixDecideIfTable()), and not run the orientation - * detector if it is a table. - * (8) Uses rasterop implementation of HMT. - *- */ -l_ok -pixOrientDetect(PIX *pixs, - l_float32 *pupconf, - l_float32 *pleftconf, - l_int32 mincount, - l_int32 debug) -{ -PIX *pix1; - - PROCNAME("pixOrientDetect"); - - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (!pupconf && !pleftconf) - return ERROR_INT("nothing to do", procName, 1); - if (mincount == 0) - mincount = DefaultMinUpDownCount; - - if (pupconf) - pixUpDownDetect(pixs, pupconf, mincount, debug); - if (pleftconf) { - pix1 = pixRotate90(pixs, 1); - pixUpDownDetect(pix1, pleftconf, mincount, debug); - pixDestroy(&pix1); - } - - return 0; -} - - -/*! - * \brief makeOrientDecision() - * - * \param[in] upconf nonzero - * \param[in] leftconf nonzero - * \param[in] minupconf minimum value for which a decision can be made - * \param[in] minratio minimum conf ratio required for a decision - * \param[out] porient text orientation enum {0,1,2,3,4} - * \param[in] debug 1 for debug output; 0 otherwise - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This can be run after pixOrientDetect() - * (2) Both upconf and leftconf must be nonzero; otherwise the - * orientation cannot be determined. - * (3) The abs values of the input confidences are compared to - * minupconf. - * (4) The abs value of the largest of (upconf/leftconf) and - * (leftconf/upconf) is compared with minratio. - * (5) Input 0.0 for the default values for minupconf and minratio. - * (6) The return value of orient is interpreted thus: - * L_TEXT_ORIENT_UNKNOWN: not enough evidence to determine - * L_TEXT_ORIENT_UP: text rightside-up - * L_TEXT_ORIENT_LEFT: landscape, text up facing left - * L_TEXT_ORIENT_DOWN: text upside-down - * L_TEXT_ORIENT_RIGHT: landscape, text up facing right - *- */ -l_ok -makeOrientDecision(l_float32 upconf, - l_float32 leftconf, - l_float32 minupconf, - l_float32 minratio, - l_int32 *porient, - l_int32 debug) -{ -l_float32 absupconf, absleftconf; - - PROCNAME("makeOrientDecision"); - - if (!porient) - return ERROR_INT("&orient not defined", procName, 1); - *porient = L_TEXT_ORIENT_UNKNOWN; /* default: no decision */ - if (upconf == 0.0 || leftconf == 0.0) { - L_INFO("not enough confidence to get orientation\n", procName); - return 0; - } - - if (minupconf == 0.0) - minupconf = DefaultMinUpDownConf; - if (minratio == 0.0) - minratio = DefaultMinUpDownRatio; - absupconf = L_ABS(upconf); - absleftconf = L_ABS(leftconf); - - /* Here are the four possible orientation decisions, based - * on satisfaction of two threshold constraints. */ - if (upconf > minupconf && absupconf > minratio * absleftconf) - *porient = L_TEXT_ORIENT_UP; - else if (leftconf > minupconf && absleftconf > minratio * absupconf) - *porient = L_TEXT_ORIENT_LEFT; - else if (upconf < -minupconf && absupconf > minratio * absleftconf) - *porient = L_TEXT_ORIENT_DOWN; - else if (leftconf < -minupconf && absleftconf > minratio * absupconf) - *porient = L_TEXT_ORIENT_RIGHT; - - if (debug) { - lept_stderr("upconf = %7.3f, leftconf = %7.3f\n", upconf, leftconf); - if (*porient == L_TEXT_ORIENT_UNKNOWN) - lept_stderr("Confidence is low; no determination is made\n"); - else if (*porient == L_TEXT_ORIENT_UP) - lept_stderr("Text is rightside-up\n"); - else if (*porient == L_TEXT_ORIENT_LEFT) - lept_stderr("Text is rotated 90 deg ccw\n"); - else if (*porient == L_TEXT_ORIENT_DOWN) - lept_stderr("Text is upside-down\n"); - else /* *porient == L_TEXT_ORIENT_RIGHT */ - lept_stderr("Text is rotated 90 deg cw\n"); - } - - return 0; -} - - -/*! - * \brief pixUpDownDetect() - * - * \param[in] pixs 1 bpp, deskewed, English text, 150 - 300 ppi - * \param[out] pconf confidence that text is rightside-up - * \param[in] mincount min number of up + down; use 0 for default - * \param[in] debug 1 for debug output; 0 otherwise - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Special (typical, slightly faster) case, where the pixels - * identified through the HMT (hit-miss transform) are not - * clipped by a truncated word mask pixm. See pixOrientDetect() - * and pixUpDownDetectGeneral() for details. - * (2) The returned confidence is the normalized difference - * between the number of detected up and down ascenders, - * assuming that the text is either rightside-up or upside-down - * and not rotated at a 90 degree angle. - *- */ -l_ok -pixUpDownDetect(PIX *pixs, - l_float32 *pconf, - l_int32 mincount, - l_int32 debug) -{ - return pixUpDownDetectGeneral(pixs, pconf, mincount, 0, debug); -} - - -/*! - * \brief pixUpDownDetectGeneral() - * - * \param[in] pixs 1 bpp, deskewed, English text, 150 - 300 ppi - * \param[out] pconf confidence that text is rightside-up - * \param[in] mincount min number of up + down; use 0 for default - * \param[in] npixels number of pixels removed from each side of word box - * \param[in] debug 1 for debug output; 0 otherwise - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) See pixOrientDetect() for other details. - * (2) %conf is the normalized difference between the number of - * detected up and down ascenders, assuming that the text - * is either rightside-up or upside-down and not rotated - * at a 90 degree angle. - * (3) The typical mode of operation is %npixels == 0. - * If %npixels > 0, this removes HMT matches at the - * beginning and ending of "words." This is useful for - * pages that may have mostly digits, because if npixels == 0, - * leading "1" and "3" digits can register as having - * ascenders or descenders, and "7" digits can match descenders. - * Consequently, a page image of only digits may register - * as being upside-down. - * (4) We want to count the number of instances found using the HMT. - * An expensive way to do this would be to count the - * number of connected components. A cheap way is to do a rank - * reduction cascade that reduces each component to a single - * pixel, and results (after two or three 2x reductions) - * in one pixel for each of the original components. - * After the reduction, you have a much smaller pix over - * which to count pixels. We do only 2 reductions, because - * this function is designed to work for input pix between - * 150 and 300 ppi, and an 8x reduction on a 150 ppi image - * is going too far -- components will get merged. - *- */ -l_ok -pixUpDownDetectGeneral(PIX *pixs, - l_float32 *pconf, - l_int32 mincount, - l_int32 npixels, - l_int32 debug) -{ -l_int32 countup, countdown, nmax; -l_float32 nup, ndown; -PIX *pix0, *pix1, *pix2, *pix3, *pixm; -SEL *sel1, *sel2, *sel3, *sel4; - - PROCNAME("pixUpDownDetectGeneral"); - - if (!pconf) - return ERROR_INT("&conf not defined", procName, 1); - *pconf = 0.0; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (mincount == 0) - mincount = DefaultMinUpDownCount; - if (npixels < 0) - npixels = 0; - - if (debug) { - lept_mkdir("lept/orient"); - } - - sel1 = selCreateFromString(textsel1, 5, 6, NULL); - sel2 = selCreateFromString(textsel2, 5, 6, NULL); - sel3 = selCreateFromString(textsel3, 5, 6, NULL); - sel4 = selCreateFromString(textsel4, 5, 6, NULL); - - /* One of many reasonable pre-filtering sequences: (1, 8) and (30, 1). - * This closes holes in x-height characters and joins them at - * the x-height. There is more noise in the descender detection - * from this, but it works fairly well. */ - pix0 = pixMorphCompSequence(pixs, "c1.8 + c30.1", 0); - - /* Optionally, make a mask of the word bounding boxes, shortening - * each of them by a fixed amount at each end. */ - pixm = NULL; - if (npixels > 0) { - l_int32 i, nbox, x, y, w, h; - BOX *box; - BOXA *boxa; - pix1 = pixMorphSequence(pix0, "o10.1", 0); - boxa = pixConnComp(pix1, NULL, 8); - pixm = pixCreateTemplate(pix1); - pixDestroy(&pix1); - nbox = boxaGetCount(boxa); - for (i = 0; i < nbox; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - boxGetGeometry(box, &x, &y, &w, &h); - if (w > 2 * npixels) - pixRasterop(pixm, x + npixels, y - 6, w - 2 * npixels, h + 13, - PIX_SET, NULL, 0, 0); - boxDestroy(&box); - } - boxaDestroy(&boxa); - } - - /* Find the ascenders and optionally filter with pixm. - * For an explanation of the procedure used for counting the result - * of the HMT, see comments at the beginning of this function. */ - pix1 = pixHMT(NULL, pix0, sel1); - pix2 = pixHMT(NULL, pix0, sel2); - pixOr(pix1, pix1, pix2); - if (pixm) - pixAnd(pix1, pix1, pixm); - pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0); - pixCountPixels(pix3, &countup, NULL); - pixDebugFlipDetect("/tmp/lept/orient/up.png", pixs, pix1, debug); - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - - /* Find the ascenders and optionally filter with pixm. */ - pix1 = pixHMT(NULL, pix0, sel3); - pix2 = pixHMT(NULL, pix0, sel4); - pixOr(pix1, pix1, pix2); - if (pixm) - pixAnd(pix1, pix1, pixm); - pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0); - pixCountPixels(pix3, &countdown, NULL); - pixDebugFlipDetect("/tmp/lept/orient/down.png", pixs, pix1, debug); - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - - /* Evaluate statistically, generating a confidence that is - * related to the probability with a gaussian distribution. */ - nup = (l_float32)(countup); - ndown = (l_float32)(countdown); - nmax = L_MAX(countup, countdown); - if (nmax > mincount) - *pconf = 2. * ((nup - ndown) / sqrt(nup + ndown)); - - if (debug) { - if (pixm) pixWriteDebug("/tmp/lept/orient/pixm1.png", pixm, IFF_PNG); - lept_stderr("nup = %7.3f, ndown = %7.3f, conf = %7.3f\n", - nup, ndown, *pconf); - if (*pconf > DefaultMinUpDownConf) - lept_stderr("Text is rightside-up\n"); - if (*pconf < -DefaultMinUpDownConf) - lept_stderr("Text is upside-down\n"); - } - - pixDestroy(&pix0); - pixDestroy(&pixm); - selDestroy(&sel1); - selDestroy(&sel2); - selDestroy(&sel3); - selDestroy(&sel4); - return 0; -} - - -/*----------------------------------------------------------------* - * Orientation detection (four 90 degree angles) * - * DWA implementation * - *----------------------------------------------------------------*/ -/*! - * \brief pixOrientDetectDwa() - * - * \param[in] pixs 1 bpp, deskewed, English text - * \param[out] pupconf [optional] ; may be NULL - * \param[out] pleftconf [optional] ; may be NULL - * \param[in] mincount min number of up + down; use 0 for default - * \param[in] debug 1 for debug output; 0 otherwise - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Same interface as for pixOrientDetect(). See notes - * there for usage. - * (2) Uses auto-gen'd code for the Sels defined at the - * top of this file, with some renaming of functions. - * The auto-gen'd code is in fliphmtgen.c, and can - * be generated by a simple executable; see prog/flipselgen.c. - * (3) This runs about 2.5 times faster than the pixOrientDetect(). - *- */ -l_ok -pixOrientDetectDwa(PIX *pixs, - l_float32 *pupconf, - l_float32 *pleftconf, - l_int32 mincount, - l_int32 debug) -{ -PIX *pix1; - - PROCNAME("pixOrientDetectDwa"); - - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (!pupconf && !pleftconf) - return ERROR_INT("nothing to do", procName, 1); - if (mincount == 0) - mincount = DefaultMinUpDownCount; - - if (pupconf) - pixUpDownDetectDwa(pixs, pupconf, mincount, debug); - if (pleftconf) { - pix1 = pixRotate90(pixs, 1); - pixUpDownDetectDwa(pix1, pleftconf, mincount, debug); - pixDestroy(&pix1); - } - - return 0; -} - - -/*! - * \brief pixUpDownDetectDwa() - * - * \param[in] pixs 1 bpp, deskewed, English text, 150 - 300 ppi - * \param[out] pconf confidence that text is rightside-up - * \param[in] mincount min number of up + down; use 0 for default - * \param[in] debug 1 for debug output; 0 otherwise - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Faster (DWA) version of pixUpDownDetect(). - * (2) This is a special case (but typical and slightly faster) of - * pixUpDownDetectGeneralDwa(), where the pixels identified - * through the HMT (hit-miss transform) are not clipped by - * a truncated word mask pixm. See pixUpDownDetectGeneral() - * for usage and other details. - * (3) The returned confidence is the normalized difference - * between the number of detected up and down ascenders, - * assuming that the text is either rightside-up or upside-down - * and not rotated at a 90 degree angle. - *- */ -l_ok -pixUpDownDetectDwa(PIX *pixs, - l_float32 *pconf, - l_int32 mincount, - l_int32 debug) -{ - return pixUpDownDetectGeneralDwa(pixs, pconf, mincount, 0, debug); -} - - -/*! - * \brief pixUpDownDetectGeneralDwa() - * - * \param[in] pixs 1 bpp, deskewed, English text - * \param[out] pconf confidence that text is rightside-up - * \param[in] mincount min number of up + down; use 0 for default - * \param[in] npixels number of pixels removed from each side of word box - * \param[in] debug 1 for debug output; 0 otherwise - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) See the notes in pixUpDownDetectGeneral() for usage. - *- */ -l_ok -pixUpDownDetectGeneralDwa(PIX *pixs, - l_float32 *pconf, - l_int32 mincount, - l_int32 npixels, - l_int32 debug) -{ -char flipsel1[] = "flipsel1"; -char flipsel2[] = "flipsel2"; -char flipsel3[] = "flipsel3"; -char flipsel4[] = "flipsel4"; -l_int32 countup, countdown, nmax; -l_float32 nup, ndown; -PIX *pixt, *pix0, *pix1, *pix2, *pix3, *pixm; - - PROCNAME("pixUpDownDetectGeneralDwa"); - - if (!pconf) - return ERROR_INT("&conf not defined", procName, 1); - *pconf = 0.0; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (mincount == 0) - mincount = DefaultMinUpDownCount; - if (npixels < 0) - npixels = 0; - - /* One of many reasonable pre-filtering sequences: (1, 8) and (30, 1). - * This closes holes in x-height characters and joins them at - * the x-height. There is more noise in the descender detection - * from this, but it works fairly well. */ - pixt = pixMorphSequenceDwa(pixs, "c1.8 + c30.1", 0); - - /* Be sure to add the border before the flip DWA operations! */ - pix0 = pixAddBorderGeneral(pixt, ADDED_BORDER, ADDED_BORDER, - ADDED_BORDER, ADDED_BORDER, 0); - pixDestroy(&pixt); - - /* Optionally, make a mask of the word bounding boxes, shortening - * each of them by a fixed amount at each end. */ - pixm = NULL; - if (npixels > 0) { - l_int32 i, nbox, x, y, w, h; - BOX *box; - BOXA *boxa; - pix1 = pixMorphSequenceDwa(pix0, "o10.1", 0); - boxa = pixConnComp(pix1, NULL, 8); - pixm = pixCreateTemplate(pix1); - pixDestroy(&pix1); - nbox = boxaGetCount(boxa); - for (i = 0; i < nbox; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - boxGetGeometry(box, &x, &y, &w, &h); - if (w > 2 * npixels) - pixRasterop(pixm, x + npixels, y - 6, w - 2 * npixels, h + 13, - PIX_SET, NULL, 0, 0); - boxDestroy(&box); - } - boxaDestroy(&boxa); - } - - /* Find the ascenders and optionally filter with pixm. - * For an explanation of the procedure used for counting the result - * of the HMT, see comments in pixUpDownDetectGeneral(). */ - pix1 = pixFlipFHMTGen(NULL, pix0, flipsel1); - pix2 = pixFlipFHMTGen(NULL, pix0, flipsel2); - pixOr(pix1, pix1, pix2); - if (pixm) - pixAnd(pix1, pix1, pixm); - pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0); - pixCountPixels(pix3, &countup, NULL); - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - - /* Find the ascenders and optionally filter with pixm. */ - pix1 = pixFlipFHMTGen(NULL, pix0, flipsel3); - pix2 = pixFlipFHMTGen(NULL, pix0, flipsel4); - pixOr(pix1, pix1, pix2); - if (pixm) - pixAnd(pix1, pix1, pixm); - pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0); - pixCountPixels(pix3, &countdown, NULL); - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - - /* Evaluate statistically, generating a confidence that is - * related to the probability with a gaussian distribution. */ - nup = (l_float32)(countup); - ndown = (l_float32)(countdown); - nmax = L_MAX(countup, countdown); - if (nmax > mincount) - *pconf = 2. * ((nup - ndown) / sqrt(nup + ndown)); - - if (debug) { - if (pixm) { - lept_mkdir("lept/orient"); - pixWriteDebug("/tmp/lept/orient/pixm2.png", pixm, IFF_PNG); - } - lept_stderr("nup = %7.3f, ndown = %7.3f, conf = %7.3f\n", - nup, ndown, *pconf); - if (*pconf > DefaultMinUpDownConf) - lept_stderr("Text is rightside-up\n"); - if (*pconf < -DefaultMinUpDownConf) - lept_stderr("Text is upside-down\n"); - } - - pixDestroy(&pix0); - pixDestroy(&pixm); - return 0; -} - - - -/*----------------------------------------------------------------* - * Left-right mirror detection * - * Rasterop implementation * - *----------------------------------------------------------------*/ -/*! - * \brief pixMirrorDetect() - * - * \param[in] pixs 1 bpp, deskewed, English text - * \param[out] pconf confidence that text is not LR mirror reversed - * \param[in] mincount min number of left + right; use 0 for default - * \param[in] debug 1 for debug output; 0 otherwise - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) For this test, it is necessary that the text is horizontally - * oriented, with ascenders going up. - * (2) conf is the normalized difference between the number of - * right and left facing characters with ascenders. - * Left-facing are {d}; right-facing are {b, h, k}. - * At least that was the expectation. In practice, we can - * really just say that it is the normalized difference in - * hits using two specific hit-miss filters, textsel1 and textsel2, - * after the image has been suitably pre-filtered so that - * these filters are effective. See (4) for what's really happening. - * (3) A large positive conf value indicates normal text, whereas - * a large negative conf value means the page is mirror reversed. - * (4) The implementation is a bit tricky. The general idea is - * to fill the x-height part of characters, but not the space - * between them, before doing the HMT. This is done by - * finding pixels added using two different operations -- a - * horizontal close and a vertical dilation -- and adding - * the intersection of these sets to the original. It turns - * out that the original intuition about the signal was largely - * in error: much of the signal for right-facing characters - * comes from the lower part of common x-height characters, like - * the e and c, that remain open after these operations. - * So it's important that the operations to close the x-height - * parts of the characters are purposely weakened sufficiently - * to allow these characters to remain open. The wonders - * of morphology! - *- */ -l_ok -pixMirrorDetect(PIX *pixs, - l_float32 *pconf, - l_int32 mincount, - l_int32 debug) -{ -l_int32 count1, count2, nmax; -l_float32 nleft, nright; -PIX *pix0, *pix1, *pix2, *pix3; -SEL *sel1, *sel2; - - PROCNAME("pixMirrorDetect"); - - if (!pconf) - return ERROR_INT("&conf not defined", procName, 1); - *pconf = 0.0; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (mincount == 0) - mincount = DefaultMinMirrorFlipCount; - - if (debug) { - lept_mkdir("lept/orient"); - } - - sel1 = selCreateFromString(textsel1, 5, 6, NULL); - sel2 = selCreateFromString(textsel2, 5, 6, NULL); - - /* Fill x-height characters but not space between them, sort of. */ - pix3 = pixMorphCompSequence(pixs, "d1.30", 0); - pixXor(pix3, pix3, pixs); - pix0 = pixMorphCompSequence(pixs, "c15.1", 0); - pixXor(pix0, pix0, pixs); - pixAnd(pix0, pix0, pix3); - pixOr(pix0, pix0, pixs); - pixDestroy(&pix3); - - /* Filter the right-facing characters. */ - pix1 = pixHMT(NULL, pix0, sel1); - pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0); - pixCountPixels(pix3, &count1, NULL); - pixDebugFlipDetect("/tmp/lept/orient/right.png", pixs, pix1, debug); - pixDestroy(&pix1); - pixDestroy(&pix3); - - /* Filter the left-facing characters. */ - pix2 = pixHMT(NULL, pix0, sel2); - pix3 = pixReduceRankBinaryCascade(pix2, 1, 1, 0, 0); - pixCountPixels(pix3, &count2, NULL); - pixDebugFlipDetect("/tmp/lept/orient/left.png", pixs, pix2, debug); - pixDestroy(&pix2); - pixDestroy(&pix3); - - nright = (l_float32)count1; - nleft = (l_float32)count2; - nmax = L_MAX(count1, count2); - pixDestroy(&pix0); - selDestroy(&sel1); - selDestroy(&sel2); - - if (nmax > mincount) - *pconf = 2. * ((nright - nleft) / sqrt(nright + nleft)); - - if (debug) { - lept_stderr("nright = %f, nleft = %f\n", nright, nleft); - if (*pconf > DefaultMinMirrorFlipConf) - lept_stderr("Text is not mirror reversed\n"); - if (*pconf < -DefaultMinMirrorFlipConf) - lept_stderr("Text is mirror reversed\n"); - } - - return 0; -} - - -/*----------------------------------------------------------------* - * Left-right mirror detection * - * DWA implementation * - *----------------------------------------------------------------*/ -/*! - * \brief pixMirrorDetectDwa() - * - * \param[in] pixs 1 bpp, deskewed, English text - * \param[out] pconf confidence that text is not LR mirror reversed - * \param[in] mincount min number of left + right; use 0 for default - * \param[in] debug 1 for debug output; 0 otherwise - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) We assume the text is horizontally oriented, with - * ascenders going up. - * (2) See notes in pixMirrorDetect(). - *- */ -l_ok -pixMirrorDetectDwa(PIX *pixs, - l_float32 *pconf, - l_int32 mincount, - l_int32 debug) -{ -char flipsel1[] = "flipsel1"; -char flipsel2[] = "flipsel2"; -l_int32 count1, count2, nmax; -l_float32 nleft, nright; -PIX *pix0, *pix1, *pix2, *pix3; - - PROCNAME("pixMirrorDetectDwa"); - - if (!pconf) - return ERROR_INT("&conf not defined", procName, 1); - *pconf = 0.0; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (mincount == 0) - mincount = DefaultMinMirrorFlipCount; - - /* Fill x-height characters but not space between them, sort of. */ - pix3 = pixMorphSequenceDwa(pixs, "d1.30", 0); - pixXor(pix3, pix3, pixs); - pix0 = pixMorphSequenceDwa(pixs, "c15.1", 0); - pixXor(pix0, pix0, pixs); - pixAnd(pix0, pix0, pix3); - pixOr(pix3, pix0, pixs); - pixDestroy(&pix0); - pix0 = pixAddBorderGeneral(pix3, ADDED_BORDER, ADDED_BORDER, - ADDED_BORDER, ADDED_BORDER, 0); - pixDestroy(&pix3); - - /* Filter the right-facing characters. */ - pix1 = pixFlipFHMTGen(NULL, pix0, flipsel1); - pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0); - pixCountPixels(pix3, &count1, NULL); - pixDestroy(&pix1); - pixDestroy(&pix3); - - /* Filter the left-facing characters. */ - pix2 = pixFlipFHMTGen(NULL, pix0, flipsel2); - pix3 = pixReduceRankBinaryCascade(pix2, 1, 1, 0, 0); - pixCountPixels(pix3, &count2, NULL); - pixDestroy(&pix2); - pixDestroy(&pix3); - - pixDestroy(&pix0); - nright = (l_float32)count1; - nleft = (l_float32)count2; - nmax = L_MAX(count1, count2); - - if (nmax > mincount) - *pconf = 2. * ((nright - nleft) / sqrt(nright + nleft)); - - if (debug) { - lept_stderr("nright = %f, nleft = %f\n", nright, nleft); - if (*pconf > DefaultMinMirrorFlipConf) - lept_stderr("Text is not mirror reversed\n"); - if (*pconf < -DefaultMinMirrorFlipConf) - lept_stderr("Text is mirror reversed\n"); - } - - return 0; -} - - -/*----------------------------------------------------------------* - * Static debug helper * - *----------------------------------------------------------------*/ -/* - * \brief pixDebugFlipDetect() - * - * \param[in] filename for output debug file - * \param[in] pixs input to pix*Detect - * \param[in] pixhm hit-miss result from ascenders or descenders - * \param[in] enable 1 to enable this function; 0 to disable - * \return void - */ -static void -pixDebugFlipDetect(const char *filename, - PIX *pixs, - PIX *pixhm, - l_int32 enable) -{ -PIX *pixt, *pixthm; - - if (!enable) return; - - /* Display with red dot at counted locations */ - pixt = pixConvert1To4Cmap(pixs); - pixthm = pixMorphSequence(pixhm, "d5.5", 0); - pixSetMaskedCmap(pixt, pixthm, 0, 0, 255, 0, 0); - - pixWriteDebug(filename, pixt, IFF_PNG); - pixDestroy(&pixthm); - pixDestroy(&pixt); - return; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fliphmtgen.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fliphmtgen.c deleted file mode 100644 index 2d76edad..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fliphmtgen.c +++ /dev/null @@ -1,360 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -/* - * fliphmtgen.c - * - * DWA implementation of hit-miss transforms with auto-generated sels - * for pixOrientDetectDwa() and pixUpDownDetectDwa() in flipdetect.c - * - * PIX *pixFlipFHMTGen() - * static l_int32 flipfhmtgen_low() -- dispatcher - * static void fhmt_1_0() - * static void fhmt_1_1() - * static void fhmt_1_2() - * static void fhmt_1_3() - * - * The code (rearranged) was generated by prog/flipselgen.c - */ - -#ifdef HAVE_CONFIG_H -#include
- * - * Main function calls: - * l_int32 fmorphautogen() - * l_int32 fmorphautogen1() - * l_int32 fmorphautogen2() - * - * Static helpers: - * static SARRAY *sarrayMakeWplsCode() - * static SARRAY *sarrayMakeInnerLoopDWACode() - * static char *makeBarrelshiftString() - * - * - * This automatically generates dwa code for erosion and dilation. - * Here's a road map for how it all works. - * - * (1) You generate an array (a SELA) of structuring elements (SELs). - * This can be done in several ways, including - * (a) calling the function selaAddBasic() for - * pre-compiled SELs - * (b) generating the SELA in code in line - * (c) reading in a SELA from file, using selaRead() or - * various other formats. - * - * (2) You call fmorphautogen1() and fmorphautogen2() on this SELA. - * These use the text files morphtemplate1.txt and - * morphtemplate2.txt for building up the source code. See the file - * prog/fmorphautogen.c for an example of how this is done. - * The output is written to files named fmorphgen.*.c - * and fmorphgenlow.*.c, where "*" is an integer that you - * input to this function. That integer labels both - * the output files, as well as all the functions that - * are generated. That way, using different integers, - * you can invoke fmorphautogen() any number of times - * to get functions that all have different names so that - * they can be linked into one program. - * - * (3) You copy the generated source files back to your src - * directory for compilation. Put their names in the - * Makefile, regenerate the prototypes, and recompile - * the library. Look at the Makefile to see how I've - * included morphgen.1.c and fmorphgenlow.1.c. These files - * provide the high-level interfaces for erosion, dilation, - * opening and closing, and the low-level interfaces to - * do the actual work, for all 58 SELs in the SEL array. - * - * (4) In an application, you now use this interface. Again - * for the example files in the library, using integer "1": - * - * PIX *pixMorphDwa_1(PIX *pixd, PIX, *pixs, - * l_int32 operation, char *selname); - * - * or - * - * PIX *pixFMorphopGen_1(PIX *pixd, PIX *pixs, - * l_int32 operation, char *selname); - * - * where the operation is one of {L_MORPH_DILATE, L_MORPH_ERODE. - * L_MORPH_OPEN, L_MORPH_CLOSE}, and the selname is one - * of the set that were defined as the name field of sels. - * This set is listed at the beginning of the file fmorphgen.1.c. - * For examples of use, see the file prog/binmorph_reg1.c, which - * verifies the consistency of the various implementations by - * comparing the dwa result with that of full-image rasterops. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This function generates all the code for implementing - * dwa morphological operations using all the sels in the sela. - * (2) See fmorphautogen1() and fmorphautogen2() for details. - *- */ -l_ok -fmorphautogen(SELA *sela, - l_int32 fileindex, - const char *filename) -{ -l_int32 ret1, ret2; - - PROCNAME("fmorphautogen"); - - if (!sela) - return ERROR_INT("sela not defined", procName, 1); - ret1 = fmorphautogen1(sela, fileindex, filename); - ret2 = fmorphautogen2(sela, fileindex, filename); - if (ret1 || ret2) - return ERROR_INT("code generation problem", procName, 1); - return 0; -} - - -/*! - * \brief fmorphautogen1() - * - * \param[in] sela - * \param[in] fileindex - * \param[in] filename [optional]; can be null - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This function uses morphtemplate1.txt to create a - * top-level file that contains two functions. These - * functions will carry out dilation, erosion, - * opening or closing for any of the sels in the input sela. - * (2) The fileindex parameter is inserted into the output - * filename, as described below. - * (3) If filename == NULL, the output file is fmorphgen.[n].c, - * where [n] is equal to the %fileindex parameter. - * (4) If filename != NULL, the output file is [%filename].[n].c. - *- */ -l_ok -fmorphautogen1(SELA *sela, - l_int32 fileindex, - const char *filename) -{ -char *filestr; -char *str_proto1, *str_proto2, *str_proto3; -char *str_doc1, *str_doc2, *str_doc3, *str_doc4; -char *str_def1, *str_def2, *str_proc1, *str_proc2; -char *str_dwa1, *str_low_dt, *str_low_ds, *str_low_ts; -char *str_low_tsp1, *str_low_dtp1; -char bigbuf[L_BUF_SIZE]; -l_int32 i, nsels, nbytes, actstart, end, newstart; -size_t size; -SARRAY *sa1, *sa2, *sa3; - - PROCNAME("fmorphautogen1"); - - if (!sela) - return ERROR_INT("sela not defined", procName, 1); - if (fileindex < 0) - fileindex = 0; - if ((nsels = selaGetCount(sela)) == 0) - return ERROR_INT("no sels in sela", procName, 1); - - /* Make array of textlines from morphtemplate1.txt */ - if ((filestr = (char *)l_binaryRead(TEMPLATE1, &size)) == NULL) - return ERROR_INT("filestr not made", procName, 1); - sa2 = sarrayCreateLinesFromString(filestr, 1); - LEPT_FREE(filestr); - if (!sa2) - return ERROR_INT("sa2 not made", procName, 1); - - /* Make array of sel names */ - sa1 = selaGetSelnames(sela); - - /* Make strings containing function call names */ - sprintf(bigbuf, "PIX *pixMorphDwa_%d(PIX *pixd, PIX *pixs, " - "l_int32 operation, char *selname);", fileindex); - str_proto1 = stringNew(bigbuf); - sprintf(bigbuf, "PIX *pixFMorphopGen_%d(PIX *pixd, PIX *pixs, " - "l_int32 operation, char *selname);", fileindex); - str_proto2 = stringNew(bigbuf); - sprintf(bigbuf, "l_int32 fmorphopgen_low_%d(l_uint32 *datad, l_int32 w,\n" - " l_int32 h, l_int32 wpld,\n" - " l_uint32 *datas, l_int32 wpls,\n" - " l_int32 index);", fileindex); - str_proto3 = stringNew(bigbuf); - sprintf(bigbuf, " * PIX *pixMorphDwa_%d()", fileindex); - str_doc1 = stringNew(bigbuf); - sprintf(bigbuf, " * PIX *pixFMorphopGen_%d()", fileindex); - str_doc2 = stringNew(bigbuf); - sprintf(bigbuf, " * \\brief pixMorphDwa_%d()", fileindex); - str_doc3 = stringNew(bigbuf); - sprintf(bigbuf, " * \\brief pixFMorphopGen_%d()", fileindex); - str_doc4 = stringNew(bigbuf); - sprintf(bigbuf, "pixMorphDwa_%d(PIX *pixd,", fileindex); - str_def1 = stringNew(bigbuf); - sprintf(bigbuf, "pixFMorphopGen_%d(PIX *pixd,", fileindex); - str_def2 = stringNew(bigbuf); - sprintf(bigbuf, " PROCNAME(\"pixMorphDwa_%d\");", fileindex); - str_proc1 = stringNew(bigbuf); - sprintf(bigbuf, " PROCNAME(\"pixFMorphopGen_%d\");", fileindex); - str_proc2 = stringNew(bigbuf); - sprintf(bigbuf, - " pixt2 = pixFMorphopGen_%d(NULL, pixt1, operation, selname);", - fileindex); - str_dwa1 = stringNew(bigbuf); - sprintf(bigbuf, - " fmorphopgen_low_%d(datad, w, h, wpld, datat, wpls, index);", - fileindex); - str_low_dt = stringNew(bigbuf); - sprintf(bigbuf, - " fmorphopgen_low_%d(datad, w, h, wpld, datas, wpls, index);", - fileindex); - str_low_ds = stringNew(bigbuf); - sprintf(bigbuf, - " fmorphopgen_low_%d(datat, w, h, wpls, datas, wpls, index+1);", - fileindex); - str_low_tsp1 = stringNew(bigbuf); - sprintf(bigbuf, - " fmorphopgen_low_%d(datat, w, h, wpls, datas, wpls, index);", - fileindex); - str_low_ts = stringNew(bigbuf); - sprintf(bigbuf, - " fmorphopgen_low_%d(datad, w, h, wpld, datat, wpls, index+1);", - fileindex); - str_low_dtp1 = stringNew(bigbuf); - - /* Make the output sa */ - sa3 = sarrayCreate(0); - - /* Copyright notice and info header */ - sarrayParseRange(sa2, 0, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - - /* Insert function names as documentation */ - sarrayAddString(sa3, str_doc1, L_INSERT); - sarrayAddString(sa3, str_doc2, L_INSERT); - - /* Add '#include's */ - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - - /* Insert function prototypes */ - sarrayAddString(sa3, str_proto1, L_INSERT); - sarrayAddString(sa3, str_proto2, L_INSERT); - sarrayAddString(sa3, str_proto3, L_INSERT); - - /* Add static globals */ - sprintf(bigbuf, "\nstatic l_int32 NUM_SELS_GENERATED = %d;", nsels); - sarrayAddString(sa3, bigbuf, L_COPY); - sprintf(bigbuf, "static char SEL_NAMES[][80] = {"); - sarrayAddString(sa3, bigbuf, L_COPY); - for (i = 0; i < nsels - 1; i++) { - sprintf(bigbuf, " \"%s\",", - sarrayGetString(sa1, i, L_NOCOPY)); - sarrayAddString(sa3, bigbuf, L_COPY); - } - sprintf(bigbuf, " \"%s\"};", - sarrayGetString(sa1, i, L_NOCOPY)); - sarrayAddString(sa3, bigbuf, L_COPY); - - /* Start pixMorphDwa_*() function description */ - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - sarrayAddString(sa3, str_doc3, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - - /* Finish pixMorphDwa_*() function definition */ - sarrayAddString(sa3, str_def1, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - sarrayAddString(sa3, str_proc1, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - sarrayAddString(sa3, str_dwa1, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - - /* Start pixFMorphopGen_*() function description */ - sarrayAddString(sa3, str_doc4, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - - /* Finish pixFMorphopGen_*() function definition */ - sarrayAddString(sa3, str_def2, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - sarrayAddString(sa3, str_proc2, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - sarrayAddString(sa3, str_low_dt, L_COPY); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - sarrayAddString(sa3, str_low_ds, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - sarrayAddString(sa3, str_low_tsp1, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - sarrayAddString(sa3, str_low_dt, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - sarrayAddString(sa3, str_low_ts, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - sarrayAddString(sa3, str_low_dtp1, L_INSERT); - sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); - sarrayAppendRange(sa3, sa2, actstart, end); - - /* Output to file */ - filestr = sarrayToString(sa3, 1); - nbytes = strlen(filestr); - if (filename) - snprintf(bigbuf, L_BUF_SIZE, "%s.%d.c", filename, fileindex); - else - sprintf(bigbuf, "%s.%d.c", OUTROOT, fileindex); - l_binaryWrite(bigbuf, "w", filestr, nbytes); - sarrayDestroy(&sa1); - sarrayDestroy(&sa2); - sarrayDestroy(&sa3); - LEPT_FREE(filestr); - return 0; -} - - -/* - * fmorphautogen2() - * - * Input: sela - * fileindex - * filename (
- * Notes: - * (1) This simply adds a border, calls the appropriate - * pixFMorphopGen_*(), and removes the border. - * See the notes for that function. - * (2) The size of the border depends on the operation - * and the boundary conditions. - *- */ -PIX * -pixMorphDwa_1(PIX *pixd, - PIX *pixs, - l_int32 operation, - char *selname) -{ -l_int32 bordercolor, bordersize; -PIX *pixt1, *pixt2, *pixt3; - - PROCNAME("pixMorphDwa_1"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, pixd); - - /* Set the border size */ - bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); - bordersize = 32; - if (bordercolor == 0 && operation == L_MORPH_CLOSE) - bordersize += 32; - - pixt1 = pixAddBorder(pixs, bordersize, 0); - pixt2 = pixFMorphopGen_1(NULL, pixt1, operation, selname); - pixt3 = pixRemoveBorder(pixt2, bordersize); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - - if (!pixd) - return pixt3; - - pixCopy(pixd, pixt3); - pixDestroy(&pixt3); - return pixd; -} - - -/*! - * \brief pixFMorphopGen_1() - * - * \param[in] pixd usual 3 choices: null, == pixs, != pixs - * \param[in] pixs 1 bpp - * \param[in] operation L_MORPH_DILATE, L_MORPH_ERODE, - * L_MORPH_OPEN, L_MORPH_CLOSE - * \param[in] sel name - * \return pixd - * - *
- * Notes: - * (1) This is a dwa operation, and the Sels must be limited in - * size to not more than 31 pixels about the origin. - * (2) A border of appropriate size (32 pixels, or 64 pixels - * for safe closing with asymmetric b.c.) must be added before - * this function is called. - * (3) This handles all required setting of the border pixels - * before erosion and dilation. - * (4) The closing operation is safe; no pixels can be removed - * near the boundary. - *- */ -PIX * -pixFMorphopGen_1(PIX *pixd, - PIX *pixs, - l_int32 operation, - char *selname) -{ -l_int32 i, index, found, w, h, wpls, wpld, bordercolor, erodeop, borderop; -l_uint32 *datad, *datas, *datat; -PIX *pixt; - - PROCNAME("pixFMorphopGen_1"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs must be 1 bpp", procName, pixd); - - /* Get boundary colors to use */ - bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); - if (bordercolor == 1) - erodeop = PIX_SET; - else - erodeop = PIX_CLR; - - found = FALSE; - for (i = 0; i < NUM_SELS_GENERATED; i++) { - if (strcmp(selname, SEL_NAMES[i]) == 0) { - found = TRUE; - index = 2 * i; - break; - } - } - if (found == FALSE) - return (PIX *)ERROR_PTR("sel index not found", procName, pixd); - - if (!pixd) { - if ((pixd = pixCreateTemplate(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - else /* for in-place or pre-allocated */ - pixResizeImageData(pixd, pixs); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - - /* The images must be surrounded, in advance, with a border of - * size 32 pixels (or 64, for closing), that we'll read from. - * Fabricate a "proper" image as the subimage within the 32 - * pixel border, having the following parameters: */ - w = pixGetWidth(pixs) - 64; - h = pixGetHeight(pixs) - 64; - datas = pixGetData(pixs) + 32 * wpls + 1; - datad = pixGetData(pixd) + 32 * wpld + 1; - - if (operation == L_MORPH_DILATE || operation == L_MORPH_ERODE) { - borderop = PIX_CLR; - if (operation == L_MORPH_ERODE) { - borderop = erodeop; - index++; - } - if (pixd == pixs) { /* in-place; generate a temp image */ - if ((pixt = pixCopy(NULL, pixs)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, pixd); - datat = pixGetData(pixt) + 32 * wpls + 1; - pixSetOrClearBorder(pixt, 32, 32, 32, 32, borderop); - fmorphopgen_low_1(datad, w, h, wpld, datat, wpls, index); - pixDestroy(&pixt); - } - else { /* not in-place */ - pixSetOrClearBorder(pixs, 32, 32, 32, 32, borderop); - fmorphopgen_low_1(datad, w, h, wpld, datas, wpls, index); - } - } - else { /* opening or closing; generate a temp image */ - if ((pixt = pixCreateTemplate(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, pixd); - datat = pixGetData(pixt) + 32 * wpls + 1; - if (operation == L_MORPH_OPEN) { - pixSetOrClearBorder(pixs, 32, 32, 32, 32, erodeop); - fmorphopgen_low_1(datat, w, h, wpls, datas, wpls, index+1); - pixSetOrClearBorder(pixt, 32, 32, 32, 32, PIX_CLR); - fmorphopgen_low_1(datad, w, h, wpld, datat, wpls, index); - } - else { /* closing */ - pixSetOrClearBorder(pixs, 32, 32, 32, 32, PIX_CLR); - fmorphopgen_low_1(datat, w, h, wpls, datas, wpls, index); - pixSetOrClearBorder(pixt, 32, 32, 32, 32, erodeop); - fmorphopgen_low_1(datad, w, h, wpld, datat, wpls, index+1); - } - pixDestroy(&pixt); - } - - return pixd; -} - diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fmorphgenlow.1.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fmorphgenlow.1.c deleted file mode 100644 index dd43da2e..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fmorphgenlow.1.c +++ /dev/null @@ -1,5862 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -/*! - * Low-level fast binary morphology with auto-generated sels - * - * Dispatcher: - * l_int32 fmorphopgen_low_1() - * - * Static Low-level: - * void fdilate_1_*() - * void ferode_1_*() - */ - -#include "allheaders.h" - -static void fdilate_1_0(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_0(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_1(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_1(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_2(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_2(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_3(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_3(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_4(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_4(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_5(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_5(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_6(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_6(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_7(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_7(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_8(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_8(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_9(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_9(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_10(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_10(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_11(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_11(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_12(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_12(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_13(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_13(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_14(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_14(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_15(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_15(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_16(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_16(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_17(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_17(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_18(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_18(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_19(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_19(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_20(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_20(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_21(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_21(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_22(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_22(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_23(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_23(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_24(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_24(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_25(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_25(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_26(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_26(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_27(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_27(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_28(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_28(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_29(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_29(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_30(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_30(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_31(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_31(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_32(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_32(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_33(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_33(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_34(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_34(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_35(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_35(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_36(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_36(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_37(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_37(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_38(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_38(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_39(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_39(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_40(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_40(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_41(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_41(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_42(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_42(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_43(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_43(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_44(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_44(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_45(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_45(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_46(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_46(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_47(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_47(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_48(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_48(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_49(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_49(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_50(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_50(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_51(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_51(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_52(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_52(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_53(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_53(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_54(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_54(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_55(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_55(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_56(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_56(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void fdilate_1_57(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); -static void ferode_1_57(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32); - - -/*---------------------------------------------------------------------* - * Fast morph dispatcher * - *---------------------------------------------------------------------*/ -/*! - * fmorphopgen_low_1() - * - * a dispatcher to appropriate low-level code - */ -l_int32 -fmorphopgen_low_1(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls, - l_int32 index) -{ - - switch (index) - { - case 0: - fdilate_1_0(datad, w, h, wpld, datas, wpls); - break; - case 1: - ferode_1_0(datad, w, h, wpld, datas, wpls); - break; - case 2: - fdilate_1_1(datad, w, h, wpld, datas, wpls); - break; - case 3: - ferode_1_1(datad, w, h, wpld, datas, wpls); - break; - case 4: - fdilate_1_2(datad, w, h, wpld, datas, wpls); - break; - case 5: - ferode_1_2(datad, w, h, wpld, datas, wpls); - break; - case 6: - fdilate_1_3(datad, w, h, wpld, datas, wpls); - break; - case 7: - ferode_1_3(datad, w, h, wpld, datas, wpls); - break; - case 8: - fdilate_1_4(datad, w, h, wpld, datas, wpls); - break; - case 9: - ferode_1_4(datad, w, h, wpld, datas, wpls); - break; - case 10: - fdilate_1_5(datad, w, h, wpld, datas, wpls); - break; - case 11: - ferode_1_5(datad, w, h, wpld, datas, wpls); - break; - case 12: - fdilate_1_6(datad, w, h, wpld, datas, wpls); - break; - case 13: - ferode_1_6(datad, w, h, wpld, datas, wpls); - break; - case 14: - fdilate_1_7(datad, w, h, wpld, datas, wpls); - break; - case 15: - ferode_1_7(datad, w, h, wpld, datas, wpls); - break; - case 16: - fdilate_1_8(datad, w, h, wpld, datas, wpls); - break; - case 17: - ferode_1_8(datad, w, h, wpld, datas, wpls); - break; - case 18: - fdilate_1_9(datad, w, h, wpld, datas, wpls); - break; - case 19: - ferode_1_9(datad, w, h, wpld, datas, wpls); - break; - case 20: - fdilate_1_10(datad, w, h, wpld, datas, wpls); - break; - case 21: - ferode_1_10(datad, w, h, wpld, datas, wpls); - break; - case 22: - fdilate_1_11(datad, w, h, wpld, datas, wpls); - break; - case 23: - ferode_1_11(datad, w, h, wpld, datas, wpls); - break; - case 24: - fdilate_1_12(datad, w, h, wpld, datas, wpls); - break; - case 25: - ferode_1_12(datad, w, h, wpld, datas, wpls); - break; - case 26: - fdilate_1_13(datad, w, h, wpld, datas, wpls); - break; - case 27: - ferode_1_13(datad, w, h, wpld, datas, wpls); - break; - case 28: - fdilate_1_14(datad, w, h, wpld, datas, wpls); - break; - case 29: - ferode_1_14(datad, w, h, wpld, datas, wpls); - break; - case 30: - fdilate_1_15(datad, w, h, wpld, datas, wpls); - break; - case 31: - ferode_1_15(datad, w, h, wpld, datas, wpls); - break; - case 32: - fdilate_1_16(datad, w, h, wpld, datas, wpls); - break; - case 33: - ferode_1_16(datad, w, h, wpld, datas, wpls); - break; - case 34: - fdilate_1_17(datad, w, h, wpld, datas, wpls); - break; - case 35: - ferode_1_17(datad, w, h, wpld, datas, wpls); - break; - case 36: - fdilate_1_18(datad, w, h, wpld, datas, wpls); - break; - case 37: - ferode_1_18(datad, w, h, wpld, datas, wpls); - break; - case 38: - fdilate_1_19(datad, w, h, wpld, datas, wpls); - break; - case 39: - ferode_1_19(datad, w, h, wpld, datas, wpls); - break; - case 40: - fdilate_1_20(datad, w, h, wpld, datas, wpls); - break; - case 41: - ferode_1_20(datad, w, h, wpld, datas, wpls); - break; - case 42: - fdilate_1_21(datad, w, h, wpld, datas, wpls); - break; - case 43: - ferode_1_21(datad, w, h, wpld, datas, wpls); - break; - case 44: - fdilate_1_22(datad, w, h, wpld, datas, wpls); - break; - case 45: - ferode_1_22(datad, w, h, wpld, datas, wpls); - break; - case 46: - fdilate_1_23(datad, w, h, wpld, datas, wpls); - break; - case 47: - ferode_1_23(datad, w, h, wpld, datas, wpls); - break; - case 48: - fdilate_1_24(datad, w, h, wpld, datas, wpls); - break; - case 49: - ferode_1_24(datad, w, h, wpld, datas, wpls); - break; - case 50: - fdilate_1_25(datad, w, h, wpld, datas, wpls); - break; - case 51: - ferode_1_25(datad, w, h, wpld, datas, wpls); - break; - case 52: - fdilate_1_26(datad, w, h, wpld, datas, wpls); - break; - case 53: - ferode_1_26(datad, w, h, wpld, datas, wpls); - break; - case 54: - fdilate_1_27(datad, w, h, wpld, datas, wpls); - break; - case 55: - ferode_1_27(datad, w, h, wpld, datas, wpls); - break; - case 56: - fdilate_1_28(datad, w, h, wpld, datas, wpls); - break; - case 57: - ferode_1_28(datad, w, h, wpld, datas, wpls); - break; - case 58: - fdilate_1_29(datad, w, h, wpld, datas, wpls); - break; - case 59: - ferode_1_29(datad, w, h, wpld, datas, wpls); - break; - case 60: - fdilate_1_30(datad, w, h, wpld, datas, wpls); - break; - case 61: - ferode_1_30(datad, w, h, wpld, datas, wpls); - break; - case 62: - fdilate_1_31(datad, w, h, wpld, datas, wpls); - break; - case 63: - ferode_1_31(datad, w, h, wpld, datas, wpls); - break; - case 64: - fdilate_1_32(datad, w, h, wpld, datas, wpls); - break; - case 65: - ferode_1_32(datad, w, h, wpld, datas, wpls); - break; - case 66: - fdilate_1_33(datad, w, h, wpld, datas, wpls); - break; - case 67: - ferode_1_33(datad, w, h, wpld, datas, wpls); - break; - case 68: - fdilate_1_34(datad, w, h, wpld, datas, wpls); - break; - case 69: - ferode_1_34(datad, w, h, wpld, datas, wpls); - break; - case 70: - fdilate_1_35(datad, w, h, wpld, datas, wpls); - break; - case 71: - ferode_1_35(datad, w, h, wpld, datas, wpls); - break; - case 72: - fdilate_1_36(datad, w, h, wpld, datas, wpls); - break; - case 73: - ferode_1_36(datad, w, h, wpld, datas, wpls); - break; - case 74: - fdilate_1_37(datad, w, h, wpld, datas, wpls); - break; - case 75: - ferode_1_37(datad, w, h, wpld, datas, wpls); - break; - case 76: - fdilate_1_38(datad, w, h, wpld, datas, wpls); - break; - case 77: - ferode_1_38(datad, w, h, wpld, datas, wpls); - break; - case 78: - fdilate_1_39(datad, w, h, wpld, datas, wpls); - break; - case 79: - ferode_1_39(datad, w, h, wpld, datas, wpls); - break; - case 80: - fdilate_1_40(datad, w, h, wpld, datas, wpls); - break; - case 81: - ferode_1_40(datad, w, h, wpld, datas, wpls); - break; - case 82: - fdilate_1_41(datad, w, h, wpld, datas, wpls); - break; - case 83: - ferode_1_41(datad, w, h, wpld, datas, wpls); - break; - case 84: - fdilate_1_42(datad, w, h, wpld, datas, wpls); - break; - case 85: - ferode_1_42(datad, w, h, wpld, datas, wpls); - break; - case 86: - fdilate_1_43(datad, w, h, wpld, datas, wpls); - break; - case 87: - ferode_1_43(datad, w, h, wpld, datas, wpls); - break; - case 88: - fdilate_1_44(datad, w, h, wpld, datas, wpls); - break; - case 89: - ferode_1_44(datad, w, h, wpld, datas, wpls); - break; - case 90: - fdilate_1_45(datad, w, h, wpld, datas, wpls); - break; - case 91: - ferode_1_45(datad, w, h, wpld, datas, wpls); - break; - case 92: - fdilate_1_46(datad, w, h, wpld, datas, wpls); - break; - case 93: - ferode_1_46(datad, w, h, wpld, datas, wpls); - break; - case 94: - fdilate_1_47(datad, w, h, wpld, datas, wpls); - break; - case 95: - ferode_1_47(datad, w, h, wpld, datas, wpls); - break; - case 96: - fdilate_1_48(datad, w, h, wpld, datas, wpls); - break; - case 97: - ferode_1_48(datad, w, h, wpld, datas, wpls); - break; - case 98: - fdilate_1_49(datad, w, h, wpld, datas, wpls); - break; - case 99: - ferode_1_49(datad, w, h, wpld, datas, wpls); - break; - case 100: - fdilate_1_50(datad, w, h, wpld, datas, wpls); - break; - case 101: - ferode_1_50(datad, w, h, wpld, datas, wpls); - break; - case 102: - fdilate_1_51(datad, w, h, wpld, datas, wpls); - break; - case 103: - ferode_1_51(datad, w, h, wpld, datas, wpls); - break; - case 104: - fdilate_1_52(datad, w, h, wpld, datas, wpls); - break; - case 105: - ferode_1_52(datad, w, h, wpld, datas, wpls); - break; - case 106: - fdilate_1_53(datad, w, h, wpld, datas, wpls); - break; - case 107: - ferode_1_53(datad, w, h, wpld, datas, wpls); - break; - case 108: - fdilate_1_54(datad, w, h, wpld, datas, wpls); - break; - case 109: - ferode_1_54(datad, w, h, wpld, datas, wpls); - break; - case 110: - fdilate_1_55(datad, w, h, wpld, datas, wpls); - break; - case 111: - ferode_1_55(datad, w, h, wpld, datas, wpls); - break; - case 112: - fdilate_1_56(datad, w, h, wpld, datas, wpls); - break; - case 113: - ferode_1_56(datad, w, h, wpld, datas, wpls); - break; - case 114: - fdilate_1_57(datad, w, h, wpld, datas, wpls); - break; - case 115: - ferode_1_57(datad, w, h, wpld, datas, wpls); - break; - } - - return 0; -} - - -/*--------------------------------------------------------------------------* - * Low-level auto-generated static routines * - *--------------------------------------------------------------------------*/ -/* - * N.B. In all the low-level routines, the part of the image - * that is accessed has been clipped by 32 pixels on - * all four sides. This is done in the higher level - * code by redefining w and h smaller and by moving the - * start-of-image pointers up to the beginning of this - * interior rectangle. - */ -static void -fdilate_1_0(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr); - } - } -} - -static void -ferode_1_0(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr); - } - } -} - -static void -fdilate_1_1(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)); - } - } -} - -static void -ferode_1_1(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)); - } - } -} - -static void -fdilate_1_2(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)); - } - } -} - -static void -ferode_1_2(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)); - } - } -} - -static void -fdilate_1_3(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)); - } - } -} - -static void -ferode_1_3(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)); - } - } -} - -static void -fdilate_1_4(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)); - } - } -} - -static void -ferode_1_4(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)); - } - } -} - -static void -fdilate_1_5(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)); - } - } -} - -static void -ferode_1_5(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)); - } - } -} - -static void -fdilate_1_6(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)); - } - } -} - -static void -ferode_1_6(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)); - } - } -} - -static void -fdilate_1_7(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)); - } - } -} - -static void -ferode_1_7(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)); - } - } -} - -static void -fdilate_1_8(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)); - } - } -} - -static void -ferode_1_8(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)); - } - } -} - -static void -fdilate_1_9(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | - ((*(sptr) >> 5) | (*(sptr - 1) << 27)); - } - } -} - -static void -ferode_1_9(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & - ((*(sptr) << 5) | (*(sptr + 1) >> 27)); - } - } -} - -static void -fdilate_1_10(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | - ((*(sptr) >> 5) | (*(sptr - 1) << 27)); - } - } -} - -static void -ferode_1_10(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & - ((*(sptr) << 5) | (*(sptr + 1) >> 27)); - } - } -} - -static void -fdilate_1_11(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | - ((*(sptr) >> 6) | (*(sptr - 1) << 26)); - } - } -} - -static void -ferode_1_11(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & - ((*(sptr) << 6) | (*(sptr + 1) >> 26)); - } - } -} - -static void -fdilate_1_12(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | - ((*(sptr) >> 6) | (*(sptr - 1) << 26)); - } - } -} - -static void -ferode_1_12(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & - ((*(sptr) << 6) | (*(sptr + 1) >> 26)); - } - } -} - -static void -fdilate_1_13(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | - ((*(sptr) >> 7) | (*(sptr - 1) << 25)); - } - } -} - -static void -ferode_1_13(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & - ((*(sptr) << 7) | (*(sptr + 1) >> 25)); - } - } -} - -static void -fdilate_1_14(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | - ((*(sptr) >> 9) | (*(sptr - 1) << 23)); - } - } -} - -static void -ferode_1_14(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & - ((*(sptr) << 9) | (*(sptr + 1) >> 23)); - } - } -} - -static void -fdilate_1_15(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | - ((*(sptr) >> 10) | (*(sptr - 1) << 22)); - } - } -} - -static void -ferode_1_15(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & - ((*(sptr) << 10) | (*(sptr + 1) >> 22)); - } - } -} - -static void -fdilate_1_16(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | - ((*(sptr) >> 12) | (*(sptr - 1) << 20)); - } - } -} - -static void -ferode_1_16(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & - ((*(sptr) << 12) | (*(sptr + 1) >> 20)); - } - } -} - -static void -fdilate_1_17(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | - ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | - ((*(sptr) << 13) | (*(sptr + 1) >> 19)) | - ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | - ((*(sptr) >> 12) | (*(sptr - 1) << 20)) | - ((*(sptr) >> 13) | (*(sptr - 1) << 19)) | - ((*(sptr) >> 14) | (*(sptr - 1) << 18)); - } - } -} - -static void -ferode_1_17(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & - ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & - ((*(sptr) >> 13) | (*(sptr - 1) << 19)) & - ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & - ((*(sptr) << 12) | (*(sptr + 1) >> 20)) & - ((*(sptr) << 13) | (*(sptr + 1) >> 19)) & - ((*(sptr) << 14) | (*(sptr + 1) >> 18)); - } - } -} - -static void -fdilate_1_18(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | - ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | - ((*(sptr) << 13) | (*(sptr + 1) >> 19)) | - ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | - ((*(sptr) >> 12) | (*(sptr - 1) << 20)) | - ((*(sptr) >> 13) | (*(sptr - 1) << 19)) | - ((*(sptr) >> 14) | (*(sptr - 1) << 18)) | - ((*(sptr) >> 15) | (*(sptr - 1) << 17)); - } - } -} - -static void -ferode_1_18(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & - ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & - ((*(sptr) >> 13) | (*(sptr - 1) << 19)) & - ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & - ((*(sptr) << 12) | (*(sptr + 1) >> 20)) & - ((*(sptr) << 13) | (*(sptr + 1) >> 19)) & - ((*(sptr) << 14) | (*(sptr + 1) >> 18)) & - ((*(sptr) << 15) | (*(sptr + 1) >> 17)); - } - } -} - -static void -fdilate_1_19(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 17) | (*(sptr + 1) >> 15)) | - ((*(sptr) << 16) | (*(sptr + 1) >> 16)) | - ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | - ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | - ((*(sptr) << 13) | (*(sptr + 1) >> 19)) | - ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | - ((*(sptr) >> 12) | (*(sptr - 1) << 20)) | - ((*(sptr) >> 13) | (*(sptr - 1) << 19)) | - ((*(sptr) >> 14) | (*(sptr - 1) << 18)) | - ((*(sptr) >> 15) | (*(sptr - 1) << 17)) | - ((*(sptr) >> 16) | (*(sptr - 1) << 16)) | - ((*(sptr) >> 17) | (*(sptr - 1) << 15)); - } - } -} - -static void -ferode_1_19(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 17) | (*(sptr - 1) << 15)) & - ((*(sptr) >> 16) | (*(sptr - 1) << 16)) & - ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & - ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & - ((*(sptr) >> 13) | (*(sptr - 1) << 19)) & - ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & - ((*(sptr) << 12) | (*(sptr + 1) >> 20)) & - ((*(sptr) << 13) | (*(sptr + 1) >> 19)) & - ((*(sptr) << 14) | (*(sptr + 1) >> 18)) & - ((*(sptr) << 15) | (*(sptr + 1) >> 17)) & - ((*(sptr) << 16) | (*(sptr + 1) >> 16)) & - ((*(sptr) << 17) | (*(sptr + 1) >> 15)); - } - } -} - -static void -fdilate_1_20(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 20) | (*(sptr + 1) >> 12)) | - ((*(sptr) << 19) | (*(sptr + 1) >> 13)) | - ((*(sptr) << 18) | (*(sptr + 1) >> 14)) | - ((*(sptr) << 17) | (*(sptr + 1) >> 15)) | - ((*(sptr) << 16) | (*(sptr + 1) >> 16)) | - ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | - ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | - ((*(sptr) << 13) | (*(sptr + 1) >> 19)) | - ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | - ((*(sptr) >> 12) | (*(sptr - 1) << 20)) | - ((*(sptr) >> 13) | (*(sptr - 1) << 19)) | - ((*(sptr) >> 14) | (*(sptr - 1) << 18)) | - ((*(sptr) >> 15) | (*(sptr - 1) << 17)) | - ((*(sptr) >> 16) | (*(sptr - 1) << 16)) | - ((*(sptr) >> 17) | (*(sptr - 1) << 15)) | - ((*(sptr) >> 18) | (*(sptr - 1) << 14)) | - ((*(sptr) >> 19) | (*(sptr - 1) << 13)); - } - } -} - -static void -ferode_1_20(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 20) | (*(sptr - 1) << 12)) & - ((*(sptr) >> 19) | (*(sptr - 1) << 13)) & - ((*(sptr) >> 18) | (*(sptr - 1) << 14)) & - ((*(sptr) >> 17) | (*(sptr - 1) << 15)) & - ((*(sptr) >> 16) | (*(sptr - 1) << 16)) & - ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & - ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & - ((*(sptr) >> 13) | (*(sptr - 1) << 19)) & - ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & - ((*(sptr) << 12) | (*(sptr + 1) >> 20)) & - ((*(sptr) << 13) | (*(sptr + 1) >> 19)) & - ((*(sptr) << 14) | (*(sptr + 1) >> 18)) & - ((*(sptr) << 15) | (*(sptr + 1) >> 17)) & - ((*(sptr) << 16) | (*(sptr + 1) >> 16)) & - ((*(sptr) << 17) | (*(sptr + 1) >> 15)) & - ((*(sptr) << 18) | (*(sptr + 1) >> 14)) & - ((*(sptr) << 19) | (*(sptr + 1) >> 13)); - } - } -} - -static void -fdilate_1_21(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 20) | (*(sptr + 1) >> 12)) | - ((*(sptr) << 19) | (*(sptr + 1) >> 13)) | - ((*(sptr) << 18) | (*(sptr + 1) >> 14)) | - ((*(sptr) << 17) | (*(sptr + 1) >> 15)) | - ((*(sptr) << 16) | (*(sptr + 1) >> 16)) | - ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | - ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | - ((*(sptr) << 13) | (*(sptr + 1) >> 19)) | - ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | - ((*(sptr) >> 12) | (*(sptr - 1) << 20)) | - ((*(sptr) >> 13) | (*(sptr - 1) << 19)) | - ((*(sptr) >> 14) | (*(sptr - 1) << 18)) | - ((*(sptr) >> 15) | (*(sptr - 1) << 17)) | - ((*(sptr) >> 16) | (*(sptr - 1) << 16)) | - ((*(sptr) >> 17) | (*(sptr - 1) << 15)) | - ((*(sptr) >> 18) | (*(sptr - 1) << 14)) | - ((*(sptr) >> 19) | (*(sptr - 1) << 13)) | - ((*(sptr) >> 20) | (*(sptr - 1) << 12)); - } - } -} - -static void -ferode_1_21(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 20) | (*(sptr - 1) << 12)) & - ((*(sptr) >> 19) | (*(sptr - 1) << 13)) & - ((*(sptr) >> 18) | (*(sptr - 1) << 14)) & - ((*(sptr) >> 17) | (*(sptr - 1) << 15)) & - ((*(sptr) >> 16) | (*(sptr - 1) << 16)) & - ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & - ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & - ((*(sptr) >> 13) | (*(sptr - 1) << 19)) & - ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & - ((*(sptr) << 12) | (*(sptr + 1) >> 20)) & - ((*(sptr) << 13) | (*(sptr + 1) >> 19)) & - ((*(sptr) << 14) | (*(sptr + 1) >> 18)) & - ((*(sptr) << 15) | (*(sptr + 1) >> 17)) & - ((*(sptr) << 16) | (*(sptr + 1) >> 16)) & - ((*(sptr) << 17) | (*(sptr + 1) >> 15)) & - ((*(sptr) << 18) | (*(sptr + 1) >> 14)) & - ((*(sptr) << 19) | (*(sptr + 1) >> 13)) & - ((*(sptr) << 20) | (*(sptr + 1) >> 12)); - } - } -} - -static void -fdilate_1_22(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 22) | (*(sptr + 1) >> 10)) | - ((*(sptr) << 21) | (*(sptr + 1) >> 11)) | - ((*(sptr) << 20) | (*(sptr + 1) >> 12)) | - ((*(sptr) << 19) | (*(sptr + 1) >> 13)) | - ((*(sptr) << 18) | (*(sptr + 1) >> 14)) | - ((*(sptr) << 17) | (*(sptr + 1) >> 15)) | - ((*(sptr) << 16) | (*(sptr + 1) >> 16)) | - ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | - ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | - ((*(sptr) << 13) | (*(sptr + 1) >> 19)) | - ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | - ((*(sptr) >> 12) | (*(sptr - 1) << 20)) | - ((*(sptr) >> 13) | (*(sptr - 1) << 19)) | - ((*(sptr) >> 14) | (*(sptr - 1) << 18)) | - ((*(sptr) >> 15) | (*(sptr - 1) << 17)) | - ((*(sptr) >> 16) | (*(sptr - 1) << 16)) | - ((*(sptr) >> 17) | (*(sptr - 1) << 15)) | - ((*(sptr) >> 18) | (*(sptr - 1) << 14)) | - ((*(sptr) >> 19) | (*(sptr - 1) << 13)) | - ((*(sptr) >> 20) | (*(sptr - 1) << 12)) | - ((*(sptr) >> 21) | (*(sptr - 1) << 11)) | - ((*(sptr) >> 22) | (*(sptr - 1) << 10)); - } - } -} - -static void -ferode_1_22(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 22) | (*(sptr - 1) << 10)) & - ((*(sptr) >> 21) | (*(sptr - 1) << 11)) & - ((*(sptr) >> 20) | (*(sptr - 1) << 12)) & - ((*(sptr) >> 19) | (*(sptr - 1) << 13)) & - ((*(sptr) >> 18) | (*(sptr - 1) << 14)) & - ((*(sptr) >> 17) | (*(sptr - 1) << 15)) & - ((*(sptr) >> 16) | (*(sptr - 1) << 16)) & - ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & - ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & - ((*(sptr) >> 13) | (*(sptr - 1) << 19)) & - ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & - ((*(sptr) << 12) | (*(sptr + 1) >> 20)) & - ((*(sptr) << 13) | (*(sptr + 1) >> 19)) & - ((*(sptr) << 14) | (*(sptr + 1) >> 18)) & - ((*(sptr) << 15) | (*(sptr + 1) >> 17)) & - ((*(sptr) << 16) | (*(sptr + 1) >> 16)) & - ((*(sptr) << 17) | (*(sptr + 1) >> 15)) & - ((*(sptr) << 18) | (*(sptr + 1) >> 14)) & - ((*(sptr) << 19) | (*(sptr + 1) >> 13)) & - ((*(sptr) << 20) | (*(sptr + 1) >> 12)) & - ((*(sptr) << 21) | (*(sptr + 1) >> 11)) & - ((*(sptr) << 22) | (*(sptr + 1) >> 10)); - } - } -} - -static void -fdilate_1_23(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 25) | (*(sptr + 1) >> 7)) | - ((*(sptr) << 24) | (*(sptr + 1) >> 8)) | - ((*(sptr) << 23) | (*(sptr + 1) >> 9)) | - ((*(sptr) << 22) | (*(sptr + 1) >> 10)) | - ((*(sptr) << 21) | (*(sptr + 1) >> 11)) | - ((*(sptr) << 20) | (*(sptr + 1) >> 12)) | - ((*(sptr) << 19) | (*(sptr + 1) >> 13)) | - ((*(sptr) << 18) | (*(sptr + 1) >> 14)) | - ((*(sptr) << 17) | (*(sptr + 1) >> 15)) | - ((*(sptr) << 16) | (*(sptr + 1) >> 16)) | - ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | - ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | - ((*(sptr) << 13) | (*(sptr + 1) >> 19)) | - ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | - ((*(sptr) >> 12) | (*(sptr - 1) << 20)) | - ((*(sptr) >> 13) | (*(sptr - 1) << 19)) | - ((*(sptr) >> 14) | (*(sptr - 1) << 18)) | - ((*(sptr) >> 15) | (*(sptr - 1) << 17)) | - ((*(sptr) >> 16) | (*(sptr - 1) << 16)) | - ((*(sptr) >> 17) | (*(sptr - 1) << 15)) | - ((*(sptr) >> 18) | (*(sptr - 1) << 14)) | - ((*(sptr) >> 19) | (*(sptr - 1) << 13)) | - ((*(sptr) >> 20) | (*(sptr - 1) << 12)) | - ((*(sptr) >> 21) | (*(sptr - 1) << 11)) | - ((*(sptr) >> 22) | (*(sptr - 1) << 10)) | - ((*(sptr) >> 23) | (*(sptr - 1) << 9)) | - ((*(sptr) >> 24) | (*(sptr - 1) << 8)); - } - } -} - -static void -ferode_1_23(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 25) | (*(sptr - 1) << 7)) & - ((*(sptr) >> 24) | (*(sptr - 1) << 8)) & - ((*(sptr) >> 23) | (*(sptr - 1) << 9)) & - ((*(sptr) >> 22) | (*(sptr - 1) << 10)) & - ((*(sptr) >> 21) | (*(sptr - 1) << 11)) & - ((*(sptr) >> 20) | (*(sptr - 1) << 12)) & - ((*(sptr) >> 19) | (*(sptr - 1) << 13)) & - ((*(sptr) >> 18) | (*(sptr - 1) << 14)) & - ((*(sptr) >> 17) | (*(sptr - 1) << 15)) & - ((*(sptr) >> 16) | (*(sptr - 1) << 16)) & - ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & - ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & - ((*(sptr) >> 13) | (*(sptr - 1) << 19)) & - ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & - ((*(sptr) << 12) | (*(sptr + 1) >> 20)) & - ((*(sptr) << 13) | (*(sptr + 1) >> 19)) & - ((*(sptr) << 14) | (*(sptr + 1) >> 18)) & - ((*(sptr) << 15) | (*(sptr + 1) >> 17)) & - ((*(sptr) << 16) | (*(sptr + 1) >> 16)) & - ((*(sptr) << 17) | (*(sptr + 1) >> 15)) & - ((*(sptr) << 18) | (*(sptr + 1) >> 14)) & - ((*(sptr) << 19) | (*(sptr + 1) >> 13)) & - ((*(sptr) << 20) | (*(sptr + 1) >> 12)) & - ((*(sptr) << 21) | (*(sptr + 1) >> 11)) & - ((*(sptr) << 22) | (*(sptr + 1) >> 10)) & - ((*(sptr) << 23) | (*(sptr + 1) >> 9)) & - ((*(sptr) << 24) | (*(sptr + 1) >> 8)); - } - } -} - -static void -fdilate_1_24(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 25) | (*(sptr + 1) >> 7)) | - ((*(sptr) << 24) | (*(sptr + 1) >> 8)) | - ((*(sptr) << 23) | (*(sptr + 1) >> 9)) | - ((*(sptr) << 22) | (*(sptr + 1) >> 10)) | - ((*(sptr) << 21) | (*(sptr + 1) >> 11)) | - ((*(sptr) << 20) | (*(sptr + 1) >> 12)) | - ((*(sptr) << 19) | (*(sptr + 1) >> 13)) | - ((*(sptr) << 18) | (*(sptr + 1) >> 14)) | - ((*(sptr) << 17) | (*(sptr + 1) >> 15)) | - ((*(sptr) << 16) | (*(sptr + 1) >> 16)) | - ((*(sptr) << 15) | (*(sptr + 1) >> 17)) | - ((*(sptr) << 14) | (*(sptr + 1) >> 18)) | - ((*(sptr) << 13) | (*(sptr + 1) >> 19)) | - ((*(sptr) << 12) | (*(sptr + 1) >> 20)) | - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) | - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) | - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) | - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) | - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) | - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) | - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) | - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) | - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) | - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) | - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) | - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) | - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) | - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) | - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) | - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) | - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) | - ((*(sptr) >> 12) | (*(sptr - 1) << 20)) | - ((*(sptr) >> 13) | (*(sptr - 1) << 19)) | - ((*(sptr) >> 14) | (*(sptr - 1) << 18)) | - ((*(sptr) >> 15) | (*(sptr - 1) << 17)) | - ((*(sptr) >> 16) | (*(sptr - 1) << 16)) | - ((*(sptr) >> 17) | (*(sptr - 1) << 15)) | - ((*(sptr) >> 18) | (*(sptr - 1) << 14)) | - ((*(sptr) >> 19) | (*(sptr - 1) << 13)) | - ((*(sptr) >> 20) | (*(sptr - 1) << 12)) | - ((*(sptr) >> 21) | (*(sptr - 1) << 11)) | - ((*(sptr) >> 22) | (*(sptr - 1) << 10)) | - ((*(sptr) >> 23) | (*(sptr - 1) << 9)) | - ((*(sptr) >> 24) | (*(sptr - 1) << 8)) | - ((*(sptr) >> 25) | (*(sptr - 1) << 7)); - } - } -} - -static void -ferode_1_24(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 25) | (*(sptr - 1) << 7)) & - ((*(sptr) >> 24) | (*(sptr - 1) << 8)) & - ((*(sptr) >> 23) | (*(sptr - 1) << 9)) & - ((*(sptr) >> 22) | (*(sptr - 1) << 10)) & - ((*(sptr) >> 21) | (*(sptr - 1) << 11)) & - ((*(sptr) >> 20) | (*(sptr - 1) << 12)) & - ((*(sptr) >> 19) | (*(sptr - 1) << 13)) & - ((*(sptr) >> 18) | (*(sptr - 1) << 14)) & - ((*(sptr) >> 17) | (*(sptr - 1) << 15)) & - ((*(sptr) >> 16) | (*(sptr - 1) << 16)) & - ((*(sptr) >> 15) | (*(sptr - 1) << 17)) & - ((*(sptr) >> 14) | (*(sptr - 1) << 18)) & - ((*(sptr) >> 13) | (*(sptr - 1) << 19)) & - ((*(sptr) >> 12) | (*(sptr - 1) << 20)) & - ((*(sptr) >> 11) | (*(sptr - 1) << 21)) & - ((*(sptr) >> 10) | (*(sptr - 1) << 22)) & - ((*(sptr) >> 9) | (*(sptr - 1) << 23)) & - ((*(sptr) >> 8) | (*(sptr - 1) << 24)) & - ((*(sptr) >> 7) | (*(sptr - 1) << 25)) & - ((*(sptr) >> 6) | (*(sptr - 1) << 26)) & - ((*(sptr) >> 5) | (*(sptr - 1) << 27)) & - ((*(sptr) >> 4) | (*(sptr - 1) << 28)) & - ((*(sptr) >> 3) | (*(sptr - 1) << 29)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr) << 3) | (*(sptr + 1) >> 29)) & - ((*(sptr) << 4) | (*(sptr + 1) >> 28)) & - ((*(sptr) << 5) | (*(sptr + 1) >> 27)) & - ((*(sptr) << 6) | (*(sptr + 1) >> 26)) & - ((*(sptr) << 7) | (*(sptr + 1) >> 25)) & - ((*(sptr) << 8) | (*(sptr + 1) >> 24)) & - ((*(sptr) << 9) | (*(sptr + 1) >> 23)) & - ((*(sptr) << 10) | (*(sptr + 1) >> 22)) & - ((*(sptr) << 11) | (*(sptr + 1) >> 21)) & - ((*(sptr) << 12) | (*(sptr + 1) >> 20)) & - ((*(sptr) << 13) | (*(sptr + 1) >> 19)) & - ((*(sptr) << 14) | (*(sptr + 1) >> 18)) & - ((*(sptr) << 15) | (*(sptr + 1) >> 17)) & - ((*(sptr) << 16) | (*(sptr + 1) >> 16)) & - ((*(sptr) << 17) | (*(sptr + 1) >> 15)) & - ((*(sptr) << 18) | (*(sptr + 1) >> 14)) & - ((*(sptr) << 19) | (*(sptr + 1) >> 13)) & - ((*(sptr) << 20) | (*(sptr + 1) >> 12)) & - ((*(sptr) << 21) | (*(sptr + 1) >> 11)) & - ((*(sptr) << 22) | (*(sptr + 1) >> 10)) & - ((*(sptr) << 23) | (*(sptr + 1) >> 9)) & - ((*(sptr) << 24) | (*(sptr + 1) >> 8)) & - ((*(sptr) << 25) | (*(sptr + 1) >> 7)); - } - } -} - -static void -fdilate_1_25(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls)) | - (*sptr); - } - } -} - -static void -ferode_1_25(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls)) & - (*sptr); - } - } -} - -static void -fdilate_1_26(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)); - } - } -} - -static void -ferode_1_26(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)); - } - } -} - -static void -fdilate_1_27(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2; - - wpls2 = 2 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)); - } - } -} - -static void -ferode_1_27(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2; - - wpls2 = 2 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)); - } - } -} - -static void -fdilate_1_28(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2; - - wpls2 = 2 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)); - } - } -} - -static void -ferode_1_28(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2; - - wpls2 = 2 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)); - } - } -} - -static void -fdilate_1_29(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)); - } - } -} - -static void -ferode_1_29(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)); - } - } -} - -static void -fdilate_1_30(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)); - } - } -} - -static void -ferode_1_30(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)); - } - } -} - -static void -fdilate_1_31(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)); - } - } -} - -static void -ferode_1_31(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)); - } - } -} - -static void -fdilate_1_32(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)); - } - } -} - -static void -ferode_1_32(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)); - } - } -} - -static void -fdilate_1_33(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls5)) | - (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)); - } - } -} - -static void -ferode_1_33(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls5)) & - (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)); - } - } -} - -static void -fdilate_1_34(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls5)) | - (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)) | - (*(sptr - wpls5)); - } - } -} - -static void -ferode_1_34(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls5)) & - (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)) & - (*(sptr + wpls5)); - } - } -} - -static void -fdilate_1_35(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls6)) | - (*(sptr + wpls5)) | - (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)) | - (*(sptr - wpls5)); - } - } -} - -static void -ferode_1_35(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls6)) & - (*(sptr - wpls5)) & - (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)) & - (*(sptr + wpls5)); - } - } -} - -static void -fdilate_1_36(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls6)) | - (*(sptr + wpls5)) | - (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)) | - (*(sptr - wpls5)) | - (*(sptr - wpls6)); - } - } -} - -static void -ferode_1_36(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls6)) & - (*(sptr - wpls5)) & - (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)) & - (*(sptr + wpls5)) & - (*(sptr + wpls6)); - } - } -} - -static void -fdilate_1_37(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls7)) | - (*(sptr + wpls6)) | - (*(sptr + wpls5)) | - (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)) | - (*(sptr - wpls5)) | - (*(sptr - wpls6)); - } - } -} - -static void -ferode_1_37(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls7)) & - (*(sptr - wpls6)) & - (*(sptr - wpls5)) & - (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)) & - (*(sptr + wpls5)) & - (*(sptr + wpls6)); - } - } -} - -static void -fdilate_1_38(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls7)) | - (*(sptr + wpls6)) | - (*(sptr + wpls5)) | - (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)) | - (*(sptr - wpls5)) | - (*(sptr - wpls6)) | - (*(sptr - wpls7)); - } - } -} - -static void -ferode_1_38(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls7)) & - (*(sptr - wpls6)) & - (*(sptr - wpls5)) & - (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)) & - (*(sptr + wpls5)) & - (*(sptr + wpls6)) & - (*(sptr + wpls7)); - } - } -} - -static void -fdilate_1_39(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls10)) | - (*(sptr + wpls9)) | - (*(sptr + wpls8)) | - (*(sptr + wpls7)) | - (*(sptr + wpls6)) | - (*(sptr + wpls5)) | - (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)) | - (*(sptr - wpls5)) | - (*(sptr - wpls6)) | - (*(sptr - wpls7)) | - (*(sptr - wpls8)) | - (*(sptr - wpls9)); - } - } -} - -static void -ferode_1_39(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls10)) & - (*(sptr - wpls9)) & - (*(sptr - wpls8)) & - (*(sptr - wpls7)) & - (*(sptr - wpls6)) & - (*(sptr - wpls5)) & - (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)) & - (*(sptr + wpls5)) & - (*(sptr + wpls6)) & - (*(sptr + wpls7)) & - (*(sptr + wpls8)) & - (*(sptr + wpls9)); - } - } -} - -static void -fdilate_1_40(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls10)) | - (*(sptr + wpls9)) | - (*(sptr + wpls8)) | - (*(sptr + wpls7)) | - (*(sptr + wpls6)) | - (*(sptr + wpls5)) | - (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)) | - (*(sptr - wpls5)) | - (*(sptr - wpls6)) | - (*(sptr - wpls7)) | - (*(sptr - wpls8)) | - (*(sptr - wpls9)) | - (*(sptr - wpls10)); - } - } -} - -static void -ferode_1_40(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls10)) & - (*(sptr - wpls9)) & - (*(sptr - wpls8)) & - (*(sptr - wpls7)) & - (*(sptr - wpls6)) & - (*(sptr - wpls5)) & - (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)) & - (*(sptr + wpls5)) & - (*(sptr + wpls6)) & - (*(sptr + wpls7)) & - (*(sptr + wpls8)) & - (*(sptr + wpls9)) & - (*(sptr + wpls10)); - } - } -} - -static void -fdilate_1_41(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls12)) | - (*(sptr + wpls11)) | - (*(sptr + wpls10)) | - (*(sptr + wpls9)) | - (*(sptr + wpls8)) | - (*(sptr + wpls7)) | - (*(sptr + wpls6)) | - (*(sptr + wpls5)) | - (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)) | - (*(sptr - wpls5)) | - (*(sptr - wpls6)) | - (*(sptr - wpls7)) | - (*(sptr - wpls8)) | - (*(sptr - wpls9)) | - (*(sptr - wpls10)) | - (*(sptr - wpls11)) | - (*(sptr - wpls12)); - } - } -} - -static void -ferode_1_41(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls12)) & - (*(sptr - wpls11)) & - (*(sptr - wpls10)) & - (*(sptr - wpls9)) & - (*(sptr - wpls8)) & - (*(sptr - wpls7)) & - (*(sptr - wpls6)) & - (*(sptr - wpls5)) & - (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)) & - (*(sptr + wpls5)) & - (*(sptr + wpls6)) & - (*(sptr + wpls7)) & - (*(sptr + wpls8)) & - (*(sptr + wpls9)) & - (*(sptr + wpls10)) & - (*(sptr + wpls11)) & - (*(sptr + wpls12)); - } - } -} - -static void -fdilate_1_42(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; -l_int32 wpls13, wpls14, wpls15; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - wpls13 = 13 * wpls; - wpls14 = 14 * wpls; - wpls15 = 15 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls15)) | - (*(sptr + wpls14)) | - (*(sptr + wpls13)) | - (*(sptr + wpls12)) | - (*(sptr + wpls11)) | - (*(sptr + wpls10)) | - (*(sptr + wpls9)) | - (*(sptr + wpls8)) | - (*(sptr + wpls7)) | - (*(sptr + wpls6)) | - (*(sptr + wpls5)) | - (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)) | - (*(sptr - wpls5)) | - (*(sptr - wpls6)) | - (*(sptr - wpls7)) | - (*(sptr - wpls8)) | - (*(sptr - wpls9)) | - (*(sptr - wpls10)) | - (*(sptr - wpls11)) | - (*(sptr - wpls12)) | - (*(sptr - wpls13)) | - (*(sptr - wpls14)); - } - } -} - -static void -ferode_1_42(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; -l_int32 wpls13, wpls14, wpls15; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - wpls13 = 13 * wpls; - wpls14 = 14 * wpls; - wpls15 = 15 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls15)) & - (*(sptr - wpls14)) & - (*(sptr - wpls13)) & - (*(sptr - wpls12)) & - (*(sptr - wpls11)) & - (*(sptr - wpls10)) & - (*(sptr - wpls9)) & - (*(sptr - wpls8)) & - (*(sptr - wpls7)) & - (*(sptr - wpls6)) & - (*(sptr - wpls5)) & - (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)) & - (*(sptr + wpls5)) & - (*(sptr + wpls6)) & - (*(sptr + wpls7)) & - (*(sptr + wpls8)) & - (*(sptr + wpls9)) & - (*(sptr + wpls10)) & - (*(sptr + wpls11)) & - (*(sptr + wpls12)) & - (*(sptr + wpls13)) & - (*(sptr + wpls14)); - } - } -} - -static void -fdilate_1_43(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; -l_int32 wpls13, wpls14, wpls15; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - wpls13 = 13 * wpls; - wpls14 = 14 * wpls; - wpls15 = 15 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls15)) | - (*(sptr + wpls14)) | - (*(sptr + wpls13)) | - (*(sptr + wpls12)) | - (*(sptr + wpls11)) | - (*(sptr + wpls10)) | - (*(sptr + wpls9)) | - (*(sptr + wpls8)) | - (*(sptr + wpls7)) | - (*(sptr + wpls6)) | - (*(sptr + wpls5)) | - (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)) | - (*(sptr - wpls5)) | - (*(sptr - wpls6)) | - (*(sptr - wpls7)) | - (*(sptr - wpls8)) | - (*(sptr - wpls9)) | - (*(sptr - wpls10)) | - (*(sptr - wpls11)) | - (*(sptr - wpls12)) | - (*(sptr - wpls13)) | - (*(sptr - wpls14)) | - (*(sptr - wpls15)); - } - } -} - -static void -ferode_1_43(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; -l_int32 wpls13, wpls14, wpls15; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - wpls13 = 13 * wpls; - wpls14 = 14 * wpls; - wpls15 = 15 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls15)) & - (*(sptr - wpls14)) & - (*(sptr - wpls13)) & - (*(sptr - wpls12)) & - (*(sptr - wpls11)) & - (*(sptr - wpls10)) & - (*(sptr - wpls9)) & - (*(sptr - wpls8)) & - (*(sptr - wpls7)) & - (*(sptr - wpls6)) & - (*(sptr - wpls5)) & - (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)) & - (*(sptr + wpls5)) & - (*(sptr + wpls6)) & - (*(sptr + wpls7)) & - (*(sptr + wpls8)) & - (*(sptr + wpls9)) & - (*(sptr + wpls10)) & - (*(sptr + wpls11)) & - (*(sptr + wpls12)) & - (*(sptr + wpls13)) & - (*(sptr + wpls14)) & - (*(sptr + wpls15)); - } - } -} - -static void -fdilate_1_44(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; -l_int32 wpls13, wpls14, wpls15, wpls16; -l_int32 wpls17; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - wpls13 = 13 * wpls; - wpls14 = 14 * wpls; - wpls15 = 15 * wpls; - wpls16 = 16 * wpls; - wpls17 = 17 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls17)) | - (*(sptr + wpls16)) | - (*(sptr + wpls15)) | - (*(sptr + wpls14)) | - (*(sptr + wpls13)) | - (*(sptr + wpls12)) | - (*(sptr + wpls11)) | - (*(sptr + wpls10)) | - (*(sptr + wpls9)) | - (*(sptr + wpls8)) | - (*(sptr + wpls7)) | - (*(sptr + wpls6)) | - (*(sptr + wpls5)) | - (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)) | - (*(sptr - wpls5)) | - (*(sptr - wpls6)) | - (*(sptr - wpls7)) | - (*(sptr - wpls8)) | - (*(sptr - wpls9)) | - (*(sptr - wpls10)) | - (*(sptr - wpls11)) | - (*(sptr - wpls12)) | - (*(sptr - wpls13)) | - (*(sptr - wpls14)) | - (*(sptr - wpls15)) | - (*(sptr - wpls16)) | - (*(sptr - wpls17)); - } - } -} - -static void -ferode_1_44(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; -l_int32 wpls13, wpls14, wpls15, wpls16; -l_int32 wpls17; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - wpls13 = 13 * wpls; - wpls14 = 14 * wpls; - wpls15 = 15 * wpls; - wpls16 = 16 * wpls; - wpls17 = 17 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls17)) & - (*(sptr - wpls16)) & - (*(sptr - wpls15)) & - (*(sptr - wpls14)) & - (*(sptr - wpls13)) & - (*(sptr - wpls12)) & - (*(sptr - wpls11)) & - (*(sptr - wpls10)) & - (*(sptr - wpls9)) & - (*(sptr - wpls8)) & - (*(sptr - wpls7)) & - (*(sptr - wpls6)) & - (*(sptr - wpls5)) & - (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)) & - (*(sptr + wpls5)) & - (*(sptr + wpls6)) & - (*(sptr + wpls7)) & - (*(sptr + wpls8)) & - (*(sptr + wpls9)) & - (*(sptr + wpls10)) & - (*(sptr + wpls11)) & - (*(sptr + wpls12)) & - (*(sptr + wpls13)) & - (*(sptr + wpls14)) & - (*(sptr + wpls15)) & - (*(sptr + wpls16)) & - (*(sptr + wpls17)); - } - } -} - -static void -fdilate_1_45(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; -l_int32 wpls13, wpls14, wpls15, wpls16; -l_int32 wpls17, wpls18, wpls19, wpls20; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - wpls13 = 13 * wpls; - wpls14 = 14 * wpls; - wpls15 = 15 * wpls; - wpls16 = 16 * wpls; - wpls17 = 17 * wpls; - wpls18 = 18 * wpls; - wpls19 = 19 * wpls; - wpls20 = 20 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls20)) | - (*(sptr + wpls19)) | - (*(sptr + wpls18)) | - (*(sptr + wpls17)) | - (*(sptr + wpls16)) | - (*(sptr + wpls15)) | - (*(sptr + wpls14)) | - (*(sptr + wpls13)) | - (*(sptr + wpls12)) | - (*(sptr + wpls11)) | - (*(sptr + wpls10)) | - (*(sptr + wpls9)) | - (*(sptr + wpls8)) | - (*(sptr + wpls7)) | - (*(sptr + wpls6)) | - (*(sptr + wpls5)) | - (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)) | - (*(sptr - wpls5)) | - (*(sptr - wpls6)) | - (*(sptr - wpls7)) | - (*(sptr - wpls8)) | - (*(sptr - wpls9)) | - (*(sptr - wpls10)) | - (*(sptr - wpls11)) | - (*(sptr - wpls12)) | - (*(sptr - wpls13)) | - (*(sptr - wpls14)) | - (*(sptr - wpls15)) | - (*(sptr - wpls16)) | - (*(sptr - wpls17)) | - (*(sptr - wpls18)) | - (*(sptr - wpls19)); - } - } -} - -static void -ferode_1_45(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; -l_int32 wpls13, wpls14, wpls15, wpls16; -l_int32 wpls17, wpls18, wpls19, wpls20; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - wpls13 = 13 * wpls; - wpls14 = 14 * wpls; - wpls15 = 15 * wpls; - wpls16 = 16 * wpls; - wpls17 = 17 * wpls; - wpls18 = 18 * wpls; - wpls19 = 19 * wpls; - wpls20 = 20 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls20)) & - (*(sptr - wpls19)) & - (*(sptr - wpls18)) & - (*(sptr - wpls17)) & - (*(sptr - wpls16)) & - (*(sptr - wpls15)) & - (*(sptr - wpls14)) & - (*(sptr - wpls13)) & - (*(sptr - wpls12)) & - (*(sptr - wpls11)) & - (*(sptr - wpls10)) & - (*(sptr - wpls9)) & - (*(sptr - wpls8)) & - (*(sptr - wpls7)) & - (*(sptr - wpls6)) & - (*(sptr - wpls5)) & - (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)) & - (*(sptr + wpls5)) & - (*(sptr + wpls6)) & - (*(sptr + wpls7)) & - (*(sptr + wpls8)) & - (*(sptr + wpls9)) & - (*(sptr + wpls10)) & - (*(sptr + wpls11)) & - (*(sptr + wpls12)) & - (*(sptr + wpls13)) & - (*(sptr + wpls14)) & - (*(sptr + wpls15)) & - (*(sptr + wpls16)) & - (*(sptr + wpls17)) & - (*(sptr + wpls18)) & - (*(sptr + wpls19)); - } - } -} - -static void -fdilate_1_46(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; -l_int32 wpls13, wpls14, wpls15, wpls16; -l_int32 wpls17, wpls18, wpls19, wpls20; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - wpls13 = 13 * wpls; - wpls14 = 14 * wpls; - wpls15 = 15 * wpls; - wpls16 = 16 * wpls; - wpls17 = 17 * wpls; - wpls18 = 18 * wpls; - wpls19 = 19 * wpls; - wpls20 = 20 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls20)) | - (*(sptr + wpls19)) | - (*(sptr + wpls18)) | - (*(sptr + wpls17)) | - (*(sptr + wpls16)) | - (*(sptr + wpls15)) | - (*(sptr + wpls14)) | - (*(sptr + wpls13)) | - (*(sptr + wpls12)) | - (*(sptr + wpls11)) | - (*(sptr + wpls10)) | - (*(sptr + wpls9)) | - (*(sptr + wpls8)) | - (*(sptr + wpls7)) | - (*(sptr + wpls6)) | - (*(sptr + wpls5)) | - (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)) | - (*(sptr - wpls5)) | - (*(sptr - wpls6)) | - (*(sptr - wpls7)) | - (*(sptr - wpls8)) | - (*(sptr - wpls9)) | - (*(sptr - wpls10)) | - (*(sptr - wpls11)) | - (*(sptr - wpls12)) | - (*(sptr - wpls13)) | - (*(sptr - wpls14)) | - (*(sptr - wpls15)) | - (*(sptr - wpls16)) | - (*(sptr - wpls17)) | - (*(sptr - wpls18)) | - (*(sptr - wpls19)) | - (*(sptr - wpls20)); - } - } -} - -static void -ferode_1_46(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; -l_int32 wpls13, wpls14, wpls15, wpls16; -l_int32 wpls17, wpls18, wpls19, wpls20; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - wpls13 = 13 * wpls; - wpls14 = 14 * wpls; - wpls15 = 15 * wpls; - wpls16 = 16 * wpls; - wpls17 = 17 * wpls; - wpls18 = 18 * wpls; - wpls19 = 19 * wpls; - wpls20 = 20 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls20)) & - (*(sptr - wpls19)) & - (*(sptr - wpls18)) & - (*(sptr - wpls17)) & - (*(sptr - wpls16)) & - (*(sptr - wpls15)) & - (*(sptr - wpls14)) & - (*(sptr - wpls13)) & - (*(sptr - wpls12)) & - (*(sptr - wpls11)) & - (*(sptr - wpls10)) & - (*(sptr - wpls9)) & - (*(sptr - wpls8)) & - (*(sptr - wpls7)) & - (*(sptr - wpls6)) & - (*(sptr - wpls5)) & - (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)) & - (*(sptr + wpls5)) & - (*(sptr + wpls6)) & - (*(sptr + wpls7)) & - (*(sptr + wpls8)) & - (*(sptr + wpls9)) & - (*(sptr + wpls10)) & - (*(sptr + wpls11)) & - (*(sptr + wpls12)) & - (*(sptr + wpls13)) & - (*(sptr + wpls14)) & - (*(sptr + wpls15)) & - (*(sptr + wpls16)) & - (*(sptr + wpls17)) & - (*(sptr + wpls18)) & - (*(sptr + wpls19)) & - (*(sptr + wpls20)); - } - } -} - -static void -fdilate_1_47(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; -l_int32 wpls13, wpls14, wpls15, wpls16; -l_int32 wpls17, wpls18, wpls19, wpls20; -l_int32 wpls21, wpls22; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - wpls13 = 13 * wpls; - wpls14 = 14 * wpls; - wpls15 = 15 * wpls; - wpls16 = 16 * wpls; - wpls17 = 17 * wpls; - wpls18 = 18 * wpls; - wpls19 = 19 * wpls; - wpls20 = 20 * wpls; - wpls21 = 21 * wpls; - wpls22 = 22 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls22)) | - (*(sptr + wpls21)) | - (*(sptr + wpls20)) | - (*(sptr + wpls19)) | - (*(sptr + wpls18)) | - (*(sptr + wpls17)) | - (*(sptr + wpls16)) | - (*(sptr + wpls15)) | - (*(sptr + wpls14)) | - (*(sptr + wpls13)) | - (*(sptr + wpls12)) | - (*(sptr + wpls11)) | - (*(sptr + wpls10)) | - (*(sptr + wpls9)) | - (*(sptr + wpls8)) | - (*(sptr + wpls7)) | - (*(sptr + wpls6)) | - (*(sptr + wpls5)) | - (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)) | - (*(sptr - wpls5)) | - (*(sptr - wpls6)) | - (*(sptr - wpls7)) | - (*(sptr - wpls8)) | - (*(sptr - wpls9)) | - (*(sptr - wpls10)) | - (*(sptr - wpls11)) | - (*(sptr - wpls12)) | - (*(sptr - wpls13)) | - (*(sptr - wpls14)) | - (*(sptr - wpls15)) | - (*(sptr - wpls16)) | - (*(sptr - wpls17)) | - (*(sptr - wpls18)) | - (*(sptr - wpls19)) | - (*(sptr - wpls20)) | - (*(sptr - wpls21)) | - (*(sptr - wpls22)); - } - } -} - -static void -ferode_1_47(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; -l_int32 wpls13, wpls14, wpls15, wpls16; -l_int32 wpls17, wpls18, wpls19, wpls20; -l_int32 wpls21, wpls22; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - wpls13 = 13 * wpls; - wpls14 = 14 * wpls; - wpls15 = 15 * wpls; - wpls16 = 16 * wpls; - wpls17 = 17 * wpls; - wpls18 = 18 * wpls; - wpls19 = 19 * wpls; - wpls20 = 20 * wpls; - wpls21 = 21 * wpls; - wpls22 = 22 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls22)) & - (*(sptr - wpls21)) & - (*(sptr - wpls20)) & - (*(sptr - wpls19)) & - (*(sptr - wpls18)) & - (*(sptr - wpls17)) & - (*(sptr - wpls16)) & - (*(sptr - wpls15)) & - (*(sptr - wpls14)) & - (*(sptr - wpls13)) & - (*(sptr - wpls12)) & - (*(sptr - wpls11)) & - (*(sptr - wpls10)) & - (*(sptr - wpls9)) & - (*(sptr - wpls8)) & - (*(sptr - wpls7)) & - (*(sptr - wpls6)) & - (*(sptr - wpls5)) & - (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)) & - (*(sptr + wpls5)) & - (*(sptr + wpls6)) & - (*(sptr + wpls7)) & - (*(sptr + wpls8)) & - (*(sptr + wpls9)) & - (*(sptr + wpls10)) & - (*(sptr + wpls11)) & - (*(sptr + wpls12)) & - (*(sptr + wpls13)) & - (*(sptr + wpls14)) & - (*(sptr + wpls15)) & - (*(sptr + wpls16)) & - (*(sptr + wpls17)) & - (*(sptr + wpls18)) & - (*(sptr + wpls19)) & - (*(sptr + wpls20)) & - (*(sptr + wpls21)) & - (*(sptr + wpls22)); - } - } -} - -static void -fdilate_1_48(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; -l_int32 wpls13, wpls14, wpls15, wpls16; -l_int32 wpls17, wpls18, wpls19, wpls20; -l_int32 wpls21, wpls22, wpls23, wpls24; -l_int32 wpls25; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - wpls13 = 13 * wpls; - wpls14 = 14 * wpls; - wpls15 = 15 * wpls; - wpls16 = 16 * wpls; - wpls17 = 17 * wpls; - wpls18 = 18 * wpls; - wpls19 = 19 * wpls; - wpls20 = 20 * wpls; - wpls21 = 21 * wpls; - wpls22 = 22 * wpls; - wpls23 = 23 * wpls; - wpls24 = 24 * wpls; - wpls25 = 25 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls25)) | - (*(sptr + wpls24)) | - (*(sptr + wpls23)) | - (*(sptr + wpls22)) | - (*(sptr + wpls21)) | - (*(sptr + wpls20)) | - (*(sptr + wpls19)) | - (*(sptr + wpls18)) | - (*(sptr + wpls17)) | - (*(sptr + wpls16)) | - (*(sptr + wpls15)) | - (*(sptr + wpls14)) | - (*(sptr + wpls13)) | - (*(sptr + wpls12)) | - (*(sptr + wpls11)) | - (*(sptr + wpls10)) | - (*(sptr + wpls9)) | - (*(sptr + wpls8)) | - (*(sptr + wpls7)) | - (*(sptr + wpls6)) | - (*(sptr + wpls5)) | - (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)) | - (*(sptr - wpls5)) | - (*(sptr - wpls6)) | - (*(sptr - wpls7)) | - (*(sptr - wpls8)) | - (*(sptr - wpls9)) | - (*(sptr - wpls10)) | - (*(sptr - wpls11)) | - (*(sptr - wpls12)) | - (*(sptr - wpls13)) | - (*(sptr - wpls14)) | - (*(sptr - wpls15)) | - (*(sptr - wpls16)) | - (*(sptr - wpls17)) | - (*(sptr - wpls18)) | - (*(sptr - wpls19)) | - (*(sptr - wpls20)) | - (*(sptr - wpls21)) | - (*(sptr - wpls22)) | - (*(sptr - wpls23)) | - (*(sptr - wpls24)); - } - } -} - -static void -ferode_1_48(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; -l_int32 wpls13, wpls14, wpls15, wpls16; -l_int32 wpls17, wpls18, wpls19, wpls20; -l_int32 wpls21, wpls22, wpls23, wpls24; -l_int32 wpls25; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - wpls13 = 13 * wpls; - wpls14 = 14 * wpls; - wpls15 = 15 * wpls; - wpls16 = 16 * wpls; - wpls17 = 17 * wpls; - wpls18 = 18 * wpls; - wpls19 = 19 * wpls; - wpls20 = 20 * wpls; - wpls21 = 21 * wpls; - wpls22 = 22 * wpls; - wpls23 = 23 * wpls; - wpls24 = 24 * wpls; - wpls25 = 25 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls25)) & - (*(sptr - wpls24)) & - (*(sptr - wpls23)) & - (*(sptr - wpls22)) & - (*(sptr - wpls21)) & - (*(sptr - wpls20)) & - (*(sptr - wpls19)) & - (*(sptr - wpls18)) & - (*(sptr - wpls17)) & - (*(sptr - wpls16)) & - (*(sptr - wpls15)) & - (*(sptr - wpls14)) & - (*(sptr - wpls13)) & - (*(sptr - wpls12)) & - (*(sptr - wpls11)) & - (*(sptr - wpls10)) & - (*(sptr - wpls9)) & - (*(sptr - wpls8)) & - (*(sptr - wpls7)) & - (*(sptr - wpls6)) & - (*(sptr - wpls5)) & - (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)) & - (*(sptr + wpls5)) & - (*(sptr + wpls6)) & - (*(sptr + wpls7)) & - (*(sptr + wpls8)) & - (*(sptr + wpls9)) & - (*(sptr + wpls10)) & - (*(sptr + wpls11)) & - (*(sptr + wpls12)) & - (*(sptr + wpls13)) & - (*(sptr + wpls14)) & - (*(sptr + wpls15)) & - (*(sptr + wpls16)) & - (*(sptr + wpls17)) & - (*(sptr + wpls18)) & - (*(sptr + wpls19)) & - (*(sptr + wpls20)) & - (*(sptr + wpls21)) & - (*(sptr + wpls22)) & - (*(sptr + wpls23)) & - (*(sptr + wpls24)); - } - } -} - -static void -fdilate_1_49(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; -l_int32 wpls13, wpls14, wpls15, wpls16; -l_int32 wpls17, wpls18, wpls19, wpls20; -l_int32 wpls21, wpls22, wpls23, wpls24; -l_int32 wpls25; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - wpls13 = 13 * wpls; - wpls14 = 14 * wpls; - wpls15 = 15 * wpls; - wpls16 = 16 * wpls; - wpls17 = 17 * wpls; - wpls18 = 18 * wpls; - wpls19 = 19 * wpls; - wpls20 = 20 * wpls; - wpls21 = 21 * wpls; - wpls22 = 22 * wpls; - wpls23 = 23 * wpls; - wpls24 = 24 * wpls; - wpls25 = 25 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr + wpls25)) | - (*(sptr + wpls24)) | - (*(sptr + wpls23)) | - (*(sptr + wpls22)) | - (*(sptr + wpls21)) | - (*(sptr + wpls20)) | - (*(sptr + wpls19)) | - (*(sptr + wpls18)) | - (*(sptr + wpls17)) | - (*(sptr + wpls16)) | - (*(sptr + wpls15)) | - (*(sptr + wpls14)) | - (*(sptr + wpls13)) | - (*(sptr + wpls12)) | - (*(sptr + wpls11)) | - (*(sptr + wpls10)) | - (*(sptr + wpls9)) | - (*(sptr + wpls8)) | - (*(sptr + wpls7)) | - (*(sptr + wpls6)) | - (*(sptr + wpls5)) | - (*(sptr + wpls4)) | - (*(sptr + wpls3)) | - (*(sptr + wpls2)) | - (*(sptr + wpls)) | - (*sptr) | - (*(sptr - wpls)) | - (*(sptr - wpls2)) | - (*(sptr - wpls3)) | - (*(sptr - wpls4)) | - (*(sptr - wpls5)) | - (*(sptr - wpls6)) | - (*(sptr - wpls7)) | - (*(sptr - wpls8)) | - (*(sptr - wpls9)) | - (*(sptr - wpls10)) | - (*(sptr - wpls11)) | - (*(sptr - wpls12)) | - (*(sptr - wpls13)) | - (*(sptr - wpls14)) | - (*(sptr - wpls15)) | - (*(sptr - wpls16)) | - (*(sptr - wpls17)) | - (*(sptr - wpls18)) | - (*(sptr - wpls19)) | - (*(sptr - wpls20)) | - (*(sptr - wpls21)) | - (*(sptr - wpls22)) | - (*(sptr - wpls23)) | - (*(sptr - wpls24)) | - (*(sptr - wpls25)); - } - } -} - -static void -ferode_1_49(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2, wpls3, wpls4; -l_int32 wpls5, wpls6, wpls7, wpls8; -l_int32 wpls9, wpls10, wpls11, wpls12; -l_int32 wpls13, wpls14, wpls15, wpls16; -l_int32 wpls17, wpls18, wpls19, wpls20; -l_int32 wpls21, wpls22, wpls23, wpls24; -l_int32 wpls25; - - wpls2 = 2 * wpls; - wpls3 = 3 * wpls; - wpls4 = 4 * wpls; - wpls5 = 5 * wpls; - wpls6 = 6 * wpls; - wpls7 = 7 * wpls; - wpls8 = 8 * wpls; - wpls9 = 9 * wpls; - wpls10 = 10 * wpls; - wpls11 = 11 * wpls; - wpls12 = 12 * wpls; - wpls13 = 13 * wpls; - wpls14 = 14 * wpls; - wpls15 = 15 * wpls; - wpls16 = 16 * wpls; - wpls17 = 17 * wpls; - wpls18 = 18 * wpls; - wpls19 = 19 * wpls; - wpls20 = 20 * wpls; - wpls21 = 21 * wpls; - wpls22 = 22 * wpls; - wpls23 = 23 * wpls; - wpls24 = 24 * wpls; - wpls25 = 25 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*(sptr - wpls25)) & - (*(sptr - wpls24)) & - (*(sptr - wpls23)) & - (*(sptr - wpls22)) & - (*(sptr - wpls21)) & - (*(sptr - wpls20)) & - (*(sptr - wpls19)) & - (*(sptr - wpls18)) & - (*(sptr - wpls17)) & - (*(sptr - wpls16)) & - (*(sptr - wpls15)) & - (*(sptr - wpls14)) & - (*(sptr - wpls13)) & - (*(sptr - wpls12)) & - (*(sptr - wpls11)) & - (*(sptr - wpls10)) & - (*(sptr - wpls9)) & - (*(sptr - wpls8)) & - (*(sptr - wpls7)) & - (*(sptr - wpls6)) & - (*(sptr - wpls5)) & - (*(sptr - wpls4)) & - (*(sptr - wpls3)) & - (*(sptr - wpls2)) & - (*(sptr - wpls)) & - (*sptr) & - (*(sptr + wpls)) & - (*(sptr + wpls2)) & - (*(sptr + wpls3)) & - (*(sptr + wpls4)) & - (*(sptr + wpls5)) & - (*(sptr + wpls6)) & - (*(sptr + wpls7)) & - (*(sptr + wpls8)) & - (*(sptr + wpls9)) & - (*(sptr + wpls10)) & - (*(sptr + wpls11)) & - (*(sptr + wpls12)) & - (*(sptr + wpls13)) & - (*(sptr + wpls14)) & - (*(sptr + wpls15)) & - (*(sptr + wpls16)) & - (*(sptr + wpls17)) & - (*(sptr + wpls18)) & - (*(sptr + wpls19)) & - (*(sptr + wpls20)) & - (*(sptr + wpls21)) & - (*(sptr + wpls22)) & - (*(sptr + wpls23)) & - (*(sptr + wpls24)) & - (*(sptr + wpls25)); - } - } -} - -static void -fdilate_1_50(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)) | - (*(sptr + wpls)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr); - } - } -} - -static void -ferode_1_50(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)) & - (*(sptr - wpls)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr); - } - } -} - -static void -fdilate_1_51(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)) | - (*(sptr + wpls)) | - ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) | - (*(sptr - wpls)) | - ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)); - } - } -} - -static void -ferode_1_51(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)) & - (*(sptr - wpls)) & - ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) & - (*(sptr + wpls)) & - ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)); - } - } -} - -static void -fdilate_1_52(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2; - - wpls2 = 2 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr + wpls2) << 2) | (*(sptr + wpls2 + 1) >> 30)) | - ((*(sptr + wpls2) << 1) | (*(sptr + wpls2 + 1) >> 31)) | - (*(sptr + wpls2)) | - ((*(sptr + wpls2) >> 1) | (*(sptr + wpls2 - 1) << 31)) | - ((*(sptr + wpls) << 2) | (*(sptr + wpls + 1) >> 30)) | - ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)) | - (*(sptr + wpls)) | - ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr - wpls) << 2) | (*(sptr - wpls + 1) >> 30)) | - ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) | - (*(sptr - wpls)) | - ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)); - } - } -} - -static void -ferode_1_52(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2; - - wpls2 = 2 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr - wpls2) >> 2) | (*(sptr - wpls2 - 1) << 30)) & - ((*(sptr - wpls2) >> 1) | (*(sptr - wpls2 - 1) << 31)) & - (*(sptr - wpls2)) & - ((*(sptr - wpls2) << 1) | (*(sptr - wpls2 + 1) >> 31)) & - ((*(sptr - wpls) >> 2) | (*(sptr - wpls - 1) << 30)) & - ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)) & - (*(sptr - wpls)) & - ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr + wpls) >> 2) | (*(sptr + wpls - 1) << 30)) & - ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) & - (*(sptr + wpls)) & - ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)); - } - } -} - -static void -fdilate_1_53(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2; - - wpls2 = 2 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr + wpls2) << 2) | (*(sptr + wpls2 + 1) >> 30)) | - ((*(sptr + wpls2) << 1) | (*(sptr + wpls2 + 1) >> 31)) | - (*(sptr + wpls2)) | - ((*(sptr + wpls2) >> 1) | (*(sptr + wpls2 - 1) << 31)) | - ((*(sptr + wpls2) >> 2) | (*(sptr + wpls2 - 1) << 30)) | - ((*(sptr + wpls) << 2) | (*(sptr + wpls + 1) >> 30)) | - ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)) | - (*(sptr + wpls)) | - ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) | - ((*(sptr + wpls) >> 2) | (*(sptr + wpls - 1) << 30)) | - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) | - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) | - (*sptr) | - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) | - ((*(sptr - wpls) << 2) | (*(sptr - wpls + 1) >> 30)) | - ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) | - (*(sptr - wpls)) | - ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)) | - ((*(sptr - wpls) >> 2) | (*(sptr - wpls - 1) << 30)) | - ((*(sptr - wpls2) << 2) | (*(sptr - wpls2 + 1) >> 30)) | - ((*(sptr - wpls2) << 1) | (*(sptr - wpls2 + 1) >> 31)) | - (*(sptr - wpls2)) | - ((*(sptr - wpls2) >> 1) | (*(sptr - wpls2 - 1) << 31)) | - ((*(sptr - wpls2) >> 2) | (*(sptr - wpls2 - 1) << 30)); - } - } -} - -static void -ferode_1_53(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2; - - wpls2 = 2 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr - wpls2) >> 2) | (*(sptr - wpls2 - 1) << 30)) & - ((*(sptr - wpls2) >> 1) | (*(sptr - wpls2 - 1) << 31)) & - (*(sptr - wpls2)) & - ((*(sptr - wpls2) << 1) | (*(sptr - wpls2 + 1) >> 31)) & - ((*(sptr - wpls2) << 2) | (*(sptr - wpls2 + 1) >> 30)) & - ((*(sptr - wpls) >> 2) | (*(sptr - wpls - 1) << 30)) & - ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)) & - (*(sptr - wpls)) & - ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) & - ((*(sptr - wpls) << 2) | (*(sptr - wpls + 1) >> 30)) & - ((*(sptr) >> 2) | (*(sptr - 1) << 30)) & - ((*(sptr) >> 1) | (*(sptr - 1) << 31)) & - (*sptr) & - ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - ((*(sptr) << 2) | (*(sptr + 1) >> 30)) & - ((*(sptr + wpls) >> 2) | (*(sptr + wpls - 1) << 30)) & - ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) & - (*(sptr + wpls)) & - ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)) & - ((*(sptr + wpls) << 2) | (*(sptr + wpls + 1) >> 30)) & - ((*(sptr + wpls2) >> 2) | (*(sptr + wpls2 - 1) << 30)) & - ((*(sptr + wpls2) >> 1) | (*(sptr + wpls2 - 1) << 31)) & - (*(sptr + wpls2)) & - ((*(sptr + wpls2) << 1) | (*(sptr + wpls2 + 1) >> 31)) & - ((*(sptr + wpls2) << 2) | (*(sptr + wpls2 + 1) >> 30)); - } - } -} - -static void -fdilate_1_54(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) >> 1) | (*(sptr - 1) << 31)) | - (*(sptr - wpls)); - } - } -} - -static void -ferode_1_54(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr) << 1) | (*(sptr + 1) >> 31)) & - (*(sptr + wpls)); - } - } -} - -static void -fdilate_1_55(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*sptr) | - ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)); - } - } -} - -static void -ferode_1_55(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; - - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = (*sptr) & - ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)); - } - } -} - -static void -fdilate_1_56(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2; - - wpls2 = 2 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr + wpls2) >> 2) | (*(sptr + wpls2 - 1) << 30)) | - ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) | - (*sptr) | - ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) | - ((*(sptr - wpls2) << 2) | (*(sptr - wpls2 + 1) >> 30)); - } - } -} - -static void -ferode_1_56(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2; - - wpls2 = 2 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr - wpls2) << 2) | (*(sptr - wpls2 + 1) >> 30)) & - ((*(sptr - wpls) << 1) | (*(sptr - wpls + 1) >> 31)) & - (*sptr) & - ((*(sptr + wpls) >> 1) | (*(sptr + wpls - 1) << 31)) & - ((*(sptr + wpls2) >> 2) | (*(sptr + wpls2 - 1) << 30)); - } - } -} - -static void -fdilate_1_57(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2; - - wpls2 = 2 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr + wpls2) << 2) | (*(sptr + wpls2 + 1) >> 30)) | - ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)) | - (*sptr) | - ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)) | - ((*(sptr - wpls2) >> 2) | (*(sptr - wpls2 - 1) << 30)); - } - } -} - -static void -ferode_1_57(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls) -{ -l_int32 i; -l_int32 j, pwpls; -l_uint32 *sptr, *dptr; -l_int32 wpls2; - - wpls2 = 2 * wpls; - pwpls = (l_uint32)(w + 31) / 32; /* proper wpl of src */ - - for (i = 0; i < h; i++) { - sptr = datas + i * wpls; - dptr = datad + i * wpld; - for (j = 0; j < pwpls; j++, sptr++, dptr++) { - *dptr = ((*(sptr - wpls2) >> 2) | (*(sptr - wpls2 - 1) << 30)) & - ((*(sptr - wpls) >> 1) | (*(sptr - wpls - 1) << 31)) & - (*sptr) & - ((*(sptr + wpls) << 1) | (*(sptr + wpls + 1) >> 31)) & - ((*(sptr + wpls2) << 2) | (*(sptr + wpls2 + 1) >> 30)); - } - } -} - diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fpix1.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fpix1.c deleted file mode 100644 index f9691d89..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fpix1.c +++ /dev/null @@ -1,2342 +0,0 @@ -/*====================================================================* - - 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 fpix1.c - *
- * - * --------------------------------------------------- - * This file has these FPix, FPixa and DPix utilities: - * - creation and destruction - * - accessors - * - serialization and deserialization - * --------------------------------------------------- - * - * FPix Create/copy/destroy - * FPIX *fpixCreate() - * FPIX *fpixCreateTemplate() - * FPIX *fpixClone() - * FPIX *fpixCopy() - * l_int32 fpixResizeImageData() - * void fpixDestroy() - * - * FPix accessors - * l_int32 fpixGetDimensions() - * l_int32 fpixSetDimensions() - * l_int32 fpixGetWpl() - * l_int32 fpixSetWpl() - * l_int32 fpixGetRefcount() - * l_int32 fpixChangeRefcount() - * l_int32 fpixGetResolution() - * l_int32 fpixSetResolution() - * l_int32 fpixCopyResolution() - * l_float32 *fpixGetData() - * l_int32 fpixSetData() - * l_int32 fpixGetPixel() - * l_int32 fpixSetPixel() - * - * FPixa Create/copy/destroy - * FPIXA *fpixaCreate() - * FPIXA *fpixaCopy() - * void fpixaDestroy() - * - * FPixa addition - * l_int32 fpixaAddFPix() - * static l_int32 fpixaExtendArray() - * static l_int32 fpixaExtendArrayToSize() - * - * FPixa accessors - * l_int32 fpixaGetCount() - * l_int32 fpixaChangeRefcount() - * FPIX *fpixaGetFPix() - * l_int32 fpixaGetFPixDimensions() - * l_float32 *fpixaGetData() - * l_int32 fpixaGetPixel() - * l_int32 fpixaSetPixel() - * - * DPix Create/copy/destroy - * DPIX *dpixCreate() - * DPIX *dpixCreateTemplate() - * DPIX *dpixClone() - * DPIX *dpixCopy() - * l_int32 dpixResizeImageData() - * void dpixDestroy() - * - * DPix accessors - * l_int32 dpixGetDimensions() - * l_int32 dpixSetDimensions() - * l_int32 dpixGetWpl() - * l_int32 dpixSetWpl() - * l_int32 dpixGetRefcount() - * l_int32 dpixChangeRefcount() - * l_int32 dpixGetResolution() - * l_int32 dpixSetResolution() - * l_int32 dpixCopyResolution() - * l_float64 *dpixGetData() - * l_int32 dpixSetData() - * l_int32 dpixGetPixel() - * l_int32 dpixSetPixel() - * - * FPix serialized I/O - * FPIX *fpixRead() - * FPIX *fpixReadStream() - * FPIX *fpixReadMem() - * l_int32 fpixWrite() - * l_int32 fpixWriteStream() - * l_int32 fpixWriteMem() - * FPIX *fpixEndianByteSwap() - * - * DPix serialized I/O - * DPIX *dpixRead() - * DPIX *dpixReadStream() - * DPIX *dpixReadMem() - * l_int32 dpixWrite() - * l_int32 dpixWriteStream() - * l_int32 dpixWriteMem() - * DPIX *dpixEndianByteSwap() - * - * Print FPix (subsampled, for debugging) - * l_int32 fpixPrintStream() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) Makes a FPix of specified size, with the data array - * allocated and initialized to 0. - * (2) The number of pixels must be less than 2^29. - *- */ -FPIX * -fpixCreate(l_int32 width, - l_int32 height) -{ -l_float32 *data; -l_uint64 npix64; -FPIX *fpixd; - - PROCNAME("fpixCreate"); - - if (width <= 0) - return (FPIX *)ERROR_PTR("width must be > 0", procName, NULL); - if (height <= 0) - return (FPIX *)ERROR_PTR("height must be > 0", procName, NULL); - - /* Avoid overflow in malloc arg, malicious or otherwise */ - npix64 = (l_uint64)width * (l_uint64)height; /* # of 4-byte pixels */ - if (npix64 >= (1LL << 29)) { - L_ERROR("requested w = %d, h = %d\n", procName, width, height); - return (FPIX *)ERROR_PTR("requested bytes >= 2^31", procName, NULL); - } - - fpixd = (FPIX *)LEPT_CALLOC(1, sizeof(FPIX)); - fpixSetDimensions(fpixd, width, height); - fpixSetWpl(fpixd, width); /* 4-byte words */ - fpixd->refcount = 1; - - data = (l_float32 *)LEPT_CALLOC((size_t)width * height, sizeof(l_float32)); - if (!data) { - fpixDestroy(&fpixd); - return (FPIX *)ERROR_PTR("calloc fail for data", procName, NULL); - } - fpixSetData(fpixd, data); - return fpixd; -} - - -/*! - * \brief fpixCreateTemplate() - * - * \param[in] fpixs - * \return fpixd, or NULL on error - * - *
- * Notes: - * (1) Makes a FPix of the same size as the input FPix, with the - * data array allocated and initialized to 0. - * (2) Copies the resolution. - *- */ -FPIX * -fpixCreateTemplate(FPIX *fpixs) -{ -l_int32 w, h; -FPIX *fpixd; - - PROCNAME("fpixCreateTemplate"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - - fpixGetDimensions(fpixs, &w, &h); - if ((fpixd = fpixCreate(w, h)) == NULL) - return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); - fpixCopyResolution(fpixd, fpixs); - return fpixd; -} - - -/*! - * \brief fpixClone() - * - * \param[in] fpix - * \return same fpix ptr, or NULL on error - * - *
- * Notes: - * (1) See pixClone() for definition and usage. - *- */ -FPIX * -fpixClone(FPIX *fpix) -{ - PROCNAME("fpixClone"); - - if (!fpix) - return (FPIX *)ERROR_PTR("fpix not defined", procName, NULL); - fpixChangeRefcount(fpix, 1); - - return fpix; -} - - -/*! - * \brief fpixCopy() - * - * \param[in] fpixd [optional] can be null, or equal to fpixs, - * or different from fpixs - * \param[in] fpixs - * \return fpixd, or NULL on error - * - *
- * Notes: - * (1) There are three cases: - * (a) fpixd == null (makes a new fpix; refcount = 1) - * (b) fpixd == fpixs (no-op) - * (c) fpixd != fpixs (data copy; no change in refcount) - * If the refcount of fpixd > 1, case (c) will side-effect - * these handles. - * (2) The general pattern of use is: - * fpixd = fpixCopy(fpixd, fpixs); - * This will work for all three cases. - * For clarity when the case is known, you can use: - * (a) fpixd = fpixCopy(NULL, fpixs); - * (c) fpixCopy(fpixd, fpixs); - * (3) For case (c), we check if fpixs and fpixd are the same size. - * If so, the data is copied directly. - * Otherwise, the data is reallocated to the correct size - * and the copy proceeds. The refcount of fpixd is unchanged. - * (4) This operation, like all others that may involve a pre-existing - * fpixd, will side-effect any existing clones of fpixd. - *- */ -FPIX * -fpixCopy(FPIX *fpixd, /* can be null */ - FPIX *fpixs) -{ -l_int32 w, h, bytes; -l_float32 *datas, *datad; - - PROCNAME("fpixCopy"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - if (fpixs == fpixd) - return fpixd; - - /* Total bytes in image data */ - fpixGetDimensions(fpixs, &w, &h); - bytes = 4 * w * h; - - /* If we're making a new fpix ... */ - if (!fpixd) { - if ((fpixd = fpixCreateTemplate(fpixs)) == NULL) - return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); - datas = fpixGetData(fpixs); - datad = fpixGetData(fpixd); - memcpy(datad, datas, bytes); - return fpixd; - } - - /* Reallocate image data if sizes are different */ - fpixResizeImageData(fpixd, fpixs); - - /* Copy data */ - fpixCopyResolution(fpixd, fpixs); - datas = fpixGetData(fpixs); - datad = fpixGetData(fpixd); - memcpy(datad, datas, bytes); - return fpixd; -} - - -/*! - * \brief fpixResizeImageData() - * - * \param[in] fpixd, fpixs - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) If the data sizes differ, this destroys the existing - * data in fpixd and allocates a new, uninitialized, data array - * of the same size as the data in fpixs. Otherwise, this - * doesn't do anything. - *- */ -l_ok -fpixResizeImageData(FPIX *fpixd, - FPIX *fpixs) -{ -l_int32 ws, hs, wd, hd, bytes; -l_float32 *data; - - PROCNAME("fpixResizeImageData"); - - if (!fpixs) - return ERROR_INT("fpixs not defined", procName, 1); - if (!fpixd) - return ERROR_INT("fpixd not defined", procName, 1); - - fpixGetDimensions(fpixs, &ws, &hs); - fpixGetDimensions(fpixd, &wd, &hd); - if (ws == wd && hs == hd) /* nothing to do */ - return 0; - - fpixSetDimensions(fpixd, ws, hs); - fpixSetWpl(fpixd, ws); - bytes = 4 * ws * hs; - data = fpixGetData(fpixd); - if (data) LEPT_FREE(data); - if ((data = (l_float32 *)LEPT_MALLOC(bytes)) == NULL) - return ERROR_INT("LEPT_MALLOC fail for data", procName, 1); - fpixSetData(fpixd, data); - return 0; -} - - -/*! - * \brief fpixDestroy() - * - * \param[in,out] pfpix will be set to null before returning - * \return void - * - *
- * Notes: - * (1) Decrements the ref count and, if 0, destroys the fpix. - * (2) Always nulls the input ptr. - *- */ -void -fpixDestroy(FPIX **pfpix) -{ -l_float32 *data; -FPIX *fpix; - - PROCNAME("fpixDestroy"); - - if (!pfpix) { - L_WARNING("ptr address is null!\n", procName); - return; - } - - if ((fpix = *pfpix) == NULL) - return; - - /* Decrement the ref count. If it is 0, destroy the fpix. */ - fpixChangeRefcount(fpix, -1); - if (fpixGetRefcount(fpix) <= 0) { - if ((data = fpixGetData(fpix)) != NULL) - LEPT_FREE(data); - LEPT_FREE(fpix); - } - - *pfpix = NULL; - return; -} - - -/*--------------------------------------------------------------------* - * FPix Accessors * - *--------------------------------------------------------------------*/ -/*! - * \brief fpixGetDimensions() - * - * \param[in] fpix - * \param[out] pw, ph [optional] each can be null - * \return 0 if OK, 1 on error - */ -l_ok -fpixGetDimensions(FPIX *fpix, - l_int32 *pw, - l_int32 *ph) -{ - PROCNAME("fpixGetDimensions"); - - if (!pw && !ph) - return ERROR_INT("no return val requested", procName, 1); - if (pw) *pw = 0; - if (ph) *ph = 0; - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - if (pw) *pw = fpix->w; - if (ph) *ph = fpix->h; - return 0; -} - - -/*! - * \brief fpixSetDimensions() - * - * \param[in] fpix - * \param[in] w, h - * \return 0 if OK, 1 on error - */ -l_ok -fpixSetDimensions(FPIX *fpix, - l_int32 w, - l_int32 h) -{ - PROCNAME("fpixSetDimensions"); - - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - fpix->w = w; - fpix->h = h; - return 0; -} - - -/*! - * \brief fpixGetWpl() - * - * \param[in] fpix - * \return wpl, or UNDEF on error - */ -l_int32 -fpixGetWpl(FPIX *fpix) -{ - PROCNAME("fpixGetWpl"); - - if (!fpix) - return ERROR_INT("fpix not defined", procName, UNDEF); - return fpix->wpl; -} - - -/*! - * \brief fpixSetWpl() - * - * \param[in] fpix - * \param[in] wpl - * \return 0 if OK, 1 on error - */ -l_ok -fpixSetWpl(FPIX *fpix, - l_int32 wpl) -{ - PROCNAME("fpixSetWpl"); - - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - - fpix->wpl = wpl; - return 0; -} - - -/*! - * \brief fpixGetRefcount() - * - * \param[in] fpix - * \return refcount, or UNDEF on error - */ -l_int32 -fpixGetRefcount(FPIX *fpix) -{ - PROCNAME("fpixGetRefcount"); - - if (!fpix) - return ERROR_INT("fpix not defined", procName, UNDEF); - return fpix->refcount; -} - - -/*! - * \brief fpixChangeRefcount() - * - * \param[in] fpix - * \param[in] delta - * \return 0 if OK, 1 on error - */ -l_ok -fpixChangeRefcount(FPIX *fpix, - l_int32 delta) -{ - PROCNAME("fpixChangeRefcount"); - - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - - fpix->refcount += delta; - return 0; -} - - -/*! - * \brief fpixGetResolution() - * - * \param[in] fpix - * \param[out] pxres, pyres [optional] x and y resolution - * \return 0 if OK, 1 on error - */ -l_ok -fpixGetResolution(FPIX *fpix, - l_int32 *pxres, - l_int32 *pyres) -{ - PROCNAME("fpixGetResolution"); - - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - if (pxres) *pxres = fpix->xres; - if (pyres) *pyres = fpix->yres; - return 0; -} - - -/*! - * \brief fpixSetResolution() - * - * \param[in] fpix - * \param[in] xres, yres x and y resolution - * \return 0 if OK, 1 on error - */ -l_ok -fpixSetResolution(FPIX *fpix, - l_int32 xres, - l_int32 yres) -{ - PROCNAME("fpixSetResolution"); - - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - - fpix->xres = xres; - fpix->yres = yres; - return 0; -} - - -/*! - * \brief fpixCopyResolution() - * - * \param[in] fpixd, fpixs - * \return 0 if OK, 1 on error - */ -l_ok -fpixCopyResolution(FPIX *fpixd, - FPIX *fpixs) -{ -l_int32 xres, yres; - PROCNAME("fpixCopyResolution"); - - if (!fpixs || !fpixd) - return ERROR_INT("fpixs and fpixd not both defined", procName, 1); - - fpixGetResolution(fpixs, &xres, &yres); - fpixSetResolution(fpixd, xres, yres); - return 0; -} - - -/*! - * \brief fpixGetData() - * - * \param[in] fpix - * \return ptr to fpix data, or NULL on error - */ -l_float32 * -fpixGetData(FPIX *fpix) -{ - PROCNAME("fpixGetData"); - - if (!fpix) - return (l_float32 *)ERROR_PTR("fpix not defined", procName, NULL); - return fpix->data; -} - - -/*! - * \brief fpixSetData() - * - * \param[in] fpix - * \param[in] data - * \return 0 if OK, 1 on error - */ -l_ok -fpixSetData(FPIX *fpix, - l_float32 *data) -{ - PROCNAME("fpixSetData"); - - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - - fpix->data = data; - return 0; -} - - -/*! - * \brief fpixGetPixel() - * - * \param[in] fpix - * \param[in] x,y pixel coords - * \param[out] pval pixel value - * \return 0 if OK; 1 or 2 on error - * - * Notes: - * (1) If the point is outside the image, this returns an error (2), - * with 0.0 in %pval. To avoid spamming output, it fails silently. - */ -l_ok -fpixGetPixel(FPIX *fpix, - l_int32 x, - l_int32 y, - l_float32 *pval) -{ -l_int32 w, h; - - PROCNAME("fpixGetPixel"); - - if (!pval) - return ERROR_INT("pval not defined", procName, 1); - *pval = 0.0; - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - - fpixGetDimensions(fpix, &w, &h); - if (x < 0 || x >= w || y < 0 || y >= h) - return 2; - - *pval = *(fpix->data + y * w + x); - return 0; -} - - -/*! - * \brief fpixSetPixel() - * - * \param[in] fpix - * \param[in] x,y pixel coords - * \param[in] val pixel value - * \return 0 if OK; 1 or 2 on error - * - * Notes: - * (1) If the point is outside the image, this returns an error (2), - * with 0.0 in %pval. To avoid spamming output, it fails silently. - */ -l_ok -fpixSetPixel(FPIX *fpix, - l_int32 x, - l_int32 y, - l_float32 val) -{ -l_int32 w, h; - - PROCNAME("fpixSetPixel"); - - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - - fpixGetDimensions(fpix, &w, &h); - if (x < 0 || x >= w || y < 0 || y >= h) - return 2; - - *(fpix->data + y * w + x) = val; - return 0; -} - - -/*--------------------------------------------------------------------* - * FPixa Create/copy/destroy * - *--------------------------------------------------------------------*/ -/*! - * \brief fpixaCreate() - * - * \param[in] n initial number of ptrs - * \return fpixa, or NULL on error - */ -FPIXA * -fpixaCreate(l_int32 n) -{ -FPIXA *fpixa; - - PROCNAME("fpixaCreate"); - - if (n <= 0 || n > MaxPtrArraySize) - n = InitialPtrArraySize; - - fpixa = (FPIXA *)LEPT_CALLOC(1, sizeof(FPIXA)); - fpixa->n = 0; - fpixa->nalloc = n; - fpixa->refcount = 1; - if ((fpixa->fpix = (FPIX **)LEPT_CALLOC(n, sizeof(FPIX *))) == NULL) { - fpixaDestroy(&fpixa); - return (FPIXA *)ERROR_PTR("fpixa ptrs not made", procName, NULL); - } - - return fpixa; -} - - -/*! - * \brief fpixaCopy() - * - * \param[in] fpixa - * \param[in] copyflag L_COPY, L_CLODE or L_COPY_CLONE - * \return new fpixa, or NULL on error - * - *
- * Notes: - * copyflag may be one of - * ~ L_COPY makes a new fpixa and copies each fpix - * ~ L_CLONE gives a new ref-counted handle to the input fpixa - * ~ L_COPY_CLONE makes a new fpixa with clones of all fpix - *- */ -FPIXA * -fpixaCopy(FPIXA *fpixa, - l_int32 copyflag) -{ -l_int32 i; -FPIX *fpixc; -FPIXA *fpixac; - - PROCNAME("fpixaCopy"); - - if (!fpixa) - return (FPIXA *)ERROR_PTR("fpixa not defined", procName, NULL); - - if (copyflag == L_CLONE) { - fpixaChangeRefcount(fpixa, 1); - return fpixa; - } - - if (copyflag != L_COPY && copyflag != L_COPY_CLONE) - return (FPIXA *)ERROR_PTR("invalid copyflag", procName, NULL); - - if ((fpixac = fpixaCreate(fpixa->n)) == NULL) - return (FPIXA *)ERROR_PTR("fpixac not made", procName, NULL); - for (i = 0; i < fpixa->n; i++) { - if (copyflag == L_COPY) - fpixc = fpixaGetFPix(fpixa, i, L_COPY); - else /* copy-clone */ - fpixc = fpixaGetFPix(fpixa, i, L_CLONE); - fpixaAddFPix(fpixac, fpixc, L_INSERT); - } - - return fpixac; -} - - -/*! - * \brief fpixaDestroy() - * - * \param[in,out] pfpixa will be set to null before returning - * \return void - * - *
- * Notes: - * (1) Decrements the ref count and, if 0, destroys the fpixa. - * (2) Always nulls the input ptr. - *- */ -void -fpixaDestroy(FPIXA **pfpixa) -{ -l_int32 i; -FPIXA *fpixa; - - PROCNAME("fpixaDestroy"); - - if (pfpixa == NULL) { - L_WARNING("ptr address is NULL!\n", procName); - return; - } - - if ((fpixa = *pfpixa) == NULL) - return; - - /* Decrement the refcount. If it is 0, destroy the pixa. */ - fpixaChangeRefcount(fpixa, -1); - if (fpixa->refcount <= 0) { - for (i = 0; i < fpixa->n; i++) - fpixDestroy(&fpixa->fpix[i]); - LEPT_FREE(fpixa->fpix); - LEPT_FREE(fpixa); - } - - *pfpixa = NULL; - return; -} - - -/*--------------------------------------------------------------------* - * FPixa addition * - *--------------------------------------------------------------------*/ -/*! - * \brief fpixaAddFPix() - * - * \param[in] fpixa - * \param[in] fpix to be added - * \param[in] copyflag L_INSERT, L_COPY, L_CLONE - * \return 0 if OK; 1 on error - */ -l_ok -fpixaAddFPix(FPIXA *fpixa, - FPIX *fpix, - l_int32 copyflag) -{ -l_int32 n; -FPIX *fpixc; - - PROCNAME("fpixaAddFPix"); - - if (!fpixa) - return ERROR_INT("fpixa not defined", procName, 1); - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - - if (copyflag == L_INSERT) - fpixc = fpix; - else if (copyflag == L_COPY) - fpixc = fpixCopy(NULL, fpix); - else if (copyflag == L_CLONE) - fpixc = fpixClone(fpix); - else - return ERROR_INT("invalid copyflag", procName, 1); - if (!fpixc) - return ERROR_INT("fpixc not made", procName, 1); - - n = fpixaGetCount(fpixa); - if (n >= fpixa->nalloc) - fpixaExtendArray(fpixa); - fpixa->fpix[n] = fpixc; - fpixa->n++; - - return 0; -} - - -/*! - * \brief fpixaExtendArray() - * - * \param[in] fpixa - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) Doubles the size of the fpixa ptr array. - *- */ -static l_int32 -fpixaExtendArray(FPIXA *fpixa) -{ - PROCNAME("fpixaExtendArray"); - - if (!fpixa) - return ERROR_INT("fpixa not defined", procName, 1); - - return fpixaExtendArrayToSize(fpixa, 2 * fpixa->nalloc); -} - - -/*! - * \brief fpixaExtendArrayToSize() - * - * \param[in] fpixa - * \param[in] size new ptr array size - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) If necessary, reallocs new fpixa ptrs array to %size. - *- */ -static l_int32 -fpixaExtendArrayToSize(FPIXA *fpixa, - l_int32 size) -{ - PROCNAME("fpixaExtendArrayToSize"); - - if (!fpixa) - return ERROR_INT("fpixa not defined", procName, 1); - - if (size > fpixa->nalloc) { - if ((fpixa->fpix = (FPIX **)reallocNew((void **)&fpixa->fpix, - sizeof(FPIX *) * fpixa->nalloc, - size * sizeof(FPIX *))) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - fpixa->nalloc = size; - } - return 0; -} - - -/*--------------------------------------------------------------------* - * FPixa accessors * - *--------------------------------------------------------------------*/ -/*! - * \brief fpixaGetCount() - * - * \param[in] fpixa - * \return count, or 0 if no pixa - */ -l_int32 -fpixaGetCount(FPIXA *fpixa) -{ - PROCNAME("fpixaGetCount"); - - if (!fpixa) - return ERROR_INT("fpixa not defined", procName, 0); - - return fpixa->n; -} - - -/*! - * \brief fpixaChangeRefcount() - * - * \param[in] fpixa - * \param[in] delta - * \return 0 if OK, 1 on error - */ -l_ok -fpixaChangeRefcount(FPIXA *fpixa, - l_int32 delta) -{ - PROCNAME("fpixaChangeRefcount"); - - if (!fpixa) - return ERROR_INT("fpixa not defined", procName, 1); - - fpixa->refcount += delta; - return 0; -} - - -/*! - * \brief fpixaGetFPix() - * - * \param[in] fpixa - * \param[in] index to the index-th fpix - * \param[in] accesstype L_COPY or L_CLONE - * \return fpix, or NULL on error - */ -FPIX * -fpixaGetFPix(FPIXA *fpixa, - l_int32 index, - l_int32 accesstype) -{ - PROCNAME("fpixaGetFPix"); - - if (!fpixa) - return (FPIX *)ERROR_PTR("fpixa not defined", procName, NULL); - if (index < 0 || index >= fpixa->n) - return (FPIX *)ERROR_PTR("index not valid", procName, NULL); - - if (accesstype == L_COPY) - return fpixCopy(NULL, fpixa->fpix[index]); - else if (accesstype == L_CLONE) - return fpixClone(fpixa->fpix[index]); - else - return (FPIX *)ERROR_PTR("invalid accesstype", procName, NULL); -} - - -/*! - * \brief fpixaGetFPixDimensions() - * - * \param[in] fpixa - * \param[in] index to the index-th box - * \param[out] pw, ph [optional] each can be null - * \return 0 if OK, 1 on error - */ -l_ok -fpixaGetFPixDimensions(FPIXA *fpixa, - l_int32 index, - l_int32 *pw, - l_int32 *ph) -{ -FPIX *fpix; - - PROCNAME("fpixaGetFPixDimensions"); - - if (!pw && !ph) - return ERROR_INT("no return val requested", procName, 1); - if (pw) *pw = 0; - if (ph) *ph = 0; - if (!fpixa) - return ERROR_INT("fpixa not defined", procName, 1); - if (index < 0 || index >= fpixa->n) - return ERROR_INT("index not valid", procName, 1); - - if ((fpix = fpixaGetFPix(fpixa, index, L_CLONE)) == NULL) - return ERROR_INT("fpix not found!", procName, 1); - fpixGetDimensions(fpix, pw, ph); - fpixDestroy(&fpix); - return 0; -} - - -/*! - * \brief fpixaGetData() - * - * \param[in] fpixa - * \param[in] index into fpixa array - * \return data not a copy, or NULL on error - */ -l_float32 * -fpixaGetData(FPIXA *fpixa, - l_int32 index) -{ -l_int32 n; -l_float32 *data; -FPIX *fpix; - - PROCNAME("fpixaGetData"); - - if (!fpixa) - return (l_float32 *)ERROR_PTR("fpixa not defined", procName, NULL); - n = fpixaGetCount(fpixa); - if (index < 0 || index >= n) - return (l_float32 *)ERROR_PTR("invalid index", procName, NULL); - - fpix = fpixaGetFPix(fpixa, index, L_CLONE); - data = fpixGetData(fpix); - fpixDestroy(&fpix); - return data; -} - - -/*! - * \brief fpixaGetPixel() - * - * \param[in] fpixa - * \param[in] index into fpixa array - * \param[in] x,y pixel coords - * \param[out] pval pixel value - * \return 0 if OK; 1 on error - */ -l_ok -fpixaGetPixel(FPIXA *fpixa, - l_int32 index, - l_int32 x, - l_int32 y, - l_float32 *pval) -{ -l_int32 n, ret; -FPIX *fpix; - - PROCNAME("fpixaGetPixel"); - - if (!pval) - return ERROR_INT("pval not defined", procName, 1); - *pval = 0.0; - if (!fpixa) - return ERROR_INT("fpixa not defined", procName, 1); - n = fpixaGetCount(fpixa); - if (index < 0 || index >= n) - return ERROR_INT("invalid index into fpixa", procName, 1); - - fpix = fpixaGetFPix(fpixa, index, L_CLONE); - ret = fpixGetPixel(fpix, x, y, pval); - fpixDestroy(&fpix); - return ret; -} - - -/*! - * \brief fpixaSetPixel() - * - * \param[in] fpixa - * \param[in] index into fpixa array - * \param[in] x,y pixel coords - * \param[in] val pixel value - * \return 0 if OK; 1 on error - */ -l_ok -fpixaSetPixel(FPIXA *fpixa, - l_int32 index, - l_int32 x, - l_int32 y, - l_float32 val) -{ -l_int32 n, ret; -FPIX *fpix; - - PROCNAME("fpixaSetPixel"); - - if (!fpixa) - return ERROR_INT("fpixa not defined", procName, 1); - n = fpixaGetCount(fpixa); - if (index < 0 || index >= n) - return ERROR_INT("invalid index into fpixa", procName, 1); - - fpix = fpixaGetFPix(fpixa, index, L_CLONE); - ret = fpixSetPixel(fpix, x, y, val); - fpixDestroy(&fpix); - return ret; -} - - -/*--------------------------------------------------------------------* - * DPix Create/copy/destroy * - *--------------------------------------------------------------------*/ -/*! - * \brief dpixCreate() - * - * \param[in] width, height - * \return dpix with data allocated and initialized to 0, or NULL on error - * - *
- * Notes: - * (1) Makes a DPix of specified size, with the data array - * allocated and initialized to 0. - * (2) The number of pixels must be less than 2^28. - *- */ -DPIX * -dpixCreate(l_int32 width, - l_int32 height) -{ -l_float64 *data; -l_uint64 npix64; -DPIX *dpix; - - PROCNAME("dpixCreate"); - - if (width <= 0) - return (DPIX *)ERROR_PTR("width must be > 0", procName, NULL); - if (height <= 0) - return (DPIX *)ERROR_PTR("height must be > 0", procName, NULL); - - /* Avoid overflow in malloc arg, malicious or otherwise */ - npix64 = (l_uint64)width * (l_uint64)height; /* # of 8 byte pixels */ - if (npix64 >= (1LL << 28)) { - L_ERROR("requested w = %d, h = %d\n", procName, width, height); - return (DPIX *)ERROR_PTR("requested bytes >= 2^31", procName, NULL); - } - - dpix = (DPIX *)LEPT_CALLOC(1, sizeof(DPIX)); - dpixSetDimensions(dpix, width, height); - dpixSetWpl(dpix, width); /* 8 byte words */ - dpix->refcount = 1; - - data = (l_float64 *)LEPT_CALLOC((size_t)width * height, sizeof(l_float64)); - if (!data) { - dpixDestroy(&dpix); - return (DPIX *)ERROR_PTR("calloc fail for data", procName, NULL); - } - dpixSetData(dpix, data); - return dpix; -} - - -/*! - * \brief dpixCreateTemplate() - * - * \param[in] dpixs - * \return dpixd, or NULL on error - * - *
- * Notes: - * (1) Makes a DPix of the same size as the input DPix, with the - * data array allocated and initialized to 0. - * (2) Copies the resolution. - *- */ -DPIX * -dpixCreateTemplate(DPIX *dpixs) -{ -l_int32 w, h; -DPIX *dpixd; - - PROCNAME("dpixCreateTemplate"); - - if (!dpixs) - return (DPIX *)ERROR_PTR("dpixs not defined", procName, NULL); - - dpixGetDimensions(dpixs, &w, &h); - dpixd = dpixCreate(w, h); - dpixCopyResolution(dpixd, dpixs); - return dpixd; -} - - -/*! - * \brief dpixClone() - * - * \param[in] dpix - * \return same dpix ptr, or NULL on error - * - *
- * Notes: - * (1) See pixClone() for definition and usage. - *- */ -DPIX * -dpixClone(DPIX *dpix) -{ - PROCNAME("dpixClone"); - - if (!dpix) - return (DPIX *)ERROR_PTR("dpix not defined", procName, NULL); - dpixChangeRefcount(dpix, 1); - - return dpix; -} - - -/*! - * \brief dpixCopy() - * - * \param[in] dpixd [optional] can be null, or equal to dpixs, - * or different from dpixs - * \param[in] dpixs - * \return dpixd, or NULL on error - * - *
- * Notes: - * (1) There are three cases: - * (a) dpixd == null (makes a new dpix; refcount = 1) - * (b) dpixd == dpixs (no-op) - * (c) dpixd != dpixs (data copy; no change in refcount) - * If the refcount of dpixd > 1, case (c) will side-effect - * these handles. - * (2) The general pattern of use is: - * dpixd = dpixCopy(dpixd, dpixs); - * This will work for all three cases. - * For clarity when the case is known, you can use: - * (a) dpixd = dpixCopy(NULL, dpixs); - * (c) dpixCopy(dpixd, dpixs); - * (3) For case (c), we check if dpixs and dpixd are the same size. - * If so, the data is copied directly. - * Otherwise, the data is reallocated to the correct size - * and the copy proceeds. The refcount of dpixd is unchanged. - * (4) This operation, like all others that may involve a pre-existing - * dpixd, will side-effect any existing clones of dpixd. - *- */ -DPIX * -dpixCopy(DPIX *dpixd, /* can be null */ - DPIX *dpixs) -{ -l_int32 w, h, bytes; -l_float64 *datas, *datad; - - PROCNAME("dpixCopy"); - - if (!dpixs) - return (DPIX *)ERROR_PTR("dpixs not defined", procName, NULL); - if (dpixs == dpixd) - return dpixd; - - /* Total bytes in image data */ - dpixGetDimensions(dpixs, &w, &h); - bytes = 8 * w * h; - - /* If we're making a new dpix ... */ - if (!dpixd) { - if ((dpixd = dpixCreateTemplate(dpixs)) == NULL) - return (DPIX *)ERROR_PTR("dpixd not made", procName, NULL); - datas = dpixGetData(dpixs); - datad = dpixGetData(dpixd); - memcpy(datad, datas, bytes); - return dpixd; - } - - /* Reallocate image data if sizes are different */ - dpixResizeImageData(dpixd, dpixs); - - /* Copy data */ - dpixCopyResolution(dpixd, dpixs); - datas = dpixGetData(dpixs); - datad = dpixGetData(dpixd); - memcpy(datad, datas, bytes); - return dpixd; -} - - -/*! - * \brief dpixResizeImageData() - * - * \param[in] dpixd, dpixs - * \return 0 if OK, 1 on error - */ -l_ok -dpixResizeImageData(DPIX *dpixd, - DPIX *dpixs) -{ -l_int32 ws, hs, wd, hd, bytes; -l_float64 *data; - - PROCNAME("dpixResizeImageData"); - - if (!dpixs) - return ERROR_INT("dpixs not defined", procName, 1); - if (!dpixd) - return ERROR_INT("dpixd not defined", procName, 1); - - dpixGetDimensions(dpixs, &ws, &hs); - dpixGetDimensions(dpixd, &wd, &hd); - if (ws == wd && hs == hd) /* nothing to do */ - return 0; - - dpixSetDimensions(dpixd, ws, hs); - dpixSetWpl(dpixd, ws); /* 8 byte words */ - bytes = 8 * ws * hs; - data = dpixGetData(dpixd); - if (data) LEPT_FREE(data); - if ((data = (l_float64 *)LEPT_MALLOC(bytes)) == NULL) - return ERROR_INT("LEPT_MALLOC fail for data", procName, 1); - dpixSetData(dpixd, data); - return 0; -} - - -/*! - * \brief dpixDestroy() - * - * \param[in,out] pdpix will be set to null before returning - * \return void - * - *
- * Notes: - * (1) Decrements the ref count and, if 0, destroys the dpix. - * (2) Always nulls the input ptr. - *- */ -void -dpixDestroy(DPIX **pdpix) -{ -l_float64 *data; -DPIX *dpix; - - PROCNAME("dpixDestroy"); - - if (!pdpix) { - L_WARNING("ptr address is null!\n", procName); - return; - } - - if ((dpix = *pdpix) == NULL) - return; - - /* Decrement the ref count. If it is 0, destroy the dpix. */ - dpixChangeRefcount(dpix, -1); - if (dpixGetRefcount(dpix) <= 0) { - if ((data = dpixGetData(dpix)) != NULL) - LEPT_FREE(data); - LEPT_FREE(dpix); - } - - *pdpix = NULL; - return; -} - - -/*--------------------------------------------------------------------* - * DPix Accessors * - *--------------------------------------------------------------------*/ -/*! - * \brief dpixGetDimensions() - * - * \param[in] dpix - * \param[out] pw, ph [optional] each can be null - * \return 0 if OK, 1 on error - */ -l_ok -dpixGetDimensions(DPIX *dpix, - l_int32 *pw, - l_int32 *ph) -{ - PROCNAME("dpixGetDimensions"); - - if (!pw && !ph) - return ERROR_INT("no return val requested", procName, 1); - if (pw) *pw = 0; - if (ph) *ph = 0; - if (!dpix) - return ERROR_INT("dpix not defined", procName, 1); - if (pw) *pw = dpix->w; - if (ph) *ph = dpix->h; - return 0; -} - - -/*! - * \brief dpixSetDimensions() - * - * \param[in] dpix - * \param[in] w, h - * \return 0 if OK, 1 on error - */ -l_ok -dpixSetDimensions(DPIX *dpix, - l_int32 w, - l_int32 h) -{ - PROCNAME("dpixSetDimensions"); - - if (!dpix) - return ERROR_INT("dpix not defined", procName, 1); - dpix->w = w; - dpix->h = h; - return 0; -} - - -/*! - * \brief dpixGetWpl() - * - * \param[in] dpix - * \return wpl, or UNDEF on error - */ -l_int32 -dpixGetWpl(DPIX *dpix) -{ - PROCNAME("dpixGetWpl"); - - if (!dpix) - return ERROR_INT("dpix not defined", procName, 1); - return dpix->wpl; -} - - -/*! - * \brief dpixSetWpl() - * - * \param[in] dpix - * \param[in] wpl - * \return 0 if OK, 1 on error - */ -l_ok -dpixSetWpl(DPIX *dpix, - l_int32 wpl) -{ - PROCNAME("dpixSetWpl"); - - if (!dpix) - return ERROR_INT("dpix not defined", procName, 1); - - dpix->wpl = wpl; - return 0; -} - - -/*! - * \brief dpixGetRefcount() - * - * \param[in] dpix - * \return refcount, or UNDEF on error - */ -l_int32 -dpixGetRefcount(DPIX *dpix) -{ - PROCNAME("dpixGetRefcount"); - - if (!dpix) - return ERROR_INT("dpix not defined", procName, UNDEF); - return dpix->refcount; -} - - -/*! - * \brief dpixChangeRefcount() - * - * \param[in] dpix - * \param[in] delta - * \return 0 if OK, 1 on error - */ -l_ok -dpixChangeRefcount(DPIX *dpix, - l_int32 delta) -{ - PROCNAME("dpixChangeRefcount"); - - if (!dpix) - return ERROR_INT("dpix not defined", procName, 1); - - dpix->refcount += delta; - return 0; -} - - -/*! - * \brief dpixGetResolution() - * - * \param[in] dpix - * \param[out] pxres, pyres [optional] x and y resolution - * \return 0 if OK, 1 on error - */ -l_ok -dpixGetResolution(DPIX *dpix, - l_int32 *pxres, - l_int32 *pyres) -{ - PROCNAME("dpixGetResolution"); - - if (!dpix) - return ERROR_INT("dpix not defined", procName, 1); - if (pxres) *pxres = dpix->xres; - if (pyres) *pyres = dpix->yres; - return 0; -} - - -/*! - * \brief dpixSetResolution() - * - * \param[in] dpix - * \param[in] xres, yres x and y resolution - * \return 0 if OK, 1 on error - */ -l_ok -dpixSetResolution(DPIX *dpix, - l_int32 xres, - l_int32 yres) -{ - PROCNAME("dpixSetResolution"); - - if (!dpix) - return ERROR_INT("dpix not defined", procName, 1); - - dpix->xres = xres; - dpix->yres = yres; - return 0; -} - - -/*! - * \brief dpixCopyResolution() - * - * \param[in] dpixd, dpixs - * \return 0 if OK, 1 on error - */ -l_ok -dpixCopyResolution(DPIX *dpixd, - DPIX *dpixs) -{ -l_int32 xres, yres; - PROCNAME("dpixCopyResolution"); - - if (!dpixs || !dpixd) - return ERROR_INT("dpixs and dpixd not both defined", procName, 1); - - dpixGetResolution(dpixs, &xres, &yres); - dpixSetResolution(dpixd, xres, yres); - return 0; -} - - -/*! - * \brief dpixGetData() - * - * \param[in] dpix - * \return ptr to dpix data, or NULL on error - */ -l_float64 * -dpixGetData(DPIX *dpix) -{ - PROCNAME("dpixGetData"); - - if (!dpix) - return (l_float64 *)ERROR_PTR("dpix not defined", procName, NULL); - return dpix->data; -} - - -/*! - * \brief dpixSetData() - * - * \param[in] dpix - * \param[in] data - * \return 0 if OK, 1 on error - */ -l_ok -dpixSetData(DPIX *dpix, - l_float64 *data) -{ - PROCNAME("dpixSetData"); - - if (!dpix) - return ERROR_INT("dpix not defined", procName, 1); - - dpix->data = data; - return 0; -} - - -/*! - * \brief dpixGetPixel() - * - * \param[in] dpix - * \param[in] x,y pixel coords - * \param[out] pval pixel value - * \return 0 if OK; 1 or 2 on error - * - * Notes: - * (1) If the point is outside the image, this returns an error (2), - * with 0.0 in %pval. To avoid spamming output, it fails silently. - */ -l_ok -dpixGetPixel(DPIX *dpix, - l_int32 x, - l_int32 y, - l_float64 *pval) -{ -l_int32 w, h; - - PROCNAME("dpixGetPixel"); - - if (!pval) - return ERROR_INT("pval not defined", procName, 1); - *pval = 0.0; - if (!dpix) - return ERROR_INT("dpix not defined", procName, 1); - - dpixGetDimensions(dpix, &w, &h); - if (x < 0 || x >= w || y < 0 || y >= h) - return 2; - - *pval = *(dpix->data + y * w + x); - return 0; -} - - -/*! - * \brief dpixSetPixel() - * - * \param[in] dpix - * \param[in] x,y pixel coords - * \param[in] val pixel value - * \return 0 if OK; 1 or 2 on error - * - * Notes: - * (1) If the point is outside the image, this returns an error (2), - * with 0.0 in %pval. To avoid spamming output, it fails silently. - */ -l_ok -dpixSetPixel(DPIX *dpix, - l_int32 x, - l_int32 y, - l_float64 val) -{ -l_int32 w, h; - - PROCNAME("dpixSetPixel"); - - if (!dpix) - return ERROR_INT("dpix not defined", procName, 1); - - dpixGetDimensions(dpix, &w, &h); - if (x < 0 || x >= w || y < 0 || y >= h) - return 2; - - *(dpix->data + y * w + x) = val; - return 0; -} - - -/*--------------------------------------------------------------------* - * FPix serialized I/O * - *--------------------------------------------------------------------*/ -/*! - * \brief fpixRead() - * - * \param[in] filename - * \return fpix, or NULL on error - */ -FPIX * -fpixRead(const char *filename) -{ -FILE *fp; -FPIX *fpix; - - PROCNAME("fpixRead"); - - if (!filename) - return (FPIX *)ERROR_PTR("filename not defined", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (FPIX *)ERROR_PTR("stream not opened", procName, NULL); - fpix = fpixReadStream(fp); - fclose(fp); - if (!fpix) - return (FPIX *)ERROR_PTR("fpix not read", procName, NULL); - return fpix; -} - - -/*! - * \brief fpixReadStream() - * - * \param[in] fp file stream - * \return fpix, or NULL on error - */ -FPIX * -fpixReadStream(FILE *fp) -{ -char buf[256]; -l_int32 w, h, nbytes, xres, yres, version; -l_float32 *data; -FPIX *fpix; - - PROCNAME("fpixReadStream"); - - if (!fp) - return (FPIX *)ERROR_PTR("stream not defined", procName, NULL); - - if (fscanf(fp, "\nFPix Version %d\n", &version) != 1) - return (FPIX *)ERROR_PTR("not a fpix file", procName, NULL); - if (version != FPIX_VERSION_NUMBER) - return (FPIX *)ERROR_PTR("invalid fpix version", procName, NULL); - if (fscanf(fp, "w = %d, h = %d, nbytes = %d\n", &w, &h, &nbytes) != 3) - return (FPIX *)ERROR_PTR("read fail for data size", procName, NULL); - - /* Use fgets() and sscanf(); not fscanf(), for the last - * bit of header data before the float data. The reason is - * that fscanf throws away white space, and if the float data - * happens to begin with ascii character(s) that are white - * space, it will swallow them and all will be lost! */ - if (fgets(buf, sizeof(buf), fp) == NULL) - return (FPIX *)ERROR_PTR("fgets read fail", procName, NULL); - if (sscanf(buf, "xres = %d, yres = %d\n", &xres, &yres) != 2) - return (FPIX *)ERROR_PTR("read fail for xres, yres", procName, NULL); - - if ((fpix = fpixCreate(w, h)) == NULL) - return (FPIX *)ERROR_PTR("fpix not made", procName, NULL); - fpixSetResolution(fpix, xres, yres); - data = fpixGetData(fpix); - if (fread(data, 1, nbytes, fp) != nbytes) { - fpixDestroy(&fpix); - return (FPIX *)ERROR_PTR("read error for nbytes", procName, NULL); - } - fgetc(fp); /* ending nl */ - - /* Convert to little-endian if necessary */ - fpixEndianByteSwap(fpix, fpix); - return fpix; -} - - -/*! - * \brief fpixReadMem() - * - * \param[in] data of serialized fpix - * \param[in] size of data in bytes - * \return fpix, or NULL on error - */ -FPIX * -fpixReadMem(const l_uint8 *data, - size_t size) -{ -FILE *fp; -FPIX *fpix; - - PROCNAME("fpixReadMem"); - - if (!data) - return (FPIX *)ERROR_PTR("data not defined", procName, NULL); - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return (FPIX *)ERROR_PTR("stream not opened", procName, NULL); - - fpix = fpixReadStream(fp); - fclose(fp); - if (!fpix) L_ERROR("fpix not read\n", procName); - return fpix; -} - - -/*! - * \brief fpixWrite() - * - * \param[in] filename - * \param[in] fpix - * \return 0 if OK, 1 on error - */ -l_ok -fpixWrite(const char *filename, - FPIX *fpix) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("fpixWrite"); - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "wb")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = fpixWriteStream(fp, fpix); - fclose(fp); - if (ret) - return ERROR_INT("fpix not written to stream", procName, 1); - return 0; -} - - -/*! - * \brief fpixWriteStream() - * - * \param[in] fp file stream opened for "wb" - * \param[in] fpix - * \return 0 if OK, 1 on error - */ -l_ok -fpixWriteStream(FILE *fp, - FPIX *fpix) -{ -l_int32 w, h, xres, yres; -l_uint32 nbytes; -l_float32 *data; -FPIX *fpixt; - - PROCNAME("fpixWriteStream"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - - /* Convert to little-endian if necessary */ - fpixt = fpixEndianByteSwap(NULL, fpix); - - fpixGetDimensions(fpixt, &w, &h); - data = fpixGetData(fpixt); - nbytes = sizeof(l_float32) * w * h; - fpixGetResolution(fpixt, &xres, &yres); - fprintf(fp, "\nFPix Version %d\n", FPIX_VERSION_NUMBER); - fprintf(fp, "w = %d, h = %d, nbytes = %u\n", w, h, nbytes); - fprintf(fp, "xres = %d, yres = %d\n", xres, yres); - fwrite(data, 1, nbytes, fp); - fprintf(fp, "\n"); - - fpixDestroy(&fpixt); - return 0; -} - - -/*! - * \brief fpixWriteMem() - * - * \param[out] pdata data of serialized fpix - * \param[out] psize size of returned data - * \param[in] fpix - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Serializes a fpix in memory and puts the result in a buffer. - *- */ -l_ok -fpixWriteMem(l_uint8 **pdata, - size_t *psize, - FPIX *fpix) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("fpixWriteMem"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - if (!psize) - return ERROR_INT("&size not defined", procName, 1); - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = fpixWriteStream(fp, fpix); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = fpixWriteStream(fp, fpix); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} - - -/*! - * \brief fpixEndianByteSwap() - * - * \param[in] fpixd can be equal to fpixs or NULL - * \param[in] fpixs - * \return fpixd always - * - *
- * Notes: - * (1) On big-endian hardware, this does byte-swapping on each of - * the 4-byte floats in the fpix data. On little-endians, - * the data is unchanged. This is used for serialization - * of fpix; the data is serialized in little-endian byte - * order because most hardware is little-endian. - * (2) The operation can be either in-place or, if fpixd == NULL, - * a new fpix is made. If not in-place, caller must catch - * the returned pointer. - *- */ -FPIX * -fpixEndianByteSwap(FPIX *fpixd, - FPIX *fpixs) -{ - PROCNAME("fpixEndianByteSwap"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, fpixd); - if (fpixd && (fpixs != fpixd)) - return (FPIX *)ERROR_PTR("fpixd != fpixs", procName, fpixd); - -#ifdef L_BIG_ENDIAN - { - l_uint32 *data; - l_int32 i, j, w, h; - l_uint32 word; - - fpixGetDimensions(fpixs, &w, &h); - fpixd = fpixCopy(fpixd, fpixs); /* no copy if fpixd == fpixs */ - - data = (l_uint32 *)fpixGetData(fpixd); - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++, data++) { - word = *data; - *data = (word >> 24) | - ((word >> 8) & 0x0000ff00) | - ((word << 8) & 0x00ff0000) | - (word << 24); - } - } - return fpixd; - } -#else /* L_LITTLE_ENDIAN */ - - if (fpixd) - return fpixd; /* no-op */ - else - return fpixClone(fpixs); - -#endif /* L_BIG_ENDIAN */ -} - - -/*--------------------------------------------------------------------* - * DPix serialized I/O * - *--------------------------------------------------------------------*/ -/*! - * \brief dpixRead() - * - * \param[in] filename - * \return dpix, or NULL on error - */ -DPIX * -dpixRead(const char *filename) -{ -FILE *fp; -DPIX *dpix; - - PROCNAME("dpixRead"); - - if (!filename) - return (DPIX *)ERROR_PTR("filename not defined", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (DPIX *)ERROR_PTR("stream not opened", procName, NULL); - dpix = dpixReadStream(fp); - fclose(fp); - if (!dpix) - return (DPIX *)ERROR_PTR("dpix not read", procName, NULL); - return dpix; -} - - -/*! - * \brief dpixReadStream() - * - * \param[in] fp file stream - * \return dpix, or NULL on error - */ -DPIX * -dpixReadStream(FILE *fp) -{ -char buf[256]; -l_int32 w, h, nbytes, version, xres, yres; -l_float64 *data; -DPIX *dpix; - - PROCNAME("dpixReadStream"); - - if (!fp) - return (DPIX *)ERROR_PTR("stream not defined", procName, NULL); - - if (fscanf(fp, "\nDPix Version %d\n", &version) != 1) - return (DPIX *)ERROR_PTR("not a dpix file", procName, NULL); - if (version != DPIX_VERSION_NUMBER) - return (DPIX *)ERROR_PTR("invalid dpix version", procName, NULL); - if (fscanf(fp, "w = %d, h = %d, nbytes = %d\n", &w, &h, &nbytes) != 3) - return (DPIX *)ERROR_PTR("read fail for data size", procName, NULL); - - /* Use fgets() and sscanf(); not fscanf(), for the last - * bit of header data before the float data. The reason is - * that fscanf throws away white space, and if the float data - * happens to begin with ascii character(s) that are white - * space, it will swallow them and all will be lost! */ - if (fgets(buf, sizeof(buf), fp) == NULL) - return (DPIX *)ERROR_PTR("fgets read fail", procName, NULL); - if (sscanf(buf, "xres = %d, yres = %d\n", &xres, &yres) != 2) - return (DPIX *)ERROR_PTR("read fail for xres, yres", procName, NULL); - - if ((dpix = dpixCreate(w, h)) == NULL) - return (DPIX *)ERROR_PTR("dpix not made", procName, NULL); - dpixSetResolution(dpix, xres, yres); - data = dpixGetData(dpix); - if (fread(data, 1, nbytes, fp) != nbytes) { - dpixDestroy(&dpix); - return (DPIX *)ERROR_PTR("read error for nbytes", procName, NULL); - } - fgetc(fp); /* ending nl */ - - /* Convert to little-endian if necessary */ - dpixEndianByteSwap(dpix, dpix); - return dpix; -} - - -/*! - * \brief dpixReadMem() - * - * \param[in] data of serialized dpix - * \param[in] size of data in bytes - * \return dpix, or NULL on error - */ -DPIX * -dpixReadMem(const l_uint8 *data, - size_t size) -{ -FILE *fp; -DPIX *dpix; - - PROCNAME("dpixReadMem"); - - if (!data) - return (DPIX *)ERROR_PTR("data not defined", procName, NULL); - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return (DPIX *)ERROR_PTR("stream not opened", procName, NULL); - - dpix = dpixReadStream(fp); - fclose(fp); - if (!dpix) L_ERROR("dpix not read\n", procName); - return dpix; -} - - -/*! - * \brief dpixWrite() - * - * \param[in] filename - * \param[in] dpix - * \return 0 if OK, 1 on error - */ -l_ok -dpixWrite(const char *filename, - DPIX *dpix) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("dpixWrite"); - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!dpix) - return ERROR_INT("dpix not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "wb")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = dpixWriteStream(fp, dpix); - fclose(fp); - if (ret) - return ERROR_INT("dpix not written to stream", procName, 1); - return 0; -} - - -/*! - * \brief dpixWriteStream() - * - * \param[in] fp file stream opened for "wb" - * \param[in] dpix - * \return 0 if OK, 1 on error - */ -l_ok -dpixWriteStream(FILE *fp, - DPIX *dpix) -{ -l_int32 w, h, xres, yres; -l_uint32 nbytes; -l_float64 *data; -DPIX *dpixt; - - PROCNAME("dpixWriteStream"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!dpix) - return ERROR_INT("dpix not defined", procName, 1); - - /* Convert to little-endian if necessary */ - dpixt = dpixEndianByteSwap(NULL, dpix); - - dpixGetDimensions(dpixt, &w, &h); - dpixGetResolution(dpixt, &xres, &yres); - data = dpixGetData(dpixt); - nbytes = sizeof(l_float64) * w * h; - fprintf(fp, "\nDPix Version %d\n", DPIX_VERSION_NUMBER); - fprintf(fp, "w = %d, h = %d, nbytes = %u\n", w, h, nbytes); - fprintf(fp, "xres = %d, yres = %d\n", xres, yres); - fwrite(data, 1, nbytes, fp); - fprintf(fp, "\n"); - - dpixDestroy(&dpixt); - return 0; -} - - -/*! - * \brief dpixWriteMem() - * - * \param[out] pdata data of serialized dpix - * \param[out] psize size of returned data - * \param[in] dpix - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Serializes a dpix in memory and puts the result in a buffer. - *- */ -l_ok -dpixWriteMem(l_uint8 **pdata, - size_t *psize, - DPIX *dpix) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("dpixWriteMem"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - if (!psize) - return ERROR_INT("&size not defined", procName, 1); - if (!dpix) - return ERROR_INT("dpix not defined", procName, 1); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = dpixWriteStream(fp, dpix); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = dpixWriteStream(fp, dpix); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} - - -/*! - * \brief dpixEndianByteSwap() - * - * \param[in] dpixd can be equal to dpixs or NULL - * \param[in] dpixs - * \return dpixd always - * - *
- * Notes: - * (1) On big-endian hardware, this does byte-swapping on each of - * the 4-byte words in the dpix data. On little-endians, - * the data is unchanged. This is used for serialization - * of dpix; the data is serialized in little-endian byte - * order because most hardware is little-endian. - * (2) The operation can be either in-place or, if dpixd == NULL, - * a new dpix is made. If not in-place, caller must catch - * the returned pointer. - *- */ -DPIX * -dpixEndianByteSwap(DPIX *dpixd, - DPIX *dpixs) -{ - PROCNAME("dpixEndianByteSwap"); - - if (!dpixs) - return (DPIX *)ERROR_PTR("dpixs not defined", procName, dpixd); - if (dpixd && (dpixs != dpixd)) - return (DPIX *)ERROR_PTR("dpixd != dpixs", procName, dpixd); - -#ifdef L_BIG_ENDIAN - { - l_uint32 *data; - l_int32 i, j, w, h; - l_uint32 word; - - dpixGetDimensions(dpixs, &w, &h); - dpixd = dpixCopy(dpixd, dpixs); /* no copy if dpixd == dpixs */ - - data = (l_uint32 *)dpixGetData(dpixd); - for (i = 0; i < h; i++) { - for (j = 0; j < 2 * w; j++, data++) { - word = *data; - *data = (word >> 24) | - ((word >> 8) & 0x0000ff00) | - ((word << 8) & 0x00ff0000) | - (word << 24); - } - } - return dpixd; - } -#else /* L_LITTLE_ENDIAN */ - - if (dpixd) - return dpixd; /* no-op */ - else - return dpixClone(dpixs); - -#endif /* L_BIG_ENDIAN */ -} - - -/*--------------------------------------------------------------------* - * Print FPix (subsampled, for debugging) * - *--------------------------------------------------------------------*/ -/*! - * \brief fpixPrintStream() - * - * \param[in] fp file stream - * \param[in] fpix - * \param[in] factor for subsampling - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Subsampled printout of fpix for debugging. - *- */ -l_ok -fpixPrintStream(FILE *fp, - FPIX *fpix, - l_int32 factor) -{ -l_int32 i, j, w, h, count; -l_float32 val; - - PROCNAME("fpixPrintStream"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - if (factor < 1) - return ERROR_INT("sampling factor < 1f", procName, 1); - - fpixGetDimensions(fpix, &w, &h); - fprintf(fp, "\nFPix: w = %d, h = %d\n", w, h); - for (i = 0; i < h; i += factor) { - for (count = 0, j = 0; j < w; j += factor, count++) { - fpixGetPixel(fpix, j, i, &val); - fprintf(fp, "val[%d, %d] = %f ", i, j, val); - if ((count + 1) % 3 == 0) fprintf(fp, "\n"); - } - if (count % 3) fprintf(fp, "\n"); - } - fprintf(fp, "\n"); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fpix2.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fpix2.c deleted file mode 100644 index befe30eb..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/fpix2.c +++ /dev/null @@ -1,2471 +0,0 @@ -/*====================================================================* - - 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 fpix2.c - *
- * - * ------------------------------------------ - * This file has these FPix utilities: - * ~ interconversions with pix, fpix, dpix - * ~ min and max values - * ~ integer scaling - * ~ arithmetic operations - * ~ set all - * ~ border functions - * ~ simple rasterop (source --> dest) - * ~ geometric transforms - * ------------------------------------------ - * - * Interconversions between Pix, FPix and DPix - * FPIX *pixConvertToFPix() - * DPIX *pixConvertToDPix() - * PIX *fpixConvertToPix() - * PIX *fpixDisplayMaxDynamicRange() [useful for debugging] - * DPIX *fpixConvertToDPix() - * PIX *dpixConvertToPix() - * FPIX *dpixConvertToFPix() - * - * Min/max value - * l_int32 fpixGetMin() - * l_int32 fpixGetMax() - * l_int32 dpixGetMin() - * l_int32 dpixGetMax() - * - * Integer scaling - * FPIX *fpixScaleByInteger() - * DPIX *dpixScaleByInteger() - * - * Arithmetic operations - * FPIX *fpixLinearCombination() - * l_int32 fpixAddMultConstant() - * DPIX *dpixLinearCombination() - * l_int32 dpixAddMultConstant() - * - * Set all - * l_int32 fpixSetAllArbitrary() - * l_int32 dpixSetAllArbitrary() - * - * FPix border functions - * FPIX *fpixAddBorder() - * FPIX *fpixRemoveBorder() - * FPIX *fpixAddMirroredBorder() - * FPIX *fpixAddContinuedBorder() - * FPIX *fpixAddSlopeBorder() - * - * FPix simple rasterop - * l_int32 fpixRasterop() - * - * FPix rotation by multiples of 90 degrees - * FPIX *fpixRotateOrth() - * FPIX *fpixRotate180() - * FPIX *fpixRotate90() - * FPIX *fpixFlipLR() - * FPIX *fpixFlipTB() - * - * FPix affine and projective interpolated transforms - * FPIX *fpixAffinePta() - * FPIX *fpixAffine() - * FPIX *fpixProjectivePta() - * FPIX *fpixProjective() - * l_int32 linearInterpolatePixelFloat() - * - * Thresholding to 1 bpp Pix - * PIX *fpixThresholdToPix() - * - * Generate function from components - * FPIX *pixComponentFunction() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) If colormapped, remove to grayscale. - * (2) If 32 bpp and %ncomps == 3, this is RGB; convert to luminance. - * In all other cases the src image is treated as having a single - * component of pixel values. - *- */ -FPIX * -pixConvertToFPix(PIX *pixs, - l_int32 ncomps) -{ -l_int32 w, h, d, i, j, val, wplt, wpld; -l_uint32 uval; -l_uint32 *datat, *linet; -l_float32 *datad, *lined; -PIX *pixt; -FPIX *fpixd; - - PROCNAME("pixConvertToFPix"); - - if (!pixs) - return (FPIX *)ERROR_PTR("pixs not defined", procName, NULL); - - /* Convert to a single component */ - if (pixGetColormap(pixs)) - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - else if (pixGetDepth(pixs) == 32 && ncomps == 3) - pixt = pixConvertRGBToLuminance(pixs); - else - pixt = pixClone(pixs); - pixGetDimensions(pixt, &w, &h, &d); - if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) { - pixDestroy(&pixt); - return (FPIX *)ERROR_PTR("invalid depth", procName, NULL); - } - - if ((fpixd = fpixCreate(w, h)) == NULL) { - pixDestroy(&pixt); - return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); - } - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - datad = fpixGetData(fpixd); - wpld = fpixGetWpl(fpixd); - for (i = 0; i < h; i++) { - linet = datat + i * wplt; - lined = datad + i * wpld; - if (d == 1) { - for (j = 0; j < w; j++) { - val = GET_DATA_BIT(linet, j); - lined[j] = (l_float32)val; - } - } else if (d == 2) { - for (j = 0; j < w; j++) { - val = GET_DATA_DIBIT(linet, j); - lined[j] = (l_float32)val; - } - } else if (d == 4) { - for (j = 0; j < w; j++) { - val = GET_DATA_QBIT(linet, j); - lined[j] = (l_float32)val; - } - } else if (d == 8) { - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(linet, j); - lined[j] = (l_float32)val; - } - } else if (d == 16) { - for (j = 0; j < w; j++) { - val = GET_DATA_TWO_BYTES(linet, j); - lined[j] = (l_float32)val; - } - } else { /* d == 32 */ - for (j = 0; j < w; j++) { - uval = GET_DATA_FOUR_BYTES(linet, j); - lined[j] = (l_float32)uval; - } - } - } - - pixDestroy(&pixt); - return fpixd; -} - - -/*! - * \brief pixConvertToDPix() - * - * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp - * \param[in] ncomps number of components: 3 for RGB, 1 otherwise - * \return dpix, or NULL on error - * - *
- * Notes: - * (1) If colormapped, remove to grayscale. - * (2) If 32 bpp and %ncomps == 3, this is RGB; convert to luminance. - * In all other cases the src image is treated as having a single - * component of pixel values. - *- */ -DPIX * -pixConvertToDPix(PIX *pixs, - l_int32 ncomps) -{ -l_int32 w, h, d, i, j, val, wplt, wpld; -l_uint32 uval; -l_uint32 *datat, *linet; -l_float64 *datad, *lined; -PIX *pixt; -DPIX *dpixd; - - PROCNAME("pixConvertToDPix"); - - if (!pixs) - return (DPIX *)ERROR_PTR("pixs not defined", procName, NULL); - - /* Convert to a single component */ - if (pixGetColormap(pixs)) - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - else if (pixGetDepth(pixs) == 32 && ncomps == 3) - pixt = pixConvertRGBToLuminance(pixs); - else - pixt = pixClone(pixs); - pixGetDimensions(pixt, &w, &h, &d); - if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) { - pixDestroy(&pixt); - return (DPIX *)ERROR_PTR("invalid depth", procName, NULL); - } - - if ((dpixd = dpixCreate(w, h)) == NULL) { - pixDestroy(&pixt); - return (DPIX *)ERROR_PTR("dpixd not made", procName, NULL); - } - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - datad = dpixGetData(dpixd); - wpld = dpixGetWpl(dpixd); - for (i = 0; i < h; i++) { - linet = datat + i * wplt; - lined = datad + i * wpld; - if (d == 1) { - for (j = 0; j < w; j++) { - val = GET_DATA_BIT(linet, j); - lined[j] = (l_float64)val; - } - } else if (d == 2) { - for (j = 0; j < w; j++) { - val = GET_DATA_DIBIT(linet, j); - lined[j] = (l_float64)val; - } - } else if (d == 4) { - for (j = 0; j < w; j++) { - val = GET_DATA_QBIT(linet, j); - lined[j] = (l_float64)val; - } - } else if (d == 8) { - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(linet, j); - lined[j] = (l_float64)val; - } - } else if (d == 16) { - for (j = 0; j < w; j++) { - val = GET_DATA_TWO_BYTES(linet, j); - lined[j] = (l_float64)val; - } - } else { /* d == 32 */ - for (j = 0; j < w; j++) { - uval = GET_DATA_FOUR_BYTES(linet, j); - lined[j] = (l_float64)uval; - } - } - } - - pixDestroy(&pixt); - return dpixd; -} - - -/*! - * \brief fpixConvertToPix() - * - * \param[in] fpixs - * \param[in] outdepth 0, 8, 16 or 32 bpp - * \param[in] negvals L_CLIP_TO_ZERO, L_TAKE_ABSVAL - * \param[in] errorflag 1 to output error stats; 0 otherwise - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Use %outdepth = 0 to programmatically determine the - * output depth. If no values are greater than 255, - * it will set outdepth = 8; otherwise to 16 or 32. - * (2) Because we are converting a float to an unsigned int - * with a specified dynamic range (8, 16 or 32 bits), errors - * can occur. If errorflag == TRUE, output the number - * of values out of range, both negative and positive. - * (3) If a pixel value is positive and out of range, clip to - * the maximum value represented at the outdepth of 8, 16 - * or 32 bits. - *- */ -PIX * -fpixConvertToPix(FPIX *fpixs, - l_int32 outdepth, - l_int32 negvals, - l_int32 errorflag) -{ -l_int32 w, h, i, j, wpls, wpld; -l_uint32 vald, maxval; -l_float32 val; -l_float32 *datas, *lines; -l_uint32 *datad, *lined; -PIX *pixd; - - PROCNAME("fpixConvertToPix"); - - if (!fpixs) - return (PIX *)ERROR_PTR("fpixs not defined", procName, NULL); - if (negvals != L_CLIP_TO_ZERO && negvals != L_TAKE_ABSVAL) - return (PIX *)ERROR_PTR("invalid negvals", procName, NULL); - if (outdepth != 0 && outdepth != 8 && outdepth != 16 && outdepth != 32) - return (PIX *)ERROR_PTR("outdepth not in {0,8,16,32}", procName, NULL); - - fpixGetDimensions(fpixs, &w, &h); - datas = fpixGetData(fpixs); - wpls = fpixGetWpl(fpixs); - - /* Adaptive determination of output depth */ - if (outdepth == 0) { - outdepth = 8; - for (i = 0; i < h && outdepth < 32; i++) { - lines = datas + i * wpls; - for (j = 0; j < w && outdepth < 32; j++) { - if (lines[j] > 65535.5) - outdepth = 32; - else if (lines[j] > 255.5) - outdepth = 16; - } - } - } - if (outdepth == 8) - maxval = 0xff; - else if (outdepth == 16) - maxval = 0xffff; - else /* outdepth == 32 */ - maxval = 0xffffffff; - - /* Gather statistics if %errorflag = TRUE */ - if (errorflag) { - l_int32 negs = 0; - l_int32 overvals = 0; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) { - val = lines[j]; - if (val < 0.0) - negs++; - else if (val > maxval) - overvals++; - } - } - if (negs > 0) - L_ERROR("Number of negative values: %d\n", procName, negs); - if (overvals > 0) - L_ERROR("Number of too-large values: %d\n", procName, overvals); - } - - /* Make the pix and convert the data */ - if ((pixd = pixCreate(w, h, outdepth)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = lines[j]; - if (val >= 0.0) - vald = (l_uint32)(val + 0.5); - else if (negvals == L_CLIP_TO_ZERO) /* and val < 0.0 */ - vald = 0; - else - vald = (l_uint32)(-val + 0.5); - if (vald > maxval) - vald = maxval; - - if (outdepth == 8) - SET_DATA_BYTE(lined, j, vald); - else if (outdepth == 16) - SET_DATA_TWO_BYTES(lined, j, vald); - else /* outdepth == 32 */ - SET_DATA_FOUR_BYTES(lined, j, vald); - } - } - - return pixd; -} - - -/*! - * \brief fpixDisplayMaxDynamicRange() - * - * \param[in] fpixs - * \return pixd 8 bpp, or NULL on error - */ -PIX * -fpixDisplayMaxDynamicRange(FPIX *fpixs) -{ -l_uint8 dval; -l_int32 i, j, w, h, wpls, wpld; -l_float32 factor, sval, maxval; -l_float32 *lines, *datas; -l_uint32 *lined, *datad; -PIX *pixd; - - PROCNAME("fpixDisplayMaxDynamicRange"); - - if (!fpixs) - return (PIX *)ERROR_PTR("fpixs not defined", procName, NULL); - - fpixGetDimensions(fpixs, &w, &h); - datas = fpixGetData(fpixs); - wpls = fpixGetWpl(fpixs); - - maxval = 0.0; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) { - sval = *(lines + j); - if (sval > maxval) - maxval = sval; - } - } - - pixd = pixCreate(w, h, 8); - if (maxval == 0.0) - return pixd; /* all pixels are 0 */ - - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - factor = 255. / maxval; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - sval = *(lines + j); - if (sval < 0.0) sval = 0.0; - dval = (l_uint8)(factor * sval + 0.5); - SET_DATA_BYTE(lined, j, dval); - } - } - - return pixd; -} - - -/*! - * \brief fpixConvertToDPix() - * - * \param[in] fpix - * \return dpix, or NULL on error - */ -DPIX * -fpixConvertToDPix(FPIX *fpix) -{ -l_int32 w, h, i, j, wpls, wpld; -l_float32 val; -l_float32 *datas, *lines; -l_float64 *datad, *lined; -DPIX *dpix; - - PROCNAME("fpixConvertToDPix"); - - if (!fpix) - return (DPIX *)ERROR_PTR("fpix not defined", procName, NULL); - - fpixGetDimensions(fpix, &w, &h); - if ((dpix = dpixCreate(w, h)) == NULL) - return (DPIX *)ERROR_PTR("dpix not made", procName, NULL); - - datas = fpixGetData(fpix); - datad = dpixGetData(dpix); - wpls = fpixGetWpl(fpix); - wpld = dpixGetWpl(dpix); /* 8 byte words */ - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = lines[j]; - lined[j] = val; - } - } - - return dpix; -} - - -/*! - * \brief dpixConvertToPix() - * - * \param[in] dpixs - * \param[in] outdepth 0, 8, 16 or 32 bpp - * \param[in] negvals L_CLIP_TO_ZERO, L_TAKE_ABSVAL - * \param[in] errorflag 1 to output error stats; 0 otherwise - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Use %outdepth = 0 to programmatically determine the - * output depth. If no values are greater than 255, - * it will set outdepth = 8; otherwise to 16 or 32. - * (2) Because we are converting a float to an unsigned int - * with a specified dynamic range (8, 16 or 32 bits), errors - * can occur. If errorflag == TRUE, output the number - * of values out of range, both negative and positive. - * (3) If a pixel value is positive and out of range, clip to - * the maximum value represented at the outdepth of 8, 16 - * or 32 bits. - *- */ -PIX * -dpixConvertToPix(DPIX *dpixs, - l_int32 outdepth, - l_int32 negvals, - l_int32 errorflag) -{ -l_int32 w, h, i, j, wpls, wpld, maxval; -l_uint32 vald; -l_float64 val; -l_float64 *datas, *lines; -l_uint32 *datad, *lined; -PIX *pixd; - - PROCNAME("dpixConvertToPix"); - - if (!dpixs) - return (PIX *)ERROR_PTR("dpixs not defined", procName, NULL); - if (negvals != L_CLIP_TO_ZERO && negvals != L_TAKE_ABSVAL) - return (PIX *)ERROR_PTR("invalid negvals", procName, NULL); - if (outdepth != 0 && outdepth != 8 && outdepth != 16 && outdepth != 32) - return (PIX *)ERROR_PTR("outdepth not in {0,8,16,32}", procName, NULL); - - dpixGetDimensions(dpixs, &w, &h); - datas = dpixGetData(dpixs); - wpls = dpixGetWpl(dpixs); - - /* Adaptive determination of output depth */ - if (outdepth == 0) { - outdepth = 8; - for (i = 0; i < h && outdepth < 32; i++) { - lines = datas + i * wpls; - for (j = 0; j < w && outdepth < 32; j++) { - if (lines[j] > 65535.5) - outdepth = 32; - else if (lines[j] > 255.5) - outdepth = 16; - } - } - } - maxval = 0xff; - if (outdepth == 16) - maxval = 0xffff; - else /* outdepth == 32 */ - maxval = 0xffffffff; - - /* Gather statistics if %errorflag = TRUE */ - if (errorflag) { - l_int32 negs = 0; - l_int32 overvals = 0; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) { - val = lines[j]; - if (val < 0.0) - negs++; - else if (val > maxval) - overvals++; - } - } - if (negs > 0) - L_ERROR("Number of negative values: %d\n", procName, negs); - if (overvals > 0) - L_ERROR("Number of too-large values: %d\n", procName, overvals); - } - - /* Make the pix and convert the data */ - if ((pixd = pixCreate(w, h, outdepth)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = lines[j]; - if (val >= 0.0) { - vald = (l_uint32)(val + 0.5); - } else { /* val < 0.0 */ - if (negvals == L_CLIP_TO_ZERO) - vald = 0; - else - vald = (l_uint32)(-val + 0.5); - } - if (vald > maxval) - vald = maxval; - if (outdepth == 8) - SET_DATA_BYTE(lined, j, vald); - else if (outdepth == 16) - SET_DATA_TWO_BYTES(lined, j, vald); - else /* outdepth == 32 */ - SET_DATA_FOUR_BYTES(lined, j, vald); - } - } - - return pixd; -} - - -/*! - * \brief dpixConvertToFPix() - * - * \param[in] dpix - * \return fpix, or NULL on error - */ -FPIX * -dpixConvertToFPix(DPIX *dpix) -{ -l_int32 w, h, i, j, wpls, wpld; -l_float64 val; -l_float32 *datad, *lined; -l_float64 *datas, *lines; -FPIX *fpix; - - PROCNAME("dpixConvertToFPix"); - - if (!dpix) - return (FPIX *)ERROR_PTR("dpix not defined", procName, NULL); - - dpixGetDimensions(dpix, &w, &h); - if ((fpix = fpixCreate(w, h)) == NULL) - return (FPIX *)ERROR_PTR("fpix not made", procName, NULL); - - datas = dpixGetData(dpix); - datad = fpixGetData(fpix); - wpls = dpixGetWpl(dpix); /* 8 byte words */ - wpld = fpixGetWpl(fpix); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = lines[j]; - lined[j] = (l_float32)val; - } - } - - return fpix; -} - - - -/*--------------------------------------------------------------------* - * Min/max value * - *--------------------------------------------------------------------*/ -/*! - * \brief fpixGetMin() - * - * \param[in] fpix - * \param[out] pminval [optional] min value - * \param[out] pxminloc [optional] x location of min - * \param[out] pyminloc [optional] y location of min - * \return 0 if OK; 1 on error - */ -l_ok -fpixGetMin(FPIX *fpix, - l_float32 *pminval, - l_int32 *pxminloc, - l_int32 *pyminloc) -{ -l_int32 i, j, w, h, wpl, xminloc, yminloc; -l_float32 *data, *line; -l_float32 minval; - - PROCNAME("fpixGetMin"); - - if (!pminval && !pxminloc && !pyminloc) - return ERROR_INT("no return val requested", procName, 1); - if (pminval) *pminval = 0.0; - if (pxminloc) *pxminloc = 0; - if (pyminloc) *pyminloc = 0; - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - - minval = +1.0e20; - xminloc = 0; - yminloc = 0; - fpixGetDimensions(fpix, &w, &h); - data = fpixGetData(fpix); - wpl = fpixGetWpl(fpix); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - if (line[j] < minval) { - minval = line[j]; - xminloc = j; - yminloc = i; - } - } - } - - if (pminval) *pminval = minval; - if (pxminloc) *pxminloc = xminloc; - if (pyminloc) *pyminloc = yminloc; - return 0; -} - - -/*! - * \brief fpixGetMax() - * - * \param[in] fpix - * \param[out] pmaxval [optional] max value - * \param[out] pxmaxloc [optional] x location of max - * \param[out] pymaxloc [optional] y location of max - * \return 0 if OK; 1 on error - */ -l_ok -fpixGetMax(FPIX *fpix, - l_float32 *pmaxval, - l_int32 *pxmaxloc, - l_int32 *pymaxloc) -{ -l_int32 i, j, w, h, wpl, xmaxloc, ymaxloc; -l_float32 *data, *line; -l_float32 maxval; - - PROCNAME("fpixGetMax"); - - if (!pmaxval && !pxmaxloc && !pymaxloc) - return ERROR_INT("no return val requested", procName, 1); - if (pmaxval) *pmaxval = 0.0; - if (pxmaxloc) *pxmaxloc = 0; - if (pymaxloc) *pymaxloc = 0; - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - - maxval = -1.0e20; - xmaxloc = 0; - ymaxloc = 0; - fpixGetDimensions(fpix, &w, &h); - data = fpixGetData(fpix); - wpl = fpixGetWpl(fpix); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - if (line[j] > maxval) { - maxval = line[j]; - xmaxloc = j; - ymaxloc = i; - } - } - } - - if (pmaxval) *pmaxval = maxval; - if (pxmaxloc) *pxmaxloc = xmaxloc; - if (pymaxloc) *pymaxloc = ymaxloc; - return 0; -} - - -/*! - * \brief dpixGetMin() - * - * \param[in] dpix - * \param[out] pminval [optional] min value - * \param[out] pxminloc [optional] x location of min - * \param[out] pyminloc [optional] y location of min - * \return 0 if OK; 1 on error - */ -l_ok -dpixGetMin(DPIX *dpix, - l_float64 *pminval, - l_int32 *pxminloc, - l_int32 *pyminloc) -{ -l_int32 i, j, w, h, wpl, xminloc, yminloc; -l_float64 *data, *line; -l_float64 minval; - - PROCNAME("dpixGetMin"); - - if (!pminval && !pxminloc && !pyminloc) - return ERROR_INT("no return val requested", procName, 1); - if (pminval) *pminval = 0.0; - if (pxminloc) *pxminloc = 0; - if (pyminloc) *pyminloc = 0; - if (!dpix) - return ERROR_INT("dpix not defined", procName, 1); - - minval = +1.0e300; - xminloc = 0; - yminloc = 0; - dpixGetDimensions(dpix, &w, &h); - data = dpixGetData(dpix); - wpl = dpixGetWpl(dpix); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - if (line[j] < minval) { - minval = line[j]; - xminloc = j; - yminloc = i; - } - } - } - - if (pminval) *pminval = minval; - if (pxminloc) *pxminloc = xminloc; - if (pyminloc) *pyminloc = yminloc; - return 0; -} - - -/*! - * \brief dpixGetMax() - * - * \param[in] dpix - * \param[out] pmaxval [optional] max value - * \param[out] pxmaxloc [optional] x location of max - * \param[out] pymaxloc [optional] y location of max - * \return 0 if OK; 1 on error - */ -l_ok -dpixGetMax(DPIX *dpix, - l_float64 *pmaxval, - l_int32 *pxmaxloc, - l_int32 *pymaxloc) -{ -l_int32 i, j, w, h, wpl, xmaxloc, ymaxloc; -l_float64 *data, *line; -l_float64 maxval; - - PROCNAME("dpixGetMax"); - - if (!pmaxval && !pxmaxloc && !pymaxloc) - return ERROR_INT("no return val requested", procName, 1); - if (pmaxval) *pmaxval = 0.0; - if (pxmaxloc) *pxmaxloc = 0; - if (pymaxloc) *pymaxloc = 0; - if (!dpix) - return ERROR_INT("dpix not defined", procName, 1); - - maxval = -1.0e20; - xmaxloc = 0; - ymaxloc = 0; - dpixGetDimensions(dpix, &w, &h); - data = dpixGetData(dpix); - wpl = dpixGetWpl(dpix); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - if (line[j] > maxval) { - maxval = line[j]; - xmaxloc = j; - ymaxloc = i; - } - } - } - - if (pmaxval) *pmaxval = maxval; - if (pxmaxloc) *pxmaxloc = xmaxloc; - if (pymaxloc) *pymaxloc = ymaxloc; - return 0; -} - - -/*--------------------------------------------------------------------* - * Special integer scaling * - *--------------------------------------------------------------------*/ -/*! - * \brief fpixScaleByInteger() - * - * \param[in] fpixs typically low resolution - * \param[in] factor integer scaling factor - * \return fpixd interpolated result, or NULL on error - * - *
- * Notes: - * (1) The width wd of fpixd is related to ws of fpixs by: - * wd = factor * (ws - 1) + 1 (and ditto for the height) - * We avoid special-casing boundary pixels in the interpolation - * by constructing fpixd by inserting (factor - 1) interpolated - * pixels between each pixel in fpixs. Then - * wd = ws + (ws - 1) * (factor - 1) (same as above) - * This also has the advantage that if we subsample by %factor, - * throwing out all the interpolated pixels, we regain the - * original low resolution fpix. - *- */ -FPIX * -fpixScaleByInteger(FPIX *fpixs, - l_int32 factor) -{ -l_int32 i, j, k, m, ws, hs, wd, hd, wpls, wpld; -l_float32 val0, val1, val2, val3; -l_float32 *datas, *datad, *lines, *lined, *fract; -FPIX *fpixd; - - PROCNAME("fpixScaleByInteger"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - - fpixGetDimensions(fpixs, &ws, &hs); - wd = factor * (ws - 1) + 1; - hd = factor * (hs - 1) + 1; - fpixd = fpixCreate(wd, hd); - datas = fpixGetData(fpixs); - datad = fpixGetData(fpixd); - wpls = fpixGetWpl(fpixs); - wpld = fpixGetWpl(fpixd); - fract = (l_float32 *)LEPT_CALLOC(factor, sizeof(l_float32)); - for (i = 0; i < factor; i++) - fract[i] = i / (l_float32)factor; - for (i = 0; i < hs - 1; i++) { - lines = datas + i * wpls; - for (j = 0; j < ws - 1; j++) { - val0 = lines[j]; - val1 = lines[j + 1]; - val2 = lines[wpls + j]; - val3 = lines[wpls + j + 1]; - for (k = 0; k < factor; k++) { /* rows of sub-block */ - lined = datad + (i * factor + k) * wpld; - for (m = 0; m < factor; m++) { /* cols of sub-block */ - lined[j * factor + m] = - val0 * (1.0 - fract[m]) * (1.0 - fract[k]) + - val1 * fract[m] * (1.0 - fract[k]) + - val2 * (1.0 - fract[m]) * fract[k] + - val3 * fract[m] * fract[k]; - } - } - } - } - - /* Do the right-most column of fpixd, skipping LR corner */ - for (i = 0; i < hs - 1; i++) { - lines = datas + i * wpls; - val0 = lines[ws - 1]; - val1 = lines[wpls + ws - 1]; - for (k = 0; k < factor; k++) { - lined = datad + (i * factor + k) * wpld; - lined[wd - 1] = val0 * (1.0 - fract[k]) + val1 * fract[k]; - } - } - - /* Do the bottom-most row of fpixd */ - lines = datas + (hs - 1) * wpls; - lined = datad + (hd - 1) * wpld; - for (j = 0; j < ws - 1; j++) { - val0 = lines[j]; - val1 = lines[j + 1]; - for (m = 0; m < factor; m++) - lined[j * factor + m] = val0 * (1.0 - fract[m]) + val1 * fract[m]; - lined[wd - 1] = lines[ws - 1]; /* LR corner */ - } - - LEPT_FREE(fract); - return fpixd; -} - - -/*! - * \brief dpixScaleByInteger() - * - * \param[in] dpixs typically low resolution - * \param[in] factor integer scaling factor - * \return dpixd interpolated result, or NULL on error - * - *
- * Notes: - * (1) The width wd of dpixd is related to ws of dpixs by: - * wd = factor * (ws - 1) + 1 (and ditto for the height) - * We avoid special-casing boundary pixels in the interpolation - * by constructing fpixd by inserting (factor - 1) interpolated - * pixels between each pixel in fpixs. Then - * wd = ws + (ws - 1) * (factor - 1) (same as above) - * This also has the advantage that if we subsample by %factor, - * throwing out all the interpolated pixels, we regain the - * original low resolution dpix. - *- */ -DPIX * -dpixScaleByInteger(DPIX *dpixs, - l_int32 factor) -{ -l_int32 i, j, k, m, ws, hs, wd, hd, wpls, wpld; -l_float64 val0, val1, val2, val3; -l_float64 *datas, *datad, *lines, *lined, *fract; -DPIX *dpixd; - - PROCNAME("dpixScaleByInteger"); - - if (!dpixs) - return (DPIX *)ERROR_PTR("dpixs not defined", procName, NULL); - - dpixGetDimensions(dpixs, &ws, &hs); - wd = factor * (ws - 1) + 1; - hd = factor * (hs - 1) + 1; - dpixd = dpixCreate(wd, hd); - datas = dpixGetData(dpixs); - datad = dpixGetData(dpixd); - wpls = dpixGetWpl(dpixs); - wpld = dpixGetWpl(dpixd); - fract = (l_float64 *)LEPT_CALLOC(factor, sizeof(l_float64)); - for (i = 0; i < factor; i++) - fract[i] = i / (l_float64)factor; - for (i = 0; i < hs - 1; i++) { - lines = datas + i * wpls; - for (j = 0; j < ws - 1; j++) { - val0 = lines[j]; - val1 = lines[j + 1]; - val2 = lines[wpls + j]; - val3 = lines[wpls + j + 1]; - for (k = 0; k < factor; k++) { /* rows of sub-block */ - lined = datad + (i * factor + k) * wpld; - for (m = 0; m < factor; m++) { /* cols of sub-block */ - lined[j * factor + m] = - val0 * (1.0 - fract[m]) * (1.0 - fract[k]) + - val1 * fract[m] * (1.0 - fract[k]) + - val2 * (1.0 - fract[m]) * fract[k] + - val3 * fract[m] * fract[k]; - } - } - } - } - - /* Do the right-most column of dpixd, skipping LR corner */ - for (i = 0; i < hs - 1; i++) { - lines = datas + i * wpls; - val0 = lines[ws - 1]; - val1 = lines[wpls + ws - 1]; - for (k = 0; k < factor; k++) { - lined = datad + (i * factor + k) * wpld; - lined[wd - 1] = val0 * (1.0 - fract[k]) + val1 * fract[k]; - } - } - - /* Do the bottom-most row of dpixd */ - lines = datas + (hs - 1) * wpls; - lined = datad + (hd - 1) * wpld; - for (j = 0; j < ws - 1; j++) { - val0 = lines[j]; - val1 = lines[j + 1]; - for (m = 0; m < factor; m++) - lined[j * factor + m] = val0 * (1.0 - fract[m]) + val1 * fract[m]; - lined[wd - 1] = lines[ws - 1]; /* LR corner */ - } - - LEPT_FREE(fract); - return dpixd; -} - - -/*--------------------------------------------------------------------* - * Arithmetic operations * - *--------------------------------------------------------------------*/ -/*! - * \brief fpixLinearCombination() - * - * \param[in] fpixd [optional] this can be null, equal to fpixs1, or - * different from fpixs1 - * \param[in] fpixs1 can be equal to fpixd - * \param[in] fpixs2 - * \param[in] a, b multiplication factors on fpixs1 and fpixs2, rsp. - * \return fpixd always - * - *
- * Notes: - * (1) Computes pixelwise linear combination: a * src1 + b * src2 - * (2) Alignment is to UL corner. - * (3) There are 3 cases. The result can go to a new dest, - * in-place to fpixs1, or to an existing input dest: - * * fpixd == null: (src1 + src2) --> new fpixd - * * fpixd == fpixs1: (src1 + src2) --> src1 (in-place) - * * fpixd != fpixs1: (src1 + src2) --> input fpixd - * (4) fpixs2 must be different from both fpixd and fpixs1. - *- */ -FPIX * -fpixLinearCombination(FPIX *fpixd, - FPIX *fpixs1, - FPIX *fpixs2, - l_float32 a, - l_float32 b) -{ -l_int32 i, j, ws, hs, w, h, wpls, wpld; -l_float32 *datas, *datad, *lines, *lined; - - PROCNAME("fpixLinearCombination"); - - if (!fpixs1) - return (FPIX *)ERROR_PTR("fpixs1 not defined", procName, fpixd); - if (!fpixs2) - return (FPIX *)ERROR_PTR("fpixs2 not defined", procName, fpixd); - if (fpixs1 == fpixs2) - return (FPIX *)ERROR_PTR("fpixs1 == fpixs2", procName, fpixd); - if (fpixs2 == fpixd) - return (FPIX *)ERROR_PTR("fpixs2 == fpixd", procName, fpixd); - - if (fpixs1 != fpixd) - fpixd = fpixCopy(fpixd, fpixs1); - - datas = fpixGetData(fpixs2); - datad = fpixGetData(fpixd); - wpls = fpixGetWpl(fpixs2); - wpld = fpixGetWpl(fpixd); - fpixGetDimensions(fpixs2, &ws, &hs); - fpixGetDimensions(fpixd, &w, &h); - w = L_MIN(ws, w); - h = L_MIN(hs, h); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) - lined[j] = a * lined[j] + b * lines[j]; - } - - return fpixd; -} - - -/*! - * \brief fpixAddMultConstant() - * - * \param[in] fpix - * \param[in] addc use 0.0 to skip the operation - * \param[in] multc use 1.0 to skip the operation - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is an in-place operation. - * (2) It can be used to multiply each pixel by a constant, - * and also to add a constant to each pixel. Multiplication - * is done first. - *- */ -l_ok -fpixAddMultConstant(FPIX *fpix, - l_float32 addc, - l_float32 multc) -{ -l_int32 i, j, w, h, wpl; -l_float32 *line, *data; - - PROCNAME("fpixAddMultConstant"); - - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - - if (addc == 0.0 && multc == 1.0) - return 0; - - fpixGetDimensions(fpix, &w, &h); - data = fpixGetData(fpix); - wpl = fpixGetWpl(fpix); - for (i = 0; i < h; i++) { - line = data + i * wpl; - if (addc == 0.0) { - for (j = 0; j < w; j++) - line[j] *= multc; - } else if (multc == 1.0) { - for (j = 0; j < w; j++) - line[j] += addc; - } else { - for (j = 0; j < w; j++) { - line[j] = multc * line[j] + addc; - } - } - } - - return 0; -} - - -/*! - * \brief dpixLinearCombination() - * - * \param[in] dpixd [optional] this can be null, equal to dpixs1, or - * different from dpixs1 - * \param[in] dpixs1 can be equal to dpixd - * \param[in] dpixs2 - * \param[in] a, b multiplication factors on dpixs1 and dpixs2, rsp. - * \return dpixd always - * - *
- * Notes: - * (1) Computes pixelwise linear combination: a * src1 + b * src2 - * (2) Alignment is to UL corner. - * (3) There are 3 cases. The result can go to a new dest, - * in-place to dpixs1, or to an existing input dest: - * * dpixd == null: (src1 + src2) --> new dpixd - * * dpixd == dpixs1: (src1 + src2) --> src1 (in-place) - * * dpixd != dpixs1: (src1 + src2) --> input dpixd - * (4) dpixs2 must be different from both dpixd and dpixs1. - *- */ -DPIX * -dpixLinearCombination(DPIX *dpixd, - DPIX *dpixs1, - DPIX *dpixs2, - l_float32 a, - l_float32 b) -{ -l_int32 i, j, ws, hs, w, h, wpls, wpld; -l_float64 *datas, *datad, *lines, *lined; - - PROCNAME("dpixLinearCombination"); - - if (!dpixs1) - return (DPIX *)ERROR_PTR("dpixs1 not defined", procName, dpixd); - if (!dpixs2) - return (DPIX *)ERROR_PTR("dpixs2 not defined", procName, dpixd); - if (dpixs1 == dpixs2) - return (DPIX *)ERROR_PTR("dpixs1 == dpixs2", procName, dpixd); - if (dpixs2 == dpixd) - return (DPIX *)ERROR_PTR("dpixs2 == dpixd", procName, dpixd); - - if (dpixs1 != dpixd) - dpixd = dpixCopy(dpixd, dpixs1); - - datas = dpixGetData(dpixs2); - datad = dpixGetData(dpixd); - wpls = dpixGetWpl(dpixs2); - wpld = dpixGetWpl(dpixd); - dpixGetDimensions(dpixs2, &ws, &hs); - dpixGetDimensions(dpixd, &w, &h); - w = L_MIN(ws, w); - h = L_MIN(hs, h); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) - lined[j] = a * lined[j] + b * lines[j]; - } - - return dpixd; -} - - -/*! - * \brief dpixAddMultConstant() - * - * \param[in] dpix - * \param[in] addc use 0.0 to skip the operation - * \param[in] multc use 1.0 to skip the operation - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is an in-place operation. - * (2) It can be used to multiply each pixel by a constant, - * and also to add a constant to each pixel. Multiplication - * is done first. - *- */ -l_ok -dpixAddMultConstant(DPIX *dpix, - l_float64 addc, - l_float64 multc) -{ -l_int32 i, j, w, h, wpl; -l_float64 *line, *data; - - PROCNAME("dpixAddMultConstant"); - - if (!dpix) - return ERROR_INT("dpix not defined", procName, 1); - - if (addc == 0.0 && multc == 1.0) - return 0; - - dpixGetDimensions(dpix, &w, &h); - data = dpixGetData(dpix); - wpl = dpixGetWpl(dpix); - for (i = 0; i < h; i++) { - line = data + i * wpl; - if (addc == 0.0) { - for (j = 0; j < w; j++) - line[j] *= multc; - } else if (multc == 1.0) { - for (j = 0; j < w; j++) - line[j] += addc; - } else { - for (j = 0; j < w; j++) - line[j] = multc * line[j] + addc; - } - } - - return 0; -} - - -/*--------------------------------------------------------------------* - * Set all * - *--------------------------------------------------------------------*/ -/*! - * \brief fpixSetAllArbitrary() - * - * \param[in] fpix - * \param[in] inval to set at each pixel - * \return 0 if OK, 1 on error - */ -l_ok -fpixSetAllArbitrary(FPIX *fpix, - l_float32 inval) -{ -l_int32 i, j, w, h; -l_float32 *data, *line; - - PROCNAME("fpixSetAllArbitrary"); - - if (!fpix) - return ERROR_INT("fpix not defined", procName, 1); - - fpixGetDimensions(fpix, &w, &h); - data = fpixGetData(fpix); - for (i = 0; i < h; i++) { - line = data + i * w; - for (j = 0; j < w; j++) - *(line + j) = inval; - } - - return 0; -} - - -/*! - * \brief dpixSetAllArbitrary() - * - * \param[in] dpix - * \param[in] inval to set at each pixel - * \return 0 if OK, 1 on error - */ -l_ok -dpixSetAllArbitrary(DPIX *dpix, - l_float64 inval) -{ -l_int32 i, j, w, h; -l_float64 *data, *line; - - PROCNAME("dpixSetAllArbitrary"); - - if (!dpix) - return ERROR_INT("dpix not defined", procName, 1); - - dpixGetDimensions(dpix, &w, &h); - data = dpixGetData(dpix); - for (i = 0; i < h; i++) { - line = data + i * w; - for (j = 0; j < w; j++) - *(line + j) = inval; - } - - return 0; -} - - -/*--------------------------------------------------------------------* - * Border functions * - *--------------------------------------------------------------------*/ -/*! - * \brief fpixAddBorder() - * - * \param[in] fpixs - * \param[in] left, right, top, bot pixels on each side to be added - * \return fpixd, or NULL on error - * - *
- * Notes: - * (1) Adds border of '0' 32-bit pixels - *- */ -FPIX * -fpixAddBorder(FPIX *fpixs, - l_int32 left, - l_int32 right, - l_int32 top, - l_int32 bot) -{ -l_int32 ws, hs, wd, hd; -FPIX *fpixd; - - PROCNAME("fpixAddBorder"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - - if (left <= 0 && right <= 0 && top <= 0 && bot <= 0) - return fpixCopy(NULL, fpixs); - fpixGetDimensions(fpixs, &ws, &hs); - wd = ws + left + right; - hd = hs + top + bot; - if ((fpixd = fpixCreate(wd, hd)) == NULL) - return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); - - fpixCopyResolution(fpixd, fpixs); - fpixRasterop(fpixd, left, top, ws, hs, fpixs, 0, 0); - return fpixd; -} - - -/*! - * \brief fpixRemoveBorder() - * - * \param[in] fpixs - * \param[in] left, right, top, bot pixels on each side to be removed - * \return fpixd, or NULL on error - */ -FPIX * -fpixRemoveBorder(FPIX *fpixs, - l_int32 left, - l_int32 right, - l_int32 top, - l_int32 bot) -{ -l_int32 ws, hs, wd, hd; -FPIX *fpixd; - - PROCNAME("fpixRemoveBorder"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - - if (left <= 0 && right <= 0 && top <= 0 && bot <= 0) - return fpixCopy(NULL, fpixs); - fpixGetDimensions(fpixs, &ws, &hs); - wd = ws - left - right; - hd = hs - top - bot; - if (wd <= 0 || hd <= 0) - return (FPIX *)ERROR_PTR("width & height not both > 0", procName, NULL); - if ((fpixd = fpixCreate(wd, hd)) == NULL) - return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); - - fpixCopyResolution(fpixd, fpixs); - fpixRasterop(fpixd, 0, 0, wd, hd, fpixs, left, top); - return fpixd; -} - - - -/*! - * \brief fpixAddMirroredBorder() - * - * \param[in] fpixs - * \param[in] left, right, top, bot pixels on each side to be added - * \return fpixd, or NULL on error - * - *
- * Notes: - * (1) See pixAddMirroredBorder() for situations of usage. - *- */ -FPIX * -fpixAddMirroredBorder(FPIX *fpixs, - l_int32 left, - l_int32 right, - l_int32 top, - l_int32 bot) -{ -l_int32 i, j, w, h; -FPIX *fpixd; - - PROCNAME("fpixAddMirroredBorder"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - - fpixd = fpixAddBorder(fpixs, left, right, top, bot); - fpixGetDimensions(fpixs, &w, &h); - for (j = 0; j < left; j++) - fpixRasterop(fpixd, left - 1 - j, top, 1, h, - fpixd, left + j, top); - for (j = 0; j < right; j++) - fpixRasterop(fpixd, left + w + j, top, 1, h, - fpixd, left + w - 1 - j, top); - for (i = 0; i < top; i++) - fpixRasterop(fpixd, 0, top - 1 - i, left + w + right, 1, - fpixd, 0, top + i); - for (i = 0; i < bot; i++) - fpixRasterop(fpixd, 0, top + h + i, left + w + right, 1, - fpixd, 0, top + h - 1 - i); - - return fpixd; -} - - -/*! - * \brief fpixAddContinuedBorder() - * - * \param[in] fpixs - * \param[in] left, right, top, bot pixels on each side to be added - * \return fpixd, or NULL on error - * - *
- * Notes: - * (1) This adds pixels on each side whose values are equal to - * the value on the closest boundary pixel. - *- */ -FPIX * -fpixAddContinuedBorder(FPIX *fpixs, - l_int32 left, - l_int32 right, - l_int32 top, - l_int32 bot) -{ -l_int32 i, j, w, h; -FPIX *fpixd; - - PROCNAME("fpixAddContinuedBorder"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - - fpixd = fpixAddBorder(fpixs, left, right, top, bot); - fpixGetDimensions(fpixs, &w, &h); - for (j = 0; j < left; j++) - fpixRasterop(fpixd, j, top, 1, h, fpixd, left, top); - for (j = 0; j < right; j++) - fpixRasterop(fpixd, left + w + j, top, 1, h, fpixd, left + w - 1, top); - for (i = 0; i < top; i++) - fpixRasterop(fpixd, 0, i, left + w + right, 1, fpixd, 0, top); - for (i = 0; i < bot; i++) - fpixRasterop(fpixd, 0, top + h + i, left + w + right, 1, - fpixd, 0, top + h - 1); - - return fpixd; -} - - -/*! - * \brief fpixAddSlopeBorder() - * - * \param[in] fpixs - * \param[in] left, right, top, bot pixels on each side to be added - * \return fpixd, or NULL on error - * - *
- * Notes: - * (1) This adds pixels on each side whose values have a normal - * derivative equal to the normal derivative at the boundary - * of fpixs. - *- */ -FPIX * -fpixAddSlopeBorder(FPIX *fpixs, - l_int32 left, - l_int32 right, - l_int32 top, - l_int32 bot) -{ -l_int32 i, j, w, h, fullw, fullh; -l_float32 val1, val2, del; -FPIX *fpixd; - - PROCNAME("fpixAddSlopeBorder"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - - fpixd = fpixAddBorder(fpixs, left, right, top, bot); - fpixGetDimensions(fpixs, &w, &h); - - /* Left */ - for (i = top; i < top + h; i++) { - fpixGetPixel(fpixd, left, i, &val1); - fpixGetPixel(fpixd, left + 1, i, &val2); - del = val1 - val2; - for (j = 0; j < left; j++) - fpixSetPixel(fpixd, j, i, val1 + del * (left - j)); - } - - /* Right */ - fullw = left + w + right; - for (i = top; i < top + h; i++) { - fpixGetPixel(fpixd, left + w - 1, i, &val1); - fpixGetPixel(fpixd, left + w - 2, i, &val2); - del = val1 - val2; - for (j = left + w; j < fullw; j++) - fpixSetPixel(fpixd, j, i, val1 + del * (j - left - w + 1)); - } - - /* Top */ - for (j = 0; j < fullw; j++) { - fpixGetPixel(fpixd, j, top, &val1); - fpixGetPixel(fpixd, j, top + 1, &val2); - del = val1 - val2; - for (i = 0; i < top; i++) - fpixSetPixel(fpixd, j, i, val1 + del * (top - i)); - } - - /* Bottom */ - fullh = top + h + bot; - for (j = 0; j < fullw; j++) { - fpixGetPixel(fpixd, j, top + h - 1, &val1); - fpixGetPixel(fpixd, j, top + h - 2, &val2); - del = val1 - val2; - for (i = top + h; i < fullh; i++) - fpixSetPixel(fpixd, j, i, val1 + del * (i - top - h + 1)); - } - - return fpixd; -} - - -/*--------------------------------------------------------------------* - * Simple rasterop * - *--------------------------------------------------------------------*/ -/*! - * \brief fpixRasterop() - * - * \param[in] fpixd dest fpix - * \param[in] dx x val of UL corner of dest rectangle - * \param[in] dy y val of UL corner of dest rectangle - * \param[in] dw width of dest rectangle - * \param[in] dh height of dest rectangle - * \param[in] fpixs src fpix - * \param[in] sx x val of UL corner of src rectangle - * \param[in] sy y val of UL corner of src rectangle - * \return 0 if OK; 1 on error. - * - *
- * Notes: - * (1) This is similar in structure to pixRasterop(), except - * it only allows copying from the source into the destination. - * For that reason, no op code is necessary. Additionally, - * all pixels are 32 bit words (float values), which makes - * the copy very simple. - * (2) Clipping of both src and dest fpix are done automatically. - * (3) This allows in-place copying, without checking to see if - * the result is valid: use for in-place with caution! - *- */ -l_ok -fpixRasterop(FPIX *fpixd, - l_int32 dx, - l_int32 dy, - l_int32 dw, - l_int32 dh, - FPIX *fpixs, - l_int32 sx, - l_int32 sy) -{ -l_int32 fsw, fsh, fdw, fdh, dhangw, shangw, dhangh, shangh; -l_int32 i, j, wpls, wpld; -l_float32 *datas, *datad, *lines, *lined; - - PROCNAME("fpixRasterop"); - - if (!fpixs) - return ERROR_INT("fpixs not defined", procName, 1); - if (!fpixd) - return ERROR_INT("fpixd not defined", procName, 1); - - /* -------------------------------------------------------- * - * Clip to maximum rectangle with both src and dest * - * -------------------------------------------------------- */ - fpixGetDimensions(fpixs, &fsw, &fsh); - fpixGetDimensions(fpixd, &fdw, &fdh); - - /* First clip horizontally (sx, dx, dw) */ - if (dx < 0) { - sx -= dx; /* increase sx */ - dw += dx; /* reduce dw */ - dx = 0; - } - if (sx < 0) { - dx -= sx; /* increase dx */ - dw += sx; /* reduce dw */ - sx = 0; - } - dhangw = dx + dw - fdw; /* rect overhang of dest to right */ - if (dhangw > 0) - dw -= dhangw; /* reduce dw */ - shangw = sx + dw - fsw; /* rect overhang of src to right */ - if (shangw > 0) - dw -= shangw; /* reduce dw */ - - /* Then clip vertically (sy, dy, dh) */ - if (dy < 0) { - sy -= dy; /* increase sy */ - dh += dy; /* reduce dh */ - dy = 0; - } - if (sy < 0) { - dy -= sy; /* increase dy */ - dh += sy; /* reduce dh */ - sy = 0; - } - dhangh = dy + dh - fdh; /* rect overhang of dest below */ - if (dhangh > 0) - dh -= dhangh; /* reduce dh */ - shangh = sy + dh - fsh; /* rect overhang of src below */ - if (shangh > 0) - dh -= shangh; /* reduce dh */ - - /* if clipped entirely, quit */ - if ((dw <= 0) || (dh <= 0)) - return 0; - - /* -------------------------------------------------------- * - * Copy block of data * - * -------------------------------------------------------- */ - datas = fpixGetData(fpixs); - datad = fpixGetData(fpixd); - wpls = fpixGetWpl(fpixs); - wpld = fpixGetWpl(fpixd); - datas += sy * wpls + sx; /* at UL corner of block */ - datad += dy * wpld + dx; /* at UL corner of block */ - for (i = 0; i < dh; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < dw; j++) { - *lined = *lines; - lines++; - lined++; - } - } - - return 0; -} - - -/*--------------------------------------------------------------------* - * Rotation by multiples of 90 degrees * - *--------------------------------------------------------------------*/ -/*! - * \brief fpixRotateOrth() - * - * \param[in] fpixs - * \param[in] quads 0-3; number of 90 degree cw rotations - * \return fpixd, or NULL on error - */ -FPIX * -fpixRotateOrth(FPIX *fpixs, - l_int32 quads) -{ - PROCNAME("fpixRotateOrth"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - if (quads < 0 || quads > 3) - return (FPIX *)ERROR_PTR("quads not in {0,1,2,3}", procName, NULL); - - if (quads == 0) - return fpixCopy(NULL, fpixs); - else if (quads == 1) - return fpixRotate90(fpixs, 1); - else if (quads == 2) - return fpixRotate180(NULL, fpixs); - else /* quads == 3 */ - return fpixRotate90(fpixs, -1); -} - - -/*! - * \brief fpixRotate180() - * - * \param[in] fpixd [optional] can be null, equal to fpixs, - * or different from fpixs - * \param[in] fpixs - * \return fpixd, or NULL on error - * - *
- * Notes: - * (1) This does a 180 rotation of the image about the center, - * which is equivalent to a left-right flip about a vertical - * line through the image center, followed by a top-bottom - * flip about a horizontal line through the image center. - * (2) There are 3 cases for input: - * (a) fpixd == null (creates a new fpixd) - * (b) fpixd == fpixs (in-place operation) - * (c) fpixd != fpixs (existing fpixd) - * (3) For clarity, use these three patterns, respectively: - * (a) fpixd = fpixRotate180(NULL, fpixs); - * (b) fpixRotate180(fpixs, fpixs); - * (c) fpixRotate180(fpixd, fpixs); - *- */ -FPIX * -fpixRotate180(FPIX *fpixd, - FPIX *fpixs) -{ - PROCNAME("fpixRotate180"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - - /* Prepare pixd for in-place operation */ - if ((fpixd = fpixCopy(fpixd, fpixs)) == NULL) - return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); - - fpixFlipLR(fpixd, fpixd); - fpixFlipTB(fpixd, fpixd); - return fpixd; -} - - -/*! - * \brief fpixRotate90() - * - * \param[in] fpixs - * \param[in] direction 1 = clockwise; -1 = counter-clockwise - * \return fpixd, or NULL on error - * - *
- * Notes: - * (1) This does a 90 degree rotation of the image about the center, - * either cw or ccw, returning a new pix. - * (2) The direction must be either 1 (cw) or -1 (ccw). - *- */ -FPIX * -fpixRotate90(FPIX *fpixs, - l_int32 direction) -{ -l_int32 i, j, wd, hd, wpls, wpld; -l_float32 *datas, *datad, *lines, *lined; -FPIX *fpixd; - - PROCNAME("fpixRotate90"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - if (direction != 1 && direction != -1) - return (FPIX *)ERROR_PTR("invalid direction", procName, NULL); - - fpixGetDimensions(fpixs, &hd, &wd); - if ((fpixd = fpixCreate(wd, hd)) == NULL) - return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); - fpixCopyResolution(fpixd, fpixs); - - datas = fpixGetData(fpixs); - wpls = fpixGetWpl(fpixs); - datad = fpixGetData(fpixd); - wpld = fpixGetWpl(fpixd); - if (direction == 1) { /* clockwise */ - for (i = 0; i < hd; i++) { - lined = datad + i * wpld; - lines = datas + (wd - 1) * wpls; - for (j = 0; j < wd; j++) { - lined[j] = lines[i]; - lines -= wpls; - } - } - } else { /* ccw */ - for (i = 0; i < hd; i++) { - lined = datad + i * wpld; - lines = datas; - for (j = 0; j < wd; j++) { - lined[j] = lines[hd - 1 - i]; - lines += wpls; - } - } - } - - return fpixd; -} - - -/*! - * \brief pixFlipLR() - * - * \param[in] fpixd [optional] can be null, equal to fpixs, - * or different from fpixs - * \param[in] fpixs - * \return fpixd, or NULL on error - * - *
- * Notes: - * (1) This does a left-right flip of the image, which is - * equivalent to a rotation out of the plane about a - * vertical line through the image center. - * (2) There are 3 cases for input: - * (a) fpixd == null (creates a new fpixd) - * (b) fpixd == fpixs (in-place operation) - * (c) fpixd != fpixs (existing fpixd) - * (3) For clarity, use these three patterns, respectively: - * (a) fpixd = fpixFlipLR(NULL, fpixs); - * (b) fpixFlipLR(fpixs, fpixs); - * (c) fpixFlipLR(fpixd, fpixs); - * (4) If an existing fpixd is not the same size as fpixs, the - * image data will be reallocated. - *- */ -FPIX * -fpixFlipLR(FPIX *fpixd, - FPIX *fpixs) -{ -l_int32 i, j, w, h, wpl, bpl; -l_float32 *line, *data, *buffer; - - PROCNAME("fpixFlipLR"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - - fpixGetDimensions(fpixs, &w, &h); - - /* Prepare fpixd for in-place operation */ - if ((fpixd = fpixCopy(fpixd, fpixs)) == NULL) - return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); - - data = fpixGetData(fpixd); - wpl = fpixGetWpl(fpixd); /* 4-byte words */ - bpl = 4 * wpl; - if ((buffer = (l_float32 *)LEPT_CALLOC(wpl, sizeof(l_float32))) == NULL) { - fpixDestroy(&fpixd); - return (FPIX *)ERROR_PTR("buffer not made", procName, NULL); - } - for (i = 0; i < h; i++) { - line = data + i * wpl; - memcpy(buffer, line, bpl); - for (j = 0; j < w; j++) - line[j] = buffer[w - 1 - j]; - } - LEPT_FREE(buffer); - return fpixd; -} - - -/*! - * \brief fpixFlipTB() - * - * \param[in] fpixd [optional] can be null, equal to fpixs, - * or different from fpixs - * \param[in] fpixs - * \return fpixd, or NULL on error - * - *
- * Notes: - * (1) This does a top-bottom flip of the image, which is - * equivalent to a rotation out of the plane about a - * horizontal line through the image center. - * (2) There are 3 cases for input: - * (a) fpixd == null (creates a new fpixd) - * (b) fpixd == fpixs (in-place operation) - * (c) fpixd != fpixs (existing fpixd) - * (3) For clarity, use these three patterns, respectively: - * (a) fpixd = fpixFlipTB(NULL, fpixs); - * (b) fpixFlipTB(fpixs, fpixs); - * (c) fpixFlipTB(fpixd, fpixs); - * (4) If an existing fpixd is not the same size as fpixs, the - * image data will be reallocated. - *- */ -FPIX * -fpixFlipTB(FPIX *fpixd, - FPIX *fpixs) -{ -l_int32 i, k, h, h2, wpl, bpl; -l_float32 *linet, *lineb, *data, *buffer; - - PROCNAME("fpixFlipTB"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - - /* Prepare fpixd for in-place operation */ - if ((fpixd = fpixCopy(fpixd, fpixs)) == NULL) - return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL); - - data = fpixGetData(fpixd); - wpl = fpixGetWpl(fpixd); - fpixGetDimensions(fpixd, NULL, &h); - if ((buffer = (l_float32 *)LEPT_CALLOC(wpl, sizeof(l_float32))) == NULL) { - fpixDestroy(&fpixd); - return (FPIX *)ERROR_PTR("buffer not made", procName, NULL); - } - h2 = h / 2; - bpl = 4 * wpl; - for (i = 0, k = h - 1; i < h2; i++, k--) { - linet = data + i * wpl; - lineb = data + k * wpl; - memcpy(buffer, linet, bpl); - memcpy(linet, lineb, bpl); - memcpy(lineb, buffer, bpl); - } - LEPT_FREE(buffer); - return fpixd; -} - - -/*--------------------------------------------------------------------* - * Affine and projective interpolated transforms * - *--------------------------------------------------------------------*/ -/*! - * \brief fpixAffinePta() - * - * \param[in] fpixs 8 bpp - * \param[in] ptad 4 pts of final coordinate space - * \param[in] ptas 4 pts of initial coordinate space - * \param[in] border size of extension with constant normal derivative - * \param[in] inval value brought in; typ. 0 - * \return fpixd, or NULL on error - * - *
- * Notes: - * (1) If %border > 0, all four sides are extended by that distance, - * and removed after the transformation is finished. Pixels - * that would be brought in to the trimmed result from outside - * the extended region are assigned %inval. The purpose of - * extending the image is to avoid such assignments. - * (2) On the other hand, you may want to give all pixels that - * are brought in from outside fpixs a specific value. In that - * case, set %border == 0. - *- */ -FPIX * -fpixAffinePta(FPIX *fpixs, - PTA *ptad, - PTA *ptas, - l_int32 border, - l_float32 inval) -{ -l_float32 *vc; -PTA *ptas2, *ptad2; -FPIX *fpixs2, *fpixd, *fpixd2; - - PROCNAME("fpixAffinePta"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - if (!ptas) - return (FPIX *)ERROR_PTR("ptas not defined", procName, NULL); - if (!ptad) - return (FPIX *)ERROR_PTR("ptad not defined", procName, NULL); - - /* If a border is to be added, also translate the ptas */ - if (border > 0) { - ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); - ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); - fpixs2 = fpixAddSlopeBorder(fpixs, border, border, border, border); - } else { - ptas2 = ptaClone(ptas); - ptad2 = ptaClone(ptad); - fpixs2 = fpixClone(fpixs); - } - - /* Get backwards transform from dest to src, and apply it */ - getAffineXformCoeffs(ptad2, ptas2, &vc); - fpixd2 = fpixAffine(fpixs2, vc, inval); - fpixDestroy(&fpixs2); - ptaDestroy(&ptas2); - ptaDestroy(&ptad2); - LEPT_FREE(vc); - - if (border == 0) - return fpixd2; - - /* Remove the added border */ - fpixd = fpixRemoveBorder(fpixd2, border, border, border, border); - fpixDestroy(&fpixd2); - return fpixd; -} - - -/*! - * \brief fpixAffine() - * - * \param[in] fpixs 8 bpp - * \param[in] vc vector of 8 coefficients for projective transformation - * \param[in] inval value brought in; typ. 0 - * \return fpixd, or NULL on error - */ -FPIX * -fpixAffine(FPIX *fpixs, - l_float32 *vc, - l_float32 inval) -{ -l_int32 i, j, w, h, wpld; -l_float32 val; -l_float32 *datas, *datad, *lined; -l_float32 x, y; -FPIX *fpixd; - - PROCNAME("fpixAffine"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - fpixGetDimensions(fpixs, &w, &h); - if (!vc) - return (FPIX *)ERROR_PTR("vc not defined", procName, NULL); - - datas = fpixGetData(fpixs); - fpixd = fpixCreateTemplate(fpixs); - fpixSetAllArbitrary(fpixd, inval); - datad = fpixGetData(fpixd); - wpld = fpixGetWpl(fpixd); - - /* Iterate over destination pixels */ - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - /* Compute float src pixel location corresponding to (i,j) */ - affineXformPt(vc, j, i, &x, &y); - linearInterpolatePixelFloat(datas, w, h, x, y, inval, &val); - *(lined + j) = val; - } - } - - return fpixd; -} - - -/*! - * \brief fpixProjectivePta() - * - * \param[in] fpixs 8 bpp - * \param[in] ptad 4 pts of final coordinate space - * \param[in] ptas 4 pts of initial coordinate space - * \param[in] border size of extension with constant normal derivative - * \param[in] inval value brought in; typ. 0 - * \return fpixd, or NULL on error - * - *
- * Notes: - * (1) If %border > 0, all four sides are extended by that distance, - * and removed after the transformation is finished. Pixels - * that would be brought in to the trimmed result from outside - * the extended region are assigned %inval. The purpose of - * extending the image is to avoid such assignments. - * (2) On the other hand, you may want to give all pixels that - * are brought in from outside fpixs a specific value. In that - * case, set %border == 0. - *- */ -FPIX * -fpixProjectivePta(FPIX *fpixs, - PTA *ptad, - PTA *ptas, - l_int32 border, - l_float32 inval) -{ -l_float32 *vc; -PTA *ptas2, *ptad2; -FPIX *fpixs2, *fpixd, *fpixd2; - - PROCNAME("fpixProjectivePta"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - if (!ptas) - return (FPIX *)ERROR_PTR("ptas not defined", procName, NULL); - if (!ptad) - return (FPIX *)ERROR_PTR("ptad not defined", procName, NULL); - - /* If a border is to be added, also translate the ptas */ - if (border > 0) { - ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); - ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); - fpixs2 = fpixAddSlopeBorder(fpixs, border, border, border, border); - } else { - ptas2 = ptaClone(ptas); - ptad2 = ptaClone(ptad); - fpixs2 = fpixClone(fpixs); - } - - /* Get backwards transform from dest to src, and apply it */ - getProjectiveXformCoeffs(ptad2, ptas2, &vc); - fpixd2 = fpixProjective(fpixs2, vc, inval); - fpixDestroy(&fpixs2); - ptaDestroy(&ptas2); - ptaDestroy(&ptad2); - LEPT_FREE(vc); - - if (border == 0) - return fpixd2; - - /* Remove the added border */ - fpixd = fpixRemoveBorder(fpixd2, border, border, border, border); - fpixDestroy(&fpixd2); - return fpixd; -} - - -/*! - * \brief fpixProjective() - * - * \param[in] fpixs 8 bpp - * \param[in] vc vector of 8 coefficients for projective transform - * \param[in] inval value brought in; typ. 0 - * \return fpixd, or NULL on error - */ -FPIX * -fpixProjective(FPIX *fpixs, - l_float32 *vc, - l_float32 inval) -{ -l_int32 i, j, w, h, wpld; -l_float32 val; -l_float32 *datas, *datad, *lined; -l_float32 x, y; -FPIX *fpixd; - - PROCNAME("fpixProjective"); - - if (!fpixs) - return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL); - fpixGetDimensions(fpixs, &w, &h); - if (!vc) - return (FPIX *)ERROR_PTR("vc not defined", procName, NULL); - - datas = fpixGetData(fpixs); - fpixd = fpixCreateTemplate(fpixs); - fpixSetAllArbitrary(fpixd, inval); - datad = fpixGetData(fpixd); - wpld = fpixGetWpl(fpixd); - - /* Iterate over destination pixels */ - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - /* Compute float src pixel location corresponding to (i,j) */ - projectiveXformPt(vc, j, i, &x, &y); - linearInterpolatePixelFloat(datas, w, h, x, y, inval, &val); - *(lined + j) = val; - } - } - - return fpixd; -} - - -/*! - * \brief linearInterpolatePixelFloat() - * - * \param[in] datas ptr to beginning of float image data - * \param[in] w, h dimensions of image - * \param[in] x, y floating pt location for evaluation - * \param[in] inval float value brought in from the outside when the - * input x,y location is outside the image - * \param[out] pval interpolated float value - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is a standard linear interpolation function. It is - * equivalent to area weighting on each component, and - * avoids "jaggies" when rendering sharp edges. - *- */ -l_ok -linearInterpolatePixelFloat(l_float32 *datas, - l_int32 w, - l_int32 h, - l_float32 x, - l_float32 y, - l_float32 inval, - l_float32 *pval) -{ -l_int32 xpm, ypm, xp, yp, xf, yf; -l_float32 v00, v01, v10, v11; -l_float32 *lines; - - PROCNAME("linearInterpolatePixelFloat"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = inval; - if (!datas) - return ERROR_INT("datas not defined", procName, 1); - - /* Skip if off the edge */ - if (x < 0.0 || y < 0.0 || x > w - 2.0 || y > h - 2.0) - return 0; - - xpm = (l_int32)(16.0 * x + 0.5); - ypm = (l_int32)(16.0 * y + 0.5); - xp = xpm >> 4; - yp = ypm >> 4; - xf = xpm & 0x0f; - yf = ypm & 0x0f; - -#if DEBUG - if (xf < 0 || yf < 0) - lept_stderr("xp = %d, yp = %d, xf = %d, yf = %d\n", xp, yp, xf, yf); -#endif /* DEBUG */ - - /* Interpolate by area weighting. */ - lines = datas + yp * w; - v00 = (16.0 - xf) * (16.0 - yf) * (*(lines + xp)); - v10 = xf * (16.0 - yf) * (*(lines + xp + 1)); - v01 = (16.0 - xf) * yf * (*(lines + w + xp)); - v11 = (l_float32)(xf) * yf * (*(lines + w + xp + 1)); - *pval = (v00 + v01 + v10 + v11) / 256.0; - return 0; -} - - -/*--------------------------------------------------------------------* - * Thresholding to 1 bpp Pix * - *--------------------------------------------------------------------*/ -/*! - * \brief fpixThresholdToPix() - * - * \param[in] fpix - * \param[in] thresh - * \return pixd 1 bpp, or NULL on error - * - *
- * Notes: - * (1) For all values of fpix that are <= thresh, sets the pixel - * in pixd to 1. - *- */ -PIX * -fpixThresholdToPix(FPIX *fpix, - l_float32 thresh) -{ -l_int32 i, j, w, h, wpls, wpld; -l_float32 *datas, *lines; -l_uint32 *datad, *lined; -PIX *pixd; - - PROCNAME("fpixThresholdToPix"); - - if (!fpix) - return (PIX *)ERROR_PTR("fpix not defined", procName, NULL); - - fpixGetDimensions(fpix, &w, &h); - datas = fpixGetData(fpix); - wpls = fpixGetWpl(fpix); - pixd = pixCreate(w, h, 1); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - if (lines[j] <= thresh) - SET_DATA_BIT(lined, j); - } - } - - return pixd; -} - - -/*--------------------------------------------------------------------* - * Generate function from components * - *--------------------------------------------------------------------*/ -/*! - * \brief pixComponentFunction() - * - * \param[in] pix 32 bpp rgb - * \param[in] rnum, gnum, bnum coefficients for numerator - * \param[in] rdenom, gdenom, bdenom coefficients for denominator - * \return fpixd, or NULL on error - * - *
- * Notes: - * (1) This stores a function of the component values of each - * input pixel in %fpixd. - * (2) The function is a ratio of linear combinations of component values. - * There are two special cases for denominator coefficients: - * (a) The denominator is 1.0: input 0 for all denominator coefficients - * (b) Only one component is used in the denominator: input 1.0 - * for that denominator component and 0.0 for the other two. - * (3) If the denominator is 0, multiply by an arbitrary number that - * is much larger than 1. Choose 256 "arbitrarily". - * - *- */ -FPIX * -pixComponentFunction(PIX *pix, - l_float32 rnum, - l_float32 gnum, - l_float32 bnum, - l_float32 rdenom, - l_float32 gdenom, - l_float32 bdenom) -{ -l_int32 i, j, w, h, wpls, wpld, rval, gval, bval, zerodenom, onedenom; -l_float32 fnum, fdenom; -l_uint32 *datas, *lines; -l_float32 *datad, *lined, *recip; -FPIX *fpixd; - - PROCNAME("pixComponentFunction"); - - if (!pix || pixGetDepth(pix) != 32) - return (FPIX *)ERROR_PTR("pix undefined or not 32 bpp", procName, NULL); - - pixGetDimensions(pix, &w, &h, NULL); - datas = pixGetData(pix); - wpls = pixGetWpl(pix); - fpixd = fpixCreate(w, h); - datad = fpixGetData(fpixd); - wpld = fpixGetWpl(fpixd); - zerodenom = (rdenom == 0.0 && gdenom == 0.0 && bdenom == 0.0) ? 1: 0; - onedenom = ((rdenom == 1.0 && gdenom == 0.0 && bdenom == 0.0) || - (rdenom == 0.0 && gdenom == 1.0 && bdenom == 0.0) || - (rdenom == 0.0 && gdenom == 0.0 && bdenom == 1.0)) ? 1 : 0; - recip = NULL; - if (onedenom) { - recip = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32)); - recip[0] = 256; /* arbitrary large number */ - for (i = 1; i < 256; i++) - recip[i] = 1.0 / (l_float32)i; - } - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - if (zerodenom) { - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - lined[j] = rnum * rval + gnum * gval + bnum * bval; - } - } else if (onedenom && rdenom == 1.0) { - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - lined[j] - = recip[rval] * (rnum * rval + gnum * gval + bnum * bval); - } - } else if (onedenom && gdenom == 1.0) { - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - lined[j] - = recip[gval] * (rnum * rval + gnum * gval + bnum * bval); - } - } else if (onedenom && bdenom == 1.0) { - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - lined[j] - = recip[bval] * (rnum * rval + gnum * gval + bnum * bval); - } - } else { /* general case */ - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - fnum = rnum * rval + gnum * gval + bnum * bval; - fdenom = rdenom * rval + gdenom * gval + bdenom * bval; - lined[j] = (fdenom == 0) ? 256.0 * fnum : fnum / fdenom; - } - } - } - - LEPT_FREE(recip); - return fpixd; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/gifio.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/gifio.c deleted file mode 100644 index 2131826a..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/gifio.c +++ /dev/null @@ -1,675 +0,0 @@ -/*====================================================================* - - 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 gifio.c - *
- * - * Reading gif - * PIX *pixReadStreamGif() - * PIX *pixReadMemGif() - * static l_int32 gifReadFunc() - * static PIX *gifToPix() - * - * Writing gif - * l_int32 pixWriteStreamGif() - * l_int32 pixWriteMemGif() - * static l_int32 gifWriteFunc() - * static l_int32 pixToGif() - * - * The initial version of this module was generously contribued by - * Antony Dovgal. - * - * The functions that read and write from pix to gif-compressed memory, - * using gif internal functions DGifOpen() and EGifOpen() that are - * available in 5.1 and later, were contributed by Tobias Peirick. - * - * Version information: - * - * (1) This supports the gif library, version 5.1 or later, for which - * gif read-from-mem and write-to-mem allow these operations - * without writing temporary files. - * (2) There has never been a gif stream interface. For versions - * before 5.1, it was necessary to use a file descriptor, and to - * generate a file stream from the low-level descriptor. With the - * memory interface in 5.1 that can be used on all platforms, it - * is no longer necessary to use any API code with file descriptors. - * (3) The public interface changed with 5.0 and with 5.1, and we - * no longer support 4.6.1 and 5.0. - * (4) Version 5.1.2 came out on Jan 7, 2016. Leptonica cannot - * successfully read gif files that it writes with this version; - * DGifSlurp() gets an internal error from an uninitialized array - * and returns failure. The problem was fixed in 5.1.3. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) For libgif version >= 5.1, this uses the DGifOpen() buffer - * interface. No temp files are required. - * (2) For libgif version < 5.1, it was necessary to write the compressed - * data to file and read it back, and we couldn't use the GNU - * runtime extension fmemopen() because libgif doesn't have a file - * stream interface. - *- */ -PIX * -pixReadMemGif(const l_uint8 *cdata, - size_t size) -{ -GifFileType *gif; -GifReadBuffer buffer; - - PROCNAME("pixReadMemGif"); - - /* 5.1+ and not 5.1.2 */ -#if (GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)) - L_ERROR("Require giflib-5.1 or later\n", procName); - return NULL; -#endif /* < 5.1 */ -#if GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 1 && GIFLIB_RELEASE == 2 /* 5.1.2 */ - L_ERROR("Can't use giflib-5.1.2; suggest 5.1.3 or later\n", procName); - return NULL; -#endif /* 5.1.2 */ - - if (!cdata) - return (PIX *)ERROR_PTR("cdata not defined", procName, NULL); - - buffer.cdata = cdata; - buffer.size = size; - buffer.pos = 0; - if ((gif = DGifOpen((void*)&buffer, gifReadFunc, NULL)) == NULL) - return (PIX *)ERROR_PTR("could not open gif stream from memory", - procName, NULL); - - return gifToPix(gif); -} - - -static l_int32 -gifReadFunc(GifFileType *gif, - GifByteType *dest, - l_int32 bytesToRead) -{ -GifReadBuffer *buffer; -l_int32 bytesRead; - - PROCNAME("gifReadFunc"); - - if ((buffer = (GifReadBuffer*)gif->UserData) == NULL) - return ERROR_INT("UserData not set", procName, -1); - - if(buffer->pos >= buffer->size || bytesToRead > buffer->size) - return -1; - - bytesRead = (buffer->pos < buffer->size - bytesToRead) - ? bytesToRead : buffer->size - buffer->pos; - memcpy(dest, buffer->cdata + buffer->pos, bytesRead); - buffer->pos += bytesRead; - return bytesRead; -} - - -/*! - * \brief gifToPix() - * - * \param[in] gif opened gif stream - * \return pix, or NULL on error - * - *
- * Notes: - * (1) This decodes the pix from the compressed gif stream and - * closes the stream. - * (2) It is static so that the stream is not exposed to clients. - *- */ -static PIX * -gifToPix(GifFileType *gif) -{ -l_int32 wpl, i, j, w, h, d, cindex, ncolors; -l_int32 rval, gval, bval; -l_uint32 *data, *line; -PIX *pixd; -PIXCMAP *cmap; -ColorMapObject *gif_cmap; -SavedImage si; -int giferr; - - PROCNAME("gifToPix"); - - /* Read all the data, but use only the first image found */ - if (DGifSlurp(gif) != GIF_OK) { - DGifCloseFile(gif, &giferr); - return (PIX *)ERROR_PTR("failed to read GIF data", procName, NULL); - } - - if (gif->SavedImages == NULL) { - DGifCloseFile(gif, &giferr); - return (PIX *)ERROR_PTR("no images found in GIF", procName, NULL); - } - - si = gif->SavedImages[0]; - w = si.ImageDesc.Width; - h = si.ImageDesc.Height; - if (w <= 0 || h <= 0) { - DGifCloseFile(gif, &giferr); - return (PIX *)ERROR_PTR("invalid image dimensions", procName, NULL); - } - - if (si.RasterBits == NULL) { - DGifCloseFile(gif, &giferr); - return (PIX *)ERROR_PTR("no raster data in GIF", procName, NULL); - } - - if (si.ImageDesc.ColorMap) { - /* private cmap for this image */ - gif_cmap = si.ImageDesc.ColorMap; - } else if (gif->SColorMap) { - /* global cmap for whole picture */ - gif_cmap = gif->SColorMap; - } else { - /* don't know where to take cmap from */ - DGifCloseFile(gif, &giferr); - return (PIX *)ERROR_PTR("color map is missing", procName, NULL); - } - - ncolors = gif_cmap->ColorCount; - if (ncolors <= 2) - d = 1; - else if (ncolors <= 4) - d = 2; - else if (ncolors <= 16) - d = 4; - else - d = 8; - if ((cmap = pixcmapCreate(d)) == NULL) { - DGifCloseFile(gif, &giferr); - return (PIX *)ERROR_PTR("cmap creation failed", procName, NULL); - } - - for (cindex = 0; cindex < ncolors; cindex++) { - rval = gif_cmap->Colors[cindex].Red; - gval = gif_cmap->Colors[cindex].Green; - bval = gif_cmap->Colors[cindex].Blue; - pixcmapAddColor(cmap, rval, gval, bval); - } - - if ((pixd = pixCreate(w, h, d)) == NULL) { - DGifCloseFile(gif, &giferr); - pixcmapDestroy(&cmap); - return (PIX *)ERROR_PTR("failed to allocate pixd", procName, NULL); - } - pixSetInputFormat(pixd, IFF_GIF); - pixSetColormap(pixd, cmap); - - wpl = pixGetWpl(pixd); - data = pixGetData(pixd); - for (i = 0; i < h; i++) { - line = data + i * wpl; - if (d == 1) { - for (j = 0; j < w; j++) { - if (si.RasterBits[i * w + j]) - SET_DATA_BIT(line, j); - } - } else if (d == 2) { - for (j = 0; j < w; j++) - SET_DATA_DIBIT(line, j, si.RasterBits[i * w + j]); - } else if (d == 4) { - for (j = 0; j < w; j++) - SET_DATA_QBIT(line, j, si.RasterBits[i * w + j]); - } else { /* d == 8 */ - for (j = 0; j < w; j++) - SET_DATA_BYTE(line, j, si.RasterBits[i * w + j]); - } - } - - /* Versions before 5.0 required un-interlacing to restore - * the raster lines to normal order if the image - * had been interlaced (for viewing in a browser): - if (gif->Image.Interlace) { - PIX *pixdi = pixUninterlaceGIF(pixd); - pixTransferAllData(pixd, &pixdi, 0, 0); - } - * This is no longer required. */ - - DGifCloseFile(gif, &giferr); - return pixd; -} - - -/*---------------------------------------------------------------------* - * Writing gif * - *---------------------------------------------------------------------*/ -/*! - * \brief pixWriteStreamGif() - * - * \param[in] fp file stream opened for writing - * \param[in] pix 1, 2, 4, 8, 16 or 32 bpp - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) All output gif have colormaps. If the pix is 32 bpp rgb, - * this quantizes the colors and writes out 8 bpp. - * If the pix is 16 bpp grayscale, it converts to 8 bpp first. - *- */ -l_ok -pixWriteStreamGif(FILE *fp, - PIX *pix) -{ -l_uint8 *filedata; -size_t filebytes, nbytes; - - PROCNAME("pixWriteStreamGif"); - - if (!fp) - return ERROR_INT("stream not open", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - pixSetPadBits(pix, 0); - if (pixWriteMemGif(&filedata, &filebytes, pix) != 0) { - LEPT_FREE(filedata); - return ERROR_INT("failure to gif encode pix", procName, 1); - } - - rewind(fp); - nbytes = fwrite(filedata, 1, filebytes, fp); - LEPT_FREE(filedata); - if (nbytes != filebytes) - return ERROR_INT("write error", procName, 1); - return 0; -} - - -/*! - * \brief pixWriteMemGif() - * - * \param[out] pdata data of gif compressed image - * \param[out] psize size of returned data - * \param[in] pix - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) See comments in pixReadMemGif() - *- */ -l_ok -pixWriteMemGif(l_uint8 **pdata, - size_t *psize, - PIX *pix) -{ -int giferr; -l_int32 result; -GifFileType *gif; -L_BBUFFER *buffer; - - PROCNAME("pixWriteMemGif"); - - /* 5.1+ and not 5.1.2 */ -#if (GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)) - L_ERROR("Require giflib-5.1 or later\n", procName); - return 1; -#endif /* < 5.1 */ -#if GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 1 && GIFLIB_RELEASE == 2 /* 5.1.2 */ - L_ERROR("Can't use giflib-5.1.2; suggest 5.1.3 or later\n", procName); - return 1; -#endif /* 5.1.2 */ - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1 ); - *pdata = NULL; - if (!psize) - return ERROR_INT("&size not defined", procName, 1 ); - *psize = 0; - if (!pix) - return ERROR_INT("&pix not defined", procName, 1 ); - - if ((buffer = bbufferCreate(NULL, 0)) == NULL) - return ERROR_INT("failed to create buffer", procName, 1); - - if ((gif = EGifOpen((void*)buffer, gifWriteFunc, NULL)) == NULL) { - bbufferDestroy(&buffer); - return ERROR_INT("failed to create GIF image handle", procName, 1); - } - - result = pixToGif(pix, gif); - EGifCloseFile(gif, &giferr); - - if (result == 0) { - *pdata = bbufferDestroyAndSaveData(&buffer, psize); - } else { - bbufferDestroy(&buffer); - } - return result; -} - - -static l_int32 -gifWriteFunc(GifFileType *gif, - const GifByteType *src, - l_int32 bytesToWrite) -{ -L_BBUFFER *buffer; - - PROCNAME("gifWriteFunc"); - - if ((buffer = (L_BBUFFER*)gif->UserData) == NULL) - return ERROR_INT("UserData not set", procName, -1); - - if(bbufferRead(buffer, (l_uint8*)src, bytesToWrite) == 0) - return bytesToWrite; - return 0; -} - - -/*! - * \brief pixToGif() - * - * \param[in] pix 1, 2, 4, 8, 16 or 32 bpp - * \param[in] gif opened gif stream - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This encodes the pix to the gif stream. The stream is not - * closed by this function. - * (2) It is static to make this function private. - *- */ -static l_int32 -pixToGif(PIX *pix, - GifFileType *gif) -{ -char *text; -l_int32 wpl, i, j, w, h, d, ncolor, rval, gval, bval; -l_int32 gif_ncolor = 0; -l_uint32 *data, *line; -PIX *pixd; -PIXCMAP *cmap; -ColorMapObject *gif_cmap; -GifByteType *gif_line; - - PROCNAME("pixToGif"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!gif) - return ERROR_INT("gif not defined", procName, 1); - - d = pixGetDepth(pix); - if (d == 32) { - pixd = pixConvertRGBToColormap(pix, 1); - } else if (d > 1) { - pixd = pixConvertTo8(pix, TRUE); - } else { /* d == 1; make sure there's a colormap */ - pixd = pixClone(pix); - if (!pixGetColormap(pixd)) { - cmap = pixcmapCreate(1); - pixcmapAddColor(cmap, 255, 255, 255); - pixcmapAddColor(cmap, 0, 0, 0); - pixSetColormap(pixd, cmap); - } - } - - if (!pixd) - return ERROR_INT("failed to convert image to indexed", procName, 1); - d = pixGetDepth(pixd); - - if ((cmap = pixGetColormap(pixd)) == NULL) { - pixDestroy(&pixd); - return ERROR_INT("cmap is missing", procName, 1); - } - - /* 'Round' the number of gif colors up to a power of 2 */ - ncolor = pixcmapGetCount(cmap); - for (i = 0; i <= 8; i++) { - if ((1 << i) >= ncolor) { - gif_ncolor = (1 << i); - break; - } - } - if (gif_ncolor < 1) { - pixDestroy(&pixd); - return ERROR_INT("number of colors is invalid", procName, 1); - } - - /* Save the cmap colors in a gif_cmap */ - if ((gif_cmap = GifMakeMapObject(gif_ncolor, NULL)) == NULL) { - pixDestroy(&pixd); - return ERROR_INT("failed to create GIF color map", procName, 1); - } - for (i = 0; i < gif_ncolor; i++) { - rval = gval = bval = 0; - if (ncolor > 0) { - if (pixcmapGetColor(cmap, i, &rval, &gval, &bval) != 0) { - pixDestroy(&pixd); - GifFreeMapObject(gif_cmap); - return ERROR_INT("failed to get color from color map", - procName, 1); - } - ncolor--; - } - gif_cmap->Colors[i].Red = rval; - gif_cmap->Colors[i].Green = gval; - gif_cmap->Colors[i].Blue = bval; - } - - pixGetDimensions(pixd, &w, &h, NULL); - if (EGifPutScreenDesc(gif, w, h, gif_cmap->BitsPerPixel, 0, gif_cmap) - != GIF_OK) { - pixDestroy(&pixd); - GifFreeMapObject(gif_cmap); - return ERROR_INT("failed to write screen description", procName, 1); - } - GifFreeMapObject(gif_cmap); /* not needed after this point */ - - if (EGifPutImageDesc(gif, 0, 0, w, h, FALSE, NULL) != GIF_OK) { - pixDestroy(&pixd); - return ERROR_INT("failed to image screen description", procName, 1); - } - - data = pixGetData(pixd); - wpl = pixGetWpl(pixd); - if (d != 1 && d != 2 && d != 4 && d != 8) { - pixDestroy(&pixd); - return ERROR_INT("image depth is not in {1, 2, 4, 8}", procName, 1); - } - - if ((gif_line = (GifByteType *)LEPT_CALLOC(sizeof(GifByteType), w)) - == NULL) { - pixDestroy(&pixd); - return ERROR_INT("mem alloc fail for data line", procName, 1); - } - - for (i = 0; i < h; i++) { - line = data + i * wpl; - /* Gif's way of setting the raster line up for compression */ - for (j = 0; j < w; j++) { - switch(d) - { - case 8: - gif_line[j] = GET_DATA_BYTE(line, j); - break; - case 4: - gif_line[j] = GET_DATA_QBIT(line, j); - break; - case 2: - gif_line[j] = GET_DATA_DIBIT(line, j); - break; - case 1: - gif_line[j] = GET_DATA_BIT(line, j); - break; - } - } - - /* Compress and save the line */ - if (EGifPutLine(gif, gif_line, w) != GIF_OK) { - LEPT_FREE(gif_line); - pixDestroy(&pixd); - return ERROR_INT("failed to write data line into GIF", procName, 1); - } - } - - /* Write a text comment. This must be placed after writing the - * data (!!) Note that because libgif does not provide a function - * for reading comments from file, you will need another way - * to read comments. */ - if ((text = pixGetText(pix)) != NULL) { - if (EGifPutComment(gif, text) != GIF_OK) - L_WARNING("gif comment not written\n", procName); - } - - LEPT_FREE(gif_line); - pixDestroy(&pixd); - return 0; -} - - -#if 0 -/*---------------------------------------------------------------------* - * Removing interlacing (reference only; not used) * - *---------------------------------------------------------------------*/ - /* GIF supports 4-way interlacing by raster lines. - * Before 5.0, it was necessary for leptonica to restore interlaced - * data to normal raster order when reading to a pix. With 5.0, - * the de-interlacing is done by the library read function. - * It is here only as a reference. */ -static const l_int32 InterlacedOffset[] = {0, 4, 2, 1}; -static const l_int32 InterlacedJumps[] = {8, 8, 4, 2}; - -static PIX * -pixUninterlaceGIF(PIX *pixs) -{ -l_int32 w, h, d, wpl, j, k, srow, drow; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; - - PROCNAME("pixUninterlaceGIF"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - pixGetDimensions(pixs, &w, &h, &d); - wpl = pixGetWpl(pixs); - pixd = pixCreateTemplate(pixs); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - for (k = 0, srow = 0; k < 4; k++) { - for (drow = InterlacedOffset[k]; drow < h; - drow += InterlacedJumps[k], srow++) { - lines = datas + srow * wpl; - lined = datad + drow * wpl; - for (j = 0; j < w; j++) - memcpy(lined, lines, 4 * wpl); - } - } - - return pixd; -} -#endif - - -/* -----------------------------------------------------------------*/ -#endif /* HAVE_LIBGIF || HAVE_LIBUNGIF */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/gifiostub.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/gifiostub.c deleted file mode 100644 index 20129b9c..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/gifiostub.c +++ /dev/null @@ -1,72 +0,0 @@ -/*====================================================================* - - 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 gifiostub.c - *
- * - * Stubs for gifio.c functions - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * - * Basic plotting functions - * GPLOT *gplotCreate() - * void gplotDestroy() - * l_int32 gplotAddPlot() - * l_int32 gplotSetScaling() - * PIX *gplotMakeOutputPix() - * l_int32 gplotMakeOutput() - * l_int32 gplotGenCommandFile() - * l_int32 gplotGenDataFiles() - * - * Quick, one-line plots - * l_int32 gplotSimple1() - * l_int32 gplotSimple2() - * l_int32 gplotSimpleN() - * PIX *gplotSimplePix1() - * PIX *gplotSimplePix2() - * PIX *gplotSimplePixN() - * GPLOT *gplotSimpleXY1() - * GPLOT *gplotSimpleXY2() - * GPLOT *gplotSimpleXYN() - * PIX *gplotGeneralPix1() - * PIX *gplotGeneralPix2() - * PIX *gplotGeneralPixN() - * - * Serialize for I/O - * GPLOT *gplotRead() - * l_int32 gplotWrite() - * - * - * Utility for programmatic plotting using gnuplot 4.6 or later - * Enabled: - * ~ output to png (color), ps and eps (mono), latex (mono) - * ~ optional title for plot - * ~ optional x and y axis labels - * ~ multiple plots on one frame - * ~ optional label for each plot on the frame - * ~ optional log scaling on either or both axes - * ~ choice of 5 plot styles for each array of input data - * ~ choice of 2 plot modes, either using one input array - * (Y vs index) or two input arrays (Y vs X). For functions - * that take two arrays, the first mode (Y vs index) is - * employed if the first array is NULL. - * - * General usage: - * gplotCreate() initializes for plotting - * gplotAddPlot() for each plot on the frame - * gplotMakeOutput() to generate all output files and run gnuplot - * gplotDestroy() to clean up - * - * Example of use: - * gplot = gplotCreate("tempskew", GPLOT_PNG, "Skew score vs angle", - * "angle (deg)", "score"); - * gplotAddPlot(gplot, natheta, nascore1, GPLOT_LINES, "plot 1"); - * gplotAddPlot(gplot, natheta, nascore2, GPLOT_POINTS, "plot 2"); - * gplotSetScaling(gplot, GPLOT_LOG_SCALE_Y); - * gplotMakeOutput(gplot); - * gplotDestroy(&gplot); - * - * Example usage of one-line plot generators: - * - * -- Simple plots -- - * Specify the root of output files, the output format, - * and the title (optional), but not the x and y coordinate labels - * or the plot labels. The plotstyle defaults to GPLOT_LINES. - * gplotSimple2(na1, na2, GPLOT_PNG, "/tmp/lept/histo/gray", - * "gray histogram"); - * Multiple plots can be generated using gplotSimpleN(). - * - * -- Simple plots with more options -- - * Specify the root of output files, the plotstyle, the output format, - * and optionally the title, but not the x and y coordinate labels - * or the plot labels. - * gplotSimpleXY1(na1, na2, GPLOT_LINES, GPLOT_PNG, - * "/tmp/lept/histo/gray", "gray histogram"); - * Multiple plots can be generated using gplotSimpleXYN(). - * - * -- Simple plots returning a pix -- - * Specify only the title (optional). The plotstyle defaults - * GPLOT_LINES and the output format is GPLOT_PNG.. - * You can't specify the x and y coordinate lables or the plot label. - * The rootname of the generated files is determined internally. - * Pix *pix = gplotSimplePix2(na1, na2, "gray histogram"); - * Multiple plots can be generated using gplotSimplePixN(). - * - * -- General plots returning a pix -- - * Specify the root of the output files, the plotstyle, and optionally - * the title and axis labels. This does not allow the individual - * plots to have plot labels, or to use different plotstyles - * for each plot. - * Pix *pix = gplotGeneralPix2(na1, na2, "/tmp/lept/histo/gray", - * GPLOT_LINES, "gray histogram", - * "pix value", "num pixels"); - * Multiple plots can be generated using gplotGeneralPixN(). - * - * Note for output to GPLOT_LATEX: - * This creates latex output of the plot, named- */ - -#ifdef HAVE_CONFIG_H -#include.tex. - * It needs to be placed in a latex file .tex - * that precedes the plot output with, at a minimum: - * \documentclass{article} - * \begin{document} - * and ends with - * \end{document} - * You can then generate a dvi file .dvi using - * latex .tex - * and a PostScript file .ps from that using - * dvips -o .ps .dvi - * - * N.B. To generate plots, it is necessary to have gnuplot installed on - * your Unix system, or wgnuplot on Windows. - *
- * Notes: - * (1) This initializes the plot. - * (2) The 'title', 'xlabel' and 'ylabel' strings can have spaces, - * double quotes and backquotes, but not single quotes. - *- */ -GPLOT * -gplotCreate(const char *rootname, - l_int32 outformat, - const char *title, - const char *xlabel, - const char *ylabel) -{ -char *newroot; -char buf[Bufsize]; -l_int32 badchar; -GPLOT *gplot; - - PROCNAME("gplotCreate"); - - if (!rootname) - return (GPLOT *)ERROR_PTR("rootname not defined", procName, NULL); - if (outformat != GPLOT_PNG && outformat != GPLOT_PS && - outformat != GPLOT_EPS && outformat != GPLOT_LATEX && - outformat != GPLOT_PNM) - return (GPLOT *)ERROR_PTR("outformat invalid", procName, NULL); - stringCheckForChars(rootname, "`;&|><\"?*$()", &badchar); - if (badchar) /* danger of command injection */ - return (GPLOT *)ERROR_PTR("invalid rootname", procName, NULL); - -#if !defined(HAVE_LIBPNG) - if (outformat == GPLOT_PNG) { - L_WARNING("png library missing; output pnm format\n", procName); - outformat = GPLOT_PNM; - } -#endif - - gplot = (GPLOT *)LEPT_CALLOC(1, sizeof(GPLOT)); - gplot->cmddata = sarrayCreate(0); - gplot->datanames = sarrayCreate(0); - gplot->plotdata = sarrayCreate(0); - gplot->plotlabels = sarrayCreate(0); - gplot->plotstyles = numaCreate(0); - - /* Save title, labels, rootname, outformat, cmdname, outname */ - newroot = genPathname(rootname, NULL); - gplot->rootname = newroot; - gplot->outformat = outformat; - snprintf(buf, Bufsize, "%s.cmd", rootname); - gplot->cmdname = stringNew(buf); - if (outformat == GPLOT_PNG) - snprintf(buf, Bufsize, "%s.png", newroot); - else if (outformat == GPLOT_PS) - snprintf(buf, Bufsize, "%s.ps", newroot); - else if (outformat == GPLOT_EPS) - snprintf(buf, Bufsize, "%s.eps", newroot); - else if (outformat == GPLOT_LATEX) - snprintf(buf, Bufsize, "%s.tex", newroot); - else if (outformat == GPLOT_PNM) - snprintf(buf, Bufsize, "%s.pnm", newroot); - gplot->outname = stringNew(buf); - if (title) gplot->title = stringNew(title); - if (xlabel) gplot->xlabel = stringNew(xlabel); - if (ylabel) gplot->ylabel = stringNew(ylabel); - - return gplot; -} - - -/*! - * \brief gplotDestroy() - * - * \param[in,out] pgplot will be set to null before returning - */ -void -gplotDestroy(GPLOT **pgplot) -{ -GPLOT *gplot; - - PROCNAME("gplotDestroy"); - - if (pgplot == NULL) { - L_WARNING("ptr address is null!\n", procName); - return; - } - - if ((gplot = *pgplot) == NULL) - return; - - LEPT_FREE(gplot->rootname); - LEPT_FREE(gplot->cmdname); - sarrayDestroy(&gplot->cmddata); - sarrayDestroy(&gplot->datanames); - sarrayDestroy(&gplot->plotdata); - sarrayDestroy(&gplot->plotlabels); - numaDestroy(&gplot->plotstyles); - LEPT_FREE(gplot->outname); - if (gplot->title) - LEPT_FREE(gplot->title); - if (gplot->xlabel) - LEPT_FREE(gplot->xlabel); - if (gplot->ylabel) - LEPT_FREE(gplot->ylabel); - - LEPT_FREE(gplot); - *pgplot = NULL; - return; -} - - -/*! - * \brief gplotAddPlot() - * - * \param[in] gplot - * \param[in] nax [optional] numa: set to null for Y_VS_I; - * required for Y_VS_X - * \param[in] nay numa; required for both Y_VS_I and Y_VS_X - * \param[in] plotstyle GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES, - * GPLOT_LINESPOINTS, GPLOT_DOTS - * \param[in] plotlabel [optional] label for individual plot - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) There are 2 options for (x,y) values: - * o To plot an array vs a linear function of the - * index, set %nax = NULL. - * o To plot one array vs another, use both %nax and %nay. - * (2) If %nax is NULL, the x value corresponding to the i-th - * value of %nay is found from the startx and delx fields - * in %nay: - * x = startx + i * delx - * These are set with numaSetParameters(). Their default - * values are startx = 0.0, delx = 1.0. - * (3) If %nax is defined, it must be the same size as %nay, and - * must have at least one number. - * (4) The 'plotlabel' string can have spaces, double - * quotes and backquotes, but not single quotes. - *- */ -l_ok -gplotAddPlot(GPLOT *gplot, - NUMA *nax, - NUMA *nay, - l_int32 plotstyle, - const char *plotlabel) -{ -char buf[Bufsize]; -char emptystring[] = ""; -char *datastr, *title; -l_int32 n, i; -l_float32 valx, valy, startx, delx; -SARRAY *sa; - - PROCNAME("gplotAddPlot"); - - if (!gplot) - return ERROR_INT("gplot not defined", procName, 1); - if (!nay) - return ERROR_INT("nay not defined", procName, 1); - if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES) - return ERROR_INT("invalid plotstyle", procName, 1); - - if ((n = numaGetCount(nay)) == 0) - return ERROR_INT("no points to plot", procName, 1); - if (nax && (n != numaGetCount(nax))) - return ERROR_INT("nax and nay sizes differ", procName, 1); - if (n == 1 && plotstyle == GPLOT_LINES) { - L_INFO("only 1 pt; changing style to points\n", procName); - plotstyle = GPLOT_POINTS; - } - - /* Save plotstyle and plotlabel */ - numaGetParameters(nay, &startx, &delx); - numaAddNumber(gplot->plotstyles, plotstyle); - if (plotlabel) { - title = stringNew(plotlabel); - sarrayAddString(gplot->plotlabels, title, L_INSERT); - } else { - sarrayAddString(gplot->plotlabels, emptystring, L_COPY); - } - - /* Generate and save data filename */ - gplot->nplots++; - snprintf(buf, Bufsize, "%s.data.%d", gplot->rootname, gplot->nplots); - sarrayAddString(gplot->datanames, buf, L_COPY); - - /* Generate data and save as a string */ - sa = sarrayCreate(n); - for (i = 0; i < n; i++) { - if (nax) - numaGetFValue(nax, i, &valx); - else - valx = startx + i * delx; - numaGetFValue(nay, i, &valy); - snprintf(buf, Bufsize, "%f %f\n", valx, valy); - sarrayAddString(sa, buf, L_COPY); - } - datastr = sarrayToString(sa, 0); - sarrayAddString(gplot->plotdata, datastr, L_INSERT); - sarrayDestroy(&sa); - - return 0; -} - - -/*! - * \brief gplotSetScaling() - * - * \param[in] gplot - * \param[in] scaling GPLOT_LINEAR_SCALE, GPLOT_LOG_SCALE_X, - * GPLOT_LOG_SCALE_Y, GPLOT_LOG_SCALE_X_Y - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) By default, the x and y axis scaling is linear. - * (2) Call this function to set semi-log or log-log scaling. - *- */ -l_ok -gplotSetScaling(GPLOT *gplot, - l_int32 scaling) -{ - PROCNAME("gplotSetScaling"); - - if (!gplot) - return ERROR_INT("gplot not defined", procName, 1); - if (scaling != GPLOT_LINEAR_SCALE && - scaling != GPLOT_LOG_SCALE_X && - scaling != GPLOT_LOG_SCALE_Y && - scaling != GPLOT_LOG_SCALE_X_Y) - return ERROR_INT("invalid gplot scaling", procName, 1); - gplot->scaling = scaling; - return 0; -} - - -/*! - * \brief gplotMakeOutputPix() - * - * \param[in] gplot - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This wraps gplotMakeOutput(), and returns a pix. - * See gplotMakeOutput() for details. - * (2) The gplot output format must be an image (png or pnm). - *- */ -PIX * -gplotMakeOutputPix(GPLOT *gplot) -{ - PROCNAME("gplotMakeOutputPix"); - - if (!gplot) - return (PIX *)ERROR_PTR("gplot not defined", procName, NULL); - if (gplot->outformat != GPLOT_PNG && gplot->outformat != GPLOT_PNM) - return (PIX *)ERROR_PTR("output format not an image", procName, NULL); - - if (gplotMakeOutput(gplot)) - return (PIX *)ERROR_PTR("plot output not made", procName, NULL); - return pixRead(gplot->outname); -} - - -/*! - * \brief gplotMakeOutput() - * - * \param[in] gplot - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This uses gplot and the new arrays to add a plot - * to the output, by writing a new data file and appending - * the appropriate plot commands to the command file. - * (2) Along with gplotMakeOutputPix(), these are the only functions - * in this file that requires the gnuplot executable to - * actually generate the plot. - * (3) The command file name for unix is canonical (i.e., directory /tmp) - * but the temp filename paths in the command file must be correct. - * (4) The gnuplot program for windows is wgnuplot.exe. - *- */ -l_ok -gplotMakeOutput(GPLOT *gplot) -{ -#if WINAPI_FAMILY_APP || TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE - return ERROR_INT("gplot not defined", procName, 1); -#else -char buf[Bufsize]; -char *cmdname; - - PROCNAME("gplotMakeOutput"); - - if (!gplot) - return ERROR_INT("gplot not defined", procName, 1); - - if (!LeptDebugOK) { - L_INFO("running gnuplot is disabled; " - "use setLeptDebugOK(1) to enable\n", procName); - return 0; - } - -#ifdef OS_IOS /* iOS 11 does not support system() */ - return ERROR_INT("iOS 11 does not support system()", procName, 0); -#endif /* OS_IOS */ - - gplotGenCommandFile(gplot); - gplotGenDataFiles(gplot); - cmdname = genPathname(gplot->cmdname, NULL); - -#ifndef _WIN32 - snprintf(buf, Bufsize, "gnuplot %s", cmdname); -#else - snprintf(buf, Bufsize, "wgnuplot %s", cmdname); -#endif /* _WIN32 */ - - callSystemDebug(buf); /* gnuplot || wgnuplot */ - LEPT_FREE(cmdname); - return 0; -#endif -} - - -/*! - * \brief gplotGenCommandFile() - * - * \param[in] gplot - * \return 0 if OK, 1 on error - */ -l_ok -gplotGenCommandFile(GPLOT *gplot) -{ -char buf[Bufsize]; -char *cmdstr, *plotlabel, *dataname; -l_int32 i, plotstyle, nplots; -FILE *fp; - - PROCNAME("gplotGenCommandFile"); - - if (!gplot) - return ERROR_INT("gplot not defined", procName, 1); - - /* Remove any previous command data */ - sarrayClear(gplot->cmddata); - - /* Generate command data instructions */ - if (gplot->title) { /* set title */ - snprintf(buf, Bufsize, "set title '%s'", gplot->title); - sarrayAddString(gplot->cmddata, buf, L_COPY); - } - if (gplot->xlabel) { /* set xlabel */ - snprintf(buf, Bufsize, "set xlabel '%s'", gplot->xlabel); - sarrayAddString(gplot->cmddata, buf, L_COPY); - } - if (gplot->ylabel) { /* set ylabel */ - snprintf(buf, Bufsize, "set ylabel '%s'", gplot->ylabel); - sarrayAddString(gplot->cmddata, buf, L_COPY); - } - - /* Set terminal type and output */ - if (gplot->outformat == GPLOT_PNG) { - snprintf(buf, Bufsize, "set terminal png; set output '%s'", - gplot->outname); - } else if (gplot->outformat == GPLOT_PS) { - snprintf(buf, Bufsize, "set terminal postscript; set output '%s'", - gplot->outname); - } else if (gplot->outformat == GPLOT_EPS) { - snprintf(buf, Bufsize, "set terminal postscript eps; set output '%s'", - gplot->outname); - } else if (gplot->outformat == GPLOT_LATEX) { - snprintf(buf, Bufsize, "set terminal latex; set output '%s'", - gplot->outname); - } else if (gplot->outformat == GPLOT_PNM) { - snprintf(buf, Bufsize, "set terminal pbm color; set output '%s'", - gplot->outname); - } - sarrayAddString(gplot->cmddata, buf, L_COPY); - - if (gplot->scaling == GPLOT_LOG_SCALE_X || - gplot->scaling == GPLOT_LOG_SCALE_X_Y) { - snprintf(buf, Bufsize, "set logscale x"); - sarrayAddString(gplot->cmddata, buf, L_COPY); - } - if (gplot->scaling == GPLOT_LOG_SCALE_Y || - gplot->scaling == GPLOT_LOG_SCALE_X_Y) { - snprintf(buf, Bufsize, "set logscale y"); - sarrayAddString(gplot->cmddata, buf, L_COPY); - } - - nplots = sarrayGetCount(gplot->datanames); - for (i = 0; i < nplots; i++) { - plotlabel = sarrayGetString(gplot->plotlabels, i, L_NOCOPY); - dataname = sarrayGetString(gplot->datanames, i, L_NOCOPY); - numaGetIValue(gplot->plotstyles, i, &plotstyle); - if (nplots == 1) { - snprintf(buf, Bufsize, "plot '%s' title '%s' %s", - dataname, plotlabel, gplotstylenames[plotstyle]); - } else { - if (i == 0) - snprintf(buf, Bufsize, "plot '%s' title '%s' %s, \\", - dataname, plotlabel, gplotstylenames[plotstyle]); - else if (i < nplots - 1) - snprintf(buf, Bufsize, " '%s' title '%s' %s, \\", - dataname, plotlabel, gplotstylenames[plotstyle]); - else - snprintf(buf, Bufsize, " '%s' title '%s' %s", - dataname, plotlabel, gplotstylenames[plotstyle]); - } - sarrayAddString(gplot->cmddata, buf, L_COPY); - } - - /* Write command data to file */ - cmdstr = sarrayToString(gplot->cmddata, 1); - if ((fp = fopenWriteStream(gplot->cmdname, "w")) == NULL) { - LEPT_FREE(cmdstr); - return ERROR_INT("cmd stream not opened", procName, 1); - } - fwrite(cmdstr, 1, strlen(cmdstr), fp); - fclose(fp); - LEPT_FREE(cmdstr); - return 0; -} - - -/*! - * \brief gplotGenDataFiles() - * - * \param[in] gplot - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The pathnames in the gplot command file are actual pathnames, - * which can be in temp directories. Consequently, they must not be - * rewritten by calling fopenWriteStream(), and we use fopen(). - *- */ -l_ok -gplotGenDataFiles(GPLOT *gplot) -{ -char *plotdata, *dataname; -l_int32 i, nplots; -FILE *fp; - - PROCNAME("gplotGenDataFiles"); - - if (!gplot) - return ERROR_INT("gplot not defined", procName, 1); - - nplots = sarrayGetCount(gplot->datanames); - for (i = 0; i < nplots; i++) { - plotdata = sarrayGetString(gplot->plotdata, i, L_NOCOPY); - dataname = sarrayGetString(gplot->datanames, i, L_NOCOPY); - if ((fp = fopen(dataname, "w")) == NULL) - return ERROR_INT("datafile stream not opened", procName, 1); - fwrite(plotdata, 1, strlen(plotdata), fp); - fclose(fp); - } - - return 0; -} - - -/*-----------------------------------------------------------------* - * Quick one-line plots * - *-----------------------------------------------------------------*/ -/*! - * \brief gplotSimple1() - * - * \param[in] na numa; plot Y_VS_I - * \param[in] outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, - * GPLOT_LATEX, GPLOT_PNM - * \param[in] outroot root of output files - * \param[in] title [optional], can be NULL - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This generates a line plot of a numa, where the array value - * is plotted vs the array index. The plot is generated - * in the specified output format; the title is optional. - * (2) When calling these simple plot functions more than once, use - * different %outroot to avoid overwriting the output files. - *- */ -l_ok -gplotSimple1(NUMA *na, - l_int32 outformat, - const char *outroot, - const char *title) -{ -GPLOT *gplot; - - PROCNAME("gplotSimple1"); - - gplot = gplotSimpleXY1(NULL, na, GPLOT_LINES, outformat, outroot, title); - if (!gplot) - return ERROR_INT("failed to generate plot", procName, 1); - gplotDestroy(&gplot); - return 0; -} - - -/*! - * \brief gplotSimple2() - * - * \param[in] na1 numa; plot with Y_VS_I - * \param[in] na2 ditto - * \param[in] outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, - * GPLOT_LATEX, GPLOT_PNM - * \param[in] outroot root of output files - * \param[in] title [optional] - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This generates a line plot of two numa, where the array values - * are each plotted vs the array index. The plot is generated - * in the specified output format; the title is optional. - * (2) When calling these simple plot functions more than once, use - * different %outroot to avoid overwriting the output files. - *- */ -l_ok -gplotSimple2(NUMA *na1, - NUMA *na2, - l_int32 outformat, - const char *outroot, - const char *title) -{ -GPLOT *gplot; - - PROCNAME("gplotSimple2"); - - gplot = gplotSimpleXY2(NULL, na1, na2, GPLOT_LINES, - outformat, outroot, title); - if (!gplot) - return ERROR_INT("failed to generate plot", procName, 1); - gplotDestroy(&gplot); - return 0; -} - - -/*! - * \brief gplotSimpleN() - * - * \param[in] naa numaa; plot Y_VS_I for each numa - * \param[in] outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, - * GPLOT_LATEX, GPLOT_PNM - * \param[in] outroot root of output files - * \param[in] title [optional] - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This generates a line plot of all numas in a numaa (array of numa), - * where the array values are each plotted vs the array index. - * The plot is generated in the specified output format; - * the title is optional. - * (2) When calling these simple plot functions more than once, use - * different %outroot to avoid overwriting the output files. - *- */ -l_ok -gplotSimpleN(NUMAA *naa, - l_int32 outformat, - const char *outroot, - const char *title) -{ -GPLOT *gplot; - - PROCNAME("gplotSimpleN"); - - gplot = gplotSimpleXYN(NULL, naa, GPLOT_LINES, outformat, outroot, title); - if (!gplot) - return ERROR_INT("failed to generate plot", procName, 1); - gplotDestroy(&gplot); - return 0; -} - - -/*! - * \brief gplotSimplePix1() - * - * \param[in] na numa; plot Y_VS_I - * \param[in] title [optional], can be NULL - * \return pix of plot, or null on error - * - *
- * Notes: - * (1) This generates a line plot of a numa as a pix, where the array - * value is plotted vs the array index. The title is optional. - * (2) The temporary plot file is a png; its name is generated internally - * and stored in gplot. - *- */ -PIX * -gplotSimplePix1(NUMA *na, - const char *title) -{ -char buf[64]; -static l_int32 index; -GPLOT *gplot; -PIX *pix; - - PROCNAME("gplotSimplePix1"); - - if (!na) - return (PIX *)ERROR_PTR("na not defined", procName, NULL); - - lept_mkdir("lept/gplot/pix"); - snprintf(buf, sizeof(buf), "/tmp/lept/gplot/pix1.%d", index++); - gplot = gplotSimpleXY1(NULL, na, GPLOT_LINES, GPLOT_PNG, buf, title); - if (!gplot) - return (PIX *)ERROR_PTR("failed to generate plot", procName, NULL); - pix = pixRead(gplot->outname); - gplotDestroy(&gplot); - if (!pix) - return (PIX *)ERROR_PTR("failed to generate plot", procName, NULL); - return pix; -} - - -/*! - * \brief gplotSimplePix2() - * - * \param[in] na1 numa; plot with Y_VS_I - * \param[in] na2 ditto - * \param[in] title [optional], can be NULL - * \return pix of plot, or null on error - * - *
- * Notes: - * (1) This generates a pix with line plots of two numa, where each of - * two arrays is plotted vs the array index. the title is optional. - * (2) The temporary plot file is a png; its name is generated internally - * and stored in gplot. - *- */ -PIX * -gplotSimplePix2(NUMA *na1, - NUMA *na2, - const char *title) -{ -char buf[64]; -static l_int32 index; -GPLOT *gplot; -PIX *pix; - - PROCNAME("gplotSimplePix2"); - - if (!na1 || !na2) - return (PIX *)ERROR_PTR("both na1, na2 not defined", procName, NULL); - - lept_mkdir("lept/gplot/pix"); - snprintf(buf, sizeof(buf), "/tmp/lept/gplot/pix2.%d", index++); - gplot = gplotSimpleXY2(NULL, na1, na2, GPLOT_LINES, GPLOT_PNG, buf, title); - if (!gplot) - return (PIX *)ERROR_PTR("failed to generate plot", procName, NULL); - pix = pixRead(gplot->outname); - gplotDestroy(&gplot); - if (!pix) - return (PIX *)ERROR_PTR("failed to generate plot", procName, NULL); - return pix; -} - - -/*! - * \brief gplotSimplePixN() - * - * \param[in] naa numaa; plot Y_VS_I for each numa - * \param[in] title [optional], can be NULL - * \return pix of plot, or null on error - * - *
- * Notes: - * (1) This generates a pix with an arbitrary number of line plots, - * each coming from a numa in %naa. Each array value is plotted - * vs the array index. The title is optional. - * (2) The temporary plot file is a png; its name is generated internally - * and stored in gplot. - *- */ -PIX * -gplotSimplePixN(NUMAA *naa, - const char *title) -{ -char buf[64]; -static l_int32 index; -GPLOT *gplot; -PIX *pix; - - PROCNAME("gplotSimplePixN"); - - if (!naa) - return (PIX *)ERROR_PTR("naa not defined", procName, NULL); - - lept_mkdir("lept/gplot/pix"); - snprintf(buf, sizeof(buf), "/tmp/lept/gplot/pixN.%d", index++); - gplot = gplotSimpleXYN(NULL, naa, GPLOT_LINES, GPLOT_PNG, buf, title); - if (!gplot) - return (PIX *)ERROR_PTR("failed to generate plot", procName, NULL); - pix = pixRead(gplot->outname); - gplotDestroy(&gplot); - if (!pix) - return (PIX *)ERROR_PTR("failed to generate plot", procName, NULL); - return pix; -} - - -/*! - * \brief gplotSimpleXY1() - * - * \param[in] nax [optional] - * \param[in] nay [required] - * \param[in] plotstyle GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES, - * GPLOT_LINESPOINTS, GPLOT_DOTS - * \param[in] outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, - * GPLOT_LATEX, GPLOT_PNM - * \param[in] outroot root of output files - * \param[in] title [optional], can be NULL - * \return gplot or null on error - * - *
- * Notes: - * (1) This generates a plot of a %nay vs %nax, generated in - * the specified output format. The title is optional. - * (2) Use 0 for default plotstyle (lines). - * (3) %nax is optional. If NULL, %nay is plotted against - * the array index. - * (4) When calling these simple plot functions more than once, use - * different %outroot to avoid overwriting the output files. - * (5) The returned gplot must be destroyed by the caller. - *- */ -GPLOT * -gplotSimpleXY1(NUMA *nax, - NUMA *nay, - l_int32 plotstyle, - l_int32 outformat, - const char *outroot, - const char *title) -{ -GPLOT *gplot; - - PROCNAME("gplotSimpleXY1"); - - if (!nay) - return (GPLOT *)ERROR_PTR("nay not defined", procName, NULL); - if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES) - return (GPLOT *)ERROR_PTR("invalid plotstyle", procName, NULL); - if (outformat != GPLOT_PNG && outformat != GPLOT_PS && - outformat != GPLOT_EPS && outformat != GPLOT_LATEX && - outformat != GPLOT_PNM) - return (GPLOT *)ERROR_PTR("invalid outformat", procName, NULL); - if (!outroot) - return (GPLOT *)ERROR_PTR("outroot not specified", procName, NULL); - - if ((gplot = gplotCreate(outroot, outformat, title, NULL, NULL)) == 0) - return (GPLOT *)ERROR_PTR("gplot not made", procName, NULL); - gplotAddPlot(gplot, nax, nay, plotstyle, NULL); - gplotMakeOutput(gplot); - return gplot; -} - - -/*! - * \brief gplotSimpleXY2() - * - * \param[in] nax [optional], can be NULL - * \param[in] nay1 - * \param[in] nay2 - * \param[in] plotstyle GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES, - * GPLOT_LINESPOINTS, GPLOT_DOTS - * \param[in] outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, - * GPLOT_LATEX, GPLOT_PNM - * \param[in] outroot root of output files - * \param[in] title [optional] - * \return gplot or null on error - * - *
- * Notes: - * (1) This generates plots of %nay1 and %nay2 against %nax, generated - * in the specified output format. The title is optional. - * (2) Use 0 for default plotstyle (lines). - * (3) %nax is optional. If NULL, %nay1 and %nay2 are plotted - * against the array index. - * (4) When calling these simple plot functions more than once, use - * different %outroot to avoid overwriting the output files. - * (5) The returned gplot must be destroyed by the caller. - *- */ -GPLOT * -gplotSimpleXY2(NUMA *nax, - NUMA *nay1, - NUMA *nay2, - l_int32 plotstyle, - l_int32 outformat, - const char *outroot, - const char *title) -{ -GPLOT *gplot; - - PROCNAME("gplotSimpleXY2"); - - if (!nay1 || !nay2) - return (GPLOT *)ERROR_PTR("nay1 and nay2 not both defined", - procName, NULL); - if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES) - return (GPLOT *)ERROR_PTR("invalid plotstyle", procName, NULL); - if (outformat != GPLOT_PNG && outformat != GPLOT_PS && - outformat != GPLOT_EPS && outformat != GPLOT_LATEX && - outformat != GPLOT_PNM) - return (GPLOT *)ERROR_PTR("invalid outformat", procName, NULL); - if (!outroot) - return (GPLOT *)ERROR_PTR("outroot not specified", procName, NULL); - - if ((gplot = gplotCreate(outroot, outformat, title, NULL, NULL)) == 0) - return (GPLOT *)ERROR_PTR("gplot not made", procName, NULL); - gplotAddPlot(gplot, nax, nay1, plotstyle, NULL); - gplotAddPlot(gplot, nax, nay2, plotstyle, NULL); - gplotMakeOutput(gplot); - return gplot; -} - - -/*! - * \brief gplotSimpleXYN() - * - * \param[in] nax [optional]; can be NULL - * \param[in] naay numaa of arrays to plot against %nax - * \param[in] plotstyle GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES, - * GPLOT_LINESPOINTS, GPLOT_DOTS - * \param[in] outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, - * GPLOT_LATEX, GPLOT_PNM - * \param[in] outroot root of output files - * \param[in] title [optional] - * \return gplot or null on error - * - *
- * Notes: - * (1) This generates plots of each Numa in %naa against %nax, - * generated in the specified output format. The title is optional. - * (2) Use 0 for default plotstyle (lines). - * (3) %nax is optional. If NULL, each Numa array is plotted against - * the array index. - * (4) When calling these simple plot functions more than once, use - * different %outroot to avoid overwriting the output files. - * (5) The returned gplot must be destroyed by the caller. - *- */ -GPLOT * -gplotSimpleXYN(NUMA *nax, - NUMAA *naay, - l_int32 plotstyle, - l_int32 outformat, - const char *outroot, - const char *title) -{ -l_int32 i, n; -GPLOT *gplot; -NUMA *nay; - - PROCNAME("gplotSimpleXYN"); - - if (!naay) - return (GPLOT *)ERROR_PTR("naay not defined", procName, NULL); - if ((n = numaaGetCount(naay)) == 0) - return (GPLOT *)ERROR_PTR("no numa in array", procName, NULL); - if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES) - return (GPLOT *)ERROR_PTR("invalid plotstyle", procName, NULL); - if (outformat != GPLOT_PNG && outformat != GPLOT_PS && - outformat != GPLOT_EPS && outformat != GPLOT_LATEX && - outformat != GPLOT_PNM) - return (GPLOT *)ERROR_PTR("invalid outformat", procName, NULL); - if (!outroot) - return (GPLOT *)ERROR_PTR("outroot not specified", procName, NULL); - - if ((gplot = gplotCreate(outroot, outformat, title, NULL, NULL)) == 0) - return (GPLOT *)ERROR_PTR("gplot not made", procName, NULL); - for (i = 0; i < n; i++) { - nay = numaaGetNuma(naay, i, L_CLONE); - gplotAddPlot(gplot, nax, nay, plotstyle, NULL); - numaDestroy(&nay); - } - gplotMakeOutput(gplot); - return gplot; -} - - -/*! - * \brief gplotGeneralPix1() - * - * \param[in] na data array - * \param[in] plotstyle GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES, - * GPLOT_LINESPOINTS, GPLOT_DOTS - * \param[in] rootname root for all output files - * \param[in] title [optional] overall title - * \param[in] xlabel [optional] x axis label - * \param[in] ylabel [optional] y axis label - * \return pix of plot, or NULL on error - * - *
- * Notes: - * (1) The 'title', 'xlabel' and 'ylabel' strings can have spaces, - * double quotes and backquotes, but not single quotes. - *- */ -PIX * -gplotGeneralPix1(NUMA *na, - l_int32 plotstyle, - const char *rootname, - const char *title, - const char *xlabel, - const char *ylabel) -{ -GPLOT *gplot; -PIX *pix; - - PROCNAME("gplotGeneralPix1"); - - if (!na) - return (PIX *)ERROR_PTR("na not defined", procName, NULL); - if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES) - return (PIX *)ERROR_PTR("invalid plotstyle", procName, NULL); - if (!rootname) - return (PIX *)ERROR_PTR("rootname not defined", procName, NULL); - - gplot = gplotCreate(rootname, GPLOT_PNG, title, xlabel, ylabel); - if (!gplot) - return (PIX *)ERROR_PTR("gplot not made", procName, NULL); - gplotAddPlot(gplot, NULL, na, plotstyle, NULL); - pix = gplotMakeOutputPix(gplot); - gplotDestroy(&gplot); - return pix; -} - - -/*! - * \brief gplotGeneralPix2() - * - * \param[in] na1 x-axis data array - * \param[in] na2 y-axis data array - * \param[in] plotstyle GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES, - * GPLOT_LINESPOINTS, GPLOT_DOTS - * \param[in] rootname root for all output files - * \param[in] title [optional] overall title - * \param[in] xlabel [optional] x axis label - * \param[in] ylabel [optional] y axis label - * \return pix of plot, or NULL on error - * - *
- * Notes: - * (1) The 'title', 'xlabel' and 'ylabel' strings can have spaces, - * double quotes and backquotes, but not single quotes. - *- */ -PIX * -gplotGeneralPix2(NUMA *na1, - NUMA *na2, - l_int32 plotstyle, - const char *rootname, - const char *title, - const char *xlabel, - const char *ylabel) -{ -GPLOT *gplot; -PIX *pix; - - PROCNAME("gplotGeneralPix2"); - - if (!na1) - return (PIX *)ERROR_PTR("na1 not defined", procName, NULL); - if (!na2) - return (PIX *)ERROR_PTR("na2 not defined", procName, NULL); - if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES) - return (PIX *)ERROR_PTR("invalid plotstyle", procName, NULL); - if (!rootname) - return (PIX *)ERROR_PTR("rootname not defined", procName, NULL); - - gplot = gplotCreate(rootname, GPLOT_PNG, title, xlabel, ylabel); - if (!gplot) - return (PIX *)ERROR_PTR("gplot not made", procName, NULL); - gplotAddPlot(gplot, na1, na2, plotstyle, NULL); - pix = gplotMakeOutputPix(gplot); - gplotDestroy(&gplot); - return pix; -} - - -/*! - * \brief gplotGeneralPixN() - * - * \param[in] nax x-axis data array - * \param[in] naay array of y-axis data arrays - * \param[in] plotstyle GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES, - * GPLOT_LINESPOINTS, GPLOT_DOTS - * \param[in] rootname root for all output files - * \param[in] title [optional] overall title - * \param[in] xlabel [optional] x axis label - * \param[in] ylabel [optional] y axis label - * \return pix of plot, or NULL on error - * - *
- * Notes: - * (1) The 'title', 'xlabel' and 'ylabel' strings can have spaces, - * double quotes and backquotes, but not single quotes. - *- */ -PIX * -gplotGeneralPixN(NUMA *nax, - NUMAA *naay, - l_int32 plotstyle, - const char *rootname, - const char *title, - const char *xlabel, - const char *ylabel) -{ -l_int32 i, n; -GPLOT *gplot; -NUMA *nay; -PIX *pix; - - PROCNAME("gplotGeneralPixN"); - - if (!nax) - return (PIX *)ERROR_PTR("nax not defined", procName, NULL); - if (!naay) - return (PIX *)ERROR_PTR("naay not defined", procName, NULL); - if ((n = numaaGetCount(naay)) == 0) - return (PIX *)ERROR_PTR("no numa in array", procName, NULL); - if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES) - return (PIX *)ERROR_PTR("invalid plotstyle", procName, NULL); - if (!rootname) - return (PIX *)ERROR_PTR("rootname not defined", procName, NULL); - - gplot = gplotCreate(rootname, GPLOT_PNG, title, xlabel, ylabel); - if (!gplot) - return (PIX *)ERROR_PTR("gplot not made", procName, NULL); - for (i = 0; i < n; i++) { - nay = numaaGetNuma(naay, i, L_CLONE); - gplotAddPlot(gplot, nax, nay, plotstyle, NULL); - numaDestroy(&nay); - } - pix = gplotMakeOutputPix(gplot); - gplotDestroy(&gplot); - return pix; -} - - -/*-----------------------------------------------------------------* - * Serialize for I/O * - *-----------------------------------------------------------------*/ -/*! - * \brief gplotRead() - * - * \param[in] filename - * \return gplot, or NULL on error - */ -GPLOT * -gplotRead(const char *filename) -{ -char buf[Bufsize]; -char *rootname, *title, *xlabel, *ylabel, *ignores; -l_int32 outformat, ret, version, ignore; -FILE *fp; -GPLOT *gplot; - - PROCNAME("gplotRead"); - - if (!filename) - return (GPLOT *)ERROR_PTR("filename not defined", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (GPLOT *)ERROR_PTR("stream not opened", procName, NULL); - - ret = fscanf(fp, "Gplot Version %d\n", &version); - if (ret != 1) { - fclose(fp); - return (GPLOT *)ERROR_PTR("not a gplot file", procName, NULL); - } - if (version != GPLOT_VERSION_NUMBER) { - fclose(fp); - return (GPLOT *)ERROR_PTR("invalid gplot version", procName, NULL); - } - - ignore = fscanf(fp, "Rootname: %511s\n", buf); /* Bufsize - 1 */ - rootname = stringNew(buf); - ignore = fscanf(fp, "Output format: %d\n", &outformat); - ignores = fgets(buf, Bufsize, fp); /* Title: ... */ - title = stringNew(buf + 7); - title[strlen(title) - 1] = '\0'; - ignores = fgets(buf, Bufsize, fp); /* X axis label: ... */ - xlabel = stringNew(buf + 14); - xlabel[strlen(xlabel) - 1] = '\0'; - ignores = fgets(buf, Bufsize, fp); /* Y axis label: ... */ - ylabel = stringNew(buf + 14); - ylabel[strlen(ylabel) - 1] = '\0'; - - gplot = gplotCreate(rootname, outformat, title, xlabel, ylabel); - LEPT_FREE(rootname); - LEPT_FREE(title); - LEPT_FREE(xlabel); - LEPT_FREE(ylabel); - if (!gplot) { - fclose(fp); - return (GPLOT *)ERROR_PTR("gplot not made", procName, NULL); - } - sarrayDestroy(&gplot->cmddata); - sarrayDestroy(&gplot->datanames); - sarrayDestroy(&gplot->plotdata); - sarrayDestroy(&gplot->plotlabels); - numaDestroy(&gplot->plotstyles); - - ignore = fscanf(fp, "Commandfile name: %511s\n", buf); /* Bufsize - 1 */ - stringReplace(&gplot->cmdname, buf); - ignore = fscanf(fp, "\nCommandfile data:"); - gplot->cmddata = sarrayReadStream(fp); - ignore = fscanf(fp, "\nDatafile names:"); - gplot->datanames = sarrayReadStream(fp); - ignore = fscanf(fp, "\nPlot data:"); - gplot->plotdata = sarrayReadStream(fp); - ignore = fscanf(fp, "\nPlot titles:"); - gplot->plotlabels = sarrayReadStream(fp); - ignore = fscanf(fp, "\nPlot styles:"); - gplot->plotstyles = numaReadStream(fp); - - ignore = fscanf(fp, "Number of plots: %d\n", &gplot->nplots); - ignore = fscanf(fp, "Output file name: %511s\n", buf); - stringReplace(&gplot->outname, buf); - ignore = fscanf(fp, "Axis scaling: %d\n", &gplot->scaling); - - fclose(fp); - return gplot; -} - - -/*! - * \brief gplotWrite() - * - * \param[in] filename - * \param[in] gplot - * \return 0 if OK; 1 on error - */ -l_ok -gplotWrite(const char *filename, - GPLOT *gplot) -{ -FILE *fp; - - PROCNAME("gplotWrite"); - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!gplot) - return ERROR_INT("gplot not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "wb")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - - fprintf(fp, "Gplot Version %d\n", GPLOT_VERSION_NUMBER); - fprintf(fp, "Rootname: %s\n", gplot->rootname); - fprintf(fp, "Output format: %d\n", gplot->outformat); - fprintf(fp, "Title: %s\n", gplot->title); - fprintf(fp, "X axis label: %s\n", gplot->xlabel); - fprintf(fp, "Y axis label: %s\n", gplot->ylabel); - - fprintf(fp, "Commandfile name: %s\n", gplot->cmdname); - fprintf(fp, "\nCommandfile data:"); - sarrayWriteStream(fp, gplot->cmddata); - fprintf(fp, "\nDatafile names:"); - sarrayWriteStream(fp, gplot->datanames); - fprintf(fp, "\nPlot data:"); - sarrayWriteStream(fp, gplot->plotdata); - fprintf(fp, "\nPlot titles:"); - sarrayWriteStream(fp, gplot->plotlabels); - fprintf(fp, "\nPlot styles:"); - numaWriteStderr(gplot->plotstyles); - - fprintf(fp, "Number of plots: %d\n", gplot->nplots); - fprintf(fp, "Output file name: %s\n", gplot->outname); - fprintf(fp, "Axis scaling: %d\n", gplot->scaling); - - fclose(fp); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/gplot.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/gplot.h deleted file mode 100644 index d2e4f7e5..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/gplot.h +++ /dev/null @@ -1,96 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_GPLOT_H -#define LEPTONICA_GPLOT_H - -/*! - * \file gplot.h - * - *
- * Data structures and parameters for generating gnuplot files - * - * We used to support X11 output, but recent versions of gnuplot do not - * support the X11 terminal. To get display to your screen, use - * GPLOT_PNG output; e.g., - * gplotSimple1(na, GPLOT_PNG, "/tmp/someroot", ...); - * l_fileDisplay("/tmp/someroot.png", ...); - *- */ - -#define GPLOT_VERSION_NUMBER 1 - -#define NUM_GPLOT_STYLES 5 -enum GPLOT_STYLE { - GPLOT_LINES = 0, - GPLOT_POINTS = 1, - GPLOT_IMPULSES = 2, - GPLOT_LINESPOINTS = 3, - GPLOT_DOTS = 4 -}; - -#define NUM_GPLOT_OUTPUTS 6 -enum GPLOT_OUTPUT { - GPLOT_NONE = 0, - GPLOT_PNG = 1, - GPLOT_PS = 2, - GPLOT_EPS = 3, - GPLOT_LATEX = 4, - GPLOT_PNM = 5, -}; - -enum GPLOT_SCALING { - GPLOT_LINEAR_SCALE = 0, /*!< default */ - GPLOT_LOG_SCALE_X = 1, - GPLOT_LOG_SCALE_Y = 2, - GPLOT_LOG_SCALE_X_Y = 3 -}; - -extern const char *gplotstylenames[]; /*!< used in gnuplot cmd file */ -extern const char *gplotfileoutputs[]; /*!< used in simple file input */ - -/*! Data structure for generating gnuplot files */ -struct GPlot -{ - char *rootname; /*!< for cmd, data, output */ - char *cmdname; /*!< command file name */ - struct Sarray *cmddata; /*!< command file contents */ - struct Sarray *datanames; /*!< data file names */ - struct Sarray *plotdata; /*!< plot data (1 string/file) */ - struct Sarray *plotlabels; /*!< label for each individual plot */ - struct Numa *plotstyles; /*!< plot style for individual plots */ - l_int32 nplots; /*!< current number of plots */ - char *outname; /*!< output file name */ - l_int32 outformat; /*!< GPLOT_OUTPUT values */ - l_int32 scaling; /*!< GPLOT_SCALING values */ - char *title; /*!< optional */ - char *xlabel; /*!< optional x axis label */ - char *ylabel; /*!< optional y axis label */ -}; -typedef struct GPlot GPLOT; - - -#endif /* LEPTONICA_GPLOT_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/graphics.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/graphics.c deleted file mode 100644 index 91057499..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/graphics.c +++ /dev/null @@ -1,2903 +0,0 @@ -/*====================================================================* - - 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 graphics.c - *
- * - * Pta generation for arbitrary shapes built with lines - * PTA *generatePtaLine() - * PTA *generatePtaWideLine() - * PTA *generatePtaBox() - * PTA *generatePtaBoxa() - * PTA *generatePtaHashBox() - * PTA *generatePtaHashBoxa() - * PTAA *generatePtaaBoxa() - * PTAA *generatePtaaHashBoxa() - * PTA *generatePtaPolyline() - * PTA *generatePtaGrid() - * PTA *convertPtaLineTo4cc() - * PTA *generatePtaFilledCircle() - * PTA *generatePtaFilledSquare() - * PTA *generatePtaLineFromPt() - * l_int32 locatePtRadially() - * - * Rendering function plots directly on images - * l_int32 pixRenderPlotFromNuma() - * l_int32 pixRenderPlotFromNumaGen() - * PTA *makePlotPtaFromNuma() - * PTA *makePlotPtaFromNumaGen() - * - * Pta rendering - * l_int32 pixRenderPta() - * l_int32 pixRenderPtaArb() - * l_int32 pixRenderPtaBlend() - * - * Rendering of arbitrary shapes built with lines - * l_int32 pixRenderLine() - * l_int32 pixRenderLineArb() - * l_int32 pixRenderLineBlend() - * - * l_int32 pixRenderBox() - * l_int32 pixRenderBoxArb() - * l_int32 pixRenderBoxBlend() - * - * l_int32 pixRenderBoxa() - * l_int32 pixRenderBoxaArb() - * l_int32 pixRenderBoxaBlend() - * - * l_int32 pixRenderHashBox() - * l_int32 pixRenderHashBoxArb() - * l_int32 pixRenderHashBoxBlend() - * l_int32 pixRenderHashMaskArb() - * - * l_int32 pixRenderHashBoxa() - * l_int32 pixRenderHashBoxaArb() - * l_int32 pixRenderHashBoxaBlend() - * - * l_int32 pixRenderPolyline() - * l_int32 pixRenderPolylineArb() - * l_int32 pixRenderPolylineBlend() - * - * l_int32 pixRenderGrid() - * - * l_int32 pixRenderRandomCmapPtaa() - * - * Rendering and filling of polygons - * PIX *pixRenderPolygon() - * PIX *pixFillPolygon() - * - * Contour rendering on grayscale images - * PIX *pixRenderContours() - * PIX *fpixAutoRenderContours() - * PIX *fpixRenderContours() - * - * Boundary pt generation on 1 bpp images - * PTA *pixGeneratePtaBoundary() - * - * The line rendering functions are relatively crude, but they - * get the job done for most simple situations. We use the pta - * (array of points) as an intermediate data structure. For example, - * to render a line we first generate a pta. - * - * Some rendering functions come in sets of three. For example - * pixRenderLine() -- render on 1 bpp pix - * pixRenderLineArb() -- render on 32 bpp pix with arbitrary (r,g,b) - * pixRenderLineBlend() -- render on 32 bpp pix, blending the - * (r,g,b) graphic object with the underlying rgb pixels. - * - * There are also procedures for plotting a function, computed - * from the row or column pixels, directly on the image. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) Uses Bresenham line drawing, which results in an 8-connected line. - *- */ -PTA * -generatePtaLine(l_int32 x1, - l_int32 y1, - l_int32 x2, - l_int32 y2) -{ -l_int32 npts, diff, getyofx, sign, i, x, y; -l_float32 slope; -PTA *pta; - - PROCNAME("generatePtaLine"); - - /* Generate line parameters */ - if (x1 == x2 && y1 == y2) { /* same point */ - getyofx = TRUE; - npts = 1; - } else if (L_ABS(x2 - x1) >= L_ABS(y2 - y1)) { - getyofx = TRUE; - npts = L_ABS(x2 - x1) + 1; - diff = x2 - x1; - sign = L_SIGN(x2 - x1); - slope = (l_float32)(sign * (y2 - y1)) / (l_float32)diff; - } else { - getyofx = FALSE; - npts = L_ABS(y2 - y1) + 1; - diff = y2 - y1; - sign = L_SIGN(y2 - y1); - slope = (l_float32)(sign * (x2 - x1)) / (l_float32)diff; - } - - if ((pta = ptaCreate(npts)) == NULL) - return (PTA *)ERROR_PTR("pta not made", procName, NULL); - - if (npts == 1) { /* degenerate case */ - ptaAddPt(pta, x1, y1); - return pta; - } - - /* Generate the set of points */ - if (getyofx) { /* y = y(x) */ - for (i = 0; i < npts; i++) { - x = x1 + sign * i; - y = (l_int32)(y1 + (l_float32)i * slope + 0.5); - ptaAddPt(pta, x, y); - } - } else { /* x = x(y) */ - for (i = 0; i < npts; i++) { - x = (l_int32)(x1 + (l_float32)i * slope + 0.5); - y = y1 + sign * i; - ptaAddPt(pta, x, y); - } - } - - return pta; -} - - -/*! - * \brief generatePtaWideLine() - * - * \param[in] x1, y1 end point 1 - * \param[in] x2, y2 end point 2 - * \param[in] width - * \return ptaj, or NULL on error - */ -PTA * -generatePtaWideLine(l_int32 x1, - l_int32 y1, - l_int32 x2, - l_int32 y2, - l_int32 width) -{ -l_int32 i, x1a, x2a, y1a, y2a; -PTA *pta, *ptaj; - - PROCNAME("generatePtaWideLine"); - - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - - if ((ptaj = generatePtaLine(x1, y1, x2, y2)) == NULL) - return (PTA *)ERROR_PTR("ptaj not made", procName, NULL); - if (width == 1) - return ptaj; - - /* width > 1; estimate line direction & join */ - if (L_ABS(x1 - x2) > L_ABS(y1 - y2)) { /* "horizontal" line */ - for (i = 1; i < width; i++) { - if ((i & 1) == 1) { /* place above */ - y1a = y1 - (i + 1) / 2; - y2a = y2 - (i + 1) / 2; - } else { /* place below */ - y1a = y1 + (i + 1) / 2; - y2a = y2 + (i + 1) / 2; - } - if ((pta = generatePtaLine(x1, y1a, x2, y2a)) != NULL) { - ptaJoin(ptaj, pta, 0, -1); - ptaDestroy(&pta); - } - } - } else { /* "vertical" line */ - for (i = 1; i < width; i++) { - if ((i & 1) == 1) { /* place to left */ - x1a = x1 - (i + 1) / 2; - x2a = x2 - (i + 1) / 2; - } else { /* place to right */ - x1a = x1 + (i + 1) / 2; - x2a = x2 + (i + 1) / 2; - } - if ((pta = generatePtaLine(x1a, y1, x2a, y2)) != NULL) { - ptaJoin(ptaj, pta, 0, -1); - ptaDestroy(&pta); - } - } - } - - return ptaj; -} - - -/*! - * \brief generatePtaBox() - * - * \param[in] box - * \param[in] width of line - * \return ptad, or NULL on error - * - *
- * Notes: - * (1) Because the box is constructed so that we don't have any - * overlapping lines, there is no need to remove duplicates. - *- */ -PTA * -generatePtaBox(BOX *box, - l_int32 width) -{ -l_int32 x, y, w, h; -PTA *ptad, *pta; - - PROCNAME("generatePtaBox"); - - if (!box) - return (PTA *)ERROR_PTR("box not defined", procName, NULL); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - - /* Generate line points and add them to the pta. */ - boxGetGeometry(box, &x, &y, &w, &h); - if (w == 0 || h == 0) - return (PTA *)ERROR_PTR("box has w = 0 or h = 0", procName, NULL); - ptad = ptaCreate(0); - if ((width & 1) == 1) { /* odd width */ - pta = generatePtaWideLine(x - width / 2, y, - x + w - 1 + width / 2, y, width); - ptaJoin(ptad, pta, 0, -1); - ptaDestroy(&pta); - pta = generatePtaWideLine(x + w - 1, y + 1 + width / 2, - x + w - 1, y + h - 2 - width / 2, width); - ptaJoin(ptad, pta, 0, -1); - ptaDestroy(&pta); - pta = generatePtaWideLine(x + w - 1 + width / 2, y + h - 1, - x - width / 2, y + h - 1, width); - ptaJoin(ptad, pta, 0, -1); - ptaDestroy(&pta); - pta = generatePtaWideLine(x, y + h - 2 - width / 2, - x, y + 1 + width / 2, width); - ptaJoin(ptad, pta, 0, -1); - ptaDestroy(&pta); - } else { /* even width */ - pta = generatePtaWideLine(x - width / 2, y, - x + w - 2 + width / 2, y, width); - ptaJoin(ptad, pta, 0, -1); - ptaDestroy(&pta); - pta = generatePtaWideLine(x + w - 1, y + 0 + width / 2, - x + w - 1, y + h - 2 - width / 2, width); - ptaJoin(ptad, pta, 0, -1); - ptaDestroy(&pta); - pta = generatePtaWideLine(x + w - 2 + width / 2, y + h - 1, - x - width / 2, y + h - 1, width); - ptaJoin(ptad, pta, 0, -1); - ptaDestroy(&pta); - pta = generatePtaWideLine(x, y + h - 2 - width / 2, - x, y + 0 + width / 2, width); - ptaJoin(ptad, pta, 0, -1); - ptaDestroy(&pta); - } - - return ptad; -} - - -/*! - * \brief generatePtaBoxa() - * - * \param[in] boxa - * \param[in] width - * \param[in] removedups 1 to remove, 0 to leave - * \return ptad, or NULL on error - * - *
- * Notes: - * (1) If %boxa has overlapping boxes, and if blending will - * be used to give a transparent effect, transparency - * artifacts at line intersections can be removed using - * %removedups = 1. - *- */ -PTA * -generatePtaBoxa(BOXA *boxa, - l_int32 width, - l_int32 removedups) -{ -l_int32 i, n; -BOX *box; -PTA *ptad, *ptat, *pta; - - PROCNAME("generatePtaBoxa"); - - if (!boxa) - return (PTA *)ERROR_PTR("boxa not defined", procName, NULL); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - - n = boxaGetCount(boxa); - ptat = ptaCreate(0); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - pta = generatePtaBox(box, width); - ptaJoin(ptat, pta, 0, -1); - ptaDestroy(&pta); - boxDestroy(&box); - } - - if (removedups) - ptad = ptaRemoveDupsByAset(ptat); - else - ptad = ptaClone(ptat); - - ptaDestroy(&ptat); - return ptad; -} - - -/*! - * \brief generatePtaHashBox() - * - * \param[in] box - * \param[in] spacing spacing between lines; must be > 1 - * \param[in] width of line - * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, - * L_POS_SLOPE_LINE, L_VERTICAL_LINE, - * L_NEG_SLOPE_LINE - * \param[in] outline 0 to skip drawing box outline - * \return ptad, or NULL on error - * - *
- * Notes: - * (1) The orientation takes on one of 4 orientations (horiz, vertical, - * slope +1, slope -1). - * (2) The full outline is also drawn if %outline = 1. - *- */ -PTA * -generatePtaHashBox(BOX *box, - l_int32 spacing, - l_int32 width, - l_int32 orient, - l_int32 outline) -{ -l_int32 bx, by, bh, bw, x, y, x1, y1, x2, y2, i, n, npts; -PTA *ptad, *pta; - - PROCNAME("generatePtaHashBox"); - - if (!box) - return (PTA *)ERROR_PTR("box not defined", procName, NULL); - if (spacing <= 1) - return (PTA *)ERROR_PTR("spacing not > 1", procName, NULL); - if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && - orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) - return (PTA *)ERROR_PTR("invalid line orientation", procName, NULL); - boxGetGeometry(box, &bx, &by, &bw, &bh); - if (bw == 0 || bh == 0) - return (PTA *)ERROR_PTR("box has bw = 0 or bh = 0", procName, NULL); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - - /* Generate line points and add them to the pta. */ - ptad = ptaCreate(0); - if (outline) { - pta = generatePtaBox(box, width); - ptaJoin(ptad, pta, 0, -1); - ptaDestroy(&pta); - } - if (orient == L_HORIZONTAL_LINE) { - n = 1 + bh / spacing; - for (i = 0; i < n; i++) { - y = by + (i * (bh - 1)) / (n - 1); - pta = generatePtaWideLine(bx, y, bx + bw - 1, y, width); - ptaJoin(ptad, pta, 0, -1); - ptaDestroy(&pta); - } - } else if (orient == L_VERTICAL_LINE) { - n = 1 + bw / spacing; - for (i = 0; i < n; i++) { - x = bx + (i * (bw - 1)) / (n - 1); - pta = generatePtaWideLine(x, by, x, by + bh - 1, width); - ptaJoin(ptad, pta, 0, -1); - ptaDestroy(&pta); - } - } else if (orient == L_POS_SLOPE_LINE) { - n = 2 + (l_int32)((bw + bh) / (1.4 * spacing)); - for (i = 0; i < n; i++) { - x = (l_int32)(bx + (i + 0.5) * 1.4 * spacing); - boxIntersectByLine(box, x, by - 1, 1.0, &x1, &y1, &x2, &y2, &npts); - if (npts == 2) { - pta = generatePtaWideLine(x1, y1, x2, y2, width); - ptaJoin(ptad, pta, 0, -1); - ptaDestroy(&pta); - } - } - } else { /* orient == L_NEG_SLOPE_LINE */ - n = 2 + (l_int32)((bw + bh) / (1.4 * spacing)); - for (i = 0; i < n; i++) { - x = (l_int32)(bx - bh + (i + 0.5) * 1.4 * spacing); - boxIntersectByLine(box, x, by - 1, -1.0, &x1, &y1, &x2, &y2, &npts); - if (npts == 2) { - pta = generatePtaWideLine(x1, y1, x2, y2, width); - ptaJoin(ptad, pta, 0, -1); - ptaDestroy(&pta); - } - } - } - - return ptad; -} - - -/*! - * \brief generatePtaHashBoxa() - * - * \param[in] boxa - * \param[in] spacing spacing between lines; must be > 1 - * \param[in] width of line - * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, ... - * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, - * L_POS_SLOPE_LINE, L_VERTICAL_LINE, - * L_NEG_SLOPE_LINE - * \param[in] outline 0 to skip drawing box outline - * \param[in] removedups 1 to remove, 0 to leave - * \return ptad, or NULL on error - * - *
- * Notes: - * (1) The orientation takes on one of 4 orientations (horiz, vertical, - * slope +1, slope -1). - * (2) The full outline is also drawn if %outline = 1. - * (3) If the boxa has overlapping boxes, and if blending will - * be used to give a transparent effect, transparency - * artifacts at line intersections can be removed using - * %removedups = 1. - *- */ -PTA * -generatePtaHashBoxa(BOXA *boxa, - l_int32 spacing, - l_int32 width, - l_int32 orient, - l_int32 outline, - l_int32 removedups) -{ -l_int32 i, n; -BOX *box; -PTA *ptad, *ptat, *pta; - - PROCNAME("generatePtaHashBoxa"); - - if (!boxa) - return (PTA *)ERROR_PTR("boxa not defined", procName, NULL); - if (spacing <= 1) - return (PTA *)ERROR_PTR("spacing not > 1", procName, NULL); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && - orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) - return (PTA *)ERROR_PTR("invalid line orientation", procName, NULL); - - n = boxaGetCount(boxa); - ptat = ptaCreate(0); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - pta = generatePtaHashBox(box, spacing, width, orient, outline); - ptaJoin(ptat, pta, 0, -1); - ptaDestroy(&pta); - boxDestroy(&box); - } - - if (removedups) - ptad = ptaRemoveDupsByAset(ptat); - else - ptad = ptaClone(ptat); - - ptaDestroy(&ptat); - return ptad; -} - - -/*! - * \brief generatePtaaBoxa() - * - * \param[in] boxa - * \return ptaa, or NULL on error - * - *
- * Notes: - * (1) This generates a pta of the four corners for each box in - * the boxa. - * (2) Each of these pta can be rendered onto a pix with random colors, - * by using pixRenderRandomCmapPtaa() with closeflag = 1. - *- */ -PTAA * -generatePtaaBoxa(BOXA *boxa) -{ -l_int32 i, n, x, y, w, h; -BOX *box; -PTA *pta; -PTAA *ptaa; - - PROCNAME("generatePtaaBoxa"); - - if (!boxa) - return (PTAA *)ERROR_PTR("boxa not defined", procName, NULL); - - n = boxaGetCount(boxa); - ptaa = ptaaCreate(n); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - boxGetGeometry(box, &x, &y, &w, &h); - pta = ptaCreate(4); - ptaAddPt(pta, x, y); - ptaAddPt(pta, x + w - 1, y); - ptaAddPt(pta, x + w - 1, y + h - 1); - ptaAddPt(pta, x, y + h - 1); - ptaaAddPta(ptaa, pta, L_INSERT); - boxDestroy(&box); - } - - return ptaa; -} - - -/*! - * \brief generatePtaaHashBoxa() - * - * \param[in] boxa - * \param[in] spacing spacing between hash lines; must be > 1 - * \param[in] width hash line width - * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, - * L_POS_SLOPE_LINE, L_VERTICAL_LINE, - * L_NEG_SLOPE_LINE - * \param[in] outline 0 to skip drawing box outline - * \return ptaa, or NULL on error - * - *
- * Notes: - * (1) The orientation takes on one of 4 orientations (horiz, vertical, - * slope +1, slope -1). - * (2) The full outline is also drawn if %outline = 1. - * (3) Each of these pta can be rendered onto a pix with random colors, - * by using pixRenderRandomCmapPtaa() with closeflag = 1. - * - *- */ -PTAA * -generatePtaaHashBoxa(BOXA *boxa, - l_int32 spacing, - l_int32 width, - l_int32 orient, - l_int32 outline) -{ -l_int32 i, n; -BOX *box; -PTA *pta; -PTAA *ptaa; - - PROCNAME("generatePtaaHashBoxa"); - - if (!boxa) - return (PTAA *)ERROR_PTR("boxa not defined", procName, NULL); - if (spacing <= 1) - return (PTAA *)ERROR_PTR("spacing not > 1", procName, NULL); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && - orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) - return (PTAA *)ERROR_PTR("invalid line orientation", procName, NULL); - - n = boxaGetCount(boxa); - ptaa = ptaaCreate(n); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - pta = generatePtaHashBox(box, spacing, width, orient, outline); - ptaaAddPta(ptaa, pta, L_INSERT); - boxDestroy(&box); - } - - return ptaa; -} - - -/*! - * \brief generatePtaPolyline() - * - * \param[in] ptas vertices of polyline - * \param[in] width - * \param[in] closeflag 1 to close the contour; 0 otherwise - * \param[in] removedups 1 to remove, 0 to leave - * \return ptad, or NULL on error - */ -PTA * -generatePtaPolyline(PTA *ptas, - l_int32 width, - l_int32 closeflag, - l_int32 removedups) -{ -l_int32 i, n, x1, y1, x2, y2; -PTA *ptad, *ptat, *pta; - - PROCNAME("generatePtaPolyline"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - - n = ptaGetCount(ptas); - ptat = ptaCreate(0); - if (n < 2) /* nothing to do */ - return ptat; - - ptaGetIPt(ptas, 0, &x1, &y1); - for (i = 1; i < n; i++) { - ptaGetIPt(ptas, i, &x2, &y2); - pta = generatePtaWideLine(x1, y1, x2, y2, width); - ptaJoin(ptat, pta, 0, -1); - ptaDestroy(&pta); - x1 = x2; - y1 = y2; - } - - if (closeflag) { - ptaGetIPt(ptas, 0, &x2, &y2); - pta = generatePtaWideLine(x1, y1, x2, y2, width); - ptaJoin(ptat, pta, 0, -1); - ptaDestroy(&pta); - } - - if (removedups) - ptad = ptaRemoveDupsByAset(ptat); - else - ptad = ptaClone(ptat); - - ptaDestroy(&ptat); - return ptad; -} - - -/*! - * \brief generatePtaGrid() - * - * \param[in] w, h of region where grid will be displayed - * \param[in] nx, ny number of rectangles in each direction in grid - * \param[in] width of rendered lines - * \return ptad, or NULL on error - */ -PTA * -generatePtaGrid(l_int32 w, - l_int32 h, - l_int32 nx, - l_int32 ny, - l_int32 width) -{ -l_int32 i, j, bx, by, x1, x2, y1, y2; -BOX *box; -BOXA *boxa; -PTA *pta; - - PROCNAME("generatePtaGrid"); - - if (nx < 1 || ny < 1) - return (PTA *)ERROR_PTR("nx and ny must be > 0", procName, NULL); - if (w < 2 * nx || h < 2 * ny) - return (PTA *)ERROR_PTR("w and/or h too small", procName, NULL); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - - boxa = boxaCreate(nx * ny); - bx = (w + nx - 1) / nx; - by = (h + ny - 1) / ny; - for (i = 0; i < ny; i++) { - y1 = by * i; - y2 = L_MIN(y1 + by, h - 1); - for (j = 0; j < nx; j++) { - x1 = bx * j; - x2 = L_MIN(x1 + bx, w - 1); - box = boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1); - boxaAddBox(boxa, box, L_INSERT); - } - } - - pta = generatePtaBoxa(boxa, width, 1); - boxaDestroy(&boxa); - return pta; -} - - -/*! - * \brief convertPtaLineTo4cc() - * - * \param[in] ptas 8-connected line of points - * \return ptad 4-connected line, or NULL on error - * - *
- * Notes: - * (1) When a polyline is generated with width = 1, the resulting - * line is not 4-connected in general. This function adds - * points as necessary to convert the line to 4-cconnected. - * It is useful when rendering 1 bpp on a pix. - * (2) Do not use this for lines generated with width > 1. - *- */ -PTA * -convertPtaLineTo4cc(PTA *ptas) -{ -l_int32 i, n, x, y, xp, yp; -PTA *ptad; - - PROCNAME("convertPtaLineTo4cc"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - - n = ptaGetCount(ptas); - ptad = ptaCreate(n); - ptaGetIPt(ptas, 0, &xp, &yp); - ptaAddPt(ptad, xp, yp); - for (i = 1; i < n; i++) { - ptaGetIPt(ptas, i, &x, &y); - if (x != xp && y != yp) /* diagonal */ - ptaAddPt(ptad, x, yp); - ptaAddPt(ptad, x, y); - xp = x; - yp = y; - } - - return ptad; -} - - -/*! - * \brief generatePtaFilledCircle() - * - * \param[in] radius - * \return pta, or NULL on error - * - *
- * Notes: - * (1) The circle is has diameter = 2 * radius + 1. - * (2) It is located with the center of the circle at the - * point (%radius, %radius). - * (3) Consequently, it typically must be translated if - * it is to represent a set of pixels in an image. - *- */ -PTA * -generatePtaFilledCircle(l_int32 radius) -{ -l_int32 x, y; -l_float32 radthresh, sqdist; -PTA *pta; - - PROCNAME("generatePtaFilledCircle"); - - if (radius < 1) - return (PTA *)ERROR_PTR("radius must be >= 1", procName, NULL); - - pta = ptaCreate(0); - radthresh = (radius + 0.5) * (radius + 0.5); - for (y = 0; y <= 2 * radius; y++) { - for (x = 0; x <= 2 * radius; x++) { - sqdist = (l_float32)((y - radius) * (y - radius) + - (x - radius) * (x - radius)); - if (sqdist <= radthresh) - ptaAddPt(pta, x, y); - } - } - - return pta; -} - - -/*! - * \brief generatePtaFilledSquare() - * - * \param[in] side - * \return pta, or NULL on error - * - *
- * Notes: - * (1) The center of the square can be chosen to be at - * (side / 2, side / 2). It must be translated by this amount - * when used for replication. - *- */ -PTA * -generatePtaFilledSquare(l_int32 side) -{ -l_int32 x, y; -PTA *pta; - - PROCNAME("generatePtaFilledSquare"); - if (side < 1) - return (PTA *)ERROR_PTR("side must be > 0", procName, NULL); - - pta = ptaCreate(0); - for (y = 0; y < side; y++) - for (x = 0; x < side; x++) - ptaAddPt(pta, x, y); - - return pta; -} - - -/*! - * \brief generatePtaLineFromPt() - * - * \param[in] x, y point of origination - * \param[in] length of line, including starting point - * \param[in] radang angle in radians, CW from horizontal - * \return pta, or NULL on error - * - *
- * Notes: - * (1) %length of the line is 1 greater than the distance - * used in locatePtRadially(). Example: a distance of 1 - * gives rise to a length of 2. - *- */ -PTA * -generatePtaLineFromPt(l_int32 x, - l_int32 y, - l_float64 length, - l_float64 radang) -{ -l_int32 x2, y2; /* the point at the other end of the line */ - - x2 = x + (l_int32)((length - 1.0) * cos(radang)); - y2 = y + (l_int32)((length - 1.0) * sin(radang)); - return generatePtaLine(x, y, x2, y2); -} - - -/*! - * \brief locatePtRadially() - * - * \param[in] xr, yr reference point - * \param[in] radang angle in radians, CW from horizontal - * \param[in] dist distance of point from reference point along - * line given by the specified angle - * \param[out] px, py location of point - * \return 0 if OK, 1 on error - */ -l_ok -locatePtRadially(l_int32 xr, - l_int32 yr, - l_float64 dist, - l_float64 radang, - l_float64 *px, - l_float64 *py) -{ - PROCNAME("locatePtRadially"); - - if (!px || !py) - return ERROR_INT("&x and &y not both defined", procName, 1); - - *px = xr + dist * cos(radang); - *py = yr + dist * sin(radang); - return 0; -} - - -/*------------------------------------------------------------------* - * Rendering function plots directly on images * - *------------------------------------------------------------------*/ -/*! - * \brief pixRenderPlotFromNuma() - * - * \param[in,out] ppix any type; replaced if not 32 bpp rgb - * \param[in] na to be plotted - * \param[in] plotloc location of plot: L_PLOT_AT_TOP, etc - * \param[in] linewidth width of "line" that is drawn; between 1 and 7 - * \param[in] max maximum excursion in pixels from baseline - * \param[in] color plot color: 0xrrggbb00 - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Simplified interface for plotting row or column aligned data - * on a pix. - * (2) This replaces %pix with a 32 bpp rgb version if it is not - * already 32 bpp. It then draws the plot on the pix. - * (3) See makePlotPtaFromNumaGen() for more details. - *- */ -l_ok -pixRenderPlotFromNuma(PIX **ppix, - NUMA *na, - l_int32 plotloc, - l_int32 linewidth, - l_int32 max, - l_uint32 color) -{ -l_int32 w, h, size, rval, gval, bval; -PIX *pix1; -PTA *pta; - - PROCNAME("pixRenderPlotFromNuma"); - - if (!ppix) - return ERROR_INT("&pix not defined", procName, 1); - if (*ppix == NULL) - return ERROR_INT("pix not defined", procName, 1); - - pixGetDimensions(*ppix, &w, &h, NULL); - size = (plotloc == L_PLOT_AT_TOP || plotloc == L_PLOT_AT_MID_HORIZ || - plotloc == L_PLOT_AT_BOT) ? h : w; - pta = makePlotPtaFromNuma(na, size, plotloc, linewidth, max); - if (!pta) - return ERROR_INT("pta not made", procName, 1); - - if (pixGetDepth(*ppix) != 32) { - pix1 = pixConvertTo32(*ppix); - pixDestroy(ppix); - *ppix = pix1; - } - extractRGBValues(color, &rval, &gval, &bval); - pixRenderPtaArb(*ppix, pta, rval, gval, bval); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief makePlotPtaFromNuma() - * - * \param[in] na - * \param[in] size pix height for horizontal plot; pix width - * for vertical plot - * \param[in] plotloc location of plot: L_PLOT_AT_TOP, etc - * \param[in] linewidth width of "line" that is drawn; between 1 and 7 - * \param[in] max maximum excursion in pixels from baseline - * \return ptad, or NULL on error - * - *
- * Notes: - * (1) This generates points from %numa representing y(x) or x(y) - * with respect to a pix. A horizontal plot y(x) is drawn for - * a function of column position, and a vertical plot is drawn - * for a function x(y) of row position. The baseline is located - * so that all plot points will fit in the pix. - * (2) See makePlotPtaFromNumaGen() for more details. - *- */ -PTA * -makePlotPtaFromNuma(NUMA *na, - l_int32 size, - l_int32 plotloc, - l_int32 linewidth, - l_int32 max) -{ -l_int32 orient, refpos; - - PROCNAME("makePlotPtaFromNuma"); - - if (!na) - return (PTA *)ERROR_PTR("na not defined", procName, NULL); - if (plotloc == L_PLOT_AT_TOP || plotloc == L_PLOT_AT_MID_HORIZ || - plotloc == L_PLOT_AT_BOT) { - orient = L_HORIZONTAL_LINE; - } else if (plotloc == L_PLOT_AT_LEFT || plotloc == L_PLOT_AT_MID_VERT || - plotloc == L_PLOT_AT_RIGHT) { - orient = L_VERTICAL_LINE; - } else { - return (PTA *)ERROR_PTR("invalid plotloc", procName, NULL); - } - - if (plotloc == L_PLOT_AT_LEFT || plotloc == L_PLOT_AT_TOP) - refpos = max; - else if (plotloc == L_PLOT_AT_MID_VERT || plotloc == L_PLOT_AT_MID_HORIZ) - refpos = size / 2; - else /* L_PLOT_AT_RIGHT || L_PLOT_AT_BOT */ - refpos = size - max - 1; - - return makePlotPtaFromNumaGen(na, orient, linewidth, refpos, max, 1); -} - - -/*! - * \brief pixRenderPlotFromNumaGen() - * - * \param[in,out] ppix any type; replaced if not 32 bpp rgb - * \param[in] na to be plotted - * \param[in] orient L_HORIZONTAL_LINE, L_VERTICAL_LINE - * \param[in] linewidth width of "line" that is drawn; between 1 and 7 - * \param[in] refpos reference position: y for horizontal; - * x for vertical - * \param[in] max maximum excursion in pixels from baseline - * \param[in] drawref 1 to draw the reference line and its normal - * \param[in] color plot color: 0xrrggbb00 - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) General interface for plotting row or column aligned data - * on a pix. - * (2) This replaces %pix with a 32 bpp rgb version if it is not - * already 32 bpp. It then draws the plot on the pix. - * (3) See makePlotPtaFromNumaGen() for other input parameters. - *- */ -l_ok -pixRenderPlotFromNumaGen(PIX **ppix, - NUMA *na, - l_int32 orient, - l_int32 linewidth, - l_int32 refpos, - l_int32 max, - l_int32 drawref, - l_uint32 color) -{ -l_int32 rval, gval, bval; -PIX *pix1; -PTA *pta; - - PROCNAME("pixRenderPlotFromNumaGen"); - - if (!ppix) - return ERROR_INT("&pix not defined", procName, 1); - if (*ppix == NULL) - return ERROR_INT("pix not defined", procName, 1); - - pta = makePlotPtaFromNumaGen(na, orient, linewidth, refpos, max, drawref); - if (!pta) - return ERROR_INT("pta not made", procName, 1); - - if (pixGetDepth(*ppix) != 32) { - pix1 = pixConvertTo32(*ppix); - pixDestroy(ppix); - *ppix = pix1; - } - extractRGBValues(color, &rval, &gval, &bval); - pixRenderPtaArb(*ppix, pta, rval, gval, bval); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief makePlotPtaFromNumaGen() - * - * \param[in] na - * \param[in] orient L_HORIZONTAL_LINE, L_VERTICAL_LINE - * \param[in] linewidth width of "line" that is drawn; between 1 and 7 - * \param[in] refpos reference position: y for horizontal; - * x for vertical - * \param[in] max maximum excursion in pixels from baseline - * \param[in] drawref 1 to draw the reference line and its normal - * \return ptad, or NULL on error - * - *
- * Notes: - * (1) This generates points from %numa representing y(x) or x(y) - * with respect to a pix. For y(x), we draw a horizontal line - * at the reference position and a vertical line at the edge; then - * we draw the values of %numa, scaled so that the maximum - * excursion from the reference position is %max pixels. - * (2) The start and delx parameters of %numa are used to refer - * its values to the raster lines (L_VERTICAL_LINE) or columns - * (L_HORIZONTAL_LINE). - * (3) The linewidth is chosen in the interval [1 ... 7]. - * (4) %refpos should be chosen so the plot is entirely within the pix - * that it will be painted onto. - * (5) This would typically be used to plot, in place, a function - * computed along pixel rows or columns. - *- */ -PTA * -makePlotPtaFromNumaGen(NUMA *na, - l_int32 orient, - l_int32 linewidth, - l_int32 refpos, - l_int32 max, - l_int32 drawref) -{ -l_int32 i, n, maxw, maxh; -l_float32 minval, maxval, absval, val, scale, start, del; -PTA *pta1, *pta2, *ptad; - - PROCNAME("makePlotPtaFromNumaGen"); - - if (!na) - return (PTA *)ERROR_PTR("na not defined", procName, NULL); - if (orient != L_HORIZONTAL_LINE && orient != L_VERTICAL_LINE) - return (PTA *)ERROR_PTR("invalid orient", procName, NULL); - if (linewidth < 1) { - L_WARNING("linewidth < 1; setting to 1\n", procName); - linewidth = 1; - } - if (linewidth > 7) { - L_WARNING("linewidth > 7; setting to 7\n", procName); - linewidth = 7; - } - - numaGetMin(na, &minval, NULL); - numaGetMax(na, &maxval, NULL); - absval = L_MAX(L_ABS(minval), L_ABS(maxval)); - scale = (l_float32)max / (l_float32)absval; - n = numaGetCount(na); - numaGetParameters(na, &start, &del); - - /* Generate the plot points */ - pta1 = ptaCreate(n); - for (i = 0; i < n; i++) { - numaGetFValue(na, i, &val); - if (orient == L_HORIZONTAL_LINE) { - ptaAddPt(pta1, start + i * del, refpos + scale * val); - maxw = (del >= 0) ? start + n * del + linewidth - : start + linewidth; - maxh = refpos + max + linewidth; - } else { /* vertical line */ - ptaAddPt(pta1, refpos + scale * val, start + i * del); - maxw = refpos + max + linewidth; - maxh = (del >= 0) ? start + n * del + linewidth - : start + linewidth; - } - } - - /* Optionally, widen the plot */ - if (linewidth > 1) { - if (linewidth % 2 == 0) /* even linewidth; use side of a square */ - pta2 = generatePtaFilledSquare(linewidth); - else /* odd linewidth; use radius of a circle */ - pta2 = generatePtaFilledCircle(linewidth / 2); - ptad = ptaReplicatePattern(pta1, NULL, pta2, linewidth / 2, - linewidth / 2, maxw, maxh); - ptaDestroy(&pta2); - } else { - ptad = ptaClone(pta1); - } - ptaDestroy(&pta1); - - /* Optionally, add the reference lines */ - if (drawref) { - if (orient == L_HORIZONTAL_LINE) { - pta1 = generatePtaLine(start, refpos, start + n * del, refpos); - ptaJoin(ptad, pta1, 0, -1); - ptaDestroy(&pta1); - pta1 = generatePtaLine(start, refpos - max, - start, refpos + max); - ptaJoin(ptad, pta1, 0, -1); - } else { /* vertical line */ - pta1 = generatePtaLine(refpos, start, refpos, start + n * del); - ptaJoin(ptad, pta1, 0, -1); - ptaDestroy(&pta1); - pta1 = generatePtaLine(refpos - max, start, - refpos + max, start); - ptaJoin(ptad, pta1, 0, -1); - } - ptaDestroy(&pta1); - } - - return ptad; -} - - -/*------------------------------------------------------------------* - * Pta generation for arbitrary shapes built with lines * - *------------------------------------------------------------------*/ -/*! - * \brief pixRenderPta() - * - * \param[in] pix any depth, not cmapped - * \param[in] pta arbitrary set of points - * \param[in] op one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) L_SET_PIXELS puts all image bits in each pixel to 1 - * (black for 1 bpp; white for depth > 1) - * (2) L_CLEAR_PIXELS puts all image bits in each pixel to 0 - * (white for 1 bpp; black for depth > 1) - * (3) L_FLIP_PIXELS reverses all image bits in each pixel - * (4) This function clips the rendering to the pix. It performs - * clipping for functions such as pixRenderLine(), - * pixRenderBox() and pixRenderBoxa(), that call pixRenderPta(). - *- */ -l_ok -pixRenderPta(PIX *pix, - PTA *pta, - l_int32 op) -{ -l_int32 i, n, x, y, w, h, d, maxval; - - PROCNAME("pixRenderPta"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (pixGetColormap(pix)) - return ERROR_INT("pix is colormapped", procName, 1); - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) - return ERROR_INT("invalid op", procName, 1); - - pixGetDimensions(pix, &w, &h, &d); - maxval = 1; - if (op == L_SET_PIXELS) { - switch (d) - { - case 2: - maxval = 0x3; - break; - case 4: - maxval = 0xf; - break; - case 8: - maxval = 0xff; - break; - case 16: - maxval = 0xffff; - break; - case 32: - maxval = 0xffffffff; - break; - } - } - - n = ptaGetCount(pta); - for (i = 0; i < n; i++) { - ptaGetIPt(pta, i, &x, &y); - if (x < 0 || x >= w) - continue; - if (y < 0 || y >= h) - continue; - switch (op) - { - case L_SET_PIXELS: - pixSetPixel(pix, x, y, maxval); - break; - case L_CLEAR_PIXELS: - pixClearPixel(pix, x, y); - break; - case L_FLIP_PIXELS: - pixFlipPixel(pix, x, y); - break; - default: - break; - } - } - - return 0; -} - - -/*! - * \brief pixRenderPtaArb() - * - * \param[in] pix any depth, cmapped ok - * \param[in] pta arbitrary set of points - * \param[in] rval, gval, bval - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) If %pix is colormapped, render this color (or the nearest - * color if the cmap is full) on each pixel. - * (2) The rgb components have the standard dynamic range [0 ... 255] - * (3) If pix is not colormapped, do the best job you can using - * the input colors: - * ~ d = 1: set the pixels - * ~ d = 2, 4, 8: average the input rgb value - * ~ d = 32: use the input rgb value - * (4) This function clips the rendering to %pix. - *- */ -l_ok -pixRenderPtaArb(PIX *pix, - PTA *pta, - l_uint8 rval, - l_uint8 gval, - l_uint8 bval) -{ -l_int32 i, n, x, y, w, h, d, index; -l_uint8 val; -l_uint32 val32; -PIXCMAP *cmap; - - PROCNAME("pixRenderPtaArb"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - d = pixGetDepth(pix); - if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32) - return ERROR_INT("depth not in {1,2,4,8,32}", procName, 1); - - if (d == 1) { - pixRenderPta(pix, pta, L_SET_PIXELS); - return 0; - } - - cmap = pixGetColormap(pix); - pixGetDimensions(pix, &w, &h, &d); - if (cmap) { - pixcmapAddNearestColor(cmap, rval, gval, bval, &index); - } else { - if (d == 2) - val = (rval + gval + bval) / (3 * 64); - else if (d == 4) - val = (rval + gval + bval) / (3 * 16); - else if (d == 8) - val = (rval + gval + bval) / 3; - else /* d == 32 */ - composeRGBPixel(rval, gval, bval, &val32); - } - - n = ptaGetCount(pta); - for (i = 0; i < n; i++) { - ptaGetIPt(pta, i, &x, &y); - if (x < 0 || x >= w) - continue; - if (y < 0 || y >= h) - continue; - if (cmap) - pixSetPixel(pix, x, y, index); - else if (d == 32) - pixSetPixel(pix, x, y, val32); - else - pixSetPixel(pix, x, y, val); - } - - return 0; -} - - -/*! - * \brief pixRenderPtaBlend() - * - * \param[in] pix 32 bpp rgb - * \param[in] pta arbitrary set of points - * \param[in] rval, gval, bval - * \param[in] fract - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This function clips the rendering to %pix. - *- */ -l_ok -pixRenderPtaBlend(PIX *pix, - PTA *pta, - l_uint8 rval, - l_uint8 gval, - l_uint8 bval, - l_float32 fract) -{ -l_int32 i, n, x, y, w, h; -l_uint8 nrval, ngval, nbval; -l_uint32 val32; -l_float32 frval, fgval, fbval; - - PROCNAME("pixRenderPtaBlend"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - if (pixGetDepth(pix) != 32) - return ERROR_INT("depth not 32 bpp", procName, 1); - if (fract < 0.0 || fract > 1.0) { - L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName); - fract = 0.5; - } - - pixGetDimensions(pix, &w, &h, NULL); - n = ptaGetCount(pta); - frval = fract * rval; - fgval = fract * gval; - fbval = fract * bval; - for (i = 0; i < n; i++) { - ptaGetIPt(pta, i, &x, &y); - if (x < 0 || x >= w) - continue; - if (y < 0 || y >= h) - continue; - pixGetPixel(pix, x, y, &val32); - nrval = GET_DATA_BYTE(&val32, COLOR_RED); - nrval = (l_uint8)((1. - fract) * nrval + frval); - ngval = GET_DATA_BYTE(&val32, COLOR_GREEN); - ngval = (l_uint8)((1. - fract) * ngval + fgval); - nbval = GET_DATA_BYTE(&val32, COLOR_BLUE); - nbval = (l_uint8)((1. - fract) * nbval + fbval); - composeRGBPixel(nrval, ngval, nbval, &val32); - pixSetPixel(pix, x, y, val32); - } - - return 0; -} - - -/*------------------------------------------------------------------* - * Rendering of arbitrary shapes built with lines * - *------------------------------------------------------------------*/ -/*! - * \brief pixRenderLine() - * - * \param[in] pix any depth, not cmapped - * \param[in] x1, y1 - * \param[in] x2, y2 - * \param[in] width thickness of line - * \param[in] op one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS - * \return 0 if OK, 1 on error - */ -l_ok -pixRenderLine(PIX *pix, - l_int32 x1, - l_int32 y1, - l_int32 x2, - l_int32 y2, - l_int32 width, - l_int32 op) -{ -PTA *pta; - - PROCNAME("pixRenderLine"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (width < 1) { - L_WARNING("width must be > 0; setting to 1\n", procName); - width = 1; - } - if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) - return ERROR_INT("invalid op", procName, 1); - - if ((pta = generatePtaWideLine(x1, y1, x2, y2, width)) == NULL) - return ERROR_INT("pta not made", procName, 1); - pixRenderPta(pix, pta, op); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderLineArb() - * - * \param[in] pix any depth, cmapped ok - * \param[in] x1, y1 - * \param[in] x2, y2 - * \param[in] width thickness of line - * \param[in] rval, gval, bval - * \return 0 if OK, 1 on error - */ -l_ok -pixRenderLineArb(PIX *pix, - l_int32 x1, - l_int32 y1, - l_int32 x2, - l_int32 y2, - l_int32 width, - l_uint8 rval, - l_uint8 gval, - l_uint8 bval) -{ -PTA *pta; - - PROCNAME("pixRenderLineArb"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (width < 1) { - L_WARNING("width must be > 0; setting to 1\n", procName); - width = 1; - } - - if ((pta = generatePtaWideLine(x1, y1, x2, y2, width)) == NULL) - return ERROR_INT("pta not made", procName, 1); - pixRenderPtaArb(pix, pta, rval, gval, bval); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderLineBlend() - * - * \param[in] pix 32 bpp rgb - * \param[in] x1, y1 - * \param[in] x2, y2 - * \param[in] width thickness of line - * \param[in] rval, gval, bval - * \param[in] fract - * \return 0 if OK, 1 on error - */ -l_ok -pixRenderLineBlend(PIX *pix, - l_int32 x1, - l_int32 y1, - l_int32 x2, - l_int32 y2, - l_int32 width, - l_uint8 rval, - l_uint8 gval, - l_uint8 bval, - l_float32 fract) -{ -PTA *pta; - - PROCNAME("pixRenderLineBlend"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (width < 1) { - L_WARNING("width must be > 0; setting to 1\n", procName); - width = 1; - } - - if ((pta = generatePtaWideLine(x1, y1, x2, y2, width)) == NULL) - return ERROR_INT("pta not made", procName, 1); - pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderBox() - * - * \param[in] pix any depth, not cmapped - * \param[in] box - * \param[in] width thickness of box lines - * \param[in] op one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS - * \return 0 if OK, 1 on error - */ -l_ok -pixRenderBox(PIX *pix, - BOX *box, - l_int32 width, - l_int32 op) -{ -PTA *pta; - - PROCNAME("pixRenderBox"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) - return ERROR_INT("invalid op", procName, 1); - - if ((pta = generatePtaBox(box, width)) == NULL) - return ERROR_INT("pta not made", procName, 1); - pixRenderPta(pix, pta, op); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderBoxArb() - * - * \param[in] pix any depth, cmapped ok - * \param[in] box - * \param[in] width thickness of box lines - * \param[in] rval, gval, bval - * \return 0 if OK, 1 on error - */ -l_ok -pixRenderBoxArb(PIX *pix, - BOX *box, - l_int32 width, - l_uint8 rval, - l_uint8 gval, - l_uint8 bval) -{ -PTA *pta; - - PROCNAME("pixRenderBoxArb"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - - if ((pta = generatePtaBox(box, width)) == NULL) - return ERROR_INT("pta not made", procName, 1); - pixRenderPtaArb(pix, pta, rval, gval, bval); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderBoxBlend() - * - * \param[in] pix 32 bpp rgb - * \param[in] box - * \param[in] width thickness of box lines - * \param[in] rval, gval, bval - * \param[in] fract in [0.0 - 1.0]: 1.0 is no transparency; - * 0.0 is complete transparency (no effect) - * \return 0 if OK, 1 on error - */ -l_ok -pixRenderBoxBlend(PIX *pix, - BOX *box, - l_int32 width, - l_uint8 rval, - l_uint8 gval, - l_uint8 bval, - l_float32 fract) -{ -PTA *pta; - - PROCNAME("pixRenderBoxBlend"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - - if ((pta = generatePtaBox(box, width)) == NULL) - return ERROR_INT("pta not made", procName, 1); - pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderBoxa() - * - * \param[in] pix any depth, not cmapped - * \param[in] boxa - * \param[in] width thickness of line - * \param[in] op one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS - * \return 0 if OK, 1 on error - */ -l_ok -pixRenderBoxa(PIX *pix, - BOXA *boxa, - l_int32 width, - l_int32 op) -{ -PTA *pta; - - PROCNAME("pixRenderBoxa"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) - return ERROR_INT("invalid op", procName, 1); - - if ((pta = generatePtaBoxa(boxa, width, 0)) == NULL) - return ERROR_INT("pta not made", procName, 1); - pixRenderPta(pix, pta, op); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderBoxaArb() - * - * \param[in] pix any depth; colormapped is ok - * \param[in] boxa - * \param[in] width thickness of line - * \param[in] rval, gval, bval - * \return 0 if OK, 1 on error - */ -l_ok -pixRenderBoxaArb(PIX *pix, - BOXA *boxa, - l_int32 width, - l_uint8 rval, - l_uint8 gval, - l_uint8 bval) -{ -PTA *pta; - - PROCNAME("pixRenderBoxaArb"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - - if ((pta = generatePtaBoxa(boxa, width, 0)) == NULL) - return ERROR_INT("pta not made", procName, 1); - pixRenderPtaArb(pix, pta, rval, gval, bval); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderBoxaBlend() - * - * \param[in] pix 32 bpp rgb - * \param[in] boxa - * \param[in] width thickness of line - * \param[in] rval, gval, bval - * \param[in] fract in [0.0 - 1.0]: 1.0 is no transparency; - * 0.0 is complete transparency (no effect) - * \param[in] removedups 1 to remove; 0 otherwise - * \return 0 if OK, 1 on error - */ -l_ok -pixRenderBoxaBlend(PIX *pix, - BOXA *boxa, - l_int32 width, - l_uint8 rval, - l_uint8 gval, - l_uint8 bval, - l_float32 fract, - l_int32 removedups) -{ -PTA *pta; - - PROCNAME("pixRenderBoxaBlend"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - - if ((pta = generatePtaBoxa(boxa, width, removedups)) == NULL) - return ERROR_INT("pta not made", procName, 1); - pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderHashBox() - * - * \param[in] pix any depth, not cmapped - * \param[in] box - * \param[in] spacing spacing between lines; must be > 1 - * \param[in] width thickness of box and hash lines - * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, ... - * \param[in] outline 0 to skip drawing box outline - * \param[in] op one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS - * \return 0 if OK, 1 on error - */ -l_ok -pixRenderHashBox(PIX *pix, - BOX *box, - l_int32 spacing, - l_int32 width, - l_int32 orient, - l_int32 outline, - l_int32 op) -{ -PTA *pta; - - PROCNAME("pixRenderHashBox"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - if (spacing <= 1) - return ERROR_INT("spacing not > 1", procName, 1); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && - orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) - return ERROR_INT("invalid line orientation", procName, 1); - if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) - return ERROR_INT("invalid op", procName, 1); - - pta = generatePtaHashBox(box, spacing, width, orient, outline); - if (!pta) - return ERROR_INT("pta not made", procName, 1); - pixRenderPta(pix, pta, op); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderHashBoxArb() - * - * \param[in] pix any depth; cmapped ok - * \param[in] box - * \param[in] spacing spacing between lines; must be > 1 - * \param[in] width thickness of box and hash lines - * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, ... - * \param[in] outline 0 to skip drawing box outline - * \param[in] rval, gval, bval - * \return 0 if OK, 1 on error - */ -l_ok -pixRenderHashBoxArb(PIX *pix, - BOX *box, - l_int32 spacing, - l_int32 width, - l_int32 orient, - l_int32 outline, - l_int32 rval, - l_int32 gval, - l_int32 bval) -{ -PTA *pta; - - PROCNAME("pixRenderHashBoxArb"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - if (spacing <= 1) - return ERROR_INT("spacing not > 1", procName, 1); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && - orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) - return ERROR_INT("invalid line orientation", procName, 1); - - pta = generatePtaHashBox(box, spacing, width, orient, outline); - if (!pta) - return ERROR_INT("pta not made", procName, 1); - pixRenderPtaArb(pix, pta, rval, gval, bval); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderHashBoxBlend() - * - * \param[in] pix 32 bpp - * \param[in] box - * \param[in] spacing spacing between lines; must be > 1 - * \param[in] width thickness of box and hash lines - * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, ... - * \param[in] outline 0 to skip drawing box outline - * \param[in] rval, gval, bval - * \param[in] fract in [0.0 - 1.0]: 1.0 is no transparency; - * 0.0 is complete transparency (no effect) - * \return 0 if OK, 1 on error - */ -l_ok -pixRenderHashBoxBlend(PIX *pix, - BOX *box, - l_int32 spacing, - l_int32 width, - l_int32 orient, - l_int32 outline, - l_int32 rval, - l_int32 gval, - l_int32 bval, - l_float32 fract) -{ -PTA *pta; - - PROCNAME("pixRenderHashBoxBlend"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - if (spacing <= 1) - return ERROR_INT("spacing not > 1", procName, 1); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && - orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) - return ERROR_INT("invalid line orientation", procName, 1); - - pta = generatePtaHashBox(box, spacing, width, orient, outline); - if (!pta) - return ERROR_INT("pta not made", procName, 1); - pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderHashMaskArb() - * - * \param[in] pix any depth; cmapped ok - * \param[in] pixm 1 bpp clipping mask for hash marks - * \param[in] x,y UL corner of %pixm with respect to %pix - * \param[in] spacing spacing between lines; must be > 1 - * \param[in] width thickness of box and hash lines - * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, - * L_POS_SLOPE_LINE, L_VERTICAL_LINE, - * L_NEG_SLOPE_LINE - * \param[in] outline 0 to skip drawing box outline - * \param[in] rval, gval, bval - * \return 0 if OK, 1 on error - *
- * Notes: - * (1) This is an in-place operation that renders hash lines - * through a mask %pixm onto %pix. The mask origin is - * translated by (%x,%y) relative to the origin of %pix. - *- */ -l_ok -pixRenderHashMaskArb(PIX *pix, - PIX *pixm, - l_int32 x, - l_int32 y, - l_int32 spacing, - l_int32 width, - l_int32 orient, - l_int32 outline, - l_int32 rval, - l_int32 gval, - l_int32 bval) -{ -l_int32 w, h; -BOX *box1, *box2; -PIX *pix1; -PTA *pta1, *pta2; - - PROCNAME("pixRenderHashMaskArb"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!pixm || pixGetDepth(pixm) != 1) - return ERROR_INT("pixm not defined or not 1 bpp", procName, 1); - if (spacing <= 1) - return ERROR_INT("spacing not > 1", procName, 1); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && - orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) - return ERROR_INT("invalid line orientation", procName, 1); - - /* Get the points for masked hash lines */ - pixGetDimensions(pixm, &w, &h, NULL); - box1 = boxCreate(0, 0, w, h); - pta1 = generatePtaHashBox(box1, spacing, width, orient, outline); - pta2 = ptaCropToMask(pta1, pixm); - boxDestroy(&box1); - ptaDestroy(&pta1); - - /* Clip out the region and apply the hash lines */ - box2 = boxCreate(x, y, w, h); - pix1 = pixClipRectangle(pix, box2, NULL); - pixRenderPtaArb(pix1, pta2, rval, gval, bval); - ptaDestroy(&pta2); - boxDestroy(&box2); - - /* Rasterop the altered rectangle back in place */ - pixRasterop(pix, x, y, w, h, PIX_SRC, pix1, 0, 0); - pixDestroy(&pix1); - return 0; -} - - -/*! - * \brief pixRenderHashBoxa() - * - * \param[in] pix any depth, not cmapped - * \param[in] boxa - * \param[in] spacing spacing between lines; must be > 1 - * \param[in] width thickness of box and hash lines - * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, - * L_POS_SLOPE_LINE, L_VERTICAL_LINE, - * L_NEG_SLOPE_LINE - * \param[in] outline 0 to skip drawing box outline - * \param[in] op one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS - * \return 0 if OK, 1 on error - */ -l_ok -pixRenderHashBoxa(PIX *pix, - BOXA *boxa, - l_int32 spacing, - l_int32 width, - l_int32 orient, - l_int32 outline, - l_int32 op) - { -PTA *pta; - - PROCNAME("pixRenderHashBoxa"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (spacing <= 1) - return ERROR_INT("spacing not > 1", procName, 1); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && - orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) - return ERROR_INT("invalid line orientation", procName, 1); - if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) - return ERROR_INT("invalid op", procName, 1); - - pta = generatePtaHashBoxa(boxa, spacing, width, orient, outline, 1); - if (!pta) - return ERROR_INT("pta not made", procName, 1); - pixRenderPta(pix, pta, op); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderHashBoxaArb() - * - * \param[in] pix any depth; cmapped ok - * \param[in] box - * \param[in] spacing spacing between lines; must be > 1 - * \param[in] width thickness of box and hash lines - * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, - * L_POS_SLOPE_LINE, L_VERTICAL_LINE, - * L_NEG_SLOPE_LINE - * \param[in] outline 0 to skip drawing box outline - * \param[in] rval, gval, bval - * \return 0 if OK, 1 on error - */ -l_ok -pixRenderHashBoxaArb(PIX *pix, - BOXA *boxa, - l_int32 spacing, - l_int32 width, - l_int32 orient, - l_int32 outline, - l_int32 rval, - l_int32 gval, - l_int32 bval) -{ -PTA *pta; - - PROCNAME("pixRenderHashBoxArb"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (spacing <= 1) - return ERROR_INT("spacing not > 1", procName, 1); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && - orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) - return ERROR_INT("invalid line orientation", procName, 1); - - pta = generatePtaHashBoxa(boxa, spacing, width, orient, outline, 1); - if (!pta) - return ERROR_INT("pta not made", procName, 1); - pixRenderPtaArb(pix, pta, rval, gval, bval); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderHashBoxaBlend() - * - * \param[in] pix 32 bpp rgb - * \param[in] boxa - * \param[in] spacing spacing between lines; must be > 1 - * \param[in] width thickness of box and hash lines - * \param[in] orient orientation of lines: L_HORIZONTAL_LINE, - * L_POS_SLOPE_LINE, L_VERTICAL_LINE, - * L_NEG_SLOPE_LINE - * \param[in] outline 0 to skip drawing box outline - * \param[in] rval, gval, bval - * \param[in] fract in [0.0 - 1.0]: 1.0 is no transparency; - * 0.0 is complete transparency (no effect) - * \return 0 if OK, 1 on error - */ -l_ok -pixRenderHashBoxaBlend(PIX *pix, - BOXA *boxa, - l_int32 spacing, - l_int32 width, - l_int32 orient, - l_int32 outline, - l_int32 rval, - l_int32 gval, - l_int32 bval, - l_float32 fract) -{ -PTA *pta; - - PROCNAME("pixRenderHashBoxaBlend"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (spacing <= 1) - return ERROR_INT("spacing not > 1", procName, 1); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - if (orient != L_HORIZONTAL_LINE && orient != L_POS_SLOPE_LINE && - orient != L_VERTICAL_LINE && orient != L_NEG_SLOPE_LINE) - return ERROR_INT("invalid line orientation", procName, 1); - - pta = generatePtaHashBoxa(boxa, spacing, width, orient, outline, 1); - if (!pta) - return ERROR_INT("pta not made", procName, 1); - pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderPolyline() - * - * \param[in] pix any depth, not cmapped - * \param[in] ptas - * \param[in] width thickness of line - * \param[in] op one of L_SET_PIXELS, L_CLEAR_PIXELS, L_FLIP_PIXELS - * \param[in] closeflag 1 to close the contour; 0 otherwise - * \return 0 if OK, 1 on error - * - *
- * Notes: - * This renders a closed contour. - *- */ -l_ok -pixRenderPolyline(PIX *pix, - PTA *ptas, - l_int32 width, - l_int32 op, - l_int32 closeflag) -{ -PTA *pta; - - PROCNAME("pixRenderPolyline"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!ptas) - return ERROR_INT("ptas not defined", procName, 1); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - if (op != L_SET_PIXELS && op != L_CLEAR_PIXELS && op != L_FLIP_PIXELS) - return ERROR_INT("invalid op", procName, 1); - - if ((pta = generatePtaPolyline(ptas, width, closeflag, 0)) == NULL) - return ERROR_INT("pta not made", procName, 1); - pixRenderPta(pix, pta, op); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderPolylineArb() - * - * \param[in] pix any depth; cmapped ok - * \param[in] ptas - * \param[in] width thickness of line - * \param[in] rval, gval, bval - * \param[in] closeflag 1 to close the contour; 0 otherwise - * \return 0 if OK, 1 on error - * - *
- * Notes: - * This renders a closed contour. - *- */ -l_ok -pixRenderPolylineArb(PIX *pix, - PTA *ptas, - l_int32 width, - l_uint8 rval, - l_uint8 gval, - l_uint8 bval, - l_int32 closeflag) -{ -PTA *pta; - - PROCNAME("pixRenderPolylineArb"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!ptas) - return ERROR_INT("ptas not defined", procName, 1); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - - if ((pta = generatePtaPolyline(ptas, width, closeflag, 0)) == NULL) - return ERROR_INT("pta not made", procName, 1); - pixRenderPtaArb(pix, pta, rval, gval, bval); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderPolylineBlend() - * - * \param[in] pix 32 bpp rgb - * \param[in] ptas - * \param[in] width thickness of line - * \param[in] rval, gval, bval - * \param[in] fract in [0.0 - 1.0]: 1.0 is no transparency; - * 0.0 is complete transparency (no effect) - * \param[in] closeflag 1 to close the contour; 0 otherwise - * \param[in] removedups 1 to remove; 0 otherwise - * \return 0 if OK, 1 on error - */ -l_ok -pixRenderPolylineBlend(PIX *pix, - PTA *ptas, - l_int32 width, - l_uint8 rval, - l_uint8 gval, - l_uint8 bval, - l_float32 fract, - l_int32 closeflag, - l_int32 removedups) -{ -PTA *pta; - - PROCNAME("pixRenderPolylineBlend"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!ptas) - return ERROR_INT("ptas not defined", procName, 1); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - - if ((pta = generatePtaPolyline(ptas, width, closeflag, removedups)) == NULL) - return ERROR_INT("pta not made", procName, 1); - pixRenderPtaBlend(pix, pta, rval, gval, bval, fract); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderGridArb() - * - * \param[in] pix any depth, cmapped ok - * \param[in] nx, ny number of rectangles in each direction - * \param[in] width thickness of grid lines - * \param[in] rval, gval, bval - * \return 0 if OK, 1 on error - */ -l_ok -pixRenderGridArb(PIX *pix, - l_int32 nx, - l_int32 ny, - l_int32 width, - l_uint8 rval, - l_uint8 gval, - l_uint8 bval) -{ -l_int32 w, h; -PTA *pta; - - PROCNAME("pixRenderGridArb"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (nx < 1 || ny < 1) - return ERROR_INT("nx, ny must be > 0", procName, 1); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - - pixGetDimensions(pix, &w, &h, NULL); - if ((pta = generatePtaGrid(w, h, nx, ny, width)) == NULL) - return ERROR_INT("pta not made", procName, 1); - pixRenderPtaArb(pix, pta, rval, gval, bval); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief pixRenderRandomCmapPtaa() - * - * \param[in] pix 1, 2, 4, 8, 16, 32 bpp - * \param[in] ptaa - * \param[in] polyflag 1 to interpret each Pta as a polyline; - * 0 to simply render the Pta as a set of pixels - * \param[in] width thickness of line; use only for polyline - * \param[in] closeflag 1 to close the contour; 0 otherwise; - * use only for polyline mode - * \return pixd cmapped, 8 bpp or NULL on error - * - *
- * Notes: - * (1) This is a debugging routine, that displays a set of - * pixels, selected by the set of Ptas in a Ptaa, - * in a random color in a pix. - * (2) If %polyflag == 1, each Pta is considered to be a polyline, - * and is rendered using %width and %closeflag. Each polyline - * is rendered in a random color. - * (3) If %polyflag == 0, all points in each Pta are rendered in a - * random color. The %width and %closeflag parameters are ignored. - * (4) The output pix is 8 bpp and colormapped. Up to 254 - * different, randomly selected colors, can be used. - * (5) The rendered pixels replace the input pixels. They will - * be clipped silently to the input pix. - *- */ -PIX * -pixRenderRandomCmapPtaa(PIX *pix, - PTAA *ptaa, - l_int32 polyflag, - l_int32 width, - l_int32 closeflag) -{ -l_int32 i, n, index, rval, gval, bval; -PIXCMAP *cmap; -PTA *pta, *ptat; -PIX *pixd; - - PROCNAME("pixRenderRandomCmapPtaa"); - - if (!pix) - return (PIX *)ERROR_PTR("pix not defined", procName, NULL); - if (!ptaa) - return (PIX *)ERROR_PTR("ptaa not defined", procName, NULL); - if (polyflag != 0 && width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - - pixd = pixConvertTo8(pix, FALSE); - cmap = pixcmapCreateRandom(8, 1, 1); - pixSetColormap(pixd, cmap); - - if ((n = ptaaGetCount(ptaa)) == 0) - return pixd; - - for (i = 0; i < n; i++) { - index = 1 + (i % 254); - pixcmapGetColor(cmap, index, &rval, &gval, &bval); - pta = ptaaGetPta(ptaa, i, L_CLONE); - if (polyflag) - ptat = generatePtaPolyline(pta, width, closeflag, 0); - else - ptat = ptaClone(pta); - pixRenderPtaArb(pixd, ptat, rval, gval, bval); - ptaDestroy(&pta); - ptaDestroy(&ptat); - } - - return pixd; -} - - - -/*------------------------------------------------------------------* - * Rendering and filling of polygons * - *------------------------------------------------------------------*/ -/*! - * \brief pixRenderPolygon() - * - * \param[in] ptas of vertices, none repeated - * \param[in] width of polygon outline - * \param[out] pxmin [optional] min x value of input pts - * \param[out] pymin [optional] min y value of input pts - * \return pix 1 bpp, with outline generated, or NULL on error - * - *
- * Notes: - * (1) The pix is the minimum size required to contain the origin - * and the polygon. For example, the max x value of the input - * points is w - 1, where w is the pix width. - * (2) The rendered line is 4-connected, so that an interior or - * exterior 8-c.c. flood fill operation works properly. - *- */ -PIX * -pixRenderPolygon(PTA *ptas, - l_int32 width, - l_int32 *pxmin, - l_int32 *pymin) -{ -l_float32 fxmin, fxmax, fymin, fymax; -PIX *pixd; -PTA *pta1, *pta2; - - PROCNAME("pixRenderPolygon"); - - if (pxmin) *pxmin = 0; - if (pymin) *pymin = 0; - if (!ptas) - return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); - - /* Generate a 4-connected polygon line */ - if ((pta1 = generatePtaPolyline(ptas, width, 1, 0)) == NULL) - return (PIX *)ERROR_PTR("pta1 not made", procName, NULL); - if (width < 2) - pta2 = convertPtaLineTo4cc(pta1); - else - pta2 = ptaClone(pta1); - - /* Render onto a minimum-sized pix */ - ptaGetRange(pta2, &fxmin, &fxmax, &fymin, &fymax); - if (pxmin) *pxmin = (l_int32)(fxmin + 0.5); - if (pymin) *pymin = (l_int32)(fymin + 0.5); - pixd = pixCreate((l_int32)(fxmax + 0.5) + 1, (l_int32)(fymax + 0.5) + 1, 1); - pixRenderPolyline(pixd, pta2, width, L_SET_PIXELS, 1); - ptaDestroy(&pta1); - ptaDestroy(&pta2); - return pixd; -} - - -/*! - * \brief pixFillPolygon() - * - * \param[in] pixs 1 bpp, with 4-connected polygon outline - * \param[in] pta vertices of the polygon - * \param[in] xmin, ymin min values of vertices of polygon - * \return pixd with outline filled, or NULL on error - * - *
- * Notes: - * (1) This fills the interior of the polygon, returning a - * new pix. It works for both convex and non-convex polygons. - * (2) To generate a filled polygon from %pta: - * PIX *pixt = pixRenderPolygon(pta, 1, &xmin, &ymin); - * PIX *pixd = pixFillPolygon(pixt, pta, xmin, ymin); - * pixDestroy(&pixt); - *- */ -PIX * -pixFillPolygon(PIX *pixs, - PTA *pta, - l_int32 xmin, - l_int32 ymin) -{ -l_int32 w, h, i, n, inside, found; -l_int32 *xstart, *xend; -PIX *pixi, *pixd; - - PROCNAME("pixFillPolygon"); - - if (!pixs || (pixGetDepth(pixs) != 1)) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (!pta) - return (PIX *)ERROR_PTR("pta not defined", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - xstart = (l_int32 *)LEPT_CALLOC(w / 2, sizeof(l_int32)); - xend = (l_int32 *)LEPT_CALLOC(w / 2, sizeof(l_int32)); - - /* Find a raster with 2 or more black runs. The first background - * pixel after the end of the first run is likely to be inside - * the polygon, and can be used as a seed pixel. */ - found = FALSE; - for (i = ymin + 1; i < h; i++) { - pixFindHorizontalRuns(pixs, i, xstart, xend, &n); - if (n > 1) { - ptaPtInsidePolygon(pta, xend[0] + 1, i, &inside); - if (inside) { - found = TRUE; - break; - } - } - } - if (!found) { - L_WARNING("nothing found to fill\n", procName); - LEPT_FREE(xstart); - LEPT_FREE(xend); - return 0; - } - - /* Place the seed pixel in the output image */ - pixd = pixCreateTemplate(pixs); - pixSetPixel(pixd, xend[0] + 1, i, 1); - - /* Invert pixs to make a filling mask, and fill from the seed */ - pixi = pixInvert(NULL, pixs); - pixSeedfillBinary(pixd, pixd, pixi, 4); - - /* Add the pixels of the original polygon outline */ - pixOr(pixd, pixd, pixs); - - pixDestroy(&pixi); - LEPT_FREE(xstart); - LEPT_FREE(xend); - return pixd; -} - - -/*------------------------------------------------------------------* - * Contour rendering on grayscale images * - *------------------------------------------------------------------*/ -/*! - * \brief pixRenderContours() - * - * \param[in] pixs 8 or 16 bpp; no colormap - * \param[in] startval value of lowest contour; must be in [0 ... maxval] - * \param[in] incr increment to next contour; must be > 0 - * \param[in] outdepth either 1 or depth of pixs - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) The output can be either 1 bpp, showing just the contour - * lines, or a copy of the input pixs with the contour lines - * superposed. - *- */ -PIX * -pixRenderContours(PIX *pixs, - l_int32 startval, - l_int32 incr, - l_int32 outdepth) -{ -l_int32 w, h, d, maxval, wpls, wpld, i, j, val, test; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; - - PROCNAME("pixRenderContours"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8 && d != 16) - return (PIX *)ERROR_PTR("pixs not 8 or 16 bpp", procName, NULL); - if (outdepth != 1 && outdepth != d) { - L_WARNING("invalid outdepth; setting to 1\n", procName); - outdepth = 1; - } - maxval = (1 << d) - 1; - if (startval < 0 || startval > maxval) - return (PIX *)ERROR_PTR("startval not in [0 ... maxval]", - procName, NULL); - if (incr < 1) - return (PIX *)ERROR_PTR("incr < 1", procName, NULL); - - if (outdepth == d) - pixd = pixCopy(NULL, pixs); - else - pixd = pixCreate(w, h, 1); - - pixCopyResolution(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - - switch (d) - { - case 8: - if (outdepth == 1) { - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(lines, j); - if (val < startval) - continue; - test = (val - startval) % incr; - if (!test) - SET_DATA_BIT(lined, j); - } - } - } else { /* outdepth == d */ - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(lines, j); - if (val < startval) - continue; - test = (val - startval) % incr; - if (!test) - SET_DATA_BYTE(lined, j, 0); - } - } - } - break; - - case 16: - if (outdepth == 1) { - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = GET_DATA_TWO_BYTES(lines, j); - if (val < startval) - continue; - test = (val - startval) % incr; - if (!test) - SET_DATA_BIT(lined, j); - } - } - } else { /* outdepth == d */ - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = GET_DATA_TWO_BYTES(lines, j); - if (val < startval) - continue; - test = (val - startval) % incr; - if (!test) - SET_DATA_TWO_BYTES(lined, j, 0); - } - } - } - break; - - default: - return (PIX *)ERROR_PTR("pixs not 8 or 16 bpp", procName, NULL); - } - - return pixd; -} - - -/*! - * \brief fpixAutoRenderContours() - * - * \param[in] fpix - * \param[in] ncontours in [2 ... 500]; typically about 50 - * \return pixd 8 bpp, or NULL on error - * - *
- * Notes: - * (1) The increment is set to get approximately %ncontours. - * (2) The proximity to the target value for contour display - * is set to 0.15. - * (3) Negative values are rendered in red; positive values as black. - *- */ -PIX * -fpixAutoRenderContours(FPIX *fpix, - l_int32 ncontours) -{ -l_float32 minval, maxval, incr; - - PROCNAME("fpixAutoRenderContours"); - - if (!fpix) - return (PIX *)ERROR_PTR("fpix not defined", procName, NULL); - if (ncontours < 2 || ncontours > 500) - return (PIX *)ERROR_PTR("ncontours < 2 or > 500", procName, NULL); - - fpixGetMin(fpix, &minval, NULL, NULL); - fpixGetMax(fpix, &maxval, NULL, NULL); - if (minval == maxval) - return (PIX *)ERROR_PTR("all values in fpix are equal", procName, NULL); - incr = (maxval - minval) / ((l_float32)ncontours - 1); - return fpixRenderContours(fpix, incr, 0.15); -} - - -/*! - * \brief fpixRenderContours() - * - * \param[in] fpixs - * \param[in] incr increment between contours; must be > 0.0 - * \param[in] proxim required proximity to target value; default 0.15 - * \return pixd 8 bpp, or NULL on error - * - *
- * Notes: - * (1) Values are displayed when val/incr is within +-proxim - * to an integer. The default value is 0.15; smaller values - * result in thinner contour lines. - * (2) Negative values are rendered in red; positive values as black. - *- */ -PIX * -fpixRenderContours(FPIX *fpixs, - l_float32 incr, - l_float32 proxim) -{ -l_int32 i, j, w, h, wpls, wpld; -l_float32 val, invincr, finter, above, below, diff; -l_uint32 *datad, *lined; -l_float32 *datas, *lines; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("fpixRenderContours"); - - if (!fpixs) - return (PIX *)ERROR_PTR("fpixs not defined", procName, NULL); - if (incr <= 0.0) - return (PIX *)ERROR_PTR("incr <= 0.0", procName, NULL); - if (proxim <= 0.0) - proxim = 0.15; /* default */ - - fpixGetDimensions(fpixs, &w, &h); - if ((pixd = pixCreate(w, h, 8)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - cmap = pixcmapCreate(8); - pixSetColormap(pixd, cmap); - pixcmapAddColor(cmap, 255, 255, 255); /* white */ - pixcmapAddColor(cmap, 0, 0, 0); /* black */ - pixcmapAddColor(cmap, 255, 0, 0); /* red */ - - datas = fpixGetData(fpixs); - wpls = fpixGetWpl(fpixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - invincr = 1.0 / incr; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = lines[j]; - finter = invincr * val; - above = finter - floorf(finter); - below = ceilf(finter) - finter; - diff = L_MIN(above, below); - if (diff <= proxim) { - if (val < 0.0) - SET_DATA_BYTE(lined, j, 2); - else - SET_DATA_BYTE(lined, j, 1); - } - } - } - - return pixd; -} - - -/*------------------------------------------------------------------* - * Boundary pt generation on 1 bpp images * - *------------------------------------------------------------------*/ -/*! - * \brief pixGeneratePtaBoundary() - * - * \param[in] pixs 1 bpp - * \param[in] width of boundary line - * \return pta, or NULL on error - * - *
- * Notes: - * (1) Similar to ptaGetBoundaryPixels(), except here: - * * we only get pixels in the foreground - * * we can have a "line" width greater than 1 pixel. - * (2) Once generated, this can be applied to a random 1 bpp image - * to add a color boundary as follows: - * Pta *pta = pixGeneratePtaBoundary(pixs, width); - * Pix *pix1 = pixConvert1To8Cmap(pixs); - * pixRenderPtaArb(pix1, pta, rval, gval, bval); - *- */ -PTA * -pixGeneratePtaBoundary(PIX *pixs, - l_int32 width) -{ -PIX *pix1; -PTA *pta; - - PROCNAME("pixGeneratePtaBoundary"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (width < 1) { - L_WARNING("width < 1; setting to 1\n", procName); - width = 1; - } - - pix1 = pixErodeBrick(NULL, pixs, 2 * width + 1, 2 * width + 1); - pixXor(pix1, pix1, pixs); - pta = ptaGetPixelsFromPix(pix1, NULL); - pixDestroy(&pix1); - return pta; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/graymorph.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/graymorph.c deleted file mode 100644 index 1d7440ce..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/graymorph.c +++ /dev/null @@ -1,1376 +0,0 @@ -/*====================================================================* - - 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 graymorph.c - *
- * - * Top-level grayscale morphological operations (van Herk / Gil-Werman) - * PIX *pixErodeGray() - * PIX *pixDilateGray() - * PIX *pixOpenGray() - * PIX *pixCloseGray() - * - * Special operations for 1x3, 3x1 and 3x3 Sels (direct) - * PIX *pixErodeGray3() - * static PIX *pixErodeGray3h() - * static PIX *pixErodeGray3v() - * PIX *pixDilateGray3() - * static PIX *pixDilateGray3h() - * static PIX *pixDilateGray3v() - * PIX *pixOpenGray3() - * PIX *pixCloseGray3() - * - * Low-level grayscale morphological operations - * static void dilateGrayLow() - * static void erodeGrayLow() - * - * - * Method: Algorithm by van Herk and Gil and Werman, 1992 - * - * Measured speed of the vH/G-W implementation is about 1 output - * pixel per 120 PIII clock cycles, for a horizontal or vertical - * erosion or dilation. The computation time doubles for opening - * or closing, or for a square SE, as expected, and is independent - * of the size of the SE. - * - * A faster implementation can be made directly for brick Sels - * of maximum size 3. We unroll the computation for sets of 8 bytes. - * It needs to be called explicitly; the general functions do not - * default for the size 3 brick Sels. - * - * We use the van Herk/Gil-Werman (vHGW) algorithm, [van Herk, - * Patt. Recog. Let. 13, pp. 517-521, 1992; Gil and Werman, - * IEEE Trans PAMI 15(5), pp. 504-507, 1993.] - * This was the first grayscale morphology - * algorithm to compute dilation and erosion with - * complexity independent of the size of the structuring - * element. It is simple and elegant, and surprising that - * it was discovered as recently as 1992. It works for - * SEs composed of horizontal and/or vertical lines. The - * general case requires finding the Min or Max over an - * arbitrary set of pixels, and this requires a number of - * pixel comparisons equal to the SE "size" at each pixel - * in the image. The vHGW algorithm requires not - * more than 3 comparisons at each point. The algorithm has been - * recently refined by Gil and Kimmel ("Efficient Dilation - * Erosion, Opening and Closing Algorithms", in "Mathematical - * Morphology and its Applications to Image and Signal Processing", - * the proceedings of the International Symposium on Mathematical - * Morphology, Palo Alto, CA, June 2000, Kluwer Academic - * Publishers, pp. 301-310). They bring this number down below - * 1.5 comparisons per output pixel but at a cost of significantly - * increased complexity, so I don't bother with that here. - * - * In brief, the method is as follows. We evaluate the dilation - * in groups of "size" pixels, equal to the size of the SE. - * For horizontal, we start at x = "size"/2 and go - * (w - 2 * ("size"/2))/"size" steps. This means that - * we don't evaluate the first 0.5 * "size" pixels and, worst - * case, the last 1.5 * "size" pixels. Thus we embed the - * image in a larger image with these augmented dimensions, where - * the new border pixels are appropriately initialized (0 for - * dilation; 255 for erosion), and remove the boundary at the end. - * (For vertical, use h instead of w.) Then for each group - * of "size" pixels, we form an array of length 2 * "size" + 1, - * consisting of backward and forward partial maxima (for - * dilation) or minima (for erosion). This represents a - * jumping window computed from the source image, over which - * the SE will slide. The center of the array gets the source - * pixel at the center of the SE. Call this the center pixel - * of the window. Array values to left of center get - * the maxima(minima) of the pixels from the center - * one and going to the left an equal distance. Array - * values to the right of center get the maxima(minima) to - * the pixels from the center one and going to the right - * an equal distance. These are computed sequentially starting - * from the center one. The SE (of length "size") can slide over this - * window (of length 2 * "size + 1) at "size" different places. - * At each place, the maxima(minima) of the values in the window - * that correspond to the end points of the SE give the extremal - * values over that interval, and these are stored at the dest - * pixel corresponding to the SE center. A picture is worth - * at least this many words, so if this isn't clear, see the - * leptonica documentation on grayscale morphology. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) Sel is a brick with all elements being hits - * (2) If hsize = vsize = 1, just returns a copy. - *- */ -PIX * -pixErodeGray(PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -l_uint8 *buffer, *minarray; -l_int32 w, h, wplb, wplt; -l_int32 leftpix, rightpix, toppix, bottompix, maxsize; -l_uint32 *datab, *datat; -PIX *pixb, *pixt, *pixd; - - PROCNAME("pixErodeGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); - if ((hsize & 1) == 0 ) { - L_WARNING("horiz sel size must be odd; increasing by 1\n", procName); - hsize++; - } - if ((vsize & 1) == 0 ) { - L_WARNING("vert sel size must be odd; increasing by 1\n", procName); - vsize++; - } - - pixb = pixt = pixd = NULL; - buffer = minarray = NULL; - - if (hsize == 1 && vsize == 1) - return pixCopy(NULL, pixs); - - if (vsize == 1) { /* horizontal sel */ - leftpix = (hsize + 1) / 2; - rightpix = (3 * hsize + 1) / 2; - toppix = 0; - bottompix = 0; - } else if (hsize == 1) { /* vertical sel */ - leftpix = 0; - rightpix = 0; - toppix = (vsize + 1) / 2; - bottompix = (3 * vsize + 1) / 2; - } else { - leftpix = (hsize + 1) / 2; - rightpix = (3 * hsize + 1) / 2; - toppix = (vsize + 1) / 2; - bottompix = (3 * vsize + 1) / 2; - } - - pixb = pixAddBorderGeneral(pixs, leftpix, rightpix, toppix, bottompix, 255); - pixt = pixCreateTemplate(pixb); - if (!pixb || !pixt) { - L_ERROR("pixb and pixt not made\n", procName); - goto cleanup; - } - - pixGetDimensions(pixt, &w, &h, NULL); - datab = pixGetData(pixb); - datat = pixGetData(pixt); - wplb = pixGetWpl(pixb); - wplt = pixGetWpl(pixt); - - buffer = (l_uint8 *)LEPT_CALLOC(L_MAX(w, h), sizeof(l_uint8)); - maxsize = L_MAX(hsize, vsize); - minarray = (l_uint8 *)LEPT_CALLOC(2 * maxsize, sizeof(l_uint8)); - if (!buffer || !minarray) { - L_ERROR("buffer and minarray not made\n", procName); - goto cleanup; - } - - if (vsize == 1) { - erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, - buffer, minarray); - } else if (hsize == 1) { - erodeGrayLow(datat, w, h, wplt, datab, wplb, vsize, L_VERT, - buffer, minarray); - } else { - erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, - buffer, minarray); - pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, - PIX_SET); - erodeGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, - buffer, minarray); - pixDestroy(&pixt); - pixt = pixClone(pixb); - } - - pixd = pixRemoveBorderGeneral(pixt, leftpix, rightpix, toppix, bottompix); - if (!pixd) - L_ERROR("pixd not made\n", procName); - -cleanup: - LEPT_FREE(buffer); - LEPT_FREE(minarray); - pixDestroy(&pixb); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixDilateGray() - * - * \param[in] pixs - * \param[in] hsize of Sel; must be odd; origin implicitly in center - * \param[in] vsize ditto - * \return pixd - * - *
- * Notes: - * (1) Sel is a brick with all elements being hits - * (2) If hsize = vsize = 1, just returns a copy. - *- */ -PIX * -pixDilateGray(PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -l_uint8 *buffer, *maxarray; -l_int32 w, h, wplb, wplt; -l_int32 leftpix, rightpix, toppix, bottompix, maxsize; -l_uint32 *datab, *datat; -PIX *pixb, *pixt, *pixd; - - PROCNAME("pixDilateGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); - if ((hsize & 1) == 0 ) { - L_WARNING("horiz sel size must be odd; increasing by 1\n", procName); - hsize++; - } - if ((vsize & 1) == 0 ) { - L_WARNING("vert sel size must be odd; increasing by 1\n", procName); - vsize++; - } - - pixb = pixt = pixd = NULL; - buffer = maxarray = NULL; - - if (hsize == 1 && vsize == 1) - return pixCopy(NULL, pixs); - - if (vsize == 1) { /* horizontal sel */ - leftpix = (hsize + 1) / 2; - rightpix = (3 * hsize + 1) / 2; - toppix = 0; - bottompix = 0; - } else if (hsize == 1) { /* vertical sel */ - leftpix = 0; - rightpix = 0; - toppix = (vsize + 1) / 2; - bottompix = (3 * vsize + 1) / 2; - } else { - leftpix = (hsize + 1) / 2; - rightpix = (3 * hsize + 1) / 2; - toppix = (vsize + 1) / 2; - bottompix = (3 * vsize + 1) / 2; - } - - pixb = pixAddBorderGeneral(pixs, leftpix, rightpix, toppix, bottompix, 0); - pixt = pixCreateTemplate(pixb); - if (!pixb || !pixt) { - L_ERROR("pixb and pixt not made\n", procName); - goto cleanup; - } - - pixGetDimensions(pixt, &w, &h, NULL); - datab = pixGetData(pixb); - datat = pixGetData(pixt); - wplb = pixGetWpl(pixb); - wplt = pixGetWpl(pixt); - - buffer = (l_uint8 *)LEPT_CALLOC(L_MAX(w, h), sizeof(l_uint8)); - maxsize = L_MAX(hsize, vsize); - maxarray = (l_uint8 *)LEPT_CALLOC(2 * maxsize, sizeof(l_uint8)); - if (!buffer || !maxarray) { - L_ERROR("buffer and maxarray not made\n", procName); - goto cleanup; - } - - if (vsize == 1) { - dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, - buffer, maxarray); - } else if (hsize == 1) { - dilateGrayLow(datat, w, h, wplt, datab, wplb, vsize, L_VERT, - buffer, maxarray); - } else { - dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, - buffer, maxarray); - pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, - PIX_CLR); - dilateGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, - buffer, maxarray); - pixDestroy(&pixt); - pixt = pixClone(pixb); - } - - pixd = pixRemoveBorderGeneral(pixt, leftpix, rightpix, toppix, bottompix); - if (!pixd) - L_ERROR("pixd not made\n", procName); - -cleanup: - LEPT_FREE(buffer); - LEPT_FREE(maxarray); - pixDestroy(&pixb); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixOpenGray() - * - * \param[in] pixs - * \param[in] hsize of Sel; must be odd; origin implicitly in center - * \param[in] vsize ditto - * \return pixd - * - *
- * Notes: - * (1) Sel is a brick with all elements being hits - * (2) If hsize = vsize = 1, just returns a copy. - *- */ -PIX * -pixOpenGray(PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -l_uint8 *buffer; -l_uint8 *array; /* used to find either min or max in interval */ -l_int32 w, h, wplb, wplt; -l_int32 leftpix, rightpix, toppix, bottompix, maxsize; -l_uint32 *datab, *datat; -PIX *pixb, *pixt, *pixd; - - PROCNAME("pixOpenGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); - if ((hsize & 1) == 0 ) { - L_WARNING("horiz sel size must be odd; increasing by 1\n", procName); - hsize++; - } - if ((vsize & 1) == 0 ) { - L_WARNING("vert sel size must be odd; increasing by 1\n", procName); - vsize++; - } - - pixb = pixt = pixd = NULL; - buffer = array = NULL; - - if (hsize == 1 && vsize == 1) - return pixCopy(NULL, pixs); - - if (vsize == 1) { /* horizontal sel */ - leftpix = (hsize + 1) / 2; - rightpix = (3 * hsize + 1) / 2; - toppix = 0; - bottompix = 0; - } else if (hsize == 1) { /* vertical sel */ - leftpix = 0; - rightpix = 0; - toppix = (vsize + 1) / 2; - bottompix = (3 * vsize + 1) / 2; - } else { - leftpix = (hsize + 1) / 2; - rightpix = (3 * hsize + 1) / 2; - toppix = (vsize + 1) / 2; - bottompix = (3 * vsize + 1) / 2; - } - - pixb = pixAddBorderGeneral(pixs, leftpix, rightpix, toppix, bottompix, 255); - pixt = pixCreateTemplate(pixb); - if (!pixb || !pixt) { - L_ERROR("pixb and pixt not made\n", procName); - goto cleanup; - } - - pixGetDimensions(pixt, &w, &h, NULL); - datab = pixGetData(pixb); - datat = pixGetData(pixt); - wplb = pixGetWpl(pixb); - wplt = pixGetWpl(pixt); - - buffer = (l_uint8 *)LEPT_CALLOC(L_MAX(w, h), sizeof(l_uint8)); - maxsize = L_MAX(hsize, vsize); - array = (l_uint8 *)LEPT_CALLOC(2 * maxsize, sizeof(l_uint8)); - if (!buffer || !array) { - L_ERROR("buffer and array not made\n", procName); - goto cleanup; - } - - if (vsize == 1) { - erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, - buffer, array); - pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, - PIX_CLR); - dilateGrayLow(datab, w, h, wplb, datat, wplt, hsize, L_HORIZ, - buffer, array); - } - else if (hsize == 1) { - erodeGrayLow(datat, w, h, wplt, datab, wplb, vsize, L_VERT, - buffer, array); - pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, - PIX_CLR); - dilateGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, - buffer, array); - } else { - erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, - buffer, array); - pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, - PIX_SET); - erodeGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, - buffer, array); - pixSetOrClearBorder(pixb, leftpix, rightpix, toppix, bottompix, - PIX_CLR); - dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, - buffer, array); - pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, - PIX_CLR); - dilateGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, - buffer, array); - } - - pixd = pixRemoveBorderGeneral(pixb, leftpix, rightpix, toppix, bottompix); - if (!pixd) - L_ERROR("pixd not made\n", procName); - -cleanup: - LEPT_FREE(buffer); - LEPT_FREE(array); - pixDestroy(&pixb); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixCloseGray() - * - * \param[in] pixs - * \param[in] hsize of Sel; must be odd; origin implicitly in center - * \param[in] vsize ditto - * \return pixd - * - *
- * Notes: - * (1) Sel is a brick with all elements being hits - * (2) If hsize = vsize = 1, just returns a copy. - *- */ -PIX * -pixCloseGray(PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -l_uint8 *buffer; -l_uint8 *array; /* used to find either min or max in interval */ -l_int32 w, h, wplb, wplt; -l_int32 leftpix, rightpix, toppix, bottompix, maxsize; -l_uint32 *datab, *datat; -PIX *pixb, *pixt, *pixd; - - PROCNAME("pixCloseGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); - if ((hsize & 1) == 0 ) { - L_WARNING("horiz sel size must be odd; increasing by 1\n", procName); - hsize++; - } - if ((vsize & 1) == 0 ) { - L_WARNING("vert sel size must be odd; increasing by 1\n", procName); - vsize++; - } - - pixb = pixt = pixd = NULL; - buffer = array = NULL; - - if (hsize == 1 && vsize == 1) - return pixCopy(NULL, pixs); - - if (vsize == 1) { /* horizontal sel */ - leftpix = (hsize + 1) / 2; - rightpix = (3 * hsize + 1) / 2; - toppix = 0; - bottompix = 0; - } else if (hsize == 1) { /* vertical sel */ - leftpix = 0; - rightpix = 0; - toppix = (vsize + 1) / 2; - bottompix = (3 * vsize + 1) / 2; - } else { - leftpix = (hsize + 1) / 2; - rightpix = (3 * hsize + 1) / 2; - toppix = (vsize + 1) / 2; - bottompix = (3 * vsize + 1) / 2; - } - - pixb = pixAddBorderGeneral(pixs, leftpix, rightpix, toppix, bottompix, 0); - pixt = pixCreateTemplate(pixb); - if (!pixb || !pixt) { - L_ERROR("pixb and pixt not made\n", procName); - goto cleanup; - } - - pixGetDimensions(pixt, &w, &h, NULL); - datab = pixGetData(pixb); - datat = pixGetData(pixt); - wplb = pixGetWpl(pixb); - wplt = pixGetWpl(pixt); - - buffer = (l_uint8 *)LEPT_CALLOC(L_MAX(w, h), sizeof(l_uint8)); - maxsize = L_MAX(hsize, vsize); - array = (l_uint8 *)LEPT_CALLOC(2 * maxsize, sizeof(l_uint8)); - if (!buffer || !array) { - L_ERROR("buffer and array not made\n", procName); - goto cleanup; - } - - if (vsize == 1) { - dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, - buffer, array); - pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, - PIX_SET); - erodeGrayLow(datab, w, h, wplb, datat, wplt, hsize, L_HORIZ, - buffer, array); - } else if (hsize == 1) { - dilateGrayLow(datat, w, h, wplt, datab, wplb, vsize, L_VERT, - buffer, array); - pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, - PIX_SET); - erodeGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, - buffer, array); - } else { - dilateGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, - buffer, array); - pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, - PIX_CLR); - dilateGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, - buffer, array); - pixSetOrClearBorder(pixb, leftpix, rightpix, toppix, bottompix, - PIX_SET); - erodeGrayLow(datat, w, h, wplt, datab, wplb, hsize, L_HORIZ, - buffer, array); - pixSetOrClearBorder(pixt, leftpix, rightpix, toppix, bottompix, - PIX_SET); - erodeGrayLow(datab, w, h, wplb, datat, wplt, vsize, L_VERT, - buffer, array); - } - - pixd = pixRemoveBorderGeneral(pixb, leftpix, rightpix, toppix, bottompix); - if (!pixd) - L_ERROR("pixd not made\n", procName); - -cleanup: - LEPT_FREE(buffer); - LEPT_FREE(array); - pixDestroy(&pixb); - pixDestroy(&pixt); - return pixd; -} - - -/*-----------------------------------------------------------------* - * Special operations for 1x3, 3x1 and 3x3 Sels * - *-----------------------------------------------------------------*/ -/*! - * \brief pixErodeGray3() - * - * \param[in] pixs 8 bpp, not cmapped - * \param[in] hsize 1 or 3 - * \param[in] vsize 1 or 3 - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits) - * (2) If hsize = vsize = 1, just returns a copy. - * (3) It would be nice not to add a border, but it is required - * if we want the same results as from the general case. - * We add 4 bytes on the left to speed up the copying, and - * 8 bytes at the right and bottom to allow unrolling of - * the computation of 8 pixels. - *- */ -PIX * -pixErodeGray3(PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -PIX *pixt, *pixb, *pixbd, *pixd; - - PROCNAME("pixErodeGray3"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pix has colormap", procName, NULL); - if ((hsize != 1 && hsize != 3) || - (vsize != 1 && vsize != 3)) - return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", procName, NULL); - - if (hsize == 1 && vsize == 1) - return pixCopy(NULL, pixs); - - pixb = pixAddBorderGeneral(pixs, 4, 8, 2, 8, 255); - - if (vsize == 1) - pixbd = pixErodeGray3h(pixb); - else if (hsize == 1) - pixbd = pixErodeGray3v(pixb); - else { /* vize == hsize == 3 */ - pixt = pixErodeGray3h(pixb); - pixbd = pixErodeGray3v(pixt); - pixDestroy(&pixt); - } - - pixd = pixRemoveBorderGeneral(pixbd, 4, 8, 2, 8); - pixDestroy(&pixb); - pixDestroy(&pixbd); - return pixd; -} - - -/*! - * \brief pixErodeGray3h() - * - * \param[in] pixs 8 bpp, not cmapped - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Special case for horizontal 3x1 brick Sel; - * also used as the first step for the 3x3 brick Sel. - *- */ -static PIX * -pixErodeGray3h(PIX *pixs) -{ -l_uint32 *datas, *datad, *lines, *lined; -l_int32 w, h, wpl, i, j; -l_int32 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9, minval; -PIX *pixd; - - PROCNAME("pixErodeGray3h"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - - pixd = pixCreateTemplate(pixs); - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpl = pixGetWpl(pixs); - for (i = 0; i < h; i++) { - lines = datas + i * wpl; - lined = datad + i * wpl; - for (j = 1; j < w - 8; j += 8) { - val0 = GET_DATA_BYTE(lines, j - 1); - val1 = GET_DATA_BYTE(lines, j); - val2 = GET_DATA_BYTE(lines, j + 1); - val3 = GET_DATA_BYTE(lines, j + 2); - val4 = GET_DATA_BYTE(lines, j + 3); - val5 = GET_DATA_BYTE(lines, j + 4); - val6 = GET_DATA_BYTE(lines, j + 5); - val7 = GET_DATA_BYTE(lines, j + 6); - val8 = GET_DATA_BYTE(lines, j + 7); - val9 = GET_DATA_BYTE(lines, j + 8); - minval = L_MIN(val1, val2); - SET_DATA_BYTE(lined, j, L_MIN(val0, minval)); - SET_DATA_BYTE(lined, j + 1, L_MIN(minval, val3)); - minval = L_MIN(val3, val4); - SET_DATA_BYTE(lined, j + 2, L_MIN(val2, minval)); - SET_DATA_BYTE(lined, j + 3, L_MIN(minval, val5)); - minval = L_MIN(val5, val6); - SET_DATA_BYTE(lined, j + 4, L_MIN(val4, minval)); - SET_DATA_BYTE(lined, j + 5, L_MIN(minval, val7)); - minval = L_MIN(val7, val8); - SET_DATA_BYTE(lined, j + 6, L_MIN(val6, minval)); - SET_DATA_BYTE(lined, j + 7, L_MIN(minval, val9)); - } - } - return pixd; -} - - -/*! - * \brief pixErodeGray3v() - * - * \param[in] pixs 8 bpp, not cmapped - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Special case for vertical 1x3 brick Sel; - * also used as the second step for the 3x3 brick Sel. - * (2) Surprisingly, this is faster than setting up the - * lineptrs array and accessing into it; e.g., - * val4 = GET_DATA_BYTE(lines8[i + 3], j); - *- */ -static PIX * -pixErodeGray3v(PIX *pixs) -{ -l_uint32 *datas, *datad, *linesi, *linedi; -l_int32 w, h, wpl, i, j; -l_int32 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9, minval; -PIX *pixd; - - PROCNAME("pixErodeGray3v"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - - pixd = pixCreateTemplate(pixs); - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpl = pixGetWpl(pixs); - for (j = 0; j < w; j++) { - for (i = 1; i < h - 8; i += 8) { - linesi = datas + i * wpl; - linedi = datad + i * wpl; - val0 = GET_DATA_BYTE(linesi - wpl, j); - val1 = GET_DATA_BYTE(linesi, j); - val2 = GET_DATA_BYTE(linesi + wpl, j); - val3 = GET_DATA_BYTE(linesi + 2 * wpl, j); - val4 = GET_DATA_BYTE(linesi + 3 * wpl, j); - val5 = GET_DATA_BYTE(linesi + 4 * wpl, j); - val6 = GET_DATA_BYTE(linesi + 5 * wpl, j); - val7 = GET_DATA_BYTE(linesi + 6 * wpl, j); - val8 = GET_DATA_BYTE(linesi + 7 * wpl, j); - val9 = GET_DATA_BYTE(linesi + 8 * wpl, j); - minval = L_MIN(val1, val2); - SET_DATA_BYTE(linedi, j, L_MIN(val0, minval)); - SET_DATA_BYTE(linedi + wpl, j, L_MIN(minval, val3)); - minval = L_MIN(val3, val4); - SET_DATA_BYTE(linedi + 2 * wpl, j, L_MIN(val2, minval)); - SET_DATA_BYTE(linedi + 3 * wpl, j, L_MIN(minval, val5)); - minval = L_MIN(val5, val6); - SET_DATA_BYTE(linedi + 4 * wpl, j, L_MIN(val4, minval)); - SET_DATA_BYTE(linedi + 5 * wpl, j, L_MIN(minval, val7)); - minval = L_MIN(val7, val8); - SET_DATA_BYTE(linedi + 6 * wpl, j, L_MIN(val6, minval)); - SET_DATA_BYTE(linedi + 7 * wpl, j, L_MIN(minval, val9)); - } - } - return pixd; -} - - -/*! - * \brief pixDilateGray3() - * - * \param[in] pixs 8 bpp, not cmapped - * \param[in] hsize 1 or 3 - * \param[in] vsize 1 or 3 - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits) - * (2) If hsize = vsize = 1, just returns a copy. - *- */ -PIX * -pixDilateGray3(PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -PIX *pixt, *pixb, *pixbd, *pixd; - - PROCNAME("pixDilateGray3"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pix has colormap", procName, NULL); - if ((hsize != 1 && hsize != 3) || - (vsize != 1 && vsize != 3)) - return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", procName, NULL); - - if (hsize == 1 && vsize == 1) - return pixCopy(NULL, pixs); - - pixb = pixAddBorderGeneral(pixs, 4, 8, 2, 8, 0); - - if (vsize == 1) - pixbd = pixDilateGray3h(pixb); - else if (hsize == 1) - pixbd = pixDilateGray3v(pixb); - else { /* vize == hsize == 3 */ - pixt = pixDilateGray3h(pixb); - pixbd = pixDilateGray3v(pixt); - pixDestroy(&pixt); - } - - pixd = pixRemoveBorderGeneral(pixbd, 4, 8, 2, 8); - pixDestroy(&pixb); - pixDestroy(&pixbd); - return pixd; -} - - -/*! - * \brief pixDilateGray3h() - * - * \param[in] pixs 8 bpp, not cmapped - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Special case for horizontal 3x1 brick Sel; - * also used as the first step for the 3x3 brick Sel. - *- */ -static PIX * -pixDilateGray3h(PIX *pixs) -{ -l_uint32 *datas, *datad, *lines, *lined; -l_int32 w, h, wpl, i, j; -l_int32 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9, maxval; -PIX *pixd; - - PROCNAME("pixDilateGray3h"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - - pixd = pixCreateTemplate(pixs); - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpl = pixGetWpl(pixs); - for (i = 0; i < h; i++) { - lines = datas + i * wpl; - lined = datad + i * wpl; - for (j = 1; j < w - 8; j += 8) { - val0 = GET_DATA_BYTE(lines, j - 1); - val1 = GET_DATA_BYTE(lines, j); - val2 = GET_DATA_BYTE(lines, j + 1); - val3 = GET_DATA_BYTE(lines, j + 2); - val4 = GET_DATA_BYTE(lines, j + 3); - val5 = GET_DATA_BYTE(lines, j + 4); - val6 = GET_DATA_BYTE(lines, j + 5); - val7 = GET_DATA_BYTE(lines, j + 6); - val8 = GET_DATA_BYTE(lines, j + 7); - val9 = GET_DATA_BYTE(lines, j + 8); - maxval = L_MAX(val1, val2); - SET_DATA_BYTE(lined, j, L_MAX(val0, maxval)); - SET_DATA_BYTE(lined, j + 1, L_MAX(maxval, val3)); - maxval = L_MAX(val3, val4); - SET_DATA_BYTE(lined, j + 2, L_MAX(val2, maxval)); - SET_DATA_BYTE(lined, j + 3, L_MAX(maxval, val5)); - maxval = L_MAX(val5, val6); - SET_DATA_BYTE(lined, j + 4, L_MAX(val4, maxval)); - SET_DATA_BYTE(lined, j + 5, L_MAX(maxval, val7)); - maxval = L_MAX(val7, val8); - SET_DATA_BYTE(lined, j + 6, L_MAX(val6, maxval)); - SET_DATA_BYTE(lined, j + 7, L_MAX(maxval, val9)); - } - } - return pixd; -} - - -/*! - * \brief pixDilateGray3v() - * - * \param[in] pixs 8 bpp, not cmapped - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Special case for vertical 1x3 brick Sel; - * also used as the second step for the 3x3 brick Sel. - *- */ -static PIX * -pixDilateGray3v(PIX *pixs) -{ -l_uint32 *datas, *datad, *linesi, *linedi; -l_int32 w, h, wpl, i, j; -l_int32 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9, maxval; -PIX *pixd; - - PROCNAME("pixDilateGray3v"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - - pixd = pixCreateTemplate(pixs); - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpl = pixGetWpl(pixs); - for (j = 0; j < w; j++) { - for (i = 1; i < h - 8; i += 8) { - linesi = datas + i * wpl; - linedi = datad + i * wpl; - val0 = GET_DATA_BYTE(linesi - wpl, j); - val1 = GET_DATA_BYTE(linesi, j); - val2 = GET_DATA_BYTE(linesi + wpl, j); - val3 = GET_DATA_BYTE(linesi + 2 * wpl, j); - val4 = GET_DATA_BYTE(linesi + 3 * wpl, j); - val5 = GET_DATA_BYTE(linesi + 4 * wpl, j); - val6 = GET_DATA_BYTE(linesi + 5 * wpl, j); - val7 = GET_DATA_BYTE(linesi + 6 * wpl, j); - val8 = GET_DATA_BYTE(linesi + 7 * wpl, j); - val9 = GET_DATA_BYTE(linesi + 8 * wpl, j); - maxval = L_MAX(val1, val2); - SET_DATA_BYTE(linedi, j, L_MAX(val0, maxval)); - SET_DATA_BYTE(linedi + wpl, j, L_MAX(maxval, val3)); - maxval = L_MAX(val3, val4); - SET_DATA_BYTE(linedi + 2 * wpl, j, L_MAX(val2, maxval)); - SET_DATA_BYTE(linedi + 3 * wpl, j, L_MAX(maxval, val5)); - maxval = L_MAX(val5, val6); - SET_DATA_BYTE(linedi + 4 * wpl, j, L_MAX(val4, maxval)); - SET_DATA_BYTE(linedi + 5 * wpl, j, L_MAX(maxval, val7)); - maxval = L_MAX(val7, val8); - SET_DATA_BYTE(linedi + 6 * wpl, j, L_MAX(val6, maxval)); - SET_DATA_BYTE(linedi + 7 * wpl, j, L_MAX(maxval, val9)); - } - } - return pixd; -} - - -/*! - * \brief pixOpenGray3() - * - * \param[in] pixs 8 bpp, not cmapped - * \param[in] hsize 1 or 3 - * \param[in] vsize 1 or 3 - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits) - * (2) If hsize = vsize = 1, just returns a copy. - * (3) It would be nice not to add a border, but it is required - * to get the same results as for the general case. - *- */ -PIX * -pixOpenGray3(PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -PIX *pixt, *pixb, *pixbd, *pixd; - - PROCNAME("pixOpenGray3"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pix has colormap", procName, NULL); - if ((hsize != 1 && hsize != 3) || - (vsize != 1 && vsize != 3)) - return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", procName, NULL); - - if (hsize == 1 && vsize == 1) - return pixCopy(NULL, pixs); - - pixb = pixAddBorderGeneral(pixs, 4, 8, 2, 8, 255); /* set to max */ - - if (vsize == 1) { - pixt = pixErodeGray3h(pixb); - pixSetBorderVal(pixt, 4, 8, 2, 8, 0); /* set to min */ - pixbd = pixDilateGray3h(pixt); - pixDestroy(&pixt); - } else if (hsize == 1) { - pixt = pixErodeGray3v(pixb); - pixSetBorderVal(pixt, 4, 8, 2, 8, 0); - pixbd = pixDilateGray3v(pixt); - pixDestroy(&pixt); - } else { /* vize == hsize == 3 */ - pixt = pixErodeGray3h(pixb); - pixbd = pixErodeGray3v(pixt); - pixDestroy(&pixt); - pixSetBorderVal(pixbd, 4, 8, 2, 8, 0); - pixt = pixDilateGray3h(pixbd); - pixDestroy(&pixbd); - pixbd = pixDilateGray3v(pixt); - pixDestroy(&pixt); - } - - pixd = pixRemoveBorderGeneral(pixbd, 4, 8, 2, 8); - pixDestroy(&pixb); - pixDestroy(&pixbd); - return pixd; -} - - -/*! - * \brief pixCloseGray3() - * - * \param[in] pixs 8 bpp, not cmapped - * \param[in] hsize 1 or 3 - * \param[in] vsize 1 or 3 - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Special case for 1x3, 3x1 or 3x3 brick sel (all hits) - * (2) If hsize = vsize = 1, just returns a copy. - *- */ -PIX * -pixCloseGray3(PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -PIX *pixt, *pixb, *pixbd, *pixd; - - PROCNAME("pixCloseGray3"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pix has colormap", procName, NULL); - if ((hsize != 1 && hsize != 3) || - (vsize != 1 && vsize != 3)) - return (PIX *)ERROR_PTR("invalid size: must be 1 or 3", procName, NULL); - - if (hsize == 1 && vsize == 1) - return pixCopy(NULL, pixs); - - pixb = pixAddBorderGeneral(pixs, 4, 8, 2, 8, 0); /* set to min */ - - if (vsize == 1) { - pixt = pixDilateGray3h(pixb); - pixSetBorderVal(pixt, 4, 8, 2, 8, 255); /* set to max */ - pixbd = pixErodeGray3h(pixt); - pixDestroy(&pixt); - } else if (hsize == 1) { - pixt = pixDilateGray3v(pixb); - pixSetBorderVal(pixt, 4, 8, 2, 8, 255); - pixbd = pixErodeGray3v(pixt); - pixDestroy(&pixt); - } else { /* vize == hsize == 3 */ - pixt = pixDilateGray3h(pixb); - pixbd = pixDilateGray3v(pixt); - pixDestroy(&pixt); - pixSetBorderVal(pixbd, 4, 8, 2, 8, 255); - pixt = pixErodeGray3h(pixbd); - pixDestroy(&pixbd); - pixbd = pixErodeGray3v(pixt); - pixDestroy(&pixt); - } - - pixd = pixRemoveBorderGeneral(pixbd, 4, 8, 2, 8); - pixDestroy(&pixb); - pixDestroy(&pixbd); - return pixd; -} - - -/*-----------------------------------------------------------------* - * Low-level gray morphological operations * - *-----------------------------------------------------------------*/ -/*! - * \brief dilateGrayLow() - * - * \param[in] datad 8 bpp dsst image - * \param[in] w, h dimensions of src and dest - * \param[in] wpld words/line of dest - * \param[in] datas 8 bpp src image - * \param[in] wpls words/line of src - * \param[in] size full length of SEL; restricted to odd numbers - * \param[in] direction L_HORIZ or L_VERT - * \param[in] buffer holds full line or column of src image pixels - * \param[in] maxarray array of dimension 2*size+1 - * \return void - * - *
- * Notes: - * (1) To eliminate border effects on the actual image, these images - * are prepared with an additional border of dimensions: - * leftpix = 0.5 * size - * rightpix = 1.5 * size - * toppix = 0.5 * size - * bottompix = 1.5 * size - * and we initialize the src border pixels to 0. - * This allows full processing over the actual image; at - * the end the border is removed. - * (2) Uses algorithm of van Herk, Gil and Werman - *- */ -static void -dilateGrayLow(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls, - l_int32 size, - l_int32 direction, - l_uint8 *buffer, - l_uint8 *maxarray) -{ -l_int32 i, j, k; -l_int32 hsize, nsteps, startmax, startx, starty; -l_uint8 maxval; -l_uint32 *lines, *lined; - - if (direction == L_HORIZ) { - hsize = size / 2; - nsteps = (w - 2 * hsize) / size; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - - /* fill buffer with pixels in byte order */ - for (j = 0; j < w; j++) - buffer[j] = GET_DATA_BYTE(lines, j); - - for (j = 0; j < nsteps; j++) { - /* refill the minarray */ - startmax = (j + 1) * size - 1; - maxarray[size - 1] = buffer[startmax]; - for (k = 1; k < size; k++) { - maxarray[size - 1 - k] = - L_MAX(maxarray[size - k], buffer[startmax - k]); - maxarray[size - 1 + k] = - L_MAX(maxarray[size + k - 2], buffer[startmax + k]); - } - - /* compute dilation values */ - startx = hsize + j * size; - SET_DATA_BYTE(lined, startx, maxarray[0]); - SET_DATA_BYTE(lined, startx + size - 1, maxarray[2 * size - 2]); - for (k = 1; k < size - 1; k++) { - maxval = L_MAX(maxarray[k], maxarray[k + size - 1]); - SET_DATA_BYTE(lined, startx + k, maxval); - } - } - } - } else { /* direction == L_VERT */ - hsize = size / 2; - nsteps = (h - 2 * hsize) / size; - for (j = 0; j < w; j++) { - /* fill buffer with pixels in byte order */ - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - buffer[i] = GET_DATA_BYTE(lines, j); - } - - for (i = 0; i < nsteps; i++) { - /* refill the minarray */ - startmax = (i + 1) * size - 1; - maxarray[size - 1] = buffer[startmax]; - for (k = 1; k < size; k++) { - maxarray[size - 1 - k] = - L_MAX(maxarray[size - k], buffer[startmax - k]); - maxarray[size - 1 + k] = - L_MAX(maxarray[size + k - 2], buffer[startmax + k]); - } - - /* compute dilation values */ - starty = hsize + i * size; - lined = datad + starty * wpld; - SET_DATA_BYTE(lined, j, maxarray[0]); - SET_DATA_BYTE(lined + (size - 1) * wpld, j, - maxarray[2 * size - 2]); - for (k = 1; k < size - 1; k++) { - maxval = L_MAX(maxarray[k], maxarray[k + size - 1]); - SET_DATA_BYTE(lined + wpld * k, j, maxval); - } - } - } - } - - return; -} - - -/*! - * \brief erodeGrayLow() - * - * \param[in] datad 8 bpp dsst image - * \param[in] w, h dimensions of src and dest - * \param[in] wpld words/line of dest - * \param[in] datas 8 bpp src image - * \param[in] wpls words/line of src - * \param[in] size full length of SEL; restricted to odd numbers - * \param[in] direction L_HORIZ or L_VERT - * \param[in] buffer holds full line or column of src image pixels - * \param[in] minarray array of dimension 2*size+1 - * \return void - * - *
- * Notes: - * (1) See notes in dilateGrayLow() - *- */ -static void -erodeGrayLow(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls, - l_int32 size, - l_int32 direction, - l_uint8 *buffer, - l_uint8 *minarray) -{ -l_int32 i, j, k; -l_int32 hsize, nsteps, startmin, startx, starty; -l_uint8 minval; -l_uint32 *lines, *lined; - - if (direction == L_HORIZ) { - hsize = size / 2; - nsteps = (w - 2 * hsize) / size; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - - /* fill buffer with pixels in byte order */ - for (j = 0; j < w; j++) - buffer[j] = GET_DATA_BYTE(lines, j); - - for (j = 0; j < nsteps; j++) { - /* refill the minarray */ - startmin = (j + 1) * size - 1; - minarray[size - 1] = buffer[startmin]; - for (k = 1; k < size; k++) { - minarray[size - 1 - k] = - L_MIN(minarray[size - k], buffer[startmin - k]); - minarray[size - 1 + k] = - L_MIN(minarray[size + k - 2], buffer[startmin + k]); - } - - /* compute erosion values */ - startx = hsize + j * size; - SET_DATA_BYTE(lined, startx, minarray[0]); - SET_DATA_BYTE(lined, startx + size - 1, minarray[2 * size - 2]); - for (k = 1; k < size - 1; k++) { - minval = L_MIN(minarray[k], minarray[k + size - 1]); - SET_DATA_BYTE(lined, startx + k, minval); - } - } - } - } else { /* direction == L_VERT */ - hsize = size / 2; - nsteps = (h - 2 * hsize) / size; - for (j = 0; j < w; j++) { - /* fill buffer with pixels in byte order */ - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - buffer[i] = GET_DATA_BYTE(lines, j); - } - - for (i = 0; i < nsteps; i++) { - /* refill the minarray */ - startmin = (i + 1) * size - 1; - minarray[size - 1] = buffer[startmin]; - for (k = 1; k < size; k++) { - minarray[size - 1 - k] = - L_MIN(minarray[size - k], buffer[startmin - k]); - minarray[size - 1 + k] = - L_MIN(minarray[size + k - 2], buffer[startmin + k]); - } - - /* compute erosion values */ - starty = hsize + i * size; - lined = datad + starty * wpld; - SET_DATA_BYTE(lined, j, minarray[0]); - SET_DATA_BYTE(lined + (size - 1) * wpld, j, - minarray[2 * size - 2]); - for (k = 1; k < size - 1; k++) { - minval = L_MIN(minarray[k], minarray[k + size - 1]); - SET_DATA_BYTE(lined + wpld * k, j, minval); - } - } - } - } - - return; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/grayquant.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/grayquant.c deleted file mode 100644 index e9d1b9fa..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/grayquant.c +++ /dev/null @@ -1,2912 +0,0 @@ -/*====================================================================* - - 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 grayquant.c - *
- * - * Thresholding from 8 bpp to 1 bpp - * - * Floyd-Steinberg dithering to binary - * PIX *pixDitherToBinary() - * PIX *pixDitherToBinarySpec() - * static void ditherToBinaryLow() - * void ditherToBinaryLineLow() - * - * Simple (pixelwise) binarization with fixed threshold - * PIX *pixThresholdToBinary() - * static void thresholdToBinaryLow() - * void thresholdToBinaryLineLow() - * - * Binarization with variable threshold - * PIX *pixVarThresholdToBinary() - * - * Binarization by adaptive mapping - * PIX *pixAdaptThresholdToBinary() - * PIX *pixAdaptThresholdToBinaryGen() - * - * Generate a binary mask from pixels of particular values - * PIX *pixGenerateMaskByValue() - * PIX *pixGenerateMaskByBand() - * - * Thresholding from 8 bpp to 2 bpp - * - * Floyd-Steinberg-like dithering to 2 bpp - * PIX *pixDitherTo2bpp() - * PIX *pixDitherTo2bppSpec() - * static void ditherTo2bppLow() - * static void ditherTo2bppLineLow() - * static l_int32 make8To2DitherTables() - * - * Simple (pixelwise) thresholding to 2 bpp with optional cmap - * PIX *pixThresholdTo2bpp() - * static void thresholdTo2bppLow() - * - * Simple (pixelwise) thresholding from 8 bpp to 4 bpp - * PIX *pixThresholdTo4bpp() - * static void thresholdTo4bppLow() - * - * Simple (pixelwise) quantization on 8 bpp grayscale - * PIX *pixThresholdOn8bpp() - * - * Arbitrary (pixelwise) thresholding from 8 bpp to 2, 4 or 8 bpp - * PIX *pixThresholdGrayArb() - * - * Quantization tables for linear thresholds of grayscale images - * l_int32 *makeGrayQuantIndexTable() - * static l_int32 *makeGrayQuantTargetTable() - * - * Quantization table for arbitrary thresholding of grayscale images - * l_int32 makeGrayQuantTableArb() - * static l_int32 makeGrayQuantColormapArb() - * - * Thresholding from 32 bpp rgb to 1 bpp - * (really color quantization, but it's better placed in this file) - * PIX *pixGenerateMaskByBand32() - * PIX *pixGenerateMaskByDiscr32() - * - * Histogram-based grayscale quantization - * PIX *pixGrayQuantFromHisto() - * static l_int32 numaFillCmapFromHisto() - * - * Color quantize grayscale image using existing colormap - * PIX *pixGrayQuantFromCmap() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) See comments above in pixDitherToBinary() for details. - * (2) The input parameters lowerclip and upperclip specify the range - * of lower and upper values (near 0 and 255, rsp) that are - * clipped to black and white without propagating the excess. - * For that reason, lowerclip and upperclip should be small numbers. - *- */ -PIX * -pixDitherToBinarySpec(PIX *pixs, - l_int32 lowerclip, - l_int32 upperclip) -{ -l_int32 w, h, d, wplt, wpld; -l_uint32 *datat, *datad; -l_uint32 *bufs1, *bufs2; -PIX *pixt, *pixd; - - PROCNAME("pixDitherToBinarySpec"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("must be 8 bpp for dithering", procName, NULL); - if (lowerclip < 0 || lowerclip > 255) - return (PIX *)ERROR_PTR("invalid value for lowerclip", procName, NULL); - if (upperclip < 0 || upperclip > 255) - return (PIX *)ERROR_PTR("invalid value for upperclip", procName, NULL); - - if ((pixd = pixCreate(w, h, 1)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - /* Remove colormap if it exists */ - if ((pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE)) == NULL) { - pixDestroy(&pixd); - return (PIX *)ERROR_PTR("pixt not made", procName, NULL); - } - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - - /* Two line buffers, 1 for current line and 2 for next line */ - bufs1 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); - bufs2 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); - if (!bufs1 || !bufs2) { - LEPT_FREE(bufs1); - LEPT_FREE(bufs2); - pixDestroy(&pixd); - pixDestroy(&pixt); - return (PIX *)ERROR_PTR("bufs1, bufs2 not both made", procName, NULL); - } - - ditherToBinaryLow(datad, w, h, wpld, datat, wplt, bufs1, bufs2, - lowerclip, upperclip); - - LEPT_FREE(bufs1); - LEPT_FREE(bufs2); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief ditherToBinaryLow() - * - * See comments in pixDitherToBinary() in binarize.c - */ -static void -ditherToBinaryLow(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls, - l_uint32 *bufs1, - l_uint32 *bufs2, - l_int32 lowerclip, - l_int32 upperclip) -{ -l_int32 i; -l_uint32 *lined; - - /* do all lines except last line */ - memcpy(bufs2, datas, 4 * wpls); /* prime the buffer */ - for (i = 0; i < h - 1; i++) { - memcpy(bufs1, bufs2, 4 * wpls); - memcpy(bufs2, datas + (i + 1) * wpls, 4 * wpls); - lined = datad + i * wpld; - ditherToBinaryLineLow(lined, w, bufs1, bufs2, lowerclip, upperclip, 0); - } - - /* do last line */ - memcpy(bufs1, bufs2, 4 * wpls); - lined = datad + (h - 1) * wpld; - ditherToBinaryLineLow(lined, w, bufs1, bufs2, lowerclip, upperclip, 1); -} - - -/*! - * \brief ditherToBinaryLineLow() - * - * \param[in] lined ptr to beginning of dest line - * \param[in] w width of image in pixels - * \param[in] bufs1 buffer of current source line - * \param[in] bufs2 buffer of next source line - * \param[in] lowerclip lower clip distance to black - * \param[in] upperclip upper clip distance to white - * \param[in] lastlineflag 0 if not last dest line, 1 if last dest line - * \return void - * - * Dispatches FS error diffusion dithering for - * a single line of the image. If lastlineflag == 0, - * both source buffers are used; otherwise, only bufs1 - * is used. We use source buffers because the error - * is propagated into them, and we don't want to change - * the input src image. - * - * We break dithering out line by line to make it - * easier to combine functions like interpolative - * scaling and error diffusion dithering, as such a - * combination of operations obviates the need to - * generate a 2x grayscale image as an intermediary. - */ -void -ditherToBinaryLineLow(l_uint32 *lined, - l_int32 w, - l_uint32 *bufs1, - l_uint32 *bufs2, - l_int32 lowerclip, - l_int32 upperclip, - l_int32 lastlineflag) -{ -l_int32 j; -l_int32 oval, eval; -l_uint8 fval1, fval2, rval, bval, dval; - - if (lastlineflag == 0) { - for (j = 0; j < w - 1; j++) { - oval = GET_DATA_BYTE(bufs1, j); - if (oval > 127) { /* binarize to OFF */ - if ((eval = 255 - oval) > upperclip) { - /* subtract from neighbors */ - fval1 = (3 * eval) / 8; - fval2 = eval / 4; - rval = GET_DATA_BYTE(bufs1, j + 1); - rval = L_MAX(0, rval - fval1); - SET_DATA_BYTE(bufs1, j + 1, rval); - bval = GET_DATA_BYTE(bufs2, j); - bval = L_MAX(0, bval - fval1); - SET_DATA_BYTE(bufs2, j, bval); - dval = GET_DATA_BYTE(bufs2, j + 1); - dval = L_MAX(0, dval - fval2); - SET_DATA_BYTE(bufs2, j + 1, dval); - } - } else { /* oval <= 127; binarize to ON */ - SET_DATA_BIT(lined, j); /* ON pixel */ - if (oval > lowerclip) { - /* add to neighbors */ - fval1 = (3 * oval) / 8; - fval2 = oval / 4; - rval = GET_DATA_BYTE(bufs1, j + 1); - rval = L_MIN(255, rval + fval1); - SET_DATA_BYTE(bufs1, j + 1, rval); - bval = GET_DATA_BYTE(bufs2, j); - bval = L_MIN(255, bval + fval1); - SET_DATA_BYTE(bufs2, j, bval); - dval = GET_DATA_BYTE(bufs2, j + 1); - dval = L_MIN(255, dval + fval2); - SET_DATA_BYTE(bufs2, j + 1, dval); - } - } - } - - /* do last column: j = w - 1 */ - oval = GET_DATA_BYTE(bufs1, j); - if (oval > 127) { /* binarize to OFF */ - if ((eval = 255 - oval) > upperclip) { - /* subtract from neighbors */ - fval1 = (3 * eval) / 8; - bval = GET_DATA_BYTE(bufs2, j); - bval = L_MAX(0, bval - fval1); - SET_DATA_BYTE(bufs2, j, bval); - } - } else { /*oval <= 127; binarize to ON */ - SET_DATA_BIT(lined, j); /* ON pixel */ - if (oval > lowerclip) { - /* add to neighbors */ - fval1 = (3 * oval) / 8; - bval = GET_DATA_BYTE(bufs2, j); - bval = L_MIN(255, bval + fval1); - SET_DATA_BYTE(bufs2, j, bval); - } - } - } else { /* lastlineflag == 1 */ - for (j = 0; j < w - 1; j++) { - oval = GET_DATA_BYTE(bufs1, j); - if (oval > 127) { /* binarize to OFF */ - if ((eval = 255 - oval) > upperclip) { - /* subtract from neighbors */ - fval1 = (3 * eval) / 8; - rval = GET_DATA_BYTE(bufs1, j + 1); - rval = L_MAX(0, rval - fval1); - SET_DATA_BYTE(bufs1, j + 1, rval); - } - } else { /* oval <= 127; binarize to ON */ - SET_DATA_BIT(lined, j); /* ON pixel */ - if (oval > lowerclip) { - /* add to neighbors */ - fval1 = (3 * oval) / 8; - rval = GET_DATA_BYTE(bufs1, j + 1); - rval = L_MIN(255, rval + fval1); - SET_DATA_BYTE(bufs1, j + 1, rval); - } - } - } - - /* do last pixel: (i, j) = (h - 1, w - 1) */ - oval = GET_DATA_BYTE(bufs1, j); - if (oval < 128) - SET_DATA_BIT(lined, j); /* ON pixel */ - } -} - - -/*------------------------------------------------------------------* - * Simple (pixelwise) binarization with fixed threshold * - *------------------------------------------------------------------*/ -/*! - * \brief pixThresholdToBinary() - * - * \param[in] pixs 4 or 8 bpp - * \param[in] thresh threshold value - * \return pixd 1 bpp, or NULL on error - * - *
- * Notes: - * (1) If the source pixel is less than the threshold value, - * the dest will be 1; otherwise, it will be 0. - * (2) For example, for 8 bpp src pix, if %thresh == 256, the dest - * 1 bpp pix is all ones (fg), and if %thresh == 0, the dest - * pix is all zeros (bg). - * - *- */ -PIX * -pixThresholdToBinary(PIX *pixs, - l_int32 thresh) -{ -l_int32 d, w, h, wplt, wpld; -l_uint32 *datat, *datad; -PIX *pixt, *pixd; - - PROCNAME("pixThresholdToBinary"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 4 && d != 8) - return (PIX *)ERROR_PTR("pixs must be 4 or 8 bpp", procName, NULL); - if (thresh < 0) - return (PIX *)ERROR_PTR("thresh must be non-negative", procName, NULL); - if (d == 4 && thresh > 16) - return (PIX *)ERROR_PTR("4 bpp thresh not in {0-16}", procName, NULL); - if (d == 8 && thresh > 256) - return (PIX *)ERROR_PTR("8 bpp thresh not in {0-256}", procName, NULL); - - if ((pixd = pixCreate(w, h, 1)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - /* Remove colormap if it exists. If there is a colormap, - * pixt will be 8 bpp regardless of the depth of pixs. */ - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - if (pixGetColormap(pixs) && d == 4) { /* promoted to 8 bpp */ - d = 8; - thresh *= 16; - } - - thresholdToBinaryLow(datad, w, h, wpld, datat, d, wplt, thresh); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief thresholdToBinaryLow() - * - * If the source pixel is less than thresh, - * the dest will be 1; otherwise, it will be 0 - */ -static void -thresholdToBinaryLow(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 d, - l_int32 wpls, - l_int32 thresh) -{ -l_int32 i; -l_uint32 *lines, *lined; - - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - thresholdToBinaryLineLow(lined, w, lines, d, thresh); - } -} - - -/* - * thresholdToBinaryLineLow() - * - */ -void -thresholdToBinaryLineLow(l_uint32 *lined, - l_int32 w, - l_uint32 *lines, - l_int32 d, - l_int32 thresh) -{ -l_int32 j, k, gval, scount, dcount; -l_uint32 sword, dword; - - PROCNAME("thresholdToBinaryLineLow"); - - switch (d) - { - case 4: - /* Unrolled as 4 source words, 1 dest word */ - for (j = 0, scount = 0, dcount = 0; j + 31 < w; j += 32) { - dword = 0; - for (k = 0; k < 4; k++) { - sword = lines[scount++]; - dword <<= 8; - gval = (sword >> 28) & 0xf; - /* Trick used here and below: if gval < thresh then - * gval - thresh < 0, so its high-order bit is 1, and - * ((gval - thresh) >> 31) & 1 == 1; likewise, if - * gval >= thresh, then ((gval - thresh) >> 31) & 1 == 0 - * Doing it this way avoids a random (and thus easily - * mispredicted) branch on each pixel. */ - dword |= ((gval - thresh) >> 24) & 128; - gval = (sword >> 24) & 0xf; - dword |= ((gval - thresh) >> 25) & 64; - gval = (sword >> 20) & 0xf; - dword |= ((gval - thresh) >> 26) & 32; - gval = (sword >> 16) & 0xf; - dword |= ((gval - thresh) >> 27) & 16; - gval = (sword >> 12) & 0xf; - dword |= ((gval - thresh) >> 28) & 8; - gval = (sword >> 8) & 0xf; - dword |= ((gval - thresh) >> 29) & 4; - gval = (sword >> 4) & 0xf; - dword |= ((gval - thresh) >> 30) & 2; - gval = sword & 0xf; - dword |= ((gval - thresh) >> 31) & 1; - } - lined[dcount++] = dword; - } - - if (j < w) { - dword = 0; - for (; j < w; j++) { - if ((j & 7) == 0) { - sword = lines[scount++]; - } - gval = (sword >> 28) & 0xf; - sword <<= 4; - dword |= (((gval - thresh) >> 31) & 1) << (31 - (j & 31)); - } - lined[dcount] = dword; - } -#if DEBUG_UNROLLING -#define CHECK_BIT(a, b, c) if (GET_DATA_BIT(a, b) != c) { \ - lept_stderr("Error: mismatch at %d/%d(%d), %d vs %d\n", \ - j, w, d, GET_DATA_BIT(a, b), c); } - for (j = 0; j < w; j++) { - gval = GET_DATA_QBIT(lines, j); - CHECK_BIT(lined, j, gval < thresh ? 1 : 0); - } -#endif - break; - case 8: - /* Unrolled as 8 source words, 1 dest word */ - for (j = 0, scount = 0, dcount = 0; j + 31 < w; j += 32) { - dword = 0; - for (k = 0; k < 8; k++) { - sword = lines[scount++]; - dword <<= 4; - gval = (sword >> 24) & 0xff; - dword |= ((gval - thresh) >> 28) & 8; - gval = (sword >> 16) & 0xff; - dword |= ((gval - thresh) >> 29) & 4; - gval = (sword >> 8) & 0xff; - dword |= ((gval - thresh) >> 30) & 2; - gval = sword & 0xff; - dword |= ((gval - thresh) >> 31) & 1; - } - lined[dcount++] = dword; - } - - if (j < w) { - dword = 0; - for (; j < w; j++) { - if ((j & 3) == 0) { - sword = lines[scount++]; - } - gval = (sword >> 24) & 0xff; - sword <<= 8; - dword |= (l_uint64)(((gval - thresh) >> 31) & 1) - << (31 - (j & 31)); - } - lined[dcount] = dword; - } -#if DEBUG_UNROLLING - for (j = 0; j < w; j++) { - gval = GET_DATA_BYTE(lines, j); - CHECK_BIT(lined, j, gval < thresh ? 1 : 0); - } -#undef CHECK_BIT -#endif - break; - default: - L_ERROR("src depth not 4 or 8 bpp\n", procName); - break; - } -} - - -/*------------------------------------------------------------------* - * Binarization with variable threshold * - *------------------------------------------------------------------*/ -/*! - * \brief pixVarThresholdToBinary() - * - * \param[in] pixs 8 bpp - * \param[in] pixg 8 bpp; contains threshold values for each pixel - * \return pixd 1 bpp, or NULL on error - * - *
- * Notes: - * (1) If the pixel in pixs is less than the corresponding pixel - * in pixg, the dest will be 1; otherwise it will be 0. - *- */ -PIX * -pixVarThresholdToBinary(PIX *pixs, - PIX *pixg) -{ -l_int32 i, j, vals, valg, w, h, d, wpls, wplg, wpld; -l_uint32 *datas, *datag, *datad, *lines, *lineg, *lined; -PIX *pixd; - - PROCNAME("pixVarThresholdToBinary"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!pixg) - return (PIX *)ERROR_PTR("pixg not defined", procName, NULL); - if (!pixSizesEqual(pixs, pixg)) - return (PIX *)ERROR_PTR("pix sizes not equal", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); - - pixd = pixCreate(w, h, 1); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datag = pixGetData(pixg); - wplg = pixGetWpl(pixg); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lineg = datag + i * wplg; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - vals = GET_DATA_BYTE(lines, j); - valg = GET_DATA_BYTE(lineg, j); - if (vals < valg) - SET_DATA_BIT(lined, j); - } - } - - return pixd; -} - - -/*------------------------------------------------------------------* - * Binarization by adaptive mapping * - *------------------------------------------------------------------*/ -/*! - * \brief pixAdaptThresholdToBinary() - * - * \param[in] pixs 8 bpp - * \param[in] pixm [optional] 1 bpp image mask; can be null - * \param[in] gamma gamma correction; must be > 0.0; typically ~1.0 - * \return pixd 1 bpp, or NULL on error - * - *
- * Notes: - * (1) This is a simple convenience function for doing adaptive - * thresholding on a grayscale image with variable background. - * It uses default parameters appropriate for typical text images. - * (2) %pixm is a 1 bpp mask over "image" regions, which are not - * expected to have a white background. The mask inhibits - * background finding under the fg pixels of the mask. For - * images with both text and image, the image regions would - * be binarized (or quantized) by a different set of operations. - * (3) As %gamma is increased, the foreground pixels are reduced. - * (4) Under the covers: The default background value for normalization - * is 200, so we choose 170 for 'maxval' in pixGammaTRC. Likewise, - * the default foreground threshold for normalization is 60, - * so we choose 50 for 'minval' in pixGammaTRC. Because - * 170 was mapped to 255, choosing 200 for the threshold is - * quite safe for avoiding speckle noise from the background. - *- */ -PIX * -pixAdaptThresholdToBinary(PIX *pixs, - PIX *pixm, - l_float32 gamma) -{ - PROCNAME("pixAdaptThresholdToBinary"); - - if (!pixs || pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - - return pixAdaptThresholdToBinaryGen(pixs, pixm, gamma, 50, 170, 200); -} - - -/*! - * \brief pixAdaptThresholdToBinaryGen() - * - * \param[in] pixs 8 bpp - * \param[in] pixm [optional] 1 bpp image mask; can be null - * \param[in] gamma gamma correction; must be > 0.0; typically ~1.0 - * \param[in] blackval dark value to set to black (0) - * \param[in] whiteval light value to set to white (255) - * \param[in] thresh final threshold for binarization - * \return pixd 1 bpp, or NULL on error - * - *
- * Notes: - * (1) This is a convenience function for doing adaptive thresholding - * on a grayscale image with variable background. Also see notes - * in pixAdaptThresholdToBinary(). - * (2) Reducing %gamma increases the foreground (text) pixels. - * Use a low value (e.g., 0.5) for images with light text. - * (3) For normal images, see default args in pixAdaptThresholdToBinary(). - * For images with very light text, these values are appropriate: - * gamma ~0.5 - * blackval ~70 - * whiteval ~190 - * thresh ~200 - *- */ -PIX * -pixAdaptThresholdToBinaryGen(PIX *pixs, - PIX *pixm, - l_float32 gamma, - l_int32 blackval, - l_int32 whiteval, - l_int32 thresh) -{ -PIX *pix1, *pixd; - - PROCNAME("pixAdaptThresholdToBinaryGen"); - - if (!pixs || pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - - pix1 = pixBackgroundNormSimple(pixs, pixm, NULL); - pixGammaTRC(pix1, pix1, gamma, blackval, whiteval); - pixd = pixThresholdToBinary(pix1, thresh); - pixDestroy(&pix1); - return pixd; -} - - -/*--------------------------------------------------------------------* - * Generate a binary mask from pixels of particular value(s) * - *--------------------------------------------------------------------*/ -/*! - * \brief pixGenerateMaskByValue() - * - * \param[in] pixs 2, 4 or 8 bpp, or colormapped - * \param[in] val of pixels for which we set 1 in dest - * \param[in] usecmap 1 to retain cmap values; 0 to convert to gray - * \return pixd 1 bpp, or NULL on error - * - *
- * Notes: - * (1) %val is the pixel value that we are selecting. It can be - * either a gray value or a colormap index. - * (2) If pixs is colormapped, %usecmap determines if the colormap - * index values are used, or if the colormap is removed to gray and - * the gray values are used. For the latter, it generates - * an approximate grayscale value for each pixel, and then looks - * for gray pixels with the value %val. - *- */ -PIX * -pixGenerateMaskByValue(PIX *pixs, - l_int32 val, - l_int32 usecmap) -{ -l_int32 i, j, w, h, d, wplg, wpld; -l_uint32 *datag, *datad, *lineg, *lined; -PIX *pixg, *pixd; - - PROCNAME("pixGenerateMaskByValue"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - if (d != 2 && d != 4 && d != 8) - return (PIX *)ERROR_PTR("not 2, 4 or 8 bpp", procName, NULL); - - if (!usecmap && pixGetColormap(pixs)) - pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - else - pixg = pixClone(pixs); - pixGetDimensions(pixg, &w, &h, &d); - if (d == 8 && (val < 0 || val > 255)) { - pixDestroy(&pixg); - return (PIX *)ERROR_PTR("val out of 8 bpp range", procName, NULL); - } - if (d == 4 && (val < 0 || val > 15)) { - pixDestroy(&pixg); - return (PIX *)ERROR_PTR("val out of 4 bpp range", procName, NULL); - } - if (d == 2 && (val < 0 || val > 3)) { - pixDestroy(&pixg); - return (PIX *)ERROR_PTR("val out of 2 bpp range", procName, NULL); - } - - pixd = pixCreate(w, h, 1); - pixCopyResolution(pixd, pixg); - pixCopyInputFormat(pixd, pixs); - datag = pixGetData(pixg); - wplg = pixGetWpl(pixg); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lineg = datag + i * wplg; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - if (d == 8) { - if (GET_DATA_BYTE(lineg, j) == val) - SET_DATA_BIT(lined, j); - } else if (d == 4) { - if (GET_DATA_QBIT(lineg, j) == val) - SET_DATA_BIT(lined, j); - } else { /* d == 2 */ - if (GET_DATA_DIBIT(lineg, j) == val) - SET_DATA_BIT(lined, j); - } - } - } - - pixDestroy(&pixg); - return pixd; -} - - -/*! - * \brief pixGenerateMaskByBand() - * - * \param[in] pixs 2, 4 or 8 bpp, or colormapped - * \param[in] lower, upper two pixel values from which a range, either - * between (inband) or outside of (!inband), - * determines which pixels in pixs cause us to - * set a 1 in the dest mask - * \param[in] inband 1 for finding pixels in [lower, upper]; - * 0 for finding pixels in - * [0, lower) union (upper, 255] - * \param[in] usecmap 1 to retain cmap values; 0 to convert to gray - * \return pixd 1 bpp, or NULL on error - * - *
- * Notes: - * (1) Generates a 1 bpp mask pixd, the same size as pixs, where - * the fg pixels in the mask are those either within the specified - * band (for inband == 1) or outside the specified band - * (for inband == 0). - * (2) If pixs is colormapped, %usecmap determines if the colormap - * values are used, or if the colormap is removed to gray and - * the gray values are used. For the latter, it generates - * an approximate grayscale value for each pixel, and then looks - * for gray pixels with the value %val. - *- */ -PIX * -pixGenerateMaskByBand(PIX *pixs, - l_int32 lower, - l_int32 upper, - l_int32 inband, - l_int32 usecmap) -{ -l_int32 i, j, w, h, d, wplg, wpld, val; -l_uint32 *datag, *datad, *lineg, *lined; -PIX *pixg, *pixd; - - PROCNAME("pixGenerateMaskByBand"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - if (d != 2 && d != 4 && d != 8) - return (PIX *)ERROR_PTR("not 2, 4 or 8 bpp", procName, NULL); - if (lower < 0 || lower > upper) - return (PIX *)ERROR_PTR("lower < 0 or lower > upper!", procName, NULL); - - if (!usecmap && pixGetColormap(pixs)) - pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - else - pixg = pixClone(pixs); - pixGetDimensions(pixg, &w, &h, &d); - if (d == 8 && upper > 255) { - pixDestroy(&pixg); - return (PIX *)ERROR_PTR("d == 8 and upper > 255", procName, NULL); - } - if (d == 4 && upper > 15) { - pixDestroy(&pixg); - return (PIX *)ERROR_PTR("d == 4 and upper > 15", procName, NULL); - } - if (d == 2 && upper > 3) { - pixDestroy(&pixg); - return (PIX *)ERROR_PTR("d == 2 and upper > 3", procName, NULL); - } - - pixd = pixCreate(w, h, 1); - pixCopyResolution(pixd, pixg); - pixCopyInputFormat(pixd, pixs); - datag = pixGetData(pixg); - wplg = pixGetWpl(pixg); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lineg = datag + i * wplg; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - if (d == 8) - val = GET_DATA_BYTE(lineg, j); - else if (d == 4) - val = GET_DATA_QBIT(lineg, j); - else /* d == 2 */ - val = GET_DATA_DIBIT(lineg, j); - if (inband) { - if (val >= lower && val <= upper) - SET_DATA_BIT(lined, j); - } else { /* out of band */ - if (val < lower || val > upper) - SET_DATA_BIT(lined, j); - } - } - } - - pixDestroy(&pixg); - return pixd; -} - - -/*------------------------------------------------------------------* - * Thresholding to 2 bpp by dithering * - *------------------------------------------------------------------*/ -/*! - * \brief pixDitherTo2bpp() - * - * \param[in] pixs 8 bpp - * \param[in] cmapflag 1 to generate a colormap - * \return pixd dithered 2 bpp, or NULL on error - * - * An analog of the Floyd-Steinberg error diffusion dithering - * algorithm is used to "dibitize" an 8 bpp grayscale image - * to 2 bpp, using equally spaced gray values of 0, 85, 170, and 255, - * which are served by thresholds of 43, 128 and 213. - * If cmapflag == 1, the colormap values are set to 0, 85, 170 and 255. - * If a pixel has a value between 0 and 42, it is dibitized - * to 0, and the excess above 0 is added to the - * three neighboring pixels, in the fractions 3/8 to i, j+1, - * 3/8 to i+1, j) and 1/4 to (i+1, j+1, truncating to 255 if - * necessary. If a pixel has a value between 43 and 127, it is - * dibitized to 1, and the excess above 85 is added to the three - * neighboring pixels as before. If the value is below 85, the - * excess is subtracted. With a value between 128 - * and 212, it is dibitized to 2, with the excess on either side - * of 170 distributed as before. Finally, with a value between - * 213 and 255, it is dibitized to 3, with the excess below 255 - * subtracted from the neighbors. We always truncate to 0 or 255. - * The details can be seen in the lookup table generation. - * - * This function differs from straight dithering in that it allows - * clipping of grayscale to 0 or 255 if the values are - * sufficiently close, without distribution of the excess. - * This uses default values from pix.h to specify the range of lower - * and upper values near 0 and 255, rsp that are clipped to black - * and white without propagating the excess. - * Not propagating the excess has the effect of reducing the snake - * patterns in parts of the image that are nearly black or white; - * however, it also prevents any attempt to reproduce gray for those values. - * - * The implementation uses 3 lookup tables for simplicity, and - * a pair of line buffers to avoid modifying pixs. - */ -PIX * -pixDitherTo2bpp(PIX *pixs, - l_int32 cmapflag) -{ - PROCNAME("pixDitherTo2bpp"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("must be 8 bpp for dithering", procName, NULL); - - return pixDitherTo2bppSpec(pixs, DEFAULT_CLIP_LOWER_2, - DEFAULT_CLIP_UPPER_2, cmapflag); -} - - -/*! - * \brief pixDitherTo2bppSpec() - * - * \param[in] pixs 8 bpp - * \param[in] lowerclip lower clip distance to black; use 0 for default - * \param[in] upperclip upper clip distance to white; use 0 for default - * \param[in] cmapflag 1 to generate a colormap - * \return pixd dithered 2 bpp, or NULL on error - * - *
- * Notes: - * (1) See comments above in pixDitherTo2bpp() for details. - * (2) The input parameters lowerclip and upperclip specify the range - * of lower and upper values (near 0 and 255, rsp) that are - * clipped to black and white without propagating the excess. - * For that reason, lowerclip and upperclip should be small numbers. - *- */ -PIX * -pixDitherTo2bppSpec(PIX *pixs, - l_int32 lowerclip, - l_int32 upperclip, - l_int32 cmapflag) -{ -l_int32 w, h, d, wplt, wpld; -l_int32 *tabval, *tab38, *tab14; -l_uint32 *datat, *datad; -l_uint32 *bufs1, *bufs2; -PIX *pixt, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixDitherTo2bppSpec"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("must be 8 bpp for dithering", procName, NULL); - if (lowerclip < 0 || lowerclip > 255) - return (PIX *)ERROR_PTR("invalid value for lowerclip", procName, NULL); - if (upperclip < 0 || upperclip > 255) - return (PIX *)ERROR_PTR("invalid value for upperclip", procName, NULL); - - if ((pixd = pixCreate(w, h, 2)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - /* If there is a colormap, remove it */ - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - - /* Two line buffers, 1 for current line and 2 for next line */ - bufs1 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); - bufs2 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); - if (!bufs1 || !bufs2) { - LEPT_FREE(bufs1); - LEPT_FREE(bufs2); - pixDestroy(&pixd); - pixDestroy(&pixt); - return (PIX *)ERROR_PTR("bufs1, bufs2 not both made", procName, NULL); - } - - /* 3 lookup tables: 2-bit value, (3/8)excess, and (1/4)excess */ - make8To2DitherTables(&tabval, &tab38, &tab14, lowerclip, upperclip); - - ditherTo2bppLow(datad, w, h, wpld, datat, wplt, bufs1, bufs2, - tabval, tab38, tab14); - - if (cmapflag) { - cmap = pixcmapCreateLinear(2, 4); - pixSetColormap(pixd, cmap); - } - - LEPT_FREE(bufs1); - LEPT_FREE(bufs2); - LEPT_FREE(tabval); - LEPT_FREE(tab38); - LEPT_FREE(tab14); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief ditherTo2bppLow() - * - * Low-level function for doing Floyd-Steinberg error diffusion - * dithering from 8 bpp (datas) to 2 bpp (datad). Two source - * line buffers, bufs1 and bufs2, are provided, along with three - * 256-entry lookup tables: tabval gives the output pixel value, - * tab38 gives the extra (plus or minus) transferred to the pixels - * directly to the left and below, and tab14 gives the extra - * transferred to the diagonal below. The choice of 3/8 and 1/4 - * is traditional but arbitrary when you use a lookup table; the - * only constraint is that the sum is 1. See other comments - * below and in grayquant.c. - */ -static void -ditherTo2bppLow(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls, - l_uint32 *bufs1, - l_uint32 *bufs2, - l_int32 *tabval, - l_int32 *tab38, - l_int32 *tab14) -{ -l_int32 i; -l_uint32 *lined; - - /* do all lines except last line */ - memcpy(bufs2, datas, 4 * wpls); /* prime the buffer */ - for (i = 0; i < h - 1; i++) { - memcpy(bufs1, bufs2, 4 * wpls); - memcpy(bufs2, datas + (i + 1) * wpls, 4 * wpls); - lined = datad + i * wpld; - ditherTo2bppLineLow(lined, w, bufs1, bufs2, tabval, tab38, tab14, 0); - } - - /* do last line */ - memcpy(bufs1, bufs2, 4 * wpls); - lined = datad + (h - 1) * wpld; - ditherTo2bppLineLow(lined, w, bufs1, bufs2, tabval, tab38, tab14, 1); -} - - -/*! - * \brief ditherTo2bppLineLow() - * - * \param[in] lined ptr to beginning of dest line - * \param[in] w width of image in pixels - * \param[in] bufs1 buffer of current source line - * \param[in] bufs2 buffer of next source line - * \param[in] tabval value to assign for current pixel - * \param[in] tab38 excess value to give to neighboring 3/8 pixels - * \param[in] tab14 excess value to give to neighboring 1/4 pixel - * \param[in] lastlineflag 0 if not last dest line, 1 if last dest line - * \return void - * - * Dispatches error diffusion dithering for - * a single line of the image. If lastlineflag == 0, - * both source buffers are used; otherwise, only bufs1 - * is used. We use source buffers because the error - * is propagated into them, and we don't want to change - * the input src image. - * - * We break dithering out line by line to make it - * easier to combine functions like interpolative - * scaling and error diffusion dithering, as such a - * combination of operations obviates the need to - * generate a 2x grayscale image as an intermediary. - */ -static void -ditherTo2bppLineLow(l_uint32 *lined, - l_int32 w, - l_uint32 *bufs1, - l_uint32 *bufs2, - l_int32 *tabval, - l_int32 *tab38, - l_int32 *tab14, - l_int32 lastlineflag) -{ -l_int32 j; -l_int32 oval, tab38val, tab14val; -l_uint8 rval, bval, dval; - - if (lastlineflag == 0) { - for (j = 0; j < w - 1; j++) { - oval = GET_DATA_BYTE(bufs1, j); - SET_DATA_DIBIT(lined, j, tabval[oval]); - rval = GET_DATA_BYTE(bufs1, j + 1); - bval = GET_DATA_BYTE(bufs2, j); - dval = GET_DATA_BYTE(bufs2, j + 1); - tab38val = tab38[oval]; - tab14val = tab14[oval]; - if (tab38val < 0) { - rval = L_MAX(0, rval + tab38val); - bval = L_MAX(0, bval + tab38val); - dval = L_MAX(0, dval + tab14val); - } else { - rval = L_MIN(255, rval + tab38val); - bval = L_MIN(255, bval + tab38val); - dval = L_MIN(255, dval + tab14val); - } - SET_DATA_BYTE(bufs1, j + 1, rval); - SET_DATA_BYTE(bufs2, j, bval); - SET_DATA_BYTE(bufs2, j + 1, dval); - } - - /* do last column: j = w - 1 */ - oval = GET_DATA_BYTE(bufs1, j); - SET_DATA_DIBIT(lined, j, tabval[oval]); - bval = GET_DATA_BYTE(bufs2, j); - tab38val = tab38[oval]; - if (tab38val < 0) - bval = L_MAX(0, bval + tab38val); - else - bval = L_MIN(255, bval + tab38val); - SET_DATA_BYTE(bufs2, j, bval); - } else { /* lastlineflag == 1 */ - for (j = 0; j < w - 1; j++) { - oval = GET_DATA_BYTE(bufs1, j); - SET_DATA_DIBIT(lined, j, tabval[oval]); - rval = GET_DATA_BYTE(bufs1, j + 1); - tab38val = tab38[oval]; - if (tab38val < 0) - rval = L_MAX(0, rval + tab38val); - else - rval = L_MIN(255, rval + tab38val); - SET_DATA_BYTE(bufs1, j + 1, rval); - } - - /* do last pixel: (i, j) = (h - 1, w - 1) */ - oval = GET_DATA_BYTE(bufs1, j); - SET_DATA_DIBIT(lined, j, tabval[oval]); - } -} - - -/*! - * \brief make8To2DitherTables() - * - * \param[out] ptabval value assigned to output pixel; 0, 1, 2 or 3 - * \param[out] ptab38 amount propagated to pixels left and below - * \param[out] ptab14 amount propagated to pixel to left and down - * \param[in] cliptoblack values near 0 where the excess is not propagated - * \param[in] cliptowhite values near 255 where the deficit is not propagated - * - * \return 0 if OK, 1 on error - */ -static l_int32 -make8To2DitherTables(l_int32 **ptabval, - l_int32 **ptab38, - l_int32 **ptab14, - l_int32 cliptoblack, - l_int32 cliptowhite) -{ -l_int32 i; -l_int32 *tabval, *tab38, *tab14; - - /* 3 lookup tables: 2-bit value, (3/8)excess, and (1/4)excess */ - tabval = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - tab38 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - tab14 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - *ptabval = tabval; - *ptab38 = tab38; - *ptab14 = tab14; - - for (i = 0; i < 256; i++) { - if (i <= cliptoblack) { - tabval[i] = 0; - tab38[i] = 0; - tab14[i] = 0; - } else if (i < 43) { - tabval[i] = 0; - tab38[i] = (3 * i + 4) / 8; - tab14[i] = (i + 2) / 4; - } else if (i < 85) { - tabval[i] = 1; - tab38[i] = (3 * (i - 85) - 4) / 8; - tab14[i] = ((i - 85) - 2) / 4; - } else if (i < 128) { - tabval[i] = 1; - tab38[i] = (3 * (i - 85) + 4) / 8; - tab14[i] = ((i - 85) + 2) / 4; - } else if (i < 170) { - tabval[i] = 2; - tab38[i] = (3 * (i - 170) - 4) / 8; - tab14[i] = ((i - 170) - 2) / 4; - } else if (i < 213) { - tabval[i] = 2; - tab38[i] = (3 * (i - 170) + 4) / 8; - tab14[i] = ((i - 170) + 2) / 4; - } else if (i < 255 - cliptowhite) { - tabval[i] = 3; - tab38[i] = (3 * (i - 255) - 4) / 8; - tab14[i] = ((i - 255) - 2) / 4; - } else { /* i >= 255 - cliptowhite */ - tabval[i] = 3; - tab38[i] = 0; - tab14[i] = 0; - } - } - - return 0; -} - - -/*--------------------------------------------------------------------* - * Simple (pixelwise) thresholding to 2 bpp with optional colormap * - *--------------------------------------------------------------------*/ -/*! - * \brief pixThresholdTo2bpp() - * - * \param[in] pixs 8 bpp - * \param[in] nlevels equally spaced; must be between 2 and 4 - * \param[in] cmapflag 1 to build colormap; 0 otherwise - * \return pixd 2 bpp, optionally with colormap, or NULL on error - * - *
- * Notes: - * (1) Valid values for nlevels is the set {2, 3, 4}. - * (2) Any colormap on the input pixs is removed to 8 bpp grayscale. - * (3) This function is typically invoked with cmapflag == 1. - * In the situation where no colormap is desired, nlevels is - * ignored and pixs is thresholded to 4 levels. - * (4) The target output colors are equally spaced, with the - * darkest at 0 and the lightest at 255. The thresholds are - * chosen halfway between adjacent output values. A table - * is built that specifies the mapping from src to dest. - * (5) If cmapflag == 1, a colormap of size 'nlevels' is made, - * and the pixel values in pixs are replaced by their - * appropriate color indices. The number of holdouts, - * 4 - nlevels, will be between 0 and 2. - * (6) If you don't want the thresholding to be equally spaced, - * either first transform the 8 bpp src using pixGammaTRC(). - * or, if cmapflag == 1, after calling this function you can use - * pixcmapResetColor() to change any individual colors. - * (7) If a colormap is generated, it will specify (to display - * programs) exactly how each level is to be represented in RGB - * space. When representing text, 3 levels is far better than - * 2 because of the antialiasing of the single gray level, - * and 4 levels (black, white and 2 gray levels) is getting - * close to the perceptual quality of a (nearly continuous) - * grayscale image. With 2 bpp, you can set up a colormap - * and allocate from 2 to 4 levels to represent antialiased text. - * Any left over colormap entries can be used for coloring regions. - * For the same number of levels, the file size of a 2 bpp image - * is about 10% smaller than that of a 4 bpp result for the same - * number of levels. For both 2 bpp and 4 bpp, using 4 levels you - * get compression far better than that of jpeg, because the - * quantization to 4 levels will remove the jpeg ringing in the - * background near character edges. - *- */ -PIX * -pixThresholdTo2bpp(PIX *pixs, - l_int32 nlevels, - l_int32 cmapflag) -{ -l_int32 *qtab; -l_int32 w, h, d, wplt, wpld; -l_uint32 *datat, *datad; -PIX *pixt, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixThresholdTo2bpp"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (nlevels < 2 || nlevels > 4) - return (PIX *)ERROR_PTR("nlevels not in {2, 3, 4}", procName, NULL); - - if ((pixd = pixCreate(w, h, 2)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - if (cmapflag) { /* hold out (4 - nlevels) cmap entries */ - cmap = pixcmapCreateLinear(2, nlevels); - pixSetColormap(pixd, cmap); - } - - /* If there is a colormap in the src, remove it */ - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - - /* Make the appropriate table */ - if (cmapflag) - qtab = makeGrayQuantIndexTable(nlevels); - else - qtab = makeGrayQuantTargetTable(4, 2); - - thresholdTo2bppLow(datad, h, wpld, datat, wplt, qtab); - - LEPT_FREE(qtab); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief thresholdTo2bppLow() - * - * Low-level function for thresholding from 8 bpp (datas) to - * 2 bpp (datad), using thresholds implicitly defined through %tab, - * a 256-entry lookup table that gives a 2-bit output value - * for each possible input. - * - * For each line, unroll the loop so that for each 32 bit src word, - * representing four consecutive 8-bit pixels, we compose one byte - * of output consisiting of four 2-bit pixels. - */ -static void -thresholdTo2bppLow(l_uint32 *datad, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls, - l_int32 *tab) -{ -l_uint8 sval1, sval2, sval3, sval4, dval; -l_int32 i, j, k; -l_uint32 *lines, *lined; - - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < wpls; j++) { - k = 4 * j; - sval1 = GET_DATA_BYTE(lines, k); - sval2 = GET_DATA_BYTE(lines, k + 1); - sval3 = GET_DATA_BYTE(lines, k + 2); - sval4 = GET_DATA_BYTE(lines, k + 3); - dval = (tab[sval1] << 6) | (tab[sval2] << 4) | - (tab[sval3] << 2) | tab[sval4]; - SET_DATA_BYTE(lined, j, dval); - } - } -} - - -/*----------------------------------------------------------------------* - * Simple (pixelwise) thresholding to 4 bpp * - *----------------------------------------------------------------------*/ -/*! - * \brief pixThresholdTo4bpp() - * - * \param[in] pixs 8 bpp, can have colormap - * \param[in] nlevels equally spaced; must be between 2 and 16 - * \param[in] cmapflag 1 to build colormap; 0 otherwise - * \return pixd 4 bpp, optionally with colormap, or NULL on error - * - *
- * Notes: - * (1) Valid values for nlevels is the set {2, ... 16}. - * (2) Any colormap on the input pixs is removed to 8 bpp grayscale. - * (3) This function is typically invoked with cmapflag == 1. - * In the situation where no colormap is desired, nlevels is - * ignored and pixs is thresholded to 16 levels. - * (4) The target output colors are equally spaced, with the - * darkest at 0 and the lightest at 255. The thresholds are - * chosen halfway between adjacent output values. A table - * is built that specifies the mapping from src to dest. - * (5) If cmapflag == 1, a colormap of size 'nlevels' is made, - * and the pixel values in pixs are replaced by their - * appropriate color indices. The number of holdouts, - * 16 - nlevels, will be between 0 and 14. - * (6) If you don't want the thresholding to be equally spaced, - * either first transform the 8 bpp src using pixGammaTRC(). - * or, if cmapflag == 1, after calling this function you can use - * pixcmapResetColor() to change any individual colors. - * (7) If a colormap is generated, it will specify, to display - * programs, exactly how each level is to be represented in RGB - * space. When representing text, 3 levels is far better than - * 2 because of the antialiasing of the single gray level, - * and 4 levels (black, white and 2 gray levels) is getting - * close to the perceptual quality of a (nearly continuous) - * grayscale image. Therefore, with 4 bpp, you can set up a - * colormap, allocate a relatively small fraction of the 16 - * possible values to represent antialiased text, and use the - * other colormap entries for other things, such as coloring - * text or background. Two other reasons for using a small number - * of gray values for antialiased text are (1) PNG compression - * gets worse as the number of levels that are used is increased, - * and (2) using a small number of levels will filter out most of - * the jpeg ringing that is typically introduced near sharp edges - * of text. This filtering is partly responsible for the improved - * compression. - *- */ -PIX * -pixThresholdTo4bpp(PIX *pixs, - l_int32 nlevels, - l_int32 cmapflag) -{ -l_int32 *qtab; -l_int32 w, h, d, wplt, wpld; -l_uint32 *datat, *datad; -PIX *pixt, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixThresholdTo4bpp"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (nlevels < 2 || nlevels > 16) - return (PIX *)ERROR_PTR("nlevels not in [2,...,16]", procName, NULL); - - if ((pixd = pixCreate(w, h, 4)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - if (cmapflag) { /* hold out (16 - nlevels) cmap entries */ - cmap = pixcmapCreateLinear(4, nlevels); - pixSetColormap(pixd, cmap); - } - - /* If there is a colormap in the src, remove it */ - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - - /* Make the appropriate table */ - if (cmapflag) - qtab = makeGrayQuantIndexTable(nlevels); - else - qtab = makeGrayQuantTargetTable(16, 4); - - thresholdTo4bppLow(datad, h, wpld, datat, wplt, qtab); - - LEPT_FREE(qtab); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief thresholdTo4bppLow() - * - * Low-level function for thresholding from 8 bpp (datas) to - * 4 bpp (datad), using thresholds implicitly defined through %tab, - * a 256-entry lookup table that gives a 4-bit output value - * for each possible input. - * - * For each line, unroll the loop so that for each 32 bit src word, - * representing four consecutive 8-bit pixels, we compose two bytes - * of output consisiting of four 4-bit pixels. - */ -static void -thresholdTo4bppLow(l_uint32 *datad, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls, - l_int32 *tab) -{ -l_uint8 sval1, sval2, sval3, sval4; -l_uint16 dval; -l_int32 i, j, k; -l_uint32 *lines, *lined; - - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < wpls; j++) { - k = 4 * j; - sval1 = GET_DATA_BYTE(lines, k); - sval2 = GET_DATA_BYTE(lines, k + 1); - sval3 = GET_DATA_BYTE(lines, k + 2); - sval4 = GET_DATA_BYTE(lines, k + 3); - dval = (tab[sval1] << 12) | (tab[sval2] << 8) | - (tab[sval3] << 4) | tab[sval4]; - SET_DATA_TWO_BYTES(lined, j, dval); - } - } -} - - -/*----------------------------------------------------------------------* - * Simple (pixelwise) thresholding on 8 bpp with optional colormap * - *----------------------------------------------------------------------*/ -/*! - * \brief pixThresholdOn8bpp() - * - * \param[in] pixs 8 bpp, can have colormap - * \param[in] nlevels equally spaced; must be between 2 and 256 - * \param[in] cmapflag 1 to build colormap; 0 otherwise - * \return pixd 8 bpp, optionally with colormap, or NULL on error - * - *
- * Notes: - * (1) Valid values for nlevels is the set {2,...,256}. - * (2) Any colormap on the input pixs is removed to 8 bpp grayscale. - * (3) If cmapflag == 1, a colormap of size 'nlevels' is made, - * and the pixel values in pixs are replaced by their - * appropriate color indices. Otherwise, the pixel values - * are the actual thresholded (i.e., quantized) grayscale values. - * (4) If you don't want the thresholding to be equally spaced, - * first transform the input 8 bpp src using pixGammaTRC(). - *- */ -PIX * -pixThresholdOn8bpp(PIX *pixs, - l_int32 nlevels, - l_int32 cmapflag) -{ -l_int32 *qtab; /* quantization table */ -l_int32 i, j, w, h, wpld, val, newval; -l_uint32 *datad, *lined; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixThresholdOn8bpp"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (nlevels < 2 || nlevels > 256) - return (PIX *)ERROR_PTR("nlevels not in [2,...,256]", procName, NULL); - - /* Get a new pixd; if there is a colormap in the src, remove it */ - if (pixGetColormap(pixs)) - pixd = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - else - pixd = pixCopy(NULL, pixs); - - if (cmapflag) { /* hold out (256 - nlevels) cmap entries */ - cmap = pixcmapCreateLinear(8, nlevels); - pixSetColormap(pixd, cmap); - } - - if (cmapflag) - qtab = makeGrayQuantIndexTable(nlevels); - else - qtab = makeGrayQuantTargetTable(nlevels, 8); - - pixGetDimensions(pixd, &w, &h, NULL); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(lined, j); - newval = qtab[val]; - SET_DATA_BYTE(lined, j, newval); - } - } - - LEPT_FREE(qtab); - return pixd; -} - - -/*----------------------------------------------------------------------* - * Arbitrary (pixelwise) thresholding from 8 bpp to 2, 4 or 8 bpp * - *----------------------------------------------------------------------*/ -/*! - * \brief pixThresholdGrayArb() - * - * \param[in] pixs 8 bpp grayscale; can have colormap - * \param[in] edgevals string giving edge value of each bin - * \param[in] outdepth 0, 2, 4 or 8 bpp; 0 is default for min depth - * \param[in] use_average 1 if use the average pixel value in colormap - * \param[in] setblack 1 if darkest color is set to black - * \param[in] setwhite 1 if lightest color is set to white - * \return pixd 2, 4 or 8 bpp quantized image with colormap, - * or NULL on error - * - *
- * Notes: - * (1) This function allows exact specification of the quantization bins. - * The string %edgevals is a space-separated set of values - * specifying the dividing points between output quantization bins. - * These threshold values are assigned to the bin with higher - * values, so that each of them is the smallest value in their bin. - * (2) The output image (pixd) depth is specified by %outdepth. The - * number of bins is the number of edgevals + 1. The - * relation between outdepth and the number of bins is: - * outdepth = 2 nbins <= 4 - * outdepth = 4 nbins <= 16 - * outdepth = 8 nbins <= 256 - * With %outdepth == 0, the minimum required depth for the - * given number of bins is used. - * The output pixd has a colormap. - * (3) The last 3 args determine the specific values that go into - * the colormap. - * (4) For %use_average: - * ~ if TRUE, the average value of pixels falling in the bin is - * chosen as the representative gray value. Otherwise, - * ~ if FALSE, the central value of each bin is chosen as - * the representative value. - * The colormap holds the representative value. - * (5) For %setblack, if TRUE the darkest color is set to (0,0,0). - * (6) For %setwhite, if TRUE the lightest color is set to (255,255,255). - * (7) An alternative to using this function to quantize to - * unequally-spaced bins is to first transform the 8 bpp pixs - * using pixGammaTRC(), and follow this with pixThresholdTo4bpp(). - *- */ -PIX * -pixThresholdGrayArb(PIX *pixs, - const char *edgevals, - l_int32 outdepth, - l_int32 use_average, - l_int32 setblack, - l_int32 setwhite) -{ -l_int32 *qtab; -l_int32 w, h, d, i, j, n, wplt, wpld, val, newval; -l_uint32 *datat, *datad, *linet, *lined; -NUMA *na; -PIX *pixt, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixThresholdGrayArb"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (!edgevals) - return (PIX *)ERROR_PTR("edgevals not defined", procName, NULL); - if (outdepth != 0 && outdepth != 2 && outdepth != 4 && outdepth != 8) - return (PIX *)ERROR_PTR("invalid outdepth", procName, NULL); - - /* Parse and sort (if required) the bin edge values */ - na = parseStringForNumbers(edgevals, " \t\n,"); - n = numaGetCount(na); - if (n > 255) { - numaDestroy(&na); - return (PIX *)ERROR_PTR("more than 256 levels", procName, NULL); - } - if (outdepth == 0) { - if (n <= 3) - outdepth = 2; - else if (n <= 15) - outdepth = 4; - else - outdepth = 8; - } else if (n + 1 > (1 << outdepth)) { - L_WARNING("outdepth too small; setting to 8 bpp\n", procName); - outdepth = 8; - } - numaSort(na, na, L_SORT_INCREASING); - - /* Make the quantization LUT and the colormap */ - makeGrayQuantTableArb(na, outdepth, &qtab, &cmap); - if (use_average) { /* use the average value in each bin */ - pixcmapDestroy(&cmap); - makeGrayQuantColormapArb(pixs, qtab, outdepth, &cmap); - } - pixcmapSetBlackAndWhite(cmap, setblack, setwhite); - numaDestroy(&na); - - if ((pixd = pixCreate(w, h, outdepth)) == NULL) { - LEPT_FREE(qtab); - pixcmapDestroy(&cmap); - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - pixSetColormap(pixd, cmap); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - /* If there is a colormap in the src, remove it */ - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - - if (outdepth == 2) { - thresholdTo2bppLow(datad, h, wpld, datat, wplt, qtab); - } else if (outdepth == 4) { - thresholdTo4bppLow(datad, h, wpld, datat, wplt, qtab); - } else { - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - linet = datat + i * wplt; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(linet, j); - newval = qtab[val]; - SET_DATA_BYTE(lined, j, newval); - } - } - } - - LEPT_FREE(qtab); - pixDestroy(&pixt); - return pixd; -} - - -/*----------------------------------------------------------------------* - * Quantization tables for linear thresholds of grayscale images * - *----------------------------------------------------------------------*/ -/*! - * \brief makeGrayQuantIndexTable() - * - * \param[in] nlevels number of output levels - * \return table maps input gray level to colormap index, - * or NULL on error - *
- * Notes: - * (1) 'nlevels' is some number between 2 and 256 (typically 8 or less). - * (2) The table is typically used for quantizing 2, 4 and 8 bpp - * grayscale src pix, and generating a colormapped dest pix. - *- */ -l_int32 * -makeGrayQuantIndexTable(l_int32 nlevels) -{ -l_int32 *tab; -l_int32 i, j, thresh; - - tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - for (i = 0; i < 256; i++) { - for (j = 0; j < nlevels; j++) { - thresh = 255 * (2 * j + 1) / (2 * nlevels - 2); - if (i <= thresh) { - tab[i] = j; -/* lept_stderr("tab[%d] = %d\n", i, j); */ - break; - } - } - } - return tab; -} - - -/*! - * \brief makeGrayQuantTargetTable() - * - * \param[in] nlevels number of output levels - * \param[in] depth of dest pix, in bpp; 2, 4 or 8 bpp - * \return table maps input gray level to thresholded gray level, - * or NULL on error - * - *
- * Notes: - * (1) nlevels is some number between 2 and 2^(depth) - * (2) The table is used in two similar ways: - * ~ for 8 bpp, it quantizes to a given number of target levels - * ~ for 2 and 4 bpp, it thresholds to appropriate target values - * that will use the full dynamic range of the dest pix. - * (3) For depth = 8, the number of thresholds chosen is - * ('nlevels' - 1), and the 'nlevels' values stored in the - * table are at the two at the extreme ends, (0, 255), plus - * plus ('nlevels' - 2) values chosen at equal intervals between. - * For example, for depth = 8 and 'nlevels' = 3, the two - * threshold values are 3f and bf, and the three target pixel - * values are 0, 7f and ff. - * (4) For depth < 8, we ignore nlevels, and always use the maximum - * number of levels, which is 2^(depth). - * If you want nlevels < the maximum number, you should always - * use a colormap. - *- */ -static l_int32 * -makeGrayQuantTargetTable(l_int32 nlevels, - l_int32 depth) -{ -l_int32 *tab; -l_int32 i, j, thresh, maxval, quantval; - - tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - maxval = (1 << depth) - 1; - if (depth < 8) - nlevels = 1 << depth; - for (i = 0; i < 256; i++) { - for (j = 0; j < nlevels; j++) { - thresh = 255 * (2 * j + 1) / (2 * nlevels - 2); - if (i <= thresh) { - quantval = maxval * j / (nlevels - 1); - tab[i] = quantval; -/* lept_stderr("tab[%d] = %d\n", i, tab[i]); */ - break; - } - } - } - return tab; -} - - -/*----------------------------------------------------------------------* - * Quantization table for arbitrary thresholding of grayscale images * - *----------------------------------------------------------------------*/ -/*! - * \brief makeGrayQuantTableArb() - * - * \param[in] na numa of bin boundaries - * \param[in] outdepth of colormap: 1, 2, 4 or 8 - * \param[out] ptab table mapping input gray level to cmap index - * \param[out] pcmap colormap - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The number of bins is the count of %na + 1. - * (2) The bin boundaries in na must be sorted in increasing order. - * (3) The table is an inverse colormap: it maps input gray level - * to colormap index (the bin number). - * (4) The colormap generated here has quantized values at the - * center of each bin. If you want to use the average gray - * value of pixels within the bin, discard the colormap and - * compute it using makeGrayQuantColormapArb(). - * (5) Returns an error if there are not enough levels in the - * output colormap for the number of bins. The number - * of bins must not exceed 2^outdepth. - *- */ -l_ok -makeGrayQuantTableArb(NUMA *na, - l_int32 outdepth, - l_int32 **ptab, - PIXCMAP **pcmap) -{ -l_int32 i, j, n, jstart, ave, val; -l_int32 *tab; -PIXCMAP *cmap; - - PROCNAME("makeGrayQuantTableArb"); - - if (!ptab) - return ERROR_INT("&tab not defined", procName, 1); - *ptab = NULL; - if (!pcmap) - return ERROR_INT("&cmap not defined", procName, 1); - *pcmap = NULL; - if (!na) - return ERROR_INT("na not defined", procName, 1); - n = numaGetCount(na); - if (n + 1 > (1 << outdepth)) - return ERROR_INT("more bins than cmap levels", procName, 1); - - if ((cmap = pixcmapCreate(outdepth)) == NULL) - return ERROR_INT("cmap not made", procName, 1); - tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - *ptab = tab; - *pcmap = cmap; - - /* First n bins */ - jstart = 0; - for (i = 0; i < n; i++) { - numaGetIValue(na, i, &val); - ave = (jstart + val) / 2; - pixcmapAddColor(cmap, ave, ave, ave); - for (j = jstart; j < val; j++) - tab[j] = i; - jstart = val; - } - - /* Last bin */ - ave = (jstart + 255) / 2; - pixcmapAddColor(cmap, ave, ave, ave); - for (j = jstart; j < 256; j++) - tab[j] = n; - - return 0; -} - - -/*! - * \brief makeGrayQuantColormapArb() - * - * \param[in] pixs 8 bpp - * \param[in] tab table mapping input gray level to cmap index - * \param[in] outdepth of colormap: 1, 2, 4 or 8 - * \param[out] pcmap colormap - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The table is a 256-entry inverse colormap: it maps input gray - * level to colormap index (the bin number). It is computed - * using makeGrayQuantTableArb(). - * (2) The colormap generated here has quantized values at the - * average gray value of the pixels that are in each bin. - * (3) Returns an error if there are not enough levels in the - * output colormap for the number of bins. The number - * of bins must not exceed 2^outdepth. - *- */ -static l_int32 -makeGrayQuantColormapArb(PIX *pixs, - l_int32 *tab, - l_int32 outdepth, - PIXCMAP **pcmap) -{ -l_int32 i, j, index, w, h, d, nbins, wpl, factor, val; -l_int32 *bincount, *binave, *binstart; -l_uint32 *line, *data; - - PROCNAME("makeGrayQuantColormapArb"); - - if (!pcmap) - return ERROR_INT("&cmap not defined", procName, 1); - *pcmap = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return ERROR_INT("pixs not 8 bpp", procName, 1); - if (!tab) - return ERROR_INT("tab not defined", procName, 1); - nbins = tab[255] + 1; - if (nbins > (1 << outdepth)) - return ERROR_INT("more bins than cmap levels", procName, 1); - - /* Find the count and weighted count for each bin */ - if ((bincount = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32))) == NULL) - return ERROR_INT("calloc fail for bincount", procName, 1); - if ((binave = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32))) == NULL) { - LEPT_FREE(bincount); - return ERROR_INT("calloc fail for binave", procName, 1); - } - factor = (l_int32)(sqrt((l_float64)(w * h) / 30000.) + 0.5); - factor = L_MAX(1, factor); - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - for (i = 0; i < h; i += factor) { - line = data + i * wpl; - for (j = 0; j < w; j += factor) { - val = GET_DATA_BYTE(line, j); - bincount[tab[val]]++; - binave[tab[val]] += val; - } - } - - /* Find the smallest gray values in each bin */ - binstart = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32)); - for (i = 1, index = 1; i < 256; i++) { - if (tab[i] < index) continue; - if (tab[i] == index) - binstart[index++] = i; - } - - /* Get the averages. If there are no samples in a bin, use - * the center value of the bin. */ - *pcmap = pixcmapCreate(outdepth); - for (i = 0; i < nbins; i++) { - if (bincount[i]) { - val = binave[i] / bincount[i]; - } else { /* no samples in the bin */ - if (i < nbins - 1) - val = (binstart[i] + binstart[i + 1]) / 2; - else /* last bin */ - val = (binstart[i] + 255) / 2; - } - pixcmapAddColor(*pcmap, val, val, val); - } - - LEPT_FREE(bincount); - LEPT_FREE(binave); - LEPT_FREE(binstart); - return 0; -} - - -/*--------------------------------------------------------------------* - * Thresholding from 32 bpp rgb to 1 bpp * - *--------------------------------------------------------------------*/ -/*! - * \brief pixGenerateMaskByBand32() - * - * \param[in] pixs 32 bpp - * \param[in] refval reference rgb value - * \param[in] delm max amount below the ref value for any component - * \param[in] delp max amount above the ref value for any component - * \param[in] fractm fractional amount below ref value for all components - * \param[in] fractp fractional amount above ref value for all components - * \return pixd 1 bpp, or NULL on error - * - *
- * Notes: - * (1) Generates a 1 bpp mask pixd, the same size as pixs, where - * the fg pixels in the mask within a band of rgb values - * surrounding %refval. The band can be chosen in two ways - * for each component: - * (a) Use (%delm, %delp) to specify how many levels down and up - * (b) Use (%fractm, %fractp) to specify the fractional - * distance toward 0 and 255, respectively. - * Note that %delm and %delp must be in [0 ... 255], whereas - * %fractm and %fractp must be in [0.0 - 1.0]. - * (2) Either (%delm, %delp) or (%fractm, %fractp) can be used. - * Set each value in the other pair to 0. - *- */ -PIX * -pixGenerateMaskByBand32(PIX *pixs, - l_uint32 refval, - l_int32 delm, - l_int32 delp, - l_float32 fractm, - l_float32 fractp) -{ -l_int32 i, j, w, h, d, wpls, wpld; -l_int32 rref, gref, bref, rval, gval, bval; -l_int32 rmin, gmin, bmin, rmax, gmax, bmax; -l_uint32 pixel; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; - - PROCNAME("pixGenerateMaskByBand32"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 32) - return (PIX *)ERROR_PTR("not 32 bpp", procName, NULL); - if (delm < 0 || delp < 0) - return (PIX *)ERROR_PTR("delm and delp must be >= 0", procName, NULL); - if (fractm < 0.0 || fractm > 1.0 || fractp < 0.0 || fractp > 1.0) - return (PIX *)ERROR_PTR("fractm and/or fractp invalid", procName, NULL); - - extractRGBValues(refval, &rref, &gref, &bref); - if (fractm == 0.0 && fractp == 0.0) { - rmin = rref - delm; - gmin = gref - delm; - bmin = bref - delm; - rmax = rref + delm; - gmax = gref + delm; - bmax = bref + delm; - } else if (delm == 0 && delp == 0) { - rmin = (l_int32)((1.0 - fractm) * rref); - gmin = (l_int32)((1.0 - fractm) * gref); - bmin = (l_int32)((1.0 - fractm) * bref); - rmax = rref + (l_int32)(fractp * (255 - rref)); - gmax = gref + (l_int32)(fractp * (255 - gref)); - bmax = bref + (l_int32)(fractp * (255 - bref)); - } else { - L_ERROR("bad input: either (delm, delp) or (fractm, fractp) " - "must be 0\n", procName); - return NULL; - } - - pixd = pixCreate(w, h, 1); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - pixel = lines[j]; - rval = (pixel >> L_RED_SHIFT) & 0xff; - if (rval < rmin || rval > rmax) - continue; - gval = (pixel >> L_GREEN_SHIFT) & 0xff; - if (gval < gmin || gval > gmax) - continue; - bval = (pixel >> L_BLUE_SHIFT) & 0xff; - if (bval < bmin || bval > bmax) - continue; - SET_DATA_BIT(lined, j); - } - } - - return pixd; -} - - -/*! - * \brief pixGenerateMaskByDiscr32() - * - * \param[in] pixs 32 bpp - * \param[in] refval1 reference rgb value - * \param[in] refval2 reference rgb value - * \param[in] distflag L_MANHATTAN_DISTANCE, L_EUCLIDEAN_DISTANCE - * \return pixd 1 bpp, or NULL on error - * - *
- * Notes: - * (1) Generates a 1 bpp mask pixd, the same size as pixs, where - * the fg pixels in the mask are those where the pixel in pixs - * is "closer" to refval1 than to refval2. - * (2) "Closer" can be defined in several ways, such as: - * ~ manhattan distance (L1) - * ~ euclidean distance (L2) - * ~ majority vote of the individual components - * Here, we have a choice of L1 or L2. - *- */ -PIX * -pixGenerateMaskByDiscr32(PIX *pixs, - l_uint32 refval1, - l_uint32 refval2, - l_int32 distflag) -{ -l_int32 i, j, w, h, d, wpls, wpld; -l_int32 rref1, gref1, bref1, rref2, gref2, bref2, rval, gval, bval; -l_uint32 pixel, dist1, dist2; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; - - PROCNAME("pixGenerateMaskByDiscr32"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 32) - return (PIX *)ERROR_PTR("not 32 bpp", procName, NULL); - if (distflag != L_MANHATTAN_DISTANCE && distflag != L_EUCLIDEAN_DISTANCE) - return (PIX *)ERROR_PTR("invalid distflag", procName, NULL); - - extractRGBValues(refval1, &rref1, &gref1, &bref1); - extractRGBValues(refval2, &rref2, &gref2, &bref2); - pixd = pixCreate(w, h, 1); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - pixel = lines[j]; - extractRGBValues(pixel, &rval, &gval, &bval); - if (distflag == L_MANHATTAN_DISTANCE) { - dist1 = L_ABS(rref1 - rval); - dist2 = L_ABS(rref2 - rval); - dist1 += L_ABS(gref1 - gval); - dist2 += L_ABS(gref2 - gval); - dist1 += L_ABS(bref1 - bval); - dist2 += L_ABS(bref2 - bval); - } else { - dist1 = (rref1 - rval) * (rref1 - rval); - dist2 = (rref2 - rval) * (rref2 - rval); - dist1 += (gref1 - gval) * (gref1 - gval); - dist2 += (gref2 - gval) * (gref2 - gval); - dist1 += (bref1 - bval) * (bref1 - bval); - dist2 += (bref2 - bval) * (bref2 - bval); - } - if (dist1 < dist2) - SET_DATA_BIT(lined, j); - } - } - - return pixd; -} - - -/*----------------------------------------------------------------------* - * Histogram-based grayscale quantization * - *----------------------------------------------------------------------*/ -/*! - * \brief pixGrayQuantFromHisto() - * - * \param[in] pixd [optional] quantized pix with cmap; can be null - * \param[in] pixs 8 bpp gray input pix; not cmapped - * \param[in] pixm [optional] mask over pixels in pixs to quantize - * \param[in] minfract minimum fraction of pixels in a set of adjacent - * histo bins that causes the set to be automatically - * set aside as a color in the colormap; must be - * at least 0.01 - * \param[in] maxsize maximum number of adjacent bins allowed to represent - * a color, regardless of the population of pixels - * in the bins; must be at least 2 - * \return pixd 8 bpp, cmapped, or NULL on error - * - *
- * Notes: - * (1) This is useful for quantizing images with relatively few - * colors, but which may have both color and gray pixels. - * If there are color pixels, it is assumed that an input - * rgb image has been color quantized first so that: - * ~ pixd has a colormap describing the color pixels - * ~ pixm is a mask over the non-color pixels in pixd - * ~ the colormap in pixd, and the color pixels in pixd, - * have been repacked to go from 0 to n-1 (n colors) - * If there are no color pixels, pixd and pixm are both null, - * and all pixels in pixs are quantized to gray. - * (2) A 256-entry histogram is built of the gray values in pixs. - * If pixm exists, the pixels contributing to the histogram are - * restricted to the fg of pixm. A colormap and LUT are generated - * from this histogram. We break up the array into a set - * of intervals, each one constituting a color in the colormap: - * An interval is identified by summing histogram bins until - * either the sum equals or exceeds the %minfract of the total - * number of pixels, or the span itself equals or exceeds %maxsize. - * The color of each bin is always an average of the pixels - * that constitute it. - * (3) Note that we do not specify the number of gray colors in - * the colormap. Instead, we specify two parameters that - * describe the accuracy of the color assignments; this and - * the actual image determine the number of resulting colors. - * (4) If a mask exists and it is not the same size as pixs, make - * a new mask the same size as pixs, with the original mask - * aligned at the UL corners. Set all additional pixels - * in the (larger) new mask set to 1, causing those pixels - * in pixd to be set as gray. - * (5) We estimate the total number of colors (color plus gray); - * if it exceeds 255, return null. - *- */ -PIX * -pixGrayQuantFromHisto(PIX *pixd, - PIX *pixs, - PIX *pixm, - l_float32 minfract, - l_int32 maxsize) -{ -l_int32 w, h, wd, hd, wm, hm, wpls, wplm, wpld; -l_int32 nc, nestim, i, j, vals, vald; -l_int32 *lut; -l_uint32 *datas, *datam, *datad, *lines, *linem, *lined; -NUMA *na; -PIX *pixmr; /* resized mask */ -PIXCMAP *cmap; - - PROCNAME("pixGrayQuantFromHisto"); - - if (!pixs || pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - if (minfract < 0.01) { - L_WARNING("minfract < 0.01; setting to 0.05\n", procName); - minfract = 0.05; - } - if (maxsize < 2) { - L_WARNING("maxsize < 2; setting to 10\n", procName); - maxsize = 10; - } - if ((pixd && !pixm) || (!pixd && pixm)) - return (PIX *)ERROR_PTR("(pixd,pixm) not defined together", - procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - if (pixd) { - if (pixGetDepth(pixm) != 1) - return (PIX *)ERROR_PTR("pixm not 1 bpp", procName, NULL); - if ((cmap = pixGetColormap(pixd)) == NULL) - return (PIX *)ERROR_PTR("pixd not cmapped", procName, NULL); - pixGetDimensions(pixd, &wd, &hd, NULL); - if (w != wd || h != hd) - return (PIX *)ERROR_PTR("pixs, pixd sizes differ", procName, NULL); - nc = pixcmapGetCount(cmap); - nestim = nc + (l_int32)(1.5 * 255 / maxsize); - lept_stderr( "nestim = %d\n", nestim); - if (nestim > 255) { - L_ERROR("Estimate %d colors!\n", procName, nestim); - return (PIX *)ERROR_PTR("probably too many colors", procName, NULL); - } - pixGetDimensions(pixm, &wm, &hm, NULL); - if (w != wm || h != hm) { /* resize the mask */ - L_WARNING("mask and dest sizes not equal\n", procName); - pixmr = pixCreateNoInit(w, h, 1); - pixRasterop(pixmr, 0, 0, wm, hm, PIX_SRC, pixm, 0, 0); - pixRasterop(pixmr, wm, 0, w - wm, h, PIX_SET, NULL, 0, 0); - pixRasterop(pixmr, 0, hm, wm, h - hm, PIX_SET, NULL, 0, 0); - } else { - pixmr = pixClone(pixm); - } - } else { - pixd = pixCreateTemplate(pixs); - cmap = pixcmapCreate(8); - pixSetColormap(pixd, cmap); - } - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - - /* Use original mask, if it exists, to select gray pixels */ - na = pixGetGrayHistogramMasked(pixs, pixm, 0, 0, 1); - - /* Fill out the cmap with gray colors, and generate the lut - * for pixel assignment. Issue a warning on failure. */ - if (numaFillCmapFromHisto(na, cmap, minfract, maxsize, &lut)) - L_ERROR("ran out of colors in cmap!\n", procName); - numaDestroy(&na); - - /* Assign the gray pixels to their cmap indices */ - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - if (!pixm) { - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - vals = GET_DATA_BYTE(lines, j); - vald = lut[vals]; - SET_DATA_BYTE(lined, j, vald); - } - } - LEPT_FREE(lut); - return pixd; - } - - datam = pixGetData(pixmr); - wplm = pixGetWpl(pixmr); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - linem = datam + i * wplm; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - if (!GET_DATA_BIT(linem, j)) - continue; - vals = GET_DATA_BYTE(lines, j); - vald = lut[vals]; - SET_DATA_BYTE(lined, j, vald); - } - } - pixDestroy(&pixmr); - LEPT_FREE(lut); - return pixd; -} - - -/*! - * \brief numaFillCmapFromHisto() - * - * \param[in] na histogram of gray values - * \param[in] cmap 8 bpp cmap, possibly initialized with color value - * \param[in] minfract minimum fraction of pixels in a set of adjacent - * histo bins that causes the set to be automatically - * set aside as a color in the colormap; must be - * at least 0.01 - * \param[in] maxsize maximum number of adjacent bins allowed to represent - * a color, regardless of the population of pixels - * in the bins; must be at least 2 - * \param[out] plut lookup table from gray value to colormap index - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This static function must be called from pixGrayQuantFromHisto() - *- */ -static l_int32 -numaFillCmapFromHisto(NUMA *na, - PIXCMAP *cmap, - l_float32 minfract, - l_int32 maxsize, - l_int32 **plut) -{ -l_int32 mincount, index, sum, wtsum, span, istart, i, val, ret; -l_int32 *iahisto, *lut; -l_float32 total; - - PROCNAME("numaFillCmapFromHisto"); - - if (!plut) - return ERROR_INT("&lut not defined", procName, 1); - *plut = NULL; - if (!na) - return ERROR_INT("na not defined", procName, 1); - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - - numaGetSum(na, &total); - mincount = (l_int32)(minfract * total); - iahisto = numaGetIArray(na); - lut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - *plut = lut; - index = pixcmapGetCount(cmap); /* start with number of colors - * already reserved */ - - /* March through, associating colors with sets of adjacent - * gray levels. During the process, the LUT that gives - * the colormap index for each gray level is computed. - * To complete a color, either the total count must equal - * or exceed %mincount, or the current span of colors must - * equal or exceed %maxsize. An empty span is not converted - * into a color; it is simply ignored. When a span is completed for a - * color, the weighted color in the span is added to the colormap. */ - sum = 0; - wtsum = 0; - istart = 0; - ret = 0; - for (i = 0; i < 256; i++) { - lut[i] = index; - sum += iahisto[i]; - wtsum += i * iahisto[i]; - span = i - istart + 1; - if (sum < mincount && span < maxsize) - continue; - - if (sum == 0) { /* empty span; don't save */ - istart = i + 1; - continue; - } - - /* Found new color; sum > 0 */ - val = (l_int32)((l_float32)wtsum / (l_float32)sum + 0.5); - ret = pixcmapAddColor(cmap, val, val, val); - istart = i + 1; - sum = 0; - wtsum = 0; - index++; - } - if (istart < 256 && sum > 0) { /* last one */ - span = 256 - istart; - val = (l_int32)((l_float32)wtsum / (l_float32)sum + 0.5); - ret = pixcmapAddColor(cmap, val, val, val); - } - - LEPT_FREE(iahisto); - return ret; -} - - -/*----------------------------------------------------------------------* - * Color quantize grayscale image using existing colormap * - *----------------------------------------------------------------------*/ -/*! - * \brief pixGrayQuantFromCmap() - * - * \param[in] pixs 8 bpp grayscale without cmap - * \param[in] cmap to quantize to; of dest pix - * \param[in] mindepth minimum depth of pixd: can be 2, 4 or 8 bpp - * \return pixd 2, 4 or 8 bpp, colormapped, or NULL on error - * - *
- * Notes: - * (1) In use, pixs is an 8 bpp grayscale image without a colormap. - * If there is an existing colormap, a warning is issued and - * a copy of the input pixs is returned. - *- */ -PIX * -pixGrayQuantFromCmap(PIX *pixs, - PIXCMAP *cmap, - l_int32 mindepth) -{ -l_int32 i, j, index, w, h, d, depth, wpls, wpld; -l_int32 hascolor, vals, vald; -l_int32 *tab; -l_uint32 *datas, *datad, *lines, *lined; -PIXCMAP *cmapd; -PIX *pixd; - - PROCNAME("pixGrayQuantFromCmap"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetColormap(pixs) != NULL) { - L_WARNING("pixs already has a colormap; returning a copy\n", procName); - return pixCopy(NULL, pixs); - } - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (!cmap) - return (PIX *)ERROR_PTR("cmap not defined", procName, NULL); - if (mindepth != 2 && mindepth != 4 && mindepth != 8) - return (PIX *)ERROR_PTR("invalid mindepth", procName, NULL); - - /* Make sure the colormap is gray */ - pixcmapHasColor(cmap, &hascolor); - if (hascolor) { - L_WARNING("Converting colormap colors to gray\n", procName); - cmapd = pixcmapColorToGray(cmap, 0.3, 0.5, 0.2); - } else { - cmapd = pixcmapCopy(cmap); - } - - /* Make LUT into colormap */ - tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - for (i = 0; i < 256; i++) { - pixcmapGetNearestGrayIndex(cmapd, i, &index); - tab[i] = index; - } - - pixcmapGetMinDepth(cmap, &depth); - depth = L_MAX(depth, mindepth); - pixd = pixCreate(w, h, depth); - pixSetColormap(pixd, cmapd); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - vals = GET_DATA_BYTE(lines, j); - vald = tab[vals]; - if (depth == 2) - SET_DATA_DIBIT(lined, j, vald); - else if (depth == 4) - SET_DATA_QBIT(lined, j, vald); - else /* depth == 8 */ - SET_DATA_BYTE(lined, j, vald); - } - } - - LEPT_FREE(tab); - return pixd; -} - - -#if 0 /* Documentation */ -/*--------------------------------------------------------------------* - * Implementation of binarization by dithering using LUTs * - * It is archived here. * - *--------------------------------------------------------------------*/ -/*! - * \brief pixDitherToBinaryLUT() - * - * \param[in] pixs - * \param[in] lowerclip lower clip distance to black; use -1 for default - * \param[in] upperclip upper clip distance to white; use -1 for default - * \return pixd dithered binary, or NULL on error - * - * We don't need two implementations of Floyd-Steinberg dithering, - * and this one with LUTs is a little more complicated than - * pixDitherToBinary(). It uses three lookup tables to generate the - * output pixel value and the excess or deficit carried over to the - * neighboring pixels. It's here for pedagogical reasons only. - */ -PIX * -pixDitherToBinaryLUT(PIX *pixs, - l_int32 lowerclip, - l_int32 upperclip) -{ -l_int32 w, h, d, wplt, wpld; -l_int32 *tabval, *tab38, *tab14; -l_uint32 *datat, *datad; -l_uint32 *bufs1, *bufs2; -PIX *pixt, *pixd; - - PROCNAME("pixDitherToBinaryLUT"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("must be 8 bpp for dithering", procName, NULL); - if (lowerclip < 0) - lowerclip = DEFAULT_CLIP_LOWER_1; - if (upperclip < 0) - upperclip = DEFAULT_CLIP_UPPER_1; - - if ((pixd = pixCreate(w, h, 1)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - /* Remove colormap if it exists */ - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - - /* Two line buffers, 1 for current line and 2 for next line */ - bufs1 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); - bufs2 = (l_uint32 *)LEPT_CALLOC(wplt, sizeof(l_uint32)); - if (!bufs1 || !bufs2) { - LEPT_FREE(bufs1); - LEPT_FREE(bufs2); - pixDestroy(&pixd); - pixDestroy(&pixt); - return (PIX *)ERROR_PTR("bufs1, bufs2 not both made", procName, NULL); - } - - /* 3 lookup tables: 1-bit value, (3/8)excess, and (1/4)excess */ - make8To1DitherTables(&tabval, &tab38, &tab14, lowerclip, upperclip); - - ditherToBinaryLUTLow(datad, w, h, wpld, datat, wplt, bufs1, bufs2, - tabval, tab38, tab14); - - LEPT_FREE(bufs1); - LEPT_FREE(bufs2); - LEPT_FREE(tabval); - LEPT_FREE(tab38); - LEPT_FREE(tab14); - pixDestroy(&pixt); - return pixd; -} - -/*! - * \brief ditherToBinaryLUTLow() - * - * Low-level function for doing Floyd-Steinberg error diffusion - * dithering from 8 bpp (datas) to 1 bpp (datad). Two source - * line buffers, bufs1 and bufs2, are provided, along with three - * 256-entry lookup tables: tabval gives the output pixel value, - * tab38 gives the extra (plus or minus) transferred to the pixels - * directly to the left and below, and tab14 gives the extra - * transferred to the diagonal below. The choice of 3/8 and 1/4 - * is traditional but arbitrary when you use a lookup table; the - * only constraint is that the sum is 1. See other comments below. - */ -void -ditherToBinaryLUTLow(l_uint32 *datad, - l_int32 w, - l_int32 h, - l_int32 wpld, - l_uint32 *datas, - l_int32 wpls, - l_uint32 *bufs1, - l_uint32 *bufs2, - l_int32 *tabval, - l_int32 *tab38, - l_int32 *tab14) -{ -l_int32 i; -l_uint32 *lined; - - /* do all lines except last line */ - memcpy(bufs2, datas, 4 * wpls); /* prime the buffer */ - for (i = 0; i < h - 1; i++) { - memcpy(bufs1, bufs2, 4 * wpls); - memcpy(bufs2, datas + (i + 1) * wpls, 4 * wpls); - lined = datad + i * wpld; - ditherToBinaryLineLUTLow(lined, w, bufs1, bufs2, - tabval, tab38, tab14, 0); - } - - /* do last line */ - memcpy(bufs1, bufs2, 4 * wpls); - lined = datad + (h - 1) * wpld; - ditherToBinaryLineLUTLow(lined, w, bufs1, bufs2, tabval, tab38, tab14, 1); - return; -} - -/*! - * \brief ditherToBinaryLineLUTLow() - * - * \param[in] lined ptr to beginning of dest line - * \param[in] w width of image in pixels - * \param[in] bufs1 buffer of current source line - * \param[in] bufs2 buffer of next source line - * \param[in] tabval value to assign for current pixel - * \param[in] tab38 excess value to give to neighboring 3/8 pixels - * \param[in] tab14 excess value to give to neighboring 1/4 pixel - * \param[in] lastlineflag 0 if not last dest line, 1 if last dest line - * \return void - */ -void -ditherToBinaryLineLUTLow(l_uint32 *lined, - l_int32 w, - l_uint32 *bufs1, - l_uint32 *bufs2, - l_int32 *tabval, - l_int32 *tab38, - l_int32 *tab14, - l_int32 lastlineflag) -{ -l_int32 j; -l_int32 oval, tab38val, tab14val; -l_uint8 rval, bval, dval; - - if (lastlineflag == 0) { - for (j = 0; j < w - 1; j++) { - oval = GET_DATA_BYTE(bufs1, j); - if (tabval[oval]) - SET_DATA_BIT(lined, j); - rval = GET_DATA_BYTE(bufs1, j + 1); - bval = GET_DATA_BYTE(bufs2, j); - dval = GET_DATA_BYTE(bufs2, j + 1); - tab38val = tab38[oval]; - if (tab38val == 0) - continue; - tab14val = tab14[oval]; - if (tab38val < 0) { - rval = L_MAX(0, rval + tab38val); - bval = L_MAX(0, bval + tab38val); - dval = L_MAX(0, dval + tab14val); - } else { - rval = L_MIN(255, rval + tab38val); - bval = L_MIN(255, bval + tab38val); - dval = L_MIN(255, dval + tab14val); - } - SET_DATA_BYTE(bufs1, j + 1, rval); - SET_DATA_BYTE(bufs2, j, bval); - SET_DATA_BYTE(bufs2, j + 1, dval); - } - - /* do last column: j = w - 1 */ - oval = GET_DATA_BYTE(bufs1, j); - if (tabval[oval]) - SET_DATA_BIT(lined, j); - bval = GET_DATA_BYTE(bufs2, j); - tab38val = tab38[oval]; - if (tab38val < 0) { - bval = L_MAX(0, bval + tab38val); - SET_DATA_BYTE(bufs2, j, bval); - } else if (tab38val > 0 ) { - bval = L_MIN(255, bval + tab38val); - SET_DATA_BYTE(bufs2, j, bval); - } - } else { /* lastlineflag == 1 */ - for (j = 0; j < w - 1; j++) { - oval = GET_DATA_BYTE(bufs1, j); - if (tabval[oval]) - SET_DATA_BIT(lined, j); - rval = GET_DATA_BYTE(bufs1, j + 1); - tab38val = tab38[oval]; - if (tab38val == 0) - continue; - if (tab38val < 0) - rval = L_MAX(0, rval + tab38val); - else - rval = L_MIN(255, rval + tab38val); - SET_DATA_BYTE(bufs1, j + 1, rval); - } - - /* do last pixel: (i, j) = (h - 1, w - 1) */ - oval = GET_DATA_BYTE(bufs1, j); - if (tabval[oval]) - SET_DATA_BIT(lined, j); - } - - return; -} - -/*! - * \brief make8To1DitherTables() - * - * \param[out] ptabval value assigned to output pixel; 0 or 1 - * \param[out] ptab38 amount propagated to pixels left and below - * \param[out] ptab14 amount propagated to pixel to left and down - * \param[in] lowerclip values near 0 where the excess is not propagated - * \param[in] upperclip values near 255 where the deficit is not propagated - * - * \return 0 if OK, 1 on error - */ -l_ok -make8To1DitherTables(l_int32 **ptabval, - l_int32 **ptab38, - l_int32 **ptab14, - l_int32 lowerclip, - l_int32 upperclip) -{ -l_int32 i; -l_int32 *tabval, *tab38, *tab14; - - PROCNAME("make8To1DitherTables"); - - if (ptabval) *ptabval = NULL; - if (ptab38) *ptab38 = NULL; - if (ptab14) *ptab14 = NULL; - if (!ptabval || !ptab38 || !ptab14) - return ERROR_INT("table ptrs not all defined", procName, 1); - - /* 3 lookup tables: 1-bit value, (3/8)excess, and (1/4)excess */ - tabval = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - tab38 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - tab14 = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - if (!tabval || !tab38 || !tab14) - return ERROR_INT("calloc failure to make small table", procName, 1); - *ptabval = tabval; - *ptab38 = tab38; - *ptab14 = tab14; - - for (i = 0; i < 256; i++) { - if (i <= lowerclip) { - tabval[i] = 1; - tab38[i] = 0; - tab14[i] = 0; - } else if (i < 128) { - tabval[i] = 1; - tab38[i] = (3 * i + 4) / 8; - tab14[i] = (i + 2) / 4; - } else if (i < 255 - upperclip) { - tabval[i] = 0; - tab38[i] = (3 * (i - 255) + 4) / 8; - tab14[i] = ((i - 255) + 2) / 4; - } else { /* i >= 255 - upperclip */ - tabval[i] = 0; - tab38[i] = 0; - tab14[i] = 0; - } - } - - return 0; -} -#endif /* Documentation */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/heap.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/heap.c deleted file mode 100644 index 9b6738e5..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/heap.c +++ /dev/null @@ -1,589 +0,0 @@ -/*====================================================================* - - 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 heap.c - *
- * - * Create/Destroy L_Heap - * L_HEAP *lheapCreate() - * void *lheapDestroy() - * - * Operations to add/remove to/from the heap - * l_int32 lheapAdd() - * static l_int32 lheapExtendArray() - * void *lheapRemove() - * - * Other accessors - * l_int32 lheapGetCount() - * void *lheapGetElement() - * - * Heap sort - * l_int32 lheapSort() - * l_int32 lheapSortStrictOrder() - * - * Low-level heap operations - * static l_int32 lheapSwapUp() - * static l_int32 lheapSwapDown() - * - * Debug output - * l_int32 lheapPrint() - * - * The L_Heap is useful to implement a priority queue, that is sorted - * on a key in each element of the heap. The heap is an array - * of nearly arbitrary structs, with a l_float32 the first field. - * This field is the key on which the heap is sorted. - * - * Internally, we keep track of the heap size, n. The item at the - * root of the heap is at the head of the array. Items are removed - * from the head of the array and added to the end of the array. - * When an item is removed from the head, the item at the end - * of the array is moved to the head. When items are either - * added or removed, it is usually necessary to swap array items - * to restore the heap order. It is guaranteed that the number - * of swaps does not exceed log(n). - * - * -------------------------- N.B. ------------------------------ - * The items on the heap (or, equivalently, in the array) are cast - * to void*. Their key is a l_float32, and it is REQUIRED that the - * key be the first field in the struct. That allows us to get the - * key by simply dereferencing the struct. Alternatively, we could - * choose (but don't) to pass an application-specific comparison - * function into the heap operation functions. - * -------------------------- N.B. ------------------------------ - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * 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\n", procName); - return; - } - if ((lh = *plh) == NULL) - return; - - if (freeflag) { /* free each struct in the array */ - for (i = 0; i < lh->n; i++) - LEPT_FREE(lh->array[i]); - } else if (lh->n > 0) { /* freeflag == FALSE but elements exist on array */ - L_WARNING("memory leak of %d items in lheap!\n", procName, lh->n); - } - - if (lh->array) - LEPT_FREE(lh->array); - LEPT_FREE(lh); - *plh = NULL; - - return; -} - -/*--------------------------------------------------------------------------* - * Operations to add/remove to/from the heap * - *--------------------------------------------------------------------------*/ -/*! - * \brief lheapAdd() - * - * \param[in] lh heap - * \param[in] item to be added to the tail of the heap - * \return 0 if OK, 1 on error - */ -l_ok -lheapAdd(L_HEAP *lh, - void *item) -{ - PROCNAME("lheapAdd"); - - if (!lh) - return ERROR_INT("lh not defined", procName, 1); - if (!item) - return ERROR_INT("item not defined", procName, 1); - - /* If necessary, expand the allocated array by a factor of 2 */ - if (lh->n >= lh->nalloc) - lheapExtendArray(lh); - - /* Add the item */ - lh->array[lh->n] = item; - lh->n++; - - /* Restore the heap */ - lheapSwapUp(lh, lh->n - 1); - return 0; -} - - -/*! - * \brief lheapExtendArray() - * - * \param[in] lh heap - * \return 0 if OK, 1 on error - */ -static l_int32 -lheapExtendArray(L_HEAP *lh) -{ - PROCNAME("lheapExtendArray"); - - if (!lh) - return ERROR_INT("lh not defined", procName, 1); - - if ((lh->array = (void **)reallocNew((void **)&lh->array, - sizeof(void *) * lh->nalloc, - 2 * sizeof(void *) * lh->nalloc)) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - - lh->nalloc = 2 * lh->nalloc; - return 0; -} - - -/*! - * \brief lheapRemove() - * - * \param[in] lh heap - * \return ptr to item popped from the root of the heap, - * or NULL if the heap is empty or on error - */ -void * -lheapRemove(L_HEAP *lh) -{ -void *item; - - PROCNAME("lheapRemove"); - - if (!lh) - return (void *)ERROR_PTR("lh not defined", procName, NULL); - - if (lh->n == 0) - return NULL; - - item = lh->array[0]; - lh->array[0] = lh->array[lh->n - 1]; /* move last to the head */ - lh->array[lh->n - 1] = NULL; /* set ptr to null */ - lh->n--; - - lheapSwapDown(lh); /* restore the heap */ - return item; -} - - -/*--------------------------------------------------------------------------* - * Other accessors * - *--------------------------------------------------------------------------*/ -/*! - * \brief lheapGetCount() - * - * \param[in] lh heap - * \return count, or 0 on error - */ -l_int32 -lheapGetCount(L_HEAP *lh) -{ - PROCNAME("lheapGetCount"); - - if (!lh) - return ERROR_INT("lh not defined", procName, 0); - - return lh->n; -} - - -/*! - * \brief lheapGetElement() - * - * \param[in] lh heap - * \param[in] index into the internal heap array - * \return ptr to the element at array[index], or NULL on error - * - *
- * Notes: - * (1) This is useful for retrieving an arbitrary element in the - * heap array without disturbing the heap. It allows all the - * elements on the heap to be queried in linear time; for - * example, to find the min or max of some value. - * (2) Tbe retrieved element is owned by the heap. Do not destroy it. - *- */ -void * -lheapGetElement(L_HEAP *lh, - l_int32 index) -{ - PROCNAME("lheapGetElement"); - - if (!lh) - return ERROR_PTR("lh not defined", procName, NULL); - if (index < 0 || index >= lh->n) - return ERROR_PTR("invalid index", procName, NULL); - - return (void *)lh->array[index]; -} - - -/*--------------------------------------------------------------------------* - * Heap sort * - *--------------------------------------------------------------------------*/ -/*! - * \brief lheapSort() - * - * \param[in] lh heap, with internal array - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This sorts an array into heap order. If the heap is already - * in heap order for the direction given, this has no effect. - *- */ -l_ok -lheapSort(L_HEAP *lh) -{ -l_int32 i; - - PROCNAME("lheapSort"); - - if (!lh) - return ERROR_INT("lh not defined", procName, 1); - - for (i = 0; i < lh->n; i++) - lheapSwapUp(lh, i); - - return 0; -} - - -/*! - * \brief lheapSortStrictOrder() - * - * \param[in] lh heap, with internal array - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This sorts a heap into strict order. - * (2) For each element, starting at the end of the array and - * working forward, the element is swapped with the head - * element and then allowed to swap down onto a heap of - * size reduced by one. The result is that the heap is - * reversed but in strict order. The array elements are - * then reversed to put it in the original order. - *- */ -l_ok -lheapSortStrictOrder(L_HEAP *lh) -{ -l_int32 i, index, size; - - PROCNAME("lheapSortStrictOrder"); - - if (!lh) - return ERROR_INT("lh not defined", procName, 1); - - /* Start from a sorted heap */ - lheapSort(lh); - - size = lh->n; /* save the actual size */ - for (i = 0; i < size; i++) { - index = size - i; - SWAP_ITEMS(0, index - 1); - lh->n--; /* reduce the apparent heap size by 1 */ - lheapSwapDown(lh); - } - lh->n = size; /* restore the size */ - - for (i = 0; i < size / 2; i++) /* reverse */ - SWAP_ITEMS(i, size - i - 1); - - return 0; -} - - -/*--------------------------------------------------------------------------* - * Low-level heap operations * - *--------------------------------------------------------------------------*/ -/*! - * \brief lheapSwapUp() - * - * \param[in] lh heap - * \param[in] index of array corresponding to node to be swapped up - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is called after a new item is put on the heap, at the - * bottom of a complete tree. - * (2) To regain the heap order, we let it bubble up, - * iteratively swapping with its parent, until it either - * reaches the root of the heap or it finds a parent that - * is in the correct position already vis-a-vis the child. - *- */ -static l_ok -lheapSwapUp(L_HEAP *lh, - l_int32 index) -{ -l_int32 ip; /* index to heap for parent; 1 larger than array index */ -l_int32 ic; /* index into heap for child */ -l_float32 valp, valc; - - PROCNAME("lheapSwapUp"); - - if (!lh) - return ERROR_INT("lh not defined", procName, 1); - if (index < 0 || index >= lh->n) - return ERROR_INT("invalid index", procName, 1); - - ic = index + 1; /* index into heap: add 1 to array index */ - if (lh->direction == L_SORT_INCREASING) { - while (1) { - if (ic == 1) /* root of heap */ - break; - ip = ic / 2; - valc = *(l_float32 *)(lh->array[ic - 1]); - valp = *(l_float32 *)(lh->array[ip - 1]); - if (valp <= valc) - break; - SWAP_ITEMS(ip - 1, ic - 1); - ic = ip; - } - } else { /* lh->direction == L_SORT_DECREASING */ - while (1) { - if (ic == 1) /* root of heap */ - break; - ip = ic / 2; - valc = *(l_float32 *)(lh->array[ic - 1]); - valp = *(l_float32 *)(lh->array[ip - 1]); - if (valp >= valc) - break; - SWAP_ITEMS(ip - 1, ic - 1); - ic = ip; - } - } - return 0; -} - - -/*! - * \brief lheapSwapDown() - * - * \param[in] lh heap - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is called after an item has been popped off the - * root of the heap, and the last item in the heap has - * been placed at the root. - * (2) To regain the heap order, we let it bubble down, - * iteratively swapping with one of its children. For a - * decreasing sort, it swaps with the largest child; for - * an increasing sort, the smallest. This continues until - * it either reaches the lowest level in the heap, or the - * parent finds that neither child should swap with it - * (e.g., for a decreasing heap, the parent is larger - * than or equal to both children). - *- */ -static l_ok -lheapSwapDown(L_HEAP *lh) -{ -l_int32 ip; /* index to heap for parent; 1 larger than array index */ -l_int32 icr, icl; /* index into heap for left/right children */ -l_float32 valp, valcl, valcr; - - PROCNAME("lheapSwapDown"); - - if (!lh) - return ERROR_INT("lh not defined", procName, 1); - if (lheapGetCount(lh) < 1) - return 0; - - ip = 1; /* index into top of heap: corresponds to array[0] */ - if (lh->direction == L_SORT_INCREASING) { - while (1) { - icl = 2 * ip; - if (icl > lh->n) - break; - valp = *(l_float32 *)(lh->array[ip - 1]); - valcl = *(l_float32 *)(lh->array[icl - 1]); - icr = icl + 1; - if (icr > lh->n) { /* only a left child; no iters below */ - if (valp > valcl) - SWAP_ITEMS(ip - 1, icl - 1); - break; - } else { /* both children exist; swap with the smallest if bigger */ - valcr = *(l_float32 *)(lh->array[icr - 1]); - if (valp <= valcl && valp <= valcr) /* smaller than both */ - break; - if (valcl <= valcr) { /* left smaller; swap */ - SWAP_ITEMS(ip - 1, icl - 1); - ip = icl; - } else { /* right smaller; swap */ - SWAP_ITEMS(ip - 1, icr - 1); - ip = icr; - } - } - } - } else { /* lh->direction == L_SORT_DECREASING */ - while (1) { - icl = 2 * ip; - if (icl > lh->n) - break; - valp = *(l_float32 *)(lh->array[ip - 1]); - valcl = *(l_float32 *)(lh->array[icl - 1]); - icr = icl + 1; - if (icr > lh->n) { /* only a left child; no iters below */ - if (valp < valcl) - SWAP_ITEMS(ip - 1, icl - 1); - break; - } else { /* both children exist; swap with the biggest if smaller */ - valcr = *(l_float32 *)(lh->array[icr - 1]); - if (valp >= valcl && valp >= valcr) /* bigger than both */ - break; - if (valcl >= valcr) { /* left bigger; swap */ - SWAP_ITEMS(ip - 1, icl - 1); - ip = icl; - } else { /* right bigger; swap */ - SWAP_ITEMS(ip - 1, icr - 1); - ip = icr; - } - } - } - } - - return 0; -} - - -/*---------------------------------------------------------------------* - * Debug output * - *---------------------------------------------------------------------*/ -/*! - * \brief lheapPrint() - * - * \param[in] fp file stream - * \param[in] lh heap - * \return 0 if OK; 1 on error - */ -l_ok -lheapPrint(FILE *fp, - L_HEAP *lh) -{ -l_int32 i; - - PROCNAME("lheapPrint"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!lh) - return ERROR_INT("lh not defined", procName, 1); - - fprintf(fp, "\n L_Heap: nalloc = %d, n = %d, array = %p\n", - lh->nalloc, lh->n, lh->array); - for (i = 0; i < lh->n; i++) - fprintf(fp, "keyval[%d] = %f\n", i, *(l_float32 *)lh->array[i]); - - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/heap.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/heap.h deleted file mode 100644 index d39b06b9..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/heap.h +++ /dev/null @@ -1,87 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_HEAP_H -#define LEPTONICA_HEAP_H - -/*! - * \file heap.h - * - *
- * Expandable priority queue configured as a heap for arbitrary void* data - * - * The L_Heap is used to implement a priority queue. The elements - * in the heap are ordered in either increasing or decreasing key value. - * The key is a float field 'keyval' that is required to be - * contained in the elements of the queue. - * - * The heap is a simple binary tree with the following constraints: - * - the key of each node is >= the keys of the two children - * - the tree is complete, meaning that each level (1, 2, 4, ...) - * is filled and the last level is filled from left to right - * - * The tree structure is implicit in the queue array, with the - * array elements numbered as a breadth-first search of the tree - * from left to right. It is thus guaranteed that the largest - * (or smallest) key belongs to the first element in the array. - * - * Heap sort is used to sort the array. Once an array has been - * sorted as a heap, it is convenient to use it as a priority queue, - * because the min (or max) elements are always at the root of - * the tree (element 0), and once removed, the heap can be - * resorted in not more than log[n] steps, where n is the number - * of elements on the heap. Likewise, if an arbitrary element is - * added to the end of the array A, the sorted heap can be restored - * in not more than log[n] steps. - * - * A L_Heap differs from a L_Queue in that the elements in the former - * are sorted by a key. Internally, the array is maintained - * as a queue, with a pointer to the end of the array. The - * head of the array always remains at array[0]. The array is - * maintained (sorted) as a heap. When an item is removed from - * the head, the last item takes its place (thus reducing the - * array length by 1), and this is followed by array element - * swaps to restore the heap property. When an item is added, - * it goes at the end of the array, and is swapped up to restore - * the heap. If the ptr array is full, adding another item causes - * the ptr array size to double. - * - * For further implementation details, see heap.c. - *- */ - -/*! Heap of arbitrary void* data */ -struct L_Heap -{ - l_int32 nalloc; /*!< size of allocated ptr array */ - l_int32 n; /*!< number of elements stored in the heap */ - void **array; /*!< ptr array */ - l_int32 direction; /*!< L_SORT_INCREASING or L_SORT_DECREASING */ -}; -typedef struct L_Heap L_HEAP; - - -#endif /* LEPTONICA_HEAP_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/imageio.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/imageio.h deleted file mode 100644 index e0117482..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/imageio.h +++ /dev/null @@ -1,244 +0,0 @@ -/*====================================================================* - - 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 imageio.h - * - *
- * General features of image I/O in leptonica - * - * At present, there are 9 file formats for images that can be read - * and written: - * png (requires libpng, libz) - * jpeg (requires libjpeg) - * tiff (requires libtiff, libz) - * gif (requires libgif) - * webp (requires libwebp) - * jp2 (requires libopenjp2) - * bmp (no library required) - * pnm (no library required) - * spix (no library required) - * Additionally, there are two file formats for writing (only) images: - * PostScript (requires libpng, libz, libjpeg, libtiff) - * pdf (requires libpng, libz, libjpeg, libtiff) - * - * For all 9 read/write formats, leptonica provides interconversion - * between pix (with raster data) and formatted image data: - * Conversion from pix (typically compression): - * pixWrite(): pix --> file - * pixWriteStream(): pix --> filestream (aka FILE*) - * pixWriteMem(): pix --> memory buffer - * Conversion to pix (typically decompression): - * pixRead(): file --> pix - * pixReadStream(): filestream --> pix - * pixReadMem(): memory buffer --> pix - * - * Conversions for which the image data is not compressed are: - * * uncompressed tiff (IFF_TIFF) - * * bmp - * * pnm - * * spix (fast serialization that copies the pix raster data) - * - * The image header (metadata) information can be read from either - * the compressed file or a memory buffer, for all 9 formats. - *- */ - -#ifndef LEPTONICA_IMAGEIO_H -#define LEPTONICA_IMAGEIO_H - -/* --------------------------------------------------------------- * - * Image file format types * - * --------------------------------------------------------------- */ -/* - * The IFF_DEFAULT flag is used to write the file out in the - * same (input) file format that the pix was read from. If the pix - * was not read from file, the input format field will be - * IFF_UNKNOWN and the output file format will be chosen to - * be compressed and lossless; namely, IFF_TIFF_G4 for d = 1 - * and IFF_PNG for everything else. - * - * In the future, new format types that have defined extensions - * will be added before IFF_DEFAULT, and will be kept in sync with - * the file format extensions in writefile.c. The positions of - * file formats before IFF_DEFAULT will remain invariant. - */ - -/*! Image Formats */ -enum { - IFF_UNKNOWN = 0, - IFF_BMP = 1, - IFF_JFIF_JPEG = 2, - IFF_PNG = 3, - IFF_TIFF = 4, - IFF_TIFF_PACKBITS = 5, - IFF_TIFF_RLE = 6, - IFF_TIFF_G3 = 7, - IFF_TIFF_G4 = 8, - IFF_TIFF_LZW = 9, - IFF_TIFF_ZIP = 10, - IFF_PNM = 11, - IFF_PS = 12, - IFF_GIF = 13, - IFF_JP2 = 14, - IFF_WEBP = 15, - IFF_LPDF = 16, - IFF_TIFF_JPEG = 17, - IFF_DEFAULT = 18, - IFF_SPIX = 19 -}; - -/* Convenient macro for checking requested tiff output */ -#define L_FORMAT_IS_TIFF(f) ((f) == IFF_TIFF || (f) == IFF_TIFF_PACKBITS || \ - (f) == IFF_TIFF_RLE || (f) == IFF_TIFF_G3 || \ - (f) == IFF_TIFF_G4 || (f) == IFF_TIFF_LZW || \ - (f) == IFF_TIFF_ZIP || (f) == IFF_TIFF_JPEG) - - -/* --------------------------------------------------------------- * - * Format header ids * - * --------------------------------------------------------------- */ - -/*! Header Ids */ -enum { - BMP_ID = 0x4d42, /*!< BM - for bitmaps */ - TIFF_BIGEND_ID = 0x4d4d, /*!< MM - for 'motorola' */ - TIFF_LITTLEEND_ID = 0x4949 /*!< II - for 'intel' */ -}; - - -/* --------------------------------------------------------------- * - * Hinting bit flags in jpeg reader * - * --------------------------------------------------------------- */ - -/*! Jpeg Hints */ -enum { - L_JPEG_READ_LUMINANCE = 1, /*!< only want luminance data; no chroma */ - L_JPEG_FAIL_ON_BAD_DATA = 2 /*!< don't return possibly damaged pix */ -}; - - -/* --------------------------------------------------------------- * - * Pdf formatted encoding types * - * --------------------------------------------------------------- */ - -/*! Pdf Encoding */ -enum { - L_DEFAULT_ENCODE = 0, /*!< use default encoding based on image */ - L_JPEG_ENCODE = 1, /*!< use dct encoding: 8 and 32 bpp, no cmap */ - L_G4_ENCODE = 2, /*!< use ccitt g4 fax encoding: 1 bpp */ - L_FLATE_ENCODE = 3, /*!< use flate encoding: any depth, cmap ok */ - L_JP2K_ENCODE = 4 /*!< use jp2k encoding: 8 and 32 bpp, no cmap */ -}; - - -/* --------------------------------------------------------------- * - * Compressed image data * - * --------------------------------------------------------------- */ -/* - * In use, either datacomp or data85 will be produced, depending - * on whether the data needs to be ascii85 encoded. PostScript - * requires ascii85 encoding; pdf does not. - * - * For the colormap (flate compression only), PostScript uses ascii85 - * encoding and pdf uses a bracketed array of space-separated - * hex-encoded rgb triples. Only tiff g4 (type == L_G4_ENCODE) uses - * the minisblack field. - */ - -/*! Compressed image data */ -struct L_Compressed_Data -{ - l_int32 type; /*!< encoding type: L_JPEG_ENCODE, etc */ - l_uint8 *datacomp; /*!< gzipped raster data */ - size_t nbytescomp; /*!< number of compressed bytes */ - char *data85; /*!< ascii85-encoded gzipped raster data */ - size_t nbytes85; /*!< number of ascii85 encoded bytes */ - char *cmapdata85; /*!< ascii85-encoded uncompressed cmap */ - char *cmapdatahex; /*!< hex pdf array for the cmap */ - l_int32 ncolors; /*!< number of colors in cmap */ - l_int32 w; /*!< image width */ - l_int32 h; /*!< image height */ - l_int32 bps; /*!< bits/sample; typ. 1, 2, 4 or 8 */ - l_int32 spp; /*!< samples/pixel; typ. 1 or 3 */ - l_int32 minisblack; /*!< tiff g4 photometry */ - l_int32 predictor; /*!< flate data has PNG predictors */ - size_t nbytes; /*!< number of uncompressed raster bytes */ - l_int32 res; /*!< resolution (ppi) */ -}; -typedef struct L_Compressed_Data L_COMP_DATA; - - -/* ------------------------------------------------------------------------- * - * Pdf multi image flags * - * ------------------------------------------------------------------------- */ - -/*! Pdf MultiImage */ -enum { - L_FIRST_IMAGE = 1, /*!< first image to be used */ - L_NEXT_IMAGE = 2, /*!< intermediate image; not first or last */ - L_LAST_IMAGE = 3 /*!< last image to be used */ -}; - - -/* ------------------------------------------------------------------------- * - * Intermediate pdf generation data * - * ------------------------------------------------------------------------- */ -/* - * This accumulates data for generating a pdf of a single page consisting - * of an arbitrary number of images. - * - * None of the strings have a trailing newline. - */ - -/*! Intermediate pdf generation data */ -struct L_Pdf_Data -{ - char *title; /*!< optional title for pdf */ - l_int32 n; /*!< number of images */ - l_int32 ncmap; /*!< number of colormaps */ - struct L_Ptra *cida; /*!< array of compressed image data */ - char *id; /*!< %PDF-1.2 id string */ - char *obj1; /*!< catalog string */ - char *obj2; /*!< metadata string */ - char *obj3; /*!< pages string */ - char *obj4; /*!< page string (variable data) */ - char *obj5; /*!< content string (variable data) */ - char *poststream; /*!< post-binary-stream string */ - char *trailer; /*!< trailer string (variable data) */ - struct Pta *xy; /*!< store (xpt, ypt) array */ - struct Pta *wh; /*!< store (wpt, hpt) array */ - struct Box *mediabox; /*!< bounding region for all images */ - struct Sarray *saprex; /*!< pre-binary-stream xobject strings */ - struct Sarray *sacmap; /*!< colormap pdf object strings */ - struct L_Dna *objsize; /*!< sizes of each pdf string object */ - struct L_Dna *objloc; /*!< location of each pdf string object */ - l_int32 xrefloc; /*!< location of xref */ -}; -typedef struct L_Pdf_Data L_PDF_DATA; - - -#endif /* LEPTONICA_IMAGEIO_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/jbclass.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/jbclass.c deleted file mode 100644 index cd23a038..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/jbclass.c +++ /dev/null @@ -1,2572 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -/* - * jbclass.c - * - * These are functions for unsupervised classification of - * collections of connected components -- either characters or - * words -- in binary images. They can be used as image - * processing steps in jbig2 compression. - * - * Initialization - * - * JBCLASSER *jbRankHausInit() [rank hausdorff encoder] - * JBCLASSER *jbCorrelationInit() [correlation encoder] - * JBCLASSER *jbCorrelationInitWithoutComponents() [ditto] - * static JBCLASSER *jbCorrelationInitInternal() - * - * Classify the pages - * - * l_int32 jbAddPages() - * l_int32 jbAddPage() - * l_int32 jbAddPageComponents() - * - * Rank hausdorff classifier - * - * l_int32 jbClassifyRankHaus() - * l_int32 pixHaustest() - * l_int32 pixRankHaustest() - * - * Binary correlation classifier - * - * l_int32 jbClassifyCorrelation() - * - * Determine the image components we start with - * - * l_int32 jbGetComponents() - * l_int32 pixWordMaskByDilation() - * l_int32 pixWordBoxesByDilation() - * - * Build grayscale composites (templates) - * - * PIXA *jbAccumulateComposites - * PIXA *jbTemplatesFromComposites - * - * Utility functions for Classer - * - * JBCLASSER *jbClasserCreate() - * void jbClasserDestroy() - * - * Utility functions for Data - * - * JBDATA *jbDataSave() - * void jbDataDestroy() - * l_int32 jbDataWrite() - * JBDATA *jbDataRead() - * PIXA *jbDataRender() - * l_int32 jbGetULCorners() - * l_int32 jbGetLLCorners() - * - * Static helpers - * - * static JBFINDCTX *findSimilarSizedTemplatesInit() - * static l_int32 findSimilarSizedTemplatesNext() - * static void findSimilarSizedTemplatesDestroy() - * static l_int32 finalPositioningForAlignment() - * - * Note: this is NOT an implementation of the JPEG jbig2 - * proposed standard encoder, the specifications for which - * can be found at http://www.jpeg.org/jbigpt2.html. - * (See below for a full implementation.) - * It is an implementation of the lower-level part of an encoder that: - * - * (1) identifies connected components that are going to be used - * (2) puts them in similarity classes (this is an unsupervised - * classifier), and - * (3) stores the result in a simple file format (2 files, - * one for templates and one for page/coordinate/template-index - * quartets). - * - * An actual implementation of the official jbig2 encoder could - * start with parts (1) and (2), and would then compress the quartets - * according to the standards requirements (e.g., Huffman or - * arithmetic coding of coordinate differences and image templates). - * - * The low-level part of the encoder provided here has the - * following useful features: - * - * ~ It is accurate in the identification of templates - * and classes because it uses a windowed hausdorff - * distance metric. - * ~ It is accurate in the placement of the connected - * components, doing a two step process of first aligning - * the the centroids of the template with those of each instance, - * and then making a further correction of up to +- 1 pixel - * in each direction to best align the templates. - * ~ It is fast because it uses a morphologically based - * matching algorithm to implement the hausdorff criterion, - * and it selects the patterns that are possible matches - * based on their size. - * - * We provide two different matching functions, one using Hausdorff - * distance and one using a simple image correlation. - * The Hausdorff method sometimes produces better results for the - * same number of classes, because it gives a relatively small - * effective weight to foreground pixels near the boundary, - * and a relatively large weight to foreground pixels that are - * not near the boundary. By effectively ignoring these boundary - * pixels, Hausdorff weighting corresponds better to the expected - * probabilities of the pixel values in a scanned image, where the - * variations in instances of the same printed character are much - * more likely to be in pixels near the boundary. By contrast, - * the correlation method gives equal weight to all foreground pixels. - * - * For best results, use the correlation method. Correlation takes - * the number of fg pixels in the AND of instance and template, - * divided by the product of the number of fg pixels in instance - * and template. It compares this with a threshold that, in - * general, depends on the fractional coverage of the template. - * For heavy text, the threshold is raised above that for light - * text, By using both these parameters (basic threshold and - * adjustment factor for text weight), one has more flexibility - * and can arrive at the fewest substitution errors, although - * this comes at the price of more templates. - * - * The strict Hausdorff scoring is not a rank weighting, because a - * single pixel beyond the given distance will cause a match - * failure. A rank Hausdorff is more robust to non-boundary noise, - * but it is also more susceptible to confusing components that - * should be in different classes. For implementing a jbig2 - * application for visually lossless binary image compression, - * you have two choices: - * - * (1) use a 3x3 structuring element (size = 3) and a strict - * Hausdorff comparison (rank = 1.0 in the rank Hausdorff - * function). This will result in a minimal number of classes, - * but confusion of small characters, such as italic and - * non-italic lower-case 'o', can still occur. - * (2) use the correlation method with a threshold of 0.85 - * and a weighting factor of about 0.7. This will result in - * a larger number of classes, but should not be confused - * either by similar small characters or by extremely - * thick sans serif characters, such as in prog/cootoots.png. - * - * As mentioned above, if visual substitution errors must be - * avoided, you should use the correlation method. - * - * We provide executables that show how to do the encoding: - * prog/jbrankhaus.c - * prog/jbcorrelation.c - * - * The basic flow for correlation classification goes as follows, - * where specific choices have been made for parameters (Hausdorff - * is the same except for initialization): - * - * // Initialize and save data in the classer - * JBCLASSER *classer = - * jbCorrelationInit(JB_CONN_COMPS, 0, 0, 0.8, 0.7); - * SARRAY *safiles = getSortedPathnamesInDirectory(directory, - * NULL, 0, 0); - * jbAddPages(classer, safiles); - * - * // Save the data in a data structure for serialization, - * // and write it into two files. - * JBDATA *data = jbDataSave(classer); - * jbDataWrite(rootname, data); - * - * // Reconstruct (render) the pages from the encoded data. - * PIXA *pixa = jbDataRender(data, FALSE); - * - * Adam Langley has built a jbig2 standards-compliant encoder, the - * first one to appear in open source. You can get this encoder at: - * http://www.imperialviolet.org/jbig2.html - * - * It uses arithmetic encoding throughout. It encodes binary images - * losslessly with a single arithmetic coding over the full image. - * It also does both lossy and lossless encoding from connected - * components, using leptonica to generate the templates representing - * each cluster. - */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) For scanned text, suggested input values are: - * thresh ~ [0.8 - 0.85] - * weightfactor ~ [0.5 - 0.6] - * (2) For electronically generated fonts (e.g., rasterized pdf), - * a very high thresh (e.g., 0.95) will not cause a significant - * increase in the number of classes. - *- */ -JBCLASSER * -jbCorrelationInit(l_int32 components, - l_int32 maxwidth, - l_int32 maxheight, - l_float32 thresh, - l_float32 weightfactor) -{ - return jbCorrelationInitInternal(components, maxwidth, maxheight, thresh, - weightfactor, 1); -} - -/*! - * \brief jbCorrelationInitWithoutComponents() - * - * \param[in] components JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS - * \param[in] maxwidth of component; use 0 for default - * \param[in] maxheight of component; use 0 for default - * \param[in] thresh value for correlation score: in [0.4 - 0.98] - * \param[in] weightfactor corrects thresh for thick characters [0.0 - 1.0] - * \return jbclasser if OK; NULL on error - * - *
- * Notes: - * Acts the same as jbCorrelationInit(), but the resulting - * object doesn't keep a list of all the components. - *- */ -JBCLASSER * -jbCorrelationInitWithoutComponents(l_int32 components, - l_int32 maxwidth, - l_int32 maxheight, - l_float32 thresh, - l_float32 weightfactor) -{ - return jbCorrelationInitInternal(components, maxwidth, maxheight, thresh, - weightfactor, 0); -} - - -static JBCLASSER * -jbCorrelationInitInternal(l_int32 components, - l_int32 maxwidth, - l_int32 maxheight, - l_float32 thresh, - l_float32 weightfactor, - l_int32 keep_components) -{ -JBCLASSER *classer; - - PROCNAME("jbCorrelationInitInternal"); - - if (components != JB_CONN_COMPS && components != JB_CHARACTERS && - components != JB_WORDS) - return (JBCLASSER *)ERROR_PTR("invalid components", procName, NULL); - if (thresh < 0.4 || thresh > 0.98) - return (JBCLASSER *)ERROR_PTR("thresh not in range [0.4 - 0.98]", - procName, NULL); - if (weightfactor < 0.0 || weightfactor > 1.0) - return (JBCLASSER *)ERROR_PTR("weightfactor not in range [0.0 - 1.0]", - procName, NULL); - if (maxwidth == 0) { - if (components == JB_CONN_COMPS) - maxwidth = MAX_CONN_COMP_WIDTH; - else if (components == JB_CHARACTERS) - maxwidth = MAX_CHAR_COMP_WIDTH; - else /* JB_WORDS */ - maxwidth = MAX_WORD_COMP_WIDTH; - } - if (maxheight == 0) - maxheight = MAX_COMP_HEIGHT; - - - if ((classer = jbClasserCreate(JB_CORRELATION, components)) == NULL) - return (JBCLASSER *)ERROR_PTR("classer not made", procName, NULL); - classer->maxwidth = maxwidth; - classer->maxheight = maxheight; - classer->thresh = thresh; - classer->weightfactor = weightfactor; - classer->dahash = l_dnaHashCreate(5507, 4); /* 5507 is prime */ - classer->keep_pixaa = keep_components; - return classer; -} - - -/*----------------------------------------------------------------------* - * Classify the pages * - *----------------------------------------------------------------------*/ -/*! - * \brief jbAddPages() - * - * \param[in] jbclasser - * \param[in] safiles of page image file names - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) jbclasser makes a copy of the array of file names. - * (2) The caller is still responsible for destroying the input array. - *- */ -l_ok -jbAddPages(JBCLASSER *classer, - SARRAY *safiles) -{ -l_int32 i, nfiles; -char *fname; -PIX *pix; - - PROCNAME("jbAddPages"); - - if (!classer) - return ERROR_INT("classer not defined", procName, 1); - if (!safiles) - return ERROR_INT("safiles not defined", procName, 1); - - classer->safiles = sarrayCopy(safiles); - nfiles = sarrayGetCount(safiles); - for (i = 0; i < nfiles; i++) { - fname = sarrayGetString(safiles, i, L_NOCOPY); - if ((pix = pixRead(fname)) == NULL) { - L_WARNING("image file %d not read\n", procName, i); - continue; - } - if (pixGetDepth(pix) != 1) { - L_WARNING("image file %d not 1 bpp\n", procName, i); - continue; - } - jbAddPage(classer, pix); - pixDestroy(&pix); - } - - return 0; -} - - -/*! - * \brief jbAddPage() - * - * \param[in] jbclasser - * \param[in] pixs input page - * \return 0 if OK; 1 on error - */ -l_ok -jbAddPage(JBCLASSER *classer, - PIX *pixs) -{ -BOXA *boxas; -PIXA *pixas; - - PROCNAME("jbAddPage"); - - if (!classer) - return ERROR_INT("classer not defined", procName, 1); - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - - classer->w = pixGetWidth(pixs); - classer->h = pixGetHeight(pixs); - - /* Get the appropriate components and their bounding boxes */ - if (jbGetComponents(pixs, classer->components, classer->maxwidth, - classer->maxheight, &boxas, &pixas)) { - return ERROR_INT("components not made", procName, 1); - } - - jbAddPageComponents(classer, pixs, boxas, pixas); - boxaDestroy(&boxas); - pixaDestroy(&pixas); - return 0; -} - - -/*! - * \brief jbAddPageComponents() - * - * \param[in] jbclasser - * \param[in] pixs input page - * \param[in] boxas b.b. of components for this page - * \param[in] pixas components for this page - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) If there are no components on the page, we don't require input - * of empty boxas or pixas, although that's the typical situation. - *- */ -l_ok -jbAddPageComponents(JBCLASSER *classer, - PIX *pixs, - BOXA *boxas, - PIXA *pixas) -{ -l_int32 n; - - PROCNAME("jbAddPageComponents"); - - if (!classer) - return ERROR_INT("classer not defined", procName, 1); - if (!pixs) - return ERROR_INT("pix not defined", procName, 1); - - /* Test for no components on the current page. Always update the - * number of pages processed, even if nothing is on it. */ - if (!boxas || !pixas || (boxaGetCount(boxas) == 0)) { - classer->npages++; - return 0; - } - - /* Get classes. For hausdorff, it uses a specified size of - * structuring element and specified rank. For correlation, - * it uses a specified threshold. */ - if (classer->method == JB_RANKHAUS) { - if (jbClassifyRankHaus(classer, boxas, pixas)) - return ERROR_INT("rankhaus classification failed", procName, 1); - } else { /* classer->method == JB_CORRELATION */ - if (jbClassifyCorrelation(classer, boxas, pixas)) - return ERROR_INT("correlation classification failed", procName, 1); - } - - /* Find the global UL corners, adjusted for each instance so - * that the class template and instance will have their - * centroids in the same place. Then the template can be - * used to replace the instance. */ - if (jbGetULCorners(classer, pixs, boxas)) - return ERROR_INT("UL corners not found", procName, 1); - - /* Update total component counts and number of pages processed. */ - n = boxaGetCount(boxas); - classer->baseindex += n; - numaAddNumber(classer->nacomps, n); - classer->npages++; - return 0; -} - - -/*----------------------------------------------------------------------* - * Classification using windowed rank hausdorff metric * - *----------------------------------------------------------------------*/ -/*! - * \brief jbClassifyRankHaus() - * - * \param[in] jbclasser - * \param[in] boxa new components for classification - * \param[in] pixas new components for classification - * \return 0 if OK; 1 on error - */ -l_ok -jbClassifyRankHaus(JBCLASSER *classer, - BOXA *boxa, - PIXA *pixas) -{ -l_int32 n, nt, i, wt, ht, iclass, size, found, testval; -l_int32 npages, area1, area3; -l_int32 *tab8; -l_float32 rank, x1, y1, x2, y2; -BOX *box; -NUMA *naclass, *napage; -NUMA *nafg; /* fg area of all instances */ -NUMA *nafgt; /* fg area of all templates */ -JBFINDCTX *findcontext; -L_DNAHASH *dahash; -PIX *pix, *pix1, *pix2, *pix3, *pix4; -PIXA *pixa, *pixa1, *pixa2, *pixat, *pixatd; -PIXAA *pixaa; -PTA *pta, *ptac, *ptact; -SEL *sel; - - PROCNAME("jbClassifyRankHaus"); - - if (!classer) - return ERROR_INT("classer not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (!pixas) - return ERROR_INT("pixas not defined", procName, 1); - if ((n = pixaGetCount(pixas)) == 0) - return ERROR_INT("pixas is empty", procName, 1); - if ((nafg = pixaCountPixels(pixas)) == NULL) /* areas for this page */ - return ERROR_INT("fg counting failed", procName, 1); - - npages = classer->npages; - size = classer->sizehaus; - sel = selCreateBrick(size, size, size / 2, size / 2, SEL_HIT); - - /* Generate the bordered pixa, with and without dilation. - * pixa1 and pixa2 contain all the input components. */ - pixa1 = pixaCreate(n); - pixa2 = pixaCreate(n); - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixas, i, L_CLONE); - pix1 = pixAddBorderGeneral(pix, JB_ADDED_PIXELS, JB_ADDED_PIXELS, - JB_ADDED_PIXELS, JB_ADDED_PIXELS, 0); - pix2 = pixDilate(NULL, pix1, sel); - pixaAddPix(pixa1, pix1, L_INSERT); /* un-dilated */ - pixaAddPix(pixa2, pix2, L_INSERT); /* dilated */ - pixDestroy(&pix); - } - - /* Get the centroids of all the bordered images. - * These are relative to the UL corner of each (bordered) pix. */ - pta = pixaCentroids(pixa1); /* centroids for this page; use here */ - ptac = classer->ptac; /* holds centroids of components up to this page */ - ptaJoin(ptac, pta, 0, -1); /* save centroids of all components */ - ptact = classer->ptact; /* holds centroids of templates */ - - /* Use these to save the class and page of each component. */ - naclass = classer->naclass; - napage = classer->napage; - - /* Store the unbordered pix in a pixaa, in a hierarchical - * set of arrays. There is one pixa for each class, - * and the pix in each pixa are all the instances found - * of that class. This is actually more than one would need - * for a jbig2 encoder, but there are two reasons to keep - * them around: (1) the set of instances for each class - * can be used to make an improved binary (or, better, - * a grayscale) template, rather than simply using the first - * one in the set; (2) we can investigate the failures - * of the classifier. This pixaa grows as we process - * successive pages. */ - pixaa = classer->pixaa; - - /* arrays to store class exemplars (templates) */ - pixat = classer->pixat; /* un-dilated */ - pixatd = classer->pixatd; /* dilated */ - - /* Fill up the pixaa tree with the template exemplars as - * the first pix in each pixa. As we add each pix, - * we also add the associated box to the pixa. - * We also keep track of the centroid of each pix, - * and use the difference between centroids (of the - * pix with the exemplar we are checking it with) - * to align the two when checking that the Hausdorff - * distance does not exceed a threshold. - * The threshold is set by the Sel used for dilating. - * For example, a 3x3 brick, sel_3, corresponds to a - * Hausdorff distance of 1. In general, for an NxN brick, - * with N odd, corresponds to a Hausdorff distance of (N - 1)/2. - * It turns out that we actually need to use a sel of size 2x2 - * to avoid small bad components when there is a halftone image - * from which components can be chosen. - * The larger the Sel you use, the fewer the number of classes, - * and the greater the likelihood of putting semantically - * different objects in the same class. For simplicity, - * we do this separately for the case of rank == 1.0 (exact - * match within the Hausdorff distance) and rank < 1.0. */ - rank = classer->rankhaus; - dahash = classer->dahash; - if (rank == 1.0) { - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixa1, i, L_CLONE); - pix2 = pixaGetPix(pixa2, i, L_CLONE); - ptaGetPt(pta, i, &x1, &y1); - nt = pixaGetCount(pixat); /* number of templates */ - found = FALSE; - findcontext = findSimilarSizedTemplatesInit(classer, pix1); - while ((iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) { - /* Find score for this template */ - pix3 = pixaGetPix(pixat, iclass, L_CLONE); - pix4 = pixaGetPix(pixatd, iclass, L_CLONE); - ptaGetPt(ptact, iclass, &x2, &y2); - testval = pixHaustest(pix1, pix2, pix3, pix4, x1 - x2, y1 - y2, - MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT); - pixDestroy(&pix3); - pixDestroy(&pix4); - if (testval == 1) { - found = TRUE; - numaAddNumber(naclass, iclass); - numaAddNumber(napage, npages); - if (classer->keep_pixaa) { - pixa = pixaaGetPixa(pixaa, iclass, L_CLONE); - pix = pixaGetPix(pixas, i, L_CLONE); - pixaAddPix(pixa, pix, L_INSERT); - box = boxaGetBox(boxa, i, L_CLONE); - pixaAddBox(pixa, box, L_INSERT); - pixaDestroy(&pixa); - } - break; - } - } - findSimilarSizedTemplatesDestroy(&findcontext); - if (found == FALSE) { /* new class */ - numaAddNumber(naclass, nt); - numaAddNumber(napage, npages); - pixa = pixaCreate(0); - pix = pixaGetPix(pixas, i, L_CLONE); /* unbordered instance */ - pixaAddPix(pixa, pix, L_INSERT); - wt = pixGetWidth(pix); - ht = pixGetHeight(pix); - l_dnaHashAdd(dahash, (l_uint64)ht * wt, nt); - box = boxaGetBox(boxa, i, L_CLONE); - pixaAddBox(pixa, box, L_INSERT); - pixaaAddPixa(pixaa, pixa, L_INSERT); /* unbordered instance */ - ptaAddPt(ptact, x1, y1); - pixaAddPix(pixat, pix1, L_INSERT); /* bordered template */ - pixaAddPix(pixatd, pix2, L_INSERT); /* bordered dil template */ - } else { /* don't save them */ - pixDestroy(&pix1); - pixDestroy(&pix2); - } - } - } else { /* rank < 1.0 */ - nafgt = classer->nafgt; - tab8 = makePixelSumTab8(); - for (i = 0; i < n; i++) { /* all instances on this page */ - pix1 = pixaGetPix(pixa1, i, L_CLONE); - numaGetIValue(nafg, i, &area1); - pix2 = pixaGetPix(pixa2, i, L_CLONE); - ptaGetPt(pta, i, &x1, &y1); /* use pta for this page */ - nt = pixaGetCount(pixat); /* number of templates */ - found = FALSE; - findcontext = findSimilarSizedTemplatesInit(classer, pix1); - while ((iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) { - /* Find score for this template */ - pix3 = pixaGetPix(pixat, iclass, L_CLONE); - numaGetIValue(nafgt, iclass, &area3); - pix4 = pixaGetPix(pixatd, iclass, L_CLONE); - ptaGetPt(ptact, iclass, &x2, &y2); - testval = pixRankHaustest(pix1, pix2, pix3, pix4, - x1 - x2, y1 - y2, - MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT, - area1, area3, rank, tab8); - pixDestroy(&pix3); - pixDestroy(&pix4); - if (testval == 1) { /* greedy match; take the first */ - found = TRUE; - numaAddNumber(naclass, iclass); - numaAddNumber(napage, npages); - if (classer->keep_pixaa) { - pixa = pixaaGetPixa(pixaa, iclass, L_CLONE); - pix = pixaGetPix(pixas, i, L_CLONE); - pixaAddPix(pixa, pix, L_INSERT); - box = boxaGetBox(boxa, i, L_CLONE); - pixaAddBox(pixa, box, L_INSERT); - pixaDestroy(&pixa); - } - break; - } - } - findSimilarSizedTemplatesDestroy(&findcontext); - if (found == FALSE) { /* new class */ - numaAddNumber(naclass, nt); - numaAddNumber(napage, npages); - pixa = pixaCreate(0); - pix = pixaGetPix(pixas, i, L_CLONE); /* unbordered instance */ - pixaAddPix(pixa, pix, L_INSERT); - wt = pixGetWidth(pix); - ht = pixGetHeight(pix); - l_dnaHashAdd(dahash, (l_uint64)ht * wt, nt); - box = boxaGetBox(boxa, i, L_CLONE); - pixaAddBox(pixa, box, L_INSERT); - pixaaAddPixa(pixaa, pixa, L_INSERT); /* unbordered instance */ - ptaAddPt(ptact, x1, y1); - pixaAddPix(pixat, pix1, L_INSERT); /* bordered template */ - pixaAddPix(pixatd, pix2, L_INSERT); /* ditto */ - numaAddNumber(nafgt, area1); - } else { /* don't save them */ - pixDestroy(&pix1); - pixDestroy(&pix2); - } - } - LEPT_FREE(tab8); - } - classer->nclass = pixaGetCount(pixat); - - numaDestroy(&nafg); - ptaDestroy(&pta); - pixaDestroy(&pixa1); - pixaDestroy(&pixa2); - selDestroy(&sel); - return 0; -} - - -/*! - * \brief pixHaustest() - * - * \param[in] pix1 new pix, not dilated - * \param[in] pix2 new pix, dilated - * \param[in] pix3 exemplar pix, not dilated - * \param[in] pix4 exemplar pix, dilated - * \param[in] delx x comp of centroid difference - * \param[in] dely y comp of centroid difference - * \param[in] maxdiffw max width difference of pix1 and pix2 - * \param[in] maxdiffh max height difference of pix1 and pix2 - * \return 0 FALSE) if no match, 1 (TRUE if the new - * pix is in the same class as the exemplar. - * - *
- * Notes: - * We check first that the two pix are roughly - * the same size. Only if they meet that criterion do - * we compare the bitmaps. The Hausdorff is a 2-way - * check. The centroid difference is used to align the two - * images to the nearest integer for each of the checks. - * These check that the dilated image of one contains - * ALL the pixels of the undilated image of the other. - * Checks are done in both direction. A single pixel not - * contained in either direction results in failure of the test. - *- */ -l_int32 -pixHaustest(PIX *pix1, - PIX *pix2, - PIX *pix3, - PIX *pix4, - l_float32 delx, /* x(1) - x(3) */ - l_float32 dely, /* y(1) - y(3) */ - l_int32 maxdiffw, - l_int32 maxdiffh) -{ -l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, boolmatch; -PIX *pixt; - - /* Eliminate possible matches based on size difference */ - wi = pixGetWidth(pix1); - hi = pixGetHeight(pix1); - wt = pixGetWidth(pix3); - ht = pixGetHeight(pix3); - delw = L_ABS(wi - wt); - if (delw > maxdiffw) - return FALSE; - delh = L_ABS(hi - ht); - if (delh > maxdiffh) - return FALSE; - - /* Round difference in centroid location to nearest integer; - * use this as a shift when doing the matching. */ - if (delx >= 0) - idelx = (l_int32)(delx + 0.5); - else - idelx = (l_int32)(delx - 0.5); - if (dely >= 0) - idely = (l_int32)(dely + 0.5); - else - idely = (l_int32)(dely - 0.5); - - /* Do 1-direction hausdorff, checking that every pixel in pix1 - * is within a dilation distance of some pixel in pix3. Namely, - * that pix4 entirely covers pix1: - * pixt = pixSubtract(NULL, pix1, pix4), including shift - * where pixt has no ON pixels. */ - pixt = pixCreateTemplate(pix1); - pixRasterop(pixt, 0, 0, wi, hi, PIX_SRC, pix1, 0, 0); - pixRasterop(pixt, idelx, idely, wi, hi, PIX_DST & PIX_NOT(PIX_SRC), - pix4, 0, 0); - pixZero(pixt, &boolmatch); - if (boolmatch == 0) { - pixDestroy(&pixt); - return FALSE; - } - - /* Do 1-direction hausdorff, checking that every pixel in pix3 - * is within a dilation distance of some pixel in pix1. Namely, - * that pix2 entirely covers pix3: - * pixSubtract(pixt, pix3, pix2), including shift - * where pixt has no ON pixels. */ - pixRasterop(pixt, idelx, idely, wt, ht, PIX_SRC, pix3, 0, 0); - pixRasterop(pixt, 0, 0, wt, ht, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0); - pixZero(pixt, &boolmatch); - pixDestroy(&pixt); - return boolmatch; -} - - -/*! - * \brief pixRankHaustest() - * - * \param[in] pix1 new pix, not dilated - * \param[in] pix2 new pix, dilated - * \param[in] pix3 exemplar pix, not dilated - * \param[in] pix4 exemplar pix, dilated - * \param[in] delx x comp of centroid difference - * \param[in] dely y comp of centroid difference - * \param[in] maxdiffw max width difference of pix1 and pix2 - * \param[in] maxdiffh max height difference of pix1 and pix2 - * \param[in] area1 fg pixels in pix1 - * \param[in] area3 fg pixels in pix3 - * \param[in] rank rank value of test, each way - * \param[in] tab8 table of pixel sums for byte - * \return 0 FALSE) if no match, 1 (TRUE if the new - * pix is in the same class as the exemplar. - * - *
- * Notes: - * We check first that the two pix are roughly - * the same size. Only if they meet that criterion do - * we compare the bitmaps. We convert the rank value to - * a number of pixels by multiplying the rank fraction by the number - * of pixels in the undilated image. The Hausdorff is a 2-way - * check. The centroid difference is used to align the two - * images to the nearest integer for each of the checks. - * The rank hausdorff checks that the dilated image of one - * contains the rank fraction of the pixels of the undilated - * image of the other. Checks are done in both direction. - * Failure of the test in either direction results in failure - * of the test. - *- */ -l_int32 -pixRankHaustest(PIX *pix1, - PIX *pix2, - PIX *pix3, - PIX *pix4, - l_float32 delx, /* x(1) - x(3) */ - l_float32 dely, /* y(1) - y(3) */ - l_int32 maxdiffw, - l_int32 maxdiffh, - l_int32 area1, - l_int32 area3, - l_float32 rank, - l_int32 *tab8) -{ -l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, boolmatch; -l_int32 thresh1, thresh3; -PIX *pixt; - - /* Eliminate possible matches based on size difference */ - wi = pixGetWidth(pix1); - hi = pixGetHeight(pix1); - wt = pixGetWidth(pix3); - ht = pixGetHeight(pix3); - delw = L_ABS(wi - wt); - if (delw > maxdiffw) - return FALSE; - delh = L_ABS(hi - ht); - if (delh > maxdiffh) - return FALSE; - - /* Upper bounds in remaining pixels for allowable match */ - thresh1 = (l_int32)(area1 * (1. - rank) + 0.5); - thresh3 = (l_int32)(area3 * (1. - rank) + 0.5); - - /* Round difference in centroid location to nearest integer; - * use this as a shift when doing the matching. */ - if (delx >= 0) - idelx = (l_int32)(delx + 0.5); - else - idelx = (l_int32)(delx - 0.5); - if (dely >= 0) - idely = (l_int32)(dely + 0.5); - else - idely = (l_int32)(dely - 0.5); - - /* Do 1-direction hausdorff, checking that every pixel in pix1 - * is within a dilation distance of some pixel in pix3. Namely, - * that pix4 entirely covers pix1: - * pixt = pixSubtract(NULL, pix1, pix4), including shift - * where pixt has no ON pixels. */ - pixt = pixCreateTemplate(pix1); - pixRasterop(pixt, 0, 0, wi, hi, PIX_SRC, pix1, 0, 0); - pixRasterop(pixt, idelx, idely, wi, hi, PIX_DST & PIX_NOT(PIX_SRC), - pix4, 0, 0); - pixThresholdPixelSum(pixt, thresh1, &boolmatch, tab8); - if (boolmatch == 1) { /* above thresh1 */ - pixDestroy(&pixt); - return FALSE; - } - - /* Do 1-direction hausdorff, checking that every pixel in pix3 - * is within a dilation distance of some pixel in pix1. Namely, - * that pix2 entirely covers pix3: - * pixSubtract(pixt, pix3, pix2), including shift - * where pixt has no ON pixels. */ - pixRasterop(pixt, idelx, idely, wt, ht, PIX_SRC, pix3, 0, 0); - pixRasterop(pixt, 0, 0, wt, ht, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0); - pixThresholdPixelSum(pixt, thresh3, &boolmatch, tab8); - pixDestroy(&pixt); - if (boolmatch == 1) /* above thresh3 */ - return FALSE; - else - return TRUE; -} - - -/*----------------------------------------------------------------------* - * Classification using windowed correlation score * - *----------------------------------------------------------------------*/ -/*! - * \brief jbClassifyCorrelation() - * - * \param[in] jbclasser - * \param[in] boxa new components for classification - * \param[in] pixas new components for classification - * \return 0 if OK; 1 on error - */ -l_ok -jbClassifyCorrelation(JBCLASSER *classer, - BOXA *boxa, - PIXA *pixas) -{ -l_int32 n, nt, i, iclass, wt, ht, found, area, area1, area2, npages, - overthreshold; -l_int32 *sumtab, *centtab; -l_uint32 *row, word; -l_float32 x1, y1, x2, y2, xsum, ysum; -l_float32 thresh, weight, threshold; -BOX *box; -NUMA *naclass, *napage; -NUMA *nafgt; /* fg area of all templates */ -NUMA *naarea; /* w * h area of all templates */ -JBFINDCTX *findcontext; -L_DNAHASH *dahash; -PIX *pix, *pix1, *pix2; -PIXA *pixa, *pixa1, *pixat; -PIXAA *pixaa; -PTA *pta, *ptac, *ptact; -l_int32 *pixcts; /* pixel counts of each pixa */ -l_int32 **pixrowcts; /* row-by-row pixel counts of each pixa */ -l_int32 x, y, rowcount, downcount, wpl; -l_uint8 byte; - - PROCNAME("jbClassifyCorrelation"); - - if (!classer) - return ERROR_INT("classer not found", procName, 1); - if (!boxa) - return ERROR_INT("boxa not found", procName, 1); - if (!pixas) - return ERROR_INT("pixas not found", procName, 1); - - npages = classer->npages; - - /* Generate the bordered pixa, which contains all the the - * input components. This will not be saved. */ - if ((n = pixaGetCount(pixas)) == 0) { - L_WARNING("pixas is empty\n", procName); - return 0; - } - pixa1 = pixaCreate(n); - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixas, i, L_CLONE); - pix1 = pixAddBorderGeneral(pix, JB_ADDED_PIXELS, JB_ADDED_PIXELS, - JB_ADDED_PIXELS, JB_ADDED_PIXELS, 0); - pixaAddPix(pixa1, pix1, L_INSERT); - pixDestroy(&pix); - } - - /* Use these to save the class and page of each component. */ - naclass = classer->naclass; - napage = classer->napage; - - /* Get the number of fg pixels in each component. */ - nafgt = classer->nafgt; /* holds fg areas of the templates */ - sumtab = makePixelSumTab8(); - - pixcts = (l_int32 *)LEPT_CALLOC(n, sizeof(*pixcts)); - pixrowcts = (l_int32 **)LEPT_CALLOC(n, sizeof(*pixrowcts)); - centtab = makePixelCentroidTab8(); - - /* Count the "1" pixels in each row of the pix in pixa1; this - * allows pixCorrelationScoreThresholded to abort early if a match - * is impossible. This loop merges three calculations: the total - * number of "1" pixels, the number of "1" pixels in each row, and - * the centroid. The centroids are relative to the UL corner of - * each (bordered) pix. The pixrowcts[i][y] are the total number - * of fg pixels in pixa[i] below row y. */ - pta = ptaCreate(n); - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixa1, i, L_CLONE); - pixrowcts[i] = (l_int32 *)LEPT_CALLOC(pixGetHeight(pix), - sizeof(**pixrowcts)); - xsum = 0; - ysum = 0; - wpl = pixGetWpl(pix); - row = pixGetData(pix) + (pixGetHeight(pix) - 1) * wpl; - downcount = 0; - for (y = pixGetHeight(pix) - 1; y >= 0; y--, row -= wpl) { - pixrowcts[i][y] = downcount; - rowcount = 0; - for (x = 0; x < wpl; x++) { - word = row[x]; - byte = word & 0xff; - rowcount += sumtab[byte]; - xsum += centtab[byte] + (x * 32 + 24) * sumtab[byte]; - byte = (word >> 8) & 0xff; - rowcount += sumtab[byte]; - xsum += centtab[byte] + (x * 32 + 16) * sumtab[byte]; - byte = (word >> 16) & 0xff; - rowcount += sumtab[byte]; - xsum += centtab[byte] + (x * 32 + 8) * sumtab[byte]; - byte = (word >> 24) & 0xff; - rowcount += sumtab[byte]; - xsum += centtab[byte] + x * 32 * sumtab[byte]; - } - downcount += rowcount; - ysum += rowcount * y; - } - pixcts[i] = downcount; - if (downcount > 0) { - ptaAddPt(pta, - xsum / (l_float32)downcount, ysum / (l_float32)downcount); - } else { /* no pixels; shouldn't happen */ - L_ERROR("downcount == 0 !\n", procName); - ptaAddPt(pta, pixGetWidth(pix) / 2, pixGetHeight(pix) / 2); - } - pixDestroy(&pix); - } - - ptac = classer->ptac; /* holds centroids of components up to this page */ - ptaJoin(ptac, pta, 0, -1); /* save centroids of all components */ - ptact = classer->ptact; /* holds centroids of templates */ - - /* Store the unbordered pix in a pixaa, in a hierarchical - * set of arrays. There is one pixa for each class, - * and the pix in each pixa are all the instances found - * of that class. This is actually more than one would need - * for a jbig2 encoder, but there are two reasons to keep - * them around: (1) the set of instances for each class - * can be used to make an improved binary (or, better, - * a grayscale) template, rather than simply using the first - * one in the set; (2) we can investigate the failures - * of the classifier. This pixaa grows as we process - * successive pages. */ - pixaa = classer->pixaa; - - /* Array to store class exemplars */ - pixat = classer->pixat; - - /* Fill up the pixaa tree with the template exemplars as - * the first pix in each pixa. As we add each pix, - * we also add the associated box to the pixa. - * We also keep track of the centroid of each pix, - * and use the difference between centroids (of the - * pix with the exemplar we are checking it with) - * to align the two when checking that the correlation - * score exceeds a threshold. The correlation score - * is given by the square of the area of the AND - * between aligned instance and template, divided by - * the product of areas of each image. For identical - * template and instance, the score is 1.0. - * If the threshold is too small, non-equivalent instances - * will be placed in the same class; if too large, there will - * be an unnecessary division of classes representing the - * same character. The weightfactor adds in some of the - * difference (1.0 - thresh), depending on the heaviness - * of the template (measured as the fraction of fg pixels). */ - thresh = classer->thresh; - weight = classer->weightfactor; - naarea = classer->naarea; - dahash = classer->dahash; - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixa1, i, L_CLONE); - area1 = pixcts[i]; - ptaGetPt(pta, i, &x1, &y1); /* centroid for this instance */ - nt = pixaGetCount(pixat); - found = FALSE; - findcontext = findSimilarSizedTemplatesInit(classer, pix1); - while ( (iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) { - /* Get the template */ - pix2 = pixaGetPix(pixat, iclass, L_CLONE); - numaGetIValue(nafgt, iclass, &area2); - ptaGetPt(ptact, iclass, &x2, &y2); /* template centroid */ - - /* Find threshold for this template */ - if (weight > 0.0) { - numaGetIValue(naarea, iclass, &area); - threshold = thresh + (1. - thresh) * weight * area2 / area; - } else { - threshold = thresh; - } - - /* Find score for this template */ - overthreshold = pixCorrelationScoreThresholded(pix1, pix2, - area1, area2, x1 - x2, y1 - y2, - MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT, - sumtab, pixrowcts[i], threshold); -#if DEBUG_CORRELATION_SCORE - { - l_float32 score, testscore; - l_int32 count, testcount; - pixCorrelationScore(pix1, pix2, area1, area2, x1 - x2, y1 - y2, - MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT, - sumtab, &score); - - pixCorrelationScoreSimple(pix1, pix2, area1, area2, - x1 - x2, y1 - y2, MAX_DIFF_WIDTH, - MAX_DIFF_HEIGHT, sumtab, &testscore); - count = (l_int32)rint(sqrt(score * area1 * area2)); - testcount = (l_int32)rint(sqrt(testscore * area1 * area2)); - if ((score >= threshold) != (testscore >= threshold)) { - lept_stderr("Correlation score mismatch: " - "%d(%g,%d) vs %d(%g,%d) (%g)\n", - count, score, score >= threshold, - testcount, testscore, testscore >= threshold, - score - testscore); - } - - if ((score >= threshold) != overthreshold) { - lept_stderr("Mismatch between correlation/threshold " - "comparison: %g(%g,%d) >= %g(%g) vs %s\n", - score, score*area1*area2, count, threshold, - threshold*area1*area2, - (overthreshold ? "true" : "false")); - } - } -#endif /* DEBUG_CORRELATION_SCORE */ - pixDestroy(&pix2); - - if (overthreshold) { /* greedy match */ - found = TRUE; - numaAddNumber(naclass, iclass); - numaAddNumber(napage, npages); - if (classer->keep_pixaa) { - /* We are keeping a record of all components */ - pixa = pixaaGetPixa(pixaa, iclass, L_CLONE); - pix = pixaGetPix(pixas, i, L_CLONE); - pixaAddPix(pixa, pix, L_INSERT); - box = boxaGetBox(boxa, i, L_CLONE); - pixaAddBox(pixa, box, L_INSERT); - pixaDestroy(&pixa); - } - break; - } - } - findSimilarSizedTemplatesDestroy(&findcontext); - if (found == FALSE) { /* new class */ - numaAddNumber(naclass, nt); - numaAddNumber(napage, npages); - pixa = pixaCreate(0); - pix = pixaGetPix(pixas, i, L_CLONE); /* unbordered instance */ - pixaAddPix(pixa, pix, L_INSERT); - wt = pixGetWidth(pix); - ht = pixGetHeight(pix); - l_dnaHashAdd(dahash, (l_uint64)ht * wt, nt); - box = boxaGetBox(boxa, i, L_CLONE); - pixaAddBox(pixa, box, L_INSERT); - pixaaAddPixa(pixaa, pixa, L_INSERT); /* unbordered instance */ - ptaAddPt(ptact, x1, y1); - numaAddNumber(nafgt, area1); - pixaAddPix(pixat, pix1, L_INSERT); /* bordered template */ - area = (pixGetWidth(pix1) - 2 * JB_ADDED_PIXELS) * - (pixGetHeight(pix1) - 2 * JB_ADDED_PIXELS); - numaAddNumber(naarea, area); - } else { /* don't save it */ - pixDestroy(&pix1); - } - } - classer->nclass = pixaGetCount(pixat); - - LEPT_FREE(pixcts); - LEPT_FREE(centtab); - for (i = 0; i < n; i++) { - LEPT_FREE(pixrowcts[i]); - } - LEPT_FREE(pixrowcts); - - LEPT_FREE(sumtab); - ptaDestroy(&pta); - pixaDestroy(&pixa1); - return 0; -} - - -/*----------------------------------------------------------------------* - * Determine the image components we start with * - *----------------------------------------------------------------------*/ -/*! - * \brief jbGetComponents() - * - * \param[in] pixs 1 bpp - * \param[in] components JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS - * \param[in] maxwidth of saved components; larger are discarded - * \param[in] maxheight of saved components; larger are discarded - * \param[out] ppboxa b.b. of component items - * \param[out] pppixa component items - * \return 0 if OK, 1 on error - */ -l_ok -jbGetComponents(PIX *pixs, - l_int32 components, - l_int32 maxwidth, - l_int32 maxheight, - BOXA **pboxad, - PIXA **ppixad) -{ -l_int32 empty, res, redfactor; -BOXA *boxa; -PIX *pix1, *pix2, *pix3; -PIXA *pixa, *pixat; - - PROCNAME("jbGetComponents"); - - if (!pboxad) - return ERROR_INT("&boxad not defined", procName, 1); - *pboxad = NULL; - if (!ppixad) - return ERROR_INT("&pixad not defined", procName, 1); - *ppixad = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (components != JB_CONN_COMPS && components != JB_CHARACTERS && - components != JB_WORDS) - return ERROR_INT("invalid components", procName, 1); - - pixZero(pixs, &empty); - if (empty) { - *pboxad = boxaCreate(0); - *ppixad = pixaCreate(0); - return 0; - } - - /* If required, preprocess input pixs. The method for both - * characters and words is to generate a connected component - * mask over the units that we want to aggregrate, which are, - * in general, sets of related connected components in pixs. - * For characters, we want to include the dots with - * 'i', 'j' and '!', so we do a small vertical closing to - * generate the mask. For words, we make a mask over all - * characters in each word. This is a bit more tricky, because - * the spacing between words is difficult to predict a priori, - * and words can be typeset with variable spacing that can - * in some cases be barely larger than the space between - * characters. The first step is to generate the mask and - * identify each of its connected components. */ - if (components == JB_CONN_COMPS) { /* no preprocessing */ - boxa = pixConnComp(pixs, &pixa, 8); - } else if (components == JB_CHARACTERS) { - pix1 = pixMorphSequence(pixs, "c1.6", 0); - boxa = pixConnComp(pix1, &pixat, 8); - pixa = pixaClipToPix(pixat, pixs); - pixDestroy(&pix1); - pixaDestroy(&pixat); - } else { /* components == JB_WORDS */ - - /* Do the operations at about 150 ppi resolution. - * It is much faster at 75 ppi, but the results are - * more accurate at 150 ppi. This will segment the - * words in body text. It can be expected that relatively - * infrequent words in a larger font will be split. */ - res = pixGetXRes(pixs); - if (res <= 200) { - redfactor = 1; - pix1 = pixClone(pixs); - } else if (res <= 400) { - redfactor = 2; - pix1 = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0); - } else { - redfactor = 4; - pix1 = pixReduceRankBinaryCascade(pixs, 1, 1, 0, 0); - } - - /* Estimate the word mask, at approximately 150 ppi. - * This has both very large and very small components left in. */ - pixWordMaskByDilation(pix1, &pix2, NULL, NULL); - - /* Expand the optimally dilated word mask to full res. */ - pix3 = pixExpandReplicate(pix2, redfactor); - - /* Pull out the pixels in pixs corresponding to the mask - * components in pix3. Note that above we used threshold - * levels in the reduction of 1 to insure that the resulting - * mask fully covers the input pixs. The downside of using - * a threshold of 1 is that very close characters from adjacent - * lines can be joined. But with a level of 2 or greater, - * it is necessary to use a seedfill, followed by a pixOr(): - * pixt4 = pixSeedfillBinary(NULL, pix3, pixs, 8); - * pixOr(pix3, pix3, pixt4); - * to insure that the mask coverage is complete over pixs. */ - boxa = pixConnComp(pix3, &pixat, 4); - pixa = pixaClipToPix(pixat, pixs); - pixaDestroy(&pixat); - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - } - - /* Remove large components, and save the results. */ - *ppixad = pixaSelectBySize(pixa, maxwidth, maxheight, L_SELECT_IF_BOTH, - L_SELECT_IF_LTE, NULL); - *pboxad = boxaSelectBySize(boxa, maxwidth, maxheight, L_SELECT_IF_BOTH, - L_SELECT_IF_LTE, NULL); - pixaDestroy(&pixa); - boxaDestroy(&boxa); - - return 0; -} - - -/*! - * \brief pixWordMaskByDilation() - * - * \param[in] pixs 1 bpp; typ. at 75 to 150 ppi - * \param[out] pmask [optional] dilated word mask - * \param[out] psize [optional] size of good horizontal dilation - * \param[out] pixadb [optional] debug: pixa of intermediate steps - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This gives an estimate of the word masks. See - * pixWordBoxesByDilation() for further filtering of the word boxes. - * (2) The resolution should be between 75 and 150 ppi, and the optimal - * dilation will be between 3 and 10. - * (3) A good size for dilating to get word masks is optionally returned. - * (4) Typically, the number of c.c. reduced with each successive - * dilation (stored in nadiff) decreases quickly to a minimum - * (where the characters in a word are joined), and then - * increases again as the smaller number of words are joined. - * For the typical case, you can then look for this minimum - * and dilate to get the word mask. However, there are many - * cases where the function is not so simple. For example, if the - * pix has been upscaled 2x, the nadiff function oscillates, with - * every other value being zero! And for some images it tails - * off without a clear minimum to indicate where to break. - * So a more simple and robust method is to find the dilation - * where the initial number of c.c. has been reduced by some - * fraction (we use a 70% reduction). - *- */ -l_ok -pixWordMaskByDilation(PIX *pixs, - PIX **ppixm, - l_int32 *psize, - PIXA *pixadb) -{ -l_int32 i, n, ndil, maxdiff, diff, ibest; -l_int32 check, count, total, xres; -l_int32 ncc[13]; /* max dilation + 1 */ -l_int32 *diffa; -BOXA *boxa; -NUMA *nacc, *nadiff; -PIX *pix1, *pix2; - - PROCNAME("pixWordMaskByDilation"); - - if (ppixm) *ppixm = NULL; - if (psize) *psize = 0; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs undefined or not 1 bpp", procName, 1); - if (!ppixm && !psize) - return ERROR_INT("no output requested", procName, 1); - - /* Find a good dilation to create the word mask, by successively - * increasing dilation size and counting the connected components. */ - pix1 = pixCopy(NULL, pixs); - ndil = 12; /* appropriate for 75 to 150 ppi */ - nacc = numaCreate(ndil + 1); - nadiff = numaCreate(ndil + 1); - for (i = 0; i <= ndil; i++) { - if (i == 0) /* first one not dilated */ - pix2 = pixCopy(NULL, pix1); - else /* successive dilation by sel_2h */ - pix2 = pixMorphSequence(pix1, "d2.1", 0); - boxa = pixConnCompBB(pix2, 4); - ncc[i] = boxaGetCount(boxa); - numaAddNumber(nacc, ncc[i]); - if (i == 0) total = ncc[0]; - if (i > 0) { - diff = ncc[i - 1] - ncc[i]; - numaAddNumber(nadiff, diff); - } - pixDestroy(&pix1); - pix1 = pix2; - boxaDestroy(&boxa); - } - pixDestroy(&pix1); - - /* Find the dilation at which the c.c. count has reduced - * to 30% of the initial value. Although 30% seems high, - * it seems better to use this but add one to ibest. */ - diffa = numaGetIArray(nadiff); - n = numaGetCount(nadiff); - maxdiff = 0; - check = TRUE; - ibest = 2; - for (i = 1; i < n; i++) { - numaGetIValue(nacc, i, &count); - if (check && count < 0.3 * total) { - ibest = i + 1; - check = FALSE; - } - diff = diffa[i]; - if (diff > maxdiff) - maxdiff = diff; - } - LEPT_FREE(diffa); - - /* Add small compensation for higher resolution */ - xres = pixGetXRes(pixs); - if (xres == 0) xres = 150; - if (xres > 110) ibest++; - if (ibest < 2) { - L_INFO("setting ibest to minimum allowed value of 2\n", procName); - ibest = 2; - } - - if (pixadb) { - lept_mkdir("lept/jb"); - {GPLOT *gplot; - NUMA *naseq; - PIX *pix3, *pix4; - L_INFO("Best dilation: %d\n", procName, L_MAX(3, ibest + 1)); - naseq = numaMakeSequence(1, 1, numaGetCount(nacc)); - pix3 = gplotGeneralPix2(naseq, nacc, GPLOT_LINES, - "/tmp/lept/jb/numcc", - "Number of cc vs. horizontal dilation", - "Sel horiz", "Number of cc"); - pixaAddPix(pixadb, pix3, L_INSERT); - numaDestroy(&naseq); - naseq = numaMakeSequence(1, 1, numaGetCount(nadiff)); - pix3 = gplotGeneralPix2(naseq, nadiff, GPLOT_LINES, - "/tmp/lept/jb/diffcc", - "Diff count of cc vs. horizontal dilation", - "Sel horiz", "Diff in cc"); - pixaAddPix(pixadb, pix3, L_INSERT); - numaDestroy(&naseq); - pix3 = pixCloseBrick(NULL, pixs, ibest + 1, 1); - pix4 = pixScaleToSize(pix3, 600, 0); - pixaAddPix(pixadb, pix4, L_INSERT); - pixDestroy(&pix3); - } - } - - if (psize) *psize = ibest + 1; - if (ppixm) - *ppixm = pixCloseBrick(NULL, pixs, ibest + 1, 1); - - numaDestroy(&nacc); - numaDestroy(&nadiff); - return 0; -} - - -/*! - * \brief pixWordBoxesByDilation() - * - * \param[in] pixs 1 bpp; typ. 75 - 200 ppi - * \param[in] minwidth saved components; smaller are discarded - * \param[in] minheight saved components; smaller are discarded - * \param[in] maxwidth saved components; larger are discarded - * \param[in] maxheight saved components; larger are discarded - * \param[out] pboxa of dilated word mask - * \param[out] psize [optional] size of good horizontal dilation - * \param[out] pixadb [optional] debug: pixa of intermediate steps - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Returns a pruned set of word boxes. - * (2) See pixWordMaskByDilation(). - *- */ -l_ok -pixWordBoxesByDilation(PIX *pixs, - l_int32 minwidth, - l_int32 minheight, - l_int32 maxwidth, - l_int32 maxheight, - BOXA **pboxa, - l_int32 *psize, - PIXA *pixadb) -{ -BOXA *boxa1, *boxa2; -PIX *pix1, *pix2; - - PROCNAME("pixWordBoxesByDilation"); - - if (psize) *psize = 0; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs undefined or not 1 bpp", procName, 1); - if (!pboxa) - return ERROR_INT("&boxa not defined", procName, 1); - *pboxa = NULL; - - /* Make a first estimate of the word mask */ - if (pixWordMaskByDilation(pixs, &pix1, psize, pixadb)) - return ERROR_INT("pixWordMaskByDilation() failed", procName, 1); - - /* Prune the word mask. Get the bounding boxes of the words. - * Remove the small ones, which can be due to punctuation - * that was not joined to a word. Also remove the large ones, - * which are not likely to be words. */ - boxa1 = pixConnComp(pix1, NULL, 8); - boxa2 = boxaSelectBySize(boxa1, minwidth, minheight, L_SELECT_IF_BOTH, - L_SELECT_IF_GTE, NULL); - *pboxa = boxaSelectBySize(boxa2, maxwidth, maxheight, L_SELECT_IF_BOTH, - L_SELECT_IF_LTE, NULL); - if (pixadb) { - pix2 = pixUnpackBinary(pixs, 32, 1); - pixRenderBoxaArb(pix2, boxa1, 2, 255, 0, 0); - pixaAddPix(pixadb, pix2, L_INSERT); - pix2 = pixUnpackBinary(pixs, 32, 1); - pixRenderBoxaArb(pix2, boxa2, 2, 0, 255, 0); - pixaAddPix(pixadb, pix2, L_INSERT); - } - boxaDestroy(&boxa1); - boxaDestroy(&boxa2); - pixDestroy(&pix1); - return 0; -} - - -/*----------------------------------------------------------------------* - * Build grayscale composites (templates) * - *----------------------------------------------------------------------*/ -/*! - * \brief jbAccumulateComposites() - * - * \param[in] pixaa one pixa for each class - * \param[out] ppna number of samples used to build each composite - * \param[out] pptat centroids of bordered composites - * \return pixad accumulated sum of samples in each class, or NULL on error - * - */ -PIXA * -jbAccumulateComposites(PIXAA *pixaa, - NUMA **pna, - PTA **pptat) -{ -l_int32 n, nt, i, j, d, minw, maxw, minh, maxh, xdiff, ydiff; -l_float32 x, y, xave, yave; -NUMA *na; -PIX *pix, *pixt1, *pixt2, *pixsum; -PIXA *pixa, *pixad; -PTA *ptat, *pta; - - PROCNAME("jbAccumulateComposites"); - - if (!pptat) - return (PIXA *)ERROR_PTR("&ptat not defined", procName, NULL); - *pptat = NULL; - if (!pna) - return (PIXA *)ERROR_PTR("&na not defined", procName, NULL); - *pna = NULL; - if (!pixaa) - return (PIXA *)ERROR_PTR("pixaa not defined", procName, NULL); - - n = pixaaGetCount(pixaa, NULL); - if ((ptat = ptaCreate(n)) == NULL) - return (PIXA *)ERROR_PTR("ptat not made", procName, NULL); - *pptat = ptat; - pixad = pixaCreate(n); - na = numaCreate(n); - *pna = na; - - for (i = 0; i < n; i++) { - pixa = pixaaGetPixa(pixaa, i, L_CLONE); - nt = pixaGetCount(pixa); - numaAddNumber(na, nt); - if (nt == 0) { - L_WARNING("empty pixa found!\n", procName); - pixaDestroy(&pixa); - continue; - } - pixaSizeRange(pixa, &minw, &minh, &maxw, &maxh); - pix = pixaGetPix(pixa, 0, L_CLONE); - d = pixGetDepth(pix); - pixDestroy(&pix); - pixt1 = pixCreate(maxw, maxh, d); - pixsum = pixInitAccumulate(maxw, maxh, 0); - pta = pixaCentroids(pixa); - - /* Find the average value of the centroids ... */ - xave = yave = 0; - for (j = 0; j < nt; j++) { - ptaGetPt(pta, j, &x, &y); - xave += x; - yave += y; - } - xave = xave / (l_float32)nt; - yave = yave / (l_float32)nt; - - /* and place all centroids at their average value */ - for (j = 0; j < nt; j++) { - pixt2 = pixaGetPix(pixa, j, L_CLONE); - ptaGetPt(pta, j, &x, &y); - xdiff = (l_int32)(x - xave); - ydiff = (l_int32)(y - yave); - pixClearAll(pixt1); - pixRasterop(pixt1, xdiff, ydiff, maxw, maxh, PIX_SRC, - pixt2, 0, 0); - pixAccumulate(pixsum, pixt1, L_ARITH_ADD); - pixDestroy(&pixt2); - } - pixaAddPix(pixad, pixsum, L_INSERT); - ptaAddPt(ptat, xave, yave); - - pixaDestroy(&pixa); - pixDestroy(&pixt1); - ptaDestroy(&pta); - } - - return pixad; -} - - -/*! - * \brief jbTemplatesFromComposites() - * - * \param[in] pixac one pix of composites for each class - * \param[in] na number of samples used for each class composite - * \return pixad 8 bpp templates for each class, or NULL on error - * - */ -PIXA * -jbTemplatesFromComposites(PIXA *pixac, - NUMA *na) -{ -l_int32 n, i; -l_float32 nt; /* number of samples in the composite; always an integer */ -l_float32 factor; -PIX *pixsum; /* accumulated composite */ -PIX *pixd; -PIXA *pixad; - - PROCNAME("jbTemplatesFromComposites"); - - if (!pixac) - return (PIXA *)ERROR_PTR("pixac not defined", procName, NULL); - if (!na) - return (PIXA *)ERROR_PTR("na not defined", procName, NULL); - - n = pixaGetCount(pixac); - pixad = pixaCreate(n); - for (i = 0; i < n; i++) { - pixsum = pixaGetPix(pixac, i, L_COPY); /* changed internally */ - numaGetFValue(na, i, &nt); - factor = 255. / nt; - pixMultConstAccumulate(pixsum, factor, 0); /* changes pixsum */ - pixd = pixFinalAccumulate(pixsum, 0, 8); - pixaAddPix(pixad, pixd, L_INSERT); - pixDestroy(&pixsum); - } - - return pixad; -} - - - -/*----------------------------------------------------------------------* - * jbig2 utility routines * - *----------------------------------------------------------------------*/ -/*! - * \brief jbClasserCreate() - * - * \param[in] method JB_RANKHAUS, JB_CORRELATION - * \param[in] components JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS - * \return jbclasser, or NULL on error - */ -JBCLASSER * -jbClasserCreate(l_int32 method, - l_int32 components) -{ -JBCLASSER *classer; - - PROCNAME("jbClasserCreate"); - - if (method != JB_RANKHAUS && method != JB_CORRELATION) - return (JBCLASSER *)ERROR_PTR("invalid method", procName, NULL); - if (components != JB_CONN_COMPS && components != JB_CHARACTERS && - components != JB_WORDS) - return (JBCLASSER *)ERROR_PTR("invalid component", procName, NULL); - - classer = (JBCLASSER *)LEPT_CALLOC(1, sizeof(JBCLASSER)); - classer->method = method; - classer->components = components; - classer->nacomps = numaCreate(0); - classer->pixaa = pixaaCreate(0); - classer->pixat = pixaCreate(0); - classer->pixatd = pixaCreate(0); - classer->nafgt = numaCreate(0); - classer->naarea = numaCreate(0); - classer->ptac = ptaCreate(0); - classer->ptact = ptaCreate(0); - classer->naclass = numaCreate(0); - classer->napage = numaCreate(0); - classer->ptaul = ptaCreate(0); - return classer; -} - - -/* - * \brief jbClasserDestroy() - * - * \param[in,out] pclasser will be set to null before returning - * \return void - */ -void -jbClasserDestroy(JBCLASSER **pclasser) -{ -JBCLASSER *classer; - - if (!pclasser) - return; - if ((classer = *pclasser) == NULL) - return; - - sarrayDestroy(&classer->safiles); - numaDestroy(&classer->nacomps); - pixaaDestroy(&classer->pixaa); - pixaDestroy(&classer->pixat); - pixaDestroy(&classer->pixatd); - l_dnaHashDestroy(&classer->dahash); - numaDestroy(&classer->nafgt); - numaDestroy(&classer->naarea); - ptaDestroy(&classer->ptac); - ptaDestroy(&classer->ptact); - numaDestroy(&classer->naclass); - numaDestroy(&classer->napage); - ptaDestroy(&classer->ptaul); - ptaDestroy(&classer->ptall); - LEPT_FREE(classer); - *pclasser = NULL; - return; -} - - -/*! - * \brief jbDataSave() - * - * \param[in] jbclasser - * \param[in] latticew cell width used to store each connected - * component in the composite - * \param[in] latticeh ditto for cell height - * \return jbdata, or NULL on error - * - *
- * Notes: - * (1) This routine stores the jbig2-type data required for - * generating a lossy jbig2 version of the image. - * It can be losslessly written to (and read from) two files. - * (2) It generates and stores the mosaic of templates. - * (3) It clones the Numa and Pta arrays, so these must all - * be destroyed by the caller. - * (4) Input 0 to use the default values for latticew and/or latticeh, - *- */ -JBDATA * -jbDataSave(JBCLASSER *classer) -{ -l_int32 maxw, maxh; -JBDATA *data; -PIX *pix; - - PROCNAME("jbDataSave"); - - if (!classer) - return (JBDATA *)ERROR_PTR("classer not defined", procName, NULL); - - /* Write the templates into an array. */ - pixaSizeRange(classer->pixat, NULL, NULL, &maxw, &maxh); - pix = pixaDisplayOnLattice(classer->pixat, maxw + 1, maxh + 1, - NULL, NULL); - if (!pix) - return (JBDATA *)ERROR_PTR("data not made", procName, NULL); - - data = (JBDATA *)LEPT_CALLOC(1, sizeof(JBDATA)); - data->pix = pix; - data->npages = classer->npages; - data->w = classer->w; - data->h = classer->h; - data->nclass = classer->nclass; - data->latticew = maxw + 1; - data->latticeh = maxh + 1; - data->naclass = numaClone(classer->naclass); - data->napage = numaClone(classer->napage); - data->ptaul = ptaClone(classer->ptaul); - return data; -} - - -/* - * \brief jbDataDestroy() - * - * \param[in,out] pdata will be set to null before returning - * \return void - */ -void -jbDataDestroy(JBDATA **pdata) -{ -JBDATA *data; - - if (!pdata) - return; - if ((data = *pdata) == NULL) - return; - - pixDestroy(&data->pix); - numaDestroy(&data->naclass); - numaDestroy(&data->napage); - ptaDestroy(&data->ptaul); - LEPT_FREE(data); - *pdata = NULL; - return; -} - - -/*! - * \brief jbDataWrite() - * - * \param[in] rootname for output files; everything but the extension - * \param[in] jbdata - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Serialization function that writes data in jbdata to file. - *- */ -l_ok -jbDataWrite(const char *rootout, - JBDATA *jbdata) -{ -char buf[L_BUF_SIZE]; -l_int32 w, h, nclass, npages, cellw, cellh, ncomp, i, x, y, iclass, ipage; -NUMA *naclass, *napage; -PTA *ptaul; -PIX *pixt; -FILE *fp; - - PROCNAME("jbDataWrite"); - - if (!rootout) - return ERROR_INT("no rootout", procName, 1); - if (!jbdata) - return ERROR_INT("no jbdata", procName, 1); - - npages = jbdata->npages; - w = jbdata->w; - h = jbdata->h; - pixt = jbdata->pix; - nclass = jbdata->nclass; - cellw = jbdata->latticew; - cellh = jbdata->latticeh; - naclass = jbdata->naclass; - napage = jbdata->napage; - ptaul = jbdata->ptaul; - - snprintf(buf, L_BUF_SIZE, "%s%s", rootout, JB_TEMPLATE_EXT); - pixWrite(buf, pixt, IFF_PNG); - - snprintf(buf, L_BUF_SIZE, "%s%s", rootout, JB_DATA_EXT); - if ((fp = fopenWriteStream(buf, "wb")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ncomp = ptaGetCount(ptaul); - fprintf(fp, "jb data file\n"); - fprintf(fp, "num pages = %d\n", npages); - fprintf(fp, "page size: w = %d, h = %d\n", w, h); - fprintf(fp, "num components = %d\n", ncomp); - fprintf(fp, "num classes = %d\n", nclass); - fprintf(fp, "template lattice size: w = %d, h = %d\n", cellw, cellh); - for (i = 0; i < ncomp; i++) { - numaGetIValue(napage, i, &ipage); - numaGetIValue(naclass, i, &iclass); - ptaGetIPt(ptaul, i, &x, &y); - fprintf(fp, "%d %d %d %d\n", ipage, iclass, x, y); - } - fclose(fp); - - return 0; -} - - -/*! - * \brief jbDataRead() - * - * \param[in] rootname for template and data files - * \return jbdata, or NULL on error - */ -JBDATA * -jbDataRead(const char *rootname) -{ -char fname[L_BUF_SIZE]; -char *linestr; -l_uint8 *data; -l_int32 nsa, i, w, h, cellw, cellh, x, y, iclass, ipage; -l_int32 npages, nclass, ncomp, ninit; -size_t size; -JBDATA *jbdata; -NUMA *naclass, *napage; -PIX *pixs; -PTA *ptaul; -SARRAY *sa; - - PROCNAME("jbDataRead"); - - if (!rootname) - return (JBDATA *)ERROR_PTR("rootname not defined", procName, NULL); - - snprintf(fname, L_BUF_SIZE, "%s%s", rootname, JB_TEMPLATE_EXT); - if ((pixs = pixRead(fname)) == NULL) - return (JBDATA *)ERROR_PTR("pix not read", procName, NULL); - - snprintf(fname, L_BUF_SIZE, "%s%s", rootname, JB_DATA_EXT); - if ((data = l_binaryRead(fname, &size)) == NULL) { - pixDestroy(&pixs); - return (JBDATA *)ERROR_PTR("data not read", procName, NULL); - } - - if ((sa = sarrayCreateLinesFromString((char *)data, 0)) == NULL) { - pixDestroy(&pixs); - LEPT_FREE(data); - return (JBDATA *)ERROR_PTR("sa not made", procName, NULL); - } - nsa = sarrayGetCount(sa); /* number of cc + 6 */ - linestr = sarrayGetString(sa, 0, L_NOCOPY); - if (strcmp(linestr, "jb data file") != 0) { - pixDestroy(&pixs); - LEPT_FREE(data); - sarrayDestroy(&sa); - return (JBDATA *)ERROR_PTR("invalid jb data file", procName, NULL); - } - linestr = sarrayGetString(sa, 1, L_NOCOPY); - sscanf(linestr, "num pages = %d", &npages); - linestr = sarrayGetString(sa, 2, L_NOCOPY); - sscanf(linestr, "page size: w = %d, h = %d", &w, &h); - linestr = sarrayGetString(sa, 3, L_NOCOPY); - sscanf(linestr, "num components = %d", &ncomp); - linestr = sarrayGetString(sa, 4, L_NOCOPY); - sscanf(linestr, "num classes = %d\n", &nclass); - linestr = sarrayGetString(sa, 5, L_NOCOPY); - sscanf(linestr, "template lattice size: w = %d, h = %d\n", &cellw, &cellh); - -#if 1 - lept_stderr("num pages = %d\n", npages); - lept_stderr("page size: w = %d, h = %d\n", w, h); - lept_stderr("num components = %d\n", ncomp); - lept_stderr("num classes = %d\n", nclass); - lept_stderr("template lattice size: w = %d, h = %d\n", cellw, cellh); -#endif - - ninit = ncomp; - if (ncomp > 1000000) { /* fuzz protection */ - L_WARNING("ncomp > 1M\n", procName); - ninit = 1000000; - } - naclass = numaCreate(ninit); - napage = numaCreate(ninit); - ptaul = ptaCreate(ninit); - for (i = 6; i < nsa; i++) { - linestr = sarrayGetString(sa, i, L_NOCOPY); - sscanf(linestr, "%d %d %d %d\n", &ipage, &iclass, &x, &y); - numaAddNumber(napage, ipage); - numaAddNumber(naclass, iclass); - ptaAddPt(ptaul, x, y); - } - - jbdata = (JBDATA *)LEPT_CALLOC(1, sizeof(JBDATA)); - jbdata->pix = pixs; - jbdata->npages = npages; - jbdata->w = w; - jbdata->h = h; - jbdata->nclass = nclass; - jbdata->latticew = cellw; - jbdata->latticeh = cellh; - jbdata->naclass = naclass; - jbdata->napage = napage; - jbdata->ptaul = ptaul; - - LEPT_FREE(data); - sarrayDestroy(&sa); - return jbdata; -} - - -/*! - * \brief jbDataRender() - * - * \param[in] jbdata - * \param[in] debugflag if TRUE, writes into 2 bpp pix and adds - * component outlines in color - * \return pixa reconstruction of original images, using templates or - * NULL on error - */ -PIXA * -jbDataRender(JBDATA *data, - l_int32 debugflag) -{ -l_int32 i, w, h, cellw, cellh, x, y, iclass, ipage; -l_int32 npages, nclass, ncomp, wp, hp; -BOX *box; -NUMA *naclass, *napage; -PIX *pixt, *pixt2, *pix, *pixd; -PIXA *pixat; /* pixa of templates */ -PIXA *pixad; /* pixa of output images */ -PIXCMAP *cmap; -PTA *ptaul; - - PROCNAME("jbDataRender"); - - if (!data) - return (PIXA *)ERROR_PTR("data not defined", procName, NULL); - - npages = data->npages; - w = data->w; - h = data->h; - pixt = data->pix; - nclass = data->nclass; - cellw = data->latticew; - cellh = data->latticeh; - naclass = data->naclass; - napage = data->napage; - ptaul = data->ptaul; - ncomp = numaGetCount(naclass); - - /* Reconstruct the original set of images from the templates - * and the data associated with each component. First, - * generate the output pixa as a set of empty pix. */ - if ((pixad = pixaCreate(npages)) == NULL) - return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); - for (i = 0; i < npages; i++) { - if (debugflag == FALSE) { - pix = pixCreate(w, h, 1); - } else { - pix = pixCreate(w, h, 2); - cmap = pixcmapCreate(2); - pixcmapAddColor(cmap, 255, 255, 255); - pixcmapAddColor(cmap, 0, 0, 0); - pixcmapAddColor(cmap, 255, 0, 0); /* for box outlines */ - pixSetColormap(pix, cmap); - } - pixaAddPix(pixad, pix, L_INSERT); - } - - /* Put the class templates into a pixa. */ - if ((pixat = pixaCreateFromPix(pixt, nclass, cellw, cellh)) == NULL) { - pixaDestroy(&pixad); - return (PIXA *)ERROR_PTR("pixat not made", procName, NULL); - } - - /* Place each component in the right location on its page. */ - for (i = 0; i < ncomp; i++) { - numaGetIValue(napage, i, &ipage); - numaGetIValue(naclass, i, &iclass); - pix = pixaGetPix(pixat, iclass, L_CLONE); /* the template */ - wp = pixGetWidth(pix); - hp = pixGetHeight(pix); - ptaGetIPt(ptaul, i, &x, &y); - pixd = pixaGetPix(pixad, ipage, L_CLONE); /* the output page */ - if (debugflag == FALSE) { - pixRasterop(pixd, x, y, wp, hp, PIX_SRC | PIX_DST, pix, 0, 0); - } else { - pixt2 = pixConvert1To2Cmap(pix); - pixRasterop(pixd, x, y, wp, hp, PIX_SRC | PIX_DST, pixt2, 0, 0); - box = boxCreate(x, y, wp, hp); - pixRenderBoxArb(pixd, box, 1, 255, 0, 0); - pixDestroy(&pixt2); - boxDestroy(&box); - } - pixDestroy(&pix); /* the clone only */ - pixDestroy(&pixd); /* the clone only */ - } - - pixaDestroy(&pixat); - return pixad; -} - - -/*! - * \brief jbGetULCorners() - * - * \param[in] jbclasser - * \param[in] pixs full res image - * \param[in] boxa of c.c. bounding rectangles for this page - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This computes the ptaul field, which has the global UL corners, - * adjusted for each specific component, so that each component - * can be replaced by the template for its class and have the - * centroid in the template in the same position as the - * centroid of the original connected component. It is important - * that this be done properly to avoid a wavy baseline in the - * result. - * (2) The array fields ptac and ptact give the centroids of - * those components relative to the UL corner of each component. - * Here, we compute the difference in each component, round to - * nearest integer, and correct the box->x and box->y by - * the appropriate integral difference. - * (3) The templates and stored instances are all bordered. - *- */ -l_ok -jbGetULCorners(JBCLASSER *classer, - PIX *pixs, - BOXA *boxa) -{ -l_int32 i, baseindex, index, n, iclass, idelx, idely, x, y, dx, dy; -l_int32 *sumtab; -l_float32 x1, x2, y1, y2, delx, dely; -BOX *box; -NUMA *naclass; -PIX *pixt; -PTA *ptac, *ptact, *ptaul; - - PROCNAME("jbGetULCorners"); - - if (!classer) - return ERROR_INT("classer not defined", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - - n = boxaGetCount(boxa); - ptaul = classer->ptaul; - naclass = classer->naclass; - ptac = classer->ptac; - ptact = classer->ptact; - baseindex = classer->baseindex; /* num components before this page */ - sumtab = makePixelSumTab8(); - for (i = 0; i < n; i++) { - index = baseindex + i; - ptaGetPt(ptac, index, &x1, &y1); - numaGetIValue(naclass, index, &iclass); - ptaGetPt(ptact, iclass, &x2, &y2); - delx = x2 - x1; - dely = y2 - y1; - if (delx >= 0) - idelx = (l_int32)(delx + 0.5); - else - idelx = (l_int32)(delx - 0.5); - if (dely >= 0) - idely = (l_int32)(dely + 0.5); - else - idely = (l_int32)(dely - 0.5); - if ((box = boxaGetBox(boxa, i, L_CLONE)) == NULL) { - LEPT_FREE(sumtab); - return ERROR_INT("box not found", procName, 1); - } - boxGetGeometry(box, &x, &y, NULL, NULL); - - /* Get final increments dx and dy for best alignment */ - pixt = pixaGetPix(classer->pixat, iclass, L_CLONE); - finalPositioningForAlignment(pixs, x, y, idelx, idely, - pixt, sumtab, &dx, &dy); -/* if (i % 20 == 0) - lept_stderr("dx = %d, dy = %d\n", dx, dy); */ - ptaAddPt(ptaul, x - idelx + dx, y - idely + dy); - boxDestroy(&box); - pixDestroy(&pixt); - } - - LEPT_FREE(sumtab); - return 0; -} - - -/*! - * \brief jbGetLLCorners() - * - * \param[in] jbclasser - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This computes the ptall field, which has the global LL corners, - * adjusted for each specific component, so that each component - * can be replaced by the template for its class and have the - * centroid in the template in the same position as the - * centroid of the original connected component. It is important - * that this be done properly to avoid a wavy baseline in the result. - * (2) It is computed here from the corresponding UL corners, where - * the input templates and stored instances are all bordered. - * This should be done after all pages have been processed. - * (3) For proper substitution, the templates whose LL corners are - * placed in these locations must be UN-bordered. - * This is available for a realistic jbig2 encoder, which would - * (1) encode each template without a border, and (2) encode - * the position using the LL corner (rather than the UL - * corner) because the difference between y-values - * of successive instances is typically close to zero. - *- */ -l_ok -jbGetLLCorners(JBCLASSER *classer) -{ -l_int32 i, iclass, n, x1, y1, h; -NUMA *naclass; -PIX *pix; -PIXA *pixat; -PTA *ptaul, *ptall; - - PROCNAME("jbGetLLCorners"); - - if (!classer) - return ERROR_INT("classer not defined", procName, 1); - - ptaul = classer->ptaul; - naclass = classer->naclass; - pixat = classer->pixat; - - ptaDestroy(&classer->ptall); - n = ptaGetCount(ptaul); - ptall = ptaCreate(n); - classer->ptall = ptall; - - /* If the templates were bordered, we would add h - 1 to the UL - * corner y-value. However, because the templates to be used - * here have their borders removed, and the borders are - * JB_ADDED_PIXELS on each side, we add h - 1 - 2 * JB_ADDED_PIXELS - * to the UL corner y-value. */ - for (i = 0; i < n; i++) { - ptaGetIPt(ptaul, i, &x1, &y1); - numaGetIValue(naclass, i, &iclass); - pix = pixaGetPix(pixat, iclass, L_CLONE); - h = pixGetHeight(pix); - ptaAddPt(ptall, x1, y1 + h - 1 - 2 * JB_ADDED_PIXELS); - pixDestroy(&pix); - } - - return 0; -} - - -/*----------------------------------------------------------------------* - * Static helpers * - *----------------------------------------------------------------------*/ -/* When looking for similar matches we check templates whose size is +/- 2 in - * each direction. This involves 25 possible sizes. This array contains the - * offsets for each of those positions in a spiral pattern. There are 25 pairs - * of numbers in this array: even positions are x values. */ -static int two_by_two_walk[50] = { - 0, 0, - 0, 1, - -1, 0, - 0, -1, - 1, 0, - -1, 1, - 1, 1, - -1, -1, - 1, -1, - 0, -2, - 2, 0, - 0, 2, - -2, 0, - -1, -2, - 1, -2, - 2, -1, - 2, 1, - 1, 2, - -1, 2, - -2, 1, - -2, -1, - -2, -2, - 2, -2, - 2, 2, - -2, 2}; - - -/*! - * \brief findSimilarSizedTemplatesInit() - * - * \param[in] classer - * \param[in] pixs instance to be matched - * \return Allocated context to be used with findSimilar* - */ -static JBFINDCTX * -findSimilarSizedTemplatesInit(JBCLASSER *classer, - PIX *pixs) -{ -JBFINDCTX *state; - - state = (JBFINDCTX *)LEPT_CALLOC(1, sizeof(JBFINDCTX)); - state->w = pixGetWidth(pixs) - 2 * JB_ADDED_PIXELS; - state->h = pixGetHeight(pixs) - 2 * JB_ADDED_PIXELS; - state->classer = classer; - return state; -} - - -static void -findSimilarSizedTemplatesDestroy(JBFINDCTX **pstate) -{ -JBFINDCTX *state; - - PROCNAME("findSimilarSizedTemplatesDestroy"); - - if (pstate == NULL) { - L_WARNING("ptr address is null\n", procName); - return; - } - if ((state = *pstate) == NULL) - return; - - l_dnaDestroy(&state->dna); - LEPT_FREE(state); - *pstate = NULL; - return; -} - - -/*! - * \brief findSimilarSizedTemplatesNext() - * - * \param[in] state from findSimilarSizedTemplatesInit - * \return next template number, or -1 when finished - * - * We have a dna hash table that maps template area to a list of template - * numbers with that area. We wish to find similar sized templates, - * so we first look for templates with the same width and height, and - * then with width + 1, etc. This walk is guided by the - * two_by_two_walk array, above. - * - * We don't want to have to collect the whole list of templates first, - * because we hope to find a well-matching template quickly. So we - * keep the context for this walk in an explictit state structure, - * and this function acts like a generator. - */ -static l_int32 -findSimilarSizedTemplatesNext(JBFINDCTX *state) -{ -l_int32 desiredh, desiredw, size, templ; -PIX *pixt; - - while(1) { /* Continue the walk over step 'i' */ - if (state->i >= 25) { /* all done; didn't find a good match */ - return -1; - } - - desiredw = state->w + two_by_two_walk[2 * state->i]; - desiredh = state->h + two_by_two_walk[2 * state->i + 1]; - if (desiredh < 1 || desiredw < 1) { /* invalid size */ - state->i++; - continue; - } - - if (!state->dna) { - /* We have yet to start walking the array for the step 'i' */ - state->dna = l_dnaHashGetDna(state->classer->dahash, - (l_uint64)desiredh * desiredw, L_CLONE); - if (!state->dna) { /* nothing there */ - state->i++; - continue; - } - - state->n = 0; /* OK, we got a dna. */ - } - - /* Continue working on this dna */ - size = l_dnaGetCount(state->dna); - for ( ; state->n < size; ) { - templ = (l_int32)(state->dna->array[state->n++] + 0.5); - pixt = pixaGetPix(state->classer->pixat, templ, L_CLONE); - if (pixGetWidth(pixt) - 2 * JB_ADDED_PIXELS == desiredw && - pixGetHeight(pixt) - 2 * JB_ADDED_PIXELS == desiredh) { - pixDestroy(&pixt); - return templ; - } - pixDestroy(&pixt); - } - - /* Exhausted the dna (no match found); take another step and - * try again. */ - state->i++; - l_dnaDestroy(&state->dna); - continue; - } -} - - -/*! - * \brief finalPositioningForAlignment() - * - * \param[in] pixs input page image - * \param[in] x, y location of UL corner of bb of component in pixs - * \param[in] idelx, idely compensation to match centroids of component - * and template - * \param[in] pixt template, with JB_ADDED_PIXELS of padding - * on all sides - * \param[in] sumtab for summing fg pixels in an image - * \param[in] pdx, pdy return delta on position for best match; each - * one is in the set {-1, 0, 1} - * \return 0 if OK, 1 on error - * - */ -static l_int32 -finalPositioningForAlignment(PIX *pixs, - l_int32 x, - l_int32 y, - l_int32 idelx, - l_int32 idely, - PIX *pixt, - l_int32 *sumtab, - l_int32 *pdx, - l_int32 *pdy) -{ -l_int32 w, h, i, j, minx, miny, count, mincount; -PIX *pixi; /* clipped from source pixs */ -PIX *pixr; /* temporary storage */ -BOX *box; - - PROCNAME("finalPositioningForAlignment"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!pixt) - return ERROR_INT("pixt not defined", procName, 1); - if (!pdx || !pdy) - return ERROR_INT("&dx and &dy not both defined", procName, 1); - if (!sumtab) - return ERROR_INT("sumtab not defined", procName, 1); - *pdx = *pdy = 0; - - /* Use JB_ADDED_PIXELS pixels padding on each side */ - pixGetDimensions(pixt, &w, &h, NULL); - box = boxCreate(x - idelx - JB_ADDED_PIXELS, - y - idely - JB_ADDED_PIXELS, w, h); - pixi = pixClipRectangle(pixs, box, NULL); - boxDestroy(&box); - if (!pixi) - return ERROR_INT("pixi not made", procName, 1); - - pixr = pixCreate(pixGetWidth(pixi), pixGetHeight(pixi), 1); - mincount = 0x7fffffff; - for (i = -1; i <= 1; i++) { - for (j = -1; j <= 1; j++) { - pixCopy(pixr, pixi); - pixRasterop(pixr, j, i, w, h, PIX_SRC ^ PIX_DST, pixt, 0, 0); - pixCountPixels(pixr, &count, sumtab); - if (count < mincount) { - minx = j; - miny = i; - mincount = count; - } - } - } - pixDestroy(&pixi); - pixDestroy(&pixr); - - *pdx = minx; - *pdy = miny; - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/jbclass.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/jbclass.h deleted file mode 100644 index 62aad60a..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/jbclass.h +++ /dev/null @@ -1,142 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_JBCLASS_H -#define LEPTONICA_JBCLASS_H - -/*! - * \file jbclass.h - * - * JbClasser - * JbData - */ - - - /*! - *
- * The JbClasser struct holds all the data accumulated during the - * classification process that can be used for a compressed - * jbig2-type representation of a set of images. This is created - * in an initialization process and added to as the selected components - * on each successive page are analyzed. - *- */ -struct JbClasser -{ - struct Sarray *safiles; /*!< input page image file names */ - l_int32 method; /*!< JB_RANKHAUS, JB_CORRELATION */ - l_int32 components; /*!< JB_CONN_COMPS, JB_CHARACTERS or */ - /*!< JB_WORDS */ - l_int32 maxwidth; /*!< max component width allowed */ - l_int32 maxheight; /*!< max component height allowed */ - l_int32 npages; /*!< number of pages already processed */ - l_int32 baseindex; /*!< number components already processed */ - /*!< on fully processed pages */ - struct Numa *nacomps; /*!< number of components on each page */ - l_int32 sizehaus; /*!< size of square struct elem for haus */ - l_float32 rankhaus; /*!< rank val of haus match, each way */ - l_float32 thresh; /*!< thresh value for correlation score */ - l_float32 weightfactor; /*!< corrects thresh value for heaver */ - /*!< components; use 0 for no correction */ - struct Numa *naarea; /*!< w * h of each template, without */ - /*!< extra border pixels */ - l_int32 w; /*!< max width of original src images */ - l_int32 h; /*!< max height of original src images */ - l_int32 nclass; /*!< current number of classes */ - l_int32 keep_pixaa; /*!< If zero, pixaa isn't filled */ - struct Pixaa *pixaa; /*!< instances for each class; unbordered */ - struct Pixa *pixat; /*!< templates for each class; bordered */ - /*!< and not dilated */ - struct Pixa *pixatd; /*!< templates for each class; bordered */ - /*!< and dilated */ - struct L_DnaHash *dahash; /*!< Hash table to find templates by size */ - struct Numa *nafgt; /*!< fg areas of undilated templates; */ - /*!< only used for rank < 1.0 */ - struct Pta *ptac; /*!< centroids of all bordered cc */ - struct Pta *ptact; /*!< centroids of all bordered template cc */ - struct Numa *naclass; /*!< array of class ids for each component */ - struct Numa *napage; /*!< array of page nums for each component */ - struct Pta *ptaul; /*!< array of UL corners at which the */ - /*!< template is to be placed for each */ - /*!< component */ - struct Pta *ptall; /*!< similar to ptaul, but for LL corners */ -}; -typedef struct JbClasser JBCLASSER; - - - /*! - *
- * The JbData struct holds all the data required for - * the compressed jbig-type representation of a set of images. - * The data can be written to file, read back, and used - * to regenerate an approximate version of the original, - * which differs in two ways from the original: - * (1) It uses a template image for each c.c. instead of the - * original instance, for each occurrence on each page. - * (2) It discards components with either a height or width larger - * than the maximuma, given here by the lattice dimensions - * used for storing the templates. - *- */ -struct JbData -{ - struct Pix *pix; /*!< template composite for all classes */ - l_int32 npages; /*!< number of pages */ - l_int32 w; /*!< max width of original page images */ - l_int32 h; /*!< max height of original page images */ - l_int32 nclass; /*!< number of classes */ - l_int32 latticew; /*!< lattice width for template composite */ - l_int32 latticeh; /*!< lattice height for template composite */ - struct Numa *naclass; /*!< array of class ids for each component */ - struct Numa *napage; /*!< array of page nums for each component */ - struct Pta *ptaul; /*!< array of UL corners at which the */ - /*!< template is to be placed for each */ - /*!< component */ -}; -typedef struct JbData JBDATA; - - -/*! JB Classifier */ -enum { - JB_RANKHAUS = 0, - JB_CORRELATION = 1 -}; - - /*! For jbGetComponents(): type of component to extract from images */ -/*! JB Component */ -enum { - JB_CONN_COMPS = 0, - JB_CHARACTERS = 1, - JB_WORDS = 2 -}; - - /*! These parameters are used for naming the two files - * in which the jbig2-like compressed data is stored. */ -#define JB_TEMPLATE_EXT ".templates.png" -#define JB_DATA_EXT ".data" - - -#endif /* LEPTONICA_JBCLASS_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/jp2kheader.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/jp2kheader.c deleted file mode 100644 index 9fb328b8..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/jp2kheader.c +++ /dev/null @@ -1,316 +0,0 @@ -/*====================================================================* - - 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 jp2kheader.c - *
- * - * Read header - * l_int32 readHeaderJp2k() - * l_int32 freadHeaderJp2k() - * l_int32 readHeaderMemJp2k() - * l_int32 fgetJp2kResolution() - * - * Note: these function read image metadata from a jp2k file, without - * using any jp2k libraries. - * - * To read and write jp2k data, using the OpenJPEG library - * (http://www.openjpeg.org), see jpegio.c. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) The ISO/IEC reference for jpeg2000 is - * http://www.jpeg.org/public/15444-1annexi.pdf - * and the file format syntax begins at page 127. - * (2) The Image Header Box begins with 'ihdr' = 0x69686472 in - * big-endian order. This typically, but not always, starts - * byte 44, with the big-endian data fields beginning at byte 48: - * h: 4 bytes - * w: 4 bytes - * spp: 2 bytes - * bps: 1 byte (contains bps - 1) - *- */ -l_ok -readHeaderMemJp2k(const l_uint8 *data, - size_t size, - l_int32 *pw, - l_int32 *ph, - l_int32 *pbps, - l_int32 *pspp) -{ -l_int32 format, val, w, h, bps, spp, loc, found, windex; -l_uint8 ihdr[4] = {0x69, 0x68, 0x64, 0x72}; /* 'ihdr' */ - - PROCNAME("readHeaderMemJp2k"); - - if (pw) *pw = 0; - if (ph) *ph = 0; - if (pbps) *pbps = 0; - if (pspp) *pspp = 0; - if (!data) - return ERROR_INT("data not defined", procName, 1); - if (size < 80) - return ERROR_INT("size < 80", procName, 1); - findFileFormatBuffer(data, &format); - if (format != IFF_JP2) - return ERROR_INT("not jp2 file", procName, 1); - - /* Search for beginning of the Image Header Box: 'ihdr' */ - arrayFindSequence(data, size, ihdr, 4, &loc, &found); - if (!found) - return ERROR_INT("image parameters not found", procName, 1); -#if DEBUG_IHDR - if (loc != 44) - L_INFO("Beginning of ihdr is at byte %d\n", procName, loc); -#endif /* DEBUG_IHDR */ - - windex = loc / 4 + 1; - if (4 * (windex + 2) + 2 >= size) - return ERROR_INT("image parameters end are outside of header", - procName, 1); - val = *((l_uint32 *)data + windex); - h = convertOnLittleEnd32(val); - val = *((l_uint32 *)data + windex + 1); - w = convertOnLittleEnd32(val); - val = *((l_uint16 *)data + 2 * (windex + 2)); - spp = convertOnLittleEnd16(val); - bps = *(data + 4 * (windex + 2) + 2) + 1; - if (w < 1 || h < 1) - return ERROR_INT("w and h must both be > 0", procName, 1); - if (w > MAX_JP2K_WIDTH || h > MAX_JP2K_HEIGHT) - return ERROR_INT("unrealistically large sizes", procName, 1); - if (spp != 1 && spp != 3 && spp != 4) - return ERROR_INT("spp must be in 1, 3 or 4", procName, 1); - if (bps != 8 && bps != 16) - return ERROR_INT("bps must be 8 or 16", procName, 1); - if (pw) *pw = w; - if (ph) *ph = h; - if (pspp) *pspp = spp; - if (pbps) *pbps = bps; - return 0; -} - - -/* - * fgetJp2kResolution() - * - * Input: fp (file stream opened for read) - * &xres, &yres (
- * - * Stubs for jp2kheader.c functions - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * - * Read jp2k from file - * PIX *pixReadJp2k() [special top level] - * PIX *pixReadStreamJp2k() - * - * Write jp2k to file - * l_int32 pixWriteJp2k() [special top level] - * l_int32 pixWriteStreamJp2k() - * static opj_image_t *pixConvertToOpjImage() - * - * Read/write to memory - * PIX *pixReadMemJp2k() - * l_int32 pixWriteMemJp2k() - * - * Static functions from opj 2.0 to retain file stream interface - * static opj_stream_t *opjCreateStream() - * [other static helpers] - * - * Based on the OpenJPEG distribution: - * http://www.openjpeg.org/ - * The ISO/IEC reference for jpeg2000 is: - * http://www.jpeg.org/public/15444-1annexi.pdf - * - * Compressing to memory and decompressing from memory - * --------------------------------------------------- - * On systems like windows without fmemopen() and open_memstream(), - * we write data to a temp file and read it back for operations - * between pix and compressed-data, such as pixReadMemJp2k() and - * pixWriteMemJp2k(). - * - * Pdf can accept jp2k compressed strings directly - * ----------------------------------------------- - * Transcoding (with the uncompress/compress cycle) is not required - * to wrap images that have already been compressed with jp2k in pdf, - * because the pdf format for jp2k includes the full string of the - * jp2k compressed images. This is also true for jpeg compressed - * strings. - * - * N.B. - * * This is based on the most recent openjpeg release: 2.1. - * * The openjpeg interface was massively changed from 1.X. The debian - * distribution is way back at 1.3. We have inquired but are unable - * to determine if or when a debian distribution will be built for 2.1. - * * For version 2.1, the openjpeg.h file is installed in an - * openjpeg-2.1 subdirectory, which is hard to support. - * * In openjpeg-2.1, reading is slow compared to jpeg or webp, - * and writing is very slow compared to jpeg or webp. This is expected - * to improve significantly in future versions. - * * Reading and writing jp2k are supported here for 2.1. - * The high-level interface to openjpeg continues to change. - * From 2.0 to 2.1, the ability to interface to a C file stream - * was removed permanently. Leptonica supports both file stream - * and memory buffer interfaces for every image I/O library, and - * it requires the libraries to support at least one of these. - * However, openjpeg-2.1 provides neither, so we have brought - * several static functions over from openjpeg-2.0 in order to - * retain the file stream interface. See our static function - * opjCreateStream(). - * * Specifying a quality factor for jpeg2000 requires caution. Unlike - * jpeg and webp, which have a sensible scale that goes from 0 (very poor) - * to 100 (nearly lossless), kakadu and openjpeg use idiosyncratic and - * non-intuitive numbers. kakadu uses "rate/distortion" numbers in - * a narrow range around 50,000; openjpeg (and our write interface) - * use SNR. The visually apparent artifacts introduced by compression - * are strongly content-dependent and vary in a highly non-linear - * way with SNR. We take SNR = 34 as default, roughly similar in - * quality to jpeg's default standard of 75. For document images, - * SNR = 25 is very poor, whereas SNR = 45 is nearly lossless. If you - * use the latter, you will pay dearly in the size of the compressed file. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This is a special function for reading jp2k files. - * The high-level pixReadStream() uses default values: - * %reduction = 1 - * %box = NULL - * (2) This decodes at either full resolution or at a reduction by - * a power of 2. The default value %reduction == 1 gives a full - * resolution image. Use %reduction > 1 to get a reduced image. - * The actual values of %reduction that can be used on an image - * depend on the number of resolution levels chosen when the - * image was compressed. Typical values might be 1, 2, 4, 8 and 16. - * Using a value representing a reduction level that was not - * stored when the file was written will fail with the message: - * "failed to read the header". - * (3) Use %box to decode only a part of the image. The box is defined - * at full resolution. It is reduced internally by %reduction, - * and clipping to the right and bottom of the image is automatic. - * (4) We presently only handle images with 8 bits/sample (bps). - * If the image has 16 bps, the read will fail. - * (5) There are 4 possible values of samples/pixel (spp). - * The values in brackets give the pixel values in the Pix: - * spp = 1 ==> grayscale [8 bpp grayscale] - * spp = 2 ==> grayscale + alpha [32 bpp rgba] - * spp = 3 ==> rgb [32 bpp rgb] - * spp = 4 ==> rgba [32 bpp rgba] - * (6) The %hint parameter is reserved for future use. - *- */ -PIX * -pixReadJp2k(const char *filename, - l_uint32 reduction, - BOX *box, - l_int32 hint, - l_int32 debug) -{ -FILE *fp; -PIX *pix; - - PROCNAME("pixReadJp2k"); - - if (!filename) - return (PIX *)ERROR_PTR("filename not defined", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (PIX *)ERROR_PTR("image file not found", procName, NULL); - pix = pixReadStreamJp2k(fp, reduction, box, hint, debug); - fclose(fp); - - if (!pix) - return (PIX *)ERROR_PTR("image not returned", procName, NULL); - return pix; -} - - -/*! - * \brief pixReadStreamJp2k() - * - * \param[in] fp file stream - * \param[in] reduction scaling factor: 1, 2, 4, 8 - * \param[in] box [optional] for extracting a subregion, can be null - * \param[in] hint a bitwise OR of L_JP2K_* values; 0 for default - * \param[in] debug output callback messages, etc - * \return pix 8 or 32 bpp, or NULL on error - * - *
- * Notes: - * (1) See pixReadJp2k() for usage. - *- */ -PIX * -pixReadStreamJp2k(FILE *fp, - l_uint32 reduction, - BOX *box, - l_int32 hint, - l_int32 debug) -{ -const char *opjVersion; -l_int32 i, j, index, bx, by, bw, bh, val, rval, gval, bval, aval; -l_int32 w, h, wpl, bps, spp, xres, yres, reduce, prec, colorspace; -l_uint32 pixel; -l_uint32 *data, *line; -opj_dparameters_t parameters; /* decompression parameters */ -opj_image_t *image = NULL; -opj_codec_t *l_codec = NULL; /* handle to decompressor */ -opj_stream_t *l_stream = NULL; /* opj stream */ -PIX *pix = NULL; - - PROCNAME("pixReadStreamJp2k"); - - if (!fp) - return (PIX *)ERROR_PTR("fp not defined", procName, NULL); - - opjVersion = opj_version(); - if (opjVersion[0] != '2') { - L_ERROR("version is %s; must be 2.0 or higher\n", procName, opjVersion); - return NULL; - } - if ((opjVersion[2] - 0x30) != OPJ_VERSION_MINOR) { - L_ERROR("version %s: differs from minor = %d\n", - procName, opjVersion, OPJ_VERSION_MINOR); - return NULL; - } - - /* Get the resolution and the bits/sample */ - rewind(fp); - fgetJp2kResolution(fp, &xres, &yres); - freadHeaderJp2k(fp, NULL, NULL, &bps, NULL); - rewind(fp); - - if (bps > 8) { - L_ERROR("found %d bps; can only handle 8 bps\n", procName, bps); - return NULL; - } - - /* Set decoding parameters to default values */ - opj_set_default_decoder_parameters(¶meters); - - /* Find and set the reduce parameter, which is log2(reduction). - * Valid reductions are powers of 2, and are determined when the - * compressed string is made. A request for an invalid reduction - * will cause an error in opj_read_header(), and no image will - * be returned. */ - for (reduce = 0; (1L << reduce) < reduction; reduce++) { } - if ((1L << reduce) != reduction) { - L_ERROR("invalid reduction %d; not power of 2\n", procName, reduction); - return NULL; - } - parameters.cp_reduce = reduce; - - /* Get a decoder handle */ - if ((l_codec = opj_create_decompress(OPJ_CODEC_JP2)) == NULL) { - L_ERROR("failed to make the codec\n", procName); - return NULL; - } - - /* Catch and report events using callbacks */ - if (debug) { - opj_set_info_handler(l_codec, info_callback, NULL); - opj_set_warning_handler(l_codec, warning_callback, NULL); - opj_set_error_handler(l_codec, error_callback, NULL); - } - - /* Setup the decoding parameters using user parameters */ - if (!opj_setup_decoder(l_codec, ¶meters)){ - L_ERROR("failed to set up decoder\n", procName); - opj_destroy_codec(l_codec); - return NULL; - } - - /* Open decompression 'stream'. In 2.0, we could call this: - * opj_stream_create_default_file_stream(fp, 1) - * but the file stream interface was removed in 2.1. */ - if ((l_stream = opjCreateStream(fp, 1)) == NULL) { - L_ERROR("failed to open the stream\n", procName); - opj_destroy_codec(l_codec); - return NULL; - } - - /* Read the main header of the codestream and, if necessary, - * the JP2 boxes */ - if(!opj_read_header(l_stream, l_codec, &image)){ - L_ERROR("failed to read the header\n", procName); - opj_stream_destroy(l_stream); - opj_destroy_codec(l_codec); - opj_image_destroy(image); - return NULL; - } - - /* Set up to decode a rectangular region */ - if (box) { - boxGetGeometry(box, &bx, &by, &bw, &bh); - if (!opj_set_decode_area(l_codec, image, bx, by, - bx + bw, by + bh)) { - L_ERROR("failed to set the region for decoding\n", procName); - opj_stream_destroy(l_stream); - opj_destroy_codec(l_codec); - opj_image_destroy(image); - return NULL; - } - } - - /* Get the decoded image */ - if (!(opj_decode(l_codec, l_stream, image) && - opj_end_decompress(l_codec, l_stream))) { - L_ERROR("failed to decode the image\n", procName); - opj_destroy_codec(l_codec); - opj_stream_destroy(l_stream); - opj_image_destroy(image); - return NULL; - } - - /* Finished with the byte stream and the codec */ - opj_stream_destroy(l_stream); - opj_destroy_codec(l_codec); - - /* Get the image parameters */ - spp = image->numcomps; - w = image->comps[0].w; - h = image->comps[0].h; - prec = image->comps[0].prec; - if (prec != bps) - L_WARNING("precision %d != bps %d!\n", procName, prec, bps); - if (debug) { - L_INFO("w = %d, h = %d, bps = %d, spp = %d\n", - procName, w, h, bps, spp); - colorspace = image->color_space; - if (colorspace == OPJ_CLRSPC_SRGB) - L_INFO("colorspace is sRGB\n", procName); - else if (colorspace == OPJ_CLRSPC_GRAY) - L_INFO("colorspace is grayscale\n", procName); - else if (colorspace == OPJ_CLRSPC_SYCC) - L_INFO("colorspace is YUV\n", procName); - } - - /* Convert the image to a pix */ - if (spp == 1) - pix = pixCreate(w, h, 8); - else - pix = pixCreate(w, h, 32); - pixSetInputFormat(pix, IFF_JP2); - pixSetResolution(pix, xres, yres); - data = pixGetData(pix); - wpl = pixGetWpl(pix); - index = 0; - if (spp == 1) { - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - val = image->comps[0].data[index]; - SET_DATA_BYTE(line, j, val); - index++; - } - } - } else if (spp == 2) { /* convert to RGBA */ - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - val = image->comps[0].data[index]; - aval = image->comps[1].data[index]; - composeRGBAPixel(val, val, val, aval, &pixel); - line[j] = pixel; - index++; - } - } - } else if (spp >= 3) { - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - rval = image->comps[0].data[index]; - gval = image->comps[1].data[index]; - bval = image->comps[2].data[index]; - if (spp == 3) { - composeRGBPixel(rval, gval, bval, &pixel); - } else { /* spp == 4 */ - aval = image->comps[3].data[index]; - composeRGBAPixel(rval, gval, bval, aval, &pixel); - } - line[j] = pixel; - index++; - } - } - } - - /* Free the opj image data structure */ - opj_image_destroy(image); - - return pix; -} - - -/*---------------------------------------------------------------------* - * Write jp2k to file * - *---------------------------------------------------------------------*/ -/*! - * \brief pixWriteJp2k() - * - * \param[in] filename - * \param[in] pix any depth, cmap is OK - * \param[in] quality SNR > 0; 0 for default (34); 100 for lossless - * \param[in] nlevels resolution levels; <= 10; default = 5 - * \param[in] hint a bitwise OR of L_JP2K_* values; 0 for default - * \param[in] debug output callback messages, etc - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) The %quality parameter is the SNR. The useful range is narrow: - * SNR < 27 (terrible quality) - * SNR = 34 (default; approximately equivalent to jpeg quality 75) - * SNR = 40 (very high quality) - * SNR = 45 (nearly lossless) - * Use 0 for default; 100 for lossless. - * (2) The %nlevels parameter is the number of resolution levels - * to be written. For example, with nlevels == 5, images with - * reduction factors of 1, 2, 4, 8 and 16 are encoded, and retrieval - * is done at the level requested when reading. For default, - * use either 5 or 0. - * (3) The %hint parameter is not yet in use. - * (4) For now, we only support 1 "layer" for quality. - *- */ -l_ok -pixWriteJp2k(const char *filename, - PIX *pix, - l_int32 quality, - l_int32 nlevels, - l_int32 hint, - l_int32 debug) -{ -FILE *fp; - - PROCNAME("pixWriteJp2k"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "wb+")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - - if (pixWriteStreamJp2k(fp, pix, quality, nlevels, hint, debug)) { - fclose(fp); - return ERROR_INT("pix not written to stream", procName, 1); - } - - fclose(fp); - return 0; -} - - -/*! - * \brief pixWriteStreamJp2k() - * - * \param[in] fp file stream - * \param[in] pix any depth, cmap is OK - * \param[in] quality SNR > 0; 0 for default (34); 100 for lossless - * \param[in] nlevels <= 10 - * \param[in] hint a bitwise OR of L_JP2K_* values; 0 for default - * \param[in] debug output callback messages, etc - * \return 0 if OK, 1 on error - *
- * Notes: - * (1) See pixWriteJp2k() for usage. - * (2) For an encoder with more encoding options, see, e.g., - * https://github.com/OpenJPEG/openjpeg/blob/master/tests/test_tile_encoder.c - *- */ -l_ok -pixWriteStreamJp2k(FILE *fp, - PIX *pix, - l_int32 quality, - l_int32 nlevels, - l_int32 hint, - l_int32 debug) -{ -l_int32 w, h, d, success; -l_float32 snr; -const char *opjVersion; -PIX *pixs; -opj_cparameters_t parameters; /* compression parameters */ -opj_stream_t *l_stream = NULL; -opj_codec_t* l_codec = NULL;; -opj_image_t *image = NULL; - - PROCNAME("pixWriteStreamJp2k"); - - if (!fp) - return ERROR_INT("stream not open", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - snr = (l_float32)quality; - if (snr <= 0) snr = 34.0; /* default */ - if (snr < 27) - L_WARNING("SNR = %d < 27; very low\n", procName, (l_int32)snr); - if (snr == 100) snr = 0; /* for lossless */ - if (snr > 45) { - L_WARNING("SNR > 45; using lossless encoding\n", procName); - snr = 0; - } - - if (nlevels <= 0) nlevels = 5; /* default */ - if (nlevels > 10) { - L_WARNING("nlevels = %d > 10; setting to 10\n", procName, nlevels); - nlevels = 10; - } - - opjVersion = opj_version(); - if (opjVersion[0] != '2') { - L_ERROR("version is %s; must be 2.0 or higher\n", procName, opjVersion); - return 1; - } - if ((opjVersion[2] - 0x30) != OPJ_VERSION_MINOR) { - L_ERROR("version %s: differs from minor = %d\n", - procName, opjVersion, OPJ_VERSION_MINOR); - return 1; - } - - /* Remove colormap if it exists; result is 8 or 32 bpp */ - pixGetDimensions(pix, &w, &h, &d); - if (d == 24) { - pixs = pixConvert24To32(pix); - } else if (d == 32) { - pixs = pixClone(pix); - } else if (pixGetColormap(pix) == NULL) { - pixs = pixConvertTo8(pix, 0); - } else { /* colormap */ - L_INFO("removing colormap; may be better to compress losslessly\n", - procName); - pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); - } - - /* Convert to opj image format. */ - pixSetPadBits(pixs, 0); - image = pixConvertToOpjImage(pixs); - pixDestroy(&pixs); - - /* Set encoding parameters to default values. - * We use one layer with the input SNR. */ - opj_set_default_encoder_parameters(¶meters); - parameters.cp_fixed_quality = 1; - parameters.cp_disto_alloc = 0; - parameters.cp_fixed_alloc = 0; - parameters.tcp_distoratio[0] = snr; - parameters.tcp_numlayers = 1; - parameters.numresolution = nlevels + 1; - - /* Create comment for codestream */ - if (parameters.cp_comment == NULL) { - const char comment1[] = "Created by Leptonica, version "; - const char comment2[] = "; using OpenJPEG, version "; - size_t len1 = strlen(comment1); - size_t len2 = strlen(comment2); - char *version1 = getLeptonicaVersion(); - const char *version2 = opj_version(); - len1 += len2 + strlen(version1) + strlen(version2) + 1; - parameters.cp_comment = (char *)LEPT_MALLOC(len1); - snprintf(parameters.cp_comment, len1, "%s%s%s%s", comment1, version1, - comment2, version2); - LEPT_FREE(version1); - } - - /* Get the encoder handle */ - if ((l_codec = opj_create_compress(OPJ_CODEC_JP2)) == NULL) { - opj_image_destroy(image); - LEPT_FREE(parameters.cp_comment); - return ERROR_INT("failed to get the encoder handle\n", procName, 1); - } - - /* Catch and report events using callbacks */ - if (debug) { - opj_set_info_handler(l_codec, info_callback, NULL); - opj_set_warning_handler(l_codec, warning_callback, NULL); - opj_set_error_handler(l_codec, error_callback, NULL); - } - - /* Set up the encoder */ - if (!opj_setup_encoder(l_codec, ¶meters, image)) { - opj_destroy_codec(l_codec); - opj_image_destroy(image); - LEPT_FREE(parameters.cp_comment); - return ERROR_INT("failed to set up the encoder\n", procName, 1); - } - - /* Open a compression stream for writing. In 2.0 we could use this: - * opj_stream_create_default_file_stream(fp, 0) - * but the file stream interface was removed in 2.1. */ - rewind(fp); - if ((l_stream = opjCreateStream(fp, 0)) == NULL) { - opj_destroy_codec(l_codec); - opj_image_destroy(image); - LEPT_FREE(parameters.cp_comment); - return ERROR_INT("failed to open l_stream\n", procName, 1); - } - - /* Encode the image */ - if (!opj_start_compress(l_codec, image, l_stream)) { - opj_stream_destroy(l_stream); - opj_destroy_codec(l_codec); - opj_image_destroy(image); - LEPT_FREE(parameters.cp_comment); - return ERROR_INT("opj_start_compress failed\n", procName, 1); - } - if (!opj_encode(l_codec, l_stream)) { - opj_stream_destroy(l_stream); - opj_destroy_codec(l_codec); - opj_image_destroy(image); - LEPT_FREE(parameters.cp_comment); - return ERROR_INT("opj_encode failed\n", procName, 1); - } - success = opj_end_compress(l_codec, l_stream); - - /* Clean up */ - opj_stream_destroy(l_stream); - opj_destroy_codec(l_codec); - opj_image_destroy(image); - LEPT_FREE(parameters.cp_comment); - if (success) - return 0; - else - return ERROR_INT("opj_end_compress failed\n", procName, 1); -} - - -/*! - * \brief pixConvertToOpjImage() - * - * \param[in] pix 8 or 32 bpp - * \return opj_image, or NULL on error - * - *
- * Notes: - * (1) Input pix is 8 bpp grayscale, 32 bpp rgb, or 32 bpp rgba. - * (2) Gray + alpha pix are all represented as rgba. - *- */ -static opj_image_t * -pixConvertToOpjImage(PIX *pix) -{ -l_int32 i, j, k, w, h, d, spp, wpl; -OPJ_COLOR_SPACE colorspace; -l_int32 *ir = NULL; -l_int32 *ig = NULL; -l_int32 *ib = NULL; -l_int32 *ia = NULL; -l_uint32 *line, *data; -opj_image_t *image; -opj_image_cmptparm_t cmptparm[4]; - - PROCNAME("pixConvertToOpjImage"); - - if (!pix) - return (opj_image_t *)ERROR_PTR("pix not defined", procName, NULL); - pixGetDimensions(pix, &w, &h, &d); - if (d != 8 && d != 32) { - L_ERROR("invalid depth: %d\n", procName, d); - return NULL; - } - - /* Allocate the opj_image. */ - spp = pixGetSpp(pix); - memset(&cmptparm[0], 0, 4 * sizeof(opj_image_cmptparm_t)); - for (i = 0; i < spp; i++) { - cmptparm[i].prec = 8; - cmptparm[i].bpp = 8; - cmptparm[i].sgnd = 0; - cmptparm[i].dx = 1; - cmptparm[i].dy = 1; - cmptparm[i].w = w; - cmptparm[i].h = h; - } - colorspace = (spp == 1) ? OPJ_CLRSPC_GRAY : OPJ_CLRSPC_SRGB; - if ((image = opj_image_create(spp, &cmptparm[0], colorspace)) == NULL) - return (opj_image_t *)ERROR_PTR("image not made", procName, NULL); - image->x0 = 0; - image->y0 = 0; - image->x1 = w; - image->y1 = h; - - /* Set the component pointers */ - ir = image->comps[0].data; - if (spp > 1) { - ig = image->comps[1].data; - ib = image->comps[2].data; - } - if(spp == 4) - ia = image->comps[3].data; - - /* Transfer the data from the pix */ - data = pixGetData(pix); - wpl = pixGetWpl(pix); - for (i = 0, k = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++, k++) { - if (spp == 1) { - ir[k] = GET_DATA_BYTE(line, j); - } else if (spp > 1) { - ir[k] = GET_DATA_BYTE(line + j, COLOR_RED); - ig[k] = GET_DATA_BYTE(line + j, COLOR_GREEN); - ib[k] = GET_DATA_BYTE(line + j, COLOR_BLUE); - } - if (spp == 4) - ia[k] = GET_DATA_BYTE(line + j, L_ALPHA_CHANNEL); - } - } - - return image; -} - - -/*---------------------------------------------------------------------* - * Read/write to memory * - *---------------------------------------------------------------------*/ -/*! - * \brief pixReadMemJp2k() - * - * \param[in] data const; jpeg-encoded - * \param[in] size of data - * \param[in] reduction scaling factor: 1, 2, 4, 8 - * \param[in] box [optional] for extracting a subregion, can be null - * \param[in] hint a bitwise OR of L_JP2K_* values; 0 for default - * \param[in] debug output callback messages, etc - * \return pix, or NULL on error - * - *
- * Notes: - * (1) This crashes when reading through the fmemopen cookie. - * Until we can fix this, we use the file-based work-around. - * And fixing this may take some time, because the basic - * stream interface is no longer supported in openjpeg. - * (2) See pixReadJp2k() for usage. - *- */ -PIX * -pixReadMemJp2k(const l_uint8 *data, - size_t size, - l_uint32 reduction, - BOX *box, - l_int32 hint, - l_int32 debug) -{ -FILE *fp; -PIX *pix; - - PROCNAME("pixReadMemJp2k"); - - if (!data) - return (PIX *)ERROR_PTR("data not defined", procName, NULL); - - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return (PIX *)ERROR_PTR("stream not opened", procName, NULL); - pix = pixReadStreamJp2k(fp, reduction, box, hint, debug); - fclose(fp); - if (!pix) L_ERROR("pix not read\n", procName); - return pix; -} - - -/*! - * \brief pixWriteMemJp2k() - * - * \param[out] pdata data of jpeg compressed image - * \param[out] psize size of returned data - * \param[in] pix 8 or 32 bpp - * \param[in] quality SNR > 0; 0 for default (34); 100 for lossless - * \param[in] nlevels 0 for default - * \param[in] hint a bitwise OR of L_JP2K_* values; 0 for default - * \param[in] debug output callback messages, etc - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) See pixWriteJp2k() for usage. This version writes to - * memory instead of to a file stream. - *- */ -l_ok -pixWriteMemJp2k(l_uint8 **pdata, - size_t *psize, - PIX *pix, - l_int32 quality, - l_int32 nlevels, - l_int32 hint, - l_int32 debug) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("pixWriteMemJp2k"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1 ); - if (!psize) - return ERROR_INT("&size not defined", procName, 1 ); - if (!pix) - return ERROR_INT("&pix not defined", procName, 1 ); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = pixWriteStreamJp2k(fp, pix, quality, nlevels, hint, debug); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = pixWriteStreamJp2k(fp, pix, quality, nlevels, hint, debug); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} - - -/*---------------------------------------------------------------------* - * Static functions from opj 2.0 to retain file stream interface * - *---------------------------------------------------------------------*/ -static l_uint64 -opj_get_user_data_length(FILE *fp) { - OPJ_OFF_T length = 0; - fseek(fp, 0, SEEK_END); - length = (OPJ_OFF_T)ftell(fp); - fseek(fp, 0, SEEK_SET); - return (l_uint64)length; -} - -static OPJ_SIZE_T -opj_read_from_file(void *p_buffer, OPJ_SIZE_T p_nb_bytes, FILE *fp) { - OPJ_SIZE_T l_nb_read = fread(p_buffer, 1, p_nb_bytes, fp); - return l_nb_read ? l_nb_read : (OPJ_SIZE_T) - 1; -} - -static OPJ_SIZE_T -opj_write_from_file(void *p_buffer, OPJ_SIZE_T p_nb_bytes, FILE *fp) -{ - return fwrite(p_buffer, 1, p_nb_bytes, fp); -} - -static OPJ_OFF_T -opj_skip_from_file(OPJ_OFF_T offset, FILE *fp) { - if (fseek(fp, offset, SEEK_CUR)) { - return -1; - } - return offset; -} - -static l_int32 -opj_seek_from_file(OPJ_OFF_T offset, FILE *fp) { - if (fseek(fp, offset, SEEK_SET)) { - return 0; - } - return 1; -} - - /* Static generator of opj_stream from file stream */ -static opj_stream_t * -opjCreateStream(FILE *fp, - l_int32 is_read_stream) -{ -opj_stream_t *l_stream; - - PROCNAME("opjCreateStream"); - - if (!fp) - return (opj_stream_t *)ERROR_PTR("fp not defined", procName, NULL); - - l_stream = opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE, is_read_stream); - if (!l_stream) - return (opj_stream_t *)ERROR_PTR("stream not made", procName, NULL); - -#if OPJ_VERSION_MINOR == 0 - opj_stream_set_user_data(l_stream, fp); -#else - opj_stream_set_user_data(l_stream, fp, - (opj_stream_free_user_data_fn)NULL); -#endif - opj_stream_set_user_data_length(l_stream, opj_get_user_data_length(fp)); - opj_stream_set_read_function(l_stream, - (opj_stream_read_fn)opj_read_from_file); - opj_stream_set_write_function(l_stream, - (opj_stream_write_fn)opj_write_from_file); - opj_stream_set_skip_function(l_stream, - (opj_stream_skip_fn)opj_skip_from_file); - opj_stream_set_seek_function(l_stream, - (opj_stream_seek_fn)opj_seek_from_file); - - return l_stream; -} - -/* --------------------------------------------*/ -#endif /* HAVE_LIBJP2K */ -/* --------------------------------------------*/ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/jp2kiostub.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/jp2kiostub.c deleted file mode 100644 index f8340677..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/jp2kiostub.c +++ /dev/null @@ -1,98 +0,0 @@ -/*====================================================================* - - 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 jp2kiostub.c - *
- * - * Stubs for jp2kio.c functions - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * - * Read jpeg from file - * PIX *pixReadJpeg() [special top level] - * PIX *pixReadStreamJpeg() - * - * Read jpeg metadata from file - * l_int32 readHeaderJpeg() - * l_int32 freadHeaderJpeg() - * l_int32 fgetJpegResolution() - * l_int32 fgetJpegComment() - * - * Write jpeg to file - * l_int32 pixWriteJpeg() [special top level] - * l_int32 pixWriteStreamJpeg() - * - * Read/write to memory - * PIX *pixReadMemJpeg() - * l_int32 readHeaderMemJpeg() - * l_int32 readResolutionMemJpeg() - * l_int32 pixWriteMemJpeg() - * - * Setting special flag for chroma sampling on write - * l_int32 pixSetChromaSampling() - * - * Static system helpers - * static void jpeg_error_catch_all_1() - * static void jpeg_error_catch_all_2() - * static l_uint8 jpeg_getc() - * static l_int32 jpeg_comment_callback() - * - * Documentation: libjpeg.doc can be found, along with all - * source code, at ftp://ftp.uu.net/graphics/jpeg - * Download and untar the file: jpegsrc.v6b.tar.gz - * A good paper on jpeg can also be found there: wallace.ps.gz - * - * The functions in libjpeg make it very simple to compress - * and decompress images. On input (decompression from file), - * 3 component color images can be read into either an 8 bpp Pix - * with a colormap or a 32 bpp Pix with RGB components. For output - * (compression to file), all color Pix, whether 8 bpp with a - * colormap or 32 bpp, are written compressed as a set of three - * 8 bpp (rgb) images. - * - * Low-level error handling - * ------------------------ - * The default behavior of the jpeg library is to call exit. - * This is often undesirable, and the caller should make the - * decision when to abort a process. To prevent the jpeg library - * from calling exit(), setjmp() has been inserted into all - * readers and writers, and the cinfo struct has been set up so that - * the low-level jpeg library will call a special error handler - * that doesn't exit, instead of the default function error_exit(). - * - * To avoid race conditions and make these functions thread-safe in - * the rare situation where calls to two threads are simultaneously - * failing on bad jpegs, we insert a local copy of the jmp_buf struct - * into the cinfo.client_data field, and use this on longjmp. - * For extracting the jpeg comment, we have the added complication - * that the client_data field must also return the jpeg comment, - * and we use a different error handler. - * - * How to avoid subsampling the chroma channels - * -------------------------------------------- - * When writing, you can avoid subsampling the U,V (chroma) - * channels. This gives higher quality for the color, which is - * important for some situations. The default subsampling is 2x2 on - * both channels. Before writing, call pixSetChromaSampling(pix, 0) - * to prevent chroma subsampling. - * - * How to extract just the luminance channel in reading RGB - * -------------------------------------------------------- - * For higher resolution and faster decoding of an RGB image, you - * can extract just the 8 bpp luminance channel, using pixReadJpeg(), - * where you use L_JPEG_READ_LUMINANCE for the %hint arg. - * - * How to fail to read if the data is corrupted - * --------------------------------------------- - * By default, if the low-level jpeg library functions do not abort, - * a pix will be returned, even if the data is corrupted and warnings - * are issued. In order to be most likely to fail to read when there - * is data corruption, use L_JPEG_FAIL_ON_BAD_DATA in the %hint arg. - * - * Compressing to memory and decompressing from memory - * --------------------------------------------------- - * On systems like windows without fmemopen() and open_memstream(), - * we write data to a temp file and read it back for operations - * between pix and compressed-data, such as pixReadMemJpeg() and - * pixWriteMemJpeg(). - * - * Vestigial code: parsing the jpeg file for header metadata - * --------------------------------------------------------- - * For extracting header metadata, we previously parsed the file, looking - * for specific markers. This is error-prone because of non-standard - * jpeg files, and we now use readHeaderJpeg() and readHeaderMemJpeg(). - * The vestigial code is retained in jpegio_notused.c to help you - * understand a bit about how to parse jpeg markers. It is not compiled - * into the library. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This is a special function for reading jpeg files. - * (2) Use this if you want the jpeg library to create - * an 8 bpp colormapped image. - * (3) Images reduced by factors of 2, 4 or 8 can be returned - * significantly faster than full resolution images. - * (4) If the jpeg data is bad, the jpeg library will continue - * silently, or return warnings, or attempt to exit. Depending - * on the severity of the data corruption, there are two possible - * outcomes: - * (a) a possibly damaged pix can be generated, along with zero - * or more warnings, or - * (b) the library will attempt to exit (caught by our error - * handler) and no pix will be returned. - * If a pix is generated with at least one warning of data - * corruption, and if L_JPEG_FAIL_ON_BAD_DATA is included in %hint, - * no pix will be returned. - * (5) The possible hint values are given in the enum in imageio.h: - * * L_JPEG_READ_LUMINANCE - * * L_JPEG_FAIL_ON_BAD_DATA - * Default (0) is to do neither. - *- */ -PIX * -pixReadJpeg(const char *filename, - l_int32 cmapflag, - l_int32 reduction, - l_int32 *pnwarn, - l_int32 hint) -{ -l_int32 ret; -l_uint8 *comment; -FILE *fp; -PIX *pix; - - PROCNAME("pixReadJpeg"); - - if (pnwarn) *pnwarn = 0; - if (!filename) - return (PIX *)ERROR_PTR("filename not defined", procName, NULL); - if (cmapflag != 0 && cmapflag != 1) - cmapflag = 0; /* default */ - if (reduction != 1 && reduction != 2 && reduction != 4 && reduction != 8) - return (PIX *)ERROR_PTR("reduction not in {1,2,4,8}", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (PIX *)ERROR_PTR("image file not found", procName, NULL); - pix = pixReadStreamJpeg(fp, cmapflag, reduction, pnwarn, hint); - if (pix) { - ret = fgetJpegComment(fp, &comment); - if (!ret && comment) - pixSetText(pix, (char *)comment); - LEPT_FREE(comment); - } - fclose(fp); - - if (!pix) - return (PIX *)ERROR_PTR("image not returned", procName, NULL); - return pix; -} - - -/*! - * \brief pixReadStreamJpeg() - * - * \param[in] fp file stream - * \param[in] cmapflag 0 for no colormap in returned pix; - * 1 to return an 8 bpp cmapped pix if spp = 3 or 4 - * \param[in] reduction scaling factor: 1, 2, 4 or 8 - * \param[out] pnwarn [optional] number of warnings - * \param[in] hint a bitwise OR of L_JPEG_* values; 0 for default - * \return pix, or NULL on error - * - * Usage: see pixReadJpeg - *
- * Notes: - * (1) The jpeg comment, if it exists, is not stored in the pix. - *- */ -PIX * -pixReadStreamJpeg(FILE *fp, - l_int32 cmapflag, - l_int32 reduction, - l_int32 *pnwarn, - l_int32 hint) -{ -l_int32 cyan, yellow, magenta, black, nwarn; -l_int32 i, j, k, rval, gval, bval; -l_int32 w, h, wpl, spp, ncolors, cindex, ycck, cmyk; -l_uint32 *data; -l_uint32 *line, *ppixel; -JSAMPROW rowbuffer; -PIX *pix; -PIXCMAP *cmap; -struct jpeg_decompress_struct cinfo; -struct jpeg_error_mgr jerr; -jmp_buf jmpbuf; /* must be local to the function */ - - PROCNAME("pixReadStreamJpeg"); - - if (pnwarn) *pnwarn = 0; - if (!fp) - return (PIX *)ERROR_PTR("fp not defined", procName, NULL); - if (cmapflag != 0 && cmapflag != 1) - cmapflag = 0; /* default */ - if (reduction != 1 && reduction != 2 && reduction != 4 && reduction != 8) - return (PIX *)ERROR_PTR("reduction not in {1,2,4,8}", procName, NULL); - - if (BITS_IN_JSAMPLE != 8) /* set in jmorecfg.h */ - return (PIX *)ERROR_PTR("BITS_IN_JSAMPLE != 8", procName, NULL); - - rewind(fp); - pix = NULL; - rowbuffer = NULL; - - /* Modify the jpeg error handling to catch fatal errors */ - cinfo.err = jpeg_std_error(&jerr); - jerr.error_exit = jpeg_error_catch_all_1; - cinfo.client_data = (void *)&jmpbuf; - if (setjmp(jmpbuf)) { - pixDestroy(&pix); - LEPT_FREE(rowbuffer); - return (PIX *)ERROR_PTR("internal jpeg error", procName, NULL); - } - - /* Initialize jpeg structs for decompression */ - jpeg_create_decompress(&cinfo); - jpeg_stdio_src(&cinfo, fp); - jpeg_read_header(&cinfo, TRUE); - cinfo.scale_denom = reduction; - cinfo.scale_num = 1; - jpeg_calc_output_dimensions(&cinfo); - if (hint & L_JPEG_READ_LUMINANCE) { - cinfo.out_color_space = JCS_GRAYSCALE; - spp = 1; - L_INFO("reading luminance channel only\n", procName); - } else { - spp = cinfo.out_color_components; - } - - /* Allocate the image and a row buffer */ - w = cinfo.output_width; - h = cinfo.output_height; - ycck = (cinfo.jpeg_color_space == JCS_YCCK && spp == 4 && cmapflag == 0); - cmyk = (cinfo.jpeg_color_space == JCS_CMYK && spp == 4 && cmapflag == 0); - if (spp != 1 && spp != 3 && !ycck && !cmyk) { - jpeg_destroy_decompress(&cinfo); - return (PIX *)ERROR_PTR("spp must be 1 or 3, or YCCK or CMYK", - procName, NULL); - } - if ((spp == 3 && cmapflag == 0) || ycck || cmyk) { /* rgb or 4 bpp color */ - rowbuffer = (JSAMPROW)LEPT_CALLOC(sizeof(JSAMPLE), (size_t)spp * w); - pix = pixCreate(w, h, 32); - } else { /* 8 bpp gray or colormapped */ - rowbuffer = (JSAMPROW)LEPT_CALLOC(sizeof(JSAMPLE), w); - pix = pixCreate(w, h, 8); - } - pixSetInputFormat(pix, IFF_JFIF_JPEG); - if (!rowbuffer || !pix) { - LEPT_FREE(rowbuffer); - pixDestroy(&pix); - jpeg_destroy_decompress(&cinfo); - return (PIX *)ERROR_PTR("rowbuffer or pix not made", procName, NULL); - } - - /* Initialize decompression. Set up a colormap for color - * quantization if requested. */ - if (spp == 1) { /* Grayscale or colormapped */ - jpeg_start_decompress(&cinfo); - } else { /* Color; spp == 3 or YCCK or CMYK */ - if (cmapflag == 0) { /* 24 bit color in 32 bit pix or YCCK/CMYK */ - cinfo.quantize_colors = FALSE; - jpeg_start_decompress(&cinfo); - } else { /* Color quantize to 8 bits */ - cinfo.quantize_colors = TRUE; - cinfo.desired_number_of_colors = 256; - jpeg_start_decompress(&cinfo); - - /* Construct a pix cmap */ - cmap = pixcmapCreate(8); - ncolors = cinfo.actual_number_of_colors; - for (cindex = 0; cindex < ncolors; cindex++) { - rval = cinfo.colormap[0][cindex]; - gval = cinfo.colormap[1][cindex]; - bval = cinfo.colormap[2][cindex]; - pixcmapAddColor(cmap, rval, gval, bval); - } - pixSetColormap(pix, cmap); - } - } - wpl = pixGetWpl(pix); - data = pixGetData(pix); - - /* Decompress. Unfortunately, we cannot use the return value - * from jpeg_read_scanlines() to determine if there was a problem - * with the data; it always appears to return 1. We can only - * tell from the warnings during decoding, such as "premature - * end of data segment". The default behavior is to return an - * image even if there are warnings. However, by setting the - * hint to have the same bit flag as L_JPEG_FAIL_ON_BAD_DATA, - * no image will be returned if there are any warnings. */ - for (i = 0; i < h; i++) { - if (jpeg_read_scanlines(&cinfo, &rowbuffer, (JDIMENSION)1) == 0) { - L_ERROR("read error at scanline %d\n", procName, i); - pixDestroy(&pix); - jpeg_destroy_decompress(&cinfo); - LEPT_FREE(rowbuffer); - return (PIX *)ERROR_PTR("bad data", procName, NULL); - } - - /* -- 24 bit color -- */ - if ((spp == 3 && cmapflag == 0) || ycck || cmyk) { - ppixel = data + i * wpl; - if (spp == 3) { - for (j = k = 0; j < w; j++) { - SET_DATA_BYTE(ppixel, COLOR_RED, rowbuffer[k++]); - SET_DATA_BYTE(ppixel, COLOR_GREEN, rowbuffer[k++]); - SET_DATA_BYTE(ppixel, COLOR_BLUE, rowbuffer[k++]); - ppixel++; - } - } else { - /* This is a conversion from CMYK -> RGB that ignores - color profiles, and is invoked when the image header - claims to be in CMYK or YCCK colorspace. If in YCCK, - libjpeg may be doing YCCK -> CMYK under the hood. - To understand why the colors need to be inverted on - read-in for the Adobe marker, see the "Special - color spaces" section of "Using the IJG JPEG - Library" by Thomas G. Lane: - http://www.jpegcameras.com/libjpeg/libjpeg-3.html#ss3.1 - The non-Adobe conversion is equivalent to: - rval = black - black * cyan / 255 - ... - The Adobe conversion is equivalent to: - rval = black - black * (255 - cyan) / 255 - ... - Note that cyan is the complement to red, and we - are subtracting the complement color (weighted - by black) from black. For Adobe conversions, - where they've already inverted the CMY but not - the K, we have to invert again. The results - must be clipped to [0 ... 255]. */ - for (j = k = 0; j < w; j++) { - cyan = rowbuffer[k++]; - magenta = rowbuffer[k++]; - yellow = rowbuffer[k++]; - black = rowbuffer[k++]; - if (cinfo.saw_Adobe_marker) { - rval = (black * cyan) / 255; - gval = (black * magenta) / 255; - bval = (black * yellow) / 255; - } else { - rval = black * (255 - cyan) / 255; - gval = black * (255 - magenta) / 255; - bval = black * (255 - yellow) / 255; - } - rval = L_MIN(L_MAX(rval, 0), 255); - gval = L_MIN(L_MAX(gval, 0), 255); - bval = L_MIN(L_MAX(bval, 0), 255); - composeRGBPixel(rval, gval, bval, ppixel); - ppixel++; - } - } - } else { /* 8 bpp grayscale or colormapped pix */ - line = data + i * wpl; - for (j = 0; j < w; j++) - SET_DATA_BYTE(line, j, rowbuffer[j]); - } - } - - nwarn = cinfo.err->num_warnings; - if (pnwarn) *pnwarn = nwarn; - - /* If the pixel density is neither 1 nor 2, it may not be defined. - * In that case, don't set the resolution. */ - if (cinfo.density_unit == 1) { /* pixels per inch */ - pixSetXRes(pix, cinfo.X_density); - pixSetYRes(pix, cinfo.Y_density); - } else if (cinfo.density_unit == 2) { /* pixels per centimeter */ - pixSetXRes(pix, (l_int32)((l_float32)cinfo.X_density * 2.54 + 0.5)); - pixSetYRes(pix, (l_int32)((l_float32)cinfo.Y_density * 2.54 + 0.5)); - } - - if (cinfo.output_components != spp) - lept_stderr("output spp = %d, spp = %d\n", - cinfo.output_components, spp); - - jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); - LEPT_FREE(rowbuffer); - - if (nwarn > 0) { - if (hint & L_JPEG_FAIL_ON_BAD_DATA) { - L_ERROR("fail with %d warning(s) of bad data\n", procName, nwarn); - pixDestroy(&pix); - } else { - L_WARNING("%d warning(s) of bad data\n", procName, nwarn); - } - } - - return pix; -} - - -/*---------------------------------------------------------------------* - * Read jpeg metadata from file * - *---------------------------------------------------------------------*/ -/*! - * \brief readHeaderJpeg() - * - * \param[in] filename - * \param[out] pw [optional] - * \param[out] ph [optional] - * \param[out] pspp [optional] samples/pixel - * \param[out] pycck [optional] 1 if ycck color space; 0 otherwise - * \param[out] pcmyk [optional] 1 if cmyk color space; 0 otherwise - * \return 0 if OK, 1 on error - */ -l_ok -readHeaderJpeg(const char *filename, - l_int32 *pw, - l_int32 *ph, - l_int32 *pspp, - l_int32 *pycck, - l_int32 *pcmyk) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("readHeaderJpeg"); - - if (pw) *pw = 0; - if (ph) *ph = 0; - if (pspp) *pspp = 0; - if (pycck) *pycck = 0; - if (pcmyk) *pcmyk = 0; - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!pw && !ph && !pspp && !pycck && !pcmyk) - return ERROR_INT("no results requested", procName, 1); - - if ((fp = fopenReadStream(filename)) == NULL) - return ERROR_INT("image file not found", procName, 1); - ret = freadHeaderJpeg(fp, pw, ph, pspp, pycck, pcmyk); - fclose(fp); - return ret; -} - - -/*! - * \brief freadHeaderJpeg() - * - * \param[in] fp file stream - * \param[out] pw [optional] - * \param[out] ph [optional] - * \param[out] pspp [optional] samples/pixel - * \param[out] pycck [optional] 1 if ycck color space; 0 otherwise - * \param[out] pcmyk [optional] 1 if cmyk color space; 0 otherwise - * \return 0 if OK, 1 on error - */ -l_ok -freadHeaderJpeg(FILE *fp, - l_int32 *pw, - l_int32 *ph, - l_int32 *pspp, - l_int32 *pycck, - l_int32 *pcmyk) -{ -l_int32 spp, w, h; -struct jpeg_decompress_struct cinfo; -struct jpeg_error_mgr jerr; -jmp_buf jmpbuf; /* must be local to the function */ - - PROCNAME("freadHeaderJpeg"); - - if (pw) *pw = 0; - if (ph) *ph = 0; - if (pspp) *pspp = 0; - if (pycck) *pycck = 0; - if (pcmyk) *pcmyk = 0; - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!pw && !ph && !pspp && !pycck && !pcmyk) - return ERROR_INT("no results requested", procName, 1); - - rewind(fp); - - /* Modify the jpeg error handling to catch fatal errors */ - cinfo.err = jpeg_std_error(&jerr); - cinfo.client_data = (void *)&jmpbuf; - jerr.error_exit = jpeg_error_catch_all_1; - if (setjmp(jmpbuf)) - return ERROR_INT("internal jpeg error", procName, 1); - - /* Initialize the jpeg structs for reading the header */ - jpeg_create_decompress(&cinfo); - jpeg_stdio_src(&cinfo, fp); - jpeg_read_header(&cinfo, TRUE); - jpeg_calc_output_dimensions(&cinfo); - spp = cinfo.out_color_components; - w = cinfo.output_width; - h = cinfo.output_height; - if (w < 1 || h < 1 || spp < 1 || spp > 4) { - jpeg_destroy_decompress(&cinfo); - rewind(fp); - return ERROR_INT("bad jpeg image parameters", procName, 1); - } - - if (pspp) *pspp = spp; - if (pw) *pw = cinfo.output_width; - if (ph) *ph = cinfo.output_height; - if (pycck) *pycck = - (cinfo.jpeg_color_space == JCS_YCCK && spp == 4); - if (pcmyk) *pcmyk = - (cinfo.jpeg_color_space == JCS_CMYK && spp == 4); - - jpeg_destroy_decompress(&cinfo); - rewind(fp); - return 0; -} - - -/* - * \brief fgetJpegResolution() - * - * \param[in] fp file stream - * \param[out] pxres, pyres resolutions - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) If neither resolution field is set, this is not an error; - * the returned resolution values are 0 (designating 'unknown'). - * (2) Side-effect: this rewinds the stream. - *- */ -l_int32 -fgetJpegResolution(FILE *fp, - l_int32 *pxres, - l_int32 *pyres) -{ -struct jpeg_decompress_struct cinfo; -struct jpeg_error_mgr jerr; -jmp_buf jmpbuf; /* must be local to the function */ - - PROCNAME("fgetJpegResolution"); - - if (pxres) *pxres = 0; - if (pyres) *pyres = 0; - if (!pxres || !pyres) - return ERROR_INT("&xres and &yres not both defined", procName, 1); - if (!fp) - return ERROR_INT("stream not opened", procName, 1); - - rewind(fp); - - /* Modify the jpeg error handling to catch fatal errors */ - cinfo.err = jpeg_std_error(&jerr); - cinfo.client_data = (void *)&jmpbuf; - jerr.error_exit = jpeg_error_catch_all_1; - if (setjmp(jmpbuf)) - return ERROR_INT("internal jpeg error", procName, 1); - - /* Initialize the jpeg structs for reading the header */ - jpeg_create_decompress(&cinfo); - jpeg_stdio_src(&cinfo, fp); - jpeg_read_header(&cinfo, TRUE); - - /* It is common for the input resolution to be omitted from the - * jpeg file. If density_unit is not 1 or 2, simply return 0. */ - if (cinfo.density_unit == 1) { /* pixels/inch */ - *pxres = cinfo.X_density; - *pyres = cinfo.Y_density; - } else if (cinfo.density_unit == 2) { /* pixels/cm */ - *pxres = (l_int32)((l_float32)cinfo.X_density * 2.54 + 0.5); - *pyres = (l_int32)((l_float32)cinfo.Y_density * 2.54 + 0.5); - } - - jpeg_destroy_decompress(&cinfo); - rewind(fp); - return 0; -} - - -/* - * \brief fgetJpegComment() - * - * \param[in] fp file stream opened for read - * \param[out] pcomment comment - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) Side-effect: this rewinds the stream. - *- */ -l_int32 -fgetJpegComment(FILE *fp, - l_uint8 **pcomment) -{ -struct jpeg_decompress_struct cinfo; -struct jpeg_error_mgr jerr; -struct callback_data cb_data; /* contains local jmp_buf */ - - PROCNAME("fgetJpegComment"); - - if (!pcomment) - return ERROR_INT("&comment not defined", procName, 1); - *pcomment = NULL; - if (!fp) - return ERROR_INT("stream not opened", procName, 1); - - rewind(fp); - - /* Modify the jpeg error handling to catch fatal errors */ - cinfo.err = jpeg_std_error(&jerr); - jerr.error_exit = jpeg_error_catch_all_2; - cb_data.comment = NULL; - cinfo.client_data = (void *)&cb_data; - if (setjmp(cb_data.jmpbuf)) { - LEPT_FREE(cb_data.comment); - return ERROR_INT("internal jpeg error", procName, 1); - } - - /* Initialize the jpeg structs for reading the header */ - jpeg_create_decompress(&cinfo); - jpeg_set_marker_processor(&cinfo, JPEG_COM, jpeg_comment_callback); - jpeg_stdio_src(&cinfo, fp); - jpeg_read_header(&cinfo, TRUE); - - /* Save the result */ - *pcomment = cb_data.comment; - jpeg_destroy_decompress(&cinfo); - rewind(fp); - return 0; -} - - -/*---------------------------------------------------------------------* - * Writing Jpeg * - *---------------------------------------------------------------------*/ -/*! - * \brief pixWriteJpeg() - * - * \param[in] filename - * \param[in] pix any depth; cmap is OK - * \param[in] quality 1 - 100; 75 is default - * \param[in] progressive 0 for baseline sequential; 1 for progressive - * \return 0 if OK; 1 on error - */ -l_ok -pixWriteJpeg(const char *filename, - PIX *pix, - l_int32 quality, - l_int32 progressive) -{ -FILE *fp; - - PROCNAME("pixWriteJpeg"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "wb+")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - - if (pixWriteStreamJpeg(fp, pix, quality, progressive)) { - fclose(fp); - return ERROR_INT("pix not written to stream", procName, 1); - } - - fclose(fp); - return 0; -} - - -/*! - * \brief pixWriteStreamJpeg() - * - * \param[in] fp file stream - * \param[in] pixs any depth; cmap is OK - * \param[in] quality 1 - 100; 75 is default value; 0 is also default - * \param[in] progressive 0 for baseline sequential; 1 for progressive - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Progressive encoding gives better compression, at the - * expense of slower encoding and decoding. - * (2) Standard chroma subsampling is 2x2 on both the U and V - * channels. For highest quality, use no subsampling; this - * option is set by pixSetChromaSampling(pix, 0). - * (3) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16 - * and 32 bpp. However, it is possible, and in some cases desirable, - * to write out a jpeg file using an rgb pix that has 24 bpp. - * This can be created by appending the raster data for a 24 bpp - * image (with proper scanline padding) directly to a 24 bpp - * pix that was created without a data array. - * (4) There are two compression paths in this function: - * * Grayscale image, no colormap: compress as 8 bpp image. - * * rgb full color image: copy each line into the color - * line buffer, and compress as three 8 bpp images. - * (5) Under the covers, the jpeg library transforms rgb to a - * luminance-chromaticity triple, each component of which is - * also 8 bits, and compresses that. It uses 2 Huffman tables, - * a higher resolution one (with more quantization levels) - * for luminosity and a lower resolution one for the chromas. - *- */ -l_ok -pixWriteStreamJpeg(FILE *fp, - PIX *pixs, - l_int32 quality, - l_int32 progressive) -{ -l_int32 xres, yres; -l_int32 i, j, k; -l_int32 w, h, d, wpl, spp, colorflag, rowsamples; -l_uint32 *ppixel, *line, *data; -JSAMPROW rowbuffer; -PIX *pix; -struct jpeg_compress_struct cinfo; -struct jpeg_error_mgr jerr; -char *text; -jmp_buf jmpbuf; /* must be local to the function */ - - PROCNAME("pixWriteStreamJpeg"); - - if (!fp) - return ERROR_INT("stream not open", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (quality <= 0) quality = 75; /* default */ - if (quality > 100) { - L_ERROR("invalid jpeg quality; setting to 75\n", procName); - quality = 75; - } - - /* If necessary, convert the pix so that it can be jpeg compressed. - * The colormap is removed based on the source, so if the colormap - * has only gray colors, the image will be compressed with spp = 1. */ - pixGetDimensions(pixs, &w, &h, &d); - pix = NULL; - if (pixGetColormap(pixs) != NULL) { - L_INFO("removing colormap; may be better to compress losslessly\n", - procName); - pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - } else if (d >= 8 && d != 16) { /* normal case; no rewrite */ - pix = pixClone(pixs); - } else if (d < 8 || d == 16) { - L_INFO("converting from %d to 8 bpp\n", procName, d); - pix = pixConvertTo8(pixs, 0); /* 8 bpp, no cmap */ - } else { - L_ERROR("unknown pix type with d = %d and no cmap\n", procName, d); - return 1; - } - if (!pix) - return ERROR_INT("pix not made", procName, 1); - pixSetPadBits(pix, 0); - - rewind(fp); - rowbuffer = NULL; - - /* Modify the jpeg error handling to catch fatal errors */ - cinfo.err = jpeg_std_error(&jerr); - cinfo.client_data = (void *)&jmpbuf; - jerr.error_exit = jpeg_error_catch_all_1; - if (setjmp(jmpbuf)) { - LEPT_FREE(rowbuffer); - pixDestroy(&pix); - return ERROR_INT("internal jpeg error", procName, 1); - } - - /* Initialize the jpeg structs for compression */ - jpeg_create_compress(&cinfo); - jpeg_stdio_dest(&cinfo, fp); - cinfo.image_width = w; - cinfo.image_height = h; - - /* Set the color space and number of components */ - d = pixGetDepth(pix); - if (d == 8) { - colorflag = 0; /* 8 bpp grayscale; no cmap */ - cinfo.input_components = 1; - cinfo.in_color_space = JCS_GRAYSCALE; - } else { /* d == 32 || d == 24 */ - colorflag = 1; /* rgb */ - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; - } - - jpeg_set_defaults(&cinfo); - - /* Setting optimize_coding to TRUE seems to improve compression - * by approx 2-4 percent, and increases comp time by approx 20%. */ - cinfo.optimize_coding = FALSE; - - /* Set resolution in pixels/in (density_unit: 1 = in, 2 = cm) */ - xres = pixGetXRes(pix); - yres = pixGetYRes(pix); - if ((xres != 0) && (yres != 0)) { - cinfo.density_unit = 1; /* designates pixels per inch */ - cinfo.X_density = xres; - cinfo.Y_density = yres; - } - - /* Set the quality and progressive parameters */ - jpeg_set_quality(&cinfo, quality, TRUE); - if (progressive) - jpeg_simple_progression(&cinfo); - - /* Set the chroma subsampling parameters. This is done in - * YUV color space. The Y (intensity) channel is never subsampled. - * The standard subsampling is 2x2 on both the U and V channels. - * Notation on this is confusing. For a nice illustrations, see - * http://en.wikipedia.org/wiki/Chroma_subsampling - * The standard subsampling is written as 4:2:0. - * We allow high quality where there is no subsampling on the - * chroma channels: denoted as 4:4:4. */ - if (pixs->special == L_NO_CHROMA_SAMPLING_JPEG) { - cinfo.comp_info[0].h_samp_factor = 1; - cinfo.comp_info[0].v_samp_factor = 1; - cinfo.comp_info[1].h_samp_factor = 1; - cinfo.comp_info[1].v_samp_factor = 1; - cinfo.comp_info[2].h_samp_factor = 1; - cinfo.comp_info[2].v_samp_factor = 1; - } - - jpeg_start_compress(&cinfo, TRUE); - - /* Cap the text the length limit, 65533, for JPEG_COM payload. - * Just to be safe, subtract 100 to cover the Adobe name space. */ - if ((text = pixGetText(pix)) != NULL) { - if (strlen(text) > 65433) { - L_WARNING("text is %zu bytes; clipping to 65433\n", - procName, strlen(text)); - text[65433] = '\0'; - } - jpeg_write_marker(&cinfo, JPEG_COM, (const JOCTET *)text, strlen(text)); - } - - /* Allocate row buffer */ - spp = cinfo.input_components; - rowsamples = spp * w; - if ((rowbuffer = (JSAMPROW)LEPT_CALLOC(sizeof(JSAMPLE), rowsamples)) - == NULL) { - pixDestroy(&pix); - return ERROR_INT("calloc fail for rowbuffer", procName, 1); - } - - data = pixGetData(pix); - wpl = pixGetWpl(pix); - for (i = 0; i < h; i++) { - line = data + i * wpl; - if (colorflag == 0) { /* 8 bpp gray */ - for (j = 0; j < w; j++) - rowbuffer[j] = GET_DATA_BYTE(line, j); - } else { /* colorflag == 1 */ - if (d == 24) { /* See note 3 above; special case of 24 bpp rgb */ - jpeg_write_scanlines(&cinfo, (JSAMPROW *)&line, 1); - } else { /* standard 32 bpp rgb */ - ppixel = line; - for (j = k = 0; j < w; j++) { - rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); - rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); - rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); - ppixel++; - } - } - } - if (d != 24) - jpeg_write_scanlines(&cinfo, &rowbuffer, 1); - } - jpeg_finish_compress(&cinfo); - - pixDestroy(&pix); - LEPT_FREE(rowbuffer); - jpeg_destroy_compress(&cinfo); - return 0; -} - - -/*---------------------------------------------------------------------* - * Read/write to memory * - *---------------------------------------------------------------------*/ - -/*! - * \brief pixReadMemJpeg() - * - * \param[in] data const; jpeg-encoded - * \param[in] size of data - * \param[in] cmflag colormap flag 0 means return RGB image if color; - * 1 means create a colormap and return - * an 8 bpp colormapped image if color - * \param[in] reduction scaling factor: 1, 2, 4 or 8 - * \param[out] pnwarn [optional] number of warnings - * \param[in] hint a bitwise OR of L_JPEG_* values; 0 for default - * \return pix, or NULL on error - * - *
- * Notes: - * (1) The %size byte of %data must be a null character. - * (2) The only hint flag so far is L_JPEG_READ_LUMINANCE, - * given in the enum in imageio.h. - * (3) See pixReadJpeg() for usage. - *- */ -PIX * -pixReadMemJpeg(const l_uint8 *data, - size_t size, - l_int32 cmflag, - l_int32 reduction, - l_int32 *pnwarn, - l_int32 hint) -{ -l_int32 ret; -l_uint8 *comment; -FILE *fp; -PIX *pix; - - PROCNAME("pixReadMemJpeg"); - - if (pnwarn) *pnwarn = 0; - if (!data) - return (PIX *)ERROR_PTR("data not defined", procName, NULL); - - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return (PIX *)ERROR_PTR("stream not opened", procName, NULL); - pix = pixReadStreamJpeg(fp, cmflag, reduction, pnwarn, hint); - if (pix) { - ret = fgetJpegComment(fp, &comment); - if (!ret && comment) { - pixSetText(pix, (char *)comment); - LEPT_FREE(comment); - } - } - fclose(fp); - if (!pix) L_ERROR("pix not read\n", procName); - return pix; -} - - -/*! - * \brief readHeaderMemJpeg() - * - * \param[in] data const; jpeg-encoded - * \param[in] size of data - * \param[out] pw [optional] width - * \param[out] ph [optional] height - * \param[out] pspp [optional] samples/pixel - * \param[out] pycck [optional] 1 if ycck color space; 0 otherwise - * \param[out] pcmyk [optional] 1 if cmyk color space; 0 otherwise - * \return 0 if OK, 1 on error - */ -l_ok -readHeaderMemJpeg(const l_uint8 *data, - size_t size, - l_int32 *pw, - l_int32 *ph, - l_int32 *pspp, - l_int32 *pycck, - l_int32 *pcmyk) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("readHeaderMemJpeg"); - - if (pw) *pw = 0; - if (ph) *ph = 0; - if (pspp) *pspp = 0; - if (pycck) *pycck = 0; - if (pcmyk) *pcmyk = 0; - if (!data) - return ERROR_INT("data not defined", procName, 1); - if (!pw && !ph && !pspp && !pycck && !pcmyk) - return ERROR_INT("no results requested", procName, 1); - - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = freadHeaderJpeg(fp, pw, ph, pspp, pycck, pcmyk); - fclose(fp); - return ret; -} - - -/*! - * \brief readResolutionMemJpeg() - * - * \param[in] data const; jpeg-encoded - * \param[in] size of data - * \param[out] pxres [optional] - * \param[out] pyres [optional] - * \return 0 if OK, 1 on error - */ -l_ok -readResolutionMemJpeg(const l_uint8 *data, - size_t size, - l_int32 *pxres, - l_int32 *pyres) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("readResolutionMemJpeg"); - - if (pxres) *pxres = 0; - if (pyres) *pyres = 0; - if (!data) - return ERROR_INT("data not defined", procName, 1); - if (!pxres && !pyres) - return ERROR_INT("no results requested", procName, 1); - - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = fgetJpegResolution(fp, pxres, pyres); - fclose(fp); - return ret; -} - - -/*! - * \brief pixWriteMemJpeg() - * - * \param[out] pdata data of jpeg compressed image - * \param[out] psize size of returned data - * \param[in] pix any depth; cmap is OK - * \param[in] quality 1 - 100; 75 is default value; 0 is also default - * \param[in] progressive 0 for baseline sequential; 1 for progressive - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) See pixWriteStreamJpeg() for usage. This version writes to - * memory instead of to a file stream. - *- */ -l_ok -pixWriteMemJpeg(l_uint8 **pdata, - size_t *psize, - PIX *pix, - l_int32 quality, - l_int32 progressive) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("pixWriteMemJpeg"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1 ); - if (!psize) - return ERROR_INT("&size not defined", procName, 1 ); - if (!pix) - return ERROR_INT("&pix not defined", procName, 1 ); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = pixWriteStreamJpeg(fp, pix, quality, progressive); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = pixWriteStreamJpeg(fp, pix, quality, progressive); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} - - -/*---------------------------------------------------------------------* - * Setting special flag for chroma sampling on write * - *---------------------------------------------------------------------*/ -/*! - * \brief pixSetChromaSampling() - * - * \param[in] pix - * \param[in] sampling 1 for subsampling; 0 for no subsampling - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The default is for 2x2 chroma subsampling because the files are - * considerably smaller and the appearance is typically satisfactory. - * To get full resolution output in the chroma channels for - * jpeg writing, call this with %sampling == 0. - *- */ -l_ok -pixSetChromaSampling(PIX *pix, - l_int32 sampling) -{ - PROCNAME("pixSetChromaSampling"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1 ); - if (sampling) - pixSetSpecial(pix, 0); /* default */ - else - pixSetSpecial(pix, L_NO_CHROMA_SAMPLING_JPEG); - return 0; -} - - -/*---------------------------------------------------------------------* - * Static system helpers * - *---------------------------------------------------------------------*/ -/*! - * \brief jpeg_error_catch_all_1() - * - * Notes: - * (1) The default jpeg error_exit() kills the process, but we - * never want a call to leptonica to kill a process. If you - * do want this behavior, remove the calls to these error handlers. - * (2) This is used where cinfo->client_data holds only jmpbuf. - */ -static void -jpeg_error_catch_all_1(j_common_ptr cinfo) -{ - jmp_buf *pjmpbuf = (jmp_buf *)cinfo->client_data; - (*cinfo->err->output_message) (cinfo); - jpeg_destroy(cinfo); - longjmp(*pjmpbuf, 1); - return; -} - -/*! - * \brief jpeg_error_catch_all_2() - * - * Notes: - * (1) This is used where cinfo->client_data needs to hold both - * the jmpbuf and the jpeg comment data. - * (2) On error, the comment data will be freed by the caller. - */ -static void -jpeg_error_catch_all_2(j_common_ptr cinfo) -{ -struct callback_data *pcb_data; - - pcb_data = (struct callback_data *)cinfo->client_data; - (*cinfo->err->output_message) (cinfo); - jpeg_destroy(cinfo); - longjmp(pcb_data->jmpbuf, 1); - return; -} - -/* This function was borrowed from libjpeg */ -static l_uint8 -jpeg_getc(j_decompress_ptr cinfo) -{ -struct jpeg_source_mgr *datasrc; - - datasrc = cinfo->src; - if (datasrc->bytes_in_buffer == 0) { - if (! (*datasrc->fill_input_buffer) (cinfo)) { - return 0; - } - } - datasrc->bytes_in_buffer--; - return GETJOCTET(*datasrc->next_input_byte++); -} - -/*! - * \brief jpeg_comment_callback() - * - * Notes: - * (1) This is used to read the jpeg comment (JPEG_COM). - * See the note above the declaration for why it returns - * a "boolean". - */ -static boolean -jpeg_comment_callback(j_decompress_ptr cinfo) -{ -l_int32 length, i; -l_uint8 *comment; -struct callback_data *pcb_data; - - /* Get the size of the comment */ - length = jpeg_getc(cinfo) << 8; - length += jpeg_getc(cinfo); - length -= 2; - if (length <= 0) - return 1; - - /* Extract the comment from the file */ - if ((comment = (l_uint8 *)LEPT_CALLOC(length + 1, sizeof(l_uint8))) == NULL) - return 0; - for (i = 0; i < length; i++) - comment[i] = jpeg_getc(cinfo); - - /* Save the comment and return */ - pcb_data = (struct callback_data *)cinfo->client_data; - if (pcb_data->comment) { /* clear before overwriting previous comment */ - LEPT_FREE(pcb_data->comment); - pcb_data->comment = NULL; - } - pcb_data->comment = comment; - return 1; -} - -/* --------------------------------------------*/ -#endif /* HAVE_LIBJPEG */ -/* --------------------------------------------*/ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/jpegiostub.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/jpegiostub.c deleted file mode 100644 index 7fa18142..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/jpegiostub.c +++ /dev/null @@ -1,151 +0,0 @@ -/*====================================================================* - - 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 jpegiostub.c - *
- * - * Stubs for jpegio.c functions - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * - * Basic operations on kernels for image convolution - * - * Create/destroy/copy - * L_KERNEL *kernelCreate() - * void kernelDestroy() - * L_KERNEL *kernelCopy() - * - * Accessors: - * l_int32 kernelGetElement() - * l_int32 kernelSetElement() - * l_int32 kernelGetParameters() - * l_int32 kernelSetOrigin() - * l_int32 kernelGetSum() - * l_int32 kernelGetMinMax() - * - * Normalize/invert - * L_KERNEL *kernelNormalize() - * L_KERNEL *kernelInvert() - * - * Helper function - * l_float32 **create2dFloatArray() - * - * Serialized I/O - * L_KERNEL *kernelRead() - * L_KERNEL *kernelReadStream() - * l_int32 kernelWrite() - * l_int32 kernelWriteStream() - * - * Making a kernel from a compiled string - * L_KERNEL *kernelCreateFromString() - * - * Making a kernel from a simple file format - * L_KERNEL *kernelCreateFromFile() - * - * Making a kernel from a Pix - * L_KERNEL *kernelCreateFromPix() - * - * Display a kernel in a pix - * PIX *kernelDisplayInPix() - * - * Parse string to extract numbers - * NUMA *parseStringForNumbers() - * - * Simple parametric kernels - * L_KERNEL *makeFlatKernel() - * L_KERNEL *makeGaussianKernel() - * L_KERNEL *makeGaussianKernelSep() - * L_KERNEL *makeDoGKernel() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) kernelCreate() initializes all values to 0. - * (2) After this call, (cy,cx) and nonzero data values must be - * assigned. - * (2) The number of kernel elements must be less than 2^29. - *- */ -L_KERNEL * -kernelCreate(l_int32 height, - l_int32 width) -{ -l_uint64 size64; -L_KERNEL *kel; - - PROCNAME("kernelCreate"); - - if (width <= 0) - return (L_KERNEL *)ERROR_PTR("width must be > 0", procName, NULL); - if (height <= 0) - return (L_KERNEL *)ERROR_PTR("height must be > 0", procName, NULL); - - /* Avoid overflow in malloc arg */ - size64 = (l_uint64)width * (l_uint64)height; - if (size64 >= (1LL << 29)) { - L_ERROR("requested width = %d, height = %d\n", procName, width, height); - return (L_KERNEL *)ERROR_PTR("size >= 2^29", procName, NULL); - } - - kel = (L_KERNEL *)LEPT_CALLOC(1, sizeof(L_KERNEL)); - kel->sy = height; - kel->sx = width; - if ((kel->data = create2dFloatArray(height, width)) == NULL) { - LEPT_FREE(kel); - return (L_KERNEL *)ERROR_PTR("data not allocated", procName, NULL); - } - return kel; -} - - -/*! - * \brief kernelDestroy() - * - * \param[in,out] pkel will be set to null before returning - * \return void - */ -void -kernelDestroy(L_KERNEL **pkel) -{ -l_int32 i; -L_KERNEL *kel; - - PROCNAME("kernelDestroy"); - - if (pkel == NULL) { - L_WARNING("ptr address is NULL!\n", procName); - return; - } - if ((kel = *pkel) == NULL) - return; - - for (i = 0; i < kel->sy; i++) - LEPT_FREE(kel->data[i]); - LEPT_FREE(kel->data); - LEPT_FREE(kel); - - *pkel = NULL; - return; -} - - -/*! - * \brief kernelCopy() - * - * \param[in] kels source kernel - * \return keld copy of kels, or NULL on error - */ -L_KERNEL * -kernelCopy(L_KERNEL *kels) -{ -l_int32 i, j, sx, sy, cx, cy; -L_KERNEL *keld; - - PROCNAME("kernelCopy"); - - if (!kels) - return (L_KERNEL *)ERROR_PTR("kels not defined", procName, NULL); - - kernelGetParameters(kels, &sy, &sx, &cy, &cx); - if ((keld = kernelCreate(sy, sx)) == NULL) - return (L_KERNEL *)ERROR_PTR("keld not made", procName, NULL); - keld->cy = cy; - keld->cx = cx; - for (i = 0; i < sy; i++) - for (j = 0; j < sx; j++) - keld->data[i][j] = kels->data[i][j]; - - return keld; -} - - -/*----------------------------------------------------------------------* - * Accessors * - *----------------------------------------------------------------------*/ -/*! - * \brief kernelGetElement() - * - * \param[in] kel - * \param[in] row - * \param[in] col - * \param[out] pval - * \return 0 if OK; 1 on error - */ -l_ok -kernelGetElement(L_KERNEL *kel, - l_int32 row, - l_int32 col, - l_float32 *pval) -{ - PROCNAME("kernelGetElement"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0; - if (!kel) - return ERROR_INT("kernel not defined", procName, 1); - if (row < 0 || row >= kel->sy) - return ERROR_INT("kernel row out of bounds", procName, 1); - if (col < 0 || col >= kel->sx) - return ERROR_INT("kernel col out of bounds", procName, 1); - - *pval = kel->data[row][col]; - return 0; -} - - -/*! - * \brief kernelSetElement() - * - * \param[in] kel kernel - * \param[in] row - * \param[in] col - * \param[in] val - * \return 0 if OK; 1 on error - */ -l_ok -kernelSetElement(L_KERNEL *kel, - l_int32 row, - l_int32 col, - l_float32 val) -{ - PROCNAME("kernelSetElement"); - - if (!kel) - return ERROR_INT("kel not defined", procName, 1); - if (row < 0 || row >= kel->sy) - return ERROR_INT("kernel row out of bounds", procName, 1); - if (col < 0 || col >= kel->sx) - return ERROR_INT("kernel col out of bounds", procName, 1); - - kel->data[row][col] = val; - return 0; -} - - -/*! - * \brief kernelGetParameters() - * - * \param[in] kel kernel - * \param[out] psy, psx, pcy, pcx [optional] each can be null - * \return 0 if OK, 1 on error - */ -l_ok -kernelGetParameters(L_KERNEL *kel, - l_int32 *psy, - l_int32 *psx, - l_int32 *pcy, - l_int32 *pcx) -{ - PROCNAME("kernelGetParameters"); - - if (psy) *psy = 0; - if (psx) *psx = 0; - if (pcy) *pcy = 0; - if (pcx) *pcx = 0; - if (!kel) - return ERROR_INT("kernel not defined", procName, 1); - if (psy) *psy = kel->sy; - if (psx) *psx = kel->sx; - if (pcy) *pcy = kel->cy; - if (pcx) *pcx = kel->cx; - return 0; -} - - -/*! - * \brief kernelSetOrigin() - * - * \param[in] kel kernel - * \param[in] cy, cx - * \return 0 if OK; 1 on error - */ -l_ok -kernelSetOrigin(L_KERNEL *kel, - l_int32 cy, - l_int32 cx) -{ - PROCNAME("kernelSetOrigin"); - - if (!kel) - return ERROR_INT("kel not defined", procName, 1); - kel->cy = cy; - kel->cx = cx; - return 0; -} - - -/*! - * \brief kernelGetSum() - * - * \param[in] kel kernel - * \param[out] psum sum of all kernel values - * \return 0 if OK, 1 on error - */ -l_ok -kernelGetSum(L_KERNEL *kel, - l_float32 *psum) -{ -l_int32 sx, sy, i, j; - - PROCNAME("kernelGetSum"); - - if (!psum) - return ERROR_INT("&sum not defined", procName, 1); - *psum = 0.0; - if (!kel) - return ERROR_INT("kernel not defined", procName, 1); - - kernelGetParameters(kel, &sy, &sx, NULL, NULL); - for (i = 0; i < sy; i++) { - for (j = 0; j < sx; j++) { - *psum += kel->data[i][j]; - } - } - return 0; -} - - -/*! - * \brief kernelGetMinMax() - * - * \param[in] kel kernel - * \param[out] pmin [optional] minimum value - * \param[out] pmax [optional] maximum value - * \return 0 if OK, 1 on error - */ -l_ok -kernelGetMinMax(L_KERNEL *kel, - l_float32 *pmin, - l_float32 *pmax) -{ -l_int32 sx, sy, i, j; -l_float32 val, minval, maxval; - - PROCNAME("kernelGetMinmax"); - - if (!pmin && !pmax) - return ERROR_INT("neither &min nor &max defined", procName, 1); - if (pmin) *pmin = 0.0; - if (pmax) *pmax = 0.0; - if (!kel) - return ERROR_INT("kernel not defined", procName, 1); - - kernelGetParameters(kel, &sy, &sx, NULL, NULL); - minval = 10000000.0; - maxval = -10000000.0; - for (i = 0; i < sy; i++) { - for (j = 0; j < sx; j++) { - val = kel->data[i][j]; - if (val < minval) - minval = val; - if (val > maxval) - maxval = val; - } - } - if (pmin) - *pmin = minval; - if (pmax) - *pmax = maxval; - - return 0; -} - - -/*----------------------------------------------------------------------* - * Normalize/Invert * - *----------------------------------------------------------------------*/ -/*! - * \brief kernelNormalize() - * - * \param[in] kels source kel, to be normalized - * \param[in] normsum desired sum of elements in keld - * \return keld normalized version of kels, or NULL on error - * or if sum of elements is very close to 0) - * - *
- * Notes: - * (1) If the sum of kernel elements is close to 0, do not - * try to calculate the normalized kernel. Instead, - * return a copy of the input kernel, with a warning. - *- */ -L_KERNEL * -kernelNormalize(L_KERNEL *kels, - l_float32 normsum) -{ -l_int32 i, j, sx, sy, cx, cy; -l_float32 sum, factor; -L_KERNEL *keld; - - PROCNAME("kernelNormalize"); - - if (!kels) - return (L_KERNEL *)ERROR_PTR("kels not defined", procName, NULL); - - kernelGetSum(kels, &sum); - if (L_ABS(sum) < 0.00001) { - L_WARNING("null sum; not normalizing; returning a copy\n", procName); - return kernelCopy(kels); - } - - kernelGetParameters(kels, &sy, &sx, &cy, &cx); - if ((keld = kernelCreate(sy, sx)) == NULL) - return (L_KERNEL *)ERROR_PTR("keld not made", procName, NULL); - keld->cy = cy; - keld->cx = cx; - - factor = normsum / sum; - for (i = 0; i < sy; i++) - for (j = 0; j < sx; j++) - keld->data[i][j] = factor * kels->data[i][j]; - - return keld; -} - - -/*! - * \brief kernelInvert() - * - * \param[in] kels source kel, to be inverted - * \return keld spatially inverted, about the origin, or NULL on error - * - *
- * Notes: - * (1) For convolution, the kernel is spatially inverted before - * a "correlation" operation is done between the kernel and the image. - *- */ -L_KERNEL * -kernelInvert(L_KERNEL *kels) -{ -l_int32 i, j, sx, sy, cx, cy; -L_KERNEL *keld; - - PROCNAME("kernelInvert"); - - if (!kels) - return (L_KERNEL *)ERROR_PTR("kels not defined", procName, NULL); - - kernelGetParameters(kels, &sy, &sx, &cy, &cx); - if ((keld = kernelCreate(sy, sx)) == NULL) - return (L_KERNEL *)ERROR_PTR("keld not made", procName, NULL); - keld->cy = sy - 1 - cy; - keld->cx = sx - 1 - cx; - - for (i = 0; i < sy; i++) - for (j = 0; j < sx; j++) - keld->data[i][j] = kels->data[sy - 1 - i][sx - 1 - j]; - - return keld; -} - - -/*----------------------------------------------------------------------* - * Helper function * - *----------------------------------------------------------------------*/ -/*! - * \brief create2dFloatArray() - * - * \param[in] sy rows == height - * \param[in] sx columns == width - * \return doubly indexed array i.e., an array of sy row pointers, - * each of which points to an array of sx floats - * - *
- * Notes: - * (1) The array[%sy][%sx] is indexed in standard "matrix notation", - * with the row index first. - * (2) The caller kernelCreate() limits the size to < 2^29 pixels. - *- */ -l_float32 ** -create2dFloatArray(l_int32 sy, - l_int32 sx) -{ -l_int32 i; -l_float32 **array; - - PROCNAME("create2dFloatArray"); - - if (sx <= 0 || sx > MaxArraySize) - return (l_float32 **)ERROR_PTR("sx out of bounds", procName, NULL); - if (sy <= 0 || sy > MaxArraySize) - return (l_float32 **)ERROR_PTR("sy out of bounds", procName, NULL); - - if ((array = (l_float32 **)LEPT_CALLOC(sy, sizeof(l_float32 *))) == NULL) - return (l_float32 **)ERROR_PTR("ptr array not made", procName, NULL); - for (i = 0; i < sy; i++) - array[i] = (l_float32 *)LEPT_CALLOC(sx, sizeof(l_float32)); - return array; -} - - -/*----------------------------------------------------------------------* - * Kernel serialized I/O * - *----------------------------------------------------------------------*/ -/*! - * \brief kernelRead() - * - * \param[in] fname filename - * \return kernel, or NULL on error - */ -L_KERNEL * -kernelRead(const char *fname) -{ -FILE *fp; -L_KERNEL *kel; - - PROCNAME("kernelRead"); - - if (!fname) - return (L_KERNEL *)ERROR_PTR("fname not defined", procName, NULL); - - if ((fp = fopenReadStream(fname)) == NULL) - return (L_KERNEL *)ERROR_PTR("stream not opened", procName, NULL); - if ((kel = kernelReadStream(fp)) == NULL) { - fclose(fp); - return (L_KERNEL *)ERROR_PTR("kel not returned", procName, NULL); - } - fclose(fp); - - return kel; -} - - -/*! - * \brief kernelReadStream() - * - * \param[in] fp file stream - * \return kernel, or NULL on error - */ -L_KERNEL * -kernelReadStream(FILE *fp) -{ -l_int32 sy, sx, cy, cx, i, j, ret, version, ignore; -L_KERNEL *kel; - - PROCNAME("kernelReadStream"); - - if (!fp) - return (L_KERNEL *)ERROR_PTR("stream not defined", procName, NULL); - - ret = fscanf(fp, " Kernel Version %d\n", &version); - if (ret != 1) - return (L_KERNEL *)ERROR_PTR("not a kernel file", procName, NULL); - if (version != KERNEL_VERSION_NUMBER) - return (L_KERNEL *)ERROR_PTR("invalid kernel version", procName, NULL); - - if (fscanf(fp, " sy = %d, sx = %d, cy = %d, cx = %d\n", - &sy, &sx, &cy, &cx) != 4) - return (L_KERNEL *)ERROR_PTR("dimensions not read", procName, NULL); - if (sx > MaxArraySize || sy > MaxArraySize) { - L_ERROR("sx = %d or sy = %d > %d\n", procName, sx, sy, MaxArraySize); - return NULL; - } - if ((kel = kernelCreate(sy, sx)) == NULL) - return (L_KERNEL *)ERROR_PTR("kel not made", procName, NULL); - kernelSetOrigin(kel, cy, cx); - - for (i = 0; i < sy; i++) { - for (j = 0; j < sx; j++) - ignore = fscanf(fp, "%15f", &kel->data[i][j]); - ignore = fscanf(fp, "\n"); - } - ignore = fscanf(fp, "\n"); - - return kel; -} - - -/*! - * \brief kernelWrite() - * - * \param[in] fname output file - * \param[in] kel kernel - * \return 0 if OK, 1 on error - */ -l_ok -kernelWrite(const char *fname, - L_KERNEL *kel) -{ -FILE *fp; - - PROCNAME("kernelWrite"); - - if (!fname) - return ERROR_INT("fname not defined", procName, 1); - if (!kel) - return ERROR_INT("kel not defined", procName, 1); - - if ((fp = fopenWriteStream(fname, "wb")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - kernelWriteStream(fp, kel); - fclose(fp); - - return 0; -} - - -/*! - * \brief kernelWriteStream() - * - * \param[in] fp file stream - * \param[in] kel - * \return 0 if OK, 1 on error - */ -l_ok -kernelWriteStream(FILE *fp, - L_KERNEL *kel) -{ -l_int32 sx, sy, cx, cy, i, j; - - PROCNAME("kernelWriteStream"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!kel) - return ERROR_INT("kel not defined", procName, 1); - kernelGetParameters(kel, &sy, &sx, &cy, &cx); - - fprintf(fp, " Kernel Version %d\n", KERNEL_VERSION_NUMBER); - fprintf(fp, " sy = %d, sx = %d, cy = %d, cx = %d\n", sy, sx, cy, cx); - for (i = 0; i < sy; i++) { - for (j = 0; j < sx; j++) - fprintf(fp, "%15.4f", kel->data[i][j]); - fprintf(fp, "\n"); - } - fprintf(fp, "\n"); - - return 0; -} - - -/*----------------------------------------------------------------------* - * Making a kernel from a compiled string * - *----------------------------------------------------------------------*/ -/*! - * \brief kernelCreateFromString() - * - * \param[in] h, w height, width - * \param[in] cy, cx origin - * \param[in] kdata - * \return kernel of the given size, or NULL on error - * - *
- * Notes: - * (1) The data is an array of chars, in row-major order, giving - * space separated integers in the range [-255 ... 255]. - * (2) The only other formatting limitation is that you must - * leave space between the last number in each row and - * the double-quote. If possible, it's also nice to have each - * line in the string represent a line in the kernel; e.g., - * static const char *kdata = - * " 20 50 20 " - * " 70 140 70 " - * " 20 50 20 "; - *- */ -L_KERNEL * -kernelCreateFromString(l_int32 h, - l_int32 w, - l_int32 cy, - l_int32 cx, - const char *kdata) -{ -l_int32 n, i, j, index; -l_float32 val; -L_KERNEL *kel; -NUMA *na; - - PROCNAME("kernelCreateFromString"); - - if (h < 1) - return (L_KERNEL *)ERROR_PTR("height must be > 0", procName, NULL); - if (w < 1) - return (L_KERNEL *)ERROR_PTR("width must be > 0", procName, NULL); - if (cy < 0 || cy >= h) - return (L_KERNEL *)ERROR_PTR("cy invalid", procName, NULL); - if (cx < 0 || cx >= w) - return (L_KERNEL *)ERROR_PTR("cx invalid", procName, NULL); - - kel = kernelCreate(h, w); - kernelSetOrigin(kel, cy, cx); - na = parseStringForNumbers(kdata, " \t\n"); - n = numaGetCount(na); - if (n != w * h) { - kernelDestroy(&kel); - numaDestroy(&na); - lept_stderr("w = %d, h = %d, num ints = %d\n", w, h, n); - return (L_KERNEL *)ERROR_PTR("invalid integer data", procName, NULL); - } - - index = 0; - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - numaGetFValue(na, index, &val); - kernelSetElement(kel, i, j, val); - index++; - } - } - - numaDestroy(&na); - return kel; -} - - -/*----------------------------------------------------------------------* - * Making a kernel from a simple file format * - *----------------------------------------------------------------------*/ -/*! - * \brief kernelCreateFromFile() - * - * \param[in] filename - * \return kernel, or NULL on error - * - *
- * Notes: - * (1) The file contains, in the following order: - * ~ Any number of comment lines starting with '#' are ignored - * ~ The height and width of the kernel - * ~ The y and x values of the kernel origin - * ~ The kernel data, formatted as lines of numbers (integers - * or floats) for the kernel values in row-major order, - * and with no other punctuation. - * (Note: this differs from kernelCreateFromString(), - * where each line must begin and end with a double-quote - * to tell the compiler it's part of a string.) - * ~ The kernel specification ends when a blank line, - * a comment line, or the end of file is reached. - * (2) All lines must be left-justified. - * (3) See kernelCreateFromString() for a description of the string - * format for the kernel data. As an example, here are the lines - * of a valid kernel description file In the file, all lines - * are left-justified: - * \code - * # small 3x3 kernel - * 3 3 - * 1 1 - * 25.5 51 24.3 - * 70.2 146.3 73.4 - * 20 50.9 18.4 - * \endcode - *- */ -L_KERNEL * -kernelCreateFromFile(const char *filename) -{ -char *filestr, *line; -l_int32 nlines, i, j, first, index, w, h, cx, cy, n; -l_float32 val; -size_t size; -NUMA *na, *nat; -SARRAY *sa; -L_KERNEL *kel; - - PROCNAME("kernelCreateFromFile"); - - if (!filename) - return (L_KERNEL *)ERROR_PTR("filename not defined", procName, NULL); - - if ((filestr = (char *)l_binaryRead(filename, &size)) == NULL) - return (L_KERNEL *)ERROR_PTR("file not found", procName, NULL); - if (size == 0) { - LEPT_FREE(filestr); - return (L_KERNEL *)ERROR_PTR("file is empty", procName, NULL); - } - - sa = sarrayCreateLinesFromString(filestr, 1); - LEPT_FREE(filestr); - nlines = sarrayGetCount(sa); - - /* Find the first data line. */ - for (i = 0, first = 0; i < nlines; i++) { - line = sarrayGetString(sa, i, L_NOCOPY); - if (line[0] != '#') { - first = i; - break; - } - } - - /* Find the kernel dimensions and origin location. */ - line = sarrayGetString(sa, first, L_NOCOPY); - if (sscanf(line, "%d %d", &h, &w) != 2) { - sarrayDestroy(&sa); - return (L_KERNEL *)ERROR_PTR("error reading h,w", procName, NULL); - } - if (h > MaxArraySize || w > MaxArraySize) { - L_ERROR("h = %d or w = %d > %d\n", procName, h, w, MaxArraySize); - sarrayDestroy(&sa); - return NULL; - } - line = sarrayGetString(sa, first + 1, L_NOCOPY); - if (sscanf(line, "%d %d", &cy, &cx) != 2) { - sarrayDestroy(&sa); - return (L_KERNEL *)ERROR_PTR("error reading cy,cx", procName, NULL); - } - - /* Extract the data. This ends when we reach eof, or when we - * encounter a line of data that is either a null string or - * contains just a newline. */ - na = numaCreate(0); - for (i = first + 2; i < nlines; i++) { - line = sarrayGetString(sa, i, L_NOCOPY); - if (line[0] == '\0' || line[0] == '\n' || line[0] == '#') - break; - nat = parseStringForNumbers(line, " \t\n"); - numaJoin(na, nat, 0, -1); - numaDestroy(&nat); - } - sarrayDestroy(&sa); - - n = numaGetCount(na); - if (n != w * h) { - numaDestroy(&na); - lept_stderr("w = %d, h = %d, num ints = %d\n", w, h, n); - return (L_KERNEL *)ERROR_PTR("invalid integer data", procName, NULL); - } - - kel = kernelCreate(h, w); - kernelSetOrigin(kel, cy, cx); - index = 0; - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - numaGetFValue(na, index, &val); - kernelSetElement(kel, i, j, val); - index++; - } - } - - numaDestroy(&na); - return kel; -} - - -/*----------------------------------------------------------------------* - * Making a kernel from a Pix * - *----------------------------------------------------------------------*/ -/*! - * \brief kernelCreateFromPix() - * - * \param[in] pix - * \param[in] cy, cx origin of kernel - * \return kernel, or NULL on error - * - *
- * Notes: - * (1) The origin must be positive and within the dimensions of the pix. - *- */ -L_KERNEL * -kernelCreateFromPix(PIX *pix, - l_int32 cy, - l_int32 cx) -{ -l_int32 i, j, w, h, d; -l_uint32 val; -L_KERNEL *kel; - - PROCNAME("kernelCreateFromPix"); - - if (!pix) - return (L_KERNEL *)ERROR_PTR("pix not defined", procName, NULL); - pixGetDimensions(pix, &w, &h, &d); - if (d != 8) - return (L_KERNEL *)ERROR_PTR("pix not 8 bpp", procName, NULL); - if (cy < 0 || cx < 0 || cy >= h || cx >= w) - return (L_KERNEL *)ERROR_PTR("(cy, cx) invalid", procName, NULL); - - kel = kernelCreate(h, w); - kernelSetOrigin(kel, cy, cx); - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - pixGetPixel(pix, j, i, &val); - kernelSetElement(kel, i, j, (l_float32)val); - } - } - - return kel; -} - - -/*----------------------------------------------------------------------* - * Display a kernel in a pix * - *----------------------------------------------------------------------*/ -/*! - * \brief kernelDisplayInPix() - * - * \param[in] kel kernel - * \param[in] size of grid interiors; odd; either 1 or a minimum size - * of 17 is enforced - * \param[in] gthick grid thickness; either 0 or a minimum size of 2 - * is enforced - * \return pix display of kernel, or NULL on error - * - *
- * Notes: - * (1) This gives a visual representation of a kernel. - * (2) There are two modes of display: - * (a) Grid lines of minimum width 2, surrounding regions - * representing kernel elements of minimum size 17, - * with a "plus" mark at the kernel origin, or - * (b) A pix without grid lines and using 1 pixel per kernel element. - * (3) For both cases, the kernel absolute value is displayed, - * normalized such that the maximum absolute value is 255. - * (4) Large 2D separable kernels should be used for convolution - * with two 1D kernels. However, for the bilateral filter, - * the computation time is independent of the size of the - * 2D content kernel. - *- */ -PIX * -kernelDisplayInPix(L_KERNEL *kel, - l_int32 size, - l_int32 gthick) -{ -l_int32 i, j, w, h, sx, sy, cx, cy, width, x0, y0; -l_int32 normval; -l_float32 minval, maxval, max, val, norm; -PIX *pixd, *pixt0, *pixt1; - - PROCNAME("kernelDisplayInPix"); - - if (!kel) - return (PIX *)ERROR_PTR("kernel not defined", procName, NULL); - - /* Normalize the max value to be 255 for display */ - kernelGetParameters(kel, &sy, &sx, &cy, &cx); - kernelGetMinMax(kel, &minval, &maxval); - max = L_MAX(maxval, -minval); - if (max == 0.0) - return (PIX *)ERROR_PTR("kernel elements all 0.0", procName, NULL); - norm = 255. / (l_float32)max; - - /* Handle the 1 element/pixel case; typically with large kernels */ - if (size == 1 && gthick == 0) { - pixd = pixCreate(sx, sy, 8); - for (i = 0; i < sy; i++) { - for (j = 0; j < sx; j++) { - kernelGetElement(kel, i, j, &val); - normval = (l_int32)(norm * L_ABS(val)); - pixSetPixel(pixd, j, i, normval); - } - } - return pixd; - } - - /* Enforce the constraints for the grid line version */ - if (size < 17) { - L_WARNING("size < 17; setting to 17\n", procName); - size = 17; - } - if (size % 2 == 0) - size++; - if (gthick < 2) { - L_WARNING("grid thickness < 2; setting to 2\n", procName); - gthick = 2; - } - - w = size * sx + gthick * (sx + 1); - h = size * sy + gthick * (sy + 1); - pixd = pixCreate(w, h, 8); - - /* Generate grid lines */ - for (i = 0; i <= sy; i++) - pixRenderLine(pixd, 0, gthick / 2 + i * (size + gthick), - w - 1, gthick / 2 + i * (size + gthick), - gthick, L_SET_PIXELS); - for (j = 0; j <= sx; j++) - pixRenderLine(pixd, gthick / 2 + j * (size + gthick), 0, - gthick / 2 + j * (size + gthick), h - 1, - gthick, L_SET_PIXELS); - - /* Generate mask for each element */ - pixt0 = pixCreate(size, size, 1); - pixSetAll(pixt0); - - /* Generate crossed lines for origin pattern */ - pixt1 = pixCreate(size, size, 1); - width = size / 8; - pixRenderLine(pixt1, size / 2, (l_int32)(0.12 * size), - size / 2, (l_int32)(0.88 * size), - width, L_SET_PIXELS); - pixRenderLine(pixt1, (l_int32)(0.15 * size), size / 2, - (l_int32)(0.85 * size), size / 2, - width, L_FLIP_PIXELS); - pixRasterop(pixt1, size / 2 - width, size / 2 - width, - 2 * width, 2 * width, PIX_NOT(PIX_DST), NULL, 0, 0); - - /* Paste the patterns in */ - y0 = gthick; - for (i = 0; i < sy; i++) { - x0 = gthick; - for (j = 0; j < sx; j++) { - kernelGetElement(kel, i, j, &val); - normval = (l_int32)(norm * L_ABS(val)); - pixSetMaskedGeneral(pixd, pixt0, normval, x0, y0); - if (i == cy && j == cx) - pixPaintThroughMask(pixd, pixt1, x0, y0, 255 - normval); - x0 += size + gthick; - } - y0 += size + gthick; - } - - pixDestroy(&pixt0); - pixDestroy(&pixt1); - return pixd; -} - - -/*------------------------------------------------------------------------* - * Parse string to extract numbers * - *------------------------------------------------------------------------*/ -/*! - * \brief parseStringForNumbers() - * - * \param[in] str string containing numbers; not changed - * \param[in] seps string of characters that can be used between ints - * \return numa of numbers found, or NULL on error - * - *
- * Notes: - * (1) The numbers can be ints or floats. - *- */ -NUMA * -parseStringForNumbers(const char *str, - const char *seps) -{ -char *newstr, *head; -char *tail = NULL; -l_float32 val; -NUMA *na; - - PROCNAME("parseStringForNumbers"); - - if (!str) - return (NUMA *)ERROR_PTR("str not defined", procName, NULL); - - newstr = stringNew(str); /* to enforce const-ness of str */ - na = numaCreate(0); - head = strtokSafe(newstr, seps, &tail); - val = atof(head); - numaAddNumber(na, val); - LEPT_FREE(head); - while ((head = strtokSafe(NULL, seps, &tail)) != NULL) { - val = atof(head); - numaAddNumber(na, val); - LEPT_FREE(head); - } - - LEPT_FREE(newstr); - return na; -} - - -/*------------------------------------------------------------------------* - * Simple parametric kernels * - *------------------------------------------------------------------------*/ -/*! - * \brief makeFlatKernel() - * - * \param[in] height, width - * \param[in] cy, cx origin of kernel - * \return kernel, or NULL on error - * - *
- * Notes: - * (1) This is the same low-pass filtering kernel that is used - * in the block convolution functions. - * (2) The kernel origin (%cy, %cx) is typically placed as near - * the center of the kernel as possible. If height and - * width are odd, then using %cy = height / 2 and - * %cx = width / 2 places the origin at the exact center. - * (3) This returns a normalized kernel. - *- */ -L_KERNEL * -makeFlatKernel(l_int32 height, - l_int32 width, - l_int32 cy, - l_int32 cx) -{ -l_int32 i, j; -l_float32 normval; -L_KERNEL *kel; - - PROCNAME("makeFlatKernel"); - - if ((kel = kernelCreate(height, width)) == NULL) - return (L_KERNEL *)ERROR_PTR("kel not made", procName, NULL); - kernelSetOrigin(kel, cy, cx); - normval = 1.0 / (l_float32)(height * width); - for (i = 0; i < height; i++) { - for (j = 0; j < width; j++) { - kernelSetElement(kel, i, j, normval); - } - } - - return kel; -} - - -/*! - * \brief makeGaussianKernel() - * - * \param[in] halfh sy = 2 * halfh + 1 - * \param[in] halfw sx = 2 * halfw + 1 - * \param[in] stdev standard deviation - * \param[in] max value at (cx,cy) - * \return kernel, or NULL on error - * - *
- * Notes: - * (1) The kernel size (sx, sy) = (2 * %halfw + 1, 2 * %halfh + 1) - * (2) The kernel center (cx, cy) = (%halfw, %halfh). - * (3) %halfw and %halfh are typically equal, and - * are typically several times larger than the standard deviation. - * (4) If pixConvolve() is invoked with normalization (the sum of - * kernel elements = 1.0), use 1.0 for max (or any number that's - * not too small or too large). - *- */ -L_KERNEL * -makeGaussianKernel(l_int32 halfh, - l_int32 halfw, - l_float32 stdev, - l_float32 max) -{ -l_int32 sx, sy, i, j; -l_float32 val; -L_KERNEL *kel; - - PROCNAME("makeGaussianKernel"); - - sx = 2 * halfw + 1; - sy = 2 * halfh + 1; - if ((kel = kernelCreate(sy, sx)) == NULL) - return (L_KERNEL *)ERROR_PTR("kel not made", procName, NULL); - kernelSetOrigin(kel, halfh, halfw); - for (i = 0; i < sy; i++) { - for (j = 0; j < sx; j++) { - val = expf(-(l_float32)((i - halfh) * (i - halfh) + - (j - halfw) * (j - halfw)) / - (2. * stdev * stdev)); - kernelSetElement(kel, i, j, max * val); - } - } - - return kel; -} - - -/*! - * \brief makeGaussianKernelSep() - * - * \param[in] halfh sy = 2 * halfh + 1 - * \param[in] halfw sx = 2 * halfw + 1 - * \param[in] stdev standard deviation - * \param[in] max value at (cx,cy) - * \param[out] pkelx x part of kernel - * \param[out] pkely y part of kernel - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) See makeGaussianKernel() for description of input parameters. - * (2) These kernels are constructed so that the result of both - * normalized and un-normalized convolution will be the same - * as when convolving with pixConvolve() using the full kernel. - * (3) The trick for the un-normalized convolution is to have the - * product of the two kernel elemets at (cx,cy) be equal to %max, - * not max**2. That's why %max for kely is 1.0. If instead - * we use sqrt(%max) for both, the results are slightly less - * accurate, when compared to using the full kernel in - * makeGaussianKernel(). - *- */ -l_ok -makeGaussianKernelSep(l_int32 halfh, - l_int32 halfw, - l_float32 stdev, - l_float32 max, - L_KERNEL **pkelx, - L_KERNEL **pkely) -{ - PROCNAME("makeGaussianKernelSep"); - - if (!pkelx || !pkely) - return ERROR_INT("&kelx and &kely not defined", procName, 1); - - *pkelx = makeGaussianKernel(0, halfw, stdev, max); - *pkely = makeGaussianKernel(halfh, 0, stdev, 1.0); - return 0; -} - - -/*! - * \brief makeDoGKernel() - * - * \param[in] halfh sy = 2 * halfh + 1 - * \param[in] halfw sx = 2 * halfw + 1 - * \param[in] stdev standard deviation of narrower gaussian - * \param[in] ratio of stdev for wide filter to stdev for narrow one - * \return kernel, or NULL on error - * - *
- * Notes: - * (1) The DoG (difference of gaussians) is a wavelet mother - * function with null total sum. By subtracting two blurred - * versions of the image, it acts as a bandpass filter for - * frequencies passed by the narrow gaussian but stopped - * by the wide one.See: - * http://en.wikipedia.org/wiki/Difference_of_Gaussians - * (2) The kernel size (sx, sy) = (2 * halfw + 1, 2 * halfh + 1). - * (3) The kernel center (cx, cy) = (halfw, halfh). - * (4) %halfw and %halfh are typically equal, and are typically - * several times larger than the standard deviation. - * (5) %ratio is the ratio of standard deviations of the wide - * to narrow gaussian. It must be >= 1.0; 1.0 is a no-op. - * (6) Because the kernel is a null sum, it must be invoked without - * normalization in pixConvolve(). - *- */ -L_KERNEL * -makeDoGKernel(l_int32 halfh, - l_int32 halfw, - l_float32 stdev, - l_float32 ratio) -{ -l_int32 sx, sy, i, j; -l_float32 pi, squaredist, highnorm, lownorm, val; -L_KERNEL *kel; - - PROCNAME("makeDoGKernel"); - - sx = 2 * halfw + 1; - sy = 2 * halfh + 1; - if ((kel = kernelCreate(sy, sx)) == NULL) - return (L_KERNEL *)ERROR_PTR("kel not made", procName, NULL); - kernelSetOrigin(kel, halfh, halfw); - - pi = 3.1415926535; - for (i = 0; i < sy; i++) { - for (j = 0; j < sx; j++) { - squaredist = (l_float32)((i - halfh) * (i - halfh) + - (j - halfw) * (j - halfw)); - highnorm = 1. / (2 * stdev * stdev); - lownorm = highnorm / (ratio * ratio); - val = (highnorm / pi) * expf(-(highnorm * squaredist)) - - (lownorm / pi) * expf(-(lownorm * squaredist)); - kernelSetElement(kel, i, j, val); - } - } - - return kel; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/leptwin.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/leptwin.c deleted file mode 100644 index 72643a0b..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/leptwin.c +++ /dev/null @@ -1,368 +0,0 @@ -/*====================================================================* - - 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 leptwin.c - *
- * - * This file contains Leptonica routines needed only on Microsoft Windows - * - * Currently it only contains one public function - * (based on dibsectn.c by jmh, 03-30-98): - * - * HBITMAP pixGetWindowsHBITMAP(PIX *pix) - *- */ - -#ifdef _WIN32 -#include
- * Notes: - * (1) It's the responsibility of the caller to destroy the - * returned hBitmap with a call to DeleteObject (or with - * something that eventually calls DeleteObject). - *- */ -HBITMAP -pixGetWindowsHBITMAP(PIX *pix) -{ -l_int32 width, height, depth; -l_uint32 *data; -HBITMAP hBitmap = NULL; -BITMAP bm; -DWORD imageBitsSize; -PIX *pixt = NULL; -PIXCMAP *cmap; - - PROCNAME("pixGetWindowsHBITMAP"); - if (!pix) - return (HBITMAP)ERROR_PTR("pix not defined", procName, NULL); - - pixGetDimensions(pix, &width, &height, &depth); - cmap = pixGetColormap(pix); - - if (depth == 24) depth = 32; - if (depth == 2) { - pixt = pixConvert2To8(pix, 0, 85, 170, 255, TRUE); - if (!pixt) - return (HBITMAP)ERROR_PTR("unable to convert pix from 2bpp to 8bpp", - procName, NULL); - depth = pixGetDepth(pixt); - cmap = pixGetColormap(pixt); - } - - if (depth < 16) { - if (!cmap) - cmap = pixcmapCreateLinear(depth, 1<
- * - * Image library version number - * char *getImagelibVersions() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This returns a string of version numbers; e.g., - * libgif 5.0.3 - * libjpeg 8b (libjpeg-turbo 1.3.0) - * libpng 1.4.3 - * libtiff 3.9.5 - * zlib 1.2.5 - * libwebp 0.3.0 - * libopenjp2 2.1.0 - * (2) The caller must free the memory. - *- */ -char * -getImagelibVersions(void) -{ -char buf[128]; -l_int32 first = TRUE; -char *versionNumP; -char *nextTokenP; -char *versionStrP = NULL; - -#if HAVE_LIBGIF - first = FALSE; - stringJoinIP(&versionStrP, "libgif "); - #ifdef GIFLIB_MAJOR - snprintf(buf, sizeof(buf), "%d.%d.%d", GIFLIB_MAJOR, GIFLIB_MINOR, - GIFLIB_RELEASE); - #else - stringCopy(buf, "4.1.6(?)", sizeof(buf)); - #endif - stringJoinIP(&versionStrP, buf); -#endif /* HAVE_LIBGIF */ - -#if HAVE_LIBJPEG - { - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr err; - char buffer[JMSG_LENGTH_MAX]; - cinfo.err = jpeg_std_error(&err); - err.msg_code = JMSG_VERSION; - (*err.format_message) ((j_common_ptr ) &cinfo, buffer); - - if (!first) stringJoinIP(&versionStrP, " : "); - first = FALSE; - stringJoinIP(&versionStrP, "libjpeg "); - versionNumP = strtokSafe(buffer, " ", &nextTokenP); - stringJoinIP(&versionStrP, versionNumP); - LEPT_FREE(versionNumP); - - #if defined(LIBJPEG_TURBO_VERSION) - /* To stringify the result of expansion of a macro argument, - * you must use two levels of macros. See: - * https://gcc.gnu.org/onlinedocs/cpp/Stringification.html */ - #define l_xstr(s) l_str(s) - #define l_str(s) #s - snprintf(buf, sizeof(buf), " (libjpeg-turbo %s)", - l_xstr(LIBJPEG_TURBO_VERSION)); - stringJoinIP(&versionStrP, buf); - #endif /* LIBJPEG_TURBO_VERSION */ - } -#endif /* HAVE_LIBJPEG */ - -#if HAVE_LIBPNG - if (!first) stringJoinIP(&versionStrP, " : "); - first = FALSE; - stringJoinIP(&versionStrP, "libpng "); - stringJoinIP(&versionStrP, png_get_libpng_ver(NULL)); -#endif /* HAVE_LIBPNG */ - -#if HAVE_LIBTIFF - if (!first) stringJoinIP(&versionStrP, " : "); - first = FALSE; - stringJoinIP(&versionStrP, "libtiff "); - versionNumP = strtokSafe((char *)TIFFGetVersion(), " \n", &nextTokenP); - LEPT_FREE(versionNumP); - versionNumP = strtokSafe(NULL, " \n", &nextTokenP); - LEPT_FREE(versionNumP); - versionNumP = strtokSafe(NULL, " \n", &nextTokenP); - stringJoinIP(&versionStrP, versionNumP); - LEPT_FREE(versionNumP); -#endif /* HAVE_LIBTIFF */ - -#if HAVE_LIBZ - if (!first) stringJoinIP(&versionStrP, " : "); - first = FALSE; - stringJoinIP(&versionStrP, "zlib "); - stringJoinIP(&versionStrP, zlibVersion()); -#endif /* HAVE_LIBZ */ - -#if HAVE_LIBWEBP - { - l_int32 val; - char buf[32]; - if (!first) stringJoinIP(&versionStrP, " : "); - first = FALSE; - stringJoinIP(&versionStrP, "libwebp "); - val = WebPGetEncoderVersion(); - snprintf(buf, sizeof(buf), "%d.%d.%d", val >> 16, (val >> 8) & 0xff, - val & 0xff); - stringJoinIP(&versionStrP, buf); - } -#endif /* HAVE_LIBWEBP */ - -#if HAVE_LIBJP2K - { - const char *version; - if (!first) stringJoinIP(&versionStrP, " : "); - first = FALSE; - stringJoinIP(&versionStrP, "libopenjp2 "); - version = opj_version(); - stringJoinIP(&versionStrP, version); - } -#endif /* HAVE_LIBJP2K */ - - return versionStrP; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/list.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/list.c deleted file mode 100644 index 9063af6e..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/list.c +++ /dev/null @@ -1,815 +0,0 @@ -/*====================================================================* - - 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 list.c - *
- * - * Inserting and removing elements - * - * void listDestroy() - * DLLIST *listAddToHead() - * l_int32 listAddToTail() - * l_int32 listInsertBefore() - * l_int32 listInsertAfter() - * void *listRemoveElement() - * void *listRemoveFromHead() - * void *listRemoveFromTail() - * - * Other list operations - * - * DLLIST *listFindElement() - * DLLIST *listFindTail() - * l_int32 listGetCount() - * l_int32 listReverse() - * DLLIST *listJoin() - * - * Lists are much harder to handle than arrays. There is - * more overhead for the programmer, both cognitive and - * codewise, and more likelihood that an error can be made. - * For that reason, lists should only be used when it is - * inefficient to use arrays, such as when elements are - * routinely inserted or deleted from inside arrays whose - * average size is greater than about 10. - * - * A list of data structures can be implemented in a number - * of ways. The two most popular are: - * - * (1) The list can be composed of a linked list of - * pointer cells ("cons cells"), where the data structures - * are hung off the cells. This is more difficult - * to use because you have to keep track of both - * your hanging data and the cell structures. - * It requires 3 pointers for every data structure - * that is put in a list. There is no problem - * cloning (using reference counts) for structures that - * are put in such a list. We implement lists by this - * method here. - * - * (2) The list pointers can be inserted directly into - * the data structures. This is easy to implement - * and easier to use, but it adds 2 ptrs of overhead - * to every data structure in which the ptrs are embedded. - * It also requires special care not to put the ptrs - * in any data that is cloned with a reference count; - * else your lists will break. - * - * Writing C code that uses list pointers explicitly to make - * and alter lists is difficult and prone to error. - * Consequently, a generic list utility that handles lists - * of arbitrary objects and doesn't force the programmer to - * touch the "next" and "prev" pointers, is quite useful. - * Such functions are provided here. However, the usual - * situation requires traversing a list and applying some - * function to one or more of the list elements. Macros - * for traversing the list are, in general, necessary, to - * achieve the goal of invisibly handling all "next" and "prev" - * pointers in generic lists. We provide macros for - * traversing a list in both forward and reverse directions. - * - * Because of the typing in C, implementation of a general - * list utility requires casting. If macros are used, the - * casting can be done implicitly; otherwise, using functions, - * some of the casts must be explicit. Fortunately, this - * can be implemented with void* so the programmer using - * the library will not have to make any casts! (Unless you - * compile with g++, in which case the rules on implicit - * conversion are more strict.) - * - * For example, to add an arbitrary data structure foo to the - * tail of a list, use - * listAddToTail(&head, &tail, pfoo); - * where head and tail are list cell ptrs and pfoo is - * a pointer to the foo object. - * And to remove an arbitrary data structure foo from a - * list, when you know the list cell element it is hanging from, - * use - * pfoo = listRemoveElement(&head, elem) - * where head and elem are list cell ptrs and pfoo is a pointer - * to the foo object. No casts are required for foo in - * either direction in ANSI C. (However, casts are - * required for ANSI C++). - * - * We use lists that are composed of doubly-linked - * cells with data structures hanging off the cells. - * We use doubly-linked cells to simplify insertion - * and deletion, and to allow operations to proceed in either - * direction along the list. With doubly-linked lists, - * it is tempting to make them circular, by setting head->prev - * to the tail of the list and tail->next to the head. - * The circular list costs nothing extra in storage, and - * allows operations to proceed from either end of the list - * with equal speed. However, the circular link adds - * cognitive overhead for the application programmer in - * general, and it greatly complicates list traversal when - * arbitrary list elements can be added or removed as you - * move through. It can be done, but in the spirit of - * simplicity, we avoid the temptation. The price to be paid - * is the extra cost to find the tail of a list -- a full - * traversal -- before the tail can be used. This is a - * cheap price to pay to avoid major headaches and buggy code. - * - * When you are only applying some function to each element - * in a list, you can go either forwards or backwards. - * To run through a list forwards, use: - * \code - * for (elem = head; elem; elem = nextelem) { - * nextelem = elem->next; (in case we destroy elem) - *- */ - -#ifdef HAVE_CONFIG_H -#includedata> - * } - * \endcode - * To run through a list backwards, find the tail and use: - * - * for (elem = tail; elem; elem = prevelem) { - # prevelem = elem->prev; (in case we destroy elem) - * data> - * } - * - * Even though these patterns are very simple, they are so common - * that we've provided macros for them in list.h. Using the - * macros, this becomes: - * \code - * L_BEGIN_LIST_FORWARD(head, elem) - * data> - * L_END_LIST - * - * L_BEGIN_LIST_REVERSE(tail, elem) - * data> - * L_END_LIST - * \endcode - * Note again that with macros, the application programmer does - * not need to refer explicitly to next and prev fields. Also, - * in the reverse case, note that we do not explicitly - * show the head of the list. However, the head of the list - * is always in scope, and functions can be called within the - * iterator that change the head. - * - * Some special cases are simpler. For example, when - * removing all items from the head of the list, you can use - * \code - * while (head) { - * obj = listRemoveFromHead(&head); - * - * } - * \endcode - * Removing successive elements from the tail is equally simple: - * \code - * while (tail) { - * obj = listRemoveFromTail(&head, &tail); - * - * } - * \endcode - * When removing an arbitrary element from a list, use - * \code - * obj = listRemoveElement(&head, elem); - * \endcode - * All the listRemove*() functions hand you the object, - * destroy the list cell to which it was attached, and - * reset the list pointers if necessary. - * - * Several other list operations, that do not involve - * inserting or removing objects, are also provided. - * The function listFindElement() locates a list pointer - * by matching the object hanging on it to a given - * object. The function listFindTail() gets a handle - * to the tail list ptr, allowing backwards traversals of - * the list. listGetCount() gives the number of elements - * in a list. Functions that reverse a list and concatenate - * two lists are also provided. - * - * These functions can be modified for efficiency in the - * situation where there is a large amount of creation and - * destruction of list cells. If millions of cells are - * made and destroyed, but a relatively small number are - * around at any time, the list cells can be stored for - * later re-use in a stack (see the generic stack functions - * in stack.c). - *
- * Notes: - * (1) This only destroys the cons cells. Before destroying - * the list, it is necessary to remove all data and set the - * data pointers in each cons cell to NULL. - * (2) listDestroy() will give a warning message for each data - * ptr that is not NULL. - *- */ -void -listDestroy(DLLIST **phead) -{ -DLLIST *elem, *next, *head; - - PROCNAME("listDestroy"); - - if (phead == NULL) { - L_WARNING("ptr address is null!\n", procName); - return; - } - - if ((head = *phead) == NULL) - return; - - for (elem = head; elem; elem = next) { - if (elem->data) - L_WARNING("list data ptr is not null\n", procName); - next = elem->next; - LEPT_FREE(elem); - } - *phead = NULL; - return; -} - - -/*! - * \brief listAddToHead() - * - * \param[in,out] phead [optional] input head - * \param[in] data void* ptr, to be added - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This makes a new cell, attaches %data, and adds the - * cell to the head of the list. - * (2) When consing from NULL, be sure to initialize head to NULL - * before calling this function. - *- */ -l_ok -listAddToHead(DLLIST **phead, - void *data) -{ -DLLIST *cell, *head; - - PROCNAME("listAddToHead"); - - if (!phead) - return ERROR_INT("&head not defined", procName, 1); - head = *phead; - if (!data) - return ERROR_INT("data not defined", procName, 1); - - cell = (DLLIST *)LEPT_CALLOC(1, sizeof(DLLIST)); - cell->data = data; - if (!head) { /* start the list; initialize the ptrs */ - cell->prev = NULL; - cell->next = NULL; - } else { - cell->prev = NULL; - cell->next = head; - head->prev = cell; - } - *phead = cell; - return 0; -} - - -/*! - * \brief listAddToTail() - * - * \param[in,out] phead [may be updated], can be NULL - * \param[in,out] ptail [updated], can be NULL - * \param[in] data void* ptr, to be hung on tail cons cell - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This makes a new cell, attaches %data, and adds the - * cell to the tail of the list. - * (2) &head is input to allow the list to be "cons'd" up from NULL. - * (3) &tail is input to allow the tail to be updated - * for efficient sequential operation with this function. - * (4) We assume that if *phead and/or *ptail are not NULL, - * then they are valid addresses. Therefore: - * (a) when consing from NULL, be sure to initialize both - * head and tail to NULL. - * (b) when tail == NULL for an existing list, the tail - * will be found and updated. - *- */ -l_ok -listAddToTail(DLLIST **phead, - DLLIST **ptail, - void *data) -{ -DLLIST *cell, *head, *tail; - - PROCNAME("listAddToTail"); - - if (!phead) - return ERROR_INT("&head not defined", procName, 1); - head = *phead; - if (!ptail) - return ERROR_INT("&tail not defined", procName, 1); - if (!data) - return ERROR_INT("data not defined", procName, 1); - - cell = (DLLIST *)LEPT_CALLOC(1, sizeof(DLLIST)); - cell->data = data; - if (!head) { /* Start the list and initialize the ptrs. *ptail - * should also have been initialized to NULL */ - cell->prev = NULL; - cell->next = NULL; - *phead = cell; - *ptail = cell; - } else { - if ((tail = *ptail) == NULL) - tail = listFindTail(head); - cell->prev = tail; - cell->next = NULL; - tail->next = cell; - *ptail = cell; - } - - return 0; -} - - -/*! - * \brief listInsertBefore() - * - * \param[in,out] phead [optional] input head - * \param[in] elem list element to be inserted in front of; - * must be NULL if head is NULL - * \param[in] data void* address, to be added - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This can be called on a null list, in which case both - * head and elem must be null. - * (2) If you are searching through a list, looking for a condition - * to add an element, you can do something like this: - * \code - * L_BEGIN_LIST_FORWARD(head, elem) - *- */ -l_ok -listInsertBefore(DLLIST **phead, - DLLIST *elem, - void *data) -{ -DLLIST *cell, *head; - - PROCNAME("listInsertBefore"); - - if (!phead) - return ERROR_INT("&head not defined", procName, 1); - head = *phead; - if (!data) - return ERROR_INT("data not defined", procName, 1); - if ((!head && elem) || (head && !elem)) - return ERROR_INT("head and elem not consistent", procName, 1); - - /* New cell to insert */ - cell = (DLLIST *)LEPT_CALLOC(1, sizeof(DLLIST)); - cell->data = data; - if (!head) { /* start the list; initialize the ptrs */ - cell->prev = NULL; - cell->next = NULL; - *phead = cell; - } else if (head == elem) { /* insert before head of list */ - cell->prev = NULL; - cell->next = head; - head->prev = cell; - *phead = cell; - } else { /* insert before elem and after head of list */ - cell->prev = elem->prev; - cell->next = elem; - elem->prev->next = cell; - elem->prev = cell; - } - return 0; -} - - -/*! - * \brief listInsertAfter() - * - * \param[in,out] phead [optional] input head - * \param[in] elem list element to be inserted after; - * must be NULL if head is NULL - * \param[in] data void* ptr, to be added - * \return 0 if OK; 1 on error - * - *- * listInsertBefore(&head, elem, data); - * L_END_LIST - * \endcode - *
- * Notes: - * (1) This can be called on a null list, in which case both - * head and elem must be null. The head is included - * in the call to allow "consing" up from NULL. - * (2) If you are searching through a list, looking for a condition - * to add an element, you can do something like this: - * \code - * L_BEGIN_LIST_FORWARD(head, elem) - *- */ -l_ok -listInsertAfter(DLLIST **phead, - DLLIST *elem, - void *data) -{ -DLLIST *cell, *head; - - PROCNAME("listInsertAfter"); - - if (!phead) - return ERROR_INT("&head not defined", procName, 1); - head = *phead; - if (!data) - return ERROR_INT("data not defined", procName, 1); - if ((!head && elem) || (head && !elem)) - return ERROR_INT("head and elem not consistent", procName, 1); - - /* New cell to insert */ - cell = (DLLIST *)LEPT_CALLOC(1, sizeof(DLLIST)); - cell->data = data; - if (!head) { /* start the list; initialize the ptrs */ - cell->prev = NULL; - cell->next = NULL; - *phead = cell; - } else if (elem->next == NULL) { /* insert after last */ - cell->prev = elem; - cell->next = NULL; - elem->next = cell; - } else { /* insert after elem and before the end */ - cell->prev = elem; - cell->next = elem->next; - elem->next->prev = cell; - elem->next = cell; - } - return 0; -} - - -/*! - * \brief listRemoveElement() - * - * \param[in,out] phead input head; can be changed - * \param[in] elem list element to be removed - * \return data void* struct on cell - * - *- * listInsertAfter(&head, elem, data); - * L_END_LIST - * \endcode - *
- * Notes: - * (1) in ANSI C, it is not necessary to cast return to actual type; e.g., - * pix = listRemoveElement(&head, elem); - * but in ANSI C++, it is necessary to do the cast: - * pix = (Pix *)listRemoveElement(&head, elem); - *- */ -void * -listRemoveElement(DLLIST **phead, - DLLIST *elem) -{ -void *data; -DLLIST *head; - - PROCNAME("listRemoveElement"); - - if (!phead) - return (void *)ERROR_PTR("&head not defined", procName, NULL); - head = *phead; - if (!head) - return (void *)ERROR_PTR("head not defined", procName, NULL); - if (!elem) - return (void *)ERROR_PTR("elem not defined", procName, NULL); - - data = elem->data; - - if (head->next == NULL) { /* only one */ - if (elem != head) - return (void *)ERROR_PTR("elem must be head", procName, NULL); - *phead = NULL; - } else if (head == elem) { /* first one */ - elem->next->prev = NULL; - *phead = elem->next; - } else if (elem->next == NULL) { /* last one */ - elem->prev->next = NULL; - } else { /* neither the first nor the last one */ - elem->next->prev = elem->prev; - elem->prev->next = elem->next; - } - - LEPT_FREE(elem); - return data; -} - - -/*! - * \brief listRemoveFromHead() - * - * \param[in,out] phead head of list; updated - * \return data void* struct on cell, or NULL on error - * - *
- * Notes: - * (1) in ANSI C, it is not necessary to cast return to actual type; e.g., - * pix = listRemoveFromHead(&head); - * but in ANSI C++, it is necessary to do the cast; e.g., - * pix = (Pix *)listRemoveFromHead(&head); - *- */ -void * -listRemoveFromHead(DLLIST **phead) -{ -DLLIST *head; -void *data; - - PROCNAME("listRemoveFromHead"); - - if (!phead) - return (void *)ERROR_PTR("&head not defined", procName, NULL); - if ((head = *phead) == NULL) - return (void *)ERROR_PTR("head not defined", procName, NULL); - - if (head->next == NULL) { /* only one */ - *phead = NULL; - } else { - head->next->prev = NULL; - *phead = head->next; - } - - data = head->data; - LEPT_FREE(head); - return data; -} - - -/*! - * \brief listRemoveFromTail() - * - * \param[in,out] phead list head must NOT be NULL; may be changed - * \param[in,out] ptail list tail may be NULL; always updated - * \return data void* struct on cell or NULL on error - * - *
- * Notes: - * (1) We include &head so that it can be set to NULL if - * if the only element in the list is removed. - * (2) The function is relying on the fact that if tail is - * not NULL, then is is a valid address. You can use - * this function with tail == NULL for an existing list, in - * which case the tail is found and updated, and the - * removed element is returned. - * (3) In ANSI C, it is not necessary to cast return to actual type; e.g., - * pix = listRemoveFromTail(&head, &tail); - * but in ANSI C++, it is necessary to do the cast; e.g., - * pix = (Pix *)listRemoveFromTail(&head, &tail); - *- */ -void * -listRemoveFromTail(DLLIST **phead, - DLLIST **ptail) -{ -DLLIST *head, *tail; -void *data; - - PROCNAME("listRemoveFromTail"); - - if (!phead) - return (void *)ERROR_PTR("&head not defined", procName, NULL); - if ((head = *phead) == NULL) - return (void *)ERROR_PTR("head not defined", procName, NULL); - if (!ptail) - return (void *)ERROR_PTR("&tail not defined", procName, NULL); - if ((tail = *ptail) == NULL) - tail = listFindTail(head); - - if (head->next == NULL) { /* only one */ - *phead = NULL; - *ptail = NULL; - } else { - tail->prev->next = NULL; - *ptail = tail->prev; - } - - data = tail->data; - LEPT_FREE(tail); - return data; -} - - - -/*---------------------------------------------------------------------* - * Other list operations * - *---------------------------------------------------------------------*/ -/*! - * \brief listFindElement() - * - * \param[in] head list head - * \param[in] data void* address, to be searched for - * \return cell the containing cell, or NULL if not found or on error - * - *
- * Notes: - * (1) This returns a ptr to the cell, which is still embedded in - * the list. - * (2) This handle and the attached data have not been copied or - * reference counted, so they must not be destroyed. This - * violates our basic rule that every handle returned from a - * function is owned by that function and must be destroyed, - * but if rules aren't there to be broken, why have them? - *- */ -DLLIST * -listFindElement(DLLIST *head, - void *data) -{ -DLLIST *cell; - - PROCNAME("listFindElement"); - - if (!head) - return (DLLIST *)ERROR_PTR("head not defined", procName, NULL); - if (!data) - return (DLLIST *)ERROR_PTR("data not defined", procName, NULL); - - for (cell = head; cell; cell = cell->next) { - if (cell->data == data) - return cell; - } - - return NULL; -} - - -/*! - * \brief listFindTail() - * - * \param[in] head - * \return tail, or NULL on error - */ -DLLIST * -listFindTail(DLLIST *head) -{ -DLLIST *cell; - - PROCNAME("listFindTail"); - - if (!head) - return (DLLIST *)ERROR_PTR("head not defined", procName, NULL); - - for (cell = head; cell; cell = cell->next) { - if (cell->next == NULL) - return cell; - } - - return (DLLIST *)ERROR_PTR("tail not found !!", procName, NULL); -} - - -/*! - * \brief listGetCount() - * - * \param[in] head of list - * \return number of elements; 0 if no list or on error - */ -l_int32 -listGetCount(DLLIST *head) -{ -l_int32 count; -DLLIST *elem; - - PROCNAME("listGetCount"); - - if (!head) - return ERROR_INT("head not defined", procName, 0); - - count = 0; - for (elem = head; elem; elem = elem->next) - count++; - - return count; -} - - -/*! - * \brief listReverse() - * - * \param[in,out] phead list head; may be changed - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This reverses the list in-place. - *- */ -l_ok -listReverse(DLLIST **phead) -{ -void *obj; /* whatever */ -DLLIST *head, *rhead; - - PROCNAME("listReverse"); - - if (!phead) - return ERROR_INT("&head not defined", procName, 1); - if ((head = *phead) == NULL) - return ERROR_INT("head not defined", procName, 1); - - rhead = NULL; - while (head) { - obj = listRemoveFromHead(&head); - listAddToHead(&rhead, obj); - } - - *phead = rhead; - return 0; -} - - -/*! - * \brief listJoin() - * - * \param[in,out] phead1 head of first list; may be changed - * \param[in,out] phead2 head of second list; to be nulled - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The concatenated list is returned with head1 as the new head. - * (2) Both input ptrs must exist, though either can have the value NULL. - *- */ -l_ok -listJoin(DLLIST **phead1, - DLLIST **phead2) -{ -void *obj; -DLLIST *head1, *head2, *tail1; - - PROCNAME("listJoin"); - - if (!phead1) - return ERROR_INT("&head1 not defined", procName, 1); - if (!phead2) - return ERROR_INT("&head2 not defined", procName, 1); - - /* If no list2, just return list1 unchanged */ - if ((head2 = *phead2) == NULL) - return 0; - - /* If no list1, just return list2 */ - if ((head1 = *phead1) == NULL) { - *phead1 = head2; - *phead2 = NULL; - return 0; - } - - /* General case for concatenation into list 1 */ - tail1 = listFindTail(head1); - while (head2) { - obj = listRemoveFromHead(&head2); - listAddToTail(&head1, &tail1, obj); - } - *phead2 = NULL; - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/list.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/list.h deleted file mode 100644 index d207e79f..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/list.h +++ /dev/null @@ -1,90 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - - -#ifndef LEPTONICA_LIST_H -#define LEPTONICA_LIST_H - -/*! - * \file list.h - * - *
- * Cell for double-linked lists - * - * This allows composition of a list of cells with - * prev, next and data pointers. Generic data - * structures hang on the list cell data pointers. - * - * The list is not circular because that would add much - * complexity in traversing the list under general - * conditions where list cells can be added and removed. - * The only disadvantage of not having the head point to - * the last cell is that the list must be traversed to - * find its tail. However, this traversal is fast, and - * the listRemoveFromTail() function updates the tail - * so there is no searching overhead with repeated use. - * - * The list macros are used to run through a list, and their - * use is encouraged. They are invoked, e.g., as - * - * DLLIST *head, *elem; - * ... - * L_BEGIN_LIST_FORWARD(head, elem) - *- */ - -struct DoubleLinkedList -{ - struct DoubleLinkedList *prev; - struct DoubleLinkedList *next; - void *data; -}; -typedef struct DoubleLinkedList DLLIST; - - - /*! Simple list traverse macro - forward */ -#define L_BEGIN_LIST_FORWARD(head, element) \ - { \ - DLLIST *_leptvar_nextelem_; \ - for ((element) = (head); (element); (element) = _leptvar_nextelem_) { \ - _leptvar_nextelem_ = (element)->next; - - - /*! Simple list traverse macro - reverse */ -#define L_BEGIN_LIST_REVERSE(tail, element) \ - { \ - DLLIST *_leptvar_prevelem_; \ - for ((element) = (tail); (element); (element) = _leptvar_prevelem_) { \ - _leptvar_prevelem_ = (element)->prev; - - - /*! Simple list traverse macro - end of a list traverse */ -#define L_END_LIST }} - - -#endif /* LEPTONICA_LIST_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/map.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/map.c deleted file mode 100644 index 7817aa80..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/map.c +++ /dev/null @@ -1,264 +0,0 @@ -/*====================================================================* - - 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 map.c - *data > - * L_END_LIST - *
- * - * This is an interface for map and set functions, based on using - * red-black binary search trees. Because these trees are sorted, - * they are O(nlogn) to build. They allow logn insertion, find - * and deletion of elements. - * - * Both the map and set are ordered by key value, with unique keys. - * For the map, the elements are key/value pairs. - * For the set we only store unique, ordered keys, and the value - * (set to 0 in the implementation) is ignored. - * - * The keys for the map and set can be any of the three types in the - * l_rbtree_keytype enum. The values stored can be any of the four - * types in the rb_type union. - * - * In-order forward and reverse iterators are provided for maps and sets. - * To forward iterate over the map for any type of key (in this example, - * uint32), extracting integer values: - * - * L_AMAP *m = l_amapCreate(L_UINT_TYPE); - * [add elements to the map ...] - * L_AMAP_NODE *n = l_amapGetFirst(m); - * while (n) { - * l_int32 val = n->value.itype; - * // do something ... - * n = l_amapGetNext(n); - * } - * - * If the nodes are deleted during the iteration: - * - * L_AMAP *m = l_amapCreate(L_UINT_TYPE); - * [add elements to the map ...] - * L_AMAP_NODE *n = l_amapGetFirst(m); - * L_AMAP_NODE *nn; - * while (n) { - * nn = l_amapGetNext(n); - * l_int32 val = n->value.itype; - * l_uint32 key = n->key.utype; - * // do something ... - * l_amapDelete(m, n->key); - * n = nn; - * } - * - * See prog/maptest.c and prog/settest.c for more examples of usage. - * - * Interface to (a) map using a general key and storing general values - * L_AMAP *l_amapCreate() - * RB_TYPE *l_amapFind() - * void l_amapInsert() - * void l_amapDelete() - * void l_amapDestroy() - * L_AMAP_NODE *l_amapGetFirst() - * L_AMAP_NODE *l_amapGetNext() - * L_AMAP_NODE *l_amapGetLast() - * L_AMAP_NODE *l_amapGetPrev() - * l_int32 l_amapSize() - * - * Interface to (a) set using a general key - * L_ASET *l_asetCreate() - * RB_TYPE *l_asetFind() - * void l_asetInsert() - * void l_asetDelete() - * void l_asetDestroy() - * L_ASET_NODE *l_asetGetFirst() - * L_ASET_NODE *l_asetGetNext() - * L_ASET_NODE *l_asetGetLast() - * L_ASET_NODE *l_asetGetPrev() - * l_int32 l_asetSize() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * - * This is a game with a pedagogical slant. A maze is represented - * by a binary image. The ON pixels (fg) are walls. The goal is - * to navigate on OFF pixels (bg), using Manhattan steps - * (N, S, E, W), between arbitrary start and end positions. - * The problem is thus to find the shortest route between two points - * in a binary image that are 4-connected in the bg. This is done - * with a breadth-first search, implemented with a queue. - * We also use a queue of pointers to generate the maze (image). - * - * PIX *generateBinaryMaze() - * static MAZEEL *mazeelCreate() - * - * PIX *pixSearchBinaryMaze() - * static l_int32 localSearchForBackground() - * - * Generalizing a maze to a grayscale image, the search is - * now for the "shortest" or least cost path, for some given - * cost function. - * - * PIX *pixSearchGrayMaze() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) We have two input probability factors that determine the - * density of walls and average length of straight passages. - * When ranis < 1.0, you are more likely to generate a wall - * to the side than going forward. Enter 0.0 for either if - * you want to use the default values. - * (2) This is a type of percolation problem, and exhibits - * different phases for different parameters wallps and ranis. - * For larger values of these parameters, regions in the maze - * are not explored because the maze generator walls them - * off and cannot get through. The boundary between the - * two phases in this two-dimensional parameter space goes - * near these values: - * wallps ranis - * 0.35 1.00 - * 0.40 0.85 - * 0.45 0.70 - * 0.50 0.50 - * 0.55 0.40 - * 0.60 0.30 - * 0.65 0.25 - * 0.70 0.19 - * 0.75 0.15 - * 0.80 0.11 - * (3) Because there is a considerable amount of overhead in calling - * pixGetPixel() and pixSetPixel(), this function can be sped - * up with little effort using raster line pointers and the - * GET_DATA* and SET_DATA* macros. - *- */ -PIX * -generateBinaryMaze(l_int32 w, - l_int32 h, - l_int32 xi, - l_int32 yi, - l_float32 wallps, - l_float32 ranis) -{ -l_int32 x, y, dir; -l_uint32 val; -l_float32 frand, wallpf, testp; -MAZEEL *el, *elp; -PIX *pixd; /* the destination maze */ -PIX *pixm; /* for bookkeeping, to indicate pixels already visited */ -L_QUEUE *lq; - - /* On Windows, seeding is apparently necessary to get decent mazes. - * Windows rand() returns a value up to 2^15 - 1, whereas unix - * rand() returns a value up to 2^31 - 1. Therefore the generated - * mazes will differ on the two platforms. */ -#ifdef _WIN32 - srand(28*333); -#endif /* _WIN32 */ - - if (w < MinMazeWidth) - w = MinMazeWidth; - if (h < MinMazeHeight) - h = MinMazeHeight; - if (xi <= 0 || xi >= w) - xi = w / 6; - if (yi <= 0 || yi >= h) - yi = h / 5; - if (wallps < 0.05 || wallps > 0.95) - wallps = DefaultWallProbability; - if (ranis < 0.05 || ranis > 1.0) - ranis = DefaultAnisotropyRatio; - wallpf = wallps * ranis; - -#if DEBUG_MAZE - lept_stderr("(w, h) = (%d, %d), (xi, yi) = (%d, %d)\n", w, h, xi, yi); - lept_stderr("Using: prob(wall) = %7.4f, anisotropy factor = %7.4f\n", - wallps, ranis); -#endif /* DEBUG_MAZE */ - - /* These are initialized to OFF */ - pixd = pixCreate(w, h, 1); - pixm = pixCreate(w, h, 1); - - lq = lqueueCreate(0); - - /* Prime the queue with the first pixel; it is OFF */ - el = mazeelCreate(xi, yi, START_LOC); - pixSetPixel(pixm, xi, yi, 1); /* mark visited */ - lqueueAdd(lq, el); - - /* While we're at it ... */ - while (lqueueGetCount(lq) > 0) { - elp = (MAZEEL *)lqueueRemove(lq); - x = elp->x; - y = elp->y; - dir = elp->dir; - if (x > 0) { /* check west */ - pixGetPixel(pixm, x - 1, y, &val); - if (val == 0) { /* not yet visited */ - pixSetPixel(pixm, x - 1, y, 1); /* mark visited */ - frand = (l_float32)rand() / (l_float32)RAND_MAX; - testp = wallps; - if (dir == DIR_WEST) - testp = wallpf; - if (frand <= testp) { /* make it a wall */ - pixSetPixel(pixd, x - 1, y, 1); - } else { /* not a wall */ - el = mazeelCreate(x - 1, y, DIR_WEST); - lqueueAdd(lq, el); - } - } - } - if (y > 0) { /* check north */ - pixGetPixel(pixm, x, y - 1, &val); - if (val == 0) { /* not yet visited */ - pixSetPixel(pixm, x, y - 1, 1); /* mark visited */ - frand = (l_float32)rand() / (l_float32)RAND_MAX; - testp = wallps; - if (dir == DIR_NORTH) - testp = wallpf; - if (frand <= testp) { /* make it a wall */ - pixSetPixel(pixd, x, y - 1, 1); - } else { /* not a wall */ - el = mazeelCreate(x, y - 1, DIR_NORTH); - lqueueAdd(lq, el); - } - } - } - if (x < w - 1) { /* check east */ - pixGetPixel(pixm, x + 1, y, &val); - if (val == 0) { /* not yet visited */ - pixSetPixel(pixm, x + 1, y, 1); /* mark visited */ - frand = (l_float32)rand() / (l_float32)RAND_MAX; - testp = wallps; - if (dir == DIR_EAST) - testp = wallpf; - if (frand <= testp) { /* make it a wall */ - pixSetPixel(pixd, x + 1, y, 1); - } else { /* not a wall */ - el = mazeelCreate(x + 1, y, DIR_EAST); - lqueueAdd(lq, el); - } - } - } - if (y < h - 1) { /* check south */ - pixGetPixel(pixm, x, y + 1, &val); - if (val == 0) { /* not yet visited */ - pixSetPixel(pixm, x, y + 1, 1); /* mark visited */ - frand = (l_float32)rand() / (l_float32)RAND_MAX; - testp = wallps; - if (dir == DIR_SOUTH) - testp = wallpf; - if (frand <= testp) { /* make it a wall */ - pixSetPixel(pixd, x, y + 1, 1); - } else { /* not a wall */ - el = mazeelCreate(x, y + 1, DIR_SOUTH); - lqueueAdd(lq, el); - } - } - } - LEPT_FREE(elp); - } - - lqueueDestroy(&lq, TRUE); - pixDestroy(&pixm); - return pixd; -} - - -static MAZEEL * -mazeelCreate(l_int32 x, - l_int32 y, - l_int32 dir) -{ -MAZEEL *el; - - el = (MAZEEL *)LEPT_CALLOC(1, sizeof(MAZEEL)); - el->x = x; - el->y = y; - el->dir = dir; - return el; -} - - -/*---------------------------------------------------------------------* - * Binary maze search * - *---------------------------------------------------------------------*/ -/*! - * \brief pixSearchBinaryMaze() - * - * \param[in] pixs 1 bpp, maze - * \param[in] xi, yi beginning point; use same initial point - * that was used to generate the maze - * \param[in] xf, yf end point, or close to it - * \param[out] ppixd [optional] maze with path illustrated, or - * if no path possible, the part of the maze - * that was searched - * \return pta shortest path, or NULL if either no path - * exists or on error - * - *
- * Notes: - * (1) Because of the overhead in calling pixGetPixel() and - * pixSetPixel(), we have used raster line pointers and the - * GET_DATA* and SET_DATA* macros for many of the pix accesses. - * (2) Commentary: - * The goal is to find the shortest path between beginning and - * end points, without going through walls, and there are many - * ways to solve this problem. - * We use a queue to implement a breadth-first search. Two auxiliary - * "image" data structures can be used: one to mark the visited - * pixels and one to give the direction to the parent for each - * visited pixel. The first structure is used to avoid putting - * pixels on the queue more than once, and the second is used - * for retracing back to the origin, like the breadcrumbs in - * Hansel and Gretel. Each pixel taken off the queue is destroyed - * after it is used to locate the allowed neighbors. In fact, - * only one distance image is required, if you initialize it - * to some value that signifies "not yet visited." (We use - * a binary image for marking visited pixels because it is clearer.) - * This method for a simple search of a binary maze is implemented in - * pixSearchBinaryMaze(). - * An alternative method would store the (manhattan) distance - * from the start point with each pixel on the queue. The children - * of each pixel get a distance one larger than the parent. These - * values can be stored in an auxiliary distance map image - * that is constructed simultaneously with the search. Once the - * end point is reached, the distance map is used to backtrack - * along a minimum path. There may be several equal length - * minimum paths, any one of which can be chosen this way. - *- */ -PTA * -pixSearchBinaryMaze(PIX *pixs, - l_int32 xi, - l_int32 yi, - l_int32 xf, - l_int32 yf, - PIX **ppixd) -{ -l_int32 i, j, x, y, w, h, d, found; -l_uint32 val, rpixel, gpixel, bpixel; -void **lines1, **linem1, **linep8, **lined32; -MAZEEL *el, *elp; -PIX *pixd; /* the shortest path written on the maze image */ -PIX *pixm; /* for bookkeeping, to indicate pixels already visited */ -PIX *pixp; /* for bookkeeping, to indicate direction to parent */ -L_QUEUE *lq; -PTA *pta; - - PROCNAME("pixSearchBinaryMaze"); - - if (ppixd) *ppixd = NULL; - if (!pixs) - return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 1) - return (PTA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); - if (xi <= 0 || xi >= w) - return (PTA *)ERROR_PTR("xi not valid", procName, NULL); - if (yi <= 0 || yi >= h) - return (PTA *)ERROR_PTR("yi not valid", procName, NULL); - pixGetPixel(pixs, xi, yi, &val); - if (val != 0) - return (PTA *)ERROR_PTR("(xi,yi) not bg pixel", procName, NULL); - pixd = NULL; - pta = NULL; - - /* Find a bg pixel near input point (xf, yf) */ - localSearchForBackground(pixs, &xf, &yf, 5); - -#if DEBUG_MAZE - lept_stderr("(xi, yi) = (%d, %d), (xf, yf) = (%d, %d)\n", - xi, yi, xf, yf); -#endif /* DEBUG_MAZE */ - - pixm = pixCreate(w, h, 1); /* initialized to OFF */ - pixp = pixCreate(w, h, 8); /* direction to parent stored as enum val */ - lines1 = pixGetLinePtrs(pixs, NULL); - linem1 = pixGetLinePtrs(pixm, NULL); - linep8 = pixGetLinePtrs(pixp, NULL); - - lq = lqueueCreate(0); - - /* Prime the queue with the first pixel; it is OFF */ - el = mazeelCreate(xi, yi, 0); /* don't need direction here */ - pixSetPixel(pixm, xi, yi, 1); /* mark visited */ - lqueueAdd(lq, el); - - /* Fill up the pix storing directions to parents, - * stopping when we hit the point (xf, yf) */ - found = FALSE; - while (lqueueGetCount(lq) > 0) { - elp = (MAZEEL *)lqueueRemove(lq); - x = elp->x; - y = elp->y; - if (x == xf && y == yf) { - found = TRUE; - LEPT_FREE(elp); - break; - } - - if (x > 0) { /* check to west */ - val = GET_DATA_BIT(linem1[y], x - 1); - if (val == 0) { /* not yet visited */ - SET_DATA_BIT(linem1[y], x - 1); /* mark visited */ - val = GET_DATA_BIT(lines1[y], x - 1); - if (val == 0) { /* bg, not a wall */ - SET_DATA_BYTE(linep8[y], x - 1, DIR_EAST); /* parent E */ - el = mazeelCreate(x - 1, y, 0); - lqueueAdd(lq, el); - } - } - } - if (y > 0) { /* check north */ - val = GET_DATA_BIT(linem1[y - 1], x); - if (val == 0) { /* not yet visited */ - SET_DATA_BIT(linem1[y - 1], x); /* mark visited */ - val = GET_DATA_BIT(lines1[y - 1], x); - if (val == 0) { /* bg, not a wall */ - SET_DATA_BYTE(linep8[y - 1], x, DIR_SOUTH); /* parent S */ - el = mazeelCreate(x, y - 1, 0); - lqueueAdd(lq, el); - } - } - } - if (x < w - 1) { /* check east */ - val = GET_DATA_BIT(linem1[y], x + 1); - if (val == 0) { /* not yet visited */ - SET_DATA_BIT(linem1[y], x + 1); /* mark visited */ - val = GET_DATA_BIT(lines1[y], x + 1); - if (val == 0) { /* bg, not a wall */ - SET_DATA_BYTE(linep8[y], x + 1, DIR_WEST); /* parent W */ - el = mazeelCreate(x + 1, y, 0); - lqueueAdd(lq, el); - } - } - } - if (y < h - 1) { /* check south */ - val = GET_DATA_BIT(linem1[y + 1], x); - if (val == 0) { /* not yet visited */ - SET_DATA_BIT(linem1[y + 1], x); /* mark visited */ - val = GET_DATA_BIT(lines1[y + 1], x); - if (val == 0) { /* bg, not a wall */ - SET_DATA_BYTE(linep8[y + 1], x, DIR_NORTH); /* parent N */ - el = mazeelCreate(x, y + 1, 0); - lqueueAdd(lq, el); - } - } - } - LEPT_FREE(elp); - } - - lqueueDestroy(&lq, TRUE); - pixDestroy(&pixm); - LEPT_FREE(linem1); - - if (ppixd) { - pixd = pixUnpackBinary(pixs, 32, 1); - *ppixd = pixd; - } - composeRGBPixel(255, 0, 0, &rpixel); /* start point */ - composeRGBPixel(0, 255, 0, &gpixel); - composeRGBPixel(0, 0, 255, &bpixel); /* end point */ - - if (found) { - L_INFO(" Path found\n", procName); - pta = ptaCreate(0); - x = xf; - y = yf; - while (1) { - ptaAddPt(pta, x, y); - if (x == xi && y == yi) - break; - if (pixd) /* write 'gpixel' onto the path */ - pixSetPixel(pixd, x, y, gpixel); - pixGetPixel(pixp, x, y, &val); - if (val == DIR_NORTH) - y--; - else if (val == DIR_SOUTH) - y++; - else if (val == DIR_EAST) - x++; - else if (val == DIR_WEST) - x--; - } - } else { - L_INFO(" No path found\n", procName); - if (pixd) { /* paint all visited locations */ - lined32 = pixGetLinePtrs(pixd, NULL); - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - if (GET_DATA_BYTE(linep8[i], j) != 0) - SET_DATA_FOUR_BYTES(lined32[i], j, gpixel); - } - } - LEPT_FREE(lined32); - } - } - if (pixd) { - pixSetPixel(pixd, xi, yi, rpixel); - pixSetPixel(pixd, xf, yf, bpixel); - } - - pixDestroy(&pixp); - LEPT_FREE(lines1); - LEPT_FREE(linep8); - return pta; -} - - -/*! - * \brief localSearchForBackground() - * - * \param[in] pix - * \param[out] px, py starting position for search; return found position - * \param[in] maxrad max distance to search from starting location - * \return 0 if bg pixel found; 1 if not found - */ -static l_int32 -localSearchForBackground(PIX *pix, - l_int32 *px, - l_int32 *py, - l_int32 maxrad) -{ -l_int32 x, y, w, h, r, i, j; -l_uint32 val; - - x = *px; - y = *py; - pixGetPixel(pix, x, y, &val); - if (val == 0) return 0; - - /* For each value of r, restrict the search to the boundary - * pixels in a square centered on (x,y), clipping to the - * image boundaries if necessary. */ - pixGetDimensions(pix, &w, &h, NULL); - for (r = 1; r < maxrad; r++) { - for (i = -r; i <= r; i++) { - if (y + i < 0 || y + i >= h) - continue; - for (j = -r; j <= r; j++) { - if (x + j < 0 || x + j >= w) - continue; - if (L_ABS(i) != r && L_ABS(j) != r) /* not on "r ring" */ - continue; - pixGetPixel(pix, x + j, y + i, &val); - if (val == 0) { - *px = x + j; - *py = y + i; - return 0; - } - } - } - } - return 1; -} - - - -/*---------------------------------------------------------------------* - * Gray maze search * - *---------------------------------------------------------------------*/ -/*! - * \brief pixSearchGrayMaze() - * - * \param[in] pixs 1 bpp, maze - * \param[in] xi, yi beginning point; use same initial point - * that was used to generate the maze - * \param[in] xf, yf end point, or close to it - * \param[out] ppixd [optional] maze with path illustrated, or - * if no path possible, the part of the maze - * that was searched - * \return pta shortest path, or NULL if either no path - * exists or on error - * - * Commentary: - * Consider first a slight generalization of the binary maze - * search problem. Suppose that you can go through walls, - * but the cost is higher say, an increment of 3 to go into - * a wall pixel rather than 1? You're still trying to find - * the shortest path. One way to do this is with an ordered - * queue, and a simple way to visualize an ordered queue is as - * a set of stacks, each stack being marked with the distance - * of each pixel in the stack from the start. We place the - * start pixel in stack 0, pop it, and process its 4 children. - * Each pixel is given a distance that is incremented from that - * of its parent 0 in this case, depending on if it is a wall - * pixel or not. That value may be recorded on a distance map, - * according to the algorithm below. For children of the first - * pixel, those not on a wall go in stack 1, and wall - * children go in stack 3. Stack 0 being emptied, the process - * then continues with pixels being popped from stack 1. - * Here is the algorithm for each child pixel. The pixel's - * distance value, were it to be placed on a stack, is compared - * with the value for it that is on the distance map. There - * are three possible cases: - * 1 If the pixel has not yet been registered, it is pushed - * on its stack and the distance is written to the map. - * 2 If it has previously been registered with a higher distance, - * the distance on the map is relaxed to that of the - * current pixel, which is then placed on its stack. - * 3 If it has previously been registered with an equal - * or lower value, the pixel is discarded. - * The pixels are popped and processed successively from - * stack 1, and when stack 1 is empty, popping starts on stack 2. - * This continues until the destination pixel is popped off - * a stack. The minimum path is then derived from the distance map, - * going back from the end point as before. This is just Dijkstra's - * algorithm for a directed graph; here, the underlying graph - * consisting of the pixels and four edges connecting each pixel - * to its 4-neighbor is a special case of a directed graph, where - * each edge is bi-directional. The implementation of this generalized - * maze search is left as an exercise to the reader. - * - * Let's generalize a bit further. Suppose the "maze" is just - * a grayscale image -- think of it as an elevation map. The cost - * of moving on this surface depends on the height, or the gradient, - * or whatever you want. All that is required is that the cost - * is specified and non-negative on each link between adjacent - * pixels. Now the problem becomes: find the least cost path - * moving on this surface between two specified end points. - * For example, if the cost across an edge between two pixels - * depends on the "gradient", you can use: - * cost = 1 + L_ABSdeltaV - * where deltaV is the difference in value between two adjacent - * pixels. If the costs are all integers, we can still use an array - * of stacks to avoid ordering the queue e.g., by using a heap sort. - * This is a neat problem, because you don't even have to build a - * maze -- you can can use it on any grayscale image! - * - * Rather than using an array of stacks, a more practical - * approach is to implement with a priority queue, which is - * a queue that is sorted so that the elements with the largest - * or smallest key values always come off first. The - * priority queue is efficiently implemented as a heap, and - * this is how we do it. Suppose you run the algorithm - * using a priority queue, doing the bookkeeping with an - * auxiliary image data structure that saves the distance of - * each pixel put on the queue as before, according to the method - * described above. We implement it as a 2-way choice by - * initializing the distance array to a large value and putting - * a pixel on the queue if its distance is less than the value - * found on the array. When you finally pop the end pixel from - * the queue, you're done, and you can trace the path backward, - * either always going downhill or using an auxiliary image to - * give you the direction to go at each step. This is implemented - * here in searchGrayMaze. - * - * Do we really have to use a sorted queue? Can we solve this - * generalized maze with an unsorted queue of pixels? Or even - * an unsorted stack, doing a depth-first search (DFS)? - * Consider a different algorithm for this generalized maze, where - * we travel again breadth first, but this time use a single, - * unsorted queue. An auxiliary image is used as before to - * store the distances and to determine if pixels get pushed - * on the stack or dropped. As before, we must allow pixels - * to be revisited, with relaxation of the distance if a shorter - * path arrives later. As a result, we will in general have - * multiple instances of the same pixel on the stack with different - * distances. However, because the queue is not ordered, some of - * these pixels will be popped when another instance with a lower - * distance is still on the stack. Here, we're just popping them - * in the order they go on, rather than setting up a priority - * based on minimum distance. Thus, unlike the priority queue, - * when a pixel is popped we have to check the distance map to - * see if a pixel with a lower distance has been put on the queue, - * and, if so, we discard the pixel we just popped. So the - * "while" loop looks like this: - * ~ pop a pixel from the queue - * ~ check its distance against the distance stored in the - * distance map; if larger, discard - * ~ otherwise, for each of its neighbors: - * ~ compute its distance from the start pixel - * ~ compare this distance with that on the distance map: - * ~ if the distance map value higher, relax the distance - * and push the pixel on the queue - * ~ if the distance map value is lower, discard the pixel - * - * How does this loop terminate? Before, with an ordered queue, - * it terminates when you pop the end pixel. But with an unordered - * queue or stack, the first time you hit the end pixel, the - * distance is not guaranteed to be correct, because the pixels - * along the shortest path may not have yet been visited and relaxed. - * Because the shortest path can theoretically go anywhere, - * we must keep going. How do we know when to stop? Dijkstra - * uses an ordered queue to systematically remove nodes from - * further consideration. Each time a pixel is popped, we're - * done with it; it's "finalized" in the Dijkstra sense because - * we know the shortest path to it. However, with an unordered - * queue, the brute force answer is: stop when the queue - * or stack is empty, because then every pixel in the image - * has been assigned its minimum "distance" from the start pixel. - * - * This is similar to the situation when you use a stack for the - * simpler uniform-step problem: with breadth-first search BFS - * the pixels on the queue are automatically ordered, so you are - * done when you locate the end pixel as a neighbor of a popped pixel; - * whereas depth-first search DFS, using a stack, requires, - * in general, a search of every accessible pixel. Further, if - * a pixel is revisited with a smaller distance, that distance is - * recorded and the pixel is put on the stack again. - * - * But surely, you ask, can't we stop sooner? What if the - * start and end pixels are very close to each other? - * OK, suppose they are, and you have very high walls and a - * long snaking level path that is actually the minimum cost. - * That long path can wind back and forth across the entire - * maze many times before ending up at the end point, which - * could be just over a wall from the start. With the unordered - * queue, you very quickly get a high distance for the end - * pixel, which will be relaxed to the minimum distance only - * after all the pixels of the path have been visited and placed - * on the queue, multiple times for many of them. So that's the - * price for not ordering the queue! - */ -PTA * -pixSearchGrayMaze(PIX *pixs, - l_int32 xi, - l_int32 yi, - l_int32 xf, - l_int32 yf, - PIX **ppixd) -{ -l_int32 x, y, w, h, d; -l_uint32 val, valr, vals, rpixel, gpixel, bpixel; -void **lines8, **liner32, **linep8; -l_int32 cost, dist, distparent, sival, sivals; -MAZEEL *el, *elp; -PIX *pixd; /* optionally plot the path on this RGB version of pixs */ -PIX *pixr; /* for bookkeeping, to indicate the minimum distance */ - /* to pixels already visited */ -PIX *pixp; /* for bookkeeping, to indicate direction to parent */ -L_HEAP *lh; -PTA *pta; - - PROCNAME("pixSearchGrayMaze"); - - if (ppixd) *ppixd = NULL; - if (!pixs) - return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return (PTA *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (xi <= 0 || xi >= w) - return (PTA *)ERROR_PTR("xi not valid", procName, NULL); - if (yi <= 0 || yi >= h) - return (PTA *)ERROR_PTR("yi not valid", procName, NULL); - pixd = NULL; - pta = NULL; - - /* Allocate stuff */ - pixr = pixCreate(w, h, 32); - pixSetAll(pixr); /* initialize to max value */ - pixp = pixCreate(w, h, 8); /* direction to parent stored as enum val */ - lines8 = pixGetLinePtrs(pixs, NULL); - linep8 = pixGetLinePtrs(pixp, NULL); - liner32 = pixGetLinePtrs(pixr, NULL); - lh = lheapCreate(0, L_SORT_INCREASING); /* always remove closest pixels */ - - /* Prime the heap with the first pixel */ - pixGetPixel(pixs, xi, yi, &val); - el = mazeelCreate(xi, yi, 0); /* don't need direction here */ - el->distance = 0; - pixGetPixel(pixs, xi, yi, &val); - el->val = val; - pixSetPixel(pixr, xi, yi, 0); /* distance is 0 */ - lheapAdd(lh, el); - - /* Breadth-first search with priority queue (implemented by - a heap), labeling direction to parents in pixp and minimum - distance to visited pixels in pixr. Stop when we pull - the destination point (xf, yf) off the queue. */ - while (lheapGetCount(lh) > 0) { - elp = (MAZEEL *)lheapRemove(lh); - if (!elp) { - L_ERROR("heap broken!!\n", procName); - goto cleanup_stuff; - } - x = elp->x; - y = elp->y; - if (x == xf && y == yf) { /* exit condition */ - LEPT_FREE(elp); - break; - } - distparent = (l_int32)elp->distance; - val = elp->val; - sival = val; - - if (x > 0) { /* check to west */ - vals = GET_DATA_BYTE(lines8[y], x - 1); - valr = GET_DATA_FOUR_BYTES(liner32[y], x - 1); - sivals = (l_int32)vals; - cost = 1 + L_ABS(sivals - sival); /* cost to move to this pixel */ - dist = distparent + cost; - if (dist < valr) { /* shortest path so far to this pixel */ - SET_DATA_FOUR_BYTES(liner32[y], x - 1, dist); /* new dist */ - SET_DATA_BYTE(linep8[y], x - 1, DIR_EAST); /* parent to E */ - el = mazeelCreate(x - 1, y, 0); - el->val = vals; - el->distance = dist; - lheapAdd(lh, el); - } - } - if (y > 0) { /* check north */ - vals = GET_DATA_BYTE(lines8[y - 1], x); - valr = GET_DATA_FOUR_BYTES(liner32[y - 1], x); - sivals = (l_int32)vals; - cost = 1 + L_ABS(sivals - sival); /* cost to move to this pixel */ - dist = distparent + cost; - if (dist < valr) { /* shortest path so far to this pixel */ - SET_DATA_FOUR_BYTES(liner32[y - 1], x, dist); /* new dist */ - SET_DATA_BYTE(linep8[y - 1], x, DIR_SOUTH); /* parent to S */ - el = mazeelCreate(x, y - 1, 0); - el->val = vals; - el->distance = dist; - lheapAdd(lh, el); - } - } - if (x < w - 1) { /* check east */ - vals = GET_DATA_BYTE(lines8[y], x + 1); - valr = GET_DATA_FOUR_BYTES(liner32[y], x + 1); - sivals = (l_int32)vals; - cost = 1 + L_ABS(sivals - sival); /* cost to move to this pixel */ - dist = distparent + cost; - if (dist < valr) { /* shortest path so far to this pixel */ - SET_DATA_FOUR_BYTES(liner32[y], x + 1, dist); /* new dist */ - SET_DATA_BYTE(linep8[y], x + 1, DIR_WEST); /* parent to W */ - el = mazeelCreate(x + 1, y, 0); - el->val = vals; - el->distance = dist; - lheapAdd(lh, el); - } - } - if (y < h - 1) { /* check south */ - vals = GET_DATA_BYTE(lines8[y + 1], x); - valr = GET_DATA_FOUR_BYTES(liner32[y + 1], x); - sivals = (l_int32)vals; - cost = 1 + L_ABS(sivals - sival); /* cost to move to this pixel */ - dist = distparent + cost; - if (dist < valr) { /* shortest path so far to this pixel */ - SET_DATA_FOUR_BYTES(liner32[y + 1], x, dist); /* new dist */ - SET_DATA_BYTE(linep8[y + 1], x, DIR_NORTH); /* parent to N */ - el = mazeelCreate(x, y + 1, 0); - el->val = vals; - el->distance = dist; - lheapAdd(lh, el); - } - } - LEPT_FREE(elp); - } - - lheapDestroy(&lh, TRUE); - - if (ppixd) { - pixd = pixConvert8To32(pixs); - *ppixd = pixd; - } - composeRGBPixel(255, 0, 0, &rpixel); /* start point */ - composeRGBPixel(0, 255, 0, &gpixel); - composeRGBPixel(0, 0, 255, &bpixel); /* end point */ - - x = xf; - y = yf; - pta = ptaCreate(0); - while (1) { /* write path onto pixd */ - ptaAddPt(pta, x, y); - if (x == xi && y == yi) - break; - if (pixd) - pixSetPixel(pixd, x, y, gpixel); - pixGetPixel(pixp, x, y, &val); - if (val == DIR_NORTH) - y--; - else if (val == DIR_SOUTH) - y++; - else if (val == DIR_EAST) - x++; - else if (val == DIR_WEST) - x--; - pixGetPixel(pixr, x, y, &val); - -#if DEBUG_PATH - lept_stderr("(x,y) = (%d, %d); dist = %d\n", x, y, val); -#endif /* DEBUG_PATH */ - - } - if (pixd) { - pixSetPixel(pixd, xi, yi, rpixel); - pixSetPixel(pixd, xf, yf, bpixel); - } - -cleanup_stuff: - lheapDestroy(&lh, TRUE); - pixDestroy(&pixp); - pixDestroy(&pixr); - LEPT_FREE(lines8); - LEPT_FREE(linep8); - LEPT_FREE(liner32); - return pta; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/morph.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/morph.c deleted file mode 100644 index 019ba3cc..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/morph.c +++ /dev/null @@ -1,1915 +0,0 @@ -/*====================================================================* - - 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 morph.c - *
- * - * Generic binary morphological ops implemented with rasterop - * PIX *pixDilate() - * PIX *pixErode() - * PIX *pixHMT() - * PIX *pixOpen() - * PIX *pixClose() - * PIX *pixCloseSafe() - * PIX *pixOpenGeneralized() - * PIX *pixCloseGeneralized() - * - * Binary morphological (raster) ops with brick Sels - * PIX *pixDilateBrick() - * PIX *pixErodeBrick() - * PIX *pixOpenBrick() - * PIX *pixCloseBrick() - * PIX *pixCloseSafeBrick() - * - * Binary composed morphological (raster) ops with brick Sels - * l_int32 selectComposableSels() - * l_int32 selectComposableSizes() - * PIX *pixDilateCompBrick() - * PIX *pixErodeCompBrick() - * PIX *pixOpenCompBrick() - * PIX *pixCloseCompBrick() - * PIX *pixCloseSafeCompBrick() - * - * Functions associated with boundary conditions - * void resetMorphBoundaryCondition() - * l_int32 getMorphBorderPixelColor() - * - * Static helpers for arg processing - * static PIX *processMorphArgs1() - * static PIX *processMorphArgs2() - * - * You are provided with many simple ways to do binary morphology. - * In particular, if you are using brick Sels, there are six - * convenient methods, all specially tailored for separable operations - * on brick Sels. A "brick" Sel is a Sel that is a rectangle - * of solid SEL_HITs with the origin at or near the center. - * Note that a brick Sel can have one dimension of size 1. - * This is very common. All the brick Sel operations are - * separable, meaning the operation is done first in the horizontal - * direction and then in the vertical direction. If one of the - * dimensions is 1, this is a special case where the operation is - * only performed in the other direction. - * - * These six brick Sel methods are enumerated as follows: - * - * (1) Brick Sels: pix*Brick(), where * = {Dilate, Erode, Open, Close}. - * These are separable rasterop implementations. The Sels are - * automatically generated, used, and destroyed at the end. - * You can get the result as a new Pix, in-place back into the src Pix, - * or written to another existing Pix. - * - * (2) Brick Sels: pix*CompBrick(), where * = {Dilate, Erode, Open, Close}. - * These are separable, 2-way composite, rasterop implementations. - * The Sels are automatically generated, used, and destroyed at the end. - * You can get the result as a new Pix, in-place back into the src Pix, - * or written to another existing Pix. For large Sels, these are - * considerably faster than the corresponding pix*Brick() functions. - * N.B.: The size of the Sels that are actually used are typically - * close to, but not exactly equal to, the size input to the function. - * - * (3) Brick Sels: pix*BrickDwa(), where * = {Dilate, Erode, Open, Close}. - * These are separable dwa (destination word accumulation) - * implementations. They use auto-gen'd dwa code. You can get - * the result as a new Pix, in-place back into the src Pix, - * or written to another existing Pix. This is typically - * about 3x faster than the analogous rasterop pix*Brick() - * function, but it has the limitation that the Sel size must - * be less than 63. This is pre-set to work on a number - * of pre-generated Sels. If you want to use other Sels, the - * code can be auto-gen'd for them; see the instructions in morphdwa.c. - * - * (4) Same as (1), but you run it through pixMorphSequence(), with - * the sequence string either compiled in or generated using snprintf. - * All intermediate images and Sels are created, used and destroyed. - * You always get the result as a new Pix. For example, you can - * specify a separable 11 x 17 brick opening as "o11.17", - * or you can specify the horizontal and vertical operations - * explicitly as "o11.1 + o1.11". See morphseq.c for details. - * - * (5) Same as (2), but you run it through pixMorphCompSequence(), with - * the sequence string either compiled in or generated using snprintf. - * All intermediate images and Sels are created, used and destroyed. - * You always get the result as a new Pix. See morphseq.c for details. - * - * (6) Same as (3), but you run it through pixMorphSequenceDwa(), with - * the sequence string either compiled in or generated using snprintf. - * All intermediate images and Sels are created, used and destroyed. - * You always get the result as a new Pix. See morphseq.c for details. - * - * If you are using Sels that are not bricks, you have two choices: - * (a) simplest: use the basic rasterop implementations (pixDilate(), ...) - * (b) fastest: generate the destination word accumumlation (dwa) - * code for your Sels and compile it with the library. - * - * For an example, see flipdetect.c, which gives implementations - * using hit-miss Sels with both the rasterop and dwa versions. - * For the latter, the dwa code resides in fliphmtgen.c, and it - * was generated by prog/flipselgen.c. Both the rasterop and dwa - * implementations are tested by prog/fliptest.c. - * - * A global constant MORPH_BC is used to set the boundary conditions - * for rasterop-based binary morphology. MORPH_BC, in morph.c, - * is set by default to ASYMMETRIC_MORPH_BC for a non-symmetric - * convention for boundary pixels in dilation and erosion: - * All pixels outside the image are assumed to be OFF - * for both dilation and erosion. - * To use a symmetric definition, see comments in pixErode() - * and reset MORPH_BC to SYMMETRIC_MORPH_BC, using - * resetMorphBoundaryCondition(). - * - * Boundary artifacts are possible in closing when the non-symmetric - * boundary conditions are used, because foreground pixels very close - * to the edge can be removed. This can be avoided by using either - * the symmetric boundary conditions or the function pixCloseSafe(), - * which adds a border before the operation and removes it afterwards. - * - * The hit-miss transform (HMT) is the bit-and of 2 erosions: - * (erosion of the src by the hits) & (erosion of the bit-inverted - * src by the misses) - * - * The 'generalized opening' is an HMT followed by a dilation that uses - * only the hits of the hit-miss Sel. - * The 'generalized closing' is a dilation (again, with the hits - * of a hit-miss Sel), followed by the HMT. - * Both of these 'generalized' functions are idempotent. - * - * These functions are extensively tested in prog/binmorph1_reg.c, - * prog/binmorph2_reg.c, and prog/binmorph3_reg.c. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This dilates src using hits in Sel. - * (2) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (3) For clarity, if the case is known, use these patterns: - * (a) pixd = pixDilate(NULL, pixs, ...); - * (b) pixDilate(pixs, pixs, ...); - * (c) pixDilate(pixd, pixs, ...); - * (4) The size of the result is determined by pixs. - *- */ -PIX * -pixDilate(PIX *pixd, - PIX *pixs, - SEL *sel) -{ -l_int32 i, j, w, h, sx, sy, cx, cy, seldata; -PIX *pixt; - - PROCNAME("pixDilate"); - - if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL) - return (PIX *)ERROR_PTR("processMorphArgs1 failed", procName, pixd); - - pixGetDimensions(pixs, &w, &h, NULL); - selGetParameters(sel, &sy, &sx, &cy, &cx); - pixClearAll(pixd); - for (i = 0; i < sy; i++) { - for (j = 0; j < sx; j++) { - seldata = sel->data[i][j]; - if (seldata == 1) { /* src | dst */ - pixRasterop(pixd, j - cx, i - cy, w, h, PIX_SRC | PIX_DST, - pixt, 0, 0); - } - } - } - - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixErode() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] sel - * \return pixd - * - *
- * Notes: - * (1) This erodes src using hits in Sel. - * (2) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (3) For clarity, if the case is known, use these patterns: - * (a) pixd = pixErode(NULL, pixs, ...); - * (b) pixErode(pixs, pixs, ...); - * (c) pixErode(pixd, pixs, ...); - * (4) The size of the result is determined by pixs. - *- */ -PIX * -pixErode(PIX *pixd, - PIX *pixs, - SEL *sel) -{ -l_int32 i, j, w, h, sx, sy, cx, cy, seldata; -l_int32 xp, yp, xn, yn; -PIX *pixt; - - PROCNAME("pixErode"); - - if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL) - return (PIX *)ERROR_PTR("processMorphArgs1 failed", procName, pixd); - - pixGetDimensions(pixs, &w, &h, NULL); - selGetParameters(sel, &sy, &sx, &cy, &cx); - pixSetAll(pixd); - for (i = 0; i < sy; i++) { - for (j = 0; j < sx; j++) { - seldata = sel->data[i][j]; - if (seldata == 1) { /* src & dst */ - pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC & PIX_DST, - pixt, 0, 0); - } - } - } - - /* Clear near edges. We do this for the asymmetric boundary - * condition convention that implements erosion assuming all - * pixels surrounding the image are OFF. If you use a - * use a symmetric b.c. convention, where the erosion is - * implemented assuming pixels surrounding the image - * are ON, these operations are omitted. */ - if (MORPH_BC == ASYMMETRIC_MORPH_BC) { - selFindMaxTranslations(sel, &xp, &yp, &xn, &yn); - if (xp > 0) - pixRasterop(pixd, 0, 0, xp, h, PIX_CLR, NULL, 0, 0); - if (xn > 0) - pixRasterop(pixd, w - xn, 0, xn, h, PIX_CLR, NULL, 0, 0); - if (yp > 0) - pixRasterop(pixd, 0, 0, w, yp, PIX_CLR, NULL, 0, 0); - if (yn > 0) - pixRasterop(pixd, 0, h - yn, w, yn, PIX_CLR, NULL, 0, 0); - } - - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixHMT() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] sel - * \return pixd - * - *
- * Notes: - * (1) The hit-miss transform erodes the src, using both hits - * and misses in the Sel. It ANDs the shifted src for hits - * and ANDs the inverted shifted src for misses. - * (2) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (3) For clarity, if the case is known, use these patterns: - * (a) pixd = pixHMT(NULL, pixs, ...); - * (b) pixHMT(pixs, pixs, ...); - * (c) pixHMT(pixd, pixs, ...); - * (4) The size of the result is determined by pixs. - *- */ -PIX * -pixHMT(PIX *pixd, - PIX *pixs, - SEL *sel) -{ -l_int32 i, j, w, h, sx, sy, cx, cy, firstrasterop, seldata; -l_int32 xp, yp, xn, yn; -PIX *pixt; - - PROCNAME("pixHMT"); - - if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL) - return (PIX *)ERROR_PTR("processMorphArgs1 failed", procName, pixd); - - pixGetDimensions(pixs, &w, &h, NULL); - selGetParameters(sel, &sy, &sx, &cy, &cx); - firstrasterop = TRUE; - for (i = 0; i < sy; i++) { - for (j = 0; j < sx; j++) { - seldata = sel->data[i][j]; - if (seldata == 1) { /* hit */ - if (firstrasterop == TRUE) { /* src only */ - pixClearAll(pixd); - pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC, - pixt, 0, 0); - firstrasterop = FALSE; - } else { /* src & dst */ - pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC & PIX_DST, - pixt, 0, 0); - } - } else if (seldata == 2) { /* miss */ - if (firstrasterop == TRUE) { /* ~src only */ - pixSetAll(pixd); - pixRasterop(pixd, cx - j, cy - i, w, h, PIX_NOT(PIX_SRC), - pixt, 0, 0); - firstrasterop = FALSE; - } else { /* ~src & dst */ - pixRasterop(pixd, cx - j, cy - i, w, h, - PIX_NOT(PIX_SRC) & PIX_DST, - pixt, 0, 0); - } - } - } - } - - /* Clear near edges */ - selFindMaxTranslations(sel, &xp, &yp, &xn, &yn); - if (xp > 0) - pixRasterop(pixd, 0, 0, xp, h, PIX_CLR, NULL, 0, 0); - if (xn > 0) - pixRasterop(pixd, w - xn, 0, xn, h, PIX_CLR, NULL, 0, 0); - if (yp > 0) - pixRasterop(pixd, 0, 0, w, yp, PIX_CLR, NULL, 0, 0); - if (yn > 0) - pixRasterop(pixd, 0, h - yn, w, yn, PIX_CLR, NULL, 0, 0); - - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixOpen() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] sel - * \return pixd - * - *
- * Notes: - * (1) Generic morphological opening, using hits in the Sel. - * (2) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (3) For clarity, if the case is known, use these patterns: - * (a) pixd = pixOpen(NULL, pixs, ...); - * (b) pixOpen(pixs, pixs, ...); - * (c) pixOpen(pixd, pixs, ...); - * (4) The size of the result is determined by pixs. - *- */ -PIX * -pixOpen(PIX *pixd, - PIX *pixs, - SEL *sel) -{ -PIX *pixt; - - PROCNAME("pixOpen"); - - if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL) - return (PIX *)ERROR_PTR("pixd not returned", procName, pixd); - - if ((pixt = pixErode(NULL, pixs, sel)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, pixd); - pixDilate(pixd, pixt, sel); - pixDestroy(&pixt); - - return pixd; -} - - -/*! - * \brief pixClose() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] sel - * \return pixd - * - *
- * Notes: - * (1) Generic morphological closing, using hits in the Sel. - * (2) This implementation is a strict dual of the opening if - * symmetric boundary conditions are used (see notes at top - * of this file). - * (3) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (4) For clarity, if the case is known, use these patterns: - * (a) pixd = pixClose(NULL, pixs, ...); - * (b) pixClose(pixs, pixs, ...); - * (c) pixClose(pixd, pixs, ...); - * (5) The size of the result is determined by pixs. - *- */ -PIX * -pixClose(PIX *pixd, - PIX *pixs, - SEL *sel) -{ -PIX *pixt; - - PROCNAME("pixClose"); - - if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL) - return (PIX *)ERROR_PTR("pixd not returned", procName, pixd); - - if ((pixt = pixDilate(NULL, pixs, sel)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, pixd); - pixErode(pixd, pixt, sel); - pixDestroy(&pixt); - - return pixd; -} - - -/*! - * \brief pixCloseSafe() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] sel - * \return pixd - * - *
- * Notes: - * (1) Generic morphological closing, using hits in the Sel. - * (2) If non-symmetric boundary conditions are used, this - * function adds a border of OFF pixels that is of - * sufficient size to avoid losing pixels from the dilation, - * and it removes the border after the operation is finished. - * It thus enforces a correct extensive result for closing. - * (3) If symmetric b.c. are used, it is not necessary to add - * and remove this border. - * (4) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (5) For clarity, if the case is known, use these patterns: - * (a) pixd = pixCloseSafe(NULL, pixs, ...); - * (b) pixCloseSafe(pixs, pixs, ...); - * (c) pixCloseSafe(pixd, pixs, ...); - * (6) The size of the result is determined by pixs. - *- */ -PIX * -pixCloseSafe(PIX *pixd, - PIX *pixs, - SEL *sel) -{ -l_int32 xp, yp, xn, yn, xmax, xbord; -PIX *pixt1, *pixt2; - - PROCNAME("pixCloseSafe"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (!sel) - return (PIX *)ERROR_PTR("sel not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - - /* Symmetric b.c. handles correctly without added pixels */ - if (MORPH_BC == SYMMETRIC_MORPH_BC) - return pixClose(pixd, pixs, sel); - - selFindMaxTranslations(sel, &xp, &yp, &xn, &yn); - xmax = L_MAX(xp, xn); - xbord = 32 * ((xmax + 31) / 32); /* full 32 bit words */ - - if ((pixt1 = pixAddBorderGeneral(pixs, xbord, xbord, yp, yn, 0)) == NULL) - return (PIX *)ERROR_PTR("pixt1 not made", procName, pixd); - pixClose(pixt1, pixt1, sel); - if ((pixt2 = pixRemoveBorderGeneral(pixt1, xbord, xbord, yp, yn)) == NULL) - return (PIX *)ERROR_PTR("pixt2 not made", procName, pixd); - pixDestroy(&pixt1); - - if (!pixd) - return pixt2; - - pixCopy(pixd, pixt2); - pixDestroy(&pixt2); - return pixd; -} - - -/*! - * \brief pixOpenGeneralized() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] sel - * \return pixd - * - *
- * Notes: - * (1) Generalized morphological opening, using both hits and - * misses in the Sel. - * (2) This does a hit-miss transform, followed by a dilation - * using the hits. - * (3) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (4) For clarity, if the case is known, use these patterns: - * (a) pixd = pixOpenGeneralized(NULL, pixs, ...); - * (b) pixOpenGeneralized(pixs, pixs, ...); - * (c) pixOpenGeneralized(pixd, pixs, ...); - * (5) The size of the result is determined by pixs. - *- */ -PIX * -pixOpenGeneralized(PIX *pixd, - PIX *pixs, - SEL *sel) -{ -PIX *pixt; - - PROCNAME("pixOpenGeneralized"); - - if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL) - return (PIX *)ERROR_PTR("pixd not returned", procName, pixd); - - if ((pixt = pixHMT(NULL, pixs, sel)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, pixd); - pixDilate(pixd, pixt, sel); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixCloseGeneralized() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] sel - * \return pixd - * - *
- * Notes: - * (1) Generalized morphological closing, using both hits and - * misses in the Sel. - * (2) This does a dilation using the hits, followed by a - * hit-miss transform. - * (3) This operation is a dual of the generalized opening. - * (4) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (5) For clarity, if the case is known, use these patterns: - * (a) pixd = pixCloseGeneralized(NULL, pixs, ...); - * (b) pixCloseGeneralized(pixs, pixs, ...); - * (c) pixCloseGeneralized(pixd, pixs, ...); - * (6) The size of the result is determined by pixs. - *- */ -PIX * -pixCloseGeneralized(PIX *pixd, - PIX *pixs, - SEL *sel) -{ -PIX *pixt; - - PROCNAME("pixCloseGeneralized"); - - if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL) - return (PIX *)ERROR_PTR("pixd not returned", procName, pixd); - - if ((pixt = pixDilate(NULL, pixs, sel)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, pixd); - pixHMT(pixd, pixt, sel); - pixDestroy(&pixt); - - return pixd; -} - - -/*-----------------------------------------------------------------* - * Binary morphological (raster) ops with brick Sels * - *-----------------------------------------------------------------*/ -/*! - * \brief pixDilateBrick() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd - * - *
- * Notes: - * (1) Sel is a brick with all elements being hits - * (2) The origin is at (x, y) = (hsize/2, vsize/2) - * (3) Do separably if both hsize and vsize are > 1. - * (4) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (5) For clarity, if the case is known, use these patterns: - * (a) pixd = pixDilateBrick(NULL, pixs, ...); - * (b) pixDilateBrick(pixs, pixs, ...); - * (c) pixDilateBrick(pixd, pixs, ...); - * (6) The size of the result is determined by pixs. - *- */ -PIX * -pixDilateBrick(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -PIX *pixt; -SEL *sel, *selh, *selv; - - PROCNAME("pixDilateBrick"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - if (hsize == 1 || vsize == 1) { /* no intermediate result */ - sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); - if (!sel) - return (PIX *)ERROR_PTR("sel not made", procName, pixd); - pixd = pixDilate(pixd, pixs, sel); - selDestroy(&sel); - } else { - if ((selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT)) == NULL) - return (PIX *)ERROR_PTR("selh not made", procName, pixd); - if ((selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT)) == NULL) { - selDestroy(&selh); - return (PIX *)ERROR_PTR("selv not made", procName, pixd); - } - pixt = pixDilate(NULL, pixs, selh); - pixd = pixDilate(pixd, pixt, selv); - pixDestroy(&pixt); - selDestroy(&selh); - selDestroy(&selv); - } - - return pixd; -} - - -/*! - * \brief pixErodeBrick() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd - * - *
- * Notes: - * (1) Sel is a brick with all elements being hits - * (2) The origin is at (x, y) = (hsize/2, vsize/2) - * (3) Do separably if both hsize and vsize are > 1. - * (4) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (5) For clarity, if the case is known, use these patterns: - * (a) pixd = pixErodeBrick(NULL, pixs, ...); - * (b) pixErodeBrick(pixs, pixs, ...); - * (c) pixErodeBrick(pixd, pixs, ...); - * (6) The size of the result is determined by pixs. - *- */ -PIX * -pixErodeBrick(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -PIX *pixt; -SEL *sel, *selh, *selv; - - PROCNAME("pixErodeBrick"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - if (hsize == 1 || vsize == 1) { /* no intermediate result */ - sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); - if (!sel) - return (PIX *)ERROR_PTR("sel not made", procName, pixd); - pixd = pixErode(pixd, pixs, sel); - selDestroy(&sel); - } else { - if ((selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT)) == NULL) - return (PIX *)ERROR_PTR("selh not made", procName, pixd); - if ((selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT)) == NULL) { - selDestroy(&selh); - return (PIX *)ERROR_PTR("selv not made", procName, pixd); - } - pixt = pixErode(NULL, pixs, selh); - pixd = pixErode(pixd, pixt, selv); - pixDestroy(&pixt); - selDestroy(&selh); - selDestroy(&selv); - } - - return pixd; -} - - -/*! - * \brief pixOpenBrick() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Sel is a brick with all elements being hits - * (2) The origin is at (x, y) = (hsize/2, vsize/2) - * (3) Do separably if both hsize and vsize are > 1. - * (4) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (5) For clarity, if the case is known, use these patterns: - * (a) pixd = pixOpenBrick(NULL, pixs, ...); - * (b) pixOpenBrick(pixs, pixs, ...); - * (c) pixOpenBrick(pixd, pixs, ...); - * (6) The size of the result is determined by pixs. - *- */ -PIX * -pixOpenBrick(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -PIX *pixt; -SEL *sel, *selh, *selv; - - PROCNAME("pixOpenBrick"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - if (hsize == 1 || vsize == 1) { /* no intermediate result */ - sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); - if (!sel) - return (PIX *)ERROR_PTR("sel not made", procName, pixd); - pixd = pixOpen(pixd, pixs, sel); - selDestroy(&sel); - } else { /* do separably */ - if ((selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT)) == NULL) - return (PIX *)ERROR_PTR("selh not made", procName, pixd); - if ((selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT)) == NULL) { - selDestroy(&selh); - return (PIX *)ERROR_PTR("selv not made", procName, pixd); - } - pixt = pixErode(NULL, pixs, selh); - pixd = pixErode(pixd, pixt, selv); - pixDilate(pixt, pixd, selh); - pixDilate(pixd, pixt, selv); - pixDestroy(&pixt); - selDestroy(&selh); - selDestroy(&selv); - } - - return pixd; -} - - -/*! - * \brief pixCloseBrick() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Sel is a brick with all elements being hits - * (2) The origin is at (x, y) = (hsize/2, vsize/2) - * (3) Do separably if both hsize and vsize are > 1. - * (4) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (5) For clarity, if the case is known, use these patterns: - * (a) pixd = pixCloseBrick(NULL, pixs, ...); - * (b) pixCloseBrick(pixs, pixs, ...); - * (c) pixCloseBrick(pixd, pixs, ...); - * (6) The size of the result is determined by pixs. - *- */ -PIX * -pixCloseBrick(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -PIX *pixt; -SEL *sel, *selh, *selv; - - PROCNAME("pixCloseBrick"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - if (hsize == 1 || vsize == 1) { /* no intermediate result */ - sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); - if (!sel) - return (PIX *)ERROR_PTR("sel not made", procName, pixd); - pixd = pixClose(pixd, pixs, sel); - selDestroy(&sel); - } else { /* do separably */ - if ((selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT)) == NULL) - return (PIX *)ERROR_PTR("selh not made", procName, pixd); - if ((selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT)) == NULL) { - selDestroy(&selh); - return (PIX *)ERROR_PTR("selv not made", procName, pixd); - } - pixt = pixDilate(NULL, pixs, selh); - pixd = pixDilate(pixd, pixt, selv); - pixErode(pixt, pixd, selh); - pixErode(pixd, pixt, selv); - pixDestroy(&pixt); - selDestroy(&selh); - selDestroy(&selv); - } - - return pixd; -} - - -/*! - * \brief pixCloseSafeBrick() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Sel is a brick with all elements being hits - * (2) The origin is at (x, y) = (hsize/2, vsize/2) - * (3) Do separably if both hsize and vsize are > 1. - * (4) Safe closing adds a border of 0 pixels, of sufficient size so - * that all pixels in input image are processed within - * 32-bit words in the expanded image. As a result, there is - * no special processing for pixels near the boundary, and there - * are no boundary effects. The border is removed at the end. - * (5) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (6) For clarity, if the case is known, use these patterns: - * (a) pixd = pixCloseBrick(NULL, pixs, ...); - * (b) pixCloseBrick(pixs, pixs, ...); - * (c) pixCloseBrick(pixd, pixs, ...); - * (7) The size of the result is determined by pixs. - *- */ -PIX * -pixCloseSafeBrick(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -l_int32 maxtrans, bordsize; -PIX *pixsb, *pixt, *pixdb; -SEL *sel, *selh, *selv; - - PROCNAME("pixCloseSafeBrick"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - - /* Symmetric b.c. handles correctly without added pixels */ - if (MORPH_BC == SYMMETRIC_MORPH_BC) - return pixCloseBrick(pixd, pixs, hsize, vsize); - - maxtrans = L_MAX(hsize / 2, vsize / 2); - bordsize = 32 * ((maxtrans + 31) / 32); /* full 32 bit words */ - pixsb = pixAddBorder(pixs, bordsize, 0); - - if (hsize == 1 || vsize == 1) { /* no intermediate result */ - sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT); - if (!sel) { - pixDestroy(&pixsb); - return (PIX *)ERROR_PTR("sel not made", procName, pixd); - } - pixdb = pixClose(NULL, pixsb, sel); - selDestroy(&sel); - } else { /* do separably */ - selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT); - selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT); - if (!selh || !selv) { - selDestroy(&selh); - selDestroy(&selh); - pixDestroy(&pixsb); - return (PIX *)ERROR_PTR("selh and selv not both made", - procName, pixd); - } - pixt = pixDilate(NULL, pixsb, selh); - pixdb = pixDilate(NULL, pixt, selv); - pixErode(pixt, pixdb, selh); - pixErode(pixdb, pixt, selv); - pixDestroy(&pixt); - selDestroy(&selh); - selDestroy(&selv); - } - - pixt = pixRemoveBorder(pixdb, bordsize); - pixDestroy(&pixsb); - pixDestroy(&pixdb); - - if (!pixd) { - pixd = pixt; - } else { - pixCopy(pixd, pixt); - pixDestroy(&pixt); - } - return pixd; -} - - -/*-----------------------------------------------------------------* - * Binary composed morphological (raster) ops with brick Sels * - *-----------------------------------------------------------------*/ -/* \brief selectComposableSels() - * - * \param[in] size of composed sel - * \param[in] direction L_HORIZ, L_VERT - * \param[out] psel1 [optional] contiguous sel; can be null - * \param[out] psel2 [optional] comb sel; can be null - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) When using composable Sels, where the original Sel is - * decomposed into two, the best you can do in terms - * of reducing the computation is by a factor: - * - * 2 * sqrt(size) / size - * - * In practice, you get quite close to this. E.g., - * - * Sel size | Optimum reduction factor - * -------- ------------------------ - * 36 | 1/3 - * 64 | 1/4 - * 144 | 1/6 - * 256 | 1/8 - *- */ -l_int32 -selectComposableSels(l_int32 size, - l_int32 direction, - SEL **psel1, - SEL **psel2) -{ -l_int32 factor1, factor2; - - PROCNAME("selectComposableSels"); - - if (!psel1 && !psel2) - return ERROR_INT("neither &sel1 nor &sel2 are defined", procName, 1); - if (psel1) *psel1 = NULL; - if (psel2) *psel2 = NULL; - if (size < 1 || size > 10000) - return ERROR_INT("size < 1 or size > 10000", procName, 1); - if (direction != L_HORIZ && direction != L_VERT) - return ERROR_INT("invalid direction", procName, 1); - - if (selectComposableSizes(size, &factor1, &factor2)) - return ERROR_INT("factors not found", procName, 1); - - if (psel1) { - if (direction == L_HORIZ) - *psel1 = selCreateBrick(1, factor1, 0, factor1 / 2, SEL_HIT); - else - *psel1 = selCreateBrick(factor1, 1, factor1 / 2 , 0, SEL_HIT); - } - if (psel2) - *psel2 = selCreateComb(factor1, factor2, direction); - return 0; -} - - -/*! - * \brief selectComposableSizes() - * - * \param[in] size of sel to be decomposed - * \param[out] pfactor1 larger factor - * \param[out] pfactor2 smaller factor - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This works for Sel sizes up to 10000, which seems sufficient. - * (2) The composable sel size is typically within +- 1 of - * the requested size. Up to size = 300, the maximum difference - * is +- 2. - * (3) We choose an overall cost function where the penalty for - * the size difference between input and actual is 4 times - * the penalty for additional rasterops. - * (4) Returned values: factor1 >= factor2 - * If size > 1, then factor1 > 1. - *- */ -l_ok -selectComposableSizes(l_int32 size, - l_int32 *pfactor1, - l_int32 *pfactor2) -{ -l_int32 i, midval, val1, val2m, val2p; -l_int32 index, prodm, prodp; -l_int32 mincost, totcost, rastcostm, rastcostp, diffm, diffp; -l_int32 lowval[256]; -l_int32 hival[256]; -l_int32 rastcost[256]; /* excess in sum of sizes (extra rasterops) */ -l_int32 diff[256]; /* diff between product (sel size) and input size */ - - PROCNAME("selectComposableSizes"); - - if (size < 1 || size > 10000) - return ERROR_INT("size < 1 or size > 10000", procName, 1); - if (!pfactor1 || !pfactor2) - return ERROR_INT("&factor1 or &factor2 not defined", procName, 1); - - midval = (l_int32)(sqrt((l_float64)size) + 0.001); - if (midval * midval == size) { - *pfactor1 = *pfactor2 = midval; - return 0; - } - - /* Set up arrays. For each val1, optimize for lowest diff, - * and save the rastcost, the diff, and the two factors. */ - for (val1 = midval + 1, i = 0; val1 > 0; val1--, i++) { - val2m = size / val1; - val2p = val2m + 1; - prodm = val1 * val2m; - prodp = val1 * val2p; - rastcostm = val1 + val2m - 2 * midval; - rastcostp = val1 + val2p - 2 * midval; - diffm = L_ABS(size - prodm); - diffp = L_ABS(size - prodp); - if (diffm <= diffp) { - lowval[i] = L_MIN(val1, val2m); - hival[i] = L_MAX(val1, val2m); - rastcost[i] = rastcostm; - diff[i] = diffm; - } else { - lowval[i] = L_MIN(val1, val2p); - hival[i] = L_MAX(val1, val2p); - rastcost[i] = rastcostp; - diff[i] = diffp; - } - } - - /* Choose the optimum factors; use cost ratio 4 on diff */ - mincost = 10000; - index = 1; /* unimportant initial value */ - for (i = 0; i < midval + 1; i++) { - if (diff[i] == 0 && rastcost[i] < ACCEPTABLE_COST) { - *pfactor1 = hival[i]; - *pfactor2 = lowval[i]; - return 0; - } - totcost = 4 * diff[i] + rastcost[i]; - if (totcost < mincost) { - mincost = totcost; - index = i; - } - } - *pfactor1 = hival[index]; - *pfactor2 = lowval[index]; - - return 0; -} - - -/*! - * \brief pixDilateCompBrick() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Sel is a brick with all elements being hits - * (2) The origin is at (x, y) = (hsize/2, vsize/2) - * (3) Do compositely for each dimension > 1. - * (4) Do separably if both hsize and vsize are > 1. - * (5) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (6) For clarity, if the case is known, use these patterns: - * (a) pixd = pixDilateCompBrick(NULL, pixs, ...); - * (b) pixDilateCompBrick(pixs, pixs, ...); - * (c) pixDilateCompBrick(pixd, pixs, ...); - * (7) The dimensions of the resulting image are determined by pixs. - * (8) CAUTION: both hsize and vsize are being decomposed. - * The decomposer chooses a product of sizes (call them - * 'terms') for each that is close to the input size, - * but not necessarily equal to it. It attempts to optimize: - * (a) for consistency with the input values: the product - * of terms is close to the input size - * (b) for efficiency of the operation: the sum of the - * terms is small; ideally about twice the square - * root of the input size. - * So, for example, if the input hsize = 37, which is - * a prime number, the decomposer will break this into two - * terms, 6 and 6, so that the net result is a dilation - * with hsize = 36. - *- */ -PIX * -pixDilateCompBrick(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -PIX *pix1, *pix2, *pix3; -SEL *selh1 = NULL; -SEL *selh2 = NULL; -SEL *selv1 = NULL; -SEL *selv2 = NULL; - - PROCNAME("pixDilateCompBrick"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - if (hsize > 1) { - if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) { - selDestroy(&selh1); - selDestroy(&selh2); - return (PIX *)ERROR_PTR("horiz sels not made", procName, pixd); - } - } - if (vsize > 1) { - if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) { - selDestroy(&selh1); - selDestroy(&selh2); - selDestroy(&selv1); - selDestroy(&selv2); - return (PIX *)ERROR_PTR("vert sels not made", procName, pixd); - } - } - - pix1 = pixAddBorder(pixs, 32, 0); - if (vsize == 1) { - pix2 = pixDilate(NULL, pix1, selh1); - pix3 = pixDilate(NULL, pix2, selh2); - } else if (hsize == 1) { - pix2 = pixDilate(NULL, pix1, selv1); - pix3 = pixDilate(NULL, pix2, selv2); - } else { - pix2 = pixDilate(NULL, pix1, selh1); - pix3 = pixDilate(NULL, pix2, selh2); - pixDilate(pix2, pix3, selv1); - pixDilate(pix3, pix2, selv2); - } - pixDestroy(&pix1); - pixDestroy(&pix2); - - selDestroy(&selh1); - selDestroy(&selh2); - selDestroy(&selv1); - selDestroy(&selv2); - - pix1 = pixRemoveBorder(pix3, 32); - pixDestroy(&pix3); - if (!pixd) - return pix1; - pixCopy(pixd, pix1); - pixDestroy(&pix1); - return pixd; -} - - -/*! - * \brief pixErodeCompBrick() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Sel is a brick with all elements being hits - * (2) The origin is at (x, y) = (hsize/2, vsize/2) - * (3) Do compositely for each dimension > 1. - * (4) Do separably if both hsize and vsize are > 1. - * (5) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (6) For clarity, if the case is known, use these patterns: - * (a) pixd = pixErodeCompBrick(NULL, pixs, ...); - * (b) pixErodeCompBrick(pixs, pixs, ...); - * (c) pixErodeCompBrick(pixd, pixs, ...); - * (7) The dimensions of the resulting image are determined by pixs. - * (8) CAUTION: both hsize and vsize are being decomposed. - * The decomposer chooses a product of sizes (call them - * 'terms') for each that is close to the input size, - * but not necessarily equal to it. It attempts to optimize: - * (a) for consistency with the input values: the product - * of terms is close to the input size - * (b) for efficiency of the operation: the sum of the - * terms is small; ideally about twice the square - * root of the input size. - * So, for example, if the input hsize = 37, which is - * a prime number, the decomposer will break this into two - * terms, 6 and 6, so that the net result is a dilation - * with hsize = 36. - *- */ -PIX * -pixErodeCompBrick(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -PIX *pixt; -SEL *selh1 = NULL; -SEL *selh2 = NULL; -SEL *selv1 = NULL; -SEL *selv2 = NULL; - - PROCNAME("pixErodeCompBrick"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - if (hsize > 1) { - if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) { - selDestroy(&selh1); - selDestroy(&selh2); - return (PIX *)ERROR_PTR("horiz sels not made", procName, pixd); - } - } - if (vsize > 1) { - if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) { - selDestroy(&selh1); - selDestroy(&selh2); - selDestroy(&selv1); - selDestroy(&selv2); - return (PIX *)ERROR_PTR("vert sels not made", procName, pixd); - } - } - - if (vsize == 1) { - pixt = pixErode(NULL, pixs, selh1); - pixd = pixErode(pixd, pixt, selh2); - } else if (hsize == 1) { - pixt = pixErode(NULL, pixs, selv1); - pixd = pixErode(pixd, pixt, selv2); - } else { - pixt = pixErode(NULL, pixs, selh1); - pixd = pixErode(pixd, pixt, selh2); - pixErode(pixt, pixd, selv1); - pixErode(pixd, pixt, selv2); - } - pixDestroy(&pixt); - - selDestroy(&selh1); - selDestroy(&selh2); - selDestroy(&selv1); - selDestroy(&selv2); - return pixd; -} - - -/*! - * \brief pixOpenCompBrick() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Sel is a brick with all elements being hits - * (2) The origin is at (x, y) = (hsize/2, vsize/2) - * (3) Do compositely for each dimension > 1. - * (4) Do separably if both hsize and vsize are > 1. - * (5) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (6) For clarity, if the case is known, use these patterns: - * (a) pixd = pixOpenCompBrick(NULL, pixs, ...); - * (b) pixOpenCompBrick(pixs, pixs, ...); - * (c) pixOpenCompBrick(pixd, pixs, ...); - * (7) The dimensions of the resulting image are determined by pixs. - * (8) CAUTION: both hsize and vsize are being decomposed. - * The decomposer chooses a product of sizes (call them - * 'terms') for each that is close to the input size, - * but not necessarily equal to it. It attempts to optimize: - * (a) for consistency with the input values: the product - * of terms is close to the input size - * (b) for efficiency of the operation: the sum of the - * terms is small; ideally about twice the square - * root of the input size. - * So, for example, if the input hsize = 37, which is - * a prime number, the decomposer will break this into two - * terms, 6 and 6, so that the net result is a dilation - * with hsize = 36. - *- */ -PIX * -pixOpenCompBrick(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -PIX *pixt; -SEL *selh1 = NULL; -SEL *selh2 = NULL; -SEL *selv1 = NULL; -SEL *selv2 = NULL; - - PROCNAME("pixOpenCompBrick"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - if (hsize > 1) { - if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) { - selDestroy(&selh1); - selDestroy(&selh2); - return (PIX *)ERROR_PTR("horiz sels not made", procName, pixd); - } - } - if (vsize > 1) { - if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) { - selDestroy(&selh1); - selDestroy(&selh2); - selDestroy(&selv1); - selDestroy(&selv2); - return (PIX *)ERROR_PTR("vert sels not made", procName, pixd); - } - } - - if (vsize == 1) { - pixt = pixErode(NULL, pixs, selh1); - pixd = pixErode(pixd, pixt, selh2); - pixDilate(pixt, pixd, selh1); - pixDilate(pixd, pixt, selh2); - } else if (hsize == 1) { - pixt = pixErode(NULL, pixs, selv1); - pixd = pixErode(pixd, pixt, selv2); - pixDilate(pixt, pixd, selv1); - pixDilate(pixd, pixt, selv2); - } else { /* do separably */ - pixt = pixErode(NULL, pixs, selh1); - pixd = pixErode(pixd, pixt, selh2); - pixErode(pixt, pixd, selv1); - pixErode(pixd, pixt, selv2); - pixDilate(pixt, pixd, selh1); - pixDilate(pixd, pixt, selh2); - pixDilate(pixt, pixd, selv1); - pixDilate(pixd, pixt, selv2); - } - pixDestroy(&pixt); - - selDestroy(&selh1); - selDestroy(&selh2); - selDestroy(&selv1); - selDestroy(&selv2); - return pixd; -} - - -/*! - * \brief pixCloseCompBrick() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Sel is a brick with all elements being hits - * (2) The origin is at (x, y) = (hsize/2, vsize/2) - * (3) Do compositely for each dimension > 1. - * (4) Do separably if both hsize and vsize are > 1. - * (5) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (6) For clarity, if the case is known, use these patterns: - * (a) pixd = pixCloseCompBrick(NULL, pixs, ...); - * (b) pixCloseCompBrick(pixs, pixs, ...); - * (c) pixCloseCompBrick(pixd, pixs, ...); - * (7) The dimensions of the resulting image are determined by pixs. - * (8) CAUTION: both hsize and vsize are being decomposed. - * The decomposer chooses a product of sizes (call them - * 'terms') for each that is close to the input size, - * but not necessarily equal to it. It attempts to optimize: - * (a) for consistency with the input values: the product - * of terms is close to the input size - * (b) for efficiency of the operation: the sum of the - * terms is small; ideally about twice the square - * root of the input size. - * So, for example, if the input hsize = 37, which is - * a prime number, the decomposer will break this into two - * terms, 6 and 6, so that the net result is a dilation - * with hsize = 36. - *- */ -PIX * -pixCloseCompBrick(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -PIX *pixt; -SEL *selh1 = NULL; -SEL *selh2 = NULL; -SEL *selv1 = NULL; -SEL *selv2 = NULL; - - PROCNAME("pixCloseCompBrick"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - if (hsize > 1) { - if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) { - selDestroy(&selh1); - selDestroy(&selh2); - return (PIX *)ERROR_PTR("horiz sels not made", procName, pixd); - } - } - if (vsize > 1) { - if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) { - selDestroy(&selh1); - selDestroy(&selh2); - selDestroy(&selv1); - selDestroy(&selv2); - return (PIX *)ERROR_PTR("vert sels not made", procName, pixd); - } - } - - if (vsize == 1) { - pixt = pixDilate(NULL, pixs, selh1); - pixd = pixDilate(pixd, pixt, selh2); - pixErode(pixt, pixd, selh1); - pixErode(pixd, pixt, selh2); - } else if (hsize == 1) { - pixt = pixDilate(NULL, pixs, selv1); - pixd = pixDilate(pixd, pixt, selv2); - pixErode(pixt, pixd, selv1); - pixErode(pixd, pixt, selv2); - } else { /* do separably */ - pixt = pixDilate(NULL, pixs, selh1); - pixd = pixDilate(pixd, pixt, selh2); - pixDilate(pixt, pixd, selv1); - pixDilate(pixd, pixt, selv2); - pixErode(pixt, pixd, selh1); - pixErode(pixd, pixt, selh2); - pixErode(pixt, pixd, selv1); - pixErode(pixd, pixt, selv2); - } - pixDestroy(&pixt); - - selDestroy(&selh1); - selDestroy(&selh2); - selDestroy(&selv1); - selDestroy(&selv2); - return pixd; -} - - -/*! - * \brief pixCloseSafeCompBrick() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Sel is a brick with all elements being hits - * (2) The origin is at (x, y) = (hsize/2, vsize/2) - * (3) Do compositely for each dimension > 1. - * (4) Do separably if both hsize and vsize are > 1. - * (5) Safe closing adds a border of 0 pixels, of sufficient size so - * that all pixels in input image are processed within - * 32-bit words in the expanded image. As a result, there is - * no special processing for pixels near the boundary, and there - * are no boundary effects. The border is removed at the end. - * (6) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (7) For clarity, if the case is known, use these patterns: - * (a) pixd = pixCloseSafeCompBrick(NULL, pixs, ...); - * (b) pixCloseSafeCompBrick(pixs, pixs, ...); - * (c) pixCloseSafeCompBrick(pixd, pixs, ...); - * (8) The dimensions of the resulting image are determined by pixs. - * (9) CAUTION: both hsize and vsize are being decomposed. - * The decomposer chooses a product of sizes (call them - * 'terms') for each that is close to the input size, - * but not necessarily equal to it. It attempts to optimize: - * (a) for consistency with the input values: the product - * of terms is close to the input size - * (b) for efficiency of the operation: the sum of the - * terms is small; ideally about twice the square - * root of the input size. - * So, for example, if the input hsize = 37, which is - * a prime number, the decomposer will break this into two - * terms, 6 and 6, so that the net result is a dilation - * with hsize = 36. - *- */ -PIX * -pixCloseSafeCompBrick(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -l_int32 maxtrans, bordsize; -PIX *pixsb, *pixt, *pixdb; -SEL *selh1 = NULL; -SEL *selh2 = NULL; -SEL *selv1 = NULL; -SEL *selv2 = NULL; - - PROCNAME("pixCloseSafeCompBrick"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - - /* Symmetric b.c. handles correctly without added pixels */ - if (MORPH_BC == SYMMETRIC_MORPH_BC) - return pixCloseCompBrick(pixd, pixs, hsize, vsize); - - if (hsize > 1) { - if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) { - selDestroy(&selh1); - selDestroy(&selh2); - return (PIX *)ERROR_PTR("horiz sels not made", procName, pixd); - } - } - if (vsize > 1) { - if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) { - selDestroy(&selh1); - selDestroy(&selh2); - selDestroy(&selv1); - selDestroy(&selv2); - return (PIX *)ERROR_PTR("vert sels not made", procName, pixd); - } - } - - maxtrans = L_MAX(hsize / 2, vsize / 2); - bordsize = 32 * ((maxtrans + 31) / 32); /* full 32 bit words */ - pixsb = pixAddBorder(pixs, bordsize, 0); - - if (vsize == 1) { - pixt = pixDilate(NULL, pixsb, selh1); - pixdb = pixDilate(NULL, pixt, selh2); - pixErode(pixt, pixdb, selh1); - pixErode(pixdb, pixt, selh2); - } else if (hsize == 1) { - pixt = pixDilate(NULL, pixsb, selv1); - pixdb = pixDilate(NULL, pixt, selv2); - pixErode(pixt, pixdb, selv1); - pixErode(pixdb, pixt, selv2); - } else { /* do separably */ - pixt = pixDilate(NULL, pixsb, selh1); - pixdb = pixDilate(NULL, pixt, selh2); - pixDilate(pixt, pixdb, selv1); - pixDilate(pixdb, pixt, selv2); - pixErode(pixt, pixdb, selh1); - pixErode(pixdb, pixt, selh2); - pixErode(pixt, pixdb, selv1); - pixErode(pixdb, pixt, selv2); - } - pixDestroy(&pixt); - - pixt = pixRemoveBorder(pixdb, bordsize); - pixDestroy(&pixsb); - pixDestroy(&pixdb); - - if (!pixd) { - pixd = pixt; - } else { - pixCopy(pixd, pixt); - pixDestroy(&pixt); - } - - selDestroy(&selh1); - selDestroy(&selh2); - selDestroy(&selv1); - selDestroy(&selv2); - return pixd; -} - - -/*-----------------------------------------------------------------* - * Functions associated with boundary conditions * - *-----------------------------------------------------------------*/ -/*! - * \brief resetMorphBoundaryCondition() - * - * \param[in] bc SYMMETRIC_MORPH_BC, ASYMMETRIC_MORPH_BC - * \return void - */ -void -resetMorphBoundaryCondition(l_int32 bc) -{ - PROCNAME("resetMorphBoundaryCondition"); - - if (bc != SYMMETRIC_MORPH_BC && bc != ASYMMETRIC_MORPH_BC) { - L_WARNING("invalid bc; using asymmetric\n", procName); - bc = ASYMMETRIC_MORPH_BC; - } - MORPH_BC = bc; - return; -} - - -/*! - * \brief getMorphBorderPixelColor() - * - * \param[in] type L_MORPH_DILATE, L_MORPH_ERODE - * \param[in] depth of pix - * \return color of border pixels for this operation - */ -l_uint32 -getMorphBorderPixelColor(l_int32 type, - l_int32 depth) -{ - PROCNAME("getMorphBorderPixelColor"); - - if (type != L_MORPH_DILATE && type != L_MORPH_ERODE) - return ERROR_INT("invalid type", procName, 0); - if (depth != 1 && depth != 2 && depth != 4 && depth != 8 && - depth != 16 && depth != 32) - return ERROR_INT("invalid depth", procName, 0); - - if (MORPH_BC == ASYMMETRIC_MORPH_BC || type == L_MORPH_DILATE) - return 0; - - /* Symmetric & erosion */ - if (depth < 32) - return ((1 << depth) - 1); - else /* depth == 32 */ - return 0xffffff00; -} - - -/*-----------------------------------------------------------------* - * Static helpers for arg processing * - *-----------------------------------------------------------------*/ -/*! - * \brief processMorphArgs1() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] sel - * \param[out] ppixt copy or clone of %pixs - * \return pixd, or NULL on error. - * - *
- * Notes: - * (1) This is used for generic erosion, dilation and HMT. - *- */ -static PIX * -processMorphArgs1(PIX *pixd, - PIX *pixs, - SEL *sel, - PIX **ppixt) -{ -l_int32 sx, sy; - - PROCNAME("processMorphArgs1"); - - if (!ppixt) - return (PIX *)ERROR_PTR("&pixt not defined", procName, pixd); - *ppixt = NULL; - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (!sel) - return (PIX *)ERROR_PTR("sel not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - - selGetParameters(sel, &sx, &sy, NULL, NULL); - if (sx == 0 || sy == 0) - return (PIX *)ERROR_PTR("sel of size 0", procName, pixd); - - /* We require pixd to exist and to be the same size as pixs. - * Further, pixt must be a copy (or clone) of pixs. */ - if (!pixd) { - if ((pixd = pixCreateTemplate(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - *ppixt = pixClone(pixs); - } else { - pixResizeImageData(pixd, pixs); - if (pixd == pixs) { /* in-place; must make a copy of pixs */ - if ((*ppixt = pixCopy(NULL, pixs)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, pixd); - } else { - *ppixt = pixClone(pixs); - } - } - return pixd; -} - - -/*! - * \brief processMorphArgs2() - * - * This is used for generic openings and closings. - */ -static PIX * -processMorphArgs2(PIX *pixd, - PIX *pixs, - SEL *sel) -{ -l_int32 sx, sy; - - PROCNAME("processMorphArgs2"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (!sel) - return (PIX *)ERROR_PTR("sel not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - - selGetParameters(sel, &sx, &sy, NULL, NULL); - if (sx == 0 || sy == 0) - return (PIX *)ERROR_PTR("sel of size 0", procName, pixd); - - if (!pixd) - return pixCreateTemplate(pixs); - pixResizeImageData(pixd, pixs); - return pixd; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/morph.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/morph.h deleted file mode 100644 index d17723fb..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/morph.h +++ /dev/null @@ -1,248 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_MORPH_H -#define LEPTONICA_MORPH_H - -/*! - * \file morph.h - * - *
- * Contains the following structs: - * struct Sel - * struct Sela - * struct Kernel - * - * Contains definitions for: - * morphological b.c. flags - * structuring element types - * runlength flags for granulometry - * direction flags for grayscale morphology - * morphological operation flags - * standard border size - * grayscale intensity scaling flags - * morphological tophat flags - * arithmetic and logical operator flags - * grayscale morphology selection flags - * distance function b.c. flags - * image comparison flags - * color content flags - *- */ - -/*-------------------------------------------------------------------------* - * Sel and Sel array * - *-------------------------------------------------------------------------*/ -#define SEL_VERSION_NUMBER 1 - -/*! Selection */ -struct Sel -{ - l_int32 sy; /*!< sel height */ - l_int32 sx; /*!< sel width */ - l_int32 cy; /*!< y location of sel origin */ - l_int32 cx; /*!< x location of sel origin */ - l_int32 **data; /*!< {0,1,2}; data[i][j] in [row][col] order */ - char *name; /*!< used to find sel by name */ -}; -typedef struct Sel SEL; - -/*! Array of Sel */ -struct Sela -{ - l_int32 n; /*!< number of sel actually stored */ - l_int32 nalloc; /*!< size of allocated ptr array */ - struct Sel **sel; /*!< sel ptr array */ -}; -typedef struct Sela SELA; - - -/*-------------------------------------------------------------------------* - * Kernel * - *-------------------------------------------------------------------------*/ -#define KERNEL_VERSION_NUMBER 2 - -/*! Kernel */ -struct L_Kernel -{ - l_int32 sy; /*!< kernel height */ - l_int32 sx; /*!< kernel width */ - l_int32 cy; /*!< y location of kernel origin */ - l_int32 cx; /*!< x location of kernel origin */ - l_float32 **data; /*!< data[i][j] in [row][col] order */ -}; -typedef struct L_Kernel L_KERNEL; - - -/*-------------------------------------------------------------------------* - * Morphological boundary condition flags * - * * - * Two types of boundary condition for erosion. * - * The global variable MORPH_BC takes on one of these two values. * - * See notes in morph.c for usage. * - *-------------------------------------------------------------------------*/ - -/*! Morph Boundary */ -enum { - SYMMETRIC_MORPH_BC = 0, - ASYMMETRIC_MORPH_BC = 1 -}; - -/*-------------------------------------------------------------------------* - * Structuring element vals * - *-------------------------------------------------------------------------*/ - -/*! SEL Vals */ -enum { - SEL_DONT_CARE = 0, - SEL_HIT = 1, - SEL_MISS = 2 -}; - -/*-------------------------------------------------------------------------* - * Runlength flags for granulometry * - *-------------------------------------------------------------------------*/ - -/*! Runlength Polarity */ -enum { - L_RUN_OFF = 0, - L_RUN_ON = 1 -}; - -/*-------------------------------------------------------------------------* - * Direction flags for grayscale morphology, granulometry, * - * composable Sels, convolution, etc. * - *-------------------------------------------------------------------------*/ - -/*! Direction Flags */ -enum { - L_HORIZ = 1, - L_VERT = 2, - L_BOTH_DIRECTIONS = 3 -}; - -/*-------------------------------------------------------------------------* - * Morphological operation flags * - *-------------------------------------------------------------------------*/ - -/*! Morph Operator */ -enum { - L_MORPH_DILATE = 1, - L_MORPH_ERODE = 2, - L_MORPH_OPEN = 3, - L_MORPH_CLOSE = 4, - L_MORPH_HMT = 5 -}; - -/*-------------------------------------------------------------------------* - * Grayscale intensity scaling flags * - *-------------------------------------------------------------------------*/ - -/*! Pixel Value Scaling */ -enum { - L_LINEAR_SCALE = 1, - L_LOG_SCALE = 2 -}; - -/*-------------------------------------------------------------------------* - * Morphological tophat flags * - *-------------------------------------------------------------------------*/ - -/*! Morph Tophat */ -enum { - L_TOPHAT_WHITE = 0, - L_TOPHAT_BLACK = 1 -}; - -/*-------------------------------------------------------------------------* - * Arithmetic and logical operator flags * - * (use on grayscale images and Numas) * - *-------------------------------------------------------------------------*/ - -/*! ArithLogical Ops */ -enum { - L_ARITH_ADD = 1, - L_ARITH_SUBTRACT = 2, - L_ARITH_MULTIPLY = 3, /* on numas only */ - L_ARITH_DIVIDE = 4, /* on numas only */ - L_UNION = 5, /* on numas only */ - L_INTERSECTION = 6, /* on numas only */ - L_SUBTRACTION = 7, /* on numas only */ - L_EXCLUSIVE_OR = 8 /* on numas only */ -}; - -/*-------------------------------------------------------------------------* - * Min/max selection flags * - *-------------------------------------------------------------------------*/ - -/*! MinMax Selection */ -enum { - L_CHOOSE_MIN = 1, /* useful in a downscaling "erosion" */ - L_CHOOSE_MAX = 2, /* useful in a downscaling "dilation" */ - L_CHOOSE_MAXDIFF = 3, /* useful in a downscaling contrast */ - L_CHOOSE_MIN_BOOST = 4, /* use a modification of the min value */ - L_CHOOSE_MAX_BOOST = 5 /* use a modification of the max value */ -}; - -/*-------------------------------------------------------------------------* - * Exterior value b.c. for distance function flags * - *-------------------------------------------------------------------------*/ - -/*! Exterior Value */ -enum { - L_BOUNDARY_BG = 1, /* assume bg outside image */ - L_BOUNDARY_FG = 2 /* assume fg outside image */ -}; - -/*-------------------------------------------------------------------------* - * Image comparison flags * - *-------------------------------------------------------------------------*/ - -/*! Image Comparison */ -enum { - L_COMPARE_XOR = 1, - L_COMPARE_SUBTRACT = 2, - L_COMPARE_ABS_DIFF = 3 -}; - -/*-------------------------------------------------------------------------* - * Color content flags * - *-------------------------------------------------------------------------*/ - -/*! Color Content */ -enum { - L_MAX_DIFF_FROM_AVERAGE_2 = 1, - L_MAX_MIN_DIFF_FROM_2 = 2, - L_MAX_DIFF = 3 -}; - -/*-------------------------------------------------------------------------* - * Standard size of border added around images for special processing * - *-------------------------------------------------------------------------*/ -static const l_int32 ADDED_BORDER = 32; /*!< pixels, not bits */ - - -#endif /* LEPTONICA_MORPH_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/morphapp.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/morphapp.c deleted file mode 100644 index 8ee41b0f..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/morphapp.c +++ /dev/null @@ -1,1636 +0,0 @@ -/*====================================================================* - - 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 morphapp.c - *
- * - * These are some useful and/or interesting composite - * image processing operations, of the type that are often - * useful in applications. Most are morphological in - * nature. - * - * Extraction of boundary pixels - * PIX *pixExtractBoundary() - * - * Selective morph sequence operation under mask - * PIX *pixMorphSequenceMasked() - * - * Selective morph sequence operation on each component - * PIX *pixMorphSequenceByComponent() - * PIXA *pixaMorphSequenceByComponent() - * - * Selective morph sequence operation on each region - * PIX *pixMorphSequenceByRegion() - * PIXA *pixaMorphSequenceByRegion() - * - * Union and intersection of parallel composite operations - * PIX *pixUnionOfMorphOps() - * PIX *pixIntersectionOfMorphOps() - * - * Selective connected component filling - * PIX *pixSelectiveConnCompFill() - * - * Removal of matched patterns - * PIX *pixRemoveMatchedPattern() - * - * Display of matched patterns - * PIX *pixDisplayMatchedPattern() - * - * Extension of pixa by iterative erosion or dilation (and by scaling) - * PIXA *pixaExtendByMorph() - * PIXA *pixaExtendByScaling() - * - * Iterative morphological seed filling (don't use for real work) - * PIX *pixSeedfillMorph() - * - * Granulometry on binary images - * NUMA *pixRunHistogramMorph() - * - * Composite operations on grayscale images - * PIX *pixTophat() - * PIX *pixHDome() - * PIX *pixFastTophat() - * PIX *pixMorphGradient() - * - * Centroid of component - * PTA *pixaCentroids() - * l_int32 pixCentroid() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) Extracts the fg or bg boundary pixels for each component. - * Components are assumed to end at the boundary of pixs. - *- */ -PIX * -pixExtractBoundary(PIX *pixs, - l_int32 type) -{ -PIX *pixd; - - PROCNAME("pixExtractBoundary"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - if (type == 0) - pixd = pixDilateBrick(NULL, pixs, 3, 3); - else - pixd = pixErodeBrick(NULL, pixs, 3, 3); - pixXor(pixd, pixd, pixs); - return pixd; -} - - -/*-----------------------------------------------------------------* - * Selective morph sequence operation under mask * - *-----------------------------------------------------------------*/ -/*! - * \brief pixMorphSequenceMasked() - * - * \param[in] pixs 1 bpp - * \param[in] pixm [optional] 1 bpp mask - * \param[in] sequence string specifying sequence of operations - * \param[in] dispsep horizontal separation in pixels between - * successive displays; use zero to suppress display - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) This applies the morph sequence to the image, but only allows - * changes in pixs for pixels under the background of pixm. - * (5) If pixm is NULL, this is just pixMorphSequence(). - *- */ -PIX * -pixMorphSequenceMasked(PIX *pixs, - PIX *pixm, - const char *sequence, - l_int32 dispsep) -{ -PIX *pixd; - - PROCNAME("pixMorphSequenceMasked"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!sequence) - return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); - - pixd = pixMorphSequence(pixs, sequence, dispsep); - pixCombineMasked(pixd, pixs, pixm); /* restore src pixels under mask fg */ - return pixd; -} - - -/*-----------------------------------------------------------------* - * Morph sequence operation on each component * - *-----------------------------------------------------------------*/ -/*! - * \brief pixMorphSequenceByComponent() - * - * \param[in] pixs 1 bpp - * \param[in] sequence string specifying sequence - * \param[in] connectivity 4 or 8 - * \param[in] minw min width to consider; use 0 or 1 for any width - * \param[in] minh min height to consider; use 0 or 1 for any height - * \param[out] pboxa [optional] return boxa of c.c. in pixs - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) See pixMorphSequence() for composing operation sequences. - * (2) This operates separately on each c.c. in the input pix. - * (3) The dilation does NOT increase the c.c. size; it is clipped - * to the size of the original c.c. This is necessary to - * keep the c.c. independent after the operation. - * (4) You can specify that the width and/or height must equal - * or exceed a minimum size for the operation to take place. - * (5) Use NULL for boxa to avoid returning the boxa. - *- */ -PIX * -pixMorphSequenceByComponent(PIX *pixs, - const char *sequence, - l_int32 connectivity, - l_int32 minw, - l_int32 minh, - BOXA **pboxa) -{ -l_int32 n, i, x, y, w, h; -BOXA *boxa; -PIX *pix, *pixd; -PIXA *pixas, *pixad; - - PROCNAME("pixMorphSequenceByComponent"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!sequence) - return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); - - if (minw <= 0) minw = 1; - if (minh <= 0) minh = 1; - - /* Get the c.c. */ - if ((boxa = pixConnComp(pixs, &pixas, connectivity)) == NULL) - return (PIX *)ERROR_PTR("boxa not made", procName, NULL); - - /* Operate on each c.c. independently */ - pixad = pixaMorphSequenceByComponent(pixas, sequence, minw, minh); - pixaDestroy(&pixas); - boxaDestroy(&boxa); - if (!pixad) - return (PIX *)ERROR_PTR("pixad not made", procName, NULL); - - /* Display the result out into pixd */ - pixd = pixCreateTemplate(pixs); - n = pixaGetCount(pixad); - for (i = 0; i < n; i++) { - pixaGetBoxGeometry(pixad, i, &x, &y, &w, &h); - pix = pixaGetPix(pixad, i, L_CLONE); - pixRasterop(pixd, x, y, w, h, PIX_PAINT, pix, 0, 0); - pixDestroy(&pix); - } - - if (pboxa) - *pboxa = pixaGetBoxa(pixad, L_CLONE); - pixaDestroy(&pixad); - return pixd; -} - - -/*! - * \brief pixaMorphSequenceByComponent() - * - * \param[in] pixas of 1 bpp pix - * \param[in] sequence string specifying sequence - * \param[in] minw min width to consider; use 0 or 1 for any width - * \param[in] minh min height to consider; use 0 or 1 for any height - * \return pixad, or NULL on error - * - *
- * Notes: - * (1) See pixMorphSequence() for composing operation sequences. - * (2) This operates separately on each c.c. in the input pixa. - * (3) You can specify that the width and/or height must equal - * or exceed a minimum size for the operation to take place. - * (4) The input pixa should have a boxa giving the locations - * of the pix components. - *- */ -PIXA * -pixaMorphSequenceByComponent(PIXA *pixas, - const char *sequence, - l_int32 minw, - l_int32 minh) -{ -l_int32 n, i, w, h, d; -BOX *box; -PIX *pix1, *pix2; -PIXA *pixad; - - PROCNAME("pixaMorphSequenceByComponent"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if ((n = pixaGetCount(pixas)) == 0) - return (PIXA *)ERROR_PTR("no pix in pixas", procName, NULL); - if (n != pixaGetBoxaCount(pixas)) - L_WARNING("boxa size != n\n", procName); - pixaGetPixDimensions(pixas, 0, NULL, NULL, &d); - if (d != 1) - return (PIXA *)ERROR_PTR("depth not 1 bpp", procName, NULL); - - if (!sequence) - return (PIXA *)ERROR_PTR("sequence not defined", procName, NULL); - if (minw <= 0) minw = 1; - if (minh <= 0) minh = 1; - - if ((pixad = pixaCreate(n)) == NULL) - return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); - for (i = 0; i < n; i++) { - pixaGetPixDimensions(pixas, i, &w, &h, NULL); - if (w >= minw && h >= minh) { - if ((pix1 = pixaGetPix(pixas, i, L_CLONE)) == NULL) { - pixaDestroy(&pixad); - return (PIXA *)ERROR_PTR("pix1 not found", procName, NULL); - } - if ((pix2 = pixMorphCompSequence(pix1, sequence, 0)) == NULL) { - pixaDestroy(&pixad); - return (PIXA *)ERROR_PTR("pix2 not made", procName, NULL); - } - pixaAddPix(pixad, pix2, L_INSERT); - box = pixaGetBox(pixas, i, L_COPY); - pixaAddBox(pixad, box, L_INSERT); - pixDestroy(&pix1); - } - } - - return pixad; -} - - -/*-----------------------------------------------------------------* - * Morph sequence operation on each region * - *-----------------------------------------------------------------*/ -/*! - * \brief pixMorphSequenceByRegion() - * - * \param[in] pixs 1 bpp - * \param[in] pixm mask specifying regions - * \param[in] sequence string specifying sequence - * \param[in] connectivity 4 or 8, used on mask - * \param[in] minw min width to consider; use 0 or 1 for any width - * \param[in] minh min height to consider; use 0 or 1 for any height - * \param[out] pboxa [optional] return boxa of c.c. in pixm - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) See pixMorphCompSequence() for composing operation sequences. - * (2) This operates separately on the region in pixs corresponding - * to each c.c. in the mask pixm. It differs from - * pixMorphSequenceByComponent() in that the latter does not have - * a pixm (mask), but instead operates independently on each - * component in pixs. - * (3) Dilation will NOT increase the region size; the result - * is clipped to the size of the mask region. This is necessary - * to make regions independent after the operation. - * (4) You can specify that the width and/or height of a region must - * equal or exceed a minimum size for the operation to take place. - * (5) Use NULL for %pboxa to avoid returning the boxa. - *- */ -PIX * -pixMorphSequenceByRegion(PIX *pixs, - PIX *pixm, - const char *sequence, - l_int32 connectivity, - l_int32 minw, - l_int32 minh, - BOXA **pboxa) -{ -l_int32 n, i, x, y, w, h; -BOXA *boxa; -PIX *pix, *pixd; -PIXA *pixam, *pixad; - - PROCNAME("pixMorphSequenceByRegion"); - - if (pboxa) *pboxa = NULL; - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!pixm) - return (PIX *)ERROR_PTR("pixm not defined", procName, NULL); - if (pixGetDepth(pixs) != 1 || pixGetDepth(pixm) != 1) - return (PIX *)ERROR_PTR("pixs and pixm not both 1 bpp", procName, NULL); - if (!sequence) - return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); - - if (minw <= 0) minw = 1; - if (minh <= 0) minh = 1; - - /* Get the c.c. of the mask */ - if ((boxa = pixConnComp(pixm, &pixam, connectivity)) == NULL) - return (PIX *)ERROR_PTR("boxa not made", procName, NULL); - - /* Operate on each region in pixs independently */ - pixad = pixaMorphSequenceByRegion(pixs, pixam, sequence, minw, minh); - pixaDestroy(&pixam); - boxaDestroy(&boxa); - if (!pixad) - return (PIX *)ERROR_PTR("pixad not made", procName, NULL); - - /* Display the result out into pixd */ - pixd = pixCreateTemplate(pixs); - n = pixaGetCount(pixad); - for (i = 0; i < n; i++) { - pixaGetBoxGeometry(pixad, i, &x, &y, &w, &h); - pix = pixaGetPix(pixad, i, L_CLONE); - pixRasterop(pixd, x, y, w, h, PIX_PAINT, pix, 0, 0); - pixDestroy(&pix); - } - - if (pboxa) - *pboxa = pixaGetBoxa(pixad, L_CLONE); - pixaDestroy(&pixad); - return pixd; -} - - -/*! - * \brief pixaMorphSequenceByRegion() - * - * \param[in] pixs 1 bpp - * \param[in] pixam of 1 bpp mask elements - * \param[in] sequence string specifying sequence - * \param[in] minw min width to consider; use 0 or 1 for any width - * \param[in] minh min height to consider; use 0 or 1 for any height - * \return pixad, or NULL on error - * - *
- * Notes: - * (1) See pixMorphSequence() for composing operation sequences. - * (2) This operates separately on each region in the input pixs - * defined by the components in pixam. - * (3) You can specify that the width and/or height of a mask - * component must equal or exceed a minimum size for the - * operation to take place. - * (4) The input pixam should have a boxa giving the locations - * of the regions in pixs. - *- */ -PIXA * -pixaMorphSequenceByRegion(PIX *pixs, - PIXA *pixam, - const char *sequence, - l_int32 minw, - l_int32 minh) -{ -l_int32 n, i, w, h, same, maxd, fullpa, fullba; -BOX *box; -PIX *pix1, *pix2, *pix3; -PIXA *pixad; - - PROCNAME("pixaMorphSequenceByRegion"); - - if (!pixs) - return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (PIXA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); - if (!sequence) - return (PIXA *)ERROR_PTR("sequence not defined", procName, NULL); - if (!pixam) - return (PIXA *)ERROR_PTR("pixam not defined", procName, NULL); - pixaVerifyDepth(pixam, &same, &maxd); - if (maxd != 1) - return (PIXA *)ERROR_PTR("mask depth not 1 bpp", procName, NULL); - pixaIsFull(pixam, &fullpa, &fullba); - if (!fullpa || !fullba) - return (PIXA *)ERROR_PTR("missing comps in pixam", procName, NULL); - n = pixaGetCount(pixam); - if (minw <= 0) minw = 1; - if (minh <= 0) minh = 1; - - if ((pixad = pixaCreate(n)) == NULL) - return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); - - /* Use the rectangle to remove the appropriate part of pixs; - * then AND with the mask component to get the actual fg - * of pixs that is under the mask component. */ - for (i = 0; i < n; i++) { - pixaGetPixDimensions(pixam, i, &w, &h, NULL); - if (w >= minw && h >= minh) { - pix1 = pixaGetPix(pixam, i, L_CLONE); - box = pixaGetBox(pixam, i, L_COPY); - pix2 = pixClipRectangle(pixs, box, NULL); - pixAnd(pix2, pix2, pix1); - pix3 = pixMorphCompSequence(pix2, sequence, 0); - pixDestroy(&pix1); - pixDestroy(&pix2); - if (!pix3) { - boxDestroy(&box); - pixaDestroy(&pixad); - L_ERROR("pix3 not made in iter %d; aborting\n", procName, i); - break; - } - pixaAddPix(pixad, pix3, L_INSERT); - pixaAddBox(pixad, box, L_INSERT); - } - } - - return pixad; -} - - -/*-----------------------------------------------------------------* - * Union and intersection of parallel composite operations * - *-----------------------------------------------------------------*/ -/*! - * \brief pixUnionOfMorphOps() - * - * \param[in] pixs 1 bpp - * \param[in] sela - * \param[in] type L_MORPH_DILATE, etc. - * \return pixd union of the specified morphological operation - * on pixs for each Sel in the Sela, or NULL on error - */ -PIX * -pixUnionOfMorphOps(PIX *pixs, - SELA *sela, - l_int32 type) -{ -l_int32 n, i; -PIX *pixt, *pixd; -SEL *sel; - - PROCNAME("pixUnionOfMorphOps"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (!sela) - return (PIX *)ERROR_PTR("sela not defined", procName, NULL); - n = selaGetCount(sela); - if (n == 0) - return (PIX *)ERROR_PTR("no sels in sela", procName, NULL); - if (type != L_MORPH_DILATE && type != L_MORPH_ERODE && - type != L_MORPH_OPEN && type != L_MORPH_CLOSE && - type != L_MORPH_HMT) - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - - pixd = pixCreateTemplate(pixs); - for (i = 0; i < n; i++) { - sel = selaGetSel(sela, i); - if (type == L_MORPH_DILATE) - pixt = pixDilate(NULL, pixs, sel); - else if (type == L_MORPH_ERODE) - pixt = pixErode(NULL, pixs, sel); - else if (type == L_MORPH_OPEN) - pixt = pixOpen(NULL, pixs, sel); - else if (type == L_MORPH_CLOSE) - pixt = pixClose(NULL, pixs, sel); - else /* type == L_MORPH_HMT */ - pixt = pixHMT(NULL, pixs, sel); - pixOr(pixd, pixd, pixt); - pixDestroy(&pixt); - } - - return pixd; -} - - -/*! - * \brief pixIntersectionOfMorphOps() - * - * \param[in] pixs 1 bpp - * \param[in] sela - * \param[in] type L_MORPH_DILATE, etc. - * \return pixd intersection of the specified morphological operation - * on pixs for each Sel in the Sela, or NULL on error - */ -PIX * -pixIntersectionOfMorphOps(PIX *pixs, - SELA *sela, - l_int32 type) -{ -l_int32 n, i; -PIX *pixt, *pixd; -SEL *sel; - - PROCNAME("pixIntersectionOfMorphOps"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (!sela) - return (PIX *)ERROR_PTR("sela not defined", procName, NULL); - n = selaGetCount(sela); - if (n == 0) - return (PIX *)ERROR_PTR("no sels in sela", procName, NULL); - if (type != L_MORPH_DILATE && type != L_MORPH_ERODE && - type != L_MORPH_OPEN && type != L_MORPH_CLOSE && - type != L_MORPH_HMT) - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - - pixd = pixCreateTemplate(pixs); - pixSetAll(pixd); - for (i = 0; i < n; i++) { - sel = selaGetSel(sela, i); - if (type == L_MORPH_DILATE) - pixt = pixDilate(NULL, pixs, sel); - else if (type == L_MORPH_ERODE) - pixt = pixErode(NULL, pixs, sel); - else if (type == L_MORPH_OPEN) - pixt = pixOpen(NULL, pixs, sel); - else if (type == L_MORPH_CLOSE) - pixt = pixClose(NULL, pixs, sel); - else /* type == L_MORPH_HMT */ - pixt = pixHMT(NULL, pixs, sel); - pixAnd(pixd, pixd, pixt); - pixDestroy(&pixt); - } - - return pixd; -} - - - -/*-----------------------------------------------------------------* - * Selective connected component filling * - *-----------------------------------------------------------------*/ -/*! - * \brief pixSelectiveConnCompFill() - * - * \param[in] pixs 1 bpp - * \param[in] connectivity 4 or 8 - * \param[in] minw min width to consider; use 0 or 1 for any width - * \param[in] minh min height to consider; use 0 or 1 for any height - * \return pix with holes filled in selected c.c., or NULL on error - */ -PIX * -pixSelectiveConnCompFill(PIX *pixs, - l_int32 connectivity, - l_int32 minw, - l_int32 minh) -{ -l_int32 n, i, x, y, w, h; -BOXA *boxa; -PIX *pix1, *pix2, *pixd; -PIXA *pixa; - - PROCNAME("pixSelectiveConnCompFill"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); - if (minw <= 0) minw = 1; - if (minh <= 0) minh = 1; - - if ((boxa = pixConnComp(pixs, &pixa, connectivity)) == NULL) - return (PIX *)ERROR_PTR("boxa not made", procName, NULL); - n = boxaGetCount(boxa); - pixd = pixCopy(NULL, pixs); - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); - if (w >= minw && h >= minh) { - pix1 = pixaGetPix(pixa, i, L_CLONE); - if ((pix2 = pixHolesByFilling(pix1, 12 - connectivity)) == NULL) { - L_ERROR("pix2 not made in iter %d\n", procName, i); - pixDestroy(&pix1); - continue; - } - pixRasterop(pixd, x, y, w, h, PIX_PAINT, pix2, 0, 0); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - } - pixaDestroy(&pixa); - boxaDestroy(&boxa); - - return pixd; -} - - -/*-----------------------------------------------------------------* - * Removal of matched patterns * - *-----------------------------------------------------------------*/ -/*! - * \brief pixRemoveMatchedPattern() - * - * \param[in] pixs input image, 1 bpp - * \param[in] pixp pattern to be removed from image, 1 bpp - * \param[in] pixe image after erosion by Sel that approximates pixp - * \param[in] x0, y0 center of Sel - * \param[in] dsize number of pixels on each side by which pixp is - * dilated before being subtracted from pixs; - * valid values are {0, 1, 2, 3, 4} - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is in-place. - * (2) You can use various functions in selgen to create a Sel - * that is used to generate pixe from pixs. - * (3) This function is applied after pixe has been computed. - * It finds the centroid of each c.c., and subtracts - * (the appropriately dilated version of) pixp, with the center - * of the Sel used to align pixp with pixs. - *- */ -l_ok -pixRemoveMatchedPattern(PIX *pixs, - PIX *pixp, - PIX *pixe, - l_int32 x0, - l_int32 y0, - l_int32 dsize) -{ -l_int32 i, nc, x, y, w, h, xb, yb; -BOXA *boxa; -PIX *pix1, *pix2; -PIXA *pixa; -PTA *pta; -SEL *sel; - - PROCNAME("pixRemoveMatchedPattern"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!pixp) - return ERROR_INT("pixp not defined", procName, 1); - if (!pixe) - return ERROR_INT("pixe not defined", procName, 1); - if (pixGetDepth(pixs) != 1 || pixGetDepth(pixp) != 1 || - pixGetDepth(pixe) != 1) - return ERROR_INT("all input pix not 1 bpp", procName, 1); - if (dsize < 0 || dsize > 4) - return ERROR_INT("dsize not in {0,1,2,3,4}", procName, 1); - - /* Find the connected components and their centroids */ - boxa = pixConnComp(pixe, &pixa, 8); - if ((nc = boxaGetCount(boxa)) == 0) { - L_WARNING("no matched patterns\n", procName); - boxaDestroy(&boxa); - pixaDestroy(&pixa); - return 0; - } - pta = pixaCentroids(pixa); - pixaDestroy(&pixa); - - /* Optionally dilate the pattern, first adding a border that - * is large enough to accommodate the dilated pixels */ - sel = NULL; - if (dsize > 0) { - sel = selCreateBrick(2 * dsize + 1, 2 * dsize + 1, dsize, dsize, - SEL_HIT); - pix1 = pixAddBorder(pixp, dsize, 0); - pix2 = pixDilate(NULL, pix1, sel); - selDestroy(&sel); - pixDestroy(&pix1); - } else { - pix2 = pixClone(pixp); - } - - /* Subtract out each dilated pattern. The centroid of each - * component is located at: - * (box->x + x, box->y + y) - * and the 'center' of the pattern used in making pixe is located at - * (x0 + dsize, (y0 + dsize) - * relative to the UL corner of the pattern. The center of the - * pattern is placed at the center of the component. */ - pixGetDimensions(pix2, &w, &h, NULL); - for (i = 0; i < nc; i++) { - ptaGetIPt(pta, i, &x, &y); - boxaGetBoxGeometry(boxa, i, &xb, &yb, NULL, NULL); - pixRasterop(pixs, xb + x - x0 - dsize, yb + y - y0 - dsize, - w, h, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0); - } - - boxaDestroy(&boxa); - ptaDestroy(&pta); - pixDestroy(&pix2); - return 0; -} - - -/*-----------------------------------------------------------------* - * Display of matched patterns * - *-----------------------------------------------------------------*/ -/*! - * \brief pixDisplayMatchedPattern() - * - * \param[in] pixs input image, 1 bpp - * \param[in] pixp pattern to be removed from image, 1 bpp - * \param[in] pixe image after erosion by Sel that approximates pixp - * \param[in] x0, y0 center of Sel - * \param[in] color to paint the matched patterns; 0xrrggbb00 - * \param[in] scale reduction factor for output pixd - * \param[in] nlevels if scale < 1.0, threshold to this number of levels - * \return pixd 8 bpp, colormapped, or NULL on error - * - *
- * Notes: - * (1) A 4 bpp colormapped image is generated. - * (2) If scale <= 1.0, do scale to gray for the output, and threshold - * to nlevels of gray. - * (3) You can use various functions in selgen to create a Sel - * that will generate pixe from pixs. - * (4) This function is applied after pixe has been computed. - * It finds the centroid of each c.c., and colors the output - * pixels using pixp (appropriately aligned) as a stencil. - * Alignment is done using the origin of the Sel and the - * centroid of the eroded image to place the stencil pixp. - *- */ -PIX * -pixDisplayMatchedPattern(PIX *pixs, - PIX *pixp, - PIX *pixe, - l_int32 x0, - l_int32 y0, - l_uint32 color, - l_float32 scale, - l_int32 nlevels) -{ -l_int32 i, nc, xb, yb, x, y, xi, yi, rval, gval, bval; -BOXA *boxa; -PIX *pixd, *pixt, *pixps; -PIXA *pixa; -PTA *pta; -PIXCMAP *cmap; - - PROCNAME("pixDisplayMatchedPattern"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!pixp) - return (PIX *)ERROR_PTR("pixp not defined", procName, NULL); - if (!pixe) - return (PIX *)ERROR_PTR("pixe not defined", procName, NULL); - if (pixGetDepth(pixs) != 1 || pixGetDepth(pixp) != 1 || - pixGetDepth(pixe) != 1) - return (PIX *)ERROR_PTR("all input pix not 1 bpp", procName, NULL); - if (scale > 1.0 || scale <= 0.0) { - L_WARNING("scale > 1.0 or < 0.0; setting to 1.0\n", procName); - scale = 1.0; - } - - /* Find the connected components and their centroids */ - boxa = pixConnComp(pixe, &pixa, 8); - if ((nc = boxaGetCount(boxa)) == 0) { - L_WARNING("no matched patterns\n", procName); - boxaDestroy(&boxa); - pixaDestroy(&pixa); - return 0; - } - pta = pixaCentroids(pixa); - - extractRGBValues(color, &rval, &gval, &bval); - if (scale == 1.0) { /* output 4 bpp at full resolution */ - pixd = pixConvert1To4(NULL, pixs, 0, 1); - cmap = pixcmapCreate(4); - pixcmapAddColor(cmap, 255, 255, 255); - pixcmapAddColor(cmap, 0, 0, 0); - pixSetColormap(pixd, cmap); - - /* Paint through pixp for each match location. The centroid of each - * component in pixe is located at: - * (box->x + x, box->y + y) - * and the 'center' of the pattern used in making pixe is located at - * (x0, y0) - * relative to the UL corner of the pattern. The center of the - * pattern is placed at the center of the component. */ - for (i = 0; i < nc; i++) { - ptaGetIPt(pta, i, &x, &y); - boxaGetBoxGeometry(boxa, i, &xb, &yb, NULL, NULL); - pixSetMaskedCmap(pixd, pixp, xb + x - x0, yb + y - y0, - rval, gval, bval); - } - } else { /* output 4 bpp downscaled */ - pixt = pixScaleToGray(pixs, scale); - pixd = pixThresholdTo4bpp(pixt, nlevels, 1); - pixps = pixScaleBySampling(pixp, scale, scale); - - for (i = 0; i < nc; i++) { - ptaGetIPt(pta, i, &x, &y); - boxaGetBoxGeometry(boxa, i, &xb, &yb, NULL, NULL); - xi = (l_int32)(scale * (xb + x - x0)); - yi = (l_int32)(scale * (yb + y - y0)); - pixSetMaskedCmap(pixd, pixps, xi, yi, rval, gval, bval); - } - pixDestroy(&pixt); - pixDestroy(&pixps); - } - - boxaDestroy(&boxa); - pixaDestroy(&pixa); - ptaDestroy(&pta); - return pixd; -} - - -/*------------------------------------------------------------------------* - * Extension of pixa by iterative erosion or dilation (and by scaling) * - *------------------------------------------------------------------------*/ -/*! - * \brief pixaExtendByMorph() - * - * \param[in] pixas - * \param[in] type L_MORPH_DILATE, L_MORPH_ERODE - * \param[in] niters - * \param[in] sel used for dilation, erosion; uses 2x2 if null - * \param[in] include 1 to include a copy of the input pixas in pixad; - * 0 to omit - * \return pixad with derived pix, using all iterations, or NULL on error - * - *
- * Notes: - * (1) This dilates or erodes every pix in %pixas, iteratively, - * using the input Sel (or, if null, a 2x2 Sel by default), - * and puts the results in %pixad. - * (2) If %niters <= 0, this is a no-op; it returns a clone of pixas. - * (3) If %include == 1, the output %pixad contains all the pix - * in %pixas. Otherwise, it doesn't, but pixaJoin() can be - * used later to join pixas with pixad. - *- */ -PIXA * -pixaExtendByMorph(PIXA *pixas, - l_int32 type, - l_int32 niters, - SEL *sel, - l_int32 include) -{ -l_int32 maxdepth, i, j, n; -PIX *pix0, *pix1, *pix2; -SEL *selt; -PIXA *pixad; - - PROCNAME("pixaExtendByMorph"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas undefined", procName, NULL); - if (niters <= 0) { - L_INFO("niters = %d; nothing to do\n", procName, niters); - return pixaCopy(pixas, L_CLONE); - } - if (type != L_MORPH_DILATE && type != L_MORPH_ERODE) - return (PIXA *)ERROR_PTR("invalid type", procName, NULL); - pixaGetDepthInfo(pixas, &maxdepth, NULL); - if (maxdepth > 1) - return (PIXA *)ERROR_PTR("some pix have bpp > 1", procName, NULL); - - if (!sel) - selt = selCreateBrick(2, 2, 0, 0, SEL_HIT); /* default */ - else - selt = selCopy(sel); - n = pixaGetCount(pixas); - pixad = pixaCreate(n * niters); - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixas, i, L_CLONE); - if (include) pixaAddPix(pixad, pix1, L_COPY); - pix0 = pix1; /* need to keep the handle to destroy the clone */ - for (j = 0; j < niters; j++) { - if (type == L_MORPH_DILATE) { - pix2 = pixDilate(NULL, pix1, selt); - } else { /* L_MORPH_ERODE */ - pix2 = pixErode(NULL, pix1, selt); - } - pixaAddPix(pixad, pix2, L_INSERT); - pix1 = pix2; /* owned by pixad; do not destroy */ - } - pixDestroy(&pix0); - } - - selDestroy(&selt); - return pixad; -} - - -/*! - * \brief pixaExtendByScaling() - * - * \param[in] pixas - * \param[in] nasc numa of scaling factors - * \param[in] type L_HORIZ, L_VERT, L_BOTH_DIRECTIONS - * \param[in] include 1 to include a copy of the input pixas in pixad; - * 0 to omit - * \return pixad with derived pix, using all scalings, or NULL on error - * - *
- * Notes: - * (1) This scales every pix in %pixas by each factor in %nasc. - * and puts the results in %pixad. - * (2) If %include == 1, the output %pixad contains all the pix - * in %pixas. Otherwise, it doesn't, but pixaJoin() can be - * used later to join pixas with pixad. - *- */ -PIXA * -pixaExtendByScaling(PIXA *pixas, - NUMA *nasc, - l_int32 type, - l_int32 include) -{ -l_int32 i, j, n, nsc, w, h, scalew, scaleh; -l_float32 scalefact; -PIX *pix1, *pix2; -PIXA *pixad; - - PROCNAME("pixaExtendByScaling"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas undefined", procName, NULL); - if (!nasc || numaGetCount(nasc) == 0) - return (PIXA *)ERROR_PTR("nasc undefined or empty", procName, NULL); - if (type != L_HORIZ && type != L_VERT && type != L_BOTH_DIRECTIONS) - return (PIXA *)ERROR_PTR("invalid type", procName, NULL); - - n = pixaGetCount(pixas); - nsc = numaGetCount(nasc); - if ((pixad = pixaCreate(n * (nsc + 1))) == NULL) { - L_ERROR("pixad not made: n = %d, nsc = %d\n", procName, n, nsc); - return NULL; - } - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixas, i, L_CLONE); - if (include) pixaAddPix(pixad, pix1, L_COPY); - pixGetDimensions(pix1, &w, &h, NULL); - for (j = 0; j < nsc; j++) { - numaGetFValue(nasc, j, &scalefact); - scalew = w; - scaleh = h; - if (type == L_HORIZ || type == L_BOTH_DIRECTIONS) - scalew = w * scalefact; - if (type == L_VERT || type == L_BOTH_DIRECTIONS) - scaleh = h * scalefact; - pix2 = pixScaleToSize(pix1, scalew, scaleh); - pixaAddPix(pixad, pix2, L_INSERT); - } - pixDestroy(&pix1); - } - return pixad; -} - - -/*-----------------------------------------------------------------* - * Iterative morphological seed filling * - *-----------------------------------------------------------------*/ -/*! - * \brief pixSeedfillMorph() - * - * \param[in] pixs seed - * \param[in] pixm mask - * \param[in] maxiters use 0 to go to completion - * \param[in] connectivity 4 or 8 - * \return pixd after filling into the mask or NULL on error - * - *
- * Notes: - * (1) This is in general a very inefficient method for filling - * from a seed into a mask. Use it for a small number of iterations, - * but if you expect more than a few iterations, use - * pixSeedfillBinary(). - * (2) We use a 3x3 brick SEL for 8-cc filling and a 3x3 plus SEL for 4-cc. - *- */ -PIX * -pixSeedfillMorph(PIX *pixs, - PIX *pixm, - l_int32 maxiters, - l_int32 connectivity) -{ -l_int32 same, i; -PIX *pixt, *pixd, *temp; -SEL *sel_3; - - PROCNAME("pixSeedfillMorph"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (!pixm) - return (PIX *)ERROR_PTR("mask pix not defined", procName, NULL); - if (connectivity != 4 && connectivity != 8) - return (PIX *)ERROR_PTR("connectivity not in {4,8}", procName, NULL); - if (maxiters <= 0) maxiters = 1000; - if (pixSizesEqual(pixs, pixm) == 0) - return (PIX *)ERROR_PTR("pix sizes unequal", procName, NULL); - - if ((sel_3 = selCreateBrick(3, 3, 1, 1, SEL_HIT)) == NULL) - return (PIX *)ERROR_PTR("sel_3 not made", procName, NULL); - if (connectivity == 4) { /* remove corner hits to make a '+' */ - selSetElement(sel_3, 0, 0, SEL_DONT_CARE); - selSetElement(sel_3, 2, 2, SEL_DONT_CARE); - selSetElement(sel_3, 2, 0, SEL_DONT_CARE); - selSetElement(sel_3, 0, 2, SEL_DONT_CARE); - } - - pixt = pixCopy(NULL, pixs); - pixd = pixCreateTemplate(pixs); - for (i = 1; i <= maxiters; i++) { - pixDilate(pixd, pixt, sel_3); - pixAnd(pixd, pixd, pixm); - pixEqual(pixd, pixt, &same); - if (same || i == maxiters) - break; - else - SWAP(pixt, pixd); - } - lept_stderr(" Num iters in binary reconstruction = %d\n", i); - - pixDestroy(&pixt); - selDestroy(&sel_3); - return pixd; -} - - -/*-----------------------------------------------------------------* - * Granulometry on binary images * - *-----------------------------------------------------------------*/ -/*! - * \brief pixRunHistogramMorph() - * - * \param[in] pixs 1 bpp - * \param[in] runtype L_RUN_OFF, L_RUN_ON - * \param[in] direction L_HORIZ, L_VERT - * \param[in] maxsize size of largest runlength counted - * \return numa of run-lengths - */ -NUMA * -pixRunHistogramMorph(PIX *pixs, - l_int32 runtype, - l_int32 direction, - l_int32 maxsize) -{ -l_int32 count, i, size; -l_float32 val; -NUMA *na, *nah; -PIX *pix1, *pix2, *pix3; -SEL *sel_2a; - - PROCNAME("pixRunHistogramMorph"); - - if (!pixs) - return (NUMA *)ERROR_PTR("seed pix not defined", procName, NULL); - if (runtype != L_RUN_OFF && runtype != L_RUN_ON) - return (NUMA *)ERROR_PTR("invalid run type", procName, NULL); - if (direction != L_HORIZ && direction != L_VERT) - return (NUMA *)ERROR_PTR("direction not in {L_HORIZ, L_VERT}", - procName, NULL); - if (pixGetDepth(pixs) != 1) - return (NUMA *)ERROR_PTR("pixs must be binary", procName, NULL); - - if (direction == L_HORIZ) - sel_2a = selCreateBrick(1, 2, 0, 0, SEL_HIT); - else /* direction == L_VERT */ - sel_2a = selCreateBrick(2, 1, 0, 0, SEL_HIT); - if (!sel_2a) - return (NUMA *)ERROR_PTR("sel_2a not made", procName, NULL); - - if (runtype == L_RUN_OFF) { - if ((pix1 = pixCopy(NULL, pixs)) == NULL) { - selDestroy(&sel_2a); - return (NUMA *)ERROR_PTR("pix1 not made", procName, NULL); - } - pixInvert(pix1, pix1); - } else { /* runtype == L_RUN_ON */ - pix1 = pixClone(pixs); - } - - /* Get pixel counts at different stages of erosion */ - na = numaCreate(0); - pix2 = pixCreateTemplate(pixs); - pix3 = pixCreateTemplate(pixs); - pixCountPixels(pix1, &count, NULL); - numaAddNumber(na, count); - pixErode(pix2, pix1, sel_2a); - pixCountPixels(pix2, &count, NULL); - numaAddNumber(na, count); - for (i = 0; i < maxsize / 2; i++) { - pixErode(pix3, pix2, sel_2a); - pixCountPixels(pix3, &count, NULL); - numaAddNumber(na, count); - pixErode(pix2, pix3, sel_2a); - pixCountPixels(pix2, &count, NULL); - numaAddNumber(na, count); - } - - /* Compute length histogram */ - size = numaGetCount(na); - nah = numaCreate(size); - numaAddNumber(nah, 0); /* number at length 0 */ - for (i = 1; i < size - 1; i++) { - val = na->array[i+1] - 2 * na->array[i] + na->array[i-1]; - numaAddNumber(nah, val); - } - - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - selDestroy(&sel_2a); - numaDestroy(&na); - return nah; -} - - -/*-----------------------------------------------------------------* - * Composite operations on grayscale images * - *-----------------------------------------------------------------*/ -/*! - * \brief pixTophat() - * - * \param[in] pixs 1 bpp - * \param[in] hsize of Sel; must be odd; origin implicitly in center - * \param[in] vsize ditto - * \param[in] type L_TOPHAT_WHITE: image - opening - * L_TOPHAT_BLACK: closing - image - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Sel is a brick with all elements being hits - * (2) If hsize = vsize = 1, returns an image with all 0 data. - * (3) The L_TOPHAT_WHITE flag emphasizes small bright regions, - * whereas the L_TOPHAT_BLACK flag emphasizes small dark regions. - * The L_TOPHAT_WHITE tophat can be accomplished by doing a - * L_TOPHAT_BLACK tophat on the inverse, or v.v. - *- */ -PIX * -pixTophat(PIX *pixs, - l_int32 hsize, - l_int32 vsize, - l_int32 type) -{ -PIX *pixt, *pixd; - - PROCNAME("pixTophat"); - - if (!pixs) - return (PIX *)ERROR_PTR("seed pix not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); - if ((hsize & 1) == 0 ) { - L_WARNING("horiz sel size must be odd; increasing by 1\n", procName); - hsize++; - } - if ((vsize & 1) == 0 ) { - L_WARNING("vert sel size must be odd; increasing by 1\n", procName); - vsize++; - } - if (type != L_TOPHAT_WHITE && type != L_TOPHAT_BLACK) - return (PIX *)ERROR_PTR("type must be L_TOPHAT_BLACK or L_TOPHAT_WHITE", - procName, NULL); - - if (hsize == 1 && vsize == 1) - return pixCreateTemplate(pixs); - - switch (type) - { - case L_TOPHAT_WHITE: - if ((pixt = pixOpenGray(pixs, hsize, vsize)) == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, NULL); - pixd = pixSubtractGray(NULL, pixs, pixt); - pixDestroy(&pixt); - break; - case L_TOPHAT_BLACK: - if ((pixd = pixCloseGray(pixs, hsize, vsize)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixSubtractGray(pixd, pixd, pixs); - break; - default: - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - } - - return pixd; -} - - -/*! - * \brief pixHDome() - * - * \param[in] pixs 8 bpp, filling mask - * \param[in] height of seed below the filling maskhdome; must be >= 0 - * \param[in] connectivity 4 or 8 - * \return pixd 8 bpp, or NULL on error - * - *
- * Notes: - * (1) It is more efficient to use a connectivity of 4 for the fill. - * (2) This fills bumps to some level, and extracts the unfilled - * part of the bump. To extract the troughs of basins, first - * invert pixs and then apply pixHDome(). - * (3) It is useful to compare the HDome operation with the TopHat. - * The latter extracts peaks or valleys that have a width - * not exceeding the size of the structuring element used - * in the opening or closing, rsp. The height of the peak is - * irrelevant. By contrast, for the HDome, the gray seedfill - * is used to extract all peaks that have a height not exceeding - * a given value, regardless of their width! - * (4) Slightly more precisely, suppose you set 'height' = 40. - * Then all bumps in pixs with a height greater than or equal - * to 40 become, in pixd, bumps with a max value of exactly 40. - * All shorter bumps have a max value in pixd equal to the height - * of the bump. - * (5) The method: the filling mask, pixs, is the image whose peaks - * are to be extracted. The height of a peak is the distance - * between the top of the peak and the highest "leak" to the - * outside -- think of a sombrero, where the leak occurs - * at the highest point on the rim. - * (a) Generate a seed, pixd, by subtracting some value, p, from - * each pixel in the filling mask, pixs. The value p is - * the 'height' input to this function. - * (b) Fill in pixd starting with this seed, clipping by pixs, - * in the way described in seedfillGrayLow(). The filling - * stops before the peaks in pixs are filled. - * For peaks that have a height > p, pixd is filled to - * the level equal to the (top-of-the-peak - p). - * For peaks of height < p, the peak is left unfilled - * from its highest saddle point (the leak to the outside). - * (c) Subtract the filled seed (pixd) from the filling mask (pixs). - * Note that in this procedure, everything is done starting - * with the filling mask, pixs. - * (6) For segmentation, the resulting image, pixd, can be thresholded - * and used as a seed for another filling operation. - *- */ -PIX * -pixHDome(PIX *pixs, - l_int32 height, - l_int32 connectivity) -{ -PIX *pixsd, *pixd; - - PROCNAME("pixHDome"); - - if (!pixs) - return (PIX *)ERROR_PTR("src pix not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (height < 0) - return (PIX *)ERROR_PTR("height not >= 0", procName, NULL); - if (height == 0) - return pixCreateTemplate(pixs); - - if ((pixsd = pixCopy(NULL, pixs)) == NULL) - return (PIX *)ERROR_PTR("pixsd not made", procName, NULL); - pixAddConstantGray(pixsd, -height); - pixSeedfillGray(pixsd, pixs, connectivity); - pixd = pixSubtractGray(NULL, pixs, pixsd); - pixDestroy(&pixsd); - return pixd; -} - - -/*! - * \brief pixFastTophat() - * - * \param[in] pixs 8 bpp - * \param[in] xsize width of max/min op, smoothing; any integer >= 1 - * \param[in] ysize height of max/min op, smoothing; any integer >= 1 - * \param[in] type L_TOPHAT_WHITE: image - min - * L_TOPHAT_BLACK: max - image - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) Don't be fooled. This is NOT a tophat. It is a tophat-like - * operation, where the result is similar to what you'd get - * if you used an erosion instead of an opening, or a dilation - * instead of a closing. - * (2) Instead of opening or closing at full resolution, it does - * a fast downscale/minmax operation, then a quick small smoothing - * at low res, a replicative expansion of the "background" - * to full res, and finally a removal of the background level - * from the input image. The smoothing step may not be important. - * (3) It does not remove noise as well as a tophat, but it is - * 5 to 10 times faster. - * If you need the preciseness of the tophat, don't use this. - * (4) The L_TOPHAT_WHITE flag emphasizes small bright regions, - * whereas the L_TOPHAT_BLACK flag emphasizes small dark regions. - *- */ -PIX * -pixFastTophat(PIX *pixs, - l_int32 xsize, - l_int32 ysize, - l_int32 type) -{ -PIX *pix1, *pix2, *pix3, *pixd; - - PROCNAME("pixFastTophat"); - - if (!pixs) - return (PIX *)ERROR_PTR("seed pix not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (xsize < 1 || ysize < 1) - return (PIX *)ERROR_PTR("size < 1", procName, NULL); - if (type != L_TOPHAT_WHITE && type != L_TOPHAT_BLACK) - return (PIX *)ERROR_PTR("type must be L_TOPHAT_BLACK or L_TOPHAT_WHITE", - procName, NULL); - - if (xsize == 1 && ysize == 1) - return pixCreateTemplate(pixs); - - switch (type) - { - case L_TOPHAT_WHITE: - if ((pix1 = pixScaleGrayMinMax(pixs, xsize, ysize, L_CHOOSE_MIN)) - == NULL) - return (PIX *)ERROR_PTR("pix1 not made", procName, NULL); - pix2 = pixBlockconv(pix1, 1, 1); /* small smoothing */ - pix3 = pixScaleBySampling(pix2, xsize, ysize); - pixd = pixSubtractGray(NULL, pixs, pix3); - pixDestroy(&pix3); - break; - case L_TOPHAT_BLACK: - if ((pix1 = pixScaleGrayMinMax(pixs, xsize, ysize, L_CHOOSE_MAX)) - == NULL) - return (PIX *)ERROR_PTR("pix1 not made", procName, NULL); - pix2 = pixBlockconv(pix1, 1, 1); /* small smoothing */ - pixd = pixScaleBySampling(pix2, xsize, ysize); - pixSubtractGray(pixd, pixd, pixs); - break; - default: - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - } - - pixDestroy(&pix1); - pixDestroy(&pix2); - return pixd; -} - - -/*! - * \brief pixMorphGradient() - * - * \param[in] pixs 8 bpp - * \param[in] hsize sel width; must be odd; origin implicitly in center - * \param[in] vsize sel height - * \param[in] smoothing half-width of convolution smoothing filter. - * The width is (2 * smoothing + 1, so 0 is no-op. - * \return pixd, or NULL on error - */ -PIX * -pixMorphGradient(PIX *pixs, - l_int32 hsize, - l_int32 vsize, - l_int32 smoothing) -{ -PIX *pixg, *pixd; - - PROCNAME("pixMorphGradient"); - - if (!pixs) - return (PIX *)ERROR_PTR("seed pix not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL); - if ((hsize & 1) == 0 ) { - L_WARNING("horiz sel size must be odd; increasing by 1\n", procName); - hsize++; - } - if ((vsize & 1) == 0 ) { - L_WARNING("vert sel size must be odd; increasing by 1\n", procName); - vsize++; - } - - /* Optionally smooth first to remove noise. - * If smoothing is 0, just get a copy */ - pixg = pixBlockconvGray(pixs, NULL, smoothing, smoothing); - - /* This gives approximately the gradient of a transition */ - pixd = pixDilateGray(pixg, hsize, vsize); - pixSubtractGray(pixd, pixd, pixg); - pixDestroy(&pixg); - return pixd; -} - - -/*-----------------------------------------------------------------* - * Centroid of component * - *-----------------------------------------------------------------*/ -/*! - * \brief pixaCentroids() - * - * \param[in] pixa of components; 1 or 8 bpp - * \return pta of centroids relative to the UL corner of - * each pix, or NULL on error - * - *
- * Notes: - * (1) An error message is returned if any pix has something other - * than 1 bpp or 8 bpp depth, and the centroid from that pix - * is saved as (0, 0). - *- */ -PTA * -pixaCentroids(PIXA *pixa) -{ -l_int32 i, n; -l_int32 *centtab = NULL; -l_int32 *sumtab = NULL; -l_float32 x, y; -PIX *pix; -PTA *pta; - - PROCNAME("pixaCentroids"); - - if (!pixa) - return (PTA *)ERROR_PTR("pixa not defined", procName, NULL); - if ((n = pixaGetCount(pixa)) == 0) - return (PTA *)ERROR_PTR("no pix in pixa", procName, NULL); - - if ((pta = ptaCreate(n)) == NULL) - return (PTA *)ERROR_PTR("pta not defined", procName, NULL); - centtab = makePixelCentroidTab8(); - sumtab = makePixelSumTab8(); - - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixa, i, L_CLONE); - if (pixCentroid(pix, centtab, sumtab, &x, &y) == 1) - L_ERROR("centroid failure for pix %d\n", procName, i); - pixDestroy(&pix); - ptaAddPt(pta, x, y); - } - - LEPT_FREE(centtab); - LEPT_FREE(sumtab); - return pta; -} - - -/*! - * \brief pixCentroid() - * - * \param[in] pix 1 or 8 bpp - * \param[in] centtab [optional] table for finding centroids; can be null - * \param[in] sumtab [optional] table for finding pixel sums; can be null - * \param[out] pxave x coordinate of centroid, relative to the UL corner - * of the pix - * \param[out] pyave y coordinate of centroid, relative to the UL corner - * of the pix - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The sum and centroid tables are only used for 1 bpp. - * (2) Any table not passed in will be made internally and destroyed - * after use. - *- */ -l_ok -pixCentroid(PIX *pix, - l_int32 *centtab, - l_int32 *sumtab, - l_float32 *pxave, - l_float32 *pyave) -{ -l_int32 w, h, d, i, j, wpl, pixsum, rowsum, val; -l_float32 xsum, ysum; -l_uint32 *data, *line; -l_uint32 word; -l_uint8 byte; -l_int32 *ctab, *stab; - - PROCNAME("pixCentroid"); - - if (!pxave || !pyave) - return ERROR_INT("&pxave and &pyave not defined", procName, 1); - *pxave = *pyave = 0.0; - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - pixGetDimensions(pix, &w, &h, &d); - if (d != 1 && d != 8) - return ERROR_INT("pix not 1 or 8 bpp", procName, 1); - - ctab = centtab; - stab = sumtab; - if (d == 1) { - pixSetPadBits(pix, 0); - if (!centtab) - ctab = makePixelCentroidTab8(); - if (!sumtab) - stab = makePixelSumTab8(); - } - - data = pixGetData(pix); - wpl = pixGetWpl(pix); - xsum = ysum = 0.0; - pixsum = 0; - if (d == 1) { - for (i = 0; i < h; i++) { - /* The body of this loop computes the sum of the set - * (1) bits on this row, weighted by their distance - * from the left edge of pix, and accumulates that into - * xsum; it accumulates their distance from the top - * edge of pix into ysum, and their total count into - * pixsum. It's equivalent to - * for (j = 0; j < w; j++) { - * if (GET_DATA_BIT(line, j)) { - * xsum += j; - * ysum += i; - * pixsum++; - * } - * } - */ - line = data + wpl * i; - rowsum = 0; - for (j = 0; j < wpl; j++) { - word = line[j]; - if (word) { - byte = word & 0xff; - rowsum += stab[byte]; - xsum += ctab[byte] + (j * 32 + 24) * stab[byte]; - byte = (word >> 8) & 0xff; - rowsum += stab[byte]; - xsum += ctab[byte] + (j * 32 + 16) * stab[byte]; - byte = (word >> 16) & 0xff; - rowsum += stab[byte]; - xsum += ctab[byte] + (j * 32 + 8) * stab[byte]; - byte = (word >> 24) & 0xff; - rowsum += stab[byte]; - xsum += ctab[byte] + j * 32 * stab[byte]; - } - } - pixsum += rowsum; - ysum += rowsum * i; - } - if (pixsum == 0) { - L_WARNING("no ON pixels in pix\n", procName); - } else { - *pxave = xsum / (l_float32)pixsum; - *pyave = ysum / (l_float32)pixsum; - } - } else { /* d == 8 */ - for (i = 0; i < h; i++) { - line = data + wpl * i; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(line, j); - xsum += val * j; - ysum += val * i; - pixsum += val; - } - } - if (pixsum == 0) { - L_WARNING("all pixels are 0\n", procName); - } else { - *pxave = xsum / (l_float32)pixsum; - *pyave = ysum / (l_float32)pixsum; - } - } - - if (!centtab) LEPT_FREE(ctab); - if (!sumtab) LEPT_FREE(stab); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/morphdwa.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/morphdwa.c deleted file mode 100644 index b362599e..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/morphdwa.c +++ /dev/null @@ -1,1599 +0,0 @@ -/*====================================================================* - - 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 morphdwa.c - *
- * - * Binary morphological (dwa) ops with brick Sels - * PIX *pixDilateBrickDwa() - * PIX *pixErodeBrickDwa() - * PIX *pixOpenBrickDwa() - * PIX *pixCloseBrickDwa() - * - * Binary composite morphological (dwa) ops with brick Sels - * PIX *pixDilateCompBrickDwa() - * PIX *pixErodeCompBrickDwa() - * PIX *pixOpenCompBrickDwa() - * PIX *pixCloseCompBrickDwa() - * - * Binary extended composite morphological (dwa) ops with brick Sels - * PIX *pixDilateCompBrickExtendDwa() - * PIX *pixErodeCompBrickExtendDwa() - * PIX *pixOpenCompBrickExtendDwa() - * PIX *pixCloseCompBrickExtendDwa() - * l_int32 getExtendedCompositeParameters() - * - * These are higher-level interfaces for dwa morphology with brick Sels. - * Because many morphological operations are performed using - * separable brick Sels, it is useful to have a simple interface - * for this. - * - * We have included all 58 of the brick Sels that are generated - * by selaAddBasic(). These are sufficient for all the decomposable - * bricks up to size 63, which is the limit for dwa Sels with - * origins at the center of the Sel. - * - * All three sets can be used as the basic interface for general - * brick operations. Here are the internal calling sequences: - * - * (1) If you try to apply a non-decomposable operation, such as - * pixErodeBrickDwa(), with a Sel size that doesn't exist, - * this calls a decomposable operation, pixErodeCompBrickDwa(), - * instead. This can differ in linear Sel size by up to - * 2 pixels from the request. - * - * (2) If either Sel brick dimension is greater than 63, the extended - * composite function is called. - * - * (3) The extended composite function calls the composite function - * a number of times with size 63, and once with size < 63. - * Because each operation with a size of 63 is done compositely - * with 7 x 9 (exactly 63), the net result is correct in - * length to within 2 pixels. - * - * For composite operations, both using a comb and extended (beyond 63), - * horizontal and vertical operations are composed separately - * and sequentially. - * - * We have also included use of all the 76 comb Sels that are generated - * by selaAddDwaCombs(). The generated code is in dwacomb.2.c - * and dwacomblow.2.c. These are used for the composite dwa - * brick operations. - * - * The non-composite brick operations, such as pixDilateBrickDwa(), - * will call the associated composite operation in situations where - * the requisite brick Sel has not been compiled into fmorphgen*.1.c. - * - * If you want to use brick Sels that are not represented in the - * basic set of 58, you must generate the dwa code to implement them. - * You have three choices for how to use these: - * - * (1) Add both the new Sels and the dwa code to the library: - * ~ For simplicity, add your new brick Sels to those defined - * in selaAddBasic(). - * ~ Recompile the library. - * ~ Make prog/fmorphautogen. - * ~ Run prog/fmorphautogen, to generate new versions of the - * dwa code in fmorphgen.1.c and fmorphgenlow.1.c. - * ~ Copy these two files to src. - * ~ Recompile the library again. - * ~ Use the new brick Sels in your program and compile it. - * - * (2) Make both the new Sels and dwa code outside the library, - * and link it directly to an executable: - * ~ Write a function to generate the new Sels in a Sela, and call - * fmorphautogen(sela,- */ - -#ifdef HAVE_CONFIG_H -#include, filename) to generate the code. - * ~ Compile your program that uses the newly generated function - * pixMorphDwa_ (), and link to the two new C files. - * - * (3) Make the new Sels in the library and use the dwa code outside it: - * ~ Add code in the library to generate your new brick Sels. - * (It is suggested that you NOT add these Sels to the - * selaAddBasic() function; write a new function that generates - * a new Sela.) - * ~ Recompile the library. - * ~ Write a small program that generates the Sela and calls - * fmorphautogen(sela, , filename) to generate the code. - * ~ Compile your program that uses the newly generated function - * pixMorphDwa_ (), and link to the two new C files. - * As an example of this approach, see prog/dwamorph*_reg.c: - * ~ added selaAddDwaLinear() to sel2.c - * ~ wrote dwamorph1_reg.c, to generate the dwa code. - * ~ compiled and linked the generated code with the application, - * dwamorph2_reg.c. (Note: because this was a regression test, - * dwamorph1_reg also builds and runs the application program.) - *
- * Notes: - * (1) These implement 2D brick Sels, using linear Sels generated - * with selaAddBasic(). - * (2) A brick Sel has hits for all elements. - * (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) - * (4) Do separably if both hsize and vsize are > 1. - * (5) It is necessary that both horizontal and vertical Sels - * of the input size are defined in the basic sela. - * (6) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (7) For clarity, if the case is known, use these patterns: - * (a) pixd = pixDilateBrickDwa(NULL, pixs, ...); - * (b) pixDilateBrickDwa(pixs, pixs, ...); - * (c) pixDilateBrickDwa(pixd, pixs, ...); - * (8) The size of pixd is determined by pixs. - * (9) If either linear Sel is not found, this calls - * the appropriate decomposible function. - *- */ -PIX * -pixDilateBrickDwa(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -l_int32 found; -char *selnameh, *selnamev; -SELA *sela; -PIX *pixt1, *pixt2, *pixt3; - - PROCNAME("pixDilateBrickDwa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - - sela = selaAddBasic(NULL); - found = TRUE; - selnameh = selnamev = NULL; - if (hsize > 1) { - selnameh = selaGetBrickName(sela, hsize, 1); - if (!selnameh) found = FALSE; - } - if (vsize > 1) { - selnamev = selaGetBrickName(sela, 1, vsize); - if (!selnamev) found = FALSE; - } - selaDestroy(&sela); - if (!found) { - L_INFO("Calling the decomposable dwa function\n", procName); - if (selnameh) LEPT_FREE(selnameh); - if (selnamev) LEPT_FREE(selnamev); - return pixDilateCompBrickDwa(pixd, pixs, hsize, vsize); - } - - if (vsize == 1) { - pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnameh); - LEPT_FREE(selnameh); - } else if (hsize == 1) { - pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnamev); - LEPT_FREE(selnamev); - } else { - pixt1 = pixAddBorder(pixs, 32, 0); - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh); - pixFMorphopGen_1(pixt1, pixt3, L_MORPH_DILATE, selnamev); - pixt2 = pixRemoveBorder(pixt1, 32); - pixDestroy(&pixt1); - pixDestroy(&pixt3); - LEPT_FREE(selnameh); - LEPT_FREE(selnamev); - } - - if (!pixd) - return pixt2; - - pixTransferAllData(pixd, &pixt2, 0, 0); - return pixd; -} - - -/*! - * \brief pixErodeBrickDwa() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd - * - *
- * Notes: - * (1) These implement 2D brick Sels, using linear Sels generated - * with selaAddBasic(). - * (2) A brick Sel has hits for all elements. - * (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) - * (4) Do separably if both hsize and vsize are > 1. - * (5) It is necessary that both horizontal and vertical Sels - * of the input size are defined in the basic sela. - * (6) Note that we must always set or clear the border pixels - * before each operation, depending on the the b.c. - * (symmetric or asymmetric). - * (7) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (8) For clarity, if the case is known, use these patterns: - * (a) pixd = pixErodeBrickDwa(NULL, pixs, ...); - * (b) pixErodeBrickDwa(pixs, pixs, ...); - * (c) pixErodeBrickDwa(pixd, pixs, ...); - * (9) The size of the result is determined by pixs. - * (10) If either linear Sel is not found, this calls - * the appropriate decomposible function. - *- */ -PIX * -pixErodeBrickDwa(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -l_int32 found; -char *selnameh, *selnamev; -SELA *sela; -PIX *pixt1, *pixt2, *pixt3; - - PROCNAME("pixErodeBrickDwa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - - sela = selaAddBasic(NULL); - found = TRUE; - selnameh = selnamev = NULL; - if (hsize > 1) { - selnameh = selaGetBrickName(sela, hsize, 1); - if (!selnameh) found = FALSE; - } - if (vsize > 1) { - selnamev = selaGetBrickName(sela, 1, vsize); - if (!selnamev) found = FALSE; - } - selaDestroy(&sela); - if (!found) { - L_INFO("Calling the decomposable dwa function\n", procName); - if (selnameh) LEPT_FREE(selnameh); - if (selnamev) LEPT_FREE(selnamev); - return pixErodeCompBrickDwa(pixd, pixs, hsize, vsize); - } - - if (vsize == 1) { - pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnameh); - LEPT_FREE(selnameh); - } else if (hsize == 1) { - pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnamev); - LEPT_FREE(selnamev); - } else { - pixt1 = pixAddBorder(pixs, 32, 0); - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh); - pixFMorphopGen_1(pixt1, pixt3, L_MORPH_ERODE, selnamev); - pixt2 = pixRemoveBorder(pixt1, 32); - pixDestroy(&pixt1); - pixDestroy(&pixt3); - LEPT_FREE(selnameh); - LEPT_FREE(selnamev); - } - - if (!pixd) - return pixt2; - - pixTransferAllData(pixd, &pixt2, 0, 0); - return pixd; -} - - -/*! - * \brief pixOpenBrickDwa() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd - * - *
- * Notes: - * (1) These implement 2D brick Sels, using linear Sels generated - * with selaAddBasic(). - * (2) A brick Sel has hits for all elements. - * (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) - * (4) Do separably if both hsize and vsize are > 1. - * (5) It is necessary that both horizontal and vertical Sels - * of the input size are defined in the basic sela. - * (6) Note that we must always set or clear the border pixels - * before each operation, depending on the the b.c. - * (symmetric or asymmetric). - * (7) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (8) For clarity, if the case is known, use these patterns: - * (a) pixd = pixOpenBrickDwa(NULL, pixs, ...); - * (b) pixOpenBrickDwa(pixs, pixs, ...); - * (c) pixOpenBrickDwa(pixd, pixs, ...); - * (9) The size of the result is determined by pixs. - * (10) If either linear Sel is not found, this calls - * the appropriate decomposible function. - *- */ -PIX * -pixOpenBrickDwa(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -l_int32 found; -char *selnameh, *selnamev; -SELA *sela; -PIX *pixt1, *pixt2, *pixt3; - - PROCNAME("pixOpenBrickDwa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - - sela = selaAddBasic(NULL); - found = TRUE; - selnameh = selnamev = NULL; - if (hsize > 1) { - selnameh = selaGetBrickName(sela, hsize, 1); - if (!selnameh) found = FALSE; - } - if (vsize > 1) { - selnamev = selaGetBrickName(sela, 1, vsize); - if (!selnamev) found = FALSE; - } - selaDestroy(&sela); - if (!found) { - L_INFO("Calling the decomposable dwa function\n", procName); - if (selnameh) LEPT_FREE(selnameh); - if (selnamev) LEPT_FREE(selnamev); - return pixOpenCompBrickDwa(pixd, pixs, hsize, vsize); - } - - pixt1 = pixAddBorder(pixs, 32, 0); - if (vsize == 1) { /* horizontal only */ - pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnameh); - LEPT_FREE(selnameh); - } else if (hsize == 1) { /* vertical only */ - pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnamev); - LEPT_FREE(selnamev); - } else { /* do separable */ - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh); - pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh); - pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev); - LEPT_FREE(selnameh); - LEPT_FREE(selnamev); - pixDestroy(&pixt3); - } - pixt3 = pixRemoveBorder(pixt2, 32); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - - if (!pixd) - return pixt3; - - pixTransferAllData(pixd, &pixt3, 0, 0); - return pixd; -} - - -/*! - * \brief pixCloseBrickDwa() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd - * - *
- * Notes: - * (1) This is a 'safe' closing; we add an extra border of 32 OFF - * pixels for the standard asymmetric b.c. - * (2) These implement 2D brick Sels, using linear Sels generated - * with selaAddBasic(). - * (3) A brick Sel has hits for all elements. - * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) - * (5) Do separably if both hsize and vsize are > 1. - * (6) It is necessary that both horizontal and vertical Sels - * of the input size are defined in the basic sela. - * (7) Note that we must always set or clear the border pixels - * before each operation, depending on the the b.c. - * (symmetric or asymmetric). - * (8) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (9) For clarity, if the case is known, use these patterns: - * (a) pixd = pixCloseBrickDwa(NULL, pixs, ...); - * (b) pixCloseBrickDwa(pixs, pixs, ...); - * (c) pixCloseBrickDwa(pixd, pixs, ...); - * (10) The size of the result is determined by pixs. - * (11) If either linear Sel is not found, this calls - * the appropriate decomposible function. - *- */ -PIX * -pixCloseBrickDwa(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -l_int32 bordercolor, bordersize, found; -char *selnameh, *selnamev; -SELA *sela; -PIX *pixt1, *pixt2, *pixt3; - - PROCNAME("pixCloseBrickDwa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - - sela = selaAddBasic(NULL); - found = TRUE; - selnameh = selnamev = NULL; - if (hsize > 1) { - selnameh = selaGetBrickName(sela, hsize, 1); - if (!selnameh) found = FALSE; - } - if (vsize > 1) { - selnamev = selaGetBrickName(sela, 1, vsize); - if (!selnamev) found = FALSE; - } - selaDestroy(&sela); - if (!found) { - L_INFO("Calling the decomposable dwa function\n", procName); - if (selnameh) LEPT_FREE(selnameh); - if (selnamev) LEPT_FREE(selnamev); - return pixCloseCompBrickDwa(pixd, pixs, hsize, vsize); - } - - /* For "safe closing" with ASYMMETRIC_MORPH_BC, we always need - * an extra 32 OFF pixels around the image (in addition to - * the 32 added pixels for all dwa operations), whereas with - * SYMMETRIC_MORPH_BC this is not necessary. */ - bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); - if (bordercolor == 0) /* asymmetric b.c. */ - bordersize = 64; - else /* symmetric b.c. */ - bordersize = 32; - pixt1 = pixAddBorder(pixs, bordersize, 0); - - if (vsize == 1) { /* horizontal only */ - pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh); - LEPT_FREE(selnameh); - } else if (hsize == 1) { /* vertical only */ - pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev); - LEPT_FREE(selnamev); - } else { /* do separable */ - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh); - pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh); - pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev); - LEPT_FREE(selnameh); - LEPT_FREE(selnamev); - pixDestroy(&pixt3); - } - pixt3 = pixRemoveBorder(pixt2, bordersize); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - - if (!pixd) - return pixt3; - - pixTransferAllData(pixd, &pixt3, 0, 0); - return pixd; -} - - -/*-----------------------------------------------------------------* - * Binary composite morphological (dwa) ops with brick Sels * - *-----------------------------------------------------------------*/ -/*! - * \brief pixDilateCompBrickDwa() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd - * - *
- * Notes: - * (1) These implement a separable composite dilation with 2D brick Sels. - * (2) For efficiency, it may decompose each linear morphological - * operation into two (brick + comb). - * (3) A brick Sel has hits for all elements. - * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) - * (5) Do separably if both hsize and vsize are > 1. - * (6) It is necessary that both horizontal and vertical Sels - * of the input size are defined in the basic sela. - * (7) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (8) For clarity, if the case is known, use these patterns: - * (a) pixd = pixDilateCompBrickDwa(NULL, pixs, ...); - * (b) pixDilateCompBrickDwa(pixs, pixs, ...); - * (c) pixDilateCompBrickDwa(pixd, pixs, ...); - * (9) The size of pixd is determined by pixs. - * (10) CAUTION: both hsize and vsize are being decomposed. - * The decomposer chooses a product of sizes (call them - * 'terms') for each that is close to the input size, - * but not necessarily equal to it. It attempts to optimize: - * (a) for consistency with the input values: the product - * of terms is close to the input size - * (b) for efficiency of the operation: the sum of the - * terms is small; ideally about twice the square - * root of the input size. - * So, for example, if the input hsize = 37, which is - * a prime number, the decomposer will break this into two - * terms, 6 and 6, so that the net result is a dilation - * with hsize = 36. - *- */ -PIX * -pixDilateCompBrickDwa(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -char *selnameh1, *selnameh2, *selnamev1, *selnamev2; -l_int32 hsize1, hsize2, vsize1, vsize2; -PIX *pixt1, *pixt2, *pixt3; - - PROCNAME("pixDilateCompBrickDwa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - if (hsize > 63 || vsize > 63) - return pixDilateCompBrickExtendDwa(pixd, pixs, hsize, vsize); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - - hsize1 = hsize2 = vsize1 = vsize2 = 1; - selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; - if (hsize > 1) - getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, - &selnameh2, NULL, NULL); - if (vsize > 1) - getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, - &selnamev1, &selnamev2); - -#if DEBUG_SEL_LOOKUP - lept_stderr("nameh1=%s, nameh2=%s, namev1=%s, namev2=%s\n", - selnameh1, selnameh2, selnamev1, selnamev2); - lept_stderr("hsize1=%d, hsize2=%d, vsize1=%d, vsize2=%d\n", - hsize1, hsize2, vsize1, vsize2); -#endif /* DEBUG_SEL_LOOKUP */ - - pixt1 = pixAddBorder(pixs, 64, 0); - if (vsize == 1) { - if (hsize2 == 1) { - pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); - } else { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); - pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); - pixDestroy(&pixt3); - } - } else if (hsize == 1) { - if (vsize2 == 1) { - pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1); - } else { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1); - pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnamev2); - pixDestroy(&pixt3); - } - } else { /* vsize and hsize both > 1 */ - if (hsize2 == 1) { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); - } else { - pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); - pixt3 = pixFMorphopGen_2(NULL, pixt2, L_MORPH_DILATE, selnameh2); - pixDestroy(&pixt2); - } - if (vsize2 == 1) { - pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); - } else { - pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); - pixFMorphopGen_2(pixt2, pixt2, L_MORPH_DILATE, selnamev2); - } - pixDestroy(&pixt3); - } - pixDestroy(&pixt1); - pixt1 = pixRemoveBorder(pixt2, 64); - pixDestroy(&pixt2); - if (selnameh1) LEPT_FREE(selnameh1); - if (selnameh2) LEPT_FREE(selnameh2); - if (selnamev1) LEPT_FREE(selnamev1); - if (selnamev2) LEPT_FREE(selnamev2); - - if (!pixd) - return pixt1; - - pixTransferAllData(pixd, &pixt1, 0, 0); - return pixd; -} - - -/*! - * \brief pixErodeCompBrickDwa() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd - * - *
- * Notes: - * (1) These implement a separable composite erosion with 2D brick Sels. - * (2) For efficiency, it may decompose each linear morphological - * operation into two (brick + comb). - * (3) A brick Sel has hits for all elements. - * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) - * (5) Do separably if both hsize and vsize are > 1. - * (6) It is necessary that both horizontal and vertical Sels - * of the input size are defined in the basic sela. - * (7) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (8) For clarity, if the case is known, use these patterns: - * (a) pixd = pixErodeCompBrickDwa(NULL, pixs, ...); - * (b) pixErodeCompBrickDwa(pixs, pixs, ...); - * (c) pixErodeCompBrickDwa(pixd, pixs, ...); - * (9) The size of pixd is determined by pixs. - * (10) CAUTION: both hsize and vsize are being decomposed. - * The decomposer chooses a product of sizes (call them - * 'terms') for each that is close to the input size, - * but not necessarily equal to it. It attempts to optimize: - * (a) for consistency with the input values: the product - * of terms is close to the input size - * (b) for efficiency of the operation: the sum of the - * terms is small; ideally about twice the square - * root of the input size. - * So, for example, if the input hsize = 37, which is - * a prime number, the decomposer will break this into two - * terms, 6 and 6, so that the net result is a dilation - * with hsize = 36. - *- */ -PIX * -pixErodeCompBrickDwa(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -char *selnameh1, *selnameh2, *selnamev1, *selnamev2; -l_int32 hsize1, hsize2, vsize1, vsize2, bordercolor; -PIX *pixt1, *pixt2, *pixt3; - - PROCNAME("pixErodeCompBrickDwa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - if (hsize > 63 || vsize > 63) - return pixErodeCompBrickExtendDwa(pixd, pixs, hsize, vsize); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - - hsize1 = hsize2 = vsize1 = vsize2 = 1; - selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; - if (hsize > 1) - getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, - &selnameh2, NULL, NULL); - if (vsize > 1) - getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, - &selnamev1, &selnamev2); - - /* For symmetric b.c., bordercolor == 1 for erosion */ - bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); - pixt1 = pixAddBorder(pixs, 64, bordercolor); - - if (vsize == 1) { - if (hsize2 == 1) { - pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); - } else { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); - pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); - pixDestroy(&pixt3); - } - } else if (hsize == 1) { - if (vsize2 == 1) { - pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); - } else { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); - pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnamev2); - pixDestroy(&pixt3); - } - } else { /* vsize and hsize both > 1 */ - if (hsize2 == 1) { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); - } else { - pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); - pixt3 = pixFMorphopGen_2(NULL, pixt2, L_MORPH_ERODE, selnameh2); - pixDestroy(&pixt2); - } - if (vsize2 == 1) { - pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); - } else { - pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); - pixFMorphopGen_2(pixt2, pixt2, L_MORPH_ERODE, selnamev2); - } - pixDestroy(&pixt3); - } - pixDestroy(&pixt1); - pixt1 = pixRemoveBorder(pixt2, 64); - pixDestroy(&pixt2); - if (selnameh1) LEPT_FREE(selnameh1); - if (selnameh2) LEPT_FREE(selnameh2); - if (selnamev1) LEPT_FREE(selnamev1); - if (selnamev2) LEPT_FREE(selnamev2); - - if (!pixd) - return pixt1; - - pixTransferAllData(pixd, &pixt1, 0, 0); - return pixd; -} - - -/*! - * \brief pixOpenCompBrickDwa() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd - * - *
- * Notes: - * (1) These implement a separable composite opening with 2D brick Sels. - * (2) For efficiency, it may decompose each linear morphological - * operation into two (brick + comb). - * (3) A brick Sel has hits for all elements. - * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) - * (5) Do separably if both hsize and vsize are > 1. - * (6) It is necessary that both horizontal and vertical Sels - * of the input size are defined in the basic sela. - * (7) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (8) For clarity, if the case is known, use these patterns: - * (a) pixd = pixOpenCompBrickDwa(NULL, pixs, ...); - * (b) pixOpenCompBrickDwa(pixs, pixs, ...); - * (c) pixOpenCompBrickDwa(pixd, pixs, ...); - * (9) The size of pixd is determined by pixs. - * (10) CAUTION: both hsize and vsize are being decomposed. - * The decomposer chooses a product of sizes (call them - * 'terms') for each that is close to the input size, - * but not necessarily equal to it. It attempts to optimize: - * (a) for consistency with the input values: the product - * of terms is close to the input size - * (b) for efficiency of the operation: the sum of the - * terms is small; ideally about twice the square - * root of the input size. - * So, for example, if the input hsize = 37, which is - * a prime number, the decomposer will break this into two - * terms, 6 and 6, so that the net result is a dilation - * with hsize = 36. - *- */ -PIX * -pixOpenCompBrickDwa(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -char *selnameh1, *selnameh2, *selnamev1, *selnamev2; -l_int32 hsize1, hsize2, vsize1, vsize2, bordercolor; -PIX *pixt1, *pixt2, *pixt3; - - PROCNAME("pixOpenCompBrickDwa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - if (hsize > 63 || vsize > 63) - return pixOpenCompBrickExtendDwa(pixd, pixs, hsize, vsize); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - - hsize1 = hsize2 = vsize1 = vsize2 = 1; - selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; - if (hsize > 1) - getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, - &selnameh2, NULL, NULL); - if (vsize > 1) - getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, - &selnamev1, &selnamev2); - - /* For symmetric b.c., initialize erosion with bordercolor == 1 */ - bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); - pixt1 = pixAddBorder(pixs, 64, bordercolor); - - if (vsize == 1) { - if (hsize2 == 1) { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); - if (bordercolor == 1) - pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); - pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnameh1); - } else { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); - pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); - if (bordercolor == 1) - pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1); - pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnameh2); - } - } else if (hsize == 1) { - if (vsize2 == 1) { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); - if (bordercolor == 1) - pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); - pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); - } else { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1); - pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnamev2); - if (bordercolor == 1) - pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); - pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); - } - } else { /* vsize and hsize both > 1 */ - if (hsize2 == 1 && vsize2 == 1) { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); - pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); - if (bordercolor == 1) - pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1); - pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev1); - } else if (vsize2 == 1) { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); - pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); - if (bordercolor == 1) - pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); - pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnameh1); - pixFMorphopGen_2(pixt3, pixt2, L_MORPH_DILATE, selnameh2); - pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev1); - } else if (hsize2 == 1) { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); - pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1); - pixFMorphopGen_2(pixt3, pixt2, L_MORPH_ERODE, selnamev2); - if (bordercolor == 1) - pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR); - pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnameh1); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); - pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); - } else { /* both directions are combed */ - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1); - pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); - pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); - if (bordercolor == 1) - pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1); - pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnameh2); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); - pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); - } - } - pixDestroy(&pixt3); - - pixDestroy(&pixt1); - pixt1 = pixRemoveBorder(pixt2, 64); - pixDestroy(&pixt2); - if (selnameh1) LEPT_FREE(selnameh1); - if (selnameh2) LEPT_FREE(selnameh2); - if (selnamev1) LEPT_FREE(selnamev1); - if (selnamev2) LEPT_FREE(selnamev2); - - if (!pixd) - return pixt1; - - pixTransferAllData(pixd, &pixt1, 0, 0); - return pixd; -} - - -/*! - * \brief pixCloseCompBrickDwa() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd - * - *
- * Notes: - * (1) This implements a separable composite safe closing with 2D - * brick Sels. - * (2) For efficiency, it may decompose each linear morphological - * operation into two (brick + comb). - * (3) A brick Sel has hits for all elements. - * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2) - * (5) Do separably if both hsize and vsize are > 1. - * (6) It is necessary that both horizontal and vertical Sels - * of the input size are defined in the basic sela. - * (7) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (8) For clarity, if the case is known, use these patterns: - * (a) pixd = pixCloseCompBrickDwa(NULL, pixs, ...); - * (b) pixCloseCompBrickDwa(pixs, pixs, ...); - * (c) pixCloseCompBrickDwa(pixd, pixs, ...); - * (9) The size of pixd is determined by pixs. - * (10) CAUTION: both hsize and vsize are being decomposed. - * The decomposer chooses a product of sizes (call them - * 'terms') for each that is close to the input size, - * but not necessarily equal to it. It attempts to optimize: - * (a) for consistency with the input values: the product - * of terms is close to the input size - * (b) for efficiency of the operation: the sum of the - * terms is small; ideally about twice the square - * root of the input size. - * So, for example, if the input hsize = 37, which is - * a prime number, the decomposer will break this into two - * terms, 6 and 6, so that the net result is a dilation - * with hsize = 36. - *- */ -PIX * -pixCloseCompBrickDwa(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -char *selnameh1, *selnameh2, *selnamev1, *selnamev2; -l_int32 hsize1, hsize2, vsize1, vsize2, setborder; -PIX *pixt1, *pixt2, *pixt3; - - PROCNAME("pixCloseCompBrickDwa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - if (hsize > 63 || vsize > 63) - return pixCloseCompBrickExtendDwa(pixd, pixs, hsize, vsize); - - if (hsize == 1 && vsize == 1) - return pixCopy(pixd, pixs); - - hsize1 = hsize2 = vsize1 = vsize2 = 1; - selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL; - if (hsize > 1) - getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1, - &selnameh2, NULL, NULL); - if (vsize > 1) - getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL, - &selnamev1, &selnamev2); - - pixt3 = NULL; - /* For symmetric b.c., PIX_SET border for erosions */ - setborder = getMorphBorderPixelColor(L_MORPH_ERODE, 1); - pixt1 = pixAddBorder(pixs, 64, 0); - - if (vsize == 1) { - if (hsize2 == 1) { - pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh1); - } else { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); - pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); - if (setborder == 1) - pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1); - pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnameh2); - } - } else if (hsize == 1) { - if (vsize2 == 1) { - pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev1); - } else { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1); - pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnamev2); - if (setborder == 1) - pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); - pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); - } - } else { /* vsize and hsize both > 1 */ - if (hsize2 == 1 && vsize2 == 1) { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); - pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); - if (setborder == 1) - pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1); - pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev1); - } else if (vsize2 == 1) { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); - pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); - if (setborder == 1) - pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_SET); - pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh1); - pixFMorphopGen_2(pixt3, pixt2, L_MORPH_ERODE, selnameh2); - pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev1); - } else if (hsize2 == 1) { - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); - pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1); - pixFMorphopGen_2(pixt3, pixt2, L_MORPH_DILATE, selnamev2); - if (setborder == 1) - pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_SET); - pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh1); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); - pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); - } else { /* both directions are combed */ - pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1); - pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1); - pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2); - if (setborder == 1) - pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1); - pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnameh2); - pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1); - pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2); - } - } - pixDestroy(&pixt3); - - pixDestroy(&pixt1); - pixt1 = pixRemoveBorder(pixt2, 64); - pixDestroy(&pixt2); - if (selnameh1) LEPT_FREE(selnameh1); - if (selnameh2) LEPT_FREE(selnameh2); - if (selnamev1) LEPT_FREE(selnamev1); - if (selnamev2) LEPT_FREE(selnamev2); - - if (!pixd) - return pixt1; - - pixTransferAllData(pixd, &pixt1, 0, 0); - return pixd; -} - - -/*--------------------------------------------------------------------------* - * Binary expanded composite morphological (dwa) ops with brick Sels * - *--------------------------------------------------------------------------*/ -/*! - * \brief pixDilateCompBrickExtendDwa() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd - * - *
- * Notes: - * (1) Ankur Jain suggested and implemented extending the composite - * DWA operations beyond the 63 pixel limit. This is a - * simplified and approximate implementation of the extension. - * This allows arbitrary Dwa morph operations using brick Sels, - * by decomposing the horizontal and vertical dilations into - * a sequence of 63-element dilations plus a dilation of size - * between 3 and 62. - * (2) The 63-element dilations are exact, whereas the extra dilation - * is approximate, because the underlying decomposition is - * in pixDilateCompBrickDwa(). See there for further details. - * (3) There are three cases: - * (a) pixd == null (result into new pixd) - * (b) pixd == pixs (in-place; writes result back to pixs) - * (c) pixd != pixs (puts result into existing pixd) - * (4) There is no need to call this directly: pixDilateCompBrickDwa() - * calls this function if either brick dimension exceeds 63. - *- */ -PIX * -pixDilateCompBrickExtendDwa(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -l_int32 i, nops, nh, extrah, nv, extrav; -PIX *pixt1, *pixt2, *pixt3; - - PROCNAME("pixDilateCompBrickExtendDwa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - if (hsize < 64 && vsize < 64) - return pixDilateCompBrickDwa(pixd, pixs, hsize, vsize); - - if (hsize > 63) - getExtendedCompositeParameters(hsize, &nh, &extrah, NULL); - if (vsize > 63) - getExtendedCompositeParameters(vsize, &nv, &extrav, NULL); - - /* Horizontal dilation first: pixs --> pixt2. Do not alter pixs. */ - pixt1 = pixCreateTemplate(pixs); /* temp image */ - if (hsize == 1) { - pixt2 = pixClone(pixs); - } else if (hsize < 64) { - pixt2 = pixDilateCompBrickDwa(NULL, pixs, hsize, 1); - } else if (hsize == 64) { /* approximate */ - pixt2 = pixDilateCompBrickDwa(NULL, pixs, 63, 1); - } else { - nops = (extrah < 3) ? nh : nh + 1; - if (nops & 1) { /* odd */ - if (extrah > 2) - pixt2 = pixDilateCompBrickDwa(NULL, pixs, extrah, 1); - else - pixt2 = pixDilateCompBrickDwa(NULL, pixs, 63, 1); - for (i = 0; i < nops / 2; i++) { - pixDilateCompBrickDwa(pixt1, pixt2, 63, 1); - pixDilateCompBrickDwa(pixt2, pixt1, 63, 1); - } - } else { /* nops even */ - if (extrah > 2) { - pixDilateCompBrickDwa(pixt1, pixs, extrah, 1); - pixt2 = pixDilateCompBrickDwa(NULL, pixt1, 63, 1); - } else { /* they're all 63s */ - pixDilateCompBrickDwa(pixt1, pixs, 63, 1); - pixt2 = pixDilateCompBrickDwa(NULL, pixt1, 63, 1); - } - for (i = 0; i < nops / 2 - 1; i++) { - pixDilateCompBrickDwa(pixt1, pixt2, 63, 1); - pixDilateCompBrickDwa(pixt2, pixt1, 63, 1); - } - } - } - - /* Vertical dilation: pixt2 --> pixt3. */ - if (vsize == 1) { - pixt3 = pixClone(pixt2); - } else if (vsize < 64) { - pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, vsize); - } else if (vsize == 64) { /* approximate */ - pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, 63); - } else { - nops = (extrav < 3) ? nv : nv + 1; - if (nops & 1) { /* odd */ - if (extrav > 2) - pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, extrav); - else - pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, 63); - for (i = 0; i < nops / 2; i++) { - pixDilateCompBrickDwa(pixt1, pixt3, 1, 63); - pixDilateCompBrickDwa(pixt3, pixt1, 1, 63); - } - } else { /* nops even */ - if (extrav > 2) { - pixDilateCompBrickDwa(pixt1, pixt2, 1, extrav); - pixt3 = pixDilateCompBrickDwa(NULL, pixt1, 1, 63); - } else { /* they're all 63s */ - pixDilateCompBrickDwa(pixt1, pixt2, 1, 63); - pixt3 = pixDilateCompBrickDwa(NULL, pixt1, 1, 63); - } - for (i = 0; i < nops / 2 - 1; i++) { - pixDilateCompBrickDwa(pixt1, pixt3, 1, 63); - pixDilateCompBrickDwa(pixt3, pixt1, 1, 63); - } - } - } - pixDestroy(&pixt1); - pixDestroy(&pixt2); - - if (!pixd) - return pixt3; - - pixTransferAllData(pixd, &pixt3, 0, 0); - return pixd; -} - - -/*! - * \brief pixErodeCompBrickExtendDwa() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd - * - *
- * Notes: - * (1) See pixDilateCompBrickExtendDwa() for usage. - * (2) There is no need to call this directly: pixErodeCompBrickDwa() - * calls this function if either brick dimension exceeds 63. - *- */ -PIX * -pixErodeCompBrickExtendDwa(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -l_int32 i, nops, nh, extrah, nv, extrav; -PIX *pixt1, *pixt2, *pixt3; - - PROCNAME("pixErodeCompBrickExtendDwa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - if (hsize < 64 && vsize < 64) - return pixErodeCompBrickDwa(pixd, pixs, hsize, vsize); - - if (hsize > 63) - getExtendedCompositeParameters(hsize, &nh, &extrah, NULL); - if (vsize > 63) - getExtendedCompositeParameters(vsize, &nv, &extrav, NULL); - - /* Horizontal erosion first: pixs --> pixt2. Do not alter pixs. */ - pixt1 = pixCreateTemplate(pixs); /* temp image */ - if (hsize == 1) { - pixt2 = pixClone(pixs); - } else if (hsize < 64) { - pixt2 = pixErodeCompBrickDwa(NULL, pixs, hsize, 1); - } else if (hsize == 64) { /* approximate */ - pixt2 = pixErodeCompBrickDwa(NULL, pixs, 63, 1); - } else { - nops = (extrah < 3) ? nh : nh + 1; - if (nops & 1) { /* odd */ - if (extrah > 2) - pixt2 = pixErodeCompBrickDwa(NULL, pixs, extrah, 1); - else - pixt2 = pixErodeCompBrickDwa(NULL, pixs, 63, 1); - for (i = 0; i < nops / 2; i++) { - pixErodeCompBrickDwa(pixt1, pixt2, 63, 1); - pixErodeCompBrickDwa(pixt2, pixt1, 63, 1); - } - } else { /* nops even */ - if (extrah > 2) { - pixErodeCompBrickDwa(pixt1, pixs, extrah, 1); - pixt2 = pixErodeCompBrickDwa(NULL, pixt1, 63, 1); - } else { /* they're all 63s */ - pixErodeCompBrickDwa(pixt1, pixs, 63, 1); - pixt2 = pixErodeCompBrickDwa(NULL, pixt1, 63, 1); - } - for (i = 0; i < nops / 2 - 1; i++) { - pixErodeCompBrickDwa(pixt1, pixt2, 63, 1); - pixErodeCompBrickDwa(pixt2, pixt1, 63, 1); - } - } - } - - /* Vertical erosion: pixt2 --> pixt3. */ - if (vsize == 1) { - pixt3 = pixClone(pixt2); - } else if (vsize < 64) { - pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, vsize); - } else if (vsize == 64) { /* approximate */ - pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, 63); - } else { - nops = (extrav < 3) ? nv : nv + 1; - if (nops & 1) { /* odd */ - if (extrav > 2) - pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, extrav); - else - pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, 63); - for (i = 0; i < nops / 2; i++) { - pixErodeCompBrickDwa(pixt1, pixt3, 1, 63); - pixErodeCompBrickDwa(pixt3, pixt1, 1, 63); - } - } else { /* nops even */ - if (extrav > 2) { - pixErodeCompBrickDwa(pixt1, pixt2, 1, extrav); - pixt3 = pixErodeCompBrickDwa(NULL, pixt1, 1, 63); - } else { /* they're all 63s */ - pixErodeCompBrickDwa(pixt1, pixt2, 1, 63); - pixt3 = pixErodeCompBrickDwa(NULL, pixt1, 1, 63); - } - for (i = 0; i < nops / 2 - 1; i++) { - pixErodeCompBrickDwa(pixt1, pixt3, 1, 63); - pixErodeCompBrickDwa(pixt3, pixt1, 1, 63); - } - } - } - pixDestroy(&pixt1); - pixDestroy(&pixt2); - - if (!pixd) - return pixt3; - - pixTransferAllData(pixd, &pixt3, 0, 0); - return pixd; -} - - -/*! - * \brief pixOpenCompBrickExtendDwa() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd - * - *
- * Notes: - * 1) There are three cases: - * a) pixd == null (result into new pixd - * b) pixd == pixs (in-place; writes result back to pixs - * c) pixd != pixs (puts result into existing pixd - * 2) There is no need to call this directly: pixOpenCompBrickDwa( - * calls this function if either brick dimension exceeds 63. - *- */ -PIX * -pixOpenCompBrickExtendDwa(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -PIX *pixt; - - PROCNAME("pixOpenCompBrickExtendDwa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - pixt = pixErodeCompBrickExtendDwa(NULL, pixs, hsize, vsize); - pixd = pixDilateCompBrickExtendDwa(pixd, pixt, hsize, vsize); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixCloseCompBrickExtendDwa() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs 1 bpp - * \param[in] hsize width of brick Sel - * \param[in] vsize height of brick Sel - * \return pixd - * - *
- * Notes: - * 1) There are three cases: - * a) pixd == null (result into new pixd - * b) pixd == pixs (in-place; writes result back to pixs - * c) pixd != pixs (puts result into existing pixd - * 2) There is no need to call this directly: pixCloseCompBrickDwa( - * calls this function if either brick dimension exceeds 63. - *- */ -PIX * -pixCloseCompBrickExtendDwa(PIX *pixd, - PIX *pixs, - l_int32 hsize, - l_int32 vsize) -{ -l_int32 bordercolor, borderx, bordery; -PIX *pixt1, *pixt2, *pixt3; - - PROCNAME("pixCloseCompBrickExtendDwa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - if (hsize < 1 || vsize < 1) - return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd); - - /* For "safe closing" with ASYMMETRIC_MORPH_BC, we always need - * an extra 32 OFF pixels around the image (in addition to - * the 32 added pixels for all dwa operations), whereas with - * SYMMETRIC_MORPH_BC this is not necessary. */ - bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1); - if (bordercolor == 0) { /* asymmetric b.c. */ - borderx = 32 + (hsize / 64) * 32; - bordery = 32 + (vsize / 64) * 32; - } else { /* symmetric b.c. */ - borderx = bordery = 32; - } - pixt1 = pixAddBorderGeneral(pixs, borderx, borderx, bordery, bordery, 0); - - pixt2 = pixDilateCompBrickExtendDwa(NULL, pixt1, hsize, vsize); - pixErodeCompBrickExtendDwa(pixt1, pixt2, hsize, vsize); - - pixt3 = pixRemoveBorderGeneral(pixt1, borderx, borderx, bordery, bordery); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - - if (!pixd) - return pixt3; - - pixTransferAllData(pixd, &pixt3, 0, 0); - return pixd; -} - - -/*! - * \brief getExtendedCompositeParameters() - * - * \param[in] size of linear Sel - * \param[out] pn number of 63 wide convolutions - * \param[out] pextra size of extra Sel - * \param[out] pactualsize [optional] actual size used in operation - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The DWA implementation allows Sels to be used with hits - * up to 31 pixels from the origin, either horizontally or - * vertically. Larger Sels can be used if decomposed into - * a set of operations with Sels not exceeding 63 pixels - * in either width or height (and with the origin as close - * to the center of the Sel as possible). - * (2) This returns the decomposition of a linear Sel of length - * %size into a set of %n Sels of length 63 plus an extra - * Sel of length %extra. - * (3) For notation, let w == %size, n == %n, and e == %extra. - * We have 1 < e < 63. - * - * Then if w < 64, we have n = 0 and e = w. - * The general formula for w > 63 is: - * w = 63 + (n - 1) * 62 + (e - 1) - * - * Where did this come from? Each successive convolution with - * a Sel of length L adds a total length (L - 1) to w. - * This accounts for using 62 for each additional Sel of size 63, - * and using (e - 1) for the additional Sel of size e. - * - * Solving for n and e for w > 63: - * n = 1 + Int((w - 63) / 62) - * e = w - 63 - (n - 1) * 62 + 1 - * - * The extra part is decomposed into two factors f1 and f2, - * and the actual size of the extra part is - * e' = f1 * f2 - * Then the actual width is: - * w' = 63 + (n - 1) * 62 + f1 * f2 - 1 - *- */ -l_ok -getExtendedCompositeParameters(l_int32 size, - l_int32 *pn, - l_int32 *pextra, - l_int32 *pactualsize) -{ -l_int32 n, extra, fact1, fact2; - - PROCNAME("getExtendedCompositeParameters"); - - if (!pn || !pextra) - return ERROR_INT("&n and &extra not both defined", procName, 1); - - if (size <= 63) { - n = 0; - extra = L_MIN(1, size); - } else { /* size > 63 */ - n = 1 + (l_int32)((size - 63) / 62); - extra = size - 63 - (n - 1) * 62 + 1; - } - - if (pactualsize) { - selectComposableSizes(extra, &fact1, &fact2); - *pactualsize = 63 + (n - 1) * 62 + fact1 * fact2 - 1; - } - - *pn = n; - *pextra = extra; - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/morphseq.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/morphseq.c deleted file mode 100644 index e98bb3b9..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/morphseq.c +++ /dev/null @@ -1,1243 +0,0 @@ -/*====================================================================* - - 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 morphseq.c - *
- * - * Run a sequence of binary rasterop morphological operations - * PIX *pixMorphSequence() - * - * Run a sequence of binary composite rasterop morphological operations - * PIX *pixMorphCompSequence() - * - * Run a sequence of binary dwa morphological operations - * PIX *pixMorphSequenceDwa() - * - * Run a sequence of binary composite dwa morphological operations - * PIX *pixMorphCompSequenceDwa() - * - * Parser verifier for binary morphological operations - * l_int32 morphSequenceVerify() - * - * Run a sequence of grayscale morphological operations - * PIX *pixGrayMorphSequence() - * - * Run a sequence of color morphological operations - * PIX *pixColorMorphSequence() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) This does rasterop morphology on binary images. - * (2) This runs a pipeline of operations; no branching is allowed. - * (3) This only uses brick Sels, which are created on the fly. - * In the future this will be generalized to extract Sels from - * a Sela by name. - * (4) A new image is always produced; the input image is not changed. - * (5) This contains an interpreter, allowing sequences to be - * generated and run. - * (6) The format of the sequence string is defined below. - * (7) In addition to morphological operations, rank order reduction - * and replicated expansion allow operations to take place - * downscaled by a power of 2. - * (8) Intermediate results can optionally be displayed. - * (9) Thanks to Dar-Shyang Lee, who had the idea for this and - * built the first implementation. - * (10) The sequence string is formatted as follows: - * ~ An arbitrary number of operations, each separated - * by a '+' character. White space is ignored. - * ~ Each operation begins with a case-independent character - * specifying the operation: - * d or D (dilation) - * e or E (erosion) - * o or O (opening) - * c or C (closing) - * r or R (rank binary reduction) - * x or X (replicative binary expansion) - * b or B (add a border of 0 pixels of this size) - * ~ The args to the morphological operations are bricks of hits, - * and are formatted as a.b, where a and b are horizontal and - * vertical dimensions, rsp. - * ~ The args to the reduction are a sequence of up to 4 integers, - * each from 1 to 4. - * ~ The arg to the expansion is a power of two, in the set - * {2, 4, 8, 16}. - * (11) An example valid sequence is: - * "b32 + o1.3 + C3.1 + r23 + e2.2 + D3.2 + X4" - * In this example, the following operation sequence is carried out: - * * b32: Add a 32 pixel border around the input image - * * o1.3: Opening with vert sel of length 3 (e.g., 1 x 3) - * * C3.1: Closing with horiz sel of length 3 (e.g., 3 x 1) - * * r23: Two successive 2x2 reductions with rank 2 in the first - * and rank 3 in the second. The result is a 4x reduced pix. - * * e2.2: Erosion with a 2x2 sel (origin will be at x,y: 0,0) - * * d3.2: Dilation with a 3x2 sel (origin will be at x,y: 1,0) - * * X4: 4x replicative expansion, back to original resolution - * (12) The safe closing is used. However, if you implement a - * closing as separable dilations followed by separable erosions, - * it will not be safe. For that situation, you need to add - * a sufficiently large border as the first operation in - * the sequence. This will be removed automatically at the - * end. There are two cautions: - * ~ When computing what is sufficient, remember that if - * reductions are carried out, the border is also reduced. - * ~ The border is removed at the end, so if a border is - * added at the beginning, the result must be at the - * same resolution as the input! - *- */ -PIX * -pixMorphSequence(PIX *pixs, - const char *sequence, - l_int32 dispsep) -{ -char *rawop, *op; -char fname[256]; -l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout; -l_int32 level[4]; -PIX *pix1, *pix2; -PIXA *pixa; -SARRAY *sa; - - PROCNAME("pixMorphSequence"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!sequence) - return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); - - /* Split sequence into individual operations */ - sa = sarrayCreate(0); - sarraySplitString(sa, sequence, "+"); - nops = sarrayGetCount(sa); - pdfout = (dispsep < 0) ? 1 : 0; - if (!morphSequenceVerify(sa)) { - sarrayDestroy(&sa); - return (PIX *)ERROR_PTR("sequence not valid", procName, NULL); - } - - /* Parse and operate */ - pixa = NULL; - if (pdfout) { - pixa = pixaCreate(0); - pixaAddPix(pixa, pixs, L_CLONE); - } - border = 0; - pix1 = pixCopy(NULL, pixs); - pix2 = NULL; - x = 0; - for (i = 0; i < nops; i++) { - rawop = sarrayGetString(sa, i, L_NOCOPY); - op = stringRemoveChars(rawop, " \n\t"); - switch (op[0]) - { - case 'd': - case 'D': - sscanf(&op[1], "%d.%d", &w, &h); - pix2 = pixDilateBrick(NULL, pix1, w, h); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'e': - case 'E': - sscanf(&op[1], "%d.%d", &w, &h); - pix2 = pixErodeBrick(NULL, pix1, w, h); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'o': - case 'O': - sscanf(&op[1], "%d.%d", &w, &h); - pixOpenBrick(pix1, pix1, w, h); - break; - case 'c': - case 'C': - sscanf(&op[1], "%d.%d", &w, &h); - pixCloseSafeBrick(pix1, pix1, w, h); - break; - case 'r': - case 'R': - nred = strlen(op) - 1; - for (j = 0; j < nred; j++) - level[j] = op[j + 1] - '0'; - for (j = nred; j < 4; j++) - level[j] = 0; - pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1], - level[2], level[3]); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'x': - case 'X': - sscanf(&op[1], "%d", &fact); - pix2 = pixExpandReplicate(pix1, fact); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'b': - case 'B': - sscanf(&op[1], "%d", &border); - pix2 = pixAddBorder(pix1, border, 0); - pixSwapAndDestroy(&pix1, &pix2); - break; - default: - /* All invalid ops are caught in the first pass */ - break; - } - LEPT_FREE(op); - - /* Debug output */ - if (dispsep > 0) { - pixDisplay(pix1, x, 0); - x += dispsep; - } - if (pdfout) - pixaAddPix(pixa, pix1, L_COPY); - } - if (border > 0) { - pix2 = pixRemoveBorder(pix1, border); - pixSwapAndDestroy(&pix1, &pix2); - } - - if (pdfout) { - snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf", - L_ABS(dispsep)); - pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname); - pixaDestroy(&pixa); - } - - sarrayDestroy(&sa); - return pix1; -} - - -/*-------------------------------------------------------------------------* - * Run a sequence of binary composite rasterop morphological operations * - *-------------------------------------------------------------------------*/ -/*! - * \brief pixMorphCompSequence() - * - * \param[in] pixs - * \param[in] sequence string specifying sequence - * \param[in] dispsep controls debug display of results in the sequence: - * 0: no output - * > 0: gives horizontal separation in pixels between - * successive displays - * < 0: pdf output; abs(dispsep) is used for naming - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) This does rasterop morphology on binary images, using composite - * operations for extra speed on large Sels. - * (2) Safe closing is used atomically. However, if you implement a - * closing as a sequence with a dilation followed by an - * erosion, it will not be safe, and to ensure that you have - * no boundary effects you must add a border in advance and - * remove it at the end. - * (3) For other usage details, see the notes for pixMorphSequence(). - * (4) The sequence string is formatted as follows: - * ~ An arbitrary number of operations, each separated - * by a '+' character. White space is ignored. - * ~ Each operation begins with a case-independent character - * specifying the operation: - * d or D (dilation) - * e or E (erosion) - * o or O (opening) - * c or C (closing) - * r or R (rank binary reduction) - * x or X (replicative binary expansion) - * b or B (add a border of 0 pixels of this size) - * ~ The args to the morphological operations are bricks of hits, - * and are formatted as a.b, where a and b are horizontal and - * vertical dimensions, rsp. - * ~ The args to the reduction are a sequence of up to 4 integers, - * each from 1 to 4. - * ~ The arg to the expansion is a power of two, in the set - * {2, 4, 8, 16}. - *- */ -PIX * -pixMorphCompSequence(PIX *pixs, - const char *sequence, - l_int32 dispsep) -{ -char *rawop, *op; -char fname[256]; -l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout; -l_int32 level[4]; -PIX *pix1, *pix2; -PIXA *pixa; -SARRAY *sa; - - PROCNAME("pixMorphCompSequence"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!sequence) - return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); - - /* Split sequence into individual operations */ - sa = sarrayCreate(0); - sarraySplitString(sa, sequence, "+"); - nops = sarrayGetCount(sa); - pdfout = (dispsep < 0) ? 1 : 0; - - if (!morphSequenceVerify(sa)) { - sarrayDestroy(&sa); - return (PIX *)ERROR_PTR("sequence not valid", procName, NULL); - } - - /* Parse and operate */ - pixa = NULL; - if (pdfout) { - pixa = pixaCreate(0); - pixaAddPix(pixa, pixs, L_CLONE); - } - border = 0; - pix1 = pixCopy(NULL, pixs); - pix2 = NULL; - x = 0; - for (i = 0; i < nops; i++) { - rawop = sarrayGetString(sa, i, L_NOCOPY); - op = stringRemoveChars(rawop, " \n\t"); - switch (op[0]) - { - case 'd': - case 'D': - sscanf(&op[1], "%d.%d", &w, &h); - pix2 = pixDilateCompBrick(NULL, pix1, w, h); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'e': - case 'E': - sscanf(&op[1], "%d.%d", &w, &h); - pix2 = pixErodeCompBrick(NULL, pix1, w, h); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'o': - case 'O': - sscanf(&op[1], "%d.%d", &w, &h); - pixOpenCompBrick(pix1, pix1, w, h); - break; - case 'c': - case 'C': - sscanf(&op[1], "%d.%d", &w, &h); - pixCloseSafeCompBrick(pix1, pix1, w, h); - break; - case 'r': - case 'R': - nred = strlen(op) - 1; - for (j = 0; j < nred; j++) - level[j] = op[j + 1] - '0'; - for (j = nred; j < 4; j++) - level[j] = 0; - pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1], - level[2], level[3]); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'x': - case 'X': - sscanf(&op[1], "%d", &fact); - pix2 = pixExpandReplicate(pix1, fact); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'b': - case 'B': - sscanf(&op[1], "%d", &border); - pix2 = pixAddBorder(pix1, border, 0); - pixSwapAndDestroy(&pix1, &pix2); - break; - default: - /* All invalid ops are caught in the first pass */ - break; - } - LEPT_FREE(op); - - /* Debug output */ - if (dispsep > 0) { - pixDisplay(pix1, x, 0); - x += dispsep; - } - if (pdfout) - pixaAddPix(pixa, pix1, L_COPY); - } - if (border > 0) { - pix2 = pixRemoveBorder(pix1, border); - pixSwapAndDestroy(&pix1, &pix2); - } - - if (pdfout) { - snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf", - L_ABS(dispsep)); - pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname); - pixaDestroy(&pixa); - } - - sarrayDestroy(&sa); - return pix1; -} - - -/*-------------------------------------------------------------------------* - * Run a sequence of binary dwa morphological operations * - *-------------------------------------------------------------------------*/ -/*! - * \brief pixMorphSequenceDwa() - * - * \param[in] pixs - * \param[in] sequence string specifying sequence - * \param[in] dispsep controls debug display of results in the sequence: - * 0: no output - * > 0: gives horizontal separation in pixels between - * successive displays - * < 0: pdf output; abs(dispsep) is used for naming - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) This does dwa morphology on binary images. - * (2) This runs a pipeline of operations; no branching is allowed. - * (3) This only uses brick Sels that have been pre-compiled with - * dwa code. - * (4) A new image is always produced; the input image is not changed. - * (5) This contains an interpreter, allowing sequences to be - * generated and run. - * (6) See pixMorphSequence() for further information about usage. - *- */ -PIX * -pixMorphSequenceDwa(PIX *pixs, - const char *sequence, - l_int32 dispsep) -{ -char *rawop, *op; -char fname[256]; -l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout; -l_int32 level[4]; -PIX *pix1, *pix2; -PIXA *pixa; -SARRAY *sa; - - PROCNAME("pixMorphSequenceDwa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!sequence) - return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); - - /* Split sequence into individual operations */ - sa = sarrayCreate(0); - sarraySplitString(sa, sequence, "+"); - nops = sarrayGetCount(sa); - pdfout = (dispsep < 0) ? 1 : 0; - - if (!morphSequenceVerify(sa)) { - sarrayDestroy(&sa); - return (PIX *)ERROR_PTR("sequence not valid", procName, NULL); - } - - /* Parse and operate */ - pixa = NULL; - if (pdfout) { - pixa = pixaCreate(0); - pixaAddPix(pixa, pixs, L_CLONE); - } - border = 0; - pix1 = pixCopy(NULL, pixs); - pix2 = NULL; - x = 0; - for (i = 0; i < nops; i++) { - rawop = sarrayGetString(sa, i, L_NOCOPY); - op = stringRemoveChars(rawop, " \n\t"); - switch (op[0]) - { - case 'd': - case 'D': - sscanf(&op[1], "%d.%d", &w, &h); - pix2 = pixDilateBrickDwa(NULL, pix1, w, h); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'e': - case 'E': - sscanf(&op[1], "%d.%d", &w, &h); - pix2 = pixErodeBrickDwa(NULL, pix1, w, h); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'o': - case 'O': - sscanf(&op[1], "%d.%d", &w, &h); - pixOpenBrickDwa(pix1, pix1, w, h); - break; - case 'c': - case 'C': - sscanf(&op[1], "%d.%d", &w, &h); - pixCloseBrickDwa(pix1, pix1, w, h); - break; - case 'r': - case 'R': - nred = strlen(op) - 1; - for (j = 0; j < nred; j++) - level[j] = op[j + 1] - '0'; - for (j = nred; j < 4; j++) - level[j] = 0; - pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1], - level[2], level[3]); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'x': - case 'X': - sscanf(&op[1], "%d", &fact); - pix2 = pixExpandReplicate(pix1, fact); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'b': - case 'B': - sscanf(&op[1], "%d", &border); - pix2 = pixAddBorder(pix1, border, 0); - pixSwapAndDestroy(&pix1, &pix2); - break; - default: - /* All invalid ops are caught in the first pass */ - break; - } - LEPT_FREE(op); - - /* Debug output */ - if (dispsep > 0) { - pixDisplay(pix1, x, 0); - x += dispsep; - } - if (pdfout) - pixaAddPix(pixa, pix1, L_COPY); - } - if (border > 0) { - pix2 = pixRemoveBorder(pix1, border); - pixSwapAndDestroy(&pix1, &pix2); - } - - if (pdfout) { - snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf", - L_ABS(dispsep)); - pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname); - pixaDestroy(&pixa); - } - - sarrayDestroy(&sa); - return pix1; -} - - -/*-------------------------------------------------------------------------* - * Run a sequence of binary composite dwa morphological operations * - *-------------------------------------------------------------------------*/ -/*! - * \brief pixMorphCompSequenceDwa() - * - * \param[in] pixs - * \param[in] sequence string specifying sequence - * \param[in] dispsep controls debug display of results in the sequence: - * 0: no output - * > 0: gives horizontal separation in pixels between - * successive displays - * < 0: pdf output; abs(dispsep) is used for naming - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) This does dwa morphology on binary images, using brick Sels. - * (2) This runs a pipeline of operations; no branching is allowed. - * (3) It implements all brick Sels that have dimensions up to 63 - * on each side, using a composite (linear + comb) when useful. - * (4) A new image is always produced; the input image is not changed. - * (5) This contains an interpreter, allowing sequences to be - * generated and run. - * (6) See pixMorphSequence() for further information about usage. - *- */ -PIX * -pixMorphCompSequenceDwa(PIX *pixs, - const char *sequence, - l_int32 dispsep) -{ -char *rawop, *op; -char fname[256]; -l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout; -l_int32 level[4]; -PIX *pix1, *pix2; -PIXA *pixa; -SARRAY *sa; - - PROCNAME("pixMorphCompSequenceDwa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!sequence) - return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); - - /* Split sequence into individual operations */ - sa = sarrayCreate(0); - sarraySplitString(sa, sequence, "+"); - nops = sarrayGetCount(sa); - pdfout = (dispsep < 0) ? 1 : 0; - - if (!morphSequenceVerify(sa)) { - sarrayDestroy(&sa); - return (PIX *)ERROR_PTR("sequence not valid", procName, NULL); - } - - /* Parse and operate */ - pixa = NULL; - if (pdfout) { - pixa = pixaCreate(0); - pixaAddPix(pixa, pixs, L_CLONE); - } - border = 0; - pix1 = pixCopy(NULL, pixs); - pix2 = NULL; - x = 0; - for (i = 0; i < nops; i++) { - rawop = sarrayGetString(sa, i, L_NOCOPY); - op = stringRemoveChars(rawop, " \n\t"); - switch (op[0]) - { - case 'd': - case 'D': - sscanf(&op[1], "%d.%d", &w, &h); - pix2 = pixDilateCompBrickDwa(NULL, pix1, w, h); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'e': - case 'E': - sscanf(&op[1], "%d.%d", &w, &h); - pix2 = pixErodeCompBrickDwa(NULL, pix1, w, h); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'o': - case 'O': - sscanf(&op[1], "%d.%d", &w, &h); - pixOpenCompBrickDwa(pix1, pix1, w, h); - break; - case 'c': - case 'C': - sscanf(&op[1], "%d.%d", &w, &h); - pixCloseCompBrickDwa(pix1, pix1, w, h); - break; - case 'r': - case 'R': - nred = strlen(op) - 1; - for (j = 0; j < nred; j++) - level[j] = op[j + 1] - '0'; - for (j = nred; j < 4; j++) - level[j] = 0; - pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1], - level[2], level[3]); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'x': - case 'X': - sscanf(&op[1], "%d", &fact); - pix2 = pixExpandReplicate(pix1, fact); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'b': - case 'B': - sscanf(&op[1], "%d", &border); - pix2 = pixAddBorder(pix1, border, 0); - pixSwapAndDestroy(&pix1, &pix2); - break; - default: - /* All invalid ops are caught in the first pass */ - break; - } - LEPT_FREE(op); - - /* Debug output */ - if (dispsep > 0) { - pixDisplay(pix1, x, 0); - x += dispsep; - } - if (pdfout) - pixaAddPix(pixa, pix1, L_COPY); - } - if (border > 0) { - pix2 = pixRemoveBorder(pix1, border); - pixSwapAndDestroy(&pix1, &pix2); - } - - if (pdfout) { - snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf", - L_ABS(dispsep)); - pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname); - pixaDestroy(&pixa); - } - - sarrayDestroy(&sa); - return pix1; -} - - -/*-------------------------------------------------------------------------* - * Parser verifier for binary morphological operations * - *-------------------------------------------------------------------------*/ -/*! - * \brief morphSequenceVerify() - * - * \param[in] sa string array of operation sequence - * \return TRUE if valid; FALSE otherwise or on error - * - *
- * Notes: - * (1) This does verification of valid binary morphological - * operation sequences. - * (2) See pixMorphSequence() for notes on valid operations - * in the sequence. - *- */ -l_int32 -morphSequenceVerify(SARRAY *sa) -{ -char *rawop, *op; -l_int32 nops, i, j, nred, fact, valid, w, h, netred, border; -l_int32 level[4]; -l_int32 intlogbase2[5] = {1, 2, 3, 0, 4}; /* of arg/4 */ - - PROCNAME("morphSequenceVerify"); - - if (!sa) - return ERROR_INT("sa not defined", procName, FALSE); - - nops = sarrayGetCount(sa); - valid = TRUE; - netred = 0; - border = 0; - for (i = 0; i < nops; i++) { - rawop = sarrayGetString(sa, i, L_NOCOPY); - op = stringRemoveChars(rawop, " \n\t"); - switch (op[0]) - { - case 'd': - case 'D': - case 'e': - case 'E': - case 'o': - case 'O': - case 'c': - case 'C': - if (sscanf(&op[1], "%d.%d", &w, &h) != 2) { - lept_stderr("*** op: %s invalid\n", op); - valid = FALSE; - break; - } - if (w <= 0 || h <= 0) { - lept_stderr("*** op: %s; w = %d, h = %d; must both be > 0\n", - op, w, h); - valid = FALSE; - break; - } -/* lept_stderr("op = %s; w = %d, h = %d\n", op, w, h); */ - break; - case 'r': - case 'R': - nred = strlen(op) - 1; - netred += nred; - if (nred < 1 || nred > 4) { - lept_stderr( - "*** op = %s; num reduct = %d; must be in {1,2,3,4}\n", - op, nred); - valid = FALSE; - break; - } - for (j = 0; j < nred; j++) { - level[j] = op[j + 1] - '0'; - if (level[j] < 1 || level[j] > 4) { - lept_stderr("*** op = %s; level[%d] = %d is invalid\n", - op, j, level[j]); - valid = FALSE; - break; - } - } - if (!valid) - break; -/* lept_stderr("op = %s", op); */ - for (j = 0; j < nred; j++) { - level[j] = op[j + 1] - '0'; -/* lept_stderr(", level[%d] = %d", j, level[j]); */ - } -/* lept_stderr("\n"); */ - break; - case 'x': - case 'X': - if (sscanf(&op[1], "%d", &fact) != 1) { - lept_stderr("*** op: %s; fact invalid\n", op); - valid = FALSE; - break; - } - if (fact != 2 && fact != 4 && fact != 8 && fact != 16) { - lept_stderr("*** op = %s; invalid fact = %d\n", op, fact); - valid = FALSE; - break; - } - netred -= intlogbase2[fact / 4]; -/* lept_stderr("op = %s; fact = %d\n", op, fact); */ - break; - case 'b': - case 'B': - if (sscanf(&op[1], "%d", &fact) != 1) { - lept_stderr("*** op: %s; fact invalid\n", op); - valid = FALSE; - break; - } - if (i > 0) { - lept_stderr("*** op = %s; must be first op\n", op); - valid = FALSE; - break; - } - if (fact < 1) { - lept_stderr("*** op = %s; invalid fact = %d\n", op, fact); - valid = FALSE; - break; - } - border = fact; -/* lept_stderr("op = %s; fact = %d\n", op, fact); */ - break; - default: - lept_stderr("*** nonexistent op = %s\n", op); - valid = FALSE; - } - LEPT_FREE(op); - } - - if (border != 0 && netred != 0) { - lept_stderr("*** op = %s; border added but net reduction not 0\n", op); - valid = FALSE; - } - return valid; -} - - -/*-----------------------------------------------------------------* - * Run a sequence of grayscale morphological operations * - *-----------------------------------------------------------------*/ -/*! - * \brief pixGrayMorphSequence() - * - * \param[in] pixs - * \param[in] sequence string specifying sequence - * \param[in] dispsep controls debug display of results in the sequence: - * 0: no output - * > 0: gives horizontal separation in pixels between - * successive displays - * < 0: pdf output; abs(dispsep) is used for naming - * \param[in] dispy if dispsep > 0, this gives the y-value of the - * UL corner for display; otherwise it is ignored - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) This works on 8 bpp grayscale images. - * (2) This runs a pipeline of operations; no branching is allowed. - * (3) This only uses brick SELs. - * (4) A new image is always produced; the input image is not changed. - * (5) This contains an interpreter, allowing sequences to be - * generated and run. - * (6) The format of the sequence string is defined below. - * (7) In addition to morphological operations, the composite - * morph/subtract tophat can be performed. - * (8) Sel sizes (width, height) must each be odd numbers. - * (9) Intermediate results can optionally be displayed - * (10) The sequence string is formatted as follows: - * ~ An arbitrary number of operations, each separated - * by a '+' character. White space is ignored. - * ~ Each operation begins with a case-independent character - * specifying the operation: - * d or D (dilation) - * e or E (erosion) - * o or O (opening) - * c or C (closing) - * t or T (tophat) - * ~ The args to the morphological operations are bricks of hits, - * and are formatted as a.b, where a and b are horizontal and - * vertical dimensions, rsp. (each must be an odd number) - * ~ The args to the tophat are w or W (for white tophat) - * or b or B (for black tophat), followed by a.b as for - * the dilation, erosion, opening and closing. - * Example valid sequences are: - * "c5.3 + o7.5" - * "c9.9 + tw9.9" - *- */ -PIX * -pixGrayMorphSequence(PIX *pixs, - const char *sequence, - l_int32 dispsep, - l_int32 dispy) -{ -char *rawop, *op; -char fname[256]; -l_int32 nops, i, valid, w, h, x, pdfout; -PIX *pix1, *pix2; -PIXA *pixa; -SARRAY *sa; - - PROCNAME("pixGrayMorphSequence"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!sequence) - return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); - - /* Split sequence into individual operations */ - sa = sarrayCreate(0); - sarraySplitString(sa, sequence, "+"); - nops = sarrayGetCount(sa); - pdfout = (dispsep < 0) ? 1 : 0; - - /* Verify that the operation sequence is valid */ - valid = TRUE; - for (i = 0; i < nops; i++) { - rawop = sarrayGetString(sa, i, L_NOCOPY); - op = stringRemoveChars(rawop, " \n\t"); - switch (op[0]) - { - case 'd': - case 'D': - case 'e': - case 'E': - case 'o': - case 'O': - case 'c': - case 'C': - if (sscanf(&op[1], "%d.%d", &w, &h) != 2) { - lept_stderr("*** op: %s invalid\n", op); - valid = FALSE; - break; - } - if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) { - lept_stderr("*** op: %s; w = %d, h = %d; must both be odd\n", - op, w, h); - valid = FALSE; - break; - } -/* lept_stderr("op = %s; w = %d, h = %d\n", op, w, h); */ - break; - case 't': - case 'T': - if (op[1] != 'w' && op[1] != 'W' && - op[1] != 'b' && op[1] != 'B') { - lept_stderr( - "*** op = %s; arg %c must be 'w' or 'b'\n", op, op[1]); - valid = FALSE; - break; - } - sscanf(&op[2], "%d.%d", &w, &h); - if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) { - lept_stderr("*** op: %s; w = %d, h = %d; must both be odd\n", - op, w, h); - valid = FALSE; - break; - } -/* lept_stderr("op = %s", op); */ - break; - default: - lept_stderr("*** nonexistent op = %s\n", op); - valid = FALSE; - } - LEPT_FREE(op); - } - if (!valid) { - sarrayDestroy(&sa); - return (PIX *)ERROR_PTR("sequence invalid", procName, NULL); - } - - /* Parse and operate */ - pixa = NULL; - if (pdfout) { - pixa = pixaCreate(0); - pixaAddPix(pixa, pixs, L_CLONE); - } - pix1 = pixCopy(NULL, pixs); - pix2 = NULL; - x = 0; - for (i = 0; i < nops; i++) { - rawop = sarrayGetString(sa, i, L_NOCOPY); - op = stringRemoveChars(rawop, " \n\t"); - switch (op[0]) - { - case 'd': - case 'D': - sscanf(&op[1], "%d.%d", &w, &h); - pix2 = pixDilateGray(pix1, w, h); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'e': - case 'E': - sscanf(&op[1], "%d.%d", &w, &h); - pix2 = pixErodeGray(pix1, w, h); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'o': - case 'O': - sscanf(&op[1], "%d.%d", &w, &h); - pix2 = pixOpenGray(pix1, w, h); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'c': - case 'C': - sscanf(&op[1], "%d.%d", &w, &h); - pix2 = pixCloseGray(pix1, w, h); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 't': - case 'T': - sscanf(&op[2], "%d.%d", &w, &h); - if (op[1] == 'w' || op[1] == 'W') - pix2 = pixTophat(pix1, w, h, L_TOPHAT_WHITE); - else /* 'b' or 'B' */ - pix2 = pixTophat(pix1, w, h, L_TOPHAT_BLACK); - pixSwapAndDestroy(&pix1, &pix2); - break; - default: - /* All invalid ops are caught in the first pass */ - break; - } - LEPT_FREE(op); - - /* Debug output */ - if (dispsep > 0) { - pixDisplay(pix1, x, dispy); - x += dispsep; - } - if (pdfout) - pixaAddPix(pixa, pix1, L_COPY); - } - - if (pdfout) { - snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf", - L_ABS(dispsep)); - pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname); - pixaDestroy(&pixa); - } - - sarrayDestroy(&sa); - return pix1; -} - - -/*-----------------------------------------------------------------* - * Run a sequence of color morphological operations * - *-----------------------------------------------------------------*/ -/*! - * \brief pixColorMorphSequence() - * - * \param[in] pixs - * \param[in] sequence string specifying sequence - * \param[in] dispsep controls debug display of results in the sequence: - * 0: no output - * > 0: gives horizontal separation in pixels between - * successive displays - * < 0: pdf output; abs(dispsep) is used for naming - * \param[in] dispy if dispsep > 0, this gives the y-value of the - * UL corner for display; otherwise it is ignored - * \return pixd, or NULL on error - * - *
- * Notes: - * (1) This works on 32 bpp rgb images. - * (2) Each component is processed separately. - * (3) This runs a pipeline of operations; no branching is allowed. - * (4) This only uses brick SELs. - * (5) A new image is always produced; the input image is not changed. - * (6) This contains an interpreter, allowing sequences to be - * generated and run. - * (7) Sel sizes (width, height) must each be odd numbers. - * (8) The format of the sequence string is defined below. - * (9) Intermediate results can optionally be displayed. - * (10) The sequence string is formatted as follows: - * ~ An arbitrary number of operations, each separated - * by a '+' character. White space is ignored. - * ~ Each operation begins with a case-independent character - * specifying the operation: - * d or D (dilation) - * e or E (erosion) - * o or O (opening) - * c or C (closing) - * ~ The args to the morphological operations are bricks of hits, - * and are formatted as a.b, where a and b are horizontal and - * vertical dimensions, rsp. (each must be an odd number) - * Example valid sequences are: - * "c5.3 + o7.5" - * "D9.1" - *- */ -PIX * -pixColorMorphSequence(PIX *pixs, - const char *sequence, - l_int32 dispsep, - l_int32 dispy) -{ -char *rawop, *op; -char fname[256]; -l_int32 nops, i, valid, w, h, x, pdfout; -PIX *pix1, *pix2; -PIXA *pixa; -SARRAY *sa; - - PROCNAME("pixColorMorphSequence"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!sequence) - return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); - - /* Split sequence into individual operations */ - sa = sarrayCreate(0); - sarraySplitString(sa, sequence, "+"); - nops = sarrayGetCount(sa); - pdfout = (dispsep < 0) ? 1 : 0; - - /* Verify that the operation sequence is valid */ - valid = TRUE; - for (i = 0; i < nops; i++) { - rawop = sarrayGetString(sa, i, L_NOCOPY); - op = stringRemoveChars(rawop, " \n\t"); - switch (op[0]) - { - case 'd': - case 'D': - case 'e': - case 'E': - case 'o': - case 'O': - case 'c': - case 'C': - if (sscanf(&op[1], "%d.%d", &w, &h) != 2) { - lept_stderr("*** op: %s invalid\n", op); - valid = FALSE; - break; - } - if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) { - lept_stderr("*** op: %s; w = %d, h = %d; must both be odd\n", - op, w, h); - valid = FALSE; - break; - } -/* lept_stderr("op = %s; w = %d, h = %d\n", op, w, h); */ - break; - default: - lept_stderr("*** nonexistent op = %s\n", op); - valid = FALSE; - } - LEPT_FREE(op); - } - if (!valid) { - sarrayDestroy(&sa); - return (PIX *)ERROR_PTR("sequence invalid", procName, NULL); - } - - /* Parse and operate */ - pixa = NULL; - if (pdfout) { - pixa = pixaCreate(0); - pixaAddPix(pixa, pixs, L_CLONE); - } - pix1 = pixCopy(NULL, pixs); - pix2 = NULL; - x = 0; - for (i = 0; i < nops; i++) { - rawop = sarrayGetString(sa, i, L_NOCOPY); - op = stringRemoveChars(rawop, " \n\t"); - switch (op[0]) - { - case 'd': - case 'D': - sscanf(&op[1], "%d.%d", &w, &h); - pix2 = pixColorMorph(pix1, L_MORPH_DILATE, w, h); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'e': - case 'E': - sscanf(&op[1], "%d.%d", &w, &h); - pix2 = pixColorMorph(pix1, L_MORPH_ERODE, w, h); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'o': - case 'O': - sscanf(&op[1], "%d.%d", &w, &h); - pix2 = pixColorMorph(pix1, L_MORPH_OPEN, w, h); - pixSwapAndDestroy(&pix1, &pix2); - break; - case 'c': - case 'C': - sscanf(&op[1], "%d.%d", &w, &h); - pix2 = pixColorMorph(pix1, L_MORPH_CLOSE, w, h); - pixSwapAndDestroy(&pix1, &pix2); - break; - default: - /* All invalid ops are caught in the first pass */ - break; - } - LEPT_FREE(op); - - /* Debug output */ - if (dispsep > 0) { - pixDisplay(pix1, x, dispy); - x += dispsep; - } - if (pdfout) - pixaAddPix(pixa, pix1, L_COPY); - } - - if (pdfout) { - snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf", - L_ABS(dispsep)); - pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname); - pixaDestroy(&pixa); - } - - sarrayDestroy(&sa); - return pix1; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/numabasic.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/numabasic.c deleted file mode 100644 index 2e2bc11a..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/numabasic.c +++ /dev/null @@ -1,2061 +0,0 @@ -/*====================================================================* - - 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 numabasic.c - *
- * - * Numa creation, destruction, copy, clone, etc. - * NUMA *numaCreate() - * NUMA *numaCreateFromIArray() - * NUMA *numaCreateFromFArray() - * NUMA *numaCreateFromString() - * void *numaDestroy() - * NUMA *numaCopy() - * NUMA *numaClone() - * l_int32 numaEmpty() - * - * Add/remove number (float or integer) - * l_int32 numaAddNumber() - * static l_int32 numaExtendArray() - * l_int32 numaInsertNumber() - * l_int32 numaRemoveNumber() - * l_int32 numaReplaceNumber() - * - * Numa accessors - * l_int32 numaGetCount() - * l_int32 numaSetCount() - * l_int32 numaGetIValue() - * l_int32 numaGetFValue() - * l_int32 numaSetValue() - * l_int32 numaShiftValue() - * l_int32 *numaGetIArray() - * l_float32 *numaGetFArray() - * l_int32 numaGetRefcount() - * l_int32 numaChangeRefcount() - * l_int32 numaGetParameters() - * l_int32 numaSetParameters() - * l_int32 numaCopyParameters() - * - * Convert to string array - * SARRAY *numaConvertToSarray() - * - * Serialize numa for I/O - * NUMA *numaRead() - * NUMA *numaReadStream() - * NUMA *numaReadMem() - * l_int32 numaWriteDebug() - * l_int32 numaWrite() - * l_int32 numaWriteStream() - * l_int32 numaWriteStderr() - * l_int32 numaWriteMem() - * - * Numaa creation, destruction, truncation - * NUMAA *numaaCreate() - * NUMAA *numaaCreateFull() - * NUMAA *numaaTruncate() - * void *numaaDestroy() - * - * Add Numa to Numaa - * l_int32 numaaAddNuma() - * static l_int32 numaaExtendArray() - * - * Numaa accessors - * l_int32 numaaGetCount() - * l_int32 numaaGetNumaCount() - * l_int32 numaaGetNumberCount() - * NUMA **numaaGetPtrArray() - * NUMA *numaaGetNuma() - * NUMA *numaaReplaceNuma() - * l_int32 numaaGetValue() - * l_int32 numaaAddNumber() - * - * Serialize numaa for I/O - * NUMAA *numaaRead() - * NUMAA *numaaReadStream() - * NUMAA *numaaReadMem() - * l_int32 numaaWrite() - * l_int32 numaaWriteStream() - * l_int32 numaaWriteMem() - * - * (1) The Numa is a struct holding an array of floats. It can also - * be used to store l_int32 values, with some loss of precision - * for floats larger than about 10 million. Use the L_Dna instead - * if integers larger than a few million need to be stored. - * - * (2) Always use the accessors in this file, never the fields directly. - * - * (3) Storing and retrieving numbers: - * - * * to append a new number to the array, use numaAddNumber(). If - * the number is an int, it will will automatically be converted - * to l_float32 and stored. - * - * * to reset a value stored in the array, use numaSetValue(). - * - * * to increment or decrement a value stored in the array, - * use numaShiftValue(). - * - * * to obtain a value from the array, use either numaGetIValue() - * or numaGetFValue(), depending on whether you are retrieving - * an integer or a float. This avoids doing an explicit cast, - * such as - * (a) return a l_float32 and cast it to an l_int32 - * (b) cast the return directly to (l_float32 *) to - * satisfy the function prototype, as in - * numaGetFValue(na, index, (l_float32 *)&ival); [ugly!] - * - * (4) int <--> float conversions: - * - * Tradition dictates that type conversions go automatically from - * l_int32 --> l_float32, even though it is possible to lose - * precision for large integers, whereas you must cast (l_int32) - * to go from l_float32 --> l_int32 because you're truncating - * to the integer value. - * - * (5) As with other arrays in leptonica, the numa has both an allocated - * size and a count of the stored numbers. When you add a number, it - * goes on the end of the array, and causes a realloc if the array - * is already filled. However, in situations where you want to - * add numbers randomly into an array, such as when you build a - * histogram, you must set the count of stored numbers in advance. - * This is done with numaSetCount(). If you set a count larger - * than the allocated array, it does a realloc to the size requested. - * - * (6) In situations where the data in a numa correspond to a function - * y(x), the values can be either at equal spacings in x or at - * arbitrary spacings. For the former, we can represent all x values - * by two parameters: startx (corresponding to y[0]) and delx - * for the change in x for adjacent values y[i] and y[i+1]. - * startx and delx are initialized to 0.0 and 1.0, rsp. - * For arbitrary spacings, we use a second numa, and the two - * numas are typically denoted nay and nax. - * - * (7) The numa is also the basic struct used for histograms. Every numa - * has startx and delx fields, initialized to 0.0 and 1.0, that can - * be used to represent the "x" value for the location of the - * first bin and the bin width, respectively. Accessors are the - * numa*Parameters() functions. All functions that make numa - * histograms must set these fields properly, and many functions - * that use numa histograms rely on the correctness of these values. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) We can't insert this int array into the numa, because a numa - * takes a float array. So this just copies the data from the - * input array into the numa. The input array continues to be - * owned by the caller. - *- */ -NUMA * -numaCreateFromIArray(l_int32 *iarray, - l_int32 size) -{ -l_int32 i; -NUMA *na; - - PROCNAME("numaCreateFromIArray"); - - if (!iarray) - return (NUMA *)ERROR_PTR("iarray not defined", procName, NULL); - if (size <= 0) - return (NUMA *)ERROR_PTR("size must be > 0", procName, NULL); - - na = numaCreate(size); - for (i = 0; i < size; i++) - numaAddNumber(na, iarray[i]); - - return na; -} - - -/*! - * \brief numaCreateFromFArray() - * - * \param[in] farray float array - * \param[in] size of the array - * \param[in] copyflag L_INSERT or L_COPY - * \return na, or NULL on error - * - *
- * Notes: - * (1) With L_INSERT, ownership of the input array is transferred - * to the returned numa, and all %size elements are considered - * to be valid. - *- */ -NUMA * -numaCreateFromFArray(l_float32 *farray, - l_int32 size, - l_int32 copyflag) -{ -l_int32 i; -NUMA *na; - - PROCNAME("numaCreateFromFArray"); - - if (!farray) - return (NUMA *)ERROR_PTR("farray not defined", procName, NULL); - if (size <= 0) - return (NUMA *)ERROR_PTR("size must be > 0", procName, NULL); - if (copyflag != L_INSERT && copyflag != L_COPY) - return (NUMA *)ERROR_PTR("invalid copyflag", procName, NULL); - - na = numaCreate(size); - if (copyflag == L_INSERT) { - if (na->array) LEPT_FREE(na->array); - na->array = farray; - na->n = size; - } else { /* just copy the contents */ - for (i = 0; i < size; i++) - numaAddNumber(na, farray[i]); - } - - return na; -} - - -/*! - * \brief numaCreateFromString() - * - * \param[in] str string of comma-separated numbers - * \return na, or NULL on error - * - *
- * Notes: - * (1) The numbers can be ints or floats; they will be interpreted - * and stored as floats. To use them as integers (e.g., for - * indexing into arrays), use numaGetIValue(...). - *- */ -NUMA * -numaCreateFromString(const char *str) -{ -char *substr; -l_int32 i, n, nerrors; -l_float32 val; -NUMA *na; -SARRAY *sa; - - PROCNAME("numaCreateFromString"); - - if (!str || (strlen(str) == 0)) - return (NUMA *)ERROR_PTR("str not defined or empty", procName, NULL); - - sa = sarrayCreate(0); - sarraySplitString(sa, str, ","); - n = sarrayGetCount(sa); - na = numaCreate(n); - nerrors = 0; - for (i = 0; i < n; i++) { - substr = sarrayGetString(sa, i, L_NOCOPY); - if (sscanf(substr, "%f", &val) != 1) { - L_ERROR("substr %d not float\n", procName, i); - nerrors++; - } else { - numaAddNumber(na, val); - } - } - - sarrayDestroy(&sa); - if (nerrors > 0) { - numaDestroy(&na); - return (NUMA *)ERROR_PTR("non-floats in string", procName, NULL); - } - - return na; -} - - -/*! - * \brief numaDestroy() - * - * \param[in,out] pna numa to be destroyed and nulled if it exists - * \return void - * - *
- * Notes: - * (1) Decrements the ref count and, if 0, destroys the numa. - * (2) Always nulls the input ptr. - *- */ -void -numaDestroy(NUMA **pna) -{ -NUMA *na; - - PROCNAME("numaDestroy"); - - if (pna == NULL) { - L_WARNING("ptr address is NULL\n", procName); - return; - } - - if ((na = *pna) == NULL) - return; - - /* Decrement the ref count. If it is 0, destroy the numa. */ - numaChangeRefcount(na, -1); - if (numaGetRefcount(na) <= 0) { - if (na->array) - LEPT_FREE(na->array); - LEPT_FREE(na); - } - - *pna = NULL; - return; -} - - -/*! - * \brief numaCopy() - * - * \param[in] na - * \return copy of numa, or NULL on error - */ -NUMA * -numaCopy(NUMA *na) -{ -l_int32 i; -NUMA *cna; - - PROCNAME("numaCopy"); - - if (!na) - return (NUMA *)ERROR_PTR("na not defined", procName, NULL); - - if ((cna = numaCreate(na->nalloc)) == NULL) - return (NUMA *)ERROR_PTR("cna not made", procName, NULL); - cna->startx = na->startx; - cna->delx = na->delx; - - for (i = 0; i < na->n; i++) - numaAddNumber(cna, na->array[i]); - - return cna; -} - - -/*! - * \brief numaClone() - * - * \param[in] na - * \return ptr to same numa, or NULL on error - */ -NUMA * -numaClone(NUMA *na) -{ - PROCNAME("numaClone"); - - if (!na) - return (NUMA *)ERROR_PTR("na not defined", procName, NULL); - - numaChangeRefcount(na, 1); - return na; -} - - -/*! - * \brief numaEmpty() - * - * \param[in] na - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) This does not change the allocation of the array. - * It just clears the number of stored numbers, so that - * the array appears to be empty. - *- */ -l_ok -numaEmpty(NUMA *na) -{ - PROCNAME("numaEmpty"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - - na->n = 0; - return 0; -} - - - -/*--------------------------------------------------------------------------* - * Number array: add number and extend array * - *--------------------------------------------------------------------------*/ -/*! - * \brief numaAddNumber() - * - * \param[in] na - * \param[in] val float or int to be added; stored as a float - * \return 0 if OK, 1 on error - */ -l_ok -numaAddNumber(NUMA *na, - l_float32 val) -{ -l_int32 n; - - PROCNAME("numaAddNumber"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - - n = numaGetCount(na); - if (n >= na->nalloc) - numaExtendArray(na); - na->array[n] = val; - na->n++; - return 0; -} - - -/*! - * \brief numaExtendArray() - * - * \param[in] na - * \return 0 if OK, 1 on error - */ -static l_int32 -numaExtendArray(NUMA *na) -{ - PROCNAME("numaExtendArray"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - - if ((na->array = (l_float32 *)reallocNew((void **)&na->array, - sizeof(l_float32) * na->nalloc, - 2 * sizeof(l_float32) * na->nalloc)) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - - na->nalloc *= 2; - return 0; -} - - -/*! - * \brief numaInsertNumber() - * - * \param[in] na - * \param[in] index location in na to insert new value - * \param[in] val float32 or integer to be added - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This shifts na[i] --> na[i + 1] for all i >= index, - * and then inserts val as na[index]. - * (2) It should not be used repeatedly on large arrays, - * because the function is O(n). - * - *- */ -l_ok -numaInsertNumber(NUMA *na, - l_int32 index, - l_float32 val) -{ -l_int32 i, n; - - PROCNAME("numaInsertNumber"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - n = numaGetCount(na); - if (index < 0 || index > n) - return ERROR_INT("index not in {0...n}", procName, 1); - - if (n >= na->nalloc) - numaExtendArray(na); - for (i = n; i > index; i--) - na->array[i] = na->array[i - 1]; - na->array[index] = val; - na->n++; - return 0; -} - - -/*! - * \brief numaRemoveNumber() - * - * \param[in] na - * \param[in] index element to be removed - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This shifts na[i] --> na[i - 1] for all i > index. - * (2) It should not be used repeatedly on large arrays, - * because the function is O(n). - *- */ -l_ok -numaRemoveNumber(NUMA *na, - l_int32 index) -{ -l_int32 i, n; - - PROCNAME("numaRemoveNumber"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - n = numaGetCount(na); - if (index < 0 || index >= n) - return ERROR_INT("index not in {0...n - 1}", procName, 1); - - for (i = index + 1; i < n; i++) - na->array[i - 1] = na->array[i]; - na->n--; - return 0; -} - - -/*! - * \brief numaReplaceNumber() - * - * \param[in] na - * \param[in] index element to be replaced - * \param[in] val new value to replace old one - * \return 0 if OK, 1 on error - */ -l_ok -numaReplaceNumber(NUMA *na, - l_int32 index, - l_float32 val) -{ -l_int32 n; - - PROCNAME("numaReplaceNumber"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - n = numaGetCount(na); - if (index < 0 || index >= n) - return ERROR_INT("index not in {0...n - 1}", procName, 1); - - na->array[index] = val; - return 0; -} - - -/*----------------------------------------------------------------------* - * Numa accessors * - *----------------------------------------------------------------------*/ -/*! - * \brief numaGetCount() - * - * \param[in] na - * \return count, or 0 if no numbers or on error - */ -l_int32 -numaGetCount(NUMA *na) -{ - PROCNAME("numaGetCount"); - - if (!na) - return ERROR_INT("na not defined", procName, 0); - return na->n; -} - - -/*! - * \brief numaSetCount() - * - * \param[in] na - * \param[in] newcount - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) If newcount <= na->nalloc, this resets na->n. - * Using newcount = 0 is equivalent to numaEmpty(). - * (2) If newcount > na->nalloc, this causes a realloc - * to a size na->nalloc = newcount. - * (3) All the previously unused values in na are set to 0.0. - *- */ -l_ok -numaSetCount(NUMA *na, - l_int32 newcount) -{ - PROCNAME("numaSetCount"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - if (newcount > na->nalloc) { - if ((na->array = (l_float32 *)reallocNew((void **)&na->array, - sizeof(l_float32) * na->nalloc, - sizeof(l_float32) * newcount)) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - na->nalloc = newcount; - } - na->n = newcount; - return 0; -} - - -/*! - * \brief numaGetFValue() - * - * \param[in] na - * \param[in] index into numa - * \param[out] pval float value; set to 0.0 on error - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) Caller may need to check the function return value to - * decide if a 0.0 in the returned ival is valid. - *- */ -l_ok -numaGetFValue(NUMA *na, - l_int32 index, - l_float32 *pval) -{ - PROCNAME("numaGetFValue"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0.0; - if (!na) - return ERROR_INT("na not defined", procName, 1); - - if (index < 0 || index >= na->n) - return ERROR_INT("index not valid", procName, 1); - - *pval = na->array[index]; - return 0; -} - - -/*! - * \brief numaGetIValue() - * - * \param[in] na - * \param[in] index into numa - * \param[out] pival integer value; set to 0 on error - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) Caller may need to check the function return value to - * decide if a 0 in the returned ival is valid. - *- */ -l_ok -numaGetIValue(NUMA *na, - l_int32 index, - l_int32 *pival) -{ -l_float32 val; - - PROCNAME("numaGetIValue"); - - if (!pival) - return ERROR_INT("&ival not defined", procName, 1); - *pival = 0; - if (!na) - return ERROR_INT("na not defined", procName, 1); - - if (index < 0 || index >= na->n) - return ERROR_INT("index not valid", procName, 1); - - val = na->array[index]; - *pival = (l_int32)(val + L_SIGN(val) * 0.5); - return 0; -} - - -/*! - * \brief numaSetValue() - * - * \param[in] na - * \param[in] index to element to be set - * \param[in] val to set - * \return 0 if OK; 1 on error - */ -l_ok -numaSetValue(NUMA *na, - l_int32 index, - l_float32 val) -{ - PROCNAME("numaSetValue"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - if (index < 0 || index >= na->n) - return ERROR_INT("index not valid", procName, 1); - - na->array[index] = val; - return 0; -} - - -/*! - * \brief numaShiftValue() - * - * \param[in] na - * \param[in] index to element to change relative to the current value - * \param[in] diff increment if diff > 0 or decrement if diff < 0 - * \return 0 if OK; 1 on error - */ -l_ok -numaShiftValue(NUMA *na, - l_int32 index, - l_float32 diff) -{ - PROCNAME("numaShiftValue"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - if (index < 0 || index >= na->n) - return ERROR_INT("index not valid", procName, 1); - - na->array[index] += diff; - return 0; -} - - -/*! - * \brief numaGetIArray() - * - * \param[in] na - * \return a copy of the bare internal array, integerized - * by rounding, or NULL on error - *
- * Notes: - * (1) A copy of the array is always made, because we need to - * generate an integer array from the bare float array. - * The caller is responsible for freeing the array. - * (2) The array size is determined by the number of stored numbers, - * not by the size of the allocated array in the Numa. - * (3) This function is provided to simplify calculations - * using the bare internal array, rather than continually - * calling accessors on the numa. It is typically used - * on an array of size 256. - *- */ -l_int32 * -numaGetIArray(NUMA *na) -{ -l_int32 i, n, ival; -l_int32 *array; - - PROCNAME("numaGetIArray"); - - if (!na) - return (l_int32 *)ERROR_PTR("na not defined", procName, NULL); - - n = numaGetCount(na); - if ((array = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32))) == NULL) - return (l_int32 *)ERROR_PTR("array not made", procName, NULL); - for (i = 0; i < n; i++) { - numaGetIValue(na, i, &ival); - array[i] = ival; - } - - return array; -} - - -/*! - * \brief numaGetFArray() - * - * \param[in] na - * \param[in] copyflag L_NOCOPY or L_COPY - * \return either the bare internal array or a copy of it, - * or NULL on error - * - *
- * Notes: - * (1) If copyflag == L_COPY, it makes a copy which the caller - * is responsible for freeing. Otherwise, it operates - * directly on the bare array of the numa. - * (2) Very important: for L_NOCOPY, any writes to the array - * will be in the numa. Do not write beyond the size of - * the count field, because it will not be accessible - * from the numa! If necessary, be sure to set the count - * field to a larger number (such as the alloc size) - * BEFORE calling this function. Creating with numaMakeConstant() - * is another way to insure full initialization. - *- */ -l_float32 * -numaGetFArray(NUMA *na, - l_int32 copyflag) -{ -l_int32 i, n; -l_float32 *array; - - PROCNAME("numaGetFArray"); - - if (!na) - return (l_float32 *)ERROR_PTR("na not defined", procName, NULL); - - if (copyflag == L_NOCOPY) { - array = na->array; - } else { /* copyflag == L_COPY */ - n = numaGetCount(na); - if ((array = (l_float32 *)LEPT_CALLOC(n, sizeof(l_float32))) == NULL) - return (l_float32 *)ERROR_PTR("array not made", procName, NULL); - for (i = 0; i < n; i++) - array[i] = na->array[i]; - } - - return array; -} - - -/*! - * \brief numaGetRefCount() - * - * \param[in] na - * \return refcount, or UNDEF on error - */ -l_int32 -numaGetRefcount(NUMA *na) -{ - PROCNAME("numaGetRefcount"); - - if (!na) - return ERROR_INT("na not defined", procName, UNDEF); - return na->refcount; -} - - -/*! - * \brief numaChangeRefCount() - * - * \param[in] na - * \param[in] delta change to be applied - * \return 0 if OK, 1 on error - */ -l_ok -numaChangeRefcount(NUMA *na, - l_int32 delta) -{ - PROCNAME("numaChangeRefcount"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - na->refcount += delta; - return 0; -} - - -/*! - * \brief numaGetParameters() - * - * \param[in] na - * \param[out] pstartx [optional] startx - * \param[out] pdelx [optional] delx - * \return 0 if OK, 1 on error - */ -l_ok -numaGetParameters(NUMA *na, - l_float32 *pstartx, - l_float32 *pdelx) -{ - PROCNAME("numaGetParameters"); - - if (!pdelx && !pstartx) - return ERROR_INT("no return val requested", procName, 1); - if (pstartx) *pstartx = 0.0; - if (pdelx) *pdelx = 1.0; - if (!na) - return ERROR_INT("na not defined", procName, 1); - - if (pstartx) *pstartx = na->startx; - if (pdelx) *pdelx = na->delx; - return 0; -} - - -/*! - * \brief numaSetParameters() - * - * \param[in] na - * \param[in] startx x value corresponding to na[0] - * \param[in] delx difference in x values for the situation where the - * elements of na correspond to the evaulation of a - * function at equal intervals of size %delx - * \return 0 if OK, 1 on error - */ -l_ok -numaSetParameters(NUMA *na, - l_float32 startx, - l_float32 delx) -{ - PROCNAME("numaSetParameters"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - - na->startx = startx; - na->delx = delx; - return 0; -} - - -/*! - * \brief numaCopyParameters() - * - * \param[in] nad destination Numa - * \param[in] nas source Numa - * \return 0 if OK, 1 on error - */ -l_ok -numaCopyParameters(NUMA *nad, - NUMA *nas) -{ -l_float32 start, binsize; - - PROCNAME("numaCopyParameters"); - - if (!nas || !nad) - return ERROR_INT("nas and nad not both defined", procName, 1); - - numaGetParameters(nas, &start, &binsize); - numaSetParameters(nad, start, binsize); - return 0; -} - - -/*----------------------------------------------------------------------* - * Convert to string array * - *----------------------------------------------------------------------*/ -/*! - * \brief numaConvertToSarray() - * - * \param[in] na - * \param[in] size1 size of conversion field - * \param[in] size2 for float conversion: size of field to the right - * of the decimal point - * \param[in] addzeros for integer conversion: to add lead zeros - * \param[in] type L_INTEGER_VALUE, L_FLOAT_VALUE - * \return a sarray of the float values converted to strings - * representing either integer or float values; or NULL on error. - * - *
- * Notes: - * (1) For integer conversion, size2 is ignored. - * For float conversion, addzeroes is ignored. - *- */ -SARRAY * -numaConvertToSarray(NUMA *na, - l_int32 size1, - l_int32 size2, - l_int32 addzeros, - l_int32 type) -{ -char fmt[32], strbuf[64]; -l_int32 i, n, ival; -l_float32 fval; -SARRAY *sa; - - PROCNAME("numaConvertToSarray"); - - if (!na) - return (SARRAY *)ERROR_PTR("na not defined", procName, NULL); - if (type != L_INTEGER_VALUE && type != L_FLOAT_VALUE) - return (SARRAY *)ERROR_PTR("invalid type", procName, NULL); - - if (type == L_INTEGER_VALUE) { - if (addzeros) - snprintf(fmt, sizeof(fmt), "%%0%dd", size1); - else - snprintf(fmt, sizeof(fmt), "%%%dd", size1); - } else { /* L_FLOAT_VALUE */ - snprintf(fmt, sizeof(fmt), "%%%d.%df", size1, size2); - } - - n = numaGetCount(na); - if ((sa = sarrayCreate(n)) == NULL) - return (SARRAY *)ERROR_PTR("sa not made", procName, NULL); - - for (i = 0; i < n; i++) { - if (type == L_INTEGER_VALUE) { - numaGetIValue(na, i, &ival); - snprintf(strbuf, sizeof(strbuf), fmt, ival); - } else { /* L_FLOAT_VALUE */ - numaGetFValue(na, i, &fval); - snprintf(strbuf, sizeof(strbuf), fmt, fval); - } - sarrayAddString(sa, strbuf, L_COPY); - } - - return sa; -} - - -/*----------------------------------------------------------------------* - * Serialize numa for I/O * - *----------------------------------------------------------------------*/ -/*! - * \brief numaRead() - * - * \param[in] filename - * \return na, or NULL on error - */ -NUMA * -numaRead(const char *filename) -{ -FILE *fp; -NUMA *na; - - PROCNAME("numaRead"); - - if (!filename) - return (NUMA *)ERROR_PTR("filename not defined", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (NUMA *)ERROR_PTR("stream not opened", procName, NULL); - na = numaReadStream(fp); - fclose(fp); - if (!na) - return (NUMA *)ERROR_PTR("na not read", procName, NULL); - return na; -} - - -/*! - * \brief numaReadStream() - * - * \param[in] fp file stream - * \return numa, or NULL on error - */ -NUMA * -numaReadStream(FILE *fp) -{ -l_int32 i, n, index, ret, version; -l_float32 val, startx, delx; -NUMA *na; - - PROCNAME("numaReadStream"); - - if (!fp) - return (NUMA *)ERROR_PTR("stream not defined", procName, NULL); - - ret = fscanf(fp, "\nNuma Version %d\n", &version); - if (ret != 1) - return (NUMA *)ERROR_PTR("not a numa file", procName, NULL); - if (version != NUMA_VERSION_NUMBER) - return (NUMA *)ERROR_PTR("invalid numa version", procName, NULL); - if (fscanf(fp, "Number of numbers = %d\n", &n) != 1) - return (NUMA *)ERROR_PTR("invalid number of numbers", procName, NULL); - - if (n > MaxArraySize) { - L_ERROR("n = %d > %d\n", procName, n, MaxArraySize); - return NULL; - } - if ((na = numaCreate(n)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - - for (i = 0; i < n; i++) { - if (fscanf(fp, " [%d] = %f\n", &index, &val) != 2) { - numaDestroy(&na); - return (NUMA *)ERROR_PTR("bad input data", procName, NULL); - } - numaAddNumber(na, val); - } - - /* Optional data */ - if (fscanf(fp, "startx = %f, delx = %f\n", &startx, &delx) == 2) - numaSetParameters(na, startx, delx); - - return na; -} - - -/*! - * \brief numaReadMem() - * - * \param[in] data numa serialization; in ascii - * \param[in] size of data; can use strlen to get it - * \return na, or NULL on error - */ -NUMA * -numaReadMem(const l_uint8 *data, - size_t size) -{ -FILE *fp; -NUMA *na; - - PROCNAME("numaReadMem"); - - if (!data) - return (NUMA *)ERROR_PTR("data not defined", procName, NULL); - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return (NUMA *)ERROR_PTR("stream not opened", procName, NULL); - - na = numaReadStream(fp); - fclose(fp); - if (!na) L_ERROR("numa not read\n", procName); - return na; -} - - -/*! - * \brief numaWriteDebug() - * - * \param[in] filename - * \param[in] na - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) Debug version, intended for use in the library when writing - * to files in a temp directory with names that are compiled in. - * This is used instead of numaWrite() for all such library calls. - * (2) The global variable LeptDebugOK defaults to 0, and can be set - * or cleared by the function setLeptDebugOK(). - *- */ -l_ok -numaWriteDebug(const char *filename, - NUMA *na) -{ - PROCNAME("numaWriteDebug"); - - if (LeptDebugOK) { - return numaWrite(filename, na); - } else { - L_INFO("write to named temp file %s is disabled\n", procName, filename); - return 0; - } -} - - -/*! - * \brief numaWrite() - * - * \param[in] filename - * \param[in] na - * \return 0 if OK, 1 on error - */ -l_ok -numaWrite(const char *filename, - NUMA *na) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("numaWrite"); - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!na) - return ERROR_INT("na not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "w")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = numaWriteStream(fp, na); - fclose(fp); - if (ret) - return ERROR_INT("na not written to stream", procName, 1); - return 0; -} - - -/*! - * \brief numaWriteStream() - * - * \param[in] fp file stream; use NULL to write to stderr - * \param[in] na - * \return 0 if OK, 1 on error - */ -l_ok -numaWriteStream(FILE *fp, - NUMA *na) -{ -l_int32 i, n; -l_float32 startx, delx; - - PROCNAME("numaWriteStream"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - if (!fp) - return numaWriteStderr(na); - - n = numaGetCount(na); - fprintf(fp, "\nNuma Version %d\n", NUMA_VERSION_NUMBER); - fprintf(fp, "Number of numbers = %d\n", n); - for (i = 0; i < n; i++) - fprintf(fp, " [%d] = %f\n", i, na->array[i]); - fprintf(fp, "\n"); - - /* Optional data */ - numaGetParameters(na, &startx, &delx); - if (startx != 0.0 || delx != 1.0) - fprintf(fp, "startx = %f, delx = %f\n", startx, delx); - - return 0; -} - - -/*! - * \brief numaWriteStderr() - * - * \param[in] na - * \return 0 if OK, 1 on error - */ -l_ok -numaWriteStderr(NUMA *na) -{ -l_int32 i, n; -l_float32 startx, delx; - - PROCNAME("numaWriteStderr"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - - n = numaGetCount(na); - lept_stderr("\nNuma Version %d\n", NUMA_VERSION_NUMBER); - lept_stderr("Number of numbers = %d\n", n); - for (i = 0; i < n; i++) - lept_stderr(" [%d] = %f\n", i, na->array[i]); - lept_stderr("\n"); - - /* Optional data */ - numaGetParameters(na, &startx, &delx); - if (startx != 0.0 || delx != 1.0) - lept_stderr("startx = %f, delx = %f\n", startx, delx); - - return 0; -} - - -/*! - * \brief numaWriteMem() - * - * \param[out] pdata data of serialized numa; ascii - * \param[out] psize size of returned data - * \param[in] na - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Serializes a numa in memory and puts the result in a buffer. - *- */ -l_ok -numaWriteMem(l_uint8 **pdata, - size_t *psize, - NUMA *na) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("numaWriteMem"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - if (!psize) - return ERROR_INT("&size not defined", procName, 1); - if (!na) - return ERROR_INT("na not defined", procName, 1); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = numaWriteStream(fp, na); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = numaWriteStream(fp, na); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} - - -/*--------------------------------------------------------------------------* - * Numaa creation, destruction * - *--------------------------------------------------------------------------*/ -/*! - * \brief numaaCreate() - * - * \param[in] n size of numa ptr array to be alloc'd 0 for default - * \return naa, or NULL on error - * - */ -NUMAA * -numaaCreate(l_int32 n) -{ -NUMAA *naa; - - PROCNAME("numaaCreate"); - - if (n <= 0 || n > MaxPtrArraySize) - n = InitialArraySize; - - naa = (NUMAA *)LEPT_CALLOC(1, sizeof(NUMAA)); - if ((naa->numa = (NUMA **)LEPT_CALLOC(n, sizeof(NUMA *))) == NULL) { - numaaDestroy(&naa); - return (NUMAA *)ERROR_PTR("numa ptr array not made", procName, NULL); - } - - naa->nalloc = n; - naa->n = 0; - return naa; -} - - -/*! - * \brief numaaCreateFull() - * - * \param[in] nptr size of numa ptr array to be alloc'd - * \param[in] n size of individual numa arrays to be allocated - * to 0 for default - * \return naa, or NULL on error - * - *
- * Notes: - * (1) This allocates numaa and fills the array with allocated numas. - * In use, after calling this function, use - * numaaAddNumber(naa, index, val); - * to add val to the index-th numa in naa. - *- */ -NUMAA * -numaaCreateFull(l_int32 nptr, - l_int32 n) -{ -l_int32 i; -NUMAA *naa; -NUMA *na; - - naa = numaaCreate(nptr); - for (i = 0; i < nptr; i++) { - na = numaCreate(n); - numaaAddNuma(naa, na, L_INSERT); - } - - return naa; -} - - -/*! - * \brief numaaTruncate() - * - * \param[in] naa - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This identifies the largest index containing a numa that - * has any numbers within it, destroys all numa beyond that - * index, and resets the count. - *- */ -l_ok -numaaTruncate(NUMAA *naa) -{ -l_int32 i, n, nn; -NUMA *na; - - PROCNAME("numaaTruncate"); - - if (!naa) - return ERROR_INT("naa not defined", procName, 1); - - n = numaaGetCount(naa); - for (i = n - 1; i >= 0; i--) { - na = numaaGetNuma(naa, i, L_CLONE); - if (!na) - continue; - nn = numaGetCount(na); - numaDestroy(&na); - if (nn == 0) - numaDestroy(&naa->numa[i]); - else - break; - } - naa->n = i + 1; - return 0; -} - - -/*! - * \brief numaaDestroy() - * - * \param[in,out] pnaa to be destroyed and nulled, if it exists - * \return void - */ -void -numaaDestroy(NUMAA **pnaa) -{ -l_int32 i; -NUMAA *naa; - - PROCNAME("numaaDestroy"); - - if (pnaa == NULL) { - L_WARNING("ptr address is NULL!\n", procName); - return; - } - - if ((naa = *pnaa) == NULL) - return; - - for (i = 0; i < naa->n; i++) - numaDestroy(&naa->numa[i]); - LEPT_FREE(naa->numa); - LEPT_FREE(naa); - *pnaa = NULL; - - return; -} - - - -/*--------------------------------------------------------------------------* - * Add Numa to Numaa * - *--------------------------------------------------------------------------*/ -/*! - * \brief numaaAddNuma() - * - * \param[in] naa - * \param[in] na to be added - * \param[in] copyflag L_INSERT, L_COPY, L_CLONE - * \return 0 if OK, 1 on error - */ -l_ok -numaaAddNuma(NUMAA *naa, - NUMA *na, - l_int32 copyflag) -{ -l_int32 n; -NUMA *nac; - - PROCNAME("numaaAddNuma"); - - if (!naa) - return ERROR_INT("naa not defined", procName, 1); - if (!na) - return ERROR_INT("na not defined", procName, 1); - - if (copyflag == L_INSERT) { - nac = na; - } else if (copyflag == L_COPY) { - if ((nac = numaCopy(na)) == NULL) - return ERROR_INT("nac not made", procName, 1); - } else if (copyflag == L_CLONE) { - nac = numaClone(na); - } else { - return ERROR_INT("invalid copyflag", procName, 1); - } - - n = numaaGetCount(naa); - if (n >= naa->nalloc) - numaaExtendArray(naa); - naa->numa[n] = nac; - naa->n++; - return 0; -} - - -/*! - * \brief numaaExtendArray() - * - * \param[in] naa - * \return 0 if OK, 1 on error - */ -static l_int32 -numaaExtendArray(NUMAA *naa) -{ - PROCNAME("numaaExtendArray"); - - if (!naa) - return ERROR_INT("naa not defined", procName, 1); - - if ((naa->numa = (NUMA **)reallocNew((void **)&naa->numa, - sizeof(NUMA *) * naa->nalloc, - 2 * sizeof(NUMA *) * naa->nalloc)) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - - naa->nalloc *= 2; - return 0; -} - - -/*----------------------------------------------------------------------* - * Numaa accessors * - *----------------------------------------------------------------------*/ -/*! - * \brief numaaGetCount() - * - * \param[in] naa - * \return count number of numa, or 0 if no numa or on error - */ -l_int32 -numaaGetCount(NUMAA *naa) -{ - PROCNAME("numaaGetCount"); - - if (!naa) - return ERROR_INT("naa not defined", procName, 0); - return naa->n; -} - - -/*! - * \brief numaaGetNumaCount() - * - * \param[in] naa - * \param[in] index of numa in naa - * \return count of numbers in the referenced numa, or 0 on error. - */ -l_int32 -numaaGetNumaCount(NUMAA *naa, - l_int32 index) -{ - PROCNAME("numaaGetNumaCount"); - - if (!naa) - return ERROR_INT("naa not defined", procName, 0); - if (index < 0 || index >= naa->n) - return ERROR_INT("invalid index into naa", procName, 0); - return numaGetCount(naa->numa[index]); -} - - -/*! - * \brief numaaGetNumberCount() - * - * \param[in] naa - * \return count total number of numbers in the numaa, - * or 0 if no numbers or on error - */ -l_int32 -numaaGetNumberCount(NUMAA *naa) -{ -NUMA *na; -l_int32 n, sum, i; - - PROCNAME("numaaGetNumberCount"); - - if (!naa) - return ERROR_INT("naa not defined", procName, 0); - - n = numaaGetCount(naa); - for (sum = 0, i = 0; i < n; i++) { - na = numaaGetNuma(naa, i, L_CLONE); - sum += numaGetCount(na); - numaDestroy(&na); - } - - return sum; -} - - -/*! - * \brief numaaGetPtrArray() - * - * \param[in] naa - * \return the internal array of ptrs to Numa, or NULL on error - * - *
- * Notes: - * (1) This function is convenient for doing direct manipulation on - * a fixed size array of Numas. To do this, it sets the count - * to the full size of the allocated array of Numa ptrs. - * The originating Numaa owns this array: DO NOT free it! - * (2) Intended usage: - * Numaa *naa = numaaCreate(n); - * Numa **array = numaaGetPtrArray(naa); - * ... [manipulate Numas directly on the array] - * numaaDestroy(&naa); - * (3) Cautions: - * ~ Do not free this array; it is owned by tne Numaa. - * ~ Do not call any functions on the Numaa, other than - * numaaDestroy() when you're finished with the array. - * Adding a Numa will force a resize, destroying the ptr array. - * ~ Do not address the array outside its allocated size. - * With the bare array, there are no protections. If the - * allocated size is n, array[n] is an error. - *- */ -NUMA ** -numaaGetPtrArray(NUMAA *naa) -{ - PROCNAME("numaaGetPtrArray"); - - if (!naa) - return (NUMA **)ERROR_PTR("naa not defined", procName, NULL); - - naa->n = naa->nalloc; - return naa->numa; -} - - -/*! - * \brief numaaGetNuma() - * - * \param[in] naa - * \param[in] index to the index-th numa - * \param[in] accessflag L_COPY or L_CLONE - * \return numa, or NULL on error - */ -NUMA * -numaaGetNuma(NUMAA *naa, - l_int32 index, - l_int32 accessflag) -{ - PROCNAME("numaaGetNuma"); - - if (!naa) - return (NUMA *)ERROR_PTR("naa not defined", procName, NULL); - if (index < 0 || index >= naa->n) - return (NUMA *)ERROR_PTR("index not valid", procName, NULL); - - if (accessflag == L_COPY) - return numaCopy(naa->numa[index]); - else if (accessflag == L_CLONE) - return numaClone(naa->numa[index]); - else - return (NUMA *)ERROR_PTR("invalid accessflag", procName, NULL); -} - - -/*! - * \brief numaaReplaceNuma() - * - * \param[in] naa - * \param[in] index to the index-th numa - * \param[in] na insert and replace any existing one - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Any existing numa is destroyed, and the input one - * is inserted in its place. - * (2) If the index is invalid, return 1 (error) - *- */ -l_ok -numaaReplaceNuma(NUMAA *naa, - l_int32 index, - NUMA *na) -{ -l_int32 n; - - PROCNAME("numaaReplaceNuma"); - - if (!naa) - return ERROR_INT("naa not defined", procName, 1); - if (!na) - return ERROR_INT("na not defined", procName, 1); - n = numaaGetCount(naa); - if (index < 0 || index >= n) - return ERROR_INT("index not valid", procName, 1); - - numaDestroy(&naa->numa[index]); - naa->numa[index] = na; - return 0; -} - - -/*! - * \brief numaaGetValue() - * - * \param[in] naa - * \param[in] i index of numa within numaa - * \param[in] j index into numa - * \param[out] pfval [optional] float value - * \param[out] pival [optional] int value - * \return 0 if OK, 1 on error - */ -l_ok -numaaGetValue(NUMAA *naa, - l_int32 i, - l_int32 j, - l_float32 *pfval, - l_int32 *pival) -{ -l_int32 n; -NUMA *na; - - PROCNAME("numaaGetValue"); - - if (!pfval && !pival) - return ERROR_INT("no return val requested", procName, 1); - if (pfval) *pfval = 0.0; - if (pival) *pival = 0; - if (!naa) - return ERROR_INT("naa not defined", procName, 1); - n = numaaGetCount(naa); - if (i < 0 || i >= n) - return ERROR_INT("invalid index into naa", procName, 1); - na = naa->numa[i]; - if (j < 0 || j >= na->n) - return ERROR_INT("invalid index into na", procName, 1); - if (pfval) *pfval = na->array[j]; - if (pival) *pival = (l_int32)(na->array[j]); - return 0; -} - - -/*! - * \brief numaaAddNumber() - * - * \param[in] naa - * \param[in] index of numa within numaa - * \param[in] val float or int to be added; stored as a float - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Adds to an existing numa only. - *- */ -l_ok -numaaAddNumber(NUMAA *naa, - l_int32 index, - l_float32 val) -{ -l_int32 n; -NUMA *na; - - PROCNAME("numaaAddNumber"); - - if (!naa) - return ERROR_INT("naa not defined", procName, 1); - n = numaaGetCount(naa); - if (index < 0 || index >= n) - return ERROR_INT("invalid index in naa", procName, 1); - - na = numaaGetNuma(naa, index, L_CLONE); - numaAddNumber(na, val); - numaDestroy(&na); - return 0; -} - - -/*----------------------------------------------------------------------* - * Serialize numaa for I/O * - *----------------------------------------------------------------------*/ -/*! - * \brief numaaRead() - * - * \param[in] filename - * \return naa, or NULL on error - */ -NUMAA * -numaaRead(const char *filename) -{ -FILE *fp; -NUMAA *naa; - - PROCNAME("numaaRead"); - - if (!filename) - return (NUMAA *)ERROR_PTR("filename not defined", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (NUMAA *)ERROR_PTR("stream not opened", procName, NULL); - naa = numaaReadStream(fp); - fclose(fp); - if (!naa) - return (NUMAA *)ERROR_PTR("naa not read", procName, NULL); - return naa; -} - - -/*! - * \brief numaaReadStream() - * - * \param[in] fp file stream - * \return naa, or NULL on error - */ -NUMAA * -numaaReadStream(FILE *fp) -{ -l_int32 i, n, index, ret, version; -NUMA *na; -NUMAA *naa; - - PROCNAME("numaaReadStream"); - - if (!fp) - return (NUMAA *)ERROR_PTR("stream not defined", procName, NULL); - - ret = fscanf(fp, "\nNumaa Version %d\n", &version); - if (ret != 1) - return (NUMAA *)ERROR_PTR("not a numa file", procName, NULL); - if (version != NUMA_VERSION_NUMBER) - return (NUMAA *)ERROR_PTR("invalid numaa version", procName, NULL); - if (fscanf(fp, "Number of numa = %d\n\n", &n) != 1) - return (NUMAA *)ERROR_PTR("invalid number of numa", procName, NULL); - - if (n > MaxPtrArraySize) { - L_ERROR("n = %d > %d\n", procName, n, MaxPtrArraySize); - return NULL; - } - if ((naa = numaaCreate(n)) == NULL) - return (NUMAA *)ERROR_PTR("naa not made", procName, NULL); - - for (i = 0; i < n; i++) { - if (fscanf(fp, "Numa[%d]:", &index) != 1) { - numaaDestroy(&naa); - return (NUMAA *)ERROR_PTR("invalid numa header", procName, NULL); - } - if ((na = numaReadStream(fp)) == NULL) { - numaaDestroy(&naa); - return (NUMAA *)ERROR_PTR("na not made", procName, NULL); - } - numaaAddNuma(naa, na, L_INSERT); - } - - return naa; -} - - -/*! - * \brief numaaReadMem() - * - * \param[in] data numaa serialization; in ascii - * \param[in] size of data; can use strlen to get it - * \return naa, or NULL on error - */ -NUMAA * -numaaReadMem(const l_uint8 *data, - size_t size) -{ -FILE *fp; -NUMAA *naa; - - PROCNAME("numaaReadMem"); - - if (!data) - return (NUMAA *)ERROR_PTR("data not defined", procName, NULL); - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return (NUMAA *)ERROR_PTR("stream not opened", procName, NULL); - - naa = numaaReadStream(fp); - fclose(fp); - if (!naa) L_ERROR("naa not read\n", procName); - return naa; -} - - -/*! - * \brief numaaWrite() - * - * \param[in] filename - * \param[in] naa - * \return 0 if OK, 1 on error - */ -l_ok -numaaWrite(const char *filename, - NUMAA *naa) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("numaaWrite"); - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!naa) - return ERROR_INT("naa not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "w")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = numaaWriteStream(fp, naa); - fclose(fp); - if (ret) - return ERROR_INT("naa not written to stream", procName, 1); - return 0; -} - - -/*! - * \brief numaaWriteStream() - * - * \param[in] fp file stream - * \param[in] naa - * \return 0 if OK, 1 on error - */ -l_ok -numaaWriteStream(FILE *fp, - NUMAA *naa) -{ -l_int32 i, n; -NUMA *na; - - PROCNAME("numaaWriteStream"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!naa) - return ERROR_INT("naa not defined", procName, 1); - - n = numaaGetCount(naa); - fprintf(fp, "\nNumaa Version %d\n", NUMA_VERSION_NUMBER); - fprintf(fp, "Number of numa = %d\n\n", n); - for (i = 0; i < n; i++) { - if ((na = numaaGetNuma(naa, i, L_CLONE)) == NULL) - return ERROR_INT("na not found", procName, 1); - fprintf(fp, "Numa[%d]:", i); - numaWriteStream(fp, na); - numaDestroy(&na); - } - - return 0; -} - - -/*! - * \brief numaaWriteMem() - * - * \param[out] pdata data of serialized numaa; ascii - * \param[out] psize size of returned data - * \param[in] naa - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Serializes a numaa in memory and puts the result in a buffer. - *- */ -l_ok -numaaWriteMem(l_uint8 **pdata, - size_t *psize, - NUMAA *naa) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("numaaWriteMem"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - if (!psize) - return ERROR_INT("&size not defined", procName, 1); - if (!naa) - return ERROR_INT("naa not defined", procName, 1); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = numaaWriteStream(fp, naa); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = numaaWriteStream(fp, naa); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} - diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/numafunc1.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/numafunc1.c deleted file mode 100644 index 18f848b1..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/numafunc1.c +++ /dev/null @@ -1,3491 +0,0 @@ -/*====================================================================* - - 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 numafunc1.c - *
- * - * -------------------------------------- - * This file has these Numa utilities: - * - arithmetic operations - * - simple data analysis - * - generation of special sequences - * - permutations - * - interpolation - * - sorting - * - data analysis requiring sorting - * - joins and rearrangements - * -------------------------------------- - * - * Arithmetic and logic - * NUMA *numaArithOp() - * NUMA *numaLogicalOp() - * NUMA *numaInvert() - * l_int32 numaSimilar() - * l_int32 numaAddToNumber() - * - * Simple extractions - * l_int32 numaGetMin() - * l_int32 numaGetMax() - * l_int32 numaGetSum() - * NUMA *numaGetPartialSums() - * l_int32 numaGetSumOnInterval() - * l_int32 numaHasOnlyIntegers() - * NUMA *numaSubsample() - * NUMA *numaMakeDelta() - * NUMA *numaMakeSequence() - * NUMA *numaMakeConstant() - * NUMA *numaMakeAbsValue() - * NUMA *numaAddBorder() - * NUMA *numaAddSpecifiedBorder() - * NUMA *numaRemoveBorder() - * l_int32 numaCountNonzeroRuns() - * l_int32 numaGetNonzeroRange() - * l_int32 numaGetCountRelativeToZero() - * NUMA *numaClipToInterval() - * NUMA *numaMakeThresholdIndicator() - * NUMA *numaUniformSampling() - * NUMA *numaReverse() - * - * Signal feature extraction - * NUMA *numaLowPassIntervals() - * NUMA *numaThresholdEdges() - * NUMA *numaGetSpanValues() - * NUMA *numaGetEdgeValues() - * - * Interpolation - * l_int32 numaInterpolateEqxVal() - * l_int32 numaInterpolateEqxInterval() - * l_int32 numaInterpolateArbxVal() - * l_int32 numaInterpolateArbxInterval() - * - * Functions requiring interpolation - * l_int32 numaFitMax() - * l_int32 numaDifferentiateInterval() - * l_int32 numaIntegrateInterval() - * - * Sorting - * NUMA *numaSortGeneral() - * NUMA *numaSortAutoSelect() - * NUMA *numaSortIndexAutoSelect() - * l_int32 numaChooseSortType() - * NUMA *numaSort() - * NUMA *numaBinSort() - * NUMA *numaGetSortIndex() - * NUMA *numaGetBinSortIndex() - * NUMA *numaSortByIndex() - * l_int32 numaIsSorted() - * l_int32 numaSortPair() - * NUMA *numaInvertMap() - * - * Random permutation - * NUMA *numaPseudorandomSequence() - * NUMA *numaRandomPermutation() - * - * Functions requiring sorting - * l_int32 numaGetRankValue() - * l_int32 numaGetMedian() - * l_int32 numaGetBinnedMedian() - * l_int32 numaGetMeanDevFromMedian() - * l_int32 numaGetMedianDevFromMedian() - * l_int32 numaGetMode() - * - * Rearrangements - * l_int32 numaJoin() - * l_int32 numaaJoin() - * NUMA *numaaFlattenToNuma() - * - * Things to remember when using the Numa: - * - * (1) The numa is a struct, not an array. Always use accessors - * (see numabasic.c), never the fields directly. - * - * (2) The number array holds l_float32 values. It can also - * be used to store l_int32 values. See numabasic.c for - * details on using the accessors. - * - * (3) If you use numaCreate(), no numbers are stored and the size is 0. - * You have to add numbers to increase the size. - * If you want to start with a numa of a fixed size, with each - * entry initialized to the same value, use numaMakeConstant(). - * - * (4) Occasionally, in the comments we denote the i-th element of a - * numa by na[i]. This is conceptual only -- the numa is not an array! - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) The sizes of na1 and na2 must be equal. - * (2) nad can only null or equal to na1. - * (3) To add a constant to a numa, or to multipy a numa by - * a constant, use numaTransform(). - *- */ -NUMA * -numaArithOp(NUMA *nad, - NUMA *na1, - NUMA *na2, - l_int32 op) -{ -l_int32 i, n; -l_float32 val1, val2; - - PROCNAME("numaArithOp"); - - if (!na1 || !na2) - return (NUMA *)ERROR_PTR("na1, na2 not both defined", procName, nad); - n = numaGetCount(na1); - if (n != numaGetCount(na2)) - return (NUMA *)ERROR_PTR("na1, na2 sizes differ", procName, nad); - if (nad && nad != na1) - return (NUMA *)ERROR_PTR("nad defined but not in-place", procName, nad); - if (op != L_ARITH_ADD && op != L_ARITH_SUBTRACT && - op != L_ARITH_MULTIPLY && op != L_ARITH_DIVIDE) - return (NUMA *)ERROR_PTR("invalid op", procName, nad); - if (op == L_ARITH_DIVIDE) { - for (i = 0; i < n; i++) { - numaGetFValue(na2, i, &val2); - if (val2 == 0.0) - return (NUMA *)ERROR_PTR("na2 has 0 element", procName, nad); - } - } - - /* If nad is not identical to na1, make it an identical copy */ - if (!nad) - nad = numaCopy(na1); - - for (i = 0; i < n; i++) { - numaGetFValue(nad, i, &val1); - numaGetFValue(na2, i, &val2); - switch (op) { - case L_ARITH_ADD: - numaSetValue(nad, i, val1 + val2); - break; - case L_ARITH_SUBTRACT: - numaSetValue(nad, i, val1 - val2); - break; - case L_ARITH_MULTIPLY: - numaSetValue(nad, i, val1 * val2); - break; - case L_ARITH_DIVIDE: - numaSetValue(nad, i, val1 / val2); - break; - default: - lept_stderr(" Unknown arith op: %d\n", op); - return nad; - } - } - - return nad; -} - - -/*! - * \brief numaLogicalOp() - * - * \param[in] nad [optional] can be null or equal to na1 (in-place - * \param[in] na1 - * \param[in] na2 - * \param[in] op L_UNION, L_INTERSECTION, L_SUBTRACTION, L_EXCLUSIVE_OR - * \return nad always: operation applied to na1 and na2 - * - *
- * Notes: - * (1) The sizes of na1 and na2 must be equal. - * (2) nad can only be null or equal to na1. - * (3) This is intended for use with indicator arrays (0s and 1s). - * Input data is extracted as integers (0 == false, anything - * else == true); output results are 0 and 1. - * (4) L_SUBTRACTION is subtraction of val2 from val1. For bit logical - * arithmetic this is (val1 & ~val2), but because these values - * are integers, we use (val1 && !val2). - *- */ -NUMA * -numaLogicalOp(NUMA *nad, - NUMA *na1, - NUMA *na2, - l_int32 op) -{ -l_int32 i, n, val1, val2, val; - - PROCNAME("numaLogicalOp"); - - if (!na1 || !na2) - return (NUMA *)ERROR_PTR("na1, na2 not both defined", procName, nad); - n = numaGetCount(na1); - if (n != numaGetCount(na2)) - return (NUMA *)ERROR_PTR("na1, na2 sizes differ", procName, nad); - if (nad && nad != na1) - return (NUMA *)ERROR_PTR("nad defined; not in-place", procName, nad); - if (op != L_UNION && op != L_INTERSECTION && - op != L_SUBTRACTION && op != L_EXCLUSIVE_OR) - return (NUMA *)ERROR_PTR("invalid op", procName, nad); - - /* If nad is not identical to na1, make it an identical copy */ - if (!nad) - nad = numaCopy(na1); - - for (i = 0; i < n; i++) { - numaGetIValue(nad, i, &val1); - numaGetIValue(na2, i, &val2); - val1 = (val1 == 0) ? 0 : 1; - val2 = (val2 == 0) ? 0 : 1; - switch (op) { - case L_UNION: - val = (val1 || val2) ? 1 : 0; - numaSetValue(nad, i, val); - break; - case L_INTERSECTION: - val = (val1 && val2) ? 1 : 0; - numaSetValue(nad, i, val); - break; - case L_SUBTRACTION: - val = (val1 && !val2) ? 1 : 0; - numaSetValue(nad, i, val); - break; - case L_EXCLUSIVE_OR: - val = (val1 != val2) ? 1 : 0; - numaSetValue(nad, i, val); - break; - default: - lept_stderr(" Unknown logical op: %d\n", op); - return nad; - } - } - - return nad; -} - - -/*! - * \brief numaInvert() - * - * \param[in] nad [optional] can be null or equal to nas (in-place - * \param[in] nas - * \return nad always: 'inverts' nas - * - *
- * Notes: - * (1) This is intended for use with indicator arrays (0s and 1s). - * It gives a boolean-type output, taking the input as - * an integer and inverting it: - * 0 --> 1 - * anything else --> 0 - *- */ -NUMA * -numaInvert(NUMA *nad, - NUMA *nas) -{ -l_int32 i, n, val; - - PROCNAME("numaInvert"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, nad); - if (nad && nad != nas) - return (NUMA *)ERROR_PTR("nad defined; not in-place", procName, nad); - - if (!nad) - nad = numaCopy(nas); - n = numaGetCount(nad); - for (i = 0; i < n; i++) { - numaGetIValue(nad, i, &val); - if (!val) - val = 1; - else - val = 0; - numaSetValue(nad, i, val); - } - - return nad; -} - - -/*! - * \brief numaSimilar() - * - * \param[in] na1 - * \param[in] na2 - * \param[in] maxdiff use 0.0 for exact equality - * \param[out] psimilar 1 if similar; 0 if different - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Float values can differ slightly due to roundoff and - * accumulated errors. Using %maxdiff > 0.0 allows similar - * arrays to be identified. - *-*/ -l_int32 -numaSimilar(NUMA *na1, - NUMA *na2, - l_float32 maxdiff, - l_int32 *psimilar) -{ -l_int32 i, n; -l_float32 val1, val2; - - PROCNAME("numaSimilar"); - - if (!psimilar) - return ERROR_INT("&similar not defined", procName, 1); - *psimilar = 0; - if (!na1 || !na2) - return ERROR_INT("na1 and na2 not both defined", procName, 1); - maxdiff = L_ABS(maxdiff); - - n = numaGetCount(na1); - if (n != numaGetCount(na2)) return 0; - - for (i = 0; i < n; i++) { - numaGetFValue(na1, i, &val1); - numaGetFValue(na2, i, &val2); - if (L_ABS(val1 - val2) > maxdiff) return 0; - } - - *psimilar = 1; - return 0; -} - - -/*! - * \brief numaAddToNumber() - * - * \param[in] na source numa - * \param[in] index element to be changed - * \param[in] val new value to be added - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is useful for accumulating sums, regardless of the index - * order in which the values are made available. - * (2) Before use, the numa has to be filled up to %index. This would - * typically be used by creating the numa with the full sized - * array, initialized to 0.0, using numaMakeConstant(). - *- */ -l_ok -numaAddToNumber(NUMA *na, - l_int32 index, - l_float32 val) -{ -l_int32 n; - - PROCNAME("numaAddToNumber"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - n = numaGetCount(na); - if (index < 0 || index >= n) - return ERROR_INT("index not in {0...n - 1}", procName, 1); - - na->array[index] += val; - return 0; -} - - -/*----------------------------------------------------------------------* - * Simple extractions * - *----------------------------------------------------------------------*/ -/*! - * \brief numaGetMin() - * - * \param[in] na source numa - * \param[out] pminval [optional] min value - * \param[out] piminloc [optional] index of min location - * \return 0 if OK; 1 on error - */ -l_ok -numaGetMin(NUMA *na, - l_float32 *pminval, - l_int32 *piminloc) -{ -l_int32 i, n, iminloc; -l_float32 val, minval; - - PROCNAME("numaGetMin"); - - if (!pminval && !piminloc) - return ERROR_INT("nothing to do", procName, 1); - if (pminval) *pminval = 0.0; - if (piminloc) *piminloc = 0; - if (!na) - return ERROR_INT("na not defined", procName, 1); - - minval = +1000000000.; - iminloc = 0; - n = numaGetCount(na); - for (i = 0; i < n; i++) { - numaGetFValue(na, i, &val); - if (val < minval) { - minval = val; - iminloc = i; - } - } - - if (pminval) *pminval = minval; - if (piminloc) *piminloc = iminloc; - return 0; -} - - -/*! - * \brief numaGetMax() - * - * \param[in] na source numa - * \param[out] pmaxval [optional] max value - * \param[out] pimaxloc [optional] index of max location - * \return 0 if OK; 1 on error - */ -l_ok -numaGetMax(NUMA *na, - l_float32 *pmaxval, - l_int32 *pimaxloc) -{ -l_int32 i, n, imaxloc; -l_float32 val, maxval; - - PROCNAME("numaGetMax"); - - if (!pmaxval && !pimaxloc) - return ERROR_INT("nothing to do", procName, 1); - if (pmaxval) *pmaxval = 0.0; - if (pimaxloc) *pimaxloc = 0; - if (!na) - return ERROR_INT("na not defined", procName, 1); - - maxval = -1000000000.; - imaxloc = 0; - n = numaGetCount(na); - for (i = 0; i < n; i++) { - numaGetFValue(na, i, &val); - if (val > maxval) { - maxval = val; - imaxloc = i; - } - } - - if (pmaxval) *pmaxval = maxval; - if (pimaxloc) *pimaxloc = imaxloc; - return 0; -} - - -/*! - * \brief numaGetSum() - * - * \param[in] na source numa - * \param[out] psum sum of values - * \return 0 if OK, 1 on error - */ -l_ok -numaGetSum(NUMA *na, - l_float32 *psum) -{ -l_int32 i, n; -l_float32 val, sum; - - PROCNAME("numaGetSum"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - if (!psum) - return ERROR_INT("&sum not defined", procName, 1); - - sum = 0.0; - n = numaGetCount(na); - for (i = 0; i < n; i++) { - numaGetFValue(na, i, &val); - sum += val; - } - *psum = sum; - return 0; -} - - -/*! - * \brief numaGetPartialSums() - * - * \param[in] na source numa - * \return nasum, or NULL on error - * - *
- * Notes: - * (1) nasum[i] is the sum for all j <= i of na[j]. - * So nasum[0] = na[0]. - * (2) If you want to generate a rank function, where rank[0] - 0.0, - * insert a 0.0 at the beginning of the nasum array. - *- */ -NUMA * -numaGetPartialSums(NUMA *na) -{ -l_int32 i, n; -l_float32 val, sum; -NUMA *nasum; - - PROCNAME("numaGetPartialSums"); - - if (!na) - return (NUMA *)ERROR_PTR("na not defined", procName, NULL); - - n = numaGetCount(na); - nasum = numaCreate(n); - sum = 0.0; - for (i = 0; i < n; i++) { - numaGetFValue(na, i, &val); - sum += val; - numaAddNumber(nasum, sum); - } - return nasum; -} - - -/*! - * \brief numaGetSumOnInterval() - * - * \param[in] na source numa - * \param[in] first beginning index - * \param[in] last final index - * \param[out] psum sum of values in the index interval range - * \return 0 if OK, 1 on error - */ -l_ok -numaGetSumOnInterval(NUMA *na, - l_int32 first, - l_int32 last, - l_float32 *psum) -{ -l_int32 i, n, truelast; -l_float32 val, sum; - - PROCNAME("numaGetSumOnInterval"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - if (!psum) - return ERROR_INT("&sum not defined", procName, 1); - *psum = 0.0; - - sum = 0.0; - n = numaGetCount(na); - if (first >= n) /* not an error */ - return 0; - truelast = L_MIN(last, n - 1); - - for (i = first; i <= truelast; i++) { - numaGetFValue(na, i, &val); - sum += val; - } - *psum = sum; - return 0; -} - - -/*! - * \brief numaHasOnlyIntegers() - * - * \param[in] na source numa - * \param[in] maxsamples maximum number of samples to check - * \param[out] pallints 1 if all sampled values are ints; else 0 - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Set %maxsamples == 0 to check every integer in na. Otherwise, - * this samples no more than %maxsamples. - *- */ -l_ok -numaHasOnlyIntegers(NUMA *na, - l_int32 maxsamples, - l_int32 *pallints) -{ -l_int32 i, n, incr; -l_float32 val; - - PROCNAME("numaHasOnlyIntegers"); - - if (!pallints) - return ERROR_INT("&allints not defined", procName, 1); - *pallints = TRUE; - if (!na) - return ERROR_INT("na not defined", procName, 1); - - if ((n = numaGetCount(na)) == 0) - return ERROR_INT("na empty", procName, 1); - if (maxsamples <= 0) - incr = 1; - else - incr = (l_int32)((n + maxsamples - 1) / maxsamples); - for (i = 0; i < n; i += incr) { - numaGetFValue(na, i, &val); - if (val != (l_int32)val) { - *pallints = FALSE; - return 0; - } - } - - return 0; -} - - -/*! - * \brief numaSubsample() - * - * \param[in] nas - * \param[in] subfactor subsample factor, >= 1 - * \return nad evenly sampled values from nas, or NULL on error - */ -NUMA * -numaSubsample(NUMA *nas, - l_int32 subfactor) -{ -l_int32 i, n; -l_float32 val; -NUMA *nad; - - PROCNAME("numaSubsample"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (subfactor < 1) - return (NUMA *)ERROR_PTR("subfactor < 1", procName, NULL); - - nad = numaCreate(0); - n = numaGetCount(nas); - for (i = 0; i < n; i++) { - if (i % subfactor != 0) continue; - numaGetFValue(nas, i, &val); - numaAddNumber(nad, val); - } - - return nad; -} - - -/*! - * \brief numaMakeDelta() - * - * \param[in] nas input numa - * \return numa of difference values val[i+1] - val[i], - * or NULL on error - */ -NUMA * -numaMakeDelta(NUMA *nas) -{ -l_int32 i, n, prev, cur; -NUMA *nad; - - PROCNAME("numaMakeDelta"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - n = numaGetCount(nas); - nad = numaCreate(n - 1); - prev = 0; - for (i = 1; i < n; i++) { - numaGetIValue(nas, i, &cur); - numaAddNumber(nad, cur - prev); - prev = cur; - } - return nad; -} - - -/*! - * \brief numaMakeSequence() - * - * \param[in] startval - * \param[in] increment - * \param[in] size of sequence - * \return numa of sequence of evenly spaced values, or NULL on error - */ -NUMA * -numaMakeSequence(l_float32 startval, - l_float32 increment, - l_int32 size) -{ -l_int32 i; -l_float32 val; -NUMA *na; - - PROCNAME("numaMakeSequence"); - - if ((na = numaCreate(size)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - - for (i = 0; i < size; i++) { - val = startval + i * increment; - numaAddNumber(na, val); - } - - return na; -} - - -/*! - * \brief numaMakeConstant() - * - * \param[in] val - * \param[in] size of numa - * \return numa of given size with all entries equal to 'val', - * or NULL on error - */ -NUMA * -numaMakeConstant(l_float32 val, - l_int32 size) -{ - return numaMakeSequence(val, 0.0, size); -} - - -/*! - * \brief numaMakeAbsValue() - * - * \param[in] nad can be null for new array, or the same as nas for inplace - * \param[in] nas input numa - * \return nad with all numbers being the absval of the input, - * or NULL on error - */ -NUMA * -numaMakeAbsValue(NUMA *nad, - NUMA *nas) -{ -l_int32 i, n; -l_float32 val; - - PROCNAME("numaMakeAbsValue"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (nad && nad != nas) - return (NUMA *)ERROR_PTR("nad and not in-place", procName, NULL); - - if (!nad) - nad = numaCopy(nas); - n = numaGetCount(nad); - for (i = 0; i < n; i++) { - val = nad->array[i]; - nad->array[i] = L_ABS(val); - } - - return nad; -} - - -/*! - * \brief numaAddBorder() - * - * \param[in] nas - * \param[in] left number of elements to add before the start - * \param[in] right number of elements to add after the end - * \param[in] val initialize border elements - * \return nad with added elements at left and right, or NULL on error - */ -NUMA * -numaAddBorder(NUMA *nas, - l_int32 left, - l_int32 right, - l_float32 val) -{ -l_int32 i, n, len; -l_float32 startx, delx; -l_float32 *fas, *fad; -NUMA *nad; - - PROCNAME("numaAddBorder"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (left < 0) left = 0; - if (right < 0) right = 0; - if (left == 0 && right == 0) - return numaCopy(nas); - - n = numaGetCount(nas); - len = n + left + right; - nad = numaMakeConstant(val, len); - numaGetParameters(nas, &startx, &delx); - numaSetParameters(nad, startx - delx * left, delx); - fas = numaGetFArray(nas, L_NOCOPY); - fad = numaGetFArray(nad, L_NOCOPY); - for (i = 0; i < n; i++) - fad[left + i] = fas[i]; - - return nad; -} - - -/*! - * \brief numaAddSpecifiedBorder() - * - * \param[in] nas - * \param[in] left number of elements to add before the start - * \param[in] right number of elements to add after the end - * \param[in] type L_CONTINUED_BORDER, L_MIRRORED_BORDER - * \return nad with added elements at left and right, or NULL on error - */ -NUMA * -numaAddSpecifiedBorder(NUMA *nas, - l_int32 left, - l_int32 right, - l_int32 type) -{ -l_int32 i, n; -l_float32 *fa; -NUMA *nad; - - PROCNAME("numaAddSpecifiedBorder"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (left < 0) left = 0; - if (right < 0) right = 0; - if (left == 0 && right == 0) - return numaCopy(nas); - if (type != L_CONTINUED_BORDER && type != L_MIRRORED_BORDER) - return (NUMA *)ERROR_PTR("invalid type", procName, NULL); - n = numaGetCount(nas); - if (type == L_MIRRORED_BORDER && (left > n || right > n)) - return (NUMA *)ERROR_PTR("border too large", procName, NULL); - - nad = numaAddBorder(nas, left, right, 0); - n = numaGetCount(nad); - fa = numaGetFArray(nad, L_NOCOPY); - if (type == L_CONTINUED_BORDER) { - for (i = 0; i < left; i++) - fa[i] = fa[left]; - for (i = n - right; i < n; i++) - fa[i] = fa[n - right - 1]; - } else { /* type == L_MIRRORED_BORDER */ - for (i = 0; i < left; i++) - fa[i] = fa[2 * left - 1 - i]; - for (i = 0; i < right; i++) - fa[n - right + i] = fa[n - right - i - 1]; - } - - return nad; -} - - -/*! - * \brief numaRemoveBorder() - * - * \param[in] nas - * \param[in] left number of elements to remove from the start - * \param[in] right number of elements to remove up to the end - * \return nad with removed elements at left and right, or NULL on error - */ -NUMA * -numaRemoveBorder(NUMA *nas, - l_int32 left, - l_int32 right) -{ -l_int32 i, n, len; -l_float32 startx, delx; -l_float32 *fas, *fad; -NUMA *nad; - - PROCNAME("numaRemoveBorder"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (left < 0) left = 0; - if (right < 0) right = 0; - if (left == 0 && right == 0) - return numaCopy(nas); - - n = numaGetCount(nas); - if ((len = n - left - right) < 0) - return (NUMA *)ERROR_PTR("len < 0 after removal", procName, NULL); - nad = numaMakeConstant(0, len); - numaGetParameters(nas, &startx, &delx); - numaSetParameters(nad, startx + delx * left, delx); - fas = numaGetFArray(nas, L_NOCOPY); - fad = numaGetFArray(nad, L_NOCOPY); - for (i = 0; i < len; i++) - fad[i] = fas[left + i]; - - return nad; -} - - -/*! - * \brief numaCountNonzeroRuns() - * - * \param[in] na e.g., of pixel counts in rows or columns - * \param[out] pcount number of nonzero runs - * \return 0 if OK, 1 on error - */ -l_ok -numaCountNonzeroRuns(NUMA *na, - l_int32 *pcount) -{ -l_int32 n, i, val, count, inrun; - - PROCNAME("numaCountNonzeroRuns"); - - if (!pcount) - return ERROR_INT("&count not defined", procName, 1); - *pcount = 0; - if (!na) - return ERROR_INT("na not defined", procName, 1); - n = numaGetCount(na); - count = 0; - inrun = FALSE; - for (i = 0; i < n; i++) { - numaGetIValue(na, i, &val); - if (!inrun && val > 0) { - count++; - inrun = TRUE; - } else if (inrun && val == 0) { - inrun = FALSE; - } - } - *pcount = count; - return 0; -} - - -/*! - * \brief numaGetNonzeroRange() - * - * \param[in] na source numa - * \param[in] eps largest value considered to be zero - * \param[out] pfirst, plast interval of array indices - * where values are nonzero - * \return 0 if OK, 1 on error or if no nonzero range is found. - */ -l_ok -numaGetNonzeroRange(NUMA *na, - l_float32 eps, - l_int32 *pfirst, - l_int32 *plast) -{ -l_int32 n, i, found; -l_float32 val; - - PROCNAME("numaGetNonzeroRange"); - - if (pfirst) *pfirst = 0; - if (plast) *plast = 0; - if (!pfirst || !plast) - return ERROR_INT("pfirst and plast not both defined", procName, 1); - if (!na) - return ERROR_INT("na not defined", procName, 1); - n = numaGetCount(na); - found = FALSE; - for (i = 0; i < n; i++) { - numaGetFValue(na, i, &val); - if (val > eps) { - found = TRUE; - break; - } - } - if (!found) { - *pfirst = n - 1; - *plast = 0; - return 1; - } - - *pfirst = i; - for (i = n - 1; i >= 0; i--) { - numaGetFValue(na, i, &val); - if (val > eps) - break; - } - *plast = i; - return 0; -} - - -/*! - * \brief numaGetCountRelativeToZero() - * - * \param[in] na source numa - * \param[in] type L_LESS_THAN_ZERO, L_EQUAL_TO_ZERO, L_GREATER_THAN_ZERO - * \param[out] pcount count of values of given type - * \return 0 if OK, 1 on error - */ -l_ok -numaGetCountRelativeToZero(NUMA *na, - l_int32 type, - l_int32 *pcount) -{ -l_int32 n, i, count; -l_float32 val; - - PROCNAME("numaGetCountRelativeToZero"); - - if (!pcount) - return ERROR_INT("&count not defined", procName, 1); - *pcount = 0; - if (!na) - return ERROR_INT("na not defined", procName, 1); - n = numaGetCount(na); - for (i = 0, count = 0; i < n; i++) { - numaGetFValue(na, i, &val); - if (type == L_LESS_THAN_ZERO && val < 0.0) - count++; - else if (type == L_EQUAL_TO_ZERO && val == 0.0) - count++; - else if (type == L_GREATER_THAN_ZERO && val > 0.0) - count++; - } - - *pcount = count; - return 0; -} - - -/*! - * \brief numaClipToInterval() - * - * \param[in] nas - * \param[in] first, last clipping interval - * \return numa with the same values as the input, but clipped - * to the specified interval - * - *
- * Notes: - * If you want the indices of the array values to be unchanged, - * use first = 0. - * Usage: - * This is useful to clip a histogram that has a few nonzero - * values to its nonzero range. - *- */ -NUMA * -numaClipToInterval(NUMA *nas, - l_int32 first, - l_int32 last) -{ -l_int32 n, i, truelast; -l_float32 val, startx, delx; -NUMA *nad; - - PROCNAME("numaClipToInterval"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (first > last) - return (NUMA *)ERROR_PTR("range not valid", procName, NULL); - - n = numaGetCount(nas); - if (first >= n) - return (NUMA *)ERROR_PTR("no elements in range", procName, NULL); - truelast = L_MIN(last, n - 1); - if ((nad = numaCreate(truelast - first + 1)) == NULL) - return (NUMA *)ERROR_PTR("nad not made", procName, NULL); - for (i = first; i <= truelast; i++) { - numaGetFValue(nas, i, &val); - numaAddNumber(nad, val); - } - numaGetParameters(nas, &startx, &delx); - numaSetParameters(nad, startx + first * delx, delx); - return nad; -} - - -/*! - * \brief numaMakeThresholdIndicator() - * - * \param[in] nas input numa - * \param[in] thresh threshold value - * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \return nad : indicator array: values are 0 and 1 - * - *
- * Notes: - * (1) For each element in nas, if the constraint given by 'type' - * correctly specifies its relation to thresh, a value of 1 - * is recorded in nad. - *- */ -NUMA * -numaMakeThresholdIndicator(NUMA *nas, - l_float32 thresh, - l_int32 type) -{ -l_int32 n, i, ival; -l_float32 fval; -NUMA *nai; - - PROCNAME("numaMakeThresholdIndicator"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - n = numaGetCount(nas); - nai = numaCreate(n); - for (i = 0; i < n; i++) { - numaGetFValue(nas, i, &fval); - ival = 0; - switch (type) - { - case L_SELECT_IF_LT: - if (fval < thresh) ival = 1; - break; - case L_SELECT_IF_GT: - if (fval > thresh) ival = 1; - break; - case L_SELECT_IF_LTE: - if (fval <= thresh) ival = 1; - break; - case L_SELECT_IF_GTE: - if (fval >= thresh) ival = 1; - break; - default: - numaDestroy(&nai); - return (NUMA *)ERROR_PTR("invalid type", procName, NULL); - } - numaAddNumber(nai, ival); - } - - return nai; -} - - -/*! - * \brief numaUniformSampling() - * - * \param[in] nas input numa - * \param[in] nsamp number of samples - * \return nad : resampled array, or NULL on error - * - *
- * Notes: - * (1) This resamples the values in the array, using %nsamp - * equal divisions. - *- */ -NUMA * -numaUniformSampling(NUMA *nas, - l_int32 nsamp) -{ -l_int32 n, i, j, ileft, iright; -l_float32 left, right, binsize, lfract, rfract, sum, startx, delx; -l_float32 *array; -NUMA *nad; - - PROCNAME("numaUniformSampling"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (nsamp <= 0) - return (NUMA *)ERROR_PTR("nsamp must be > 0", procName, NULL); - - n = numaGetCount(nas); - nad = numaCreate(nsamp); - array = numaGetFArray(nas, L_NOCOPY); - binsize = (l_float32)n / (l_float32)nsamp; - numaGetParameters(nas, &startx, &delx); - numaSetParameters(nad, startx, binsize * delx); - left = 0.0; - for (i = 0; i < nsamp; i++) { - sum = 0.0; - right = left + binsize; - ileft = (l_int32)left; - lfract = 1.0 - left + ileft; - if (lfract >= 1.0) /* on left bin boundary */ - lfract = 0.0; - iright = (l_int32)right; - rfract = right - iright; - iright = L_MIN(iright, n - 1); - if (ileft == iright) { /* both are within the same original sample */ - sum += (lfract + rfract - 1.0) * array[ileft]; - } else { - if (lfract > 0.0001) /* left fraction */ - sum += lfract * array[ileft]; - if (rfract > 0.0001) /* right fraction */ - sum += rfract * array[iright]; - for (j = ileft + 1; j < iright; j++) /* entire pixels */ - sum += array[j]; - } - - numaAddNumber(nad, sum); - left = right; - } - return nad; -} - - -/*! - * \brief numaReverse() - * - * \param[in] nad [optional] can be null or equal to nas - * \param[in] nas input numa - * \return nad : reversed, or NULL on error - * - *
- * Notes: - * (1) Usage: - * numaReverse(nas, nas); // in-place - * nad = numaReverse(NULL, nas); // makes a new one - *- */ -NUMA * -numaReverse(NUMA *nad, - NUMA *nas) -{ -l_int32 n, i; -l_float32 val1, val2; - - PROCNAME("numaReverse"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (nad && nas != nad) - return (NUMA *)ERROR_PTR("nad defined but != nas", procName, NULL); - - n = numaGetCount(nas); - if (nad) { /* in-place */ - for (i = 0; i < n / 2; i++) { - numaGetFValue(nad, i, &val1); - numaGetFValue(nad, n - i - 1, &val2); - numaSetValue(nad, i, val2); - numaSetValue(nad, n - i - 1, val1); - } - } else { - nad = numaCreate(n); - for (i = n - 1; i >= 0; i--) { - numaGetFValue(nas, i, &val1); - numaAddNumber(nad, val1); - } - } - - /* Reverse the startx and delx fields */ - nad->startx = nas->startx + (n - 1) * nas->delx; - nad->delx = -nas->delx; - return nad; -} - - -/*----------------------------------------------------------------------* - * Signal feature extraction * - *----------------------------------------------------------------------*/ -/*! - * \brief numaLowPassIntervals() - * - * \param[in] nas input numa - * \param[in] thresh threshold fraction of max; in [0.0 ... 1.0] - * \param[in] maxn for normalizing; set maxn = 0.0 to use the max in nas - * \return nad : interval abscissa pairs, or NULL on error - * - *
- * Notes: - * (1) For each interval where the value is less than a specified - * fraction of the maximum, this records the left and right "x" - * value. - *- */ -NUMA * -numaLowPassIntervals(NUMA *nas, - l_float32 thresh, - l_float32 maxn) -{ -l_int32 n, i, inrun; -l_float32 maxval, threshval, fval, startx, delx, x0, x1; -NUMA *nad; - - PROCNAME("numaLowPassIntervals"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (thresh < 0.0 || thresh > 1.0) - return (NUMA *)ERROR_PTR("invalid thresh", procName, NULL); - - /* The input threshold is a fraction of the max. - * The first entry in nad is the value of the max. */ - n = numaGetCount(nas); - if (maxn == 0.0) - numaGetMax(nas, &maxval, NULL); - else - maxval = maxn; - numaGetParameters(nas, &startx, &delx); - threshval = thresh * maxval; - nad = numaCreate(0); - numaAddNumber(nad, maxval); - - /* Write pairs of pts (x0, x1) for the intervals */ - inrun = FALSE; - for (i = 0; i < n; i++) { - numaGetFValue(nas, i, &fval); - if (fval < threshval && inrun == FALSE) { /* start a new run */ - inrun = TRUE; - x0 = startx + i * delx; - } else if (fval > threshval && inrun == TRUE) { /* end the run */ - inrun = FALSE; - x1 = startx + i * delx; - numaAddNumber(nad, x0); - numaAddNumber(nad, x1); - } - } - if (inrun == TRUE) { /* must end the last run */ - x1 = startx + (n - 1) * delx; - numaAddNumber(nad, x0); - numaAddNumber(nad, x1); - } - - return nad; -} - - -/*! - * \brief numaThresholdEdges() - * - * \param[in] nas input numa - * \param[in] thresh1 low threshold as fraction of max; in [0.0 ... 1.0] - * \param[in] thresh2 high threshold as fraction of max; in [0.0 ... 1.0] - * \param[in] maxn for normalizing; set maxn = 0.0 to use the max in nas - * \return nad edge interval triplets, or NULL on error - * - *
- * Notes: - * (1) For each edge interval, where where the value is less - * than %thresh1 on one side, greater than %thresh2 on - * the other, and between these thresholds throughout the - * interval, this records a triplet of values: the - * 'left' and 'right' edges, and either +1 or -1, depending - * on whether the edge is rising or falling. - * (2) No assumption is made about the value outside the array, - * so if the value at the array edge is between the threshold - * values, it is not considered part of an edge. We start - * looking for edge intervals only after leaving the thresholded - * band. - *- */ -NUMA * -numaThresholdEdges(NUMA *nas, - l_float32 thresh1, - l_float32 thresh2, - l_float32 maxn) -{ -l_int32 n, i, istart, inband, output, sign; -l_int32 startbelow, below, above, belowlast, abovelast; -l_float32 maxval, threshval1, threshval2, fval, startx, delx, x0, x1; -NUMA *nad; - - PROCNAME("numaThresholdEdges"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (thresh1 < 0.0 || thresh1 > 1.0 || thresh2 < 0.0 || thresh2 > 1.0) - return (NUMA *)ERROR_PTR("invalid thresholds", procName, NULL); - if (thresh2 < thresh1) - return (NUMA *)ERROR_PTR("thresh2 < thresh1", procName, NULL); - - /* The input thresholds are fractions of the max. - * The first entry in nad is the value of the max used - * here for normalization. */ - n = numaGetCount(nas); - if (maxn == 0.0) - numaGetMax(nas, &maxval, NULL); - else - maxval = maxn; - numaGetMax(nas, &maxval, NULL); - numaGetParameters(nas, &startx, &delx); - threshval1 = thresh1 * maxval; - threshval2 = thresh2 * maxval; - nad = numaCreate(0); - numaAddNumber(nad, maxval); - - /* Write triplets of pts (x0, x1, sign) for the edges. - * First make sure we start search from outside the band. - * Only one of {belowlast, abovelast} is true. */ - for (i = 0; i < n; i++) { - istart = i; - numaGetFValue(nas, i, &fval); - belowlast = (fval < threshval1) ? TRUE : FALSE; - abovelast = (fval > threshval2) ? TRUE : FALSE; - if (belowlast == TRUE || abovelast == TRUE) - break; - } - if (istart == n) /* no intervals found */ - return nad; - - /* x0 and x1 can only be set from outside the edge. - * They are the values just before entering the band, - * and just after entering the band. We can jump through - * the band, in which case they differ by one index in nas. */ - inband = FALSE; - startbelow = belowlast; /* one of these is true */ - output = FALSE; - x0 = startx + istart * delx; - for (i = istart + 1; i < n; i++) { - numaGetFValue(nas, i, &fval); - below = (fval < threshval1) ? TRUE : FALSE; - above = (fval > threshval2) ? TRUE : FALSE; - if (!inband && belowlast && above) { /* full jump up */ - x1 = startx + i * delx; - sign = 1; - startbelow = FALSE; /* for the next transition */ - output = TRUE; - } else if (!inband && abovelast && below) { /* full jump down */ - x1 = startx + i * delx; - sign = -1; - startbelow = TRUE; /* for the next transition */ - output = TRUE; - } else if (inband && startbelow && above) { /* exit rising; success */ - x1 = startx + i * delx; - sign = 1; - inband = FALSE; - startbelow = FALSE; /* for the next transition */ - output = TRUE; - } else if (inband && !startbelow && below) { - /* exit falling; success */ - x1 = startx + i * delx; - sign = -1; - inband = FALSE; - startbelow = TRUE; /* for the next transition */ - output = TRUE; - } else if (inband && !startbelow && above) { /* exit rising; failure */ - x0 = startx + i * delx; - inband = FALSE; - } else if (inband && startbelow && below) { /* exit falling; failure */ - x0 = startx + i * delx; - inband = FALSE; - } else if (!inband && !above && !below) { /* enter */ - inband = TRUE; - startbelow = belowlast; - } else if (!inband && (above || below)) { /* outside and remaining */ - x0 = startx + i * delx; /* update position */ - } - belowlast = below; - abovelast = above; - if (output) { /* we have exited; save new x0 */ - numaAddNumber(nad, x0); - numaAddNumber(nad, x1); - numaAddNumber(nad, sign); - output = FALSE; - x0 = startx + i * delx; - } - } - - return nad; -} - - -/*! - * \brief numaGetSpanValues() - * - * \param[in] na numa that is output of numaLowPassIntervals() - * \param[in] span span number, zero-based - * \param[out] pstart [optional] location of start of transition - * \param[out] pend [optional] location of end of transition - * \return 0 if OK, 1 on error - */ -l_int32 -numaGetSpanValues(NUMA *na, - l_int32 span, - l_int32 *pstart, - l_int32 *pend) -{ -l_int32 n, nspans; - - PROCNAME("numaGetSpanValues"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - n = numaGetCount(na); - if (n % 2 != 1) - return ERROR_INT("n is not odd", procName, 1); - nspans = n / 2; - if (nspans < 0 || span >= nspans) - return ERROR_INT("invalid span", procName, 1); - - if (pstart) numaGetIValue(na, 2 * span + 1, pstart); - if (pend) numaGetIValue(na, 2 * span + 2, pend); - return 0; -} - - -/*! - * \brief numaGetEdgeValues() - * - * \param[in] na numa that is output of numaThresholdEdges() - * \param[in] edge edge number, zero-based - * \param[out] pstart [optional] location of start of transition - * \param[out] pend [optional] location of end of transition - * \param[out] psign [optional] transition sign: +1 is rising, - * -1 is falling - * \return 0 if OK, 1 on error - */ -l_int32 -numaGetEdgeValues(NUMA *na, - l_int32 edge, - l_int32 *pstart, - l_int32 *pend, - l_int32 *psign) -{ -l_int32 n, nedges; - - PROCNAME("numaGetEdgeValues"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - n = numaGetCount(na); - if (n % 3 != 1) - return ERROR_INT("n % 3 is not 1", procName, 1); - nedges = (n - 1) / 3; - if (edge < 0 || edge >= nedges) - return ERROR_INT("invalid edge", procName, 1); - - if (pstart) numaGetIValue(na, 3 * edge + 1, pstart); - if (pend) numaGetIValue(na, 3 * edge + 2, pend); - if (psign) numaGetIValue(na, 3 * edge + 3, psign); - return 0; -} - - -/*----------------------------------------------------------------------* - * Interpolation * - *----------------------------------------------------------------------*/ -/*! - * \brief numaInterpolateEqxVal() - * - * \param[in] startx xval corresponding to first element in array - * \param[in] deltax x increment between array elements - * \param[in] nay numa of ordinate values, assumed equally spaced - * \param[in] type L_LINEAR_INTERP, L_QUADRATIC_INTERP - * \param[in] xval - * \param[out] pyval interpolated value - * \return 0 if OK, 1 on error e.g., if xval is outside range - * - *
- * Notes: - * (1) Considering nay as a function of x, the x values - * are equally spaced - * (2) Caller should check for valid return. - * - * For linear Lagrangian interpolation (through 2 data pts): - * y(x) = y1(x-x2)/(x1-x2) + y2(x-x1)/(x2-x1) - * - * For quadratic Lagrangian interpolation (through 3 data pts): - * y(x) = y1(x-x2)(x-x3)/((x1-x2)(x1-x3)) + - * y2(x-x1)(x-x3)/((x2-x1)(x2-x3)) + - * y3(x-x1)(x-x2)/((x3-x1)(x3-x2)) - * - *- */ -l_ok -numaInterpolateEqxVal(l_float32 startx, - l_float32 deltax, - NUMA *nay, - l_int32 type, - l_float32 xval, - l_float32 *pyval) -{ -l_int32 i, n, i1, i2, i3; -l_float32 x1, x2, x3, fy1, fy2, fy3, d1, d2, d3, del, fi, maxx; -l_float32 *fa; - - PROCNAME("numaInterpolateEqxVal"); - - if (!pyval) - return ERROR_INT("&yval not defined", procName, 1); - *pyval = 0.0; - if (!nay) - return ERROR_INT("nay not defined", procName, 1); - if (deltax <= 0.0) - return ERROR_INT("deltax not > 0", procName, 1); - if (type != L_LINEAR_INTERP && type != L_QUADRATIC_INTERP) - return ERROR_INT("invalid interp type", procName, 1); - n = numaGetCount(nay); - if (n < 2) - return ERROR_INT("not enough points", procName, 1); - if (type == L_QUADRATIC_INTERP && n == 2) { - type = L_LINEAR_INTERP; - L_WARNING("only 2 points; using linear interp\n", procName); - } - maxx = startx + deltax * (n - 1); - if (xval < startx || xval > maxx) - return ERROR_INT("xval is out of bounds", procName, 1); - - fa = numaGetFArray(nay, L_NOCOPY); - fi = (xval - startx) / deltax; - i = (l_int32)fi; - del = fi - i; - if (del == 0.0) { /* no interpolation required */ - *pyval = fa[i]; - return 0; - } - - if (type == L_LINEAR_INTERP) { - *pyval = fa[i] + del * (fa[i + 1] - fa[i]); - return 0; - } - - /* Quadratic interpolation */ - d1 = d3 = 0.5 / (deltax * deltax); - d2 = -2. * d1; - if (i == 0) { - i1 = i; - i2 = i + 1; - i3 = i + 2; - } else { - i1 = i - 1; - i2 = i; - i3 = i + 1; - } - x1 = startx + i1 * deltax; - x2 = startx + i2 * deltax; - x3 = startx + i3 * deltax; - fy1 = d1 * fa[i1]; - fy2 = d2 * fa[i2]; - fy3 = d3 * fa[i3]; - *pyval = fy1 * (xval - x2) * (xval - x3) + - fy2 * (xval - x1) * (xval - x3) + - fy3 * (xval - x1) * (xval - x2); - return 0; -} - - -/*! - * \brief numaInterpolateArbxVal() - * - * \param[in] nax numa of abscissa values - * \param[in] nay numa of ordinate values, corresponding to nax - * \param[in] type L_LINEAR_INTERP, L_QUADRATIC_INTERP - * \param[in] xval - * \param[out] pyval interpolated value - * \return 0 if OK, 1 on error e.g., if xval is outside range - * - *
- * Notes: - * (1) The values in nax must be sorted in increasing order. - * If, additionally, they are equally spaced, you can use - * numaInterpolateEqxVal(). - * (2) Caller should check for valid return. - * (3) Uses lagrangian interpolation. See numaInterpolateEqxVal() - * for formulas. - *- */ -l_ok -numaInterpolateArbxVal(NUMA *nax, - NUMA *nay, - l_int32 type, - l_float32 xval, - l_float32 *pyval) -{ -l_int32 i, im, nx, ny, i1, i2, i3; -l_float32 delu, dell, fract, d1, d2, d3; -l_float32 minx, maxx; -l_float32 *fax, *fay; - - PROCNAME("numaInterpolateArbxVal"); - - if (!pyval) - return ERROR_INT("&yval not defined", procName, 1); - *pyval = 0.0; - if (!nax) - return ERROR_INT("nax not defined", procName, 1); - if (!nay) - return ERROR_INT("nay not defined", procName, 1); - if (type != L_LINEAR_INTERP && type != L_QUADRATIC_INTERP) - return ERROR_INT("invalid interp type", procName, 1); - ny = numaGetCount(nay); - nx = numaGetCount(nax); - if (nx != ny) - return ERROR_INT("nax and nay not same size arrays", procName, 1); - if (ny < 2) - return ERROR_INT("not enough points", procName, 1); - if (type == L_QUADRATIC_INTERP && ny == 2) { - type = L_LINEAR_INTERP; - L_WARNING("only 2 points; using linear interp\n", procName); - } - numaGetFValue(nax, 0, &minx); - numaGetFValue(nax, nx - 1, &maxx); - if (xval < minx || xval > maxx) - return ERROR_INT("xval is out of bounds", procName, 1); - - fax = numaGetFArray(nax, L_NOCOPY); - fay = numaGetFArray(nay, L_NOCOPY); - - /* Linear search for interval. We are guaranteed - * to either return or break out of the loop. - * In addition, we are assured that fax[i] - fax[im] > 0.0 */ - if (xval == fax[0]) { - *pyval = fay[0]; - return 0; - } - im = 0; - dell = 0.0; - for (i = 1; i < nx; i++) { - delu = fax[i] - xval; - if (delu >= 0.0) { /* we've passed it */ - if (delu == 0.0) { - *pyval = fay[i]; - return 0; - } - im = i - 1; - dell = xval - fax[im]; /* >= 0 */ - break; - } - } - fract = dell / (fax[i] - fax[im]); - - if (type == L_LINEAR_INTERP) { - *pyval = fay[i] + fract * (fay[i + 1] - fay[i]); - return 0; - } - - /* Quadratic interpolation */ - if (im == 0) { - i1 = im; - i2 = im + 1; - i3 = im + 2; - } else { - i1 = im - 1; - i2 = im; - i3 = im + 1; - } - d1 = (fax[i1] - fax[i2]) * (fax[i1] - fax[i3]); - d2 = (fax[i2] - fax[i1]) * (fax[i2] - fax[i3]); - d3 = (fax[i3] - fax[i1]) * (fax[i3] - fax[i2]); - *pyval = fay[i1] * (xval - fax[i2]) * (xval - fax[i3]) / d1 + - fay[i2] * (xval - fax[i1]) * (xval - fax[i3]) / d2 + - fay[i3] * (xval - fax[i1]) * (xval - fax[i2]) / d3; - return 0; -} - - -/*! - * \brief numaInterpolateEqxInterval() - * - * \param[in] startx xval corresponding to first element in nas - * \param[in] deltax x increment between array elements in nas - * \param[in] nasy numa of ordinate values, assumed equally spaced - * \param[in] type L_LINEAR_INTERP, L_QUADRATIC_INTERP - * \param[in] x0 start value of interval - * \param[in] x1 end value of interval - * \param[in] npts number of points to evaluate function in interval - * \param[out] pnax [optional] array of x values in interval - * \param[out] pnay array of y values in interval - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Considering nasy as a function of x, the x values - * are equally spaced. - * (2) This creates nay (and optionally nax) of interpolated - * values over the specified interval (x0, x1). - * (3) If the interval (x0, x1) lies partially outside the array - * nasy (as interpreted by startx and deltax), it is an - * error and returns 1. - * (4) Note that deltax is the intrinsic x-increment for the input - * array nasy, whereas delx is the intrinsic x-increment for the - * output interpolated array nay. - *- */ -l_ok -numaInterpolateEqxInterval(l_float32 startx, - l_float32 deltax, - NUMA *nasy, - l_int32 type, - l_float32 x0, - l_float32 x1, - l_int32 npts, - NUMA **pnax, - NUMA **pnay) -{ -l_int32 i, n; -l_float32 x, yval, maxx, delx; -NUMA *nax, *nay; - - PROCNAME("numaInterpolateEqxInterval"); - - if (pnax) *pnax = NULL; - if (!pnay) - return ERROR_INT("&nay not defined", procName, 1); - *pnay = NULL; - if (!nasy) - return ERROR_INT("nasy not defined", procName, 1); - if (deltax <= 0.0) - return ERROR_INT("deltax not > 0", procName, 1); - if (type != L_LINEAR_INTERP && type != L_QUADRATIC_INTERP) - return ERROR_INT("invalid interp type", procName, 1); - n = numaGetCount(nasy); - if (type == L_QUADRATIC_INTERP && n == 2) { - type = L_LINEAR_INTERP; - L_WARNING("only 2 points; using linear interp\n", procName); - } - maxx = startx + deltax * (n - 1); - if (x0 < startx || x1 > maxx || x1 <= x0) - return ERROR_INT("[x0 ... x1] is not valid", procName, 1); - if (npts < 3) - return ERROR_INT("npts < 3", procName, 1); - delx = (x1 - x0) / (l_float32)(npts - 1); /* delx is for output nay */ - - if ((nay = numaCreate(npts)) == NULL) - return ERROR_INT("nay not made", procName, 1); - numaSetParameters(nay, x0, delx); - *pnay = nay; - if (pnax) { - nax = numaCreate(npts); - *pnax = nax; - } - - for (i = 0; i < npts; i++) { - x = x0 + i * delx; - if (pnax) - numaAddNumber(nax, x); - numaInterpolateEqxVal(startx, deltax, nasy, type, x, &yval); - numaAddNumber(nay, yval); - } - - return 0; -} - - -/*! - * \brief numaInterpolateArbxInterval() - * - * \param[in] nax numa of abscissa values - * \param[in] nay numa of ordinate values, corresponding to nax - * \param[in] type L_LINEAR_INTERP, L_QUADRATIC_INTERP - * \param[in] x0 start value of interval - * \param[in] x1 end value of interval - * \param[in] npts number of points to evaluate function in interval - * \param[out] pnadx [optional] array of x values in interval - * \param[out] pnady array of y values in interval - * \return 0 if OK, 1 on error e.g., if x0 or x1 is outside range - * - *
- * Notes: - * (1) The values in nax must be sorted in increasing order. - * If they are not sorted, we do it here, and complain. - * (2) If the values in nax are equally spaced, you can use - * numaInterpolateEqxInterval(). - * (3) Caller should check for valid return. - * (4) We don't call numaInterpolateArbxVal() for each output - * point, because that requires an O(n) search for - * each point. Instead, we do a single O(n) pass through - * nax, saving the indices to be used for each output yval. - * (5) Uses lagrangian interpolation. See numaInterpolateEqxVal() - * for formulas. - *- */ -l_ok -numaInterpolateArbxInterval(NUMA *nax, - NUMA *nay, - l_int32 type, - l_float32 x0, - l_float32 x1, - l_int32 npts, - NUMA **pnadx, - NUMA **pnady) -{ -l_int32 i, im, j, nx, ny, i1, i2, i3, sorted; -l_int32 *index; -l_float32 del, xval, yval, excess, fract, minx, maxx, d1, d2, d3; -l_float32 *fax, *fay; -NUMA *nasx, *nasy, *nadx, *nady; - - PROCNAME("numaInterpolateArbxInterval"); - - if (pnadx) *pnadx = NULL; - if (!pnady) - return ERROR_INT("&nady not defined", procName, 1); - *pnady = NULL; - if (!nay) - return ERROR_INT("nay not defined", procName, 1); - if (!nax) - return ERROR_INT("nax not defined", procName, 1); - if (type != L_LINEAR_INTERP && type != L_QUADRATIC_INTERP) - return ERROR_INT("invalid interp type", procName, 1); - if (x0 > x1) - return ERROR_INT("x0 > x1", procName, 1); - ny = numaGetCount(nay); - nx = numaGetCount(nax); - if (nx != ny) - return ERROR_INT("nax and nay not same size arrays", procName, 1); - if (ny < 2) - return ERROR_INT("not enough points", procName, 1); - if (type == L_QUADRATIC_INTERP && ny == 2) { - type = L_LINEAR_INTERP; - L_WARNING("only 2 points; using linear interp\n", procName); - } - numaGetMin(nax, &minx, NULL); - numaGetMax(nax, &maxx, NULL); - if (x0 < minx || x1 > maxx) - return ERROR_INT("xval is out of bounds", procName, 1); - - /* Make sure that nax is sorted in increasing order */ - numaIsSorted(nax, L_SORT_INCREASING, &sorted); - if (!sorted) { - L_WARNING("we are sorting nax in increasing order\n", procName); - numaSortPair(nax, nay, L_SORT_INCREASING, &nasx, &nasy); - } else { - nasx = numaClone(nax); - nasy = numaClone(nay); - } - - fax = numaGetFArray(nasx, L_NOCOPY); - fay = numaGetFArray(nasy, L_NOCOPY); - - /* Get array of indices into fax for interpolated locations */ - if ((index = (l_int32 *)LEPT_CALLOC(npts, sizeof(l_int32))) == NULL) { - numaDestroy(&nasx); - numaDestroy(&nasy); - return ERROR_INT("ind not made", procName, 1); - } - del = (x1 - x0) / (npts - 1.0); - for (i = 0, j = 0; j < nx && i < npts; i++) { - xval = x0 + i * del; - while (j < nx - 1 && xval > fax[j]) - j++; - if (xval == fax[j]) - index[i] = L_MIN(j, nx - 1); - else /* the index of fax[] is just below xval */ - index[i] = L_MAX(j - 1, 0); - } - - /* For each point to be interpolated, get the y value */ - nady = numaCreate(npts); - *pnady = nady; - if (pnadx) { - nadx = numaCreate(npts); - *pnadx = nadx; - } - for (i = 0; i < npts; i++) { - xval = x0 + i * del; - if (pnadx) - numaAddNumber(nadx, xval); - im = index[i]; - excess = xval - fax[im]; - if (excess == 0.0) { - numaAddNumber(nady, fay[im]); - continue; - } - fract = excess / (fax[im + 1] - fax[im]); - - if (type == L_LINEAR_INTERP) { - yval = fay[im] + fract * (fay[im + 1] - fay[im]); - numaAddNumber(nady, yval); - continue; - } - - /* Quadratic interpolation */ - if (im == 0) { - i1 = im; - i2 = im + 1; - i3 = im + 2; - } else { - i1 = im - 1; - i2 = im; - i3 = im + 1; - } - d1 = (fax[i1] - fax[i2]) * (fax[i1] - fax[i3]); - d2 = (fax[i2] - fax[i1]) * (fax[i2] - fax[i3]); - d3 = (fax[i3] - fax[i1]) * (fax[i3] - fax[i2]); - yval = fay[i1] * (xval - fax[i2]) * (xval - fax[i3]) / d1 + - fay[i2] * (xval - fax[i1]) * (xval - fax[i3]) / d2 + - fay[i3] * (xval - fax[i1]) * (xval - fax[i2]) / d3; - numaAddNumber(nady, yval); - } - - LEPT_FREE(index); - numaDestroy(&nasx); - numaDestroy(&nasy); - return 0; -} - - -/*----------------------------------------------------------------------* - * Functions requiring interpolation * - *----------------------------------------------------------------------*/ -/*! - * \brief numaFitMax() - * - * \param[in] na numa of ordinate values, to fit a max to - * \param[out] pmaxval max value - * \param[in] naloc [optional] associated numa of abscissa values - * \param[out] pmaxloc abscissa value that gives max value in na; - * if naloc == null, this is given as an interpolated - * index value - * \return 0 if OK; 1 on error - * - *
- * Notes: - * If %naloc is given, there is no requirement that the - * data points are evenly spaced. Lagrangian interpolation - * handles that. The only requirement is that the - * data points are ordered so that the values in naloc - * are either increasing or decreasing. We test to make - * sure that the sizes of na and naloc are equal, and it - * is assumed that the correspondences %na[i] as a function - * of %naloc[i] are properly arranged for all i. - * - * The formula for Lagrangian interpolation through 3 data pts is: - * y(x) = y1(x-x2)(x-x3)/((x1-x2)(x1-x3)) + - * y2(x-x1)(x-x3)/((x2-x1)(x2-x3)) + - * y3(x-x1)(x-x2)/((x3-x1)(x3-x2)) - * - * Then the derivative, using the constants (c1,c2,c3) defined below, - * is set to 0: - * y'(x) = 2x(c1+c2+c3) - c1(x2+x3) - c2(x1+x3) - c3(x1+x2) = 0 - *- */ -l_ok -numaFitMax(NUMA *na, - l_float32 *pmaxval, - NUMA *naloc, - l_float32 *pmaxloc) -{ -l_float32 val; -l_float32 smaxval; /* start value of maximum sample, before interpolating */ -l_int32 n, imaxloc; -l_float32 x1, x2, x3, y1, y2, y3, c1, c2, c3, a, b, xmax, ymax; - - PROCNAME("numaFitMax"); - - if (pmaxval) *pmaxval = 0.0; - if (pmaxloc) *pmaxloc = 0.0; - if (!na) - return ERROR_INT("na not defined", procName, 1); - if (!pmaxval) - return ERROR_INT("&maxval not defined", procName, 1); - if (!pmaxloc) - return ERROR_INT("&maxloc not defined", procName, 1); - - n = numaGetCount(na); - if (naloc) { - if (n != numaGetCount(naloc)) - return ERROR_INT("na and naloc of unequal size", procName, 1); - } - numaGetMax(na, &smaxval, &imaxloc); - - /* Simple case: max is at end point */ - if (imaxloc == 0 || imaxloc == n - 1) { - *pmaxval = smaxval; - if (naloc) { - numaGetFValue(naloc, imaxloc, &val); - *pmaxloc = val; - } else { - *pmaxloc = imaxloc; - } - return 0; - } - - /* Interior point; use quadratic interpolation */ - y2 = smaxval; - numaGetFValue(na, imaxloc - 1, &val); - y1 = val; - numaGetFValue(na, imaxloc + 1, &val); - y3 = val; - if (naloc) { - numaGetFValue(naloc, imaxloc - 1, &val); - x1 = val; - numaGetFValue(naloc, imaxloc, &val); - x2 = val; - numaGetFValue(naloc, imaxloc + 1, &val); - x3 = val; - } else { - x1 = imaxloc - 1; - x2 = imaxloc; - x3 = imaxloc + 1; - } - - /* Can't interpolate; just use the max val in na - * and the corresponding one in naloc */ - if (x1 == x2 || x1 == x3 || x2 == x3) { - *pmaxval = y2; - *pmaxloc = x2; - return 0; - } - - /* Use lagrangian interpolation; set dy/dx = 0 */ - c1 = y1 / ((x1 - x2) * (x1 - x3)); - c2 = y2 / ((x2 - x1) * (x2 - x3)); - c3 = y3 / ((x3 - x1) * (x3 - x2)); - a = c1 + c2 + c3; - b = c1 * (x2 + x3) + c2 * (x1 + x3) + c3 * (x1 + x2); - xmax = b / (2 * a); - ymax = c1 * (xmax - x2) * (xmax - x3) + - c2 * (xmax - x1) * (xmax - x3) + - c3 * (xmax - x1) * (xmax - x2); - *pmaxval = ymax; - *pmaxloc = xmax; - - return 0; -} - - -/*! - * \brief numaDifferentiateInterval() - * - * \param[in] nax numa of abscissa values - * \param[in] nay numa of ordinate values, corresponding to nax - * \param[in] x0 start value of interval - * \param[in] x1 end value of interval - * \param[in] npts number of points to evaluate function in interval - * \param[out] pnadx [optional] array of x values in interval - * \param[out] pnady array of derivatives in interval - * \return 0 if OK, 1 on error e.g., if x0 or x1 is outside range - * - *
- * Notes: - * (1) The values in nax must be sorted in increasing order. - * If they are not sorted, it is done in the interpolation - * step, and a warning is issued. - * (2) Caller should check for valid return. - *- */ -l_ok -numaDifferentiateInterval(NUMA *nax, - NUMA *nay, - l_float32 x0, - l_float32 x1, - l_int32 npts, - NUMA **pnadx, - NUMA **pnady) -{ -l_int32 i, nx, ny; -l_float32 minx, maxx, der, invdel; -l_float32 *fay; -NUMA *nady, *naiy; - - PROCNAME("numaDifferentiateInterval"); - - if (pnadx) *pnadx = NULL; - if (!pnady) - return ERROR_INT("&nady not defined", procName, 1); - *pnady = NULL; - if (!nay) - return ERROR_INT("nay not defined", procName, 1); - if (!nax) - return ERROR_INT("nax not defined", procName, 1); - if (x0 > x1) - return ERROR_INT("x0 > x1", procName, 1); - ny = numaGetCount(nay); - nx = numaGetCount(nax); - if (nx != ny) - return ERROR_INT("nax and nay not same size arrays", procName, 1); - if (ny < 2) - return ERROR_INT("not enough points", procName, 1); - numaGetMin(nax, &minx, NULL); - numaGetMax(nax, &maxx, NULL); - if (x0 < minx || x1 > maxx) - return ERROR_INT("xval is out of bounds", procName, 1); - if (npts < 2) - return ERROR_INT("npts < 2", procName, 1); - - /* Generate interpolated array over specified interval */ - if (numaInterpolateArbxInterval(nax, nay, L_LINEAR_INTERP, x0, x1, - npts, pnadx, &naiy)) - return ERROR_INT("interpolation failed", procName, 1); - - nady = numaCreate(npts); - *pnady = nady; - invdel = 0.5 * ((l_float32)npts - 1.0) / (x1 - x0); - fay = numaGetFArray(naiy, L_NOCOPY); - - /* Compute and save derivatives */ - der = 0.5 * invdel * (fay[1] - fay[0]); - numaAddNumber(nady, der); - for (i = 1; i < npts - 1; i++) { - der = invdel * (fay[i + 1] - fay[i - 1]); - numaAddNumber(nady, der); - } - der = 0.5 * invdel * (fay[npts - 1] - fay[npts - 2]); - numaAddNumber(nady, der); - - numaDestroy(&naiy); - return 0; -} - - -/*! - * \brief numaIntegrateInterval() - * - * \param[in] nax numa of abscissa values - * \param[in] nay numa of ordinate values, corresponding to nax - * \param[in] x0 start value of interval - * \param[in] x1 end value of interval - * \param[in] npts number of points to evaluate function in interval - * \param[out] psum integral of function over interval - * \return 0 if OK, 1 on error e.g., if x0 or x1 is outside range - * - *
- * Notes: - * (1) The values in nax must be sorted in increasing order. - * If they are not sorted, it is done in the interpolation - * step, and a warning is issued. - * (2) Caller should check for valid return. - *- */ -l_ok -numaIntegrateInterval(NUMA *nax, - NUMA *nay, - l_float32 x0, - l_float32 x1, - l_int32 npts, - l_float32 *psum) -{ -l_int32 i, nx, ny; -l_float32 minx, maxx, sum, del; -l_float32 *fay; -NUMA *naiy; - - PROCNAME("numaIntegrateInterval"); - - if (!psum) - return ERROR_INT("&sum not defined", procName, 1); - *psum = 0.0; - if (!nay) - return ERROR_INT("nay not defined", procName, 1); - if (!nax) - return ERROR_INT("nax not defined", procName, 1); - if (x0 > x1) - return ERROR_INT("x0 > x1", procName, 1); - if (npts < 2) - return ERROR_INT("npts < 2", procName, 1); - ny = numaGetCount(nay); - nx = numaGetCount(nax); - if (nx != ny) - return ERROR_INT("nax and nay not same size arrays", procName, 1); - if (ny < 2) - return ERROR_INT("not enough points", procName, 1); - numaGetMin(nax, &minx, NULL); - numaGetMax(nax, &maxx, NULL); - if (x0 < minx || x1 > maxx) - return ERROR_INT("xval is out of bounds", procName, 1); - - /* Generate interpolated array over specified interval */ - if (numaInterpolateArbxInterval(nax, nay, L_LINEAR_INTERP, x0, x1, - npts, NULL, &naiy)) - return ERROR_INT("interpolation failed", procName, 1); - - del = (x1 - x0) / ((l_float32)npts - 1.0); - fay = numaGetFArray(naiy, L_NOCOPY); - - /* Compute integral (simple trapezoid) */ - sum = 0.5 * (fay[0] + fay[npts - 1]); - for (i = 1; i < npts - 1; i++) - sum += fay[i]; - *psum = del * sum; - - numaDestroy(&naiy); - return 0; -} - - -/*----------------------------------------------------------------------* - * Sorting * - *----------------------------------------------------------------------*/ -/*! - * \brief numaSortGeneral() - * - * \param[in] na source numa - * \param[out] pnasort [optional] sorted numa - * \param[out] pnaindex [optional] index of elements in na associated - * with each element of nasort - * \param[out] pnainvert [optional] index of elements in nasort associated - * with each element of na - * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING - * \param[in] sorttype L_SHELL_SORT or L_BIN_SORT - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Sorting can be confusing. Here's an array of five values with - * the results shown for the 3 output arrays. - * - * na nasort naindex nainvert - * ----------------------------------- - * 3 9 2 3 - * 4 6 3 2 - * 9 4 1 0 - * 6 3 0 1 - * 1 1 4 4 - * - * Note that naindex is a LUT into na for the sorted array values, - * and nainvert directly gives the sorted index values for the - * input array. It is useful to view naindex is as a map: - * 0 --> 2 - * 1 --> 3 - * 2 --> 1 - * 3 --> 0 - * 4 --> 4 - * and nainvert, the inverse of this map: - * 0 --> 3 - * 1 --> 2 - * 2 --> 0 - * 3 --> 1 - * 4 --> 4 - * - * We can write these relations symbolically as: - * nasort[i] = na[naindex[i]] - * na[i] = nasort[nainvert[i]] - *- */ -l_ok -numaSortGeneral(NUMA *na, - NUMA **pnasort, - NUMA **pnaindex, - NUMA **pnainvert, - l_int32 sortorder, - l_int32 sorttype) -{ -NUMA *naindex; - - PROCNAME("numaSortGeneral"); - - if (!na) - return ERROR_INT("na not defined", procName, 1); - if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) - return ERROR_INT("invalid sort order", procName, 1); - if (sorttype != L_SHELL_SORT && sorttype != L_BIN_SORT) - return ERROR_INT("invalid sort type", procName, 1); - if (!pnasort && !pnaindex && !pnainvert) - return ERROR_INT("nothing to do", procName, 1); - if (pnasort) *pnasort = NULL; - if (pnaindex) *pnaindex = NULL; - if (pnainvert) *pnainvert = NULL; - - if (sorttype == L_SHELL_SORT) - naindex = numaGetSortIndex(na, sortorder); - else /* sorttype == L_BIN_SORT */ - naindex = numaGetBinSortIndex(na, sortorder); - - if (pnasort) - *pnasort = numaSortByIndex(na, naindex); - if (pnainvert) - *pnainvert = numaInvertMap(naindex); - if (pnaindex) - *pnaindex = naindex; - else - numaDestroy(&naindex); - return 0; -} - - -/*! - * \brief numaSortAutoSelect() - * - * \param[in] nas input numa - * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING - * \return naout output sorted numa, or NULL on error - * - *
- * Notes: - * (1) This does either a shell sort or a bin sort, depending on - * the number of elements in nas and the dynamic range. - *- */ -NUMA * -numaSortAutoSelect(NUMA *nas, - l_int32 sortorder) -{ -l_int32 type; - - PROCNAME("numaSortAutoSelect"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) - return (NUMA *)ERROR_PTR("invalid sort order", procName, NULL); - - type = numaChooseSortType(nas); - if (type == L_SHELL_SORT) - return numaSort(NULL, nas, sortorder); - else if (type == L_BIN_SORT) - return numaBinSort(nas, sortorder); - else - return (NUMA *)ERROR_PTR("invalid sort type", procName, NULL); -} - - -/*! - * \brief numaSortIndexAutoSelect() - * - * \param[in] nas - * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING - * \return nad indices of nas, sorted by value in nas, or NULL on error - * - *
- * Notes: - * (1) This does either a shell sort or a bin sort, depending on - * the number of elements in nas and the dynamic range. - *- */ -NUMA * -numaSortIndexAutoSelect(NUMA *nas, - l_int32 sortorder) -{ -l_int32 type; - - PROCNAME("numaSortIndexAutoSelect"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) - return (NUMA *)ERROR_PTR("invalid sort order", procName, NULL); - - type = numaChooseSortType(nas); - if (type == L_SHELL_SORT) - return numaGetSortIndex(nas, sortorder); - else if (type == L_BIN_SORT) - return numaGetBinSortIndex(nas, sortorder); - else - return (NUMA *)ERROR_PTR("invalid sort type", procName, NULL); -} - - -/*! - * \brief numaChooseSortType() - * - * \param[in] nas to be sorted - * \return sorttype L_SHELL_SORT or L_BIN_SORT, or UNDEF on error. - * - *
- * Notes: - * (1) This selects either a shell sort or a bin sort, depending on - * the number of elements in nas and the dynamic range. - * (2) If there are negative values in nas, it selects shell sort. - *- */ -l_int32 -numaChooseSortType(NUMA *nas) -{ -l_int32 n, type; -l_float32 minval, maxval; - - PROCNAME("numaChooseSortType"); - - if (!nas) - return ERROR_INT("nas not defined", procName, UNDEF); - - numaGetMin(nas, &minval, NULL); - n = numaGetCount(nas); - - /* Very small histogram; use shell sort */ - if (minval < 0.0 || n < 200) { - L_INFO("Shell sort chosen\n", procName); - return L_SHELL_SORT; - } - - /* Need to compare nlog(n) with maxval. The factor of 0.003 - * was determined by comparing times for different histogram - * sizes and maxval. It is very small because binsort is fast - * and shell sort gets slow for large n. */ - numaGetMax(nas, &maxval, NULL); - if (n * log((l_float32)n) < 0.003 * maxval) { - type = L_SHELL_SORT; - L_INFO("Shell sort chosen\n", procName); - } else { - type = L_BIN_SORT; - L_INFO("Bin sort chosen\n", procName); - } - return type; -} - - -/*! - * \brief numaSort() - * - * \param[in] naout output numa; can be NULL or equal to nain - * \param[in] nain input numa - * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING - * \return naout output sorted numa, or NULL on error - * - *
- * Notes: - * (1) Set naout = nain for in-place; otherwise, set naout = NULL. - * (2) Source: Shell sort, modified from K&R, 2nd edition, p.62. - * Slow but simple O(n logn) sort. - *- */ -NUMA * -numaSort(NUMA *naout, - NUMA *nain, - l_int32 sortorder) -{ -l_int32 i, n, gap, j; -l_float32 tmp; -l_float32 *array; - - PROCNAME("numaSort"); - - if (!nain) - return (NUMA *)ERROR_PTR("nain not defined", procName, NULL); - if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) - return (NUMA *)ERROR_PTR("invalid sort order", procName, NULL); - - /* Make naout if necessary; otherwise do in-place */ - if (!naout) - naout = numaCopy(nain); - else if (nain != naout) - return (NUMA *)ERROR_PTR("invalid: not in-place", procName, NULL); - array = naout->array; /* operate directly on the array */ - n = numaGetCount(naout); - - /* Shell sort */ - for (gap = n/2; gap > 0; gap = gap / 2) { - for (i = gap; i < n; i++) { - for (j = i - gap; j >= 0; j -= gap) { - if ((sortorder == L_SORT_INCREASING && - array[j] > array[j + gap]) || - (sortorder == L_SORT_DECREASING && - array[j] < array[j + gap])) - { - tmp = array[j]; - array[j] = array[j + gap]; - array[j + gap] = tmp; - } - } - } - } - - return naout; -} - - -/*! - * \brief numaBinSort() - * - * \param[in] nas of non-negative integers with a max that is - * typically less than 50,000 - * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING - * \return na sorted, or NULL on error - * - *
- * Notes: - * (1) Because this uses a bin sort with buckets of size 1, it - * is not appropriate for sorting either small arrays or - * arrays containing very large integer values. For such - * arrays, use a standard general sort function like - * numaSort(). - *- */ -NUMA * -numaBinSort(NUMA *nas, - l_int32 sortorder) -{ -NUMA *nat, *nad; - - PROCNAME("numaBinSort"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) - return (NUMA *)ERROR_PTR("invalid sort order", procName, NULL); - - nat = numaGetBinSortIndex(nas, sortorder); - nad = numaSortByIndex(nas, nat); - numaDestroy(&nat); - return nad; -} - - -/*! - * \brief numaGetSortIndex() - * - * \param[in] na source numa - * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING - * \return na giving an array of indices that would sort - * the input array, or NULL on error - */ -NUMA * -numaGetSortIndex(NUMA *na, - l_int32 sortorder) -{ -l_int32 i, n, gap, j; -l_float32 tmp; -l_float32 *array; /* copy of input array */ -l_float32 *iarray; /* array of indices */ -NUMA *naisort; - - PROCNAME("numaGetSortIndex"); - - if (!na) - return (NUMA *)ERROR_PTR("na not defined", procName, NULL); - if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) - return (NUMA *)ERROR_PTR("invalid sortorder", procName, NULL); - - n = numaGetCount(na); - if ((array = numaGetFArray(na, L_COPY)) == NULL) - return (NUMA *)ERROR_PTR("array not made", procName, NULL); - if ((iarray = (l_float32 *)LEPT_CALLOC(n, sizeof(l_float32))) == NULL) { - LEPT_FREE(array); - return (NUMA *)ERROR_PTR("iarray not made", procName, NULL); - } - for (i = 0; i < n; i++) - iarray[i] = i; - - /* Shell sort */ - for (gap = n/2; gap > 0; gap = gap / 2) { - for (i = gap; i < n; i++) { - for (j = i - gap; j >= 0; j -= gap) { - if ((sortorder == L_SORT_INCREASING && - array[j] > array[j + gap]) || - (sortorder == L_SORT_DECREASING && - array[j] < array[j + gap])) - { - tmp = array[j]; - array[j] = array[j + gap]; - array[j + gap] = tmp; - tmp = iarray[j]; - iarray[j] = iarray[j + gap]; - iarray[j + gap] = tmp; - } - } - } - } - - naisort = numaCreate(n); - for (i = 0; i < n; i++) - numaAddNumber(naisort, iarray[i]); - - LEPT_FREE(array); - LEPT_FREE(iarray); - return naisort; -} - - -/*! - * \brief numaGetBinSortIndex() - * - * \param[in] nas of non-negative integers with a max that is - * typically less than 1,000,000 - * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING - * \return na sorted, or NULL on error - * - *
- * Notes: - * (1) This creates an array (or lookup table) that contains - * the sorted position of the elements in the input Numa. - * (2) Because it uses a bin sort with buckets of size 1, it - * is not appropriate for sorting either small arrays or - * arrays containing very large integer values. For such - * arrays, use a standard general sort function like - * numaGetSortIndex(). - *- */ -NUMA * -numaGetBinSortIndex(NUMA *nas, - l_int32 sortorder) -{ -l_int32 i, n, isize, ival, imax; -l_float32 size; -NUMA *na, *nai, *nad; -L_PTRA *paindex; - - PROCNAME("numaGetBinSortIndex"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) - return (NUMA *)ERROR_PTR("invalid sort order", procName, NULL); - - /* Set up a ptra holding numa at indices for which there - * are values in nas. Suppose nas has the value 230 at index - * 7355. A numa holding the index 7355 is created and stored - * at the ptra index 230. If there is another value of 230 - * in nas, its index is added to the same numa (at index 230 - * in the ptra). When finished, the ptra can be scanned for numa, - * and the original indices in the nas can be read out. In this - * way, the ptra effectively sorts the input numbers in the nas. */ - numaGetMax(nas, &size, NULL); - isize = (l_int32)size; - if (isize > 1000000) - L_WARNING("large array: %d elements\n", procName, isize); - paindex = ptraCreate(isize + 1); - n = numaGetCount(nas); - for (i = 0; i < n; i++) { - numaGetIValue(nas, i, &ival); - nai = (NUMA *)ptraGetPtrToItem(paindex, ival); - if (!nai) { /* make it; no shifting will occur */ - nai = numaCreate(1); - ptraInsert(paindex, ival, nai, L_MIN_DOWNSHIFT); - } - numaAddNumber(nai, i); - } - - /* Sort by scanning the ptra, extracting numas and pulling - * the (index into nas) numbers out of each numa, taken - * successively in requested order. */ - ptraGetMaxIndex(paindex, &imax); - nad = numaCreate(0); - if (sortorder == L_SORT_INCREASING) { - for (i = 0; i <= imax; i++) { - na = (NUMA *)ptraRemove(paindex, i, L_NO_COMPACTION); - if (!na) continue; - numaJoin(nad, na, 0, -1); - numaDestroy(&na); - } - } else { /* L_SORT_DECREASING */ - for (i = imax; i >= 0; i--) { - na = (NUMA *)ptraRemoveLast(paindex); - if (!na) break; /* they've all been removed */ - numaJoin(nad, na, 0, -1); - numaDestroy(&na); - } - } - - ptraDestroy(&paindex, FALSE, FALSE); - return nad; -} - - -/*! - * \brief numaSortByIndex() - * - * \param[in] nas - * \param[in] naindex na that maps from the new numa to the input numa - * \return nad sorted, or NULL on error - */ -NUMA * -numaSortByIndex(NUMA *nas, - NUMA *naindex) -{ -l_int32 i, n, index; -l_float32 val; -NUMA *nad; - - PROCNAME("numaSortByIndex"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (!naindex) - return (NUMA *)ERROR_PTR("naindex not defined", procName, NULL); - - n = numaGetCount(nas); - nad = numaCreate(n); - for (i = 0; i < n; i++) { - numaGetIValue(naindex, i, &index); - numaGetFValue(nas, index, &val); - numaAddNumber(nad, val); - } - - return nad; -} - - -/*! - * \brief numaIsSorted() - * - * \param[in] nas - * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING - * \param[out] psorted 1 if sorted; 0 if not - * \return 1 if OK; 0 on error - * - *
- * Notes: - * (1) This is a quick O(n) test if nas is sorted. It is useful - * in situations where the array is likely to be already - * sorted, and a sort operation can be avoided. - *- */ -l_int32 -numaIsSorted(NUMA *nas, - l_int32 sortorder, - l_int32 *psorted) -{ -l_int32 i, n; -l_float32 prevval, val; - - PROCNAME("numaIsSorted"); - - if (!psorted) - return ERROR_INT("&sorted not defined", procName, 1); - *psorted = FALSE; - if (!nas) - return ERROR_INT("nas not defined", procName, 1); - if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) - return ERROR_INT("invalid sortorder", procName, 1); - - n = numaGetCount(nas); - numaGetFValue(nas, 0, &prevval); - for (i = 1; i < n; i++) { - numaGetFValue(nas, i, &val); - if ((sortorder == L_SORT_INCREASING && val < prevval) || - (sortorder == L_SORT_DECREASING && val > prevval)) - return 0; - } - - *psorted = TRUE; - return 0; -} - - -/*! - * \brief numaSortPair() - * - * \param[in] nax, nay input arrays - * \param[in] sortorder L_SORT_INCREASING or L_SORT_DECREASING - * \param[out] pnasx sorted - * \param[out] pnasy sorted exactly in order of nasx - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This function sorts the two input arrays, nax and nay, - * together, using nax as the key for sorting. - *- */ -l_ok -numaSortPair(NUMA *nax, - NUMA *nay, - l_int32 sortorder, - NUMA **pnasx, - NUMA **pnasy) -{ -l_int32 sorted; -NUMA *naindex; - - PROCNAME("numaSortPair"); - - if (pnasx) *pnasx = NULL; - if (pnasy) *pnasy = NULL; - if (!pnasx || !pnasy) - return ERROR_INT("&nasx and/or &nasy not defined", procName, 1); - if (!nax) - return ERROR_INT("nax not defined", procName, 1); - if (!nay) - return ERROR_INT("nay not defined", procName, 1); - if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) - return ERROR_INT("invalid sortorder", procName, 1); - - numaIsSorted(nax, sortorder, &sorted); - if (sorted == TRUE) { - *pnasx = numaCopy(nax); - *pnasy = numaCopy(nay); - } else { - naindex = numaGetSortIndex(nax, sortorder); - *pnasx = numaSortByIndex(nax, naindex); - *pnasy = numaSortByIndex(nay, naindex); - numaDestroy(&naindex); - } - - return 0; -} - - -/*! - * \brief numaInvertMap() - * - * \param[in] nas - * \return nad the inverted map, or NULL on error or if not invertible - * - *
- * Notes: - * (1) This requires that nas contain each integer from 0 to n-1. - * The array is typically an index array into a sort or permutation - * of another array. - *- */ -NUMA * -numaInvertMap(NUMA *nas) -{ -l_int32 i, n, val, error; -l_int32 *test; -NUMA *nad; - - PROCNAME("numaInvertMap"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - - n = numaGetCount(nas); - nad = numaMakeConstant(0.0, n); - test = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32)); - error = 0; - for (i = 0; i < n; i++) { - numaGetIValue(nas, i, &val); - if (val >= n) { - error = 1; - break; - } - numaReplaceNumber(nad, val, i); - if (test[val] == 0) { - test[val] = 1; - } else { - error = 1; - break; - } - } - - LEPT_FREE(test); - if (error) { - numaDestroy(&nad); - return (NUMA *)ERROR_PTR("nas not invertible", procName, NULL); - } - - return nad; -} - - -/*----------------------------------------------------------------------* - * Random permutation * - *----------------------------------------------------------------------*/ -/*! - * \brief numaPseudorandomSequence() - * - * \param[in] size of sequence - * \param[in] seed for random number generation - * \return na pseudorandom on {0,...,size - 1}, or NULL on error - * - *
- * Notes: - * (1) This uses the Durstenfeld shuffle. - * See: http://en.wikipedia.org/wiki/Fisher–Yates_shuffle. - * Result is a pseudorandom permutation of the sequence of integers - * from 0 to size - 1. - *- */ -NUMA * -numaPseudorandomSequence(l_int32 size, - l_int32 seed) -{ -l_int32 i, index, temp; -l_int32 *array; -NUMA *na; - - PROCNAME("numaPseudorandomSequence"); - - if (size <= 0) - return (NUMA *)ERROR_PTR("size <= 0", procName, NULL); - - if ((array = (l_int32 *)LEPT_CALLOC(size, sizeof(l_int32))) == NULL) - return (NUMA *)ERROR_PTR("array not made", procName, NULL); - for (i = 0; i < size; i++) - array[i] = i; - srand(seed); - for (i = size - 1; i > 0; i--) { - index = (l_int32)((i + 1) * ((l_float64)rand() / (l_float64)RAND_MAX)); - index = L_MIN(index, i); - temp = array[i]; - array[i] = array[index]; - array[index] = temp; - } - - na = numaCreateFromIArray(array, size); - LEPT_FREE(array); - return na; -} - - -/*! - * \brief numaRandomPermutation() - * - * \param[in] nas input array - * \param[in] seed for random number generation - * \return nas randomly shuffled array, or NULL on error - */ -NUMA * -numaRandomPermutation(NUMA *nas, - l_int32 seed) -{ -l_int32 i, index, size; -l_float32 val; -NUMA *naindex, *nad; - - PROCNAME("numaRandomPermutation"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - - size = numaGetCount(nas); - naindex = numaPseudorandomSequence(size, seed); - nad = numaCreate(size); - for (i = 0; i < size; i++) { - numaGetIValue(naindex, i, &index); - numaGetFValue(nas, index, &val); - numaAddNumber(nad, val); - } - - numaDestroy(&naindex); - return nad; -} - - -/*----------------------------------------------------------------------* - * Functions requiring sorting * - *----------------------------------------------------------------------*/ -/*! - * \brief numaGetRankValue() - * - * \param[in] na source numa - * \param[in] fract use 0.0 for smallest, 1.0 for largest - * \param[in] nasort [optional] increasing sorted version of na - * \param[in] usebins 0 for general sort; 1 for bin sort - * \param[out] pval rank val - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) Computes the rank value of a number in the %na, which is - * the number that is a fraction %fract from the small - * end of the sorted version of %na. - * (2) If you do this multiple times for different rank values, - * sort the array in advance and use that for %nasort; - * if you're only calling this once, input %nasort == NULL. - * (3) If %usebins == 1, this uses a bin sorting method. - * Use this only where: - * * the numbers are non-negative integers - * * there are over 100 numbers - * * the maximum value is less than about 50,000 - * (4) The advantage of using a bin sort is that it is O(n), - * instead of O(nlogn) for general sort routines. - *- */ -l_ok -numaGetRankValue(NUMA *na, - l_float32 fract, - NUMA *nasort, - l_int32 usebins, - l_float32 *pval) -{ -l_int32 n, index; -NUMA *nas; - - PROCNAME("numaGetRankValue"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0.0; /* init */ - if (!na) - return ERROR_INT("na not defined", procName, 1); - if ((n = numaGetCount(na)) == 0) - return ERROR_INT("na empty", procName, 1); - if (fract < 0.0 || fract > 1.0) - return ERROR_INT("fract not in [0.0 ... 1.0]", procName, 1); - - if (nasort) { - nas = nasort; - } else { - if (usebins == 0) - nas = numaSort(NULL, na, L_SORT_INCREASING); - else - nas = numaBinSort(na, L_SORT_INCREASING); - if (!nas) - return ERROR_INT("nas not made", procName, 1); - } - index = (l_int32)(fract * (l_float32)(n - 1) + 0.5); - numaGetFValue(nas, index, pval); - - if (!nasort) numaDestroy(&nas); - return 0; -} - - -/*! - * \brief numaGetMedian() - * - * \param[in] na source numa - * \param[out] pval median value - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) Computes the median value of the numbers in the numa, by - * sorting and finding the middle value in the sorted array. - *- */ -l_ok -numaGetMedian(NUMA *na, - l_float32 *pval) -{ - PROCNAME("numaGetMedian"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0.0; /* init */ - if (!na || numaGetCount(na) == 0) - return ERROR_INT("na not defined or empty", procName, 1); - - return numaGetRankValue(na, 0.5, NULL, 0, pval); -} - - -/*! - * \brief numaGetBinnedMedian() - * - * \param[in] na source numa - * \param[out] pval integer median value - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) Computes the median value of the numbers in the numa, - * using bin sort and finding the middle value in the sorted array. - * (2) See numaGetRankValue() for conditions on na for which - * this should be used. Otherwise, use numaGetMedian(). - *- */ -l_ok -numaGetBinnedMedian(NUMA *na, - l_int32 *pval) -{ -l_int32 ret; -l_float32 fval; - - PROCNAME("numaGetBinnedMedian"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0; /* init */ - if (!na || numaGetCount(na) == 0) - return ERROR_INT("na not defined or empty", procName, 1); - - ret = numaGetRankValue(na, 0.5, NULL, 1, &fval); - *pval = lept_roundftoi(fval); - return ret; -} - - -/*! - * \brief numaGetMeanDevFromMedian() - * - * \param[in] na source numa - * \param[in] med median value - * \param[out] pdev average absolute value deviation from median value - * \return 0 if OK; 1 on error - */ -l_ok -numaGetMeanDevFromMedian(NUMA *na, - l_float32 med, - l_float32 *pdev) -{ -l_int32 i, n; -l_float32 val, dev; - - PROCNAME("numaGetMeanDevFromMedian"); - - if (!pdev) - return ERROR_INT("&dev not defined", procName, 1); - *pdev = 0.0; /* init */ - if (!na) - return ERROR_INT("na not defined", procName, 1); - if ((n = numaGetCount(na)) == 0) - return ERROR_INT("na is empty", procName, 1); - - dev = 0.0; - for (i = 0; i < n; i++) { - numaGetFValue(na, i, &val); - dev += L_ABS(val - med); - } - *pdev = dev / (l_float32)n; - return 0; -} - - -/*! - * \brief numaGetMedianDevFromMedian() - * - * \param[in] na source numa - * \param[out] pmed [optional] median value - * \param[out] pdev median deviation from median val - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) Finds the median of the absolute value of the deviation from - * the median value in the array. Why take the absolute value? - * Consider the case where you have values equally distributed - * about both sides of a median value. Without taking the absolute - * value of the differences, you will get 0 for the deviation, - * and this is not useful. - *- */ -l_ok -numaGetMedianDevFromMedian(NUMA *na, - l_float32 *pmed, - l_float32 *pdev) -{ -l_int32 n, i; -l_float32 val, med; -NUMA *nadev; - - PROCNAME("numaGetMedianDevFromMedian"); - - if (pmed) *pmed = 0.0; - if (!pdev) - return ERROR_INT("&dev not defined", procName, 1); - *pdev = 0.0; - if (!na || numaGetCount(na) == 0) - return ERROR_INT("na not defined or empty", procName, 1); - - numaGetMedian(na, &med); - if (pmed) *pmed = med; - n = numaGetCount(na); - nadev = numaCreate(n); - for (i = 0; i < n; i++) { - numaGetFValue(na, i, &val); - numaAddNumber(nadev, L_ABS(val - med)); - } - numaGetMedian(nadev, pdev); - - numaDestroy(&nadev); - return 0; -} - - -/*! - * \brief numaGetMode() - * - * \param[in] na source numa - * \param[out] pval mode val - * \param[out] pcount [optional] mode count - * \return 0 if OK; 1 on error - * - *
- * Notes: - * (1) Computes the mode value of the numbers in the numa, by - * sorting and finding the value of the number with the - * largest count. - * (2) Optionally, also returns that count. - *- */ -l_ok -numaGetMode(NUMA *na, - l_float32 *pval, - l_int32 *pcount) -{ -l_int32 i, n, maxcount, prevcount; -l_float32 val, maxval, prevval; -l_float32 *array; -NUMA *nasort; - - PROCNAME("numaGetMode"); - - if (pcount) *pcount = 0; - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0.0; - if (!na) - return ERROR_INT("na not defined", procName, 1); - if ((n = numaGetCount(na)) == 0) - return ERROR_INT("na is empty", procName, 1); - - if ((nasort = numaSort(NULL, na, L_SORT_DECREASING)) == NULL) - return ERROR_INT("nas not made", procName, 1); - array = numaGetFArray(nasort, L_NOCOPY); - - /* Initialize with array[0] */ - prevval = array[0]; - prevcount = 1; - maxval = prevval; - maxcount = prevcount; - - /* Scan the sorted array, aggregating duplicates */ - for (i = 1; i < n; i++) { - val = array[i]; - if (val == prevval) { - prevcount++; - } else { /* new value */ - if (prevcount > maxcount) { /* new max */ - maxcount = prevcount; - maxval = prevval; - } - prevval = val; - prevcount = 1; - } - } - - /* Was the mode the last run of elements? */ - if (prevcount > maxcount) { - maxcount = prevcount; - maxval = prevval; - } - - *pval = maxval; - if (pcount) - *pcount = maxcount; - - numaDestroy(&nasort); - return 0; -} - - -/*----------------------------------------------------------------------* - * Rearrangements * - *----------------------------------------------------------------------*/ -/*! - * \brief numaJoin() - * - * \param[in] nad dest numa; add to this one - * \param[in] nas [optional] source numa; add from this one - * \param[in] istart starting index in nas - * \param[in] iend ending index in nas; use -1 to cat all - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) istart < 0 is taken to mean 'read from the start' (istart = 0) - * (2) iend < 0 means 'read to the end' - * (3) if nas == NULL, this is a no-op - *- */ -l_ok -numaJoin(NUMA *nad, - NUMA *nas, - l_int32 istart, - l_int32 iend) -{ -l_int32 n, i; -l_float32 val; - - PROCNAME("numaJoin"); - - if (!nad) - return ERROR_INT("nad not defined", procName, 1); - if (!nas) - return 0; - - if (istart < 0) - istart = 0; - n = numaGetCount(nas); - if (iend < 0 || iend >= n) - iend = n - 1; - if (istart > iend) - return ERROR_INT("istart > iend; nothing to add", procName, 1); - - for (i = istart; i <= iend; i++) { - numaGetFValue(nas, i, &val); - numaAddNumber(nad, val); - } - - return 0; -} - - -/*! - * \brief numaaJoin() - * - * \param[in] naad dest naa; add to this one - * \param[in] naas [optional] source naa; add from this one - * \param[in] istart starting index in nas - * \param[in] iend ending index in naas; use -1 to cat all - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) istart < 0 is taken to mean 'read from the start' (istart = 0) - * (2) iend < 0 means 'read to the end' - * (3) if naas == NULL, this is a no-op - *- */ -l_ok -numaaJoin(NUMAA *naad, - NUMAA *naas, - l_int32 istart, - l_int32 iend) -{ -l_int32 n, i; -NUMA *na; - - PROCNAME("numaaJoin"); - - if (!naad) - return ERROR_INT("naad not defined", procName, 1); - if (!naas) - return 0; - - if (istart < 0) - istart = 0; - n = numaaGetCount(naas); - if (iend < 0 || iend >= n) - iend = n - 1; - if (istart > iend) - return ERROR_INT("istart > iend; nothing to add", procName, 1); - - for (i = istart; i <= iend; i++) { - na = numaaGetNuma(naas, i, L_CLONE); - numaaAddNuma(naad, na, L_INSERT); - } - - return 0; -} - - -/*! - * \brief numaaFlattenToNuma() - * - * \param[in] naa - * \return numa, or NULL on error - * - *
- * Notes: - * (1) This 'flattens' the Numaa to a Numa, by joining successively - * each Numa in the Numaa. - * (2) It doesn't make any assumptions about the location of the - * Numas in the Numaa array, unlike most Numaa functions. - * (3) It leaves the input Numaa unchanged. - *- */ -NUMA * -numaaFlattenToNuma(NUMAA *naa) -{ -l_int32 i, nalloc; -NUMA *na, *nad; -NUMA **array; - - PROCNAME("numaaFlattenToNuma"); - - if (!naa) - return (NUMA *)ERROR_PTR("naa not defined", procName, NULL); - - nalloc = naa->nalloc; - array = numaaGetPtrArray(naa); - nad = numaCreate(0); - for (i = 0; i < nalloc; i++) { - na = array[i]; - if (!na) continue; - numaJoin(nad, na, 0, -1); - } - - return nad; -} - diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/numafunc2.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/numafunc2.c deleted file mode 100644 index cf84aeff..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/numafunc2.c +++ /dev/null @@ -1,3247 +0,0 @@ -/*====================================================================* - - 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 numafunc2.c - *
- * - * -------------------------------------- - * This file has these Numa utilities: - * - morphological operations - * - arithmetic transforms - * - windowed statistical operations - * - histogram extraction - * - histogram comparison - * - extrema finding - * - frequency and crossing analysis - * -------------------------------------- - - * Morphological (min/max) operations - * NUMA *numaErode() - * NUMA *numaDilate() - * NUMA *numaOpen() - * NUMA *numaClose() - * - * Other transforms - * NUMA *numaTransform() - * l_int32 numaSimpleStats() - * l_int32 numaWindowedStats() - * NUMA *numaWindowedMean() - * NUMA *numaWindowedMeanSquare() - * l_int32 numaWindowedVariance() - * NUMA *numaWindowedMedian() - * NUMA *numaConvertToInt() - * - * Histogram generation and statistics - * NUMA *numaMakeHistogram() - * NUMA *numaMakeHistogramAuto() - * NUMA *numaMakeHistogramClipped() - * NUMA *numaRebinHistogram() - * NUMA *numaNormalizeHistogram() - * l_int32 numaGetStatsUsingHistogram() - * l_int32 numaGetHistogramStats() - * l_int32 numaGetHistogramStatsOnInterval() - * l_int32 numaMakeRankFromHistogram() - * l_int32 numaHistogramGetRankFromVal() - * l_int32 numaHistogramGetValFromRank() - * l_int32 numaDiscretizeRankAndIntensity() - * l_int32 numaGetRankBinValues() - * - * Splitting a distribution - * l_int32 numaSplitDistribution() - * - * Comparing histograms - * l_int32 grayHistogramsToEMD() - * l_int32 numaEarthMoverDistance() - * l_int32 grayInterHistogramStats() - * - * Extrema finding - * NUMA *numaFindPeaks() - * NUMA *numaFindExtrema() - * NUMA *numaFindLocForThreshold() - * l_int32 *numaCountReversals() - * - * Threshold crossings and frequency analysis - * l_int32 numaSelectCrossingThreshold() - * NUMA *numaCrossingsByThreshold() - * NUMA *numaCrossingsByPeaks() - * NUMA *numaEvalBestHaarParameters() - * l_int32 numaEvalHaarSum() - * - * Generating numbers in a range under constraints - * NUMA *genConstrainedNumaInRange() - * - * Things to remember when using the Numa: - * - * (1) The numa is a struct, not an array. Always use accessors - * (see numabasic.c), never the fields directly. - * - * (2) The number array holds l_float32 values. It can also - * be used to store l_int32 values. See numabasic.c for - * details on using the accessors. Integers larger than - * about 10M will lose accuracy due on retrieval due to round-off. - * For large integers, use the dna (array of l_float64) instead. - * - * (3) Occasionally, in the comments we denote the i-th element of a - * numa by na[i]. This is conceptual only -- the numa is not an array! - * - * Some general comments on histograms: - * - * (1) Histograms are the generic statistical representation of - * the data about some attribute. Typically they're not - * normalized -- they simply give the number of occurrences - * within each range of values of the attribute. This range - * of values is referred to as a 'bucket'. For example, - * the histogram could specify how many connected components - * are found for each value of their width; in that case, - * the bucket size is 1. - * - * (2) In leptonica, all buckets have the same size. Histograms - * are therefore specified by a numa of occurrences, along - * with two other numbers: the 'value' associated with the - * occupants of the first bucket and the size (i.e., 'width') - * of each bucket. These two numbers then allow us to calculate - * the value associated with the occupants of each bucket. - * These numbers are fields in the numa, initialized to - * a startx value of 0.0 and a binsize of 1.0. Accessors for - * these fields are functions numa*Parameters(). All histograms - * must have these two numbers properly set. - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) The structuring element (sel) is linear, all "hits" - * (2) If size == 1, this returns a copy - * (3) General comment. The morphological operations are equivalent - * to those that would be performed on a 1-dimensional fpix. - * However, because we have not implemented morphological - * operations on fpix, we do this here. Because it is only - * 1 dimensional, there is no reason to use the more - * complicated van Herk/Gil-Werman algorithm, and we do it - * by brute force. - *- */ -NUMA * -numaErode(NUMA *nas, - l_int32 size) -{ -l_int32 i, j, n, hsize, len; -l_float32 minval; -l_float32 *fa, *fas, *fad; -NUMA *nad; - - PROCNAME("numaErode"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (size <= 0) - return (NUMA *)ERROR_PTR("size must be > 0", procName, NULL); - if ((size & 1) == 0 ) { - L_WARNING("sel size must be odd; increasing by 1\n", procName); - size++; - } - - if (size == 1) - return numaCopy(nas); - - /* Make a source fa (fas) that has an added (size / 2) boundary - * on left and right, contains a copy of nas in the interior region - * (between 'size' and 'size + n', and has large values - * inserted in the boundary (because it is an erosion). */ - n = numaGetCount(nas); - hsize = size / 2; - len = n + 2 * hsize; - if ((fas = (l_float32 *)LEPT_CALLOC(len, sizeof(l_float32))) == NULL) - return (NUMA *)ERROR_PTR("fas not made", procName, NULL); - for (i = 0; i < hsize; i++) - fas[i] = 1.0e37; - for (i = hsize + n; i < len; i++) - fas[i] = 1.0e37; - fa = numaGetFArray(nas, L_NOCOPY); - for (i = 0; i < n; i++) - fas[hsize + i] = fa[i]; - - nad = numaMakeConstant(0, n); - numaCopyParameters(nad, nas); - fad = numaGetFArray(nad, L_NOCOPY); - for (i = 0; i < n; i++) { - minval = 1.0e37; /* start big */ - for (j = 0; j < size; j++) - minval = L_MIN(minval, fas[i + j]); - fad[i] = minval; - } - - LEPT_FREE(fas); - return nad; -} - - -/*! - * \brief numaDilate() - * - * \param[in] nas - * \param[in] size of sel; greater than 0, odd. The origin - * is implicitly in the center. - * \return nad dilated, or NULL on error - * - *
- * Notes: - * (1) The structuring element (sel) is linear, all "hits" - * (2) If size == 1, this returns a copy - *- */ -NUMA * -numaDilate(NUMA *nas, - l_int32 size) -{ -l_int32 i, j, n, hsize, len; -l_float32 maxval; -l_float32 *fa, *fas, *fad; -NUMA *nad; - - PROCNAME("numaDilate"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (size <= 0) - return (NUMA *)ERROR_PTR("size must be > 0", procName, NULL); - if ((size & 1) == 0 ) { - L_WARNING("sel size must be odd; increasing by 1\n", procName); - size++; - } - - if (size == 1) - return numaCopy(nas); - - /* Make a source fa (fas) that has an added (size / 2) boundary - * on left and right, contains a copy of nas in the interior region - * (between 'size' and 'size + n', and has small values - * inserted in the boundary (because it is a dilation). */ - n = numaGetCount(nas); - hsize = size / 2; - len = n + 2 * hsize; - if ((fas = (l_float32 *)LEPT_CALLOC(len, sizeof(l_float32))) == NULL) - return (NUMA *)ERROR_PTR("fas not made", procName, NULL); - for (i = 0; i < hsize; i++) - fas[i] = -1.0e37; - for (i = hsize + n; i < len; i++) - fas[i] = -1.0e37; - fa = numaGetFArray(nas, L_NOCOPY); - for (i = 0; i < n; i++) - fas[hsize + i] = fa[i]; - - nad = numaMakeConstant(0, n); - numaCopyParameters(nad, nas); - fad = numaGetFArray(nad, L_NOCOPY); - for (i = 0; i < n; i++) { - maxval = -1.0e37; /* start small */ - for (j = 0; j < size; j++) - maxval = L_MAX(maxval, fas[i + j]); - fad[i] = maxval; - } - - LEPT_FREE(fas); - return nad; -} - - -/*! - * \brief numaOpen() - * - * \param[in] nas - * \param[in] size of sel; greater than 0, odd. The origin - * is implicitly in the center. - * \return nad opened, or NULL on error - * - *
- * Notes: - * (1) The structuring element (sel) is linear, all "hits" - * (2) If size == 1, this returns a copy - *- */ -NUMA * -numaOpen(NUMA *nas, - l_int32 size) -{ -NUMA *nat, *nad; - - PROCNAME("numaOpen"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (size <= 0) - return (NUMA *)ERROR_PTR("size must be > 0", procName, NULL); - if ((size & 1) == 0 ) { - L_WARNING("sel size must be odd; increasing by 1\n", procName); - size++; - } - - if (size == 1) - return numaCopy(nas); - - nat = numaErode(nas, size); - nad = numaDilate(nat, size); - numaDestroy(&nat); - return nad; -} - - -/*! - * \brief numaClose() - * - * \param[in] nas - * \param[in] size of sel; greater than 0, odd. The origin - * is implicitly in the center. - * \return nad closed, or NULL on error - * - *
- * Notes: - * (1) The structuring element (sel) is linear, all "hits" - * (2) If size == 1, this returns a copy - * (3) We add a border before doing this operation, for the same - * reason that we add a border to a pix before doing a safe closing. - * Without the border, a small component near the border gets - * clipped at the border on dilation, and can be entirely removed - * by the following erosion, violating the basic extensivity - * property of closing. - *- */ -NUMA * -numaClose(NUMA *nas, - l_int32 size) -{ -NUMA *nab, *nat1, *nat2, *nad; - - PROCNAME("numaClose"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (size <= 0) - return (NUMA *)ERROR_PTR("size must be > 0", procName, NULL); - if ((size & 1) == 0 ) { - L_WARNING("sel size must be odd; increasing by 1\n", procName); - size++; - } - - if (size == 1) - return numaCopy(nas); - - nab = numaAddBorder(nas, size, size, 0); /* to preserve extensivity */ - nat1 = numaDilate(nab, size); - nat2 = numaErode(nat1, size); - nad = numaRemoveBorder(nat2, size, size); - numaDestroy(&nab); - numaDestroy(&nat1); - numaDestroy(&nat2); - return nad; -} - - -/*----------------------------------------------------------------------* - * Other transforms * - *----------------------------------------------------------------------*/ -/*! - * \brief numaTransform() - * - * \param[in] nas - * \param[in] shift add this to each number - * \param[in] scale multiply each number by this - * \return nad with all values shifted and scaled, or NULL on error - * - *
- * Notes: - * (1) Each number is shifted before scaling. - *- */ -NUMA * -numaTransform(NUMA *nas, - l_float32 shift, - l_float32 scale) -{ -l_int32 i, n; -l_float32 val; -NUMA *nad; - - PROCNAME("numaTransform"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - n = numaGetCount(nas); - if ((nad = numaCreate(n)) == NULL) - return (NUMA *)ERROR_PTR("nad not made", procName, NULL); - numaCopyParameters(nad, nas); - for (i = 0; i < n; i++) { - numaGetFValue(nas, i, &val); - val = scale * (val + shift); - numaAddNumber(nad, val); - } - return nad; -} - - -/*! - * \brief numaSimpleStats() - * - * \param[in] na input numa - * \param[in] first first element to use - * \param[in] last last element to use; -1 to go to the end - * \param[out] pmean [optional] mean value - * \param[out] pvar [optional] variance - * \param[out] prvar [optional] rms deviation from the mean - * \return 0 if OK, 1 on error - */ -l_ok -numaSimpleStats(NUMA *na, - l_int32 first, - l_int32 last, - l_float32 *pmean, - l_float32 *pvar, - l_float32 *prvar) -{ -l_int32 i, n, ni; -l_float32 sum, sumsq, val, mean, var; - - PROCNAME("numaSimpleStats"); - - if (pmean) *pmean = 0.0; - if (pvar) *pvar = 0.0; - if (prvar) *prvar = 0.0; - if (!pmean && !pvar && !prvar) - return ERROR_INT("nothing requested", procName, 1); - if (!na) - return ERROR_INT("na not defined", procName, 1); - if ((n = numaGetCount(na)) == 0) - return ERROR_INT("na is empty", procName, 1); - first = L_MAX(0, first); - if (last < 0) last = n - 1; - if (first >= n) - return ERROR_INT("invalid first", procName, 1); - if (last >= n) { - L_WARNING("last = %d is beyond max index = %d; adjusting\n", - procName, last, n - 1); - last = n - 1; - } - if (first > last) - return ERROR_INT("first > last\n", procName, 1); - ni = last - first + 1; - sum = sumsq = 0.0; - for (i = first; i <= last; i++) { - numaGetFValue(na, i, &val); - sum += val; - sumsq += val * val; - } - - mean = sum / ni; - if (pmean) - *pmean = mean; - if (pvar || prvar) { - var = sumsq / ni - mean * mean; - if (pvar) *pvar = var; - if (prvar) *prvar = sqrtf(var); - } - - return 0; -} - - -/*! - * \brief numaWindowedStats() - * - * \param[in] nas input numa - * \param[in] wc half width of the window - * \param[out] pnam [optional] mean value in window - * \param[out] pnams [optional] mean square value in window - * \param[out] pnav [optional] variance in window - * \param[out] pnarv [optional] rms deviation from the mean - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is a high-level convenience function for calculating - * any or all of these derived arrays. - * (2) These statistical measures over the values in the - * rectangular window are: - * ~ average value: [x] (nam) - * ~ average squared value: [x*x] (nams) - * ~ variance: [(x - [x])*(x - [x])] = [x*x] - [x]*[x] (nav) - * ~ square-root of variance: (narv) - * where the brackets [ .. ] indicate that the average value is - * to be taken over the window. - * (3) Note that the variance is just the mean square difference from - * the mean value; and the square root of the variance is the - * root mean square difference from the mean, sometimes also - * called the 'standard deviation'. - * (4) Internally, use mirrored borders to handle values near the - * end of each array. - *- */ -l_ok -numaWindowedStats(NUMA *nas, - l_int32 wc, - NUMA **pnam, - NUMA **pnams, - NUMA **pnav, - NUMA **pnarv) -{ -NUMA *nam, *nams; - - PROCNAME("numaWindowedStats"); - - if (!nas) - return ERROR_INT("nas not defined", procName, 1); - if (2 * wc + 1 > numaGetCount(nas)) - L_WARNING("filter wider than input array!\n", procName); - - if (!pnav && !pnarv) { - if (pnam) *pnam = numaWindowedMean(nas, wc); - if (pnams) *pnams = numaWindowedMeanSquare(nas, wc); - return 0; - } - - nam = numaWindowedMean(nas, wc); - nams = numaWindowedMeanSquare(nas, wc); - numaWindowedVariance(nam, nams, pnav, pnarv); - if (pnam) - *pnam = nam; - else - numaDestroy(&nam); - if (pnams) - *pnams = nams; - else - numaDestroy(&nams); - return 0; -} - - -/*! - * \brief numaWindowedMean() - * - * \param[in] nas - * \param[in] wc half width of the convolution window - * \return nad after low-pass filtering, or NULL on error - * - *
- * Notes: - * (1) This is a convolution. The window has width = 2 * %wc + 1. - * (2) We add a mirrored border of size %wc to each end of the array. - *- */ -NUMA * -numaWindowedMean(NUMA *nas, - l_int32 wc) -{ -l_int32 i, n, n1, width; -l_float32 sum, norm; -l_float32 *fa1, *fad, *suma; -NUMA *na1, *nad; - - PROCNAME("numaWindowedMean"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - n = numaGetCount(nas); - width = 2 * wc + 1; /* filter width */ - if (width > n) - L_WARNING("filter wider than input array!\n", procName); - - na1 = numaAddSpecifiedBorder(nas, wc, wc, L_MIRRORED_BORDER); - n1 = n + 2 * wc; - fa1 = numaGetFArray(na1, L_NOCOPY); - nad = numaMakeConstant(0, n); - fad = numaGetFArray(nad, L_NOCOPY); - - /* Make sum array; note the indexing */ - if ((suma = (l_float32 *)LEPT_CALLOC(n1 + 1, sizeof(l_float32))) == NULL) { - numaDestroy(&na1); - numaDestroy(&nad); - return (NUMA *)ERROR_PTR("suma not made", procName, NULL); - } - sum = 0.0; - suma[0] = 0.0; - for (i = 0; i < n1; i++) { - sum += fa1[i]; - suma[i + 1] = sum; - } - - norm = 1. / (2 * wc + 1); - for (i = 0; i < n; i++) - fad[i] = norm * (suma[width + i] - suma[i]); - - LEPT_FREE(suma); - numaDestroy(&na1); - return nad; -} - - -/*! - * \brief numaWindowedMeanSquare() - * - * \param[in] nas - * \param[in] wc half width of the window - * \return nad containing windowed mean square values, or NULL on error - * - *
- * Notes: - * (1) The window has width = 2 * %wc + 1. - * (2) We add a mirrored border of size %wc to each end of the array. - *- */ -NUMA * -numaWindowedMeanSquare(NUMA *nas, - l_int32 wc) -{ -l_int32 i, n, n1, width; -l_float32 sum, norm; -l_float32 *fa1, *fad, *suma; -NUMA *na1, *nad; - - PROCNAME("numaWindowedMeanSquare"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - n = numaGetCount(nas); - width = 2 * wc + 1; /* filter width */ - if (width > n) - L_WARNING("filter wider than input array!\n", procName); - - na1 = numaAddSpecifiedBorder(nas, wc, wc, L_MIRRORED_BORDER); - n1 = n + 2 * wc; - fa1 = numaGetFArray(na1, L_NOCOPY); - nad = numaMakeConstant(0, n); - fad = numaGetFArray(nad, L_NOCOPY); - - /* Make sum array; note the indexing */ - if ((suma = (l_float32 *)LEPT_CALLOC(n1 + 1, sizeof(l_float32))) == NULL) { - numaDestroy(&na1); - numaDestroy(&nad); - return (NUMA *)ERROR_PTR("suma not made", procName, NULL); - } - sum = 0.0; - suma[0] = 0.0; - for (i = 0; i < n1; i++) { - sum += fa1[i] * fa1[i]; - suma[i + 1] = sum; - } - - norm = 1. / (2 * wc + 1); - for (i = 0; i < n; i++) - fad[i] = norm * (suma[width + i] - suma[i]); - - LEPT_FREE(suma); - numaDestroy(&na1); - return nad; -} - - -/*! - * \brief numaWindowedVariance() - * - * \param[in] nam windowed mean values - * \param[in] nams windowed mean square values - * \param[out] pnav [optional] numa of variance -- the ms deviation - * from the mean - * \param[out] pnarv [optional] numa of rms deviation from the mean - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The numas of windowed mean and mean square are precomputed, - * using numaWindowedMean() and numaWindowedMeanSquare(). - * (2) Either or both of the variance and square-root of variance - * are returned, where the variance is the average over the - * window of the mean square difference of the pixel value - * from the mean: - * [(x - [x])*(x - [x])] = [x*x] - [x]*[x] - *- */ -l_ok -numaWindowedVariance(NUMA *nam, - NUMA *nams, - NUMA **pnav, - NUMA **pnarv) -{ -l_int32 i, nm, nms; -l_float32 var; -l_float32 *fam, *fams, *fav, *farv; -NUMA *nav, *narv; /* variance and square root of variance */ - - PROCNAME("numaWindowedVariance"); - - if (pnav) *pnav = NULL; - if (pnarv) *pnarv = NULL; - if (!pnav && !pnarv) - return ERROR_INT("neither &nav nor &narv are defined", procName, 1); - if (!nam) - return ERROR_INT("nam not defined", procName, 1); - if (!nams) - return ERROR_INT("nams not defined", procName, 1); - nm = numaGetCount(nam); - nms = numaGetCount(nams); - if (nm != nms) - return ERROR_INT("sizes of nam and nams differ", procName, 1); - - if (pnav) { - nav = numaMakeConstant(0, nm); - *pnav = nav; - fav = numaGetFArray(nav, L_NOCOPY); - } - if (pnarv) { - narv = numaMakeConstant(0, nm); - *pnarv = narv; - farv = numaGetFArray(narv, L_NOCOPY); - } - fam = numaGetFArray(nam, L_NOCOPY); - fams = numaGetFArray(nams, L_NOCOPY); - - for (i = 0; i < nm; i++) { - var = fams[i] - fam[i] * fam[i]; - if (pnav) - fav[i] = var; - if (pnarv) - farv[i] = sqrtf(var); - } - - return 0; -} - - -/*! - * \brief numaWindowedMedian() - * - * \param[in] nas - * \param[in] halfwin half width of window over which the median is found - * \return nad after windowed median filtering, or NULL on error - * - *
- * Notes: - * (1) The requested window has width = 2 * %halfwin + 1. - * (2) If the input nas has less then 3 elements, return a copy. - * (3) If the filter is too small (%halfwin <= 0), return a copy. - * (4) If the filter is too large, it is reduced in size. - * (5) We add a mirrored border of size %halfwin to each end of - * the array to simplify the calculation by avoiding end-effects. - *- */ -NUMA * -numaWindowedMedian(NUMA *nas, - l_int32 halfwin) -{ -l_int32 i, n; -l_float32 medval; -NUMA *na1, *na2, *nad; - - PROCNAME("numaWindowedMedian"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if ((n = numaGetCount(nas)) < 3) - return numaCopy(nas); - if (halfwin <= 0) { - L_ERROR("filter too small; returning a copy\n", procName); - return numaCopy(nas); - } - - if (halfwin > (n - 1) / 2) { - halfwin = (n - 1) / 2; - L_INFO("reducing filter to halfwin = %d\n", procName, halfwin); - } - - /* Add a border to both ends */ - na1 = numaAddSpecifiedBorder(nas, halfwin, halfwin, L_MIRRORED_BORDER); - - /* Get the median value at the center of each window, corresponding - * to locations in the input nas. */ - nad = numaCreate(n); - for (i = 0; i < n; i++) { - na2 = numaClipToInterval(na1, i, i + 2 * halfwin); - numaGetMedian(na2, &medval); - numaAddNumber(nad, medval); - numaDestroy(&na2); - } - - numaDestroy(&na1); - return nad; -} - - -/*! - * \brief numaConvertToInt() - * - * \param[in] nas source numa - * \return na with all values rounded to nearest integer, or - * NULL on error - */ -NUMA * -numaConvertToInt(NUMA *nas) -{ -l_int32 i, n, ival; -NUMA *nad; - - PROCNAME("numaConvertToInt"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - - n = numaGetCount(nas); - if ((nad = numaCreate(n)) == NULL) - return (NUMA *)ERROR_PTR("nad not made", procName, NULL); - numaCopyParameters(nad, nas); - for (i = 0; i < n; i++) { - numaGetIValue(nas, i, &ival); - numaAddNumber(nad, ival); - } - return nad; -} - - -/*----------------------------------------------------------------------* - * Histogram generation and statistics * - *----------------------------------------------------------------------*/ -/*! - * \brief numaMakeHistogram() - * - * \param[in] na - * \param[in] maxbins max number of histogram bins - * \param[out] pbinsize size of histogram bins - * \param[out] pbinstart [optional] start val of minimum bin; - * input NULL to force start at 0 - * \return na consisiting of histogram of integerized values, - * or NULL on error. - * - *
- * Notes: - * (1) This simple interface is designed for integer data. - * The bins are of integer width and start on integer boundaries, - * so the results on float data will not have high precision. - * (2) Specify the max number of input bins. Then %binsize, - * the size of bins necessary to accommodate the input data, - * is returned. It is one of the sequence: - * {1, 2, 5, 10, 20, 50, ...}. - * (3) If &binstart is given, all values are accommodated, - * and the min value of the starting bin is returned. - * Otherwise, all negative values are discarded and - * the histogram bins start at 0. - *- */ -NUMA * -numaMakeHistogram(NUMA *na, - l_int32 maxbins, - l_int32 *pbinsize, - l_int32 *pbinstart) -{ -l_int32 i, n, ival, hval; -l_int32 iminval, imaxval, range, binsize, nbins, ibin; -l_float32 val, ratio; -NUMA *nai, *nahist; - - PROCNAME("numaMakeHistogram"); - - if (!na) - return (NUMA *)ERROR_PTR("na not defined", procName, NULL); - if (!pbinsize) - return (NUMA *)ERROR_PTR("&binsize not defined", procName, NULL); - - /* Determine input range */ - numaGetMin(na, &val, NULL); - iminval = (l_int32)(val + 0.5); - numaGetMax(na, &val, NULL); - imaxval = (l_int32)(val + 0.5); - if (pbinstart == NULL) { /* clip negative vals; start from 0 */ - iminval = 0; - if (imaxval < 0) - return (NUMA *)ERROR_PTR("all values < 0", procName, NULL); - } - - /* Determine binsize */ - range = imaxval - iminval + 1; - if (range > maxbins - 1) { - ratio = (l_float64)range / (l_float64)maxbins; - binsize = 0; - for (i = 0; i < NBinSizes; i++) { - if (ratio < BinSizeArray[i]) { - binsize = BinSizeArray[i]; - break; - } - } - if (binsize == 0) - return (NUMA *)ERROR_PTR("numbers too large", procName, NULL); - } else { - binsize = 1; - } - *pbinsize = binsize; - nbins = 1 + range / binsize; /* +1 seems to be sufficient */ - - /* Redetermine iminval */ - if (pbinstart && binsize > 1) { - if (iminval >= 0) - iminval = binsize * (iminval / binsize); - else - iminval = binsize * ((iminval - binsize + 1) / binsize); - } - if (pbinstart) - *pbinstart = iminval; - -#if DEBUG_HISTO - lept_stderr(" imaxval = %d, range = %d, nbins = %d\n", - imaxval, range, nbins); -#endif /* DEBUG_HISTO */ - - /* Use integerized data for input */ - if ((nai = numaConvertToInt(na)) == NULL) - return (NUMA *)ERROR_PTR("nai not made", procName, NULL); - n = numaGetCount(nai); - - /* Make histogram, converting value in input array - * into a bin number for this histogram array. */ - if ((nahist = numaCreate(nbins)) == NULL) { - numaDestroy(&nai); - return (NUMA *)ERROR_PTR("nahist not made", procName, NULL); - } - numaSetCount(nahist, nbins); - numaSetParameters(nahist, iminval, binsize); - for (i = 0; i < n; i++) { - numaGetIValue(nai, i, &ival); - ibin = (ival - iminval) / binsize; - if (ibin >= 0 && ibin < nbins) { - numaGetIValue(nahist, ibin, &hval); - numaSetValue(nahist, ibin, hval + 1.0); - } - } - - numaDestroy(&nai); - return nahist; -} - - -/*! - * \brief numaMakeHistogramAuto() - * - * \param[in] na numa of floats; these may be integers - * \param[in] maxbins max number of histogram bins; >= 1 - * \return na consisiting of histogram of quantized float values, - * or NULL on error. - * - *
- * Notes: - * (1) This simple interface is designed for accurate binning - * of both integer and float data. - * (2) If the array data is integers, and the range of integers - * is smaller than %maxbins, they are binned as they fall, - * with binsize = 1. - * (3) If the range of data, (maxval - minval), is larger than - * %maxbins, or if the data is floats, they are binned into - * exactly %maxbins bins. - * (4) Unlike numaMakeHistogram(), these bins in general have - * non-integer location and width, even for integer data. - *- */ -NUMA * -numaMakeHistogramAuto(NUMA *na, - l_int32 maxbins) -{ -l_int32 i, n, imin, imax, irange, ibin, ival, allints; -l_float32 minval, maxval, range, binsize, fval; -NUMA *nah; - - PROCNAME("numaMakeHistogramAuto"); - - if (!na) - return (NUMA *)ERROR_PTR("na not defined", procName, NULL); - maxbins = L_MAX(1, maxbins); - - /* Determine input range */ - numaGetMin(na, &minval, NULL); - numaGetMax(na, &maxval, NULL); - - /* Determine if values are all integers */ - n = numaGetCount(na); - numaHasOnlyIntegers(na, maxbins, &allints); - - /* Do simple integer binning if possible */ - if (allints && (maxval - minval < maxbins)) { - imin = (l_int32)minval; - imax = (l_int32)maxval; - irange = imax - imin + 1; - nah = numaCreate(irange); - numaSetCount(nah, irange); /* init */ - numaSetParameters(nah, minval, 1.0); - for (i = 0; i < n; i++) { - numaGetIValue(na, i, &ival); - ibin = ival - imin; - numaGetIValue(nah, ibin, &ival); - numaSetValue(nah, ibin, ival + 1.0); - } - - return nah; - } - - /* Do float binning, even if the data is integers. */ - range = maxval - minval; - binsize = range / (l_float32)maxbins; - if (range == 0.0) { - nah = numaCreate(1); - numaSetParameters(nah, minval, binsize); - numaAddNumber(nah, n); - return nah; - } - nah = numaCreate(maxbins); - numaSetCount(nah, maxbins); - numaSetParameters(nah, minval, binsize); - for (i = 0; i < n; i++) { - numaGetFValue(na, i, &fval); - ibin = (l_int32)((fval - minval) / binsize); - ibin = L_MIN(ibin, maxbins - 1); /* "edge" case; stay in bounds */ - numaGetIValue(nah, ibin, &ival); - numaSetValue(nah, ibin, ival + 1.0); - } - - return nah; -} - - -/*! - * \brief numaMakeHistogramClipped() - * - * \param[in] na - * \param[in] binsize typically 1.0 - * \param[in] maxsize of histogram ordinate - * \return na histogram of bins of size %binsize, starting with - * the na[0] (x = 0.0 and going up to a maximum of - * x = %maxsize, by increments of %binsize), or NULL on error - * - *
- * Notes: - * (1) This simple function generates a histogram of values - * from na, discarding all values < 0.0 or greater than - * min(%maxsize, maxval), where maxval is the maximum value in na. - * The histogram data is put in bins of size delx = %binsize, - * starting at x = 0.0. We use as many bins as are - * needed to hold the data. - *- */ -NUMA * -numaMakeHistogramClipped(NUMA *na, - l_float32 binsize, - l_float32 maxsize) -{ -l_int32 i, n, nbins, ival, ibin; -l_float32 val, maxval; -NUMA *nad; - - PROCNAME("numaMakeHistogramClipped"); - - if (!na) - return (NUMA *)ERROR_PTR("na not defined", procName, NULL); - if (binsize <= 0.0) - return (NUMA *)ERROR_PTR("binsize must be > 0.0", procName, NULL); - if (binsize > maxsize) - binsize = maxsize; /* just one bin */ - - numaGetMax(na, &maxval, NULL); - n = numaGetCount(na); - maxsize = L_MIN(maxsize, maxval); - nbins = (l_int32)(maxsize / binsize) + 1; - -/* lept_stderr("maxsize = %7.3f, nbins = %d\n", maxsize, nbins); */ - - if ((nad = numaCreate(nbins)) == NULL) - return (NUMA *)ERROR_PTR("nad not made", procName, NULL); - numaSetParameters(nad, 0.0, binsize); - numaSetCount(nad, nbins); /* interpret zeroes in bins as data */ - for (i = 0; i < n; i++) { - numaGetFValue(na, i, &val); - ibin = (l_int32)(val / binsize); - if (ibin >= 0 && ibin < nbins) { - numaGetIValue(nad, ibin, &ival); - numaSetValue(nad, ibin, ival + 1.0); - } - } - - return nad; -} - - -/*! - * \brief numaRebinHistogram() - * - * \param[in] nas input histogram - * \param[in] newsize number of old bins contained in each new bin - * \return nad more coarsely re-binned histogram, or NULL on error - */ -NUMA * -numaRebinHistogram(NUMA *nas, - l_int32 newsize) -{ -l_int32 i, j, ns, nd, index, count, val; -l_float32 start, oldsize; -NUMA *nad; - - PROCNAME("numaRebinHistogram"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (newsize <= 1) - return (NUMA *)ERROR_PTR("newsize must be > 1", procName, NULL); - if ((ns = numaGetCount(nas)) == 0) - return (NUMA *)ERROR_PTR("no bins in nas", procName, NULL); - - nd = (ns + newsize - 1) / newsize; - if ((nad = numaCreate(nd)) == NULL) - return (NUMA *)ERROR_PTR("nad not made", procName, NULL); - numaGetParameters(nad, &start, &oldsize); - numaSetParameters(nad, start, oldsize * newsize); - - for (i = 0; i < nd; i++) { /* new bins */ - count = 0; - index = i * newsize; - for (j = 0; j < newsize; j++) { - if (index < ns) { - numaGetIValue(nas, index, &val); - count += val; - index++; - } - } - numaAddNumber(nad, count); - } - - return nad; -} - - -/*! - * \brief numaNormalizeHistogram() - * - * \param[in] nas input histogram - * \param[in] tsum target sum of all numbers in dest histogram; e.g., use - * %tsum= 1.0 if this represents a probability distribution - * \return nad normalized histogram, or NULL on error - */ -NUMA * -numaNormalizeHistogram(NUMA *nas, - l_float32 tsum) -{ -l_int32 i, ns; -l_float32 sum, factor, fval; -NUMA *nad; - - PROCNAME("numaNormalizeHistogram"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (tsum <= 0.0) - return (NUMA *)ERROR_PTR("tsum must be > 0.0", procName, NULL); - if ((ns = numaGetCount(nas)) == 0) - return (NUMA *)ERROR_PTR("no bins in nas", procName, NULL); - - numaGetSum(nas, &sum); - factor = tsum / sum; - - if ((nad = numaCreate(ns)) == NULL) - return (NUMA *)ERROR_PTR("nad not made", procName, NULL); - numaCopyParameters(nad, nas); - - for (i = 0; i < ns; i++) { - numaGetFValue(nas, i, &fval); - fval *= factor; - numaAddNumber(nad, fval); - } - - return nad; -} - - -/*! - * \brief numaGetStatsUsingHistogram() - * - * \param[in] na an arbitrary set of numbers; not ordered and not - * a histogram - * \param[in] maxbins the maximum number of bins to be allowed in - * the histogram; use an integer larger than the - * largest number in %na for consecutive integer bins - * \param[out] pmin [optional] min value of set - * \param[out] pmax [optional] max value of set - * \param[out] pmean [optional] mean value of set - * \param[out] pvariance [optional] variance - * \param[out] pmedian [optional] median value of set - * \param[in] rank in [0.0 ... 1.0]; median has a rank 0.5; - * ignored if &rval == NULL - * \param[out] prval [optional] value in na corresponding to %rank - * \param[out] phisto [optional] Numa histogram; use NULL to prevent - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is a simple interface for gathering statistics - * from a numa, where a histogram is used 'under the covers' - * to avoid sorting if a rank value is requested. In that case, - * by using a histogram we are trading speed for accuracy, because - * the values in %na are quantized to the center of a set of bins. - * (2) If the median, other rank value, or histogram are not requested, - * the calculation is all performed on the input Numa. - * (3) The variance is the average of the square of the - * difference from the mean. The median is the value in na - * with rank 0.5. - * (4) There are two situations where this gives rank results with - * accuracy comparable to computing stastics directly on the input - * data, without binning into a histogram: - * (a) the data is integers and the range of data is less than - * %maxbins, and - * (b) the data is floats and the range is small compared to - * %maxbins, so that the binsize is much less than 1. - * (5) If a histogram is used and the numbers in the Numa extend - * over a large range, you can limit the required storage by - * specifying the maximum number of bins in the histogram. - * Use %maxbins == 0 to force the bin size to be 1. - * (6) This optionally returns the median and one arbitrary rank value. - * If you need several rank values, return the histogram and use - * numaHistogramGetValFromRank(nah, rank, &rval) - * multiple times. - *- */ -l_ok -numaGetStatsUsingHistogram(NUMA *na, - l_int32 maxbins, - l_float32 *pmin, - l_float32 *pmax, - l_float32 *pmean, - l_float32 *pvariance, - l_float32 *pmedian, - l_float32 rank, - l_float32 *prval, - NUMA **phisto) -{ -l_int32 i, n; -l_float32 minval, maxval, fval, mean, sum; -NUMA *nah; - - PROCNAME("numaGetStatsUsingHistogram"); - - if (pmin) *pmin = 0.0; - if (pmax) *pmax = 0.0; - if (pmean) *pmean = 0.0; - if (pvariance) *pvariance = 0.0; - if (pmedian) *pmedian = 0.0; - if (prval) *prval = 0.0; - if (phisto) *phisto = NULL; - if (!na) - return ERROR_INT("na not defined", procName, 1); - if ((n = numaGetCount(na)) == 0) - return ERROR_INT("numa is empty", procName, 1); - - numaGetMin(na, &minval, NULL); - numaGetMax(na, &maxval, NULL); - if (pmin) *pmin = minval; - if (pmax) *pmax = maxval; - if (pmean || pvariance) { - sum = 0.0; - for (i = 0; i < n; i++) { - numaGetFValue(na, i, &fval); - sum += fval; - } - mean = sum / (l_float32)n; - if (pmean) *pmean = mean; - } - if (pvariance) { - sum = 0.0; - for (i = 0; i < n; i++) { - numaGetFValue(na, i, &fval); - sum += fval * fval; - } - *pvariance = sum / (l_float32)n - mean * mean; - } - - if (!pmedian && !prval && !phisto) - return 0; - - nah = numaMakeHistogramAuto(na, maxbins); - if (pmedian) - numaHistogramGetValFromRank(nah, 0.5, pmedian); - if (prval) - numaHistogramGetValFromRank(nah, rank, prval); - if (phisto) - *phisto = nah; - else - numaDestroy(&nah); - return 0; -} - - -/*! - * \brief numaGetHistogramStats() - * - * \param[in] nahisto histogram: y(x(i)), i = 0 ... nbins - 1 - * \param[in] startx x value of first bin: x(0) - * \param[in] deltax x increment between bins; the bin size; x(1) - x(0) - * \param[out] pxmean [optional] mean value of histogram - * \param[out] pxmedian [optional] median value of histogram - * \param[out] pxmode [optional] mode value of histogram: - * xmode = x(imode), where y(xmode) >= y(x(i)) for - * all i != imode - * \param[out] pxvariance [optional] variance of x - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) If the histogram represents the relation y(x), the - * computed values that are returned are the x values. - * These are NOT the bucket indices i; they are related to the - * bucket indices by - * x(i) = startx + i * deltax - *- */ -l_ok -numaGetHistogramStats(NUMA *nahisto, - l_float32 startx, - l_float32 deltax, - l_float32 *pxmean, - l_float32 *pxmedian, - l_float32 *pxmode, - l_float32 *pxvariance) -{ - PROCNAME("numaGetHistogramStats"); - - if (pxmean) *pxmean = 0.0; - if (pxmedian) *pxmedian = 0.0; - if (pxmode) *pxmode = 0.0; - if (pxvariance) *pxvariance = 0.0; - if (!nahisto) - return ERROR_INT("nahisto not defined", procName, 1); - - return numaGetHistogramStatsOnInterval(nahisto, startx, deltax, 0, -1, - pxmean, pxmedian, pxmode, - pxvariance); -} - - -/*! - * \brief numaGetHistogramStatsOnInterval() - * - * \param[in] nahisto histogram: y(x(i)), i = 0 ... nbins - 1 - * \param[in] startx x value of first bin: x(0) - * \param[in] deltax x increment between bins; the bin size; x(1) - x(0) - * \param[in] ifirst first bin to use for collecting stats - * \param[in] ilast last bin for collecting stats; -1 to go to the end - * \param[out] pxmean [optional] mean value of histogram - * \param[out] pxmedian [optional] median value of histogram - * \param[out] pxmode [optional] mode value of histogram: - * xmode = x(imode), where y(xmode) >= y(x(i)) for - * all i != imode - * \param[out] pxvariance [optional] variance of x - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) If the histogram represents the relation y(x), the - * computed values that are returned are the x values. - * These are NOT the bucket indices i; they are related to the - * bucket indices by - * x(i) = startx + i * deltax - *- */ -l_ok -numaGetHistogramStatsOnInterval(NUMA *nahisto, - l_float32 startx, - l_float32 deltax, - l_int32 ifirst, - l_int32 ilast, - l_float32 *pxmean, - l_float32 *pxmedian, - l_float32 *pxmode, - l_float32 *pxvariance) -{ -l_int32 i, n, imax; -l_float32 sum, sumval, halfsum, moment, var, x, y, ymax; - - PROCNAME("numaGetHistogramStatsOnInterval"); - - if (pxmean) *pxmean = 0.0; - if (pxmedian) *pxmedian = 0.0; - if (pxmode) *pxmode = 0.0; - if (pxvariance) *pxvariance = 0.0; - if (!nahisto) - return ERROR_INT("nahisto not defined", procName, 1); - if (!pxmean && !pxmedian && !pxmode && !pxvariance) - return ERROR_INT("nothing to compute", procName, 1); - - n = numaGetCount(nahisto); - ifirst = L_MAX(0, ifirst); - if (ilast < 0) ilast = n - 1; - if (ifirst >= n) - return ERROR_INT("invalid ifirst", procName, 1); - if (ilast >= n) { - L_WARNING("ilast = %d is beyond max index = %d; adjusting\n", - procName, ilast, n - 1); - ilast = n - 1; - } - if (ifirst > ilast) - return ERROR_INT("ifirst > ilast", procName, 1); - for (sum = 0.0, moment = 0.0, var = 0.0, i = ifirst; i <= ilast ; i++) { - x = startx + i * deltax; - numaGetFValue(nahisto, i, &y); - sum += y; - moment += x * y; - var += x * x * y; - } - if (sum == 0.0) { - L_INFO("sum is 0\n", procName); - return 0; - } - - if (pxmean) - *pxmean = moment / sum; - if (pxvariance) - *pxvariance = var / sum - moment * moment / (sum * sum); - - if (pxmedian) { - halfsum = sum / 2.0; - for (sumval = 0.0, i = ifirst; i <= ilast; i++) { - numaGetFValue(nahisto, i, &y); - sumval += y; - if (sumval >= halfsum) { - *pxmedian = startx + i * deltax; - break; - } - } - } - - if (pxmode) { - imax = -1; - ymax = -1.0e10; - for (i = ifirst; i <= ilast; i++) { - numaGetFValue(nahisto, i, &y); - if (y > ymax) { - ymax = y; - imax = i; - } - } - *pxmode = startx + imax * deltax; - } - - return 0; -} - - -/*! - * \brief numaMakeRankFromHistogram() - * - * \param[in] startx xval corresponding to first element in nay - * \param[in] deltax x increment between array elements in nay - * \param[in] nasy input histogram, assumed equally spaced - * \param[in] npts number of points to evaluate rank function - * \param[out] pnax [optional] array of x values in range - * \param[out] pnay rank array of specified npts - * \return 0 if OK, 1 on error - */ -l_ok -numaMakeRankFromHistogram(l_float32 startx, - l_float32 deltax, - NUMA *nasy, - l_int32 npts, - NUMA **pnax, - NUMA **pnay) -{ -l_int32 i, n; -l_float32 sum, fval; -NUMA *nan, *nar; - - PROCNAME("numaMakeRankFromHistogram"); - - if (pnax) *pnax = NULL; - if (!pnay) - return ERROR_INT("&nay not defined", procName, 1); - *pnay = NULL; - if (!nasy) - return ERROR_INT("nasy not defined", procName, 1); - if ((n = numaGetCount(nasy)) == 0) - return ERROR_INT("no bins in nas", procName, 1); - - /* Normalize and generate the rank array corresponding to - * the binned histogram. */ - nan = numaNormalizeHistogram(nasy, 1.0); - nar = numaCreate(n + 1); /* rank numa corresponding to nan */ - sum = 0.0; - numaAddNumber(nar, sum); /* first element is 0.0 */ - for (i = 0; i < n; i++) { - numaGetFValue(nan, i, &fval); - sum += fval; - numaAddNumber(nar, sum); - } - - /* Compute rank array on full range with specified - * number of points and correspondence to x-values. */ - numaInterpolateEqxInterval(startx, deltax, nar, L_LINEAR_INTERP, - startx, startx + n * deltax, npts, - pnax, pnay); - numaDestroy(&nan); - numaDestroy(&nar); - return 0; -} - - -/*! - * \brief numaHistogramGetRankFromVal() - * - * \param[in] na histogram - * \param[in] rval value of input sample for which we want the rank - * \param[out] prank fraction of total samples below rval - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) If we think of the histogram as a function y(x), normalized - * to 1, for a given input value of x, this computes the - * rank of x, which is the integral of y(x) from the start - * value of x to the input value. - * (2) This function only makes sense when applied to a Numa that - * is a histogram. The values in the histogram can be ints and - * floats, and are computed as floats. The rank is returned - * as a float between 0.0 and 1.0. - * (3) The numa parameters startx and binsize are used to - * compute x from the Numa index i. - *- */ -l_ok -numaHistogramGetRankFromVal(NUMA *na, - l_float32 rval, - l_float32 *prank) -{ -l_int32 i, ibinval, n; -l_float32 startval, binsize, binval, maxval, fractval, total, sum, val; - - PROCNAME("numaHistogramGetRankFromVal"); - - if (!prank) - return ERROR_INT("prank not defined", procName, 1); - *prank = 0.0; - if (!na) - return ERROR_INT("na not defined", procName, 1); - numaGetParameters(na, &startval, &binsize); - n = numaGetCount(na); - if (rval < startval) - return 0; - maxval = startval + n * binsize; - if (rval > maxval) { - *prank = 1.0; - return 0; - } - - binval = (rval - startval) / binsize; - ibinval = (l_int32)binval; - if (ibinval >= n) { - *prank = 1.0; - return 0; - } - fractval = binval - (l_float32)ibinval; - - sum = 0.0; - for (i = 0; i < ibinval; i++) { - numaGetFValue(na, i, &val); - sum += val; - } - numaGetFValue(na, ibinval, &val); - sum += fractval * val; - numaGetSum(na, &total); - *prank = sum / total; - -/* lept_stderr("binval = %7.3f, rank = %7.3f\n", binval, *prank); */ - - return 0; -} - - -/*! - * \brief numaHistogramGetValFromRank() - * - * \param[in] na histogram - * \param[in] rank fraction of total samples - * \param[out] prval approx. to the bin value - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) If we think of the histogram as a function y(x), this returns - * the value x such that the integral of y(x) from the start - * value to x gives the fraction 'rank' of the integral - * of y(x) over all bins. - * (2) This function only makes sense when applied to a Numa that - * is a histogram. The values in the histogram can be ints and - * floats, and are computed as floats. The val is returned - * as a float, even though the buckets are of integer width. - * (3) The numa parameters startx and binsize are used to - * compute x from the Numa index i. - *- */ -l_ok -numaHistogramGetValFromRank(NUMA *na, - l_float32 rank, - l_float32 *prval) -{ -l_int32 i, n; -l_float32 startval, binsize, rankcount, total, sum, fract, val; - - PROCNAME("numaHistogramGetValFromRank"); - - if (!prval) - return ERROR_INT("prval not defined", procName, 1); - *prval = 0.0; - if (!na) - return ERROR_INT("na not defined", procName, 1); - if (rank < 0.0) { - L_WARNING("rank < 0; setting to 0.0\n", procName); - rank = 0.0; - } - if (rank > 1.0) { - L_WARNING("rank > 1.0; setting to 1.0\n", procName); - rank = 1.0; - } - - n = numaGetCount(na); - numaGetParameters(na, &startval, &binsize); - numaGetSum(na, &total); - rankcount = rank * total; /* count that corresponds to rank */ - sum = 0.0; - for (i = 0; i < n; i++) { - numaGetFValue(na, i, &val); - if (sum + val >= rankcount) - break; - sum += val; - } - if (val <= 0.0) /* can == 0 if rank == 0.0 */ - fract = 0.0; - else /* sum + fract * val = rankcount */ - fract = (rankcount - sum) / val; - - /* The use of the fraction of a bin allows a simple calculation - * for the histogram value at the given rank. */ - *prval = startval + binsize * ((l_float32)i + fract); - -/* lept_stderr("rank = %7.3f, val = %7.3f\n", rank, *prval); */ - - return 0; -} - - -/*! - * \brief numaDiscretizeRankAndIntensity() - * - * \param[in] na normalized histo of probability density vs intensity - * \param[in] nbins number of bins at which the rank is divided - * \param[out] pnarbin [optional] rank bin value vs intensity - * \param[out] pnam [optional] median intensity in a bin vs rank bin - * value, with %nbins of discretized rank values - * \param[out] pnar [optional] rank vs intensity; this is - * a cumulative norm histogram - * \param[out] pnabb [optional] intensity at the right bin boundary - * vs rank bin - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) We are inverting the rank(intensity) function to get - * the intensity(rank) function at %nbins equally spaced - * values of rank between 0.0 and 1.0. We save integer values - * for the intensity. - * (2) We are using the word "intensity" to describe the type of - * array values, but any array of non-negative numbers will work. - * (3) The output arrays give the following mappings, where the - * input is a normalized histogram of array values: - * array values --> rank bin number (narbin) - * rank bin number --> median array value in bin (nam) - * array values --> cumulative norm = rank (nar) - * rank bin number --> array value at right bin edge (nabb) - *- */ -l_ok -numaDiscretizeRankAndIntensity(NUMA *na, - l_int32 nbins, - NUMA **pnarbin, - NUMA **pnam, - NUMA **pnar, - NUMA **pnabb) -{ -NUMA *nar; /* rank value as function of intensity */ -NUMA *nam; /* median intensity in the rank bins */ -NUMA *nabb; /* rank bin right boundaries (in intensity) */ -NUMA *narbin; /* binned rank value as a function of intensity */ -l_int32 i, j, npts, start, midfound, mcount, rightedge; -l_float32 sum, midrank, endrank, val; - - PROCNAME("numaDiscretizeRankAndIntensity"); - - if (pnarbin) *pnarbin = NULL; - if (pnam) *pnam = NULL; - if (pnar) *pnar = NULL; - if (pnabb) *pnabb = NULL; - if (!pnarbin && !pnam && !pnar && !pnabb) - return ERROR_INT("no output requested", procName, 1); - if (!na) - return ERROR_INT("na not defined", procName, 1); - if (nbins < 2) - return ERROR_INT("nbins must be > 1", procName, 1); - - /* Get cumulative normalized histogram (rank vs intensity value). - * For a normalized histogram from an 8 bpp grayscale image - * as input, we have 256 bins and 257 points in the - * cumulative (rank) histogram. */ - npts = numaGetCount(na); - if ((nar = numaCreate(npts + 1)) == NULL) - return ERROR_INT("nar not made", procName, 1); - sum = 0.0; - numaAddNumber(nar, sum); /* left side of first bin */ - for (i = 0; i < npts; i++) { - numaGetFValue(na, i, &val); - sum += val; - numaAddNumber(nar, sum); - } - - nam = numaCreate(nbins); - narbin = numaCreate(npts); - nabb = numaCreate(nbins); - if (!nam || !narbin || !nabb) { - numaDestroy(&nar); - numaDestroy(&nam); - numaDestroy(&narbin); - numaDestroy(&nabb); - return ERROR_INT("numa not made", procName, 1); - } - - /* We find the intensity value at the right edge of each of - * the rank bins. We also find the median intensity in the bin, - * where approximately half the samples are lower and half are - * higher. This can be considered as a simple approximation - * for the average intensity in the bin. */ - start = 0; /* index in nar */ - mcount = 0; /* count of median values in rank bins; not to exceed nbins */ - for (i = 0; i < nbins; i++) { - midrank = (l_float32)(i + 0.5) / (l_float32)(nbins); - endrank = (l_float32)(i + 1.0) / (l_float32)(nbins); - endrank = L_MAX(0.0, L_MIN(endrank - 0.001, 1.0)); - midfound = FALSE; - for (j = start; j < npts; j++) { /* scan up for each bin value */ - numaGetFValue(nar, j, &val); - /* Use (j == npts - 1) tests in case all weight is at top end */ - if ((!midfound && val >= midrank) || - (mcount < nbins && j == npts - 1)) { - midfound = TRUE; - numaAddNumber(nam, j); - mcount++; - } - if ((val >= endrank) || (j == npts - 1)) { - numaAddNumber(nabb, j); - if (val == endrank) - start = j; - else - start = j - 1; - break; - } - } - } - numaSetValue(nabb, nbins - 1, npts - 1); /* extend to max */ - - /* Error checking: did we get data in all bins? */ - if (mcount != nbins) - L_WARNING("found data for %d bins; should be %d\n", - procName, mcount, nbins); - - /* Generate LUT that maps from intensity to bin number */ - start = 0; - for (i = 0; i < nbins; i++) { - numaGetIValue(nabb, i, &rightedge); - for (j = start; j < npts; j++) { - if (j <= rightedge) - numaAddNumber(narbin, i); - if (j > rightedge) { - start = j; - break; - } - if (j == npts - 1) { /* we're done */ - start = j + 1; - break; - } - } - } - - if (pnarbin) - *pnarbin = narbin; - else - numaDestroy(&narbin); - if (pnam) - *pnam = nam; - else - numaDestroy(&nam); - if (pnar) - *pnar = nar; - else - numaDestroy(&nar); - if (pnabb) - *pnabb = nabb; - else - numaDestroy(&nabb); - return 0; -} - - -/*! - * \brief numaGetRankBinValues() - * - * \param[in] na an array of values - * \param[in] nbins number of bins at which the rank is divided - * \param[out] pnarbin [optional] rank bin value vs array value - * \param[out] pnam [optional] median intensity in a bin vs rank bin - * value, with %nbins of discretized rank values - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Simple interface for getting a binned rank representation - * of an input array of values. This returns two mappings: - * array value --> rank bin number (narbin) - * rank bin number --> median array value in each rank bin (nam) - *- */ -l_ok -numaGetRankBinValues(NUMA *na, - l_int32 nbins, - NUMA **pnarbin, - NUMA **pnam) -{ -NUMA *nah, *nan; /* histo and normalized histo */ -l_int32 maxbins, discardval; -l_float32 maxval, delx; - - PROCNAME("numaGetRankBinValues"); - - if (pnarbin) *pnarbin = NULL; - if (pnam) *pnam = NULL; - if (!pnarbin && !pnam) - return ERROR_INT("no output requested", procName, 1); - if (!na) - return ERROR_INT("na not defined", procName, 1); - if (numaGetCount(na) == 0) - return ERROR_INT("na is empty", procName, 1); - if (nbins < 2) - return ERROR_INT("nbins must be > 1", procName, 1); - - /* Get normalized histogram */ - numaGetMax(na, &maxval, NULL); - maxbins = L_MIN(100002, (l_int32)maxval + 2); - nah = numaMakeHistogram(na, maxbins, &discardval, NULL); - nan = numaNormalizeHistogram(nah, 1.0); - - /* Warn if there is a scale change. This shouldn't happen - * unless the max value is above 100000. */ - numaGetParameters(nan, NULL, &delx); - if (delx > 1.0) - L_WARNING("scale change: delx = %6.2f\n", procName, delx); - - /* Rank bin the results */ - numaDiscretizeRankAndIntensity(nan, nbins, pnarbin, pnam, NULL, NULL); - numaDestroy(&nah); - numaDestroy(&nan); - return 0; -} - - -/*----------------------------------------------------------------------* - * Splitting a distribution * - *----------------------------------------------------------------------*/ -/*! - * \brief numaSplitDistribution() - * - * \param[in] na histogram - * \param[in] scorefract fraction of the max score, used to determine - * range over which the histogram min is searched - * \param[out] psplitindex [optional] index for splitting - * \param[out] pave1 [optional] average of lower distribution - * \param[out] pave2 [optional] average of upper distribution - * \param[out] pnum1 [optional] population of lower distribution - * \param[out] pnum2 [optional] population of upper distribution - * \param[out] pnascore [optional] for debugging; otherwise use NULL - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This function is intended to be used on a distribution of - * values that represent two sets, such as a histogram of - * pixel values for an image with a fg and bg, and the goal - * is to determine the averages of the two sets and the - * best splitting point. - * (2) The Otsu method finds a split point that divides the distribution - * into two parts by maximizing a score function that is the - * product of two terms: - * (a) the square of the difference of centroids, (ave1 - ave2)^2 - * (b) fract1 * (1 - fract1) - * where fract1 is the fraction in the lower distribution. - * (3) This works well for images where the fg and bg are - * each relatively homogeneous and well-separated in color. - * However, if the actual fg and bg sets are very different - * in size, and the bg is highly varied, as can occur in some - * scanned document images, this will bias the split point - * into the larger "bump" (i.e., toward the point where the - * (b) term reaches its maximum of 0.25 at fract1 = 0.5. - * To avoid this, we define a range of values near the - * maximum of the score function, and choose the value within - * this range such that the histogram itself has a minimum value. - * The range is determined by scorefract: we include all abscissa - * values to the left and right of the value that maximizes the - * score, such that the score stays above (1 - scorefract) * maxscore. - * The intuition behind this modification is to try to find - * a split point that both has a high variance score and is - * at or near a minimum in the histogram, so that the histogram - * slope is small at the split point. - * (4) We normalize the score so that if the two distributions - * were of equal size and at opposite ends of the numa, the - * score would be 1.0. - *- */ -l_ok -numaSplitDistribution(NUMA *na, - l_float32 scorefract, - l_int32 *psplitindex, - l_float32 *pave1, - l_float32 *pave2, - l_float32 *pnum1, - l_float32 *pnum2, - NUMA **pnascore) -{ -l_int32 i, n, bestsplit, minrange, maxrange, maxindex; -l_float32 ave1, ave2, ave1prev, ave2prev; -l_float32 num1, num2, num1prev, num2prev; -l_float32 val, minval, sum, fract1; -l_float32 norm, score, minscore, maxscore; -NUMA *nascore, *naave1, *naave2, *nanum1, *nanum2; - - PROCNAME("numaSplitDistribution"); - - if (psplitindex) *psplitindex = 0; - if (pave1) *pave1 = 0.0; - if (pave2) *pave2 = 0.0; - if (pnum1) *pnum1 = 0.0; - if (pnum2) *pnum2 = 0.0; - if (pnascore) *pnascore = NULL; - if (!na) - return ERROR_INT("na not defined", procName, 1); - - n = numaGetCount(na); - if (n <= 1) - return ERROR_INT("n = 1 in histogram", procName, 1); - numaGetSum(na, &sum); - if (sum <= 0.0) - return ERROR_INT("sum <= 0.0", procName, 1); - norm = 4.0 / ((l_float32)(n - 1) * (n - 1)); - ave1prev = 0.0; - numaGetHistogramStats(na, 0.0, 1.0, &ave2prev, NULL, NULL, NULL); - num1prev = 0.0; - num2prev = sum; - maxindex = n / 2; /* initialize with something */ - - /* Split the histogram with [0 ... i] in the lower part - * and [i+1 ... n-1] in upper part. First, compute an otsu - * score for each possible splitting. */ - if ((nascore = numaCreate(n)) == NULL) - return ERROR_INT("nascore not made", procName, 1); - naave1 = (pave1) ? numaCreate(n) : NULL; - naave2 = (pave2) ? numaCreate(n) : NULL; - nanum1 = (pnum1) ? numaCreate(n) : NULL; - nanum2 = (pnum2) ? numaCreate(n) : NULL; - maxscore = 0.0; - for (i = 0; i < n; i++) { - numaGetFValue(na, i, &val); - num1 = num1prev + val; - if (num1 == 0) - ave1 = ave1prev; - else - ave1 = (num1prev * ave1prev + i * val) / num1; - num2 = num2prev - val; - if (num2 == 0) - ave2 = ave2prev; - else - ave2 = (num2prev * ave2prev - i * val) / num2; - fract1 = num1 / sum; - score = norm * (fract1 * (1 - fract1)) * (ave2 - ave1) * (ave2 - ave1); - numaAddNumber(nascore, score); - if (pave1) numaAddNumber(naave1, ave1); - if (pave2) numaAddNumber(naave2, ave2); - if (pnum1) numaAddNumber(nanum1, num1); - if (pnum2) numaAddNumber(nanum2, num2); - if (score > maxscore) { - maxscore = score; - maxindex = i; - } - num1prev = num1; - num2prev = num2; - ave1prev = ave1; - ave2prev = ave2; - } - - /* Next, for all contiguous scores within a specified fraction - * of the max, choose the split point as the value with the - * minimum in the histogram. */ - minscore = (1. - scorefract) * maxscore; - for (i = maxindex - 1; i >= 0; i--) { - numaGetFValue(nascore, i, &val); - if (val < minscore) - break; - } - minrange = i + 1; - for (i = maxindex + 1; i < n; i++) { - numaGetFValue(nascore, i, &val); - if (val < minscore) - break; - } - maxrange = i - 1; - numaGetFValue(na, minrange, &minval); - bestsplit = minrange; - for (i = minrange + 1; i <= maxrange; i++) { - numaGetFValue(na, i, &val); - if (val < minval) { - minval = val; - bestsplit = i; - } - } - - /* Add one to the bestsplit value to get the threshold value, - * because when we take a threshold, as in pixThresholdToBinary(), - * we always choose the set with values below the threshold. */ - bestsplit = L_MIN(255, bestsplit + 1); - - if (psplitindex) *psplitindex = bestsplit; - if (pave1) numaGetFValue(naave1, bestsplit, pave1); - if (pave2) numaGetFValue(naave2, bestsplit, pave2); - if (pnum1) numaGetFValue(nanum1, bestsplit, pnum1); - if (pnum2) numaGetFValue(nanum2, bestsplit, pnum2); - - if (pnascore) { /* debug mode */ - lept_stderr("minrange = %d, maxrange = %d\n", minrange, maxrange); - lept_stderr("minval = %10.0f\n", minval); - gplotSimple1(nascore, GPLOT_PNG, "/tmp/lept/nascore", - "Score for split distribution"); - *pnascore = nascore; - } else { - numaDestroy(&nascore); - } - - if (pave1) numaDestroy(&naave1); - if (pave2) numaDestroy(&naave2); - if (pnum1) numaDestroy(&nanum1); - if (pnum2) numaDestroy(&nanum2); - return 0; -} - - -/*----------------------------------------------------------------------* - * Comparing histograms * - *----------------------------------------------------------------------*/ -/*! - * \brief grayHistogramsToEMD() - * - * \param[in] naa1, naa2 two numaa, each with one or more 256-element - * histograms - * \param[out] pnad nad of EM distances for each histogram - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The two numaas must be the same size and have corresponding - * 256-element histograms. Pairs do not need to be normalized - * to the same sum. - * (2) This is typically used on two sets of histograms from - * corresponding tiles of two images. The similarity of two - * images can be found with the scoring function used in - * pixCompareGrayByHisto(): - * score S = 1.0 - k * D, where - * k is a constant, say in the range 5-10 - * D = EMD - * for each tile; for multiple tiles, take the Min(S) over - * the set of tiles to be the final score. - *- */ -l_ok -grayHistogramsToEMD(NUMAA *naa1, - NUMAA *naa2, - NUMA **pnad) -{ -l_int32 i, n, nt; -l_float32 dist; -NUMA *na1, *na2, *nad; - - PROCNAME("grayHistogramsToEMD"); - - if (!pnad) - return ERROR_INT("&nad not defined", procName, 1); - *pnad = NULL; - if (!naa1 || !naa2) - return ERROR_INT("na1 and na2 not both defined", procName, 1); - n = numaaGetCount(naa1); - if (n != numaaGetCount(naa2)) - return ERROR_INT("naa1 and naa2 numa counts differ", procName, 1); - nt = numaaGetNumberCount(naa1); - if (nt != numaaGetNumberCount(naa2)) - return ERROR_INT("naa1 and naa2 number counts differ", procName, 1); - if (256 * n != nt) /* good enough check */ - return ERROR_INT("na sizes must be 256", procName, 1); - - nad = numaCreate(n); - *pnad = nad; - for (i = 0; i < n; i++) { - na1 = numaaGetNuma(naa1, i, L_CLONE); - na2 = numaaGetNuma(naa2, i, L_CLONE); - numaEarthMoverDistance(na1, na2, &dist); - numaAddNumber(nad, dist / 255.); /* normalize to [0.0 - 1.0] */ - numaDestroy(&na1); - numaDestroy(&na2); - } - return 0; -} - - -/*! - * \brief numaEarthMoverDistance() - * - * \param[in] na1, na2 two numas of the same size, typically histograms - * \param[out] pdist earthmover distance - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The two numas must have the same size. They do not need to be - * normalized to the same sum before applying the function. - * (2) For a 1D discrete function, the implementation of the EMD - * is trivial. Just keep filling or emptying buckets in one numa - * to match the amount in the other, moving sequentially along - * both arrays. - * (3) We divide the sum of the absolute value of everything moved - * (by 1 unit at a time) by the sum of the numa (amount of "earth") - * to get the average distance that the "earth" was moved. - * This is the value returned here. - * (4) The caller can do a further normalization, by the number of - * buckets (minus 1), to get the EM distance as a fraction of - * the maximum possible distance, which is n-1. This fraction - * is 1.0 for the situation where all the 'earth' in the first - * array is at one end, and all in the second array is at the - * other end. - *- */ -l_ok -numaEarthMoverDistance(NUMA *na1, - NUMA *na2, - l_float32 *pdist) -{ -l_int32 n, norm, i; -l_float32 sum1, sum2, diff, total; -l_float32 *array1, *array3; -NUMA *na3; - - PROCNAME("numaEarthMoverDistance"); - - if (!pdist) - return ERROR_INT("&dist not defined", procName, 1); - *pdist = 0.0; - if (!na1 || !na2) - return ERROR_INT("na1 and na2 not both defined", procName, 1); - n = numaGetCount(na1); - if (n != numaGetCount(na2)) - return ERROR_INT("na1 and na2 have different size", procName, 1); - - /* Generate na3; normalize to na1 if necessary */ - numaGetSum(na1, &sum1); - numaGetSum(na2, &sum2); - norm = (L_ABS(sum1 - sum2) < 0.00001 * L_ABS(sum1)) ? 1 : 0; - if (!norm) - na3 = numaTransform(na2, 0, sum1 / sum2); - else - na3 = numaCopy(na2); - array1 = numaGetFArray(na1, L_NOCOPY); - array3 = numaGetFArray(na3, L_NOCOPY); - - /* Move earth in n3 from array elements, to match n1 */ - total = 0; - for (i = 1; i < n; i++) { - diff = array1[i - 1] - array3[i - 1]; - array3[i] -= diff; - total += L_ABS(diff); - } - *pdist = total / sum1; - - numaDestroy(&na3); - return 0; -} - - -/*! - * \brief grayInterHistogramStats() - * - * \param[in] naa numaa with two or more 256-element histograms - * \param[in] wc half-width of the smoothing window - * \param[out] pnam [optional] mean values - * \param[out] pnams [optional] mean square values - * \param[out] pnav [optional] variances - * \param[out] pnarv [optional] rms deviations from the mean - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The %naa has two or more 256-element numa histograms, which - * are to be compared value-wise at each of the 256 gray levels. - * The result are stats (mean, mean square, variance, root variance) - * aggregated across the set of histograms, and each is output - * as a 256 entry numa. Think of these histograms as a matrix, - * where each histogram is one row of the array. The stats are - * then aggregated column-wise, between the histograms. - * (2) These stats are: - * ~ average value:- */ -l_ok -grayInterHistogramStats(NUMAA *naa, - l_int32 wc, - NUMA **pnam, - NUMA **pnams, - NUMA **pnav, - NUMA **pnarv) -{ -l_int32 i, j, n, nn; -l_float32 **arrays; -l_float32 mean, var, rvar; -NUMA *na1, *na2, *na3, *na4; - - PROCNAME("grayInterHistogramStats"); - - if (pnam) *pnam = NULL; - if (pnams) *pnams = NULL; - if (pnav) *pnav = NULL; - if (pnarv) *pnarv = NULL; - if (!pnam && !pnams && !pnav && !pnarv) - return ERROR_INT("nothing requested", procName, 1); - if (!naa) - return ERROR_INT("naa not defined", procName, 1); - n = numaaGetCount(naa); - for (i = 0; i < n; i++) { - nn = numaaGetNumaCount(naa, i); - if (nn != 256) { - L_ERROR("%d numbers in numa[%d]\n", procName, nn, i); - return 1; - } - } - - if (pnam) *pnam = numaCreate(256); - if (pnams) *pnams = numaCreate(256); - if (pnav) *pnav = numaCreate(256); - if (pnarv) *pnarv = numaCreate(256); - - /* First, use mean smoothing, normalize each histogram, - * and save all results in a 2D matrix. */ - arrays = (l_float32 **)LEPT_CALLOC(n, sizeof(l_float32 *)); - for (i = 0; i < n; i++) { - na1 = numaaGetNuma(naa, i, L_CLONE); - na2 = numaWindowedMean(na1, wc); - na3 = numaNormalizeHistogram(na2, 10000.); - arrays[i] = numaGetFArray(na3, L_COPY); - numaDestroy(&na1); - numaDestroy(&na2); - numaDestroy(&na3); - } - - /* Get stats between histograms */ - for (j = 0; j < 256; j++) { - na4 = numaCreate(n); - for (i = 0; i < n; i++) { - numaAddNumber(na4, arrays[i][j]); - } - numaSimpleStats(na4, 0, -1, &mean, &var, &rvar); - if (pnam) numaAddNumber(*pnam, mean); - if (pnams) numaAddNumber(*pnams, mean * mean); - if (pnav) numaAddNumber(*pnav, var); - if (pnarv) numaAddNumber(*pnarv, rvar); - numaDestroy(&na4); - } - - for (i = 0; i < n; i++) - LEPT_FREE(arrays[i]); - LEPT_FREE(arrays); - return 0; -} - - -/*----------------------------------------------------------------------* - * Extrema finding * - *----------------------------------------------------------------------*/ -/*! - * \brief numaFindPeaks() - * - * \param[in] nas source numa - * \param[in] nmax max number of peaks to be found - * \param[in] fract1 min fraction of peak value - * \param[in] fract2 min slope - * \return peak na, or NULL on error. - * - *(nam) - * ~ average squared value: (nams) - * ~ variance: <(v - )*(v - )> = - * (nav) - * ~ square-root of variance: (narv) - * where the brackets < .. > indicate that the average value is - * to be taken over each column of the array. - * (3) The input histograms are optionally smoothed before these - * statistical operations. - * (4) The input histograms are normalized to a sum of 10000. By - * doing this, the resulting numbers are independent of the - * number of samples used in building the individual histograms. - * (5) A typical application is on a set of histograms from tiles - * of an image, to distinguish between text/tables and photo - * regions. If the tiles are much larger than the text line - * spacing, text/table regions typically have smaller variance - * across tiles than photo regions. For this application, it - * may be useful to ignore values near white, which are large for - * text and would magnify the variance due to variations in - * illumination. However, because the variance of a drawing or - * a light photo can be similar to that of grayscale text, this - * function is only a discriminator between darker photos/drawings - * and light photos/text/line-graphics. - *
- * Notes: - * (1) The returned na consists of sets of four numbers representing - * the peak, in the following order: - * left edge; peak center; right edge; normalized peak area - *- */ -NUMA * -numaFindPeaks(NUMA *nas, - l_int32 nmax, - l_float32 fract1, - l_float32 fract2) -{ -l_int32 i, k, n, maxloc, lloc, rloc; -l_float32 fmaxval, sum, total, newtotal, val, lastval; -l_float32 peakfract; -NUMA *na, *napeak; - - PROCNAME("numaFindPeaks"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - n = numaGetCount(nas); - numaGetSum(nas, &total); - - /* We munge this copy */ - if ((na = numaCopy(nas)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - if ((napeak = numaCreate(4 * nmax)) == NULL) { - numaDestroy(&na); - return (NUMA *)ERROR_PTR("napeak not made", procName, NULL); - } - - for (k = 0; k < nmax; k++) { - numaGetSum(na, &newtotal); - if (newtotal == 0.0) /* sanity check */ - break; - numaGetMax(na, &fmaxval, &maxloc); - sum = fmaxval; - lastval = fmaxval; - lloc = 0; - for (i = maxloc - 1; i >= 0; --i) { - numaGetFValue(na, i, &val); - if (val == 0.0) { - lloc = i + 1; - break; - } - if (val > fract1 * fmaxval) { - sum += val; - lastval = val; - continue; - } - if (lastval - val > fract2 * lastval) { - sum += val; - lastval = val; - continue; - } - lloc = i; - break; - } - lastval = fmaxval; - rloc = n - 1; - for (i = maxloc + 1; i < n; ++i) { - numaGetFValue(na, i, &val); - if (val == 0.0) { - rloc = i - 1; - break; - } - if (val > fract1 * fmaxval) { - sum += val; - lastval = val; - continue; - } - if (lastval - val > fract2 * lastval) { - sum += val; - lastval = val; - continue; - } - rloc = i; - break; - } - peakfract = sum / total; - numaAddNumber(napeak, lloc); - numaAddNumber(napeak, maxloc); - numaAddNumber(napeak, rloc); - numaAddNumber(napeak, peakfract); - - for (i = lloc; i <= rloc; i++) - numaSetValue(na, i, 0.0); - } - - numaDestroy(&na); - return napeak; -} - - -/*! - * \brief numaFindExtrema() - * - * \param[in] nas input values - * \param[in] delta relative amount to resolve peaks and valleys - * \param[out] pnav [optional] values of extrema - * \return nad (locations of extrema, or NULL on error - * - *
- * Notes: - * (1) This returns a sequence of extrema (peaks and valleys). - * (2) The algorithm is analogous to that for determining - * mountain peaks. Suppose we have a local peak, with - * bumps on the side. Under what conditions can we consider - * those 'bumps' to be actual peaks? The answer: if the - * bump is separated from the peak by a saddle that is at - * least 500 feet below the bump. - * (3) Operationally, suppose we are trying to identify a peak. - * We have a previous valley, and also the largest value that - * we have seen since that valley. We can identify this as - * a peak if we find a value that is delta BELOW it. When - * we find such a value, label the peak, use the current - * value to label the starting point for the search for - * a valley, and do the same operation in reverse. Namely, - * keep track of the lowest point seen, and look for a value - * that is delta ABOVE it. Once found, the lowest point is - * labeled the valley, and continue, looking for the next peak. - *- */ -NUMA * -numaFindExtrema(NUMA *nas, - l_float32 delta, - NUMA **pnav) -{ -l_int32 i, n, found, loc, direction; -l_float32 startval, val, maxval, minval; -NUMA *nav, *nad; - - PROCNAME("numaFindExtrema"); - - if (pnav) *pnav = NULL; - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (delta < 0.0) - return (NUMA *)ERROR_PTR("delta < 0", procName, NULL); - - n = numaGetCount(nas); - nad = numaCreate(0); - nav = NULL; - if (pnav) { - nav = numaCreate(0); - *pnav = nav; - } - - /* We don't know if we'll find a peak or valley first, - * but use the first element of nas as the reference point. - * Break when we deviate by 'delta' from the first point. */ - numaGetFValue(nas, 0, &startval); - found = FALSE; - for (i = 1; i < n; i++) { - numaGetFValue(nas, i, &val); - if (L_ABS(val - startval) >= delta) { - found = TRUE; - break; - } - } - - if (!found) - return nad; /* it's empty */ - - /* Are we looking for a peak or a valley? */ - if (val > startval) { /* peak */ - direction = 1; - maxval = val; - } else { - direction = -1; - minval = val; - } - loc = i; - - /* Sweep through the rest of the array, recording alternating - * peak/valley extrema. */ - for (i = i + 1; i < n; i++) { - numaGetFValue(nas, i, &val); - if (direction == 1 && val > maxval ) { /* new local max */ - maxval = val; - loc = i; - } else if (direction == -1 && val < minval ) { /* new local min */ - minval = val; - loc = i; - } else if (direction == 1 && (maxval - val >= delta)) { - numaAddNumber(nad, loc); /* save the current max location */ - if (nav) numaAddNumber(nav, maxval); - direction = -1; /* reverse: start looking for a min */ - minval = val; - loc = i; /* current min location */ - } else if (direction == -1 && (val - minval >= delta)) { - numaAddNumber(nad, loc); /* save the current min location */ - if (nav) numaAddNumber(nav, minval); - direction = 1; /* reverse: start looking for a max */ - maxval = val; - loc = i; /* current max location */ - } - } - - /* Save the final extremum */ -/* numaAddNumber(nad, loc); */ - return nad; -} - - -/*! - * \brief numaFindLocForThreshold() - * - * \param[in] nas input histogram - * \param[in] skip distance to skip to check for false min; 0 for default - * \param[out] pthresh threshold value - * \param[out] pfract [optional] fraction below or at threshold - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This finds a good place to set a threshold for a histogram - * of values that has two peaks. The peaks can differ greatly - * in area underneath them. The number of buckets in the - * histogram is expected to be 256 (e.g, from an 8 bpp gray image). - * (2) The input histogram should have been smoothed with a window - * to avoid false peak and valley detection due to noise. For - * example, see pixThresholdByHisto(). - * (3) A skip value can be input to determine the look-ahead distance - * to ignore a false peak on the descent from the first peak. - * Input 0 to use the default value (it assumes a histo size of 256). - * (4) Optionally, the fractional area under the first peak can - * be returned. - *- */ -l_ok -numaFindLocForThreshold(NUMA *na, - l_int32 skip, - l_int32 *pthresh, - l_float32 *pfract) -{ -l_int32 i, n, start, found, index, minloc; -l_float32 val, pval, startval, jval, minval, sum, partsum; -l_float32 *fa; - - PROCNAME("numaFindLocForThreshold"); - - if (pfract) *pfract = 0.0; - if (!pthresh) - return ERROR_INT("&thresh not defined", procName, 1); - *pthresh = 0; - if (!na) - return ERROR_INT("na not defined", procName, 1); - if (skip <= 0) skip = 20; - - /* Look for the top of the first peak */ - n = numaGetCount(na); - fa = numaGetFArray(na, L_NOCOPY); - pval = fa[0]; - for (i = 1; i < n; i++) { - val = fa[i]; - index = L_MIN(i + skip, n - 1); - jval = fa[index]; - if (val < pval && jval < pval) /* near the top if not there */ - break; - pval = val; - } - - /* Look for the low point in the valley */ - start = i; - pval = fa[start]; - found = FALSE; /* signal for passing the min between peaks */ - for (i = start + 1; i < n; i++) { - val = fa[i]; - if (val <= pval) { /* going down */ - pval = val; - } else { /* going up */ - index = L_MIN(i + skip, n - 1); - jval = fa[index]; /* junp ahead 20 */ - if (val > jval) { /* still going down; jump ahead */ - pval = jval; - i = index; - } else { /* really going up; passed the min */ - found = TRUE; - break; - } - } - } - - /* Find the location of the minimum in the interval */ - minloc = index; /* likely passed the min; look backward */ - minval = fa[index]; - for (i = index - 1; i > index - skip; i--) { - if (fa[i] < minval) { - minval = fa[i]; - minloc = i; - } - } - *pthresh = minloc; - - /* Find the fraction under the first peak */ - if (pfract) { - numaGetSumOnInterval(na, 0, minloc, &partsum); - numaGetSum(na, &sum); - if (sum > 0.0) - *pfract = partsum / sum; - } - return 0; -} - - -/*! - * \brief numaCountReversals() - * - * \param[in] nas input values - * \param[in] minreversal relative amount to resolve peaks and valleys - * \param[out] pnr [optional] number of reversals - * \param[out] prd [optional] reversal density: reversals/length - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) The input numa is can be generated from pixExtractAlongLine(). - * If so, the x parameters can be used to find the reversal - * frequency along a line. - * (2) If the input numa was generated from a 1 bpp pix, the - * values will be 0 and 1. Use %minreversal == 1 to get - * the number of pixel flips. If the only values are 0 and 1, - * but %minreversal > 1, set the reversal count to 0 and - * issue a warning. - *- */ -l_ok -numaCountReversals(NUMA *nas, - l_float32 minreversal, - l_int32 *pnr, - l_float32 *prd) -{ -l_int32 i, n, nr, ival, binvals; -l_int32 *ia; -l_float32 fval, delx, len; -NUMA *nat; - - PROCNAME("numaCountReversals"); - - if (pnr) *pnr = 0; - if (prd) *prd = 0.0; - if (!pnr && !prd) - return ERROR_INT("neither &nr nor &rd are defined", procName, 1); - if (!nas) - return ERROR_INT("nas not defined", procName, 1); - if ((n = numaGetCount(nas)) == 0) { - L_INFO("nas is empty\n", procName); - return 0; - } - if (minreversal < 0.0) - return ERROR_INT("minreversal < 0", procName, 1); - - /* Decide if the only values are 0 and 1 */ - binvals = TRUE; - for (i = 0; i < n; i++) { - numaGetFValue(nas, i, &fval); - if (fval != 0.0 && fval != 1.0) { - binvals = FALSE; - break; - } - } - - nr = 0; - if (binvals) { - if (minreversal > 1.0) { - L_WARNING("binary values but minreversal > 1\n", procName); - } else { - ia = numaGetIArray(nas); - ival = ia[0]; - for (i = 1; i < n; i++) { - if (ia[i] != ival) { - nr++; - ival = ia[i]; - } - } - LEPT_FREE(ia); - } - } else { - nat = numaFindExtrema(nas, minreversal, NULL); - nr = numaGetCount(nat); - numaDestroy(&nat); - } - if (pnr) *pnr = nr; - if (prd) { - numaGetParameters(nas, NULL, &delx); - len = delx * n; - *prd = (l_float32)nr / len; - } - - return 0; -} - - -/*----------------------------------------------------------------------* - * Threshold crossings and frequency analysis * - *----------------------------------------------------------------------*/ -/*! - * \brief numaSelectCrossingThreshold() - * - * \param[in] nax [optional] numa of abscissa values; can be NULL - * \param[in] nay signal - * \param[in] estthresh estimated pixel threshold for crossing: - * e.g., for images, white <--> black; typ. ~120 - * \param[out] pbestthresh robust estimate of threshold to use - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) When a valid threshold is used, the number of crossings is - * a maximum, because none are missed. If no threshold intersects - * all the crossings, the crossings must be determined with - * numaCrossingsByPeaks(). - * (2) %estthresh is an input estimate of the threshold that should - * be used. We compute the crossings with 41 thresholds - * (20 below and 20 above). There is a range in which the - * number of crossings is a maximum. Return a threshold - * in the center of this stable plateau of crossings. - * This can then be used with numaCrossingsByThreshold() - * to get a good estimate of crossing locations. - *- */ -l_ok -numaSelectCrossingThreshold(NUMA *nax, - NUMA *nay, - l_float32 estthresh, - l_float32 *pbestthresh) -{ -l_int32 i, inrun, istart, iend, maxstart, maxend, runlen, maxrunlen; -l_int32 val, maxval, nmax, count; -l_float32 thresh, fmaxval, fmodeval; -NUMA *nat, *nac; - - PROCNAME("numaSelectCrossingThreshold"); - - if (!pbestthresh) - return ERROR_INT("&bestthresh not defined", procName, 1); - *pbestthresh = 0.0; - if (!nay) - return ERROR_INT("nay not defined", procName, 1); - - /* Compute the number of crossings for different thresholds */ - nat = numaCreate(41); - for (i = 0; i < 41; i++) { - thresh = estthresh - 80.0 + 4.0 * i; - nac = numaCrossingsByThreshold(nax, nay, thresh); - numaAddNumber(nat, numaGetCount(nac)); - numaDestroy(&nac); - } - - /* Find the center of the plateau of max crossings, which - * extends from thresh[istart] to thresh[iend]. */ - numaGetMax(nat, &fmaxval, NULL); - maxval = (l_int32)fmaxval; - nmax = 0; - for (i = 0; i < 41; i++) { - numaGetIValue(nat, i, &val); - if (val == maxval) - nmax++; - } - if (nmax < 3) { /* likely accidental max; try the mode */ - numaGetMode(nat, &fmodeval, &count); - if (count > nmax && fmodeval > 0.5 * fmaxval) - maxval = (l_int32)fmodeval; /* use the mode */ - } - - inrun = FALSE; - iend = 40; - maxrunlen = 0, maxstart = 0, maxend = 0; - for (i = 0; i < 41; i++) { - numaGetIValue(nat, i, &val); - if (val == maxval) { - if (!inrun) { - istart = i; - inrun = TRUE; - } - continue; - } - if (inrun && (val != maxval)) { - iend = i - 1; - runlen = iend - istart + 1; - inrun = FALSE; - if (runlen > maxrunlen) { - maxstart = istart; - maxend = iend; - maxrunlen = runlen; - } - } - } - if (inrun) { - runlen = i - istart; - if (runlen > maxrunlen) { - maxstart = istart; - maxend = i - 1; - maxrunlen = runlen; - } - } - - *pbestthresh = estthresh - 80.0 + 2.0 * (l_float32)(maxstart + maxend); - -#if DEBUG_CROSSINGS - lept_stderr("\nCrossings attain a maximum at %d thresholds, between:\n" - " thresh[%d] = %5.1f and thresh[%d] = %5.1f\n", - nmax, maxstart, estthresh - 80.0 + 4.0 * maxstart, - maxend, estthresh - 80.0 + 4.0 * maxend); - lept_stderr("The best choice: %5.1f\n", *pbestthresh); - lept_stderr("Number of crossings at the 41 thresholds:"); - numaWriteStderr(nat); -#endif /* DEBUG_CROSSINGS */ - - numaDestroy(&nat); - return 0; -} - - -/*! - * \brief numaCrossingsByThreshold() - * - * \param[in] nax [optional] numa of abscissa values; can be NULL - * \param[in] nay numa of ordinate values, corresponding to nax - * \param[in] thresh threshold value for nay - * \return nad abscissa pts at threshold, or NULL on error - * - *
- * Notes: - * (1) If nax == NULL, we use startx and delx from nay to compute - * the crossing values in nad. - *- */ -NUMA * -numaCrossingsByThreshold(NUMA *nax, - NUMA *nay, - l_float32 thresh) -{ -l_int32 i, n; -l_float32 startx, delx; -l_float32 xval1, xval2, yval1, yval2, delta1, delta2, crossval, fract; -NUMA *nad; - - PROCNAME("numaCrossingsByThreshold"); - - if (!nay) - return (NUMA *)ERROR_PTR("nay not defined", procName, NULL); - n = numaGetCount(nay); - - if (nax && (numaGetCount(nax) != n)) - return (NUMA *)ERROR_PTR("nax and nay sizes differ", procName, NULL); - - nad = numaCreate(0); - numaGetFValue(nay, 0, &yval1); - numaGetParameters(nay, &startx, &delx); - if (nax) - numaGetFValue(nax, 0, &xval1); - else - xval1 = startx; - for (i = 1; i < n; i++) { - numaGetFValue(nay, i, &yval2); - if (nax) - numaGetFValue(nax, i, &xval2); - else - xval2 = startx + i * delx; - delta1 = yval1 - thresh; - delta2 = yval2 - thresh; - if (delta1 == 0.0) { - numaAddNumber(nad, xval1); - } else if (delta2 == 0.0) { - numaAddNumber(nad, xval2); - } else if (delta1 * delta2 < 0.0) { /* crossing */ - fract = L_ABS(delta1) / L_ABS(yval1 - yval2); - crossval = xval1 + fract * (xval2 - xval1); - numaAddNumber(nad, crossval); - } - xval1 = xval2; - yval1 = yval2; - } - - return nad; -} - - -/*! - * \brief numaCrossingsByPeaks() - * - * \param[in] nax [optional] numa of abscissa values - * \param[in] nay numa of ordinate values, corresponding to nax - * \param[in] delta parameter used to identify when a new peak can be found - * \return nad abscissa pts at threshold, or NULL on error - * - *
- * Notes: - * (1) If nax == NULL, we use startx and delx from nay to compute - * the crossing values in nad. - *- */ -NUMA * -numaCrossingsByPeaks(NUMA *nax, - NUMA *nay, - l_float32 delta) -{ -l_int32 i, j, n, np, previndex, curindex; -l_float32 startx, delx; -l_float32 xval1, xval2, yval1, yval2, delta1, delta2; -l_float32 prevval, curval, thresh, crossval, fract; -NUMA *nap, *nad; - - PROCNAME("numaCrossingsByPeaks"); - - if (!nay) - return (NUMA *)ERROR_PTR("nay not defined", procName, NULL); - - n = numaGetCount(nay); - if (nax && (numaGetCount(nax) != n)) - return (NUMA *)ERROR_PTR("nax and nay sizes differ", procName, NULL); - - /* Find the extrema. Also add last point in nay to get - * the last transition (from the last peak to the end). - * The number of crossings is 1 more than the number of extrema. */ - nap = numaFindExtrema(nay, delta, NULL); - numaAddNumber(nap, n - 1); - np = numaGetCount(nap); - L_INFO("Number of crossings: %d\n", procName, np); - - /* Do all computation in index units of nax or the delx of nay */ - nad = numaCreate(np); /* output crossing locations, in nax units */ - previndex = 0; /* prime the search with 1st point */ - numaGetFValue(nay, 0, &prevval); /* prime the search with 1st point */ - numaGetParameters(nay, &startx, &delx); - for (i = 0; i < np; i++) { - numaGetIValue(nap, i, &curindex); - numaGetFValue(nay, curindex, &curval); - thresh = (prevval + curval) / 2.0; - if (nax) - numaGetFValue(nax, previndex, &xval1); - else - xval1 = startx + previndex * delx; - numaGetFValue(nay, previndex, &yval1); - for (j = previndex + 1; j <= curindex; j++) { - if (nax) - numaGetFValue(nax, j, &xval2); - else - xval2 = startx + j * delx; - numaGetFValue(nay, j, &yval2); - delta1 = yval1 - thresh; - delta2 = yval2 - thresh; - if (delta1 == 0.0) { - numaAddNumber(nad, xval1); - break; - } else if (delta2 == 0.0) { - numaAddNumber(nad, xval2); - break; - } else if (delta1 * delta2 < 0.0) { /* crossing */ - fract = L_ABS(delta1) / L_ABS(yval1 - yval2); - crossval = xval1 + fract * (xval2 - xval1); - numaAddNumber(nad, crossval); - break; - } - xval1 = xval2; - yval1 = yval2; - } - previndex = curindex; - prevval = curval; - } - - numaDestroy(&nap); - return nad; -} - - -/*! - * \brief numaEvalBestHaarParameters() - * - * \param[in] nas numa of non-negative signal values - * \param[in] relweight relative weight of (-1 comb) / (+1 comb) - * contributions to the 'convolution'. In effect, - * the convolution kernel is a comb consisting of - * alternating +1 and -weight. - * \param[in] nwidth number of widths to consider - * \param[in] nshift number of shifts to consider for each width - * \param[in] minwidth smallest width to consider - * \param[in] maxwidth largest width to consider - * \param[out] pbestwidth width giving largest score - * \param[out] pbestshift shift giving largest score - * \param[out] pbestscore [optional] convolution with "Haar"-like comb - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This does a linear sweep of widths, evaluating at %nshift - * shifts for each width, computing the score from a convolution - * with a long comb, and finding the (width, shift) pair that - * gives the maximum score. The best width is the "half-wavelength" - * of the signal. - * (2) The convolving function is a comb of alternating values - * +1 and -1 * relweight, separated by the width and phased by - * the shift. This is similar to a Haar transform, except - * there the convolution is performed with a square wave. - * (3) The function is useful for finding the line spacing - * and strength of line signal from pixel sum projections. - * (4) The score is normalized to the size of nas divided by - * the number of half-widths. For image applications, the input is - * typically an array of pixel projections, so one should - * normalize by dividing the score by the image width in the - * pixel projection direction. - *- */ -l_ok -numaEvalBestHaarParameters(NUMA *nas, - l_float32 relweight, - l_int32 nwidth, - l_int32 nshift, - l_float32 minwidth, - l_float32 maxwidth, - l_float32 *pbestwidth, - l_float32 *pbestshift, - l_float32 *pbestscore) -{ -l_int32 i, j; -l_float32 delwidth, delshift, width, shift, score; -l_float32 bestwidth, bestshift, bestscore; - - PROCNAME("numaEvalBestHaarParameters"); - - if (pbestscore) *pbestscore = 0.0; - if (pbestwidth) *pbestwidth = 0.0; - if (pbestshift) *pbestshift = 0.0; - if (!pbestwidth || !pbestshift) - return ERROR_INT("&bestwidth and &bestshift not defined", procName, 1); - if (!nas) - return ERROR_INT("nas not defined", procName, 1); - - bestscore = bestwidth = bestshift = 0.0; - delwidth = (maxwidth - minwidth) / (nwidth - 1.0); - for (i = 0; i < nwidth; i++) { - width = minwidth + delwidth * i; - delshift = width / (l_float32)(nshift); - for (j = 0; j < nshift; j++) { - shift = j * delshift; - numaEvalHaarSum(nas, width, shift, relweight, &score); - if (score > bestscore) { - bestscore = score; - bestwidth = width; - bestshift = shift; -#if DEBUG_FREQUENCY - lept_stderr("width = %7.3f, shift = %7.3f, score = %7.3f\n", - width, shift, score); -#endif /* DEBUG_FREQUENCY */ - } - } - } - - *pbestwidth = bestwidth; - *pbestshift = bestshift; - if (pbestscore) - *pbestscore = bestscore; - return 0; -} - - -/*! - * \brief numaEvalHaarSum() - * - * \param[in] nas numa of non-negative signal values - * \param[in] width distance between +1 and -1 in convolution comb - * \param[in] shift phase of the comb: location of first +1 - * \param[in] relweight relative weight of (-1 comb) / (+1 comb) - * contributions to the 'convolution'. In effect, - * the convolution kernel is a comb consisting of - * alternating +1 and -weight. - * \param[out] pscore convolution with "Haar"-like comb - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This does a convolution with a comb of alternating values - * +1 and -relweight, separated by the width and phased by the shift. - * This is similar to a Haar transform, except that for Haar, - * (1) the convolution kernel is symmetric about 0, so the - * relweight is 1.0, and - * (2) the convolution is performed with a square wave. - * (2) The score is normalized to the size of nas divided by - * twice the "width". For image applications, the input is - * typically an array of pixel projections, so one should - * normalize by dividing the score by the image width in the - * pixel projection direction. - * (3) To get a Haar-like result, use relweight = 1.0. For detecting - * signals where you expect every other sample to be close to - * zero, as with barcodes or filtered text lines, you can - * use relweight > 1.0. - *- */ -l_ok -numaEvalHaarSum(NUMA *nas, - l_float32 width, - l_float32 shift, - l_float32 relweight, - l_float32 *pscore) -{ -l_int32 i, n, nsamp, index; -l_float32 score, weight, val; - - PROCNAME("numaEvalHaarSum"); - - if (!pscore) - return ERROR_INT("&score not defined", procName, 1); - *pscore = 0.0; - if (!nas) - return ERROR_INT("nas not defined", procName, 1); - if ((n = numaGetCount(nas)) < 2 * width) - return ERROR_INT("nas size too small", procName, 1); - - score = 0.0; - nsamp = (l_int32)((n - shift) / width); - for (i = 0; i < nsamp; i++) { - index = (l_int32)(shift + i * width); - weight = (i % 2) ? 1.0 : -1.0 * relweight; - numaGetFValue(nas, index, &val); - score += weight * val; - } - - *pscore = 2.0 * width * score / (l_float32)n; - return 0; -} - - -/*----------------------------------------------------------------------* - * Generating numbers in a range under constraints * - *----------------------------------------------------------------------*/ -/*! - * \brief genConstrainedNumaInRange() - * - * \param[in] first first number to choose; >= 0 - * \param[in] last biggest possible number to reach; >= first - * \param[in] nmax maximum number of numbers to select; > 0 - * \param[in] use_pairs 1 = select pairs of adjacent numbers; - * 0 = select individual numbers - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Selection is made uniformly in the range. This can be used - * to select pages distributed as uniformly as possible - * through a book, where you are constrained to: - * ~ choose between [first, ... biggest], - * ~ choose no more than nmax numbers, and - * and you have the option of requiring pairs of adjacent numbers. - *- */ -NUMA * -genConstrainedNumaInRange(l_int32 first, - l_int32 last, - l_int32 nmax, - l_int32 use_pairs) -{ -l_int32 i, nsets, val; -l_float32 delta; -NUMA *na; - - PROCNAME("genConstrainedNumaInRange"); - - first = L_MAX(0, first); - if (last < first) - return (NUMA *)ERROR_PTR("last < first!", procName, NULL); - if (nmax < 1) - return (NUMA *)ERROR_PTR("nmax < 1!", procName, NULL); - - nsets = L_MIN(nmax, last - first + 1); - if (use_pairs == 1) - nsets = nsets / 2; - if (nsets == 0) - return (NUMA *)ERROR_PTR("nsets == 0", procName, NULL); - - /* Select delta so that selection covers the full range if possible */ - if (nsets == 1) { - delta = 0.0; - } else { - if (use_pairs == 0) - delta = (l_float32)(last - first) / (nsets - 1); - else - delta = (l_float32)(last - first - 1) / (nsets - 1); - } - - na = numaCreate(nsets); - for (i = 0; i < nsets; i++) { - val = (l_int32)(first + i * delta + 0.5); - numaAddNumber(na, val); - if (use_pairs == 1) - numaAddNumber(na, val + 1); - } - - return na; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pageseg.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pageseg.c deleted file mode 100644 index 74fcd08f..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pageseg.c +++ /dev/null @@ -1,2466 +0,0 @@ -/*====================================================================* - - 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 pageseg.c - *
- * - * Top level page segmentation - * l_int32 pixGetRegionsBinary() - * - * Halftone region extraction - * PIX *pixGenHalftoneMask() **Deprecated wrapper** - * PIX *pixGenerateHalftoneMask() - - * - * Textline extraction - * PIX *pixGenTextlineMask() - * - * Textblock extraction - * PIX *pixGenTextblockMask() - * - * Location of page foreground - * PIX *pixFindPageForeground() - * - * Extraction of characters from image with only text - * l_int32 pixSplitIntoCharacters() - * BOXA *pixSplitComponentWithProfile() - * - * Extraction of lines of text - * PIXA *pixExtractTextlines() - * PIXA *pixExtractRawTextlines() - * - * How many text columns - * l_int32 pixCountTextColumns() - * - * Decision: text vs photo - * l_int32 pixDecideIfText() - * l_int32 pixFindThreshFgExtent() - * - * Decision: table vs text - * l_int32 pixDecideIfTable() - * Pix *pixPrepare1bpp() - * - * Estimate the grayscale background value - * l_int32 pixEstimateBackground() - * - * Largest white or black rectangles in an image - * l_int32 pixFindLargeRectangles() - * l_int32 pixFindLargestRectangle() - * - * Generate rectangle inside connected component - * BOX *pixFindRectangleInCC() - * - * Automatic photoinvert for OCR - * PIX *pixAutoPhotoinvert() - *- */ - -#ifdef HAVE_CONFIG_H -#include
- * Notes: - * (1) It is best to deskew the image before segmenting. - * (2) Passing in %pixadb enables debug output. - *- */ -l_ok -pixGetRegionsBinary(PIX *pixs, - PIX **ppixhm, - PIX **ppixtm, - PIX **ppixtb, - PIXA *pixadb) -{ -l_int32 w, h, htfound, tlfound; -PIX *pixr, *pix1, *pix2; -PIX *pixtext; /* text pixels only */ -PIX *pixhm2; /* halftone mask; 2x reduction */ -PIX *pixhm; /* halftone mask; */ -PIX *pixtm2; /* textline mask; 2x reduction */ -PIX *pixtm; /* textline mask */ -PIX *pixvws; /* vertical white space mask */ -PIX *pixtb2; /* textblock mask; 2x reduction */ -PIX *pixtbf2; /* textblock mask; 2x reduction; small comps filtered */ -PIX *pixtb; /* textblock mask */ - - PROCNAME("pixGetRegionsBinary"); - - if (ppixhm) *ppixhm = NULL; - if (ppixtm) *ppixtm = NULL; - if (ppixtb) *ppixtb = NULL; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs undefined or not 1 bpp", procName, 1); - pixGetDimensions(pixs, &w, &h, NULL); - if (w < MinWidth || h < MinHeight) { - L_ERROR("pix too small: w = %d, h = %d\n", procName, w, h); - return 1; - } - - /* 2x reduce, to 150 -200 ppi */ - pixr = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0); - if (pixadb) pixaAddPix(pixadb, pixr, L_COPY); - - /* Get the halftone mask */ - pixhm2 = pixGenerateHalftoneMask(pixr, &pixtext, &htfound, pixadb); - - /* Get the textline mask from the text pixels */ - pixtm2 = pixGenTextlineMask(pixtext, &pixvws, &tlfound, pixadb); - - /* Get the textblock mask from the textline mask */ - pixtb2 = pixGenTextblockMask(pixtm2, pixvws, pixadb); - pixDestroy(&pixr); - pixDestroy(&pixtext); - pixDestroy(&pixvws); - - /* Remove small components from the mask, where a small - * component is defined as one with both width and height < 60 */ - pixtbf2 = NULL; - if (pixtb2) { - pixtbf2 = pixSelectBySize(pixtb2, 60, 60, 4, L_SELECT_IF_EITHER, - L_SELECT_IF_GTE, NULL); - pixDestroy(&pixtb2); - if (pixadb) pixaAddPix(pixadb, pixtbf2, L_COPY); - } - - /* Expand all masks to full resolution, and do filling or - * small dilations for better coverage. */ - pixhm = pixExpandReplicate(pixhm2, 2); - pix1 = pixSeedfillBinary(NULL, pixhm, pixs, 8); - pixOr(pixhm, pixhm, pix1); - pixDestroy(&pixhm2); - pixDestroy(&pix1); - if (pixadb) pixaAddPix(pixadb, pixhm, L_COPY); - - pix1 = pixExpandReplicate(pixtm2, 2); - pixtm = pixDilateBrick(NULL, pix1, 3, 3); - pixDestroy(&pixtm2); - pixDestroy(&pix1); - if (pixadb) pixaAddPix(pixadb, pixtm, L_COPY); - - if (pixtbf2) { - pix1 = pixExpandReplicate(pixtbf2, 2); - pixtb = pixDilateBrick(NULL, pix1, 3, 3); - pixDestroy(&pixtbf2); - pixDestroy(&pix1); - if (pixadb) pixaAddPix(pixadb, pixtb, L_COPY); - } else { - pixtb = pixCreateTemplate(pixs); /* empty mask */ - } - - /* Debug: identify objects that are neither text nor halftone image */ - if (pixadb) { - pix1 = pixSubtract(NULL, pixs, pixtm); /* remove text pixels */ - pix2 = pixSubtract(NULL, pix1, pixhm); /* remove halftone pixels */ - pixaAddPix(pixadb, pix2, L_INSERT); - pixDestroy(&pix1); - } - - /* Debug: display textline components with random colors */ - if (pixadb) { - l_int32 w, h; - BOXA *boxa; - PIXA *pixa; - boxa = pixConnComp(pixtm, &pixa, 8); - pixGetDimensions(pixtm, &w, &h, NULL); - pix1 = pixaDisplayRandomCmap(pixa, w, h); - pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255); - pixaAddPix(pixadb, pix1, L_INSERT); - pixaDestroy(&pixa); - boxaDestroy(&boxa); - } - - /* Debug: identify the outlines of each textblock */ - if (pixadb) { - PIXCMAP *cmap; - PTAA *ptaa; - ptaa = pixGetOuterBordersPtaa(pixtb); - lept_mkdir("lept/pageseg"); - ptaaWriteDebug("/tmp/lept/pageseg/tb_outlines.ptaa", ptaa, 1); - pix1 = pixRenderRandomCmapPtaa(pixtb, ptaa, 1, 16, 1); - cmap = pixGetColormap(pix1); - pixcmapResetColor(cmap, 0, 130, 130, 130); - pixaAddPix(pixadb, pix1, L_INSERT); - ptaaDestroy(&ptaa); - } - - /* Debug: get b.b. for all mask components */ - if (pixadb) { - BOXA *bahm, *batm, *batb; - bahm = pixConnComp(pixhm, NULL, 4); - batm = pixConnComp(pixtm, NULL, 4); - batb = pixConnComp(pixtb, NULL, 4); - boxaWriteDebug("/tmp/lept/pageseg/htmask.boxa", bahm); - boxaWriteDebug("/tmp/lept/pageseg/textmask.boxa", batm); - boxaWriteDebug("/tmp/lept/pageseg/textblock.boxa", batb); - boxaDestroy(&bahm); - boxaDestroy(&batm); - boxaDestroy(&batb); - } - if (pixadb) { - pixaConvertToPdf(pixadb, 0, 1.0, 0, 0, "Debug page segmentation", - "/tmp/lept/pageseg/debug.pdf"); - L_INFO("Writing debug pdf to /tmp/lept/pageseg/debug.pdf\n", procName); - } - - if (ppixhm) - *ppixhm = pixhm; - else - pixDestroy(&pixhm); - if (ppixtm) - *ppixtm = pixtm; - else - pixDestroy(&pixtm); - if (ppixtb) - *ppixtb = pixtb; - else - pixDestroy(&pixtb); - - return 0; -} - - -/*------------------------------------------------------------------* - * Halftone region extraction * - *------------------------------------------------------------------*/ -/*! - * \brief pixGenHalftoneMask() - * - *
- * Deprecated: - * This wrapper avoids an ABI change with tesseract 3.0.4. - * It should be removed when we no longer need to support 3.0.4. - * The debug parameter is ignored (assumed 0). - *- */ -PIX * -pixGenHalftoneMask(PIX *pixs, - PIX **ppixtext, - l_int32 *phtfound, - l_int32 debug) -{ - return pixGenerateHalftoneMask(pixs, ppixtext, phtfound, NULL); -} - - -/*! - * \brief pixGenerateHalftoneMask() - * - * \param[in] pixs 1 bpp, assumed to be 150 to 200 ppi - * \param[out] ppixtext [optional] text part of pixs - * \param[out] phtfound [optional] 1 if the mask is not empty - * \param[in] pixadb input for collecting debug pix; use NULL to skip - * \return pixd halftone mask, or NULL on error - * - *
- * Notes: - * (1) This is not intended to work on small thumbnails. The - * dimensions of pixs must be at least MinWidth x MinHeight. - *- */ -PIX * -pixGenerateHalftoneMask(PIX *pixs, - PIX **ppixtext, - l_int32 *phtfound, - PIXA *pixadb) -{ -l_int32 w, h, empty; -PIX *pix1, *pix2, *pixhs, *pixhm, *pixd; - - PROCNAME("pixGenerateHalftoneMask"); - - if (ppixtext) *ppixtext = NULL; - if (phtfound) *phtfound = 0; - if (!pixs || pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - if (w < MinWidth || h < MinHeight) { - L_ERROR("pix too small: w = %d, h = %d\n", procName, w, h); - return NULL; - } - - /* Compute seed for halftone parts at 8x reduction */ - pix1 = pixReduceRankBinaryCascade(pixs, 4, 4, 0, 0); - pix2 = pixOpenBrick(NULL, pix1, 5, 5); - pixhs = pixExpandReplicate(pix2, 4); /* back to 2x reduction */ - pixDestroy(&pix1); - pixDestroy(&pix2); - if (pixadb) pixaAddPix(pixadb, pixhs, L_COPY); - - /* Compute mask for connected regions */ - pixhm = pixCloseSafeBrick(NULL, pixs, 4, 4); - if (pixadb) pixaAddPix(pixadb, pixhm, L_COPY); - - /* Fill seed into mask to get halftone mask */ - pixd = pixSeedfillBinary(NULL, pixhs, pixhm, 4); - if (pixadb) pixaAddPix(pixadb, pixd, L_COPY); - -#if 0 - pixOpenBrick(pixd, pixd, 9, 9); -#endif - - /* Check if mask is empty */ - pixZero(pixd, &empty); - if (phtfound && !empty) - *phtfound = 1; - - /* Optionally, get all pixels that are not under the halftone mask */ - if (ppixtext) { - if (empty) - *ppixtext = pixCopy(NULL, pixs); - else - *ppixtext = pixSubtract(NULL, pixs, pixd); - if (pixadb) pixaAddPix(pixadb, *ppixtext, L_COPY); - } - - pixDestroy(&pixhs); - pixDestroy(&pixhm); - return pixd; -} - - -/*------------------------------------------------------------------* - * Textline extraction * - *------------------------------------------------------------------*/ -/*! - * \brief pixGenTextlineMask() - * - * \param[in] pixs 1 bpp, assumed to be 150 to 200 ppi - * \param[out] ppixvws vertical whitespace mask - * \param[out] ptlfound [optional] 1 if the mask is not empty - * \param[in] pixadb input for collecting debug pix; use NULL to skip - * \return pixd textline mask, or NULL on error - * - *
- * Notes: - * (1) The input pixs should be deskewed. - * (2) pixs should have no halftone pixels. - * (3) This is not intended to work on small thumbnails. The - * dimensions of pixs must be at least MinWidth x MinHeight. - * (4) Both the input image and the returned textline mask - * are at the same resolution. - *- */ -PIX * -pixGenTextlineMask(PIX *pixs, - PIX **ppixvws, - l_int32 *ptlfound, - PIXA *pixadb) -{ -l_int32 w, h, empty; -PIX *pix1, *pix2, *pixvws, *pixd; - - PROCNAME("pixGenTextlineMask"); - - if (ptlfound) *ptlfound = 0; - if (!ppixvws) - return (PIX *)ERROR_PTR("&pixvws not defined", procName, NULL); - *ppixvws = NULL; - if (!pixs || pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - if (w < MinWidth || h < MinHeight) { - L_ERROR("pix too small: w = %d, h = %d\n", procName, w, h); - return NULL; - } - - /* First we need a vertical whitespace mask. Invert the image. */ - pix1 = pixInvert(NULL, pixs); - - /* The whitespace mask will break textlines where there - * is a large amount of white space below or above. - * This can be prevented by identifying regions of the - * inverted image that have large horizontal extent (bigger than - * the separation between columns) and significant - * vertical extent (bigger than the separation between - * textlines), and subtracting this from the bg. */ - pix2 = pixMorphCompSequence(pix1, "o80.60", 0); - pixSubtract(pix1, pix1, pix2); - if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); - pixDestroy(&pix2); - - /* Identify vertical whitespace by opening the remaining bg. - * o5.1 removes thin vertical bg lines and o1.200 extracts - * long vertical bg lines. */ - pixvws = pixMorphCompSequence(pix1, "o5.1 + o1.200", 0); - *ppixvws = pixvws; - if (pixadb) pixaAddPix(pixadb, pixvws, L_COPY); - pixDestroy(&pix1); - - /* Three steps to getting text line mask: - * (1) close the characters and words in the textlines - * (2) open the vertical whitespace corridors back up - * (3) small opening to remove noise */ - pix1 = pixMorphSequence(pixs, "c30.1", 0); - if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); - pixd = pixSubtract(NULL, pix1, pixvws); - pixOpenBrick(pixd, pixd, 3, 3); - if (pixadb) pixaAddPix(pixadb, pixd, L_COPY); - pixDestroy(&pix1); - - /* Check if text line mask is empty */ - if (ptlfound) { - pixZero(pixd, &empty); - if (!empty) - *ptlfound = 1; - } - - return pixd; -} - - -/*------------------------------------------------------------------* - * Textblock extraction * - *------------------------------------------------------------------*/ -/*! - * \brief pixGenTextblockMask() - * - * \param[in] pixs 1 bpp, textline mask, assumed to be 150 to 200 ppi - * \param[in] pixvws vertical white space mask - * \param[in] pixadb input for collecting debug pix; use NULL to skip - * \return pixd textblock mask, or NULL if empty or on error - * - *
- * Notes: - * (1) Both the input masks (textline and vertical white space) and - * the returned textblock mask are at the same resolution. - * (2) This is not intended to work on small thumbnails. The - * dimensions of pixs must be at least MinWidth x MinHeight. - * (3) The result is somewhat noisy, in that small "blocks" of - * text may be included. These can be removed by post-processing, - * using, e.g., - * pixSelectBySize(pix, 60, 60, 4, L_SELECT_IF_EITHER, - * L_SELECT_IF_GTE, NULL); - *- */ -PIX * -pixGenTextblockMask(PIX *pixs, - PIX *pixvws, - PIXA *pixadb) -{ -l_int32 w, h, empty; -PIX *pix1, *pix2, *pix3, *pixd; - - PROCNAME("pixGenTextblockMask"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - if (w < MinWidth || h < MinHeight) { - L_ERROR("pix too small: w = %d, h = %d\n", procName, w, h); - return NULL; - } - if (!pixvws) - return (PIX *)ERROR_PTR("pixvws not defined", procName, NULL); - - /* Join pixels vertically to make a textblock mask */ - pix1 = pixMorphSequence(pixs, "c1.10 + o4.1", 0); - pixZero(pix1, &empty); - if (empty) { - pixDestroy(&pix1); - L_INFO("no fg pixels in textblock mask\n", procName); - return NULL; - } - if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); - - /* Solidify the textblock mask and remove noise: - * (1) For each cc, close the blocks and dilate slightly - * to form a solid mask. - * (2) Small horizontal closing between components. - * (3) Open the white space between columns, again. - * (4) Remove small components. */ - pix2 = pixMorphSequenceByComponent(pix1, "c30.30 + d3.3", 8, 0, 0, NULL); - pixCloseSafeBrick(pix2, pix2, 10, 1); - if (pixadb) pixaAddPix(pixadb, pix2, L_COPY); - pix3 = pixSubtract(NULL, pix2, pixvws); - if (pixadb) pixaAddPix(pixadb, pix3, L_COPY); - pixd = pixSelectBySize(pix3, 25, 5, 8, L_SELECT_IF_BOTH, - L_SELECT_IF_GTE, NULL); - if (pixadb) pixaAddPix(pixadb, pixd, L_COPY); - - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - return pixd; -} - - -/*------------------------------------------------------------------* - * Location of page foreground * - *------------------------------------------------------------------*/ -/*! - * \brief pixFindPageForeground() - * - * \param[in] pixs full resolution (any type or depth - * \param[in] threshold for binarization; typically about 128 - * \param[in] mindist min distance of text from border to allow - * cleaning near border; at 2x reduction, this - * should be larger than 50; typically about 70 - * \param[in] erasedist when conditions are satisfied, erase anything - * within this distance of the edge; - * typically 20-30 at 2x reduction - * \param[in] showmorph debug: set to a negative integer to show steps - * in generating masks; this is typically used - * for debugging region extraction - * \param[in] pixac debug: allocate outside and pass this in to - * accumulate results of each call to this function, - * which can be displayed in a mosaic or a pdf. - * \return box region including foreground, with some pixel noise - * removed, or NULL if not found - * - *
- * Notes: - * (1) This doesn't simply crop to the fg. It attempts to remove - * pixel noise and junk at the edge of the image before cropping. - * The input %threshold is used if pixs is not 1 bpp. - * (2) This is not intended to work on small thumbnails. The - * dimensions of pixs must be at least MinWidth x MinHeight. - * (3) Debug: set showmorph to display the intermediate image in - * the morphological operations on this page. - * (4) Debug: to get pdf output of results when called repeatedly, - * call with an existing pixac, which will add an image of this page, - * with the fg outlined. If no foreground is found, there is - * no output for this page image. - *- */ -BOX * -pixFindPageForeground(PIX *pixs, - l_int32 threshold, - l_int32 mindist, - l_int32 erasedist, - l_int32 showmorph, - PIXAC *pixac) -{ -l_int32 flag, nbox, intersects; -l_int32 w, h, bx, by, bw, bh, left, right, top, bottom; -PIX *pixb, *pixb2, *pixseed, *pixsf, *pixm, *pix1, *pixg2; -BOX *box, *boxfg, *boxin, *boxd; -BOXA *ba1, *ba2; - - PROCNAME("pixFindPageForeground"); - - if (!pixs) - return (BOX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - if (w < MinWidth || h < MinHeight) { - L_ERROR("pix too small: w = %d, h = %d\n", procName, w, h); - return NULL; - } - - /* Binarize, downscale by 0.5, remove the noise to generate a seed, - * and do a seedfill back from the seed into those 8-connected - * components of the binarized image for which there was at least - * one seed pixel. Also clear out any components that are within - * 10 pixels of the edge at 2x reduction. */ - flag = (showmorph) ? 100 : 0; - pixb = pixConvertTo1(pixs, threshold); - pixb2 = pixScale(pixb, 0.5, 0.5); - pixseed = pixMorphSequence(pixb2, "o1.2 + c9.9 + o3.3", flag); - pix1 = pixMorphSequence(pixb2, "o50.1", 0); - pixOr(pixseed, pixseed, pix1); - pixDestroy(&pix1); - pix1 = pixMorphSequence(pixb2, "o1.50", 0); - pixOr(pixseed, pixseed, pix1); - pixDestroy(&pix1); - pixsf = pixSeedfillBinary(NULL, pixseed, pixb2, 8); - pixSetOrClearBorder(pixsf, 10, 10, 10, 10, PIX_SET); - pixm = pixRemoveBorderConnComps(pixsf, 8); - - /* Now, where is the main block of text? We want to remove noise near - * the edge of the image, but to do that, we have to be convinced that - * (1) there is noise and (2) it is far enough from the text block - * and close enough to the edge. For each edge, if the block - * is more than mindist from that edge, then clean 'erasedist' - * pixels from the edge. */ - pix1 = pixMorphSequence(pixm, "c50.50", flag); - ba1 = pixConnComp(pix1, NULL, 8); - ba2 = boxaSort(ba1, L_SORT_BY_AREA, L_SORT_DECREASING, NULL); - pixGetDimensions(pix1, &w, &h, NULL); - nbox = boxaGetCount(ba2); - if (nbox > 1) { - box = boxaGetBox(ba2, 0, L_CLONE); - boxGetGeometry(box, &bx, &by, &bw, &bh); - left = (bx > mindist) ? erasedist : 0; - right = (w - bx - bw > mindist) ? erasedist : 0; - top = (by > mindist) ? erasedist : 0; - bottom = (h - by - bh > mindist) ? erasedist : 0; - pixSetOrClearBorder(pixm, left, right, top, bottom, PIX_CLR); - boxDestroy(&box); - } - pixDestroy(&pix1); - boxaDestroy(&ba1); - boxaDestroy(&ba2); - - /* Locate the foreground region; don't bother cropping */ - pixClipToForeground(pixm, NULL, &boxfg); - - /* Sanity check the fg region. Make sure it's not confined - * to a thin boundary on the left and right sides of the image, - * in which case it is likely to be noise. */ - if (boxfg) { - boxin = boxCreate(0.1 * w, 0, 0.8 * w, h); - boxIntersects(boxfg, boxin, &intersects); - boxDestroy(&boxin); - if (!intersects) boxDestroy(&boxfg); - } - - boxd = NULL; - if (boxfg) { - boxAdjustSides(boxfg, boxfg, -2, 2, -2, 2); /* tiny expansion */ - boxd = boxTransform(boxfg, 0, 0, 2.0, 2.0); - - /* Save the debug image showing the box for this page */ - if (pixac) { - pixg2 = pixConvert1To4Cmap(pixb); - pixRenderBoxArb(pixg2, boxd, 3, 255, 0, 0); - pixacompAddPix(pixac, pixg2, IFF_DEFAULT); - pixDestroy(&pixg2); - } - } - - pixDestroy(&pixb); - pixDestroy(&pixb2); - pixDestroy(&pixseed); - pixDestroy(&pixsf); - pixDestroy(&pixm); - boxDestroy(&boxfg); - return boxd; -} - - -/*------------------------------------------------------------------* - * Extraction of characters from image with only text * - *------------------------------------------------------------------*/ -/*! - * \brief pixSplitIntoCharacters() - * - * \param[in] pixs 1 bpp, contains only deskewed text - * \param[in] minw min component width for initial filtering; typ. 4 - * \param[in] minh min component height for initial filtering; typ. 4 - * \param[out] pboxa [optional] character bounding boxes - * \param[out] ppixa [optional] character images - * \param[out] ppixdebug [optional] showing splittings - * - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is a simple function that attempts to find split points - * based on vertical pixel profiles. - * (2) It should be given an image that has an arbitrary number - * of text characters. - * (3) The returned pixa includes the boxes from which the - * (possibly split) components are extracted. - *- */ -l_ok -pixSplitIntoCharacters(PIX *pixs, - l_int32 minw, - l_int32 minh, - BOXA **pboxa, - PIXA **ppixa, - PIX **ppixdebug) -{ -l_int32 ncomp, i, xoff, yoff; -BOXA *boxa1, *boxa2, *boxat1, *boxat2, *boxad; -BOXAA *baa; -PIX *pix, *pix1, *pix2, *pixdb; -PIXA *pixa1, *pixadb; - - PROCNAME("pixSplitIntoCharacters"); - - if (pboxa) *pboxa = NULL; - if (ppixa) *ppixa = NULL; - if (ppixdebug) *ppixdebug = NULL; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - - /* Remove the small stuff */ - pix1 = pixSelectBySize(pixs, minw, minh, 8, L_SELECT_IF_BOTH, - L_SELECT_IF_GT, NULL); - - /* Small vertical close for consolidation */ - pix2 = pixMorphSequence(pix1, "c1.10", 0); - pixDestroy(&pix1); - - /* Get the 8-connected components */ - boxa1 = pixConnComp(pix2, &pixa1, 8); - pixDestroy(&pix2); - boxaDestroy(&boxa1); - - /* Split the components if obvious */ - ncomp = pixaGetCount(pixa1); - boxa2 = boxaCreate(ncomp); - pixadb = (ppixdebug) ? pixaCreate(ncomp) : NULL; - for (i = 0; i < ncomp; i++) { - pix = pixaGetPix(pixa1, i, L_CLONE); - if (ppixdebug) { - boxat1 = pixSplitComponentWithProfile(pix, 10, 7, &pixdb); - if (pixdb) - pixaAddPix(pixadb, pixdb, L_INSERT); - } else { - boxat1 = pixSplitComponentWithProfile(pix, 10, 7, NULL); - } - pixaGetBoxGeometry(pixa1, i, &xoff, &yoff, NULL, NULL); - boxat2 = boxaTransform(boxat1, xoff, yoff, 1.0, 1.0); - boxaJoin(boxa2, boxat2, 0, -1); - pixDestroy(&pix); - boxaDestroy(&boxat1); - boxaDestroy(&boxat2); - } - pixaDestroy(&pixa1); - - /* Generate the debug image */ - if (ppixdebug) { - if (pixaGetCount(pixadb) > 0) { - *ppixdebug = pixaDisplayTiledInRows(pixadb, 32, 1500, - 1.0, 0, 20, 1); - } - pixaDestroy(&pixadb); - } - - /* Do a 2D sort on the bounding boxes, and flatten the result to 1D */ - baa = boxaSort2d(boxa2, NULL, 0, 0, 5); - boxad = boxaaFlattenToBoxa(baa, NULL, L_CLONE); - boxaaDestroy(&baa); - boxaDestroy(&boxa2); - - /* Optionally extract the pieces from the input image */ - if (ppixa) - *ppixa = pixClipRectangles(pixs, boxad); - if (pboxa) - *pboxa = boxad; - else - boxaDestroy(&boxad); - return 0; -} - - -/*! - * \brief pixSplitComponentWithProfile() - * - * \param[in] pixs 1 bpp, exactly one connected component - * \param[in] delta distance used in extrema finding in a numa; typ. 10 - * \param[in] mindel minimum required difference between profile - * minimum and profile values +2 and -2 away; typ. 7 - * \param[out] ppixdebug [optional] debug image of splitting - * \return boxa of c.c. after splitting, or NULL on error - * - *
- * Notes: - * (1) This will split the most obvious cases of touching characters. - * The split points it is searching for are narrow and deep - * minimima in the vertical pixel projection profile, after a - * large vertical closing has been applied to the component. - *- */ -BOXA * -pixSplitComponentWithProfile(PIX *pixs, - l_int32 delta, - l_int32 mindel, - PIX **ppixdebug) -{ -l_int32 w, h, n2, i, firstmin, xmin, xshift; -l_int32 nmin, nleft, nright, nsplit, isplit, ncomp; -l_int32 *array1, *array2; -BOX *box; -BOXA *boxad; -NUMA *na1, *na2, *nasplit; -PIX *pix1, *pixdb; - - PROCNAME("pixSplitComponentsWithProfile"); - - if (ppixdebug) *ppixdebug = NULL; - if (!pixs || pixGetDepth(pixs) != 1) - return (BOXA *)ERROR_PTR("pixa undefined or not 1 bpp", procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - - /* Closing to consolidate characters vertically */ - pix1 = pixCloseSafeBrick(NULL, pixs, 1, 100); - - /* Get extrema of column projections */ - boxad = boxaCreate(2); - na1 = pixCountPixelsByColumn(pix1); /* w elements */ - pixDestroy(&pix1); - na2 = numaFindExtrema(na1, delta, NULL); - n2 = numaGetCount(na2); - if (n2 < 3) { /* no split possible */ - box = boxCreate(0, 0, w, h); - boxaAddBox(boxad, box, L_INSERT); - numaDestroy(&na1); - numaDestroy(&na2); - return boxad; - } - - /* Look for sufficiently deep and narrow minima. - * All minima of of interest must be surrounded by max on each - * side. firstmin is the index of first possible minimum. */ - array1 = numaGetIArray(na1); - array2 = numaGetIArray(na2); - if (ppixdebug) numaWriteStderr(na2); - firstmin = (array1[array2[0]] > array1[array2[1]]) ? 1 : 2; - nasplit = numaCreate(n2); /* will hold split locations */ - for (i = firstmin; i < n2 - 1; i+= 2) { - xmin = array2[i]; - nmin = array1[xmin]; - if (xmin + 2 >= w) break; /* no more splits possible */ - nleft = array1[xmin - 2]; - nright = array1[xmin + 2]; - if (ppixdebug) { - lept_stderr( - "Splitting: xmin = %d, w = %d; nl = %d, nmin = %d, nr = %d\n", - xmin, w, nleft, nmin, nright); - } - if (nleft - nmin >= mindel && nright - nmin >= mindel) /* split */ - numaAddNumber(nasplit, xmin); - } - nsplit = numaGetCount(nasplit); - -#if 0 - if (ppixdebug && nsplit > 0) { - lept_mkdir("lept/split"); - gplotSimple1(na1, GPLOT_PNG, "/tmp/lept/split/split", NULL); - } -#endif - - numaDestroy(&na1); - numaDestroy(&na2); - LEPT_FREE(array1); - LEPT_FREE(array2); - - if (nsplit == 0) { /* no splitting */ - numaDestroy(&nasplit); - box = boxCreate(0, 0, w, h); - boxaAddBox(boxad, box, L_INSERT); - return boxad; - } - - /* Use split points to generate b.b. after splitting */ - for (i = 0, xshift = 0; i < nsplit; i++) { - numaGetIValue(nasplit, i, &isplit); - box = boxCreate(xshift, 0, isplit - xshift, h); - boxaAddBox(boxad, box, L_INSERT); - xshift = isplit + 1; - } - box = boxCreate(xshift, 0, w - xshift, h); - boxaAddBox(boxad, box, L_INSERT); - numaDestroy(&nasplit); - - if (ppixdebug) { - pixdb = pixConvertTo32(pixs); - ncomp = boxaGetCount(boxad); - for (i = 0; i < ncomp; i++) { - box = boxaGetBox(boxad, i, L_CLONE); - pixRenderBoxBlend(pixdb, box, 1, 255, 0, 0, 0.5); - boxDestroy(&box); - } - *ppixdebug = pixdb; - } - - return boxad; -} - - -/*------------------------------------------------------------------* - * Extraction of lines of text * - *------------------------------------------------------------------*/ -/*! - * \brief pixExtractTextlines() - * - * \param[in] pixs any depth, assumed to have nearly horizontal text - * \param[in] maxw, maxh initial filtering: remove any components in pixs - * with components larger than maxw or maxh - * \param[in] minw, minh final filtering: remove extracted 'lines' - * with sizes smaller than minw or minh; use - * 0 for default. - * \param[in] adjw, adjh final adjustment of boxes representing each - * text line. If > 0, these increase the box - * size at each edge by this amount. - * \param[in] pixadb pixa for saving intermediate steps; NULL to omit - * \return pixa of textline images, including bounding boxes, or - * NULL on error - * - *
- * Notes: - * (1) This function assumes that textline fragments have sufficient - * vertical separation and small enough skew so that a - * horizontal dilation sufficient to join words will not join - * textlines. It does not guarantee that horizontally adjacent - * textline fragments on the same line will be joined. - * (2) For images with multiple columns, it attempts to avoid joining - * textlines across the space between columns. If that is not - * a concern, you can also use pixExtractRawTextlines(), - * which will join them with alacrity. - * (3) This first removes components from pixs that are either - * wide (> %maxw) or tall (> %maxh). - * (4) A final filtering operation removes small components, such - * that width < %minw or height < %minh. - * (5) For reasonable accuracy, the resolution of pixs should be - * at least 100 ppi. For reasonable efficiency, the resolution - * should not exceed 600 ppi. - * (6) This can be used to determine if some region of a scanned - * image is horizontal text. - * (7) As an example, for a pix with resolution 300 ppi, a reasonable - * set of parameters is: - * pixExtractTextlines(pix, 150, 150, 36, 20, 5, 5, NULL); - * The defaults minw and minh for 300 ppi are about 36 and 20, - * so the same result is obtained with: - * pixExtractTextlines(pix, 150, 150, 0, 0, 5, 5, NULL); - * (8) The output pixa is composed of subimages, one for each textline, - * and the boxa in the pixa tells where in %pixs each textline goes. - *- */ -PIXA * -pixExtractTextlines(PIX *pixs, - l_int32 maxw, - l_int32 maxh, - l_int32 minw, - l_int32 minh, - l_int32 adjw, - l_int32 adjh, - PIXA *pixadb) -{ -char buf[64]; -l_int32 res, csize, empty; -BOXA *boxa1, *boxa2, *boxa3; -PIX *pix1, *pix2, *pix3; -PIXA *pixa1, *pixa2, *pixa3; - - PROCNAME("pixExtractTextlines"); - - if (!pixs) - return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); - - /* Binarize carefully, if necessary */ - if (pixGetDepth(pixs) > 1) { - pix2 = pixConvertTo8(pixs, FALSE); - pix3 = pixCleanBackgroundToWhite(pix2, NULL, NULL, 1.0, 70, 190); - pix1 = pixThresholdToBinary(pix3, 150); - pixDestroy(&pix2); - pixDestroy(&pix3); - } else { - pix1 = pixClone(pixs); - } - pixZero(pix1, &empty); - if (empty) { - pixDestroy(&pix1); - L_INFO("no fg pixels in input image\n", procName); - return NULL; - } - if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); - - /* Remove any very tall or very wide connected components */ - pix2 = pixSelectBySize(pix1, maxw, maxh, 8, L_SELECT_IF_BOTH, - L_SELECT_IF_LT, NULL); - if (pixadb) pixaAddPix(pixadb, pix2, L_COPY); - pixDestroy(&pix1); - - /* Filter to solidify the text lines within the x-height region. - * The closing (csize) bridges gaps between words. The opening - * removes isolated bridges between textlines. */ - if ((res = pixGetXRes(pixs)) == 0) { - L_INFO("Resolution is not set: setting to 300 ppi\n", procName); - res = 300; - } - csize = L_MIN(120., 60.0 * res / 300.0); - snprintf(buf, sizeof(buf), "c%d.1 + o%d.1", csize, csize / 3); - pix3 = pixMorphCompSequence(pix2, buf, 0); - if (pixadb) pixaAddPix(pixadb, pix3, L_COPY); - - /* Extract the connected components. These should be dilated lines */ - boxa1 = pixConnComp(pix3, &pixa1, 4); - if (pixadb) { - pix1 = pixaDisplayRandomCmap(pixa1, 0, 0); - pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255); - pixaAddPix(pixadb, pix1, L_INSERT); - } - - /* Set minw, minh if default is requested */ - minw = (minw != 0) ? minw : (l_int32)(0.12 * res); - minh = (minh != 0) ? minh : (l_int32)(0.07 * res); - - /* Remove line components that are too small */ - pixa2 = pixaSelectBySize(pixa1, minw, minh, L_SELECT_IF_BOTH, - L_SELECT_IF_GTE, NULL); - if (pixadb) { - pix1 = pixaDisplayRandomCmap(pixa2, 0, 0); - pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255); - pixaAddPix(pixadb, pix1, L_INSERT); - pix1 = pixConvertTo32(pix2); - pixRenderBoxaArb(pix1, pixa2->boxa, 2, 255, 0, 0); - pixaAddPix(pixadb, pix1, L_INSERT); - } - - /* Selectively AND with the version before dilation, and save */ - boxa2 = pixaGetBoxa(pixa2, L_CLONE); - boxa3 = boxaAdjustSides(boxa2, -adjw, adjw, -adjh, adjh); - pixa3 = pixClipRectangles(pix2, boxa3); - if (pixadb) { - pix1 = pixaDisplayRandomCmap(pixa3, 0, 0); - pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255); - pixaAddPix(pixadb, pix1, L_INSERT); - } - - pixDestroy(&pix2); - pixDestroy(&pix3); - pixaDestroy(&pixa1); - pixaDestroy(&pixa2); - boxaDestroy(&boxa1); - boxaDestroy(&boxa2); - boxaDestroy(&boxa3); - return pixa3; -} - - -/*! - * \brief pixExtractRawTextlines() - * - * \param[in] pixs any depth, assumed to have nearly horizontal text - * \param[in] maxw, maxh initial filtering: remove any components in pixs - * with components larger than maxw or maxh; - * use 0 for default values. - * \param[in] adjw, adjh final adjustment of boxes representing each - * text line. If > 0, these increase the box - * size at each edge by this amount. - * \param[in] pixadb pixa for saving intermediate steps; NULL to omit - * \return pixa of textline images, including bounding boxes, or - * NULL on error - * - *
- * Notes: - * (1) This function assumes that textlines have sufficient - * vertical separation and small enough skew so that a - * horizontal dilation sufficient to join words will not join - * textlines. It aggressively joins textlines across multiple - * columns, so if that is not desired, you must either (a) make - * sure that %pixs is a single column of text or (b) use instead - * pixExtractTextlines(), which is more conservative - * about joining text fragments that have vertical overlap. - * (2) This first removes components from pixs that are either - * very wide (> %maxw) or very tall (> %maxh). - * (3) For reasonable accuracy, the resolution of pixs should be - * at least 100 ppi. For reasonable efficiency, the resolution - * should not exceed 600 ppi. - * (4) This can be used to determine if some region of a scanned - * image is horizontal text. - * (5) As an example, for a pix with resolution 300 ppi, a reasonable - * set of parameters is: - * pixExtractRawTextlines(pix, 150, 150, 0, 0, NULL); - * (6) The output pixa is composed of subimages, one for each textline, - * and the boxa in the pixa tells where in %pixs each textline goes. - *- */ -PIXA * -pixExtractRawTextlines(PIX *pixs, - l_int32 maxw, - l_int32 maxh, - l_int32 adjw, - l_int32 adjh, - PIXA *pixadb) -{ -char buf[64]; -l_int32 res, csize, empty; -BOXA *boxa1, *boxa2, *boxa3; -BOXAA *baa1; -PIX *pix1, *pix2, *pix3; -PIXA *pixa1, *pixa2; - - PROCNAME("pixExtractRawTextlines"); - - if (!pixs) - return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); - - /* Set maxw, maxh if default is requested */ - if ((res = pixGetXRes(pixs)) == 0) { - L_INFO("Resolution is not set: setting to 300 ppi\n", procName); - res = 300; - } - maxw = (maxw != 0) ? maxw : (l_int32)(0.5 * res); - maxh = (maxh != 0) ? maxh : (l_int32)(0.5 * res); - - /* Binarize carefully, if necessary */ - if (pixGetDepth(pixs) > 1) { - pix2 = pixConvertTo8(pixs, FALSE); - pix3 = pixCleanBackgroundToWhite(pix2, NULL, NULL, 1.0, 70, 190); - pix1 = pixThresholdToBinary(pix3, 150); - pixDestroy(&pix2); - pixDestroy(&pix3); - } else { - pix1 = pixClone(pixs); - } - pixZero(pix1, &empty); - if (empty) { - pixDestroy(&pix1); - L_INFO("no fg pixels in input image\n", procName); - return NULL; - } - if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); - - /* Remove any very tall or very wide connected components */ - pix2 = pixSelectBySize(pix1, maxw, maxh, 8, L_SELECT_IF_BOTH, - L_SELECT_IF_LT, NULL); - if (pixadb) pixaAddPix(pixadb, pix2, L_COPY); - pixDestroy(&pix1); - - /* Filter to solidify the text lines within the x-height region. - * The closing (csize) bridges gaps between words. */ - csize = L_MIN(120., 60.0 * res / 300.0); - snprintf(buf, sizeof(buf), "c%d.1", csize); - pix3 = pixMorphCompSequence(pix2, buf, 0); - if (pixadb) pixaAddPix(pixadb, pix3, L_COPY); - - /* Extract the connected components. These should be dilated lines */ - boxa1 = pixConnComp(pix3, &pixa1, 4); - if (pixadb) { - pix1 = pixaDisplayRandomCmap(pixa1, 0, 0); - pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255); - pixaAddPix(pixadb, pix1, L_INSERT); - } - - /* Do a 2-d sort, and generate a bounding box for each set of text - * line segments that is aligned horizontally (i.e., has vertical - * overlap) into a box representing a single text line. */ - baa1 = boxaSort2d(boxa1, NULL, -1, -1, 5); - boxaaGetExtent(baa1, NULL, NULL, NULL, &boxa2); - if (pixadb) { - pix1 = pixConvertTo32(pix2); - pixRenderBoxaArb(pix1, boxa2, 2, 255, 0, 0); - pixaAddPix(pixadb, pix1, L_INSERT); - } - - /* Optionally adjust the sides of each text line box, and then - * use the boxes to generate a pixa of the text lines. */ - boxa3 = boxaAdjustSides(boxa2, -adjw, adjw, -adjh, adjh); - pixa2 = pixClipRectangles(pix2, boxa3); - if (pixadb) { - pix1 = pixaDisplayRandomCmap(pixa2, 0, 0); - pixcmapResetColor(pixGetColormap(pix1), 0, 255, 255, 255); - pixaAddPix(pixadb, pix1, L_INSERT); - } - - pixDestroy(&pix2); - pixDestroy(&pix3); - pixaDestroy(&pixa1); - boxaDestroy(&boxa1); - boxaDestroy(&boxa2); - boxaDestroy(&boxa3); - boxaaDestroy(&baa1); - return pixa2; -} - - -/*------------------------------------------------------------------* - * How many text columns * - *------------------------------------------------------------------*/ -/*! - * \brief pixCountTextColumns() - * - * \param[in] pixs 1 bpp - * \param[in] deltafract fraction of (max - min) to be used in the delta - * for extrema finding; typ 0.3 - * \param[in] peakfract fraction of (max - min) to be used to threshold - * the peak value; typ. 0.5 - * \param[in] clipfract fraction of image dimension removed on each side; - * typ. 0.1, which leaves w and h reduced by 0.8 - * \param[out] pncols number of columns; -1 if not determined - * \param[in] pixadb [optional] pre-allocated, for showing - * intermediate computation; use null to skip - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) It is assumed that pixs has the correct resolution set. - * If the resolution is 0, we set to 300 and issue a warning. - * (2) If necessary, the image is scaled to between 37 and 75 ppi; - * most of the processing is done at this resolution. - * (3) If no text is found (essentially a blank page), - * this returns ncols = 0. - * (4) For debug output, input a pre-allocated pixa. - *- */ -l_ok -pixCountTextColumns(PIX *pixs, - l_float32 deltafract, - l_float32 peakfract, - l_float32 clipfract, - l_int32 *pncols, - PIXA *pixadb) -{ -l_int32 w, h, res, i, n, npeak; -l_float32 scalefact, redfact, minval, maxval, val4, val5, fract; -BOX *box; -NUMA *na1, *na2, *na3, *na4, *na5; -PIX *pix1, *pix2, *pix3, *pix4, *pix5; - - PROCNAME("pixCountTextColumns"); - - if (!pncols) - return ERROR_INT("&ncols not defined", procName, 1); - *pncols = -1; /* init */ - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (deltafract < 0.15 || deltafract > 0.75) - L_WARNING("deltafract not in [0.15 ... 0.75]\n", procName); - if (peakfract < 0.25 || peakfract > 0.9) - L_WARNING("peakfract not in [0.25 ... 0.9]\n", procName); - if (clipfract < 0.0 || clipfract >= 0.5) - return ERROR_INT("clipfract not in [0.0 ... 0.5)\n", procName, 1); - if (pixadb) pixaAddPix(pixadb, pixs, L_COPY); - - /* Scale to between 37.5 and 75 ppi */ - if ((res = pixGetXRes(pixs)) == 0) { - L_WARNING("resolution undefined; set to 300\n", procName); - pixSetResolution(pixs, 300, 300); - res = 300; - } - if (res < 37) { - L_WARNING("resolution %d very low\n", procName, res); - scalefact = 37.5 / res; - pix1 = pixScale(pixs, scalefact, scalefact); - } else { - redfact = (l_float32)res / 37.5; - if (redfact < 2.0) - pix1 = pixClone(pixs); - else if (redfact < 4.0) - pix1 = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0); - else if (redfact < 8.0) - pix1 = pixReduceRankBinaryCascade(pixs, 1, 2, 0, 0); - else if (redfact < 16.0) - pix1 = pixReduceRankBinaryCascade(pixs, 1, 2, 2, 0); - else - pix1 = pixReduceRankBinaryCascade(pixs, 1, 2, 2, 2); - } - if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); - - /* Crop inner 80% of image */ - pixGetDimensions(pix1, &w, &h, NULL); - box = boxCreate(clipfract * w, clipfract * h, - (1.0 - 2 * clipfract) * w, (1.0 - 2 * clipfract) * h); - pix2 = pixClipRectangle(pix1, box, NULL); - pixGetDimensions(pix2, &w, &h, NULL); - boxDestroy(&box); - if (pixadb) pixaAddPix(pixadb, pix2, L_COPY); - - /* Deskew */ - pix3 = pixDeskew(pix2, 0); - if (pixadb) pixaAddPix(pixadb, pix3, L_COPY); - - /* Close to increase column counts for text */ - pix4 = pixCloseSafeBrick(NULL, pix3, 5, 21); - if (pixadb) pixaAddPix(pixadb, pix4, L_COPY); - pixInvert(pix4, pix4); - na1 = pixCountByColumn(pix4, NULL); - - if (pixadb) { - gplotSimple1(na1, GPLOT_PNG, "/tmp/lept/plot", NULL); - pix5 = pixRead("/tmp/lept/plot.png"); - pixaAddPix(pixadb, pix5, L_INSERT); - } - - /* Analyze the column counts. na4 gives the locations of - * the extrema in normalized units (0.0 to 1.0) across the - * cropped image. na5 gives the magnitude of the - * extrema, normalized to the dynamic range. The peaks - * are values that are at least peakfract of (max - min). */ - numaGetMax(na1, &maxval, NULL); - numaGetMin(na1, &minval, NULL); - fract = (l_float32)(maxval - minval) / h; /* is there much at all? */ - if (fract < 0.05) { - L_INFO("very little content on page; 0 text columns\n", procName); - *pncols = 0; - } else { - na2 = numaFindExtrema(na1, deltafract * (maxval - minval), &na3); - na4 = numaTransform(na2, 0, 1.0 / w); - na5 = numaTransform(na3, -minval, 1.0 / (maxval - minval)); - n = numaGetCount(na4); - for (i = 0, npeak = 0; i < n; i++) { - numaGetFValue(na4, i, &val4); - numaGetFValue(na5, i, &val5); - if (val4 > 0.3 && val4 < 0.7 && val5 >= peakfract) { - npeak++; - L_INFO("Peak(loc,val) = (%5.3f,%5.3f)\n", procName, val4, val5); - } - } - *pncols = npeak + 1; - numaDestroy(&na2); - numaDestroy(&na3); - numaDestroy(&na4); - numaDestroy(&na5); - } - - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - pixDestroy(&pix4); - numaDestroy(&na1); - return 0; -} - - -/*------------------------------------------------------------------* - * Decision text vs photo * - *------------------------------------------------------------------*/ -/*! - * \brief pixDecideIfText() - * - * \param[in] pixs any depth - * \param[in] box [optional] if null, use entire pixs - * \param[out] pistext 1 if text; 0 if photo; -1 if not determined or empty - * \param[in] pixadb [optional] pre-allocated, for showing intermediate - * computation; use NULL to skip - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) It is assumed that pixs has the correct resolution set. - * If the resolution is 0, we set to 300 and issue a warning. - * (2) If necessary, the image is scaled to 300 ppi; most of the - * processing is done at this resolution. - * (3) Text is assumed to be in horizontal lines. - * (4) Because thin vertical lines are removed before filtering for - * text lines, this should identify tables as text. - * (5) If %box is null and pixs contains both text lines and line art, - * this function might return %istext == true. - * (6) If the input pixs is empty, or for some other reason the - * result can not be determined, return -1. - * (7) For debug output, input a pre-allocated pixa. - *- */ -l_ok -pixDecideIfText(PIX *pixs, - BOX *box, - l_int32 *pistext, - PIXA *pixadb) -{ -l_int32 i, empty, maxw, w, h, n1, n2, n3, minlines, big_comp; -l_float32 ratio1, ratio2; -L_BMF *bmf; -BOXA *boxa1, *boxa2, *boxa3, *boxa4, *boxa5; -PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7; -PIXA *pixa1; -SEL *sel1; - - PROCNAME("pixDecideIfText"); - - if (!pistext) - return ERROR_INT("&istext not defined", procName, 1); - *pistext = -1; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - - /* Crop, convert to 1 bpp, 300 ppi */ - if ((pix1 = pixPrepare1bpp(pixs, box, 0.1, 300)) == NULL) - return ERROR_INT("pix1 not made", procName, 1); - - pixZero(pix1, &empty); - if (empty) { - pixDestroy(&pix1); - L_INFO("pix is empty\n", procName); - return 0; - } - w = pixGetWidth(pix1); - - /* Identify and remove tall, thin vertical lines (as found in tables) - * that are up to 9 pixels wide. Make a hit-miss sel with an - * 81 pixel vertical set of hits and with 3 pairs of misses that - * are 10 pixels apart horizontally. It is necessary to use a - * hit-miss transform; if we only opened with a vertical line of - * hits, we would remove solid regions of pixels that are not - * text or vertical lines. */ - pix2 = pixCreate(11, 81, 1); - for (i = 0; i < 81; i++) - pixSetPixel(pix2, 5, i, 1); - sel1 = selCreateFromPix(pix2, 40, 5, NULL); - selSetElement(sel1, 20, 0, SEL_MISS); - selSetElement(sel1, 20, 10, SEL_MISS); - selSetElement(sel1, 40, 0, SEL_MISS); - selSetElement(sel1, 40, 10, SEL_MISS); - selSetElement(sel1, 60, 0, SEL_MISS); - selSetElement(sel1, 60, 10, SEL_MISS); - pix3 = pixHMT(NULL, pix1, sel1); - pix4 = pixSeedfillBinaryRestricted(NULL, pix3, pix1, 8, 5, 1000); - pix5 = pixXor(NULL, pix1, pix4); - pixDestroy(&pix2); - selDestroy(&sel1); - - /* Convert the text lines to separate long horizontal components */ - pix6 = pixMorphCompSequence(pix5, "c30.1 + o15.1 + c60.1 + o2.2", 0); - - /* Estimate the distance to the bottom of the significant region */ - if (box) { /* use full height */ - pixGetDimensions(pix6, NULL, &h, NULL); - } else { /* use height of region that has text lines */ - pixFindThreshFgExtent(pix6, 400, NULL, &h); - } - - if (pixadb) { - bmf = bmfCreate(NULL, 6); - pixaAddPixWithText(pixadb, pix1, 1, bmf, "threshold/crop to binary", - 0x0000ff00, L_ADD_BELOW); - pixaAddPixWithText(pixadb, pix3, 2, bmf, "hit-miss for vertical line", - 0x0000ff00, L_ADD_BELOW); - pixaAddPixWithText(pixadb, pix4, 2, bmf, "restricted seed-fill", - 0x0000ff00, L_ADD_BELOW); - pixaAddPixWithText(pixadb, pix5, 2, bmf, "remove using xor", - 0x0000ff00, L_ADD_BELOW); - pixaAddPixWithText(pixadb, pix6, 2, bmf, "make long horiz components", - 0x0000ff00, L_ADD_BELOW); - } - - /* Extract the connected components */ - if (pixadb) { - boxa1 = pixConnComp(pix6, &pixa1, 8); - pix7 = pixaDisplayRandomCmap(pixa1, 0, 0); - pixcmapResetColor(pixGetColormap(pix7), 0, 255, 255, 255); - pixaAddPixWithText(pixadb, pix7, 2, bmf, "show connected components", - 0x0000ff00, L_ADD_BELOW); - pixDestroy(&pix7); - pixaDestroy(&pixa1); - bmfDestroy(&bmf); - } else { - boxa1 = pixConnComp(pix6, NULL, 8); - } - - /* Analyze the connected components. The following conditions - * at 300 ppi must be satisfied if the image is text: - * (1) There are no components that are wider than 400 pixels and - * taller than 175 pixels. - * (2) The second longest component is at least 60% of the - * (possibly cropped) image width. This catches images - * that don't have any significant content. - * (3) Of the components that are at least 40% of the length - * of the longest (n2), at least 80% of them must not exceed - * 60 pixels in height. - * (4) The number of those long, thin components (n3) must - * equal or exceed a minimum that scales linearly with the - * image height. - * Most images that are not text fail more than one of these - * conditions. */ - boxa2 = boxaSort(boxa1, L_SORT_BY_WIDTH, L_SORT_DECREASING, NULL); - boxaGetBoxGeometry(boxa2, 1, NULL, NULL, &maxw, NULL); /* 2nd longest */ - boxa3 = boxaSelectBySize(boxa1, 0.4 * maxw, 0, L_SELECT_WIDTH, - L_SELECT_IF_GTE, NULL); - boxa4 = boxaSelectBySize(boxa3, 0, 60, L_SELECT_HEIGHT, - L_SELECT_IF_LTE, NULL); - boxa5 = boxaSelectBySize(boxa1, 400, 175, L_SELECT_IF_BOTH, - L_SELECT_IF_GT, NULL); - big_comp = (boxaGetCount(boxa5) == 0) ? 0 : 1; - n1 = boxaGetCount(boxa1); - n2 = boxaGetCount(boxa3); - n3 = boxaGetCount(boxa4); - ratio1 = (l_float32)maxw / (l_float32)w; - ratio2 = (l_float32)n3 / (l_float32)n2; - minlines = L_MAX(2, h / 125); - if (big_comp || ratio1 < 0.6 || ratio2 < 0.8 || n3 < minlines) - *pistext = 0; - else - *pistext = 1; - if (pixadb) { - if (*pistext == 1) { - L_INFO("This is text: \n n1 = %d, n2 = %d, n3 = %d, " - "minlines = %d\n maxw = %d, ratio1 = %4.2f, h = %d, " - "big_comp = %d\n", procName, n1, n2, n3, minlines, - maxw, ratio1, h, big_comp); - } else { - L_INFO("This is not text: \n n1 = %d, n2 = %d, n3 = %d, " - "minlines = %d\n maxw = %d, ratio1 = %4.2f, h = %d, " - "big_comp = %d\n", procName, n1, n2, n3, minlines, - maxw, ratio1, h, big_comp); - } - } - - boxaDestroy(&boxa1); - boxaDestroy(&boxa2); - boxaDestroy(&boxa3); - boxaDestroy(&boxa4); - boxaDestroy(&boxa5); - pixDestroy(&pix1); - pixDestroy(&pix3); - pixDestroy(&pix4); - pixDestroy(&pix5); - pixDestroy(&pix6); - return 0; -} - - -/*! - * \brief pixFindThreshFgExtent() - * - * \param[in] pixs 1 bpp - * \param[in] thresh threshold number of pixels in row - * \param[out] ptop [optional] location of top of region - * \param[out] pbot [optional] location of bottom of region - * \return 0 if OK, 1 on error - */ -l_ok -pixFindThreshFgExtent(PIX *pixs, - l_int32 thresh, - l_int32 *ptop, - l_int32 *pbot) -{ -l_int32 i, n; -l_int32 *array; -NUMA *na; - - PROCNAME("pixFindThreshFgExtent"); - - if (ptop) *ptop = 0; - if (pbot) *pbot = 0; - if (!ptop && !pbot) - return ERROR_INT("nothing to determine", procName, 1); - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - - na = pixCountPixelsByRow(pixs, NULL); - n = numaGetCount(na); - array = numaGetIArray(na); - if (ptop) { - for (i = 0; i < n; i++) { - if (array[i] >= thresh) { - *ptop = i; - break; - } - } - } - if (pbot) { - for (i = n - 1; i >= 0; i--) { - if (array[i] >= thresh) { - *pbot = i; - break; - } - } - } - LEPT_FREE(array); - numaDestroy(&na); - return 0; -} - - -/*------------------------------------------------------------------* - * Decision: table vs text * - *------------------------------------------------------------------*/ -/*! - * \brief pixDecideIfTable() - * - * \param[in] pixs any depth, any resolution >= 75 ppi - * \param[in] box [optional] if null, use entire pixs - * \param[in] orient L_PORTRAIT_MODE, L_LANDSCAPE_MODE - * \param[out] pscore 0 - 4; -1 if not determined - * \param[in] pixadb [optional] pre-allocated, for showing intermediate - * computation; use NULL to skip - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) It is assumed that pixs has the correct resolution set. - * If the resolution is 0, we assume it is 300 ppi and issue a warning. - * (2) If %orient == L_LANDSCAPE_MODE, the image is rotated 90 degrees - * clockwise before being analyzed. - * (3) The interpretation of the returned score: - * -1 undetermined - * 0 no table - * 1 unlikely to have a table - * 2 likely to have a table - * 3 even more likely to have a table - * 4 extremely likely to have a table - * * Setting the condition for finding a table at score >= 2 works - * well, except for false positives on kanji and landscape text. - * * These false positives can be removed by setting the condition - * at score >= 3, but recall is lowered because it will not find - * tables without either horizontal or vertical lines. - * (4) Most of the processing takes place at 75 ppi. - * (5) Internally, three numbers are determined, for horizontal and - * vertical fg lines, and for vertical bg lines. From these, - * four tests are made to decide if there is a table occupying - * a significant part of the image. - * (6) Images have arbitrary content and would be likely to trigger - * this detector, so they are checked for first, and if found, - * return with a 0 (no table) score. - * (7) Musical scores (tablature) are likely to trigger the detector. - * (8) Tables of content with more than 2 columns are likely to - * trigger the detector. - * (9) For debug output, input a pre-allocated pixa. - *- */ -l_ok -pixDecideIfTable(PIX *pixs, - BOX *box, - l_int32 orient, - l_int32 *pscore, - PIXA *pixadb) -{ -l_int32 empty, nhb, nvb, nvw, score, htfound; -PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7, *pix8, *pix9; - - PROCNAME("pixDecideIfTable"); - - if (!pscore) - return ERROR_INT("&score not defined", procName, 1); - *pscore = -1; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - - /* Check if there is an image region. First convert to 1 bpp - * at 175 ppi. If an image is found, assume there is no table. */ - pix1 = pixPrepare1bpp(pixs, box, 0.1, 175); - pix2 = pixGenerateHalftoneMask(pix1, NULL, &htfound, NULL); - if (htfound && pixadb) pixaAddPix(pixadb, pix2, L_COPY); - pixDestroy(&pix1); - pixDestroy(&pix2); - if (htfound) { - *pscore = 0; - L_INFO("pix has an image region\n", procName); - return 0; - } - - /* Crop, convert to 1 bpp, 75 ppi */ - if ((pix1 = pixPrepare1bpp(pixs, box, 0.05, 75)) == NULL) - return ERROR_INT("pix1 not made", procName, 1); - - pixZero(pix1, &empty); - if (empty) { - *pscore = 0; - pixDestroy(&pix1); - L_INFO("pix is empty\n", procName); - return 0; - } - - /* The 2x2 dilation on 75 ppi makes these two approaches very similar: - * (1) pix1 = pixPrepare1bpp(..., 300); // 300 ppi resolution - * pix2 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0); - * (2) pix1 = pixPrepare1bpp(..., 75); // 75 ppi resolution - * pix2 = pixDilateBrick(NULL, pix1, 2, 2); - * But (2) is more efficient if the input image to pixPrepare1bpp() - * is not at 300 ppi. */ - pix2 = pixDilateBrick(NULL, pix1, 2, 2); - - /* Deskew both horizontally and vertically; rotate by 90 - * degrees if in landscape mode. */ - pix3 = pixDeskewBoth(pix2, 1); - if (pixadb) { - pixaAddPix(pixadb, pix2, L_COPY); - pixaAddPix(pixadb, pix3, L_COPY); - } - if (orient == L_LANDSCAPE_MODE) - pix4 = pixRotate90(pix3, 1); - else - pix4 = pixClone(pix3); - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - pix1 = pixClone(pix4); - pixDestroy(&pix4); - - /* Look for horizontal and vertical lines */ - pix2 = pixMorphSequence(pix1, "o100.1 + c1.4", 0); - pix3 = pixSeedfillBinary(NULL, pix2, pix1, 8); - pix4 = pixMorphSequence(pix1, "o1.100 + c4.1", 0); - pix5 = pixSeedfillBinary(NULL, pix4, pix1, 8); - pix6 = pixOr(NULL, pix3, pix5); - if (pixadb) { - pixaAddPix(pixadb, pix2, L_COPY); - pixaAddPix(pixadb, pix4, L_COPY); - pixaAddPix(pixadb, pix3, L_COPY); - pixaAddPix(pixadb, pix5, L_COPY); - pixaAddPix(pixadb, pix6, L_COPY); - } - pixCountConnComp(pix2, 8, &nhb); /* number of horizontal black lines */ - pixCountConnComp(pix4, 8, &nvb); /* number of vertical black lines */ - - /* Remove the lines */ - pixSubtract(pix1, pix1, pix6); - if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); - - /* Remove noise pixels */ - pix7 = pixMorphSequence(pix1, "c4.1 + o8.1", 0); - if (pixadb) pixaAddPix(pixadb, pix7, L_COPY); - - /* Look for vertical white space. Invert to convert white bg - * to fg. Use a single rank-1 2x reduction, which closes small - * fg holes, for the final processing at 37.5 ppi. - * The vertical opening is then about 3 inches on a 300 ppi image. - * We also remove vertical whitespace that is less than 5 pixels - * wide at this resolution (about 0.1 inches) */ - pixInvert(pix7, pix7); - pix8 = pixMorphSequence(pix7, "r1 + o1.100", 0); - pix9 = pixSelectBySize(pix8, 5, 0, 8, L_SELECT_WIDTH, - L_SELECT_IF_GTE, NULL); - pixCountConnComp(pix9, 8, &nvw); /* number of vertical white lines */ - if (pixadb) { - pixaAddPix(pixadb, pixScale(pix8, 2.0, 2.0), L_INSERT); - pixaAddPix(pixadb, pixScale(pix9, 2.0, 2.0), L_INSERT); - } - - /* Require at least 2 of the following 4 conditions for a table. - * Some tables do not have black (fg) lines, and for those we - * require more than 6 long vertical whitespace (bg) lines. */ - score = 0; - if (nhb > 1) score++; - if (nvb > 2) score++; - if (nvw > 3) score++; - if (nvw > 6) score++; - *pscore = score; - - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - pixDestroy(&pix4); - pixDestroy(&pix5); - pixDestroy(&pix6); - pixDestroy(&pix7); - pixDestroy(&pix8); - pixDestroy(&pix9); - return 0; -} - - -/*! - * \brief pixPrepare1bpp() - * - * \param[in] pixs any depth - * \param[in] box [optional] if null, use entire pixs - * \param[in] cropfract fraction to be removed from the boundary; - * use 0.0 to retain the entire image - * \param[in] outres desired resolution of output image; if the - * input image resolution is not set, assume - * 300 ppi; use 0 to skip scaling. - * \return pixd if OK, NULL on error - * - *
- * Notes: - * (1) This handles some common pre-processing operations, - * where the page segmentation algorithm takes a 1 bpp image. - *- */ -PIX * -pixPrepare1bpp(PIX *pixs, - BOX *box, - l_float32 cropfract, - l_int32 outres) -{ -l_int32 w, h, res; -l_float32 factor; -BOX *box1; -PIX *pix1, *pix2, *pix3, *pix4, *pix5; - - PROCNAME("pixPrepare1bpp"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - /* Crop the image. If no box is given, use %cropfract to remove - * pixels near the image boundary; this helps avoid false - * negatives from noise that is often found there. */ - if (box) { - pix1 = pixClipRectangle(pixs, box, NULL); - } else { - pixGetDimensions(pixs, &w, &h, NULL); - box1 = boxCreate((l_int32)(cropfract * w), (l_int32)(cropfract * h), - (l_int32)((1.0 - 2 * cropfract) * w), - (l_int32)((1.0 - 2 * cropfract) * h)); - pix1 = pixClipRectangle(pixs, box1, NULL); - boxDestroy(&box1); - } - - /* Convert to 1 bpp with adaptive background cleaning */ - if (pixGetDepth(pixs) > 1) { - pix2 = pixConvertTo8(pix1, 0); - pix3 = pixCleanBackgroundToWhite(pix2, NULL, NULL, 1.0, 70, 160); - pixDestroy(&pix1); - pixDestroy(&pix2); - if (!pix3) { - L_INFO("pix cleaning failed\n", procName); - return NULL; - } - pix4 = pixThresholdToBinary(pix3, 200); - pixDestroy(&pix3); - } else { - pix4 = pixClone(pix1); - pixDestroy(&pix1); - } - - /* Scale the image to the requested output resolution; - do not scale if %outres <= 0 */ - if (outres <= 0) - return pix4; - if ((res = pixGetXRes(pixs)) == 0) { - L_WARNING("Resolution is not set: using 300 ppi\n", procName); - res = 300; - } - if (res != outres) { - factor = (l_float32)outres / (l_float32)res; - pix5 = pixScale(pix4, factor, factor); - } else { - pix5 = pixClone(pix4); - } - pixDestroy(&pix4); - return pix5; -} - - -/*------------------------------------------------------------------* - * Estimate the grayscale background value * - *------------------------------------------------------------------*/ -/*! - * \brief pixEstimateBackground() - * - * \param[in] pixs 8 bpp, with or without colormap - * \param[in] darkthresh pixels below this value are never considered - * part of the background; typ. 70; use 0 to skip - * \param[in] edgecrop fraction of half-width on each side, and of - * half-height at top and bottom, that are cropped - * \param[out] pbg estimated background, or 0 on error - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) Caller should check that return bg value is > 0. - *- */ -l_ok -pixEstimateBackground(PIX *pixs, - l_int32 darkthresh, - l_float32 edgecrop, - l_int32 *pbg) -{ -l_int32 w, h, sampling; -l_float32 fbg; -BOX *box; -PIX *pix1, *pix2, *pixm; - - PROCNAME("pixEstimateBackground"); - - if (!pbg) - return ERROR_INT("&bg not defined", procName, 1); - *pbg = 0; - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); - if (darkthresh > 128) - L_WARNING("darkthresh unusually large\n", procName); - if (edgecrop < 0.0 || edgecrop >= 1.0) - return ERROR_INT("edgecrop not in [0.0 ... 1.0)", procName, 1); - - pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - pixGetDimensions(pix1, &w, &h, NULL); - - /* Optionally crop inner part of image */ - if (edgecrop > 0.0) { - box = boxCreate(0.5 * edgecrop * w, 0.5 * edgecrop * h, - (1.0 - edgecrop) * w, (1.0 - edgecrop) * h); - pix2 = pixClipRectangle(pix1, box, NULL); - boxDestroy(&box); - } else { - pix2 = pixClone(pix1); - } - - /* We will use no more than 50K samples */ - sampling = L_MAX(1, (l_int32)sqrt((l_float64)(w * h) / 50000. + 0.5)); - - /* Optionally make a mask over all pixels lighter than %darkthresh */ - pixm = NULL; - if (darkthresh > 0) { - pixm = pixThresholdToBinary(pix2, darkthresh); - pixInvert(pixm, pixm); - } - - pixGetRankValueMasked(pix2, pixm, 0, 0, sampling, 0.5, &fbg, NULL); - *pbg = (l_int32)(fbg + 0.5); - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pixm); - return 0; -} - - -/*---------------------------------------------------------------------* - * Largest white or black rectangles in an image * - *---------------------------------------------------------------------*/ -/*! - * \brief pixFindLargeRectangles() - * - * \param[in] pixs 1 bpp - * \param[in] polarity 0 within background, 1 within foreground - * \param[in] nrect number of rectangles to be found - * \param[out] pboxa largest rectangles, sorted by decreasing area - * \param[in,out] ppixdb optional return output with rectangles drawn on it - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This does a greedy search to find the largest rectangles, - * either black or white and without overlaps, in %pix. - * (2) See pixFindLargestRectangle(), which is called multiple - * times, for details. On each call, the largest rectangle - * found is painted, so that none of its pixels can be - * used later, before calling it again. - * (3) This function is surprisingly fast. Although - * pixFindLargestRectangle() runs at about 50 MPix/sec, when it - * is run multiple times by pixFindLargeRectangles(), it processes - * at 150 - 250 MPix/sec, and the time is approximately linear - * in %nrect. For example, for a 1 MPix image, searching for - * the largest 50 boxes takes about 0.2 seconds. - *- */ -l_ok -pixFindLargeRectangles(PIX *pixs, - l_int32 polarity, - l_int32 nrect, - BOXA **pboxa, - PIX **ppixdb) -{ -l_int32 i, op, bx, by, bw, bh; -BOX *box; -BOXA *boxa; -PIX *pix; - - PROCNAME("pixFindLargeRectangles"); - - if (ppixdb) *ppixdb = NULL; - if (!pboxa) - return ERROR_INT("&boxa not defined", procName, 1); - *pboxa = NULL; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (polarity != 0 && polarity != 1) - return ERROR_INT("invalid polarity", procName, 1); - if (nrect > 1000) { - L_WARNING("large num rectangles = %d requested; using 1000\n", - procName, nrect); - nrect = 1000; - } - - pix = pixCopy(NULL, pixs); - boxa = boxaCreate(nrect); - *pboxa = boxa; - - /* Sequentially find largest rectangle and fill with opposite color */ - for (i = 0; i < nrect; i++) { - if (pixFindLargestRectangle(pix, polarity, &box, NULL) == 1) { - boxDestroy(&box); - L_ERROR("failure in pixFindLargestRectangle\n", procName); - break; - } - boxaAddBox(boxa, box, L_INSERT); - op = (polarity == 0) ? PIX_SET : PIX_CLR; - boxGetGeometry(box, &bx, &by, &bw, &bh); - pixRasterop(pix, bx, by, bw, bh, op, NULL, 0, 0); - } - - if (ppixdb) - *ppixdb = pixDrawBoxaRandom(pixs, boxa, 3); - - pixDestroy(&pix); - return 0; -} - - -/*! - * \brief pixFindLargestRectangle() - * - * \param[in] pixs 1 bpp - * \param[in] polarity 0 within background, 1 within foreground - * \param[out] pbox largest area rectangle - * \param[in,out] ppixdb optional return output with rectangle drawn on it - * \return 0 if OK, 1 on error - * - *
- * Notes: - * (1) This is a simple and elegant solution to a problem in - * computational geometry that at first appears to be quite - * difficult: what is the largest rectangle that can be - * placed in the image, covering only pixels of one polarity - * (bg or fg)? The solution is O(n), where n is the number - * of pixels in the image, and it requires nothing more than - * using a simple recursion relation in a single sweep of the image. - * (2) In a sweep from UL to LR with left-to-right being the fast - * direction, calculate the largest white rectangle at (x, y), - * using previously calculated values at pixels #1 and #2: - * #1: (x, y - 1) - * #2: (x - 1, y) - * We also need the most recent "black" pixels that were seen - * in the current row and column. - * Consider the largest area. There are only two possibilities: - * (a) Min(w(1), horizdist) * (h(1) + 1) - * (b) Min(h(2), vertdist) * (w(2) + 1) - * where - * horizdist: the distance from the rightmost "black" pixel seen - * in the current row across to the current pixel - * vertdist: the distance from the lowest "black" pixel seen - * in the current column down to the current pixel - * and we choose the Max of (a) and (b). - * (3) To convince yourself that these recursion relations are correct, - * it helps to draw the maximum rectangles at #1 and #2. - * Then for #1, you try to extend the rectangle down one line, - * so that the height is h(1) + 1. Do you get the full - * width of #1, w(1)? It depends on where the black pixels are - * in the current row. You know the final width is bounded by w(1) - * and w(2) + 1, but the actual value depends on the distribution - * of black pixels in the current row that are at a distance - * from the current pixel that is between these limits. - * We call that value "horizdist", and the area is then given - * by the expression (a) above. Using similar reasoning for #2, - * where you attempt to extend the rectangle to the right - * by 1 pixel, you arrive at (b). The largest rectangle is - * then found by taking the Max. - *- */ -l_ok -pixFindLargestRectangle(PIX *pixs, - l_int32 polarity, - BOX **pbox, - PIX **ppixdb) -{ -l_int32 i, j, w, h, d, wpls, val; -l_int32 wp, hp, w1, w2, h1, h2, wmin, hmin, area1, area2; -l_int32 xmax, ymax; /* LR corner of the largest rectangle */ -l_int32 maxarea, wmax, hmax, vertdist, horizdist, prevfg; -l_int32 *lowestfg; -l_uint32 *datas, *lines; -l_uint32 **linew, **lineh; -BOX *box; -PIX *pixw, *pixh; /* keeps the width and height for the largest */ - /* rectangles whose LR corner is located there. */ - - PROCNAME("pixFindLargestRectangle"); - - if (ppixdb) *ppixdb = NULL; - if (!pbox) - return ERROR_INT("&box not defined", procName, 1); - *pbox = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 1) - return ERROR_INT("pixs not 1 bpp", procName, 1); - if (polarity != 0 && polarity != 1) - return ERROR_INT("invalid polarity", procName, 1); - - /* Initialize lowest "fg" seen so far for each column */ - lowestfg = (l_int32 *)LEPT_CALLOC(w, sizeof(l_int32)); - for (i = 0; i < w; i++) - lowestfg[i] = -1; - - /* The combination (val ^ polarity) is the color for which we - * are searching for the maximum rectangle. For polarity == 0, - * we search in the bg (white). */ - pixw = pixCreate(w, h, 32); /* stores width */ - pixh = pixCreate(w, h, 32); /* stores height */ - linew = (l_uint32 **)pixGetLinePtrs(pixw, NULL); - lineh = (l_uint32 **)pixGetLinePtrs(pixh, NULL); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - maxarea = xmax = ymax = wmax = hmax = 0; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - prevfg = -1; - for (j = 0; j < w; j++) { - val = GET_DATA_BIT(lines, j); - if ((val ^ polarity) == 0) { /* bg (0) if polarity == 0, etc. */ - if (i == 0 && j == 0) { - wp = hp = 1; - } else if (i == 0) { - wp = linew[i][j - 1] + 1; - hp = 1; - } else if (j == 0) { - wp = 1; - hp = lineh[i - 1][j] + 1; - } else { - /* Expand #1 prev rectangle down */ - w1 = linew[i - 1][j]; - h1 = lineh[i - 1][j]; - horizdist = j - prevfg; - wmin = L_MIN(w1, horizdist); /* width of new rectangle */ - area1 = wmin * (h1 + 1); - - /* Expand #2 prev rectangle to right */ - w2 = linew[i][j - 1]; - h2 = lineh[i][j - 1]; - vertdist = i - lowestfg[j]; - hmin = L_MIN(h2, vertdist); /* height of new rectangle */ - area2 = hmin * (w2 + 1); - - if (area1 > area2) { - wp = wmin; - hp = h1 + 1; - } else { - wp = w2 + 1; - hp = hmin; - } - } - } else { /* fg (1) if polarity == 0; bg (0) if polarity == 1 */ - prevfg = j; - lowestfg[j] = i; - wp = hp = 0; - } - linew[i][j] = wp; - lineh[i][j] = hp; - if (wp * hp > maxarea) { - maxarea = wp * hp; - xmax = j; - ymax = i; - wmax = wp; - hmax = hp; - } - } - } - - /* Translate from LR corner to Box coords (UL corner, w, h) */ - box = boxCreate(xmax - wmax + 1, ymax - hmax + 1, wmax, hmax); - *pbox = box; - - if (ppixdb) { - *ppixdb = pixConvertTo8(pixs, TRUE); - pixRenderHashBoxArb(*ppixdb, box, 6, 2, L_NEG_SLOPE_LINE, 1, 255, 0, 0); - } - - LEPT_FREE(linew); - LEPT_FREE(lineh); - LEPT_FREE(lowestfg); - pixDestroy(&pixw); - pixDestroy(&pixh); - return 0; -} - - -/*---------------------------------------------------------------------* - * Generate rectangle inside connected component * - *---------------------------------------------------------------------*/ -/*! - * \brief pixFindRectangleInCC() - * - * \param[in] pixs 1 bpp, with sufficient closings to make the fg be - * a single c.c. that is a convex hull - * \param[in] boxs [optional] if NULL, %pixs should be a minimum - * container of a single c.c. - * \param[in] fract first and all consecutive lines found must be at - * least this fraction of the fast scan dimension - * \param[in] dir L_SCAN_HORIZONTAL, L_SCAN_VERTICAL; direction of - * fast scan - * \param[in] select L_GEOMETRIC_UNION, L_GEOMETRIC_INTERSECTION, - * L_LARGEST_AREA, L_SMALEST_AREA - * \param[in] debug if 1, generates output pdf showing intermediate - * computation and final result - * \return box of included rectangle, or NULL on error - * - *
- * Notes: - * (1) Computation is similar to pixFindLargestRectangle(), but allows - * a different set of results to choose from. - * (2) Select the fast scan direction. Then, scanning in the slow - * direction, finds the longest run of ON pixels in the fast - * scan direction and look for the first first run that is longer - * than %fract of the dimension. Continues until a shorter run - * is found. This generates a box of ON pixels fitting into the c.c. - * (3) Do this from both slow scan directions and use %select to get - * a resulting box from these two. - * (4) The extracted rectangle is not necessarily the largest that - * can fit in the c.c. To get that, use pixFindLargestRectangle(). - */ -BOX * -pixFindRectangleInCC(PIX *pixs, - BOX *boxs, - l_float32 fract, - l_int32 dir, - l_int32 select, - l_int32 debug) -{ -l_int32 x, y, i, j, w, h, w1, h1, w2, h2, found, res; -l_int32 xfirst, xlast, xstart, yfirst, ylast, length; -BOX *box1, *box2, *box3, *box4, *box5; -PIX *pix1, *pix2, *pixdb1, *pixdb2; -PIXA *pixadb; - - PROCNAME("pixFindRectangleInCC"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (BOX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (fract <= 0.0 || fract > 1.0) - return (BOX *)ERROR_PTR("invalid fraction", procName, NULL); - if (dir != L_SCAN_VERTICAL && dir != L_SCAN_HORIZONTAL) - return (BOX *)ERROR_PTR("invalid scan direction", procName, NULL); - if (select != L_GEOMETRIC_UNION && select != L_GEOMETRIC_INTERSECTION && - select != L_LARGEST_AREA && select != L_SMALLEST_AREA) - return (BOX *)ERROR_PTR("invalid select", procName, NULL); - - /* Extract the c.c. if necessary */ - x = y = 0; - if (boxs) { - pix1 = pixClipRectangle(pixs, boxs, NULL); - boxGetGeometry(boxs, &x, &y, NULL, NULL); - } else { - pix1 = pixClone(pixs); - } - - /* All fast scans are horizontal; rotate 90 deg cw if necessary */ - if (dir == L_SCAN_VERTICAL) - pix2 = pixRotate90(pix1, 1); - else /* L_SCAN_HORIZONTAL */ - pix2 = pixClone(pix1); - pixGetDimensions(pix2, &w, &h, NULL); - - pixadb = (debug) ? pixaCreate(0) : NULL; - pixdb1 = NULL; - if (pixadb) { - lept_mkdir("lept/rect"); - pixaAddPix(pixadb, pix1, L_CLONE); - pixdb1 = pixConvertTo32(pix2); - } - pixDestroy(&pix1); - - /* Scanning down, find the first scanline with a long enough run. - * That run goes from (xfirst, yfirst) to (xlast, yfirst). */ - found = FALSE; - for (i = 0; i < h; i++) { - pixFindMaxHorizontalRunOnLine(pix2, i, &xstart, &length); - if (length >= (l_int32)(fract * w + 0.5)) { - yfirst = i; - xfirst = xstart; - xlast = xfirst + length - 1; - found = TRUE; - break; - } - } - if (!found) { - L_WARNING("no run of sufficient size was found\n", procName); - pixDestroy(&pix2); - pixDestroy(&pixdb1); - pixaDestroy(&pixadb); - return NULL; - } - - /* Continue down until the condition fails */ - w1 = xlast - xfirst + 1; - h1 = h - yfirst; /* initialize */ - for (i = yfirst + 1; i < h; i++) { - pixFindMaxHorizontalRunOnLine(pix2, i, &xstart, &length); - if (xstart > xfirst || (xstart + length - 1 < xlast) || - i == h - 1) { - ylast = i - 1; - h1 = ylast - yfirst + 1; - break; - } - } - box1 = boxCreate(xfirst, yfirst, w1, h1); - - /* Scanning up, find the first scanline with a long enough run. - * That run goes from (xfirst, ylast) to (xlast, ylast). */ - for (i = h - 1; i >= 0; i--) { - pixFindMaxHorizontalRunOnLine(pix2, i, &xstart, &length); - if (length >= (l_int32)(fract * w + 0.5)) { - ylast = i; - xfirst = xstart; - xlast = xfirst + length - 1; - break; - } - } - - /* Continue up until the condition fails */ - w2 = xlast - xfirst + 1; - h2 = ylast + 1; /* initialize */ - for (i = ylast - 1; i >= 0; i--) { - pixFindMaxHorizontalRunOnLine(pix2, i, &xstart, &length); - if (xstart > xfirst || (xstart + length - 1 < xlast) || - i == 0) { - yfirst = i + 1; - h2 = ylast - yfirst + 1; - break; - } - } - box2 = boxCreate(xfirst, yfirst, w2, h2); - pixDestroy(&pix2); - - if (pixadb) { - pixRenderBoxArb(pixdb1, box1, 2, 255, 0, 0); - pixRenderBoxArb(pixdb1, box2, 2, 0, 255, 0); - pixaAddPix(pixadb, pixdb1, L_INSERT); - } - - /* Select the final result from the two boxes */ - if (select == L_GEOMETRIC_UNION) - box3 = boxBoundingRegion(box1, box2); - else if (select == L_GEOMETRIC_INTERSECTION) - box3 = boxOverlapRegion(box1, box2); - else if (select == L_LARGEST_AREA) - box3 = (w1 * h1 >= w2 * h2) ? boxCopy(box1) : boxCopy(box2); - else /* select == L_SMALLEST_AREA) */ - box3 = (w1 * h1 <= w2 * h2) ? boxCopy(box1) : boxCopy(box2); - boxDestroy(&box1); - boxDestroy(&box2); - - /* Rotate the box 90 degrees ccw if necessary */ - box4 = NULL; - if (box3) { - if (dir == L_SCAN_VERTICAL) - box4 = boxRotateOrth(box3, w, h, 3); - else - box4 = boxCopy(box3); - } - - /* Transform back to global coordinates if %boxs exists */ - box5 = (box4) ? boxTransform(box4, x, y, 1.0, 1.0) : NULL; - boxDestroy(&box3); - boxDestroy(&box4); - - /* Debug output */ - if (pixadb) { - pixdb1 = pixConvertTo8(pixs, 0); - pixAddConstantGray(pixdb1, 190); - pixdb2 = pixConvertTo32(pixdb1); - if (box5) pixRenderBoxArb(pixdb2, box5, 4, 0, 0, 255); - pixaAddPix(pixadb, pixdb2, L_INSERT); - res = pixGetXRes(pixs); - L_INFO("Writing debug files to /tmp/lept/rect/\n", procName); - pixaConvertToPdf(pixadb, res, 1.0, L_DEFAULT_ENCODE, 75, NULL, - "/tmp/lept/rect/fitrect.pdf"); - pix1 = pixaDisplayTiledAndScaled(pixadb, 32, 800, 1, 0, 40, 2); - pixWrite("/tmp/lept/rect/fitrect.png", pix1, IFF_PNG); - pixDestroy(&pix1); - pixDestroy(&pixdb1); - pixaDestroy(&pixadb); - } - - return box5; -} - -/*------------------------------------------------------------------* - * Automatic photoinvert for OCR * - *------------------------------------------------------------------*/ -/*! - * \brief pixAutoPhotoinvert() - * - * \param[in] pixs any depth, colormap ok - * \param[in] thresh binarization threshold; use 0 for default - * \param[out] ppixm [optional] image regions to be inverted - * \param[out] pixadb [optional] debug; input NULL to skip - * \return pixd 1 bpp image to be sent to OCR, or NULL on error - * - *- * Notes: - * (1) A 1 bpp image is returned, where pixels in image regions are - * photo-inverted. - * (2) If there is light text with a dark background, this will - * identify the region and photoinvert the pixels there if - * there are at least 60% fg pixels in the region. - * (3) For debug output, input a (typically empty) %pixadb. - *- */ -PIX * -pixAutoPhotoinvert(PIX *pixs, - l_int32 thresh, - PIX **ppixm, - PIXA *pixadb) -{ -l_int32 i, n, empty, x, y, w, h; -l_float32 fgfract; -BOX *box1; -BOXA *boxa1; -PIX *pix1, *pix2, *pix3, *pix4, *pix5; -PIXA *pixa1; - - PROCNAME("pixAutoPhotoinvert"); - - if (ppixm) *ppixm = NULL; - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (thresh == 0) thresh = 128; - - if ((pix1 = pixConvertTo1(pixs, thresh)) == NULL) - return (PIX *)ERROR_PTR("pix1 not made", procName, NULL); - if (pixadb) pixaAddPix(pixadb, pix1, L_COPY); - - /* Make the halftone mask to identify region for photo-inversion */ - pix2 = pixGenerateHalftoneMask(pix1, NULL, NULL, pixadb); - pix3 = pixMorphSequence(pix2, "o15.15 + c25.25", 0); /* clean it up */ - if (pixadb) { - pixaAddPix(pixadb, pix2, L_CLONE); - pixaAddPix(pixadb, pix3, L_COPY); - } - pixDestroy(&pix2); - pixZero(pix3, &empty); - if (empty) { - pixDestroy(&pix3); - return pix1; - } - - /* Examine each component and validate the inversion. - * Require at least 60% of pixels under each component to be FG. */ - boxa1 = pixConnCompBB(pix3, 8); - n = boxaGetCount(boxa1); - for (i = 0; i < n; i++) { - box1 = boxaGetBox(boxa1, i, L_COPY); - pix5 = pixClipRectangle(pix1, box1, NULL); - pixForegroundFraction(pix5, &fgfract); - if (pixadb) lept_stderr("fg fraction: %5.3f\n", fgfract); - if (fgfract < 0.6) { /* erase from the mask */ - boxGetGeometry(box1, &x, &y, &w, &h); - pixRasterop(pix3, x, y, w, h, PIX_CLR, NULL, 0, 0); - } - pixDestroy(&pix5); - boxDestroy(&box1); - } - boxaDestroy(&boxa1); - pixZero(pix3, &empty); - if (empty) { - pixDestroy(&pix3); - return pix1; - } - - /* Combine pixels of the photo-inverted pix with the binarized input */ - pix4 = pixInvert(NULL, pix1); - pixCombineMasked(pix1, pix4, pix3); - - if (pixadb) { - pixaAddPix(pixadb, pix4, L_CLONE); - pixaAddPix(pixadb, pix1, L_COPY); - } - pixDestroy(&pix4); - if (ppixm) - *ppixm = pix3; - else - pixDestroy(&pix3); - return pix1; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/paintcmap.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/paintcmap.c deleted file mode 100644 index b0f27982..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/paintcmap.c +++ /dev/null @@ -1,765 +0,0 @@ -/*====================================================================* - - 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 paintcmap.c - *- * - * These in-place functions paint onto colormap images. - * - * Repaint selected pixels in region - * l_int32 pixSetSelectCmap() - * - * Repaint non-white pixels in region - * l_int32 pixColorGrayRegionsCmap() - * l_int32 pixColorGrayCmap() - * l_int32 pixColorGrayMaskedCmap() - * l_int32 addColorizedGrayToCmap() - * - * Repaint selected pixels through mask - * l_int32 pixSetSelectMaskedCmap() - * - * Repaint all pixels through mask - * l_int32 pixSetMaskedCmap() - * - * - * The 'set select' functions condition the setting on a specific - * pixel value (i.e., index into the colormap) of the underyling - * Pix that is being modified. The same conditioning is used in - * pixBlendCmap(). - * - * The pixColorGrayCmap() function sets all truly gray (r = g = b) pixels, - * with the exception of either black or white pixels, to a new color. - * - * The pixSetSelectMaskedCmap() function conditions pixel painting - * on both a specific pixel value and location within the fg mask. - * By contrast, pixSetMaskedCmap() sets all pixels under the - * mask foreground, without considering the initial pixel values. - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include "allheaders.h" - -/*-------------------------------------------------------------* - * Repaint selected pixels in region * - *-------------------------------------------------------------*/ -/*! - * \brief pixSetSelectCmap() - * - * \param[in] pixs 1, 2, 4 or 8 bpp, with colormap - * \param[in] box [optional] region to set color; can be NULL - * \param[in] sindex colormap index of pixels to be changed - * \param[in] rval, gval, bval new color to paint - * \return 0 if OK, 1 on error - * - * - * Notes: - * (1) This is an in-place operation. - * (2) It sets all pixels in region that have the color specified - * by the colormap index %sindex to the new color. - * (3) %sindex must be in the existing colormap; otherwise an - * error is returned. - * (4) If the new color exists in the colormap, it is used; - * otherwise, it is added to the colormap. If it cannot be - * added because the colormap is full, an error is returned. - * (5) If %box is NULL, applies function to the entire image; otherwise, - * clips the operation to the intersection of the box and pix. - * (6) An example of use would be to set to a specific color all - * the light (background) pixels within a certain region of - * a 3-level 2 bpp image, while leaving light pixels outside - * this region unchanged. - *- */ -l_ok -pixSetSelectCmap(PIX *pixs, - BOX *box, - l_int32 sindex, - l_int32 rval, - l_int32 gval, - l_int32 bval) -{ -l_int32 i, j, w, h, d, n, x1, y1, x2, y2, bw, bh, val, wpls; -l_int32 index; /* of new color to be set */ -l_uint32 *lines, *datas; -PIXCMAP *cmap; - - PROCNAME("pixSetSelectCmap"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if ((cmap = pixGetColormap(pixs)) == NULL) - return ERROR_INT("no colormap", procName, 1); - d = pixGetDepth(pixs); - if (d != 1 && d != 2 && d != 4 && d != 8) - return ERROR_INT("depth not in {1,2,4,8}", procName, 1); - - /* Add new color if necessary; get index of this color in cmap */ - n = pixcmapGetCount(cmap); - if (sindex >= n) - return ERROR_INT("sindex too large; no cmap entry", procName, 1); - if (pixcmapGetIndex(cmap, rval, gval, bval, &index)) { /* not found */ - if (pixcmapAddColor(cmap, rval, gval, bval)) - return ERROR_INT("error adding cmap entry", procName, 1); - else - index = n; /* we've added one color */ - } - - /* Determine the region of substitution */ - pixGetDimensions(pixs, &w, &h, NULL); - if (!box) { - x1 = y1 = 0; - x2 = w; - y2 = h; - } else { - boxGetGeometry(box, &x1, &y1, &bw, &bh); - x2 = x1 + bw - 1; - y2 = y1 + bh - 1; - } - - /* Replace pixel value sindex by index in the region */ - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - for (i = y1; i <= y2; i++) { - if (i < 0 || i >= h) /* clip */ - continue; - lines = datas + i * wpls; - for (j = x1; j <= x2; j++) { - if (j < 0 || j >= w) /* clip */ - continue; - switch (d) { - case 1: - val = GET_DATA_BIT(lines, j); - if (val == sindex) { - if (index == 0) - CLEAR_DATA_BIT(lines, j); - else - SET_DATA_BIT(lines, j); - } - break; - case 2: - val = GET_DATA_DIBIT(lines, j); - if (val == sindex) - SET_DATA_DIBIT(lines, j, index); - break; - case 4: - val = GET_DATA_QBIT(lines, j); - if (val == sindex) - SET_DATA_QBIT(lines, j, index); - break; - case 8: - val = GET_DATA_BYTE(lines, j); - if (val == sindex) - SET_DATA_BYTE(lines, j, index); - break; - default: - return ERROR_INT("depth not in {1,2,4,8}", procName, 1); - } - } - } - - return 0; -} - - -/*-------------------------------------------------------------* - * Repaint gray pixels in region * - *-------------------------------------------------------------*/ -/*! - * \brief pixColorGrayRegionsCmap() - * - * \param[in] pixs 8 bpp, with colormap - * \param[in] boxa of regions in which to apply color - * \param[in] type L_PAINT_LIGHT, L_PAINT_DARK - * \param[in] rval, gval, bval target color - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This is an in-place operation. - * (2) If %type == L_PAINT_LIGHT, it colorizes non-black pixels, - * preserving antialiasing. - * If %type == L_PAINT_DARK, it colorizes non-white pixels, - * preserving antialiasing. See pixColorGrayCmap() for details. - * (3) This can also be called through pixColorGrayRegions(). - * (4) This increases the colormap size by the number of - * different gray (non-black or non-white) colors in the - * selected regions of pixs. If there is not enough room in - * the colormap for this expansion, it returns 1 (error), - * and the caller should check the return value. - * (5) Because two boxes in %boxa can overlap, pixels that - * are colorized in the first box must be excluded in the - * second because their value exceeds the size of the map. - *- */ -l_ok -pixColorGrayRegionsCmap(PIX *pixs, - BOXA *boxa, - l_int32 type, - l_int32 rval, - l_int32 gval, - l_int32 bval) -{ -l_int32 i, j, k, w, h, n, nc, x1, y1, x2, y2, bw, bh, wpl; -l_int32 val, nval; -l_int32 *map; -l_uint32 *line, *data; -BOX *box; -NUMA *na; -PIXCMAP *cmap; - - PROCNAME("pixColorGrayRegionsCmap"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if ((cmap = pixGetColormap(pixs)) == NULL) - return ERROR_INT("no colormap", procName, 1); - if (pixGetDepth(pixs) != 8) - return ERROR_INT("depth not 8 bpp", procName, 1); - if (type != L_PAINT_DARK && type != L_PAINT_LIGHT) - return ERROR_INT("invalid type", procName, 1); - - nc = pixcmapGetCount(cmap); - if (addColorizedGrayToCmap(cmap, type, rval, gval, bval, &na)) - return ERROR_INT("no room; cmap full", procName, 1); - map = numaGetIArray(na); - numaDestroy(&na); - if (!map) - return ERROR_INT("map not made", procName, 1); - - pixGetDimensions(pixs, &w, &h, NULL); - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - n = boxaGetCount(boxa); - for (k = 0; k < n; k++) { - box = boxaGetBox(boxa, k, L_CLONE); - boxGetGeometry(box, &x1, &y1, &bw, &bh); - x2 = x1 + bw - 1; - y2 = y1 + bh - 1; - - /* Remap gray pixels in the region */ - for (i = y1; i <= y2; i++) { - if (i < 0 || i >= h) /* clip */ - continue; - line = data + i * wpl; - for (j = x1; j <= x2; j++) { - if (j < 0 || j >= w) /* clip */ - continue; - val = GET_DATA_BYTE(line, j); - if (val >= nc) continue; /* from overlapping b.b. */ - nval = map[val]; - if (nval != 256) - SET_DATA_BYTE(line, j, nval); - } - } - boxDestroy(&box); - } - - LEPT_FREE(map); - return 0; -} - - -/*! - * \brief pixColorGrayCmap() - * - * \param[in] pixs 2, 4 or 8 bpp, with colormap - * \param[in] box [optional] region to set color; can be NULL - * \param[in] type L_PAINT_LIGHT, L_PAINT_DARK - * \param[in] rval, gval, bval target color - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This is an in-place operation. - * (2) If %type == L_PAINT_LIGHT, it colorizes non-black pixels, - * preserving antialiasing. - * If %type == L_PAINT_DARK, it colorizes non-white pixels, - * preserving antialiasing. - * (3) %box gives the region to apply color; if NULL, this - * colorizes the entire image. - * (4) If the cmap is only 2 or 4 bpp, pixs is converted in-place - * to an 8 bpp cmap. A 1 bpp cmap is not a valid input pix. - * (5) This can also be called through pixColorGray(). - * (6) This operation increases the colormap size by the number of - * different gray (non-black or non-white) colors in the - * input colormap. If there is not enough room in the colormap - * for this expansion, it returns 1 (error), and the caller - * should check the return value. - * (7) Using the darkness of each original pixel in the rect, - * it generates a new color (based on the input rgb values). - * If %type == L_PAINT_LIGHT, the new color is a (generally) - * darken-to-black version of the input rgb color, where the - * amount of darkening increases with the darkness of the - * original pixel color. - * If %type == L_PAINT_DARK, the new color is a (generally) - * faded-to-white version of the input rgb color, where the - * amount of fading increases with the brightness of the - * original pixel color. - *- */ -l_ok -pixColorGrayCmap(PIX *pixs, - BOX *box, - l_int32 type, - l_int32 rval, - l_int32 gval, - l_int32 bval) -{ -l_int32 w, h, d, ret; -PIX *pixt; -BOXA *boxa; -PIXCMAP *cmap; - - PROCNAME("pixColorGrayCmap"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if ((cmap = pixGetColormap(pixs)) == NULL) - return ERROR_INT("no colormap", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 2 && d != 4 && d != 8) - return ERROR_INT("depth not in {2, 4, 8}", procName, 1); - if (type != L_PAINT_DARK && type != L_PAINT_LIGHT) - return ERROR_INT("invalid type", procName, 1); - - /* If 2 bpp or 4 bpp, convert in-place to 8 bpp. */ - if (d == 2 || d == 4) { - pixt = pixConvertTo8(pixs, 1); - pixTransferAllData(pixs, &pixt, 0, 0); - } - - /* If box == NULL, color the entire image */ - boxa = boxaCreate(1); - if (box) { - boxaAddBox(boxa, box, L_COPY); - } else { - box = boxCreate(0, 0, w, h); - boxaAddBox(boxa, box, L_INSERT); - } - ret = pixColorGrayRegionsCmap(pixs, boxa, type, rval, gval, bval); - - boxaDestroy(&boxa); - return ret; -} - - -/*! - * \brief pixColorGrayMaskedCmap() - * - * \param[in] pixs 8 bpp, with colormap - * \param[in] pixm 1 bpp mask, through which to apply color - * \param[in] type L_PAINT_LIGHT, L_PAINT_DARK - * \param[in] rval, gval, bval target color - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This is an in-place operation. - * (2) If %type == L_PAINT_LIGHT, it colorizes non-black pixels, - * preserving antialiasing. - * If %type == L_PAINT_DARK, it colorizes non-white pixels, - * preserving antialiasing. See pixColorGrayCmap() for details. - * (3) This increases the colormap size by the number of - * different gray (non-black or non-white) colors in the - * input colormap. If there is not enough room in the colormap - * for this expansion, it returns 1 (error). - *- */ -l_ok -pixColorGrayMaskedCmap(PIX *pixs, - PIX *pixm, - l_int32 type, - l_int32 rval, - l_int32 gval, - l_int32 bval) -{ -l_int32 i, j, w, h, wm, hm, wmin, hmin, wpl, wplm; -l_int32 val, nval; -l_int32 *map; -l_uint32 *line, *data, *linem, *datam; -NUMA *na; -PIXCMAP *cmap; - - PROCNAME("pixColorGrayMaskedCmap"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!pixm || pixGetDepth(pixm) != 1) - return ERROR_INT("pixm undefined or not 1 bpp", procName, 1); - if ((cmap = pixGetColormap(pixs)) == NULL) - return ERROR_INT("no colormap", procName, 1); - if (pixGetDepth(pixs) != 8) - return ERROR_INT("depth not 8 bpp", procName, 1); - if (type != L_PAINT_DARK && type != L_PAINT_LIGHT) - return ERROR_INT("invalid type", procName, 1); - - if (addColorizedGrayToCmap(cmap, type, rval, gval, bval, &na)) - return ERROR_INT("no room; cmap full", procName, 1); - map = numaGetIArray(na); - numaDestroy(&na); - if (!map) - return ERROR_INT("map not made", procName, 1); - - pixGetDimensions(pixs, &w, &h, NULL); - pixGetDimensions(pixm, &wm, &hm, NULL); - if (wm != w) - L_WARNING("wm = %d differs from w = %d\n", procName, wm, w); - if (hm != h) - L_WARNING("hm = %d differs from h = %d\n", procName, hm, h); - wmin = L_MIN(w, wm); - hmin = L_MIN(h, hm); - - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - datam = pixGetData(pixm); - wplm = pixGetWpl(pixm); - - /* Remap gray pixels in the region */ - for (i = 0; i < hmin; i++) { - line = data + i * wpl; - linem = datam + i * wplm; - for (j = 0; j < wmin; j++) { - if (GET_DATA_BIT(linem, j) == 0) - continue; - val = GET_DATA_BYTE(line, j); - nval = map[val]; - if (nval != 256) - SET_DATA_BYTE(line, j, nval); - } - } - - LEPT_FREE(map); - return 0; -} - - -/*! - * \brief addColorizedGrayToCmap() - * - * \param[in] cmap from 2 or 4 bpp pix - * \param[in] type L_PAINT_LIGHT, L_PAINT_DARK - * \param[in] rval, gval, bval target color - * \param[out] pna [optional] table for mapping new cmap entries - * \return 0 if OK; 1 on error; 2 if new colors will not fit in cmap. - * - *- * Notes: - * (1) If %type == L_PAINT_LIGHT, it colorizes non-black pixels, - * preserving antialiasing. - * If %type == L_PAINT_DARK, it colorizes non-white pixels, - * preserving antialiasing. - * (2) This increases the colormap size by the number of - * different gray (non-black or non-white) colors in the - * input colormap. If there is not enough room in the colormap - * for this expansion, it returns 1 (treated as a warning); - * the caller should check the return value. - * (3) This can be used to determine if the new colors will fit in - * the cmap, using null for &na. Returns 0 if they fit; 2 if - * they don't fit. - * (4) The mapping table contains, for each gray color found, the - * index of the corresponding colorized pixel. Non-gray - * pixels are assigned the invalid index 256. - * (5) See pixColorGrayCmap() for usage. - *- */ -l_ok -addColorizedGrayToCmap(PIXCMAP *cmap, - l_int32 type, - l_int32 rval, - l_int32 gval, - l_int32 bval, - NUMA **pna) -{ -l_int32 i, n, erval, egval, ebval, nrval, ngval, nbval, newindex; -NUMA *na; - - PROCNAME("addColorizedGrayToCmap"); - - if (pna) *pna = NULL; - if (!cmap) - return ERROR_INT("cmap not defined", procName, 1); - if (type != L_PAINT_DARK && type != L_PAINT_LIGHT) - return ERROR_INT("invalid type", procName, 1); - - n = pixcmapGetCount(cmap); - na = numaCreate(n); - for (i = 0; i < n; i++) { - pixcmapGetColor(cmap, i, &erval, &egval, &ebval); - if (type == L_PAINT_LIGHT) { - if (erval == egval && erval == ebval && erval != 0) { - nrval = (l_int32)(rval * (l_float32)erval / 255.); - ngval = (l_int32)(gval * (l_float32)egval / 255.); - nbval = (l_int32)(bval * (l_float32)ebval / 255.); - if (pixcmapAddNewColor(cmap, nrval, ngval, nbval, &newindex)) { - numaDestroy(&na); - L_WARNING("no room; colormap full\n", procName); - return 2; - } - numaAddNumber(na, newindex); - } else { - numaAddNumber(na, 256); /* invalid number; not gray */ - } - } else { /* L_PAINT_DARK */ - if (erval == egval && erval == ebval && erval != 255) { - nrval = rval + - (l_int32)((255. - rval) * (l_float32)erval / 255.); - ngval = gval + - (l_int32)((255. - gval) * (l_float32)egval / 255.); - nbval = bval + - (l_int32)((255. - bval) * (l_float32)ebval / 255.); - if (pixcmapAddNewColor(cmap, nrval, ngval, nbval, &newindex)) { - numaDestroy(&na); - L_WARNING("no room; colormap full\n", procName); - return 2; - } - numaAddNumber(na, newindex); - } else { - numaAddNumber(na, 256); /* invalid number; not gray */ - } - } - } - - if (pna) - *pna = na; - else - numaDestroy(&na); - return 0; -} - - -/*-------------------------------------------------------------* - * Repaint selected pixels through mask * - *-------------------------------------------------------------*/ -/*! - * \brief pixSetSelectMaskedCmap() - * - * \param[in] pixs 2, 4 or 8 bpp, with colormap - * \param[in] pixm [optional] 1 bpp mask; no-op if NULL - * \param[in] x, y UL corner of mask relative to pixs - * \param[in] sindex cmap index of pixels in pixs to be changed - * \param[in] rval, gval, bval new color to substitute - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This is an in-place operation. - * (2) This paints through the fg of pixm and replaces all pixels - * in pixs that have the value %sindex with the new color. - * (3) If pixm == NULL, a warning is given. - * (4) %sindex must be in the existing colormap; otherwise an - * error is returned. - * (5) If the new color exists in the colormap, it is used; - * otherwise, it is added to the colormap. If the colormap - * is full, an error is returned. - *- */ -l_ok -pixSetSelectMaskedCmap(PIX *pixs, - PIX *pixm, - l_int32 x, - l_int32 y, - l_int32 sindex, - l_int32 rval, - l_int32 gval, - l_int32 bval) -{ -l_int32 i, j, w, h, d, n, wm, hm, wpls, wplm, val; -l_int32 index; /* of new color to be set */ -l_uint32 *lines, *linem, *datas, *datam; -PIXCMAP *cmap; - - PROCNAME("pixSetSelectMaskedCmap"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if ((cmap = pixGetColormap(pixs)) == NULL) - return ERROR_INT("no colormap", procName, 1); - if (!pixm) { - L_WARNING("no mask; nothing to do\n", procName); - return 0; - } - - d = pixGetDepth(pixs); - if (d != 2 && d != 4 && d != 8) - return ERROR_INT("depth not in {2, 4, 8}", procName, 1); - - /* add new color if necessary; get index of this color in cmap */ - n = pixcmapGetCount(cmap); - if (sindex >= n) - return ERROR_INT("sindex too large; no cmap entry", procName, 1); - if (pixcmapGetIndex(cmap, rval, gval, bval, &index)) { /* not found */ - if (pixcmapAddColor(cmap, rval, gval, bval)) - return ERROR_INT("error adding cmap entry", procName, 1); - else - index = n; /* we've added one color */ - } - - /* replace pixel value sindex by index when fg pixel in pixmc - * overlays it */ - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - wm = pixGetWidth(pixm); - hm = pixGetHeight(pixm); - datam = pixGetData(pixm); - wplm = pixGetWpl(pixm); - for (i = 0; i < hm; i++) { - if (i + y < 0 || i + y >= h) continue; - lines = datas + (y + i) * wpls; - linem = datam + i * wplm; - for (j = 0; j < wm; j++) { - if (j + x < 0 || j + x >= w) continue; - if (GET_DATA_BIT(linem, j)) { - switch (d) { - case 2: - val = GET_DATA_DIBIT(lines, x + j); - if (val == sindex) - SET_DATA_DIBIT(lines, x + j, index); - break; - case 4: - val = GET_DATA_QBIT(lines, x + j); - if (val == sindex) - SET_DATA_QBIT(lines, x + j, index); - break; - case 8: - val = GET_DATA_BYTE(lines, x + j); - if (val == sindex) - SET_DATA_BYTE(lines, x + j, index); - break; - default: - return ERROR_INT("depth not in {1,2,4,8}", procName, 1); - } - } - } - } - - return 0; -} - - -/*-------------------------------------------------------------* - * Repaint all pixels through mask * - *-------------------------------------------------------------*/ -/*! - * \brief pixSetMaskedCmap() - * - * \param[in] pixs 2, 4 or 8 bpp, colormapped - * \param[in] pixm [optional] 1 bpp mask; no-op if NULL - * \param[in] x, y origin of pixm relative to pixs; - * can be negative - * \param[in] rval, gval, bval new color to set at each masked pixel - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) This is an in-place operation. - * (2) It paints a single color through the mask (as a stencil). - * (3) The mask origin is placed at (%x,%y) on %pixs, and the - * operation is clipped to the intersection of the mask and pixs. - * (4) If %pixm == NULL, a warning is given. - * (5) Typically, %pixm is a small binary mask located somewhere - * on the larger %pixs. - * (6) If the color is in the colormap, it is used. Otherwise, - * it is added if possible; an error is returned if the - * colormap is already full. - *- */ -l_ok -pixSetMaskedCmap(PIX *pixs, - PIX *pixm, - l_int32 x, - l_int32 y, - l_int32 rval, - l_int32 gval, - l_int32 bval) -{ -l_int32 w, h, d, wpl, wm, hm, wplm; -l_int32 i, j, index; -l_uint32 *data, *datam, *line, *linem; -PIXCMAP *cmap; - - PROCNAME("pixSetMaskedCmap"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if ((cmap = pixGetColormap(pixs)) == NULL) - return ERROR_INT("no colormap in pixs", procName, 1); - if (!pixm) { - L_WARNING("no mask; nothing to do\n", procName); - return 0; - } - d = pixGetDepth(pixs); - if (d != 2 && d != 4 && d != 8) - return ERROR_INT("depth not in {2,4,8}", procName, 1); - if (pixGetDepth(pixm) != 1) - return ERROR_INT("pixm not 1 bpp", procName, 1); - - /* Add new color if necessary; store in 'index' */ - if (pixcmapGetIndex(cmap, rval, gval, bval, &index)) { /* not found */ - if (pixcmapAddColor(cmap, rval, gval, bval)) - return ERROR_INT("no room in cmap", procName, 1); - index = pixcmapGetCount(cmap) - 1; - } - - pixGetDimensions(pixs, &w, &h, NULL); - wpl = pixGetWpl(pixs); - data = pixGetData(pixs); - pixGetDimensions(pixm, &wm, &hm, NULL); - wplm = pixGetWpl(pixm); - datam = pixGetData(pixm); - for (i = 0; i < hm; i++) { - if (i + y < 0 || i + y >= h) continue; - line = data + (i + y) * wpl; - linem = datam + i * wplm; - for (j = 0; j < wm; j++) { - if (j + x < 0 || j + x >= w) continue; - if (GET_DATA_BIT(linem, j)) { /* paint color */ - switch (d) { - case 2: - SET_DATA_DIBIT(line, j + x, index); - break; - case 4: - SET_DATA_QBIT(line, j + x, index); - break; - case 8: - SET_DATA_BYTE(line, j + x, index); - break; - default: - return ERROR_INT("depth not in {2,4,8}", procName, 1); - } - } - } - } - - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/parseprotos.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/parseprotos.c deleted file mode 100644 index bc1a8969..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/parseprotos.c +++ /dev/null @@ -1,978 +0,0 @@ -/*====================================================================* - - 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 parseprotos.c - *- * - * char *parseForProtos() - * - * Static helpers - * static l_int32 getNextNonCommentLine() - * static l_int32 getNextNonBlankLine() - * static l_int32 getNextNonDoubleSlashLine() - * static l_int32 searchForProtoSignature() - * static char *captureProtoSignature() - * static char *cleanProtoSignature() - * static l_int32 skipToEndOfFunction() - * static l_int32 skipToMatchingBrace() - * static l_int32 skipToSemicolon() - * static l_int32 getOffsetForCharacter() - * static l_int32 getOffsetForMatchingRP() - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include "allheaders.h" - -static const l_int32 Bufsize = 2048; /* max token size */ - -static l_int32 getNextNonCommentLine(SARRAY *sa, l_int32 start, l_int32 *pnext); -static l_int32 getNextNonBlankLine(SARRAY *sa, l_int32 start, l_int32 *pnext); -static l_int32 getNextNonDoubleSlashLine(SARRAY *sa, l_int32 start, - l_int32 *pnext); -static l_int32 searchForProtoSignature(SARRAY *sa, l_int32 begin, - l_int32 *pstart, l_int32 *pstop, l_int32 *pcharindex, - l_int32 *pfound); -static char * captureProtoSignature(SARRAY *sa, l_int32 start, l_int32 stop, - l_int32 charindex); -static char * cleanProtoSignature(char *str); -static l_int32 skipToEndOfFunction(SARRAY *sa, l_int32 start, - l_int32 charindex, l_int32 *pnext); -static l_int32 skipToMatchingBrace(SARRAY *sa, l_int32 start, - l_int32 lbindex, l_int32 *prbline, l_int32 *prbindex); -static l_int32 skipToSemicolon(SARRAY *sa, l_int32 start, - l_int32 charindex, l_int32 *pnext); -static l_int32 getOffsetForCharacter(SARRAY *sa, l_int32 start, char tchar, - l_int32 *psoffset, l_int32 *pboffset, l_int32 *ptoffset); -static l_int32 getOffsetForMatchingRP(SARRAY *sa, l_int32 start, - l_int32 soffsetlp, l_int32 boffsetlp, l_int32 toffsetlp, - l_int32 *psoffset, l_int32 *pboffset, l_int32 *ptoffset); - - -/* - * \brief parseForProtos() - * - * \param[in] filein output of cpp - * \param[in] prestring [optional] string that prefaces each decl; - * use NULL to omit - * \return parsestr string of function prototypes, or NULL on error - * - * - * Notes: - * (1) We parse the output of cpp: - * cpp -ansi- */ -char * -parseForProtos(const char *filein, - const char *prestring) -{ -char *strdata, *str, *newstr, *parsestr, *secondword; -l_int32 start, next, stop, charindex, found; -size_t nbytes; -SARRAY *sa, *saout, *satest; - - PROCNAME("parseForProtos"); - - if (!filein) - return (char *)ERROR_PTR("filein not defined", procName, NULL); - - /* Read in the cpp output into memory, one string for each - * line in the file, omitting blank lines. */ - strdata = (char *)l_binaryRead(filein, &nbytes); - sa = sarrayCreateLinesFromString(strdata, 0); - - saout = sarrayCreate(0); - next = 0; - while (1) { /* repeat after each non-static prototype is extracted */ - searchForProtoSignature(sa, next, &start, &stop, &charindex, &found); - if (!found) - break; -/* lept_stderr(" start = %d, stop = %d, charindex = %d\n", - start, stop, charindex); */ - str = captureProtoSignature(sa, start, stop, charindex); - - /* Make sure that the signature found by cpp does not begin with - * static, extern or typedef. We get 'extern' declarations - * from header files, and with some versions of cpp running on - * #include- * Three plans were attempted, with success on the third. - * (2) Plan 1. A cursory examination of the cpp output indicated that - * every function was preceded by a cpp comment statement. - * So we just need to look at statements beginning after comments. - * Unfortunately, this is NOT the case. Some functions start - * without cpp comment lines, typically when there are no - * comments in the source that immediately precede the function. - * (3) Plan 2. Consider the keywords in the language that start - * parts of the cpp file. Some, like 'enum', 'union' and - * 'struct', are followed after a while by '{', and eventually - * end with '}, plus an optional token and a final ';'. - * Others, like 'extern', 'static' and 'typedef', are never - * the beginnings of global function definitions. Function - * prototypes have one or more sets of '(' followed eventually - * by a ')', and end with ';'. But function definitions have - * tokens, followed by '(', more tokens, ')' and then - * immediately a '{'. We would generate a prototype from this - * by adding a ';' to all tokens up to the ')'. So we use - * these special tokens to decide what we are parsing. And - * whenever a function definition is found and the prototype - * extracted, we skip through the rest of the function - * past the corresponding '}'. This token ends a line, and - * is often on a line of its own. But as it turns out, - * the only keyword we need to consider is 'static'. - * (4) Plan 3. Consider the parentheses and braces for various - * declarations. A struct, enum, or union has a pair of - * braces followed by a semicolon. With the exception of an - * __attribute__ declaration for a struct, they cannot have parentheses - * before the left brace, but a struct can have lots of parentheses - * within the brace set. A function prototype has no braces. - * A function declaration can have sets of left and right - * parentheses, but these are followed by a left brace. - * So plan 3 looks at the way parentheses and braces are - * organized. Once the beginning of a function definition - * is found, the prototype is extracted and we search for - * the ending right brace. - * (5) To find the ending right brace, it is necessary to do some - * careful parsing. For example, in this file, we have - * left and right braces as characters, and these must not - * be counted. Somewhat more tricky, the file fhmtauto.c - * generates code, and includes a right brace in a string. - * So we must not include braces that are in strings. But how - * do we know if something is inside a string? Keep state, - * starting with not-inside, and every time you hit a double quote - * that is not escaped, toggle the condition. Any brace - * found in the state of being within a string is ignored. - * (6) When a prototype is extracted, it is put in a canonical - * form (i.e., cleaned up). Finally, we check that it is - * not static and save it. (If static, it is ignored). - * (7) The %prestring for unix is NULL; it is included here so that - * you can use Microsoft's declaration for importing or - * exporting to a dll. See environ.h for examples of use. - * Here, we set: %prestring = "LEPT_DLL ". Note in particular - * the space character that will separate 'LEPT_DLL' from - * the standard unix prototype that follows. - * we get something of the form: - * extern ... (( ... )) ... ( ... ) { ... - * For this, the 1st '(' is the lp, the 2nd ')' is the rp, - * and there is a lot of garbage between the rp and the lp. - * It is easiest to simply reject any signature that starts - * with 'extern'. Note also that an 'extern' token has been - * prepended to each prototype, so the 'static' or - * 'extern' keywords we are looking for, if they exist, - * would be the second word. We also have a typedef in - * bmpio.c that has the form: - * typedef struct __attribute__((....)) { ...} ... ; - * This is avoided by blacklisting 'typedef' along with 'extern' - * and 'static'. */ - satest = sarrayCreateWordsFromString(str); - secondword = sarrayGetString(satest, 1, L_NOCOPY); - if (strcmp(secondword, "static") && /* not static */ - strcmp(secondword, "extern") && /* not extern */ - strcmp(secondword, "typedef")) { /* not typedef */ - if (prestring) { /* prepend it to the prototype */ - newstr = stringJoin(prestring, str); - sarrayAddString(saout, newstr, L_INSERT); - LEPT_FREE(str); - } else { - sarrayAddString(saout, str, L_INSERT); - } - } else { - LEPT_FREE(str); - } - sarrayDestroy(&satest); - - skipToEndOfFunction(sa, stop, charindex, &next); - if (next == -1) break; - } - - /* Flatten into a string with newlines between prototypes */ - parsestr = sarrayToString(saout, 1); - LEPT_FREE(strdata); - sarrayDestroy(&sa); - sarrayDestroy(&saout); - - return parsestr; -} - - -/* - * \brief getNextNonCommentLine() - * - * \param[in] sa output from cpp, by line) - * \param[in] start starting index to search) - * \param[out] pnext index of first uncommented line after the start line - * \return 0 if OK, o on error - * - * - * Notes: - * (1) Skips over all consecutive comment lines, beginning at 'start' - * (2) If all lines to the end are '#' comments, return next = -1 - *- */ -static l_int32 -getNextNonCommentLine(SARRAY *sa, - l_int32 start, - l_int32 *pnext) -{ -char *str; -l_int32 i, n; - - PROCNAME("getNextNonCommentLine"); - - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - if (!pnext) - return ERROR_INT("&pnext not defined", procName, 1); - - /* Init for situation where this line and all following are comments */ - *pnext = -1; - - n = sarrayGetCount(sa); - for (i = start; i < n; i++) { - if ((str = sarrayGetString(sa, i, L_NOCOPY)) == NULL) - return ERROR_INT("str not returned; shouldn't happen", procName, 1); - if (str[0] != '#') { - *pnext = i; - return 0; - } - } - - return 0; -} - - -/* - * \brief getNextNonBlankLine() - * - * \param[in] sa output from cpp, by line - * \param[in] start starting index to search - * \param[out] pnext index of first nonblank line after the start line - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Skips over all consecutive blank lines, beginning at 'start' - * (2) A blank line has only whitespace characters (' ', '\t', '\n', '\r') - * (3) If all lines to the end are blank, return next = -1 - *- */ -static l_int32 -getNextNonBlankLine(SARRAY *sa, - l_int32 start, - l_int32 *pnext) -{ -char *str; -l_int32 i, j, n, len; - - PROCNAME("getNextNonBlankLine"); - - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - if (!pnext) - return ERROR_INT("&pnext not defined", procName, 1); - - /* Init for situation where this line and all following are blank */ - *pnext = -1; - - n = sarrayGetCount(sa); - for (i = start; i < n; i++) { - if ((str = sarrayGetString(sa, i, L_NOCOPY)) == NULL) - return ERROR_INT("str not returned; shouldn't happen", procName, 1); - len = strlen(str); - for (j = 0; j < len; j++) { - if (str[j] != ' ' && str[j] != '\t' - && str[j] != '\n' && str[j] != '\r') { /* non-blank */ - *pnext = i; - return 0; - } - } - } - - return 0; -} - - -/* - * \brief getNextNonDoubleSlashLine() - * - * \param[in] sa output from cpp, by line - * \param[in] start starting index to search - * \param[out] pnext index of first uncommented line after the start line - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Skips over all consecutive '//' lines, beginning at 'start' - * (2) If all lines to the end start with '//', return next = -1 - *- */ -static l_int32 -getNextNonDoubleSlashLine(SARRAY *sa, - l_int32 start, - l_int32 *pnext) -{ -char *str; -l_int32 i, n, len; - - PROCNAME("getNextNonDoubleSlashLine"); - - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - if (!pnext) - return ERROR_INT("&pnext not defined", procName, 1); - - /* Init for situation where this line and all following - * start with '//' */ - *pnext = -1; - - n = sarrayGetCount(sa); - for (i = start; i < n; i++) { - if ((str = sarrayGetString(sa, i, L_NOCOPY)) == NULL) - return ERROR_INT("str not returned; shouldn't happen", procName, 1); - len = strlen(str); - if (len < 2 || str[0] != '/' || str[1] != '/') { - *pnext = i; - return 0; - } - } - - return 0; -} - - -/* - * \brief searchForProtoSignature() - * - * \param[in] sa output from cpp, by line - * \param[in] begin beginning index to search - * \param[out] pstart starting index for function definition - * \param[out] pstop index of line on which proto is completed - * \param[out] pcharindex char index of completing ')' character - * \param[out] pfound 1 if valid signature is found; 0 otherwise - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If this returns found == 0, it means that there are no - * more function definitions in the file. Caller must check - * this value and exit the loop over the entire cpp file. - * (2) This follows plan 3 (see above). We skip comment and blank - * lines at the beginning. Then we don't check for keywords. - * Instead, find the relative locations of the first occurrences - * of these four tokens: left parenthesis (lp), right - * parenthesis (rp), left brace (lb) and semicolon (sc). - * (3) The signature of a function definition looks like this: - * .... '(' .... ')' '{' - * where the lp and rp must both precede the lb, with only - * whitespace between the rp and the lb. The '....' - * are sets of tokens that have no braces. - * (4) If a function definition is found, this returns found = 1, - * with 'start' being the first line of the definition and - * 'charindex' being the position of the ')' in line 'stop' - * at the end of the arg list. - *- */ -static l_int32 -searchForProtoSignature(SARRAY *sa, - l_int32 begin, - l_int32 *pstart, - l_int32 *pstop, - l_int32 *pcharindex, - l_int32 *pfound) -{ -l_int32 next, rbline, rbindex, scline; -l_int32 soffsetlp, soffsetrp, soffsetlb, soffsetsc; -l_int32 boffsetlp, boffsetrp, boffsetlb, boffsetsc; -l_int32 toffsetlp, toffsetrp, toffsetlb, toffsetsc; - - PROCNAME("searchForProtoSignature"); - - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - if (!pstart) - return ERROR_INT("&start not defined", procName, 1); - if (!pstop) - return ERROR_INT("&stop not defined", procName, 1); - if (!pcharindex) - return ERROR_INT("&charindex not defined", procName, 1); - if (!pfound) - return ERROR_INT("&found not defined", procName, 1); - - *pfound = FALSE; - - while (1) { - - /* Skip over sequential '#' comment lines */ - getNextNonCommentLine(sa, begin, &next); - if (next == -1) return 0; - if (next != begin) { - begin = next; - continue; - } - - /* Skip over sequential blank lines */ - getNextNonBlankLine(sa, begin, &next); - if (next == -1) return 0; - if (next != begin) { - begin = next; - continue; - } - - /* Skip over sequential lines starting with '//' */ - getNextNonDoubleSlashLine(sa, begin, &next); - if (next == -1) return 0; - if (next != begin) { - begin = next; - continue; - } - - /* Search for specific character sequence patterns; namely - * a lp, a matching rp, a lb and a semicolon. - * Abort the search if no lp is found. */ - getOffsetForCharacter(sa, next, '(', &soffsetlp, &boffsetlp, - &toffsetlp); - if (soffsetlp == -1) - break; - getOffsetForMatchingRP(sa, next, soffsetlp, boffsetlp, toffsetlp, - &soffsetrp, &boffsetrp, &toffsetrp); - getOffsetForCharacter(sa, next, '{', &soffsetlb, &boffsetlb, - &toffsetlb); - getOffsetForCharacter(sa, next, ';', &soffsetsc, &boffsetsc, - &toffsetsc); - - /* We've found a lp. Now weed out the case where a matching - * rp and a lb are not both found. */ - if (soffsetrp == -1 || soffsetlb == -1) - break; - - /* Check if a left brace occurs before a left parenthesis; - * if so, skip it */ - if (toffsetlb < toffsetlp) { - skipToMatchingBrace(sa, next + soffsetlb, boffsetlb, - &rbline, &rbindex); - skipToSemicolon(sa, rbline, rbindex, &scline); - begin = scline + 1; - continue; - } - - /* Check if a semicolon occurs before a left brace or - * a left parenthesis; if so, skip it */ - if ((soffsetsc != -1) && - (toffsetsc < toffsetlb || toffsetsc < toffsetlp)) { - skipToSemicolon(sa, next, 0, &scline); - begin = scline + 1; - continue; - } - - /* OK, it should be a function definition. We haven't - * checked that there is only white space between the - * rp and lb, but we've only seen problems with two - * extern inlines in sys/stat.h, and this is handled - * later by eliminating any prototype beginning with 'extern'. */ - *pstart = next; - *pstop = next + soffsetrp; - *pcharindex = boffsetrp; - *pfound = TRUE; - break; - } - - return 0; -} - - -/* - * \brief captureProtoSignature() - * - * \param[in] sa output from cpp, by line - * \param[in] start starting index to search; never a comment line - * \param[in] stop index of line on which pattern is completed - * \param[in] charindex char index of completing ')' character - * \return cleanstr prototype string, or NULL on error - * - *- * Notes: - * (1) Return all characters, ending with a ';' after the ')' - *- */ -static char * -captureProtoSignature(SARRAY *sa, - l_int32 start, - l_int32 stop, - l_int32 charindex) -{ -char *str, *newstr, *protostr, *cleanstr; -SARRAY *sap; -l_int32 i; - - PROCNAME("captureProtoSignature"); - - if (!sa) - return (char *)ERROR_PTR("sa not defined", procName, NULL); - - sap = sarrayCreate(0); - for (i = start; i < stop; i++) { - str = sarrayGetString(sa, i, L_COPY); - sarrayAddString(sap, str, L_INSERT); - } - str = sarrayGetString(sa, stop, L_COPY); - str[charindex + 1] = '\0'; - newstr = stringJoin(str, ";"); - sarrayAddString(sap, newstr, L_INSERT); - LEPT_FREE(str); - protostr = sarrayToString(sap, 2); - sarrayDestroy(&sap); - cleanstr = cleanProtoSignature(protostr); - LEPT_FREE(protostr); - - return cleanstr; -} - - -/* - * \brief cleanProtoSignature() - * - * \param[in] instr input prototype string - * \return cleanstr clean prototype string, or NULL on error - * - *- * Notes: - * (1) Adds 'extern' at beginning and regularizes spaces - * between tokens. - *- */ -static char * -cleanProtoSignature(char *instr) -{ -char *str, *cleanstr; -char buf[Bufsize]; -char externstring[] = "extern"; -l_int32 i, j, nwords, nchars, index, len; -SARRAY *sa, *saout; - - PROCNAME("cleanProtoSignature"); - - if (!instr) - return (char *)ERROR_PTR("instr not defined", procName, NULL); - - sa = sarrayCreateWordsFromString(instr); - nwords = sarrayGetCount(sa); - saout = sarrayCreate(0); - sarrayAddString(saout, externstring, L_COPY); - for (i = 0; i < nwords; i++) { - str = sarrayGetString(sa, i, L_NOCOPY); - nchars = strlen(str); - index = 0; - for (j = 0; j < nchars; j++) { - if (index > Bufsize - 6) { - sarrayDestroy(&sa); - sarrayDestroy(&saout); - return (char *)ERROR_PTR("token too large", procName, NULL); - } - if (str[j] == '(') { - buf[index++] = ' '; - buf[index++] = '('; - buf[index++] = ' '; - } else if (str[j] == ')') { - buf[index++] = ' '; - buf[index++] = ')'; - } else { - buf[index++] = str[j]; - } - } - buf[index] = '\0'; - sarrayAddString(saout, buf, L_COPY); - } - - /* Flatten to a prototype string with spaces added after - * each word, and remove the last space */ - cleanstr = sarrayToString(saout, 2); - len = strlen(cleanstr); - cleanstr[len - 1] = '\0'; - - sarrayDestroy(&sa); - sarrayDestroy(&saout); - return cleanstr; -} - - -/* - * \brief skipToEndOfFunction() - * - * \param[in] sa output from cpp, by line - * \param[in] start index of starting line with left bracket to search - * \param[in] lbindex starting char index for left bracket - * \param[out] pnext index of line following the ending '}' for function - * \return 0 if OK, 1 on error - */ -static l_int32 -skipToEndOfFunction(SARRAY *sa, - l_int32 start, - l_int32 lbindex, - l_int32 *pnext) -{ -l_int32 end, rbindex; -l_int32 soffsetlb, boffsetlb, toffsetlb; - - PROCNAME("skipToEndOfFunction"); - - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - if (!pnext) - return ERROR_INT("&next not defined", procName, 1); - - getOffsetForCharacter(sa, start, '{', &soffsetlb, &boffsetlb, - &toffsetlb); - skipToMatchingBrace(sa, start + soffsetlb, boffsetlb, &end, &rbindex); - if (end == -1) { /* shouldn't happen! */ - *pnext = -1; - return 1; - } - - *pnext = end + 1; - return 0; -} - - -/* - * \brief skipToMatchingBrace() - * - * \param[in] sa output from cpp, by line - * \param[in] start index of starting line with left bracket to search - * \param[in] lbindex starting char index for left bracket - * \param[out] pstop index of line with the matching right bracket - * \param[out] prbindex char index of matching right bracket - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If the matching right brace is not found, returns - * stop = -1. This shouldn't happen. - *- */ -static l_int32 -skipToMatchingBrace(SARRAY *sa, - l_int32 start, - l_int32 lbindex, - l_int32 *pstop, - l_int32 *prbindex) -{ -char *str; -l_int32 i, j, jstart, n, sumbrace, found, instring, nchars; - - PROCNAME("skipToMatchingBrace"); - - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - if (!pstop) - return ERROR_INT("&stop not defined", procName, 1); - if (!prbindex) - return ERROR_INT("&rbindex not defined", procName, 1); - - instring = 0; /* init to FALSE; toggle on double quotes */ - *pstop = -1; - n = sarrayGetCount(sa); - sumbrace = 1; - found = FALSE; - for (i = start; i < n; i++) { - str = sarrayGetString(sa, i, L_NOCOPY); - jstart = 0; - if (i == start) - jstart = lbindex + 1; - nchars = strlen(str); - for (j = jstart; j < nchars; j++) { - /* Toggle the instring state every time you encounter - * a double quote that is NOT escaped. */ - if (j == jstart && str[j] == '\"') - instring = 1 - instring; - if (j > jstart && str[j] == '\"' && str[j-1] != '\\') - instring = 1 - instring; - /* Record the braces if they are neither a literal character - * nor within a string. */ - if (str[j] == '{' && str[j+1] != '\'' && !instring) { - sumbrace++; - } else if (str[j] == '}' && str[j+1] != '\'' && !instring) { - sumbrace--; - if (sumbrace == 0) { - found = TRUE; - *prbindex = j; - break; - } - } - } - if (found) { - *pstop = i; - return 0; - } - } - - return ERROR_INT("matching right brace not found", procName, 1); -} - - -/* - * \brief skipToSemicolon() - * - * \param[in] sa output from cpp, by line - * \param[in] start index of starting line to search - * \param[in] charindex starting char index for search - * \param[out] pnext index of line containing the next ';' - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If the semicolon isn't found, returns next = -1. - * This shouldn't happen. - * (2) This is only used in contexts where the semicolon is - * not within a string. - *- */ -static l_int32 -skipToSemicolon(SARRAY *sa, - l_int32 start, - l_int32 charindex, - l_int32 *pnext) -{ -char *str; -l_int32 i, j, n, jstart, nchars, found; - - PROCNAME("skipToSemicolon"); - - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - if (!pnext) - return ERROR_INT("&next not defined", procName, 1); - - *pnext = -1; - n = sarrayGetCount(sa); - found = FALSE; - for (i = start; i < n; i++) { - str = sarrayGetString(sa, i, L_NOCOPY); - jstart = 0; - if (i == start) - jstart = charindex + 1; - nchars = strlen(str); - for (j = jstart; j < nchars; j++) { - if (str[j] == ';') { - found = TRUE;; - break; - } - } - if (found) { - *pnext = i; - return 0; - } - } - - return ERROR_INT("semicolon not found", procName, 1); -} - - -/* - * \brief getOffsetForCharacter() - * - * \param[in] sa output from cpp, by line - * \param[in] start starting index in sa to search; - * never a comment line - * \param[in] tchar we are searching for the first instance of this - * \param[out] psoffset offset in strings from start index - * \param[out] pboffset offset in bytes within string in which - * the character is first found - * \param[out] ptoffset offset in total bytes from beginning of string - * indexed by 'start' to the location where - * the character is first found - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) We are searching for the first instance of 'tchar', starting - * at the beginning of the string indexed by start. - * (2) If the character is not found, soffset is returned as -1, - * and the other offsets are set to very large numbers. The - * caller must check the value of soffset. - * (3) This is only used in contexts where it is not necessary to - * consider if the character is inside a string. - *- */ -static l_int32 -getOffsetForCharacter(SARRAY *sa, - l_int32 start, - char tchar, - l_int32 *psoffset, - l_int32 *pboffset, - l_int32 *ptoffset) -{ -char *str; -l_int32 i, j, n, nchars, totchars, found; - - PROCNAME("getOffsetForCharacter"); - - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - if (!psoffset) - return ERROR_INT("&soffset not defined", procName, 1); - if (!pboffset) - return ERROR_INT("&boffset not defined", procName, 1); - if (!ptoffset) - return ERROR_INT("&toffset not defined", procName, 1); - - *psoffset = -1; /* init to not found */ - *pboffset = 100000000; - *ptoffset = 100000000; - - n = sarrayGetCount(sa); - found = FALSE; - totchars = 0; - for (i = start; i < n; i++) { - if ((str = sarrayGetString(sa, i, L_NOCOPY)) == NULL) - return ERROR_INT("str not returned; shouldn't happen", procName, 1); - nchars = strlen(str); - for (j = 0; j < nchars; j++) { - if (str[j] == tchar) { - found = TRUE; - break; - } - } - if (found) - break; - totchars += nchars; - } - - if (found) { - *psoffset = i - start; - *pboffset = j; - *ptoffset = totchars + j; - } - - return 0; -} - - -/* - * \brief getOffsetForMatchingRP() - * - * \param[in] sa output from cpp, by line - * \param[in] start starting index in sa to search; - * never a comment line - * \param[in] soffsetlp string offset to first LP - * \param[in] boffsetlp byte offset within string to first LP - * \param[in] toffsetlp total byte offset to first LP - * \param[out] psoffset offset in strings from start index - * \param[out] pboffset offset in bytes within string in which - * the matching RP is found - * \param[out] ptoffset offset in total bytes from beginning of string - * indexed by 'start' to the location where - * the matching RP is found - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) We are searching for the matching right parenthesis (RP) that - * corresponds to the first LP found beginning at the string - * indexed by start. - * (2) If the matching RP is not found, soffset is returned as -1, - * and the other offsets are set to very large numbers. The - * caller must check the value of soffset. - * (3) This is only used in contexts where it is not necessary to - * consider if the character is inside a string. - * (4) We must do this because although most arg lists have a single - * left and right parenthesis, it is possible to construct - * more complicated prototype declarations, such as those - * where functions are passed in. The C++ rules for prototypes - * are strict, and require that for functions passed in as args, - * the function name arg be placed in parenthesis, as well - * as its arg list, thus incurring two extra levels of parentheses. - *- */ -static l_int32 -getOffsetForMatchingRP(SARRAY *sa, - l_int32 start, - l_int32 soffsetlp, - l_int32 boffsetlp, - l_int32 toffsetlp, - l_int32 *psoffset, - l_int32 *pboffset, - l_int32 *ptoffset) -{ -char *str; -l_int32 i, j, n, nchars, totchars, leftmatch, firstline, jstart, found; - - PROCNAME("getOffsetForMatchingRP"); - - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - if (!psoffset) - return ERROR_INT("&soffset not defined", procName, 1); - if (!pboffset) - return ERROR_INT("&boffset not defined", procName, 1); - if (!ptoffset) - return ERROR_INT("&toffset not defined", procName, 1); - - *psoffset = -1; /* init to not found */ - *pboffset = 100000000; - *ptoffset = 100000000; - - n = sarrayGetCount(sa); - found = FALSE; - totchars = toffsetlp; - leftmatch = 1; /* count of (LP - RP); we're finished when it goes to 0. */ - firstline = start + soffsetlp; - for (i = firstline; i < n; i++) { - if ((str = sarrayGetString(sa, i, L_NOCOPY)) == NULL) - return ERROR_INT("str not returned; shouldn't happen", procName, 1); - nchars = strlen(str); - jstart = 0; - if (i == firstline) - jstart = boffsetlp + 1; - for (j = jstart; j < nchars; j++) { - if (str[j] == '(') - leftmatch++; - else if (str[j] == ')') - leftmatch--; - if (leftmatch == 0) { - found = TRUE; - break; - } - } - if (found) - break; - if (i == firstline) - totchars += nchars - boffsetlp; - else - totchars += nchars; - } - - if (found) { - *psoffset = i - start; - *pboffset = j; - *ptoffset = totchars + j; - } - - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/partify.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/partify.c deleted file mode 100644 index 4625b84a..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/partify.c +++ /dev/null @@ -1,315 +0,0 @@ -/*====================================================================* - - 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 partify.c - *- * - * Top level - * l_int32 partifyFiles() - * l_int32 partifyPixac() - * - * Helpers - * static BOXA *pixLocateStaveSets() - * static l_int32 boxaRemoveVGaps() - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include "allheaders.h" - - /* Static helplers */ -static BOXA *pixLocateStaveSets(PIX *pixs, l_int32 pageno, PIXA *pixadb); -static l_ok boxaRemoveVGaps(BOXA *boxa); - -/*---------------------------------------------------------------------* - * Top level * - *---------------------------------------------------------------------*/ -/*! - * \brief partifyFiles() - * - * \param[in] dirname directory of files - * \param[in] substr required filename substring; use NULL for all files - * \param[in] nparts number of parts to generate (counting from top) - * \param[in] outroot root name of output pdf files - * \param[in] debugfile [optional] set to NULL for no debug output - * \return 0 if OK, 1 on error - * - * - * Notes: - * (1) All page images are compressed in png format into a pixacomp. - * (2) Each page image is deskewed, binarized at 300 ppi, - * partified into %nparts, and saved in a set of pixacomps - * in tiff-g4 format. - * (3) Each partified pixacomp is rendered into a set of page images, - * and output as a pdf. - *- */ -l_ok -partifyFiles(const char *dirname, - const char *substr, - l_int32 nparts, - const char *outroot, - const char *debugfile) -{ -PIXA *pixadb; -PIXAC *pixac; - - PROCNAME("partifyFiles"); - - if (!dirname) - return ERROR_INT("dirname not defined", procName, 1); - if (nparts < 0 || nparts > 10) - return ERROR_INT("nparts not in [1 ... 10]", procName, 1); - if (!outroot || outroot[0] == '\n') - return ERROR_INT("outroot undefined or empty", procName, 1); - - pixadb = (debugfile) ? pixaCreate(0) : NULL; - pixac = pixacompCreateFromFiles(dirname, substr, IFF_PNG); - partifyPixac(pixac, nparts, outroot, pixadb); - if (pixadb) { - L_INFO("writing debug output to %s\n", procName, debugfile); - pixaConvertToPdf(pixadb, 300, 1.0, L_FLATE_ENCODE, 0, - "Partify Debug", debugfile); - } - pixacompDestroy(&pixac); - pixaDestroy(&pixadb); - return 0; -} - - -/*! - * \brief partifyPixac() - * - * \param[in] pixac with at least one image - * \param[in] nparts number of parts to generate (counting from top) - * \param[in] outroot root name of output pdf files - * \param[in] pixadb [optional] debug pixa; can be NULL - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) See partifyPixac(). - * (2) If the image files do not have a resolution, 300 ppi is assumed. - *- */ -l_ok -partifyPixac(PIXAC *pixac, - l_int32 nparts, - const char *outroot, - PIXA *pixadb) -{ -char buf[512]; -l_int32 i, j, pageno, res, npage, nbox, icount, line; -l_float32 factor; -L_BMF *bmf; -BOX *box1, *box2; -BOXA *boxa1, *boxa2, *boxa3; -PIX *pix1, *pix2, *pix3, *pix4, *pix5; -PIXAC **pixaca; - - PROCNAME("partifyPixac"); - - if (!pixac) - return ERROR_INT("pixac not defined", procName, 1); - if ((npage = pixacompGetCount(pixac)) == 0) - return ERROR_INT("pixac is empty", procName, 1); - if (nparts < 1 || nparts > 10) - return ERROR_INT("nparts not in [1 ... 10]", procName, 1); - if (!outroot || outroot[0] == '\n') - return ERROR_INT("outroot undefined or empty", procName, 1); - - /* Initialize the output array for each of the nparts */ - pixaca = (PIXAC **)LEPT_CALLOC(nparts, sizeof(PIXAC *)); - for (i = 0; i < nparts; i++) - pixaca[i] = pixacompCreate(0); - - /* Process each page */ - line = 1; - bmf = bmfCreate(NULL, 10); - for (pageno = 0; pageno < npage; pageno++) { - if ((pix1 = pixacompGetPix(pixac, pageno)) == NULL) { - L_ERROR("pix for page %d not found\n", procName, pageno); - continue; - } - - /* Scale, binarize and deskew */ - res = pixGetXRes(pix1); - if (res == 0 || res == 300 || res > 600) { - pix2 = pixClone(pix1); - } else { - factor = 300.0 / (l_float32)res; - if (factor > 3) - L_WARNING("resolution is very low\n", procName); - pix2 = pixScale(pix1, factor, factor); - } - pix3 = pixConvertTo1Adaptive(pix2); - pix4 = pixDeskew(pix3, 0); - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - if (!pix4) { - L_ERROR("pix for page %d not deskewed\n", procName, pageno); - continue; - } - pix1 = pixClone(pix4); /* rename */ - pixDestroy(&pix4); - - /* Find the stave sets at 4x reduction */ - boxa1 = pixLocateStaveSets(pix1, pageno, pixadb); - - /* Break each stave set into the separate staves (parts). - * A typical set will have more than one part, but if one of - * the parts is a keyboard, it will usually have two staves - * (also called a Grand Staff), composed of treble and - * bass staves. For example, a classical violin sonata - * could have a staff for the violin and two staves for - * the piano. We would set nparts == 2, and extract both - * of the piano staves as the piano part. */ - nbox = boxaGetCount(boxa1); - lept_stderr("number of boxes in page %d: %d\n", pageno, nbox); - for (i = 0; i < nbox; i++, line++) { - snprintf(buf, sizeof(buf), "%d", line); - box1 = boxaGetBox(boxa1, i, L_COPY); - pix2 = pixClipRectangle(pix1, box1, NULL); - pix3 = pixMorphSequence(pix2, "d1.20 + o50.1 + o1.30", 0); - boxa2 = pixConnCompBB(pix3, 8); - boxa3 = boxaSort(boxa2, L_SORT_BY_Y, L_SORT_INCREASING, NULL); - boxaRemoveVGaps(boxa3); - icount = boxaGetCount(boxa3); - if (icount < nparts) - L_WARNING("nparts requested = %d, but only found %d\n", - procName, nparts, icount); - for (j = 0; j < icount && j < nparts; j++) { - box2 = boxaGetBox(boxa3, j, L_COPY); - if (j == nparts - 1) /* extend the box to the bottom */ - boxSetSideLocations(box2, -1, -1, -1, - pixGetHeight(pix1) - 1); - pix4 = pixClipRectangle(pix2, box2, NULL); - pix5 = pixAddTextlines(pix4, bmf, buf, 1, L_ADD_LEFT); - pixacompAddPix(pixaca[j], pix5, IFF_TIFF_G4); - boxDestroy(&box2); - pixDestroy(&pix4); - pixDestroy(&pix5); - } - boxaDestroy(&boxa2); - boxaDestroy(&boxa3); - boxDestroy(&box1); - pixDestroy(&pix2); - pixDestroy(&pix3); - } - boxaDestroy(&boxa1); - pixDestroy(&pix1); - } - - /* Output separate pdfs for each part */ - for (i = 0; i < nparts; i++) { - snprintf(buf, sizeof(buf), "%s-%d.pdf", outroot, i); - L_INFO("writing part %d: %s\n", procName, i, buf); - pixacompConvertToPdf(pixaca[i], 300, 1.0, L_G4_ENCODE, 0, NULL, buf); - pixacompDestroy(&pixaca[i]); - } - LEPT_FREE(pixaca); - bmfDestroy(&bmf); - return 0; -} - - -/* - * \brief pixLocateStaveSets() - * - * \param[in] pixs 1 bpp, 300 ppi, deskewed - * \param[in] pageno page number; used for debug output - * \param[in] pixadb [optional] debug pixa; can be NULL - * \return boxa containing the stave sets at full resolution - */ -static BOXA * -pixLocateStaveSets(PIX *pixs, - l_int32 pageno, - PIXA *pixadb) -{ -BOXA *boxa1, *boxa2, *boxa3, *boxa4; -PIX *pix1, *pix2; - - /* Find the stave sets at 4x reduction */ - pix1 = pixMorphSequence(pixs, "r11", 0); - boxa1 = pixConnCompBB(pix1, 8); - boxa2 = boxaSelectByArea(boxa1, 15000, L_SELECT_IF_GT, NULL); - boxa3 = boxaSort(boxa2, L_SORT_BY_Y, L_SORT_INCREASING, NULL); - if (pixadb) { - pix2 = pixConvertTo32(pix1); - pixRenderBoxaArb(pix2, boxa3, 2, 255, 0, 0); - pixaAddPix(pixadb, pix2, L_INSERT); - pixDisplay(pix2, 100 * pageno, 100); - } - boxaDestroy(&boxa1); - boxaDestroy(&boxa2); - - boxaRemoveVGaps(boxa3); - if (pixadb) { - pix2 = pixConvertTo32(pix1); - pixRenderBoxaArb(pix2, boxa3, 2, 0, 255, 0); - pixaAddPix(pixadb, pix2, L_INSERT); - pixDisplay(pix2, 100 * pageno, 600); - } - boxa4 = boxaTransform(boxa3, 0, 0, 4.0, 4.0); /* back to full res */ - boxaDestroy(&boxa3); - pixDestroy(&pix1); - return boxa4; -} - - -/* - * \brief boxaRemoveVGaps() - * - * \param[in] boxa - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The boxes in %boxa are aligned vertically. Move the horizontal - * edges vertically to remove the gaps between boxes. - *- */ -static l_ok -boxaRemoveVGaps(BOXA *boxa) -{ -l_int32 nbox, i, y1, h1, y2, h2, delta; - - nbox = boxaGetCount(boxa); - for (i = 0; i < nbox - 1; i++) { - boxaGetBoxGeometry(boxa, i, NULL, &y1, NULL, &h1); - boxaGetBoxGeometry(boxa, i + 1, NULL, &y2, NULL, &h2); - delta = (y2 - y1 - h1) / 2; - boxaAdjustBoxSides(boxa, i, 0, 0, 0, delta); - boxaAdjustBoxSides(boxa, i + 1, 0, 0, -delta, 0); - } - boxaAdjustBoxSides(boxa, nbox - 1, 0, 0, 0, delta); /* bot of last */ - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/partition.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/partition.c deleted file mode 100644 index 3d4c74b9..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/partition.c +++ /dev/null @@ -1,662 +0,0 @@ -/*====================================================================* - - 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 partition.c - *- * - * Whitespace block extraction - * BOXA *boxaGetWhiteblocks() - * - * Helpers - * static PARTEL *partelCreate() - * static void partelDestroy() - * static l_int32 partelSetSize() - * static BOXA *boxaGenerateSubboxes() - * static BOX *boxaSelectPivotBox() - * static l_int32 boxaCheckIfOverlapIsSmall() - * BOXA *boxaPruneSortedOnOverlap() - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include "allheaders.h" - -/*! Partition element */ -struct PartitionElement { - l_float32 size; /* sorting key */ - BOX *box; /* region of the element */ - BOXA *boxa; /* set of intersecting boxes */ -}; -typedef struct PartitionElement PARTEL; - -static PARTEL * partelCreate(BOX *box); -static void partelDestroy(PARTEL **ppartel); -static l_int32 partelSetSize(PARTEL *partel, l_int32 sortflag); -static BOXA * boxaGenerateSubboxes(BOX *box, BOXA *boxa, l_int32 maxperim, - l_float32 fract); -static BOX * boxaSelectPivotBox(BOX *box, BOXA *boxa, l_int32 maxperim, - l_float32 fract); -static l_int32 boxCheckIfOverlapIsBig(BOX *box, BOXA *boxa, - l_float32 maxoverlap); - -static const l_int32 DefaultMaxPops = 20000; - - -#ifndef NO_CONSOLE_IO -#define OUTPUT_HEAP_STATS 0 -#endif /* ~NO_CONSOLE_IO */ - -/*------------------------------------------------------------------* - * Whitespace block extraction * - *------------------------------------------------------------------*/ -/*! - * \brief boxaGetWhiteblocks() - * - * \param[in] boxas typ. a set of bounding boxes of fg components - * \param[in] box initial region; typically including all boxes - * in boxas; if null, it computes the region to - * include all boxes in boxas - * \param[in] sortflag L_SORT_BY_WIDTH, L_SORT_BY_HEIGHT, - * L_SORT_BY_MIN_DIMENSION, L_SORT_BY_MAX_DIMENSION, - * L_SORT_BY_PERIMETER, L_SORT_BY_AREA - * \param[in] maxboxes max number of output whitespace boxes; e.g., 100 - * \param[in] maxoverlap maximum fractional overlap of a box by any - * of the larger boxes; e.g., 0.2 - * \param[in] maxperim maximum half-perimeter, in pixels, for which - * pivot is selected by proximity to box centroid; - * e.g., 200 - * \param[in] fract fraction of box diagonal that is an acceptable - * distance from the box centroid to select - * the pivot; e.g., 0.2 - * \param[in] maxpops max number of pops from the heap; use 0 as default - * \return boxa of sorted whitespace boxes, or NULL on error - * - * - * Notes: - * (1) This uses the elegant Breuel algorithm, found in "Two - * Geometric Algorithms for Layout Analysis", 2002, - * url: "citeseer.ist.psu.edu/breuel02two.html". - * It starts with the bounding boxes (b.b.) of the connected - * components (c.c.) in a region, along with the rectangle - * representing that region. It repeatedly divides the - * rectangle into four maximal rectangles that exclude a - * pivot rectangle, sorting them in a priority queue - * according to one of the six sort flags. It returns a boxa - * of the "largest" set that have no intersection with boxes - * from the input boxas. - * (2) If box == NULL, the initial region is the minimal region - * that includes the origin and every box in boxas. - * (3) maxboxes is the maximum number of whitespace boxes that will - * be returned. The actual number will depend on the image - * and the values chosen for maxoverlap and maxpops. In many - * cases, the actual number will be 'maxboxes'. - * (4) maxoverlap allows pruning of whitespace boxes depending on - * the overlap. To avoid all pruning, use maxoverlap = 1.0. - * To select only boxes that have no overlap with each other - * (maximal pruning), choose maxoverlap = 0.0. - * Otherwise, no box can have more than the 'maxoverlap' fraction - * of its area overlapped by any larger (in the sense of the - * sortflag) box. - * (5) Choose maxperim (actually, maximum half-perimeter) to - * represent a c.c. that is small enough so that you don't care - * about the white space that could be inside of it. For all such - * c.c., the pivot for 'quadfurcation' of a rectangle is selected - * as having a reasonable proximity to the rectangle centroid. - * (6) Use fract in the range [0.0 ... 1.0]. Set fract = 0.0 - * to choose the small box nearest the centroid as the pivot. - * If you choose fract > 0.0, it is suggested that you call - * boxaPermuteRandom() first, to permute the boxes (see usage below). - * This should reduce the search time for each of the pivot boxes. - * (7) Choose maxpops to be the maximum number of rectangles that - * are popped from the heap. This is an indirect way to limit the - * execution time. Use 0 for default (a fairly large number). - * At any time, you can expect the heap to contain about - * 2.5 times as many boxes as have been popped off. - * (8) The output result is a sorted set of overlapping - * boxes, constrained by 'maxboxes', 'maxoverlap' and 'maxpops'. - * (9) The main defect of the method is that it abstracts out the - * actual components, retaining only the b.b. for analysis. - * Consider a component with a large b.b. If this is chosen - * as a pivot, all white space inside is immediately taken - * out of consideration. Furthermore, even if it is never chosen - * as a pivot, as the partitioning continues, at no time will - * any of the whitespace inside this component be part of a - * rectangle with zero overlapping boxes. Thus, the interiors - * of all boxes are necessarily excluded from the union of - * the returned whitespace boxes. - * (10) It should be noted that the algorithm puts a large number - * of partels on the queue. Setting a limit of X partels to - * remove from the queue, one typically finds that there will be - * several times that number (say, 2X - 3X) left on the queue. - * For an efficient algorithm to find the largest white or - * or black rectangles, without permitting them to overlap, - * see pixFindLargeRectangles(). - * (11) USAGE: One way to accommodate to this weakness is to remove such - * large b.b. before starting the computation. For example, - * if 'box' is an input image region containing 'boxa' b.b. of c.c.: - * - * // Faster pivot choosing - * boxaPermuteRandom(boxa, boxa); - * - * // Remove anything either large width or height - * boxat = boxaSelectBySize(boxa, maxwidth, maxheight, - * L_SELECT_IF_BOTH, L_SELECT_IF_LT, - * NULL); - * - * boxad = boxaGetWhiteblocks(boxat, box, type, maxboxes, - * maxoverlap, maxperim, fract, - * maxpops); - * - * The result will be rectangular regions of "white space" that - * extend into (and often through) the excluded components. - * (11) As a simple example, suppose you wish to find the columns on a page. - * First exclude large c.c. that may block the columns, and then call: - * - * boxad = boxaGetWhiteblocks(boxa, box, L_SORT_BY_HEIGHT, - * 20, 0.15, 200, 0.2, 2000); - * - * to get the 20 tallest boxes with no more than 0.15 overlap - * between a box and any of the taller ones, and avoiding the - * use of any c.c. with a b.b. half perimeter greater than 200 - * as a pivot. - *- */ -BOXA * -boxaGetWhiteblocks(BOXA *boxas, - BOX *box, - l_int32 sortflag, - l_int32 maxboxes, - l_float32 maxoverlap, - l_int32 maxperim, - l_float32 fract, - l_int32 maxpops) -{ -l_int32 i, w, h, n, nsub, npush, npop; -BOX *boxsub; -BOXA *boxa, *boxa4, *boxasub, *boxad; -PARTEL *partel; -L_HEAP *lh; - - PROCNAME("boxaGetWhiteblocks"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (sortflag != L_SORT_BY_WIDTH && sortflag != L_SORT_BY_HEIGHT && - sortflag != L_SORT_BY_MIN_DIMENSION && - sortflag != L_SORT_BY_MAX_DIMENSION && - sortflag != L_SORT_BY_PERIMETER && sortflag != L_SORT_BY_AREA) - return (BOXA *)ERROR_PTR("invalid sort flag", procName, NULL); - if (maxboxes < 1) { - maxboxes = 1; - L_WARNING("setting maxboxes = 1\n", procName); - } - if (maxoverlap < 0.0 || maxoverlap > 1.0) - return (BOXA *)ERROR_PTR("invalid maxoverlap", procName, NULL); - if (maxpops == 0) - maxpops = DefaultMaxPops; - - if (!box) { - boxaGetExtent(boxas, &w, &h, NULL); - box = boxCreate(0, 0, w, h); - } - - /* Prime the heap */ - lh = lheapCreate(20, L_SORT_DECREASING); - partel = partelCreate(box); - partel->boxa = boxaCopy(boxas, L_CLONE); - partelSetSize(partel, sortflag); - lheapAdd(lh, partel); - - npush = 1; - npop = 0; - boxad = boxaCreate(0); - while (1) { - if ((partel = (PARTEL *)lheapRemove(lh)) == NULL) /* we're done */ - break; - - npop++; /* How many boxes have we retrieved from the queue? */ - if (npop > maxpops) { - partelDestroy(&partel); - break; - } - - /* Extract the contents */ - boxa = boxaCopy(partel->boxa, L_CLONE); - box = boxClone(partel->box); - partelDestroy(&partel); - - /* Can we output this one? */ - n = boxaGetCount(boxa); - if (n == 0) { - if (boxCheckIfOverlapIsBig(box, boxad, maxoverlap) == 0) - boxaAddBox(boxad, box, L_INSERT); - else - boxDestroy(&box); - boxaDestroy(&boxa); - if (boxaGetCount(boxad) >= maxboxes) /* we're done */ - break; - continue; - } - - - /* Generate up to 4 subboxes and put them on the heap */ - boxa4 = boxaGenerateSubboxes(box, boxa, maxperim, fract); - boxDestroy(&box); - nsub = boxaGetCount(boxa4); - for (i = 0; i < nsub; i++) { - boxsub = boxaGetBox(boxa4, i, L_CLONE); - boxasub = boxaIntersectsBox(boxa, boxsub); - partel = partelCreate(boxsub); - partel->boxa = boxasub; - partelSetSize(partel, sortflag); - lheapAdd(lh, partel); - boxDestroy(&boxsub); - } - npush += nsub; /* How many boxes have we put on the queue? */ - -/* boxaWriteStderr(boxa4); */ - - boxaDestroy(&boxa4); - boxaDestroy(&boxa); - } - -#if OUTPUT_HEAP_STATS - lept_stderr("Heap statistics:\n"); - lept_stderr(" Number of boxes pushed: %d\n", npush); - lept_stderr(" Number of boxes popped: %d\n", npop); - lept_stderr(" Number of boxes on heap: %d\n", lheapGetCount(lh)); -#endif /* OUTPUT_HEAP_STATS */ - - /* Clean up the heap */ - while ((partel = (PARTEL *)lheapRemove(lh)) != NULL) - partelDestroy(&partel); - lheapDestroy(&lh, FALSE); - - return boxad; -} - - -/*------------------------------------------------------------------* - * Helpers * - *------------------------------------------------------------------*/ -/*! - * \brief partelCreate() - * - * \param[in] box region; inserts a copy - * \return partel, or NULL on error - */ -static PARTEL * -partelCreate(BOX *box) -{ -PARTEL *partel; - - partel = (PARTEL *)LEPT_CALLOC(1, sizeof(PARTEL)); - partel->box = boxCopy(box); - return partel; -} - - -/*! - * \brief partelDestroy() - * - * \param[in,out] ppartel contents will be set to null before returning - * \return void - */ -static void -partelDestroy(PARTEL **ppartel) -{ -PARTEL *partel; - - PROCNAME("partelDestroy"); - - if (ppartel == NULL) { - L_WARNING("ptr address is null!\n", procName); - return; - } - - if ((partel = *ppartel) == NULL) - return; - - boxDestroy(&partel->box); - boxaDestroy(&partel->boxa); - LEPT_FREE(partel); - *ppartel = NULL; - return; -} - - -/*! - * \brief partelSetSize() - * - * \param[in] partel - * \param[in] sortflag L_SORT_BY_WIDTH, L_SORT_BY_HEIGHT, - * L_SORT_BY_MIN_DIMENSION, L_SORT_BY_MAX_DIMENSION, - * L_SORT_BY_PERIMETER, L_SORT_BY_AREA - * \return 0 if OK, 1 on error - */ -static l_int32 -partelSetSize(PARTEL *partel, - l_int32 sortflag) -{ -l_int32 w, h; - - PROCNAME("partelSetSize"); - - if (!partel) - return ERROR_INT("partel not defined", procName, 1); - - boxGetGeometry(partel->box, NULL, NULL, &w, &h); - if (sortflag == L_SORT_BY_WIDTH) - partel->size = (l_float32)w; - else if (sortflag == L_SORT_BY_HEIGHT) - partel->size = (l_float32)h; - else if (sortflag == L_SORT_BY_MIN_DIMENSION) - partel->size = (l_float32)L_MIN(w, h); - else if (sortflag == L_SORT_BY_MAX_DIMENSION) - partel->size = (l_float32)L_MAX(w, h); - else if (sortflag == L_SORT_BY_PERIMETER) - partel->size = (l_float32)(w + h); - else if (sortflag == L_SORT_BY_AREA) - partel->size = (l_float32)(w * h); - else - return ERROR_INT("invalid sortflag", procName, 1); - return 0; -} - - -/*! - * \brief boxaGenerateSubboxes() - * - * \param[in] box region to be split into up to four overlapping - * subregions - * \param[in] boxa boxes of rectangles intersecting the box - * \param[in] maxperim maximum half-perimeter for which pivot - * is selected by proximity to box centroid - * \param[in] fract fraction of box diagonal that is an acceptable - * distance from the box centroid to select the pivot - * \return boxa of four or less overlapping subrectangles of - * the box, or NULL on error - */ -static BOXA * -boxaGenerateSubboxes(BOX *box, - BOXA *boxa, - l_int32 maxperim, - l_float32 fract) -{ -l_int32 x, y, w, h, xp, yp, wp, hp; -BOX *boxp; /* pivot box */ -BOX *boxsub; -BOXA *boxa4; - - PROCNAME("boxaGenerateSubboxes"); - - if (!box) - return (BOXA *)ERROR_PTR("box not defined", procName, NULL); - if (!boxa) - return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL); - - boxa4 = boxaCreate(4); - boxp = boxaSelectPivotBox(box, boxa, maxperim, fract); - boxGetGeometry(box, &x, &y, &w, &h); - boxGetGeometry(boxp, &xp, &yp, &wp, &hp); - boxDestroy(&boxp); - if (xp > x) { /* left sub-box */ - boxsub = boxCreate(x, y, xp - x, h); - boxaAddBox(boxa4, boxsub, L_INSERT); - } - if (yp > y) { /* top sub-box */ - boxsub = boxCreate(x, y, w, yp - y); - boxaAddBox(boxa4, boxsub, L_INSERT); - } - if (xp + wp < x + w) { /* right sub-box */ - boxsub = boxCreate(xp + wp, y, x + w - xp - wp, h); - boxaAddBox(boxa4, boxsub, L_INSERT); - } - if (yp + hp < y + h) { /* bottom sub-box */ - boxsub = boxCreate(x, yp + hp, w, y + h - yp - hp); - boxaAddBox(boxa4, boxsub, L_INSERT); - } - - return boxa4; -} - - -/*! - * \brief boxaSelectPivotBox() - * - * \param[in] box containing box; to be split by the pivot box - * \param[in] boxa boxes of rectangles, from which 1 is to be chosen - * \param[in] maxperim maximum half-perimeter for which pivot - * is selected by proximity to box centroid - * \param[in] fract fraction of box diagonal that is an acceptable - * distance from the box centroid to select the pivot - * \return box pivot box for subdivision into 4 rectangles, - * or NULL on error - * - *- * Notes: - * (1) This is a tricky piece that wasn't discussed in the - * Breuel's 2002 paper. - * (2) Selects a box from boxa whose centroid is reasonably close to - * the centroid of the containing box (xc, yc) and whose - * half-perimeter does not exceed the maxperim value. - * (3) If there are no boxes in the boxa that are small enough, - * then it selects the smallest of the larger boxes, - * without reference to its location in the containing box. - * (4) If a small box has a centroid at a distance from the - * centroid of the containing box that is not more than - * the fraction 'fract' of the diagonal of the containing - * box, that box is chosen as the pivot, terminating the - * search for the nearest small box. - * (5) Use fract in the range [0.0 ... 1.0]. Set fract = 0.0 - * to choose the small box nearest the centroid. - * (6) Choose maxperim to represent a connected component that is - * small enough so that you don't care about the white space - * that could be inside of it. - *- */ -static BOX * -boxaSelectPivotBox(BOX *box, - BOXA *boxa, - l_int32 maxperim, - l_float32 fract) -{ -l_int32 i, n, bw, bh, w, h; -l_int32 smallfound, minindex, perim, minsize; -l_float32 delx, dely, mindist, threshdist, dist, x, y, cx, cy; -BOX *boxt; - - PROCNAME("boxaSelectPivotBox"); - - if (!box) - return (BOX *)ERROR_PTR("box not defined", procName, NULL); - if (!boxa) - return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); - n = boxaGetCount(boxa); - if (n == 0) - return (BOX *)ERROR_PTR("no boxes in boxa", procName, NULL); - if (fract < 0.0 || fract > 1.0) { - L_WARNING("fract out of bounds; using 0.0\n", procName); - fract = 0.0; - } - - boxGetGeometry(box, NULL, NULL, &w, &h); - boxGetCenter(box, &x, &y); - threshdist = fract * (w * w + h * h); - mindist = 1000000000.; - minindex = 0; - smallfound = FALSE; - for (i = 0; i < n; i++) { - boxt = boxaGetBox(boxa, i, L_CLONE); - boxGetGeometry(boxt, NULL, NULL, &bw, &bh); - boxGetCenter(boxt, &cx, &cy); - boxDestroy(&boxt); - if (bw + bh > maxperim) - continue; - smallfound = TRUE; - delx = cx - x; - dely = cy - y; - dist = delx * delx + dely * dely; - if (dist <= threshdist) - return boxaGetBox(boxa, i, L_COPY); - if (dist < mindist) { - minindex = i; - mindist = dist; - } - } - - /* If there are small boxes but none are within 'fract' of the - * centroid, return the nearest one. */ - if (smallfound == TRUE) - return boxaGetBox(boxa, minindex, L_COPY); - - /* No small boxes; return the smallest of the large boxes */ - minsize = 1000000000; - minindex = 0; - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxa, i, NULL, NULL, &bw, &bh); - perim = bw + bh; - if (perim < minsize) { - minsize = perim; - minindex = i; - } - } - return boxaGetBox(boxa, minindex, L_COPY); -} - - -/*! - * \brief boxCheckIfOverlapIsBig() - * - * \param[in] box to be tested - * \param[in] boxa of boxes already stored - * \param[in] maxoverlap maximum fractional overlap of the input box - * by any of the boxes in boxa - * \return 0 if box has small overlap with every box in boxa; - * 1 otherwise or on error - */ -static l_int32 -boxCheckIfOverlapIsBig(BOX *box, - BOXA *boxa, - l_float32 maxoverlap) -{ -l_int32 i, n, bigoverlap; -l_float32 fract; -BOX *boxt; - - PROCNAME("boxCheckIfOverlapIsBig"); - - if (!box) - return ERROR_INT("box not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (maxoverlap < 0.0 || maxoverlap > 1.0) - return ERROR_INT("invalid maxoverlap", procName, 1); - - n = boxaGetCount(boxa); - if (n == 0 || maxoverlap == 1.0) - return 0; - - bigoverlap = 0; - for (i = 0; i < n; i++) { - boxt = boxaGetBox(boxa, i, L_CLONE); - boxOverlapFraction(boxt, box, &fract); - boxDestroy(&boxt); - if (fract > maxoverlap) { - bigoverlap = 1; - break; - } - } - - return bigoverlap; -} - - -/*! - * \brief boxaPruneSortedOnOverlap() - * - * \param[in] boxas sorted by size in decreasing order - * \param[in] maxoverlap maximum fractional overlap of a box by any - * of the larger boxes - * \return boxad pruned, or NULL on error - * - *- * Notes: - * (1) This selectively removes smaller boxes when they are overlapped - * by any larger box by more than the input 'maxoverlap' fraction. - * (2) To avoid all pruning, use maxoverlap = 1.0. To select only - * boxes that have no overlap with each other (maximal pruning), - * set maxoverlap = 0.0. - * (3) If there are no boxes in boxas, returns an empty boxa. - *- */ -BOXA * -boxaPruneSortedOnOverlap(BOXA *boxas, - l_float32 maxoverlap) -{ -l_int32 i, j, n, remove; -l_float32 fract; -BOX *box1, *box2; -BOXA *boxad; - - PROCNAME("boxaPruneSortedOnOverlap"); - - if (!boxas) - return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); - if (maxoverlap < 0.0 || maxoverlap > 1.0) - return (BOXA *)ERROR_PTR("invalid maxoverlap", procName, NULL); - - n = boxaGetCount(boxas); - if (n == 0 || maxoverlap == 1.0) - return boxaCopy(boxas, L_COPY); - - boxad = boxaCreate(0); - box2 = boxaGetBox(boxas, 0, L_COPY); - boxaAddBox(boxad, box2, L_INSERT); - for (j = 1; j < n; j++) { /* prune on j */ - box2 = boxaGetBox(boxas, j, L_COPY); - remove = FALSE; - for (i = 0; i < j; i++) { /* test on i */ - box1 = boxaGetBox(boxas, i, L_CLONE); - boxOverlapFraction(box1, box2, &fract); - boxDestroy(&box1); - if (fract > maxoverlap) { - remove = TRUE; - break; - } - } - if (remove == TRUE) - boxDestroy(&box2); - else - boxaAddBox(boxad, box2, L_INSERT); - } - - return boxad; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pdfio1.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pdfio1.c deleted file mode 100644 index c95f2a38..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pdfio1.c +++ /dev/null @@ -1,2255 +0,0 @@ -/*====================================================================* - - 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 pdfio1.c - *- * - * Higher-level operations for generating pdf from images. - * Use poppler's pdfimages to invert the process, extracting - * raster images from pdf. - * - * |=============================================================| - * | Important notes | - * |=============================================================| - * | Some of these functions require I/O libraries such as | - * | libtiff, libjpeg, libpng, libz and libopenjp2. If you do | - * | not have these libraries, some calls will fail. For | - * | example, if you do not have libopenjp2, you cannot write a | - * | pdf where transcoding is required to incorporate a | - * | jp2k image. | - * | | - * | You can manually deactivate all pdf writing by setting | - * | this in environ.h: | - * | \code | - * | #define USE_PDFIO 0 | - * | \endcode | - * | This will link the stub file pdfiostub.c. | - * |=============================================================| - * - * Set 1. These functions convert a set of image files - * to a multi-page pdf file, with one image on each page. - * All images are rendered at the same (input) resolution. - * The images can be specified as being in a directory, or they - * can be in an sarray. The output pdf can be either a file - * or an array of bytes in memory. - * - * Set 2. These functions are a special case of set 1, where - * no scaling or change in quality is required. For jpeg and jp2k - * images, the bytes in each file can be directly incorporated - * into the output pdf, and the wrapping up of multiple image - * files is very fast. For non-interlaced png, the data bytes - * including the predictors can also be written directly into the - * flate pdf data. For other image formats (e.g., tiff-g4), - * transcoding is required, where the image data is first decompressed - * and then the G4 or Flate (gzip) encodings are generated. - * - * Set 3. These functions convert a set of images in memory - * to a multi-page pdf, with one image on each page. The pdf - * output can be either a file or an array of bytes in memory. - * - * Set 4. These functions implement a pdf output "device driver" - * for wrapping (encoding) any number of images on a single page - * in pdf. The input can be either an image file or a Pix; - * the pdf output can be either a file or an array of bytes in memory. - * - * Set 5. These "segmented" functions take a set of image - * files, along with optional segmentation information, and - * generate a multi-page pdf file, where each page consists - * in general of a mixed raster pdf of image and non-image regions. - * The segmentation information for each page can be input as - * either a mask over the image parts, or as a Boxa of those - * regions. - * - * Set 6. These "segmented" functions convert an image and - * an optional Boxa of image regions into a mixed raster pdf file - * for the page. The input image can be either a file or a Pix. - * - * Set 7. These functions take a set of single-page pdf files - * and concatenates it into a multi-page pdf. The input can be - * a set of either single page pdf files or pdf 'strings' in memory. - * The output can be either a file or an array of bytes in memory. - * - * The images in the pdf file can be rendered using a pdf viewer, - * such as evince, gv, xpdf or acroread. - * - * Reference on the pdf file format: - * http://www.adobe.com/devnet/pdf/pdf_reference_archive.html - * - * 1. Convert specified image files to pdf (one image file per page) - * l_int32 convertFilesToPdf() - * l_int32 saConvertFilesToPdf() - * l_int32 saConvertFilesToPdfData() - * l_int32 selectDefaultPdfEncoding() - * - * 2. Convert specified image files to pdf without scaling - * l_int32 convertUnscaledFilesToPdf() - * l_int32 saConvertUnscaledFilesToPdf() - * l_int32 saConvertUnscaledFilesToPdfData() - * l_int32 convertUnscaledToPdfData() - * - * 3. Convert multiple images to pdf (one image per page) - * l_int32 pixaConvertToPdf() - * l_int32 pixaConvertToPdfData() - * - * 4. Single page, multi-image converters - * l_int32 convertToPdf() - * l_int32 convertImageDataToPdf() - * l_int32 convertToPdfData() - * l_int32 convertImageDataToPdfData() - * l_int32 pixConvertToPdf() - * l_int32 pixWriteStreamPdf() - * l_int32 pixWriteMemPdf() - * - * 5. Segmented multi-page, multi-image converter - * l_int32 convertSegmentedFilesToPdf() - * BOXAA *convertNumberedMasksToBoxaa() - * - * 6. Segmented single page, multi-image converters - * l_int32 convertToPdfSegmented() - * l_int32 pixConvertToPdfSegmented() - * l_int32 convertToPdfDataSegmented() - * l_int32 pixConvertToPdfDataSegmented() - * - * 7. Multipage concatenation - * l_int32 concatenatePdf() - * l_int32 saConcatenatePdf() - * l_int32 ptraConcatenatePdf() - * l_int32 concatenatePdfToData() - * l_int32 saConcatenatePdfToData() - * - * The top-level multi-image functions can be visualized as follows: - * Output pdf data to file: - * convertToPdf() and convertImageDataToPdf() - * --> pixConvertToPdf() - * --> pixConvertToPdfData() - * - * Output pdf data to array in memory: - * convertToPdfData() and convertImageDataToPdfData() - * --> pixConvertToPdfData() - * - * The top-level segmented image functions can be visualized as follows: - * Output pdf data to file: - * convertToPdfSegmented() - * --> pixConvertToPdfSegmented() - * --> pixConvertToPdfDataSegmented() - * - * Output pdf data to array in memory: - * convertToPdfDataSegmented() - * --> pixConvertToPdfDataSegmented() - * - * For multi-page concatenation, there are three different types of input - * (1) directory and optional filename filter - * (2) sarray of filenames - * (3) ptra of byte arrays of pdf data - * and two types of output for the concatenated pdf data - * (1) filename - * (2) data array and size - * High-level interfaces are given for each of the six combinations. - * - * Note: When wrapping small images into pdf, it is useful to give - * them a relatively low resolution value, to avoid rounding errors - * when rendering the images. For example, if you want an image - * of width w pixels to be 5 inches wide on a screen, choose a - * resolution w/5. - * - * The very fast functions in section (2) require neither transcoding - * nor parsing of the compressed jpeg file. With three types of image - * compression, the compressed strings can be incorporated into - * the pdf data without decompression and re-encoding: jpeg, jp2k - * and png. The DCTDecode and JPXDecode filters can handle the - * entire jpeg and jp2k encoded string as a byte array in the pdf file. - * The FlateDecode filter can handle the png compressed image data, - * including predictors that occur as the first byte in each - * raster line, but it is necessary to store only the png IDAT chunk - * data in the pdf array. The alternative for wrapping png images - * is to transcode them: uncompress into a raster (a pix) and then - * gzip the raster data. This typically results in a larger pdf file - * because it doesn't use the two-dimensional png predictor. - * Colormaps, which are found in png PLTE chunks, must always be - * pulled out and included separately in the pdf. For CCITT-G4 - * compression, you can not simply include a tiff G4 file -- you must - * either parse it and extract the G4 compressed data within it, - * or uncompress to a raster and G4 compress again. - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include -#include "allheaders.h" - -/* --------------------------------------------*/ -#if USE_PDFIO /* defined in environ.h */ - /* --------------------------------------------*/ - - /* Typical scan resolution in ppi (pixels/inch) */ -static const l_int32 DefaultInputRes = 300; - -/*---------------------------------------------------------------------* - * Convert specified image files to pdf (one image file per page) * - *---------------------------------------------------------------------*/ -/*! - * \brief convertFilesToPdf() - * - * \param[in] dirname directory name containing images - * \param[in] substr [optional] substring filter on filenames; - * can be NULL - * \param[in] res input resolution of all images - * \param[in] scalefactor scaling factor applied to each image; > 0.0 - * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, - * L_FLATE_ENCODE, L_JP2K_ENCODE or - * L_DEFAULT_ENCODE for default) - * \param[in] quality for jpeg: 1-100; 0 for default (75) - * for jp2k: 27-45; 0 for default (34) - * \param[in] title [optional] pdf title; if null, taken from - * the first image filename - * \param[in] fileout pdf file of all images - * \return 0 if OK, 1 on error - * - * - * Notes: - * (1) If %substr is not NULL, only image filenames that contain - * the substring can be used. If %substr == NULL, all files - * in the directory are used. - * (2) The files in the directory, after optional filtering by - * the substring, are lexically sorted in increasing order - * before concatenation. - * (3) The scalefactor is applied to each image before encoding. - * If you enter a value <= 0.0, it will be set to 1.0. - * (4) Specifying one of the four encoding types for %type forces - * all images to be compressed with that type. Use 0 to have - * the type determined for each image based on depth and whether - * or not it has a colormap. - *- */ -l_ok -convertFilesToPdf(const char *dirname, - const char *substr, - l_int32 res, - l_float32 scalefactor, - l_int32 type, - l_int32 quality, - const char *title, - const char *fileout) -{ -l_int32 ret; -SARRAY *sa; - - PROCNAME("convertFilesToPdf"); - - if (!dirname) - return ERROR_INT("dirname not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - - if ((sa = getSortedPathnamesInDirectory(dirname, substr, 0, 0)) == NULL) - return ERROR_INT("sa not made", procName, 1); - ret = saConvertFilesToPdf(sa, res, scalefactor, type, quality, - title, fileout); - sarrayDestroy(&sa); - return ret; -} - - -/*! - * \brief saConvertFilesToPdf() - * - * \param[in] sa string array of pathnames for images - * \param[in] res input resolution of all images - * \param[in] scalefactor scaling factor applied to each image; > 0.0 - * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, - * L_FLATE_ENCODE, L_JP2K_ENCODE or - * L_DEFAULT_ENCODE for default) - * \param[in] quality for jpeg: 1-100; 0 for default (75) - * for jp2k: 27-45; 0 for default (34) - * \param[in] title [optional] pdf title; if null, taken from - * the first image filename - * \param[in] fileout pdf file of all images - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) See convertFilesToPdf(). - *- */ -l_ok -saConvertFilesToPdf(SARRAY *sa, - l_int32 res, - l_float32 scalefactor, - l_int32 type, - l_int32 quality, - const char *title, - const char *fileout) -{ -l_uint8 *data; -l_int32 ret; -size_t nbytes; - - PROCNAME("saConvertFilesToPdf"); - - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - - ret = saConvertFilesToPdfData(sa, res, scalefactor, type, quality, - title, &data, &nbytes); - if (ret) { - if (data) LEPT_FREE(data); - return ERROR_INT("pdf data not made", procName, 1); - } - - ret = l_binaryWrite(fileout, "w", data, nbytes); - LEPT_FREE(data); - if (ret) - L_ERROR("pdf data not written to file\n", procName); - return ret; -} - - -/*! - * \brief saConvertFilesToPdfData() - * - * \param[in] sa string array of pathnames for images - * \param[in] res input resolution of all images - * \param[in] scalefactor scaling factor applied to each image; > 0.0 - * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, - * L_FLATE_ENCODE, L_JP2K_ENCODE or - * L_DEFAULT_ENCODE for default) - * \param[in] quality for jpeg: 1-100; 0 for default (75) - * for jp2k: 27-45; 0 for default (34) - * \param[in] title [optional] pdf title; if null, taken from - * the first image filename - * \param[out] pdata output pdf data (of all images - * \param[out] pnbytes size of output pdf data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) See convertFilesToPdf(). - *- */ -l_ok -saConvertFilesToPdfData(SARRAY *sa, - l_int32 res, - l_float32 scalefactor, - l_int32 type, - l_int32 quality, - const char *title, - l_uint8 **pdata, - size_t *pnbytes) -{ -char *fname; -const char *pdftitle; -l_uint8 *imdata; -l_int32 i, n, ret, pagetype, npages, scaledres; -size_t imbytes; -L_BYTEA *ba; -PIX *pixs, *pix; -L_PTRA *pa_data; - - PROCNAME("saConvertFilesToPdfData"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *pnbytes = 0; - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - if (scalefactor <= 0.0) scalefactor = 1.0; - if (type != L_JPEG_ENCODE && type != L_G4_ENCODE && - type != L_FLATE_ENCODE && type != L_JP2K_ENCODE) { - type = L_DEFAULT_ENCODE; - } - - /* Generate all the encoded pdf strings */ - n = sarrayGetCount(sa); - pa_data = ptraCreate(n); - pdftitle = NULL; - for (i = 0; i < n; i++) { - if (i && (i % 10 == 0)) lept_stderr(".. %d ", i); - fname = sarrayGetString(sa, i, L_NOCOPY); - if ((pixs = pixRead(fname)) == NULL) { - L_ERROR("image not readable from file %s\n", procName, fname); - continue; - } - if (!pdftitle) - pdftitle = (title) ? title : fname; - if (scalefactor != 1.0) - pix = pixScale(pixs, scalefactor, scalefactor); - else - pix = pixClone(pixs); - pixDestroy(&pixs); - scaledres = (l_int32)(res * scalefactor); - - /* Select the encoding type */ - if (type != L_DEFAULT_ENCODE) { - pagetype = type; - } else if (selectDefaultPdfEncoding(pix, &pagetype) != 0) { - pixDestroy(&pix); - L_ERROR("encoding type selection failed for file %s\n", - procName, fname); - continue; - } - - ret = pixConvertToPdfData(pix, pagetype, quality, &imdata, &imbytes, - 0, 0, scaledres, pdftitle, NULL, 0); - pixDestroy(&pix); - if (ret) { - LEPT_FREE(imdata); - L_ERROR("pdf encoding failed for %s\n", procName, fname); - continue; - } - ba = l_byteaInitFromMem(imdata, imbytes); - LEPT_FREE(imdata); - ptraAdd(pa_data, ba); - } - ptraGetActualCount(pa_data, &npages); - if (npages == 0) { - L_ERROR("no pdf files made\n", procName); - ptraDestroy(&pa_data, FALSE, FALSE); - return 1; - } - - /* Concatenate them */ - lept_stderr("\nconcatenating ... "); - ret = ptraConcatenatePdfToData(pa_data, NULL, pdata, pnbytes); - lept_stderr("done\n"); - - ptraGetActualCount(pa_data, &npages); /* recalculate in case it changes */ - for (i = 0; i < npages; i++) { - ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION); - l_byteaDestroy(&ba); - } - ptraDestroy(&pa_data, FALSE, FALSE); - return ret; -} - - -/*! - * \brief selectDefaultPdfEncoding() - * - * \param[in] pix - * \param[out] ptype L_G4_ENCODE, L_JPEG_ENCODE, L_FLATE_ENCODE - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This attempts to choose an encoding for the pix that results - * in the smallest file, assuming that if jpeg encoded, it will - * use quality = 75. The decision is approximate, in that - * (a) all colormapped images will be losslessly encoded with - * gzip (flate), and (b) an image with less than about 20 colors - * is likely to be smaller if flate encoded than if encoded - * as a jpeg (dct). For example, an image made by pixScaleToGray3() - * will have 10 colors, and flate encoding will give about - * twice the compression as jpeg with quality = 75. - *- */ -l_ok -selectDefaultPdfEncoding(PIX *pix, - l_int32 *ptype) -{ -l_int32 w, h, d, factor, ncolors; -PIXCMAP *cmap; - - PROCNAME("selectDefaultPdfEncoding"); - - if (!ptype) - return ERROR_INT("&type not defined", procName, 1); - *ptype = L_FLATE_ENCODE; /* default universal encoding */ - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - pixGetDimensions(pix, &w, &h, &d); - cmap = pixGetColormap(pix); - if (d == 8 && !cmap) { - factor = L_MAX(1, (l_int32)sqrt((l_float64)(w * h) / 20000.)); - pixNumColors(pix, factor, &ncolors); - if (ncolors < 20) - *ptype = L_FLATE_ENCODE; - else - *ptype = L_JPEG_ENCODE; - } else if (d == 1) { - *ptype = L_G4_ENCODE; - } else if (cmap || d == 2 || d == 4) { - *ptype = L_FLATE_ENCODE; - } else if (d == 8 || d == 32) { - *ptype = L_JPEG_ENCODE; - } else { - return ERROR_INT("type selection failure", procName, 1); - } - - return 0; -} - - -/*---------------------------------------------------------------------* - * Convert specified image files to pdf without scaling * - *---------------------------------------------------------------------*/ -/*! - * \brief convertUnscaledFilesToPdf() - * - * \param[in] dirname directory name containing images - * \param[in] substr [optional] substring filter on filenames; can be NULL - * \param[in] title [optional] pdf title; if null, taken from the first - * image filename - * \param[in] fileout pdf file of all images - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If %substr is not NULL, only image filenames that contain - * the substring can be used. If %substr == NULL, all files - * in the directory are used. - * (2) The files in the directory, after optional filtering by - * the substring, are lexically sorted in increasing order - * before concatenation. - * (3) This is very fast for jpeg, jp2k and some png files, because - * the compressed data is wrapped up and concatenated. For tiffg4 - * and other types of png, the images must be read and recompressed. - *- */ -l_ok -convertUnscaledFilesToPdf(const char *dirname, - const char *substr, - const char *title, - const char *fileout) -{ -l_int32 ret; -SARRAY *sa; - - PROCNAME("convertUnscaledFilesToPdf"); - - if (!dirname) - return ERROR_INT("dirname not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - - if ((sa = getSortedPathnamesInDirectory(dirname, substr, 0, 0)) == NULL) - return ERROR_INT("sa not made", procName, 1); - ret = saConvertUnscaledFilesToPdf(sa, title, fileout); - sarrayDestroy(&sa); - return ret; -} - - -/*! - * \brief saConvertUnscaledFilesToPdf() - * - * \param[in] sa string array of pathnames for images - * \param[in] title [optional] pdf title; if null, taken from the first - * image filename - * \param[in] fileout pdf file of all images - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) See convertUnscaledFilesToPdf(). - *- */ -l_ok -saConvertUnscaledFilesToPdf(SARRAY *sa, - const char *title, - const char *fileout) -{ -l_uint8 *data; -l_int32 ret; -size_t nbytes; - - PROCNAME("saConvertUnscaledFilesToPdf"); - - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - - ret = saConvertUnscaledFilesToPdfData(sa, title, &data, &nbytes); - if (ret) { - if (data) LEPT_FREE(data); - return ERROR_INT("pdf data not made", procName, 1); - } - - ret = l_binaryWrite(fileout, "w", data, nbytes); - LEPT_FREE(data); - if (ret) - L_ERROR("pdf data not written to file\n", procName); - return ret; -} - - -/*! - * \brief saConvertUnscaledFilesToPdfData() - * - * \param[in] sa string array of pathnames for image files - * \param[in] title [optional] pdf title; if null, taken from the first - * image filename - * \param[out] pdata output pdf data (of all images) - * \param[out] pnbytes size of output pdf data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This is very fast for jpeg, jp2k and some png files, because - * the compressed data is wrapped up and concatenated. For tiffg4 - * and other types of png, the images must be read and recompressed. - *- */ -l_ok -saConvertUnscaledFilesToPdfData(SARRAY *sa, - const char *title, - l_uint8 **pdata, - size_t *pnbytes) -{ -char *fname; -l_uint8 *imdata; -l_int32 i, n, ret, npages; -size_t imbytes; -L_BYTEA *ba; -L_PTRA *pa_data; - - PROCNAME("saConvertUnscaledFilesToPdfData"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *pnbytes = 0; - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - - /* Generate all the encoded pdf strings */ - n = sarrayGetCount(sa); - pa_data = ptraCreate(n); - for (i = 0; i < n; i++) { - if (i && (i % 10 == 0)) lept_stderr(".. %d ", i); - fname = sarrayGetString(sa, i, L_NOCOPY); - - /* Generate the pdf data */ - if (convertUnscaledToPdfData(fname, title, &imdata, &imbytes)) - continue; - - /* ... and add it to the array of single page data */ - ba = l_byteaInitFromMem(imdata, imbytes); - if (imdata) LEPT_FREE(imdata); - ptraAdd(pa_data, ba); - } - ptraGetActualCount(pa_data, &npages); - if (npages == 0) { - L_ERROR("no pdf files made\n", procName); - ptraDestroy(&pa_data, FALSE, FALSE); - return 1; - } - - /* Concatenate to generate a multipage pdf */ - lept_stderr("\nconcatenating ... "); - ret = ptraConcatenatePdfToData(pa_data, NULL, pdata, pnbytes); - lept_stderr("done\n"); - - /* Clean up */ - ptraGetActualCount(pa_data, &npages); /* maybe failed to read some files */ - for (i = 0; i < npages; i++) { - ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION); - l_byteaDestroy(&ba); - } - ptraDestroy(&pa_data, FALSE, FALSE); - return ret; -} - - -/*! - * \brief convertUnscaledToPdfData() - * - * \param[in] fname of image file in all formats - * \param[in] title [optional] pdf title; can be NULL - * \param[out] pdata output pdf data for image - * \param[out] pnbytes size of output pdf data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This is very fast for jpeg, jp2k and some png files, because - * the compressed data is wrapped up and concatenated. For tiffg4 - * and other types of png, the images must be read and recompressed. - *- */ -l_ok -convertUnscaledToPdfData(const char *fname, - const char *title, - l_uint8 **pdata, - size_t *pnbytes) -{ -const char *pdftitle = NULL; -char *tail = NULL; -l_int32 format; -L_COMP_DATA *cid; - - PROCNAME("convertUnscaledToPdfData"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *pnbytes = 0; - if (!fname) - return ERROR_INT("fname not defined", procName, 1); - - findFileFormat(fname, &format); - if (format == IFF_UNKNOWN) { - L_WARNING("file %s format is unknown; skip\n", procName, fname); - return 1; - } - if (format == IFF_PS || format == IFF_LPDF) { - L_WARNING("file %s format is %d; skip\n", procName, fname, format); - return 1; - } - - /* Generate the image data required for pdf generation, always - * in binary (not ascii85) coding. Note that jpeg, jp2k and - * some png files are not transcoded. */ - l_generateCIDataForPdf(fname, NULL, 0, &cid); - if (!cid) { - L_ERROR("file %s format is %d; unreadable\n", procName, fname, format); - return 1; - } - - /* If %title == NULL, use the tail of %fname. */ - if (title) { - pdftitle = title; - } else { - splitPathAtDirectory(fname, NULL, &tail); - pdftitle = tail; - } - - /* Generate the pdf string for this page (image). This destroys - * the cid by attaching it to an lpd and destroying the lpd. */ - cidConvertToPdfData(cid, pdftitle, pdata, pnbytes); - LEPT_FREE(tail); - return 0; -} - - -/*---------------------------------------------------------------------* - * Convert multiple images to pdf (one image per page) * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaConvertToPdf() - * - * \param[in] pixa containing images all at the same resolution - * \param[in] res override the resolution of each input image, - * in ppi; use 0 to respect the resolution - * embedded in the input images - * \param[in] scalefactor scaling factor applied to each image; > 0.0 - * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, - * L_FLATE_ENCODE, L_JP2K_ENCODE, or - * L_DEFAULT_ENCODE for default) - * \param[in] quality for jpeg: 1-100; 0 for default (75) - * for jp2k: 27-45; 0 for default (34) - * \param[in] title [optional] pdf title - * \param[in] fileout pdf file of all images - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The images are encoded with G4 if 1 bpp; JPEG if 8 bpp without - * colormap and many colors, or 32 bpp; FLATE for anything else. - * (2) The scalefactor must be > 0.0; otherwise it is set to 1.0. - * (3) Specifying one of the three encoding types for %type forces - * all images to be compressed with that type. Use 0 to have - * the type determined for each image based on depth and whether - * or not it has a colormap. - *- */ -l_ok -pixaConvertToPdf(PIXA *pixa, - l_int32 res, - l_float32 scalefactor, - l_int32 type, - l_int32 quality, - const char *title, - const char *fileout) -{ -l_uint8 *data; -l_int32 ret; -size_t nbytes; - - PROCNAME("pixaConvertToPdf"); - - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - - ret = pixaConvertToPdfData(pixa, res, scalefactor, type, quality, - title, &data, &nbytes); - if (ret) { - LEPT_FREE(data); - return ERROR_INT("conversion to pdf failed", procName, 1); - } - - ret = l_binaryWrite(fileout, "w", data, nbytes); - LEPT_FREE(data); - if (ret) - L_ERROR("pdf data not written to file\n", procName); - return ret; -} - - -/*! - * \brief pixaConvertToPdfData() - * - * \param[in] pixa containing images all at the same resolution - * \param[in] res input resolution of all images - * \param[in] scalefactor scaling factor applied to each image; > 0.0 - * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, - * L_FLATE_ENCODE, L_JP2K_ENCODE, or - * L_DEFAULT_ENCODE for default) - * \param[in] quality for jpeg: 1-100; 0 for default (75) - * for jp2k: 27-45; 0 for default (34) - * \param[in] title [optional] pdf title - * \param[out] pdata output pdf data of all images - * \param[out] pnbytes size of output pdf data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) See pixaConvertToPdf(). - *- */ -l_ok -pixaConvertToPdfData(PIXA *pixa, - l_int32 res, - l_float32 scalefactor, - l_int32 type, - l_int32 quality, - const char *title, - l_uint8 **pdata, - size_t *pnbytes) -{ -l_uint8 *imdata; -l_int32 i, n, ret, scaledres, pagetype; -size_t imbytes; -L_BYTEA *ba; -PIX *pixs, *pix; -L_PTRA *pa_data; - - PROCNAME("pixaConvertToPdfData"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *pnbytes = 0; - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - if (scalefactor <= 0.0) scalefactor = 1.0; - if (type != L_DEFAULT_ENCODE && type != L_JPEG_ENCODE && - type != L_G4_ENCODE && type != L_FLATE_ENCODE && - type != L_JP2K_ENCODE) { - L_WARNING("invalid compression type; using per-page default\n", - procName); - type = L_DEFAULT_ENCODE; - } - - /* Generate all the encoded pdf strings */ - n = pixaGetCount(pixa); - pa_data = ptraCreate(n); - for (i = 0; i < n; i++) { - if ((pixs = pixaGetPix(pixa, i, L_CLONE)) == NULL) { - L_ERROR("pix[%d] not retrieved\n", procName, i); - continue; - } - if (scalefactor != 1.0) - pix = pixScale(pixs, scalefactor, scalefactor); - else - pix = pixClone(pixs); - pixDestroy(&pixs); - scaledres = (l_int32)(res * scalefactor); - - /* Select the encoding type */ - if (type != L_DEFAULT_ENCODE) { - pagetype = type; - } else if (selectDefaultPdfEncoding(pix, &pagetype) != 0) { - L_ERROR("encoding type selection failed for pix[%d]\n", - procName, i); - pixDestroy(&pix); - continue; - } - - ret = pixConvertToPdfData(pix, pagetype, quality, &imdata, &imbytes, - 0, 0, scaledres, title, NULL, 0); - pixDestroy(&pix); - if (ret) { - LEPT_FREE(imdata); - L_ERROR("pdf encoding failed for pix[%d]\n", procName, i); - continue; - } - ba = l_byteaInitFromMem(imdata, imbytes); - LEPT_FREE(imdata); - ptraAdd(pa_data, ba); - } - ptraGetActualCount(pa_data, &n); - if (n == 0) { - L_ERROR("no pdf files made\n", procName); - ptraDestroy(&pa_data, FALSE, FALSE); - return 1; - } - - /* Concatenate them */ - ret = ptraConcatenatePdfToData(pa_data, NULL, pdata, pnbytes); - - ptraGetActualCount(pa_data, &n); /* recalculate in case it changes */ - for (i = 0; i < n; i++) { - ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION); - l_byteaDestroy(&ba); - } - ptraDestroy(&pa_data, FALSE, FALSE); - return ret; -} - - -/*---------------------------------------------------------------------* - * Single page, multi-image converters * - *---------------------------------------------------------------------*/ -/*! - * \brief convertToPdf() - * - * \param[in] filein input image file -- any format - * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, - * L_FLATE_ENCODE, or L_JP2K_ENCODE) - * \param[in] quality for jpeg: 1-100; 0 for default (75) - * for jp2k: 27-45; 0 for default (34) - * \param[in] fileout output pdf file; only required on last - * image on page - * \param[in] x, y location of lower-left corner of image, - * in pixels, relative to the PostScript origin - * (0,0) at the lower-left corner of the page - * \param[in] res override the resolution of the input image, - * in ppi; use 0 to respect the resolution - * embedded in the input images - * \param[in] title [optional] pdf title; if null, taken from filein - * \param[in,out] plpd ptr to lpd, which is created on the first - * invocation and returned until last image is - * processed, at which time it is destroyed - * \param[in] position in image sequence: L_FIRST_IMAGE, L_NEXT_IMAGE, - * L_LAST_IMAGE - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) To wrap only one image in pdf, input %plpd = NULL, and - * the value of %position will be ignored: - * convertToPdf(... type, quality, x, y, res, NULL, 0); - * (2) To wrap multiple images on a single pdf page, this is called - * once for each successive image. Do it this way: - * L_PDF_DATA *lpd; - * convertToPdf(... type, quality, x, y, res, &lpd, L_FIRST_IMAGE); - * convertToPdf(... type, quality, x, y, res, &lpd, L_NEXT_IMAGE); - * ... - * convertToPdf(... type, quality, x, y, res, &lpd, L_LAST_IMAGE); - * This will write the result to the value of %fileout specified - * in the first call; succeeding values of %fileout are ignored. - * On the last call: the pdf data bytes are computed and written - * to %fileout, lpd is destroyed internally, and the returned - * value of lpd is null. So the client has nothing to clean up. - * (3) (a) Set %res == 0 to respect the resolution embedded in the - * image file. If no resolution is embedded, it will be set - * to the default value. - * (b) Set %res to some other value to override the file resolution. - * (4) (a) If the input %res and the resolution of the output device - * are equal, the image will be "displayed" at the same size - * as the original. - * (b) If the input %res is 72, the output device will render - * the image at 1 pt/pixel. - * (c) Some possible choices for the default input pix resolution are: - * 72 ppi Render pix on any output device at one pt/pixel - * 96 ppi Windows default for generated display images - * 300 ppi Typical default for scanned images. - * We choose 300, which is sensible for rendering page images. - * However, images come from a variety of sources, and - * some are explicitly created for viewing on a display. - *- */ -l_ok -convertToPdf(const char *filein, - l_int32 type, - l_int32 quality, - const char *fileout, - l_int32 x, - l_int32 y, - l_int32 res, - const char *title, - L_PDF_DATA **plpd, - l_int32 position) -{ -l_uint8 *data; -l_int32 ret; -size_t nbytes; - - PROCNAME("convertToPdf"); - - if (!filein) - return ERROR_INT("filein not defined", procName, 1); - if (!plpd || (position == L_LAST_IMAGE)) { - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - } - - if (convertToPdfData(filein, type, quality, &data, &nbytes, x, y, - res, title, plpd, position)) - return ERROR_INT("pdf data not made", procName, 1); - - if (!plpd || (position == L_LAST_IMAGE)) { - ret = l_binaryWrite(fileout, "w", data, nbytes); - LEPT_FREE(data); - if (ret) - return ERROR_INT("pdf data not written to file", procName, 1); - } - - return 0; -} - - -/*! - * \brief convertImageDataToPdf() - * - * \param[in] imdata array of formatted image data; e.g., png, jpeg - * \param[in] size size of image data - * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, - * L_FLATE_ENCODE, or L_JP2K_ENCODE) - * \param[in] quality for jpeg: 1-100; 0 for default (75) - * for jp2k: 27-45; 0 for default (34) - * \param[in] fileout output pdf file; only required on last - * image on page - * \param[in] x, y location of lower-left corner of image, - * in pixels, relative to the PostScript origin - * (0,0) at the lower-left corner of the page - * \param[in] res override the resolution of the input image, - * in ppi; use 0 to respect the resolution - * embedded in the input images - * \param[in] title [optional] pdf title - * \param[in,out] plpd ptr to lpd, which is created on the first - * invocation and returned until last image is - * processed, at which time it is destroyed - * \param[in] position in image sequence: L_FIRST_IMAGE, L_NEXT_IMAGE, - * L_LAST_IMAGE - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If %res == 0 and the input resolution field is 0, - * this will use DefaultInputRes. - * (2) See comments in convertToPdf(). - *- */ -l_ok -convertImageDataToPdf(l_uint8 *imdata, - size_t size, - l_int32 type, - l_int32 quality, - const char *fileout, - l_int32 x, - l_int32 y, - l_int32 res, - const char *title, - L_PDF_DATA **plpd, - l_int32 position) -{ -l_int32 ret; -PIX *pix; - - PROCNAME("convertImageDataToPdf"); - - if (!imdata) - return ERROR_INT("image data not defined", procName, 1); - if (!plpd || (position == L_LAST_IMAGE)) { - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - } - - if ((pix = pixReadMem(imdata, size)) == NULL) - return ERROR_INT("pix not read", procName, 1); - if (type != L_JPEG_ENCODE && type != L_G4_ENCODE && - type != L_FLATE_ENCODE && type != L_JP2K_ENCODE) { - selectDefaultPdfEncoding(pix, &type); - } - ret = pixConvertToPdf(pix, type, quality, fileout, x, y, res, - title, plpd, position); - pixDestroy(&pix); - return ret; -} - - -/*! - * \brief convertToPdfData() - * - * \param[in] filein input image file -- any format - * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, - * L_FLATE_ENCODE, or L_JP2K_ENCODE) - * \param[in] quality for jpeg: 1-100; 0 for default (75) - * for jp2k: 27-45; 0 for default (34) - * \param[out] pdata pdf data in memory - * \param[out] pnbytes number of bytes in pdf data - * \param[in] x, y location of lower-left corner of image, - * in pixels, relative to the PostScript origin - * (0,0) at the lower-left corner of the page - * \param[in] res override the resolution of the input image, - * in ppi; use 0 to respect the resolution - * embedded in the input images - * \param[in] title [optional] pdf title; if null, use filein - * \param[in,out] plpd ptr to lpd, which is created on the first - * invocation and returned until last image is - * processed, at which time it is destroyed - * \param[in] position in image sequence: L_FIRST_IMAGE, L_NEXT_IMAGE, - * L_LAST_IMAGE - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If %res == 0 and the input resolution field is 0, - * this will use DefaultInputRes. - * (2) See comments in convertToPdf(). - *- */ -l_ok -convertToPdfData(const char *filein, - l_int32 type, - l_int32 quality, - l_uint8 **pdata, - size_t *pnbytes, - l_int32 x, - l_int32 y, - l_int32 res, - const char *title, - L_PDF_DATA **plpd, - l_int32 position) -{ -PIX *pix; - - PROCNAME("convertToPdfData"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *pnbytes = 0; - if (!filein) - return ERROR_INT("filein not defined", procName, 1); - - if ((pix = pixRead(filein)) == NULL) - return ERROR_INT("pix not made", procName, 1); - - pixConvertToPdfData(pix, type, quality, pdata, pnbytes, - x, y, res, (title) ? title : filein, plpd, position); - pixDestroy(&pix); - return 0; -} - - -/*! - * \brief convertImageDataToPdfData() - * - * \param[in] imdata array of formatted image data; e.g., png, jpeg - * \param[in] size size of image data - * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, - * L_FLATE_ENCODE, or L_JP2K_ENCODE) - * \param[in] quality for jpeg: 1-100; 0 for default (75) - * for jp2k: 27-45; 0 for default (34) - * \param[out] pdata pdf data in memory - * \param[out] pnbytes number of bytes in pdf data - * \param[in] x, y location of lower-left corner of image, - * in pixels, relative to the PostScript origin - * (0,0) at the lower-left corner of the page - * \param[in] res override the resolution of the input image, - * in ppi; use 0 to respect the resolution - * embedded in the input images - * \param[in] title [optional] pdf title - * \param[out] plpd ptr to lpd, which is created on the first - * invocation and returned until last image is - * processed, at which time it is destroyed - * \param[in] position in image sequence: L_FIRST_IMAGE, L_NEXT_IMAGE, - * L_LAST_IMAGE - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If %res == 0 and the input resolution field is 0, - * this will use DefaultInputRes. - * (2) See comments in convertToPdf(). - *- */ -l_ok -convertImageDataToPdfData(l_uint8 *imdata, - size_t size, - l_int32 type, - l_int32 quality, - l_uint8 **pdata, - size_t *pnbytes, - l_int32 x, - l_int32 y, - l_int32 res, - const char *title, - L_PDF_DATA **plpd, - l_int32 position) -{ -l_int32 ret; -PIX *pix; - - PROCNAME("convertImageDataToPdfData"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *pnbytes = 0; - if (!imdata) - return ERROR_INT("image data not defined", procName, 1); - if (plpd) { /* part of multi-page invocation */ - if (position == L_FIRST_IMAGE) - *plpd = NULL; - } - - if ((pix = pixReadMem(imdata, size)) == NULL) - return ERROR_INT("pix not read", procName, 1); - if (type != L_JPEG_ENCODE && type != L_G4_ENCODE && - type != L_FLATE_ENCODE && type != L_JP2K_ENCODE) { - selectDefaultPdfEncoding(pix, &type); - } - ret = pixConvertToPdfData(pix, type, quality, pdata, pnbytes, - x, y, res, title, plpd, position); - pixDestroy(&pix); - return ret; -} - - -/*! - * \brief pixConvertToPdf() - * - * \param[in] pix - * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, - * L_FLATE_ENCODE, L_JP2K_ENCODE) - * \param[in] quality for jpeg: 1-100; 0 for default (75) - * for jp2k: 27-45; 0 for default (34) - * \param[in] fileout output pdf file; only required on last - * image on page - * \param[in] x, y location of lower-left corner of image, - * in pixels, relative to the PostScript origin - * (0,0) at the lower-left corner of the page - * \param[in] res override the resolution of the input image, - * in ppi; use 0 to respect the resolution - * embedded in the input images - * \param[in] title [optional] pdf title - * \param[in,out] plpd ptr to lpd, which is created on the first - * invocation and returned until last image is - * processed, at which time it is destroyed - * \param[in] position in image sequence: L_FIRST_IMAGE, L_NEXT_IMAGE, - * L_LAST_IMAGE - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If %res == 0 and the input resolution field is 0, - * this will use DefaultInputRes. - * (2) This only writes data to fileout if it is the last - * image to be written on the page. - * (3) See comments in convertToPdf(). - *- */ -l_ok -pixConvertToPdf(PIX *pix, - l_int32 type, - l_int32 quality, - const char *fileout, - l_int32 x, - l_int32 y, - l_int32 res, - const char *title, - L_PDF_DATA **plpd, - l_int32 position) -{ -l_uint8 *data; -l_int32 ret; -size_t nbytes; - - PROCNAME("pixConvertToPdf"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!plpd || (position == L_LAST_IMAGE)) { - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - } - - if (pixConvertToPdfData(pix, type, quality, &data, &nbytes, - x, y, res, title, plpd, position)) { - LEPT_FREE(data); - return ERROR_INT("pdf data not made", procName, 1); - } - - if (!plpd || (position == L_LAST_IMAGE)) { - ret = l_binaryWrite(fileout, "w", data, nbytes); - LEPT_FREE(data); - if (ret) - return ERROR_INT("pdf data not written to file", procName, 1); - } - return 0; -} - - -/*! - * \brief pixWriteStreamPdf() - * - * \param[in] fp file stream opened for writing - * \param[in] pix all depths, cmap OK - * \param[in] res override the resolution of the input image, in ppi; - * use 0 to respect the resolution embedded in the input - * \param[in] title [optional] pdf title; taken from the first image - * placed on a page; e.g., an input image filename - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This is the simplest interface for writing a single image - * with pdf encoding to a stream. It uses G4 encoding for 1 bpp, - * JPEG encoding for 8 bpp (no cmap) and 32 bpp, and FLATE - * encoding for everything else. - *- */ -l_ok -pixWriteStreamPdf(FILE *fp, - PIX *pix, - l_int32 res, - const char *title) -{ -l_uint8 *data; -size_t nbytes, nbytes_written; - - PROCNAME("pixWriteStreamPdf"); - - if (!fp) - return ERROR_INT("stream not opened", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - if (pixWriteMemPdf(&data, &nbytes, pix, res, title) != 0) { - LEPT_FREE(data); - return ERROR_INT("pdf data not made", procName, 1); - } - - nbytes_written = fwrite(data, 1, nbytes, fp); - LEPT_FREE(data); - if (nbytes != nbytes_written) - return ERROR_INT("failure writing pdf data to stream", procName, 1); - return 0; -} - - -/*! - * \brief pixWriteMemPdf() - * - * \param[out] pdata pdf as byte array - * \param[out] pnbytes number of bytes in pdf array - * \param[in] pix all depths, cmap OK - * \param[in] res override the resolution of the input image, in ppi; - * use 0 to respect the res embedded in the input - * \param[in] title [optional] pdf title; taken from the first image - * placed on a page; e.g., an input image filename - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This is the simplest interface for writing a single image - * with pdf encoding to memory. It uses G4 encoding for 1 bpp, - * and makes a guess whether to use JPEG or FLATE encoding for - * everything else. - *- */ -l_ok -pixWriteMemPdf(l_uint8 **pdata, - size_t *pnbytes, - PIX *pix, - l_int32 res, - const char *title) -{ -l_int32 ret, type; - - PROCNAME("pixWriteMemPdf"); - - if (pdata) *pdata = NULL; - if (pnbytes) *pnbytes = 0; - if (!pdata || !pnbytes) - return ERROR_INT("&data or &nbytes not defined", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - selectDefaultPdfEncoding(pix, &type); - ret = pixConvertToPdfData(pix, type, 75, pdata, pnbytes, - 0, 0, res, title, NULL, 0); - if (ret) - return ERROR_INT("pdf data not made", procName, 1); - return 0; -} - - -/*---------------------------------------------------------------------* - * Segmented multi-page, multi-image converter * - *---------------------------------------------------------------------*/ -/*! - * \brief convertSegmentedFilesToPdf() - * - * \param[in] dirname directory name containing images - * \param[in] substr [optional] substring filter on filenames; - * can be NULL - * \param[in] res input resolution of all images - * \param[in] type compression type for non-image regions; the - * image regions are always compressed with - * L_JPEG_ENCODE - * \param[in] thresh used for converting gray --> 1 bpp with - * L_G4_ENCODE - * \param[in] baa [optional] boxaa of image regions - * \param[in] quality used for JPEG only; 0 for default (75) - * \param[in] scalefactor scaling factor applied to each image region - * \param[in] title [optional] pdf title; if null, taken from - * the first image filename - * \param[in] fileout pdf file of all images - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If %substr is not NULL, only image filenames that contain - * the substring can be used. If %substr == NULL, all files - * in the directory are used. - * (2) The files in the directory, after optional filtering by - * the substring, are lexically sorted in increasing order - * before concatenation. - * (3) The images are encoded with G4 if 1 bpp; JPEG if 8 bpp without - * colormap and many colors, or 32 bpp; FLATE for anything else. - * (4) The boxaa, if it exists, contains one boxa of "image regions" - * for each image file. The boxa must be aligned with the - * sorted set of images. - * (5) The scalefactor is applied to each image region. It is - * typically < 1.0, to save bytes in the final pdf, because - * the resolution is often not critical in non-text regions. - * (6) If the non-image regions have pixel depth > 1 and the encoding - * type is G4, they are automatically scaled up by 2x and - * thresholded. Otherwise, no scaling is performed on them. - * (7) Note that this function can be used to generate multipage - * G4 compressed pdf from any input, by using %boxaa == NULL - * and %type == L_G4_ENCODE. - *- */ -l_ok -convertSegmentedFilesToPdf(const char *dirname, - const char *substr, - l_int32 res, - l_int32 type, - l_int32 thresh, - BOXAA *baa, - l_int32 quality, - l_float32 scalefactor, - const char *title, - const char *fileout) -{ -char *fname; -l_uint8 *imdata, *data; -l_int32 i, npages, nboxa, nboxes, ret; -size_t imbytes, databytes; -BOXA *boxa; -L_BYTEA *ba; -L_PTRA *pa_data; -SARRAY *sa; - - PROCNAME("convertSegmentedFilesToPdf"); - - if (!dirname) - return ERROR_INT("dirname not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - - if ((sa = getNumberedPathnamesInDirectory(dirname, substr, 0, 0, 10000)) - == NULL) - return ERROR_INT("sa not made", procName, 1); - - npages = sarrayGetCount(sa); - /* If necessary, extend the boxaa, which is page-aligned with - * the image files, to be as large as the set of images. */ - if (baa) { - nboxa = boxaaGetCount(baa); - if (nboxa < npages) { - boxa = boxaCreate(1); - boxaaExtendWithInit(baa, npages, boxa); - boxaDestroy(&boxa); - } - } - - /* Generate and save all the encoded pdf strings */ - pa_data = ptraCreate(npages); - for (i = 0; i < npages; i++) { - fname = sarrayGetString(sa, i, L_NOCOPY); - if (!strcmp(fname, "")) continue; - boxa = NULL; - if (baa) { - boxa = boxaaGetBoxa(baa, i, L_CLONE); - nboxes = boxaGetCount(boxa); - if (nboxes == 0) - boxaDestroy(&boxa); - } - ret = convertToPdfDataSegmented(fname, res, type, thresh, boxa, - quality, scalefactor, title, - &imdata, &imbytes); - boxaDestroy(&boxa); /* safe; in case nboxes > 0 */ - if (ret) { - L_ERROR("pdf encoding failed for %s\n", procName, fname); - continue; - } - ba = l_byteaInitFromMem(imdata, imbytes); - if (imdata) LEPT_FREE(imdata); - ptraAdd(pa_data, ba); - } - sarrayDestroy(&sa); - - ptraGetActualCount(pa_data, &npages); - if (npages == 0) { - L_ERROR("no pdf files made\n", procName); - ptraDestroy(&pa_data, FALSE, FALSE); - return 1; - } - - /* Concatenate */ - ret = ptraConcatenatePdfToData(pa_data, NULL, &data, &databytes); - - /* Clean up */ - ptraGetActualCount(pa_data, &npages); /* recalculate in case it changes */ - for (i = 0; i < npages; i++) { - ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION); - l_byteaDestroy(&ba); - } - ptraDestroy(&pa_data, FALSE, FALSE); - - if (ret) { - if (data) LEPT_FREE(data); - return ERROR_INT("pdf data not made", procName, 1); - } - - ret = l_binaryWrite(fileout, "w", data, databytes); - LEPT_FREE(data); - if (ret) - L_ERROR("pdf data not written to file\n", procName); - return ret; -} - - -/*! - * \brief convertNumberedMasksToBoxaa() - * - * \param[in] dirname directory name containing mask images - * \param[in] substr [optional] substring filter on filenames; can be NULL - * \param[in] numpre number of characters in name before number - * \param[in] numpost number of characters in name after number, up - * to a dot before an extension - * \return boxaa of mask regions, or NULL on error - * - *- * Notes: - * (1) This is conveniently used to generate the input boxaa - * for convertSegmentedFilesToPdf(). It guarantees that the - * boxa will be aligned with the page images, even if some - * of the boxa are empty. - *- */ -BOXAA * -convertNumberedMasksToBoxaa(const char *dirname, - const char *substr, - l_int32 numpre, - l_int32 numpost) -{ -char *fname; -l_int32 i, n; -BOXA *boxa; -BOXAA *baa; -PIX *pix; -SARRAY *sa; - - PROCNAME("convertNumberedMasksToBoxaa"); - - if (!dirname) - return (BOXAA *)ERROR_PTR("dirname not defined", procName, NULL); - - if ((sa = getNumberedPathnamesInDirectory(dirname, substr, numpre, - numpost, 10000)) == NULL) - return (BOXAA *)ERROR_PTR("sa not made", procName, NULL); - - /* Generate and save all the encoded pdf strings */ - n = sarrayGetCount(sa); - baa = boxaaCreate(n); - boxa = boxaCreate(1); - boxaaInitFull(baa, boxa); - boxaDestroy(&boxa); - for (i = 0; i < n; i++) { - fname = sarrayGetString(sa, i, L_NOCOPY); - if (!strcmp(fname, "")) continue; - if ((pix = pixRead(fname)) == NULL) { - L_WARNING("invalid image on page %d\n", procName, i); - continue; - } - boxa = pixConnComp(pix, NULL, 8); - boxaaReplaceBoxa(baa, i, boxa); - pixDestroy(&pix); - } - - sarrayDestroy(&sa); - return baa; -} - - -/*---------------------------------------------------------------------* - * Segmented single page, multi-image converters * - *---------------------------------------------------------------------*/ -/*! - * \brief convertToPdfSegmented() - * - * \param[in] filein input image file -- any format - * \param[in] res input image resolution; typ. 300 ppi; - * use 0 for default - * \param[in] type compression type for non-image regions; image - * regions are always compressed with L_JPEG_ENCODE - * \param[in] thresh for converting gray --> 1 bpp with L_G4_ENCODE - * \param[in] boxa [optional] of image regions; can be null - * \param[in] quality used for jpeg image regions; 0 for default - * \param[in] scalefactor used for jpeg regions; must be <= 1.0 - * \param[in] title [optional] pdf title; typically taken from the - * input file for the pix - * \param[in] fileout output pdf file - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If there are no image regions, set %boxa == NULL; - * %quality and %scalefactor are ignored. - * (2) Typically, %scalefactor is < 1.0, because the image regions - * can be rendered at a lower resolution (for better compression) - * than the text regions. If %scalefactor == 0, we use 1.0. - * If the input image is 1 bpp and scalefactor < 1.0, we - * use scaleToGray() to downsample the image regions to gray - * before compressing them. - * (3) If the compression type for non-image regions is L_G4_ENCODE - * and bpp > 1, the image is upscaled 2x and thresholded - * to 1 bpp. That is the only situation where %thresh is used. - * (4) The parameter %quality is only used for image regions. - * If %type == L_JPEG_ENCODE, default jpeg quality (75) is - * used for the non-image regions. - * (5) Processing matrix for non-image regions. - * - * Input G4 JPEG FLATE - * ----------|--------------------------------------------------- - * 1 bpp | 1x, 1 bpp 1x flate, 1 bpp 1x, 1 bpp - * | - * cmap | 2x, 1 bpp 1x flate, cmap 1x, cmap - * | - * 2,4 bpp | 2x, 1 bpp 1x flate 1x, 2,4 bpp - * no cmap | 2,4 bpp - * | - * 8,32 bpp | 2x, 1 bpp 1x (jpeg) 1x, 8,32 bpp - * no cmap | 8,32 bpp - * - * Summary: - * (a) if G4 is requested, G4 is used, with 2x upscaling - * for all cases except 1 bpp. - * (b) if JPEG is requested, use flate encoding for all cases - * except 8 bpp without cmap and 32 bpp (rgb). - * (c) if FLATE is requested, use flate with no transformation - * of the raster data. - * (6) Calling options/sequence for these functions: - * file --> file (convertToPdfSegmented) - * pix --> file (pixConvertToPdfSegmented) - * pix --> data (pixConvertToPdfDataSegmented) - * file --> data (convertToPdfDataSegmented) - * pix --> data (pixConvertToPdfDataSegmented) - *- */ -l_ok -convertToPdfSegmented(const char *filein, - l_int32 res, - l_int32 type, - l_int32 thresh, - BOXA *boxa, - l_int32 quality, - l_float32 scalefactor, - const char *title, - const char *fileout) -{ -l_int32 ret; -PIX *pixs; - - PROCNAME("convertToPdfSegmented"); - - if (!filein) - return ERROR_INT("filein not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - if (type != L_G4_ENCODE && type != L_JPEG_ENCODE && - type != L_FLATE_ENCODE) - return ERROR_INT("invalid conversion type", procName, 1); - if (boxa && scalefactor > 1.0) { - L_WARNING("setting scalefactor to 1.0\n", procName); - scalefactor = 1.0; - } - - if ((pixs = pixRead(filein)) == NULL) - return ERROR_INT("pixs not made", procName, 1); - - ret = pixConvertToPdfSegmented(pixs, res, type, thresh, boxa, quality, - scalefactor, (title) ? title : filein, - fileout); - pixDestroy(&pixs); - return ret; -} - - -/*! - * \brief pixConvertToPdfSegmented() - * - * \param[in] pixs any depth, cmap OK - * \param[in] res input image resolution; typ. 300 ppi; - * use 0 for default - * \param[in] type compression type for non-image regions; image - * regions are always compressed with L_JPEG_ENCODE - * \param[in] thresh for converting gray --> 1 bpp with L_G4_ENCODE - * \param[in] boxa [optional] of image regions; can be null - * \param[in] quality used for jpeg image regions; 0 for default - * \param[in] scalefactor used for jpeg regions; must be <= 1.0 - * \param[in] title [optional] pdf title; typically taken from the - * input file for the pix - * \param[in] fileout output pdf file - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) See convertToPdfSegmented() for details. - *- */ -l_ok -pixConvertToPdfSegmented(PIX *pixs, - l_int32 res, - l_int32 type, - l_int32 thresh, - BOXA *boxa, - l_int32 quality, - l_float32 scalefactor, - const char *title, - const char *fileout) -{ -l_uint8 *data; -l_int32 ret; -size_t nbytes; - - PROCNAME("pixConvertToPdfSegmented"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - if (type != L_G4_ENCODE && type != L_JPEG_ENCODE && - type != L_FLATE_ENCODE) - return ERROR_INT("invalid conversion type", procName, 1); - if (boxa && scalefactor > 1.0) { - L_WARNING("setting scalefactor to 1.0\n", procName); - scalefactor = 1.0; - } - - ret = pixConvertToPdfDataSegmented(pixs, res, type, thresh, boxa, quality, - scalefactor, title, &data, &nbytes); - if (ret) - return ERROR_INT("pdf generation failure", procName, 1); - - ret = l_binaryWrite(fileout, "w", data, nbytes); - if (data) LEPT_FREE(data); - return ret; -} - - -/*! - * \brief convertToPdfDataSegmented() - * - * \param[in] filein input image file -- any format - * \param[in] res input image resolution; typ. 300 ppi; - * use 0 for default - * \param[in] type compression type for non-image regions; image - * regions are always compressed with L_JPEG_ENCODE - * \param[in] thresh for converting gray --> 1 bpp with L_G4_ENCODE - * \param[in] boxa [optional] image regions; can be null - * \param[in] quality used for jpeg image regions; 0 for default - * \param[in] scalefactor used for jpeg regions; must be <= 1.0 - * \param[in] title [optional] pdf title; if null, uses filein - * \param[out] pdata pdf data in memory - * \param[out] pnbytes number of bytes in pdf data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If there are no image regions, set %boxa == NULL; - * %quality and %scalefactor are ignored. - * (2) Typically, %scalefactor is < 1.0. The image regions are - *- */ -l_ok -convertToPdfDataSegmented(const char *filein, - l_int32 res, - l_int32 type, - l_int32 thresh, - BOXA *boxa, - l_int32 quality, - l_float32 scalefactor, - const char *title, - l_uint8 **pdata, - size_t *pnbytes) -{ -l_int32 ret; -PIX *pixs; - - PROCNAME("convertToPdfDataSegmented"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *pnbytes = 0; - if (!filein) - return ERROR_INT("filein not defined", procName, 1); - if (type != L_G4_ENCODE && type != L_JPEG_ENCODE && - type != L_FLATE_ENCODE) - return ERROR_INT("invalid conversion type", procName, 1); - if (boxa && scalefactor > 1.0) { - L_WARNING("setting scalefactor to 1.0\n", procName); - scalefactor = 1.0; - } - - if ((pixs = pixRead(filein)) == NULL) - return ERROR_INT("pixs not made", procName, 1); - - ret = pixConvertToPdfDataSegmented(pixs, res, type, thresh, boxa, - quality, scalefactor, - (title) ? title : filein, - pdata, pnbytes); - pixDestroy(&pixs); - return ret; -} - - -/*! - * \brief pixConvertToPdfDataSegmented() - * - * \param[in] pixs any depth, cmap OK - * \param[in] res input image resolution; typ. 300 ppi; - * use 0 for default - * \param[in] type compression type for non-image regions; image - * regions are always compressed with L_JPEG_ENCODE - * \param[in] thresh for converting gray --> 1 bpp with L_G4_ENCODE - * \param[in] boxa [optional] of image regions; can be null - * \param[in] quality used for jpeg image regions; 0 for default - * \param[in] scalefactor used for jpeg regions; must be <= 1.0 - * \param[in] title [optional] pdf title; typically taken from the - * input file for the pix - * \param[out] pdata pdf data in memory - * \param[out] pnbytes number of bytes in pdf data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) See convertToPdfSegmented() for details. - *- */ -l_ok -pixConvertToPdfDataSegmented(PIX *pixs, - l_int32 res, - l_int32 type, - l_int32 thresh, - BOXA *boxa, - l_int32 quality, - l_float32 scalefactor, - const char *title, - l_uint8 **pdata, - size_t *pnbytes) -{ -l_int32 i, nbox, seq, bx, by, bw, bh, upscale; -l_float32 scale; -BOX *box, *boxc, *box2; -PIX *pix, *pixt1, *pixt2, *pixt3, *pixt4, *pixt5, *pixt6; -PIXCMAP *cmap; -L_PDF_DATA *lpd; - - PROCNAME("pixConvertToPdfDataSegmented"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *pnbytes = 0; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (type != L_G4_ENCODE && type != L_JPEG_ENCODE && - type != L_FLATE_ENCODE) - return ERROR_INT("invalid conversion type", procName, 1); - if (boxa && (scalefactor <= 0.0 || scalefactor > 1.0)) { - L_WARNING("setting scalefactor to 1.0\n", procName); - scalefactor = 1.0; - } - - /* Adjust scalefactor so that the product with res gives an integer */ - if (res <= 0) - res = DefaultInputRes; - scale = (l_float32)((l_int32)(scalefactor * res + 0.5)) / (l_float32)res; - cmap = pixGetColormap(pixs); - - /* Simple case: single image to be encoded */ - if (!boxa || boxaGetCount(boxa) == 0) { - if (pixGetDepth(pixs) > 1 && type == L_G4_ENCODE) { - if (cmap) - pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - else - pixt1 = pixConvertTo8(pixs, FALSE); - pixt2 = pixScaleGray2xLIThresh(pixt1, thresh); - pixConvertToPdfData(pixt2, type, quality, pdata, pnbytes, - 0, 0, 2 * res, title, NULL, 0); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - } else { - pixConvertToPdfData(pixs, type, quality, pdata, pnbytes, - 0, 0, res, title, NULL, 0); - } - return 0; - } - - /* Multiple images to be encoded. If %type == L_G4_ENCODE, - * jpeg encode a version of pixs that is blanked in the non-image - * regions, and paint the scaled non-image part onto it through a mask. - * Otherwise, we must put the non-image part down first and - * then render all the image regions separately on top of it, - * at their own resolution. */ - pixt1 = pixSetBlackOrWhiteBoxa(pixs, boxa, L_SET_WHITE); /* non-image */ - nbox = boxaGetCount(boxa); - if (type == L_G4_ENCODE) { - pixt2 = pixCreateTemplate(pixs); /* only image regions */ - pixSetBlackOrWhite(pixt2, L_SET_WHITE); - for (i = 0; i < nbox; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - pix = pixClipRectangle(pixs, box, &boxc); - boxGetGeometry(boxc, &bx, &by, &bw, &bh); - pixRasterop(pixt2, bx, by, bw, bh, PIX_SRC, pix, 0, 0); - pixDestroy(&pix); - boxDestroy(&box); - boxDestroy(&boxc); - } - pixt3 = pixRemoveColormap(pixt2, REMOVE_CMAP_BASED_ON_SRC); - if (pixGetDepth(pixt3) == 1) - pixt4 = pixScaleToGray(pixt3, scale); - else - pixt4 = pixScale(pixt3, scale, scale); - pixConvertToPdfData(pixt4, L_JPEG_ENCODE, quality, pdata, pnbytes, - 0, 0, (l_int32)(scale * res), title, - &lpd, L_FIRST_IMAGE); - - if (pixGetDepth(pixt1) == 1) { - pixt5 = pixClone(pixt1); - upscale = 1; - } else { - pixt6 = pixConvertTo8(pixt1, 0); - pixt5 = pixScaleGray2xLIThresh(pixt6, thresh); - pixDestroy(&pixt6); - upscale = 2; - } - pixConvertToPdfData(pixt5, L_G4_ENCODE, quality, pdata, pnbytes, - 0, 0, upscale * res, title, &lpd, L_LAST_IMAGE); - pixDestroy(&pixt2); - pixDestroy(&pixt3); - pixDestroy(&pixt4); - pixDestroy(&pixt5); - } else { - /* Put the non-image part down first. This is the full - size of the page, so we can use it to find the page - height in pixels, which is required for determining - the LL corner of the image relative to the LL corner - of the page. */ - pixConvertToPdfData(pixt1, type, quality, pdata, pnbytes, 0, 0, - res, title, &lpd, L_FIRST_IMAGE); - for (i = 0; i < nbox; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - pixt2 = pixClipRectangle(pixs, box, &boxc); - pixt3 = pixRemoveColormap(pixt2, REMOVE_CMAP_BASED_ON_SRC); - if (pixGetDepth(pixt3) == 1) - pixt4 = pixScaleToGray(pixt3, scale); - else - pixt4 = pixScale(pixt3, scale, scale); - box2 = boxTransform(boxc, 0, 0, scale, scale); - boxGetGeometry(box2, &bx, &by, NULL, &bh); - seq = (i == nbox - 1) ? L_LAST_IMAGE : L_NEXT_IMAGE; - pixConvertToPdfData(pixt4, L_JPEG_ENCODE, quality, pdata, pnbytes, - bx, by, (l_int32)(scale * res), title, - &lpd, seq); - pixDestroy(&pixt2); - pixDestroy(&pixt3); - pixDestroy(&pixt4); - boxDestroy(&box); - boxDestroy(&boxc); - boxDestroy(&box2); - } - } - - pixDestroy(&pixt1); - return 0; -} - - -/*---------------------------------------------------------------------* - * Multi-page concatenation * - *---------------------------------------------------------------------*/ -/*! - * \brief concatenatePdf() - * - * \param[in] dirname directory name containing single-page pdf files - * \param[in] substr [optional] substring filter on filenames; can be NULL - * \param[in] fileout concatenated pdf file - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This only works with leptonica-formatted single-page pdf files. - * (2) If %substr is not NULL, only filenames that contain - * the substring can be returned. If %substr == NULL, - * none of the filenames are filtered out. - * (3) The files in the directory, after optional filtering by - * the substring, are lexically sorted in increasing order - * before concatenation. - *- */ -l_ok -concatenatePdf(const char *dirname, - const char *substr, - const char *fileout) -{ -l_int32 ret; -SARRAY *sa; - - PROCNAME("concatenatePdf"); - - if (!dirname) - return ERROR_INT("dirname not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - - if ((sa = getSortedPathnamesInDirectory(dirname, substr, 0, 0)) == NULL) - return ERROR_INT("sa not made", procName, 1); - ret = saConcatenatePdf(sa, fileout); - sarrayDestroy(&sa); - return ret; -} - - -/*! - * \brief saConcatenatePdf() - * - * \param[in] sa string array of pathnames for single-page pdf files - * \param[in] fileout concatenated pdf file - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This only works with leptonica-formatted single-page pdf files. - *- */ -l_ok -saConcatenatePdf(SARRAY *sa, - const char *fileout) -{ -l_uint8 *data; -l_int32 ret; -size_t nbytes; - - PROCNAME("saConcatenatePdf"); - - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - - ret = saConcatenatePdfToData(sa, &data, &nbytes); - if (ret) - return ERROR_INT("pdf data not made", procName, 1); - ret = l_binaryWrite(fileout, "w", data, nbytes); - LEPT_FREE(data); - return ret; -} - - -/*! - * \brief ptraConcatenatePdf() - * - * \param[in] pa array of pdf strings, each for a single-page pdf file - * \param[in] fileout concatenated pdf file - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This only works with leptonica-formatted single-page pdf files. - *- */ -l_ok -ptraConcatenatePdf(L_PTRA *pa, - const char *fileout) -{ -l_uint8 *data; -l_int32 ret; -size_t nbytes; - - PROCNAME("ptraConcatenatePdf"); - - if (!pa) - return ERROR_INT("pa not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - - ret = ptraConcatenatePdfToData(pa, NULL, &data, &nbytes); - if (ret) - return ERROR_INT("pdf data not made", procName, 1); - ret = l_binaryWrite(fileout, "w", data, nbytes); - LEPT_FREE(data); - return ret; -} - - -/*! - * \brief concatenatePdfToData() - * - * \param[in] dirname directory name containing single-page pdf files - * \param[in] substr [optional] substring filter on filenames; can be NULL - * \param[out] pdata concatenated pdf data in memory - * \param[out] pnbytes number of bytes in pdf data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This only works with leptonica-formatted single-page pdf files. - * (2) If %substr is not NULL, only filenames that contain - * the substring can be returned. If %substr == NULL, - * none of the filenames are filtered out. - * (3) The files in the directory, after optional filtering by - * the substring, are lexically sorted in increasing order - * before concatenation. - *- */ -l_ok -concatenatePdfToData(const char *dirname, - const char *substr, - l_uint8 **pdata, - size_t *pnbytes) -{ -l_int32 ret; -SARRAY *sa; - - PROCNAME("concatenatePdfToData"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *pnbytes = 0; - if (!dirname) - return ERROR_INT("dirname not defined", procName, 1); - - if ((sa = getSortedPathnamesInDirectory(dirname, substr, 0, 0)) == NULL) - return ERROR_INT("sa not made", procName, 1); - ret = saConcatenatePdfToData(sa, pdata, pnbytes); - sarrayDestroy(&sa); - return ret; -} - - -/*! - * \brief saConcatenatePdfToData() - * - * \param[in] sa string array of pathnames for single-page pdf files - * \param[out] pdata concatenated pdf data in memory - * \param[out] pnbytes number of bytes in pdf data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This only works with leptonica-formatted single-page pdf files. - *- */ -l_ok -saConcatenatePdfToData(SARRAY *sa, - l_uint8 **pdata, - size_t *pnbytes) -{ -char *fname; -l_int32 i, npages, ret; -L_BYTEA *bas; -L_PTRA *pa_data; /* input pdf data for each page */ - - PROCNAME("saConcatenatePdfToData"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *pnbytes = 0; - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - - /* Read the pdf files into memory */ - if ((npages = sarrayGetCount(sa)) == 0) - return ERROR_INT("no filenames found", procName, 1); - pa_data = ptraCreate(npages); - for (i = 0; i < npages; i++) { - fname = sarrayGetString(sa, i, L_NOCOPY); - bas = l_byteaInitFromFile(fname); - ptraAdd(pa_data, bas); - } - - ret = ptraConcatenatePdfToData(pa_data, sa, pdata, pnbytes); - - /* Cleanup: some pages could have been removed */ - ptraGetActualCount(pa_data, &npages); - for (i = 0; i < npages; i++) { - bas = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION); - l_byteaDestroy(&bas); - } - ptraDestroy(&pa_data, FALSE, FALSE); - return ret; -} - -/* --------------------------------------------*/ -#endif /* USE_PDFIO */ -/* --------------------------------------------*/ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pdfio1stub.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pdfio1stub.c deleted file mode 100644 index 78cf1158..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pdfio1stub.c +++ /dev/null @@ -1,309 +0,0 @@ -/*====================================================================* - - 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 pdfio1stub.c - *- * - * Stubs for pdfio1.c functions - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include "allheaders.h" - -/* --------------------------------------------*/ -#if !USE_PDFIO /* defined in environ.h */ -/* --------------------------------------------*/ - -/* ----------------------------------------------------------------------*/ - -l_ok convertFilesToPdf(const char *dirname, const char *substr, - l_int32 res, l_float32 scalefactor, - l_int32 type, l_int32 quality, - const char *title, const char *fileout) -{ - return ERROR_INT("function not present", "convertFilesToPdf", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok saConvertFilesToPdf(SARRAY *sa, l_int32 res, l_float32 scalefactor, - l_int32 type, l_int32 quality, - const char *title, const char *fileout) -{ - return ERROR_INT("function not present", "saConvertFilesToPdf", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok saConvertFilesToPdfData(SARRAY *sa, l_int32 res, - l_float32 scalefactor, l_int32 type, - l_int32 quality, const char *title, - l_uint8 **pdata, size_t *pnbytes) -{ - return ERROR_INT("function not present", "saConvertFilesToPdfData", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok selectDefaultPdfEncoding(PIX *pix, l_int32 *ptype) -{ - return ERROR_INT("function not present", "selectDefaultPdfEncoding", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertUnscaledFilesToPdf(const char *dirname, const char *substr, - const char *title, const char *fileout) -{ - return ERROR_INT("function not present", "convertUnscaledFilesToPdf", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok saConvertUnscaledFilesToPdf(SARRAY *sa, const char *title, - const char *fileout) -{ - return ERROR_INT("function not present", "saConvertUnscaledFilesToPdf", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok saConvertUnscaledFilesToPdfData(SARRAY *sa, const char *title, - l_uint8 **pdata, size_t *pnbytes) -{ - return ERROR_INT("function not present", - "saConvertUnscaledFilesToPdfData", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertUnscaledToPdfData(const char *fname, const char *title, - l_uint8 **pdata, size_t *pnbytes) -{ - return ERROR_INT("function not present", "convertUnscaledToPdfData", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixaConvertToPdf(PIXA *pixa, l_int32 res, l_float32 scalefactor, - l_int32 type, l_int32 quality, - const char *title, const char *fileout) -{ - return ERROR_INT("function not present", "pixaConvertToPdf", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixaConvertToPdfData(PIXA *pixa, l_int32 res, l_float32 scalefactor, - l_int32 type, l_int32 quality, const char *title, - l_uint8 **pdata, size_t *pnbytes) -{ - return ERROR_INT("function not present", "pixaConvertToPdfData", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertToPdf(const char *filein, - l_int32 type, l_int32 quality, - const char *fileout, - l_int32 x, l_int32 y, l_int32 res, - const char *title, - L_PDF_DATA **plpd, l_int32 position) -{ - return ERROR_INT("function not present", "convertToPdf", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertImageDataToPdf(l_uint8 *imdata, size_t size, - l_int32 type, l_int32 quality, - const char *fileout, - l_int32 x, l_int32 y, l_int32 res, - const char *title, - L_PDF_DATA **plpd, l_int32 position) -{ - return ERROR_INT("function not present", "convertImageDataToPdf", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertToPdfData(const char *filein, - l_int32 type, l_int32 quality, - l_uint8 **pdata, size_t *pnbytes, - l_int32 x, l_int32 y, l_int32 res, - const char *title, - L_PDF_DATA **plpd, l_int32 position) -{ - return ERROR_INT("function not present", "convertToPdfData", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertImageDataToPdfData(l_uint8 *imdata, size_t size, - l_int32 type, l_int32 quality, - l_uint8 **pdata, size_t *pnbytes, - l_int32 x, l_int32 y, l_int32 res, - const char *title, - L_PDF_DATA **plpd, l_int32 position) -{ - return ERROR_INT("function not present", "convertImageDataToPdfData", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixConvertToPdf(PIX *pix, l_int32 type, l_int32 quality, - const char *fileout, - l_int32 x, l_int32 y, l_int32 res, - const char *title, - L_PDF_DATA **plpd, l_int32 position) -{ - return ERROR_INT("function not present", "pixConvertToPdf", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixWriteStreamPdf(FILE *fp, PIX *pix, l_int32 res, const char *title) -{ - return ERROR_INT("function not present", "pixWriteStreamPdf", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixWriteMemPdf(l_uint8 **pdata, size_t *pnbytes, PIX *pix, - l_int32 res, const char *title) -{ - return ERROR_INT("function not present", "pixWriteMemPdf", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertSegmentedFilesToPdf(const char *dirname, const char *substr, - l_int32 res, l_int32 type, l_int32 thresh, - BOXAA *baa, l_int32 quality, - l_float32 scalefactor, const char *title, - const char *fileout) -{ - return ERROR_INT("function not present", "convertSegmentedFilesToPdf", 1); -} - -/* ----------------------------------------------------------------------*/ - -BOXAA * convertNumberedMasksToBoxaa(const char *dirname, const char *substr, - l_int32 numpre, l_int32 numpost) -{ - return (BOXAA *)ERROR_PTR("function not present", - "convertNumberedMasksToBoxaa", NULL); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertToPdfSegmented(const char *filein, l_int32 res, l_int32 type, - l_int32 thresh, BOXA *boxa, l_int32 quality, - l_float32 scalefactor, const char *title, - const char *fileout) -{ - return ERROR_INT("function not present", "convertToPdfSegmented", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixConvertToPdfSegmented(PIX *pixs, l_int32 res, l_int32 type, - l_int32 thresh, BOXA *boxa, l_int32 quality, - l_float32 scalefactor, const char *title, - const char *fileout) -{ - return ERROR_INT("function not present", "pixConvertToPdfSegmented", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertToPdfDataSegmented(const char *filein, l_int32 res, - l_int32 type, l_int32 thresh, BOXA *boxa, - l_int32 quality, l_float32 scalefactor, - const char *title, - l_uint8 **pdata, size_t *pnbytes) -{ - return ERROR_INT("function not present", "convertToPdfDataSegmented", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixConvertToPdfDataSegmented(PIX *pixs, l_int32 res, l_int32 type, - l_int32 thresh, BOXA *boxa, - l_int32 quality, l_float32 scalefactor, - const char *title, - l_uint8 **pdata, size_t *pnbytes) -{ - return ERROR_INT("function not present", "pixConvertToPdfDataSegmented", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok concatenatePdf(const char *dirname, const char *substr, - const char *fileout) -{ - return ERROR_INT("function not present", "concatenatePdf", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok saConcatenatePdf(SARRAY *sa, const char *fileout) -{ - return ERROR_INT("function not present", "saConcatenatePdf", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok ptraConcatenatePdf(L_PTRA *pa, const char *fileout) -{ - return ERROR_INT("function not present", "ptraConcatenatePdf", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok concatenatePdfToData(const char *dirname, const char *substr, - l_uint8 **pdata, size_t *pnbytes) -{ - return ERROR_INT("function not present", "concatenatePdfToData", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok saConcatenatePdfToData(SARRAY *sa, l_uint8 **pdata, size_t *pnbytes) -{ - return ERROR_INT("function not present", "saConcatenatePdfToData", 1); -} - -/* ----------------------------------------------------------------------*/ - -/* --------------------------------------------*/ -#endif /* !USE_PDFIO */ -/* --------------------------------------------*/ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pdfio2.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pdfio2.c deleted file mode 100644 index 82407993..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pdfio2.c +++ /dev/null @@ -1,2589 +0,0 @@ -/*====================================================================* - - 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 pdfio2.c - * - * - * Lower-level operations for generating pdf. - * - * Intermediate function for single page, multi-image conversion - * l_int32 pixConvertToPdfData() - * - * Intermediate function for generating multipage pdf output - * l_int32 ptraConcatenatePdfToData() - * - * Convert tiff multipage to pdf file - * l_int32 convertTiffMultipageToPdf() - * - * Low-level CID-based operations - * - * Without transcoding - * l_int32 l_generateCIDataForPdf() - * L_COMP_DATA *l_generateFlateDataPdf() - * L_COMP_DATA *l_generateJpegData() - * L_COMP_DATA *l_generateJpegDataMem() - * static L_COMP_DATA *l_generateJp2kData() - * - * With transcoding - * l_int32 l_generateCIData() - * l_int32 pixGenerateCIData() - * L_COMP_DATA *l_generateFlateData() - * static L_COMP_DATA *pixGenerateFlateData() - * static L_COMP_DATA *pixGenerateJpegData() - * static L_COMP_DATA *pixGenerateJp2kData() - * static L_COMP_DATA *pixGenerateG4Data() - * L_COMP_DATA *l_generateG4Data() - * - * Other - * l_int32 cidConvertToPdfData() - * void l_CIDataDestroy() - * - * Helper functions for generating the output pdf string - * static l_int32 l_generatePdf() - * static void generateFixedStringsPdf() - * static char *generateEscapeString() - * static void generateMediaboxPdf() - * static l_int32 generatePageStringPdf() - * static l_int32 generateContentStringPdf() - * static l_int32 generatePreXStringsPdf() - * static l_int32 generateColormapStringsPdf() - * static void generateTrailerPdf() - * static l_int32 makeTrailerStringPdf() - * static l_int32 generateOutputDataPdf() - * - * Helper functions for generating multipage pdf output - * static l_int32 parseTrailerPdf() - * static char *generatePagesObjStringPdf() - * static L_BYTEA *substituteObjectNumbers() - * - * Create/destroy/access pdf data - * static L_PDF_DATA *pdfdataCreate() - * static void pdfdataDestroy() - * static L_COMP_DATA *pdfdataGetCid() - * - * Set flags for special modes - * void l_pdfSetG4ImageMask() - * void l_pdfSetDateAndVersion() - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include -#include "allheaders.h" - -/* --------------------------------------------*/ -#if USE_PDFIO /* defined in environ.h */ - /* --------------------------------------------*/ - - /* Typical scan resolution in ppi (pixels/inch) */ -static const l_int32 DefaultInputRes = 300; - - /* Static helpers */ -static L_COMP_DATA *l_generateJp2kData(const char *fname); -static L_COMP_DATA *pixGenerateFlateData(PIX *pixs, l_int32 ascii85flag); -static L_COMP_DATA *pixGenerateJpegData(PIX *pixs, l_int32 ascii85flag, - l_int32 quality); -static L_COMP_DATA *pixGenerateJp2kData(PIX *pixs, l_int32 quality); -static L_COMP_DATA *pixGenerateG4Data(PIX *pixs, l_int32 ascii85flag); - -static l_int32 l_generatePdf(l_uint8 **pdata, size_t *pnbytes, - L_PDF_DATA *lpd); -static void generateFixedStringsPdf(L_PDF_DATA *lpd); -static char *generateEscapeString(const char *str); -static void generateMediaboxPdf(L_PDF_DATA *lpd); -static l_int32 generatePageStringPdf(L_PDF_DATA *lpd); -static l_int32 generateContentStringPdf(L_PDF_DATA *lpd); -static l_int32 generatePreXStringsPdf(L_PDF_DATA *lpd); -static l_int32 generateColormapStringsPdf(L_PDF_DATA *lpd); -static void generateTrailerPdf(L_PDF_DATA *lpd); -static char *makeTrailerStringPdf(L_DNA *daloc); -static l_int32 generateOutputDataPdf(l_uint8 **pdata, size_t *pnbytes, - L_PDF_DATA *lpd); - -static l_int32 parseTrailerPdf(L_BYTEA *bas, L_DNA **pda); -static char *generatePagesObjStringPdf(NUMA *napage); -static L_BYTEA *substituteObjectNumbers(L_BYTEA *bas, NUMA *na_objs); - -static L_PDF_DATA *pdfdataCreate(const char *title); -static void pdfdataDestroy(L_PDF_DATA **plpd); -static L_COMP_DATA *pdfdataGetCid(L_PDF_DATA *lpd, l_int32 index); - - -/* ---------------- Defaults for rendering options ----------------- */ - /* Output G4 as writing through image mask; this is the default */ -static l_int32 var_WRITE_G4_IMAGE_MASK = 1; - /* Write date/time and lib version into pdf; this is the default */ -static l_int32 var_WRITE_DATE_AND_VERSION = 1; - -#define L_SMALLBUF 256 -#define L_BIGBUF 2048 /* must be able to hold hex colormap */ - - -#ifndef NO_CONSOLE_IO -#define DEBUG_MULTIPAGE 0 -#endif /* ~NO_CONSOLE_IO */ - - -/*---------------------------------------------------------------------* - * Intermediate function for generating multipage pdf output * - *---------------------------------------------------------------------*/ -/*! - * \brief pixConvertToPdfData() - * - * \param[in] pix all depths; cmap OK - * \param[in] type L_G4_ENCODE, L_JPEG_ENCODE, L_FLATE_ENCODE, - * L_JP2K_ENCODE - * \param[in] quality for jpeg: 1-100; 0 for default (75) - * for jp2k: 27-45; 0 for default (34) - * \param[out] pdata pdf array - * \param[out] pnbytes number of bytes in pdf array - * \param[in] x, y location of lower-left corner of image, in pixels, - * relative to the PostScript origin (0,0) at - * the lower-left corner of the page) - * \param[in] res override the resolution of the input image, in ppi; - * use 0 to respect resolution embedded in the input - * \param[in] title [optional] pdf title; can be null - * \param[in,out] plpd ptr to lpd; created on the first invocation and - * returned until last image is processed - * \param[in] position in image sequence: L_FIRST_IMAGE, L_NEXT_IMAGE, - * L_LAST_IMAGE - * \return 0 if OK, 1 on error - * - * - * Notes: - * (1) If %res == 0 and the input resolution field is 0, - * this will use DefaultInputRes. - * (2) This only writes %data if it is the last image to be - * written on the page. - * (3) See comments in convertToPdf(). - *- */ -l_ok -pixConvertToPdfData(PIX *pix, - l_int32 type, - l_int32 quality, - l_uint8 **pdata, - size_t *pnbytes, - l_int32 x, - l_int32 y, - l_int32 res, - const char *title, - L_PDF_DATA **plpd, - l_int32 position) -{ -l_int32 pixres, w, h, ret; -l_float32 xpt, ypt, wpt, hpt; -L_COMP_DATA *cid = NULL; -L_PDF_DATA *lpd = NULL; - - PROCNAME("pixConvertToPdfData"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *pnbytes = 0; - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (type != L_JPEG_ENCODE && type != L_G4_ENCODE && - type != L_FLATE_ENCODE && type != L_JP2K_ENCODE) { - selectDefaultPdfEncoding(pix, &type); - } - if (plpd) { /* part of multi-page invocation */ - if (position == L_FIRST_IMAGE) - *plpd = NULL; - } - - /* Generate the compressed image data. It must NOT - * be ascii85 encoded. */ - pixGenerateCIData(pix, type, quality, 0, &cid); - if (!cid) - return ERROR_INT("cid not made", procName, 1); - - /* Get media box in pts. Guess the input image resolution - * based on the input parameter %res, the resolution data in - * the pix, and the size of the image. */ - pixres = cid->res; - w = cid->w; - h = cid->h; - if (res <= 0.0) { - if (pixres > 0) - res = pixres; - else - res = DefaultInputRes; - } - xpt = x * 72. / res; - ypt = y * 72. / res; - wpt = w * 72. / res; - hpt = h * 72. / res; - - /* Set up lpd */ - if (!plpd) { /* single image */ - if ((lpd = pdfdataCreate(title)) == NULL) - return ERROR_INT("lpd not made", procName, 1); - } else if (position == L_FIRST_IMAGE) { /* first of multiple images */ - if ((lpd = pdfdataCreate(title)) == NULL) - return ERROR_INT("lpd not made", procName, 1); - *plpd = lpd; - } else { /* not the first of multiple images */ - lpd = *plpd; - } - - /* Add the data to the lpd */ - ptraAdd(lpd->cida, cid); - lpd->n++; - ptaAddPt(lpd->xy, xpt, ypt); - ptaAddPt(lpd->wh, wpt, hpt); - - /* If a single image or the last of multiple images, - * generate the pdf and destroy the lpd */ - if (!plpd || (position == L_LAST_IMAGE)) { - ret = l_generatePdf(pdata, pnbytes, lpd); - pdfdataDestroy(&lpd); - if (plpd) *plpd = NULL; - if (ret) - return ERROR_INT("pdf output not made", procName, 1); - } - - return 0; -} - - -/*---------------------------------------------------------------------* - * Intermediate function for generating multipage pdf output * - *---------------------------------------------------------------------*/ -/*! - * \brief ptraConcatenatePdfToData() - * - * \param[in] pa_data ptra array of pdf strings, each for a - * single-page pdf file - * \param[in] sa [optional] string array of pathnames for - * input pdf files; can be null - * \param[out] pdata concatenated pdf data in memory - * \param[out] pnbytes number of bytes in pdf data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This only works with leptonica-formatted single-page pdf files. - * pdf files generated by other programs will have unpredictable - * (and usually bad) results. The requirements for each pdf file: - * (a) The Catalog and Info objects are the first two. - * (b) Object 3 is Pages - * (c) Object 4 is Page - * (d) The remaining objects are Contents, XObjects, and ColorSpace - * (2) We remove trailers from each page, and append the full trailer - * for all pages at the end. - * (3) For all but the first file, remove the ID and the first 3 - * objects (catalog, info, pages), so that each subsequent - * file has only objects of these classes: - * Page, Contents, XObject, ColorSpace (Indexed RGB). - * For those objects, we substitute these refs to objects - * in the local file: - * Page: Parent(object 3), Contents, XObject(typically multiple) - * XObject: [ColorSpace if indexed] - * The Pages object on the first page (object 3) has a Kids array - * of references to all the Page objects, with a Count equal - * to the number of pages. Each Page object refers back to - * this parent. - *- */ -l_ok -ptraConcatenatePdfToData(L_PTRA *pa_data, - SARRAY *sa, - l_uint8 **pdata, - size_t *pnbytes) -{ -char *fname, *str_pages, *str_trailer; -l_uint8 *pdfdata, *data; -l_int32 i, j, index, nobj, npages; -l_int32 *sizes, *locs; -size_t size; -L_BYTEA *bas, *bad, *bat1, *bat2; -L_DNA *da_locs, *da_sizes, *da_outlocs, *da; -L_DNAA *daa_locs; /* object locations on each page */ -NUMA *na_objs, *napage; -NUMAA *naa_objs; /* object mapping numbers to new values */ - - PROCNAME("ptraConcatenatePdfToData"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *pnbytes = 0; - if (!pa_data) - return ERROR_INT("pa_data not defined", procName, 1); - - /* Parse the files and find the object locations. - * Remove file data that cannot be parsed. */ - ptraGetActualCount(pa_data, &npages); - daa_locs = l_dnaaCreate(npages); - for (i = 0; i < npages; i++) { - bas = (L_BYTEA *)ptraGetPtrToItem(pa_data, i); - if (parseTrailerPdf(bas, &da_locs) != 0) { - bas = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION); - l_byteaDestroy(&bas); - if (sa) { - fname = sarrayGetString(sa, i, L_NOCOPY); - L_ERROR("can't parse file %s; skipping\n", procName, fname); - } else { - L_ERROR("can't parse file %d; skipping\n", procName, i); - } - } else { - l_dnaaAddDna(daa_locs, da_locs, L_INSERT); - } - } - - /* Recompute npages in case some of the files were not pdf */ - ptraCompactArray(pa_data); - ptraGetActualCount(pa_data, &npages); - if (npages == 0) { - l_dnaaDestroy(&daa_locs); - return ERROR_INT("no parsable pdf files found", procName, 1); - } - - /* Find the mapping from initial to final object numbers */ - naa_objs = numaaCreate(npages); /* stores final object numbers */ - napage = numaCreate(npages); /* stores "Page" object numbers */ - index = 0; - for (i = 0; i < npages; i++) { - da = l_dnaaGetDna(daa_locs, i, L_CLONE); - nobj = l_dnaGetCount(da); - if (i == 0) { - numaAddNumber(napage, 4); /* object 4 on first page */ - na_objs = numaMakeSequence(0.0, 1.0, nobj - 1); - index = nobj - 1; - } else { /* skip the first 3 objects in each file */ - numaAddNumber(napage, index); /* Page object is first we add */ - na_objs = numaMakeConstant(0.0, nobj - 1); - numaReplaceNumber(na_objs, 3, 3); /* refers to parent of all */ - for (j = 4; j < nobj - 1; j++) - numaSetValue(na_objs, j, index++); - } - numaaAddNuma(naa_objs, na_objs, L_INSERT); - l_dnaDestroy(&da); - } - - /* Make the Pages object (#3) */ - str_pages = generatePagesObjStringPdf(napage); - - /* Build the output */ - bad = l_byteaCreate(5000); - da_outlocs = l_dnaCreate(0); /* locations of all output objects */ - for (i = 0; i < npages; i++) { - bas = (L_BYTEA *)ptraGetPtrToItem(pa_data, i); - pdfdata = l_byteaGetData(bas, &size); - da_locs = l_dnaaGetDna(daa_locs, i, L_CLONE); /* locs on this page */ - na_objs = numaaGetNuma(naa_objs, i, L_CLONE); /* obj # on this page */ - nobj = l_dnaGetCount(da_locs) - 1; - da_sizes = l_dnaDiffAdjValues(da_locs); /* object sizes on this page */ - sizes = l_dnaGetIArray(da_sizes); - locs = l_dnaGetIArray(da_locs); - if (i == 0) { - l_byteaAppendData(bad, pdfdata, sizes[0]); - l_byteaAppendData(bad, pdfdata + locs[1], sizes[1]); - l_byteaAppendData(bad, pdfdata + locs[2], sizes[2]); - l_byteaAppendString(bad, str_pages); - for (j = 0; j < 4; j++) - l_dnaAddNumber(da_outlocs, locs[j]); - } - for (j = 4; j < nobj; j++) { - l_dnaAddNumber(da_outlocs, l_byteaGetSize(bad)); - bat1 = l_byteaInitFromMem(pdfdata + locs[j], sizes[j]); - bat2 = substituteObjectNumbers(bat1, na_objs); - data = l_byteaGetData(bat2, &size); - l_byteaAppendData(bad, data, size); - l_byteaDestroy(&bat1); - l_byteaDestroy(&bat2); - } - if (i == npages - 1) /* last one */ - l_dnaAddNumber(da_outlocs, l_byteaGetSize(bad)); - LEPT_FREE(sizes); - LEPT_FREE(locs); - l_dnaDestroy(&da_locs); - numaDestroy(&na_objs); - l_dnaDestroy(&da_sizes); - } - - /* Add the trailer */ - str_trailer = makeTrailerStringPdf(da_outlocs); - l_byteaAppendString(bad, str_trailer); - - /* Transfer the output data */ - *pdata = l_byteaCopyData(bad, pnbytes); - l_byteaDestroy(&bad); - -#if DEBUG_MULTIPAGE - lept_stderr("******** object mapper **********"); - numaaWriteStream(stderr, naa_objs); - - lept_stderr("******** Page object numbers ***********"); - numaWriteStderr(napage); - - lept_stderr("******** Pages object ***********\n"); - lept_stderr("%s\n", str_pages); -#endif /* DEBUG_MULTIPAGE */ - - numaDestroy(&napage); - numaaDestroy(&naa_objs); - l_dnaDestroy(&da_outlocs); - l_dnaaDestroy(&daa_locs); - LEPT_FREE(str_pages); - LEPT_FREE(str_trailer); - return 0; -} - - -/*---------------------------------------------------------------------* - * Convert tiff multipage to pdf file * - *---------------------------------------------------------------------*/ -/*! - * \brief convertTiffMultipageToPdf() - * - * \param[in] filein (tiff) - * \param[in] fileout (pdf) - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) A multipage tiff file can also be converted to PS, using - * convertTiffMultipageToPS() - *- */ -l_ok -convertTiffMultipageToPdf(const char *filein, - const char *fileout) -{ -l_int32 istiff; -PIXA *pixa; -FILE *fp; - - PROCNAME("convertTiffMultipageToPdf"); - - if ((fp = fopenReadStream(filein)) == NULL) - return ERROR_INT("file not found", procName, 1); - istiff = fileFormatIsTiff(fp); - fclose(fp); - if (!istiff) - return ERROR_INT("file not tiff format", procName, 1); - - pixa = pixaReadMultipageTiff(filein); - pixaConvertToPdf(pixa, 0, 1.0, 0, 0, "weasel2", fileout); - pixaDestroy(&pixa); - return 0; -} - - -/*---------------------------------------------------------------------* - * Low-level CID-based operations * - *---------------------------------------------------------------------*/ -/*! - * \brief l_generateCIDataForPdf() - * - * \param[in] fname [optional] can be null - * \param[in] pix [optional] can be null - * \param[in] quality for jpeg if transcoded: 1-100; 0 for default (75) - * for jp2k if transcoded: 27-45; 0 for default (34) - * \param[out] pcid compressed data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) You must set either filename or pix. - * (2) Given an image file and optionally a pix raster of that data, - * this provides a CID that is compatible with PDF, preferably - * without transcoding. - * (3) The pix is included for efficiency, in case transcoding - * is required and the pix is available to the caller. - * (4) We don't try to open files named "stdin" or "-" for Tesseract - * compatibility reasons. We may remove this restriction - * in the future. - *- */ -l_ok -l_generateCIDataForPdf(const char *fname, - PIX *pix, - l_int32 quality, - L_COMP_DATA **pcid) -{ -l_int32 format, type; -L_COMP_DATA *cid; -PIX *pixt; - - PROCNAME("l_generateCIDataForPdf"); - - if (!pcid) - return ERROR_INT("&cid not defined", procName, 1); - *pcid = cid = NULL; - if (!fname && !pix) - return ERROR_INT("neither fname nor pix are defined", procName, 1); - - /* If a compressed file is given that is not 'stdin', see if we - * can generate the pdf output without transcoding. */ - if (fname && strcmp(fname, "-") != 0 && strcmp(fname, "stdin") != 0) { - findFileFormat(fname, &format); - if (format == IFF_UNKNOWN) - L_WARNING("file %s format is unknown\n", procName, fname); - if (format == IFF_PS || format == IFF_LPDF) { - L_ERROR("file %s is unsupported format %d\n", - procName, fname, format); - return 1; - } - if (format == IFF_JFIF_JPEG) { - cid = l_generateJpegData(fname, 0); - } else if (format == IFF_JP2) { - cid = l_generateJp2kData(fname); - } else if (format == IFF_PNG) { - cid = l_generateFlateDataPdf(fname, pix); - } - - } - - /* Otherwise, use the pix to generate the pdf output */ - if (!cid) { - if (!pix) - pixt = pixRead(fname); - else - pixt = pixClone(pix); - if (!pixt) - return ERROR_INT("pixt not made", procName, 1); - if (selectDefaultPdfEncoding(pixt, &type)) { - pixDestroy(&pixt); - return 1; - } - pixGenerateCIData(pixt, type, quality, 0, &cid); - pixDestroy(&pixt); - } - if (!cid) { - L_ERROR("totally kerflummoxed\n", procName); - return 1; - } - *pcid = cid; - return 0; -} - - -/*! - * \brief l_generateFlateDataPdf() - * - * \param[in] fname preferably png - * \param[in] pixs [optional] can be null - * \return cid containing png data, or NULL on error - * - *- * Notes: - * (1) If you hand this a png file, you are going to get - * png predictors embedded in the flate data. So it has - * come to this. http://xkcd.com/1022/ - * (2) Exception: if the png is interlaced or if it is RGBA, - * it will be transcoded. - * (3) If transcoding is required, this will not have to read from - * file if you also input a pix. - *- */ -L_COMP_DATA * -l_generateFlateDataPdf(const char *fname, - PIX *pixs) -{ -l_uint8 *pngcomp = NULL; /* entire PNG compressed file */ -l_uint8 *datacomp = NULL; /* gzipped raster data */ -l_uint8 *cmapdata = NULL; /* uncompressed colormap */ -char *cmapdatahex = NULL; /* hex ascii uncompressed colormap */ -l_uint32 i, j, n; -l_int32 format, interlaced; -l_int32 ncolors; /* in colormap */ -l_int32 bps; /* bits/sample: usually 8 */ -l_int32 spp; /* samples/pixel: 1-grayscale/cmap); 3-rgb; 4-rgba */ -l_int32 w, h, cmapflag; -l_int32 xres, yres; -size_t nbytescomp = 0, nbytespng = 0; -FILE *fp; -L_COMP_DATA *cid; -PIX *pix; -PIXCMAP *cmap = NULL; - - PROCNAME("l_generateFlateDataPdf"); - - if (!fname) - return (L_COMP_DATA *)ERROR_PTR("fname not defined", procName, NULL); - - findFileFormat(fname, &format); - spp = 0; /* init to spp != 4 if not png */ - interlaced = 0; /* initialize to no interlacing */ - bps = 0; /* initialize to a nonsense value */ - if (format == IFF_PNG) { - isPngInterlaced(fname, &interlaced); - if (readHeaderPng(fname, NULL, NULL, &bps, &spp, NULL)) - return (L_COMP_DATA *)ERROR_PTR("bad png input", procName, NULL); - } - - /* PDF is capable of inlining some types of PNG files, but not all - of them. We need to transcode anything with interlacing, an - alpha channel, or 1 bpp (which would otherwise be photo-inverted). - - Be careful with spp. Any PNG image file with an alpha - channel is converted on reading to RGBA (spp == 4). This - includes the (gray + alpha) format with spp == 2. You - will get different results if you look at spp via - readHeaderPng() versus pixGetSpp() */ - if (format != IFF_PNG || interlaced || bps == 1 || spp == 4 || spp == 2) { - if (!pixs) - pix = pixRead(fname); - else - pix = pixClone(pixs); - if (!pix) - return (L_COMP_DATA *)ERROR_PTR("pix not made", procName, NULL); - cid = pixGenerateFlateData(pix, 0); - pixDestroy(&pix); - return cid; - } - - /* It's png. Generate the pdf data without transcoding. - * Implementation by Jeff Breidenbach. - * First, read the metadata */ - if ((fp = fopenReadStream(fname)) == NULL) - return (L_COMP_DATA *)ERROR_PTR("stream not opened", procName, NULL); - freadHeaderPng(fp, &w, &h, &bps, &spp, &cmapflag); - fgetPngResolution(fp, &xres, &yres); - fclose(fp); - - /* We get pdf corruption when inlining the data from 16 bpp png. */ - if (bps == 16) - return l_generateFlateData(fname, 0); - - /* Read the entire png file */ - if ((pngcomp = l_binaryRead(fname, &nbytespng)) == NULL) - return (L_COMP_DATA *)ERROR_PTR("unable to read file", - procName, NULL); - - /* Extract flate data, copying portions of it to memory, including - * the predictor information in a byte at the beginning of each - * raster line. The flate data makes up the vast majority of - * the png file, so after extraction we expect datacomp to - * be nearly full (i.e., nbytescomp will be only slightly less - * than nbytespng). Also extract the colormap if present. */ - if ((datacomp = (l_uint8 *)LEPT_CALLOC(1, nbytespng)) == NULL) { - LEPT_FREE(pngcomp); - return (L_COMP_DATA *)ERROR_PTR("unable to allocate memory", - procName, NULL); - } - - /* Parse the png file. Each chunk consists of: - * length: 4 bytes - * name: 4 bytes (e.g., "IDAT") - * data: n bytes - * CRC: 4 bytes - * Start at the beginning of the data section of the first chunk, - * byte 16, because the png file begins with 8 bytes of header, - * followed by the first 8 bytes of the first chunk - * (length and name). On each loop, increment by 12 bytes to - * skip over the CRC, length and name of the next chunk. */ - for (i = 16; i < nbytespng; i += 12) { /* do each successive chunk */ - /* Get the chunk length */ - n = pngcomp[i - 8] << 24; - n += pngcomp[i - 7] << 16; - n += pngcomp[i - 6] << 8; - n += pngcomp[i - 5] << 0; - if (n >= nbytespng - i) { /* "n + i" can overflow */ - LEPT_FREE(pngcomp); - LEPT_FREE(datacomp); - pixcmapDestroy(&cmap); - L_ERROR("invalid png: i = %d, n = %d, nbytes = %zu\n", procName, - i, n, nbytespng); - return NULL; - } - - /* Is it a data chunk? */ - if (memcmp(pngcomp + i - 4, "IDAT", 4) == 0) { - memcpy(datacomp + nbytescomp, pngcomp + i, n); - nbytescomp += n; - } - - /* Is it a palette chunk? */ - if (cmapflag && !cmap && - memcmp(pngcomp + i - 4, "PLTE", 4) == 0) { - if ((n / 3) > (1 << bps)) { - LEPT_FREE(pngcomp); - LEPT_FREE(datacomp); - pixcmapDestroy(&cmap); - L_ERROR("invalid png: i = %d, n = %d, cmapsize = %d\n", - procName, i, n, (1 << bps)); - return NULL; - } - cmap = pixcmapCreate(bps); - for (j = i; j < i + n; j += 3) { - pixcmapAddColor(cmap, pngcomp[j], pngcomp[j + 1], - pngcomp[j + 2]); - } - } - i += n; /* move to the end of the data chunk */ - } - LEPT_FREE(pngcomp); - - if (nbytescomp == 0) { - LEPT_FREE(datacomp); - pixcmapDestroy(&cmap); - return (L_COMP_DATA *)ERROR_PTR("invalid PNG file", procName, NULL); - } - - /* Extract and encode the colormap data as hexascii */ - ncolors = 0; - if (cmap) { - pixcmapSerializeToMemory(cmap, 3, &ncolors, &cmapdata); - pixcmapDestroy(&cmap); - if (!cmapdata) { - LEPT_FREE(datacomp); - return (L_COMP_DATA *)ERROR_PTR("cmapdata not made", - procName, NULL); - } - cmapdatahex = pixcmapConvertToHex(cmapdata, ncolors); - LEPT_FREE(cmapdata); - } - - /* Note that this is the only situation where the predictor - * field of the CID is set to 1. Adobe's predictor values on - * p. 76 of pdf_reference_1-7.pdf give 1 for no predictor and - * 10-14 for inline predictors, the specifics of which are - * ignored by the pdf interpreter, which just needs to know that - * the first byte on each compressed scanline is some predictor - * whose type can be inferred from the byte itself. */ - cid = (L_COMP_DATA *)LEPT_CALLOC(1, sizeof(L_COMP_DATA)); - cid->datacomp = datacomp; - cid->type = L_FLATE_ENCODE; - cid->cmapdatahex = cmapdatahex; - cid->nbytescomp = nbytescomp; - cid->ncolors = ncolors; - cid->predictor = TRUE; - cid->w = w; - cid->h = h; - cid->bps = bps; - cid->spp = spp; - cid->res = xres; - return cid; -} - - -/*! - * \brief l_generateJpegData() - * - * \param[in] fname of jpeg file - * \param[in] ascii85flag 0 for jpeg; 1 for ascii85-encoded jpeg - * \return cid containing jpeg data, or NULL on error - * - *- * Notes: - * (1) Set ascii85flag: - * ~ 0 for binary data (not permitted in PostScript) - * ~ 1 for ascii85 (5 for 4) encoded binary data - * (not permitted in pdf) - * (2) Do not free the data. l_generateJpegDataMem() will free - * the data if the data is invalid, or if it does not use - * ascii encoding. - *- */ -L_COMP_DATA * -l_generateJpegData(const char *fname, - l_int32 ascii85flag) -{ -l_uint8 *data = NULL; -size_t nbytes; - - PROCNAME("l_generateJpegData"); - - if (!fname) - return (L_COMP_DATA *)ERROR_PTR("fname not defined", procName, NULL); - - /* The returned jpeg data in memory is the entire jpeg file, - * which starts with ffd8 and ends with ffd9 */ - if ((data = l_binaryRead(fname, &nbytes)) == NULL) - return (L_COMP_DATA *)ERROR_PTR("data not extracted", procName, NULL); - - return l_generateJpegDataMem(data, nbytes, ascii85flag); -} - - -/*! - * \brief l_generateJpegDataMem() - * - * \param[in] data of jpeg file - * \param[in] nbytes of jpeg file - * \param[in] ascii85flag 0 for jpeg; 1 for ascii85-encoded jpeg - * \return cid containing jpeg data, or NULL on error - * - *- * Notes: - * (1) See l_generateJpegData(). - *- */ -L_COMP_DATA * -l_generateJpegDataMem(l_uint8 *data, - size_t nbytes, - l_int32 ascii85flag) -{ -char *data85 = NULL; /* ascii85 encoded jpeg compressed file */ -l_int32 w, h, xres, yres, bps, spp; -l_int32 nbytes85; -L_COMP_DATA *cid; - - PROCNAME("l_generateJpegDataMem"); - - if (!data) - return (L_COMP_DATA *)ERROR_PTR("data not defined", procName, NULL); - - /* Read the metadata */ - if (readHeaderMemJpeg(data, nbytes, &w, &h, &spp, NULL, NULL)) { - LEPT_FREE(data); - return (L_COMP_DATA *)ERROR_PTR("bad jpeg metadata", procName, NULL); - } - bps = 8; - readResolutionMemJpeg(data, nbytes, &xres, &yres); - - /* Optionally, encode the compressed data */ - if (ascii85flag == 1) { - data85 = encodeAscii85(data, nbytes, &nbytes85); - LEPT_FREE(data); - if (!data85) - return (L_COMP_DATA *)ERROR_PTR("data85 not made", procName, NULL); - else - data85[nbytes85 - 1] = '\0'; /* remove the newline */ - } - - cid = (L_COMP_DATA *)LEPT_CALLOC(1, sizeof(L_COMP_DATA)); - if (ascii85flag == 0) { - cid->datacomp = data; - } else { /* ascii85 */ - cid->data85 = data85; - cid->nbytes85 = nbytes85; - } - cid->type = L_JPEG_ENCODE; - cid->nbytescomp = nbytes; - cid->w = w; - cid->h = h; - cid->bps = bps; - cid->spp = spp; - cid->res = xres; - return cid; -} - - -/*! - * \brief l_generateJp2kData() - * - * \param[in] fname of jp2k file - * \return cid containing jp2k data, or NULL on error - * - *- * Notes: - * (1) This is only called after the file is verified to be jp2k. - *- */ -static L_COMP_DATA * -l_generateJp2kData(const char *fname) -{ -l_int32 w, h, bps, spp, xres, yres; -size_t nbytes; -L_COMP_DATA *cid; -FILE *fp; - - PROCNAME("l_generateJp2kData"); - - if (!fname) - return (L_COMP_DATA *)ERROR_PTR("fname not defined", procName, NULL); - - if (readHeaderJp2k(fname, &w, &h, &bps, &spp)) - return (L_COMP_DATA *)ERROR_PTR("bad jp2k metadata", procName, NULL); - - if ((cid = (L_COMP_DATA *)LEPT_CALLOC(1, sizeof(L_COMP_DATA))) == NULL) - return (L_COMP_DATA *)ERROR_PTR("cid not made", procName, NULL); - - /* The returned jp2k data in memory is the entire jp2k file */ - if ((cid->datacomp = l_binaryRead(fname, &nbytes)) == NULL) { - l_CIDataDestroy(&cid); - return (L_COMP_DATA *)ERROR_PTR("data not extracted", procName, NULL); - } - - xres = yres = 0; - if ((fp = fopenReadStream(fname)) != NULL) { - fgetJp2kResolution(fp, &xres, &yres); - fclose(fp); - } - cid->type = L_JP2K_ENCODE; - cid->nbytescomp = nbytes; - cid->w = w; - cid->h = h; - cid->bps = bps; - cid->spp = spp; - cid->res = xres; - return cid; -} - - -/*! - * \brief l_generateCIData() - * - * \param[in] fname - * \param[in] type L_G4_ENCODE, L_JPEG_ENCODE, L_FLATE_ENCODE, - * L_JP2K_ENCODE - * \param[in] quality for jpeg if transcoded: 1-100; 0 for default (75) - * for jp2k if transcoded: 27-45; 0 for default (34) - * \param[in] ascii85 0 for binary; 1 for ascii85-encoded - * \param[out] pcid compressed data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This can be used for both PostScript and pdf. - * (1) Set ascii85: - * ~ 0 for binary data (not permitted in PostScript) - * ~ 1 for ascii85 (5 for 4) encoded binary data - * (2) This attempts to compress according to the requested type. - * If this can't be done, it falls back to ordinary flate encoding. - * (3) This differs from l_generateCIDataPdf(), which determines - * the format and attempts to generate the CID without transcoding. - *- */ -l_ok -l_generateCIData(const char *fname, - l_int32 type, - l_int32 quality, - l_int32 ascii85, - L_COMP_DATA **pcid) -{ -l_int32 format, d, bps, spp, iscmap; -L_COMP_DATA *cid; -PIX *pix; - - PROCNAME("l_generateCIData"); - - if (!pcid) - return ERROR_INT("&cid not defined", procName, 1); - *pcid = NULL; - if (!fname) - return ERROR_INT("fname not defined", procName, 1); - if (type != L_G4_ENCODE && type != L_JPEG_ENCODE && - type != L_FLATE_ENCODE && type != L_JP2K_ENCODE) - return ERROR_INT("invalid conversion type", procName, 1); - if (ascii85 != 0 && ascii85 != 1) - return ERROR_INT("invalid ascii85", procName, 1); - - /* Sanity check on requested encoding */ - pixReadHeader(fname, &format, NULL, NULL, &bps, &spp, &iscmap); - d = bps * spp; - if (d == 24) d = 32; - if (iscmap && type != L_FLATE_ENCODE) { - L_WARNING("pixs has cmap; using flate encoding\n", procName); - type = L_FLATE_ENCODE; - } else if (d < 8 && type == L_JPEG_ENCODE) { - L_WARNING("pixs has < 8 bpp; using flate encoding\n", procName); - type = L_FLATE_ENCODE; - } else if (d < 8 && type == L_JP2K_ENCODE) { - L_WARNING("pixs has < 8 bpp; using flate encoding\n", procName); - type = L_FLATE_ENCODE; - } else if (d > 1 && type == L_G4_ENCODE) { - L_WARNING("pixs has > 1 bpp; using flate encoding\n", procName); - type = L_FLATE_ENCODE; - } - - if (type == L_JPEG_ENCODE) { - if (format == IFF_JFIF_JPEG) { /* do not transcode */ - cid = l_generateJpegData(fname, ascii85); - } else { - if ((pix = pixRead(fname)) == NULL) - return ERROR_INT("pix not returned", procName, 1); - cid = pixGenerateJpegData(pix, ascii85, quality); - pixDestroy(&pix); - } - if (!cid) - return ERROR_INT("jpeg data not made", procName, 1); - } else if (type == L_JP2K_ENCODE) { - if (format == IFF_JP2) { /* do not transcode */ - cid = l_generateJp2kData(fname); - } else { - if ((pix = pixRead(fname)) == NULL) - return ERROR_INT("pix not returned", procName, 1); - cid = pixGenerateJp2kData(pix, quality); - pixDestroy(&pix); - } - if (!cid) - return ERROR_INT("jp2k data not made", procName, 1); - } else if (type == L_G4_ENCODE) { - if ((cid = l_generateG4Data(fname, ascii85)) == NULL) - return ERROR_INT("g4 data not made", procName, 1); - } else if (type == L_FLATE_ENCODE) { - if ((cid = l_generateFlateData(fname, ascii85)) == NULL) - return ERROR_INT("flate data not made", procName, 1); - } else { - return ERROR_INT("invalid conversion type", procName, 1); - } - *pcid = cid; - - return 0; -} - - -/*! - * \brief pixGenerateCIData() - * - * \param[in] pixs 8 or 32 bpp, no colormap - * \param[in] type L_G4_ENCODE, L_JPEG_ENCODE, L_FLATE_ENCODE or - * L_JP2K_ENCODE - * \param[in] quality for jpeg if transcoded: 1-100; 0 for default (75) - * for jp2k if transcoded: 27-45; 0 for default (34) - * \param[in] ascii85 0 for binary; 1 for ascii85-encoded - * \param[out] pcid compressed data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Set ascii85: - * ~ 0 for binary data (not permitted in PostScript) - * ~ 1 for ascii85 (5 for 4) encoded binary data - *- */ -l_ok -pixGenerateCIData(PIX *pixs, - l_int32 type, - l_int32 quality, - l_int32 ascii85, - L_COMP_DATA **pcid) -{ -l_int32 d; -PIXCMAP *cmap; - - PROCNAME("pixGenerateCIData"); - - if (!pcid) - return ERROR_INT("&cid not defined", procName, 1); - *pcid = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (type != L_G4_ENCODE && type != L_JPEG_ENCODE && - type != L_FLATE_ENCODE && type != L_JP2K_ENCODE) { - selectDefaultPdfEncoding(pixs, &type); - } - if (ascii85 != 0 && ascii85 != 1) - return ERROR_INT("invalid ascii85", procName, 1); - - /* Conditionally modify the encoding type if libz is - * available and the requested library is missing. */ -#if defined(HAVE_LIBZ) -# if !defined(HAVE_LIBJPEG) - if (type == L_JPEG_ENCODE) { - L_WARNING("no libjpeg; using flate encoding\n", procName); - type = L_FLATE_ENCODE; - } -# endif /* !defined(HAVE_LIBJPEG) */ -# if !defined(HAVE_LIBJP2K) - if (type == L_JP2K_ENCODE) { - L_WARNING("no libjp2k; using flate encoding\n", procName); - type = L_FLATE_ENCODE; - } -# endif /* !defined(HAVE_LIBJP2K) */ -# if !defined(HAVE_LIBTIFF) - if (type == L_G4_ENCODE) { - L_WARNING("no libtiff; using flate encoding\n", procName); - type = L_FLATE_ENCODE; - } -# endif /* !defined(HAVE_LIBTIFF) */ -#endif /* defined(HAVE_LIBZ) */ - - /* Sanity check on requested encoding */ - d = pixGetDepth(pixs); - cmap = pixGetColormap(pixs); - if (cmap && type != L_FLATE_ENCODE) { - L_WARNING("pixs has cmap; using flate encoding\n", procName); - type = L_FLATE_ENCODE; - } else if (d < 8 && (type == L_JPEG_ENCODE || type == L_JP2K_ENCODE)) { - L_WARNING("pixs has < 8 bpp; using flate encoding\n", procName); - type = L_FLATE_ENCODE; - } else if (d > 1 && type == L_G4_ENCODE) { - L_WARNING("pixs has > 1 bpp; using flate encoding\n", procName); - type = L_FLATE_ENCODE; - } - - if (type == L_JPEG_ENCODE) { - if ((*pcid = pixGenerateJpegData(pixs, ascii85, quality)) == NULL) - return ERROR_INT("jpeg data not made", procName, 1); - } else if (type == L_JP2K_ENCODE) { - if ((*pcid = pixGenerateJp2kData(pixs, quality)) == NULL) - return ERROR_INT("jp2k data not made", procName, 1); - } else if (type == L_G4_ENCODE) { - if ((*pcid = pixGenerateG4Data(pixs, ascii85)) == NULL) - return ERROR_INT("g4 data not made", procName, 1); - } else { /* type == L_FLATE_ENCODE */ - if ((*pcid = pixGenerateFlateData(pixs, ascii85)) == NULL) - return ERROR_INT("flate data not made", procName, 1); - } - return 0; -} - - -/*! - * \brief l_generateFlateData() - * - * \param[in] fname - * \param[in] ascii85flag 0 for gzipped; 1 for ascii85-encoded gzipped - * \return cid flate compressed image data, or NULL on error - * - *- * Notes: - * (1) The input image is converted to one of these 4 types: - * ~ 1 bpp - * ~ 8 bpp, no colormap - * ~ 8 bpp, colormap - * ~ 32 bpp rgb - * (2) Set ascii85flag: - * ~ 0 for binary data (not permitted in PostScript) - * ~ 1 for ascii85 (5 for 4) encoded binary data - *- */ -L_COMP_DATA * -l_generateFlateData(const char *fname, - l_int32 ascii85flag) -{ -L_COMP_DATA *cid; -PIX *pixs; - - PROCNAME("l_generateFlateData"); - - if (!fname) - return (L_COMP_DATA *)ERROR_PTR("fname not defined", procName, NULL); - - if ((pixs = pixRead(fname)) == NULL) - return (L_COMP_DATA *)ERROR_PTR("pixs not made", procName, NULL); - cid = pixGenerateFlateData(pixs, ascii85flag); - pixDestroy(&pixs); - return cid; -} - - -/*! - * \brief pixGenerateFlateData() - * - * \param[in] pixs - * \param[in] ascii85flag 0 for gzipped; 1 for ascii85-encoded gzipped - * \return cid flate compressed image data, or NULL on error - * - *- * Notes: - * (1) If called with an RGBA pix (spp == 4), the alpha channel - * will be removed, projecting a white backgrouond through - * any transparency. - * (2) If called with a colormapped pix, any transparency in the - * alpha component in the colormap will be ignored, as it is - * for all leptonica operations on colormapped pix. - *- */ -static L_COMP_DATA * -pixGenerateFlateData(PIX *pixs, - l_int32 ascii85flag) -{ -l_uint8 *data = NULL; /* uncompressed raster data in required format */ -l_uint8 *datacomp = NULL; /* gzipped raster data */ -char *data85 = NULL; /* ascii85 encoded gzipped raster data */ -l_uint8 *cmapdata = NULL; /* uncompressed colormap */ -char *cmapdata85 = NULL; /* ascii85 encoded uncompressed colormap */ -char *cmapdatahex = NULL; /* hex ascii uncompressed colormap */ -l_int32 ncolors; /* in colormap; not used if cmapdata85 is null */ -l_int32 bps; /* bits/sample: usually 8 */ -l_int32 spp; /* samples/pixel: 1-grayscale/cmap); 3-rgb */ -l_int32 w, h, d, cmapflag; -l_int32 ncmapbytes85 = 0; -l_int32 nbytes85 = 0; -size_t nbytes, nbytescomp; -L_COMP_DATA *cid; -PIX *pixt; -PIXCMAP *cmap; - - PROCNAME("pixGenerateFlateData"); - - if (!pixs) - return (L_COMP_DATA *)ERROR_PTR("pixs not defined", procName, NULL); - - /* Convert the image to one of these 4 types: - * 1 bpp - * 8 bpp, no colormap - * 8 bpp, colormap - * 32 bpp rgb */ - pixGetDimensions(pixs, &w, &h, &d); - cmap = pixGetColormap(pixs); - cmapflag = (cmap) ? 1 : 0; - if (d == 2 || d == 4 || d == 16) { - pixt = pixConvertTo8(pixs, cmapflag); - cmap = pixGetColormap(pixt); - d = pixGetDepth(pixt); - } else if (d == 32 && pixGetSpp(pixs) == 4) { /* remove alpha */ - pixt = pixAlphaBlendUniform(pixs, 0xffffff00); - } else { - pixt = pixClone(pixs); - } - spp = (d == 32) ? 3 : 1; - bps = (d == 32) ? 8 : d; - - /* Extract and encode the colormap data as both ascii85 and hexascii */ - ncolors = 0; - if (cmap) { - pixcmapSerializeToMemory(cmap, 3, &ncolors, &cmapdata); - if (!cmapdata) { - pixDestroy(&pixt); - return (L_COMP_DATA *)ERROR_PTR("cmapdata not made", - procName, NULL); - } - - cmapdata85 = encodeAscii85(cmapdata, 3 * ncolors, &ncmapbytes85); - cmapdatahex = pixcmapConvertToHex(cmapdata, ncolors); - LEPT_FREE(cmapdata); - } - - /* Extract and compress the raster data */ - pixGetRasterData(pixt, &data, &nbytes); - pixDestroy(&pixt); - datacomp = zlibCompress(data, nbytes, &nbytescomp); - LEPT_FREE(data); - if (!datacomp) { - LEPT_FREE(cmapdata85); - LEPT_FREE(cmapdatahex); - return (L_COMP_DATA *)ERROR_PTR("datacomp not made", procName, NULL); - } - - /* Optionally, encode the compressed data */ - if (ascii85flag == 1) { - data85 = encodeAscii85(datacomp, nbytescomp, &nbytes85); - LEPT_FREE(datacomp); - if (!data85) { - LEPT_FREE(cmapdata85); - LEPT_FREE(cmapdatahex); - return (L_COMP_DATA *)ERROR_PTR("data85 not made", procName, NULL); - } else { - data85[nbytes85 - 1] = '\0'; /* remove the newline */ - } - } - - cid = (L_COMP_DATA *)LEPT_CALLOC(1, sizeof(L_COMP_DATA)); - if (ascii85flag == 0) { - cid->datacomp = datacomp; - } else { /* ascii85 */ - cid->data85 = data85; - cid->nbytes85 = nbytes85; - } - cid->type = L_FLATE_ENCODE; - cid->cmapdatahex = cmapdatahex; - cid->cmapdata85 = cmapdata85; - cid->nbytescomp = nbytescomp; - cid->ncolors = ncolors; - cid->w = w; - cid->h = h; - cid->bps = bps; - cid->spp = spp; - cid->res = pixGetXRes(pixs); - cid->nbytes = nbytes; /* only for debugging */ - return cid; -} - - -/*! - * \brief pixGenerateJpegData() - * - * \param[in] pixs 8 or 32 bpp, no colormap - * \param[in] ascii85flag 0 for jpeg; 1 for ascii85-encoded jpeg - * \param[in] quality 0 for default, which is 75 - * \return cid jpeg compressed data, or NULL on error - * - *- * Notes: - * (1) Set ascii85flag: - * ~ 0 for binary data (not permitted in PostScript) - * ~ 1 for ascii85 (5 for 4) encoded binary data - *- */ -static L_COMP_DATA * -pixGenerateJpegData(PIX *pixs, - l_int32 ascii85flag, - l_int32 quality) -{ -l_int32 d; -char *fname; -L_COMP_DATA *cid; - - PROCNAME("pixGenerateJpegData"); - - if (!pixs) - return (L_COMP_DATA *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetColormap(pixs)) - return (L_COMP_DATA *)ERROR_PTR("pixs has colormap", procName, NULL); - d = pixGetDepth(pixs); - if (d != 8 && d != 32) - return (L_COMP_DATA *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); - - /* Compress to a temp jpeg file */ - fname = l_makeTempFilename(); - if (pixWriteJpeg(fname, pixs, quality, 0)) { - LEPT_FREE(fname); - return NULL; - } - - /* Generate the data */ - cid = l_generateJpegData(fname, ascii85flag); - if (lept_rmfile(fname) != 0) - L_ERROR("temp file %s was not deleted\n", procName, fname); - LEPT_FREE(fname); - return cid; -} - - -/*! - * \brief pixGenerateJp2kData() - * - * \param[in] pixs 8 or 32 bpp, no colormap - * \param[in] quality 0 for default, which is 34 - * \return cid jp2k compressed data, or NULL on error - * - *- * Notes: - * (1) The quality can be set between 27 (very poor) and 45 - * (nearly perfect). Use 0 for default (34). Use 100 for lossless, - * but this is very expensive and not recommended. - *- */ -static L_COMP_DATA * -pixGenerateJp2kData(PIX *pixs, - l_int32 quality) -{ -l_int32 d; -char *fname; -L_COMP_DATA *cid; - - PROCNAME("pixGenerateJp2kData"); - - if (!pixs) - return (L_COMP_DATA *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetColormap(pixs)) - return (L_COMP_DATA *)ERROR_PTR("pixs has colormap", procName, NULL); - d = pixGetDepth(pixs); - if (d != 8 && d != 32) - return (L_COMP_DATA *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); - - /* Compress to a temp jp2k file */ - fname = l_makeTempFilename(); - if (pixWriteJp2k(fname, pixs, quality, 5, 0, 0)) { - LEPT_FREE(fname); - return NULL; - } - - /* Generate the data */ - cid = l_generateJp2kData(fname); - if (lept_rmfile(fname) != 0) - L_ERROR("temp file %s was not deleted\n", procName, fname); - LEPT_FREE(fname); - return cid; -} - - -/*! - * \brief pixGenerateG4Data() - * - * \param[in] pixs 1 bpp - * \param[in] ascii85flag 0 for gzipped; 1 for ascii85-encoded gzipped - * \return cid g4 compressed image data, or NULL on error - * - *- * Notes: - * (1) Set ascii85flag: - * ~ 0 for binary data (not permitted in PostScript) - * ~ 1 for ascii85 (5 for 4) encoded binary data - *- */ -static L_COMP_DATA * -pixGenerateG4Data(PIX *pixs, - l_int32 ascii85flag) -{ -char *fname; -L_COMP_DATA *cid; - - PROCNAME("pixGenerateG4Data"); - - if (!pixs) - return (L_COMP_DATA *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (L_COMP_DATA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); - - /* Compress to a temp tiff g4 file */ - fname = l_makeTempFilename(); - if (pixWrite(fname, pixs, IFF_TIFF_G4)) { - LEPT_FREE(fname); - return NULL; - } - - cid = l_generateG4Data(fname, ascii85flag); - if (lept_rmfile(fname) != 0) - L_ERROR("temp file %s was not deleted\n", procName, fname); - LEPT_FREE(fname); - return cid; -} - - -/*! - * \brief l_generateG4Data() - * - * \param[in] fname of g4 compressed file - * \param[in] ascii85flag 0 for g4 compressed; 1 for ascii85-encoded g4 - * \return cid g4 compressed image data, or NULL on error - * - *- * Notes: - * (1) Set ascii85flag: - * ~ 0 for binary data (not permitted in PostScript) - * ~ 1 for ascii85 (5 for 4) encoded binary data - * (not permitted in pdf) - *- */ -L_COMP_DATA * -l_generateG4Data(const char *fname, - l_int32 ascii85flag) -{ -l_uint8 *datacomp = NULL; /* g4 compressed raster data */ -char *data85 = NULL; /* ascii85 encoded g4 compressed data */ -l_int32 w, h, xres, yres; -l_int32 minisblack; /* TRUE or FALSE */ -l_int32 nbytes85; -size_t nbytescomp; -L_COMP_DATA *cid; -FILE *fp; - - PROCNAME("l_generateG4Data"); - - if (!fname) - return (L_COMP_DATA *)ERROR_PTR("fname not defined", procName, NULL); - - /* Read the resolution */ - if ((fp = fopenReadStream(fname)) == NULL) - return (L_COMP_DATA *)ERROR_PTR("stream not opened", procName, NULL); - getTiffResolution(fp, &xres, &yres); - fclose(fp); - - /* The returned ccitt g4 data in memory is the block of - * bytes in the tiff file, starting after 8 bytes and - * ending before the directory. */ - if (extractG4DataFromFile(fname, &datacomp, &nbytescomp, - &w, &h, &minisblack)) { - return (L_COMP_DATA *)ERROR_PTR("datacomp not extracted", - procName, NULL); - } - - /* Optionally, encode the compressed data */ - if (ascii85flag == 1) { - data85 = encodeAscii85(datacomp, nbytescomp, &nbytes85); - LEPT_FREE(datacomp); - if (!data85) - return (L_COMP_DATA *)ERROR_PTR("data85 not made", procName, NULL); - else - data85[nbytes85 - 1] = '\0'; /* remove the newline */ - } - - cid = (L_COMP_DATA *)LEPT_CALLOC(1, sizeof(L_COMP_DATA)); - if (ascii85flag == 0) { - cid->datacomp = datacomp; - } else { /* ascii85 */ - cid->data85 = data85; - cid->nbytes85 = nbytes85; - } - cid->type = L_G4_ENCODE; - cid->nbytescomp = nbytescomp; - cid->w = w; - cid->h = h; - cid->bps = 1; - cid->spp = 1; - cid->minisblack = minisblack; - cid->res = xres; - return cid; -} - - -/*! - * \brief cidConvertToPdfData() - * - * \param[in] cid compressed image data - * \param[in] title [optional] pdf title; can be NULL - * \param[out] pdata output pdf data for image - * \param[out] pnbytes size of output pdf data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Caller must not destroy the cid. It is absorbed in the - * lpd and destroyed by this function. - *- */ -l_ok -cidConvertToPdfData(L_COMP_DATA *cid, - const char *title, - l_uint8 **pdata, - size_t *pnbytes) -{ -l_int32 res, ret; -l_float32 wpt, hpt; -L_PDF_DATA *lpd = NULL; - - PROCNAME("cidConvertToPdfData"); - - if (!pdata || !pnbytes) - return ERROR_INT("&data and &nbytes not both defined", procName, 1); - *pdata = NULL; - *pnbytes = 0; - if (!cid) - return ERROR_INT("cid not defined", procName, 1); - - /* Get media box parameters, in pts */ - res = cid->res; - if (res <= 0) - res = DefaultInputRes; - wpt = cid->w * 72. / res; - hpt = cid->h * 72. / res; - - /* Set up the pdf data struct (lpd) */ - if ((lpd = pdfdataCreate(title)) == NULL) - return ERROR_INT("lpd not made", procName, 1); - ptraAdd(lpd->cida, cid); - lpd->n++; - ptaAddPt(lpd->xy, 0, 0); /* xpt = ypt = 0 */ - ptaAddPt(lpd->wh, wpt, hpt); - - /* Generate the pdf string and destroy the lpd */ - ret = l_generatePdf(pdata, pnbytes, lpd); - pdfdataDestroy(&lpd); - if (ret) - return ERROR_INT("pdf output not made", procName, 1); - return 0; -} - - -/*! - * \brief l_CIDataDestroy() - * - * \param[in,out] pcid will be set to null before returning - * \return void - */ -void -l_CIDataDestroy(L_COMP_DATA **pcid) -{ -L_COMP_DATA *cid; - - PROCNAME("l_CIDataDestroy"); - - if (pcid == NULL) { - L_WARNING("ptr address is null!\n", procName); - return; - } - if ((cid = *pcid) == NULL) - return; - - if (cid->datacomp) LEPT_FREE(cid->datacomp); - if (cid->data85) LEPT_FREE(cid->data85); - if (cid->cmapdata85) LEPT_FREE(cid->cmapdata85); - if (cid->cmapdatahex) LEPT_FREE(cid->cmapdatahex); - LEPT_FREE(cid); - *pcid = NULL; - return; -} - - -/*---------------------------------------------------------------------* - * Helper functions for generating the output pdf string * - *---------------------------------------------------------------------*/ -/*! - * \brief l_generatePdf() - * - * \param[out] pdata pdf array - * \param[out] pnbytes number of bytes in pdf array - * \param[in] lpd all the required input image data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) On error, no data is returned. - * (2) The objects are: - * 1: Catalog - * 2: Info - * 3: Pages - * 4: Page - * 5: Contents (rendering command) - * 6 to 6+n-1: n XObjects - * 6+n to 6+n+m-1: m colormaps - *- */ -static l_int32 -l_generatePdf(l_uint8 **pdata, - size_t *pnbytes, - L_PDF_DATA *lpd) -{ - PROCNAME("l_generatePdf"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *pnbytes = 0; - if (!lpd) - return ERROR_INT("lpd not defined", procName, 1); - - generateFixedStringsPdf(lpd); - generateMediaboxPdf(lpd); - generatePageStringPdf(lpd); - generateContentStringPdf(lpd); - generatePreXStringsPdf(lpd); - generateColormapStringsPdf(lpd); - generateTrailerPdf(lpd); - return generateOutputDataPdf(pdata, pnbytes, lpd); -} - - -static void -generateFixedStringsPdf(L_PDF_DATA *lpd) -{ -char buf[L_SMALLBUF]; -char *version, *datestr; -SARRAY *sa; - - PROCNAME("generateFixedStringsPdf"); - - /* Accumulate data for the header and objects 1-3 */ - lpd->id = stringNew("%PDF-1.5\n"); - l_dnaAddNumber(lpd->objsize, strlen(lpd->id)); - - lpd->obj1 = stringNew("1 0 obj\n" - "<<\n" - "/Type /Catalog\n" - "/Pages 3 0 R\n" - ">>\n" - "endobj\n"); - l_dnaAddNumber(lpd->objsize, strlen(lpd->obj1)); - - sa = sarrayCreate(0); - sarrayAddString(sa, "2 0 obj\n" - "<<\n", L_COPY); - if (var_WRITE_DATE_AND_VERSION) { - datestr = l_getFormattedDate(); - snprintf(buf, sizeof(buf), "/CreationDate (D:%s)\n", datestr); - sarrayAddString(sa, buf, L_COPY); - LEPT_FREE(datestr); - version = getLeptonicaVersion(); - snprintf(buf, sizeof(buf), - "/Producer (leptonica: %s)\n", version); - LEPT_FREE(version); - } else { - snprintf(buf, sizeof(buf), "/Producer (leptonica)\n"); - } - sarrayAddString(sa, buf, L_COPY); - if (lpd->title) { - char *hexstr; - if ((hexstr = generateEscapeString(lpd->title)) != NULL) { - snprintf(buf, sizeof(buf), "/Title %s\n", hexstr); - sarrayAddString(sa, buf, L_COPY); - } else { - L_ERROR("title string is not ascii\n", procName); - } - LEPT_FREE(hexstr); - } - sarrayAddString(sa, ">>\n" - "endobj\n", L_COPY); - lpd->obj2 = sarrayToString(sa, 0); - l_dnaAddNumber(lpd->objsize, strlen(lpd->obj2)); - sarrayDestroy(&sa); - - lpd->obj3 = stringNew("3 0 obj\n" - "<<\n" - "/Type /Pages\n" - "/Kids [ 4 0 R ]\n" - "/Count 1\n" - ">>\n"); - l_dnaAddNumber(lpd->objsize, strlen(lpd->obj3)); - - /* Do the post-datastream string */ - lpd->poststream = stringNew("\n" - "endstream\n" - "endobj\n"); - return; -} - - -/*! - * \brief generateEscapeString() - * - * \param[in] str input string - * \return hex escape string, or null on error - * - *- * Notes: - * (1) If the input string is not ascii, returns null. - * (2) This takes an input ascii string and generates a hex - * ascii output string with 4 bytes out for each byte in. - * The feff code at the beginning tells the pdf interpreter - * that the data is to be interpreted as big-endian, 4 bytes - * at a time. For ascii, the first two bytes are 0 and the - * last two bytes are less than 0x80. - *- */ -static char * -generateEscapeString(const char *str) -{ -char smallbuf[8]; -char *buffer; -l_int32 i, nchar, buflen; - - PROCNAME("generateEscapeString"); - - if (!str) - return (char *)ERROR_PTR("str not defined", procName, NULL); - nchar = strlen(str); - for (i = 0; i < nchar; i++) { - if (str[i] < 0) - return (char *)ERROR_PTR("str not all ascii", procName, NULL); - } - - buflen = 4 * nchar + 10; - buffer = (char *)LEPT_CALLOC(buflen, sizeof(char)); - stringCat(buffer, buflen, ""); - return buffer; -} - - -static void -generateMediaboxPdf(L_PDF_DATA *lpd) -{ -l_int32 i; -l_float32 xpt, ypt, wpt, hpt, maxx, maxy; - - /* First get the full extent of all the images. - * This is the mediabox, in pts. */ - maxx = maxy = 0; - for (i = 0; i < lpd->n; i++) { - ptaGetPt(lpd->xy, i, &xpt, &ypt); - ptaGetPt(lpd->wh, i, &wpt, &hpt); - maxx = L_MAX(maxx, xpt + wpt); - maxy = L_MAX(maxy, ypt + hpt); - } - - lpd->mediabox = boxCreate(0, 0, (l_int32)(maxx + 0.5), - (l_int32)(maxy + 0.5)); - - /* ypt is in standard image coordinates: the location of - * the UL image corner with respect to the UL media box corner. - * Rewrite each ypt for PostScript coordinates: the location of - * the LL image corner with respect to the LL media box corner. */ - for (i = 0; i < lpd->n; i++) { - ptaGetPt(lpd->xy, i, &xpt, &ypt); - ptaGetPt(lpd->wh, i, &wpt, &hpt); - ptaSetPt(lpd->xy, i, xpt, maxy - ypt - hpt); - } - - return; -} - - -static l_int32 -generatePageStringPdf(L_PDF_DATA *lpd) -{ -char *buf; -char *xstr; -l_int32 bufsize, i, wpt, hpt; -SARRAY *sa; - - PROCNAME("generatePageStringPdf"); - - /* Allocate 1000 bytes for the boilerplate text, and - * 50 bytes for each reference to an image in the - * ProcSet array. */ - bufsize = 1000 + 50 * lpd->n; - if ((buf = (char *)LEPT_CALLOC(bufsize, sizeof(char))) == NULL) - return ERROR_INT("calloc fail for buf", procName, 1); - - boxGetGeometry(lpd->mediabox, NULL, NULL, &wpt, &hpt); - sa = sarrayCreate(lpd->n); - for (i = 0; i < lpd->n; i++) { - snprintf(buf, bufsize, "/Im%d %d 0 R ", i + 1, 6 + i); - sarrayAddString(sa, buf, L_COPY); - } - xstr = sarrayToString(sa, 0); - sarrayDestroy(&sa); - if (!xstr) { - LEPT_FREE(buf); - return ERROR_INT("xstr not made", procName, 1); - } - - snprintf(buf, bufsize, "4 0 obj\n" - "<<\n" - "/Type /Page\n" - "/Parent 3 0 R\n" - "/MediaBox [%d %d %d %d]\n" - "/Contents 5 0 R\n" - "/Resources\n" - "<<\n" - "/XObject << %s >>\n" - "/ProcSet [ /ImageB /ImageI /ImageC ]\n" - ">>\n" - ">>\n" - "endobj\n", - 0, 0, wpt, hpt, xstr); - - lpd->obj4 = stringNew(buf); - l_dnaAddNumber(lpd->objsize, strlen(lpd->obj4)); - sarrayDestroy(&sa); - LEPT_FREE(buf); - LEPT_FREE(xstr); - return 0; -} - - -static l_int32 -generateContentStringPdf(L_PDF_DATA *lpd) -{ -char *buf; -char *cstr; -l_int32 i, bufsize; -l_float32 xpt, ypt, wpt, hpt; -SARRAY *sa; - - PROCNAME("generateContentStringPdf"); - - bufsize = 1000 + 200 * lpd->n; - if ((buf = (char *)LEPT_CALLOC(bufsize, sizeof(char))) == NULL) - return ERROR_INT("calloc fail for buf", procName, 1); - - sa = sarrayCreate(lpd->n); - for (i = 0; i < lpd->n; i++) { - ptaGetPt(lpd->xy, i, &xpt, &ypt); - ptaGetPt(lpd->wh, i, &wpt, &hpt); - snprintf(buf, bufsize, - "q %.4f %.4f %.4f %.4f %.4f %.4f cm /Im%d Do Q\n", - wpt, 0.0, 0.0, hpt, xpt, ypt, i + 1); - sarrayAddString(sa, buf, L_COPY); - } - cstr = sarrayToString(sa, 0); - sarrayDestroy(&sa); - if (!cstr) { - LEPT_FREE(buf); - return ERROR_INT("cstr not made", procName, 1); - } - - snprintf(buf, bufsize, "5 0 obj\n" - "<< /Length %d >>\n" - "stream\n" - "%s" - "endstream\n" - "endobj\n", - (l_int32)strlen(cstr), cstr); - - lpd->obj5 = stringNew(buf); - l_dnaAddNumber(lpd->objsize, strlen(lpd->obj5)); - sarrayDestroy(&sa); - LEPT_FREE(buf); - LEPT_FREE(cstr); - return 0; -} - - -static l_int32 -generatePreXStringsPdf(L_PDF_DATA *lpd) -{ -char buff[256]; -char buf[L_BIGBUF]; -char *cstr, *bstr, *fstr, *pstr, *xstr; -l_int32 i, cmindex; -L_COMP_DATA *cid; -SARRAY *sa; - - PROCNAME("generatePreXStringsPdf"); - - sa = lpd->saprex; - cmindex = 6 + lpd->n; /* starting value */ - for (i = 0; i < lpd->n; i++) { - pstr = cstr = NULL; - if ((cid = pdfdataGetCid(lpd, i)) == NULL) - return ERROR_INT("cid not found", procName, 1); - - if (cid->type == L_G4_ENCODE) { - if (var_WRITE_G4_IMAGE_MASK) { - cstr = stringNew("/ImageMask true\n" - "/ColorSpace /DeviceGray"); - } else { - cstr = stringNew("/ColorSpace /DeviceGray"); - } - bstr = stringNew("/BitsPerComponent 1\n" - "/Interpolate true"); - snprintf(buff, sizeof(buff), - "/Filter /CCITTFaxDecode\n" - "/DecodeParms\n" - "<<\n" - "/K -1\n" - "/Columns %d\n" - ">>", cid->w); - fstr = stringNew(buff); - } else if (cid->type == L_JPEG_ENCODE) { - if (cid->spp == 1) - cstr = stringNew("/ColorSpace /DeviceGray"); - else if (cid->spp == 3) - cstr = stringNew("/ColorSpace /DeviceRGB"); - else if (cid->spp == 4) /* pdf supports cmyk */ - cstr = stringNew("/ColorSpace /DeviceCMYK"); - else - L_ERROR("in jpeg: spp != 1, 3 or 4\n", procName); - bstr = stringNew("/BitsPerComponent 8"); - fstr = stringNew("/Filter /DCTDecode"); - } else if (cid->type == L_JP2K_ENCODE) { - if (cid->spp == 1) - cstr = stringNew("/ColorSpace /DeviceGray"); - else if (cid->spp == 3) - cstr = stringNew("/ColorSpace /DeviceRGB"); - else - L_ERROR("in jp2k: spp != 1 && spp != 3\n", procName); - bstr = stringNew("/BitsPerComponent 8"); - fstr = stringNew("/Filter /JPXDecode"); - } else { /* type == L_FLATE_ENCODE */ - if (cid->ncolors > 0) { /* cmapped */ - snprintf(buff, sizeof(buff), "/ColorSpace %d 0 R", cmindex++); - cstr = stringNew(buff); - } else { - if (cid->spp == 1 && cid->bps == 1) - cstr = stringNew("/ColorSpace /DeviceGray\n" - "/Decode [1 0]"); - else if (cid->spp == 1) /* 8 bpp */ - cstr = stringNew("/ColorSpace /DeviceGray"); - else if (cid->spp == 3) - cstr = stringNew("/ColorSpace /DeviceRGB"); - else - L_ERROR("unknown colorspace: spp = %d\n", - procName, cid->spp); - } - snprintf(buff, sizeof(buff), "/BitsPerComponent %d", cid->bps); - bstr = stringNew(buff); - fstr = stringNew("/Filter /FlateDecode"); - if (cid->predictor == TRUE) { - snprintf(buff, sizeof(buff), - "/DecodeParms\n" - "<<\n" - " /Columns %d\n" - " /Predictor 14\n" - " /Colors %d\n" - " /BitsPerComponent %d\n" - ">>\n", cid->w, cid->spp, cid->bps); - pstr = stringNew(buff); - } - } - if (!pstr) /* no decode parameters */ - pstr = stringNew(""); - - snprintf(buf, sizeof(buf), - "%d 0 obj\n" - "<<\n" - "/Length %zu\n" - "/Subtype /Image\n" - "%s\n" /* colorspace */ - "/Width %d\n" - "/Height %d\n" - "%s\n" /* bits/component */ - "%s\n" /* filter */ - "%s" /* decode parms; can be empty */ - ">>\n" - "stream\n", - 6 + i, cid->nbytescomp, cstr, - cid->w, cid->h, bstr, fstr, pstr); - xstr = stringNew(buf); - sarrayAddString(sa, xstr, L_INSERT); - l_dnaAddNumber(lpd->objsize, - strlen(xstr) + cid->nbytescomp + strlen(lpd->poststream)); - LEPT_FREE(cstr); - LEPT_FREE(bstr); - LEPT_FREE(fstr); - LEPT_FREE(pstr); - } - - return 0; -} - - -static l_int32 -generateColormapStringsPdf(L_PDF_DATA *lpd) -{ -char buf[L_BIGBUF]; -char *cmstr; -l_int32 i, cmindex, ncmap; -L_COMP_DATA *cid; -SARRAY *sa; - - PROCNAME("generateColormapStringsPdf"); - - /* In our canonical format, we have 5 objects, followed - * by n XObjects, followed by m colormaps, so the index of - * the first colormap object is 6 + n. */ - sa = lpd->sacmap; - cmindex = 6 + lpd->n; /* starting value */ - ncmap = 0; - for (i = 0; i < lpd->n; i++) { - if ((cid = pdfdataGetCid(lpd, i)) == NULL) - return ERROR_INT("cid not found", procName, 1); - if (cid->ncolors == 0) continue; - - ncmap++; - snprintf(buf, sizeof(buf), "%d 0 obj\n" - "[ /Indexed /DeviceRGB\n" - "%d\n" - "%s\n" - "]\n" - "endobj\n", - cmindex, cid->ncolors - 1, cid->cmapdatahex); - cmindex++; - cmstr = stringNew(buf); - l_dnaAddNumber(lpd->objsize, strlen(cmstr)); - sarrayAddString(sa, cmstr, L_INSERT); - } - - lpd->ncmap = ncmap; - return 0; -} - - -static void -generateTrailerPdf(L_PDF_DATA *lpd) -{ -l_int32 i, n, size, linestart; -L_DNA *daloc, *dasize; - - /* Let nobj be the number of numbered objects. These numbered - * objects are indexed by their pdf number in arrays naloc[] - * and nasize[]. The 0th object is the 9 byte header. Then - * the number of objects in nasize, which includes the header, - * is n = nobj + 1. The array naloc[] has n + 1 elements, - * because it includes as the last element the starting - * location of xref. The indexing of these objects, their - * starting locations and sizes are: - * - * Object number Starting location Size - * ------------- ----------------- -------------- - * 0 daloc[0] = 0 dasize[0] = 9 - * 1 daloc[1] = 9 dasize[1] = 49 - * n daloc[n] dasize[n] - * xref daloc[n+1] - * - * We first generate daloc. - */ - dasize = lpd->objsize; - daloc = lpd->objloc; - linestart = 0; - l_dnaAddNumber(daloc, linestart); /* header */ - n = l_dnaGetCount(dasize); - for (i = 0; i < n; i++) { - l_dnaGetIValue(dasize, i, &size); - linestart += size; - l_dnaAddNumber(daloc, linestart); - } - l_dnaGetIValue(daloc, n, &lpd->xrefloc); /* save it */ - - /* Now make the actual trailer string */ - lpd->trailer = makeTrailerStringPdf(daloc); -} - - -static char * -makeTrailerStringPdf(L_DNA *daloc) -{ -char *outstr; -char buf[L_BIGBUF]; -l_int32 i, n, linestart, xrefloc; -SARRAY *sa; - - PROCNAME("makeTrailerStringPdf"); - - if (!daloc) - return (char *)ERROR_PTR("daloc not defined", procName, NULL); - n = l_dnaGetCount(daloc) - 1; /* numbered objects + 1 (yes, +1) */ - - sa = sarrayCreate(0); - snprintf(buf, sizeof(buf), "xref\n" - "0 %d\n" - "0000000000 65535 f \n", n); - sarrayAddString(sa, buf, L_COPY); - for (i = 1; i < n; i++) { - l_dnaGetIValue(daloc, i, &linestart); - snprintf(buf, sizeof(buf), "%010d 00000 n \n", linestart); - sarrayAddString(sa, buf, L_COPY); - } - - l_dnaGetIValue(daloc, n, &xrefloc); - snprintf(buf, sizeof(buf), "trailer\n" - "<<\n" - "/Size %d\n" - "/Root 1 0 R\n" - "/Info 2 0 R\n" - ">>\n" - "startxref\n" - "%d\n" - "%%%%EOF\n", n, xrefloc); - sarrayAddString(sa, buf, L_COPY); - outstr = sarrayToString(sa, 0); - sarrayDestroy(&sa); - return outstr; -} - - -/*! - * \brief generateOutputDataPdf() - * - * \param[out] pdata pdf data array - * \param[out] pnbytes size of pdf data array - * \param[in] lpd input data used to make pdf - * \return 0 if OK, 1 on error - * - * - * Notes: - * (1) Only called from l_generatePdf(). On error, no data is returned. - *- */ -static l_int32 -generateOutputDataPdf(l_uint8 **pdata, - size_t *pnbytes, - L_PDF_DATA *lpd) -{ -char *str; -l_uint8 *data; -l_int32 nimages, i, len; -l_int32 *sizes, *locs; -size_t nbytes; -L_COMP_DATA *cid; - - PROCNAME("generateOutputDataPdf"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - nbytes = lpd->xrefloc + strlen(lpd->trailer); - *pnbytes = nbytes; - if ((data = (l_uint8 *)LEPT_CALLOC(nbytes, sizeof(l_uint8))) == NULL) - return ERROR_INT("calloc fail for data", procName, 1); - *pdata = data; - - sizes = l_dnaGetIArray(lpd->objsize); - locs = l_dnaGetIArray(lpd->objloc); - memcpy(data, lpd->id, sizes[0]); - memcpy(data + locs[1], lpd->obj1, sizes[1]); - memcpy(data + locs[2], lpd->obj2, sizes[2]); - memcpy(data + locs[3], lpd->obj3, sizes[3]); - memcpy(data + locs[4], lpd->obj4, sizes[4]); - memcpy(data + locs[5], lpd->obj5, sizes[5]); - - /* Each image has 3 parts: variable preamble, the compressed - * data stream, and the fixed poststream. */ - nimages = lpd->n; - for (i = 0; i < nimages; i++) { - if ((cid = pdfdataGetCid(lpd, i)) == NULL) { /* should not happen */ - LEPT_FREE(sizes); - LEPT_FREE(locs); - return ERROR_INT("cid not found", procName, 1); - } - str = sarrayGetString(lpd->saprex, i, L_NOCOPY); - len = strlen(str); - memcpy(data + locs[6 + i], str, len); - memcpy(data + locs[6 + i] + len, - cid->datacomp, cid->nbytescomp); - memcpy(data + locs[6 + i] + len + cid->nbytescomp, - lpd->poststream, strlen(lpd->poststream)); - } - - /* Each colormap is simply a stored string */ - for (i = 0; i < lpd->ncmap; i++) { - str = sarrayGetString(lpd->sacmap, i, L_NOCOPY); - memcpy(data + locs[6 + nimages + i], str, strlen(str)); - } - - /* And finally the trailer */ - memcpy(data + lpd->xrefloc, lpd->trailer, strlen(lpd->trailer)); - LEPT_FREE(sizes); - LEPT_FREE(locs); - return 0; -} - - -/*---------------------------------------------------------------------* - * Helper functions for generating multipage pdf output * - *---------------------------------------------------------------------*/ -/*! - * \brief parseTrailerPdf() - * - * \param[in] bas lba of a pdf file - * \param[out] pda byte locations of the beginning of each object - * \return 0 if OK, 1 on error - */ -static l_int32 -parseTrailerPdf(L_BYTEA *bas, - L_DNA **pda) -{ -char *str; -l_uint8 nl = '\n'; -l_uint8 *data; -l_int32 i, j, start, startloc, xrefloc, found, loc, nobj, objno, trailer_ok; -size_t size; -L_DNA *da, *daobj, *daxref; -SARRAY *sa; - - PROCNAME("parseTrailerPdf"); - - if (!pda) - return ERROR_INT("&da not defined", procName, 1); - *pda = NULL; - if (!bas) - return ERROR_INT("bas not defined", procName, 1); - data = l_byteaGetData(bas, &size); - if (memcmp(data, "%PDF-1.", 7) != 0) - return ERROR_INT("PDF header signature not found", procName, 1); - - /* Search for "startxref" starting 50 bytes from the EOF */ - start = 0; - if (size > 50) - start = size - 50; - arrayFindSequence(data + start, size - start, - (l_uint8 *)"startxref\n", 10, &loc, &found); - if (!found) - return ERROR_INT("startxref not found!", procName, 1); - if (sscanf((char *)(data + start + loc + 10), "%d\n", &xrefloc) != 1) - return ERROR_INT("xrefloc not found!", procName, 1); - if (xrefloc < 0 || xrefloc >= size) - return ERROR_INT("invalid xrefloc!", procName, 1); - sa = sarrayCreateLinesFromString((char *)(data + xrefloc), 0); - str = sarrayGetString(sa, 1, L_NOCOPY); - if ((sscanf(str, "0 %d", &nobj)) != 1) { - sarrayDestroy(&sa); - return ERROR_INT("nobj not found", procName, 1); - } - - /* Get starting locations. The numa index is the - * object number. loc[0] is the ID; loc[nobj + 1] is xrefloc. */ - da = l_dnaCreate(nobj + 1); - *pda = da; - for (i = 0; i < nobj; i++) { - str = sarrayGetString(sa, i + 2, L_NOCOPY); - sscanf(str, "%d", &startloc); - l_dnaAddNumber(da, startloc); - } - l_dnaAddNumber(da, xrefloc); - -#if DEBUG_MULTIPAGE - lept_stderr("************** Trailer string ************\n"); - lept_stderr("xrefloc = %d", xrefloc); - sarrayWriteStream(stderr, sa); - - lept_stderr("************** Object locations ************"); - l_dnaWriteStream(stderr, da); -#endif /* DEBUG_MULTIPAGE */ - sarrayDestroy(&sa); - - /* Verify correct parsing */ - trailer_ok = TRUE; - for (i = 1; i < nobj; i++) { - l_dnaGetIValue(da, i, &startloc); - if ((sscanf((char *)(data + startloc), "%d 0 obj", &objno)) != 1) { - L_ERROR("bad trailer for object %d\n", procName, i); - trailer_ok = FALSE; - break; - } - } - - /* If the trailer is broken, reconstruct the correct obj locations */ - if (!trailer_ok) { - L_INFO("rebuilding pdf trailer\n", procName); - l_dnaEmpty(da); - l_dnaAddNumber(da, 0); - l_byteaFindEachSequence(bas, (l_uint8 *)" 0 obj\n", 7, &daobj); - nobj = l_dnaGetCount(daobj); - for (i = 0; i < nobj; i++) { - l_dnaGetIValue(daobj, i, &loc); - for (j = loc - 1; j > 0; j--) { - if (data[j] == nl) - break; - } - l_dnaAddNumber(da, j + 1); - } - l_byteaFindEachSequence(bas, (l_uint8 *)"xref", 4, &daxref); - l_dnaGetIValue(daxref, 0, &loc); - l_dnaAddNumber(da, loc); - l_dnaDestroy(&daobj); - l_dnaDestroy(&daxref); - } - - return 0; -} - - -static char * -generatePagesObjStringPdf(NUMA *napage) -{ -char *str; -char *buf; -l_int32 i, n, index, bufsize; -SARRAY *sa; - - PROCNAME("generatePagesObjStringPdf"); - - if (!napage) - return (char *)ERROR_PTR("napage not defined", procName, NULL); - - n = numaGetCount(napage); - bufsize = 100 + 16 * n; /* large enough to hold the output string */ - buf = (char *)LEPT_CALLOC(bufsize, sizeof(char)); - sa = sarrayCreate(n); - for (i = 0; i < n; i++) { - numaGetIValue(napage, i, &index); - snprintf(buf, bufsize, " %d 0 R ", index); - sarrayAddString(sa, buf, L_COPY); - } - - str = sarrayToString(sa, 0); - snprintf(buf, bufsize - 1, "3 0 obj\n" - "<<\n" - "/Type /Pages\n" - "/Kids [%s]\n" - "/Count %d\n" - ">>\n", str, n); - sarrayDestroy(&sa); - LEPT_FREE(str); - return buf; -} - - -/*! - * \brief substituteObjectNumbers() - * - * \param[in] bas lba of a pdf object - * \param[in] na_objs object number mapping array - * \return bad lba of rewritten pdf for the object - * - *- * Notes: - * (1) Interpret the first set of bytes as the object number, - * map to the new number, and write it out. - * (2) Find all occurrences of this 4-byte sequence: " 0 R" - * (3) Find the location and value of the integer preceding this, - * and map it to the new value. - * (4) Rewrite the object with new object numbers. - *- */ -static L_BYTEA * -substituteObjectNumbers(L_BYTEA *bas, - NUMA *na_objs) -{ -l_uint8 space = ' '; -l_uint8 *datas; -l_uint8 buf[32]; /* only needs to hold one integer in ascii format */ -l_int32 start, nrepl, i, j, objin, objout, found; -l_int32 *objs, *matches; -size_t size; -L_BYTEA *bad; -L_DNA *da_match; - - datas = l_byteaGetData(bas, &size); - bad = l_byteaCreate(100); - objs = numaGetIArray(na_objs); /* object number mapper */ - - /* Substitute the object number on the first line */ - sscanf((char *)datas, "%d", &objin); - objout = objs[objin]; - snprintf((char *)buf, 32, "%d", objout); - l_byteaAppendString(bad, (char *)buf); - - /* Find the set of matching locations for object references */ - arrayFindSequence(datas, size, &space, 1, &start, &found); - da_match = arrayFindEachSequence(datas, size, (l_uint8 *)" 0 R", 4); - if (!da_match) { - l_byteaAppendData(bad, datas + start, size - start); - LEPT_FREE(objs); - return bad; - } - - /* Substitute all the object reference numbers */ - nrepl = l_dnaGetCount(da_match); - matches = l_dnaGetIArray(da_match); - for (i = 0; i < nrepl; i++) { - /* Find the first space before the object number */ - for (j = matches[i] - 1; j > 0; j--) { - if (datas[j] == space) - break; - } - /* Copy bytes from 'start' up to the object number */ - l_byteaAppendData(bad, datas + start, j - start + 1); - sscanf((char *)(datas + j + 1), "%d", &objin); - objout = objs[objin]; - snprintf((char *)buf, 32, "%d", objout); - l_byteaAppendString(bad, (char *)buf); - start = matches[i]; - } - l_byteaAppendData(bad, datas + start, size - start); - - LEPT_FREE(objs); - LEPT_FREE(matches); - l_dnaDestroy(&da_match); - return bad; -} - - -/*---------------------------------------------------------------------* - * Create/destroy/access pdf data * - *---------------------------------------------------------------------*/ -static L_PDF_DATA * -pdfdataCreate(const char *title) -{ -L_PDF_DATA *lpd; - - lpd = (L_PDF_DATA *)LEPT_CALLOC(1, sizeof(L_PDF_DATA)); - if (title) lpd->title = stringNew(title); - lpd->cida = ptraCreate(10); - lpd->xy = ptaCreate(10); - lpd->wh = ptaCreate(10); - lpd->saprex = sarrayCreate(10); - lpd->sacmap = sarrayCreate(10); - lpd->objsize = l_dnaCreate(20); - lpd->objloc = l_dnaCreate(20); - return lpd; -} - -static void -pdfdataDestroy(L_PDF_DATA **plpd) -{ -l_int32 i; -L_COMP_DATA *cid; -L_PDF_DATA *lpd; - - PROCNAME("pdfdataDestroy"); - - if (plpd== NULL) { - L_WARNING("ptr address is null!\n", procName); - return; - } - if ((lpd = *plpd) == NULL) - return; - - if (lpd->title) LEPT_FREE(lpd->title); - for (i = 0; i < lpd->n; i++) { - cid = (L_COMP_DATA *)ptraRemove(lpd->cida, i, L_NO_COMPACTION); - l_CIDataDestroy(&cid); - } - - ptraDestroy(&lpd->cida, 0, 0); - if (lpd->id) LEPT_FREE(lpd->id); - if (lpd->obj1) LEPT_FREE(lpd->obj1); - if (lpd->obj2) LEPT_FREE(lpd->obj2); - if (lpd->obj3) LEPT_FREE(lpd->obj3); - if (lpd->obj4) LEPT_FREE(lpd->obj4); - if (lpd->obj5) LEPT_FREE(lpd->obj5); - if (lpd->poststream) LEPT_FREE(lpd->poststream); - if (lpd->trailer) LEPT_FREE(lpd->trailer); - if (lpd->xy) ptaDestroy(&lpd->xy); - if (lpd->wh) ptaDestroy(&lpd->wh); - if (lpd->mediabox) boxDestroy(&lpd->mediabox); - if (lpd->saprex) sarrayDestroy(&lpd->saprex); - if (lpd->sacmap) sarrayDestroy(&lpd->sacmap); - if (lpd->objsize) l_dnaDestroy(&lpd->objsize); - if (lpd->objloc) l_dnaDestroy(&lpd->objloc); - LEPT_FREE(lpd); - *plpd = NULL; - return; -} - - -static L_COMP_DATA * -pdfdataGetCid(L_PDF_DATA *lpd, - l_int32 index) -{ - PROCNAME("pdfdataGetCid"); - - if (!lpd) - return (L_COMP_DATA *)ERROR_PTR("lpd not defined", procName, NULL); - if (index < 0 || index >= lpd->n) - return (L_COMP_DATA *)ERROR_PTR("invalid image index", procName, NULL); - - return (L_COMP_DATA *)ptraGetPtrToItem(lpd->cida, index); -} - - -/*---------------------------------------------------------------------* - * Set flags for special modes * - *---------------------------------------------------------------------*/ -/*! - * \brief l_pdfSetG4ImageMask() - * - * \param[in] flag 1 for writing g4 data as fg only through a mask; - * 0 for writing fg and bg - * \return void - * - *- * Notes: - * (1) The default is for writing only the fg (through the mask). - * That way when you write a 1 bpp image, the bg is transparent, - * so any previously written image remains visible behind it. - *- */ -void -l_pdfSetG4ImageMask(l_int32 flag) -{ - var_WRITE_G4_IMAGE_MASK = flag; -} - - -/*! - * \brief l_pdfSetDateAndVersion() - * - * \param[in] flag 1 for writing date/time and leptonica version; - * 0 for omitting this from the metadata - * \return void - * - *- * Notes: - * (1) The default is for writing this data. For regression tests - * that compare output against golden files, it is useful to omit. - *- */ -void -l_pdfSetDateAndVersion(l_int32 flag) -{ - var_WRITE_DATE_AND_VERSION = flag; -} - -/* --------------------------------------------*/ -#endif /* USE_PDFIO */ -/* --------------------------------------------*/ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pdfio2stub.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pdfio2stub.c deleted file mode 100644 index cb297b12..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pdfio2stub.c +++ /dev/null @@ -1,172 +0,0 @@ -/*====================================================================* - - 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 pdfio2stub.c - *- * - * Stubs for pdfio2.c functions - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include "allheaders.h" - -/* --------------------------------------------*/ -#if !USE_PDFIO /* defined in environ.h */ -/* --------------------------------------------*/ - -/* ----------------------------------------------------------------------*/ - -l_ok pixConvertToPdfData(PIX *pix, l_int32 type, l_int32 quality, - l_uint8 **pdata, size_t *pnbytes, - l_int32 x, l_int32 y, l_int32 res, - const char *title, - L_PDF_DATA **plpd, l_int32 position) -{ - return ERROR_INT("function not present", "pixConvertToPdfData", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok ptraConcatenatePdfToData(L_PTRA *pa_data, SARRAY *sa, - l_uint8 **pdata, size_t *pnbytes) -{ - return ERROR_INT("function not present", "ptraConcatenatePdfToData", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertTiffMultipageToPdf(const char *filein, const char *fileout) -{ - return ERROR_INT("function not present", "convertTiffMultipageToPdf", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok l_generateCIDataForPdf(const char *fname, PIX *pix, l_int32 quality, - L_COMP_DATA **pcid) -{ - return ERROR_INT("function not present", "l_generateCIDataForPdf", 1); -} - -/* ----------------------------------------------------------------------*/ - -L_COMP_DATA * l_generateFlateDataPdf(const char *fname, PIX *pix) -{ - return (L_COMP_DATA *)ERROR_PTR("function not present", - "l_generateFlateDataPdf", NULL); -} - -/* ----------------------------------------------------------------------*/ - -L_COMP_DATA * l_generateJpegData(const char *fname, l_int32 ascii85flag) -{ - return (L_COMP_DATA *)ERROR_PTR("function not present", - "l_generateJpegData", NULL); -} - -/* ----------------------------------------------------------------------*/ - -L_COMP_DATA * l_generateJpegDataMem(l_uint8 *data, size_t nbytes, - l_int32 ascii85flag) -{ - return (L_COMP_DATA *)ERROR_PTR("function not present", - "l_generateJpegDataMem", NULL); -} - -/* ----------------------------------------------------------------------*/ - -l_ok l_generateCIData(const char *fname, l_int32 type, l_int32 quality, - l_int32 ascii85, L_COMP_DATA **pcid) -{ - return ERROR_INT("function not present", "l_generateCIData", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixGenerateCIData(PIX *pixs, l_int32 type, l_int32 quality, - l_int32 ascii85, L_COMP_DATA **pcid) -{ - return ERROR_INT("function not present", "pixGenerateCIData", 1); -} - -/* ----------------------------------------------------------------------*/ - -L_COMP_DATA * l_generateFlateData(const char *fname, l_int32 ascii85flag) -{ - return (L_COMP_DATA *)ERROR_PTR("function not present", - "l_generateFlateData", NULL); -} - -/* ----------------------------------------------------------------------*/ - -L_COMP_DATA * l_generateG4Data(const char *fname, l_int32 ascii85flag) -{ - return (L_COMP_DATA *)ERROR_PTR("function not present", - "l_generateG4Data", NULL); -} - -/* ----------------------------------------------------------------------*/ - -l_ok cidConvertToPdfData(L_COMP_DATA *cid, const char *title, - l_uint8 **pdata, size_t *pnbytes) -{ - return ERROR_INT("function not present", "cidConvertToPdfData", 1); -} - -/* ----------------------------------------------------------------------*/ - -void l_CIDataDestroy(L_COMP_DATA **pcid) -{ - L_ERROR("function not present\n", "l_CIDataDestroy"); - return; -} - -/* ----------------------------------------------------------------------*/ - -void l_pdfSetG4ImageMask(l_int32 flag) -{ - L_ERROR("function not present\n", "l_pdfSetG4ImageMask"); - return; -} - -/* ----------------------------------------------------------------------*/ - -void l_pdfSetDateAndVersion(l_int32 flag) -{ - L_ERROR("function not present\n", "l_pdfSetDateAndVersion"); - return; -} - -/* ----------------------------------------------------------------------*/ - -/* --------------------------------------------*/ -#endif /* !USE_PDFIO */ -/* --------------------------------------------*/ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix.h deleted file mode 100644 index 597b8dbd..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix.h +++ /dev/null @@ -1,1342 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_PIX_H -#define LEPTONICA_PIX_H - -/*! - * \file pix.h - * - * - * Valid image types in leptonica: - * Pix: 1 bpp, with and without colormap - * Pix: 2 bpp, with and without colormap - * Pix: 4 bpp, with and without colormap - * Pix: 8 bpp, with and without colormap - * Pix: 16 bpp (1 spp) - * Pix: 32 bpp (rgb, 3 spp) - * Pix: 32 bpp (rgba, 4 spp) - * FPix: 32 bpp float - * DPix: 64 bpp double - * Notes: - * (1) The only valid Pix image type with alpha is rgba. - * In particular, the alpha component is not used in - * cmapped images. - * (2) PixComp can hold any Pix with IFF_PNG encoding. - * - * Contents: - * - * (1) This file defines most of the image-related structs used in leptonica: - * struct Pix - * struct PixColormap - * struct RGBA_Quad - * struct Pixa - * struct Pixaa - * struct Box - * struct Boxa - * struct Boxaa - * struct Pta - * struct Ptaa - * struct Pixacc - * struct PixTiling - * struct FPix - * struct FPixa - * struct DPix - * struct PixComp - * struct PixaComp - * - * (2) This file has definitions for: - * Colors for RGBA - * Colors for drawing boxes - * Perceptual color weights - * Colormap conversion flags - * Rasterop bit flags - * Structure access flags (for insert, copy, clone, copy-clone) - * Sorting flags (by type and direction) - * Blending flags - * Graphics pixel setting flags - * Size and location filter flags - * Color component selection flags - * 16-bit conversion flags - * Rotation and shear flags - * Affine transform order flags - * Grayscale filling flags - * Flags for setting to white or black - * Flags for getting white or black pixel value - * Flags for 8 and 16 bit pixel sums - * Dithering flags - * Distance flags - * Value flags - * Statistical measures - * Set selection flags - * Text orientation flags - * Edge orientation flags - * Line orientation flags - * Image orientation flags - * Scan direction flags - * Box size adjustment flags - * Flags for modifying box boundaries using a second box - * Handling overlapping bounding boxes in boxa - * Selecting or making a box from two (intersecting) boxes - * Flags for replacing invalid boxes - * Flags for box corners and center - * Horizontal warp - * Pixel selection for resampling - * Thinning flags - * Runlength flags - * Edge filter flags - * Subpixel color component ordering in LCD display - * HSV histogram flags - * Region flags (inclusion, exclusion) - * Flags for adding text to a pix - * Flags for plotting on a pix - * Flags for making simple masks - * Flags for selecting display program - * Flags in the 'special' pix field for non-default operations - * Handling negative values in conversion to unsigned int - * Relative to zero flags - * Flags for adding or removing trailing slash from string - * - * (3) This file has typedefs for the pix allocator and deallocator functions - * alloc_fn() - * dealloc_fn(). - *- */ - - -/*-------------------------------------------------------------------------* - * Basic Pix * - *-------------------------------------------------------------------------*/ - /* The 'special' field is by default 0, but it can hold integers - * that direct non-default actions, e.g., in png and jpeg I/O. */ - -/*! Basic Pix */ -struct Pix -{ - l_uint32 w; /*!< width in pixels */ - l_uint32 h; /*!< height in pixels */ - l_uint32 d; /*!< depth in bits (bpp) */ - l_uint32 spp; /*!< number of samples per pixel */ - l_uint32 wpl; /*!< 32-bit words/line */ - l_uint32 refcount; /*!< reference count (1 if no clones) */ - l_int32 xres; /*!< image res (ppi) in x direction */ - /*!< (use 0 if unknown) */ - l_int32 yres; /*!< image res (ppi) in y direction */ - /*!< (use 0 if unknown) */ - l_int32 informat; /*!< input file format, IFF_* */ - l_int32 special; /*!< special instructions for I/O, etc */ - char *text; /*!< text string associated with pix */ - struct PixColormap *colormap; /*!< colormap (may be null) */ - l_uint32 *data; /*!< the image data */ -}; -typedef struct Pix PIX; - -/*! Colormap of a Pix */ -struct PixColormap -{ - void *array; /*!< colormap table (array of RGBA_QUAD) */ - l_int32 depth; /*!< of pix (1, 2, 4 or 8 bpp) */ - l_int32 nalloc; /*!< number of color entries allocated */ - l_int32 n; /*!< number of color entries used */ -}; -typedef struct PixColormap PIXCMAP; - - - /*! Colormap table entry (after the BMP version). - * Note that the BMP format stores the colormap table exactly - * as it appears here, with color samples being stored sequentially, - * in the order (b,g,r,a). */ -struct RGBA_Quad -{ - l_uint8 blue; /*!< blue value */ - l_uint8 green; /*!< green value */ - l_uint8 red; /*!< red value */ - l_uint8 alpha; /*!< alpha value */ -}; -typedef struct RGBA_Quad RGBA_QUAD; - - -/*-------------------------------------------------------------------------* - * Colors for 32 RGBA * - *-------------------------------------------------------------------------*/ -/*- * Notes: - * (1) These are the byte indices for colors in 32 bpp images. - * They are used through the GET/SET_DATA_BYTE accessors. - * The 4th byte, typically known as the "alpha channel" and used - * for blending, is used to a small extent in leptonica. - * (2) Do not change these values! If you redefine them, functions - * that have the shifts hardcoded for efficiency and conciseness - * (instead of using the constants below) will break. These - * functions are labelled with "***" next to their names at - * the top of the files in which they are defined. - * (3) The shifts to extract the red, green, blue and alpha components - * from a 32 bit pixel are defined here. - *- */ - -/*! RGBA Color */ -enum { - COLOR_RED = 0, /*!< red color index in RGBA_QUAD */ - COLOR_GREEN = 1, /*!< green color index in RGBA_QUAD */ - COLOR_BLUE = 2, /*!< blue color index in RGBA_QUAD */ - L_ALPHA_CHANNEL = 3 /*!< alpha value index in RGBA_QUAD */ -}; - -static const l_int32 L_RED_SHIFT = - 8 * (sizeof(l_uint32) - 1 - COLOR_RED); /* 24 */ -static const l_int32 L_GREEN_SHIFT = - 8 * (sizeof(l_uint32) - 1 - COLOR_GREEN); /* 16 */ -static const l_int32 L_BLUE_SHIFT = - 8 * (sizeof(l_uint32) - 1 - COLOR_BLUE); /* 8 */ -static const l_int32 L_ALPHA_SHIFT = - 8 * (sizeof(l_uint32) - 1 - L_ALPHA_CHANNEL); /* 0 */ - - -/*-------------------------------------------------------------------------* - * Colors for drawing boxes * - *-------------------------------------------------------------------------*/ -/*! Box Color */ -enum { - L_DRAW_RED = 0, /*!< draw in red */ - L_DRAW_GREEN = 1, /*!< draw in green */ - L_DRAW_BLUE = 2, /*!< draw in blue */ - L_DRAW_SPECIFIED = 3, /*!< draw specified color */ - L_DRAW_RGB = 4, /*!< draw as sequence of r,g,b */ - L_DRAW_RANDOM = 5 /*!< draw randomly chosen colors */ -}; - - -/*-------------------------------------------------------------------------* - * Perceptual color weights * - *-------------------------------------------------------------------------*/ -/*- * Notes: - * (1) These perceptual weighting factors are ad-hoc, but they do - * add up to 1. Unlike, for example, the weighting factors for - * converting RGB to luminance, or more specifically to Y in the - * YUV colorspace. Those numbers come from the - * International Telecommunications Union, via ITU-R. - *- */ -static const l_float32 L_RED_WEIGHT = 0.3f; /*!< Percept. weight for red */ -static const l_float32 L_GREEN_WEIGHT = 0.5f; /*!< Percept. weight for green */ -static const l_float32 L_BLUE_WEIGHT = 0.2f; /*!< Percept. weight for blue */ - - -/*-------------------------------------------------------------------------* - * Flags for colormap conversion * - *-------------------------------------------------------------------------*/ -/*! Cmap Conversion */ -enum { - REMOVE_CMAP_TO_BINARY = 0, /*!< remove colormap for conv to 1 bpp */ - REMOVE_CMAP_TO_GRAYSCALE = 1, /*!< remove colormap for conv to 8 bpp */ - REMOVE_CMAP_TO_FULL_COLOR = 2, /*!< remove colormap for conv to 32 bpp */ - REMOVE_CMAP_WITH_ALPHA = 3, /*!< remove colormap and alpha */ - REMOVE_CMAP_BASED_ON_SRC = 4 /*!< remove depending on src format */ -}; - - -/*------------------------------------------------------------------------* - *! - *- * The following operation bit flags have been modified from - * Sun's pixrect.h. - * - * The 'op' in 'rasterop' is represented by an integer - * composed with Boolean functions using the set of five integers - * given below. The integers, and the op codes resulting from - * boolean expressions on them, need only be in the range from 0 to 15. - * The function is applied on a per-pixel basis. - * - * Examples: the op code representing ORing the src and dest - * is computed using the bit OR, as PIX_SRC | PIX_DST; the op - * code representing XORing src and dest is found from - * PIX_SRC ^ PIX_DST; the op code representing ANDing src and dest - * is found from PIX_SRC & PIX_DST. Note that - * PIX_NOT(PIX_CLR) = PIX_SET, and v.v., as they must be. - * - * We use the following set of definitions: - * - * #define PIX_SRC 0xc - * #define PIX_DST 0xa - * #define PIX_NOT(op) (op) ^ 0xf - * #define PIX_CLR 0x0 - * #define PIX_SET 0xf - * - * These definitions differ from Sun's, in that Sun left-shifted - * each value by 1 pixel, and used the least significant bit as a - * flag for the "pseudo-operation" of clipping. We don't need - * this bit, because it is both efficient and safe ALWAYS to clip - * the rectangles to the src and dest images, which is what we do. - * See the notes in rop.h on the general choice of these bit flags. - * - * [If for some reason you need compatibility with Sun's xview package, - * you can adopt the original Sun definitions to avoid redefinition conflicts: - * - * #define PIX_SRC (0xc << 1) - * #define PIX_DST (0xa << 1) - * #define PIX_NOT(op) ((op) ^ 0x1e) - * #define PIX_CLR (0x0 << 1) - * #define PIX_SET (0xf << 1) - * ] - * - * We have, for reference, the following 16 unique op flags: - * - * PIX_CLR 0000 0x0 - * PIX_SET 1111 0xf - * PIX_SRC 1100 0xc - * PIX_DST 1010 0xa - * PIX_NOT(PIX_SRC) 0011 0x3 - * PIX_NOT(PIX_DST) 0101 0x5 - * PIX_SRC | PIX_DST 1110 0xe - * PIX_SRC & PIX_DST 1000 0x8 - * PIX_SRC ^ PIX_DST 0110 0x6 - * PIX_NOT(PIX_SRC) | PIX_DST 1011 0xb - * PIX_NOT(PIX_SRC) & PIX_DST 0010 0x2 - * PIX_SRC | PIX_NOT(PIX_DST) 1101 0xd - * PIX_SRC & PIX_NOT(PIX_DST) 0100 0x4 - * PIX_NOT(PIX_SRC | PIX_DST) 0001 0x1 - * PIX_NOT(PIX_SRC & PIX_DST) 0111 0x7 - * PIX_NOT(PIX_SRC ^ PIX_DST) 1001 0x9 - * - *- *-------------------------------------------------------------------------*/ - -#define PIX_SRC (0xc) /*!< use source pixels */ -#define PIX_DST (0xa) /*!< use destination pixels */ -#define PIX_NOT(op) ((op) ^ 0x0f) /*!< invert operation %op */ -#define PIX_CLR (0x0) /*!< clear pixels */ -#define PIX_SET (0xf) /*!< set pixels */ - -#define PIX_PAINT (PIX_SRC | PIX_DST) /*!< paint = src | dst */ -#define PIX_MASK (PIX_SRC & PIX_DST) /*!< mask = src & dst */ -#define PIX_SUBTRACT (PIX_DST & PIX_NOT(PIX_SRC)) /*!< subtract = */ - /*!< src & !dst */ -#define PIX_XOR (PIX_SRC ^ PIX_DST) /*!< xor = src ^ dst */ - - -/*-------------------------------------------------------------------------* - *- * Important Notes: - * - * (1) The image data is stored in a single contiguous - * array of l_uint32, into which the pixels are packed. - * By "packed" we mean that there are no unused bits - * between pixels, except for end-of-line padding to - * satisfy item (2) below. - * - * (2) Every image raster line begins on a 32-bit word - * boundary within this array. - * - * (3) Pix image data is stored in 32-bit units, with the - * pixels ordered from left to right in the image being - * stored in order from the MSB to LSB within the word, - * for both big-endian and little-endian machines. - * This is the natural ordering for big-endian machines, - * as successive bytes are stored and fetched progressively - * to the right. However, for little-endians, when storing - * we re-order the bytes from this byte stream order, and - * reshuffle again for byte access on 32-bit entities. - * So if the bytes come in sequence from left to right, we - * store them on little-endians in byte order: - * 3 2 1 0 7 6 5 4 ... - * This MSB to LSB ordering allows left and right shift - * operations on 32 bit words to move the pixels properly. - * - * (4) We use 32 bit pixels for both RGB and RGBA color images. - * The A (alpha) byte is ignored in most leptonica functions - * operating on color images. Within each 4 byte pixel, the - * color samples are ordered from MSB to LSB, as follows: - * - * | MSB | 2nd MSB | 3rd MSB | LSB | - * red green blue alpha - * 0 1 2 3 (big-endian) - * 3 2 1 0 (little-endian) - * - * Because we use MSB to LSB ordering within the 32-bit word, - * the individual 8-bit samples can be accessed with - * GET_DATA_BYTE and SET_DATA_BYTE macros, using the - * (implicitly big-ending) ordering - * red: byte 0 (MSB) - * green: byte 1 (2nd MSB) - * blue: byte 2 (3rd MSB) - * alpha: byte 3 (LSB) - * - * The specific color assignment is made in this file, - * through the definitions of COLOR_RED, etc. Then the R, G - * B and A sample values can be retrieved using - * redval = GET_DATA_BYTE(&pixel, COLOR_RED); - * greenval = GET_DATA_BYTE(&pixel, COLOR_GREEN); - * blueval = GET_DATA_BYTE(&pixel, COLOR_BLUE); - * alphaval = GET_DATA_BYTE(&pixel, L_ALPHA_CHANNEL); - * and they can be set with - * SET_DATA_BYTE(&pixel, COLOR_RED, redval); - * SET_DATA_BYTE(&pixel, COLOR_GREEN, greenval); - * SET_DATA_BYTE(&pixel, COLOR_BLUE, blueval); - * SET_DATA_BYTE(&pixel, L_ALPHA_CHANNEL, alphaval); - * - * More efficiently, these components can be extracted directly - * by shifting and masking, explicitly using the values in - * L_RED_SHIFT, etc.: - * (pixel32 >> L_RED_SHIFT) & 0xff; (red) - * (pixel32 >> L_GREEN_SHIFT) & 0xff; (green) - * (pixel32 >> L_BLUE_SHIFT) & 0xff; (blue) - * (pixel32 >> L_ALPHA_SHIFT) & 0xff; (alpha) - * The functions extractRGBValues() and extractRGBAValues() are - * provided to do this. Likewise, the pixels can be set - * directly by shifting, using composeRGBPixel() and - * composeRGBAPixel(). - * - * All these operations work properly on both big- and little-endians. - * - * (5) A reference count is held within each pix, giving the - * number of ptrs to the pix. When a pixClone() call - * is made, the ref count is increased by 1, and - * when a pixDestroy() call is made, the reference count - * of the pix is decremented. The pix is only destroyed - * when the reference count goes to zero. - * - * (6) The version numbers (below) are used in the serialization - * of these data structures. They are placed in the files, - * and rarely (if ever) change. Provision is currently made for - * backward compatibility in reading from boxaa version 2. - * - * (7) The serialization dependencies are as follows: - * pixaa : pixa : boxa - * boxaa : boxa - * So, for example, pixaa and boxaa can be changed without - * forcing a change in pixa or boxa. However, if pixa is - * changed, it forces a change in pixaa, and if boxa is - * changed, if forces a change in the other three. - * We define four version numbers: - * PIXAA_VERSION_NUMBER - * PIXA_VERSION_NUMBER - * BOXAA_VERSION_NUMBER - * BOXA_VERSION_NUMBER - *- *-------------------------------------------------------------------------*/ - - - -/*-------------------------------------------------------------------------* - * Array of pix * - *-------------------------------------------------------------------------*/ - - /* Serialization for primary data structures */ -#define PIXAA_VERSION_NUMBER 2 /*!< Version for Pixaa serialization */ -#define PIXA_VERSION_NUMBER 2 /*!< Version for Pixa serialization */ -#define BOXA_VERSION_NUMBER 2 /*!< Version for Boxa serialization */ -#define BOXAA_VERSION_NUMBER 3 /*!< Version for Boxaa serialization */ - -/*! Array of pix */ -struct Pixa -{ - l_int32 n; /*!< number of Pix in ptr array */ - l_int32 nalloc; /*!< number of Pix ptrs allocated */ - l_uint32 refcount; /*!< reference count (1 if no clones) */ - struct Pix **pix; /*!< the array of ptrs to pix */ - struct Boxa *boxa; /*!< array of boxes */ -}; -typedef struct Pixa PIXA; - -/*! Array of arrays of pix */ -struct Pixaa -{ - l_int32 n; /*!< number of Pixa in ptr array */ - l_int32 nalloc; /*!< number of Pixa ptrs allocated */ - struct Pixa **pixa; /*!< array of ptrs to pixa */ - struct Boxa *boxa; /*!< array of boxes */ -}; -typedef struct Pixaa PIXAA; - - -/*-------------------------------------------------------------------------* - * Basic rectangle and rectangle arrays * - *-------------------------------------------------------------------------*/ - -/*! Basic rectangle */ -struct Box -{ - l_int32 x; /*!< left coordinate */ - l_int32 y; /*!< top coordinate */ - l_int32 w; /*!< box width */ - l_int32 h; /*!< box height */ - l_uint32 refcount; /*!< reference count (1 if no clones) */ -}; -typedef struct Box BOX; - -/*! Array of Box */ -struct Boxa -{ - l_int32 n; /*!< number of box in ptr array */ - l_int32 nalloc; /*!< number of box ptrs allocated */ - l_uint32 refcount; /*!< reference count (1 if no clones) */ - struct Box **box; /*!< box ptr array */ -}; -typedef struct Boxa BOXA; - -/*! Array of Boxa */ -struct Boxaa -{ - l_int32 n; /*!< number of boxa in ptr array */ - l_int32 nalloc; /*!< number of boxa ptrs allocated */ - struct Boxa **boxa; /*!< boxa ptr array */ -}; -typedef struct Boxaa BOXAA; - - -/*-------------------------------------------------------------------------* - * Array of points * - *-------------------------------------------------------------------------*/ -#define PTA_VERSION_NUMBER 1 /*!< Version for Pta serialization */ - -/*! Array of points */ -struct Pta -{ - l_int32 n; /*!< actual number of pts */ - l_int32 nalloc; /*!< size of allocated arrays */ - l_uint32 refcount; /*!< reference count (1 if no clones) */ - l_float32 *x, *y; /*!< arrays of floats */ -}; -typedef struct Pta PTA; - - -/*-------------------------------------------------------------------------* - * Array of Pta * - *-------------------------------------------------------------------------*/ - -/*! Array of Pta */ -struct Ptaa -{ - l_int32 n; /*!< number of pta in ptr array */ - l_int32 nalloc; /*!< number of pta ptrs allocated */ - struct Pta **pta; /*!< pta ptr array */ -}; -typedef struct Ptaa PTAA; - - -/*-------------------------------------------------------------------------* - * Pix accumulator container * - *-------------------------------------------------------------------------*/ - -/*! Pix accumulator container */ -struct Pixacc -{ - l_int32 w; /*!< array width */ - l_int32 h; /*!< array height */ - l_int32 offset; /*!< used to allow negative */ - /*!< intermediate results */ - struct Pix *pix; /*!< the 32 bit accumulator pix */ -}; -typedef struct Pixacc PIXACC; - - -/*-------------------------------------------------------------------------* - * Pix tiling * - *-------------------------------------------------------------------------*/ - -/*! Pix tiling */ -struct PixTiling -{ - struct Pix *pix; /*!< input pix (a clone) */ - l_int32 nx; /*!< number of tiles horizontally */ - l_int32 ny; /*!< number of tiles vertically */ - l_int32 w; /*!< tile width */ - l_int32 h; /*!< tile height */ - l_int32 xoverlap; /*!< overlap on left and right */ - l_int32 yoverlap; /*!< overlap on top and bottom */ - l_int32 strip; /*!< strip for paint; default is TRUE */ -}; -typedef struct PixTiling PIXTILING; - - -/*-------------------------------------------------------------------------* - * FPix: pix with float array * - *-------------------------------------------------------------------------*/ -#define FPIX_VERSION_NUMBER 2 /*!< Version for FPix serialization */ - -/*! Pix with float array */ -struct FPix -{ - l_int32 w; /*!< width in pixels */ - l_int32 h; /*!< height in pixels */ - l_int32 wpl; /*!< 32-bit words/line */ - l_uint32 refcount; /*!< reference count (1 if no clones) */ - l_int32 xres; /*!< image res (ppi) in x direction */ - /*!< (use 0 if unknown) */ - l_int32 yres; /*!< image res (ppi) in y direction */ - /*!< (use 0 if unknown) */ - l_float32 *data; /*!< the float image data */ -}; -typedef struct FPix FPIX; - -/*! Array of FPix */ -struct FPixa -{ - l_int32 n; /*!< number of fpix in ptr array */ - l_int32 nalloc; /*!< number of fpix ptrs allocated */ - l_uint32 refcount; /*!< reference count (1 if no clones) */ - struct FPix **fpix; /*!< the array of ptrs to fpix */ -}; -typedef struct FPixa FPIXA; - - -/*-------------------------------------------------------------------------* - * DPix: pix with double array * - *-------------------------------------------------------------------------*/ -#define DPIX_VERSION_NUMBER 2 /*!< Version for DPix serialization */ - -/*! Pix with double array */ -struct DPix -{ - l_int32 w; /*!< width in pixels */ - l_int32 h; /*!< height in pixels */ - l_int32 wpl; /*!< 32-bit words/line */ - l_uint32 refcount; /*!< reference count (1 if no clones) */ - l_int32 xres; /*!< image res (ppi) in x direction */ - /*!< (use 0 if unknown) */ - l_int32 yres; /*!< image res (ppi) in y direction */ - /*!< (use 0 if unknown) */ - l_float64 *data; /*!< the double image data */ -}; -typedef struct DPix DPIX; - - -/*-------------------------------------------------------------------------* - * PixComp: compressed pix * - *-------------------------------------------------------------------------*/ - -/*! Compressed Pix */ -struct PixComp -{ - l_int32 w; /*!< width in pixels */ - l_int32 h; /*!< height in pixels */ - l_int32 d; /*!< depth in bits */ - l_int32 xres; /*!< image res (ppi) in x direction */ - /*!< (use 0 if unknown) */ - l_int32 yres; /*!< image res (ppi) in y direction */ - /*!< (use 0 if unknown) */ - l_int32 comptype; /*!< compressed format (IFF_TIFF_G4, */ - /*!< IFF_PNG, IFF_JFIF_JPEG) */ - char *text; /*!< text string associated with pix */ - l_int32 cmapflag; /*!< flag (1 for cmap, 0 otherwise) */ - l_uint8 *data; /*!< the compressed image data */ - size_t size; /*!< size of the data array */ -}; -typedef struct PixComp PIXC; - - -/*-------------------------------------------------------------------------* - * PixaComp: array of compressed pix * - *-------------------------------------------------------------------------*/ -#define PIXACOMP_VERSION_NUMBER 2 /*!< Version for PixaComp serialization */ - -/*! Array of compressed pix */ -struct PixaComp -{ - l_int32 n; /*!< number of PixComp in ptr array */ - l_int32 nalloc; /*!< number of PixComp ptrs allocated */ - l_int32 offset; /*!< indexing offset into ptr array */ - struct PixComp **pixc; /*!< the array of ptrs to PixComp */ - struct Boxa *boxa; /*!< array of boxes */ -}; -typedef struct PixaComp PIXAC; - - -/*-------------------------------------------------------------------------* - * Access and storage flags * - *-------------------------------------------------------------------------*/ -/* - *- * For Pix, Box, Pta and Numa, there are 3 standard methods for handling - * the retrieval or insertion of a struct: - * (1) direct insertion (Don't do this if there is another handle - * somewhere to this same struct!) - * (2) copy (Always safe, sets up a refcount of 1 on the new object. - * Can be undesirable if very large, such as an image or - * an array of images.) - * (3) clone (Makes another handle to the same struct, and bumps the - * refcount up by 1. OK to use except in two situations: - * (a) You change data through one of the handles but don't - * want those changes to be seen by the other handle. - * (b) The application is multi-threaded. Because the clone - * operation is not atomic (e.g., locked with a mutex), - * it is possible to end up with an incorrect ref count, - * causing either a memory leak or a crash. - * - * For Pixa and Boxa, which are structs that hold an array of clonable - * structs, there is an additional method: - * (4) copy-clone (Makes a new higher-level struct with a refcount - * of 1, but clones all the structs in the array.) - * - * Unlike the other structs, when retrieving a string from an Sarray, - * you are allowed to get a handle without a copy or clone (i.e., the - * string is not owned by the handle). You must not either free the string - * or insert it in some other struct that would own it. Specifically, - * for an Sarray, the copyflag for retrieval is either: - * L_COPY or L_NOCOPY - * and for insertion, the copyflag is either: - * L_COPY or one of {L_INSERT , L_NOCOPY} (the latter are equivalent - * for insertion)) - * Typical patterns are: - * (1) Reference a string in an Sarray with L_NOCOPY and insert a copy - * of it in another Sarray with L_COPY. - * (2) Copy a string from an Sarray with L_COPY and insert it in - * another Sarray with L_INSERT (or L_NOCOPY). - * In both cases, a copy is made and both Sarrays own their instance - * of that string. - *- */ - -/*! Object Access */ -enum { - L_NOCOPY = 0, /*!< do not copy the object; do not delete the ptr */ - L_INSERT = L_NOCOPY, /*!< stuff it in; do not copy or clone */ - L_COPY = 1, /*!< make/use a copy of the object */ - L_CLONE = 2, /*!< make/use clone (ref count) of the object */ - L_COPY_CLONE = 3 /*!< make a new array object (e.g., pixa) and fill */ - /*!< the array with clones (e.g., pix) */ -}; - - -/*----------------------------------------------------------------------------* - * Sort flags * - *----------------------------------------------------------------------------*/ -/*! Sort Mode */ -enum { - L_SHELL_SORT = 1, /*!< use shell sort */ - L_BIN_SORT = 2 /*!< use bin sort */ -}; - -/*! Sort Order */ -enum { - L_SORT_INCREASING = 1, /*!< sort in increasing order */ - L_SORT_DECREASING = 2 /*!< sort in decreasing order */ -}; - -/*! Sort Type */ -enum { - L_SORT_BY_X = 1, /*!< sort box or c.c. by left edge location */ - L_SORT_BY_Y = 2, /*!< sort box or c.c. by top edge location */ - L_SORT_BY_RIGHT = 3, /*!< sort box or c.c. by right edge location */ - L_SORT_BY_BOT = 4, /*!< sort box or c.c. by bot edge location */ - L_SORT_BY_WIDTH = 5, /*!< sort box or c.c. by width */ - L_SORT_BY_HEIGHT = 6, /*!< sort box or c.c. by height */ - L_SORT_BY_MIN_DIMENSION = 7, /*!< sort box or c.c. by min dimension */ - L_SORT_BY_MAX_DIMENSION = 8, /*!< sort box or c.c. by max dimension */ - L_SORT_BY_PERIMETER = 9, /*!< sort box or c.c. by perimeter */ - L_SORT_BY_AREA = 10, /*!< sort box or c.c. by area */ - L_SORT_BY_ASPECT_RATIO = 11 /*!< sort box or c.c. by width/height ratio */ -}; - - -/*---------------------------------------------------------------------------* - * Blend flags * - *---------------------------------------------------------------------------*/ -/*! Blend Types */ -enum { - L_BLEND_WITH_INVERSE = 1, /*!< add some of src inverse to itself */ - L_BLEND_TO_WHITE = 2, /*!< shift src colors towards white */ - L_BLEND_TO_BLACK = 3, /*!< shift src colors towards black */ - L_BLEND_GRAY = 4, /*!< blend src directly with blender */ - L_BLEND_GRAY_WITH_INVERSE = 5 /*!< add amount of src inverse to itself, */ - /*!< based on blender pix value */ -}; - -/*! Paint Selection */ -enum { - L_PAINT_LIGHT = 1, /*!< colorize non-black pixels */ - L_PAINT_DARK = 2 /*!< colorize non-white pixels */ -}; - - -/*-------------------------------------------------------------------------* - * Graphics pixel setting * - *-------------------------------------------------------------------------*/ -/*! Pixel Setting */ -enum { - L_SET_PIXELS = 1, /*!< set all bits in each pixel to 1 */ - L_CLEAR_PIXELS = 2, /*!< set all bits in each pixel to 0 */ - L_FLIP_PIXELS = 3 /*!< flip all bits in each pixel */ -}; - - -/*-------------------------------------------------------------------------* - * Size and location filter flags * - *-------------------------------------------------------------------------*/ -/*! Size Comparison */ -enum { - L_SELECT_IF_LT = 1, /*!< save if value is less than threshold */ - L_SELECT_IF_GT = 2, /*!< save if value is more than threshold */ - L_SELECT_IF_LTE = 3, /*!< save if value is <= to the threshold */ - L_SELECT_IF_GTE = 4 /*!< save if value is >= to the threshold */ -}; - -/*! Size Selection */ -enum { - L_SELECT_BY_WIDTH = 1, /*!< select by width; 1 bpp */ - L_SELECT_BY_HEIGHT = 2, /*!< select by height; 1 bpp */ - L_SELECT_BY_MAX_DIMENSION = 3, /*!< select by max of width and */ - /*!< height; 1 bpp */ - L_SELECT_BY_AREA = 4, /*!< select by foreground area; 1 bpp */ - L_SELECT_BY_PERIMETER = 5 /*!< select by perimeter; 1 bpp */ -}; - -/*! Location Filter */ -enum { - L_SELECT_WIDTH = 1, /*!< width must satisfy constraint */ - L_SELECT_HEIGHT = 2, /*!< height must satisfy constraint */ - L_SELECT_XVAL = 3, /*!< x value must satisfy constraint */ - L_SELECT_YVAL = 4, /*!< y value must satisfy constraint */ - L_SELECT_IF_EITHER = 5, /*!< either width or height (or xval */ - /*!< or yval) can satisfy constraint */ - L_SELECT_IF_BOTH = 6 /*!< both width and height (or xval */ - /*!< and yval must satisfy constraint */ -}; - -/*! Boxa Check */ -enum { - L_CHECK_WIDTH = 1, /*!< check and possibly modify width */ - L_CHECK_HEIGHT = 2, /*!< check and possibly modify height */ - L_CHECK_BOTH = 3 /*!< check and possibly modify both */ -}; - - -/*-------------------------------------------------------------------------* - * Color component selection flags * - *-------------------------------------------------------------------------*/ -/*! Color Selection */ -enum { - L_SELECT_RED = 1, /*!< use red component */ - L_SELECT_GREEN = 2, /*!< use green component */ - L_SELECT_BLUE = 3, /*!< use blue component */ - L_SELECT_MIN = 4, /*!< use min color component */ - L_SELECT_MAX = 5, /*!< use max color component */ - L_SELECT_AVERAGE = 6, /*!< use average of color components */ - L_SELECT_HUE = 7, /*!< use hue value (in HSV color space) */ - L_SELECT_SATURATION = 8 /*!< use saturation value (in HSV space) */ -}; - - -/*-------------------------------------------------------------------------* - * 16-bit conversion flags * - *-------------------------------------------------------------------------*/ -/*! 16-bit Conversion */ -enum { - L_LS_BYTE = 1, /*!< use LSB */ - L_MS_BYTE = 2, /*!< use MSB */ - L_AUTO_BYTE = 3, /*!< use LSB if max(val) < 256; else MSB */ - L_CLIP_TO_FF = 4, /*!< use max(val, 255) */ - L_LS_TWO_BYTES = 5, /*!< use two LSB */ - L_MS_TWO_BYTES = 6, /*!< use two MSB */ - L_CLIP_TO_FFFF = 7 /*!< use max(val, 65535) */ -}; - - -/*-------------------------------------------------------------------------* - * Rotate and shear flags * - *-------------------------------------------------------------------------*/ -/*! Rotation Type */ -enum { - L_ROTATE_AREA_MAP = 1, /*!< use area map rotation, if possible */ - L_ROTATE_SHEAR = 2, /*!< use shear rotation */ - L_ROTATE_SAMPLING = 3 /*!< use sampling */ -}; - -/*! Background Color */ -enum { - L_BRING_IN_WHITE = 1, /*!< bring in white pixels from the outside */ - L_BRING_IN_BLACK = 2 /*!< bring in black pixels from the outside */ -}; - -/*! Shear Point */ -enum { - L_SHEAR_ABOUT_CORNER = 1, /*!< shear image about UL corner */ - L_SHEAR_ABOUT_CENTER = 2 /*!< shear image about center */ -}; - - -/*-------------------------------------------------------------------------* - * Affine transform order flags * - *-------------------------------------------------------------------------*/ -/*! Affine Transform Order */ -enum { - L_TR_SC_RO = 1, /*!< translate, scale, rotate */ - L_SC_RO_TR = 2, /*!< scale, rotate, translate */ - L_RO_TR_SC = 3, /*!< rotate, translate, scale */ - L_TR_RO_SC = 4, /*!< translate, rotate, scale */ - L_RO_SC_TR = 5, /*!< rotate, scale, translate */ - L_SC_TR_RO = 6 /*!< scale, translate, rotate */ -}; - - -/*-------------------------------------------------------------------------* - * Grayscale filling flags * - *-------------------------------------------------------------------------*/ -/*! Grayscale Fill */ -enum { - L_FILL_WHITE = 1, /*!< fill white pixels (e.g, in fg map) */ - L_FILL_BLACK = 2 /*!< fill black pixels (e.g., in bg map) */ -}; - - -/*-------------------------------------------------------------------------* - * Flags for setting to white or black * - *-------------------------------------------------------------------------*/ -/*! BlackWhite Set */ -enum { - L_SET_WHITE = 1, /*!< set pixels to white */ - L_SET_BLACK = 2 /*!< set pixels to black */ -}; - - -/*-------------------------------------------------------------------------* - * Flags for getting white or black value * - *-------------------------------------------------------------------------*/ -/*! BlackWhite Get */ -enum { - L_GET_WHITE_VAL = 1, /*!< get white pixel value */ - L_GET_BLACK_VAL = 2 /*!< get black pixel value */ -}; - - -/*-------------------------------------------------------------------------* - * Flags for 8 bit and 16 bit pixel sums * - *-------------------------------------------------------------------------*/ -/*! BlackWhite Sum */ -enum { - L_WHITE_IS_MAX = 1, /*!< white pixels are 0xff or 0xffff; black are 0 */ - L_BLACK_IS_MAX = 2 /*!< black pixels are 0xff or 0xffff; white are 0 */ -}; - - -/*-------------------------------------------------------------------------* - * Dither parameters * - * If within this grayscale distance from black or white, * - * do not propagate excess or deficit to neighboring pixels. * - *-------------------------------------------------------------------------*/ -/*! Dither Distance */ -enum { - DEFAULT_CLIP_LOWER_1 = 10, /*!< dist to black with no prop; 1 bpp */ - DEFAULT_CLIP_UPPER_1 = 10, /*!< dist to black with no prop; 1 bpp */ - DEFAULT_CLIP_LOWER_2 = 5, /*!< dist to black with no prop; 2 bpp */ - DEFAULT_CLIP_UPPER_2 = 5 /*!< dist to black with no prop; 2 bpp */ -}; - - -/*-------------------------------------------------------------------------* - * Distance type flags * - *-------------------------------------------------------------------------*/ -/*! Distance Type */ -enum { - L_MANHATTAN_DISTANCE = 1, /*!< L1 distance (e.g., in color space) */ - L_EUCLIDEAN_DISTANCE = 2 /*!< L2 distance */ -}; - - -/*-------------------------------------------------------------------------* - * Distance Value flags * - *-------------------------------------------------------------------------*/ -/*! Distance Value */ -enum { - L_NEGATIVE = 1, /*!< values < 0 */ - L_NON_NEGATIVE = 2, /*!< values >= 0 */ - L_POSITIVE = 3, /*!< values > 0 */ - L_NON_POSITIVE = 4, /*!< values <= 0 */ - L_ZERO = 5, /*!< values = 0 */ - L_ALL = 6 /*!< all values */ -}; - - -/*-------------------------------------------------------------------------* - * Statistical measures * - *-------------------------------------------------------------------------*/ -/*! Stats Type */ -enum { - L_MEAN_ABSVAL = 1, /*!< average of abs values */ - L_MEDIAN_VAL = 2, /*!< median value of set */ - L_MODE_VAL = 3, /*!< mode value of set */ - L_MODE_COUNT = 4, /*!< mode count of set */ - L_ROOT_MEAN_SQUARE = 5, /*!< rms of values */ - L_STANDARD_DEVIATION = 6, /*!< standard deviation from mean */ - L_VARIANCE = 7 /*!< variance of values */ -}; - - -/*-------------------------------------------------------------------------* - * Set index selection flags * - *-------------------------------------------------------------------------*/ -/*! Index Selection */ -enum { - L_CHOOSE_CONSECUTIVE = 1, /*!< select 'n' consecutive */ - L_CHOOSE_SKIP_BY = 2 /*!< select at intervals of 'n' */ -}; - - -/*-------------------------------------------------------------------------* - * Text orientation flags * - *-------------------------------------------------------------------------*/ -/*! Text Orientation */ -enum { - L_TEXT_ORIENT_UNKNOWN = 0, /*!< low confidence on text orientation */ - L_TEXT_ORIENT_UP = 1, /*!< portrait, text rightside-up */ - L_TEXT_ORIENT_LEFT = 2, /*!< landscape, text up to left */ - L_TEXT_ORIENT_DOWN = 3, /*!< portrait, text upside-down */ - L_TEXT_ORIENT_RIGHT = 4 /*!< landscape, text up to right */ -}; - - -/*-------------------------------------------------------------------------* - * Edge orientation flags * - *-------------------------------------------------------------------------*/ -/*! Edge Orientation */ -enum { - L_HORIZONTAL_EDGES = 0, /*!< filters for horizontal edges */ - L_VERTICAL_EDGES = 1, /*!< filters for vertical edges */ - L_ALL_EDGES = 2 /*!< filters for all edges */ -}; - - -/*-------------------------------------------------------------------------* - * Line orientation flags * - *-------------------------------------------------------------------------*/ -/*! Line Orientation */ -enum { - L_HORIZONTAL_LINE = 0, /*!< horizontal line */ - L_POS_SLOPE_LINE = 1, /*!< 45 degree line with positive slope */ - L_VERTICAL_LINE = 2, /*!< vertical line */ - L_NEG_SLOPE_LINE = 3, /*!< 45 degree line with negative slope */ - L_OBLIQUE_LINE = 4 /*!< neither horizontal nor vertical */ -}; - - -/*-------------------------------------------------------------------------* - * Image orientation flags * - *-------------------------------------------------------------------------*/ -/*! Image Orientation */ -enum { - L_PORTRAIT_MODE = 0, /*!< typical: page is viewed with height > width */ - L_LANDSCAPE_MODE = 1 /*!< page is viewed at 90 deg to portrait mode */ -}; - - -/*-------------------------------------------------------------------------* - * Scan direction flags * - *-------------------------------------------------------------------------*/ -/*! Scan Direction */ -enum { - L_FROM_LEFT = 0, /*!< scan from left */ - L_FROM_RIGHT = 1, /*!< scan from right */ - L_FROM_TOP = 2, /*!< scan from top */ - L_FROM_BOT = 3, /*!< scan from bottom */ - L_SCAN_NEGATIVE = 4, /*!< scan in negative direction */ - L_SCAN_POSITIVE = 5, /*!< scan in positive direction */ - L_SCAN_BOTH = 6, /*!< scan in both directions */ - L_SCAN_HORIZONTAL = 7, /*!< horizontal scan (direction unimportant) */ - L_SCAN_VERTICAL = 8 /*!< vertical scan (direction unimportant) */ -}; - - -/*-------------------------------------------------------------------------* - * Box size adjustment and location flags * - *-------------------------------------------------------------------------*/ -/*! Box Adjustment */ -enum { - L_ADJUST_SKIP = 0, /*!< do not adjust */ - L_ADJUST_LEFT = 1, /*!< adjust left edge */ - L_ADJUST_RIGHT = 2, /*!< adjust right edge */ - L_ADJUST_LEFT_AND_RIGHT = 3, /*!< adjust both left and right edges */ - L_ADJUST_TOP = 4, /*!< adjust top edge */ - L_ADJUST_BOT = 5, /*!< adjust bottom edge */ - L_ADJUST_TOP_AND_BOT = 6, /*!< adjust both top and bottom edges */ - L_ADJUST_CHOOSE_MIN = 7, /*!< choose the min median value */ - L_ADJUST_CHOOSE_MAX = 8, /*!< choose the max median value */ - L_SET_LEFT = 9, /*!< set left side to a given value */ - L_SET_RIGHT = 10, /*!< set right side to a given value */ - L_SET_TOP = 11, /*!< set top side to a given value */ - L_SET_BOT = 12, /*!< set bottom side to a given value */ - L_GET_LEFT = 13, /*!< get left side location */ - L_GET_RIGHT = 14, /*!< get right side location */ - L_GET_TOP = 15, /*!< get top side location */ - L_GET_BOT = 16 /*!< get bottom side location */ -}; - - -/*-------------------------------------------------------------------------* - * Flags for modifying box boundaries using a second box * - *-------------------------------------------------------------------------*/ -/*! Box Boundary Mod */ -enum { - L_USE_MINSIZE = 1, /*!< use boundaries giving min size */ - L_USE_MAXSIZE = 2, /*!< use boundaries giving max size */ - L_SUB_ON_LOC_DIFF = 3, /*!< modify boundary if big location diff */ - L_SUB_ON_SIZE_DIFF = 4, /*!< modify boundary if big size diff */ - L_USE_CAPPED_MIN = 5, /*!< modify boundary with capped min */ - L_USE_CAPPED_MAX = 6 /*!< modify boundary with capped max */ -}; - - -/*-------------------------------------------------------------------------* - * Handling overlapping bounding boxes in boxa * - *-------------------------------------------------------------------------*/ -/*! Box Overlap Mod */ -enum { - L_COMBINE = 1, /*!< resize to bounding region; remove smaller */ - L_REMOVE_SMALL = 2 /*!< only remove smaller */ -}; - - -/*-------------------------------------------------------------------------* - * Selecting or making a box from two (intersecting) boxes * - *-------------------------------------------------------------------------*/ -/*! Box Combine or Select */ -enum { - L_GEOMETRIC_UNION = 1, /*!< use union of two boxes */ - L_GEOMETRIC_INTERSECTION = 2, /*!< use intersection of two boxes */ - L_LARGEST_AREA = 3, /*!< use box with largest area */ - L_SMALLEST_AREA = 4 /*!< use box with smallest area */ -}; - - -/*-------------------------------------------------------------------------* - * Flags for replacing invalid boxes * - *-------------------------------------------------------------------------*/ -/*! Box Replacement */ -enum { - L_USE_ALL_BOXES = 1, /*!< consider all boxes in the sequence */ - L_USE_SAME_PARITY_BOXES = 2 /*!< consider boxes with the same parity */ -}; - - -/*-------------------------------------------------------------------------* - * Flags for box corners and center * - *-------------------------------------------------------------------------*/ -/*! Box Corners and Center */ -enum { - L_UPPER_LEFT = 1, /*!< UL corner */ - L_UPPER_RIGHT = 2, /*!< UR corner */ - L_LOWER_LEFT = 3, /*!< LL corner */ - L_LOWER_RIGHT = 4, /*!< LR corner */ - L_BOX_CENTER = 5 /*!< center */ -}; - - -/*-------------------------------------------------------------------------* - * Horizontal warp * - *-------------------------------------------------------------------------*/ -/*! Horiz Warp Stretch */ -enum { - L_WARP_TO_LEFT = 1, /*!< increasing stretch or contraction to left */ - L_WARP_TO_RIGHT = 2 /*!< increasing stretch or contraction to right */ -}; - -/*! Horiz Warp Mode */ -enum { - L_LINEAR_WARP = 1, /*!< stretch or contraction grows linearly */ - L_QUADRATIC_WARP = 2 /*!< stretch or contraction grows quadratically */ -}; - - -/*-------------------------------------------------------------------------* - * Pixel selection for resampling * - *-------------------------------------------------------------------------*/ -/*! Pixel Selection */ -enum { - L_INTERPOLATED = 1, /*!< linear interpolation from src pixels */ - L_SAMPLED = 2 /*!< nearest src pixel sampling only */ -}; - - -/*-------------------------------------------------------------------------* - * Thinning flags * - *-------------------------------------------------------------------------*/ -/*! Thinning Polarity */ -enum { - L_THIN_FG = 1, /*!< thin foreground of 1 bpp image */ - L_THIN_BG = 2 /*!< thin background of 1 bpp image */ -}; - - -/*-------------------------------------------------------------------------* - * Runlength flags * - *-------------------------------------------------------------------------*/ -/*! Runlength Direction */ -enum { - L_HORIZONTAL_RUNS = 0, /*!< determine runlengths of horizontal runs */ - L_VERTICAL_RUNS = 1 /*!< determine runlengths of vertical runs */ -}; - - -/*-------------------------------------------------------------------------* - * Edge filter flags * - *-------------------------------------------------------------------------*/ -/*! Edge Filter */ -enum { - L_SOBEL_EDGE = 1, /*!< Sobel edge filter */ - L_TWO_SIDED_EDGE = 2 /*!< Two-sided edge filter */ -}; - - -/*-------------------------------------------------------------------------* - * Subpixel color component ordering in LCD display * - *-------------------------------------------------------------------------*/ -/*! Subpixel Color Order */ -enum { - L_SUBPIXEL_ORDER_RGB = 1, /*!< sensor order left-to-right RGB */ - L_SUBPIXEL_ORDER_BGR = 2, /*!< sensor order left-to-right BGR */ - L_SUBPIXEL_ORDER_VRGB = 3, /*!< sensor order top-to-bottom RGB */ - L_SUBPIXEL_ORDER_VBGR = 4 /*!< sensor order top-to-bottom BGR */ -}; - - -/*-------------------------------------------------------------------------* - * HSV histogram flags * - *-------------------------------------------------------------------------*/ -/*! HSV Histogram */ -enum { - L_HS_HISTO = 1, /*!< Use hue-saturation histogram */ - L_HV_HISTO = 2, /*!< Use hue-value histogram */ - L_SV_HISTO = 3 /*!< Use saturation-value histogram */ -}; - - -/*-------------------------------------------------------------------------* - * HSV Region flags (inclusion, exclusion) * - *-------------------------------------------------------------------------*/ -/*! HSV Region */ -enum { - L_INCLUDE_REGION = 1, /*!< Use pixels with specified HSV region */ - L_EXCLUDE_REGION = 2 /*!< Use pixels outside HSV region */ -}; - - -/*-------------------------------------------------------------------------* - * Location flags for adding text to a pix * - *-------------------------------------------------------------------------*/ -/*! Add Text Location */ -enum { - L_ADD_ABOVE = 1, /*!< Add text above the image */ - L_ADD_BELOW = 2, /*!< Add text below the image */ - L_ADD_LEFT = 3, /*!< Add text to the left of the image */ - L_ADD_RIGHT = 4, /*!< Add text to the right of the image */ - L_ADD_AT_TOP = 5, /*!< Add text over the top of the image */ - L_ADD_AT_BOT = 6, /*!< Add text over the bottom of the image */ - L_ADD_AT_LEFT = 7, /*!< Add text over left side of the image */ - L_ADD_AT_RIGHT = 8 /*!< Add text over right side of the image */ -}; - - -/*-------------------------------------------------------------------------* - * Flags for plotting on a pix * - *-------------------------------------------------------------------------*/ -/*! Pix Plot */ -enum { - L_PLOT_AT_TOP = 1, /*!< Plot horizontally at top */ - L_PLOT_AT_MID_HORIZ = 2, /*!< Plot horizontally at middle */ - L_PLOT_AT_BOT = 3, /*!< Plot horizontally at bottom */ - L_PLOT_AT_LEFT = 4, /*!< Plot vertically at left */ - L_PLOT_AT_MID_VERT = 5, /*!< Plot vertically at middle */ - L_PLOT_AT_RIGHT = 6 /*!< Plot vertically at right */ -}; - - -/*-------------------------------------------------------------------------* - * Flags for making simple masks * - *-------------------------------------------------------------------------*/ -/*! Mask Generation */ -enum { - L_USE_INNER = 1, /*!< Select the interior part */ - L_USE_OUTER = 2 /*!< Select the outer part (e.g., a frame) */ -}; - - -/*-------------------------------------------------------------------------* - * Flags for selecting display program * - *-------------------------------------------------------------------------*/ -/*! Display Program */ -enum { - L_DISPLAY_WITH_XZGV = 1, /*!< Use xzgv with pixDisplay() */ - L_DISPLAY_WITH_XLI = 2, /*!< Use xli with pixDisplay() */ - L_DISPLAY_WITH_XV = 3, /*!< Use xv with pixDisplay() */ - L_DISPLAY_WITH_IV = 4, /*!< Use irfvanview (win) with pixDisplay() */ - L_DISPLAY_WITH_OPEN = 5 /*!< Use open (apple) with pixDisplay() */ -}; - -/*-------------------------------------------------------------------------* - * Flag(s) used in the 'special' pix field for non-default operations * - * - 0 is default for chroma sampling in jpeg * - * - 10-19 are used for zlib compression in png write * - * - 4 and 8 are used for specifying connectivity in labelling * - *-------------------------------------------------------------------------*/ -/*! Flags used in Pix::special */ -enum { - L_NO_CHROMA_SAMPLING_JPEG = 1 /*!< Write full resolution chroma */ -}; - - -/*-------------------------------------------------------------------------* - * Handling negative values in conversion to unsigned int * - *-------------------------------------------------------------------------*/ -/*! Negative Value */ -enum { - L_CLIP_TO_ZERO = 1, /*!< Clip negative values to 0 */ - L_TAKE_ABSVAL = 2 /*!< Convert to positive using L_ABS() */ -}; - - -/*-------------------------------------------------------------------------* - * Relative to zero flags * - *-------------------------------------------------------------------------*/ -/*! Relative To Zero */ -enum { - L_LESS_THAN_ZERO = 1, /*!< Choose values less than zero */ - L_EQUAL_TO_ZERO = 2, /*!< Choose values equal to zero */ - L_GREATER_THAN_ZERO = 3 /*!< Choose values greater than zero */ -}; - - -/*-------------------------------------------------------------------------* - * Flags for adding or removing trailing slash from string * - *-------------------------------------------------------------------------*/ -/*! Trailing Slash */ -enum { - L_ADD_TRAIL_SLASH = 1, /*!< Add trailing slash to string */ - L_REMOVE_TRAIL_SLASH = 2 /*!< Remove trailing slash from string */ -}; - - -/*-------------------------------------------------------------------------* - * Pix allocator and deallocator function types * - *-------------------------------------------------------------------------*/ -/*! Allocator function type */ -typedef void *(*alloc_fn)(size_t); - -/*! Deallocator function type */ -typedef void (*dealloc_fn)(void *); - - -#endif /* LEPTONICA_PIX_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix1.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix1.c deleted file mode 100644 index 527d42fe..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix1.c +++ /dev/null @@ -1,1930 +0,0 @@ -/*====================================================================* - - 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 pix1.c - *- * - * The pixN.c {N = 1,2,3,4,5} files are sorted by the type of operation. - * The primary functions in these files are: - * - * pix1.c: constructors, destructors and field accessors - * pix2.c: pixel poking of image, pad and border pixels - * pix3.c: masking and logical ops, counting, mirrored tiling - * pix4.c: histograms, statistics, fg/bg estimation - * pix5.c: property measurements, rectangle extraction - * - * - * This file has the basic constructors, destructors and field accessors - * - * Pix memory management (allows custom allocator and deallocator) - * static void *pix_malloc() - * static void pix_free() - * void setPixMemoryManager() - * - * Pix creation - * PIX *pixCreate() - * PIX *pixCreateNoInit() - * PIX *pixCreateTemplate() - * PIX *pixCreateTemplateNoInit() - * PIX *pixCreateWithCmap() - * PIX *pixCreateHeader() - * PIX *pixClone() - * - * Pix destruction - * void pixDestroy() - * static void pixFree() - * - * Pix copy - * PIX *pixCopy() - * l_int32 pixResizeImageData() - * l_int32 pixCopyColormap() - * l_int32 pixSizesEqual() - * l_int32 pixTransferAllData() - * l_int32 pixSwapAndDestroy() - * - * Pix accessors - * l_int32 pixGetWidth() - * l_int32 pixSetWidth() - * l_int32 pixGetHeight() - * l_int32 pixSetHeight() - * l_int32 pixGetDepth() - * l_int32 pixSetDepth() - * l_int32 pixGetDimensions() - * l_int32 pixSetDimensions() - * l_int32 pixCopyDimensions() - * l_int32 pixGetSpp() - * l_int32 pixSetSpp() - * l_int32 pixCopySpp() - * l_int32 pixGetWpl() - * l_int32 pixSetWpl() - * l_int32 pixGetRefcount() - * l_int32 pixChangeRefcount() - * l_uint32 pixGetXRes() - * l_int32 pixSetXRes() - * l_uint32 pixGetYRes() - * l_int32 pixSetYRes() - * l_int32 pixGetResolution() - * l_int32 pixSetResolution() - * l_int32 pixCopyResolution() - * l_int32 pixScaleResolution() - * l_int32 pixGetInputFormat() - * l_int32 pixSetInputFormat() - * l_int32 pixCopyInputFormat() - * l_int32 pixSetSpecial() - * char *pixGetText() - * l_int32 pixSetText() - * l_int32 pixAddText() - * l_int32 pixCopyText() - * PIXCMAP *pixGetColormap() - * l_int32 pixSetColormap() - * l_int32 pixDestroyColormap() - * l_uint32 *pixGetData() - * l_int32 pixSetData() - * l_uint32 *pixExtractData() - * l_int32 pixFreeData() - * - * Pix line ptrs - * void **pixGetLinePtrs() - * - * Pix debug - * l_int32 pixPrintStreamInfo() - * - * - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * Important notes on direct management of pix image data - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * - * Custom allocator and deallocator - * -------------------------------- - * - * At the lowest level, you can specify the function that does the - * allocation and deallocation of the data field in the pix. - * By default, this is malloc and free. However, by calling - * setPixMemoryManager(), custom functions can be substituted. - * When using this, keep two things in mind: - * - * (1) Call setPixMemoryManager() before any pix have been allocated - * (2) Destroy all pix as usual, in order to prevent leaks. - * - * In pixalloc.c, we provide an example custom allocator and deallocator. - * To use it, you must call pmsCreate() before any pix have been allocated - * and pmsDestroy() at the end after all pix have been destroyed. - * - * - * Direct manipulation of the pix data field - * ----------------------------------------- - * - * Memory management of the (image) data field in the pix is - * handled differently from that in the colormap or text fields. - * For colormap and text, the functions pixSetColormap() and - * pixSetText() remove the existing heap data and insert the - * new data. For the image data, pixSetData() just reassigns the - * data field; any existing data will be lost if there isn't - * another handle for it. - * - * Why is pixSetData() limited in this way? Because the image - * data can be very large, we need flexible ways to handle it, - * particularly when you want to re-use the data in a different - * context without making a copy. Here are some different - * things you might want to do: - * - * (1) Use pixCopy(pixd, pixs) where pixd is not the same size - * as pixs. This will remove the data in pixd, allocate a - * new data field in pixd, and copy the data from pixs, leaving - * pixs unchanged. - * - * (2) Use pixTransferAllData(pixd, &pixs, ...) to transfer the - * data from pixs to pixd without making a copy of it. If - * pixs is not cloned, this will do the transfer and destroy pixs. - * But if the refcount of pixs is greater than 1, it just copies - * the data and decrements the ref count. - * - * (3) Use pixSwapAndDestroy(pixd, &pixs) to replace pixs by an - * existing pixd. This is similar to pixTransferAllData(), but - * simpler, in that it never makes any copies and if pixs is - * cloned, the other references are not changed by this operation. - * - * (4) Use pixExtractData() to extract the image data from the pix - * without copying if possible. This could be used, for example, - * to convert from a pix to some other data structure with minimal - * heap allocation. After the data is extracated, the pixels can - * be munged and used in another context. However, the danger - * here is that the pix might have a refcount > 1, in which case - * a copy of the data must be made and the input pix left unchanged. - * If there are no clones, the image data can be extracted without - * a copy, and the data ptr in the pix must be nulled before - * destroying it because the pix will no longer 'own' the data. - * - * We have provided accessors and functions here that should be - * sufficient so that you can do anything you want without - * explicitly referencing any of the pix member fields. - * - * However, to avoid memory smashes and leaks when doing special operations - * on the pix data field, look carefully at the behavior of the image - * data accessors and keep in mind that when you invoke pixDestroy(), - * the pix considers itself the owner of all its heap data. - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include "allheaders.h" - -static void pixFree(PIX *pix); - - -/*-------------------------------------------------------------------------* - * Pix Memory Management * - * * - * These functions give you the freedom to specify at compile or run * - * time the allocator and deallocator to be used for pix. It has no * - * effect on memory management for other data structs, which are * - * controlled by the #defines in environ.h. Likewise, the #defines * - * in environ.h have no effect on the pix memory management. * - * The default functions are malloc and free. Use setPixMemoryManager() * - * to specify other functions to use. * - *-------------------------------------------------------------------------*/ - -/*! Pix memory manager */ - /* - * - * Notes: - * (1) The allocator and deallocator function types, - * alloc_fn and dealloc_fn, are defined in pix.h. - *- */ -struct PixMemoryManager -{ - alloc_fn allocator; - dealloc_fn deallocator; -}; - -/*! Default Pix memory manager */ -static struct PixMemoryManager pix_mem_manager = { - &malloc, - &free -}; - -static void * -pix_malloc(size_t size) -{ -#ifndef _MSC_VER - return (*pix_mem_manager.allocator)(size); -#else /* _MSC_VER */ - /* Under MSVC++, pix_mem_manager is initialized after a call - * to pix_malloc. Just ignore the custom allocator feature. */ - return malloc(size); -#endif /* _MSC_VER */ -} - -static void -pix_free(void *ptr) -{ -#ifndef _MSC_VER - (*pix_mem_manager.deallocator)(ptr); - return; -#else /* _MSC_VER */ - /* Under MSVC++, pix_mem_manager is initialized after a call - * to pix_malloc. Just ignore the custom allocator feature. */ - free(ptr); - return; -#endif /* _MSC_VER */ -} - -/*! - * \brief setPixMemoryManager() - * - * \param[in] allocator [optional] use NULL to skip - * \param[in] deallocator [optional] use NULL to skip - * \return void - * - *- * Notes: - * (1) Use this to change the alloc and/or dealloc functions; - * e.g., setPixMemoryManager(my_malloc, my_free). - * (2) The C99 standard (section 6.7.5.3, par. 8) says: - * A declaration of a parameter as "function returning type" - * shall be adjusted to "pointer to function returning type" - * so that it can be in either of these two forms: - * (a) type (function-ptr(type, ...)) - * (b) type ((*function-ptr)(type, ...)) - * because form (a) is implictly converted to form (b), as in the - * definition of struct PixMemoryManager above. So, for example, - * we should be able to declare either of these: - * (a) void *(allocator(size_t)) - * (b) void *((*allocator)(size_t)) - * However, MSVC++ only accepts the second version. - *- */ -void -setPixMemoryManager(alloc_fn allocator, - dealloc_fn deallocator) -{ - if (allocator) pix_mem_manager.allocator = allocator; - if (deallocator) pix_mem_manager.deallocator = deallocator; - return; -} - - -/*--------------------------------------------------------------------* - * Pix Creation * - *--------------------------------------------------------------------*/ -/*! - * \brief pixCreate() - * - * \param[in] width, height, depth - * \return pixd with data allocated and initialized to 0, - * or NULL on error - */ -PIX * -pixCreate(l_int32 width, - l_int32 height, - l_int32 depth) -{ -PIX *pixd; - - PROCNAME("pixCreate"); - - if ((pixd = pixCreateNoInit(width, height, depth)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - memset(pixd->data, 0, 4LL * pixd->wpl * pixd->h); - return pixd; -} - - -/*! - * \brief pixCreateNoInit() - * - * \param[in] width, height, depth - * \return pixd with data allocated but not initialized, - * or NULL on error - * - *- * Notes: - * (1) Must set pad bits to avoid reading uninitialized data, because - * some optimized routines (e.g., pixConnComp()) read from pad bits. - *- */ -PIX * -pixCreateNoInit(l_int32 width, - l_int32 height, - l_int32 depth) -{ -l_int32 wpl; -PIX *pixd; -l_uint32 *data; - - PROCNAME("pixCreateNoInit"); - if ((pixd = pixCreateHeader(width, height, depth)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - wpl = pixGetWpl(pixd); - if ((data = (l_uint32 *)pix_malloc(4LL * wpl * height)) == NULL) { - pixDestroy(&pixd); - return (PIX *)ERROR_PTR("pix_malloc fail for data", procName, NULL); - } - pixSetData(pixd, data); - pixSetPadBits(pixd, 0); - return pixd; -} - - -/*! - * \brief pixCreateTemplate() - * - * \param[in] pixs - * \return pixd, or NULL on error - * - *- * Notes: - * (1) Makes a Pix of the same size as the input Pix, with the - * data array allocated and initialized to 0. - * (2) Copies the other fields, including colormap if it exists. - *- */ -PIX * -pixCreateTemplate(const PIX *pixs) -{ -PIX *pixd; - - PROCNAME("pixCreateTemplate"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - memset(pixd->data, 0, 4LL * pixd->wpl * pixd->h); - return pixd; -} - - -/*! - * \brief pixCreateTemplateNoInit() - * - * \param[in] pixs - * \return pixd, or NULL on error - * - *- * Notes: - * (1) Makes a Pix of the same size as the input Pix, with - * the data array allocated but not initialized to 0. - * (2) Copies the other fields, including colormap if it exists. - *- */ -PIX * -pixCreateTemplateNoInit(const PIX *pixs) -{ -l_int32 w, h, d; -PIX *pixd; - - PROCNAME("pixCreateTemplateNoInit"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - pixGetDimensions(pixs, &w, &h, &d); - if ((pixd = pixCreateNoInit(w, h, d)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopySpp(pixd, pixs); - pixCopyResolution(pixd, pixs); - pixCopyColormap(pixd, pixs); - pixCopyText(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - return pixd; -} - - -/*! - * \brief pixCreateWithCmap() - * - * \param[in] width - * \param[in] height - * \param[in] depth 2, 4 or 8 bpp - * \param[in] initcolor L_SET_BLACK, L_SET_WHITE - * \return pixd with the initialization color assigned to all pixels, - * or NULL on error. - * - *- * Notes: - * (1) Creates a pix with a cmap, initialized to value 0. - * (2) Initializes the pix black or white by adding that color - * to the cmap at index 0. - *- */ -PIX * -pixCreateWithCmap(l_int32 width, - l_int32 height, - l_int32 depth, - l_int32 initcolor) -{ -PIX *pix; -PIXCMAP *cmap; - - PROCNAME("pixCreateWithCmap"); - - if (depth != 2 && depth != 4 && depth != 8) - return (PIX *)ERROR_PTR("depth not 2, 4 or 8 bpp", procName, NULL); - - if ((pix = pixCreate(width, height, depth)) == NULL) - return (PIX *)ERROR_PTR("pix not made", procName, NULL); - cmap = pixcmapCreate(depth); - pixSetColormap(pix, cmap); - if (initcolor == L_SET_BLACK) - pixcmapAddColor(cmap, 0, 0, 0); - else /* L_SET_WHITE */ - pixcmapAddColor(cmap, 255, 255, 255); - return pix; -} - - -/*! - * \brief pixCreateHeader() - * - * \param[in] width, height, depth - * \return pixd with no data allocated, or NULL on error - * - *- * Notes: - * (1) It is assumed that all 32 bit pix have 3 spp. If there is - * a valid alpha channel, this will be set to 4 spp later. - * (2) All pixCreate*() functions call pixCreateHeader(). - If the number of bytes to be allocated is larger than the - * maximum value in an int32, we can get overflow, resulting - * in a smaller amount of memory actually being allocated. - * Later, an attempt to access memory that wasn't allocated will - * cause a crash. So to avoid crashing a program (or worse) - * with bad (or malicious) input, we limit the requested - * allocation of image data in a typesafe way. - *- */ -PIX * -pixCreateHeader(l_int32 width, - l_int32 height, - l_int32 depth) -{ -l_int32 wpl; -l_uint64 wpl64, bignum; -PIX *pixd; - - PROCNAME("pixCreateHeader"); - - if ((depth != 1) && (depth != 2) && (depth != 4) && (depth != 8) - && (depth != 16) && (depth != 24) && (depth != 32)) - return (PIX *)ERROR_PTR("depth must be {1, 2, 4, 8, 16, 24, 32}", - procName, NULL); - if (width <= 0) - return (PIX *)ERROR_PTR("width must be > 0", procName, NULL); - if (height <= 0) - return (PIX *)ERROR_PTR("height must be > 0", procName, NULL); - - /* Avoid overflow in malloc, malicious or otherwise */ - wpl64 = ((l_uint64)width * (l_uint64)depth + 31) / 32; - if (wpl64 > ((1LL << 29) - 1)) { - L_ERROR("requested w = %d, h = %d, d = %d\n", - procName, width, height, depth); - return (PIX *)ERROR_PTR("wpl >= 2^29", procName, NULL); - } - wpl = (l_int32)wpl64; - bignum = 4LL * wpl * height; /* number of bytes to be requested */ - if (bignum > ((1LL << 31) - 1)) { - L_ERROR("requested w = %d, h = %d, d = %d\n", - procName, width, height, depth); - return (PIX *)ERROR_PTR("requested bytes >= 2^31", procName, NULL); - } - - pixd = (PIX *)LEPT_CALLOC(1, sizeof(PIX)); - pixSetWidth(pixd, width); - pixSetHeight(pixd, height); - pixSetDepth(pixd, depth); - pixSetWpl(pixd, wpl); - if (depth == 24 || depth == 32) - pixSetSpp(pixd, 3); - else - pixSetSpp(pixd, 1); - pixd->refcount = 1; - pixd->informat = IFF_UNKNOWN; - return pixd; -} - - -/*! - * \brief pixClone() - * - * \param[in] pixs - * \return same pix ptr, or NULL on error - * - *- * Notes: - * (1) A "clone" is simply a handle (ptr) to an existing pix. - * It is implemented because (a) images can be large and - * hence expensive to copy, and (b) extra handles to a data - * structure need to be made with a simple policy to avoid - * both double frees and memory leaks. Pix are reference - * counted. The side effect of pixClone() is an increase - * by 1 in the ref count. - * (2) The protocol to be used is: - * (a) Whenever you want a new handle to an existing image, - * call pixClone(), which just bumps a ref count. - * (b) Always call pixDestroy() on all handles. This - * decrements the ref count, nulls the handle, and - * only destroys the pix when pixDestroy() has been - * called on all handles. - *- */ -PIX * -pixClone(PIX *pixs) -{ - PROCNAME("pixClone"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixChangeRefcount(pixs, 1); - - return pixs; -} - - -/*--------------------------------------------------------------------* - * Pix Destruction * - *--------------------------------------------------------------------*/ -/*! - * \brief pixDestroy() - * - * \param[in,out] ppix will be set to null before returning - * \return void - * - *- * Notes: - * (1) Decrements the ref count and, if 0, destroys the pix. - * (2) Always nulls the input ptr. - *- */ -void -pixDestroy(PIX **ppix) -{ -PIX *pix; - - PROCNAME("pixDestroy"); - - if (!ppix) { - L_WARNING("ptr address is null!\n", procName); - return; - } - - if ((pix = *ppix) == NULL) - return; - pixFree(pix); - *ppix = NULL; - return; -} - - -/*! - * \brief pixFree() - * - * \param[in] pix - * \return void - * - *- * Notes: - * (1) Decrements the ref count and, if 0, destroys the pix. - *- */ -static void -pixFree(PIX *pix) -{ -l_uint32 *data; -char *text; - - if (!pix) return; - - pixChangeRefcount(pix, -1); - if (pixGetRefcount(pix) <= 0) { - if ((data = pixGetData(pix)) != NULL) - pix_free(data); - if ((text = pixGetText(pix)) != NULL) - LEPT_FREE(text); - pixDestroyColormap(pix); - LEPT_FREE(pix); - } - return; -} - - -/*-------------------------------------------------------------------------* - * Pix Copy * - *-------------------------------------------------------------------------*/ -/*! - * \brief pixCopy() - * - * \param[in] pixd [optional] can be null, equal to pixs, - * different from pixs - * \param[in] pixs - * \return pixd, or NULL on error - * - *- * Notes: - * (1) There are three cases: - * (a) pixd == null (makes a new pix; refcount = 1) - * (b) pixd == pixs (no-op) - * (c) pixd != pixs (data copy; no change in refcount) - * If the refcount of pixd > 1, case (c) will side-effect - * these handles. - * (2) The general pattern of use is: - * pixd = pixCopy(pixd, pixs); - * This will work for all three cases. - * For clarity when the case is known, you can use: - * (a) pixd = pixCopy(NULL, pixs); - * (c) pixCopy(pixd, pixs); - * (3) For case (c), we check if pixs and pixd are the same - * size (w,h,d). If so, the data is copied directly. - * Otherwise, the data is reallocated to the correct size - * and the copy proceeds. The refcount of pixd is unchanged. - * (4) This operation, like all others that may involve a pre-existing - * pixd, will side-effect any existing clones of pixd. - *- */ -PIX * -pixCopy(PIX *pixd, /* can be null */ - const PIX *pixs) -{ -l_int32 bytes; - - PROCNAME("pixCopy"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixs == pixd) - return pixd; - - /* Total bytes in image data */ - bytes = 4 * pixGetWpl(pixs) * pixGetHeight(pixs); - - /* If we're making a new pix ... */ - if (!pixd) { - if ((pixd = pixCreateTemplate(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - memcpy(pixd->data, pixs->data, bytes); - return pixd; - } - - /* Reallocate image data if sizes are different. If this fails, - * pixd hasn't been changed. But we want to signal that the copy - * failed, so return NULL. This will cause a memory leak if the - * return ptr is assigned to pixd, but that is preferred to proceeding - * with an incorrect pixd, and in any event this use case of - * pixCopy() -- reallocating into an existing pix -- is infrequent. */ - if (pixResizeImageData(pixd, pixs) == 1) - return (PIX *)ERROR_PTR("reallocation of data failed", procName, NULL); - - /* Copy non-image data fields */ - pixCopyColormap(pixd, pixs); - pixCopySpp(pixd, pixs); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - pixCopyText(pixd, pixs); - - /* Copy image data */ - memcpy(pixd->data, pixs->data, bytes); - return pixd; -} - - -/*! - * \brief pixResizeImageData() - * - * \param[in] pixd gets new uninitialized buffer for image data - * \param[in] pixs determines the size of the buffer; not changed - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If the sizes of data in pixs and pixd are unequal, this - * frees the existing image data in pixd and allocates - * an uninitialized buffer that will hold the required amount - * of image data in pixs. The image data from pixs is not - * copied into the new buffer. - * (2) On failure to allocate, pixd is unchanged. - *- */ -l_ok -pixResizeImageData(PIX *pixd, - const PIX *pixs) -{ -l_int32 w, h, d, wpl, bytes; -l_uint32 *data; - - PROCNAME("pixResizeImageData"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - - if (pixSizesEqual(pixs, pixd)) /* nothing to do */ - return 0; - - /* Make sure we can copy the data */ - pixGetDimensions(pixs, &w, &h, &d); - wpl = pixGetWpl(pixs); - bytes = 4 * wpl * h; - if ((data = (l_uint32 *)pix_malloc(bytes)) == NULL) - return ERROR_INT("pix_malloc fail for data", procName, 1); - - /* OK, do it */ - pixSetWidth(pixd, w); - pixSetHeight(pixd, h); - pixSetDepth(pixd, d); - pixSetWpl(pixd, wpl); - pixFreeData(pixd); /* free any existing image data */ - pixSetData(pixd, data); /* set the uninitialized memory buffer */ - pixCopyResolution(pixd, pixs); - return 0; -} - - -/*! - * \brief pixCopyColormap() - * - * \param[in] pixd - * \param[in] pixs copies the colormap to %pixd - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This destroys the colormap in pixd, unless the operation is a no-op - *- */ -l_ok -pixCopyColormap(PIX *pixd, - const PIX *pixs) -{ -l_int32 valid; -const PIXCMAP *cmaps; -PIXCMAP *cmapd; - - PROCNAME("pixCopyColormap"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - if (pixs == pixd) - return 0; /* no-op */ - - pixDestroyColormap(pixd); - if ((cmaps = pixs->colormap) == NULL) /* not an error */ - return 0; - pixcmapIsValid(cmaps, &valid); - if (!valid) - return ERROR_INT("cmap not valid", procName, 1); - - if ((cmapd = pixcmapCopy(cmaps)) == NULL) - return ERROR_INT("cmapd not made", procName, 1); - pixSetColormap(pixd, cmapd); - return 0; -} - - -/*! - * \brief pixSizesEqual() - * - * \param[in] pix1, pix2 - * \return 1 if the two pix have same {h, w, d}; 0 otherwise. - */ -l_int32 -pixSizesEqual(const PIX *pix1, - const PIX *pix2) -{ - PROCNAME("pixSizesEqual"); - - if (!pix1 || !pix2) - return ERROR_INT("pix1 and pix2 not both defined", procName, 0); - - if (pix1 == pix2) - return 1; - - if ((pixGetWidth(pix1) != pixGetWidth(pix2)) || - (pixGetHeight(pix1) != pixGetHeight(pix2)) || - (pixGetDepth(pix1) != pixGetDepth(pix2))) - return 0; - else - return 1; -} - - -/*! - * \brief pixTransferAllData() - * - * \param[in] pixd must be different from pixs - * \param[in,out] ppixs will be nulled if refcount goes to 0 - * \param[in] copytext 1 to copy the text field; 0 to skip - * \param[in] copyformat 1 to copy the informat field; 0 to skip - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This does a complete data transfer from pixs to pixd, - * followed by the destruction of pixs (refcount permitting). - * (2) If the refcount of pixs is 1, pixs is destroyed. Otherwise, - * the data in pixs is copied (rather than transferred) to pixd. - * (3) This operation, like all others with a pre-existing pixd, - * will side-effect any existing clones of pixd. The pixd - * refcount does not change. - * (4) When might you use this? Suppose you have an in-place Pix - * function (returning void) with the typical signature: - * void function-inplace(PIX *pix, ...) - * where "..." are non-pointer input parameters, and suppose - * further that you sometimes want to return an arbitrary Pix - * in place of the input Pix. There are two ways you can do this: - * (a) The straightforward way is to change the function - * signature to take the address of the Pix ptr: - * \code - * void function-inplace(PIX **ppix, ...) { - * PIX *pixt = function-makenew(*ppix); - * pixDestroy(ppix); - * *ppix = pixt; - * return; - * } - * \endcode - * Here, the input and returned pix are different, as viewed - * by the calling function, and the inplace function is - * expected to destroy the input pix to avoid a memory leak. - * (b) Keep the signature the same and use pixTransferAllData() - * to return the new Pix in the input Pix struct: - * \code - * void function-inplace(PIX *pix, ...) { - * PIX *pixt = function-makenew(pix); - * pixTransferAllData(pix, &pixt, 0, 0); - * // pixDestroy() is called on pixt - * return; - * } - * \endcode - * Here, the input and returned pix are the same, as viewed - * by the calling function, and the inplace function must - * never destroy the input pix, because the calling function - * maintains an unchanged handle to it. - *- */ -l_ok -pixTransferAllData(PIX *pixd, - PIX **ppixs, - l_int32 copytext, - l_int32 copyformat) -{ -l_int32 nbytes; -PIX *pixs; - - PROCNAME("pixTransferAllData"); - - if (!ppixs) - return ERROR_INT("&pixs not defined", procName, 1); - if ((pixs = *ppixs) == NULL) - return ERROR_INT("pixs not defined", procName, 1); - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - if (pixs == pixd) /* no-op */ - return ERROR_INT("pixd == pixs", procName, 1); - - if (pixGetRefcount(pixs) == 1) { /* transfer the data, cmap, text */ - pixFreeData(pixd); /* dealloc any existing data */ - pixSetData(pixd, pixGetData(pixs)); /* transfer new data from pixs */ - pixs->data = NULL; /* pixs no longer owns data */ - pixSetColormap(pixd, pixGetColormap(pixs)); /* frees old; sets new */ - pixs->colormap = NULL; /* pixs no longer owns colormap */ - if (copytext) { - pixSetText(pixd, pixGetText(pixs)); - pixSetText(pixs, NULL); - } - } else { /* preserve pixs by making a copy of the data, cmap, text */ - pixResizeImageData(pixd, pixs); - nbytes = 4 * pixGetWpl(pixs) * pixGetHeight(pixs); - memcpy(pixGetData(pixd), pixGetData(pixs), nbytes); - pixCopyColormap(pixd, pixs); - if (copytext) - pixCopyText(pixd, pixs); - } - - pixCopySpp(pixd, pixs); - pixCopyResolution(pixd, pixs); - pixCopyDimensions(pixd, pixs); - if (copyformat) - pixCopyInputFormat(pixd, pixs); - - /* This will destroy pixs if data was transferred; - * otherwise, it just decrements its refcount. */ - pixDestroy(ppixs); - return 0; -} - - -/*! - * \brief pixSwapAndDestroy() - * - * \param[out] ppixd [optional] input pixd can be null, - * and it must be different from pixs - * \param[in,out] ppixs will be nulled after the swap - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Simple operation to change the handle name safely. - * After this operation, the original image in pixd has - * been destroyed, pixd points to what was pixs, and - * the input pixs ptr has been nulled. - * (2) This works safely whether or not pixs and pixd are cloned. - * If pixs is cloned, the other handles still point to - * the original image, with the ref count reduced by 1. - * (3) Usage example: - * \code - * Pix *pix1 = pixRead("..."); - * Pix *pix2 = function(pix1, ...); - * pixSwapAndDestroy(&pix1, &pix2); - * pixDestroy(&pix1); // holds what was in pix2 - * \endcode - * Example with clones ([] shows ref count of image generated - * by the function): - * \code - * Pix *pixs = pixRead("..."); - * Pix *pix1 = pixClone(pixs); - * Pix *pix2 = function(pix1, ...); [1] - * Pix *pix3 = pixClone(pix2); [1] --> [2] - * pixSwapAndDestroy(&pix1, &pix2); - * pixDestroy(&pixs); // still holds read image - * pixDestroy(&pix1); // holds what was in pix2 [2] --> [1] - * pixDestroy(&pix3); // holds what was in pix2 [1] --> [0] - * \endcode - *- */ -l_ok -pixSwapAndDestroy(PIX **ppixd, - PIX **ppixs) -{ - PROCNAME("pixSwapAndDestroy"); - - if (!ppixd) - return ERROR_INT("&pixd not defined", procName, 1); - if (!ppixs) - return ERROR_INT("&pixs not defined", procName, 1); - if (*ppixs == NULL) - return ERROR_INT("pixs not defined", procName, 1); - if (ppixs == ppixd) /* no-op */ - return ERROR_INT("&pixd == &pixs", procName, 1); - - pixDestroy(ppixd); - *ppixd = pixClone(*ppixs); - pixDestroy(ppixs); - return 0; -} - - -/*--------------------------------------------------------------------* - * Accessors * - *--------------------------------------------------------------------*/ -l_int32 -pixGetWidth(const PIX *pix) -{ - PROCNAME("pixGetWidth"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 0); - - return pix->w; -} - - -l_int32 -pixSetWidth(PIX *pix, - l_int32 width) -{ - PROCNAME("pixSetWidth"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (width < 0) { - pix->w = 0; - return ERROR_INT("width must be >= 0", procName, 1); - } - - pix->w = width; - return 0; -} - - -l_int32 -pixGetHeight(const PIX *pix) -{ - PROCNAME("pixGetHeight"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 0); - - return pix->h; -} - - -l_int32 -pixSetHeight(PIX *pix, - l_int32 height) -{ - PROCNAME("pixSetHeight"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (height < 0) { - pix->h = 0; - return ERROR_INT("h must be >= 0", procName, 1); - } - - pix->h = height; - return 0; -} - - -l_int32 -pixGetDepth(const PIX *pix) -{ - PROCNAME("pixGetDepth"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 0); - - return pix->d; -} - - -l_int32 -pixSetDepth(PIX *pix, - l_int32 depth) -{ - PROCNAME("pixSetDepth"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (depth < 1) - return ERROR_INT("d must be >= 1", procName, 1); - - pix->d = depth; - return 0; -} - - -/*! - * \brief pixGetDimensions() - * - * \param[in] pix - * \param[out] pw, ph, pd [optional] each can be null - * \return 0 if OK, 1 on error - */ -l_ok -pixGetDimensions(const PIX *pix, - l_int32 *pw, - l_int32 *ph, - l_int32 *pd) -{ - PROCNAME("pixGetDimensions"); - - if (pw) *pw = 0; - if (ph) *ph = 0; - if (pd) *pd = 0; - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (pw) *pw = pix->w; - if (ph) *ph = pix->h; - if (pd) *pd = pix->d; - return 0; -} - - -/*! - * \brief pixSetDimensions() - * - * \param[in] pix - * \param[in] w, h, d use 0 to skip the setting for any of these - * \return 0 if OK, 1 on error - */ -l_ok -pixSetDimensions(PIX *pix, - l_int32 w, - l_int32 h, - l_int32 d) -{ - PROCNAME("pixSetDimensions"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (w > 0) pixSetWidth(pix, w); - if (h > 0) pixSetHeight(pix, h); - if (d > 0) pixSetDepth(pix, d); - return 0; -} - - -/*! - * \brief pixCopyDimensions() - * - * \param[in] pixd - * \param[in] pixs - * \return 0 if OK, 1 on error - */ -l_ok -pixCopyDimensions(PIX *pixd, - const PIX *pixs) -{ - PROCNAME("pixCopyDimensions"); - - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixs == pixd) - return 0; /* no-op */ - - pixSetWidth(pixd, pixGetWidth(pixs)); - pixSetHeight(pixd, pixGetHeight(pixs)); - pixSetDepth(pixd, pixGetDepth(pixs)); - pixSetWpl(pixd, pixGetWpl(pixs)); - return 0; -} - - -l_int32 -pixGetSpp(const PIX *pix) -{ - PROCNAME("pixGetSpp"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 0); - - return pix->spp; -} - - -/* - * \brief pixSetSpp() - * - * \param[in] pix - * \param[in] spp 1, 3 or 4 samples - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) For a 32 bpp pix, this can be used to ignore the - * alpha sample (spp == 3) or to use it (spp == 4). - * For example, to write a spp == 4 image without the alpha - * sample (as an rgb pix), call pixSetSpp(pix, 3) and - * then write it out as a png. - *- */ -l_int32 -pixSetSpp(PIX *pix, - l_int32 spp) -{ - PROCNAME("pixSetSpp"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (spp < 1) - return ERROR_INT("spp must be >= 1", procName, 1); - - pix->spp = spp; - return 0; -} - - -/*! - * \brief pixCopySpp() - * - * \param[in] pixd - * \param[in] pixs - * \return 0 if OK, 1 on error - */ -l_ok -pixCopySpp(PIX *pixd, - const PIX *pixs) -{ - PROCNAME("pixCopySpp"); - - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixs == pixd) - return 0; /* no-op */ - - pixSetSpp(pixd, pixGetSpp(pixs)); - return 0; -} - - -l_int32 -pixGetWpl(const PIX *pix) -{ - PROCNAME("pixGetWpl"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 0); - return pix->wpl; -} - - -l_int32 -pixSetWpl(PIX *pix, - l_int32 wpl) -{ - PROCNAME("pixSetWpl"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - pix->wpl = wpl; - return 0; -} - - -l_int32 -pixGetRefcount(const PIX *pix) -{ - PROCNAME("pixGetRefcount"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 0); - return pix->refcount; -} - - -l_int32 -pixChangeRefcount(PIX *pix, - l_int32 delta) -{ - PROCNAME("pixChangeRefcount"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - pix->refcount += delta; - return 0; -} - - -l_int32 -pixGetXRes(const PIX *pix) -{ - PROCNAME("pixGetXRes"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 0); - return pix->xres; -} - - -l_int32 -pixSetXRes(PIX *pix, - l_int32 res) -{ - PROCNAME("pixSetXRes"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - pix->xres = res; - return 0; -} - - -l_int32 -pixGetYRes(const PIX *pix) -{ - PROCNAME("pixGetYRes"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 0); - return pix->yres; -} - - -l_int32 -pixSetYRes(PIX *pix, - l_int32 res) -{ - PROCNAME("pixSetYRes"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - pix->yres = res; - return 0; -} - - -/*! - * \brief pixGetResolution() - * - * \param[in] pix - * \param[out] pxres, pyres [optional] each can be null - * \return 0 if OK, 1 on error - */ -l_ok -pixGetResolution(const PIX *pix, - l_int32 *pxres, - l_int32 *pyres) -{ - PROCNAME("pixGetResolution"); - - if (pxres) *pxres = 0; - if (pyres) *pyres = 0; - if (!pxres && !pyres) - return ERROR_INT("no output requested", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (pxres) *pxres = pix->xres; - if (pyres) *pyres = pix->yres; - return 0; -} - - -/*! - * \brief pixSetResolution() - * - * \param[in] pix - * \param[in] xres, yres use 0 to skip setting a value for either of these - * \return 0 if OK, 1 on error - */ -l_ok -pixSetResolution(PIX *pix, - l_int32 xres, - l_int32 yres) -{ - PROCNAME("pixSetResolution"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (xres > 0) pix->xres = xres; - if (yres > 0) pix->yres = yres; - return 0; -} - - -l_int32 -pixCopyResolution(PIX *pixd, - const PIX *pixs) -{ - PROCNAME("pixCopyResolution"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - if (pixs == pixd) - return 0; /* no-op */ - - pixSetXRes(pixd, pixGetXRes(pixs)); - pixSetYRes(pixd, pixGetYRes(pixs)); - return 0; -} - - -l_int32 -pixScaleResolution(PIX *pix, - l_float32 xscale, - l_float32 yscale) -{ -l_float64 xres, yres; -l_float64 maxres = 100000000.0; - - PROCNAME("pixScaleResolution"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (xscale <= 0 || yscale <= 0) - return ERROR_INT("invalid scaling ratio", procName, 1); - - xres = (l_float64)xscale * (l_float32)(pix->xres) + 0.5; - yres = (l_float64)yscale * (l_float32)(pix->yres) + 0.5; - pix->xres = (l_uint32)L_MIN(xres, maxres); - pix->yres = (l_uint32)L_MIN(yres, maxres); - return 0; -} - - -l_int32 -pixGetInputFormat(const PIX *pix) -{ - PROCNAME("pixGetInputFormat"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 0); - return pix->informat; -} - - -l_int32 -pixSetInputFormat(PIX *pix, - l_int32 informat) -{ - PROCNAME("pixSetInputFormat"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - pix->informat = informat; - return 0; -} - - -l_int32 -pixCopyInputFormat(PIX *pixd, - const PIX *pixs) -{ - PROCNAME("pixCopyInputFormat"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - if (pixs == pixd) - return 0; /* no-op */ - - pixSetInputFormat(pixd, pixGetInputFormat(pixs)); - return 0; -} - - -l_int32 -pixSetSpecial(PIX *pix, - l_int32 special) -{ - PROCNAME("pixSetSpecial"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - pix->special = special; - return 0; -} - - -/*! - * \brief pixGetText() - * - * \param[in] pix - * \return ptr to existing text string - * - *- * Notes: - * (1) The text string belongs to the pix: - * * the caller must NOT free it - * * it must not be used after the pix is destroyed - *- */ -char * -pixGetText(PIX *pix) -{ - PROCNAME("pixGetText"); - - if (!pix) - return (char *)ERROR_PTR("pix not defined", procName, NULL); - return pix->text; -} - - -/*! - * \brief pixSetText() - * - * \param[in] pix - * \param[in] textstring can be null - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This removes any existing textstring and puts a copy of - * the input textstring there. - *- */ -l_ok -pixSetText(PIX *pix, - const char *textstring) -{ - PROCNAME("pixSetText"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - stringReplace(&pix->text, textstring); - return 0; -} - - -/*! - * \brief pixAddText() - * - * \param[in] pix - * \param[in] textstring can be null - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This adds the new textstring to any existing text. - * (2) Either or both the existing text and the new text - * string can be null. - *- */ -l_ok -pixAddText(PIX *pix, - const char *textstring) -{ -char *newstring; - - PROCNAME("pixAddText"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - newstring = stringJoin(pixGetText(pix), textstring); - stringReplace(&pix->text, newstring); - LEPT_FREE(newstring); - return 0; -} - - -l_int32 -pixCopyText(PIX *pixd, - const PIX *pixs) -{ - PROCNAME("pixCopyText"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - if (pixs == pixd) - return 0; /* no-op */ - - pixSetText(pixd, pixs->text); - return 0; -} - - -PIXCMAP * -pixGetColormap(PIX *pix) -{ - PROCNAME("pixGetColormap"); - - if (!pix) - return (PIXCMAP *)ERROR_PTR("pix not defined", procName, NULL); - return pix->colormap; -} - - -/*! - * \brief pixSetColormap() - * - * \param[in] pix - * \param[in] colormap to be assigned - * \return 0 if OK, 1 on error. - * - *- * Notes: - * (1) Unlike with the pix data field, pixSetColormap() destroys - * any existing colormap before assigning the new one. - * Because colormaps are not ref counted, it is important that - * the new colormap does not belong to any other pix. - *- */ -l_ok -pixSetColormap(PIX *pix, - PIXCMAP *colormap) -{ - PROCNAME("pixSetColormap"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - pixDestroyColormap(pix); - pix->colormap = colormap; - return 0; -} - - -/*! - * \brief pixDestroyColormap() - * - * \param[in] pix - * \return 0 if OK, 1 on error - */ -l_ok -pixDestroyColormap(PIX *pix) -{ -PIXCMAP *cmap; - - PROCNAME("pixDestroyColormap"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - if ((cmap = pix->colormap) != NULL) { - pixcmapDestroy(&cmap); - pix->colormap = NULL; - } - return 0; -} - - -/*! - * \brief pixGetData() - * - * \param[in] pix - * \return ptr to image data - * - *- * Notes: - * (1) This gives a new handle for the data. The data is still - * owned by the pix, so do not call LEPT_FREE() on it. - *- */ -l_uint32 * -pixGetData(PIX *pix) -{ - PROCNAME("pixGetData"); - - if (!pix) - return (l_uint32 *)ERROR_PTR("pix not defined", procName, NULL); - return pix->data; -} - - -/*! - * \brief pixSetData() - * - * \param[in] pix - * \param[in] data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This does not free any existing data. To free existing - * data, use pixFreeData() before pixSetData(). - *- */ -l_int32 -pixSetData(PIX *pix, - l_uint32 *data) -{ - PROCNAME("pixSetData"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - pix->data = data; - return 0; -} - - -/*! - * \brief pixExtractData() - * - * \param[in] pix - * \return ptr to data, or null on error - * - *- * Notes: - * (1) This extracts the pix image data for use in another context. - * The caller still needs to use pixDestroy() on the input pix. - * (2) If refcount == 1, the data is extracted and the - * pix->data ptr is set to NULL. - * (3) If refcount > 1, this simply returns a copy of the data, - * using the pix allocator, and leaving the input pix unchanged. - *- */ -l_uint32 * -pixExtractData(PIX *pixs) -{ -l_int32 count, bytes; -l_uint32 *data, *datas; - - PROCNAME("pixExtractData"); - - if (!pixs) - return (l_uint32 *)ERROR_PTR("pixs not defined", procName, NULL); - - count = pixGetRefcount(pixs); - if (count == 1) { /* extract */ - data = pixGetData(pixs); - pixSetData(pixs, NULL); - } else { /* refcount > 1; copy */ - bytes = 4 * pixGetWpl(pixs) * pixGetHeight(pixs); - datas = pixGetData(pixs); - if ((data = (l_uint32 *)pix_malloc(bytes)) == NULL) - return (l_uint32 *)ERROR_PTR("data not made", procName, NULL); - memcpy(data, datas, bytes); - } - - return data; -} - - -/*! - * \brief pixFreeData() - * - * \param[in] pix - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This frees the data and sets the pix data ptr to null. - * It should be used before pixSetData() in the situation where - * you want to free any existing data before doing - * a subsequent assignment with pixSetData(). - *- */ -l_int32 -pixFreeData(PIX *pix) -{ -l_uint32 *data; - - PROCNAME("pixFreeData"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - if ((data = pixGetData(pix)) != NULL) { - pix_free(data); - pix->data = NULL; - } - return 0; -} - - -/*--------------------------------------------------------------------* - * Pix line ptrs * - *--------------------------------------------------------------------*/ -/*! - * \brief pixGetLinePtrs() - * - * \param[in] pix - * \param[out] psize [optional] array size, which is the pix height - * \return array of line ptrs, or NULL on error - * - *- * Notes: - * (1) This is intended to be used for fast random pixel access. - * For example, for an 8 bpp image, - * val = GET_DATA_BYTE(lines8[i], j); - * is equivalent to, but much faster than, - * pixGetPixel(pix, j, i, &val); - * (2) How much faster? For 1 bpp, it's from 6 to 10x faster. - * For 8 bpp, it's an amazing 30x faster. So if you are - * doing random access over a substantial part of the image, - * use this line ptr array. - * (3) When random access is used in conjunction with a stack, - * queue or heap, the overall computation time depends on - * the operations performed on each struct that is popped - * or pushed, and whether we are using a priority queue (O(logn)) - * or a queue or stack (O(1)). For example, for maze search, - * the overall ratio of time for line ptrs vs. pixGet/Set* is - * Maze type Type Time ratio - * binary queue 0.4 - * gray heap (priority queue) 0.6 - * (4) Because this returns a void** and the accessors take void*, - * the compiler cannot check the pointer types. It is - * strongly recommended that you adopt a naming scheme for - * the returned ptr arrays that indicates the pixel depth. - * (This follows the original intent of Simonyi's "Hungarian" - * application notation, where naming is used proactively - * to make errors visibly obvious.) By doing this, you can - * tell by inspection if the correct accessor is used. - * For example, for an 8 bpp pixg: - * void **lineg8 = pixGetLinePtrs(pixg, NULL); - * val = GET_DATA_BYTE(lineg8[i], j); // fast access; BYTE, 8 - * ... - * LEPT_FREE(lineg8); // don't forget this - * (5) These are convenient for accessing bytes sequentially in an - * 8 bpp grayscale image. People who write image processing code - * on 8 bpp images are accustomed to grabbing pixels directly out - * of the raster array. Note that for little endians, you first - * need to reverse the byte order in each 32-bit word. - * Here's a typical usage pattern: - * pixEndianByteSwap(pix); // always safe; no-op on big-endians - * l_uint8 **lineptrs = (l_uint8 **)pixGetLinePtrs(pix, NULL); - * pixGetDimensions(pix, &w, &h, NULL); - * for (i = 0; i < h; i++) { - * l_uint8 *line = lineptrs[i]; - * for (j = 0; j < w; j++) { - * val = line[j]; - * ... - * } - * } - * pixEndianByteSwap(pix); // restore big-endian order - * LEPT_FREE(lineptrs); - * This can be done even more simply as follows: - * l_uint8 **lineptrs = pixSetupByteProcessing(pix, &w, &h); - * for (i = 0; i < h; i++) { - * l_uint8 *line = lineptrs[i]; - * for (j = 0; j < w; j++) { - * val = line[j]; - * ... - * } - * } - * pixCleanupByteProcessing(pix, lineptrs); - *- */ -void ** -pixGetLinePtrs(PIX *pix, - l_int32 *psize) -{ -l_int32 i, h, wpl; -l_uint32 *data; -void **lines; - - PROCNAME("pixGetLinePtrs"); - - if (psize) *psize = 0; - if (!pix) - return (void **)ERROR_PTR("pix not defined", procName, NULL); - - h = pixGetHeight(pix); - if (psize) *psize = h; - if ((lines = (void **)LEPT_CALLOC(h, sizeof(void *))) == NULL) - return (void **)ERROR_PTR("lines not made", procName, NULL); - wpl = pixGetWpl(pix); - data = pixGetData(pix); - for (i = 0; i < h; i++) - lines[i] = (void *)(data + i * wpl); - - return lines; -} - - -/*--------------------------------------------------------------------* - * Print output for debugging * - *--------------------------------------------------------------------*/ -extern const char *ImageFileFormatExtensions[]; - -/*! - * \brief pixPrintStreamInfo() - * - * \param[in] fp file stream - * \param[in] pix - * \param[in] text [optional] identifying string; can be null - * \return 0 if OK, 1 on error - */ -l_ok -pixPrintStreamInfo(FILE *fp, - const PIX *pix, - const char *text) -{ -l_int32 informat; -const PIXCMAP *cmap; - - PROCNAME("pixPrintStreamInfo"); - - if (!fp) - return ERROR_INT("fp not defined", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - if (text) - fprintf(fp, " Pix Info for %s:\n", text); - fprintf(fp, " width = %d, height = %d, depth = %d, spp = %d\n", - pixGetWidth(pix), pixGetHeight(pix), pixGetDepth(pix), - pixGetSpp(pix)); - fprintf(fp, " wpl = %d, data = %p, refcount = %d\n", - pixGetWpl(pix), pix->data, pixGetRefcount(pix)); - fprintf(fp, " xres = %d, yres = %d\n", pixGetXRes(pix), pixGetYRes(pix)); - if ((cmap = pix->colormap) != NULL) - pixcmapWriteStream(fp, cmap); - else - fprintf(fp, " no colormap\n"); - informat = pixGetInputFormat(pix); - fprintf(fp, " input format: %d (%s)\n", informat, - ImageFileFormatExtensions[informat]); - if (pix->text != NULL) - fprintf(fp, " text: %s\n", pix->text); - - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix2.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix2.c deleted file mode 100644 index ce2f39ce..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix2.c +++ /dev/null @@ -1,3506 +0,0 @@ -/*====================================================================* - - 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 pix2.c - *- * - * This file has these basic operations: - * - * (1) Get and set: individual pixels, full image, rectangular region, - * pad pixels, border pixels, and color components for RGB - * (2) Add and remove border pixels - * (3) Endian byte swaps - * (4) Simple method for byte-processing images (instead of words) - * - * Pixel poking - * l_int32 pixGetPixel() - * l_int32 pixSetPixel() - * l_int32 pixGetRGBPixel() - * l_int32 pixSetRGBPixel() - * l_int32 pixSetCmapPixel() - * l_int32 pixGetRandomPixel() - * l_int32 pixClearPixel() - * l_int32 pixFlipPixel() - * void setPixelLow() - * - * Find black or white value - * l_int32 pixGetBlackOrWhiteVal() - * - * Full image clear/set/set-to-arbitrary-value - * l_int32 pixClearAll() - * l_int32 pixSetAll() - * l_int32 pixSetAllGray() - * l_int32 pixSetAllArbitrary() - * l_int32 pixSetBlackOrWhite() - * l_int32 pixSetComponentArbitrary() - * - * Rectangular region clear/set/set-to-arbitrary-value/blend - * l_int32 pixClearInRect() - * l_int32 pixSetInRect() - * l_int32 pixSetInRectArbitrary() - * l_int32 pixBlendInRect() - * - * Set pad bits - * l_int32 pixSetPadBits() - * l_int32 pixSetPadBitsBand() - * - * Assign border pixels - * l_int32 pixSetOrClearBorder() - * l_int32 pixSetBorderVal() - * l_int32 pixSetBorderRingVal() - * l_int32 pixSetMirroredBorder() - * PIX *pixCopyBorder() - * - * Add and remove border - * PIX *pixAddBorder() - * PIX *pixAddBlackOrWhiteBorder() - * PIX *pixAddBorderGeneral() - * PIX *pixRemoveBorder() - * PIX *pixRemoveBorderGeneral() - * PIX *pixRemoveBorderToSize() - * PIX *pixAddMirroredBorder() - * PIX *pixAddRepeatedBorder() - * PIX *pixAddMixedBorder() - * PIX *pixAddContinuedBorder() - * - * Helper functions using alpha - * l_int32 pixShiftAndTransferAlpha() - * PIX *pixDisplayLayersRGBA() - * - * Color sample setting and extraction - * PIX *pixCreateRGBImage() - * PIX *pixGetRGBComponent() - * l_int32 pixSetRGBComponent() - * PIX *pixGetRGBComponentCmap() - * l_int32 pixCopyRGBComponent() - * l_int32 composeRGBPixel() - * l_int32 composeRGBAPixel() - * void extractRGBValues() - * void extractRGBAValues() - * l_int32 extractMinMaxComponent() - * l_int32 pixGetRGBLine() - * - * Raster line pixel setter - * l_int32 setLineDataVal() - * - * Conversion between big and little endians - * PIX *pixEndianByteSwapNew() - * l_int32 pixEndianByteSwap() - * l_int32 lineEndianByteSwap() - * PIX *pixEndianTwoByteSwapNew() - * l_int32 pixEndianTwoByteSwap() - * - * Extract raster data as binary string - * l_int32 pixGetRasterData() - * - * Test alpha component opaqueness - * l_int32 pixAlphaIsOpaque - * - * Setup helpers for 8 bpp byte processing - * l_uint8 **pixSetupByteProcessing() - * l_int32 pixCleanupByteProcessing() - * - * Setting parameters for antialias masking with alpha transforms - * void l_setAlphaMaskBorder() - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include "allheaders.h" - -static const l_uint32 rmask32[] = {0x0, - 0x00000001, 0x00000003, 0x00000007, 0x0000000f, - 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, - 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, - 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, - 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, - 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, - 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff, - 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff}; - - /* This is a global that determines the default 8 bpp alpha mask values - * for rings at distance 1 and 2 from the border. Declare extern - * to use. To change the values, use l_setAlphaMaskBorder(). */ -LEPT_DLL l_float32 AlphaMaskBorderVals[2] = {0.0, 0.5}; - - -#ifndef NO_CONSOLE_IO -#define DEBUG_SERIALIZE 0 -#endif /* ~NO_CONSOLE_IO */ - - -/*-------------------------------------------------------------* - * Pixel poking * - *-------------------------------------------------------------*/ -/*! - * \brief pixGetPixel() - * - * \param[in] pix - * \param[in] x,y pixel coords - * \param[out] pval pixel value - * \return 0 if OK; 1 or 2 on error - * - * - * Notes: - * (1) This returns the value in the data array. If the pix is - * colormapped, it returns the colormap index, not the rgb value. - * (2) Because of the function overhead and the parameter checking, - * this is much slower than using the GET_DATA_*() macros directly. - * Speed on a 1 Mpixel RGB image, using a 3 GHz machine: - * * pixGet/pixSet: ~25 Mpix/sec - * * GET_DATA/SET_DATA: ~350 MPix/sec - * If speed is important and you're doing random access into - * the pix, use pixGetLinePtrs() and the array access macros. - * (3) If the point is outside the image, this returns an error (2), - * with 0 in %pval. To avoid spamming output, it fails silently. - *- */ -l_ok -pixGetPixel(PIX *pix, - l_int32 x, - l_int32 y, - l_uint32 *pval) -{ -l_int32 w, h, d, wpl, val; -l_uint32 *line, *data; - - PROCNAME("pixGetPixel"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0; - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - pixGetDimensions(pix, &w, &h, &d); - if (x < 0 || x >= w || y < 0 || y >= h) - return 2; - - wpl = pixGetWpl(pix); - data = pixGetData(pix); - line = data + y * wpl; - switch (d) - { - case 1: - val = GET_DATA_BIT(line, x); - break; - case 2: - val = GET_DATA_DIBIT(line, x); - break; - case 4: - val = GET_DATA_QBIT(line, x); - break; - case 8: - val = GET_DATA_BYTE(line, x); - break; - case 16: - val = GET_DATA_TWO_BYTES(line, x); - break; - case 32: - val = line[x]; - break; - default: - return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", procName, 1); - } - - *pval = val; - return 0; -} - - -/*! - * \brief pixSetPixel() - * - * \param[in] pix - * \param[in] x,y pixel coords - * \param[in] val value to be inserted - * \return 0 if OK; 1 or 2 on error - * - *- * Notes: - * (1) Warning: the input value is not checked for overflow with respect - * the the depth of %pix, and the sign bit (if any) is ignored. - * * For d == 1, %val > 0 sets the bit on. - * * For d == 2, 4, 8 and 16, %val is masked to the maximum allowable - * pixel value, and any (invalid) higher order bits are discarded. - * (2) See pixGetPixel() for information on performance. - * (3) If the point is outside the image, this returns an error (2), - * with 0 in %pval. To avoid spamming output, it fails silently. - *- */ -l_ok -pixSetPixel(PIX *pix, - l_int32 x, - l_int32 y, - l_uint32 val) -{ -l_int32 w, h, d, wpl; -l_uint32 *line, *data; - - PROCNAME("pixSetPixel"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - pixGetDimensions(pix, &w, &h, &d); - if (x < 0 || x >= w || y < 0 || y >= h) - return 2; - - data = pixGetData(pix); - wpl = pixGetWpl(pix); - line = data + y * wpl; - switch (d) - { - case 1: - if (val) - SET_DATA_BIT(line, x); - else - CLEAR_DATA_BIT(line, x); - break; - case 2: - SET_DATA_DIBIT(line, x, val); - break; - case 4: - SET_DATA_QBIT(line, x, val); - break; - case 8: - SET_DATA_BYTE(line, x, val); - break; - case 16: - SET_DATA_TWO_BYTES(line, x, val); - break; - case 32: - line[x] = val; - break; - default: - return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", procName, 1); - } - - return 0; -} - - -/*! - * \brief pixGetRGBPixel() - * - * \param[in] pix 32 bpp rgb, not colormapped - * \param[in] x,y pixel coords - * \param[out] prval [optional] red component - * \param[out] pgval [optional] green component - * \param[out] pbval [optional] blue component - * \return 0 if OK; 1 or 2 on error - * - * Notes: - * (1) If the point is outside the image, this returns an error (2), - * with 0 in %pval. To avoid spamming output, it fails silently. - */ -l_ok -pixGetRGBPixel(PIX *pix, - l_int32 x, - l_int32 y, - l_int32 *prval, - l_int32 *pgval, - l_int32 *pbval) -{ -l_int32 w, h, d, wpl; -l_uint32 *data, *ppixel; - - PROCNAME("pixGetRGBPixel"); - - if (prval) *prval = 0; - if (pgval) *pgval = 0; - if (pbval) *pbval = 0; - if (!prval && !pgval && !pbval) - return ERROR_INT("no output requested", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - pixGetDimensions(pix, &w, &h, &d); - if (d != 32) - return ERROR_INT("pix not 32 bpp", procName, 1); - if (x < 0 || x >= w || y < 0 || y >= h) - return 2; - - wpl = pixGetWpl(pix); - data = pixGetData(pix); - ppixel = data + y * wpl + x; - if (prval) *prval = GET_DATA_BYTE(ppixel, COLOR_RED); - if (pgval) *pgval = GET_DATA_BYTE(ppixel, COLOR_GREEN); - if (pbval) *pbval = GET_DATA_BYTE(ppixel, COLOR_BLUE); - return 0; -} - - -/*! - * \brief pixSetRGBPixel() - * - * \param[in] pix 32 bpp rgb - * \param[in] x,y pixel coords - * \param[in] rval red component - * \param[in] gval green component - * \param[in] bval blue component - * \return 0 if OK; 1 or 2 on error - * - * Notes: - * (1) If the point is outside the image, this returns an error (2), - * and to avoid spamming output, it fails silently. - */ -l_ok -pixSetRGBPixel(PIX *pix, - l_int32 x, - l_int32 y, - l_int32 rval, - l_int32 gval, - l_int32 bval) -{ -l_int32 w, h, d, wpl; -l_uint32 pixel; -l_uint32 *data, *line; - - PROCNAME("pixSetRGBPixel"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - pixGetDimensions(pix, &w, &h, &d); - if (d != 32) - return ERROR_INT("pix not 32 bpp", procName, 1); - if (x < 0 || x >= w || y < 0 || y >= h) - return 2; - - wpl = pixGetWpl(pix); - data = pixGetData(pix); - line = data + y * wpl; - composeRGBPixel(rval, gval, bval, &pixel); - *(line + x) = pixel; - return 0; -} - - -/*! - * \brief pixSetCmapPixel() - * - * \param[in] pix 2, 4 or 8 bpp, colormapped - * \param[in] x,y pixel coords - * \param[in] rval red component - * \param[in] gval green component - * \param[in] bval blue component - * \return 0 if OK; 1 or 2 on error - * - * Notes: - * (1) If the point is outside the image, this returns an error (2), - * and to avoid spamming output, it fails silently. - * (2) - If the color already exists, use it. - * - If the color does not exist in the colormap, it is added - * if possible. - * - If there is not room in the colormap for the new color: - * * if d < 8, return 2 with a warning. - * * if d == 8, find and use the nearest color. - * (3) Note that this operation scales with the number of colors - * in the colormap, and therefore can be very expensive if an - * attempt is made to set many pixels. (In that case, it should - * be implemented with a map:rgb-->index for efficiency.) - * This is best used with very small images. - */ -l_ok -pixSetCmapPixel(PIX *pix, - l_int32 x, - l_int32 y, - l_int32 rval, - l_int32 gval, - l_int32 bval) -{ -l_int32 w, h, d, index; -PIXCMAP *cmap; - - PROCNAME("pixSetCmapPixel"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if ((cmap = pixGetColormap(pix)) == NULL) - return ERROR_INT("pix is not colormapped", procName, 1); - pixGetDimensions(pix, &w, &h, &d); - if (d != 2 && d != 4 && d != 8) - return ERROR_INT("pix depth not 2, 4 or 8", procName, 1); - if (x < 0 || x >= w || y < 0 || y >= h) - return 2; - - if (d == 8) { /* always add */ - pixcmapAddNearestColor(cmap, rval, gval, bval, &index); - } else { /* d < 8 */ - if (pixcmapAddNewColor(cmap, rval, gval, bval, &index) == 2) - return ERROR_INT("colormap is full", procName, 2); - } - pixSetPixel(pix, x, y, index); - return 0; -} - - -/*! - * \brief pixGetRandomPixel() - * - * \param[in] pix any depth; can be colormapped - * \param[out] pval [optional] pixel value - * \param[out] px [optional] x coordinate chosen; can be null - * \param[out] py [optional] y coordinate chosen; can be null - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) If the pix is colormapped, it returns the rgb value. - *- */ -l_ok -pixGetRandomPixel(PIX *pix, - l_uint32 *pval, - l_int32 *px, - l_int32 *py) -{ -l_int32 w, h, x, y, rval, gval, bval; -l_uint32 val; -PIXCMAP *cmap; - - PROCNAME("pixGetRandomPixel"); - - if (pval) *pval = 0; - if (px) *px = 0; - if (py) *py = 0; - if (!pval && !px && !py) - return ERROR_INT("no output requested", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - pixGetDimensions(pix, &w, &h, NULL); - x = rand() % w; - y = rand() % h; - if (px) *px = x; - if (py) *py = y; - if (pval) { - pixGetPixel(pix, x, y, &val); - if ((cmap = pixGetColormap(pix)) != NULL) { - pixcmapGetColor(cmap, val, &rval, &gval, &bval); - composeRGBPixel(rval, gval, bval, pval); - } else { - *pval = val; - } - } - - return 0; -} - - -/*! - * \brief pixClearPixel() - * - * \param[in] pix any depth; warning if colormapped - * \param[in] x,y pixel coords - * \return 0 if OK; 1 or 2 on error. - * - * Notes: - * (1) If the point is outside the image, this returns an error (2), - * with 0 in %pval. To avoid spamming output, it fails silently. - */ -l_ok -pixClearPixel(PIX *pix, - l_int32 x, - l_int32 y) -{ -l_int32 w, h, d, wpl; -l_uint32 *line, *data; - - PROCNAME("pixClearPixel"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (pixGetColormap(pix)) - L_WARNING("cmapped: setting to 0 may not be intended\n", procName); - pixGetDimensions(pix, &w, &h, &d); - if (x < 0 || x >= w || y < 0 || y >= h) - return 2; - - wpl = pixGetWpl(pix); - data = pixGetData(pix); - line = data + y * wpl; - switch (d) - { - case 1: - CLEAR_DATA_BIT(line, x); - break; - case 2: - CLEAR_DATA_DIBIT(line, x); - break; - case 4: - CLEAR_DATA_QBIT(line, x); - break; - case 8: - SET_DATA_BYTE(line, x, 0); - break; - case 16: - SET_DATA_TWO_BYTES(line, x, 0); - break; - case 32: - line[x] = 0; - break; - default: - return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", procName, 1); - } - - return 0; -} - - -/*! - * \brief pixFlipPixel() - * - * \param[in] pix any depth, warning if colormapped - * \param[in] x,y pixel coords - * \return 0 if OK; 1 or 2 on error - * - * Notes: - * (1) If the point is outside the image, this returns an error (2), - * with 0 in %pval. To avoid spamming output, it fails silently. - */ -l_ok -pixFlipPixel(PIX *pix, - l_int32 x, - l_int32 y) -{ -l_int32 w, h, d, wpl; -l_uint32 val; -l_uint32 *line, *data; - - PROCNAME("pixFlipPixel"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (pixGetColormap(pix)) - L_WARNING("cmapped: setting to 0 may not be intended\n", procName); - pixGetDimensions(pix, &w, &h, &d); - if (x < 0 || x >= w || y < 0 || y >= h) - return 2; - - data = pixGetData(pix); - wpl = pixGetWpl(pix); - line = data + y * wpl; - switch (d) - { - case 1: - val = GET_DATA_BIT(line, x); - if (val) - CLEAR_DATA_BIT(line, x); - else - SET_DATA_BIT(line, x); - break; - case 2: - val = GET_DATA_DIBIT(line, x); - val ^= 0x3; - SET_DATA_DIBIT(line, x, val); - break; - case 4: - val = GET_DATA_QBIT(line, x); - val ^= 0xf; - SET_DATA_QBIT(line, x, val); - break; - case 8: - val = GET_DATA_BYTE(line, x); - val ^= 0xff; - SET_DATA_BYTE(line, x, val); - break; - case 16: - val = GET_DATA_TWO_BYTES(line, x); - val ^= 0xffff; - SET_DATA_TWO_BYTES(line, x, val); - break; - case 32: - val = line[x] ^ 0xffffffff; - line[x] = val; - break; - default: - return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", procName, 1); - } - - return 0; -} - - -/*! - * \brief setPixelLow() - * - * \param[in] line ptr to beginning of line, - * \param[in] x pixel location in line - * \param[in] depth bpp - * \param[in] val to be inserted - * \return void - * - *- * Notes: - * (1) Caution: input variables are not checked! - *- */ -void -setPixelLow(l_uint32 *line, - l_int32 x, - l_int32 depth, - l_uint32 val) -{ - switch (depth) - { - case 1: - if (val) - SET_DATA_BIT(line, x); - else - CLEAR_DATA_BIT(line, x); - break; - case 2: - SET_DATA_DIBIT(line, x, val); - break; - case 4: - SET_DATA_QBIT(line, x, val); - break; - case 8: - SET_DATA_BYTE(line, x, val); - break; - case 16: - SET_DATA_TWO_BYTES(line, x, val); - break; - case 32: - line[x] = val; - break; - default: - lept_stderr("illegal depth in setPixelLow()\n"); - } - - return; -} - - -/*-------------------------------------------------------------* - * Find black or white value * - *-------------------------------------------------------------*/ -/*! - * \brief pixGetBlackOrWhiteVal() - * - * \param[in] pixs all depths; cmap ok - * \param[in] op L_GET_BLACK_VAL, L_GET_WHITE_VAL - * \param[out] pval pixel value - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) Side effect. For a colormapped image, if the requested - * color is not present and there is room to add it in the cmap, - * it is added and the new index is returned. If there is no room, - * the index of the closest color in intensity is returned. - *- */ -l_ok -pixGetBlackOrWhiteVal(PIX *pixs, - l_int32 op, - l_uint32 *pval) -{ -l_int32 d, val; -PIXCMAP *cmap; - - PROCNAME("pixGetBlackOrWhiteVal"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (op != L_GET_BLACK_VAL && op != L_GET_WHITE_VAL) - return ERROR_INT("invalid op", procName, 1); - - cmap = pixGetColormap(pixs); - d = pixGetDepth(pixs); - if (!cmap) { - if ((d == 1 && op == L_GET_WHITE_VAL) || - (d > 1 && op == L_GET_BLACK_VAL)) { /* min val */ - val = 0; - } else { /* max val */ - val = (d == 32) ? 0xffffff00 : (1 << d) - 1; - } - } else { /* handle colormap */ - if (op == L_GET_BLACK_VAL) - pixcmapAddBlackOrWhite(cmap, 0, &val); - else /* L_GET_WHITE_VAL */ - pixcmapAddBlackOrWhite(cmap, 1, &val); - } - *pval = val; - - return 0; -} - - -/*-------------------------------------------------------------* - * Full image clear/set/set-to-arbitrary-value/invert * - *-------------------------------------------------------------*/ -/*! - * \brief pixClearAll() - * - * \param[in] pix all depths; use cmapped with caution - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Clears all data to 0. For 1 bpp, this is white; for grayscale - * or color, this is black. - * (2) Caution: for colormapped pix, this sets the color to the first - * one in the colormap. Be sure that this is the intended color! - *- */ -l_ok -pixClearAll(PIX *pix) -{ - PROCNAME("pixClearAll"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - pixRasterop(pix, 0, 0, pixGetWidth(pix), pixGetHeight(pix), - PIX_CLR, NULL, 0, 0); - return 0; -} - - -/*! - * \brief pixSetAll() - * - * \param[in] pix all depths; use cmapped with caution - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Sets all data to 1. For 1 bpp, this is black; for grayscale - * or color, this is white. - * (2) Caution: for colormapped pix, this sets the pixel value to the - * maximum value supported by the colormap: 2^d - 1. However, this - * color may not be defined, because the colormap may not be full. - *- */ -l_ok -pixSetAll(PIX *pix) -{ -l_int32 n; -PIXCMAP *cmap; - - PROCNAME("pixSetAll"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if ((cmap = pixGetColormap(pix)) != NULL) { - n = pixcmapGetCount(cmap); - if (n < cmap->nalloc) /* cmap is not full */ - return ERROR_INT("cmap entry does not exist", procName, 1); - } - - pixRasterop(pix, 0, 0, pixGetWidth(pix), pixGetHeight(pix), - PIX_SET, NULL, 0, 0); - return 0; -} - - -/*! - * \brief pixSetAllGray() - * - * \param[in] pix all depths, cmap ok - * \param[in] grayval in range 0 ... 255 - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) N.B. For all images, %grayval == 0 represents black and - * %grayval == 255 represents white. - * (2) For depth < 8, we do our best to approximate the gray level. - * For 1 bpp images, any %grayval < 128 is black; >= 128 is white. - * For 32 bpp images, each r,g,b component is set to %grayval, - * and the alpha component is preserved. - * (3) If pix is colormapped, it adds the gray value, replicated in - * all components, to the colormap if it's not there and there - * is room. If the colormap is full, it finds the closest color in - * L2 distance of components. This index is written to all pixels. - *- */ -l_ok -pixSetAllGray(PIX *pix, - l_int32 grayval) -{ -l_int32 d, spp, index; -l_uint32 val32; -PIX *alpha; -PIXCMAP *cmap; - - PROCNAME("pixSetAllGray"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (grayval < 0) { - L_WARNING("grayval < 0; setting to 0\n", procName); - grayval = 0; - } else if (grayval > 255) { - L_WARNING("grayval > 255; setting to 255\n", procName); - grayval = 255; - } - - /* Handle the colormap case */ - cmap = pixGetColormap(pix); - if (cmap) { - pixcmapAddNearestColor(cmap, grayval, grayval, grayval, &index); - pixSetAllArbitrary(pix, index); - return 0; - } - - /* Non-cmapped */ - d = pixGetDepth(pix); - spp = pixGetSpp(pix); - if (d == 1) { - if (grayval < 128) /* black */ - pixSetAll(pix); - else - pixClearAll(pix); /* white */ - } else if (d < 8) { - grayval >>= 8 - d; - pixSetAllArbitrary(pix, grayval); - } else if (d == 8) { - pixSetAllArbitrary(pix, grayval); - } else if (d == 16) { - grayval |= (grayval << 8); - pixSetAllArbitrary(pix, grayval); - } else if (d == 32 && spp == 3) { - composeRGBPixel(grayval, grayval, grayval, &val32); - pixSetAllArbitrary(pix, val32); - } else if (d == 32 && spp == 4) { - alpha = pixGetRGBComponent(pix, L_ALPHA_CHANNEL); - composeRGBPixel(grayval, grayval, grayval, &val32); - pixSetAllArbitrary(pix, val32); - pixSetRGBComponent(pix, alpha, L_ALPHA_CHANNEL); - pixDestroy(&alpha); - } else { - L_ERROR("invalid depth: %d\n", procName, d); - return 1; - } - - return 0; -} - - -/*! - * \brief pixSetAllArbitrary() - * - * \param[in] pix all depths; use cmapped with caution - * \param[in] val value to set all pixels - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) Caution 1! For colormapped pix, %val is used as an index - * into a colormap. Be sure that index refers to the intended color. - * If the color is not in the colormap, you should first add it - * and then call this function. - * (2) Caution 2! For 32 bpp pix, the interpretation of the LSB - * of %val depends on whether spp == 3 (RGB) or spp == 4 (RGBA). - * For RGB, the LSB is ignored in image transformations. - * For RGBA, the LSB is interpreted as the alpha (transparency) - * component; full transparency has alpha == 0x0, whereas - * full opacity has alpha = 0xff. An RGBA image with full - * opacity behaves like an RGB image. - * (3) As an example of (2), suppose you want to initialize a 32 bpp - * pix with partial opacity, say 0xee337788. If the pix is 3 spp, - * the 0x88 alpha component will be ignored and may be changed - * in subsequent processing. However, if the pix is 4 spp, the - * alpha component will be retained and used. The function - * pixCreate(w, h, 32) makes an RGB image by default, and - * pixSetSpp(pix, 4) can be used to promote an RGB image to RGBA. - *- */ -l_ok -pixSetAllArbitrary(PIX *pix, - l_uint32 val) -{ -l_int32 n, i, j, w, h, d, wpl, npix; -l_uint32 maxval, wordval; -l_uint32 *data, *line; -PIXCMAP *cmap; - - PROCNAME("pixSetAllArbitrary"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - /* If colormapped, make sure that val is less than the size - * of the cmap array. */ - if ((cmap = pixGetColormap(pix)) != NULL) { - n = pixcmapGetCount(cmap); - if (val >= n) { - L_WARNING("index not in colormap; using last color\n", procName); - val = n - 1; - } - } - - /* Make sure val isn't too large for the pixel depth. - * If it is too large, set the pixel color to white. */ - pixGetDimensions(pix, &w, &h, &d); - if (d < 32) { - maxval = (1 << d) - 1; - if (val > maxval) { - L_WARNING("val = %d too large for depth; using maxval = %d\n", - procName, val, maxval); - val = maxval; - } - } - - /* Set up word to tile with */ - wordval = 0; - npix = 32 / d; /* number of pixels per 32 bit word */ - for (j = 0; j < npix; j++) - wordval |= (val << (j * d)); - wpl = pixGetWpl(pix); - data = pixGetData(pix); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < wpl; j++) { - *(line + j) = wordval; - } - } - return 0; -} - - -/*! - * \brief pixSetBlackOrWhite() - * - * \param[in] pixs all depths; cmap ok - * \param[in] op L_SET_BLACK, L_SET_WHITE - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) Function for setting all pixels in an image to either black - * or white. - * (2) If pixs is colormapped, it adds black or white to the - * colormap if it's not there and there is room. If the colormap - * is full, it finds the closest color in intensity. - * This index is written to all pixels. - *- */ -l_ok -pixSetBlackOrWhite(PIX *pixs, - l_int32 op) -{ -l_int32 d, index; -PIXCMAP *cmap; - - PROCNAME("pixSetBlackOrWhite"); - - if (!pixs) - return ERROR_INT("pix not defined", procName, 1); - if (op != L_SET_BLACK && op != L_SET_WHITE) - return ERROR_INT("invalid op", procName, 1); - - cmap = pixGetColormap(pixs); - d = pixGetDepth(pixs); - if (!cmap) { - if ((d == 1 && op == L_SET_BLACK) || (d > 1 && op == L_SET_WHITE)) - pixSetAll(pixs); - else - pixClearAll(pixs); - } else { /* handle colormap */ - if (op == L_SET_BLACK) - pixcmapAddBlackOrWhite(cmap, 0, &index); - else /* L_SET_WHITE */ - pixcmapAddBlackOrWhite(cmap, 1, &index); - pixSetAllArbitrary(pixs, index); - } - - return 0; -} - - -/*! - * \brief pixSetComponentArbitrary() - * - * \param[in] pix 32 bpp - * \param[in] comp COLOR_RED, COLOR_GREEN, COLOR_BLUE, L_ALPHA_CHANNEL - * \param[in] val value to set this component - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) For example, this can be used to set the alpha component to opaque: - * pixSetComponentArbitrary(pix, L_ALPHA_CHANNEL, 255) - *- */ -l_ok -pixSetComponentArbitrary(PIX *pix, - l_int32 comp, - l_int32 val) -{ -l_int32 i, nwords; -l_uint32 mask1, mask2; -l_uint32 *data; - - PROCNAME("pixSetComponentArbitrary"); - - if (!pix || pixGetDepth(pix) != 32) - return ERROR_INT("pix not defined or not 32 bpp", procName, 1); - if (comp != COLOR_RED && comp != COLOR_GREEN && comp != COLOR_BLUE && - comp != L_ALPHA_CHANNEL) - return ERROR_INT("invalid component", procName, 1); - if (val < 0 || val > 255) - return ERROR_INT("val not in [0 ... 255]", procName, 1); - - mask1 = ~(255 << (8 * (3 - comp))); - mask2 = val << (8 * (3 - comp)); - nwords = pixGetHeight(pix) * pixGetWpl(pix); - data = pixGetData(pix); - for (i = 0; i < nwords; i++) { - data[i] &= mask1; /* clear out the component */ - data[i] |= mask2; /* insert the new component value */ - } - - return 0; -} - - -/*-------------------------------------------------------------* - * Rectangular region clear/set/set-to-arbitrary-value * - *-------------------------------------------------------------*/ -/*! - * \brief pixClearInRect() - * - * \param[in] pix all depths; can be cmapped - * \param[in] box in which all pixels will be cleared - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Clears all data in rect to 0. For 1 bpp, this is white; - * for grayscale or color, this is black. - * (2) Caution: for colormapped pix, this sets the color to the first - * one in the colormap. Be sure that this is the intended color! - *- */ -l_ok -pixClearInRect(PIX *pix, - BOX *box) -{ -l_int32 x, y, w, h; - - PROCNAME("pixClearInRect"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - - boxGetGeometry(box, &x, &y, &w, &h); - pixRasterop(pix, x, y, w, h, PIX_CLR, NULL, 0, 0); - return 0; -} - - -/*! - * \brief pixSetInRect() - * - * \param[in] pix all depths, can be cmapped - * \param[in] box in which all pixels will be set - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Sets all data in rect to 1. For 1 bpp, this is black; - * for grayscale or color, this is white. - * (2) Caution: for colormapped pix, this sets the pixel value to the - * maximum value supported by the colormap: 2^d - 1. However, this - * color may not be defined, because the colormap may not be full. - *- */ -l_ok -pixSetInRect(PIX *pix, - BOX *box) -{ -l_int32 n, x, y, w, h; -PIXCMAP *cmap; - - PROCNAME("pixSetInRect"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - if ((cmap = pixGetColormap(pix)) != NULL) { - n = pixcmapGetCount(cmap); - if (n < cmap->nalloc) /* cmap is not full */ - return ERROR_INT("cmap entry does not exist", procName, 1); - } - - boxGetGeometry(box, &x, &y, &w, &h); - pixRasterop(pix, x, y, w, h, PIX_SET, NULL, 0, 0); - return 0; -} - - -/*! - * \brief pixSetInRectArbitrary() - * - * \param[in] pix all depths; can be cmapped - * \param[in] box in which all pixels will be set to val - * \param[in] val value to set all pixels - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) For colormapped pix, be sure the value is the intended - * one in the colormap. - * (2) Caution: for colormapped pix, this sets each pixel in the - * rect to the color at the index equal to val. Be sure that - * this index exists in the colormap and that it is the intended one! - *- */ -l_ok -pixSetInRectArbitrary(PIX *pix, - BOX *box, - l_uint32 val) -{ -l_int32 n, x, y, xstart, xend, ystart, yend, bw, bh, w, h, d, wpl, maxval; -l_uint32 *data, *line; -BOX *boxc; -PIXCMAP *cmap; - - PROCNAME("pixSetInRectArbitrary"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - pixGetDimensions(pix, &w, &h, &d); - if (d != 1 && d != 2 && d != 4 && d !=8 && d != 16 && d != 32) - return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", procName, 1); - if ((cmap = pixGetColormap(pix)) != NULL) { - n = pixcmapGetCount(cmap); - if (val >= n) { - L_WARNING("index not in colormap; using last color\n", procName); - val = n - 1; - } - } - - maxval = (d == 32) ? 0xffffff00 : (1 << d) - 1; - if (val > maxval) val = maxval; - - /* Handle the simple cases: the min and max values */ - if (val == 0) { - pixClearInRect(pix, box); - return 0; - } - if (d == 1 || - (d == 2 && val == 3) || - (d == 4 && val == 0xf) || - (d == 8 && val == 0xff) || - (d == 16 && val == 0xffff) || - (d == 32 && ((val ^ 0xffffff00) >> 8 == 0))) { - pixSetInRect(pix, box); - return 0; - } - - /* Find the overlap of box with the input pix */ - if ((boxc = boxClipToRectangle(box, w, h)) == NULL) - return ERROR_INT("no overlap of box with image", procName, 1); - boxGetGeometry(boxc, &xstart, &ystart, &bw, &bh); - xend = xstart + bw - 1; - yend = ystart + bh - 1; - boxDestroy(&boxc); - - wpl = pixGetWpl(pix); - data = pixGetData(pix); - for (y = ystart; y <= yend; y++) { - line = data + y * wpl; - for (x = xstart; x <= xend; x++) { - switch(d) - { - case 2: - SET_DATA_DIBIT(line, x, val); - break; - case 4: - SET_DATA_QBIT(line, x, val); - break; - case 8: - SET_DATA_BYTE(line, x, val); - break; - case 16: - SET_DATA_TWO_BYTES(line, x, val); - break; - case 32: - line[x] = val; - break; - default: - return ERROR_INT("depth not 2|4|8|16|32 bpp", procName, 1); - } - } - } - - return 0; -} - - -/*! - * \brief pixBlendInRect() - * - * \param[in] pixs 32 bpp rgb - * \param[in] box [optional] in which all pixels will be blended - * \param[in] val blend value; 0xrrggbb00 - * \param[in] fract fraction of color to be blended with each pixel in pixs - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) This is an in-place function. It blends the input color %val - * with the pixels in pixs in the specified rectangle. - * If no rectangle is specified, it blends over the entire image. - *- */ -l_ok -pixBlendInRect(PIX *pixs, - BOX *box, - l_uint32 val, - l_float32 fract) -{ -l_int32 i, j, bx, by, bw, bh, w, h, wpls; -l_int32 prval, pgval, pbval, rval, gval, bval; -l_uint32 val32; -l_uint32 *datas, *lines; - - PROCNAME("pixBlendInRect"); - - if (!pixs || pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); - - extractRGBValues(val, &rval, &gval, &bval); - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if (!box) { - for (i = 0; i < h; i++) { /* scan over box */ - lines = datas + i * wpls; - for (j = 0; j < w; j++) { - val32 = *(lines + j); - extractRGBValues(val32, &prval, &pgval, &pbval); - prval = (l_int32)((1. - fract) * prval + fract * rval); - pgval = (l_int32)((1. - fract) * pgval + fract * gval); - pbval = (l_int32)((1. - fract) * pbval + fract * bval); - composeRGBPixel(prval, pgval, pbval, &val32); - *(lines + j) = val32; - } - } - return 0; - } - - boxGetGeometry(box, &bx, &by, &bw, &bh); - for (i = 0; i < bh; i++) { /* scan over box */ - if (by + i < 0 || by + i >= h) continue; - lines = datas + (by + i) * wpls; - for (j = 0; j < bw; j++) { - if (bx + j < 0 || bx + j >= w) continue; - val32 = *(lines + bx + j); - extractRGBValues(val32, &prval, &pgval, &pbval); - prval = (l_int32)((1. - fract) * prval + fract * rval); - pgval = (l_int32)((1. - fract) * pgval + fract * gval); - pbval = (l_int32)((1. - fract) * pbval + fract * bval); - composeRGBPixel(prval, pgval, pbval, &val32); - *(lines + bx + j) = val32; - } - } - return 0; -} - - -/*-------------------------------------------------------------* - * Set pad bits * - *-------------------------------------------------------------*/ -/*! - * \brief pixSetPadBits() - * - * \param[in] pix 1, 2, 4, 8, 16, 32 bpp - * \param[in] val 0 or 1 - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) The pad bits are the bits that expand each scanline to a - * multiple of 32 bits. They are usually not used in - * image processing operations. When boundary conditions - * are important, as in seedfill, they must be set properly. - * (2) This sets the value of the pad bits (if any) in the last - * 32-bit word in each scanline. - * (3) For 32 bpp pix, there are no pad bits, so this is a no-op. - * (4) When writing formatted output, such as tiff, png or jpeg, - * the pad bits have no effect on the raster image that is - * generated by reading back from the file. However, in some - * cases, the compressed file itself will depend on the pad - * bits. This is seen, for example, in Windows with 2 and 4 bpp - * tiff-compressed images that have pad bits on each scanline. - * It is sometimes convenient to use a golden file with a - * byte-by-byte check to verify invariance. Consequently, - * and because setting the pad bits is cheap, the pad bits are - * set to 0 before writing these compressed files. - *- */ -l_ok -pixSetPadBits(PIX *pix, - l_int32 val) -{ -l_int32 i, w, h, d, wpl, endbits, fullwords; -l_uint32 mask; -l_uint32 *data, *pword; - - PROCNAME("pixSetPadBits"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - pixGetDimensions(pix, &w, &h, &d); - if (d == 32) /* no padding exists for 32 bpp */ - return 0; - - data = pixGetData(pix); - wpl = pixGetWpl(pix); - endbits = 32 - (((l_int64)w * d) % 32); - if (endbits == 32) /* no partial word */ - return 0; - fullwords = (1LL * w * d) / 32; - mask = rmask32[endbits]; - if (val == 0) - mask = ~mask; - - for (i = 0; i < h; i++) { - pword = data + i * wpl + fullwords; - if (val == 0) /* clear */ - *pword = *pword & mask; - else /* set */ - *pword = *pword | mask; - } - - return 0; -} - - -/*! - * \brief pixSetPadBitsBand() - * - * \param[in] pix 1, 2, 4, 8, 16, 32 bpp - * \param[in] by starting y value of band - * \param[in] bh height of band - * \param[in] val 0 or 1 - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) The pad bits are the bits that expand each scanline to a - * multiple of 32 bits. They are usually not used in - * image processing operations. When boundary conditions - * are important, as in seedfill, they must be set properly. - * (2) This sets the value of the pad bits (if any) in the last - * 32-bit word in each scanline, within the specified - * band of raster lines. - * (3) For 32 bpp pix, there are no pad bits, so this is a no-op. - *- */ -l_ok -pixSetPadBitsBand(PIX *pix, - l_int32 by, - l_int32 bh, - l_int32 val) -{ -l_int32 i, w, h, d, wpl, endbits, fullwords; -l_uint32 mask; -l_uint32 *data, *pword; - - PROCNAME("pixSetPadBitsBand"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - pixGetDimensions(pix, &w, &h, &d); - if (d == 32) /* no padding exists for 32 bpp */ - return 0; - - if (by < 0) - by = 0; - if (by >= h) - return ERROR_INT("start y not in image", procName, 1); - if (by + bh > h) - bh = h - by; - - data = pixGetData(pix); - wpl = pixGetWpl(pix); - endbits = 32 - (((l_int64)w * d) % 32); - if (endbits == 32) /* no partial word */ - return 0; - fullwords = (l_int64)w * d / 32; - - mask = rmask32[endbits]; - if (val == 0) - mask = ~mask; - - for (i = by; i < by + bh; i++) { - pword = data + i * wpl + fullwords; - if (val == 0) /* clear */ - *pword = *pword & mask; - else /* set */ - *pword = *pword | mask; - } - - return 0; -} - - -/*-------------------------------------------------------------* - * Set border pixels * - *-------------------------------------------------------------*/ -/*! - * \brief pixSetOrClearBorder() - * - * \param[in] pixs all depths - * \param[in] left, right, top, bot amount to set or clear - * \param[in] op operation PIX_SET or PIX_CLR - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) The border region is defined to be the region in the - * image within a specific distance of each edge. Here, we - * allow the pixels within a specified distance of each - * edge to be set independently. This either sets or - * clears all pixels in the border region. - * (2) For binary images, use PIX_SET for black and PIX_CLR for white. - * (3) For grayscale or color images, use PIX_SET for white - * and PIX_CLR for black. - *- */ -l_ok -pixSetOrClearBorder(PIX *pixs, - l_int32 left, - l_int32 right, - l_int32 top, - l_int32 bot, - l_int32 op) -{ -l_int32 w, h; - - PROCNAME("pixSetOrClearBorder"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (op != PIX_SET && op != PIX_CLR) - return ERROR_INT("op must be PIX_SET or PIX_CLR", procName, 1); - - pixGetDimensions(pixs, &w, &h, NULL); - pixRasterop(pixs, 0, 0, left, h, op, NULL, 0, 0); - pixRasterop(pixs, w - right, 0, right, h, op, NULL, 0, 0); - pixRasterop(pixs, 0, 0, w, top, op, NULL, 0, 0); - pixRasterop(pixs, 0, h - bot, w, bot, op, NULL, 0, 0); - - return 0; -} - - -/*! - * \brief pixSetBorderVal() - * - * \param[in] pixs 8, 16 or 32 bpp - * \param[in] left, right, top, bot amount to set - * \param[in] val value to set at each border pixel - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) The border region is defined to be the region in the - * image within a specific distance of each edge. Here, we - * allow the pixels within a specified distance of each - * edge to be set independently. This sets the pixels - * in the border region to the given input value. - * (2) For efficiency, use pixSetOrClearBorder() if - * you're setting the border to either black or white. - * (3) If d != 32, the input value should be masked off - * to the appropriate number of least significant bits. - * (4) The code is easily generalized for 2 or 4 bpp. - *- */ -l_ok -pixSetBorderVal(PIX *pixs, - l_int32 left, - l_int32 right, - l_int32 top, - l_int32 bot, - l_uint32 val) -{ -l_int32 w, h, d, wpls, i, j, bstart, rstart; -l_uint32 *datas, *lines; - - PROCNAME("pixSetBorderVal"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8 && d != 16 && d != 32) - return ERROR_INT("depth must be 8, 16 or 32 bpp", procName, 1); - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if (d == 8) { - val &= 0xff; - for (i = 0; i < top; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) - SET_DATA_BYTE(lines, j, val); - } - rstart = w - right; - bstart = h - bot; - for (i = top; i < bstart; i++) { - lines = datas + i * wpls; - for (j = 0; j < left; j++) - SET_DATA_BYTE(lines, j, val); - for (j = rstart; j < w; j++) - SET_DATA_BYTE(lines, j, val); - } - for (i = bstart; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) - SET_DATA_BYTE(lines, j, val); - } - } else if (d == 16) { - val &= 0xffff; - for (i = 0; i < top; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) - SET_DATA_TWO_BYTES(lines, j, val); - } - rstart = w - right; - bstart = h - bot; - for (i = top; i < bstart; i++) { - lines = datas + i * wpls; - for (j = 0; j < left; j++) - SET_DATA_TWO_BYTES(lines, j, val); - for (j = rstart; j < w; j++) - SET_DATA_TWO_BYTES(lines, j, val); - } - for (i = bstart; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) - SET_DATA_TWO_BYTES(lines, j, val); - } - } else { /* d == 32 */ - for (i = 0; i < top; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) - *(lines + j) = val; - } - rstart = w - right; - bstart = h - bot; - for (i = top; i < bstart; i++) { - lines = datas + i * wpls; - for (j = 0; j < left; j++) - *(lines + j) = val; - for (j = rstart; j < w; j++) - *(lines + j) = val; - } - for (i = bstart; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) - *(lines + j) = val; - } - } - - return 0; -} - - -/*! - * \brief pixSetBorderRingVal() - * - * \param[in] pixs any depth; cmap OK - * \param[in] dist distance from outside; must be > 0; first ring is 1 - * \param[in] val value to set at each border pixel - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) The rings are single-pixel-wide rectangular sets of - * pixels at a given distance from the edge of the pix. - * This sets all pixels in a given ring to a value. - *- */ -l_ok -pixSetBorderRingVal(PIX *pixs, - l_int32 dist, - l_uint32 val) -{ -l_int32 w, h, d, i, j, xend, yend; - - PROCNAME("pixSetBorderRingVal"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (dist < 1) - return ERROR_INT("dist must be > 0", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - if (w < 2 * dist + 1 || h < 2 * dist + 1) - return ERROR_INT("ring doesn't exist", procName, 1); - if (d < 32 && (val >= (1 << d))) - return ERROR_INT("invalid pixel value", procName, 1); - - xend = w - dist; - yend = h - dist; - for (j = dist - 1; j <= xend; j++) - pixSetPixel(pixs, j, dist - 1, val); - for (j = dist - 1; j <= xend; j++) - pixSetPixel(pixs, j, yend, val); - for (i = dist - 1; i <= yend; i++) - pixSetPixel(pixs, dist - 1, i, val); - for (i = dist - 1; i <= yend; i++) - pixSetPixel(pixs, xend, i, val); - - return 0; -} - - -/*! - * \brief pixSetMirroredBorder() - * - * \param[in] pixs all depths; colormap ok - * \param[in] left, right, top, bot number of pixels to set - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This applies what is effectively mirror boundary conditions - * to a border region in the image. It is in-place. - * (2) This is useful for setting pixels near the border to a - * value representative of the near pixels to the interior. - * (3) The general pixRasterop() is used for an in-place operation here - * because there is no overlap between the src and dest rectangles. - *- */ -l_ok -pixSetMirroredBorder(PIX *pixs, - l_int32 left, - l_int32 right, - l_int32 top, - l_int32 bot) -{ -l_int32 i, j, w, h; - - PROCNAME("pixSetMirroredBorder"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - - pixGetDimensions(pixs, &w, &h, NULL); - for (j = 0; j < left; j++) - pixRasterop(pixs, left - 1 - j, top, 1, h - top - bot, PIX_SRC, - pixs, left + j, top); - for (j = 0; j < right; j++) - pixRasterop(pixs, w - right + j, top, 1, h - top - bot, PIX_SRC, - pixs, w - right - 1 - j, top); - for (i = 0; i < top; i++) - pixRasterop(pixs, 0, top - 1 - i, w, 1, PIX_SRC, - pixs, 0, top + i); - for (i = 0; i < bot; i++) - pixRasterop(pixs, 0, h - bot + i, w, 1, PIX_SRC, - pixs, 0, h - bot - 1 - i); - - return 0; -} - - -/*! - * \brief pixCopyBorder() - * - * \param[in] pixd all depths; colormap ok; can be NULL - * \param[in] pixs same depth and size as pixd - * \param[in] left, right, top, bot number of pixels to copy - * \return pixd, or NULL on error if pixd is not defined - * - *- * Notes: - * (1) pixd can be null, but otherwise it must be the same size - * and depth as pixs. Always returns pixd. - * (2) This is useful in situations where by setting a few border - * pixels we can avoid having to copy all pixels in pixs into - * pixd as an initialization step for some operation. - * Nevertheless, for safety, if making a new pixd, all the - * non-border pixels are initialized to 0. - *- */ -PIX * -pixCopyBorder(PIX *pixd, - PIX *pixs, - l_int32 left, - l_int32 right, - l_int32 top, - l_int32 bot) -{ -l_int32 w, h; - - PROCNAME("pixCopyBorder"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - - if (pixd) { - if (pixd == pixs) { - L_WARNING("same: nothing to do\n", procName); - return pixd; - } else if (!pixSizesEqual(pixs, pixd)) { - return (PIX *)ERROR_PTR("pixs and pixd sizes differ", - procName, pixd); - } - } else { - if ((pixd = pixCreateTemplate(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, pixd); - } - - pixGetDimensions(pixs, &w, &h, NULL); - pixRasterop(pixd, 0, 0, left, h, PIX_SRC, pixs, 0, 0); - pixRasterop(pixd, w - right, 0, right, h, PIX_SRC, pixs, w - right, 0); - pixRasterop(pixd, 0, 0, w, top, PIX_SRC, pixs, 0, 0); - pixRasterop(pixd, 0, h - bot, w, bot, PIX_SRC, pixs, 0, h - bot); - return pixd; -} - - - -/*-------------------------------------------------------------* - * Add and remove border * - *-------------------------------------------------------------*/ -/*! - * \brief pixAddBorder() - * - * \param[in] pixs all depths; colormap ok - * \param[in] npix number of pixels to be added to each side - * \param[in] val value of added border pixels - * \return pixd with the added exterior pixels, or NULL on error - * - *- * Notes: - * (1) See pixGetBlackOrWhiteVal() for values of black and white pixels. - *- */ -PIX * -pixAddBorder(PIX *pixs, - l_int32 npix, - l_uint32 val) -{ - PROCNAME("pixAddBorder"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (npix == 0) - return pixClone(pixs); - return pixAddBorderGeneral(pixs, npix, npix, npix, npix, val); -} - - -/*! - * \brief pixAddBlackOrWhiteBorder() - * - * \param[in] pixs all depths; colormap ok - * \param[in] left, right, top, bot number of pixels added - * \param[in] op L_GET_BLACK_VAL, L_GET_WHITE_VAL - * \return pixd with the added exterior pixels, or NULL on error - * - *- * Notes: - * (1) See pixGetBlackOrWhiteVal() for possible side effect (adding - * a color to a colormap). - * (2) The only complication is that pixs may have a colormap. - * There are two ways to add the black or white border: - * (a) As done here (simplest, most efficient) - * (b) l_int32 ws, hs, d; - * pixGetDimensions(pixs, &ws, &hs, &d); - * Pix *pixd = pixCreate(ws + left + right, hs + top + bot, d); - * PixColormap *cmap = pixGetColormap(pixs); - * if (cmap != NULL) - * pixSetColormap(pixd, pixcmapCopy(cmap)); - * pixSetBlackOrWhite(pixd, L_SET_WHITE); // uses cmap - * pixRasterop(pixd, left, top, ws, hs, PIX_SET, pixs, 0, 0); - *- */ -PIX * -pixAddBlackOrWhiteBorder(PIX *pixs, - l_int32 left, - l_int32 right, - l_int32 top, - l_int32 bot, - l_int32 op) -{ -l_uint32 val; - - PROCNAME("pixAddBlackOrWhiteBorder"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (op != L_GET_BLACK_VAL && op != L_GET_WHITE_VAL) - return (PIX *)ERROR_PTR("invalid op", procName, NULL); - - pixGetBlackOrWhiteVal(pixs, op, &val); - return pixAddBorderGeneral(pixs, left, right, top, bot, val); -} - - -/*! - * \brief pixAddBorderGeneral() - * - * \param[in] pixs all depths; colormap ok - * \param[in] left, right, top, bot number of pixels added - * \param[in] val value of added border pixels - * \return pixd with the added exterior pixels, or NULL 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, set val to the appropriate colormap index. - * (2) If the added border is either black or white, you can use - * pixAddBlackOrWhiteBorder() - * The black and white values for all images can be found with - * pixGetBlackOrWhiteVal() - * which, if pixs is cmapped, may add an entry to the colormap. - * Alternatively, if pixs has a colormap, you can find the index - * of the pixel whose intensity is closest to white or black: - * white: pixcmapGetRankIntensity(cmap, 1.0, &index); - * black: pixcmapGetRankIntensity(cmap, 0.0, &index); - * and use that for val. - *- */ -PIX * -pixAddBorderGeneral(PIX *pixs, - l_int32 left, - l_int32 right, - l_int32 top, - l_int32 bot, - l_uint32 val) -{ -l_int32 ws, hs, wd, hd, d, maxval, op; -PIX *pixd; - - PROCNAME("pixAddBorderGeneral"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (left < 0 || right < 0 || top < 0 || bot < 0) - return (PIX *)ERROR_PTR("negative border added!", procName, NULL); - - pixGetDimensions(pixs, &ws, &hs, &d); - wd = ws + left + right; - hd = hs + top + bot; - if ((pixd = pixCreateNoInit(wd, hd, d)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyColormap(pixd, pixs); - - /* Set the new border pixels */ - maxval = (d == 32) ? 0xffffff00 : (1 << d) - 1; - op = UNDEF; - if (val == 0) - op = PIX_CLR; - else if (val >= maxval) - op = PIX_SET; - if (op == UNDEF) { - pixSetAllArbitrary(pixd, val); - } else { /* just set or clear the border pixels */ - pixRasterop(pixd, 0, 0, left, hd, op, NULL, 0, 0); - pixRasterop(pixd, wd - right, 0, right, hd, op, NULL, 0, 0); - pixRasterop(pixd, 0, 0, wd, top, op, NULL, 0, 0); - pixRasterop(pixd, 0, hd - bot, wd, bot, op, NULL, 0, 0); - } - - /* Copy pixs into the interior */ - pixRasterop(pixd, left, top, ws, hs, PIX_SRC, pixs, 0, 0); - return pixd; -} - - -/*! - * \brief pixRemoveBorder() - * - * \param[in] pixs all depths; colormap ok - * \param[in] npix number to be removed from each of the 4 sides - * \return pixd with pixels removed around border, or NULL on error - */ -PIX * -pixRemoveBorder(PIX *pixs, - l_int32 npix) -{ - PROCNAME("pixRemoveBorder"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (npix == 0) - return pixClone(pixs); - return pixRemoveBorderGeneral(pixs, npix, npix, npix, npix); -} - - -/*! - * \brief pixRemoveBorderGeneral() - * - * \param[in] pixs all depths; colormap ok - * \param[in] left, right, top, bot number of pixels removed - * \return pixd with pixels removed around border, or NULL on error - */ -PIX * -pixRemoveBorderGeneral(PIX *pixs, - l_int32 left, - l_int32 right, - l_int32 top, - l_int32 bot) -{ -l_int32 ws, hs, wd, hd, d; -PIX *pixd; - - PROCNAME("pixRemoveBorderGeneral"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (left < 0 || right < 0 || top < 0 || bot < 0) - return (PIX *)ERROR_PTR("negative border removed!", procName, NULL); - - pixGetDimensions(pixs, &ws, &hs, &d); - wd = ws - left - right; - hd = hs - top - bot; - if (wd <= 0) - return (PIX *)ERROR_PTR("width must be > 0", procName, NULL); - if (hd <= 0) - return (PIX *)ERROR_PTR("height must be > 0", procName, NULL); - if ((pixd = pixCreateNoInit(wd, hd, d)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopySpp(pixd, pixs); - pixCopyColormap(pixd, pixs); - - pixRasterop(pixd, 0, 0, wd, hd, PIX_SRC, pixs, left, top); - if (pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4) - pixShiftAndTransferAlpha(pixd, pixs, -left, -top); - return pixd; -} - - -/*! - * \brief pixRemoveBorderToSize() - * - * \param[in] pixs all depths; colormap ok - * \param[in] wd target width; use 0 if only removing from height - * \param[in] hd target height; use 0 if only removing from width - * \return pixd with pixels removed around border, or NULL on error - * - *- * Notes: - * (1) Removes pixels as evenly as possible from the sides of the - * image, leaving the central part. - * (2) Returns clone if no pixels requested removed, or the target - * sizes are larger than the image. - *- */ -PIX * -pixRemoveBorderToSize(PIX *pixs, - l_int32 wd, - l_int32 hd) -{ -l_int32 w, h, top, bot, left, right, delta; - - PROCNAME("pixRemoveBorderToSize"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - if ((wd <= 0 || wd >= w) && (hd <= 0 || hd >= h)) - return pixClone(pixs); - - left = right = (w - wd) / 2; - delta = w - 2 * left - wd; - right += delta; - top = bot = (h - hd) / 2; - delta = h - hd - 2 * top; - bot += delta; - if (wd <= 0 || wd > w) - left = right = 0; - else if (hd <= 0 || hd > h) - top = bot = 0; - - return pixRemoveBorderGeneral(pixs, left, right, top, bot); -} - - -/*! - * \brief pixAddMirroredBorder() - * - * \param[in] pixs all depths; colormap ok - * \param[in] left, right, top, bot number of pixels added - * \return pixd, or NULL on error - * - *- * Notes: - * (1) This applies what is effectively mirror boundary conditions. - * For the added border pixels in pixd, the pixels in pixs - * near the border are mirror-copied into the border region. - * (2) This is useful for avoiding special operations near - * boundaries when doing image processing operations - * such as rank filters and convolution. In use, one first - * adds mirrored pixels to each side of the image. The number - * of pixels added on each side is half the filter dimension. - * Then the image processing operations proceed over a - * region equal to the size of the original image, and - * write directly into a dest pix of the same size as pixs. - * (3) The general pixRasterop() is used for an in-place operation here - * because there is no overlap between the src and dest rectangles. - *- */ -PIX * -pixAddMirroredBorder(PIX *pixs, - l_int32 left, - l_int32 right, - l_int32 top, - l_int32 bot) -{ -l_int32 i, j, w, h; -PIX *pixd; - - PROCNAME("pixAddMirroredBorder"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - if (left > w || right > w || top > h || bot > h) - return (PIX *)ERROR_PTR("border too large", procName, NULL); - - /* Set pixels on left, right, top and bottom, in that order */ - pixd = pixAddBorderGeneral(pixs, left, right, top, bot, 0); - for (j = 0; j < left; j++) - pixRasterop(pixd, left - 1 - j, top, 1, h, PIX_SRC, - pixd, left + j, top); - for (j = 0; j < right; j++) - pixRasterop(pixd, left + w + j, top, 1, h, PIX_SRC, - pixd, left + w - 1 - j, top); - for (i = 0; i < top; i++) - pixRasterop(pixd, 0, top - 1 - i, left + w + right, 1, PIX_SRC, - pixd, 0, top + i); - for (i = 0; i < bot; i++) - pixRasterop(pixd, 0, top + h + i, left + w + right, 1, PIX_SRC, - pixd, 0, top + h - 1 - i); - - return pixd; -} - - -/*! - * \brief pixAddRepeatedBorder() - * - * \param[in] pixs all depths; colormap ok - * \param[in] left, right, top, bot number of pixels added - * \return pixd, or NULL on error - * - *- * Notes: - * (1) This applies a repeated border, as if the central part of - * the image is tiled over the plane. So, for example, the - * pixels in the left border come from the right side of the image. - * (2) The general pixRasterop() is used for an in-place operation here - * because there is no overlap between the src and dest rectangles. - *- */ -PIX * -pixAddRepeatedBorder(PIX *pixs, - l_int32 left, - l_int32 right, - l_int32 top, - l_int32 bot) -{ -l_int32 w, h; -PIX *pixd; - - PROCNAME("pixAddRepeatedBorder"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - if (left > w || right > w || top > h || bot > h) - return (PIX *)ERROR_PTR("border too large", procName, NULL); - - pixd = pixAddBorderGeneral(pixs, left, right, top, bot, 0); - - /* Set pixels on left, right, top and bottom, in that order */ - pixRasterop(pixd, 0, top, left, h, PIX_SRC, pixd, w, top); - pixRasterop(pixd, left + w, top, right, h, PIX_SRC, pixd, left, top); - pixRasterop(pixd, 0, 0, left + w + right, top, PIX_SRC, pixd, 0, h); - pixRasterop(pixd, 0, top + h, left + w + right, bot, PIX_SRC, pixd, 0, top); - - return pixd; -} - - -/*! - * \brief pixAddMixedBorder() - * - * \param[in] pixs all depths; colormap ok - * \param[in] left, right, top, bot number of pixels added - * \return pixd, or NULL on error - * - *- * Notes: - * (1) This applies mirrored boundary conditions horizontally - * and repeated b.c. vertically. - * (2) It is specifically used for avoiding special operations - * near boundaries when convolving a hue-saturation histogram - * with a given window size. The repeated b.c. are used - * vertically for hue, and the mirrored b.c. are used - * horizontally for saturation. The number of pixels added - * on each side is approximately (but not quite) half the - * filter dimension. The image processing operations can - * then proceed over a region equal to the size of the original - * image, and write directly into a dest pix of the same - * size as pixs. - * (3) The general pixRasterop() can be used for an in-place - * operation here because there is no overlap between the - * src and dest rectangles. - *- */ -PIX * -pixAddMixedBorder(PIX *pixs, - l_int32 left, - l_int32 right, - l_int32 top, - l_int32 bot) -{ -l_int32 j, w, h; -PIX *pixd; - - PROCNAME("pixAddMixedBorder"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - if (left > w || right > w || top > h || bot > h) - return (PIX *)ERROR_PTR("border too large", procName, NULL); - - /* Set mirrored pixels on left and right; - * then set repeated pixels on top and bottom. */ - pixd = pixAddBorderGeneral(pixs, left, right, top, bot, 0); - for (j = 0; j < left; j++) - pixRasterop(pixd, left - 1 - j, top, 1, h, PIX_SRC, - pixd, left + j, top); - for (j = 0; j < right; j++) - pixRasterop(pixd, left + w + j, top, 1, h, PIX_SRC, - pixd, left + w - 1 - j, top); - pixRasterop(pixd, 0, 0, left + w + right, top, PIX_SRC, pixd, 0, h); - pixRasterop(pixd, 0, top + h, left + w + right, bot, PIX_SRC, pixd, 0, top); - - return pixd; -} - - -/*! - * \brief pixAddContinuedBorder() - * - * \param[in] pixs all depths; colormap ok - * \param[in] left, right, top, bot pixels on each side to be added - * \return pixd, or NULL on error - * - *- * Notes: - * (1) This adds pixels on each side whose values are equal to - * the value on the closest boundary pixel. - *- */ -PIX * -pixAddContinuedBorder(PIX *pixs, - l_int32 left, - l_int32 right, - l_int32 top, - l_int32 bot) -{ -l_int32 i, j, w, h; -PIX *pixd; - - PROCNAME("pixAddContinuedBorder"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - pixd = pixAddBorderGeneral(pixs, left, right, top, bot, 0); - pixGetDimensions(pixs, &w, &h, NULL); - for (j = 0; j < left; j++) - pixRasterop(pixd, j, top, 1, h, PIX_SRC, pixd, left, top); - for (j = 0; j < right; j++) - pixRasterop(pixd, left + w + j, top, 1, h, - PIX_SRC, pixd, left + w - 1, top); - for (i = 0; i < top; i++) - pixRasterop(pixd, 0, i, left + w + right, 1, PIX_SRC, pixd, 0, top); - for (i = 0; i < bot; i++) - pixRasterop(pixd, 0, top + h + i, left + w + right, 1, - PIX_SRC, pixd, 0, top + h - 1); - - return pixd; -} - - -/*-------------------------------------------------------------------* - * Helper functions using alpha * - *-------------------------------------------------------------------*/ -/*! - * \brief pixShiftAndTransferAlpha() - * - * \param[in] pixd 32 bpp - * \param[in] pixs 32 bpp - * \param[in] shiftx, shifty - * \return 0 if OK; 1 on error - */ -l_ok -pixShiftAndTransferAlpha(PIX *pixd, - PIX *pixs, - l_float32 shiftx, - l_float32 shifty) -{ -l_int32 w, h; -PIX *pix1, *pix2; - - PROCNAME("pixShiftAndTransferAlpha"); - - if (!pixs || !pixd) - return ERROR_INT("pixs and pixd not both defined", procName, 1); - if (pixGetDepth(pixs) != 32 || pixGetSpp(pixs) != 4) - return ERROR_INT("pixs not 32 bpp and 4 spp", procName, 1); - if (pixGetDepth(pixd) != 32) - return ERROR_INT("pixd not 32 bpp", procName, 1); - - if (shiftx == 0 && shifty == 0) { - pixCopyRGBComponent(pixd, pixs, L_ALPHA_CHANNEL); - return 0; - } - - pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); - pixGetDimensions(pixd, &w, &h, NULL); - pix2 = pixCreate(w, h, 8); - pixRasterop(pix2, 0, 0, w, h, PIX_SRC, pix1, -shiftx, -shifty); - pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); - pixDestroy(&pix1); - pixDestroy(&pix2); - return 0; -} - - -/*! - * \brief pixDisplayLayersRGBA() - * - * \param[in] pixs cmap or 32 bpp rgba - * \param[in] val 32 bit unsigned color to use as background - * \param[in] maxw max output image width; 0 for no scaling - * \return pixd showing various image views, or NULL on error - * - *- * Notes: - * (1) Use %val == 0xffffff00 for white background. - * (2) Three views are given: - * ~ the image with a fully opaque alpha - * ~ the alpha layer - * ~ the image as it would appear with a white background. - *- */ -PIX * -pixDisplayLayersRGBA(PIX *pixs, - l_uint32 val, - l_int32 maxw) -{ -l_int32 w, width; -l_float32 scalefact; -PIX *pix1, *pix2, *pixd; -PIXA *pixa; -PIXCMAP *cmap; - - PROCNAME("pixDisplayLayersRGBA"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - cmap = pixGetColormap(pixs); - if (!cmap && !(pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4)) - return (PIX *)ERROR_PTR("pixs not cmap and not 32 bpp rgba", - procName, NULL); - if ((w = pixGetWidth(pixs)) == 0) - return (PIX *)ERROR_PTR("pixs width 0 !!", procName, NULL); - - if (cmap) - pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_WITH_ALPHA); - else - pix1 = pixCopy(NULL, pixs); - - /* Scale if necessary so the output width is not larger than maxw */ - scalefact = (maxw == 0) ? 1.0 : L_MIN(1.0, (l_float32)(maxw) / w); - width = (l_int32)(scalefact * w); - - pixa = pixaCreate(3); - pixSetSpp(pix1, 3); - pixaAddPix(pixa, pix1, L_INSERT); /* show the rgb values */ - pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); - pix2 = pixConvertTo32(pix1); - pixaAddPix(pixa, pix2, L_INSERT); /* show the alpha channel */ - pixDestroy(&pix1); - pix1 = pixAlphaBlendUniform(pixs, (val & 0xffffff00)); - pixaAddPix(pixa, pix1, L_INSERT); /* with %val color bg showing */ - pixd = pixaDisplayTiledInRows(pixa, 32, width, scalefact, 0, 25, 2); - pixaDestroy(&pixa); - return pixd; -} - - -/*-------------------------------------------------------------* - * Color sample setting and extraction * - *-------------------------------------------------------------*/ -/*! - * \brief pixCreateRGBImage() - * - * \param[in] pixr 8 bpp red pix - * \param[in] pixg 8 bpp green pix - * \param[in] pixb 8 bpp blue pix - * \return 32 bpp pix, interleaved with 4 samples/pixel, - * or NULL on error - * - *- * Notes: - * (1) the 4th byte, sometimes called the "alpha channel", - * and which is often used for blending between different - * images, is left with 0 value. - * (2) see Note (4) in pix.h for details on storage of - * 8-bit samples within each 32-bit word. - * (3) This implementation, setting the r, g and b components - * sequentially, is much faster than setting them in parallel - * by constructing an RGB dest pixel and writing it to dest. - * The reason is there are many more cache misses when reading - * from 3 input images simultaneously. - *- */ -PIX * -pixCreateRGBImage(PIX *pixr, - PIX *pixg, - PIX *pixb) -{ -l_int32 wr, wg, wb, hr, hg, hb, dr, dg, db; -PIX *pixd; - - PROCNAME("pixCreateRGBImage"); - - if (!pixr) - return (PIX *)ERROR_PTR("pixr not defined", procName, NULL); - if (!pixg) - return (PIX *)ERROR_PTR("pixg not defined", procName, NULL); - if (!pixb) - return (PIX *)ERROR_PTR("pixb not defined", procName, NULL); - pixGetDimensions(pixr, &wr, &hr, &dr); - pixGetDimensions(pixg, &wg, &hg, &dg); - pixGetDimensions(pixb, &wb, &hb, &db); - if (dr != 8 || dg != 8 || db != 8) - return (PIX *)ERROR_PTR("input pix not all 8 bpp", procName, NULL); - if (wr != wg || wr != wb) - return (PIX *)ERROR_PTR("widths not the same", procName, NULL); - if (hr != hg || hr != hb) - return (PIX *)ERROR_PTR("heights not the same", procName, NULL); - - if ((pixd = pixCreate(wr, hr, 32)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixr); - pixSetRGBComponent(pixd, pixr, COLOR_RED); - pixSetRGBComponent(pixd, pixg, COLOR_GREEN); - pixSetRGBComponent(pixd, pixb, COLOR_BLUE); - - return pixd; -} - - -/*! - * \brief pixGetRGBComponent() - * - * \param[in] pixs 32 bpp, or colormapped - * \param[in] comp one of {COLOR_RED, COLOR_GREEN, COLOR_BLUE, - * L_ALPHA_CHANNEL} - * \return pixd the selected 8 bpp component image of the - * input 32 bpp image or NULL on error - * - *- * Notes: - * (1) Three calls to this function generate the r, g and b 8 bpp - * component images. This is much faster than generating the - * three images in parallel, by extracting a src pixel and setting - * the pixels of each component image from it. The reason is - * there are many more cache misses when writing to three - * output images simultaneously. - *- */ -PIX * -pixGetRGBComponent(PIX *pixs, - l_int32 comp) -{ -l_int32 i, j, w, h, wpls, wpld, val; -l_uint32 *lines, *lined; -l_uint32 *datas, *datad; -PIX *pixd; - - PROCNAME("pixGetRGBComponent"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetColormap(pixs)) - return pixGetRGBComponentCmap(pixs, comp); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (comp != COLOR_RED && comp != COLOR_GREEN && - comp != COLOR_BLUE && comp != L_ALPHA_CHANNEL) - return (PIX *)ERROR_PTR("invalid comp", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - if ((pixd = pixCreate(w, h, 8)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(lines + j, comp); - SET_DATA_BYTE(lined, j, val); - } - } - - return pixd; -} - - -/*! - * \brief pixSetRGBComponent() - * - * \param[in] pixd 32 bpp - * \param[in] pixs 8 bpp - * \param[in] comp one of the set: {COLOR_RED, COLOR_GREEN, - * COLOR_BLUE, L_ALPHA_CHANNEL} - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) This places the 8 bpp pixel in pixs into the - * specified component (properly interleaved) in pixd, - * (2) The two images are registered to the UL corner; the sizes - * need not be the same, but a warning is issued if they differ. - *- */ -l_ok -pixSetRGBComponent(PIX *pixd, - PIX *pixs, - l_int32 comp) -{ -l_uint8 srcbyte; -l_int32 i, j, w, h, ws, hs, wd, hd; -l_int32 wpls, wpld; -l_uint32 *lines, *lined; -l_uint32 *datas, *datad; - - PROCNAME("pixSetRGBComponent"); - - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetDepth(pixd) != 32) - return ERROR_INT("pixd not 32 bpp", procName, 1); - if (pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not 8 bpp", procName, 1); - if (comp != COLOR_RED && comp != COLOR_GREEN && - comp != COLOR_BLUE && comp != L_ALPHA_CHANNEL) - return ERROR_INT("invalid comp", procName, 1); - pixGetDimensions(pixs, &ws, &hs, NULL); - pixGetDimensions(pixd, &wd, &hd, NULL); - if (ws != wd || hs != hd) - L_WARNING("images sizes not equal\n", procName); - w = L_MIN(ws, wd); - h = L_MIN(hs, hd); - if (comp == L_ALPHA_CHANNEL) - pixSetSpp(pixd, 4); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - srcbyte = GET_DATA_BYTE(lines, j); - SET_DATA_BYTE(lined + j, comp, srcbyte); - } - } - - return 0; -} - - -/*! - * \brief pixGetRGBComponentCmap() - * - * \param[in] pixs colormapped - * \param[in] comp one of the set: {COLOR_RED, COLOR_GREEN, COLOR_BLUE} - * \return pixd the selected 8 bpp component image of the - * input cmapped image, or NULL on error - * - *- * Notes: - * (1) In leptonica, we do not support alpha in colormaps. - *- */ -PIX * -pixGetRGBComponentCmap(PIX *pixs, - l_int32 comp) -{ -l_int32 i, j, w, h, val, index; -l_int32 wplc, wpld; -l_uint32 *linec, *lined; -l_uint32 *datac, *datad; -PIX *pixc, *pixd; -PIXCMAP *cmap; -RGBA_QUAD *cta; - - PROCNAME("pixGetRGBComponentCmap"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if ((cmap = pixGetColormap(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixs not cmapped", procName, NULL); - if (comp == L_ALPHA_CHANNEL) - return (PIX *)ERROR_PTR("alpha in cmaps not supported", procName, NULL); - if (comp != COLOR_RED && comp != COLOR_GREEN && comp != COLOR_BLUE) - return (PIX *)ERROR_PTR("invalid comp", procName, NULL); - - /* If not 8 bpp, make a cmapped 8 bpp pix */ - if (pixGetDepth(pixs) == 8) - pixc = pixClone(pixs); - else - pixc = pixConvertTo8(pixs, TRUE); - - pixGetDimensions(pixs, &w, &h, NULL); - if ((pixd = pixCreateNoInit(w, h, 8)) == NULL) { - pixDestroy(&pixc); - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - pixCopyResolution(pixd, pixs); - wplc = pixGetWpl(pixc); - wpld = pixGetWpl(pixd); - datac = pixGetData(pixc); - datad = pixGetData(pixd); - cta = (RGBA_QUAD *)cmap->array; - - for (i = 0; i < h; i++) { - linec = datac + i * wplc; - lined = datad + i * wpld; - if (comp == COLOR_RED) { - for (j = 0; j < w; j++) { - index = GET_DATA_BYTE(linec, j); - val = cta[index].red; - SET_DATA_BYTE(lined, j, val); - } - } else if (comp == COLOR_GREEN) { - for (j = 0; j < w; j++) { - index = GET_DATA_BYTE(linec, j); - val = cta[index].green; - SET_DATA_BYTE(lined, j, val); - } - } else if (comp == COLOR_BLUE) { - for (j = 0; j < w; j++) { - index = GET_DATA_BYTE(linec, j); - val = cta[index].blue; - SET_DATA_BYTE(lined, j, val); - } - } - } - - pixDestroy(&pixc); - return pixd; -} - - -/*! - * \brief pixCopyRGBComponent() - * - * \param[in] pixd 32 bpp - * \param[in] pixs 32 bpp - * \param[in] comp one of the set: {COLOR_RED, COLOR_GREEN, - * COLOR_BLUE, L_ALPHA_CHANNEL} - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) The two images are registered to the UL corner. The sizes - * are usually the same, and a warning is issued if they differ. - *- */ -l_ok -pixCopyRGBComponent(PIX *pixd, - PIX *pixs, - l_int32 comp) -{ -l_int32 i, j, w, h, ws, hs, wd, hd, val; -l_int32 wpls, wpld; -l_uint32 *lines, *lined; -l_uint32 *datas, *datad; - - PROCNAME("pixCopyRGBComponent"); - - if (!pixd && pixGetDepth(pixd) != 32) - return ERROR_INT("pixd not defined or not 32 bpp", procName, 1); - if (!pixs && pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); - if (comp != COLOR_RED && comp != COLOR_GREEN && - comp != COLOR_BLUE && comp != L_ALPHA_CHANNEL) - return ERROR_INT("invalid component", procName, 1); - pixGetDimensions(pixs, &ws, &hs, NULL); - pixGetDimensions(pixd, &wd, &hd, NULL); - if (ws != wd || hs != hd) - L_WARNING("images sizes not equal\n", procName); - w = L_MIN(ws, wd); - h = L_MIN(hs, hd); - if (comp == L_ALPHA_CHANNEL) - pixSetSpp(pixd, 4); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(lines + j, comp); - SET_DATA_BYTE(lined + j, comp, val); - } - } - return 0; -} - - -/*! - * \brief composeRGBPixel() - * - * \param[in] rval, gval, bval - * \param[out] ppixel 32-bit pixel - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) All channels are 8 bits: the input values must be between - * 0 and 255. For speed, this is not enforced by masking - * with 0xff before shifting. - * (2) A slower implementation uses macros: - * SET_DATA_BYTE(ppixel, COLOR_RED, rval); - * SET_DATA_BYTE(ppixel, COLOR_GREEN, gval); - * SET_DATA_BYTE(ppixel, COLOR_BLUE, bval); - *- */ -l_ok -composeRGBPixel(l_int32 rval, - l_int32 gval, - l_int32 bval, - l_uint32 *ppixel) -{ - PROCNAME("composeRGBPixel"); - - if (!ppixel) - return ERROR_INT("&pixel not defined", procName, 1); - - *ppixel = ((l_uint32)rval << L_RED_SHIFT) | - ((l_uint32)gval << L_GREEN_SHIFT) | - ((l_uint32)bval << L_BLUE_SHIFT); - return 0; -} - - -/*! - * \brief composeRGBAPixel() - * - * \param[in] rval, gval, bval, aval - * \param[out] ppixel 32-bit pixel - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) All channels are 8 bits: the input values must be between - * 0 and 255. For speed, this is not enforced by masking - * with 0xff before shifting. - *- */ -l_ok -composeRGBAPixel(l_int32 rval, - l_int32 gval, - l_int32 bval, - l_int32 aval, - l_uint32 *ppixel) -{ - PROCNAME("composeRGBAPixel"); - - if (!ppixel) - return ERROR_INT("&pixel not defined", procName, 1); - - *ppixel = ((l_uint32)rval << L_RED_SHIFT) | - ((l_uint32)gval << L_GREEN_SHIFT) | - ((l_uint32)bval << L_BLUE_SHIFT) | - aval; - return 0; -} - - -/*! - * \brief extractRGBValues() - * - * \param[in] pixel 32 bit - * \param[out] prval [optional] red component - * \param[out] pgval [optional] green component - * \param[out] pbval [optional] blue component - * \return void - * - *- * Notes: - * (1) A slower implementation uses macros: - * *prval = GET_DATA_BYTE(&pixel, COLOR_RED); - * *pgval = GET_DATA_BYTE(&pixel, COLOR_GREEN); - * *pbval = GET_DATA_BYTE(&pixel, COLOR_BLUE); - *- */ -void -extractRGBValues(l_uint32 pixel, - l_int32 *prval, - l_int32 *pgval, - l_int32 *pbval) -{ - if (prval) *prval = (pixel >> L_RED_SHIFT) & 0xff; - if (pgval) *pgval = (pixel >> L_GREEN_SHIFT) & 0xff; - if (pbval) *pbval = (pixel >> L_BLUE_SHIFT) & 0xff; - return; -} - - -/*! - * \brief extractRGBAValues() - * - * \param[in] pixel 32 bit - * \param[out] prval [optional] red component - * \param[out] pgval [optional] green component - * \param[out] pbval [optional] blue component - * \param[out] paval [optional] alpha component - * \return void - */ -void -extractRGBAValues(l_uint32 pixel, - l_int32 *prval, - l_int32 *pgval, - l_int32 *pbval, - l_int32 *paval) -{ - if (prval) *prval = (pixel >> L_RED_SHIFT) & 0xff; - if (pgval) *pgval = (pixel >> L_GREEN_SHIFT) & 0xff; - if (pbval) *pbval = (pixel >> L_BLUE_SHIFT) & 0xff; - if (paval) *paval = (pixel >> L_ALPHA_SHIFT) & 0xff; - return; -} - - -/*! - * \brief extractMinMaxComponent() - * - * \param[in] pixel 32 bpp RGB - * \param[in] type L_CHOOSE_MIN or L_CHOOSE_MAX - * \return component in range [0 ... 255], or NULL on error - */ -l_int32 -extractMinMaxComponent(l_uint32 pixel, - l_int32 type) -{ -l_int32 rval, gval, bval, val; - - extractRGBValues(pixel, &rval, &gval, &bval); - if (type == L_CHOOSE_MIN) { - val = L_MIN(rval, gval); - val = L_MIN(val, bval); - } else { /* type == L_CHOOSE_MAX */ - val = L_MAX(rval, gval); - val = L_MAX(val, bval); - } - return val; -} - - -/*! - * \brief pixGetRGBLine() - * - * \param[in] pixs 32 bpp - * \param[in] row - * \param[in] bufr array of red samples; size w bytes - * \param[in] bufg array of green samples; size w bytes - * \param[in] bufb array of blue samples; size w bytes - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) This puts rgb components from the input line in pixs - * into the given buffers. - *- */ -l_ok -pixGetRGBLine(PIX *pixs, - l_int32 row, - l_uint8 *bufr, - l_uint8 *bufg, - l_uint8 *bufb) -{ -l_uint32 *lines; -l_int32 j, w, h; -l_int32 wpls; - - PROCNAME("pixGetRGBLine"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not 32 bpp", procName, 1); - if (!bufr || !bufg || !bufb) - return ERROR_INT("buffer not defined", procName, 1); - - pixGetDimensions(pixs, &w, &h, NULL); - if (row < 0 || row >= h) - return ERROR_INT("row out of bounds", procName, 1); - wpls = pixGetWpl(pixs); - lines = pixGetData(pixs) + row * wpls; - - for (j = 0; j < w; j++) { - bufr[j] = GET_DATA_BYTE(lines + j, COLOR_RED); - bufg[j] = GET_DATA_BYTE(lines + j, COLOR_GREEN); - bufb[j] = GET_DATA_BYTE(lines + j, COLOR_BLUE); - } - - return 0; -} - - -/*-------------------------------------------------------------* - * Raster line pixel setter * - *-------------------------------------------------------------*/ -/*! - * \brief setLineDataVal() - * - * \param[in] line ptr to first word in raster line data - * \param[in] j index of pixels into the raster line - * \param[in] d depth of the pixel - * \param[in] val pixel value to be set - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This is a convenience function to set a pixel value in a - * raster line where the depth of the image can have different - * values (1, 2, 4, 8, 16 or 32). - *- */ -l_ok -setLineDataVal(l_uint32 *line, - l_int32 j, - l_int32 d, - l_uint32 val) -{ - PROCNAME("setLineDataVal"); - - if (!line) - return ERROR_INT("line not defined", procName, 1); - if (j < 0) - return ERROR_INT("j must be >= 0", procName, 1); - if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) - return ERROR_INT("invalid d", procName, 1); - - if (d == 1) - SET_DATA_BIT_VAL(line, j, val); - else if (d == 2) - SET_DATA_DIBIT(line, j, val); - else if (d == 4) - SET_DATA_QBIT(line, j, val); - else if (d == 8) - SET_DATA_BYTE(line, j, val); - else if (d == 16) - SET_DATA_TWO_BYTES(line, j, val); - else /* d == 32 */ - *(line + j) = val; - return 0; -} - - -/*-------------------------------------------------------------* - * Pixel endian conversion * - *-------------------------------------------------------------*/ -/*! - * \brief pixEndianByteSwapNew() - * - * \param[in] pixs - * \return pixd, or NULL on error - * - *- * Notes: - * (1) This is used to convert the data in a pix to a - * serialized byte buffer in raster order, and, for RGB, - * in order RGBA. This requires flipping bytes within - * each 32-bit word for little-endian platforms, because the - * words have a MSB-to-the-left rule, whereas byte raster-order - * requires the left-most byte in each word to be byte 0. - * For big-endians, no swap is necessary, so this returns a clone. - * (2) Unlike pixEndianByteSwap(), which swaps the bytes in-place, - * this returns a new pix (or a clone). We provide this - * because often when serialization is done, the source - * pix needs to be restored to canonical little-endian order, - * and this requires a second byte swap. In such a situation, - * it is twice as fast to make a new pix in big-endian order, - * use it, and destroy it. - *- */ -PIX * -pixEndianByteSwapNew(PIX *pixs) -{ -l_uint32 *datas, *datad; -l_int32 i, j, h, wpl; -l_uint32 word; -PIX *pixd; - - PROCNAME("pixEndianByteSwapNew"); - -#ifdef L_BIG_ENDIAN - - return pixClone(pixs); - -#else /* L_LITTLE_ENDIAN */ - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - datas = pixGetData(pixs); - wpl = pixGetWpl(pixs); - h = pixGetHeight(pixs); - pixd = pixCreateTemplate(pixs); - datad = pixGetData(pixd); - for (i = 0; i < h; i++) { - for (j = 0; j < wpl; j++, datas++, datad++) { - word = *datas; - *datad = (word >> 24) | - ((word >> 8) & 0x0000ff00) | - ((word << 8) & 0x00ff0000) | - (word << 24); - } - } - - return pixd; - -#endif /* L_BIG_ENDIAN */ - -} - - -/*! - * \brief pixEndianByteSwap() - * - * \param[in] pixs - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This is used on little-endian platforms to swap - * the bytes within a word; bytes 0 and 3 are swapped, - * and bytes 1 and 2 are swapped. - * (2) This is required for little-endians in situations - * where we convert from a serialized byte order that is - * in raster order, as one typically has in file formats, - * to one with MSB-to-the-left in each 32-bit word, or v.v. - * See pix.h for a description of the canonical format - * (MSB-to-the left) that is used for both little-endian - * and big-endian platforms. For big-endians, the - * MSB-to-the-left word order has the bytes in raster - * order when serialized, so no byte flipping is required. - *- */ -l_ok -pixEndianByteSwap(PIX *pixs) -{ -l_uint32 *data; -l_int32 i, j, h, wpl; -l_uint32 word; - - PROCNAME("pixEndianByteSwap"); - -#ifdef L_BIG_ENDIAN - - return 0; - -#else /* L_LITTLE_ENDIAN */ - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - h = pixGetHeight(pixs); - for (i = 0; i < h; i++) { - for (j = 0; j < wpl; j++, data++) { - word = *data; - *data = (word >> 24) | - ((word >> 8) & 0x0000ff00) | - ((word << 8) & 0x00ff0000) | - (word << 24); - } - } - - return 0; - -#endif /* L_BIG_ENDIAN */ - -} - - -/*! - * \brief lineEndianByteSwap() - * - * \param[in] datad dest byte array data, reordered on little-endians - * \param[in] datas a src line of pix data) - * \param[in] wpl number of 32 bit words in the line - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This is used on little-endian platforms to swap - * the bytes within each word in the line of image data. - * Bytes 0 <==> 3 and 1 <==> 2 are swapped in the dest - * byte array data8d, relative to the pix data in datas. - * (2) The bytes represent 8 bit pixel values. They are swapped - * for little endians so that when the dest array datad - * is addressed by bytes, the pixels are chosen sequentially - * from left to right in the image. - *- */ -l_int32 -lineEndianByteSwap(l_uint32 *datad, - l_uint32 *datas, - l_int32 wpl) -{ -l_int32 j; -l_uint32 word; - - PROCNAME("lineEndianByteSwap"); - - if (!datad || !datas) - return ERROR_INT("datad and datas not both defined", procName, 1); - -#ifdef L_BIG_ENDIAN - - memcpy(datad, datas, 4 * wpl); - return 0; - -#else /* L_LITTLE_ENDIAN */ - - for (j = 0; j < wpl; j++, datas++, datad++) { - word = *datas; - *datad = (word >> 24) | - ((word >> 8) & 0x0000ff00) | - ((word << 8) & 0x00ff0000) | - (word << 24); - } - return 0; - -#endif /* L_BIG_ENDIAN */ - -} - - -/*! - * \brief pixEndianTwoByteSwapNew() - * - * \param[in] pixs - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This is used on little-endian platforms to swap the - * 2-byte entities within a 32-bit word. - * (2) This is equivalent to a full byte swap, as performed - * by pixEndianByteSwap(), followed by byte swaps in - * each of the 16-bit entities separately. - * (3) Unlike pixEndianTwoByteSwap(), which swaps the shorts in-place, - * this returns a new pix (or a clone). We provide this - * to avoid having to swap twice in situations where the input - * pix must be restored to canonical little-endian order. - *- */ -PIX * -pixEndianTwoByteSwapNew(PIX *pixs) -{ -l_uint32 *datas, *datad; -l_int32 i, j, h, wpl; -l_uint32 word; -PIX *pixd; - - PROCNAME("pixEndianTwoByteSwapNew"); - -#ifdef L_BIG_ENDIAN - - return pixClone(pixs); - -#else /* L_LITTLE_ENDIAN */ - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - datas = pixGetData(pixs); - wpl = pixGetWpl(pixs); - h = pixGetHeight(pixs); - pixd = pixCreateTemplate(pixs); - datad = pixGetData(pixd); - for (i = 0; i < h; i++) { - for (j = 0; j < wpl; j++, datas++, datad++) { - word = *datas; - *datad = (word << 16) | (word >> 16); - } - } - - return pixd; - -#endif /* L_BIG_ENDIAN */ - -} - - -/*! - * \brief pixEndianTwoByteSwap() - * - * \param[in] pixs - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This is used on little-endian platforms to swap the - * 2-byte entities within a 32-bit word. - * (2) This is equivalent to a full byte swap, as performed - * by pixEndianByteSwap(), followed by byte swaps in - * each of the 16-bit entities separately. - *- */ -l_ok -pixEndianTwoByteSwap(PIX *pixs) -{ -l_uint32 *data; -l_int32 i, j, h, wpl; -l_uint32 word; - - PROCNAME("pixEndianTwoByteSwap"); - -#ifdef L_BIG_ENDIAN - - return 0; - -#else /* L_LITTLE_ENDIAN */ - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - h = pixGetHeight(pixs); - for (i = 0; i < h; i++) { - for (j = 0; j < wpl; j++, data++) { - word = *data; - *data = (word << 16) | (word >> 16); - } - } - - return 0; - -#endif /* L_BIG_ENDIAN */ - -} - - -/*-------------------------------------------------------------* - * Extract raster data as binary string * - *-------------------------------------------------------------*/ -/*! - * \brief pixGetRasterData() - * - * \param[in] pixs 1, 8, 32 bpp - * \param[out] pdata raster data in memory - * \param[out] pnbytes number of bytes in data string - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This returns the raster data as a byte string, padded to the - * byte. For 1 bpp, the first pixel is the MSbit in the first byte. - * For rgb, the bytes are in (rgb) order. This is the format - * required for flate encoding of pixels in a PostScript file. - *- */ -l_ok -pixGetRasterData(PIX *pixs, - l_uint8 **pdata, - size_t *pnbytes) -{ -l_int32 w, h, d, wpl, i, j, rval, gval, bval; -l_int32 databpl; /* bytes for each raster line in returned data */ -l_uint8 *line, *data; /* packed data in returned array */ -l_uint32 *rline, *rdata; /* data in pix raster */ - - PROCNAME("pixGetRasterData"); - - if (pdata) *pdata = NULL; - if (pnbytes) *pnbytes = 0; - if (!pdata || !pnbytes) - return ERROR_INT("&data and &nbytes not both defined", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) - return ERROR_INT("depth not in {1,2,4,8,16,32}", procName, 1); - rdata = pixGetData(pixs); - wpl = pixGetWpl(pixs); - if (d == 1) - databpl = (w + 7) / 8; - else if (d == 2) - databpl = (w + 3) / 4; - else if (d == 4) - databpl = (w + 1) / 2; - else if (d == 8 || d == 16) - databpl = w * (d / 8); - else /* d == 32 bpp rgb */ - databpl = 3 * w; - if ((data = (l_uint8 *)LEPT_CALLOC((size_t)databpl * h, sizeof(l_uint8))) - == NULL) - return ERROR_INT("data not allocated", procName, 1); - *pdata = data; - *pnbytes = (size_t)databpl * h; - - for (i = 0; i < h; i++) { - rline = rdata + i * wpl; - line = data + i * databpl; - if (d <= 8) { - for (j = 0; j < databpl; j++) - line[j] = GET_DATA_BYTE(rline, j); - } else if (d == 16) { - for (j = 0; j < w; j++) - line[2 * j] = GET_DATA_TWO_BYTES(rline, j); - } else { /* d == 32 bpp rgb */ - for (j = 0; j < w; j++) { - extractRGBValues(rline[j], &rval, &gval, &bval); - *(line + 3 * j) = rval; - *(line + 3 * j + 1) = gval; - *(line + 3 * j + 2) = bval; - } - } - } - - return 0; -} - - -/*-------------------------------------------------------------* - * Test alpha component opaqueness * - *-------------------------------------------------------------*/ -/*! - * \brief pixAlphaIsOpaque() - * - * \param[in] pix 32 bpp, spp == 4 - * \param[out] popaque 1 if spp == 4 and all alpha component - * values are 255 (opaque); 0 otherwise - * \return 0 if OK, 1 on error - * Notes: - * 1) On error, opaque is returned as 0 (FALSE). - */ -l_ok -pixAlphaIsOpaque(PIX *pix, - l_int32 *popaque) -{ -l_int32 w, h, wpl, i, j, alpha; -l_uint32 *data, *line; - - PROCNAME("pixAlphaIsOpaque"); - - if (!popaque) - return ERROR_INT("&opaque not defined", procName, 1); - *popaque = FALSE; - if (!pix) - return ERROR_INT("&pix not defined", procName, 1); - if (pixGetDepth(pix) != 32) - return ERROR_INT("&pix not 32 bpp", procName, 1); - if (pixGetSpp(pix) != 4) - return ERROR_INT("&pix not 4 spp", procName, 1); - - data = pixGetData(pix); - wpl = pixGetWpl(pix); - pixGetDimensions(pix, &w, &h, NULL); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - alpha = GET_DATA_BYTE(line + j, L_ALPHA_CHANNEL); - if (alpha ^ 0xff) /* not opaque */ - return 0; - } - } - - *popaque = TRUE; - return 0; -} - - -/*-------------------------------------------------------------* - * Setup helpers for 8 bpp byte processing * - *-------------------------------------------------------------*/ -/*! - * \brief pixSetupByteProcessing() - * - * \param[in] pix 8 bpp, no colormap - * \param[out] pw [optional] width - * \param[out] ph [optional] height - * \return line ptr array, or NULL on error - * - *- * Notes: - * (1) This is a simple helper for processing 8 bpp images with - * direct byte access. It can swap byte order within each word. - * (2) After processing, you must call pixCleanupByteProcessing(), - * which frees the lineptr array and restores byte order. - * (3) Usage: - * l_uint8 **lineptrs = pixSetupByteProcessing(pix, &w, &h); - * for (i = 0; i < h; i++) { - * l_uint8 *line = lineptrs[i]; - * for (j = 0; j < w; j++) { - * val = line[j]; - * ... - * } - * } - * pixCleanupByteProcessing(pix, lineptrs); - *- */ -l_uint8 ** -pixSetupByteProcessing(PIX *pix, - l_int32 *pw, - l_int32 *ph) -{ -l_int32 w, h; - - PROCNAME("pixSetupByteProcessing"); - - if (pw) *pw = 0; - if (ph) *ph = 0; - if (!pix || pixGetDepth(pix) != 8) - return (l_uint8 **)ERROR_PTR("pix not defined or not 8 bpp", - procName, NULL); - pixGetDimensions(pix, &w, &h, NULL); - if (pw) *pw = w; - if (ph) *ph = h; - if (pixGetColormap(pix)) - return (l_uint8 **)ERROR_PTR("pix has colormap", procName, NULL); - - pixEndianByteSwap(pix); - return (l_uint8 **)pixGetLinePtrs(pix, NULL); -} - - -/*! - * \brief pixCleanupByteProcessing() - * - * \param[in] pix 8 bpp, no colormap - * \param[in] lineptrs ptrs to the beginning of each raster line of data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This must be called after processing that was initiated - * by pixSetupByteProcessing() has finished. - *- */ -l_ok -pixCleanupByteProcessing(PIX *pix, - l_uint8 **lineptrs) -{ - PROCNAME("pixCleanupByteProcessing"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!lineptrs) - return ERROR_INT("lineptrs not defined", procName, 1); - - pixEndianByteSwap(pix); - LEPT_FREE(lineptrs); - return 0; -} - - -/*------------------------------------------------------------------------* - * Setting parameters for antialias masking with alpha transforms * - *------------------------------------------------------------------------*/ -/*! - * \brief l_setAlphaMaskBorder() - * - * \param[in] val1, val2 in [0.0 ... 1.0] - * \return void - * - *- * Notes: - * (1) This sets the opacity values used to generate the two outer - * boundary rings in the alpha mask associated with geometric - * transforms such as pixRotateWithAlpha(). - * (2) The default values are val1 = 0.0 (completely transparent - * in the outermost ring) and val2 = 0.5 (half transparent - * in the second ring). When the image is blended, this - * completely removes the outer ring (shrinking the image by - * 2 in each direction), and alpha-blends with 0.5 the second ring. - * Using val1 = 0.25 and val2 = 0.75 gives a slightly more - * blurred border, with no perceptual difference at screen resolution. - * (3) The actual mask values are found by multiplying these - * normalized opacity values by 255. - *- */ -void -l_setAlphaMaskBorder(l_float32 val1, - l_float32 val2) -{ - val1 = L_MAX(0.0, L_MIN(1.0, val1)); - val2 = L_MAX(0.0, L_MIN(1.0, val2)); - AlphaMaskBorderVals[0] = val1; - AlphaMaskBorderVals[1] = val2; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix3.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix3.c deleted file mode 100644 index 20ab924c..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix3.c +++ /dev/null @@ -1,3717 +0,0 @@ -/*====================================================================* - - 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 pix3.c - *- * - * This file has these operations: - * - * (1) Mask-directed operations - * (2) Full-image bit-logical operations - * (3) Foreground pixel counting operations on 1 bpp images - * (4) Average and variance of pixel values - * (5) Mirrored tiling of a smaller image - * - * - * Masked operations - * l_int32 pixSetMasked() - * l_int32 pixSetMaskedGeneral() - * l_int32 pixCombineMasked() - * l_int32 pixCombineMaskedGeneral() - * l_int32 pixPaintThroughMask() - * l_int32 pixCopyWithBoxa() -- this is boxa-directed - * PIX *pixPaintSelfThroughMask() - * PIX *pixMakeMaskFromVal() - * PIX *pixMakeMaskFromLUT() - * PIX *pixMakeArbMaskFromRGB() - * PIX *pixSetUnderTransparency() - * PIX *pixMakeAlphaFromMask() - * l_int32 pixGetColorNearMaskBoundary() - * PIX *pixDisplaySelectedPixels() -- for debugging - * - * One and two-image boolean operations on arbitrary depth images - * PIX *pixInvert() - * PIX *pixOr() - * PIX *pixAnd() - * PIX *pixXor() - * PIX *pixSubtract() - * - * Foreground pixel counting in 1 bpp images - * l_int32 pixZero() - * l_int32 pixForegroundFraction() - * NUMA *pixaCountPixels() - * l_int32 pixCountPixels() - * l_int32 pixCountPixelsInRect() - * NUMA *pixCountByRow() - * NUMA *pixCountByColumn() - * NUMA *pixCountPixelsByRow() - * NUMA *pixCountPixelsByColumn() - * l_int32 pixCountPixelsInRow() - * NUMA *pixGetMomentByColumn() - * l_int32 pixThresholdPixelSum() - * l_int32 *makePixelSumTab8() - * l_int32 *makePixelCentroidTab8() - * - * Average of pixel values in gray images - * NUMA *pixAverageByRow() - * NUMA *pixAverageByColumn() - * l_int32 pixAverageInRect() - * - * Average of pixel values in RGB images - * l_int32 pixAverageInRectRGB() - * - * Variance of pixel values in gray images - * NUMA *pixVarianceByRow() - * NUMA *pixVarianceByColumn() - * l_int32 pixVarianceInRect() - * - * Average of absolute value of pixel differences in gray images - * NUMA *pixAbsDiffByRow() - * NUMA *pixAbsDiffByColumn() - * l_int32 pixAbsDiffInRect() - * l_int32 pixAbsDiffOnLine() - * - * Count of pixels with specific value - * l_int32 pixCountArbInRect() - * - * Mirrored tiling - * PIX *pixMirroredTiling() - * - * Representative tile near but outside region - * l_int32 pixFindRepCloseTile() - * - * Static helper function - * static BOXA *findTileRegionsForSearch() - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include -#include "allheaders.h" - -static BOXA *findTileRegionsForSearch(BOX *box, l_int32 w, l_int32 h, - l_int32 searchdir, l_int32 mindist, - l_int32 tsize, l_int32 ntiles); - -#ifndef NO_CONSOLE_IO -#define EQUAL_SIZE_WARNING 0 -#endif /* ~NO_CONSOLE_IO */ - -/*-------------------------------------------------------------* - * Masked operations * - *-------------------------------------------------------------*/ -/*! - * \brief pixSetMasked() - * - * \param[in] pixd 1, 2, 4, 8, 16 or 32 bpp; or colormapped - * \param[in] pixm [optional] 1 bpp mask; no operation if NULL - * \param[in] val value to set at each masked pixel - * \return 0 if OK; 1 on error - * - * - * Notes: - * (1) In-place operation. - * (2) NOTE: For cmapped images, this calls pixSetMaskedCmap(). - * %val must be the 32-bit color representation of the RGB pixel. - * It is not the index into the colormap! - * (2) If pixm == NULL, a warning is given. - * (3) This is an implicitly aligned operation, where the UL - * corners of pixd and pixm coincide. A warning is - * issued if the two image sizes differ significantly, - * but the operation proceeds. - * (4) Each pixel in pixd that co-locates with an ON pixel - * in pixm is set to the specified input value. - * Other pixels in pixd are not changed. - * (5) You can visualize this as painting the color through - * the mask, as a stencil. - * (6) If you do not want to have the UL corners aligned, - * use the function pixSetMaskedGeneral(), which requires - * you to input the UL corner of pixm relative to pixd. - * (7) Implementation details: see comments in pixPaintThroughMask() - * for when we use rasterop to do the painting. - *- */ -l_ok -pixSetMasked(PIX *pixd, - PIX *pixm, - l_uint32 val) -{ -l_int32 wd, hd, wm, hm, w, h, d, wpld, wplm; -l_int32 i, j, rval, gval, bval; -l_uint32 *datad, *datam, *lined, *linem; - - PROCNAME("pixSetMasked"); - - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - if (!pixm) { - L_WARNING("no mask; nothing to do\n", procName); - return 0; - } - if (pixGetColormap(pixd)) { - extractRGBValues(val, &rval, &gval, &bval); - return pixSetMaskedCmap(pixd, pixm, 0, 0, rval, gval, bval); - } - - if (pixGetDepth(pixm) != 1) - return ERROR_INT("pixm not 1 bpp", procName, 1); - d = pixGetDepth(pixd); - if (d == 1) - val &= 1; - else if (d == 2) - val &= 3; - else if (d == 4) - val &= 0x0f; - else if (d == 8) - val &= 0xff; - else if (d == 16) - val &= 0xffff; - else if (d != 32) - return ERROR_INT("pixd not 1, 2, 4, 8, 16 or 32 bpp", procName, 1); - pixGetDimensions(pixm, &wm, &hm, NULL); - - /* If d == 1, use rasterop; it's about 25x faster */ - if (d == 1) { - if (val == 0) { - PIX *pixmi = pixInvert(NULL, pixm); - pixRasterop(pixd, 0, 0, wm, hm, PIX_MASK, pixmi, 0, 0); - pixDestroy(&pixmi); - } else { /* val == 1 */ - pixRasterop(pixd, 0, 0, wm, hm, PIX_PAINT, pixm, 0, 0); - } - return 0; - } - - /* For d < 32, use rasterop for val == 0 (black); ~3x faster. */ - if (d < 32 && val == 0) { - PIX *pixmd = pixUnpackBinary(pixm, d, 1); - pixRasterop(pixd, 0, 0, wm, hm, PIX_MASK, pixmd, 0, 0); - pixDestroy(&pixmd); - return 0; - } - - /* For d < 32, use rasterop for val == maxval (white); ~3x faster. */ - if (d < 32 && val == ((1 << d) - 1)) { - PIX *pixmd = pixUnpackBinary(pixm, d, 0); - pixRasterop(pixd, 0, 0, wm, hm, PIX_PAINT, pixmd, 0, 0); - pixDestroy(&pixmd); - return 0; - } - - pixGetDimensions(pixd, &wd, &hd, &d); - w = L_MIN(wd, wm); - h = L_MIN(hd, hm); - if (L_ABS(wd - wm) > 7 || L_ABS(hd - hm) > 7) /* allow a small tolerance */ - L_WARNING("pixd and pixm sizes differ\n", procName); - - datad = pixGetData(pixd); - datam = pixGetData(pixm); - wpld = pixGetWpl(pixd); - wplm = pixGetWpl(pixm); - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - linem = datam + i * wplm; - for (j = 0; j < w; j++) { - if (GET_DATA_BIT(linem, j)) { - switch(d) - { - case 2: - SET_DATA_DIBIT(lined, j, val); - break; - case 4: - SET_DATA_QBIT(lined, j, val); - break; - case 8: - SET_DATA_BYTE(lined, j, val); - break; - case 16: - SET_DATA_TWO_BYTES(lined, j, val); - break; - case 32: - *(lined + j) = val; - break; - default: - return ERROR_INT("shouldn't get here", procName, 1); - } - } - } - } - - return 0; -} - - -/*! - * \brief pixSetMaskedGeneral() - * - * \param[in] pixd 8, 16 or 32 bpp - * \param[in] pixm [optional] 1 bpp mask; no operation if null - * \param[in] val value to set at each masked pixel - * \param[in] x, y location of UL corner of pixm relative to pixd; - * can be negative - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) This is an in-place operation. - * (2) Alignment is explicit. If you want the UL corners of - * the two images to be aligned, use pixSetMasked(). - * (3) A typical use would be painting through the foreground - * of a small binary mask pixm, located somewhere on a - * larger pixd. Other pixels in pixd are not changed. - * (4) You can visualize this as painting the color through - * the mask, as a stencil. - * (5) This uses rasterop to handle clipping and different depths of pixd. - * (6) If pixd has a colormap, you should call pixPaintThroughMask(). - * (7) Why is this function here, if pixPaintThroughMask() does the - * same thing, and does it more generally? I've retained it here - * to show how one can paint through a mask using only full - * image rasterops, rather than pixel peeking in pixm and poking - * in pixd. It's somewhat baroque, but I found it amusing. - *- */ -l_ok -pixSetMaskedGeneral(PIX *pixd, - PIX *pixm, - l_uint32 val, - l_int32 x, - l_int32 y) -{ -l_int32 wm, hm, d; -PIX *pixmu, *pixc; - - PROCNAME("pixSetMaskedGeneral"); - - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - if (!pixm) /* nothing to do */ - return 0; - - d = pixGetDepth(pixd); - if (d != 8 && d != 16 && d != 32) - return ERROR_INT("pixd not 8, 16 or 32 bpp", procName, 1); - if (pixGetDepth(pixm) != 1) - return ERROR_INT("pixm not 1 bpp", procName, 1); - - /* Unpack binary to depth d, with inversion: 1 --> 0, 0 --> 0xff... */ - if ((pixmu = pixUnpackBinary(pixm, d, 1)) == NULL) - return ERROR_INT("pixmu not made", procName, 1); - - /* Clear stenciled pixels in pixd */ - pixGetDimensions(pixm, &wm, &hm, NULL); - pixRasterop(pixd, x, y, wm, hm, PIX_SRC & PIX_DST, pixmu, 0, 0); - - /* Generate image with requisite color */ - if ((pixc = pixCreateTemplate(pixmu)) == NULL) { - pixDestroy(&pixmu); - return ERROR_INT("pixc not made", procName, 1); - } - pixSetAllArbitrary(pixc, val); - - /* Invert stencil mask, and paint color color into stencil */ - pixInvert(pixmu, pixmu); - pixAnd(pixmu, pixmu, pixc); - - /* Finally, repaint stenciled pixels, with val, in pixd */ - pixRasterop(pixd, x, y, wm, hm, PIX_SRC | PIX_DST, pixmu, 0, 0); - - pixDestroy(&pixmu); - pixDestroy(&pixc); - return 0; -} - - -/*! - * \brief pixCombineMasked() - * - * \param[in] pixd 1 bpp, 8 bpp gray or 32 bpp rgb; no cmap - * \param[in] pixs 1 bpp, 8 bpp gray or 32 bpp rgb; no cmap - * \param[in] pixm [optional] 1 bpp mask; no operation if NULL - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) In-place operation; pixd is changed. - * (2) This sets each pixel in pixd that co-locates with an ON - * pixel in pixm to the corresponding value of pixs. - * (3) pixs and pixd must be the same depth and not colormapped. - * (4) All three input pix are aligned at the UL corner, and the - * operation is clipped to the intersection of all three images. - * (5) If pixm == NULL, it's a no-op. - * (6) Implementation: see notes in pixCombineMaskedGeneral(). - * For 8 bpp selective masking, you might guess that it - * would be faster to generate an 8 bpp version of pixm, - * using pixConvert1To8(pixm, 0, 255), and then use a - * general combine operation - * d = (d & ~m) | (s & m) - * on a word-by-word basis. Not always. The word-by-word - * combine takes a time that is independent of the mask data. - * If the mask is relatively sparse, the byte-check method - * is actually faster! - *- */ -l_ok -pixCombineMasked(PIX *pixd, - PIX *pixs, - PIX *pixm) -{ -l_int32 w, h, d, ws, hs, ds, wm, hm, dm, wmin, hmin; -l_int32 wpl, wpls, wplm, i, j, val; -l_uint32 *data, *datas, *datam, *line, *lines, *linem; -PIX *pixt; - - PROCNAME("pixCombineMasked"); - - if (!pixm) /* nothing to do */ - return 0; - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - pixGetDimensions(pixd, &w, &h, &d); - pixGetDimensions(pixs, &ws, &hs, &ds); - pixGetDimensions(pixm, &wm, &hm, &dm); - if (d != ds) - return ERROR_INT("pixs and pixd depths differ", procName, 1); - if (dm != 1) - return ERROR_INT("pixm not 1 bpp", procName, 1); - if (d != 1 && d != 8 && d != 32) - return ERROR_INT("pixd not 1, 8 or 32 bpp", procName, 1); - if (pixGetColormap(pixd) || pixGetColormap(pixs)) - return ERROR_INT("pixs and/or pixd is cmapped", procName, 1); - - /* For d = 1, use rasterop. pixt is the part from pixs, under - * the fg of pixm, that is to be combined with pixd. We also - * use pixt to remove all fg of pixd that is under the fg of pixm. - * Then pixt and pixd are combined by ORing. */ - wmin = L_MIN(w, L_MIN(ws, wm)); - hmin = L_MIN(h, L_MIN(hs, hm)); - if (d == 1) { - pixt = pixAnd(NULL, pixs, pixm); - pixRasterop(pixd, 0, 0, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC), - pixm, 0, 0); - pixRasterop(pixd, 0, 0, wmin, hmin, PIX_SRC | PIX_DST, pixt, 0, 0); - pixDestroy(&pixt); - return 0; - } - - data = pixGetData(pixd); - datas = pixGetData(pixs); - datam = pixGetData(pixm); - wpl = pixGetWpl(pixd); - wpls = pixGetWpl(pixs); - wplm = pixGetWpl(pixm); - if (d == 8) { - for (i = 0; i < hmin; i++) { - line = data + i * wpl; - lines = datas + i * wpls; - linem = datam + i * wplm; - for (j = 0; j < wmin; j++) { - if (GET_DATA_BIT(linem, j)) { - val = GET_DATA_BYTE(lines, j); - SET_DATA_BYTE(line, j, val); - } - } - } - } else { /* d == 32 */ - for (i = 0; i < hmin; i++) { - line = data + i * wpl; - lines = datas + i * wpls; - linem = datam + i * wplm; - for (j = 0; j < wmin; j++) { - if (GET_DATA_BIT(linem, j)) - line[j] = lines[j]; - } - } - } - - return 0; -} - - -/*! - * \brief pixCombineMaskedGeneral() - * - * \param[in] pixd 1 bpp, 8 bpp gray or 32 bpp rgb - * \param[in] pixs 1 bpp, 8 bpp gray or 32 bpp rgb - * \param[in] pixm [optional] 1 bpp mask - * \param[in] x, y origin of pixs and pixm relative to pixd; can be negative - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) In-place operation; pixd is changed. - * (2) This is a generalized version of pixCombinedMasked(), where - * the source and mask can be placed at the same (arbitrary) - * location relative to pixd. - * (3) pixs and pixd must be the same depth and not colormapped. - * (4) The UL corners of both pixs and pixm are aligned with - * the point (x, y) of pixd, and the operation is clipped to - * the intersection of all three images. - * (5) If pixm == NULL, it's a no-op. - * (6) Implementation. There are two ways to do these. In the first, - * we use rasterop, ORing the part of pixs under the mask - * with pixd (which has been appropriately cleared there first). - * In the second, the mask is used one pixel at a time to - * selectively replace pixels of pixd with those of pixs. - * Here, we use rasterop for 1 bpp and pixel-wise replacement - * for 8 and 32 bpp. To use rasterop for 8 bpp, for example, - * we must first generate an 8 bpp version of the mask. - * The code is simple: - * - * Pix *pixm8 = pixConvert1To8(NULL, pixm, 0, 255); - * Pix *pixt = pixAnd(NULL, pixs, pixm8); - * pixRasterop(pixd, x, y, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC), - * pixm8, 0, 0); - * pixRasterop(pixd, x, y, wmin, hmin, PIX_SRC | PIX_DST, - * pixt, 0, 0); - * pixDestroy(&pixt); - * pixDestroy(&pixm8); - *- */ -l_ok -pixCombineMaskedGeneral(PIX *pixd, - PIX *pixs, - PIX *pixm, - l_int32 x, - l_int32 y) -{ -l_int32 d, w, h, ws, hs, ds, wm, hm, dm, wmin, hmin; -l_int32 wpl, wpls, wplm, i, j, val; -l_uint32 *data, *datas, *datam, *line, *lines, *linem; -PIX *pixt; - - PROCNAME("pixCombineMaskedGeneral"); - - if (!pixm) /* nothing to do */ - return 0; - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - pixGetDimensions(pixd, &w, &h, &d); - pixGetDimensions(pixs, &ws, &hs, &ds); - pixGetDimensions(pixm, &wm, &hm, &dm); - if (d != ds) - return ERROR_INT("pixs and pixd depths differ", procName, 1); - if (dm != 1) - return ERROR_INT("pixm not 1 bpp", procName, 1); - if (d != 1 && d != 8 && d != 32) - return ERROR_INT("pixd not 1, 8 or 32 bpp", procName, 1); - if (pixGetColormap(pixd) || pixGetColormap(pixs)) - return ERROR_INT("pixs and/or pixd is cmapped", procName, 1); - - /* For d = 1, use rasterop. pixt is the part from pixs, under - * the fg of pixm, that is to be combined with pixd. We also - * use pixt to remove all fg of pixd that is under the fg of pixm. - * Then pixt and pixd are combined by ORing. */ - wmin = L_MIN(ws, wm); - hmin = L_MIN(hs, hm); - if (d == 1) { - pixt = pixAnd(NULL, pixs, pixm); - pixRasterop(pixd, x, y, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC), - pixm, 0, 0); - pixRasterop(pixd, x, y, wmin, hmin, PIX_SRC | PIX_DST, pixt, 0, 0); - pixDestroy(&pixt); - return 0; - } - - wpl = pixGetWpl(pixd); - data = pixGetData(pixd); - wpls = pixGetWpl(pixs); - datas = pixGetData(pixs); - wplm = pixGetWpl(pixm); - datam = pixGetData(pixm); - - for (i = 0; i < hmin; i++) { - if (y + i < 0 || y + i >= h) continue; - line = data + (y + i) * wpl; - lines = datas + i * wpls; - linem = datam + i * wplm; - for (j = 0; j < wmin; j++) { - if (x + j < 0 || x + j >= w) continue; - if (GET_DATA_BIT(linem, j)) { - switch (d) - { - case 8: - val = GET_DATA_BYTE(lines, j); - SET_DATA_BYTE(line, x + j, val); - break; - case 32: - *(line + x + j) = *(lines + j); - break; - default: - return ERROR_INT("shouldn't get here", procName, 1); - } - } - } - } - - return 0; -} - - -/*! - * \brief pixPaintThroughMask() - * - * \param[in] pixd 1, 2, 4, 8, 16 or 32 bpp; or colormapped - * \param[in] pixm [optional] 1 bpp mask - * \param[in] x, y origin of pixm relative to pixd; can be negative - * \param[in] val pixel value to set at each masked pixel - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) In-place operation. Calls pixSetMaskedCmap() for colormapped - * images. - * (2) For 1, 2, 4, 8 and 16 bpp gray, we take the appropriate - * number of least significant bits of val. - * (3) If pixm == NULL, it's a no-op. - * (4) The mask origin is placed at (x,y) on pixd, and the - * operation is clipped to the intersection of rectangles. - * (5) For rgb, the components in val are in the canonical locations, - * with red in location COLOR_RED, etc. - * (6) Implementation detail 1: - * For painting with val == 0 or val == maxval, you can use rasterop. - * If val == 0, invert the mask so that it's 0 over the region - * into which you want to write, and use PIX_SRC & PIX_DST to - * clear those pixels. To write with val = maxval (all 1's), - * use PIX_SRC | PIX_DST to set all bits under the mask. - * (7) Implementation detail 2: - * The rasterop trick can be used for depth > 1 as well. - * For val == 0, generate the mask for depth d from the binary - * mask using - * pixmd = pixUnpackBinary(pixm, d, 1); - * and use pixRasterop() with PIX_MASK. For val == maxval, - * pixmd = pixUnpackBinary(pixm, d, 0); - * and use pixRasterop() with PIX_PAINT. - * But note that if d == 32 bpp, it is about 3x faster to use - * the general implementation (not pixRasterop()). - * (8) Implementation detail 3: - * It might be expected that the switch in the inner loop will - * cause large branching delays and should be avoided. - * This is not the case, because the entrance is always the - * same and the compiler can correctly predict the jump. - *- */ -l_ok -pixPaintThroughMask(PIX *pixd, - PIX *pixm, - l_int32 x, - l_int32 y, - l_uint32 val) -{ -l_int32 d, w, h, wm, hm, wpl, wplm, i, j, rval, gval, bval; -l_uint32 *data, *datam, *line, *linem; - - PROCNAME("pixPaintThroughMask"); - - if (!pixm) /* nothing to do */ - return 0; - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - if (pixGetColormap(pixd)) { - extractRGBValues(val, &rval, &gval, &bval); - return pixSetMaskedCmap(pixd, pixm, x, y, rval, gval, bval); - } - - if (pixGetDepth(pixm) != 1) - return ERROR_INT("pixm not 1 bpp", procName, 1); - d = pixGetDepth(pixd); - if (d == 1) - val &= 1; - else if (d == 2) - val &= 3; - else if (d == 4) - val &= 0x0f; - else if (d == 8) - val &= 0xff; - else if (d == 16) - val &= 0xffff; - else if (d != 32) - return ERROR_INT("pixd not 1, 2, 4, 8, 16 or 32 bpp", procName, 1); - pixGetDimensions(pixm, &wm, &hm, NULL); - - /* If d == 1, use rasterop; it's about 25x faster. */ - if (d == 1) { - if (val == 0) { - PIX *pixmi = pixInvert(NULL, pixm); - pixRasterop(pixd, x, y, wm, hm, PIX_MASK, pixmi, 0, 0); - pixDestroy(&pixmi); - } else { /* val == 1 */ - pixRasterop(pixd, x, y, wm, hm, PIX_PAINT, pixm, 0, 0); - } - return 0; - } - - /* For d < 32, use rasterop if val == 0 (black); ~3x faster. */ - if (d < 32 && val == 0) { - PIX *pixmd = pixUnpackBinary(pixm, d, 1); - pixRasterop(pixd, x, y, wm, hm, PIX_MASK, pixmd, 0, 0); - pixDestroy(&pixmd); - return 0; - } - - /* For d < 32, use rasterop if val == maxval (white); ~3x faster. */ - if (d < 32 && val == ((1 << d) - 1)) { - PIX *pixmd = pixUnpackBinary(pixm, d, 0); - pixRasterop(pixd, x, y, wm, hm, PIX_PAINT, pixmd, 0, 0); - pixDestroy(&pixmd); - return 0; - } - - /* All other cases */ - pixGetDimensions(pixd, &w, &h, NULL); - wpl = pixGetWpl(pixd); - data = pixGetData(pixd); - wplm = pixGetWpl(pixm); - datam = pixGetData(pixm); - for (i = 0; i < hm; i++) { - if (y + i < 0 || y + i >= h) continue; - line = data + (y + i) * wpl; - linem = datam + i * wplm; - for (j = 0; j < wm; j++) { - if (x + j < 0 || x + j >= w) continue; - if (GET_DATA_BIT(linem, j)) { - switch (d) - { - case 2: - SET_DATA_DIBIT(line, x + j, val); - break; - case 4: - SET_DATA_QBIT(line, x + j, val); - break; - case 8: - SET_DATA_BYTE(line, x + j, val); - break; - case 16: - SET_DATA_TWO_BYTES(line, x + j, val); - break; - case 32: - *(line + x + j) = val; - break; - default: - return ERROR_INT("shouldn't get here", procName, 1); - } - } - } - } - - return 0; -} - - -/*! - * \brief pixCopyWithBoxa() - * - * \param[in] pixs all depths; cmap ok - * \param[in] boxa e.g., from components of a photomask - * \param[in] background L_SET_WHITE or L_SET_BLACK - * \return pixd or NULL on error - * - *- * Notes: - * (1) Pixels from pixs are copied ("blitted") through each box into pixd. - * (2) Pixels not copied are preset to either white or black. - * (3) This fast and simple implementation can use rasterop because - * each region to be copied is rectangular. - * (4) A much slower implemention that doesn't use rasterop would make - * a 1 bpp mask from the boxa and then copy, pixel by pixel, - * through the mask: - * pixGetDimensions(pixs, &w, &h, NULL); - * pixm = pixCreate(w, h, 1); - * pixm = pixMaskBoxa(pixm, pixm, boxa); - * pixd = pixCreateTemplate(pixs); - * pixSetBlackOrWhite(pixd, background); - * pixCombineMasked(pixd, pixs, pixm); - * pixDestroy(&pixm); - *- */ -PIX * -pixCopyWithBoxa(PIX *pixs, - BOXA *boxa, - l_int32 background) -{ -l_int32 i, n, x, y, w, h; -PIX *pixd; - - PROCNAME("pixCopyWithBoxa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!boxa) - return (PIX *)ERROR_PTR("boxa not defined", procName, NULL); - if (background != L_SET_WHITE && background != L_SET_BLACK) - return (PIX *)ERROR_PTR("invalid background", procName, NULL); - - pixd = pixCreateTemplate(pixs); - pixSetBlackOrWhite(pixd, background); - n = boxaGetCount(boxa); - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); - pixRasterop(pixd, x, y, w, h, PIX_SRC, pixs, x, y); - } - return pixd; -} - - -/*! - * \brief pixPaintSelfThroughMask() - * - * \param[in] pixd 8 bpp gray or 32 bpp rgb; not colormapped - * \param[in] pixm 1 bpp mask - * \param[in] x, y origin of pixm relative to pixd; must not be negative - * \param[in] searchdir L_HORIZ, L_VERT or L_BOTH_DIRECTIONS - * \param[in] mindist min distance of nearest tile edge to box; >= 0 - * \param[in] tilesize requested size for tiling; may be reduced - * \param[in] ntiles number of tiles tested in each row/column - * \param[in] distblend distance outside the fg used for blending with pixs - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) In-place operation; pixd is changed. - * (2) If pixm == NULL, it's a no-op. - * (3) The mask origin is placed at (x,y) on pixd, and the - * operation is clipped to the intersection of pixd and the - * fg of the mask. - * (4) %tsize is the the requested size for tiling. The actual - * actual size for each c.c. will be bounded by the minimum - * dimension of the c.c. - * (5) For %mindist, %searchdir and %ntiles, see pixFindRepCloseTile(). - * They determine the set of possible tiles that can be used - * to build a larger mirrored tile to paint onto pixd through - * the c.c. of pixm. - * (6) %distblend is used for alpha blending. It is only applied - * if there is exactly one c.c. in the mask. Use distblend == 0 - * to skip blending and just paint through the 1 bpp mask. - * (7) To apply blending to more than 1 component, call this function - * repeatedly with %pixm, %x and %y representing one component of - * the mask each time. This would be done as follows, for an - * underlying image pixs and mask pixm of components to fill: - * Boxa *boxa = pixConnComp(pixm, &pixa, 8); - * n = boxaGetCount(boxa); - * for (i = 0; i < n; i++) { - * Pix *pix = pixaGetPix(pixa, i, L_CLONE); - * Box *box = pixaGetBox(pixa, i, L_CLONE); - * boxGetGeometry(box, &bx, &by, &bw, &bh); - * pixPaintSelfThroughMask(pixs, pix, bx, by, searchdir, - * mindist, tilesize, ntiles, distblend); - * pixDestroy(&pix); - * boxDestroy(&box); - * } - * pixaDestroy(&pixa); - * boxaDestroy(&boxa); - * (8) If no tiles can be found, this falls back to estimating the - * color near the boundary of the region to be textured. - * (9) This can be used to replace the pixels in some regions of - * an image by selected neighboring pixels. The mask represents - * the pixels to be replaced. For each connected component in - * the mask, this function selects up to two tiles of neighboring - * pixels to be used for replacement of pixels represented by - * the component (i.e., under the FG of that component in the mask). - * After selection, mirror replication is used to generate an - * image that is large enough to cover the component. Alpha - * blending can also be used outside of the component, but near the - * edge, to blur the transition between painted and original pixels. - *- */ -l_ok -pixPaintSelfThroughMask(PIX *pixd, - PIX *pixm, - l_int32 x, - l_int32 y, - l_int32 searchdir, - l_int32 mindist, - l_int32 tilesize, - l_int32 ntiles, - l_int32 distblend) -{ -l_int32 w, h, d, wm, hm, dm, i, n, bx, by, bw, bh, edgeblend, retval, minside; -l_uint32 pixval; -BOX *box, *boxv, *boxh; -BOXA *boxa; -PIX *pixf, *pixv, *pixh, *pix1, *pix2, *pix3, *pix4, *pix5; -PIXA *pixa; - - PROCNAME("pixPaintSelfThroughMask"); - - if (!pixm) /* nothing to do */ - return 0; - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - if (pixGetColormap(pixd) != NULL) - return ERROR_INT("pixd has colormap", procName, 1); - pixGetDimensions(pixd, &w, &h, &d); - if (d != 8 && d != 32) - return ERROR_INT("pixd not 8 or 32 bpp", procName, 1); - pixGetDimensions(pixm, &wm, &hm, &dm); - if (dm != 1) - return ERROR_INT("pixm not 1 bpp", procName, 1); - if (x < 0 || y < 0) - return ERROR_INT("x and y must be non-negative", procName, 1); - if (searchdir != L_HORIZ && searchdir != L_VERT && - searchdir != L_BOTH_DIRECTIONS) - return ERROR_INT("invalid searchdir", procName, 1); - if (tilesize < 2) - return ERROR_INT("tilesize must be >= 2", procName, 1); - if (distblend < 0) - return ERROR_INT("distblend must be >= 0", procName, 1); - - /* Embed mask in full sized mask */ - if (wm < w || hm < h) { - pixf = pixCreate(w, h, 1); - pixRasterop(pixf, x, y, wm, hm, PIX_SRC, pixm, 0, 0); - } else { - pixf = pixCopy(NULL, pixm); - } - - /* Get connected components of mask */ - boxa = pixConnComp(pixf, &pixa, 8); - if ((n = pixaGetCount(pixa)) == 0) { - L_WARNING("no fg in mask\n", procName); - pixDestroy(&pixf); - pixaDestroy(&pixa); - boxaDestroy(&boxa); - return 1; - } - boxaDestroy(&boxa); - - /* For each c.c., generate one or two representative tiles for - * texturizing and apply through the mask. The input 'tilesize' - * is the requested value. Note that if there is exactly one - * component, and blending at the edge is requested, an alpha mask - * is generated, which is larger than the bounding box of the c.c. */ - edgeblend = (n == 1 && distblend > 0) ? 1 : 0; - if (distblend > 0 && n > 1) - L_WARNING("%d components; can not blend at edges\n", procName, n); - retval = 0; - for (i = 0; i < n; i++) { - if (edgeblend) { - pix1 = pixMakeAlphaFromMask(pixf, distblend, &box); - } else { - pix1 = pixaGetPix(pixa, i, L_CLONE); - box = pixaGetBox(pixa, i, L_CLONE); - } - boxGetGeometry(box, &bx, &by, &bw, &bh); - minside = L_MIN(bw, bh); - - boxh = boxv = NULL; - if (searchdir == L_HORIZ || searchdir == L_BOTH_DIRECTIONS) { - pixFindRepCloseTile(pixd, box, L_HORIZ, mindist, - L_MIN(minside, tilesize), ntiles, &boxh, 0); - } - if (searchdir == L_VERT || searchdir == L_BOTH_DIRECTIONS) { - pixFindRepCloseTile(pixd, box, L_VERT, mindist, - L_MIN(minside, tilesize), ntiles, &boxv, 0); - } - if (!boxh && !boxv) { - L_WARNING("tile region not selected; paint color near boundary\n", - procName); - pixDestroy(&pix1); - pix1 = pixaGetPix(pixa, i, L_CLONE); - pixaGetBoxGeometry(pixa, i, &bx, &by, NULL, NULL); - retval = pixGetColorNearMaskBoundary(pixd, pixm, box, distblend, - &pixval, 0); - pixSetMaskedGeneral(pixd, pix1, pixval, bx, by); - pixDestroy(&pix1); - boxDestroy(&box); - continue; - } - - /* Extract the selected squares from pixd */ - pixh = (boxh) ? pixClipRectangle(pixd, boxh, NULL) : NULL; - pixv = (boxv) ? pixClipRectangle(pixd, boxv, NULL) : NULL; - if (pixh && pixv) - pix2 = pixBlend(pixh, pixv, 0, 0, 0.5); - else if (pixh) - pix2 = pixClone(pixh); - else /* pixv */ - pix2 = pixClone(pixv); - pixDestroy(&pixh); - pixDestroy(&pixv); - boxDestroy(&boxh); - boxDestroy(&boxv); - - /* Generate an image the size of the b.b. of the c.c., - * possibly extended by the blending distance, which - * is then either painted through the c.c. mask or - * blended using the alpha mask for that c.c. */ - pix3 = pixMirroredTiling(pix2, bw, bh); - if (edgeblend) { - pix4 = pixClipRectangle(pixd, box, NULL); - pix5 = pixBlendWithGrayMask(pix4, pix3, pix1, 0, 0); - pixRasterop(pixd, bx, by, bw, bh, PIX_SRC, pix5, 0, 0); - pixDestroy(&pix4); - pixDestroy(&pix5); - } else { - pixCombineMaskedGeneral(pixd, pix3, pix1, bx, by); - } - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - boxDestroy(&box); - } - - pixaDestroy(&pixa); - pixDestroy(&pixf); - return retval; -} - - -/*! - * \brief pixMakeMaskFromVal() - * - * \param[in] pixs 2, 4 or 8 bpp; can be colormapped - * \param[in] val pixel value - * \return pixd 1 bpp mask, or NULL on error - * - *- * Notes: - * (1) This generates a 1 bpp mask image, where a 1 is written in - * the mask for each pixel in pixs that has a value %val. - * (2) If no pixels have the value, an empty mask is generated. - *- */ -PIX * -pixMakeMaskFromVal(PIX *pixs, - l_int32 val) -{ -l_int32 w, h, d, i, j, sval, wpls, wpld; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; - - PROCNAME("pixMakeMaskFromVal"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 2 && d != 4 && d != 8) - return (PIX *)ERROR_PTR("pix not 2, 4 or 8 bpp", procName, NULL); - - pixd = pixCreate(w, h, 1); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - if (d == 2) - sval = GET_DATA_DIBIT(lines, j); - else if (d == 4) - sval = GET_DATA_QBIT(lines, j); - else /* d == 8 */ - sval = GET_DATA_BYTE(lines, j); - if (sval == val) - SET_DATA_BIT(lined, j); - } - } - - return pixd; -} - - -/*! - * \brief pixMakeMaskFromLUT() - * - * \param[in] pixs 2, 4 or 8 bpp; can be colormapped - * \param[in] tab 256-entry LUT; 1 means to write to mask - * \return pixd 1 bpp mask, or NULL on error - * - *- * Notes: - * (1) This generates a 1 bpp mask image, where a 1 is written in - * the mask for each pixel in pixs that has a value corresponding - * to a 1 in the LUT. - * (2) The LUT should be of size 256. - *- */ -PIX * -pixMakeMaskFromLUT(PIX *pixs, - l_int32 *tab) -{ -l_int32 w, h, d, i, j, val, wpls, wpld; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; - - PROCNAME("pixMakeMaskFromLUT"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!tab) - return (PIX *)ERROR_PTR("tab not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 2 && d != 4 && d != 8) - return (PIX *)ERROR_PTR("pix not 2, 4 or 8 bpp", procName, NULL); - - pixd = pixCreate(w, h, 1); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - if (d == 2) - val = GET_DATA_DIBIT(lines, j); - else if (d == 4) - val = GET_DATA_QBIT(lines, j); - else /* d == 8 */ - val = GET_DATA_BYTE(lines, j); - if (tab[val] == 1) - SET_DATA_BIT(lined, j); - } - } - - return pixd; -} - - -/*! - * \brief pixMakeArbMaskFromRGB() - * - * \param[in] pixs 32 bpp RGB - * \param[in] rc, gc, bc arithmetic factors; can be negative - * \param[in] thresh lower threshold on weighted sum of components - * \return pixd 1 bpp mask, or NULL on error - * - *- * Notes: - * (1) This generates a 1 bpp mask image, where a 1 is written in - * the mask for each pixel in pixs that satisfies - * rc * rval + gc * gval + bc * bval > thresh - * where rval is the red component, etc. - * (2) Unlike with pixConvertToGray(), there are no constraints - * on the color coefficients, which can be negative. For - * example, a mask that discriminates against red and in favor - * of blue will have rc < 0.0 and bc > 0.0. - * (3) To make the result independent of intensity (the 'V' in HSV), - * select coefficients so that %thresh = 0. Then the result - * is not changed when all components are multiplied by the - * same constant (as long as nothing saturates). This can be - * useful if, for example, the illumination is not uniform. - *- */ -PIX * -pixMakeArbMaskFromRGB(PIX *pixs, - l_float32 rc, - l_float32 gc, - l_float32 bc, - l_float32 thresh) -{ -PIX *pix1, *pix2; - - PROCNAME("pixMakeArbMaskFromRGB"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); - if (thresh >= 255.0) thresh = 254.0; /* avoid 8 bit overflow */ - - if ((pix1 = pixConvertRGBToGrayArb(pixs, rc, gc, bc)) == NULL) - return (PIX *)ERROR_PTR("pix1 not made", procName, NULL); - pix2 = pixThresholdToBinary(pix1, thresh + 1); - pixInvert(pix2, pix2); - pixDestroy(&pix1); - return pix2; -} - - -/*! - * \brief pixSetUnderTransparency() - * - * \param[in] pixs 32 bpp rgba - * \param[in] val 32 bit unsigned color to use where alpha == 0 - * \param[in] debug displays layers of pixs - * \return pixd 32 bpp rgba, or NULL on error - * - *- * Notes: - * (1) This sets the r, g and b components under every fully - * transparent alpha component to %val. The alpha components - * are unchanged. - * (2) Full transparency is denoted by alpha == 0. Setting - * all pixels to a constant %val where alpha is transparent - * can improve compressibility by reducing the entropy. - * (3) The visual result depends on how the image is displayed. - * (a) For display devices that respect the use of the alpha - * layer, this will not affect the appearance. - * (b) For typical leptonica operations, alpha is ignored, - * so there will be a change in appearance because this - * resets the rgb values in the fully transparent region. - * (4) pixRead() and pixWrite() will, by default, read and write - * 4-component (rgba) pix in png format. To ignore the alpha - * component after reading, or omit it on writing, pixSetSpp(..., 3). - * (5) Here are some examples: - * * To convert all fully transparent pixels in a 4 component - * (rgba) png file to white: - * pixs = pixRead(- */ -PIX * -pixSetUnderTransparency(PIX *pixs, - l_uint32 val, - l_int32 debug) -{ -PIX *pixg, *pixm, *pixt, *pixd; - - PROCNAME("pixSetUnderTransparency"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not defined or not 32 bpp", - procName, NULL); - - if (pixGetSpp(pixs) != 4) { - L_WARNING("no alpha channel; returning a copy\n", procName); - return pixCopy(NULL, pixs); - } - - /* Make a mask from the alpha component with ON pixels - * wherever the alpha component is fully transparent (0). - * The hard way: - * l_int32 *lut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - * lut[0] = 1; - * pixg = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); - * pixm = pixMakeMaskFromLUT(pixg, lut); - * LEPT_FREE(lut); - * But there's an easier way to set pixels in a mask where - * the alpha component is 0 ... */ - pixg = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); - pixm = pixThresholdToBinary(pixg, 1); - - if (debug) { - pixt = pixDisplayLayersRGBA(pixs, 0xffffff00, 600); - pixDisplay(pixt, 0, 0); - pixDestroy(&pixt); - } - - pixd = pixCopy(NULL, pixs); - pixSetMasked(pixd, pixm, (val & 0xffffff00)); - pixDestroy(&pixg); - pixDestroy(&pixm); - return pixd; -} - - -/*! - * \brief pixMakeAlphaFromMask() - * - * \param[in] pixs 1 bpp - * \param[in] dist blending distance; typically 10 - 30 - * \param[out] pbox [optional] use NULL to get the full size - * \return pixd (8 bpp gray, or NULL on error - * - *); - * pixd = pixSetUnderTransparency(pixs, 0xffffff00, 0); - * * To write pixd with the alpha component: - * pixWrite( , pixd, IFF_PNG); - * * To write and rgba image without the alpha component, first do: - * pixSetSpp(pixd, 3); - * If you later want to use the alpha, spp must be reset to 4. - * * (fancier) To remove the alpha by blending the image over - * a white background: - * pixRemoveAlpha() - * This changes all pixel values where the alpha component is - * not opaque (255). - * (6) Caution. rgb images in leptonica typically have value 0 in - * the alpha channel, which is fully transparent. If spp for - * such an image were changed from 3 to 4, the image becomes - * fully transparent, and this function will set each pixel to %val. - * If you really want to set every pixel to the same value, - * use pixSetAllArbitrary(). - * (7) This is useful for compressing an RGBA image where the part - * of the image that is fully transparent is random junk; compression - * is typically improved by setting that region to a constant. - * For rendering as a 3 component RGB image over a uniform - * background of arbitrary color, use pixAlphaBlendUniform(). - * - * Notes: - * (1) This generates a 8 bpp alpha layer that is opaque (256) - * over the FG of pixs, and goes transparent linearly away - * from the FG pixels, decaying to 0 (transparent) is an - * 8-connected distance given by %dist. If %dist == 0, - * this does a simple conversion from 1 to 8 bpp. - * (2) If &box == NULL, this returns an alpha mask that is the - * full size of pixs. Otherwise, the returned mask pixd covers - * just the FG pixels of pixs, expanded by %dist in each - * direction (if possible), and the returned box gives the - * location of the returned mask relative to pixs. - * (3) This is useful for painting through a mask and allowing - * blending of the painted image with an underlying image - * in the mask background for pixels near foreground mask pixels. - * For example, with an underlying rgb image pix1, an overlaying - * image rgb pix2, binary mask pixm, and dist > 0, this - * blending is achieved with: - * pix3 = pixMakeAlphaFromMask(pixm, dist, &box); - * boxGetGeometry(box, &x, &y, NULL, NULL); - * pix4 = pixBlendWithGrayMask(pix1, pix2, pix3, x, y); - *- */ -PIX * -pixMakeAlphaFromMask(PIX *pixs, - l_int32 dist, - BOX **pbox) -{ -l_int32 w, h; -BOX *box1, *box2; -PIX *pix1, *pixd; - - PROCNAME("pixMakeAlphaFromMask"); - - if (pbox) *pbox = NULL; - if (!pixs || pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (dist < 0) - return (PIX *)ERROR_PTR("dist must be >= 0", procName, NULL); - - /* If requested, extract just the region to be affected by the mask */ - if (pbox) { - pixClipToForeground(pixs, NULL, &box1); - if (!box1) { - L_WARNING("no ON pixels in mask\n", procName); - return pixCreateTemplate(pixs); /* all background (0) */ - } - - boxAdjustSides(box1, box1, -dist, dist, -dist, dist); - pixGetDimensions(pixs, &w, &h, NULL); - box2 = boxClipToRectangle(box1, w, h); - *pbox = box2; - pix1 = pixClipRectangle(pixs, box2, NULL); - boxDestroy(&box1); - } else { - pix1 = pixCopy(NULL, pixs); - } - - if (dist == 0) { - pixd = pixConvert1To8(NULL, pix1, 0, 255); - pixDestroy(&pix1); - return pixd; - } - - /* Blur the boundary of the input mask */ - pixInvert(pix1, pix1); - pixd = pixDistanceFunction(pix1, 8, 8, L_BOUNDARY_FG); - pixMultConstantGray(pixd, 256.0 / dist); - pixInvert(pixd, pixd); - pixDestroy(&pix1); - return pixd; -} - - -/*! - * \brief pixGetColorNearMaskBoundary() - * - * \param[in] pixs 32 bpp rgb - * \param[in] pixm 1 bpp mask, full image - * \param[in] box region of mask; typically b.b. of a component - * \param[in] dist distance into BG from mask boundary to use - * \param[out] pval average pixel value - * \param[in] debug 1 to output mask images - * \return 0 if OK, 1 on error. - * - *- * Notes: - * (1) This finds the average color in a set of pixels that are - * roughly a distance %dist from the c.c. boundary and in the - * background of the mask image. - *- */ -l_ok -pixGetColorNearMaskBoundary(PIX *pixs, - PIX *pixm, - BOX *box, - l_int32 dist, - l_uint32 *pval, - l_int32 debug) -{ -char op[64]; -l_int32 empty, bx, by; -l_float32 rval, gval, bval; -BOX *box1, *box2; -PIX *pix1, *pix2, *pix3; - - PROCNAME("pixGetColorNearMaskBoundary"); - - if (!pval) - return ERROR_INT("&pval not defined", procName, 1); - *pval = 0xffffff00; /* white */ - if (!pixs || pixGetDepth(pixs) != 32) - return ERROR_INT("pixs undefined or not 32 bpp", procName, 1); - if (!pixm || pixGetDepth(pixm) != 1) - return ERROR_INT("pixm undefined or not 1 bpp", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - if (dist < 0) - return ERROR_INT("dist must be >= 0", procName, 1); - - /* Clip mask piece, expanded beyond %box by (%dist + 5) on each side. - * box1 is the region requested; box2 is the actual region retrieved, - * which is clipped to %pixm */ - box1 = boxAdjustSides(NULL, box, -dist - 5, dist + 5, -dist - 5, dist + 5); - pix1 = pixClipRectangle(pixm, box1, &box2); - - /* Expand FG by %dist into the BG */ - if (dist == 0) { - pix2 = pixCopy(NULL, pix1); - } else { - snprintf(op, sizeof(op), "d%d.%d", 2 * dist, 2 * dist); - pix2 = pixMorphSequence(pix1, op, 0); - } - - /* Expand again by 5 pixels on all sides (dilate 11x11) and XOR, - * getting the annulus of FG pixels between %dist and %dist + 5 */ - pix3 = pixCopy(NULL, pix2); - pixDilateBrick(pix3, pix3, 11, 11); - pixXor(pix3, pix3, pix2); - pixZero(pix3, &empty); - if (!empty) { - /* Scan the same region in %pixs, to get average under FG in pix3 */ - boxGetGeometry(box2, &bx, &by, NULL, NULL); - pixGetAverageMaskedRGB(pixs, pix3, bx, by, 1, L_MEAN_ABSVAL, - &rval, &gval, &bval); - composeRGBPixel((l_int32)(rval + 0.5), (l_int32)(gval + 0.5), - (l_int32)(bval + 0.5), pval); - } else { - L_WARNING("no pixels found\n", procName); - } - - if (debug) { - lept_rmdir("masknear"); /* erase previous images */ - lept_mkdir("masknear"); - pixWriteDebug("/tmp/masknear/input.png", pix1, IFF_PNG); - pixWriteDebug("/tmp/masknear/adjusted.png", pix2, IFF_PNG); - pixWriteDebug("/tmp/masknear/outerfive.png", pix3, IFF_PNG); - lept_stderr("Input box; with adjusted sides; clipped\n"); - boxPrintStreamInfo(stderr, box); - boxPrintStreamInfo(stderr, box1); - boxPrintStreamInfo(stderr, box2); - } - - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - boxDestroy(&box1); - boxDestroy(&box2); - return 0; -} - - -/*! - * \brief pixDisplaySelectedPixels() - * - * \param[in] pixs [optional] any depth - * \param[in] pixm 1 bpp mask, aligned UL corner with %pixs - * \param[in] sel [optional] pattern to paint at each pixel in pixm - * \param[in] val rgb rendering of pattern - * \return pixd, or NULL on error - * - *- * Notes: - * (1) For every fg pixel in %pixm, this paints the pattern in %sel - * in color %val on a copy of %pixs. - * (2) The implementation is to dilate %pixm by %sel, and then - * paint through the dilated mask onto %pixs. - * (3) If %pixs == NULL, it paints on a white image. - * (4) If %sel == NULL, it paints only the pixels in the input %pixm. - * (5) This visualization would typically be used in debugging. - *- */ -PIX * -pixDisplaySelectedPixels(PIX *pixs, - PIX *pixm, - SEL *sel, - l_uint32 val) -{ -l_int32 w, h; -PIX *pix1, *pix2; - - PROCNAME("pixDisplaySelectedPixels"); - - if (!pixm || pixGetDepth(pixm) != 1) - return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", procName, NULL); - - if (pixs) { - pix1 = pixConvertTo32(pixs); - } else { - pixGetDimensions(pixm, &w, &h, NULL); - pix1 = pixCreate(w, h, 32); - pixSetAll(pix1); - } - - if (sel) - pix2 = pixDilate(NULL, pixm, sel); - else - pix2 = pixClone(pixm); - pixSetMasked(pix1, pix2, val); - pixDestroy(&pix2); - return pix1; -} - - -/*-------------------------------------------------------------* - * One and two-image boolean ops on arbitrary depth images * - *-------------------------------------------------------------*/ -/*! - * \brief pixInvert() - * - * \param[in] pixd [optional]; this can be null, equal to pixs, - * or different from pixs - * \param[in] pixs - * \return pixd, or NULL on error - * - *- * Notes: - * (1) This inverts pixs, for all pixel depths. - * (2) There are 3 cases: - * (a) pixd == null, ~src --> new pixd - * (b) pixd == pixs, ~src --> src (in-place) - * (c) pixd != pixs, ~src --> input pixd - * (3) For clarity, if the case is known, use these patterns: - * (a) pixd = pixInvert(NULL, pixs); - * (b) pixInvert(pixs, pixs); - * (c) pixInvert(pixd, pixs); - *- */ -PIX * -pixInvert(PIX *pixd, - PIX *pixs) -{ - PROCNAME("pixInvert"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - /* Prepare pixd for in-place operation */ - if ((pixd = pixCopy(pixd, pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - - pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd), - PIX_NOT(PIX_DST), NULL, 0, 0); /* invert pixd */ - - return pixd; -} - - -/*! - * \brief pixOr() - * - * \param[in] pixd [optional]; this can be null, equal to pixs1, - * different from pixs1 - * \param[in] pixs1 can be == pixd - * \param[in] pixs2 must be != pixd - * \return pixd always - * - *- * Notes: - * (1) This gives the union of two images with equal depth, - * aligning them to the the UL corner. pixs1 and pixs2 - * need not have the same width and height. - * (2) There are 3 cases: - * (a) pixd == null, (src1 | src2) --> new pixd - * (b) pixd == pixs1, (src1 | src2) --> src1 (in-place) - * (c) pixd != pixs1, (src1 | src2) --> input pixd - * (3) For clarity, if the case is known, use these patterns: - * (a) pixd = pixOr(NULL, pixs1, pixs2); - * (b) pixOr(pixs1, pixs1, pixs2); - * (c) pixOr(pixd, pixs1, pixs2); - * (4) The size of the result is determined by pixs1. - * (5) The depths of pixs1 and pixs2 must be equal. - * (6) Note carefully that the order of pixs1 and pixs2 only matters - * for the in-place case. For in-place, you must have - * pixd == pixs1. Setting pixd == pixs2 gives an incorrect - * result: the copy puts pixs1 image data in pixs2, and - * the rasterop is then between pixs2 and pixs2 (a no-op). - *- */ -PIX * -pixOr(PIX *pixd, - PIX *pixs1, - PIX *pixs2) -{ - PROCNAME("pixOr"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); - if (pixd == pixs2) - return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd); - if (pixGetDepth(pixs1) != pixGetDepth(pixs2)) - return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd); - -#if EQUAL_SIZE_WARNING - if (!pixSizesEqual(pixs1, pixs2)) - L_WARNING("pixs1 and pixs2 not equal sizes\n", procName); -#endif /* EQUAL_SIZE_WARNING */ - - /* Prepare pixd to be a copy of pixs1 */ - if ((pixd = pixCopy(pixd, pixs1)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, pixd); - - /* src1 | src2 --> dest */ - pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd), - PIX_SRC | PIX_DST, pixs2, 0, 0); - - return pixd; -} - - -/*! - * \brief pixAnd() - * - * \param[in] pixd [optional]; this can be null, equal to pixs1, - * different from pixs1 - * \param[in] pixs1 can be == pixd - * \param[in] pixs2 must be != pixd - * \return pixd always - * - *- * Notes: - * (1) This gives the intersection of two images with equal depth, - * aligning them to the the UL corner. pixs1 and pixs2 - * need not have the same width and height. - * (2) There are 3 cases: - * (a) pixd == null, (src1 & src2) --> new pixd - * (b) pixd == pixs1, (src1 & src2) --> src1 (in-place) - * (c) pixd != pixs1, (src1 & src2) --> input pixd - * (3) For clarity, if the case is known, use these patterns: - * (a) pixd = pixAnd(NULL, pixs1, pixs2); - * (b) pixAnd(pixs1, pixs1, pixs2); - * (c) pixAnd(pixd, pixs1, pixs2); - * (4) The size of the result is determined by pixs1. - * (5) The depths of pixs1 and pixs2 must be equal. - * (6) Note carefully that the order of pixs1 and pixs2 only matters - * for the in-place case. For in-place, you must have - * pixd == pixs1. Setting pixd == pixs2 gives an incorrect - * result: the copy puts pixs1 image data in pixs2, and - * the rasterop is then between pixs2 and pixs2 (a no-op). - *- */ -PIX * -pixAnd(PIX *pixd, - PIX *pixs1, - PIX *pixs2) -{ - PROCNAME("pixAnd"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); - if (pixd == pixs2) - return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd); - if (pixGetDepth(pixs1) != pixGetDepth(pixs2)) - return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd); - -#if EQUAL_SIZE_WARNING - if (!pixSizesEqual(pixs1, pixs2)) - L_WARNING("pixs1 and pixs2 not equal sizes\n", procName); -#endif /* EQUAL_SIZE_WARNING */ - - /* Prepare pixd to be a copy of pixs1 */ - if ((pixd = pixCopy(pixd, pixs1)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, pixd); - - /* src1 & src2 --> dest */ - pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd), - PIX_SRC & PIX_DST, pixs2, 0, 0); - - return pixd; -} - - -/*! - * \brief pixXor() - * - * \param[in] pixd [optional]; this can be null, equal to pixs1, - * different from pixs1 - * \param[in] pixs1 can be == pixd - * \param[in] pixs2 must be != pixd - * \return pixd always - * - *- * Notes: - * (1) This gives the XOR of two images with equal depth, - * aligning them to the the UL corner. pixs1 and pixs2 - * need not have the same width and height. - * (2) There are 3 cases: - * (a) pixd == null, (src1 ^ src2) --> new pixd - * (b) pixd == pixs1, (src1 ^ src2) --> src1 (in-place) - * (c) pixd != pixs1, (src1 ^ src2) --> input pixd - * (3) For clarity, if the case is known, use these patterns: - * (a) pixd = pixXor(NULL, pixs1, pixs2); - * (b) pixXor(pixs1, pixs1, pixs2); - * (c) pixXor(pixd, pixs1, pixs2); - * (4) The size of the result is determined by pixs1. - * (5) The depths of pixs1 and pixs2 must be equal. - * (6) Note carefully that the order of pixs1 and pixs2 only matters - * for the in-place case. For in-place, you must have - * pixd == pixs1. Setting pixd == pixs2 gives an incorrect - * result: the copy puts pixs1 image data in pixs2, and - * the rasterop is then between pixs2 and pixs2 (a no-op). - *- */ -PIX * -pixXor(PIX *pixd, - PIX *pixs1, - PIX *pixs2) -{ - PROCNAME("pixXor"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); - if (pixd == pixs2) - return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd); - if (pixGetDepth(pixs1) != pixGetDepth(pixs2)) - return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd); - -#if EQUAL_SIZE_WARNING - if (!pixSizesEqual(pixs1, pixs2)) - L_WARNING("pixs1 and pixs2 not equal sizes\n", procName); -#endif /* EQUAL_SIZE_WARNING */ - - /* Prepare pixd to be a copy of pixs1 */ - if ((pixd = pixCopy(pixd, pixs1)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, pixd); - - /* src1 ^ src2 --> dest */ - pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd), - PIX_SRC ^ PIX_DST, pixs2, 0, 0); - - return pixd; -} - - -/*! - * \brief pixSubtract() - * - * \param[in] pixd [optional]; this can be null, equal to pixs1, - * equal to pixs2, or different from both pixs1 and pixs2 - * \param[in] pixs1 can be == pixd - * \param[in] pixs2 can be == pixd - * \return pixd always - * - *- * Notes: - * (1) This gives the set subtraction of two images with equal depth, - * aligning them to the the UL corner. pixs1 and pixs2 - * need not have the same width and height. - * (2) Source pixs2 is always subtracted from source pixs1. - * The result is - * pixs1 \ pixs2 = pixs1 & (~pixs2) - * (3) There are 4 cases: - * (a) pixd == null, (src1 - src2) --> new pixd - * (b) pixd == pixs1, (src1 - src2) --> src1 (in-place) - * (c) pixd == pixs2, (src1 - src2) --> src2 (in-place) - * (d) pixd != pixs1 && pixd != pixs2), - * (src1 - src2) --> input pixd - * (4) For clarity, if the case is known, use these patterns: - * (a) pixd = pixSubtract(NULL, pixs1, pixs2); - * (b) pixSubtract(pixs1, pixs1, pixs2); - * (c) pixSubtract(pixs2, pixs1, pixs2); - * (d) pixSubtract(pixd, pixs1, pixs2); - * (5) The size of the result is determined by pixs1. - * (6) The depths of pixs1 and pixs2 must be equal. - *- */ -PIX * -pixSubtract(PIX *pixd, - PIX *pixs1, - PIX *pixs2) -{ -l_int32 w, h; - - PROCNAME("pixSubtract"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); - if (pixGetDepth(pixs1) != pixGetDepth(pixs2)) - return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd); - -#if EQUAL_SIZE_WARNING - if (!pixSizesEqual(pixs1, pixs2)) - L_WARNING("pixs1 and pixs2 not equal sizes\n", procName); -#endif /* EQUAL_SIZE_WARNING */ - - pixGetDimensions(pixs1, &w, &h, NULL); - if (!pixd) { - pixd = pixCopy(NULL, pixs1); - pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC), - pixs2, 0, 0); /* src1 & (~src2) */ - } else if (pixd == pixs1) { - pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC), - pixs2, 0, 0); /* src1 & (~src2) */ - } else if (pixd == pixs2) { - pixRasterop(pixd, 0, 0, w, h, PIX_NOT(PIX_DST) & PIX_SRC, - pixs1, 0, 0); /* src1 & (~src2) */ - } else { /* pixd != pixs1 && pixd != pixs2 */ - pixCopy(pixd, pixs1); /* sizes pixd to pixs1 if unequal */ - pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC), - pixs2, 0, 0); /* src1 & (~src2) */ - } - - return pixd; -} - - -/*-------------------------------------------------------------* - * Pixel counting * - *-------------------------------------------------------------*/ -/*! - * \brief pixZero() - * - * \param[in] pix all depths; colormap OK - * \param[out] pempty 1 if all bits in image data field are 0; 0 otherwise - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) For a binary image, if there are no fg (black) pixels, empty = 1. - * (2) For a grayscale image, if all pixels are black (0), empty = 1. - * (3) For an RGB image, if all 4 components in every pixel is 0, - * empty = 1. - * (4) For a colormapped image, pixel values are 0. The colormap - * is ignored. - *- */ -l_ok -pixZero(PIX *pix, - l_int32 *pempty) -{ -l_int32 w, h, wpl, i, j, fullwords, endbits; -l_uint32 endmask; -l_uint32 *data, *line; - - PROCNAME("pixZero"); - - if (!pempty) - return ERROR_INT("&empty not defined", procName, 1); - *pempty = 1; - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - w = pixGetWidth(pix) * pixGetDepth(pix); /* in bits */ - h = pixGetHeight(pix); - wpl = pixGetWpl(pix); - data = pixGetData(pix); - fullwords = w / 32; - endbits = w & 31; - endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits)); - - for (i = 0; i < h; i++) { - line = data + wpl * i; - for (j = 0; j < fullwords; j++) - if (*line++) { - *pempty = 0; - return 0; - } - if (endbits) { - if (*line & endmask) { - *pempty = 0; - return 0; - } - } - } - - return 0; -} - - -/*! - * \brief pixForegroundFraction() - * - * \param[in] pix 1 bpp - * \param[out] pfract fraction of ON pixels - * \return 0 if OK; 1 on error - */ -l_ok -pixForegroundFraction(PIX *pix, - l_float32 *pfract) -{ -l_int32 w, h, count; - - PROCNAME("pixForegroundFraction"); - - if (!pfract) - return ERROR_INT("&fract not defined", procName, 1); - *pfract = 0.0; - if (!pix || pixGetDepth(pix) != 1) - return ERROR_INT("pix not defined or not 1 bpp", procName, 1); - - pixCountPixels(pix, &count, NULL); - pixGetDimensions(pix, &w, &h, NULL); - *pfract = (l_float32)count / (l_float32)(w * h); - return 0; -} - - -/*! - * \brief pixaCountPixels() - * - * \param[in] pixa array of 1 bpp pix - * \return na of ON pixels in each pix, or NULL on error - */ -NUMA * -pixaCountPixels(PIXA *pixa) -{ -l_int32 d, i, n, count; -l_int32 *tab; -NUMA *na; -PIX *pix; - - PROCNAME("pixaCountPixels"); - - if (!pixa) - return (NUMA *)ERROR_PTR("pix not defined", procName, NULL); - - if ((n = pixaGetCount(pixa)) == 0) - return numaCreate(1); - - pix = pixaGetPix(pixa, 0, L_CLONE); - d = pixGetDepth(pix); - pixDestroy(&pix); - if (d != 1) - return (NUMA *)ERROR_PTR("pixa not 1 bpp", procName, NULL); - - if ((na = numaCreate(n)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - tab = makePixelSumTab8(); - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixa, i, L_CLONE); - pixCountPixels(pix, &count, tab); - numaAddNumber(na, count); - pixDestroy(&pix); - } - - LEPT_FREE(tab); - return na; -} - - -/*! - * \brief pixCountPixels() - * - * \param[in] pixs 1 bpp - * \param[out] pcount count of ON pixels - * \param[in] tab8 [optional] 8-bit pixel lookup table - * \return 0 if OK; 1 on error - */ -l_ok -pixCountPixels(PIX *pixs, - l_int32 *pcount, - l_int32 *tab8) -{ -l_uint32 endmask; -l_int32 w, h, wpl, i, j; -l_int32 fullwords, endbits, sum; -l_int32 *tab; -l_uint32 *data; - - PROCNAME("pixCountPixels"); - - if (!pcount) - return ERROR_INT("&count not defined", procName, 1); - *pcount = 0; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - - tab = (tab8) ? tab8 : makePixelSumTab8(); - pixGetDimensions(pixs, &w, &h, NULL); - wpl = pixGetWpl(pixs); - data = pixGetData(pixs); - fullwords = w >> 5; - endbits = w & 31; - endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits)); - - sum = 0; - for (i = 0; i < h; i++, data += wpl) { - for (j = 0; j < fullwords; j++) { - l_uint32 word = data[j]; - if (word) { - sum += tab[word & 0xff] + - tab[(word >> 8) & 0xff] + - tab[(word >> 16) & 0xff] + - tab[(word >> 24) & 0xff]; - } - } - if (endbits) { - l_uint32 word = data[j] & endmask; - if (word) { - sum += tab[word & 0xff] + - tab[(word >> 8) & 0xff] + - tab[(word >> 16) & 0xff] + - tab[(word >> 24) & 0xff]; - } - } - } - *pcount = sum; - - if (!tab8) LEPT_FREE(tab); - return 0; -} - - -/*! - * \brief pixCountPixelsInRect() - * - * \param[in] pixs 1 bpp - * \param[in] box (can be null) - * \param[out] pcount count of ON pixels - * \param[in] tab8 [optional] 8-bit pixel lookup table - * \return 0 if OK; 1 on error - */ -l_ok -pixCountPixelsInRect(PIX *pixs, - BOX *box, - l_int32 *pcount, - l_int32 *tab8) -{ -l_int32 bx, by, bw, bh; -PIX *pix1; - - PROCNAME("pixCountPixelsInRect"); - - if (!pcount) - return ERROR_INT("&count not defined", procName, 1); - *pcount = 0; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - - if (box) { - boxGetGeometry(box, &bx, &by, &bw, &bh); - pix1 = pixCreate(bw, bh, 1); - pixRasterop(pix1, 0, 0, bw, bh, PIX_SRC, pixs, bx, by); - pixCountPixels(pix1, pcount, tab8); - pixDestroy(&pix1); - } else { - pixCountPixels(pixs, pcount, tab8); - } - - return 0; -} - - -/*! - * \brief pixCountByRow() - * - * \param[in] pix 1 bpp - * \param[in] box [optional] clipping box for count; can be null - * \return na of number of ON pixels by row, or NULL on error - * - *- * Notes: - * (1) To resample for a bin size different from 1, use - * numaUniformSampling() on the result of this function. - *- */ -NUMA * -pixCountByRow(PIX *pix, - BOX *box) -{ -l_int32 i, j, w, h, wpl, count, xstart, xend, ystart, yend, bw, bh; -l_uint32 *line, *data; -NUMA *na; - - PROCNAME("pixCountByRow"); - - if (!pix || pixGetDepth(pix) != 1) - return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL); - if (!box) - return pixCountPixelsByRow(pix, NULL); - - pixGetDimensions(pix, &w, &h, NULL); - if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, - &bw, &bh) == 1) - return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL); - - if ((na = numaCreate(bh)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - numaSetParameters(na, ystart, 1); - data = pixGetData(pix); - wpl = pixGetWpl(pix); - for (i = ystart; i < yend; i++) { - count = 0; - line = data + i * wpl; - for (j = xstart; j < xend; j++) { - if (GET_DATA_BIT(line, j)) - count++; - } - numaAddNumber(na, count); - } - - return na; -} - - -/*! - * \brief pixCountByColumn() - * - * \param[in] pix 1 bpp - * \param[in] box [optional] clipping box for count; can be null - * \return na of number of ON pixels by column, or NULL on error - * - *- * Notes: - * (1) To resample for a bin size different from 1, use - * numaUniformSampling() on the result of this function. - *- */ -NUMA * -pixCountByColumn(PIX *pix, - BOX *box) -{ -l_int32 i, j, w, h, wpl, count, xstart, xend, ystart, yend, bw, bh; -l_uint32 *line, *data; -NUMA *na; - - PROCNAME("pixCountByColumn"); - - if (!pix || pixGetDepth(pix) != 1) - return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL); - if (!box) - return pixCountPixelsByColumn(pix); - - pixGetDimensions(pix, &w, &h, NULL); - if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, - &bw, &bh) == 1) - return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL); - - if ((na = numaCreate(bw)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - numaSetParameters(na, xstart, 1); - data = pixGetData(pix); - wpl = pixGetWpl(pix); - for (j = xstart; j < xend; j++) { - count = 0; - for (i = ystart; i < yend; i++) { - line = data + i * wpl; - if (GET_DATA_BIT(line, j)) - count++; - } - numaAddNumber(na, count); - } - - return na; -} - - -/*! - * \brief pixCountPixelsByRow() - * - * \param[in] pix 1 bpp - * \param[in] tab8 [optional] 8-bit pixel lookup table - * \return na of counts, or NULL on error - */ -NUMA * -pixCountPixelsByRow(PIX *pix, - l_int32 *tab8) -{ -l_int32 h, i, count; -l_int32 *tab; -NUMA *na; - - PROCNAME("pixCountPixelsByRow"); - - if (!pix || pixGetDepth(pix) != 1) - return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL); - - h = pixGetHeight(pix); - if ((na = numaCreate(h)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - - tab = (tab8) ? tab8 : makePixelSumTab8(); - for (i = 0; i < h; i++) { - pixCountPixelsInRow(pix, i, &count, tab); - numaAddNumber(na, count); - } - - if (!tab8) LEPT_FREE(tab); - return na; -} - - -/*! - * \brief pixCountPixelsByColumn() - * - * \param[in] pix 1 bpp - * \return na of counts in each column, or NULL on error - */ -NUMA * -pixCountPixelsByColumn(PIX *pix) -{ -l_int32 i, j, w, h, wpl; -l_uint32 *line, *data; -l_float32 *array; -NUMA *na; - - PROCNAME("pixCountPixelsByColumn"); - - if (!pix || pixGetDepth(pix) != 1) - return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL); - - pixGetDimensions(pix, &w, &h, NULL); - if ((na = numaCreate(w)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - numaSetCount(na, w); - array = numaGetFArray(na, L_NOCOPY); - data = pixGetData(pix); - wpl = pixGetWpl(pix); - for (i = 0; i < h; i++) { - line = data + wpl * i; - for (j = 0; j < w; j++) { - if (GET_DATA_BIT(line, j)) - array[j] += 1.0; - } - } - - return na; -} - - -/*! - * \brief pixCountPixelsInRow() - * - * \param[in] pix 1 bpp - * \param[in] row number - * \param[out] pcount sum of ON pixels in raster line - * \param[in] tab8 [optional] 8-bit pixel lookup table - * \return 0 if OK; 1 on error - */ -l_ok -pixCountPixelsInRow(PIX *pix, - l_int32 row, - l_int32 *pcount, - l_int32 *tab8) -{ -l_uint32 word, endmask; -l_int32 j, w, h, wpl; -l_int32 fullwords, endbits, sum; -l_int32 *tab; -l_uint32 *line; - - PROCNAME("pixCountPixelsInRow"); - - if (!pcount) - return ERROR_INT("&count not defined", procName, 1); - *pcount = 0; - if (!pix || pixGetDepth(pix) != 1) - return ERROR_INT("pix not defined or not 1 bpp", procName, 1); - - pixGetDimensions(pix, &w, &h, NULL); - if (row < 0 || row >= h) - return ERROR_INT("row out of bounds", procName, 1); - wpl = pixGetWpl(pix); - line = pixGetData(pix) + row * wpl; - fullwords = w >> 5; - endbits = w & 31; - endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits)); - - tab = (tab8) ? tab8 : makePixelSumTab8(); - sum = 0; - for (j = 0; j < fullwords; j++) { - word = line[j]; - if (word) { - sum += tab[word & 0xff] + - tab[(word >> 8) & 0xff] + - tab[(word >> 16) & 0xff] + - tab[(word >> 24) & 0xff]; - } - } - if (endbits) { - word = line[j] & endmask; - if (word) { - sum += tab[word & 0xff] + - tab[(word >> 8) & 0xff] + - tab[(word >> 16) & 0xff] + - tab[(word >> 24) & 0xff]; - } - } - *pcount = sum; - - if (!tab8) LEPT_FREE(tab); - return 0; -} - - -/*! - * \brief pixGetMomentByColumn() - * - * \param[in] pix 1 bpp - * \param[in] order of moment, either 1 or 2 - * \return na of first moment of fg pixels, by column, or NULL on error - */ -NUMA * -pixGetMomentByColumn(PIX *pix, - l_int32 order) -{ -l_int32 i, j, w, h, wpl; -l_uint32 *line, *data; -l_float32 *array; -NUMA *na; - - PROCNAME("pixGetMomentByColumn"); - - if (!pix || pixGetDepth(pix) != 1) - return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL); - if (order != 1 && order != 2) - return (NUMA *)ERROR_PTR("order of moment not 1 or 2", procName, NULL); - - pixGetDimensions(pix, &w, &h, NULL); - if ((na = numaCreate(w)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - numaSetCount(na, w); - array = numaGetFArray(na, L_NOCOPY); - data = pixGetData(pix); - wpl = pixGetWpl(pix); - for (i = 0; i < h; i++) { - line = data + wpl * i; - for (j = 0; j < w; j++) { - if (GET_DATA_BIT(line, j)) { - if (order == 1) - array[j] += i; - else /* order == 2 */ - array[j] += i * i; - } - } - } - - return na; -} - - -/*! - * \brief pixThresholdPixelSum() - * - * \param[in] pix 1 bpp - * \param[in] thresh threshold - * \param[out] pabove 1 if above threshold; - * 0 if equal to or less than threshold - * \param[in] tab8 [optional] 8-bit pixel lookup table - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) This sums the ON pixels and returns immediately if the count - * goes above threshold. It is therefore more efficient - * for matching images (by running this function on the xor of - * the 2 images) than using pixCountPixels(), which counts all - * pixels before returning. - *- */ -l_ok -pixThresholdPixelSum(PIX *pix, - l_int32 thresh, - l_int32 *pabove, - l_int32 *tab8) -{ -l_uint32 word, endmask; -l_int32 *tab; -l_int32 w, h, wpl, i, j; -l_int32 fullwords, endbits, sum; -l_uint32 *line, *data; - - PROCNAME("pixThresholdPixelSum"); - - if (!pabove) - return ERROR_INT("&above not defined", procName, 1); - *pabove = 0; - if (!pix || pixGetDepth(pix) != 1) - return ERROR_INT("pix not defined or not 1 bpp", procName, 1); - - tab = (tab8) ? tab8 : makePixelSumTab8(); - pixGetDimensions(pix, &w, &h, NULL); - wpl = pixGetWpl(pix); - data = pixGetData(pix); - fullwords = w >> 5; - endbits = w & 31; - endmask = 0xffffffff << (32 - endbits); - - sum = 0; - for (i = 0; i < h; i++) { - line = data + wpl * i; - for (j = 0; j < fullwords; j++) { - word = line[j]; - if (word) { - sum += tab[word & 0xff] + - tab[(word >> 8) & 0xff] + - tab[(word >> 16) & 0xff] + - tab[(word >> 24) & 0xff]; - } - } - if (endbits) { - word = line[j] & endmask; - if (word) { - sum += tab[word & 0xff] + - tab[(word >> 8) & 0xff] + - tab[(word >> 16) & 0xff] + - tab[(word >> 24) & 0xff]; - } - } - if (sum > thresh) { - *pabove = 1; - if (!tab8) LEPT_FREE(tab); - return 0; - } - } - - if (!tab8) LEPT_FREE(tab); - return 0; -} - - -/*! - * \brief makePixelSumTab8() - * - * \return table of 256 l_int32. - * - *- * Notes: - * (1) This table of integers gives the number of 1 bits - * in the 8 bit index. - *- */ -l_int32 * -makePixelSumTab8(void) -{ -l_uint8 byte; -l_int32 i; -l_int32 *tab; - - tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - for (i = 0; i < 256; i++) { - byte = (l_uint8)i; - tab[i] = (byte & 0x1) + - ((byte >> 1) & 0x1) + - ((byte >> 2) & 0x1) + - ((byte >> 3) & 0x1) + - ((byte >> 4) & 0x1) + - ((byte >> 5) & 0x1) + - ((byte >> 6) & 0x1) + - ((byte >> 7) & 0x1); - } - return tab; -} - - -/*! - * \brief makePixelCentroidTab8() - * - * \return table of 256 l_int32. - * - *- * Notes: - * (1) This table of integers gives the centroid weight of the 1 bits - * in the 8 bit index. In other words, if sumtab is obtained by - * makePixelSumTab8, and centroidtab is obtained by - * makePixelCentroidTab8, then, for 1 <= i <= 255, - * centroidtab[i] / (float)sumtab[i] - * is the centroid of the 1 bits in the 8-bit index i, where the - * MSB is considered to have position 0 and the LSB is considered - * to have position 7. - *- */ -l_int32 * -makePixelCentroidTab8(void) -{ -l_int32 i; -l_int32 *tab; - - tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - tab[0] = 0; - tab[1] = 7; - for (i = 2; i < 4; i++) { - tab[i] = tab[i - 2] + 6; - } - for (i = 4; i < 8; i++) { - tab[i] = tab[i - 4] + 5; - } - for (i = 8; i < 16; i++) { - tab[i] = tab[i - 8] + 4; - } - for (i = 16; i < 32; i++) { - tab[i] = tab[i - 16] + 3; - } - for (i = 32; i < 64; i++) { - tab[i] = tab[i - 32] + 2; - } - for (i = 64; i < 128; i++) { - tab[i] = tab[i - 64] + 1; - } - for (i = 128; i < 256; i++) { - tab[i] = tab[i - 128]; - } - return tab; -} - - -/*-------------------------------------------------------------* - * Average of pixel values in gray images * - *-------------------------------------------------------------*/ -/*! - * \brief pixAverageByRow() - * - * \param[in] pix 8 or 16 bpp; no colormap - * \param[in] box [optional] clipping box for sum; can be null - * \param[in] type L_WHITE_IS_MAX, L_BLACK_IS_MAX - * \return na of pixel averages by row, or NULL on error - * - *- * Notes: - * (1) To resample for a bin size different from 1, use - * numaUniformSampling() on the result of this function. - * (2) If type == L_BLACK_IS_MAX, black pixels get the maximum - * value (0xff for 8 bpp, 0xffff for 16 bpp) and white get 0. - *- */ -NUMA * -pixAverageByRow(PIX *pix, - BOX *box, - l_int32 type) -{ -l_int32 i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh; -l_uint32 *line, *data; -l_float64 norm, sum; -NUMA *na; - - PROCNAME("pixAverageByRow"); - - if (!pix) - return (NUMA *)ERROR_PTR("pix not defined", procName, NULL); - pixGetDimensions(pix, &w, &h, &d); - if (d != 8 && d != 16) - return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", procName, NULL); - if (type != L_WHITE_IS_MAX && type != L_BLACK_IS_MAX) - return (NUMA *)ERROR_PTR("invalid type", procName, NULL); - if (pixGetColormap(pix) != NULL) - return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL); - - if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, - &bw, &bh) == 1) - return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL); - - norm = 1. / (l_float32)bw; - if ((na = numaCreate(bh)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - numaSetParameters(na, ystart, 1); - data = pixGetData(pix); - wpl = pixGetWpl(pix); - for (i = ystart; i < yend; i++) { - sum = 0.0; - line = data + i * wpl; - if (d == 8) { - for (j = xstart; j < xend; j++) - sum += GET_DATA_BYTE(line, j); - if (type == L_BLACK_IS_MAX) - sum = bw * 255 - sum; - } else { /* d == 16 */ - for (j = xstart; j < xend; j++) - sum += GET_DATA_TWO_BYTES(line, j); - if (type == L_BLACK_IS_MAX) - sum = bw * 0xffff - sum; - } - numaAddNumber(na, (l_float32)(norm * sum)); - } - - return na; -} - - -/*! - * \brief pixAverageByColumn() - * - * \param[in] pix 8 or 16 bpp; no colormap - * \param[in] box [optional] clipping box for sum; can be null - * \param[in] type L_WHITE_IS_MAX, L_BLACK_IS_MAX - * \return na of pixel averages by column, or NULL on error - * - *- * Notes: - * (1) To resample for a bin size different from 1, use - * numaUniformSampling() on the result of this function. - * (2) If type == L_BLACK_IS_MAX, black pixels get the maximum - * value (0xff for 8 bpp, 0xffff for 16 bpp) and white get 0. - *- */ -NUMA * -pixAverageByColumn(PIX *pix, - BOX *box, - l_int32 type) -{ -l_int32 i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh; -l_uint32 *line, *data; -l_float32 norm, sum; -NUMA *na; - - PROCNAME("pixAverageByColumn"); - - if (!pix) - return (NUMA *)ERROR_PTR("pix not defined", procName, NULL); - pixGetDimensions(pix, &w, &h, &d); - - if (d != 8 && d != 16) - return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", procName, NULL); - if (type != L_WHITE_IS_MAX && type != L_BLACK_IS_MAX) - return (NUMA *)ERROR_PTR("invalid type", procName, NULL); - if (pixGetColormap(pix) != NULL) - return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL); - - if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, - &bw, &bh) == 1) - return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL); - - if ((na = numaCreate(bw)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - numaSetParameters(na, xstart, 1); - norm = 1. / (l_float32)bh; - data = pixGetData(pix); - wpl = pixGetWpl(pix); - for (j = xstart; j < xend; j++) { - sum = 0.0; - if (d == 8) { - for (i = ystart; i < yend; i++) { - line = data + i * wpl; - sum += GET_DATA_BYTE(line, j); - } - if (type == L_BLACK_IS_MAX) - sum = bh * 255 - sum; - } else { /* d == 16 */ - for (i = ystart; i < yend; i++) { - line = data + i * wpl; - sum += GET_DATA_TWO_BYTES(line, j); - } - if (type == L_BLACK_IS_MAX) - sum = bh * 0xffff - sum; - } - numaAddNumber(na, (l_float32)(norm * sum)); - } - - return na; -} - - -/*! - * \brief pixAverageInRect() - * - * \param[in] pixs 1, 2, 4, 8 bpp; not cmapped - * \param[in] pixm [optional] 1 bpp mask; if null, use all pixels - * \param[in] box [optional] if null, use entire image - * \param[in] minval ignore values less than this - * \param[in] maxval ignore values greater than this - * \param[in] subsamp subsample factor: integer; use 1 for all pixels - * \param[out] pave average of pixel values under consideration - * \return 0 if OK; 1 on error; 2 if all pixels are filtered out - * - *- * Notes: - * (1) The average is computed with 4 optional filters: a rectangle, - * a mask, a contiguous set of range values, and subsampling. - * In practice you might use only one or two of these. - * (2) The mask %pixm is a blocking mask: only count pixels in the bg. - * If it exists, alignment is assumed at UL corner and computation - * is over the minimum intersection of %pixs and %pixm. - * If you want the average of pixels under the mask fg, invert it. - * (3) Set the range limits %minval = 0 and %maxval = 255 to use - * all non-masked pixels (regardless of value) in the average. - * (4) If no pixels are used in the averaging, the returned average - * value is 0 and the function returns 2. This is not an error, - * but it says to disregard the returned average value. - * (5) For example, to average all pixels in a given clipping rect %box, - * pixAverageInRect(pixs, NULL, box, 0, 255, 1, &aveval); - *- */ -l_ok -pixAverageInRect(PIX *pixs, - PIX *pixm, - BOX *box, - l_int32 minval, - l_int32 maxval, - l_int32 subsamp, - l_float32 *pave) -{ -l_int32 w, h, d, wpls, wm, hm, dm, wplm, val, count; -l_int32 i, j, xstart, xend, ystart, yend; -l_uint32 *datas, *datam, *lines, *linem; -l_float32 ave; -l_float64 sum; - - PROCNAME("pixAverageInRect"); - - if (!pave) - return ERROR_INT("&ave not defined", procName, 1); - *pave = 0; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetColormap(pixs) != NULL) - return ERROR_INT("pixs is colormapped", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 1 && d != 2 && d != 4 && d != 8) - return ERROR_INT("pixs not 1, 2, 4 or 8 bpp", procName, 1); - if (pixm) { - pixGetDimensions(pixm, &wm, &hm, &dm); - if (dm != 1) - return ERROR_INT("pixm not 1 bpp", procName, 1); - w = L_MIN(w, wm); - h = L_MIN(h, hm); - } - if (subsamp < 1) - return ERROR_INT("subsamp must be >= 1", procName, 1); - - if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, - NULL, NULL) == 1) - return ERROR_INT("invalid clipping box", procName, 1); - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if (pixm) { - datam = pixGetData(pixm); - wplm = pixGetWpl(pixm); - } - sum = 0.0; - count = 0; - for (i = ystart; i < yend; i += subsamp) { - lines = datas + i * wpls; - if (pixm) - linem = datam + i * wplm; - for (j = xstart; j < xend; j += subsamp) { - if (pixm && (GET_DATA_BIT(linem, j) == 1)) - continue; - if (d == 1) - val = GET_DATA_BIT(lines, j); - else if (d == 2) - val = GET_DATA_DIBIT(lines, j); - else if (d == 4) - val = GET_DATA_QBIT(lines, j); - else /* d == 8 */ - val = GET_DATA_BYTE(lines, j); - if (val >= minval && val <= maxval) { - sum += val; - count++; - } - } - } - - if (count == 0) - return 2; /* not an error; don't use the average value (0.0) */ - *pave = sum / (l_float32)count; - return 0; -} - - -/*-------------------------------------------------------------* - * Average of pixel values in RGB images * - *-------------------------------------------------------------*/ -/*! - * \brief pixAverageInRectRGB() - * - * \param[in] pixs rgb; not cmapped - * \param[in] pixm [optional] 1 bpp mask; if null, use all pixels - * \param[in] box [optional] if null, use entire image - * \param[in] subsamp subsample factor: integer; use 1 for all pixels - * \param[out] pave average color of pixel values under consideration, - * in format 0xrrggbb00. - * \return 0 if OK; 1 on error; 2 if all pixels are filtered out - * - *- * Notes: - * (1) The average is computed with 3 optional filters: a rectangle, - * a mask, and subsampling. - * In practice you might use only one or two of these. - * (2) The mask %pixm is a blocking mask: only count pixels in the bg. - * If it exists, alignment is assumed at UL corner and computation - * is over the minimum intersection of %pixs and %pixm. - * If you want the average of pixels under the mask fg, invert it. - * (3) If no pixels are used in the averaging, the returned average - * value is 0 and the function returns 2. This is not an error, - * but it says to disregard the returned average value. - * (4) For example, to average all pixels in a given clipping rect %box, - * pixAverageInRectRGB(pixs, NULL, box, 1, &aveval); - *- */ -l_ok -pixAverageInRectRGB(PIX *pixs, - PIX *pixm, - BOX *box, - l_int32 subsamp, - l_uint32 *pave) -{ -l_int32 w, h, wpls, wm, hm, dm, wplm, i, j, xstart, xend, ystart, yend; -l_int32 rval, gval, bval, rave, gave, bave, count; -l_uint32 *datas, *datam, *lines, *linem; -l_uint32 pixel; -l_float64 rsum, gsum, bsum; - - PROCNAME("pixAverageInRectRGB"); - - if (!pave) - return ERROR_INT("&ave not defined", procName, 1); - *pave = 0; - if (!pixs || pixGetDepth(pixs) != 32) - return ERROR_INT("pixs undefined or not 32 bpp", procName, 1); - pixGetDimensions(pixs, &w, &h, NULL); - if (pixm) { - pixGetDimensions(pixm, &wm, &hm, &dm); - if (dm != 1) - return ERROR_INT("pixm not 1 bpp", procName, 1); - w = L_MIN(w, wm); - h = L_MIN(h, hm); - } - if (subsamp < 1) - return ERROR_INT("subsamp must be >= 1", procName, 1); - - if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, - NULL, NULL) == 1) - return ERROR_INT("invalid clipping box", procName, 1); - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if (pixm) { - datam = pixGetData(pixm); - wplm = pixGetWpl(pixm); - } - rsum = gsum = bsum = 0.0; - count = 0; - for (i = ystart; i < yend; i += subsamp) { - lines = datas + i * wpls; - if (pixm) - linem = datam + i * wplm; - for (j = xstart; j < xend; j += subsamp) { - if (pixm && (GET_DATA_BIT(linem, j) == 1)) - continue; - pixel = *(lines + j); - extractRGBValues(pixel, &rval, &gval, &bval); - rsum += rval; - gsum += gval; - bsum += bval; - count++; - } - } - - if (count == 0) - return 2; /* not an error */ - rave = (l_uint32)(rsum / (l_float64)count); - gave = (l_uint32)(gsum / (l_float64)count); - bave = (l_uint32)(bsum / (l_float64)count); - composeRGBPixel(rave, gave, bave, pave); - return 0; -} - - -/*------------------------------------------------------------------* - * Variance of pixel values in gray images * - *------------------------------------------------------------------*/ -/*! - * \brief pixVarianceByRow() - * - * \param[in] pix 8 or 16 bpp; no colormap - * \param[in] box [optional] clipping box for variance; can be null - * \return na of rmsdev by row, or NULL on error - * - *- * Notes: - * (1) To resample for a bin size different from 1, use - * numaUniformSampling() on the result of this function. - * (2) We are actually computing the RMS deviation in each row. - * This is the square root of the variance. - *- */ -NUMA * -pixVarianceByRow(PIX *pix, - BOX *box) -{ -l_int32 i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh, val; -l_uint32 *line, *data; -l_float64 sum1, sum2, norm, ave, var, rootvar; -NUMA *na; - - PROCNAME("pixVarianceByRow"); - - if (!pix) - return (NUMA *)ERROR_PTR("pix not defined", procName, NULL); - pixGetDimensions(pix, &w, &h, &d); - if (d != 8 && d != 16) - return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", procName, NULL); - if (pixGetColormap(pix) != NULL) - return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL); - - if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, - &bw, &bh) == 1) - return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL); - - if ((na = numaCreate(bh)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - numaSetParameters(na, ystart, 1); - norm = 1. / (l_float32)bw; - data = pixGetData(pix); - wpl = pixGetWpl(pix); - for (i = ystart; i < yend; i++) { - sum1 = sum2 = 0.0; - line = data + i * wpl; - for (j = xstart; j < xend; j++) { - if (d == 8) - val = GET_DATA_BYTE(line, j); - else /* d == 16 */ - val = GET_DATA_TWO_BYTES(line, j); - sum1 += val; - sum2 += (l_float64)(val) * val; - } - ave = norm * sum1; - var = norm * sum2 - ave * ave; - rootvar = sqrt(var); - numaAddNumber(na, (l_float32)rootvar); - } - - return na; -} - - -/*! - * \brief pixVarianceByColumn() - * - * \param[in] pix 8 or 16 bpp; no colormap - * \param[in] box [optional] clipping box for variance; can be null - * \return na of rmsdev by column, or NULL on error - * - *- * Notes: - * (1) To resample for a bin size different from 1, use - * numaUniformSampling() on the result of this function. - * (2) We are actually computing the RMS deviation in each row. - * This is the square root of the variance. - *- */ -NUMA * -pixVarianceByColumn(PIX *pix, - BOX *box) -{ -l_int32 i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh, val; -l_uint32 *line, *data; -l_float64 sum1, sum2, norm, ave, var, rootvar; -NUMA *na; - - PROCNAME("pixVarianceByColumn"); - - if (!pix) - return (NUMA *)ERROR_PTR("pix not defined", procName, NULL); - pixGetDimensions(pix, &w, &h, &d); - if (d != 8 && d != 16) - return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", procName, NULL); - if (pixGetColormap(pix) != NULL) - return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL); - - if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, - &bw, &bh) == 1) - return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL); - - if ((na = numaCreate(bw)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - numaSetParameters(na, xstart, 1); - norm = 1. / (l_float32)bh; - data = pixGetData(pix); - wpl = pixGetWpl(pix); - for (j = xstart; j < xend; j++) { - sum1 = sum2 = 0.0; - for (i = ystart; i < yend; i++) { - line = data + wpl * i; - if (d == 8) - val = GET_DATA_BYTE(line, j); - else /* d == 16 */ - val = GET_DATA_TWO_BYTES(line, j); - sum1 += val; - sum2 += (l_float64)(val) * val; - } - ave = norm * sum1; - var = norm * sum2 - ave * ave; - rootvar = sqrt(var); - numaAddNumber(na, (l_float32)rootvar); - } - - return na; -} - - -/*! - * \brief pixVarianceInRect() - * - * \param[in] pix 1, 2, 4, 8 bpp; not cmapped - * \param[in] box [optional] if null, use entire image - * \param[out] prootvar sqrt variance of pixel values in region - * \return 0 if OK; 1 on error - */ -l_ok -pixVarianceInRect(PIX *pix, - BOX *box, - l_float32 *prootvar) -{ -l_int32 w, h, d, wpl, i, j, xstart, xend, ystart, yend, bw, bh, val; -l_uint32 *data, *line; -l_float64 sum1, sum2, norm, ave, var; - - PROCNAME("pixVarianceInRect"); - - if (!prootvar) - return ERROR_INT("&rootvar not defined", procName, 1); - *prootvar = 0.0; - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - pixGetDimensions(pix, &w, &h, &d); - if (d != 1 && d != 2 && d != 4 && d != 8) - return ERROR_INT("pix not 1, 2, 4 or 8 bpp", procName, 1); - if (pixGetColormap(pix) != NULL) - return ERROR_INT("pix is colormapped", procName, 1); - - if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, - &bw, &bh) == 1) - return ERROR_INT("invalid clipping box", procName, 1); - - wpl = pixGetWpl(pix); - data = pixGetData(pix); - sum1 = sum2 = 0.0; - for (i = ystart; i < yend; i++) { - line = data + i * wpl; - for (j = xstart; j < xend; j++) { - if (d == 1) { - val = GET_DATA_BIT(line, j); - sum1 += val; - sum2 += (l_float64)(val) * val; - } else if (d == 2) { - val = GET_DATA_DIBIT(line, j); - sum1 += val; - sum2 += (l_float64)(val) * val; - } else if (d == 4) { - val = GET_DATA_QBIT(line, j); - sum1 += val; - sum2 += (l_float64)(val) * val; - } else { /* d == 8 */ - val = GET_DATA_BYTE(line, j); - sum1 += val; - sum2 += (l_float64)(val) * val; - } - } - } - norm = 1.0 / ((l_float64)(bw) * bh); - ave = norm * sum1; - var = norm * sum2 - ave * ave; - *prootvar = (l_float32)sqrt(var); - return 0; -} - - -/*---------------------------------------------------------------------* - * Average of absolute value of pixel differences in gray images * - *---------------------------------------------------------------------*/ -/*! - * \brief pixAbsDiffByRow() - * - * \param[in] pix 8 bpp; no colormap - * \param[in] box [optional] clipping box for region; can be null - * \return na of abs val pixel difference averages by row, or NULL on error - * - *- * Notes: - * (1) This is an average over differences of adjacent pixels along - * each row. - * (2) To resample for a bin size different from 1, use - * numaUniformSampling() on the result of this function. - *- */ -NUMA * -pixAbsDiffByRow(PIX *pix, - BOX *box) -{ -l_int32 i, j, w, h, wpl, xstart, xend, ystart, yend, bw, bh, val0, val1; -l_uint32 *line, *data; -l_float64 norm, sum; -NUMA *na; - - PROCNAME("pixAbsDiffByRow"); - - if (!pix || pixGetDepth(pix) != 8) - return (NUMA *)ERROR_PTR("pix undefined or not 8 bpp", procName, NULL); - if (pixGetColormap(pix) != NULL) - return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL); - - pixGetDimensions(pix, &w, &h, NULL); - if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, - &bw, &bh) == 1) - return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL); - if (bw < 2) - return (NUMA *)ERROR_PTR("row width must be >= 2", procName, NULL); - - norm = 1. / (l_float32)(bw - 1); - if ((na = numaCreate(bh)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - numaSetParameters(na, ystart, 1); - data = pixGetData(pix); - wpl = pixGetWpl(pix); - for (i = ystart; i < yend; i++) { - sum = 0.0; - line = data + i * wpl; - val0 = GET_DATA_BYTE(line, xstart); - for (j = xstart + 1; j < xend; j++) { - val1 = GET_DATA_BYTE(line, j); - sum += L_ABS(val1 - val0); - val0 = val1; - } - numaAddNumber(na, (l_float32)(norm * sum)); - } - - return na; -} - - -/*! - * \brief pixAbsDiffByColumn() - * - * \param[in] pix 8 bpp; no colormap - * \param[in] box [optional] clipping box for region; can be null - * \return na of abs val pixel difference averages by column, - * or NULL on error - * - *- * Notes: - * (1) This is an average over differences of adjacent pixels along - * each column. - * (2) To resample for a bin size different from 1, use - * numaUniformSampling() on the result of this function. - *- */ -NUMA * -pixAbsDiffByColumn(PIX *pix, - BOX *box) -{ -l_int32 i, j, w, h, wpl, xstart, xend, ystart, yend, bw, bh, val0, val1; -l_uint32 *line, *data; -l_float64 norm, sum; -NUMA *na; - - PROCNAME("pixAbsDiffByColumn"); - - if (!pix || pixGetDepth(pix) != 8) - return (NUMA *)ERROR_PTR("pix undefined or not 8 bpp", procName, NULL); - if (pixGetColormap(pix) != NULL) - return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL); - - pixGetDimensions(pix, &w, &h, NULL); - if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, - &bw, &bh) == 1) - return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL); - if (bh < 2) - return (NUMA *)ERROR_PTR("column height must be >= 2", procName, NULL); - - norm = 1. / (l_float32)(bh - 1); - if ((na = numaCreate(bw)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - numaSetParameters(na, xstart, 1); - data = pixGetData(pix); - wpl = pixGetWpl(pix); - for (j = xstart; j < xend; j++) { - sum = 0.0; - line = data + ystart * wpl; - val0 = GET_DATA_BYTE(line, j); - for (i = ystart + 1; i < yend; i++) { - line = data + i * wpl; - val1 = GET_DATA_BYTE(line, j); - sum += L_ABS(val1 - val0); - val0 = val1; - } - numaAddNumber(na, (l_float32)(norm * sum)); - } - - return na; -} - - -/*! - * \brief pixAbsDiffInRect() - * - * \param[in] pix 8 bpp; not cmapped - * \param[in] box [optional] if null, use entire image - * \param[in] dir differences along L_HORIZONTAL_LINE or L_VERTICAL_LINE - * \param[out] pabsdiff average of abs diff pixel values in region - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) This gives the average over the abs val of differences of - * adjacent pixels values, along either each - * row: dir == L_HORIZONTAL_LINE - * column: dir == L_VERTICAL_LINE - *- */ -l_ok -pixAbsDiffInRect(PIX *pix, - BOX *box, - l_int32 dir, - l_float32 *pabsdiff) -{ -l_int32 w, h, wpl, i, j, xstart, xend, ystart, yend, bw, bh, val0, val1; -l_uint32 *data, *line; -l_float64 norm, sum; - - PROCNAME("pixAbsDiffInRect"); - - if (!pabsdiff) - return ERROR_INT("&absdiff not defined", procName, 1); - *pabsdiff = 0.0; - if (!pix || pixGetDepth(pix) != 8) - return ERROR_INT("pix undefined or not 8 bpp", procName, 1); - if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE) - return ERROR_INT("invalid direction", procName, 1); - if (pixGetColormap(pix) != NULL) - return ERROR_INT("pix is colormapped", procName, 1); - - pixGetDimensions(pix, &w, &h, NULL); - if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, - &bw, &bh) == 1) - return ERROR_INT("invalid clipping box", procName, 1); - - wpl = pixGetWpl(pix); - data = pixGetData(pix); - if (dir == L_HORIZONTAL_LINE) { - norm = 1. / (l_float32)(bh * (bw - 1)); - sum = 0.0; - for (i = ystart; i < yend; i++) { - line = data + i * wpl; - val0 = GET_DATA_BYTE(line, xstart); - for (j = xstart + 1; j < xend; j++) { - val1 = GET_DATA_BYTE(line, j); - sum += L_ABS(val1 - val0); - val0 = val1; - } - } - } else { /* vertical line */ - norm = 1. / (l_float32)(bw * (bh - 1)); - sum = 0.0; - for (j = xstart; j < xend; j++) { - line = data + ystart * wpl; - val0 = GET_DATA_BYTE(line, j); - for (i = ystart + 1; i < yend; i++) { - line = data + i * wpl; - val1 = GET_DATA_BYTE(line, j); - sum += L_ABS(val1 - val0); - val0 = val1; - } - } - } - *pabsdiff = (l_float32)(norm * sum); - return 0; -} - - -/*! - * \brief pixAbsDiffOnLine() - * - * \param[in] pix 8 bpp; not cmapped - * \param[in] x1, y1 first point; x1 <= x2, y1 <= y2 - * \param[in] x2, y2 first point - * \param[out] pabsdiff average of abs diff pixel values on line - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) This gives the average over the abs val of differences of - * adjacent pixels values, along a line that is either horizontal - * or vertical. - * (2) If horizontal, require x1 < x2; if vertical, require y1 < y2. - *- */ -l_ok -pixAbsDiffOnLine(PIX *pix, - l_int32 x1, - l_int32 y1, - l_int32 x2, - l_int32 y2, - l_float32 *pabsdiff) -{ -l_int32 w, h, i, j, dir, size, sum; -l_uint32 val0, val1; - - PROCNAME("pixAbsDiffOnLine"); - - if (!pabsdiff) - return ERROR_INT("&absdiff not defined", procName, 1); - *pabsdiff = 0.0; - if (!pix || pixGetDepth(pix) != 8) - return ERROR_INT("pix undefined or not 8 bpp", procName, 1); - if (y1 == y2) { - dir = L_HORIZONTAL_LINE; - } else if (x1 == x2) { - dir = L_VERTICAL_LINE; - } else { - return ERROR_INT("line is neither horiz nor vert", procName, 1); - } - if (pixGetColormap(pix) != NULL) - return ERROR_INT("pix is colormapped", procName, 1); - - pixGetDimensions(pix, &w, &h, NULL); - sum = 0; - if (dir == L_HORIZONTAL_LINE) { - x1 = L_MAX(x1, 0); - x2 = L_MIN(x2, w - 1); - if (x1 >= x2) - return ERROR_INT("x1 >= x2", procName, 1); - size = x2 - x1; - pixGetPixel(pix, x1, y1, &val0); - for (j = x1 + 1; j <= x2; j++) { - pixGetPixel(pix, j, y1, &val1); - sum += L_ABS((l_int32)val1 - (l_int32)val0); - val0 = val1; - } - } else { /* vertical */ - y1 = L_MAX(y1, 0); - y2 = L_MIN(y2, h - 1); - if (y1 >= y2) - return ERROR_INT("y1 >= y2", procName, 1); - size = y2 - y1; - pixGetPixel(pix, x1, y1, &val0); - for (i = y1 + 1; i <= y2; i++) { - pixGetPixel(pix, x1, i, &val1); - sum += L_ABS((l_int32)val1 - (l_int32)val0); - val0 = val1; - } - } - *pabsdiff = (l_float32)sum / (l_float32)size; - return 0; -} - - -/*-------------------------------------------------------------* - * Count of pixels with specific value * - *-------------------------------------------------------------*/ -/*! - * \brief pixCountArbInRect() - * - * \param[in] pixs 8 bpp, or colormapped - * \param[in] box [optional] over which count is made; - * use entire image if NULL - * \param[in] val pixel value to count - * \param[in] factor subsampling factor; integer >= 1 - * \param[out] pcount count; estimate it if factor > 1 - * \return na histogram, or NULL on error - * - *- * Notes: - * (1) If pixs is cmapped, %val is compared to the colormap index; - * otherwise, %val is compared to the grayscale value. - * (2) Set the subsampling %factor > 1 to reduce the amount of computation. - * If %factor > 1, multiply the count by %factor * %factor. - *- */ -l_int32 -pixCountArbInRect(PIX *pixs, - BOX *box, - l_int32 val, - l_int32 factor, - l_int32 *pcount) -{ -l_int32 i, j, bx, by, bw, bh, w, h, wpl, pixval; -l_uint32 *data, *line; - - PROCNAME("pixCountArbInRect"); - - if (!pcount) - return ERROR_INT("&count not defined", procName, 1); - *pcount = 0; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs)) - return ERROR_INT("pixs neither 8 bpp nor colormapped", - procName, 1); - if (factor < 1) - return ERROR_INT("sampling factor < 1", procName, 1); - - pixGetDimensions(pixs, &w, &h, NULL); - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - - if (!box) { - for (i = 0; i < h; i += factor) { - line = data + i * wpl; - for (j = 0; j < w; j += factor) { - pixval = GET_DATA_BYTE(line, j); - if (pixval == val) (*pcount)++; - } - } - } else { - boxGetGeometry(box, &bx, &by, &bw, &bh); - for (i = 0; i < bh; i += factor) { - if (by + i < 0 || by + i >= h) continue; - line = data + (by + i) * wpl; - for (j = 0; j < bw; j += factor) { - if (bx + j < 0 || bx + j >= w) continue; - pixval = GET_DATA_BYTE(line, bx + j); - if (pixval == val) (*pcount)++; - } - } - } - - if (factor > 1) /* assume pixel color is randomly distributed */ - *pcount = *pcount * factor * factor; - return 0; -} - - -/*-------------------------------------------------------------* - * Mirrored tiling of a smaller image * - *-------------------------------------------------------------*/ -/*! - * \brief pixMirroredTiling() - * - * \param[in] pixs 8 or 32 bpp, small tile; to be replicated - * \param[in] w, h dimensions of output pix - * \return pixd usually larger pix, mirror-tiled with pixs, - * or NULL on error - * - *- * Notes: - * (1) This uses mirrored tiling, where each row alternates - * with LR flips and every column alternates with TB - * flips, such that the result is a tiling with identical - * 2 x 2 tiles, each of which is composed of these transforms: - * ----------------- - * | 1 | LR | - * ----------------- - * | TB | LR/TB | - * ----------------- - *- */ -PIX * -pixMirroredTiling(PIX *pixs, - l_int32 w, - l_int32 h) -{ -l_int32 wt, ht, d, i, j, nx, ny; -PIX *pixd, *pixsfx, *pixsfy, *pixsfxy, *pix; - - PROCNAME("pixMirroredTiling"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &wt, &ht, &d); - if (wt <= 0 || ht <= 0) - return (PIX *)ERROR_PTR("pixs size illegal", procName, NULL); - if (d != 8 && d != 32) - return (PIX *)ERROR_PTR("depth not 32 bpp", procName, NULL); - - if ((pixd = pixCreate(w, h, d)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopySpp(pixd, pixs); - - nx = (w + wt - 1) / wt; - ny = (h + ht - 1) / ht; - pixsfx = pixFlipLR(NULL, pixs); - pixsfy = pixFlipTB(NULL, pixs); - pixsfxy = pixFlipTB(NULL, pixsfx); - for (i = 0; i < ny; i++) { - for (j = 0; j < nx; j++) { - pix = pixs; - if ((i & 1) && !(j & 1)) - pix = pixsfy; - else if (!(i & 1) && (j & 1)) - pix = pixsfx; - else if ((i & 1) && (j & 1)) - pix = pixsfxy; - pixRasterop(pixd, j * wt, i * ht, wt, ht, PIX_SRC, pix, 0, 0); - } - } - - pixDestroy(&pixsfx); - pixDestroy(&pixsfy); - pixDestroy(&pixsfxy); - return pixd; -} - - -/*! - * \brief pixFindRepCloseTile() - * - * \param[in] pixs 32 bpp rgb - * \param[in] box region of pixs to search around - * \param[in] searchdir L_HORIZ or L_VERT; direction to search - * \param[in] mindist min distance of selected tile edge from box; >= 0 - * \param[in] tsize tile size; > 1; even; typically ~50 - * \param[in] ntiles number of tiles tested in each row/column - * \param[out] pboxtile region of best tile - * \param[in] debug 1 for debug output - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This looks for one or two square tiles with conforming median - * intensity and low variance, that is outside but near the input box. - * (2) %mindist specifies the gap between the box and the - * potential tiles. The tiles are given an overlap of 50%. - * %ntiles specifies the number of tiles that are tested - * beyond %mindist for each row or column. - * (3) For example, if %mindist = 20, %tilesize = 50 and %ntiles = 3, - * a horizontal search to the right will have 3 tiles in each row, - * with left edges at 20, 45 and 70 from the right edge of the - * input %box. The number of rows of tiles is determined by - * the height of %box and %tsize, with the 50% overlap.. - *- */ -l_ok -pixFindRepCloseTile(PIX *pixs, - BOX *box, - l_int32 searchdir, - l_int32 mindist, - l_int32 tsize, - l_int32 ntiles, - BOX **pboxtile, - l_int32 debug) -{ -l_int32 w, h, i, n, bestindex; -l_float32 var_of_mean, median_of_mean, median_of_stdev, mean_val, stdev_val; -l_float32 mindels, bestdelm, delm, dels, mean, stdev; -BOXA *boxa; -NUMA *namean, *nastdev; -PIX *pix, *pixg; -PIXA *pixa; - - PROCNAME("pixFindRepCloseTile"); - - if (!pboxtile) - return ERROR_INT("&boxtile not defined", procName, 1); - *pboxtile = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - if (searchdir != L_HORIZ && searchdir != L_VERT) - return ERROR_INT("invalid searchdir", procName, 1); - if (mindist < 0) - return ERROR_INT("mindist must be >= 0", procName, 1); - if (tsize < 2) - return ERROR_INT("tsize must be > 1", procName, 1); - if (ntiles > 7) { - L_WARNING("ntiles = %d; larger than suggested max of 7\n", - procName, ntiles); - } - - /* Locate tile regions */ - pixGetDimensions(pixs, &w, &h, NULL); - boxa = findTileRegionsForSearch(box, w, h, searchdir, mindist, - tsize, ntiles); - if (!boxa) - return ERROR_INT("no tiles found", procName, 1); - - /* Generate the tiles and the mean and stdev of intensity */ - pixa = pixClipRectangles(pixs, boxa); - n = pixaGetCount(pixa); - namean = numaCreate(n); - nastdev = numaCreate(n); - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixa, i, L_CLONE); - pixg = pixConvertRGBToGray(pix, 0.33, 0.34, 0.33); - pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_MEAN_ABSVAL, &mean); - pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_STANDARD_DEVIATION, &stdev); - numaAddNumber(namean, mean); - numaAddNumber(nastdev, stdev); - pixDestroy(&pix); - pixDestroy(&pixg); - } - - /* Find the median and variance of the averages. We require - * the best tile to have a mean pixel intensity within a standard - * deviation of the median of mean intensities, and choose the - * tile in that set with the smallest stdev of pixel intensities - * (as a proxy for the tile with least visible structure). - * The median of the stdev is used, for debugging, as a normalizing - * factor for the stdev of intensities within a tile. */ - numaGetStatsUsingHistogram(namean, 256, NULL, NULL, NULL, &var_of_mean, - &median_of_mean, 0.0, NULL, NULL); - numaGetStatsUsingHistogram(nastdev, 256, NULL, NULL, NULL, NULL, - &median_of_stdev, 0.0, NULL, NULL); - mindels = 1000.0; - bestdelm = 1000.0; - bestindex = 0; - for (i = 0; i < n; i++) { - numaGetFValue(namean, i, &mean_val); - numaGetFValue(nastdev, i, &stdev_val); - if (var_of_mean == 0.0) { /* uniform color; any box will do */ - delm = 0.0; /* any value < 1.01 */ - dels = 1.0; /* n'importe quoi */ - } else { - delm = L_ABS(mean_val - median_of_mean) / sqrt(var_of_mean); - dels = stdev_val / median_of_stdev; - } - if (delm < 1.01) { - if (dels < mindels) { - if (debug) { - lept_stderr("i = %d, mean = %7.3f, delm = %7.3f," - " stdev = %7.3f, dels = %7.3f\n", - i, mean_val, delm, stdev_val, dels); - } - mindels = dels; - bestdelm = delm; - bestindex = i; - } - } - } - *pboxtile = boxaGetBox(boxa, bestindex, L_COPY); - - if (debug) { - L_INFO("median of mean = %7.3f\n", procName, median_of_mean); - L_INFO("standard dev of mean = %7.3f\n", procName, sqrt(var_of_mean)); - L_INFO("median of stdev = %7.3f\n", procName, median_of_stdev); - L_INFO("best tile: index = %d\n", procName, bestindex); - L_INFO("delta from median in units of stdev = %5.3f\n", - procName, bestdelm); - L_INFO("stdev as fraction of median stdev = %5.3f\n", - procName, mindels); - } - - numaDestroy(&namean); - numaDestroy(&nastdev); - pixaDestroy(&pixa); - boxaDestroy(&boxa); - return 0; -} - - -/*! - * \brief findTileRegionsForSearch() - * - * \param[in] box region of Pix to search around - * \param[in] w, h dimensions of Pix - * \param[in] searchdir L_HORIZ or L_VERT; direction to search - * \param[in] mindist min distance of selected tile edge from box; >= 0 - * \param[in] tsize tile size; > 1; even; typically ~50 - * \param[in] ntiles number of tiles tested in each row/column - * \return boxa if OK, or NULL on error - * - *- * Notes: - * (1) See calling function pixfindRepCloseTile(). - *- */ -static BOXA * -findTileRegionsForSearch(BOX *box, - l_int32 w, - l_int32 h, - l_int32 searchdir, - l_int32 mindist, - l_int32 tsize, - l_int32 ntiles) -{ -l_int32 bx, by, bw, bh, left, right, top, bot, i, j, nrows, ncols; -l_int32 x0, y0, x, y, w_avail, w_needed, h_avail, h_needed, t_avail; -BOX *box1; -BOXA *boxa; - - PROCNAME("findTileRegionsForSearch"); - - if (!box) - return (BOXA *)ERROR_PTR("box not defined", procName, NULL); - if (ntiles == 0) - return (BOXA *)ERROR_PTR("no tiles requested", procName, NULL); - - boxGetGeometry(box, &bx, &by, &bw, &bh); - if (searchdir == L_HORIZ) { - /* Find the tile parameters for the search. Note that the - * tiles are overlapping by 50% in each direction. */ - left = bx; /* distance to left of box */ - right = w - bx - bw + 1; /* distance to right of box */ - w_avail = L_MAX(left, right) - mindist; - if (tsize & 1) tsize++; /* be sure it's even */ - if (w_avail < tsize) { - L_ERROR("tsize = %d, w_avail = %d\n", procName, tsize, w_avail); - return NULL; - } - w_needed = tsize + (ntiles - 1) * (tsize / 2); - if (w_needed > w_avail) { - t_avail = 1 + 2 * (w_avail - tsize) / tsize; - L_WARNING("ntiles = %d; room for only %d\n", procName, - ntiles, t_avail); - ntiles = t_avail; - w_needed = tsize + (ntiles - 1) * (tsize / 2); - } - nrows = L_MAX(1, 1 + 2 * (bh - tsize) / tsize); - - /* Generate the tile regions to search */ - boxa = boxaCreate(0); - if (left > right) /* search to left */ - x0 = bx - w_needed; - else /* search to right */ - x0 = bx + bw + mindist; - for (i = 0; i < nrows; i++) { - y = by + i * tsize / 2; - for (j = 0; j < ntiles; j++) { - x = x0 + j * tsize / 2; - box1 = boxCreate(x, y, tsize, tsize); - boxaAddBox(boxa, box1, L_INSERT); - } - } - } else { /* L_VERT */ - /* Find the tile parameters for the search */ - top = by; /* distance above box */ - bot = h - by - bh + 1; /* distance below box */ - h_avail = L_MAX(top, bot) - mindist; - if (h_avail < tsize) { - L_ERROR("tsize = %d, h_avail = %d\n", procName, tsize, h_avail); - return NULL; - } - h_needed = tsize + (ntiles - 1) * (tsize / 2); - if (h_needed > h_avail) { - t_avail = 1 + 2 * (h_avail - tsize) / tsize; - L_WARNING("ntiles = %d; room for only %d\n", procName, - ntiles, t_avail); - ntiles = t_avail; - h_needed = tsize + (ntiles - 1) * (tsize / 2); - } - ncols = L_MAX(1, 1 + 2 * (bw - tsize) / tsize); - - /* Generate the tile regions to search */ - boxa = boxaCreate(0); - if (top > bot) /* search above */ - y0 = by - h_needed; - else /* search below */ - y0 = by + bh + mindist; - for (j = 0; j < ncols; j++) { - x = bx + j * tsize / 2; - for (i = 0; i < ntiles; i++) { - y = y0 + i * tsize / 2; - box1 = boxCreate(x, y, tsize, tsize); - boxaAddBox(boxa, box1, L_INSERT); - } - } - } - return boxa; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix4.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix4.c deleted file mode 100644 index eb40bd9c..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix4.c +++ /dev/null @@ -1,3460 +0,0 @@ -/*====================================================================* - - 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 pix4.c - *- * - * This file has these operations: - * - * (1) Pixel histograms - * (2) Pixel row/column statistics - * (3) Foreground/background estimation - * - * Pixel histogram, rank val, averaging and min/max - * NUMA *pixGetGrayHistogram() - * NUMA *pixGetGrayHistogramMasked() - * NUMA *pixGetGrayHistogramInRect() - * NUMAA *pixGetGrayHistogramTiled() - * l_int32 pixGetColorHistogram() - * l_int32 pixGetColorHistogramMasked() - * NUMA *pixGetCmapHistogram() - * NUMA *pixGetCmapHistogramMasked() - * NUMA *pixGetCmapHistogramInRect() - * l_int32 pixCountRGBColors() - * L_AMAP *pixGetColorAmapHistogram() - * l_int32 amapGetCountForColor() - * l_int32 pixGetRankValue() - * l_int32 pixGetRankValueMaskedRGB() - * l_int32 pixGetRankValueMasked() - * l_int32 pixGetPixelAverage() - * l_int32 pixGetPixelStats() - * l_int32 pixGetAverageMaskedRGB() - * l_int32 pixGetAverageMasked() - * l_int32 pixGetAverageTiledRGB() - * PIX *pixGetAverageTiled() - * NUMA *pixRowStats() - * NUMA *pixColumnStats() - * l_int32 pixGetRangeValues() - * l_int32 pixGetExtremeValue() - * l_int32 pixGetMaxValueInRect() - * l_int32 pixGetBinnedComponentRange() - * l_int32 pixGetRankColorArray() - * l_int32 pixGetBinnedColor() - * PIX *pixDisplayColorArray() - * PIX *pixRankBinByStrip() - * - * Pixelwise aligned statistics - * PIX *pixaGetAlignedStats() - * l_int32 pixaExtractColumnFromEachPix() - * l_int32 pixGetRowStats() - * l_int32 pixGetColumnStats() - * l_int32 pixSetPixelColumn() - * - * Foreground/background estimation - * l_int32 pixThresholdForFgBg() - * l_int32 pixSplitDistributionFgBg() - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include -#include "allheaders.h" - - -/*------------------------------------------------------------------* - * Pixel histogram and averaging * - *------------------------------------------------------------------*/ -/*! - * \brief pixGetGrayHistogram() - * - * \param[in] pixs 1, 2, 4, 8, 16 bpp; can be colormapped - * \param[in] factor subsampling factor; integer >= 1 - * \return na histogram, or NULL on error - * - * - * Notes: - * (1) If pixs has a colormap, it is converted to 8 bpp gray. - * If you want a histogram of the colormap indices, use - * pixGetCmapHistogram(). - * (2) If pixs does not have a colormap, the output histogram is - * of size 2^d, where d is the depth of pixs. - * (3) Set the subsampling factor > 1 to reduce the amount of computation. - *- */ -NUMA * -pixGetGrayHistogram(PIX *pixs, - l_int32 factor) -{ -l_int32 i, j, w, h, d, wpl, val, size, count; -l_uint32 *data, *line; -l_float32 *array; -NUMA *na; -PIX *pixg; - - PROCNAME("pixGetGrayHistogram"); - - if (!pixs) - return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - if (d > 16) - return (NUMA *)ERROR_PTR("depth not in {1,2,4,8,16}", procName, NULL); - if (factor < 1) - return (NUMA *)ERROR_PTR("sampling must be >= 1", procName, NULL); - - if (pixGetColormap(pixs)) - pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - else - pixg = pixClone(pixs); - - pixGetDimensions(pixg, &w, &h, &d); - size = 1 << d; - if ((na = numaCreate(size)) == NULL) { - pixDestroy(&pixg); - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - } - numaSetCount(na, size); /* all initialized to 0.0 */ - array = numaGetFArray(na, L_NOCOPY); - - if (d == 1) { /* special case */ - pixCountPixels(pixg, &count, NULL); - array[0] = w * h - count; - array[1] = count; - pixDestroy(&pixg); - return na; - } - - wpl = pixGetWpl(pixg); - data = pixGetData(pixg); - for (i = 0; i < h; i += factor) { - line = data + i * wpl; - if (d == 2) { - for (j = 0; j < w; j += factor) { - val = GET_DATA_DIBIT(line, j); - array[val] += 1.0; - } - } else if (d == 4) { - for (j = 0; j < w; j += factor) { - val = GET_DATA_QBIT(line, j); - array[val] += 1.0; - } - } else if (d == 8) { - for (j = 0; j < w; j += factor) { - val = GET_DATA_BYTE(line, j); - array[val] += 1.0; - } - } else { /* d == 16 */ - for (j = 0; j < w; j += factor) { - val = GET_DATA_TWO_BYTES(line, j); - array[val] += 1.0; - } - } - } - - pixDestroy(&pixg); - return na; -} - - -/*! - * \brief pixGetGrayHistogramMasked() - * - * \param[in] pixs 8 bpp, or colormapped - * \param[in] pixm [optional] 1 bpp mask over which histogram is - * to be computed; use all pixels if null - * \param[in] x, y UL corner of pixm relative to the UL corner of pixs; - * can be < 0; these values are ignored if pixm is null - * \param[in] factor subsampling factor; integer >= 1 - * \return na histogram, or NULL on error - * - *- * Notes: - * (1) If pixs is cmapped, it is converted to 8 bpp gray. - * If you want a histogram of the colormap indices, use - * pixGetCmapHistogramMasked(). - * (2) This always returns a 256-value histogram of pixel values. - * (3) Set the subsampling factor > 1 to reduce the amount of computation. - * (4) Clipping of pixm (if it exists) to pixs is done in the inner loop. - * (5) Input x,y are ignored unless pixm exists. - *- */ -NUMA * -pixGetGrayHistogramMasked(PIX *pixs, - PIX *pixm, - l_int32 x, - l_int32 y, - l_int32 factor) -{ -l_int32 i, j, w, h, wm, hm, dm, wplg, wplm, val; -l_uint32 *datag, *datam, *lineg, *linem; -l_float32 *array; -NUMA *na; -PIX *pixg; - - PROCNAME("pixGetGrayHistogramMasked"); - - if (!pixm) - return pixGetGrayHistogram(pixs, factor); - if (!pixs) - return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs)) - return (NUMA *)ERROR_PTR("pixs neither 8 bpp nor colormapped", - procName, NULL); - pixGetDimensions(pixm, &wm, &hm, &dm); - if (dm != 1) - return (NUMA *)ERROR_PTR("pixm not 1 bpp", procName, NULL); - if (factor < 1) - return (NUMA *)ERROR_PTR("sampling must be >= 1", procName, NULL); - - if ((na = numaCreate(256)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - numaSetCount(na, 256); /* all initialized to 0.0 */ - array = numaGetFArray(na, L_NOCOPY); - - if (pixGetColormap(pixs)) - pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - else - pixg = pixClone(pixs); - pixGetDimensions(pixg, &w, &h, NULL); - datag = pixGetData(pixg); - wplg = pixGetWpl(pixg); - datam = pixGetData(pixm); - wplm = pixGetWpl(pixm); - - /* Generate the histogram */ - for (i = 0; i < hm; i += factor) { - if (y + i < 0 || y + i >= h) continue; - lineg = datag + (y + i) * wplg; - linem = datam + i * wplm; - for (j = 0; j < wm; j += factor) { - if (x + j < 0 || x + j >= w) continue; - if (GET_DATA_BIT(linem, j)) { - val = GET_DATA_BYTE(lineg, x + j); - array[val] += 1.0; - } - } - } - - pixDestroy(&pixg); - return na; -} - - -/*! - * \brief pixGetGrayHistogramInRect() - * - * \param[in] pixs 8 bpp, or colormapped - * \param[in] box [optional] over which histogram is to be computed; - * use full image if NULL - * \param[in] factor subsampling factor; integer >= 1 - * \return na histogram, or NULL on error - * - *- * Notes: - * (1) If pixs is cmapped, it is converted to 8 bpp gray. - * If you want a histogram of the colormap indices, use - * pixGetCmapHistogramInRect(). - * (2) This always returns a 256-value histogram of pixel values. - * (3) Set the subsampling %factor > 1 to reduce the amount of computation. - *- */ -NUMA * -pixGetGrayHistogramInRect(PIX *pixs, - BOX *box, - l_int32 factor) -{ -l_int32 i, j, bx, by, bw, bh, w, h, wplg, val; -l_uint32 *datag, *lineg; -l_float32 *array; -NUMA *na; -PIX *pixg; - - PROCNAME("pixGetGrayHistogramInRect"); - - if (!box) - return pixGetGrayHistogram(pixs, factor); - if (!pixs) - return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs)) - return (NUMA *)ERROR_PTR("pixs neither 8 bpp nor colormapped", - procName, NULL); - if (factor < 1) - return (NUMA *)ERROR_PTR("sampling must be >= 1", procName, NULL); - - if ((na = numaCreate(256)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - numaSetCount(na, 256); /* all initialized to 0.0 */ - array = numaGetFArray(na, L_NOCOPY); - - if (pixGetColormap(pixs)) - pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - else - pixg = pixClone(pixs); - pixGetDimensions(pixg, &w, &h, NULL); - datag = pixGetData(pixg); - wplg = pixGetWpl(pixg); - boxGetGeometry(box, &bx, &by, &bw, &bh); - - /* Generate the histogram */ - for (i = 0; i < bh; i += factor) { - if (by + i < 0 || by + i >= h) continue; - lineg = datag + (by + i) * wplg; - for (j = 0; j < bw; j += factor) { - if (bx + j < 0 || bx + j >= w) continue; - val = GET_DATA_BYTE(lineg, bx + j); - array[val] += 1.0; - } - } - - pixDestroy(&pixg); - return na; -} - - -/*! - * \brief pixGetGrayHistogramTiled() - * - * \param[in] pixs any depth, colormap OK - * \param[in] factor subsampling factor; integer >= 1 - * \param[in] nx, ny tiling; >= 1; typically small - * \return naa set of histograms, or NULL on error - * - *- * Notes: - * (1) If pixs is cmapped, it is converted to 8 bpp gray. - * (2) This returns a set of 256-value histograms of pixel values. - * (3) Set the subsampling factor > 1 to reduce the amount of computation. - *- */ -NUMAA * -pixGetGrayHistogramTiled(PIX *pixs, - l_int32 factor, - l_int32 nx, - l_int32 ny) -{ -l_int32 i, n; -NUMA *na; -NUMAA *naa; -PIX *pix1, *pix2; -PIXA *pixa; - - PROCNAME("pixGetGrayHistogramTiled"); - - if (!pixs) - return (NUMAA *)ERROR_PTR("pixs not defined", procName, NULL); - if (factor < 1) - return (NUMAA *)ERROR_PTR("sampling must be >= 1", procName, NULL); - if (nx < 1 || ny < 1) - return (NUMAA *)ERROR_PTR("nx and ny must both be > 0", procName, NULL); - - n = nx * ny; - if ((naa = numaaCreate(n)) == NULL) - return (NUMAA *)ERROR_PTR("naa not made", procName, NULL); - - pix1 = pixConvertTo8(pixs, FALSE); - pixa = pixaSplitPix(pix1, nx, ny, 0, 0); - for (i = 0; i < n; i++) { - pix2 = pixaGetPix(pixa, i, L_CLONE); - na = pixGetGrayHistogram(pix2, factor); - numaaAddNuma(naa, na, L_INSERT); - pixDestroy(&pix2); - } - - pixDestroy(&pix1); - pixaDestroy(&pixa); - return naa; -} - - -/*! - * \brief pixGetColorHistogram() - * - * \param[in] pixs rgb or colormapped - * \param[in] factor subsampling factor; integer >= 1 - * \param[out] pnar red histogram - * \param[out] pnag green histogram - * \param[out] pnab blue histogram - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This generates a set of three 256 entry histograms, - * one for each color component (r,g,b). - * (2) Set the subsampling %factor > 1 to reduce the amount of computation. - *- */ -l_ok -pixGetColorHistogram(PIX *pixs, - l_int32 factor, - NUMA **pnar, - NUMA **pnag, - NUMA **pnab) -{ -l_int32 i, j, w, h, d, wpl, index, rval, gval, bval; -l_uint32 *data, *line; -l_float32 *rarray, *garray, *barray; -NUMA *nar, *nag, *nab; -PIXCMAP *cmap; - - PROCNAME("pixGetColorHistogram"); - - if (pnar) *pnar = NULL; - if (pnag) *pnag = NULL; - if (pnab) *pnab = NULL; - if (!pnar || !pnag || !pnab) - return ERROR_INT("&nar, &nag, &nab not all defined", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - cmap = pixGetColormap(pixs); - if (cmap && (d != 2 && d != 4 && d != 8)) - return ERROR_INT("colormap and not 2, 4, or 8 bpp", procName, 1); - if (!cmap && d != 32) - return ERROR_INT("no colormap and not rgb", procName, 1); - if (factor < 1) - return ERROR_INT("sampling factor must be >= 1", procName, 1); - - /* Set up the histogram arrays */ - nar = numaCreate(256); - nag = numaCreate(256); - nab = numaCreate(256); - numaSetCount(nar, 256); - numaSetCount(nag, 256); - numaSetCount(nab, 256); - rarray = numaGetFArray(nar, L_NOCOPY); - garray = numaGetFArray(nag, L_NOCOPY); - barray = numaGetFArray(nab, L_NOCOPY); - *pnar = nar; - *pnag = nag; - *pnab = nab; - - /* Generate the color histograms */ - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - if (cmap) { - for (i = 0; i < h; i += factor) { - line = data + i * wpl; - for (j = 0; j < w; j += factor) { - if (d == 8) - index = GET_DATA_BYTE(line, j); - else if (d == 4) - index = GET_DATA_QBIT(line, j); - else /* 2 bpp */ - index = GET_DATA_DIBIT(line, j); - pixcmapGetColor(cmap, index, &rval, &gval, &bval); - rarray[rval] += 1.0; - garray[gval] += 1.0; - barray[bval] += 1.0; - } - } - } else { /* 32 bpp rgb */ - for (i = 0; i < h; i += factor) { - line = data + i * wpl; - for (j = 0; j < w; j += factor) { - extractRGBValues(line[j], &rval, &gval, &bval); - rarray[rval] += 1.0; - garray[gval] += 1.0; - barray[bval] += 1.0; - } - } - } - - return 0; -} - - -/*! - * \brief pixGetColorHistogramMasked() - * - * \param[in] pixs 32 bpp rgb, or colormapped - * \param[in] pixm [optional] 1 bpp mask over which histogram is - * to be computed; use all pixels if null - * \param[in] x, y UL corner of pixm relative to the UL corner of pixs; - * can be < 0; these values are ignored if pixm is null - * \param[in] factor subsampling factor; integer >= 1 - * \param[out] pnar red histogram - * \param[out] pnag green histogram - * \param[out] pnab blue histogram - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This generates a set of three 256 entry histograms, - * (2) Set the subsampling %factor > 1 to reduce the amount of computation. - * (3) Clipping of pixm (if it exists) to pixs is done in the inner loop. - * (4) Input x,y are ignored unless pixm exists. - *- */ -l_ok -pixGetColorHistogramMasked(PIX *pixs, - PIX *pixm, - l_int32 x, - l_int32 y, - l_int32 factor, - NUMA **pnar, - NUMA **pnag, - NUMA **pnab) -{ -l_int32 i, j, w, h, d, wm, hm, dm, wpls, wplm, index, rval, gval, bval; -l_uint32 *datas, *datam, *lines, *linem; -l_float32 *rarray, *garray, *barray; -NUMA *nar, *nag, *nab; -PIXCMAP *cmap; - - PROCNAME("pixGetColorHistogramMasked"); - - if (!pixm) - return pixGetColorHistogram(pixs, factor, pnar, pnag, pnab); - - if (pnar) *pnar = NULL; - if (pnag) *pnag = NULL; - if (pnab) *pnab = NULL; - if (!pnar || !pnag || !pnab) - return ERROR_INT("&nar, &nag, &nab not all defined", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - cmap = pixGetColormap(pixs); - if (cmap && (d != 2 && d != 4 && d != 8)) - return ERROR_INT("colormap and not 2, 4, or 8 bpp", procName, 1); - if (!cmap && d != 32) - return ERROR_INT("no colormap and not rgb", procName, 1); - pixGetDimensions(pixm, &wm, &hm, &dm); - if (dm != 1) - return ERROR_INT("pixm not 1 bpp", procName, 1); - if (factor < 1) - return ERROR_INT("sampling factor must be >= 1", procName, 1); - - /* Set up the histogram arrays */ - nar = numaCreate(256); - nag = numaCreate(256); - nab = numaCreate(256); - numaSetCount(nar, 256); - numaSetCount(nag, 256); - numaSetCount(nab, 256); - rarray = numaGetFArray(nar, L_NOCOPY); - garray = numaGetFArray(nag, L_NOCOPY); - barray = numaGetFArray(nab, L_NOCOPY); - *pnar = nar; - *pnag = nag; - *pnab = nab; - - /* Generate the color histograms */ - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datam = pixGetData(pixm); - wplm = pixGetWpl(pixm); - if (cmap) { - for (i = 0; i < hm; i += factor) { - if (y + i < 0 || y + i >= h) continue; - lines = datas + (y + i) * wpls; - linem = datam + i * wplm; - for (j = 0; j < wm; j += factor) { - if (x + j < 0 || x + j >= w) continue; - if (GET_DATA_BIT(linem, j)) { - if (d == 8) - index = GET_DATA_BYTE(lines, x + j); - else if (d == 4) - index = GET_DATA_QBIT(lines, x + j); - else /* 2 bpp */ - index = GET_DATA_DIBIT(lines, x + j); - pixcmapGetColor(cmap, index, &rval, &gval, &bval); - rarray[rval] += 1.0; - garray[gval] += 1.0; - barray[bval] += 1.0; - } - } - } - } else { /* 32 bpp rgb */ - for (i = 0; i < hm; i += factor) { - if (y + i < 0 || y + i >= h) continue; - lines = datas + (y + i) * wpls; - linem = datam + i * wplm; - for (j = 0; j < wm; j += factor) { - if (x + j < 0 || x + j >= w) continue; - if (GET_DATA_BIT(linem, j)) { - extractRGBValues(lines[x + j], &rval, &gval, &bval); - rarray[rval] += 1.0; - garray[gval] += 1.0; - barray[bval] += 1.0; - } - } - } - } - - return 0; -} - - -/*! - * \brief pixGetCmapHistogram() - * - * \param[in] pixs colormapped: d = 2, 4 or 8 - * \param[in] factor subsampling factor; integer >= 1 - * \return na histogram of cmap indices, or NULL on error - * - *- * Notes: - * (1) This generates a histogram of colormap pixel indices, - * and is of size 2^d. - * (2) Set the subsampling %factor > 1 to reduce the amount of computation. - *- */ -NUMA * -pixGetCmapHistogram(PIX *pixs, - l_int32 factor) -{ -l_int32 i, j, w, h, d, wpl, val, size; -l_uint32 *data, *line; -l_float32 *array; -NUMA *na; - - PROCNAME("pixGetCmapHistogram"); - - if (!pixs) - return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetColormap(pixs) == NULL) - return (NUMA *)ERROR_PTR("pixs not cmapped", procName, NULL); - if (factor < 1) - return (NUMA *)ERROR_PTR("sampling must be >= 1", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 2 && d != 4 && d != 8) - return (NUMA *)ERROR_PTR("d not 2, 4 or 8", procName, NULL); - - size = 1 << d; - if ((na = numaCreate(size)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - numaSetCount(na, size); /* all initialized to 0.0 */ - array = numaGetFArray(na, L_NOCOPY); - - wpl = pixGetWpl(pixs); - data = pixGetData(pixs); - for (i = 0; i < h; i += factor) { - line = data + i * wpl; - for (j = 0; j < w; j += factor) { - if (d == 8) - val = GET_DATA_BYTE(line, j); - else if (d == 4) - val = GET_DATA_QBIT(line, j); - else /* d == 2 */ - val = GET_DATA_DIBIT(line, j); - array[val] += 1.0; - } - } - - return na; -} - - -/*! - * \brief pixGetCmapHistogramMasked() - * - * \param[in] pixs colormapped: d = 2, 4 or 8 - * \param[in] pixm [optional] 1 bpp mask over which histogram is - * to be computed; use all pixels if null - * \param[in] x, y UL corner of pixm relative to the UL corner of pixs; - * can be < 0; these values are ignored if pixm is null - * \param[in] factor subsampling factor; integer >= 1 - * \return na histogram, or NULL on error - * - *- * Notes: - * (1) This generates a histogram of colormap pixel indices, - * and is of size 2^d. - * (2) Set the subsampling %factor > 1 to reduce the amount of computation. - * (3) Clipping of pixm to pixs is done in the inner loop. - *- */ -NUMA * -pixGetCmapHistogramMasked(PIX *pixs, - PIX *pixm, - l_int32 x, - l_int32 y, - l_int32 factor) -{ -l_int32 i, j, w, h, d, wm, hm, dm, wpls, wplm, val, size; -l_uint32 *datas, *datam, *lines, *linem; -l_float32 *array; -NUMA *na; - - PROCNAME("pixGetCmapHistogramMasked"); - - if (!pixm) - return pixGetCmapHistogram(pixs, factor); - - if (!pixs) - return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetColormap(pixs) == NULL) - return (NUMA *)ERROR_PTR("pixs not cmapped", procName, NULL); - pixGetDimensions(pixm, &wm, &hm, &dm); - if (dm != 1) - return (NUMA *)ERROR_PTR("pixm not 1 bpp", procName, NULL); - if (factor < 1) - return (NUMA *)ERROR_PTR("sampling must be >= 1", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 2 && d != 4 && d != 8) - return (NUMA *)ERROR_PTR("d not 2, 4 or 8", procName, NULL); - - size = 1 << d; - if ((na = numaCreate(size)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - numaSetCount(na, size); /* all initialized to 0.0 */ - array = numaGetFArray(na, L_NOCOPY); - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datam = pixGetData(pixm); - wplm = pixGetWpl(pixm); - - for (i = 0; i < hm; i += factor) { - if (y + i < 0 || y + i >= h) continue; - lines = datas + (y + i) * wpls; - linem = datam + i * wplm; - for (j = 0; j < wm; j += factor) { - if (x + j < 0 || x + j >= w) continue; - if (GET_DATA_BIT(linem, j)) { - if (d == 8) - val = GET_DATA_BYTE(lines, x + j); - else if (d == 4) - val = GET_DATA_QBIT(lines, x + j); - else /* d == 2 */ - val = GET_DATA_DIBIT(lines, x + j); - array[val] += 1.0; - } - } - } - - return na; -} - - -/*! - * \brief pixGetCmapHistogramInRect() - * - * \param[in] pixs colormapped: d = 2, 4 or 8 - * \param[in] box [optional] over which histogram is to be computed; - * use full image if NULL - * \param[in] factor subsampling factor; integer >= 1 - * \return na histogram, or NULL on error - * - *- * Notes: - * (1) This generates a histogram of colormap pixel indices, - * and is of size 2^d. - * (2) Set the subsampling %factor > 1 to reduce the amount of computation. - * (3) Clipping to the box is done in the inner loop. - *- */ -NUMA * -pixGetCmapHistogramInRect(PIX *pixs, - BOX *box, - l_int32 factor) -{ -l_int32 i, j, bx, by, bw, bh, w, h, d, wpls, val, size; -l_uint32 *datas, *lines; -l_float32 *array; -NUMA *na; - - PROCNAME("pixGetCmapHistogramInRect"); - - if (!box) - return pixGetCmapHistogram(pixs, factor); - if (!pixs) - return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetColormap(pixs) == NULL) - return (NUMA *)ERROR_PTR("pixs not cmapped", procName, NULL); - if (factor < 1) - return (NUMA *)ERROR_PTR("sampling must be >= 1", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 2 && d != 4 && d != 8) - return (NUMA *)ERROR_PTR("d not 2, 4 or 8", procName, NULL); - - size = 1 << d; - if ((na = numaCreate(size)) == NULL) - return (NUMA *)ERROR_PTR("na not made", procName, NULL); - numaSetCount(na, size); /* all initialized to 0.0 */ - array = numaGetFArray(na, L_NOCOPY); - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - boxGetGeometry(box, &bx, &by, &bw, &bh); - - for (i = 0; i < bh; i += factor) { - if (by + i < 0 || by + i >= h) continue; - lines = datas + (by + i) * wpls; - for (j = 0; j < bw; j += factor) { - if (bx + j < 0 || bx + j >= w) continue; - if (d == 8) - val = GET_DATA_BYTE(lines, bx + j); - else if (d == 4) - val = GET_DATA_QBIT(lines, bx + j); - else /* d == 2 */ - val = GET_DATA_DIBIT(lines, bx + j); - array[val] += 1.0; - } - } - - return na; -} - - -/*! - * \brief pixCountRGBColors() - * - * \param[in] pixs rgb or rgba - * \param[in] factor subsampling factor; integer >= 1 - * \param[out] pncolors number of colors found - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If %factor == 1, this gives the exact number of colors. - *- */ -l_ok -pixCountRGBColors(PIX *pixs, - l_int32 factor, - l_int32 *pncolors) -{ -L_AMAP *amap; - - PROCNAME("pixCountRGBColors"); - - if (!pncolors) - return ERROR_INT("&ncolors not defined", procName, 1); - if (!pixs || pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); - if (factor <= 0) - return ERROR_INT("factor must be > 0", procName, 1); - amap = pixGetColorAmapHistogram(pixs, factor); - *pncolors = l_amapSize(amap); - l_amapDestroy(&amap); - return 0; -} - - -/*! - * \brief pixGetColorAmapHistogram() - * - * \param[in] pixs rgb or rgba - * \param[in] factor subsampling factor; integer >= 1 - * \return amap, or NULL on error - * - *- * Notes: - * (1) This generates an ordered map from pixel value to histogram count. - * (2) Use amapGetCountForColor() to use the map to look up a count. - *- */ -L_AMAP * -pixGetColorAmapHistogram(PIX *pixs, - l_int32 factor) -{ -l_int32 i, j, w, h, wpl; -l_uint32 *data, *line; -L_AMAP *amap; -RB_TYPE key, value; -RB_TYPE *pval; - - PROCNAME("pixGetColorAmapHistogram"); - - if (!pixs) - return (L_AMAP *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (L_AMAP *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (factor <= 0) - return (L_AMAP *)ERROR_PTR("factor must be > 0", procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - amap = l_amapCreate(L_UINT_TYPE); - for (i = 0; i < h; i += factor) { - line = data + i * wpl; - for (j = 0; j < w; j += factor) { - key.utype = line[j]; - pval = l_amapFind(amap, key); - if (!pval) - value.itype = 1; - else - value.itype = 1 + pval->itype; - l_amapInsert(amap, key, value); - } - } - - return amap; -} - - -/*! - * \brief amapGetCountForColor() - * - * \param[in] amap map from pixel value to count - * \param[in] val rgb or rgba pixel value - * \return count, or -1 on error - * - *- * Notes: - * (1) The ordered map is made by pixGetColorAmapHistogram(). - *- */ -l_int32 -amapGetCountForColor(L_AMAP *amap, - l_uint32 val) -{ -RB_TYPE key; -RB_TYPE *pval; - - PROCNAME("amapGetCountForColor"); - - if (!amap) - return ERROR_INT("amap not defined", procName, -1); - - key.utype = val; - pval = l_amapFind(amap, key); - return (pval) ? pval->itype : 0; -} - - -/*! - * \brief pixGetRankValue() - * - * \param[in] pixs 8 bpp, 32 bpp or colormapped - * \param[in] factor subsampling factor; integer >= 1 - * \param[in] rank between 0.0 and 1.0; 1.0 is brightest, 0.0 is darkest - * \param[out] pvalue pixel value corresponding to input rank - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Simple function to get rank values of an image. - * For a color image, the median value (rank = 0.5) can be - * used to linearly remap the colors based on the median - * of a target image, using pixLinearMapToTargetColor(). - *- */ -l_ok -pixGetRankValue(PIX *pixs, - l_int32 factor, - l_float32 rank, - l_uint32 *pvalue) -{ -l_int32 d; -l_float32 val, rval, gval, bval; -PIX *pixt; -PIXCMAP *cmap; - - PROCNAME("pixGetRankValue"); - - if (!pvalue) - return ERROR_INT("&value not defined", procName, 1); - *pvalue = 0; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - d = pixGetDepth(pixs); - cmap = pixGetColormap(pixs); - if (d != 8 && d != 32 && !cmap) - return ERROR_INT("pixs not 8 or 32 bpp, or cmapped", procName, 1); - if (cmap) - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - else - pixt = pixClone(pixs); - d = pixGetDepth(pixt); - - if (d == 8) { - pixGetRankValueMasked(pixt, NULL, 0, 0, factor, rank, &val, NULL); - *pvalue = lept_roundftoi(val); - } else { - pixGetRankValueMaskedRGB(pixt, NULL, 0, 0, factor, rank, - &rval, &gval, &bval); - composeRGBPixel(lept_roundftoi(rval), lept_roundftoi(gval), - lept_roundftoi(bval), pvalue); - } - - pixDestroy(&pixt); - return 0; -} - - -/*! - * \brief pixGetRankValueMaskedRGB() - * - * \param[in] pixs 32 bpp - * \param[in] pixm [optional] 1 bpp mask over which rank val is to be taken; - * use all pixels if null - * \param[in] x, y UL corner of pixm relative to the UL corner of pixs; - * can be < 0; these values are ignored if pixm is null - * \param[in] factor subsampling factor; integer >= 1 - * \param[in] rank between 0.0 and 1.0; 1.0 is brightest, 0.0 is darkest - * \param[out] prval [optional] red component val for input rank - * \param[out] pgval [optional] green component val for input rank - * \param[out] pbval [optional] blue component val for input rank - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Computes the rank component values of pixels in pixs that - * are under the fg of the optional mask. If the mask is null, it - * computes the average of the pixels in pixs. - * (2) Set the subsampling %factor > 1 to reduce the amount of - * computation. - * (4) Input x,y are ignored unless pixm exists. - * (5) The rank must be in [0.0 ... 1.0], where the brightest pixel - * has rank 1.0. For the median pixel value, use 0.5. - *- */ -l_ok -pixGetRankValueMaskedRGB(PIX *pixs, - PIX *pixm, - l_int32 x, - l_int32 y, - l_int32 factor, - l_float32 rank, - l_float32 *prval, - l_float32 *pgval, - l_float32 *pbval) -{ -l_float32 scale; -PIX *pixmt, *pixt; - - PROCNAME("pixGetRankValueMaskedRGB"); - - if (prval) *prval = 0.0; - if (pgval) *pgval = 0.0; - if (pbval) *pbval = 0.0; - if (!prval && !pgval && !pbval) - return ERROR_INT("no results requested", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not 32 bpp", procName, 1); - if (pixm && pixGetDepth(pixm) != 1) - return ERROR_INT("pixm not 1 bpp", procName, 1); - if (factor < 1) - return ERROR_INT("sampling factor must be >= 1", procName, 1); - if (rank < 0.0 || rank > 1.0) - return ERROR_INT("rank not in [0.0 ... 1.0]", procName, 1); - - pixmt = NULL; - if (pixm) { - scale = 1.0 / (l_float32)factor; - pixmt = pixScale(pixm, scale, scale); - } - if (prval) { - pixt = pixScaleRGBToGrayFast(pixs, factor, COLOR_RED); - pixGetRankValueMasked(pixt, pixmt, x / factor, y / factor, - factor, rank, prval, NULL); - pixDestroy(&pixt); - } - if (pgval) { - pixt = pixScaleRGBToGrayFast(pixs, factor, COLOR_GREEN); - pixGetRankValueMasked(pixt, pixmt, x / factor, y / factor, - factor, rank, pgval, NULL); - pixDestroy(&pixt); - } - if (pbval) { - pixt = pixScaleRGBToGrayFast(pixs, factor, COLOR_BLUE); - pixGetRankValueMasked(pixt, pixmt, x / factor, y / factor, - factor, rank, pbval, NULL); - pixDestroy(&pixt); - } - pixDestroy(&pixmt); - return 0; -} - - -/*! - * \brief pixGetRankValueMasked() - * - * \param[in] pixs 8 bpp, or colormapped - * \param[in] pixm [optional] 1 bpp mask, over which the rank val - * is to be taken; use all pixels if null - * \param[in] x, y UL corner of pixm relative to the UL corner of pixs; - * can be < 0; these values are ignored if pixm is null - * \param[in] factor subsampling factor; integer >= 1 - * \param[in] rank between 0.0 and 1.0; 1.0 is brightest, 0.0 is darkest - * \param[out] pval pixel value corresponding to input rank - * \param[out] pna [optional] of histogram - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Computes the rank value of pixels in pixs that are under - * the fg of the optional mask. If the mask is null, it - * computes the average of the pixels in pixs. - * (2) Set the subsampling %factor > 1 to reduce the amount of - * computation. - * (3) Clipping of pixm (if it exists) to pixs is done in the inner loop. - * (4) Input x,y are ignored unless pixm exists. - * (5) The rank must be in [0.0 ... 1.0], where the brightest pixel - * has rank 1.0. For the median pixel value, use 0.5. - * (6) The histogram can optionally be returned, so that other rank - * values can be extracted without recomputing the histogram. - * In that case, just use - * numaHistogramGetValFromRank(na, rank, &val); - * on the returned Numa for additional rank values. - *- */ -l_ok -pixGetRankValueMasked(PIX *pixs, - PIX *pixm, - l_int32 x, - l_int32 y, - l_int32 factor, - l_float32 rank, - l_float32 *pval, - NUMA **pna) -{ -NUMA *na; - - PROCNAME("pixGetRankValueMasked"); - - if (pna) *pna = NULL; - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0.0; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs)) - return ERROR_INT("pixs neither 8 bpp nor colormapped", procName, 1); - if (pixm && pixGetDepth(pixm) != 1) - return ERROR_INT("pixm not 1 bpp", procName, 1); - if (factor < 1) - return ERROR_INT("sampling factor must be >= 1", procName, 1); - if (rank < 0.0 || rank > 1.0) - return ERROR_INT("rank not in [0.0 ... 1.0]", procName, 1); - - if ((na = pixGetGrayHistogramMasked(pixs, pixm, x, y, factor)) == NULL) - return ERROR_INT("na not made", procName, 1); - numaHistogramGetValFromRank(na, rank, pval); - if (pna) - *pna = na; - else - numaDestroy(&na); - - return 0; -} - - -/*! - * \brief pixGetPixelAverage() - * - * \param[in] pixs 8 or 32 bpp, or colormapped - * \param[in] pixm [optional] 1 bpp mask over which average is - * to be taken; use all pixels if null - * \param[in] x, y UL corner of pixm relative to the UL corner of pixs; - * can be < 0 - * \param[in] factor subsampling factor; >= 1 - * \param[out] pval average pixel value - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) For rgb pix, this is a more direct computation of the - * average value of the pixels in %pixs that are under the - * mask %pixm. It is faster than pixGetPixelStats(), which - * calls pixGetAverageMaskedRGB() and has the overhead of - * generating a temporary pix of each of the three components; - * this can take most of the time if %factor > 1. - * (2) If %pixm is null, this gives the average value of all - * pixels in %pixs. The returned value is an integer. - * (3) For color %pixs, the returned pixel value is in the standard - * uint32 RGBA packing. - * (4) Clipping of pixm (if it exists) to pixs is done in the inner loop. - * (5) Input x,y are ignored if %pixm does not exist. - * (6) For general averaging of 1, 2, 4 or 8 bpp grayscale, use - * pixAverageInRect(). - *- */ -l_ok -pixGetPixelAverage(PIX *pixs, - PIX *pixm, - l_int32 x, - l_int32 y, - l_int32 factor, - l_uint32 *pval) -{ -l_int32 i, j, w, h, d, wm, hm, wpl1, wplm, val, rval, gval, bval, count; -l_uint32 *data1, *datam, *line1, *linem; -l_float64 sum, rsum, gsum, bsum; -PIX *pix1; - - PROCNAME("pixGetPixelAverage"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - d = pixGetDepth(pixs); - if (d != 32 && !pixGetColormap(pixs)) - return ERROR_INT("pixs not rgb or colormapped", procName, 1); - if (pixm && pixGetDepth(pixm) != 1) - return ERROR_INT("pixm not 1 bpp", procName, 1); - if (factor < 1) - return ERROR_INT("sampling factor must be >= 1", procName, 1); - - if (pixGetColormap(pixs)) - pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - else - pix1 = pixClone(pixs); - pixGetDimensions(pix1, &w, &h, &d); - if (d == 1) { - pixDestroy(&pix1); - return ERROR_INT("pix1 is just 1 bpp", procName, 1); - } - data1 = pixGetData(pix1); - wpl1 = pixGetWpl(pix1); - - sum = rsum = gsum = bsum = 0.0; - count = 0; - if (!pixm) { - for (i = 0; i < h; i += factor) { - line1 = data1 + i * wpl1; - for (j = 0; j < w; j += factor) { - if (d == 8) { - val = GET_DATA_BYTE(line1, j); - sum += val; - } else { /* rgb */ - extractRGBValues(*(line1 + j), &rval, &gval, &bval); - rsum += rval; - gsum += gval; - bsum += bval; - } - count++; - } - } - } else { /* masked */ - pixGetDimensions(pixm, &wm, &hm, NULL); - datam = pixGetData(pixm); - wplm = pixGetWpl(pixm); - for (i = 0; i < hm; i += factor) { - if (y + i < 0 || y + i >= h) continue; - line1 = data1 + (y + i) * wpl1; - linem = datam + i * wplm; - for (j = 0; j < wm; j += factor) { - if (x + j < 0 || x + j >= w) continue; - if (GET_DATA_BIT(linem, j)) { - if (d == 8) { - val = GET_DATA_BYTE(line1, x + j); - sum += val; - } else { /* rgb */ - extractRGBValues(*(line1 + x + j), &rval, &gval, &bval); - rsum += rval; - gsum += gval; - bsum += bval; - } - count++; - } - } - } - } - - pixDestroy(&pix1); - if (count == 0) - return ERROR_INT("no pixels sampled", procName, 1); - if (d == 8) { - *pval = (l_uint32)(sum / (l_float64)count); - } else { /* d == 32 */ - rval = (l_uint32)(rsum / (l_float64)count); - gval = (l_uint32)(gsum / (l_float64)count); - bval = (l_uint32)(bsum / (l_float64)count); - composeRGBPixel(rval, gval, bval, pval); - } - - return 0; -} - - -/*! - * \brief pixGetPixelStats() - * - * \param[in] pixs 8 bpp, 32 bpp or colormapped - * \param[in] factor subsampling factor; integer >= 1 - * \param[in] type L_MEAN_ABSVAL, L_ROOT_MEAN_SQUARE, - * L_STANDARD_DEVIATION, L_VARIANCE - * \param[out] pvalue pixel value corresponding to input type - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Simple function to get one of four statistical values of an image. - * (2) It does not take a mask: it uses the entire image. - * (3) To get the average pixel value of an RGB image, suggest using - * pixGetPixelAverage(), which is considerably faster. - *- */ -l_ok -pixGetPixelStats(PIX *pixs, - l_int32 factor, - l_int32 type, - l_uint32 *pvalue) -{ -l_int32 d; -l_float32 val, rval, gval, bval; -PIX *pixt; -PIXCMAP *cmap; - - PROCNAME("pixGetPixelStats"); - - if (!pvalue) - return ERROR_INT("&value not defined", procName, 1); - *pvalue = 0; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - d = pixGetDepth(pixs); - cmap = pixGetColormap(pixs); - if (d != 8 && d != 32 && !cmap) - return ERROR_INT("pixs not 8 or 32 bpp, or cmapped", procName, 1); - if (cmap) - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - else - pixt = pixClone(pixs); - d = pixGetDepth(pixt); - - if (d == 8) { - pixGetAverageMasked(pixt, NULL, 0, 0, factor, type, &val); - *pvalue = lept_roundftoi(val); - } else { - pixGetAverageMaskedRGB(pixt, NULL, 0, 0, factor, type, - &rval, &gval, &bval); - composeRGBPixel(lept_roundftoi(rval), lept_roundftoi(gval), - lept_roundftoi(bval), pvalue); - } - - pixDestroy(&pixt); - return 0; -} - - -/*! - * \brief pixGetAverageMaskedRGB() - * - * \param[in] pixs 32 bpp, or colormapped - * \param[in] pixm [optional] 1 bpp mask over which average is - * to be taken; use all pixels if null - * \param[in] x, y UL corner of pixm relative to the UL corner of pixs; - * can be < 0 - * \param[in] factor subsampling factor; >= 1 - * \param[in] type L_MEAN_ABSVAL, L_ROOT_MEAN_SQUARE, - * L_STANDARD_DEVIATION, L_VARIANCE - * \param[out] prval [optional] measured red value of given 'type' - * \param[out] pgval [optional] measured green value of given 'type' - * \param[out] pbval [optional] measured blue value of given 'type' - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) For usage, see pixGetAverageMasked(). - * (2) If there is a colormap, it is removed before the 8 bpp - * component images are extracted. - * (3) A better name for this would be: pixGetPixelStatsRGB() - *- */ -l_ok -pixGetAverageMaskedRGB(PIX *pixs, - PIX *pixm, - l_int32 x, - l_int32 y, - l_int32 factor, - l_int32 type, - l_float32 *prval, - l_float32 *pgval, - l_float32 *pbval) -{ -PIX *pixt; -PIXCMAP *cmap; - - PROCNAME("pixGetAverageMaskedRGB"); - - if (prval) *prval = 0.0; - if (pgval) *pgval = 0.0; - if (pbval) *pbval = 0.0; - if (!prval && !pgval && !pbval) - return ERROR_INT("no values requested", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - cmap = pixGetColormap(pixs); - if (pixGetDepth(pixs) != 32 && !cmap) - return ERROR_INT("pixs neither 32 bpp nor colormapped", procName, 1); - if (pixm && pixGetDepth(pixm) != 1) - return ERROR_INT("pixm not 1 bpp", procName, 1); - if (factor < 1) - return ERROR_INT("sampling factor must be >= 1", procName, 1); - if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE && - type != L_STANDARD_DEVIATION && type != L_VARIANCE) - return ERROR_INT("invalid measure type", procName, 1); - - if (prval) { - if (cmap) - pixt = pixGetRGBComponentCmap(pixs, COLOR_RED); - else - pixt = pixGetRGBComponent(pixs, COLOR_RED); - pixGetAverageMasked(pixt, pixm, x, y, factor, type, prval); - pixDestroy(&pixt); - } - if (pgval) { - if (cmap) - pixt = pixGetRGBComponentCmap(pixs, COLOR_GREEN); - else - pixt = pixGetRGBComponent(pixs, COLOR_GREEN); - pixGetAverageMasked(pixt, pixm, x, y, factor, type, pgval); - pixDestroy(&pixt); - } - if (pbval) { - if (cmap) - pixt = pixGetRGBComponentCmap(pixs, COLOR_BLUE); - else - pixt = pixGetRGBComponent(pixs, COLOR_BLUE); - pixGetAverageMasked(pixt, pixm, x, y, factor, type, pbval); - pixDestroy(&pixt); - } - - return 0; -} - - -/*! - * \brief pixGetAverageMasked() - * - * \param[in] pixs 8 or 16 bpp, or colormapped - * \param[in] pixm [optional] 1 bpp mask over which average is - * to be taken; use all pixels if null - * \param[in] x, y UL corner of pixm relative to the UL corner of pixs; - * can be < 0 - * \param[in] factor subsampling factor; >= 1 - * \param[in] type L_MEAN_ABSVAL, L_ROOT_MEAN_SQUARE, - * L_STANDARD_DEVIATION, L_VARIANCE - * \param[out] pval measured value of given 'type' - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Use L_MEAN_ABSVAL to get the average value of pixels in pixs - * that are under the fg of the optional mask. If the mask - * is null, it finds the average of the pixels in pixs. - * (2) Likewise, use L_ROOT_MEAN_SQUARE to get the rms value of - * pixels in pixs, either masked or not; L_STANDARD_DEVIATION - * to get the standard deviation from the mean of the pixels; - * L_VARIANCE to get the average squared difference from the - * expected value. The variance is the square of the stdev. - * For the standard deviation, we use - * sqrt([([x] - x)]^2) = sqrt([x^2] - [x]^2) - * (3) Set the subsampling %factor > 1 to reduce the amount of - * computation. - * (4) Clipping of pixm (if it exists) to pixs is done in the inner loop. - * (5) Input x,y are ignored unless pixm exists. - * (6) A better name for this would be: pixGetPixelStatsGray() - *- */ -l_ok -pixGetAverageMasked(PIX *pixs, - PIX *pixm, - l_int32 x, - l_int32 y, - l_int32 factor, - l_int32 type, - l_float32 *pval) -{ -l_int32 i, j, w, h, d, wm, hm, wplg, wplm, val, count; -l_uint32 *datag, *datam, *lineg, *linem; -l_float64 sumave, summs, ave, meansq, var; -PIX *pixg; - - PROCNAME("pixGetAverageMasked"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0.0; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - d = pixGetDepth(pixs); - if (d != 8 && d != 16 && !pixGetColormap(pixs)) - return ERROR_INT("pixs not 8 or 16 bpp or colormapped", procName, 1); - if (pixm && pixGetDepth(pixm) != 1) - return ERROR_INT("pixm not 1 bpp", procName, 1); - if (factor < 1) - return ERROR_INT("sampling factor must be >= 1", procName, 1); - if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE && - type != L_STANDARD_DEVIATION && type != L_VARIANCE) - return ERROR_INT("invalid measure type", procName, 1); - - if (pixGetColormap(pixs)) - pixg = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - else - pixg = pixClone(pixs); - pixGetDimensions(pixg, &w, &h, &d); - datag = pixGetData(pixg); - wplg = pixGetWpl(pixg); - - sumave = summs = 0.0; - count = 0; - if (!pixm) { - for (i = 0; i < h; i += factor) { - lineg = datag + i * wplg; - for (j = 0; j < w; j += factor) { - if (d == 8) - val = GET_DATA_BYTE(lineg, j); - else /* d == 16 */ - val = GET_DATA_TWO_BYTES(lineg, j); - if (type != L_ROOT_MEAN_SQUARE) - sumave += val; - if (type != L_MEAN_ABSVAL) - summs += (l_float64)(val) * val; - count++; - } - } - } else { - pixGetDimensions(pixm, &wm, &hm, NULL); - datam = pixGetData(pixm); - wplm = pixGetWpl(pixm); - for (i = 0; i < hm; i += factor) { - if (y + i < 0 || y + i >= h) continue; - lineg = datag + (y + i) * wplg; - linem = datam + i * wplm; - for (j = 0; j < wm; j += factor) { - if (x + j < 0 || x + j >= w) continue; - if (GET_DATA_BIT(linem, j)) { - if (d == 8) - val = GET_DATA_BYTE(lineg, x + j); - else /* d == 16 */ - val = GET_DATA_TWO_BYTES(lineg, x + j); - if (type != L_ROOT_MEAN_SQUARE) - sumave += val; - if (type != L_MEAN_ABSVAL) - summs += (l_float64)(val) * val; - count++; - } - } - } - } - - pixDestroy(&pixg); - if (count == 0) - return ERROR_INT("no pixels sampled", procName, 1); - ave = sumave / (l_float64)count; - meansq = summs / (l_float64)count; - var = meansq - ave * ave; - if (type == L_MEAN_ABSVAL) - *pval = (l_float32)ave; - else if (type == L_ROOT_MEAN_SQUARE) - *pval = (l_float32)sqrt(meansq); - else if (type == L_STANDARD_DEVIATION) - *pval = (l_float32)sqrt(var); - else /* type == L_VARIANCE */ - *pval = (l_float32)var; - - return 0; -} - - -/*! - * \brief pixGetAverageTiledRGB() - * - * \param[in] pixs 32 bpp, or colormapped - * \param[in] sx, sy tile size; must be at least 2 x 2 - * \param[in] type L_MEAN_ABSVAL, L_ROOT_MEAN_SQUARE, L_STANDARD_DEVIATION - * \param[out] ppixr [optional] tiled 'average' of red component - * \param[out] ppixg [optional] tiled 'average' of green component - * \param[out] ppixb [optional] tiled 'average' of blue component - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) For usage, see pixGetAverageTiled(). - * (2) If there is a colormap, it is removed before the 8 bpp - * component images are extracted. - *- */ -l_ok -pixGetAverageTiledRGB(PIX *pixs, - l_int32 sx, - l_int32 sy, - l_int32 type, - PIX **ppixr, - PIX **ppixg, - PIX **ppixb) -{ -PIX *pixt; -PIXCMAP *cmap; - - PROCNAME("pixGetAverageTiledRGB"); - - if (ppixr) *ppixr = NULL; - if (ppixg) *ppixg = NULL; - if (ppixb) *ppixb = NULL; - if (!ppixr && !ppixg && !ppixb) - return ERROR_INT("no data requested", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - cmap = pixGetColormap(pixs); - if (pixGetDepth(pixs) != 32 && !cmap) - return ERROR_INT("pixs neither 32 bpp nor colormapped", procName, 1); - if (sx < 2 || sy < 2) - return ERROR_INT("sx and sy not both > 1", procName, 1); - if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE && - type != L_STANDARD_DEVIATION) - return ERROR_INT("invalid measure type", procName, 1); - - if (ppixr) { - if (cmap) - pixt = pixGetRGBComponentCmap(pixs, COLOR_RED); - else - pixt = pixGetRGBComponent(pixs, COLOR_RED); - *ppixr = pixGetAverageTiled(pixt, sx, sy, type); - pixDestroy(&pixt); - } - if (ppixg) { - if (cmap) - pixt = pixGetRGBComponentCmap(pixs, COLOR_GREEN); - else - pixt = pixGetRGBComponent(pixs, COLOR_GREEN); - *ppixg = pixGetAverageTiled(pixt, sx, sy, type); - pixDestroy(&pixt); - } - if (ppixb) { - if (cmap) - pixt = pixGetRGBComponentCmap(pixs, COLOR_BLUE); - else - pixt = pixGetRGBComponent(pixs, COLOR_BLUE); - *ppixb = pixGetAverageTiled(pixt, sx, sy, type); - pixDestroy(&pixt); - } - - return 0; -} - - -/*! - * \brief pixGetAverageTiled() - * - * \param[in] pixs 8 bpp, or colormapped - * \param[in] sx, sy tile size; must be at least 2 x 2 - * \param[in] type L_MEAN_ABSVAL, L_ROOT_MEAN_SQUARE, L_STANDARD_DEVIATION - * \return pixd average values in each tile, or NULL on error - * - *- * Notes: - * (1) Only computes for tiles that are entirely contained in pixs. - * (2) Use L_MEAN_ABSVAL to get the average abs value within the tile; - * L_ROOT_MEAN_SQUARE to get the rms value within each tile; - * L_STANDARD_DEVIATION to get the standard dev. from the average - * within each tile. - * (3) If colormapped, converts to 8 bpp gray. - *- */ -PIX * -pixGetAverageTiled(PIX *pixs, - l_int32 sx, - l_int32 sy, - l_int32 type) -{ -l_int32 i, j, k, m, w, h, wd, hd, d, pos, wplt, wpld, valt; -l_uint32 *datat, *datad, *linet, *lined, *startt; -l_float64 sumave, summs, ave, meansq, normfact; -PIX *pixt, *pixd; - - PROCNAME("pixGetAverageTiled"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8 && !pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs not 8 bpp or cmapped", procName, NULL); - if (sx < 2 || sy < 2) - return (PIX *)ERROR_PTR("sx and sy not both > 1", procName, NULL); - wd = w / sx; - hd = h / sy; - if (wd < 1 || hd < 1) - return (PIX *)ERROR_PTR("wd or hd == 0", procName, NULL); - if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE && - type != L_STANDARD_DEVIATION) - return (PIX *)ERROR_PTR("invalid measure type", procName, NULL); - - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - pixd = pixCreate(wd, hd, 8); - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - normfact = 1. / (l_float64)(sx * sy); - for (i = 0; i < hd; i++) { - lined = datad + i * wpld; - linet = datat + i * sy * wplt; - for (j = 0; j < wd; j++) { - if (type == L_MEAN_ABSVAL || type == L_STANDARD_DEVIATION) { - sumave = 0.0; - for (k = 0; k < sy; k++) { - startt = linet + k * wplt; - for (m = 0; m < sx; m++) { - pos = j * sx + m; - valt = GET_DATA_BYTE(startt, pos); - sumave += valt; - } - } - ave = normfact * sumave; - } - if (type == L_ROOT_MEAN_SQUARE || type == L_STANDARD_DEVIATION) { - summs = 0.0; - for (k = 0; k < sy; k++) { - startt = linet + k * wplt; - for (m = 0; m < sx; m++) { - pos = j * sx + m; - valt = GET_DATA_BYTE(startt, pos); - summs += (l_float64)(valt) * valt; - } - } - meansq = normfact * summs; - } - if (type == L_MEAN_ABSVAL) - valt = (l_int32)(ave + 0.5); - else if (type == L_ROOT_MEAN_SQUARE) - valt = (l_int32)(sqrt(meansq) + 0.5); - else /* type == L_STANDARD_DEVIATION */ - valt = (l_int32)(sqrt(meansq - ave * ave) + 0.5); - SET_DATA_BYTE(lined, j, valt); - } - } - - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixRowStats() - * - * \param[in] pixs 8 bpp; not cmapped - * \param[in] box [optional] clipping box; can be null - * \param[out] pnamean [optional] numa of mean values - * \param[out] pnamedian [optional] numa of median values - * \param[out] pnamode [optional] numa of mode intensity values - * \param[out] pnamodecount [optional] numa of mode counts - * \param[out] pnavar [optional] numa of variance - * \param[out] pnarootvar [optional] numa of square root of variance - * \return na numa of requested statistic for each row, or NULL on error - * - *- * Notes: - * (1) This computes numas that represent column vectors of statistics, - * with each of its values derived from the corresponding row of a Pix. - * (2) Use NULL on input to prevent computation of any of the 5 numas. - * (3) Other functions that compute pixel row statistics are: - * pixCountPixelsByRow() - * pixAverageByRow() - * pixVarianceByRow() - * pixGetRowStats() - *- */ -l_int32 -pixRowStats(PIX *pixs, - BOX *box, - NUMA **pnamean, - NUMA **pnamedian, - NUMA **pnamode, - NUMA **pnamodecount, - NUMA **pnavar, - NUMA **pnarootvar) -{ -l_int32 i, j, k, w, h, val, wpls, sum, sumsq, target, max, modeval; -l_int32 xstart, xend, ystart, yend, bw, bh; -l_int32 *histo; -l_uint32 *lines, *datas; -l_float32 norm; -l_float32 *famean, *fameansq, *favar, *farootvar; -l_float32 *famedian, *famode, *famodecount; - - PROCNAME("pixRowStats"); - - if (pnamean) *pnamean = NULL; - if (pnamedian) *pnamedian = NULL; - if (pnamode) *pnamode = NULL; - if (pnamodecount) *pnamodecount = NULL; - if (pnavar) *pnavar = NULL; - if (pnarootvar) *pnarootvar = NULL; - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs undefined or not 8 bpp", procName, 1); - famean = fameansq = favar = farootvar = NULL; - famedian = famode = famodecount = NULL; - - pixGetDimensions(pixs, &w, &h, NULL); - if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, - &bw, &bh) == 1) - return ERROR_INT("invalid clipping box", procName, 1); - - /* We need the mean for variance and root variance */ - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if (pnamean || pnavar || pnarootvar) { - norm = 1. / (l_float32)bw; - famean = (l_float32 *)LEPT_CALLOC(bh, sizeof(l_float32)); - fameansq = (l_float32 *)LEPT_CALLOC(bh, sizeof(l_float32)); - if (pnavar || pnarootvar) { - favar = (l_float32 *)LEPT_CALLOC(bh, sizeof(l_float32)); - if (pnarootvar) - farootvar = (l_float32 *)LEPT_CALLOC(bh, sizeof(l_float32)); - } - for (i = ystart; i < yend; i++) { - sum = sumsq = 0; - lines = datas + i * wpls; - for (j = xstart; j < xend; j++) { - val = GET_DATA_BYTE(lines, j); - sum += val; - sumsq += val * val; - } - famean[i] = norm * sum; - fameansq[i] = norm * sumsq; - if (pnavar || pnarootvar) { - favar[i] = fameansq[i] - famean[i] * famean[i]; - if (pnarootvar) - farootvar[i] = sqrtf(favar[i]); - } - } - LEPT_FREE(fameansq); - if (pnamean) - *pnamean = numaCreateFromFArray(famean, bh, L_INSERT); - else - LEPT_FREE(famean); - if (pnavar) - *pnavar = numaCreateFromFArray(favar, bh, L_INSERT); - else - LEPT_FREE(favar); - if (pnarootvar) - *pnarootvar = numaCreateFromFArray(farootvar, bh, L_INSERT); - } - - /* We need a histogram to find the median and/or mode values */ - if (pnamedian || pnamode || pnamodecount) { - histo = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - if (pnamedian) { - *pnamedian = numaMakeConstant(0, bh); - famedian = numaGetFArray(*pnamedian, L_NOCOPY); - } - if (pnamode) { - *pnamode = numaMakeConstant(0, bh); - famode = numaGetFArray(*pnamode, L_NOCOPY); - } - if (pnamodecount) { - *pnamodecount = numaMakeConstant(0, bh); - famodecount = numaGetFArray(*pnamodecount, L_NOCOPY); - } - for (i = ystart; i < yend; i++) { - lines = datas + i * wpls; - memset(histo, 0, 1024); - for (j = xstart; j < xend; j++) { - val = GET_DATA_BYTE(lines, j); - histo[val]++; - } - - if (pnamedian) { - sum = 0; - target = (bw + 1) / 2; - for (k = 0; k < 256; k++) { - sum += histo[k]; - if (sum >= target) { - famedian[i] = k; - break; - } - } - } - - if (pnamode || pnamodecount) { - max = 0; - modeval = 0; - for (k = 0; k < 256; k++) { - if (histo[k] > max) { - max = histo[k]; - modeval = k; - } - } - if (pnamode) - famode[i] = modeval; - if (pnamodecount) - famodecount[i] = max; - } - } - LEPT_FREE(histo); - } - - return 0; -} - - -/*! - * \brief pixColumnStats() - * - * \param[in] pixs 8 bpp; not cmapped - * \param[in] box [optional] clipping box; can be null - * \param[out] pnamean [optional] numa of mean values - * \param[out] pnamedian [optional] numa of median values - * \param[out] pnamode [optional] numa of mode intensity values - * \param[out] pnamodecount [optional] numa of mode counts - * \param[out] pnavar [optional] numa of variance - * \param[out] pnarootvar [optional] numa of square root of variance - * \return na numa of requested statistic for each column, - * or NULL on error - * - *- * Notes: - * (1) This computes numas that represent row vectors of statistics, - * with each of its values derived from the corresponding col of a Pix. - * (2) Use NULL on input to prevent computation of any of the 5 numas. - * (3) Other functions that compute pixel column statistics are: - * pixCountPixelsByColumn() - * pixAverageByColumn() - * pixVarianceByColumn() - * pixGetColumnStats() - *- */ -l_int32 -pixColumnStats(PIX *pixs, - BOX *box, - NUMA **pnamean, - NUMA **pnamedian, - NUMA **pnamode, - NUMA **pnamodecount, - NUMA **pnavar, - NUMA **pnarootvar) -{ -l_int32 i, j, k, w, h, val, wpls, sum, sumsq, target, max, modeval; -l_int32 xstart, xend, ystart, yend, bw, bh; -l_int32 *histo; -l_uint32 *lines, *datas; -l_float32 norm; -l_float32 *famean, *fameansq, *favar, *farootvar; -l_float32 *famedian, *famode, *famodecount; - - PROCNAME("pixColumnStats"); - - if (pnamean) *pnamean = NULL; - if (pnamedian) *pnamedian = NULL; - if (pnamode) *pnamode = NULL; - if (pnamodecount) *pnamodecount = NULL; - if (pnavar) *pnavar = NULL; - if (pnarootvar) *pnarootvar = NULL; - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs undefined or not 8 bpp", procName, 1); - famean = fameansq = favar = farootvar = NULL; - famedian = famode = famodecount = NULL; - - pixGetDimensions(pixs, &w, &h, NULL); - if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, ¥d, - &bw, &bh) == 1) - return ERROR_INT("invalid clipping box", procName, 1); - - /* We need the mean for variance and root variance */ - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if (pnamean || pnavar || pnarootvar) { - norm = 1. / (l_float32)bh; - famean = (l_float32 *)LEPT_CALLOC(bw, sizeof(l_float32)); - fameansq = (l_float32 *)LEPT_CALLOC(bw, sizeof(l_float32)); - if (pnavar || pnarootvar) { - favar = (l_float32 *)LEPT_CALLOC(bw, sizeof(l_float32)); - if (pnarootvar) - farootvar = (l_float32 *)LEPT_CALLOC(bw, sizeof(l_float32)); - } - for (j = xstart; j < xend; j++) { - sum = sumsq = 0; - for (i = ystart, lines = datas; i < yend; lines += wpls, i++) { - val = GET_DATA_BYTE(lines, j); - sum += val; - sumsq += val * val; - } - famean[j] = norm * sum; - fameansq[j] = norm * sumsq; - if (pnavar || pnarootvar) { - favar[j] = fameansq[j] - famean[j] * famean[j]; - if (pnarootvar) - farootvar[j] = sqrtf(favar[j]); - } - } - LEPT_FREE(fameansq); - if (pnamean) - *pnamean = numaCreateFromFArray(famean, bw, L_INSERT); - else - LEPT_FREE(famean); - if (pnavar) - *pnavar = numaCreateFromFArray(favar, bw, L_INSERT); - else - LEPT_FREE(favar); - if (pnarootvar) - *pnarootvar = numaCreateFromFArray(farootvar, bw, L_INSERT); - } - - /* We need a histogram to find the median and/or mode values */ - if (pnamedian || pnamode || pnamodecount) { - histo = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - if (pnamedian) { - *pnamedian = numaMakeConstant(0, bw); - famedian = numaGetFArray(*pnamedian, L_NOCOPY); - } - if (pnamode) { - *pnamode = numaMakeConstant(0, bw); - famode = numaGetFArray(*pnamode, L_NOCOPY); - } - if (pnamodecount) { - *pnamodecount = numaMakeConstant(0, bw); - famodecount = numaGetFArray(*pnamodecount, L_NOCOPY); - } - for (j = xstart; j < xend; j++) { - memset(histo, 0, 1024); - for (i = ystart, lines = datas; i < yend; lines += wpls, i++) { - val = GET_DATA_BYTE(lines, j); - histo[val]++; - } - - if (pnamedian) { - sum = 0; - target = (bh + 1) / 2; - for (k = 0; k < 256; k++) { - sum += histo[k]; - if (sum >= target) { - famedian[j] = k; - break; - } - } - } - - if (pnamode || pnamodecount) { - max = 0; - modeval = 0; - for (k = 0; k < 256; k++) { - if (histo[k] > max) { - max = histo[k]; - modeval = k; - } - } - if (pnamode) - famode[j] = modeval; - if (pnamodecount) - famodecount[j] = max; - } - } - LEPT_FREE(histo); - } - - return 0; -} - - -/*! - * \brief pixGetRangeValues() - * - * \param[in] pixs 8 bpp grayscale, 32 bpp rgb, or colormapped - * \param[in] factor subsampling factor; >= 1; ignored if colormapped - * \param[in] color L_SELECT_RED, L_SELECT_GREEN or L_SELECT_BLUE - * \param[out] pminval [optional] minimum value of component - * \param[out] pmaxval [optional] maximum value of component - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If pixs is 8 bpp grayscale, the color selection type is ignored. - *- */ -l_ok -pixGetRangeValues(PIX *pixs, - l_int32 factor, - l_int32 color, - l_int32 *pminval, - l_int32 *pmaxval) -{ -l_int32 d; -PIXCMAP *cmap; - - PROCNAME("pixGetRangeValues"); - - if (pminval) *pminval = 0; - if (pmaxval) *pmaxval = 0; - if (!pminval && !pmaxval) - return ERROR_INT("no result requested", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - - cmap = pixGetColormap(pixs); - if (cmap) - return pixcmapGetRangeValues(cmap, color, pminval, pmaxval, - NULL, NULL); - - if (factor < 1) - return ERROR_INT("sampling factor must be >= 1", procName, 1); - d = pixGetDepth(pixs); - if (d != 8 && d != 32) - return ERROR_INT("pixs not 8 or 32 bpp", procName, 1); - - if (d == 8) { - pixGetExtremeValue(pixs, factor, L_SELECT_MIN, - NULL, NULL, NULL, pminval); - pixGetExtremeValue(pixs, factor, L_SELECT_MAX, - NULL, NULL, NULL, pmaxval); - } else if (color == L_SELECT_RED) { - pixGetExtremeValue(pixs, factor, L_SELECT_MIN, - pminval, NULL, NULL, NULL); - pixGetExtremeValue(pixs, factor, L_SELECT_MAX, - pmaxval, NULL, NULL, NULL); - } else if (color == L_SELECT_GREEN) { - pixGetExtremeValue(pixs, factor, L_SELECT_MIN, - NULL, pminval, NULL, NULL); - pixGetExtremeValue(pixs, factor, L_SELECT_MAX, - NULL, pmaxval, NULL, NULL); - } else if (color == L_SELECT_BLUE) { - pixGetExtremeValue(pixs, factor, L_SELECT_MIN, - NULL, NULL, pminval, NULL); - pixGetExtremeValue(pixs, factor, L_SELECT_MAX, - NULL, NULL, pmaxval, NULL); - } else { - return ERROR_INT("invalid color", procName, 1); - } - - return 0; -} - - -/*! - * \brief pixGetExtremeValue() - * - * \param[in] pixs 8 bpp grayscale, 32 bpp rgb, or colormapped - * \param[in] factor subsampling factor; >= 1; ignored if colormapped - * \param[in] type L_SELECT_MIN or L_SELECT_MAX - * \param[out] prval [optional] red component - * \param[out] pgval [optional] green component - * \param[out] pbval [optional] blue component - * \param[out] pgrayval [optional] min or max gray value - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If pixs is grayscale, the result is returned in &grayval. - * Otherwise, if there is a colormap or d == 32, - * each requested color component is returned. At least - * one color component (address) must be input. - *- */ -l_ok -pixGetExtremeValue(PIX *pixs, - l_int32 factor, - l_int32 type, - l_int32 *prval, - l_int32 *pgval, - l_int32 *pbval, - l_int32 *pgrayval) -{ -l_int32 i, j, w, h, d, wpl; -l_int32 val, extval, rval, gval, bval, extrval, extgval, extbval; -l_uint32 pixel; -l_uint32 *data, *line; -PIXCMAP *cmap; - - PROCNAME("pixGetExtremeValue"); - - if (prval) *prval = -1; - if (pgval) *pgval = -1; - if (pbval) *pbval = -1; - if (pgrayval) *pgrayval = -1; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (type != L_SELECT_MIN && type != L_SELECT_MAX) - return ERROR_INT("invalid type", procName, 1); - - cmap = pixGetColormap(pixs); - if (cmap) { - if (type == L_SELECT_MIN) { - if (prval) pixcmapGetRangeValues(cmap, L_SELECT_RED, prval, NULL, - NULL, NULL); - if (pgval) pixcmapGetRangeValues(cmap, L_SELECT_GREEN, pgval, NULL, - NULL, NULL); - if (pbval) pixcmapGetRangeValues(cmap, L_SELECT_BLUE, pbval, NULL, - NULL, NULL); - } else { /* type == L_SELECT_MAX */ - if (prval) pixcmapGetRangeValues(cmap, L_SELECT_RED, NULL, prval, - NULL, NULL); - if (pgval) pixcmapGetRangeValues(cmap, L_SELECT_GREEN, NULL, pgval, - NULL, NULL); - if (pbval) pixcmapGetRangeValues(cmap, L_SELECT_BLUE, NULL, pbval, - NULL, NULL); - } - return 0; - } - - pixGetDimensions(pixs, &w, &h, &d); - if (factor < 1) - return ERROR_INT("sampling factor must be >= 1", procName, 1); - if (d != 8 && d != 32) - return ERROR_INT("pixs not 8 or 32 bpp", procName, 1); - if (d == 8 && !pgrayval) - return ERROR_INT("can't return result in grayval", procName, 1); - if (d == 32 && !prval && !pgval && !pbval) - return ERROR_INT("can't return result in r/g/b-val", procName, 1); - - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - if (d == 8) { - if (type == L_SELECT_MIN) - extval = 100000; - else /* get max */ - extval = -1; - - for (i = 0; i < h; i += factor) { - line = data + i * wpl; - for (j = 0; j < w; j += factor) { - val = GET_DATA_BYTE(line, j); - if ((type == L_SELECT_MIN && val < extval) || - (type == L_SELECT_MAX && val > extval)) - extval = val; - } - } - *pgrayval = extval; - return 0; - } - - /* 32 bpp rgb */ - if (type == L_SELECT_MIN) { - extrval = 100000; - extgval = 100000; - extbval = 100000; - } else { - extrval = -1; - extgval = -1; - extbval = -1; - } - for (i = 0; i < h; i += factor) { - line = data + i * wpl; - for (j = 0; j < w; j += factor) { - pixel = line[j]; - if (prval) { - rval = (pixel >> L_RED_SHIFT) & 0xff; - if ((type == L_SELECT_MIN && rval < extrval) || - (type == L_SELECT_MAX && rval > extrval)) - extrval = rval; - } - if (pgval) { - gval = (pixel >> L_GREEN_SHIFT) & 0xff; - if ((type == L_SELECT_MIN && gval < extgval) || - (type == L_SELECT_MAX && gval > extgval)) - extgval = gval; - } - if (pbval) { - bval = (pixel >> L_BLUE_SHIFT) & 0xff; - if ((type == L_SELECT_MIN && bval < extbval) || - (type == L_SELECT_MAX && bval > extbval)) - extbval = bval; - } - } - } - if (prval) *prval = extrval; - if (pgval) *pgval = extgval; - if (pbval) *pbval = extbval; - return 0; -} - - -/*! - * \brief pixGetMaxValueInRect() - * - * \param[in] pixs 8, 16 or 32 bpp grayscale; no color space components - * \param[in] box [optional] region; set box = NULL to use entire pixs - * \param[out] pmaxval [optional] max value in region - * \param[out] pxmax [optional] x location of max value - * \param[out] pymax [optional] y location of max value - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This can be used to find the maximum and its location - * in a 2-dimensional histogram, where the x and y directions - * represent two color components (e.g., saturation and hue). - * (2) Note that here a 32 bpp pixs has pixel values that are simply - * numbers. They are not 8 bpp components in a colorspace. - *- */ -l_ok -pixGetMaxValueInRect(PIX *pixs, - BOX *box, - l_uint32 *pmaxval, - l_int32 *pxmax, - l_int32 *pymax) -{ -l_int32 i, j, w, h, d, wpl, bw, bh; -l_int32 xstart, ystart, xend, yend, xmax, ymax; -l_uint32 val, maxval; -l_uint32 *data, *line; - - PROCNAME("pixGetMaxValueInRect"); - - if (pmaxval) *pmaxval = 0; - if (pxmax) *pxmax = 0; - if (pymax) *pymax = 0; - if (!pmaxval && !pxmax && !pymax) - return ERROR_INT("no data requested", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetColormap(pixs) != NULL) - return ERROR_INT("pixs has colormap", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8 && d != 16 && d != 32) - return ERROR_INT("pixs not 8, 16 or 32 bpp", procName, 1); - - xstart = ystart = 0; - xend = w - 1; - yend = h - 1; - if (box) { - boxGetGeometry(box, &xstart, &ystart, &bw, &bh); - xend = xstart + bw - 1; - yend = ystart + bh - 1; - } - - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - maxval = 0; - xmax = ymax = 0; - for (i = ystart; i <= yend; i++) { - line = data + i * wpl; - for (j = xstart; j <= xend; j++) { - if (d == 8) - val = GET_DATA_BYTE(line, j); - else if (d == 16) - val = GET_DATA_TWO_BYTES(line, j); - else /* d == 32 */ - val = line[j]; - if (val > maxval) { - maxval = val; - xmax = j; - ymax = i; - } - } - } - if (maxval == 0) { /* no counts; pick the center of the rectangle */ - xmax = (xstart + xend) / 2; - ymax = (ystart + yend) / 2; - } - - if (pmaxval) *pmaxval = maxval; - if (pxmax) *pxmax = xmax; - if (pymax) *pymax = ymax; - return 0; -} - - -/*! - * \brief pixGetBinnedComponentRange() - * - * \param[in] pixs 32 bpp rgb - * \param[in] nbins number of equal population bins; must be > 1 - * \param[in] factor subsampling factor; >= 1 - * \param[in] color L_SELECT_RED, L_SELECT_GREEN or L_SELECT_BLUE - * \param[out] pminval [optional] minimum value of component - * \param[out] pmaxval [optional] maximum value of component - * \param[out] pcarray [optional] color array of bins - * \param[in] fontsize [optional] 0 for no debug; for debug, valid set - * is {4,6,8,10,12,14,16,18,20}. - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This returns the min and max average values of the - * selected color component in the set of rank bins, - * where the ranking is done using the specified component. - *- */ -l_ok -pixGetBinnedComponentRange(PIX *pixs, - l_int32 nbins, - l_int32 factor, - l_int32 color, - l_int32 *pminval, - l_int32 *pmaxval, - l_uint32 **pcarray, - l_int32 fontsize) -{ -l_int32 i, minval, maxval, rval, gval, bval; -l_uint32 *carray; -PIX *pixt; - - PROCNAME("pixGetBinnedComponentRange"); - - if (pminval) *pminval = 0; - if (pmaxval) *pmaxval = 0; - if (pcarray) *pcarray = NULL; - if (!pminval && !pmaxval) - return ERROR_INT("no result requested", procName, 1); - if (!pixs || pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); - if (factor < 1) - return ERROR_INT("sampling factor must be >= 1", procName, 1); - if (color != L_SELECT_RED && color != L_SELECT_GREEN && - color != L_SELECT_BLUE) - return ERROR_INT("invalid color", procName, 1); - if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2) - return ERROR_INT("invalid fontsize", procName, 1); - - pixGetRankColorArray(pixs, nbins, color, factor, &carray, NULL, 0); - if (fontsize > 0) { - for (i = 0; i < nbins; i++) - L_INFO("c[%d] = %x\n", procName, i, carray[i]); - pixt = pixDisplayColorArray(carray, nbins, 200, 5, fontsize); - pixDisplay(pixt, 100, 100); - pixDestroy(&pixt); - } - - extractRGBValues(carray[0], &rval, &gval, &bval); - minval = rval; - if (color == L_SELECT_GREEN) - minval = gval; - else if (color == L_SELECT_BLUE) - minval = bval; - extractRGBValues(carray[nbins - 1], &rval, &gval, &bval); - maxval = rval; - if (color == L_SELECT_GREEN) - maxval = gval; - else if (color == L_SELECT_BLUE) - maxval = bval; - - if (pminval) *pminval = minval; - if (pmaxval) *pmaxval = maxval; - if (pcarray) - *pcarray = carray; - else - LEPT_FREE(carray); - return 0; -} - - -/*! - * \brief pixGetRankColorArray() - * - * \param[in] pixs 32 bpp or cmapped - * \param[in] nbins number of equal population bins; must be > 1 - * \param[in] type color selection flag - * \param[in] factor subsampling factor; integer >= 1 - * \param[out] pcarray array of colors, ranked by intensity - * \param[in] pixadb [optional] debug: caller passes this in. - * Use to display color squares and to - * capture plots of color components - * \param[in] fontsize [optional] debug: only used if pixadb exists. - * Valid set is {4,6,8,10,12,14,16,18,20}. - * fontsize == 6 is typical. - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The color selection flag is one of: L_SELECT_RED, L_SELECT_GREEN, - * L_SELECT_BLUE, L_SELECT_MIN, L_SELECT_MAX, L_SELECT_AVERAGE, - * L_SELECT_HUE, L_SELECT_SATURATION. - * (2) Then it finds the histogram of the selected color type in each - * RGB pixel. For each of the %nbins sets of pixels, - * ordered by this color type value, find the average RGB color, - * and return this as a "rank color" array. The output array - * has %nbins colors. - * (3) Set the subsampling factor > 1 to reduce the amount of - * computation. Typically you want at least 10,000 pixels - * for reasonable statistics. - * (4) The rank color as a function of rank can then be found from - * rankint = (l_int32)(rank * (nbins - 1) + 0.5); - * extractRGBValues(array[rankint], &rval, &gval, &bval); - * where the rank is in [0.0 ... 1.0]. - * This function is meant to be simple and approximate. - * (5) Compare this with pixGetBinnedColor(), which generates equal - * width intensity bins and finds the average color in each bin. - *- */ -l_ok -pixGetRankColorArray(PIX *pixs, - l_int32 nbins, - l_int32 type, - l_int32 factor, - l_uint32 **pcarray, - PIXA *pixadb, - l_int32 fontsize) -{ -l_int32 ret; -l_uint32 *array; -NUMA *na, *nan, *narbin; -PIX *pix1, *pixc, *pixg, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixGetRankColorArray"); - - if (!pcarray) - return ERROR_INT("&carray not defined", procName, 1); - *pcarray = NULL; - if (factor < 1) - return ERROR_INT("sampling factor must be >= 1", procName, 1); - if (nbins < 2) - return ERROR_INT("nbins must be at least 2", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - cmap = pixGetColormap(pixs); - if (pixGetDepth(pixs) != 32 && !cmap) - return ERROR_INT("pixs neither 32 bpp nor cmapped", procName, 1); - if (type != L_SELECT_RED && type != L_SELECT_GREEN && - type != L_SELECT_BLUE && type != L_SELECT_MIN && - type != L_SELECT_MAX && type != L_SELECT_AVERAGE && - type != L_SELECT_HUE && type != L_SELECT_SATURATION) - return ERROR_INT("invalid type", procName, 1); - if (pixadb) { - if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2) { - L_WARNING("invalid fontsize %d; setting to 6\n", procName, - fontsize); - fontsize = 6; - } - } - - /* Downscale by factor and remove colormap if it exists */ - pix1 = pixScaleByIntSampling(pixs, factor); - if (cmap) - pixc = pixRemoveColormap(pix1, REMOVE_CMAP_TO_FULL_COLOR); - else - pixc = pixClone(pix1); - pixDestroy(&pix1); - - /* Get normalized histogram of the selected component */ - if (type == L_SELECT_RED) - pixg = pixGetRGBComponent(pixc, COLOR_RED); - else if (type == L_SELECT_GREEN) - pixg = pixGetRGBComponent(pixc, COLOR_GREEN); - else if (type == L_SELECT_BLUE) - pixg = pixGetRGBComponent(pixc, COLOR_BLUE); - else if (type == L_SELECT_MIN) - pixg = pixConvertRGBToGrayMinMax(pixc, L_CHOOSE_MIN); - else if (type == L_SELECT_MAX) - pixg = pixConvertRGBToGrayMinMax(pixc, L_CHOOSE_MAX); - else if (type == L_SELECT_AVERAGE) - pixg = pixConvertRGBToGray(pixc, 0.34, 0.33, 0.33); - else if (type == L_SELECT_HUE) - pixg = pixConvertRGBToHue(pixc); - else /* L_SELECT_SATURATION */ - pixg = pixConvertRGBToSaturation(pixc); - if ((na = pixGetGrayHistogram(pixg, 1)) == NULL) { - pixDestroy(&pixc); - pixDestroy(&pixg); - return ERROR_INT("na not made", procName, 1); - } - nan = numaNormalizeHistogram(na, 1.0); - - /* Get the following arrays: - * (1) nar: cumulative normalized histogram (rank vs intensity value). - * With 256 intensity values, we have 257 rank values. - * (2) nai: "average" intensity as function of rank bin, for - * %nbins equally spaced in rank between 0.0 and 1.0. - * (3) narbin: bin number of discretized rank as a function of - * intensity. This is the 'inverse' of nai. - * (4) nabb: intensity value of the right bin boundary, for each - * of the %nbins discretized rank bins. */ - if (!pixadb) { - numaDiscretizeRankAndIntensity(nan, nbins, &narbin, NULL, NULL, NULL); - } else { - NUMA *nai, *nar, *nabb; - numaDiscretizeRankAndIntensity(nan, nbins, &narbin, &nai, &nar, &nabb); - lept_mkdir("lept/regout"); - pix1 = gplotSimplePix1(nan, "Normalized Histogram"); - pixaAddPix(pixadb, pix1, L_INSERT); - pix1 = gplotSimplePix1(nar, "Cumulative Histogram"); - pixaAddPix(pixadb, pix1, L_INSERT); - pix1 = gplotSimplePix1(nai, "Intensity vs. rank bin"); - pixaAddPix(pixadb, pix1, L_INSERT); - pix1 = gplotSimplePix1(narbin, "LUT: rank bin vs. Intensity"); - pixaAddPix(pixadb, pix1, L_INSERT); - pix1 = gplotSimplePix1(nabb, "Intensity of right edge vs. rank bin"); - pixaAddPix(pixadb, pix1, L_INSERT); - numaDestroy(&nai); - numaDestroy(&nar); - numaDestroy(&nabb); - } - - /* Get the average color in each bin for pixels whose grayscale - * values fall in the bin range. %narbin is the LUT that - * determines the bin number from the grayscale version of - * the image. Because this mapping may not be unique, - * some bins may not be represented in the LUT. In use, to get fair - * allocation into all the bins, bin population is monitored - * as pixels are accumulated, and when bins fill up, - * pixels are required to overflow into succeeding bins. */ - pixGetBinnedColor(pixc, pixg, 1, nbins, narbin, pcarray, pixadb); - ret = 0; - if ((array = *pcarray) == NULL) { - L_ERROR("color array not returned\n", procName); - ret = 1; - } - if (array && pixadb) { - pixd = pixDisplayColorArray(array, nbins, 200, 5, fontsize); - pixWriteDebug("/tmp/lept/regout/rankhisto.png", pixd, IFF_PNG); - pixDestroy(&pixd); - } - - pixDestroy(&pixc); - pixDestroy(&pixg); - numaDestroy(&na); - numaDestroy(&nan); - numaDestroy(&narbin); - return ret; -} - - -/*! - * \brief pixGetBinnedColor() - * - * \param[in] pixs 32 bpp - * \param[in] pixg 8 bpp grayscale version of pixs - * \param[in] factor sampling factor along pixel counting direction - * \param[in] nbins number of intensity bins - * \param[in] nalut LUT for mapping from intensity to bin number - * \param[out] pcarray array of average color values in each bin - * \param[in] pixadb [optional] debug: caller passes this in. - * Use to display color squares and to - * capture plots of color components - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) This takes a color image, a grayscale (intensity) version, - * a LUT from intensity to bin number, and the number of bins. - * It computes the average color for pixels whose intensity - * is in each bin. This is returned as an array of l_uint32 - * colors in our standard RGBA ordering. - * (2) This function generates equal width intensity bins and - * finds the average color in each bin. Compare this with - * pixGetRankColorArray(), which rank orders the pixels - * by the value of the selected component in each pixel, - * sets up bins with equal population (not intensity width!), - * and gets the average color in each bin. - *- */ -l_ok -pixGetBinnedColor(PIX *pixs, - PIX *pixg, - l_int32 factor, - l_int32 nbins, - NUMA *nalut, - l_uint32 **pcarray, - PIXA *pixadb) -{ -l_int32 i, j, w, h, wpls, wplg, grayval, bin, rval, gval, bval, success; -l_int32 npts, avepts, maxpts; -l_uint32 *datas, *datag, *lines, *lineg, *carray; -l_float64 norm; -l_float64 *rarray, *garray, *barray, *narray; -PIX *pix1; - - PROCNAME("pixGetBinnedColor"); - - if (!pcarray) - return ERROR_INT("&carray not defined", procName, 1); - *pcarray = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!pixg) - return ERROR_INT("pixg not defined", procName, 1); - if (!nalut) - return ERROR_INT("nalut not defined", procName, 1); - if (factor < 1) { - L_WARNING("sampling factor less than 1; setting to 1\n", procName); - factor = 1; - } - - /* Find the color for each rank bin. Note that we can have - * multiple bins filled with pixels having the same gray value. - * Therefore, because in general the mapping from gray value - * to bin number is not unique, if a bin fills up (actually, - * we allow it to slightly overfill), we roll the excess - * over to the next bin, etc. */ - pixGetDimensions(pixs, &w, &h, NULL); - npts = (w + factor - 1) * (h + factor - 1) / (factor * factor); - avepts = (npts + nbins - 1) / nbins; /* average number of pts in a bin */ - maxpts = (l_int32)((1.0 + 0.5 / (l_float32)nbins) * avepts); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datag = pixGetData(pixg); - wplg = pixGetWpl(pixg); - rarray = (l_float64 *)LEPT_CALLOC(nbins, sizeof(l_float64)); - garray = (l_float64 *)LEPT_CALLOC(nbins, sizeof(l_float64)); - barray = (l_float64 *)LEPT_CALLOC(nbins, sizeof(l_float64)); - narray = (l_float64 *)LEPT_CALLOC(nbins, sizeof(l_float64)); - for (i = 0; i < h; i += factor) { - lines = datas + i * wpls; - lineg = datag + i * wplg; - for (j = 0; j < w; j += factor) { - grayval = GET_DATA_BYTE(lineg, j); - numaGetIValue(nalut, grayval, &bin); - extractRGBValues(lines[j], &rval, &gval, &bval); - while (narray[bin] >= maxpts && bin < nbins - 1) - bin++; - rarray[bin] += rval; - garray[bin] += gval; - barray[bin] += bval; - narray[bin] += 1.0; /* count samples in each bin */ - } - } - - for (i = 0; i < nbins; i++) { - norm = 1. / narray[i]; - rarray[i] *= norm; - garray[i] *= norm; - barray[i] *= norm; -/* lept_stderr("narray[%d] = %f\n", i, narray[i]); */ - } - - if (pixadb) { - NUMA *nared, *nagreen, *nablue; - nared = numaCreate(nbins); - nagreen = numaCreate(nbins); - nablue = numaCreate(nbins); - for (i = 0; i < nbins; i++) { - numaAddNumber(nared, rarray[i]); - numaAddNumber(nagreen, garray[i]); - numaAddNumber(nablue, barray[i]); - } - lept_mkdir("lept/regout"); - pix1 = gplotSimplePix1(nared, "Average red val vs. rank bin"); - pixaAddPix(pixadb, pix1, L_INSERT); - pix1 = gplotSimplePix1(nagreen, "Average green val vs. rank bin"); - pixaAddPix(pixadb, pix1, L_INSERT); - pix1 = gplotSimplePix1(nablue, "Average blue val vs. rank bin"); - pixaAddPix(pixadb, pix1, L_INSERT); - numaDestroy(&nared); - numaDestroy(&nagreen); - numaDestroy(&nablue); - } - - /* Save colors for all bins in a single array */ - success = TRUE; - if ((carray = (l_uint32 *)LEPT_CALLOC(nbins, sizeof(l_uint32))) == NULL) { - success = FALSE; - L_ERROR("carray not made\n", procName); - goto cleanup_arrays; - } - *pcarray = carray; - for (i = 0; i < nbins; i++) { - rval = (l_int32)(rarray[i] + 0.5); - gval = (l_int32)(garray[i] + 0.5); - bval = (l_int32)(barray[i] + 0.5); - composeRGBPixel(rval, gval, bval, carray + i); - } - -cleanup_arrays: - LEPT_FREE(rarray); - LEPT_FREE(garray); - LEPT_FREE(barray); - LEPT_FREE(narray); - return (success) ? 0 : 1; -} - - -/*! - * \brief pixDisplayColorArray() - * - * \param[in] carray array of colors: 0xrrggbb00 - * \param[in] ncolors size of array - * \param[in] side size of each color square; suggest 200 - * \param[in] ncols number of columns in output color matrix - * \param[in] fontsize to label each square with text. Valid set is - * {4,6,8,10,12,14,16,18,20}. Use 0 to disable. - * \return pixd color array, or NULL on error - */ -PIX * -pixDisplayColorArray(l_uint32 *carray, - l_int32 ncolors, - l_int32 side, - l_int32 ncols, - l_int32 fontsize) -{ -char textstr[256]; -l_int32 i, rval, gval, bval; -L_BMF *bmf; -PIX *pix1, *pix2, *pix3, *pix4; -PIXA *pixa; - - PROCNAME("pixDisplayColorArray"); - - if (!carray) - return (PIX *)ERROR_PTR("carray not defined", procName, NULL); - if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2) - return (PIX *)ERROR_PTR("invalid fontsize", procName, NULL); - - bmf = (fontsize == 0) ? NULL : bmfCreate(NULL, fontsize); - pixa = pixaCreate(ncolors); - for (i = 0; i < ncolors; i++) { - pix1 = pixCreate(side, side, 32); - pixSetAllArbitrary(pix1, carray[i]); - pix2 = pixAddBorder(pix1, 2, 1); - if (bmf) { - extractRGBValues(carray[i], &rval, &gval, &bval); - snprintf(textstr, sizeof(textstr), - "%d: (%d %d %d)", i, rval, gval, bval); - pix3 = pixAddSingleTextblock(pix2, bmf, textstr, 0xff000000, - L_ADD_BELOW, NULL); - } else { - pix3 = pixClone(pix2); - } - pixaAddPix(pixa, pix3, L_INSERT); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - pix4 = pixaDisplayTiledInColumns(pixa, ncols, 1.0, 20, 2); - pixaDestroy(&pixa); - bmfDestroy(&bmf); - return pix4; -} - - -/*! - * \brief pixRankBinByStrip() - * - * \param[in] pixs 32 bpp or cmapped - * \param[in] direction L_SCAN_HORIZONTAL or L_SCAN_VERTICAL - * \param[in] size of strips in scan direction - * \param[in] nbins number of equal population bins; must be > 1 - * \param[in] type color selection flag - * \return pixd result, or NULL on error - * - *- * Notes: - * (1) This generates a pix where each column represents a strip of - * the input image. If %direction == L_SCAN_HORIZONTAL, the - * input impage is tiled into vertical strips of width %size, - * where %size is a compromise between getting better spatial - * columnwise resolution (small %size) and getting better - * columnwise statistical information (larger %size). Likewise - * with rows of the image if %direction == L_SCAN_VERTICAL. - * (2) For L_HORIZONTAL_SCAN, the output pix contains rank binned - * median colors in each column that correspond to a vertical - * strip of width %size in the input image. - * (3) The color selection flag is one of: L_SELECT_RED, L_SELECT_GREEN, - * L_SELECT_BLUE, L_SELECT_MIN, L_SELECT_MAX, L_SELECT_AVERAGE. - * It determines how the rank ordering is done. - * (4) Typical input values might be %size = 5, %nbins = 10. - *- */ -PIX * -pixRankBinByStrip(PIX *pixs, - l_int32 direction, - l_int32 size, - l_int32 nbins, - l_int32 type) -{ -l_int32 i, j, w, h, nstrips; -l_uint32 *array; -BOXA *boxa; -PIX *pix1, *pix2, *pixd; -PIXA *pixa; -PIXCMAP *cmap; - - PROCNAME("pixRankBinByStrip"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - cmap = pixGetColormap(pixs); - if (pixGetDepth(pixs) != 32 && !cmap) - return (PIX *)ERROR_PTR("pixs neither 32 bpp nor cmapped", - procName, NULL); - if (direction != L_SCAN_HORIZONTAL && direction != L_SCAN_VERTICAL) - return (PIX *)ERROR_PTR("invalid direction", procName, NULL); - if (size < 1) - return (PIX *)ERROR_PTR("size < 1", procName, NULL); - if (nbins < 2) - return (PIX *)ERROR_PTR("nbins must be at least 2", procName, NULL); - if (type != L_SELECT_RED && type != L_SELECT_GREEN && - type != L_SELECT_BLUE && type != L_SELECT_MIN && - type != L_SELECT_MAX && type != L_SELECT_AVERAGE) - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - - /* Downscale by factor and remove colormap if it exists */ - if (cmap) - pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); - else - pix1 = pixClone(pixs); - pixGetDimensions(pixs, &w, &h, NULL); - - pixd = NULL; - boxa = makeMosaicStrips(w, h, direction, size); - pixa = pixClipRectangles(pix1, boxa); - nstrips = pixaGetCount(pixa); - if (direction == L_SCAN_HORIZONTAL) { - pixd = pixCreate(nstrips, nbins, 32); - for (i = 0; i < nstrips; i++) { - pix2 = pixaGetPix(pixa, i, L_CLONE); - pixGetRankColorArray(pix2, nbins, type, 1, &array, NULL, 0); - for (j = 0; j < nbins; j++) - pixSetPixel(pixd, i, j, array[j]); - LEPT_FREE(array); - pixDestroy(&pix2); - } - } else { /* L_SCAN_VERTICAL */ - pixd = pixCreate(nbins, nstrips, 32); - for (i = 0; i < nstrips; i++) { - pix2 = pixaGetPix(pixa, i, L_CLONE); - pixGetRankColorArray(pix2, nbins, type, 1, &array, NULL, 0); - for (j = 0; j < nbins; j++) - pixSetPixel(pixd, j, i, array[j]); - LEPT_FREE(array); - pixDestroy(&pix2); - } - } - pixDestroy(&pix1); - boxaDestroy(&boxa); - pixaDestroy(&pixa); - return pixd; -} - - - -/*-------------------------------------------------------------* - * Pixelwise aligned statistics * - *-------------------------------------------------------------*/ -/*! - * \brief pixaGetAlignedStats() - * - * \param[in] pixa of identically sized, 8 bpp pix; not cmapped - * \param[in] type L_MEAN_ABSVAL, L_MEDIAN_VAL, L_MODE_VAL, L_MODE_COUNT - * \param[in] nbins of histogram for median and mode; ignored for mean - * \param[in] thresh on histogram for mode val; ignored for all other types - * \return pix with pixelwise aligned stats, or NULL on error. - * - *- * Notes: - * (1) Each pixel in the returned pix represents an average - * (or median, or mode) over the corresponding pixels in each - * pix in the pixa. - * (2) The %thresh parameter works with L_MODE_VAL only, and - * sets a minimum occupancy of the mode bin. - * If the occupancy of the mode bin is less than %thresh, the - * mode value is returned as 0. To always return the actual - * mode value, set %thresh = 0. See pixGetRowStats(). - *- */ -PIX * -pixaGetAlignedStats(PIXA *pixa, - l_int32 type, - l_int32 nbins, - l_int32 thresh) -{ -l_int32 j, n, w, h, d; -l_float32 *colvect; -PIX *pixt, *pixd; - - PROCNAME("pixaGetAlignedStats"); - - if (!pixa) - return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); - if (type != L_MEAN_ABSVAL && type != L_MEDIAN_VAL && - type != L_MODE_VAL && type != L_MODE_COUNT) - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - n = pixaGetCount(pixa); - if (n == 0) - return (PIX *)ERROR_PTR("no pix in pixa", procName, NULL); - pixaGetPixDimensions(pixa, 0, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("pix not 8 bpp", procName, NULL); - - pixd = pixCreate(w, h, 8); - pixt = pixCreate(n, h, 8); - colvect = (l_float32 *)LEPT_CALLOC(h, sizeof(l_float32)); - for (j = 0; j < w; j++) { - pixaExtractColumnFromEachPix(pixa, j, pixt); - pixGetRowStats(pixt, type, nbins, thresh, colvect); - pixSetPixelColumn(pixd, j, colvect); - } - - LEPT_FREE(colvect); - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixaExtractColumnFromEachPix() - * - * \param[in] pixa of identically sized, 8 bpp; not cmapped - * \param[in] col column index - * \param[in] pixd pix into which each column is inserted - * \return 0 if OK, 1 on error - */ -l_ok -pixaExtractColumnFromEachPix(PIXA *pixa, - l_int32 col, - PIX *pixd) -{ -l_int32 i, k, n, w, h, ht, val, wplt, wpld; -l_uint32 *datad, *datat; -PIX *pixt; - - PROCNAME("pixaExtractColumnFromEachPix"); - - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - if (!pixd || pixGetDepth(pixd) != 8) - return ERROR_INT("pixd not defined or not 8 bpp", procName, 1); - n = pixaGetCount(pixa); - pixGetDimensions(pixd, &w, &h, NULL); - if (n != w) - return ERROR_INT("pix width != n", procName, 1); - pixt = pixaGetPix(pixa, 0, L_CLONE); - wplt = pixGetWpl(pixt); - pixGetDimensions(pixt, NULL, &ht, NULL); - pixDestroy(&pixt); - if (h != ht) - return ERROR_INT("pixd height != column height", procName, 1); - - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (k = 0; k < n; k++) { - pixt = pixaGetPix(pixa, k, L_CLONE); - datat = pixGetData(pixt); - for (i = 0; i < h; i++) { - val = GET_DATA_BYTE(datat, col); - SET_DATA_BYTE(datad + i * wpld, k, val); - datat += wplt; - } - pixDestroy(&pixt); - } - - return 0; -} - - -/*! - * \brief pixGetRowStats() - * - * \param[in] pixs 8 bpp; not cmapped - * \param[in] type L_MEAN_ABSVAL, L_MEDIAN_VAL, L_MODE_VAL, L_MODE_COUNT - * \param[in] nbins of histogram for median and mode; ignored for mean - * \param[in] thresh on histogram for mode; ignored for mean and median - * \param[in] colvect vector of results gathered across the rows of pixs - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This computes a column vector of statistics using each - * row of a Pix. The result is put in %colvect. - * (2) The %thresh parameter works with L_MODE_VAL only, and - * sets a minimum occupancy of the mode bin. - * If the occupancy of the mode bin is less than %thresh, the - * mode value is returned as 0. To always return the actual - * mode value, set %thresh = 0. - * (3) What is the meaning of this %thresh parameter? - * For each row, the total count in the histogram is w, the - * image width. So %thresh, relative to w, gives a measure - * of the ratio of the bin width to the width of the distribution. - * The larger %thresh, the narrower the distribution must be - * for the mode value to be returned (instead of returning 0). - * (4) If the Pix consists of a set of corresponding columns, - * one for each Pix in a Pixa, the width of the Pix is the - * number of Pix in the Pixa and the column vector can - * be stored as a column in a Pix of the same size as - * each Pix in the Pixa. - *- */ -l_ok -pixGetRowStats(PIX *pixs, - l_int32 type, - l_int32 nbins, - l_int32 thresh, - l_float32 *colvect) -{ -l_int32 i, j, k, w, h, val, wpls, sum, target, max, modeval; -l_int32 *histo, *gray2bin, *bin2gray; -l_uint32 *lines, *datas; - - PROCNAME("pixGetRowStats"); - - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); - if (!colvect) - return ERROR_INT("colvect not defined", procName, 1); - if (type != L_MEAN_ABSVAL && type != L_MEDIAN_VAL && - type != L_MODE_VAL && type != L_MODE_COUNT) - return ERROR_INT("invalid type", procName, 1); - if (type != L_MEAN_ABSVAL && (nbins < 1 || nbins > 256)) - return ERROR_INT("invalid nbins", procName, 1); - pixGetDimensions(pixs, &w, &h, NULL); - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if (type == L_MEAN_ABSVAL) { - for (i = 0; i < h; i++) { - sum = 0; - lines = datas + i * wpls; - for (j = 0; j < w; j++) - sum += GET_DATA_BYTE(lines, j); - colvect[i] = (l_float32)sum / (l_float32)w; - } - return 0; - } - - /* We need a histogram; binwidth ~ 256 / nbins */ - histo = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32)); - gray2bin = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - bin2gray = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32)); - for (i = 0; i < 256; i++) /* gray value --> histo bin */ - gray2bin[i] = (i * nbins) / 256; - for (i = 0; i < nbins; i++) /* histo bin --> gray value */ - bin2gray[i] = (i * 256 + 128) / nbins; - - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (k = 0; k < nbins; k++) - histo[k] = 0; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(lines, j); - histo[gray2bin[val]]++; - } - - if (type == L_MEDIAN_VAL) { - sum = 0; - target = (w + 1) / 2; - for (k = 0; k < nbins; k++) { - sum += histo[k]; - if (sum >= target) { - colvect[i] = bin2gray[k]; - break; - } - } - } else if (type == L_MODE_VAL) { - max = 0; - modeval = 0; - for (k = 0; k < nbins; k++) { - if (histo[k] > max) { - max = histo[k]; - modeval = k; - } - } - if (max < thresh) - colvect[i] = 0; - else - colvect[i] = bin2gray[modeval]; - } else { /* type == L_MODE_COUNT */ - max = 0; - for (k = 0; k < nbins; k++) { - if (histo[k] > max) - max = histo[k]; - } - colvect[i] = max; - } - } - - LEPT_FREE(histo); - LEPT_FREE(gray2bin); - LEPT_FREE(bin2gray); - return 0; -} - - -/*! - * \brief pixGetColumnStats() - * - * \param[in] pixs 8 bpp; not cmapped - * \param[in] type L_MEAN_ABSVAL, L_MEDIAN_VAL, L_MODE_VAL, L_MODE_COUNT - * \param[in] nbins of histogram for median and mode; ignored for mean - * \param[in] thresh on histogram for mode val; ignored for all other types - * \param[in] rowvect vector of results gathered down the columns of pixs - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This computes a row vector of statistics using each - * column of a Pix. The result is put in %rowvect. - * (2) The %thresh parameter works with L_MODE_VAL only, and - * sets a minimum occupancy of the mode bin. - * If the occupancy of the mode bin is less than %thresh, the - * mode value is returned as 0. To always return the actual - * mode value, set %thresh = 0. - * (3) What is the meaning of this %thresh parameter? - * For each column, the total count in the histogram is h, the - * image height. So %thresh, relative to h, gives a measure - * of the ratio of the bin width to the width of the distribution. - * The larger %thresh, the narrower the distribution must be - * for the mode value to be returned (instead of returning 0). - *- */ -l_ok -pixGetColumnStats(PIX *pixs, - l_int32 type, - l_int32 nbins, - l_int32 thresh, - l_float32 *rowvect) -{ -l_int32 i, j, k, w, h, val, wpls, sum, target, max, modeval; -l_int32 *histo, *gray2bin, *bin2gray; -l_uint32 *datas; - - PROCNAME("pixGetColumnStats"); - - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); - if (!rowvect) - return ERROR_INT("rowvect not defined", procName, 1); - if (type != L_MEAN_ABSVAL && type != L_MEDIAN_VAL && - type != L_MODE_VAL && type != L_MODE_COUNT) - return ERROR_INT("invalid type", procName, 1); - if (type != L_MEAN_ABSVAL && (nbins < 1 || nbins > 256)) - return ERROR_INT("invalid nbins", procName, 1); - pixGetDimensions(pixs, &w, &h, NULL); - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if (type == L_MEAN_ABSVAL) { - for (j = 0; j < w; j++) { - sum = 0; - for (i = 0; i < h; i++) - sum += GET_DATA_BYTE(datas + i * wpls, j); - rowvect[j] = (l_float32)sum / (l_float32)h; - } - return 0; - } - - /* We need a histogram; binwidth ~ 256 / nbins */ - histo = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32)); - gray2bin = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - bin2gray = (l_int32 *)LEPT_CALLOC(nbins, sizeof(l_int32)); - for (i = 0; i < 256; i++) /* gray value --> histo bin */ - gray2bin[i] = (i * nbins) / 256; - for (i = 0; i < nbins; i++) /* histo bin --> gray value */ - bin2gray[i] = (i * 256 + 128) / nbins; - - for (j = 0; j < w; j++) { - for (i = 0; i < h; i++) { - val = GET_DATA_BYTE(datas + i * wpls, j); - histo[gray2bin[val]]++; - } - - if (type == L_MEDIAN_VAL) { - sum = 0; - target = (h + 1) / 2; - for (k = 0; k < nbins; k++) { - sum += histo[k]; - if (sum >= target) { - rowvect[j] = bin2gray[k]; - break; - } - } - } else if (type == L_MODE_VAL) { - max = 0; - modeval = 0; - for (k = 0; k < nbins; k++) { - if (histo[k] > max) { - max = histo[k]; - modeval = k; - } - } - if (max < thresh) - rowvect[j] = 0; - else - rowvect[j] = bin2gray[modeval]; - } else { /* type == L_MODE_COUNT */ - max = 0; - for (k = 0; k < nbins; k++) { - if (histo[k] > max) - max = histo[k]; - } - rowvect[j] = max; - } - for (k = 0; k < nbins; k++) - histo[k] = 0; - } - - LEPT_FREE(histo); - LEPT_FREE(gray2bin); - LEPT_FREE(bin2gray); - return 0; -} - - -/*! - * \brief pixSetPixelColumn() - * - * \param[in] pix 8 bpp; not cmapped - * \param[in] col column index - * \param[in] colvect vector of floats - * \return 0 if OK, 1 on error - */ -l_ok -pixSetPixelColumn(PIX *pix, - l_int32 col, - l_float32 *colvect) -{ -l_int32 i, w, h, wpl; -l_uint32 *data; - - PROCNAME("pixSetCPixelColumn"); - - if (!pix || pixGetDepth(pix) != 8) - return ERROR_INT("pix not defined or not 8 bpp", procName, 1); - if (!colvect) - return ERROR_INT("colvect not defined", procName, 1); - pixGetDimensions(pix, &w, &h, NULL); - if (col < 0 || col > w) - return ERROR_INT("invalid col", procName, 1); - - data = pixGetData(pix); - wpl = pixGetWpl(pix); - for (i = 0; i < h; i++) - SET_DATA_BYTE(data + i * wpl, col, (l_int32)colvect[i]); - - return 0; -} - - -/*-------------------------------------------------------------* - * Foreground/background estimation * - *-------------------------------------------------------------*/ -/*! - * \brief pixThresholdForFgBg() - * - * \param[in] pixs any depth; cmapped ok - * \param[in] factor subsampling factor; integer >= 1 - * \param[in] thresh threshold for generating foreground mask - * \param[out] pfgval [optional] average foreground value - * \param[out] pbgval [optional] average background value - * \return 0 if OK, 1 on error - */ -l_ok -pixThresholdForFgBg(PIX *pixs, - l_int32 factor, - l_int32 thresh, - l_int32 *pfgval, - l_int32 *pbgval) -{ -l_float32 fval; -PIX *pixg, *pixm; - - PROCNAME("pixThresholdForFgBg"); - - if (pfgval) *pfgval = 0; - if (pbgval) *pbgval = 0; - if (!pfgval && !pbgval) - return ERROR_INT("no data requested", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - - /* Generate a subsampled 8 bpp version and a mask over the fg */ - pixg = pixConvertTo8BySampling(pixs, factor, 0); - pixm = pixThresholdToBinary(pixg, thresh); - - if (pfgval) { - pixGetAverageMasked(pixg, pixm, 0, 0, 1, L_MEAN_ABSVAL, &fval); - *pfgval = (l_int32)(fval + 0.5); - } - - if (pbgval) { - pixInvert(pixm, pixm); - pixGetAverageMasked(pixg, pixm, 0, 0, 1, L_MEAN_ABSVAL, &fval); - *pbgval = (l_int32)(fval + 0.5); - } - - pixDestroy(&pixg); - pixDestroy(&pixm); - return 0; -} - - -/*! - * \brief pixSplitDistributionFgBg() - * - * \param[in] pixs any depth; cmapped ok - * \param[in] scorefract fraction of the max score, used to determine - * the range over which the histogram min is searched - * \param[in] factor subsampling factor; integer >= 1 - * \param[out] pthresh [optional] best threshold for separating - * \param[out] pfgval [optional] average foreground value - * \param[out] pbgval [optional] average background value - * \param[out] ppixdb [optional] plot of distribution and split point - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) See numaSplitDistribution() for details on the underlying - * method of choosing a threshold. - *- */ -l_ok -pixSplitDistributionFgBg(PIX *pixs, - l_float32 scorefract, - l_int32 factor, - l_int32 *pthresh, - l_int32 *pfgval, - l_int32 *pbgval, - PIX **ppixdb) -{ -char buf[256]; -l_int32 thresh; -l_float32 avefg, avebg, maxnum; -GPLOT *gplot; -NUMA *na, *nascore, *nax, *nay; -PIX *pixg; - - PROCNAME("pixSplitDistributionFgBg"); - - if (pthresh) *pthresh = 0; - if (pfgval) *pfgval = 0; - if (pbgval) *pbgval = 0; - if (ppixdb) *ppixdb = NULL; - if (!pthresh && !pfgval && !pbgval) - return ERROR_INT("no data requested", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - - /* Generate a subsampled 8 bpp version */ - pixg = pixConvertTo8BySampling(pixs, factor, 0); - - /* Make the fg/bg estimates */ - na = pixGetGrayHistogram(pixg, 1); - if (ppixdb) { - numaSplitDistribution(na, scorefract, &thresh, &avefg, &avebg, - NULL, NULL, &nascore); - numaDestroy(&nascore); - } else { - numaSplitDistribution(na, scorefract, &thresh, &avefg, &avebg, - NULL, NULL, NULL); - } - - if (pthresh) *pthresh = thresh; - if (pfgval) *pfgval = (l_int32)(avefg + 0.5); - if (pbgval) *pbgval = (l_int32)(avebg + 0.5); - - if (ppixdb) { - lept_mkdir("lept/redout"); - gplot = gplotCreate("/tmp/lept/redout/histplot", GPLOT_PNG, "Histogram", - "Grayscale value", "Number of pixels"); - gplotAddPlot(gplot, NULL, na, GPLOT_LINES, NULL); - nax = numaMakeConstant(thresh, 2); - numaGetMax(na, &maxnum, NULL); - nay = numaMakeConstant(0, 2); - numaReplaceNumber(nay, 1, (l_int32)(0.5 * maxnum)); - snprintf(buf, sizeof(buf), "score fract = %3.1f", scorefract); - gplotAddPlot(gplot, nax, nay, GPLOT_LINES, buf); - *ppixdb = gplotMakeOutputPix(gplot); - gplotDestroy(&gplot); - numaDestroy(&nax); - numaDestroy(&nay); - } - - pixDestroy(&pixg); - numaDestroy(&na); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix5.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix5.c deleted file mode 100644 index 7b12ead8..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pix5.c +++ /dev/null @@ -1,3157 +0,0 @@ -/*====================================================================* - - 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 pix5.c - *- * - * This file has these operations: - * - * (1) Measurement of 1 bpp image properties - * (2) Extract rectangular regions - * (3) Clip to foreground - * (4) Extract pixel averages, reversals and variance along lines - * (5) Rank row and column transforms - * - * Measurement of properties - * l_int32 pixaFindDimensions() - * l_int32 pixFindAreaPerimRatio() - * NUMA *pixaFindPerimToAreaRatio() - * l_int32 pixFindPerimToAreaRatio() - * NUMA *pixaFindPerimSizeRatio() - * l_int32 pixFindPerimSizeRatio() - * NUMA *pixaFindAreaFraction() - * l_int32 pixFindAreaFraction() - * NUMA *pixaFindAreaFractionMasked() - * l_int32 pixFindAreaFractionMasked() - * NUMA *pixaFindWidthHeightRatio() - * NUMA *pixaFindWidthHeightProduct() - * l_int32 pixFindOverlapFraction() - * BOXA *pixFindRectangleComps() - * l_int32 pixConformsToRectangle() - * - * Extract rectangular region - * PIXA *pixClipRectangles() - * PIX *pixClipRectangle() - * PIX *pixClipMasked() - * l_int32 pixCropToMatch() - * PIX *pixCropToSize() - * PIX *pixResizeToMatch() - * - * Select a connected component by size - * PIX *pixSelectComponentBySize() - * PIX *pixFilterComponentBySize() - * - * Make special masks - * PIX *pixMakeSymmetricMask() - * PIX *pixMakeFrameMask() - * - * Generate a covering of rectangles over connected components - * PIX * pixMakeCoveringOfRectangles() - * - * Fraction of Fg pixels under a mask - * l_int32 pixFractionFgInMask() - * - * Clip to foreground - * PIX *pixClipToForeground() - * l_int32 pixTestClipToForeground() - * l_int32 pixClipBoxToForeground() - * l_int32 pixScanForForeground() - * l_int32 pixClipBoxToEdges() - * l_int32 pixScanForEdge() - * - * Extract pixel averages and reversals along lines - * NUMA *pixExtractOnLine() - * l_float32 pixAverageOnLine() - * NUMA *pixAverageIntensityProfile() - * NUMA *pixReversalProfile() - * - * Extract windowed variance along a line - * NUMA *pixWindowedVarianceOnLine() - * - * Extract min/max of pixel values near lines - * l_int32 pixMinMaxNearLine() - * - * Rank row and column transforms - * PIX *pixRankRowTransform() - * PIX *pixRankColumnTransform() - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include -#include "allheaders.h" - -static const l_uint32 rmask32[] = {0x0, - 0x00000001, 0x00000003, 0x00000007, 0x0000000f, - 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, - 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, - 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, - 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, - 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, - 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff, - 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff}; - -#ifndef NO_CONSOLE_IO -#define DEBUG_EDGES 0 -#endif /* ~NO_CONSOLE_IO */ - - -/*-------------------------------------------------------------* - * Measurement of properties * - *-------------------------------------------------------------*/ -/*! - * \brief pixaFindDimensions() - * - * \param[in] pixa - * \param[out] pnaw [optional] numa of pix widths - * \param[out] pnah [optional] numa of pix heights - * \return 0 if OK, 1 on error - */ -l_ok -pixaFindDimensions(PIXA *pixa, - NUMA **pnaw, - NUMA **pnah) -{ -l_int32 i, n, w, h; -PIX *pixt; - - PROCNAME("pixaFindDimensions"); - - if (pnaw) *pnaw = NULL; - if (pnah) *pnah = NULL; - if (!pnaw && !pnah) - return ERROR_INT("no output requested", procName, 1); - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - - n = pixaGetCount(pixa); - if (pnaw) *pnaw = numaCreate(n); - if (pnah) *pnah = numaCreate(n); - for (i = 0; i < n; i++) { - pixt = pixaGetPix(pixa, i, L_CLONE); - pixGetDimensions(pixt, &w, &h, NULL); - if (pnaw) - numaAddNumber(*pnaw, w); - if (pnah) - numaAddNumber(*pnah, h); - pixDestroy(&pixt); - } - return 0; -} - - -/*! - * \brief pixFindAreaPerimRatio() - * - * \param[in] pixs 1 bpp - * \param[in] tab [optional] pixel sum table, can be NULL - * \param[out] pfract area/perimeter ratio - * \return 0 if OK, 1 on error - * - * - * Notes: - * (1) The area is the number of fg pixels that are not on the - * boundary (i.e., are not 8-connected to a bg pixel), and the - * perimeter is the number of fg boundary pixels. Returns - * 0.0 if there are no fg pixels. - * (2) This function is retained because clients are using it. - *- */ -l_ok -pixFindAreaPerimRatio(PIX *pixs, - l_int32 *tab, - l_float32 *pfract) -{ -l_int32 *tab8; -l_int32 nfg, nbound; -PIX *pixt; - - PROCNAME("pixFindAreaPerimRatio"); - - if (!pfract) - return ERROR_INT("&fract not defined", procName, 1); - *pfract = 0.0; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - - if (!tab) - tab8 = makePixelSumTab8(); - else - tab8 = tab; - - pixt = pixErodeBrick(NULL, pixs, 3, 3); - pixCountPixels(pixt, &nfg, tab8); - if (nfg == 0) { - pixDestroy(&pixt); - if (!tab) LEPT_FREE(tab8); - return 0; - } - pixXor(pixt, pixt, pixs); - pixCountPixels(pixt, &nbound, tab8); - *pfract = (l_float32)nfg / (l_float32)nbound; - pixDestroy(&pixt); - - if (!tab) LEPT_FREE(tab8); - return 0; -} - - -/*! - * \brief pixaFindPerimToAreaRatio() - * - * \param[in] pixa of 1 bpp pix - * \return na of perimeter/arear ratio for each pix, or NULL on error - * - *- * Notes: - * (1) This is typically used for a pixa consisting of - * 1 bpp connected components. - *- */ -NUMA * -pixaFindPerimToAreaRatio(PIXA *pixa) -{ -l_int32 i, n; -l_int32 *tab; -l_float32 fract; -NUMA *na; -PIX *pixt; - - PROCNAME("pixaFindPerimToAreaRatio"); - - if (!pixa) - return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); - - n = pixaGetCount(pixa); - na = numaCreate(n); - tab = makePixelSumTab8(); - for (i = 0; i < n; i++) { - pixt = pixaGetPix(pixa, i, L_CLONE); - pixFindPerimToAreaRatio(pixt, tab, &fract); - numaAddNumber(na, fract); - pixDestroy(&pixt); - } - LEPT_FREE(tab); - return na; -} - - -/*! - * \brief pixFindPerimToAreaRatio() - * - * \param[in] pixs 1 bpp - * \param[in] tab [optional] pixel sum table, can be NULL - * \param[out] pfract perimeter/area ratio - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The perimeter is the number of fg boundary pixels, and the - * area is the number of fg pixels. This returns 0.0 if - * there are no fg pixels. - * (2) Unlike pixFindAreaPerimRatio(), this uses the full set of - * fg pixels for the area, and the ratio is taken in the opposite - * order. - * (3) This is typically used for a single connected component. - * This always has a value <= 1.0, and if the average distance - * of a fg pixel from the nearest bg pixel is d, this has - * a value ~1/d. - *- */ -l_ok -pixFindPerimToAreaRatio(PIX *pixs, - l_int32 *tab, - l_float32 *pfract) -{ -l_int32 *tab8; -l_int32 nfg, nbound; -PIX *pixt; - - PROCNAME("pixFindPerimToAreaRatio"); - - if (!pfract) - return ERROR_INT("&fract not defined", procName, 1); - *pfract = 0.0; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - - if (!tab) - tab8 = makePixelSumTab8(); - else - tab8 = tab; - - pixCountPixels(pixs, &nfg, tab8); - if (nfg == 0) { - if (!tab) LEPT_FREE(tab8); - return 0; - } - pixt = pixErodeBrick(NULL, pixs, 3, 3); - pixXor(pixt, pixt, pixs); - pixCountPixels(pixt, &nbound, tab8); - *pfract = (l_float32)nbound / (l_float32)nfg; - pixDestroy(&pixt); - - if (!tab) LEPT_FREE(tab8); - return 0; -} - - -/*! - * \brief pixaFindPerimSizeRatio() - * - * \param[in] pixa of 1 bpp pix - * \return na of fg perimeter/(2*(w+h)) ratio for each pix, - * or NULL on error - * - *- * Notes: - * (1) This is typically used for a pixa consisting of - * 1 bpp connected components. - * (2) This has a minimum value for a circle of pi/4; a value for - * a rectangle component of approx. 1.0; and a value much larger - * than 1.0 for a component with a highly irregular boundary. - *- */ -NUMA * -pixaFindPerimSizeRatio(PIXA *pixa) -{ -l_int32 i, n; -l_int32 *tab; -l_float32 ratio; -NUMA *na; -PIX *pixt; - - PROCNAME("pixaFindPerimSizeRatio"); - - if (!pixa) - return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); - - n = pixaGetCount(pixa); - na = numaCreate(n); - tab = makePixelSumTab8(); - for (i = 0; i < n; i++) { - pixt = pixaGetPix(pixa, i, L_CLONE); - pixFindPerimSizeRatio(pixt, tab, &ratio); - numaAddNumber(na, ratio); - pixDestroy(&pixt); - } - LEPT_FREE(tab); - return na; -} - - -/*! - * \brief pixFindPerimSizeRatio() - * - * \param[in] pixs 1 bpp - * \param[in] tab [optional] pixel sum table, can be NULL - * \param[out] pratio perimeter/size ratio - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) We take the 'size' as twice the sum of the width and - * height of pixs, and the perimeter is the number of fg - * boundary pixels. We use the fg pixels of the boundary - * because the pix may be clipped to the boundary, so an - * erosion is required to count all boundary pixels. - * (2) This has a large value for dendritic, fractal-like components - * with highly irregular boundaries. - * (3) This is typically used for a single connected component. - * It has a value of about 1.0 for rectangular components with - * relatively smooth boundaries. - *- */ -l_ok -pixFindPerimSizeRatio(PIX *pixs, - l_int32 *tab, - l_float32 *pratio) -{ -l_int32 *tab8; -l_int32 w, h, nbound; -PIX *pixt; - - PROCNAME("pixFindPerimSizeRatio"); - - if (!pratio) - return ERROR_INT("&ratio not defined", procName, 1); - *pratio = 0.0; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - - if (!tab) - tab8 = makePixelSumTab8(); - else - tab8 = tab; - - pixt = pixErodeBrick(NULL, pixs, 3, 3); - pixXor(pixt, pixt, pixs); - pixCountPixels(pixt, &nbound, tab8); - pixGetDimensions(pixs, &w, &h, NULL); - *pratio = (0.5 * nbound) / (l_float32)(w + h); - pixDestroy(&pixt); - - if (!tab) LEPT_FREE(tab8); - return 0; -} - - -/*! - * \brief pixaFindAreaFraction() - * - * \param[in] pixa of 1 bpp pix - * \return na of area fractions for each pix, or NULL on error - * - *- * Notes: - * (1) This is typically used for a pixa consisting of - * 1 bpp connected components. - *- */ -NUMA * -pixaFindAreaFraction(PIXA *pixa) -{ -l_int32 i, n; -l_int32 *tab; -l_float32 fract; -NUMA *na; -PIX *pixt; - - PROCNAME("pixaFindAreaFraction"); - - if (!pixa) - return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); - - n = pixaGetCount(pixa); - na = numaCreate(n); - tab = makePixelSumTab8(); - for (i = 0; i < n; i++) { - pixt = pixaGetPix(pixa, i, L_CLONE); - pixFindAreaFraction(pixt, tab, &fract); - numaAddNumber(na, fract); - pixDestroy(&pixt); - } - LEPT_FREE(tab); - return na; -} - - -/*! - * \brief pixFindAreaFraction() - * - * \param[in] pixs 1 bpp - * \param[in] tab [optional] pixel sum table, can be NULL - * \param[out] pfract fg area/size ratio - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This finds the ratio of the number of fg pixels to the - * size of the pix (w * h). It is typically used for a - * single connected component. - *- */ -l_ok -pixFindAreaFraction(PIX *pixs, - l_int32 *tab, - l_float32 *pfract) -{ -l_int32 w, h, sum; -l_int32 *tab8; - - PROCNAME("pixFindAreaFraction"); - - if (!pfract) - return ERROR_INT("&fract not defined", procName, 1); - *pfract = 0.0; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - - if (!tab) - tab8 = makePixelSumTab8(); - else - tab8 = tab; - pixGetDimensions(pixs, &w, &h, NULL); - pixCountPixels(pixs, &sum, tab8); - *pfract = (l_float32)sum / (l_float32)(w * h); - - if (!tab) LEPT_FREE(tab8); - return 0; -} - - -/*! - * \brief pixaFindAreaFractionMasked() - * - * \param[in] pixa of 1 bpp pix - * \param[in] pixm mask image - * \param[in] debug 1 for output, 0 to suppress - * \return na of ratio masked/total fractions for each pix, - * or NULL on error - * - *- * Notes: - * (1) This is typically used for a pixa consisting of - * 1 bpp connected components, which has an associated - * boxa giving the location of the components relative - * to the mask origin. - * (2) The debug flag displays in green and red the masked and - * unmasked parts of the image from which pixa was derived. - *- */ -NUMA * -pixaFindAreaFractionMasked(PIXA *pixa, - PIX *pixm, - l_int32 debug) -{ -l_int32 i, n, full; -l_int32 *tab; -l_float32 fract; -BOX *box; -NUMA *na; -PIX *pix; - - PROCNAME("pixaFindAreaFractionMasked"); - - if (!pixa) - return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); - if (!pixm || pixGetDepth(pixm) != 1) - return (NUMA *)ERROR_PTR("pixm undefined or not 1 bpp", procName, NULL); - - n = pixaGetCount(pixa); - na = numaCreate(n); - tab = makePixelSumTab8(); - pixaIsFull(pixa, NULL, &full); /* check boxa */ - box = NULL; - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixa, i, L_CLONE); - if (full) - box = pixaGetBox(pixa, i, L_CLONE); - pixFindAreaFractionMasked(pix, box, pixm, tab, &fract); - numaAddNumber(na, fract); - boxDestroy(&box); - pixDestroy(&pix); - } - LEPT_FREE(tab); - - if (debug) { - l_int32 w, h; - PIX *pix1, *pix2; - pixGetDimensions(pixm, &w, &h, NULL); - pix1 = pixaDisplay(pixa, w, h); /* recover original image */ - pix2 = pixCreate(w, h, 8); /* make an 8 bpp white image ... */ - pixSetColormap(pix2, pixcmapCreate(8)); /* that's cmapped ... */ - pixSetBlackOrWhite(pix2, L_SET_WHITE); /* and init to white */ - pixSetMaskedCmap(pix2, pix1, 0, 0, 255, 0, 0); /* color all fg red */ - pixRasterop(pix1, 0, 0, w, h, PIX_MASK, pixm, 0, 0); - pixSetMaskedCmap(pix2, pix1, 0, 0, 0, 255, 0); /* turn masked green */ - pixDisplay(pix2, 100, 100); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - - return na; -} - - -/*! - * \brief pixFindAreaFractionMasked() - * - * \param[in] pixs 1 bpp, typically a single component - * \param[in] box [optional] for pixs relative to pixm - * \param[in] pixm 1 bpp mask, typically over the entire image from - * which the component pixs was extracted - * \param[in] tab [optional] pixel sum table, can be NULL - * \param[out] pfract fg area/size ratio - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This finds the ratio of the number of masked fg pixels - * in pixs to the total number of fg pixels in pixs. - * It is typically used for a single connected component. - * If there are no fg pixels, this returns a ratio of 0.0. - * (2) The box gives the location of the pix relative to that - * of the UL corner of the mask. Therefore, the rasterop - * is performed with the pix translated to its location - * (x, y) in the mask before ANDing. - * If box == NULL, the UL corners of pixs and pixm are aligned. - *- */ -l_ok -pixFindAreaFractionMasked(PIX *pixs, - BOX *box, - PIX *pixm, - l_int32 *tab, - l_float32 *pfract) -{ -l_int32 x, y, w, h, sum, masksum; -l_int32 *tab8; -PIX *pix1; - - PROCNAME("pixFindAreaFractionMasked"); - - if (!pfract) - return ERROR_INT("&fract not defined", procName, 1); - *pfract = 0.0; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (!pixm || pixGetDepth(pixm) != 1) - return ERROR_INT("pixm not defined or not 1 bpp", procName, 1); - - if (!tab) - tab8 = makePixelSumTab8(); - else - tab8 = tab; - x = y = 0; - if (box) - boxGetGeometry(box, &x, &y, NULL, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - - pix1 = pixCopy(NULL, pixs); - pixRasterop(pix1, 0, 0, w, h, PIX_MASK, pixm, x, y); - pixCountPixels(pixs, &sum, tab8); - if (sum == 0) { - pixDestroy(&pix1); - if (!tab) LEPT_FREE(tab8); - return 0; - } - pixCountPixels(pix1, &masksum, tab8); - *pfract = (l_float32)masksum / (l_float32)sum; - - if (!tab) LEPT_FREE(tab8); - pixDestroy(&pix1); - return 0; -} - - -/*! - * \brief pixaFindWidthHeightRatio() - * - * \param[in] pixa of 1 bpp pix - * \return na of width/height ratios for each pix, or NULL on error - * - *- * Notes: - * (1) This is typically used for a pixa consisting of - * 1 bpp connected components. - *- */ -NUMA * -pixaFindWidthHeightRatio(PIXA *pixa) -{ -l_int32 i, n, w, h; -NUMA *na; -PIX *pixt; - - PROCNAME("pixaFindWidthHeightRatio"); - - if (!pixa) - return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); - - n = pixaGetCount(pixa); - na = numaCreate(n); - for (i = 0; i < n; i++) { - pixt = pixaGetPix(pixa, i, L_CLONE); - pixGetDimensions(pixt, &w, &h, NULL); - numaAddNumber(na, (l_float32)w / (l_float32)h); - pixDestroy(&pixt); - } - return na; -} - - -/*! - * \brief pixaFindWidthHeightProduct() - * - * \param[in] pixa of 1 bpp pix - * \return na of width*height products for each pix, or NULL on error - * - *- * Notes: - * (1) This is typically used for a pixa consisting of - * 1 bpp connected components. - *- */ -NUMA * -pixaFindWidthHeightProduct(PIXA *pixa) -{ -l_int32 i, n, w, h; -NUMA *na; -PIX *pixt; - - PROCNAME("pixaFindWidthHeightProduct"); - - if (!pixa) - return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); - - n = pixaGetCount(pixa); - na = numaCreate(n); - for (i = 0; i < n; i++) { - pixt = pixaGetPix(pixa, i, L_CLONE); - pixGetDimensions(pixt, &w, &h, NULL); - numaAddNumber(na, w * h); - pixDestroy(&pixt); - } - return na; -} - - -/*! - * \brief pixFindOverlapFraction() - * - * \param[in] pixs1, pixs2 1 bpp - * \param[in] x2, y2 location in pixs1 of UL corner of pixs2 - * \param[in] tab [optional] pixel sum table, can be null - * \param[out] pratio ratio fg intersection to fg union - * \param[out] pnoverlap [optional] number of overlapping pixels - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The UL corner of pixs2 is placed at (x2, y2) in pixs1. - * (2) This measure is similar to the correlation. - *- */ -l_ok -pixFindOverlapFraction(PIX *pixs1, - PIX *pixs2, - l_int32 x2, - l_int32 y2, - l_int32 *tab, - l_float32 *pratio, - l_int32 *pnoverlap) -{ -l_int32 *tab8; -l_int32 w, h, nintersect, nunion; -PIX *pixt; - - PROCNAME("pixFindOverlapFraction"); - - if (pnoverlap) *pnoverlap = 0; - if (!pratio) - return ERROR_INT("&ratio not defined", procName, 1); - *pratio = 0.0; - if (!pixs1 || pixGetDepth(pixs1) != 1) - return ERROR_INT("pixs1 not defined or not 1 bpp", procName, 1); - if (!pixs2 || pixGetDepth(pixs2) != 1) - return ERROR_INT("pixs2 not defined or not 1 bpp", procName, 1); - - if (!tab) - tab8 = makePixelSumTab8(); - else - tab8 = tab; - - pixGetDimensions(pixs2, &w, &h, NULL); - pixt = pixCopy(NULL, pixs1); - pixRasterop(pixt, x2, y2, w, h, PIX_MASK, pixs2, 0, 0); /* AND */ - pixCountPixels(pixt, &nintersect, tab8); - if (pnoverlap) - *pnoverlap = nintersect; - pixCopy(pixt, pixs1); - pixRasterop(pixt, x2, y2, w, h, PIX_PAINT, pixs2, 0, 0); /* OR */ - pixCountPixels(pixt, &nunion, tab8); - if (!tab) LEPT_FREE(tab8); - pixDestroy(&pixt); - - if (nunion > 0) - *pratio = (l_float32)nintersect / (l_float32)nunion; - return 0; -} - - -/*! - * \brief pixFindRectangleComps() - * - * \param[in] pixs 1 bpp - * \param[in] dist max distance allowed between bounding box - * and nearest foreground pixel within it - * \param[in] minw, minh minimum size in each direction as a requirement - * for a conforming rectangle - * \return boxa of components that conform, or NULL on error - * - *- * Notes: - * (1) This applies the function pixConformsToRectangle() to - * each 8-c.c. in pixs, and returns a boxa containing the - * regions of all components that are conforming. - * (2) Conforming components must satisfy both the size constraint - * given by %minsize and the slop in conforming to a rectangle - * determined by %dist. - *- */ -BOXA * -pixFindRectangleComps(PIX *pixs, - l_int32 dist, - l_int32 minw, - l_int32 minh) -{ -l_int32 w, h, i, n, conforms; -BOX *box; -BOXA *boxa, *boxad; -PIX *pix; -PIXA *pixa; - - PROCNAME("pixFindRectangleComps"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (BOXA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (dist < 0) - return (BOXA *)ERROR_PTR("dist must be >= 0", procName, NULL); - if (minw <= 2 * dist && minh <= 2 * dist) - return (BOXA *)ERROR_PTR("invalid parameters", procName, NULL); - - boxa = pixConnComp(pixs, &pixa, 8); - boxad = boxaCreate(0); - n = pixaGetCount(pixa); - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixa, i, L_CLONE); - pixGetDimensions(pix, &w, &h, NULL); - if (w < minw || h < minh) { - pixDestroy(&pix); - continue; - } - pixConformsToRectangle(pix, NULL, dist, &conforms); - if (conforms) { - box = boxaGetBox(boxa, i, L_COPY); - boxaAddBox(boxad, box, L_INSERT); - } - pixDestroy(&pix); - } - boxaDestroy(&boxa); - pixaDestroy(&pixa); - return boxad; -} - - -/*! - * \brief pixConformsToRectangle() - * - * \param[in] pixs 1 bpp - * \param[in] box [optional] if null, use the entire pixs - * \param[in] dist max distance allowed between bounding box and - * nearest foreground pixel within it - * \param[out] pconforms 0 (false) if not conforming; - * 1 (true) if conforming - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) There are several ways to test if a connected component has - * an essentially rectangular boundary, such as: - * a. Fraction of fill into the bounding box - * b. Max-min distance of fg pixel from periphery of bounding box - * c. Max depth of bg intrusions into component within bounding box - * The weakness of (a) is that it is highly sensitive to holes - * within the c.c. The weakness of (b) is that it can have - * arbitrarily large intrusions into the c.c. Method (c) tests - * the integrity of the outer boundary of the c.c., with respect - * to the enclosing bounding box, so we use it. - * (2) This tests if the connected component within the box conforms - * to the box at all points on the periphery within %dist. - * Inside, at a distance from the box boundary that is greater - * than %dist, we don't care about the pixels in the c.c. - * (3) We can think of the conforming condition as follows: - * No pixel inside a distance %dist from the boundary - * can connect to the boundary through a path through the bg. - * To implement this, we need to do a flood fill. We can go - * either from inside toward the boundary, or the other direction. - * It's easiest to fill from the boundary, and then verify that - * there are no filled pixels farther than %dist from the boundary. - *- */ -l_ok -pixConformsToRectangle(PIX *pixs, - BOX *box, - l_int32 dist, - l_int32 *pconforms) -{ -l_int32 w, h, empty; -PIX *pix1, *pix2; - - PROCNAME("pixConformsToRectangle"); - - if (!pconforms) - return ERROR_INT("&conforms not defined", procName, 1); - *pconforms = 0; - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (dist < 0) - return ERROR_INT("dist must be >= 0", procName, 1); - pixGetDimensions(pixs, &w, &h, NULL); - if (w <= 2 * dist || h <= 2 * dist) { - L_WARNING("automatic conformation: distance too large\n", procName); - *pconforms = 1; - return 0; - } - - /* Extract the region, if necessary */ - if (box) - pix1 = pixClipRectangle(pixs, box, NULL); - else - pix1 = pixCopy(NULL, pixs); - - /* Invert and fill from the boundary into the interior. - * Because we're considering the connected component in an - * 8-connected sense, we do the background filling as 4 c.c. */ - pixInvert(pix1, pix1); - pix2 = pixExtractBorderConnComps(pix1, 4); - - /* Mask out all pixels within a distance %dist from the box - * boundary. Any remaining pixels are from filling that goes - * more than %dist from the boundary. If no pixels remain, - * the component conforms to the bounding rectangle within - * a distance %dist. */ - pixSetOrClearBorder(pix2, dist, dist, dist, dist, PIX_CLR); - pixZero(pix2, &empty); - pixDestroy(&pix1); - pixDestroy(&pix2); - *pconforms = (empty) ? 1 : 0; - return 0; -} - - -/*-----------------------------------------------------------------------* - * Extract rectangular region * - *-----------------------------------------------------------------------*/ -/*! - * \brief pixClipRectangles() - * - * \param[in] pixs - * \param[in] boxa requested clipping regions - * \return pixa consisting of requested regions, or NULL on error - * - *- * Notes: - * (1) The returned pixa includes the actual regions clipped out from - * the input pixs. - *- */ -PIXA * -pixClipRectangles(PIX *pixs, - BOXA *boxa) -{ -l_int32 i, n; -BOX *box, *boxc; -PIX *pix; -PIXA *pixa; - - PROCNAME("pixClipRectangles"); - - 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); - pixa = pixaCreate(n); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - pix = pixClipRectangle(pixs, box, &boxc); - pixaAddPix(pixa, pix, L_INSERT); - pixaAddBox(pixa, boxc, L_INSERT); - boxDestroy(&box); - } - - return pixa; -} - - -/*! - * \brief pixClipRectangle() - * - * \param[in] pixs - * \param[in] box requested clipping region; const - * \param[out] pboxc [optional] actual box of clipped region - * \return clipped pix, or NULL on error or if rectangle - * doesn't intersect pixs - * - *- * Notes: - * - * This should be simple, but there are choices to be made. - * The box is defined relative to the pix coordinates. However, - * if the box is not contained within the pix, we have two choices: - * - * (1) clip the box to the pix - * (2) make a new pix equal to the full box dimensions, - * but let rasterop do the clipping and positioning - * of the src with respect to the dest - * - * Choice (2) immediately brings up the problem of what pixel values - * to use that were not taken from the src. For example, on a grayscale - * image, do you want the pixels not taken from the src to be black - * or white or something else? To implement choice 2, one needs to - * specify the color of these extra pixels. - * - * So we adopt (1), and clip the box first, if necessary, - * before making the dest pix and doing the rasterop. But there - * is another issue to consider. If you want to paste the - * clipped pix back into pixs, it must be properly aligned, and - * it is necessary to use the clipped box for alignment. - * Accordingly, this function has a third (optional) argument, which is - * the input box clipped to the src pix. - *- */ -PIX * -pixClipRectangle(PIX *pixs, - BOX *box, - BOX **pboxc) -{ -l_int32 w, h, d, bx, by, bw, bh; -BOX *boxc; -PIX *pixd; - - PROCNAME("pixClipRectangle"); - - if (pboxc) *pboxc = NULL; - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!box) - return (PIX *)ERROR_PTR("box not defined", procName, NULL); - - /* Clip the input box to the pix */ - pixGetDimensions(pixs, &w, &h, &d); - if ((boxc = boxClipToRectangle(box, w, h)) == NULL) { - L_WARNING("box doesn't overlap pix\n", procName); - return NULL; - } - boxGetGeometry(boxc, &bx, &by, &bw, &bh); - - /* Extract the block */ - if ((pixd = pixCreate(bw, bh, d)) == NULL) { - boxDestroy(&boxc); - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - pixCopyResolution(pixd, pixs); - pixCopyColormap(pixd, pixs); - pixCopyText(pixd, pixs); - pixRasterop(pixd, 0, 0, bw, bh, PIX_SRC, pixs, bx, by); - - if (pboxc) - *pboxc = boxc; - else - boxDestroy(&boxc); - - return pixd; -} - - -/*! - * \brief pixClipMasked() - * - * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp; colormap ok - * \param[in] pixm clipping mask, 1 bpp - * \param[in] x, y origin of clipping mask relative to pixs - * \param[in] outval val to use for pixels that are outside the mask - * \return pixd, clipped pix or NULL on error or if pixm doesn't - * intersect pixs - * - *- * Notes: - * (1) If pixs has a colormap, it is preserved in pixd. - * (2) The depth of pixd is the same as that of pixs. - * (3) If the depth of pixs is 1, use %outval = 0 for white background - * and 1 for black; otherwise, use the max value for white - * and 0 for black. If pixs has a colormap, the max value for - * %outval is 0xffffffff; otherwise, it is 2^d - 1. - * (4) When using 1 bpp pixs, this is a simple clip and - * blend operation. For example, if both pix1 and pix2 are - * black text on white background, and you want to OR the - * fg on the two images, let pixm be the inverse of pix2. - * Then the operation takes all of pix1 that's in the bg of - * pix2, and for the remainder (which are the pixels - * corresponding to the fg of the pix2), paint them black - * (1) in pix1. The function call looks like - * pixClipMasked(pix2, pixInvert(pix1, pix1), x, y, 1); - *- */ -PIX * -pixClipMasked(PIX *pixs, - PIX *pixm, - l_int32 x, - l_int32 y, - l_uint32 outval) -{ -l_int32 wm, hm, index, rval, gval, bval; -l_uint32 pixel; -BOX *box; -PIX *pixmi, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixClipMasked"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!pixm || pixGetDepth(pixm) != 1) - return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", procName, NULL); - - /* Clip out the region specified by pixm and (x,y) */ - pixGetDimensions(pixm, &wm, &hm, NULL); - box = boxCreate(x, y, wm, hm); - pixd = pixClipRectangle(pixs, box, NULL); - - /* Paint 'outval' (or something close to it if cmapped) through - * the pixels not masked by pixm */ - cmap = pixGetColormap(pixd); - pixmi = pixInvert(NULL, pixm); - if (cmap) { - extractRGBValues(outval, &rval, &gval, &bval); - pixcmapGetNearestIndex(cmap, rval, gval, bval, &index); - pixcmapGetColor(cmap, index, &rval, &gval, &bval); - composeRGBPixel(rval, gval, bval, &pixel); - pixPaintThroughMask(pixd, pixmi, 0, 0, pixel); - } else { - pixPaintThroughMask(pixd, pixmi, 0, 0, outval); - } - - boxDestroy(&box); - pixDestroy(&pixmi); - return pixd; -} - - -/*! - * \brief pixCropToMatch() - * - * \param[in] pixs1 any depth, colormap OK - * \param[in] pixs2 any depth, colormap OK - * \param[out] ppixd1 may be a clone - * \param[out] ppixd2 may be a clone - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This resizes pixs1 and/or pixs2 by cropping at the right - * and bottom, so that they're the same size. - * (2) If a pix doesn't need to be cropped, a clone is returned. - * (3) Note: the images are implicitly aligned to the UL corner. - *- */ -l_ok -pixCropToMatch(PIX *pixs1, - PIX *pixs2, - PIX **ppixd1, - PIX **ppixd2) -{ -l_int32 w1, h1, w2, h2, w, h; - - PROCNAME("pixCropToMatch"); - - if (!ppixd1 || !ppixd2) - return ERROR_INT("&pixd1 and &pixd2 not both defined", procName, 1); - *ppixd1 = *ppixd2 = NULL; - if (!pixs1 || !pixs2) - return ERROR_INT("pixs1 and pixs2 not defined", procName, 1); - - pixGetDimensions(pixs1, &w1, &h1, NULL); - pixGetDimensions(pixs2, &w2, &h2, NULL); - w = L_MIN(w1, w2); - h = L_MIN(h1, h2); - - *ppixd1 = pixCropToSize(pixs1, w, h); - *ppixd2 = pixCropToSize(pixs2, w, h); - if (*ppixd1 == NULL || *ppixd2 == NULL) - return ERROR_INT("cropped image failure", procName, 1); - return 0; -} - - -/*! - * \brief pixCropToSize() - * - * \param[in] pixs any depth, colormap OK - * \param[in] w, h max dimensions of cropped image - * \return pixd cropped if necessary or NULL on error. - * - *- * Notes: - * (1) If either w or h is smaller than the corresponding dimension - * of pixs, this returns a cropped image; otherwise it returns - * a clone of pixs. - *- */ -PIX * -pixCropToSize(PIX *pixs, - l_int32 w, - l_int32 h) -{ -l_int32 ws, hs, wd, hd, d; -PIX *pixd; - - PROCNAME("pixCropToSize"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - pixGetDimensions(pixs, &ws, &hs, &d); - if (ws <= w && hs <= h) /* no cropping necessary */ - return pixClone(pixs); - - wd = L_MIN(ws, w); - hd = L_MIN(hs, h); - if ((pixd = pixCreate(wd, hd, d)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyColormap(pixd, pixs); - pixCopyText(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - pixRasterop(pixd, 0, 0, wd, hd, PIX_SRC, pixs, 0, 0); - return pixd; -} - - -/*! - * \brief pixResizeToMatch() - * - * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp; colormap ok - * \param[in] pixt can be null; we use only the size - * \param[in] w, h ignored if pixt is defined - * \return pixd resized to match or NULL on error - * - *- * Notes: - * (1) This resizes pixs to make pixd, without scaling, by either - * cropping or extending separately in both width and height. - * Extension is done by replicating the last row or column. - * This is useful in a situation where, due to scaling - * operations, two images that are expected to be the - * same size can differ slightly in each dimension. - * (2) You can use either an existing pixt or specify - * both %w and %h. If pixt is defined, the values - * in %w and %h are ignored. - * (3) If pixt is larger than pixs (or if w and/or d is larger - * than the dimension of pixs, replicate the outer row and - * column of pixels in pixs into pixd. - *- */ -PIX * -pixResizeToMatch(PIX *pixs, - PIX *pixt, - l_int32 w, - l_int32 h) -{ -l_int32 i, j, ws, hs, d; -PIX *pixd; - - PROCNAME("pixResizeToMatch"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!pixt && (w <= 0 || h <= 0)) - return (PIX *)ERROR_PTR("both w and h not > 0", procName, NULL); - - if (pixt) /* redefine w, h */ - pixGetDimensions(pixt, &w, &h, NULL); - pixGetDimensions(pixs, &ws, &hs, &d); - if (ws == w && hs == h) - return pixCopy(NULL, pixs); - - if ((pixd = pixCreate(w, h, d)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyColormap(pixd, pixs); - pixCopyText(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - pixRasterop(pixd, 0, 0, ws, hs, PIX_SRC, pixs, 0, 0); - if (ws >= w && hs >= h) - return pixd; - - /* Replicate the last column and then the last row */ - if (ws < w) { - for (j = ws; j < w; j++) - pixRasterop(pixd, j, 0, 1, h, PIX_SRC, pixd, ws - 1, 0); - } - if (hs < h) { - for (i = hs; i < h; i++) - pixRasterop(pixd, 0, i, w, 1, PIX_SRC, pixd, 0, hs - 1); - } - - return pixd; -} - - -/*---------------------------------------------------------------------* - * Select a connected component by size * - *---------------------------------------------------------------------*/ -/*! - * \brief pixSelectComponentBySize() - * - * \param[in] pixs 1 bpp - * \param[in] rankorder in decreasing size: 0 for largest. - * \param[in] type L_SELECT_BY_WIDTH, L_SELECT_BY_HEIGHT, - * L_SELECT_BY_MAX_DIMENSION, - * L_SELECT_BY_AREA, L_SELECT_BY_PERIMETER - * \param[in] connectivity 4 or 8 - * \param[out] pbox [optional] location of returned component - * \return pix of rank order connected component, or NULL on error. - * - *- * Notes: - * (1) This selects the Nth largest connected component, based on - * the selection type and connectivity. - * (2) Note that %rankorder is an integer. Use %rankorder = 0 for - * the largest component and %rankorder = -1 for the smallest. - * If %rankorder >= number of components, select the smallest. - */ -PIX * -pixSelectComponentBySize(PIX *pixs, - l_int32 rankorder, - l_int32 type, - l_int32 connectivity, - BOX **pbox) -{ -l_int32 n, empty, sorttype, index; -BOXA *boxa1; -NUMA *naindex; -PIX *pixd; -PIXA *pixa1, *pixa2; - - PROCNAME("pixSelectComponentBySize"); - - if (pbox) *pbox = NULL; - if (!pixs || pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (type == L_SELECT_BY_WIDTH) - sorttype = L_SORT_BY_WIDTH; - else if (type == L_SELECT_BY_HEIGHT) - sorttype = L_SORT_BY_HEIGHT; - else if (type == L_SELECT_BY_MAX_DIMENSION) - sorttype = L_SORT_BY_MAX_DIMENSION; - else if (type == L_SELECT_BY_AREA) - sorttype = L_SORT_BY_AREA; - else if (type == L_SELECT_BY_PERIMETER) - sorttype = L_SORT_BY_PERIMETER; - else - return (PIX *)ERROR_PTR("invalid selection type", procName, NULL); - if (connectivity != 4 && connectivity != 8) - return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); - pixZero(pixs, &empty); - if (empty) - return (PIX *)ERROR_PTR("no foreground pixels", procName, NULL); - - boxa1 = pixConnComp(pixs, &pixa1, connectivity); - n = boxaGetCount(boxa1); - if (rankorder < 0 || rankorder >= n) - rankorder = n - 1; /* smallest */ - pixa2 = pixaSort(pixa1, sorttype, L_SORT_DECREASING, &naindex, L_CLONE); - pixd = pixaGetPix(pixa2, rankorder, L_COPY); - if (pbox) { - numaGetIValue(naindex, rankorder, &index); - *pbox = boxaGetBox(boxa1, index, L_COPY); - } - - numaDestroy(&naindex); - boxaDestroy(&boxa1); - pixaDestroy(&pixa1); - pixaDestroy(&pixa2); - return pixd; -} - - -/*! - * \brief pixFilterComponentBySize() - * - * \param[in] pixs 1 bpp - * \param[in] rankorder in decreasing size: 0 for largest. - * \param[in] type L_SELECT_BY_WIDTH, L_SELECT_BY_HEIGHT, - * L_SELECT_BY_MAX_DIMENSION, - * L_SELECT_BY_AREA, L_SELECT_BY_PERIMETER - * \param[in] connectivity 4 or 8 - * \param[out] pbox [optional] location of returned component - * \return pix with all other components removed, or NULL on error. - * - *- * Notes: - * (1) See notes in pixSelectComponentBySize(). - * (2) This returns a copy of %pixs, with all components removed - * except for the selected one. - */ -PIX * -pixFilterComponentBySize(PIX *pixs, - l_int32 rankorder, - l_int32 type, - l_int32 connectivity, - BOX **pbox) -{ -l_int32 x, y, w, h; -BOX *box; -PIX *pix1, *pix2; - - PROCNAME("pixFilterComponentBySize"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - - pix1 = pixSelectComponentBySize(pixs, rankorder, type, connectivity, &box); - if (!pix1) { - boxDestroy(&box); - return (PIX *)ERROR_PTR("pix1 not made", procName, NULL); - } - - /* Put the selected component in a new pix at the same - * location as it had in %pixs */ - boxGetGeometry(box, &x, &y, &w, &h); - pix2 = pixCreateTemplate(pixs); - pixRasterop(pix2, x, y, w, h, PIX_SRC, pix1, 0, 0); - if (pbox) - *pbox = box; - else - boxDestroy(&box); - pixDestroy(&pix1); - return pix2; -} - - -/*---------------------------------------------------------------------* - * Make special masks * - *---------------------------------------------------------------------*/ -/*! - * \brief pixMakeSymmetricMask() - * - * \param[in] w, h dimensions of output 1 bpp pix - * \param[in] hf horizontal fraction of half-width - * \param[in] vf vertical fraction of half-height - * \param[in] type L_USE_INNER, L_USE_OUTER - * \return pixd 1 bpp, or NULL on error. - * - *- */ -PIX * -pixConvert1To2Cmap(PIX *pixs) -{ -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixConvert1To2Cmap"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); - - if ((pixd = pixConvert1To2(NULL, pixs, 0, 1)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - cmap = pixcmapCreate(2); - pixcmapAddColor(cmap, 255, 255, 255); - pixcmapAddColor(cmap, 0, 0, 0); - pixSetColormap(pixd, cmap); - pixCopyInputFormat(pixd, pixs); - - return pixd; -} - - -/*! - * \brief pixConvert1To2() - * - * \param[in] pixd [optional] 2 bpp, can be null - * \param[in] pixs 1 bpp - * \param[in] val0 2 bit value to be used for 0s in pixs - * \param[in] val1 2 bit value to be used for 1s in pixs - * \return pixd 2 bpp - * - *- * Notes: - * (1) This is a convenience function for generating masks with - * horizontal and vertical reflection symmetry, over either - * the inner or outer parts of an image. - * (2) Using L_USE_INNER to generate a mask over the inner part - * of the image, the mask is a solid rectangle, and the fractions - * describe the distance between the boundary of the image and - * the rectangle boundary. For example, with hf == vf == 0.0, - * the mask covers the full image. - * (3) Using L_USE_OUTER to generate a mask over an outer frame - * of the image, the mask touches the boundary of the image, - * and the fractions describe the location of the inner - * boundary of the frame. For example, with hf == vf == 1.0, - * the inner boundary is at the center of the image, so the - * mask covers the full image. - * (4) More examples: - * * mask covering the inner 70%: hf = vf = 0.3, type = L_USE_INNER - * * frame covering the outer 30%: hf = vf = 0.3, type = L_USE_OUTER - *- */ -PIX * -pixMakeSymmetricMask(l_int32 w, - l_int32 h, - l_float32 hf, - l_float32 vf, - l_int32 type) -{ - PROCNAME("pixMakeSymmetricMask"); - - if (w <= 0 || h <= 0) - return (PIX *)ERROR_PTR("mask size 0", procName, NULL); - if (hf < 0.0 || hf > 1.0) - return (PIX *)ERROR_PTR("invalid horiz fractions", procName, NULL); - if (vf < 0.0 || vf > 1.0) - return (PIX *)ERROR_PTR("invalid vert fractions", procName, NULL); - - if (type == L_USE_INNER) - return pixMakeFrameMask(w, h, hf, 1.0, vf, 1.0); - else if (type == L_USE_OUTER) - return pixMakeFrameMask(w, h, 0.0, hf, 0.0, vf); - else - return (PIX *)ERROR_PTR("invalid type", procName, NULL); -} - - -/*! - * \brief pixMakeFrameMask() - * - * \param[in] w, h dimensions of output 1 bpp pix - * \param[in] hf1 horizontal fraction of half-width at outer frame bdry - * \param[in] hf2 horizontal fraction of half-width at inner frame bdry - * \param[in] vf1 vertical fraction of half-width at outer frame bdry - * \param[in] vf2 vertical fraction of half-width at inner frame bdry - * \return pixd 1 bpp, or NULL on error. - * - *- * Notes: - * (1) This makes an arbitrary 1-component mask with a centered fg - * frame, which can have both an inner and an outer boundary. - * All input fractional distances are measured from the image - * border to the frame boundary, in units of the image half-width - * for hf1 and hf2 and the image half-height for vf1 and vf2. - * The distances to the outer frame boundary are given by hf1 - * and vf1; to the inner frame boundary, by hf2 and vf2. - * Input fractions are thus in [0.0 ... 1.0], with hf1 <= hf2 - * and vf1 <= vf2. Horizontal and vertical frame widths are - * thus independently specified. - * (2) Special cases: - * * full fg mask: hf1 = vf1 = 0.0, hf2 = vf2 = 1.0. - * * empty fg (zero width) mask: set hf1 = hf2 and vf1 = vf2. - * * fg rectangle with no hole: set hf2 = vf2 = 1.0. - * * frame touching outer boundary: set hf1 = vf1 = 0.0. - * (3) The vertical thickness of the horizontal mask parts - * is 0.5 * (vf2 - vf1) * h. The horizontal thickness of the - * vertical mask parts is 0.5 * (hf2 - hf1) * w. - *- */ -PIX * -pixMakeFrameMask(l_int32 w, - l_int32 h, - l_float32 hf1, - l_float32 hf2, - l_float32 vf1, - l_float32 vf2) -{ -l_int32 h1, h2, v1, v2; -PIX *pixd; - - PROCNAME("pixMakeFrameMask"); - - if (w <= 0 || h <= 0) - return (PIX *)ERROR_PTR("mask size 0", procName, NULL); - if (hf1 < 0.0 || hf1 > 1.0 || hf2 < 0.0 || hf2 > 1.0) - return (PIX *)ERROR_PTR("invalid horiz fractions", procName, NULL); - if (vf1 < 0.0 || vf1 > 1.0 || vf2 < 0.0 || vf2 > 1.0) - return (PIX *)ERROR_PTR("invalid vert fractions", procName, NULL); - if (hf1 > hf2 || vf1 > vf2) - return (PIX *)ERROR_PTR("invalid relative sizes", procName, NULL); - - pixd = pixCreate(w, h, 1); - - /* Special cases */ - if (hf1 == 0.0 && vf1 == 0.0 && hf2 == 1.0 && vf2 == 1.0) { /* full */ - pixSetAll(pixd); - return pixd; - } - if (hf1 == hf2 && vf1 == vf2) { /* empty */ - return pixd; - } - - /* General case */ - h1 = 0.5 * hf1 * w; - h2 = 0.5 * hf2 * w; - v1 = 0.5 * vf1 * h; - v2 = 0.5 * vf2 * h; - pixRasterop(pixd, h1, v1, w - 2 * h1, h - 2 * v1, PIX_SET, NULL, 0, 0); - if (hf2 < 1.0 && vf2 < 1.0) - pixRasterop(pixd, h2, v2, w - 2 * h2, h - 2 * v2, PIX_CLR, NULL, 0, 0); - return pixd; -} - - -/*---------------------------------------------------------------------* - * Generate a covering of rectangles over connected components * - *---------------------------------------------------------------------*/ -/*! - * \brief pixMakeCoveringOfRectangles() - * - * \param[in] pixs 1 bpp - * \param[in] maxiters max iterations: use 0 to iterate to completion - * \return pixd, or NULL on error - * - *- * Notes: - * (1) This iteratively finds the bounding boxes of the connected - * components and generates a mask from them. Two iterations - * should suffice for most situations. - * (2) Returns an empty pix if %pixs is empty. - * (3) If there are many small components in proximity, it may - * be useful to merge them with a morphological closing before - * calling this one. - *- */ -PIX * -pixMakeCoveringOfRectangles(PIX *pixs, - l_int32 maxiters) -{ -l_int32 empty, same, niters; -BOXA *boxa; -PIX *pix1, *pix2; - - PROCNAME("pixMakeCoveringOfRectangles"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (maxiters < 0) - return (PIX *)ERROR_PTR("maxiters must be >= 0", procName, NULL); - if (maxiters == 0) maxiters = 50; /* ridiculously large number */ - - pixZero(pixs, &empty); - pix1 = pixCreateTemplate(pixs); - if (empty) return pix1; - - /* Do first iteration */ - boxa = pixConnCompBB(pixs, 8); - pixMaskBoxa(pix1, pix1, boxa, L_SET_PIXELS); - boxaDestroy(&boxa); - if (maxiters == 1) return pix1; - - niters = 1; - while (niters < maxiters) { /* continue to add pixels to pix1 */ - niters++; - boxa = pixConnCompBB(pix1, 8); - pix2 = pixCopy(NULL, pix1); - pixMaskBoxa(pix1, pix1, boxa, L_SET_PIXELS); - boxaDestroy(&boxa); - pixEqual(pix1, pix2, &same); - pixDestroy(&pix2); - if (same) { - L_INFO("%d iterations\n", procName, niters - 1); - return pix1; - } - } - L_INFO("maxiters = %d reached\n", procName, niters); - return pix1; -} - - -/*---------------------------------------------------------------------* - * Fraction of Fg pixels under a mask * - *---------------------------------------------------------------------*/ -/*! - * \brief pixFractionFgInMask() - * - * \param[in] pix1 1 bpp - * \param[in] pix2 1 bpp - * \param[out] pfract fraction of fg pixels in 1 that are - * aligned with the fg of 2 - * \return 0 if OK, 1 on error. - * - *- * Notes: - * (1) This gives the fraction of fg pixels in pix1 that are in - * the intersection (i.e., under the fg) of pix2: - * |1 & 2|/|1|, where |...| means the number of fg pixels. - * Note that this is different from the situation where - * pix1 and pix2 are reversed. - * (2) Both pix1 and pix2 are registered to the UL corners. A warning - * is issued if pix1 and pix2 have different sizes. - * (3) This can also be used to find the fraction of fg pixels in pix1 - * that are NOT under the fg of pix2: 1.0 - |1 & 2|/|1| - * (4) If pix1 or pix2 are empty, this returns %fract = 0.0. - * (5) For example, pix2 could be a frame around the outside of the - * image, made from pixMakeFrameMask(). - *- */ -l_ok -pixFractionFgInMask(PIX *pix1, - PIX *pix2, - l_float32 *pfract) -{ -l_int32 w1, h1, w2, h2, empty, count1, count3; -PIX *pix3; - - PROCNAME("pixFractionFgInMask"); - - if (!pfract) - return ERROR_INT("&fract not defined", procName, 1); - *pfract = 0.0; - if (!pix1 || pixGetDepth(pix1) != 1) - return ERROR_INT("pix1 not defined or not 1 bpp", procName, 1); - if (!pix2 || pixGetDepth(pix2) != 1) - return ERROR_INT("pix2 not defined or not 1 bpp", procName, 1); - - pixGetDimensions(pix1, &w1, &h1, NULL); - pixGetDimensions(pix2, &w2, &h2, NULL); - if (w1 != w2 || h1 != h2) { - L_INFO("sizes unequal: (w1,w2) = (%d,%d), (h1,h2) = (%d,%d)\n", - procName, w1, w2, h1, h2); - } - pixZero(pix1, &empty); - if (empty) return 0; - pixZero(pix2, &empty); - if (empty) return 0; - - pix3 = pixCopy(NULL, pix1); - pixAnd(pix3, pix3, pix2); - pixCountPixels(pix1, &count1, NULL); /* |1| */ - pixCountPixels(pix3, &count3, NULL); /* |1 & 2| */ - *pfract = (l_float32)count3 / (l_float32)count1; - pixDestroy(&pix3); - return 0; -} - - -/*---------------------------------------------------------------------* - * Clip to Foreground * - *---------------------------------------------------------------------*/ -/*! - * \brief pixClipToForeground() - * - * \param[in] pixs 1 bpp - * \param[out] ppixd [optional] clipped pix returned - * \param[out] pbox [optional] bounding box - * \return 0 if OK; 1 on error or if there are no fg pixels - * - *- * Notes: - * (1) At least one of {&pixd, &box} must be specified. - * (2) If there are no fg pixels, the returned ptrs are null. - *- */ -l_ok -pixClipToForeground(PIX *pixs, - PIX **ppixd, - BOX **pbox) -{ -l_int32 w, h, wpl, nfullwords, extra, i, j; -l_int32 minx, miny, maxx, maxy; -l_uint32 result, mask; -l_uint32 *data, *line; -BOX *box; - - PROCNAME("pixClipToForeground"); - - if (ppixd) *ppixd = NULL; - if (pbox) *pbox = NULL; - if (!ppixd && !pbox) - return ERROR_INT("no output requested", procName, 1); - if (!pixs || (pixGetDepth(pixs) != 1)) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - - pixGetDimensions(pixs, &w, &h, NULL); - nfullwords = w / 32; - extra = w & 31; - mask = ~rmask32[32 - extra]; - wpl = pixGetWpl(pixs); - data = pixGetData(pixs); - - result = 0; - for (i = 0, miny = 0; i < h; i++, miny++) { - line = data + i * wpl; - for (j = 0; j < nfullwords; j++) - result |= line[j]; - if (extra) - result |= (line[j] & mask); - if (result) - break; - } - if (miny == h) /* no ON pixels */ - return 1; - - result = 0; - for (i = h - 1, maxy = h - 1; i >= 0; i--, maxy--) { - line = data + i * wpl; - for (j = 0; j < nfullwords; j++) - result |= line[j]; - if (extra) - result |= (line[j] & mask); - if (result) - break; - } - - minx = 0; - for (j = 0, minx = 0; j < w; j++, minx++) { - for (i = 0; i < h; i++) { - line = data + i * wpl; - if (GET_DATA_BIT(line, j)) - goto minx_found; - } - } - -minx_found: - for (j = w - 1, maxx = w - 1; j >= 0; j--, maxx--) { - for (i = 0; i < h; i++) { - line = data + i * wpl; - if (GET_DATA_BIT(line, j)) - goto maxx_found; - } - } - -maxx_found: - box = boxCreate(minx, miny, maxx - minx + 1, maxy - miny + 1); - - if (ppixd) - *ppixd = pixClipRectangle(pixs, box, NULL); - if (pbox) - *pbox = box; - else - boxDestroy(&box); - - return 0; -} - - -/*! - * \brief pixTestClipToForeground() - * - * \param[in] pixs 1 bpp - * \param[out] pcanclip 1 if fg does not extend to all four edges - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) This is a lightweight test to determine if a 1 bpp image - * can be further cropped without loss of fg pixels. - * If it cannot, canclip is set to 0. - * (2) It does not test for the existence of any fg pixels. - * If there are no fg pixels, it will return %canclip = 1. - * Check the output of the subsequent call to pixClipToForeground(). - *- */ -l_ok -pixTestClipToForeground(PIX *pixs, - l_int32 *pcanclip) -{ -l_int32 i, j, w, h, wpl, found; -l_uint32 *data, *line; - - PROCNAME("pixTestClipToForeground"); - - if (!pcanclip) - return ERROR_INT("&canclip not defined", procName, 1); - *pcanclip = 0; - if (!pixs || (pixGetDepth(pixs) != 1)) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - - /* Check top and bottom raster lines */ - pixGetDimensions(pixs, &w, &h, NULL); - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - found = FALSE; - for (j = 0; found == FALSE && j < w; j++) - found = GET_DATA_BIT(data, j); - if (!found) { - *pcanclip = 1; - return 0; - } - - line = data + (h - 1) * wpl; - found = FALSE; - for (j = 0; found == FALSE && j < w; j++) - found = GET_DATA_BIT(data, j); - if (!found) { - *pcanclip = 1; - return 0; - } - - /* Check left and right edges */ - found = FALSE; - for (i = 0, line = data; found == FALSE && i < h; line += wpl, i++) - found = GET_DATA_BIT(line, 0); - if (!found) { - *pcanclip = 1; - return 0; - } - - found = FALSE; - for (i = 0, line = data; found == FALSE && i < h; line += wpl, i++) - found = GET_DATA_BIT(line, w - 1); - if (!found) - *pcanclip = 1; - - return 0; /* fg pixels found on all edges */ -} - - -/*! - * \brief pixClipBoxToForeground() - * - * \param[in] pixs 1 bpp - * \param[in] boxs [optional] use full image if null - * \param[out] ppixd [optional] clipped pix returned - * \param[out] pboxd [optional] bounding box - * \return 0 if OK; 1 on error or if there are no fg pixels - * - *- * Notes: - * (1) At least one of {&pixd, &boxd} must be specified. - * (2) If there are no fg pixels, the returned ptrs are null. - * (3) Do not use &pixs for the 3rd arg or &boxs for the 4th arg; - * this will leak memory. - *- */ -l_ok -pixClipBoxToForeground(PIX *pixs, - BOX *boxs, - PIX **ppixd, - BOX **pboxd) -{ -l_int32 w, h, bx, by, bw, bh, cbw, cbh, left, right, top, bottom; -BOX *boxt, *boxd; - - PROCNAME("pixClipBoxToForeground"); - - if (ppixd) *ppixd = NULL; - if (pboxd) *pboxd = NULL; - if (!ppixd && !pboxd) - return ERROR_INT("no output requested", procName, 1); - if (!pixs || (pixGetDepth(pixs) != 1)) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - - if (!boxs) - return pixClipToForeground(pixs, ppixd, pboxd); - - pixGetDimensions(pixs, &w, &h, NULL); - boxGetGeometry(boxs, &bx, &by, &bw, &bh); - cbw = L_MIN(bw, w - bx); - cbh = L_MIN(bh, h - by); - if (cbw < 0 || cbh < 0) - return ERROR_INT("box not within image", procName, 1); - boxt = boxCreate(bx, by, cbw, cbh); - - if (pixScanForForeground(pixs, boxt, L_FROM_LEFT, &left)) { - boxDestroy(&boxt); - return 1; - } - pixScanForForeground(pixs, boxt, L_FROM_RIGHT, &right); - pixScanForForeground(pixs, boxt, L_FROM_TOP, &top); - pixScanForForeground(pixs, boxt, L_FROM_BOT, &bottom); - - boxd = boxCreate(left, top, right - left + 1, bottom - top + 1); - if (ppixd) - *ppixd = pixClipRectangle(pixs, boxd, NULL); - if (pboxd) - *pboxd = boxd; - else - boxDestroy(&boxd); - - boxDestroy(&boxt); - return 0; -} - - -/*! - * \brief pixScanForForeground() - * - * \param[in] pixs 1 bpp - * \param[in] box [optional] within which the search is conducted - * \param[in] scanflag direction of scan; e.g., L_FROM_LEFT - * \param[out] ploc location in scan direction of first black pixel - * \return 0 if OK; 1 on error or if no fg pixels are found - * - *- * Notes: - * (1) If there are no fg pixels, the position is set to 0. - * Caller must check the return value! - * (2) Use %box == NULL to scan from edge of pixs - *- */ -l_ok -pixScanForForeground(PIX *pixs, - BOX *box, - l_int32 scanflag, - l_int32 *ploc) -{ -l_int32 bx, by, bw, bh, x, xstart, xend, y, ystart, yend, wpl; -l_uint32 *data, *line; -BOX *boxt; - - PROCNAME("pixScanForForeground"); - - if (!ploc) - return ERROR_INT("&loc not defined", procName, 1); - *ploc = 0; - if (!pixs || (pixGetDepth(pixs) != 1)) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - - /* Clip box to pixs if it exists */ - pixGetDimensions(pixs, &bw, &bh, NULL); - if (box) { - if ((boxt = boxClipToRectangle(box, bw, bh)) == NULL) - return ERROR_INT("invalid box", procName, 1); - boxGetGeometry(boxt, &bx, &by, &bw, &bh); - boxDestroy(&boxt); - } else { - bx = by = 0; - } - xstart = bx; - ystart = by; - xend = bx + bw - 1; - yend = by + bh - 1; - - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - if (scanflag == L_FROM_LEFT) { - for (x = xstart; x <= xend; x++) { - for (y = ystart; y <= yend; y++) { - line = data + y * wpl; - if (GET_DATA_BIT(line, x)) { - *ploc = x; - return 0; - } - } - } - } else if (scanflag == L_FROM_RIGHT) { - for (x = xend; x >= xstart; x--) { - for (y = ystart; y <= yend; y++) { - line = data + y * wpl; - if (GET_DATA_BIT(line, x)) { - *ploc = x; - return 0; - } - } - } - } else if (scanflag == L_FROM_TOP) { - for (y = ystart; y <= yend; y++) { - line = data + y * wpl; - for (x = xstart; x <= xend; x++) { - if (GET_DATA_BIT(line, x)) { - *ploc = y; - return 0; - } - } - } - } else if (scanflag == L_FROM_BOT) { - for (y = yend; y >= ystart; y--) { - line = data + y * wpl; - for (x = xstart; x <= xend; x++) { - if (GET_DATA_BIT(line, x)) { - *ploc = y; - return 0; - } - } - } - } else { - return ERROR_INT("invalid scanflag", procName, 1); - } - - return 1; /* no fg found */ -} - - -/*! - * \brief pixClipBoxToEdges() - * - * \param[in] pixs 1 bpp - * \param[in] boxs [optional] ; use full image if null - * \param[in] lowthresh threshold to choose clipping location - * \param[in] highthresh threshold required to find an edge - * \param[in] maxwidth max allowed width between low and high thresh locs - * \param[in] factor sampling factor along pixel counting direction - * \param[out] ppixd [optional] clipped pix returned - * \param[out] pboxd [optional] bounding box - * \return 0 if OK; 1 on error or if a fg edge is not found from - * all four sides. - * - *- * Notes: - * (1) At least one of {&pixd, &boxd} must be specified. - * (2) If there are no fg pixels, the returned ptrs are null. - * (3) This function attempts to locate rectangular "image" regions - * of high-density fg pixels, that have well-defined edges - * on the four sides. - * (4) Edges are searched for on each side, iterating in order - * from left, right, top and bottom. As each new edge is - * found, the search box is resized to use that location. - * Once an edge is found, it is held. If no more edges - * are found in one iteration, the search fails. - * (5) See pixScanForEdge() for usage of the thresholds and %maxwidth. - * (6) The thresholds must be at least 1, and the low threshold - * cannot be larger than the high threshold. - * (7) If the low and high thresholds are both 1, this is equivalent - * to pixClipBoxToForeground(). - *- */ -l_ok -pixClipBoxToEdges(PIX *pixs, - BOX *boxs, - l_int32 lowthresh, - l_int32 highthresh, - l_int32 maxwidth, - l_int32 factor, - PIX **ppixd, - BOX **pboxd) -{ -l_int32 w, h, bx, by, bw, bh, cbw, cbh, left, right, top, bottom; -l_int32 lfound, rfound, tfound, bfound, change; -BOX *boxt, *boxd; - - PROCNAME("pixClipBoxToEdges"); - - if (ppixd) *ppixd = NULL; - if (pboxd) *pboxd = NULL; - if (!ppixd && !pboxd) - return ERROR_INT("no output requested", procName, 1); - if (!pixs || (pixGetDepth(pixs) != 1)) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (lowthresh < 1 || highthresh < 1 || - lowthresh > highthresh || maxwidth < 1) - return ERROR_INT("invalid thresholds", procName, 1); - factor = L_MIN(1, factor); - - if (lowthresh == 1 && highthresh == 1) - return pixClipBoxToForeground(pixs, boxs, ppixd, pboxd); - - pixGetDimensions(pixs, &w, &h, NULL); - if (boxs) { - boxGetGeometry(boxs, &bx, &by, &bw, &bh); - cbw = L_MIN(bw, w - bx); - cbh = L_MIN(bh, h - by); - if (cbw < 0 || cbh < 0) - return ERROR_INT("box not within image", procName, 1); - boxt = boxCreate(bx, by, cbw, cbh); - } else { - boxt = boxCreate(0, 0, w, h); - } - - lfound = rfound = tfound = bfound = 0; - while (!lfound || !rfound || !tfound || !bfound) { - change = 0; - if (!lfound) { - if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth, - factor, L_FROM_LEFT, &left)) { - lfound = 1; - change = 1; - boxRelocateOneSide(boxt, boxt, left, L_FROM_LEFT); - } - } - if (!rfound) { - if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth, - factor, L_FROM_RIGHT, &right)) { - rfound = 1; - change = 1; - boxRelocateOneSide(boxt, boxt, right, L_FROM_RIGHT); - } - } - if (!tfound) { - if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth, - factor, L_FROM_TOP, &top)) { - tfound = 1; - change = 1; - boxRelocateOneSide(boxt, boxt, top, L_FROM_TOP); - } - } - if (!bfound) { - if (!pixScanForEdge(pixs, boxt, lowthresh, highthresh, maxwidth, - factor, L_FROM_BOT, &bottom)) { - bfound = 1; - change = 1; - boxRelocateOneSide(boxt, boxt, bottom, L_FROM_BOT); - } - } - -#if DEBUG_EDGES - lept_stderr("iter: %d %d %d %d\n", lfound, rfound, tfound, bfound); -#endif /* DEBUG_EDGES */ - - if (change == 0) break; - } - boxDestroy(&boxt); - - if (change == 0) - return ERROR_INT("not all edges found", procName, 1); - - boxd = boxCreate(left, top, right - left + 1, bottom - top + 1); - if (ppixd) - *ppixd = pixClipRectangle(pixs, boxd, NULL); - if (pboxd) - *pboxd = boxd; - else - boxDestroy(&boxd); - - return 0; -} - - -/*! - * \brief pixScanForEdge() - * - * \param[in] pixs 1 bpp - * \param[in] box [optional] within which the search is conducted - * \param[in] lowthresh threshold to choose clipping location - * \param[in] highthresh threshold required to find an edge - * \param[in] maxwidth max allowed width between low and high thresh locs - * \param[in] factor sampling factor along pixel counting direction - * \param[in] scanflag direction of scan; e.g., L_FROM_LEFT - * \param[out] ploc location in scan direction of first black pixel - * \return 0 if OK; 1 on error or if the edge is not found - * - *- * Notes: - * (1) If there are no fg pixels, the position is set to 0. - * Caller must check the return value! - * (2) Use %box == NULL to scan from edge of pixs - * (3) As the scan progresses, the location where the sum of - * pixels equals or excees %lowthresh is noted (loc). The - * scan is stopped when the sum of pixels equals or exceeds - * %highthresh. If the scan distance between loc and that - * point does not exceed %maxwidth, an edge is found and - * its position is taken to be loc. %maxwidth implicitly - * sets a minimum on the required gradient of the edge. - * (4) The thresholds must be at least 1, and the low threshold - * cannot be larger than the high threshold. - *- */ -l_ok -pixScanForEdge(PIX *pixs, - BOX *box, - l_int32 lowthresh, - l_int32 highthresh, - l_int32 maxwidth, - l_int32 factor, - l_int32 scanflag, - l_int32 *ploc) -{ -l_int32 bx, by, bw, bh, foundmin, loc, sum, wpl; -l_int32 x, xstart, xend, y, ystart, yend; -l_uint32 *data, *line; -BOX *boxt; - - PROCNAME("pixScanForEdge"); - - if (!ploc) - return ERROR_INT("&ploc not defined", procName, 1); - *ploc = 0; - if (!pixs || (pixGetDepth(pixs) != 1)) - return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); - if (lowthresh < 1 || highthresh < 1 || - lowthresh > highthresh || maxwidth < 1) - return ERROR_INT("invalid thresholds", procName, 1); - factor = L_MIN(1, factor); - - /* Clip box to pixs if it exists */ - pixGetDimensions(pixs, &bw, &bh, NULL); - if (box) { - if ((boxt = boxClipToRectangle(box, bw, bh)) == NULL) - return ERROR_INT("invalid box", procName, 1); - boxGetGeometry(boxt, &bx, &by, &bw, &bh); - boxDestroy(&boxt); - } else { - bx = by = 0; - } - xstart = bx; - ystart = by; - xend = bx + bw - 1; - yend = by + bh - 1; - - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - foundmin = 0; - if (scanflag == L_FROM_LEFT) { - for (x = xstart; x <= xend; x++) { - sum = 0; - for (y = ystart; y <= yend; y += factor) { - line = data + y * wpl; - if (GET_DATA_BIT(line, x)) - sum++; - } - if (!foundmin && sum < lowthresh) - continue; - if (!foundmin) { /* save the loc of the beginning of the edge */ - foundmin = 1; - loc = x; - } - if (sum >= highthresh) { -#if DEBUG_EDGES - lept_stderr("Left: x = %d, loc = %d\n", x, loc); -#endif /* DEBUG_EDGES */ - if (x - loc < maxwidth) { - *ploc = loc; - return 0; - } else { - return 1; - } - } - } - } else if (scanflag == L_FROM_RIGHT) { - for (x = xend; x >= xstart; x--) { - sum = 0; - for (y = ystart; y <= yend; y += factor) { - line = data + y * wpl; - if (GET_DATA_BIT(line, x)) - sum++; - } - if (!foundmin && sum < lowthresh) - continue; - if (!foundmin) { - foundmin = 1; - loc = x; - } - if (sum >= highthresh) { -#if DEBUG_EDGES - lept_stderr("Right: x = %d, loc = %d\n", x, loc); -#endif /* DEBUG_EDGES */ - if (loc - x < maxwidth) { - *ploc = loc; - return 0; - } else { - return 1; - } - } - } - } else if (scanflag == L_FROM_TOP) { - for (y = ystart; y <= yend; y++) { - sum = 0; - line = data + y * wpl; - for (x = xstart; x <= xend; x += factor) { - if (GET_DATA_BIT(line, x)) - sum++; - } - if (!foundmin && sum < lowthresh) - continue; - if (!foundmin) { - foundmin = 1; - loc = y; - } - if (sum >= highthresh) { -#if DEBUG_EDGES - lept_stderr("Top: y = %d, loc = %d\n", y, loc); -#endif /* DEBUG_EDGES */ - if (y - loc < maxwidth) { - *ploc = loc; - return 0; - } else { - return 1; - } - } - } - } else if (scanflag == L_FROM_BOT) { - for (y = yend; y >= ystart; y--) { - sum = 0; - line = data + y * wpl; - for (x = xstart; x <= xend; x += factor) { - if (GET_DATA_BIT(line, x)) - sum++; - } - if (!foundmin && sum < lowthresh) - continue; - if (!foundmin) { - foundmin = 1; - loc = y; - } - if (sum >= highthresh) { -#if DEBUG_EDGES - lept_stderr("Bottom: y = %d, loc = %d\n", y, loc); -#endif /* DEBUG_EDGES */ - if (loc - y < maxwidth) { - *ploc = loc; - return 0; - } else { - return 1; - } - } - } - } else { - return ERROR_INT("invalid scanflag", procName, 1); - } - - return 1; /* edge not found */ -} - - -/*---------------------------------------------------------------------* - * Extract pixel averages and reversals along lines * - *---------------------------------------------------------------------*/ -/*! - * \brief pixExtractOnLine() - * - * \param[in] pixs 1 bpp or 8 bpp; no colormap - * \param[in] x1, y1 one end point for line - * \param[in] x2, y2 another end pt for line - * \param[in] factor sampling; >= 1 - * \return na of pixel values along line, or NULL on error. - * - *- * Notes: - * (1) Input end points are clipped to the pix. - * (2) If the line is either horizontal, or closer to horizontal - * than to vertical, the points will be extracted from left - * to right in the pix. Likewise, if the line is vertical, - * or closer to vertical than to horizontal, the points will - * be extracted from top to bottom. - * (3) Can be used with numaCountReverals(), for example, to - * characterize the intensity smoothness along a line. - *- */ -NUMA * -pixExtractOnLine(PIX *pixs, - l_int32 x1, - l_int32 y1, - l_int32 x2, - l_int32 y2, - l_int32 factor) -{ -l_int32 i, w, h, d, xmin, ymin, xmax, ymax, npts, direction; -l_uint32 val; -l_float32 x, y; -l_float64 slope; -NUMA *na; -PTA *pta; - - PROCNAME("pixExtractOnLine"); - - if (!pixs) - return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 1 && d != 8) - return (NUMA *)ERROR_PTR("d not 1 or 8 bpp", procName, NULL); - if (pixGetColormap(pixs)) - return (NUMA *)ERROR_PTR("pixs has a colormap", procName, NULL); - if (factor < 1) { - L_WARNING("factor must be >= 1; setting to 1\n", procName); - factor = 1; - } - - /* Clip line to the image */ - x1 = L_MAX(0, L_MIN(x1, w - 1)); - x2 = L_MAX(0, L_MIN(x2, w - 1)); - y1 = L_MAX(0, L_MIN(y1, h - 1)); - y2 = L_MAX(0, L_MIN(y2, h - 1)); - - if (x1 == x2 && y1 == y2) { - pixGetPixel(pixs, x1, y1, &val); - na = numaCreate(1); - numaAddNumber(na, val); - return na; - } - - if (y1 == y2) - direction = L_HORIZONTAL_LINE; - else if (x1 == x2) - direction = L_VERTICAL_LINE; - else - direction = L_OBLIQUE_LINE; - - na = numaCreate(0); - if (direction == L_HORIZONTAL_LINE) { /* plot against x */ - xmin = L_MIN(x1, x2); - xmax = L_MAX(x1, x2); - numaSetParameters(na, xmin, factor); - for (i = xmin; i <= xmax; i += factor) { - pixGetPixel(pixs, i, y1, &val); - numaAddNumber(na, val); - } - } else if (direction == L_VERTICAL_LINE) { /* plot against y */ - ymin = L_MIN(y1, y2); - ymax = L_MAX(y1, y2); - numaSetParameters(na, ymin, factor); - for (i = ymin; i <= ymax; i += factor) { - pixGetPixel(pixs, x1, i, &val); - numaAddNumber(na, val); - } - } else { /* direction == L_OBLIQUE_LINE */ - slope = (l_float64)((y2 - y1) / (x2 - x1)); - if (L_ABS(slope) < 1.0) { /* quasi-horizontal */ - xmin = L_MIN(x1, x2); - xmax = L_MAX(x1, x2); - ymin = (xmin == x1) ? y1 : y2; /* pt that goes with xmin */ - ymax = (ymin == y1) ? y2 : y1; /* pt that goes with xmax */ - pta = generatePtaLine(xmin, ymin, xmax, ymax); - numaSetParameters(na, xmin, (l_float32)factor); - } else { /* quasi-vertical */ - ymin = L_MIN(y1, y2); - ymax = L_MAX(y1, y2); - xmin = (ymin == y1) ? x1 : x2; /* pt that goes with ymin */ - xmax = (xmin == x1) ? x2 : x1; /* pt that goes with ymax */ - pta = generatePtaLine(xmin, ymin, xmax, ymax); - numaSetParameters(na, ymin, (l_float32)factor); - } - npts = ptaGetCount(pta); - for (i = 0; i < npts; i += factor) { - ptaGetPt(pta, i, &x, &y); - pixGetPixel(pixs, (l_int32)x, (l_int32)y, &val); - numaAddNumber(na, val); - } - -#if 0 /* debugging */ - pixPlotAlongPta(pixs, pta, GPLOT_PNG, NULL); -#endif - - ptaDestroy(&pta); - } - - return na; -} - - -/*! - * \brief pixAverageOnLine() - * - * \param[in] pixs 1 bpp or 8 bpp; no colormap - * \param[in] x1, y1 starting pt for line - * \param[in] x2, y2 end pt for line - * \param[in] factor sampling; >= 1 - * \return average of pixel values along line, or NULL on error. - * - *- * Notes: - * (1) The line must be either horizontal or vertical, so either - * y1 == y2 (horizontal) or x1 == x2 (vertical). - * (2) If horizontal, x1 must be <= x2. - * If vertical, y1 must be <= y2. - * characterize the intensity smoothness along a line. - * (3) Input end points are clipped to the pix. - *- */ -l_float32 -pixAverageOnLine(PIX *pixs, - l_int32 x1, - l_int32 y1, - l_int32 x2, - l_int32 y2, - l_int32 factor) -{ -l_int32 i, j, w, h, d, direction, count, wpl; -l_uint32 *data, *line; -l_float32 sum; - - PROCNAME("pixAverageOnLine"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 1 && d != 8) - return ERROR_INT("d not 1 or 8 bpp", procName, 1); - if (pixGetColormap(pixs)) - return ERROR_INT("pixs has a colormap", procName, 1); - if (x1 > x2 || y1 > y2) - return ERROR_INT("x1 > x2 or y1 > y2", procName, 1); - - if (y1 == y2) { - x1 = L_MAX(0, x1); - x2 = L_MIN(w - 1, x2); - y1 = L_MAX(0, L_MIN(y1, h - 1)); - direction = L_HORIZONTAL_LINE; - } else if (x1 == x2) { - y1 = L_MAX(0, y1); - y2 = L_MIN(h - 1, y2); - x1 = L_MAX(0, L_MIN(x1, w - 1)); - direction = L_VERTICAL_LINE; - } else { - return ERROR_INT("line neither horiz nor vert", procName, 1); - } - - if (factor < 1) { - L_WARNING("factor must be >= 1; setting to 1\n", procName); - factor = 1; - } - - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - sum = 0; - count = 0; - if (direction == L_HORIZONTAL_LINE) { - line = data + y1 * wpl; - for (j = x1, count = 0; j <= x2; count++, j += factor) { - if (d == 1) - sum += GET_DATA_BIT(line, j); - else /* d == 8 */ - sum += GET_DATA_BYTE(line, j); - } - } else if (direction == L_VERTICAL_LINE) { - for (i = y1, count = 0; i <= y2; count++, i += factor) { - line = data + i * wpl; - if (d == 1) - sum += GET_DATA_BIT(line, x1); - else /* d == 8 */ - sum += GET_DATA_BYTE(line, x1); - } - } - - return sum / (l_float32)count; -} - - -/*! - * \brief pixAverageIntensityProfile() - * - * \param[in] pixs any depth; colormap OK - * \param[in] fract fraction of image width or height to be used - * \param[in] dir averaging direction: L_HORIZONTAL_LINE or - * L_VERTICAL_LINE - * \param[in] first, last span of rows or columns to measure - * \param[in] factor1 sampling along fast scan direction; >= 1 - * \param[in] factor2 sampling along slow scan direction; >= 1 - * \return na of reversal profile, or NULL on error. - * - *- * Notes: - * (1) If d != 1 bpp, colormaps are removed and the result - * is converted to 8 bpp. - * (2) If %dir == L_HORIZONTAL_LINE, the intensity is averaged - * along each horizontal raster line (sampled by %factor1), - * and the profile is the array of these averages in the - * vertical direction between %first and %last raster lines, - * and sampled by %factor2. - * (3) If %dir == L_VERTICAL_LINE, the intensity is averaged - * along each vertical line (sampled by %factor1), - * and the profile is the array of these averages in the - * horizontal direction between %first and %last columns, - * and sampled by %factor2. - * (4) The averages are measured over the central %fract of the image. - * Use %fract == 1.0 to average across the entire width or height. - *- */ -NUMA * -pixAverageIntensityProfile(PIX *pixs, - l_float32 fract, - l_int32 dir, - l_int32 first, - l_int32 last, - l_int32 factor1, - l_int32 factor2) -{ -l_int32 i, j, w, h, d, start, end; -l_float32 ave; -NUMA *nad; -PIX *pixr, *pixg; - - PROCNAME("pixAverageIntensityProfile"); - - if (!pixs) - return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); - if (fract < 0.0 || fract > 1.0) - return (NUMA *)ERROR_PTR("fract < 0.0 or > 1.0", procName, NULL); - if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE) - return (NUMA *)ERROR_PTR("invalid direction", procName, NULL); - if (first < 0) first = 0; - if (last < first) - return (NUMA *)ERROR_PTR("last must be >= first", procName, NULL); - if (factor1 < 1) { - L_WARNING("factor1 must be >= 1; setting to 1\n", procName); - factor1 = 1; - } - if (factor2 < 1) { - L_WARNING("factor2 must be >= 1; setting to 1\n", procName); - factor2 = 1; - } - - /* Use 1 or 8 bpp, without colormap */ - if (pixGetColormap(pixs)) - pixr = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - else - pixr = pixClone(pixs); - pixGetDimensions(pixr, &w, &h, &d); - if (d == 1) - pixg = pixClone(pixr); - else - pixg = pixConvertTo8(pixr, 0); - - nad = numaCreate(0); /* output: samples in slow scan direction */ - numaSetParameters(nad, 0, factor2); - if (dir == L_HORIZONTAL_LINE) { - start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)w); - end = w - start; - if (last > h - 1) { - L_WARNING("last > h - 1; clipping\n", procName); - last = h - 1; - } - for (i = first; i <= last; i += factor2) { - ave = pixAverageOnLine(pixg, start, i, end, i, factor1); - numaAddNumber(nad, ave); - } - } else if (dir == L_VERTICAL_LINE) { - start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)h); - end = h - start; - if (last > w - 1) { - L_WARNING("last > w - 1; clipping\n", procName); - last = w - 1; - } - for (j = first; j <= last; j += factor2) { - ave = pixAverageOnLine(pixg, j, start, j, end, factor1); - numaAddNumber(nad, ave); - } - } - - pixDestroy(&pixr); - pixDestroy(&pixg); - return nad; -} - - -/*! - * \brief pixReversalProfile() - * - * \param[in] pixs any depth; colormap OK - * \param[in] fract fraction of image width or height to be used - * \param[in] dir profile direction: L_HORIZONTAL_LINE or - * L_VERTICAL_LINE - * \param[in] first, last span of rows or columns to measure - * \param[in] minreversal minimum change in intensity to trigger a reversal - * \param[in] factor1 sampling along raster line (fast scan); >= 1 - * \param[in] factor2 sampling of raster lines (slow scan); >= 1 - * \return na of reversal profile, or NULL on error. - * - *- * Notes: - * (1) If d != 1 bpp, colormaps are removed and the result - * is converted to 8 bpp. - * (2) If %dir == L_HORIZONTAL_LINE, the the reversals are counted - * along each horizontal raster line (sampled by %factor1), - * and the profile is the array of these sums in the - * vertical direction between %first and %last raster lines, - * and sampled by %factor2. - * (3) If %dir == L_VERTICAL_LINE, the the reversals are counted - * along each vertical column (sampled by %factor1), - * and the profile is the array of these sums in the - * horizontal direction between %first and %last columns, - * and sampled by %factor2. - * (4) For each row or column, the reversals are summed over the - * central %fract of the image. Use %fract == 1.0 to sum - * across the entire width (of row) or height (of column). - * (5) %minreversal is the relative change in intensity that is - * required to resolve peaks and valleys. A typical number for - * locating text in 8 bpp might be 50. For 1 bpp, minreversal - * must be 1. - * (6) The reversal profile is simply the number of reversals - * in a row or column, vs the row or column index. - *- */ -NUMA * -pixReversalProfile(PIX *pixs, - l_float32 fract, - l_int32 dir, - l_int32 first, - l_int32 last, - l_int32 minreversal, - l_int32 factor1, - l_int32 factor2) -{ -l_int32 i, j, w, h, d, start, end, nr; -NUMA *naline, *nad; -PIX *pixr, *pixg; - - PROCNAME("pixReversalProfile"); - - if (!pixs) - return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); - if (fract < 0.0 || fract > 1.0) - return (NUMA *)ERROR_PTR("fract < 0.0 or > 1.0", procName, NULL); - if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE) - return (NUMA *)ERROR_PTR("invalid direction", procName, NULL); - if (first < 0) first = 0; - if (last < first) - return (NUMA *)ERROR_PTR("last must be >= first", procName, NULL); - if (factor1 < 1) { - L_WARNING("factor1 must be >= 1; setting to 1\n", procName); - factor1 = 1; - } - if (factor2 < 1) { - L_WARNING("factor2 must be >= 1; setting to 1\n", procName); - factor2 = 1; - } - - /* Use 1 or 8 bpp, without colormap */ - if (pixGetColormap(pixs)) - pixr = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - else - pixr = pixClone(pixs); - pixGetDimensions(pixr, &w, &h, &d); - if (d == 1) { - pixg = pixClone(pixr); - minreversal = 1; /* enforce this */ - } else { - pixg = pixConvertTo8(pixr, 0); - } - - nad = numaCreate(0); /* output: samples in slow scan direction */ - numaSetParameters(nad, 0, factor2); - if (dir == L_HORIZONTAL_LINE) { - start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)w); - end = w - start; - if (last > h - 1) { - L_WARNING("last > h - 1; clipping\n", procName); - last = h - 1; - } - for (i = first; i <= last; i += factor2) { - naline = pixExtractOnLine(pixg, start, i, end, i, factor1); - numaCountReversals(naline, minreversal, &nr, NULL); - numaAddNumber(nad, nr); - numaDestroy(&naline); - } - } else if (dir == L_VERTICAL_LINE) { - start = (l_int32)(0.5 * (1.0 - fract) * (l_float32)h); - end = h - start; - if (last > w - 1) { - L_WARNING("last > w - 1; clipping\n", procName); - last = w - 1; - } - for (j = first; j <= last; j += factor2) { - naline = pixExtractOnLine(pixg, j, start, j, end, factor1); - numaCountReversals(naline, minreversal, &nr, NULL); - numaAddNumber(nad, nr); - numaDestroy(&naline); - } - } - - pixDestroy(&pixr); - pixDestroy(&pixg); - return nad; -} - - -/*---------------------------------------------------------------------* - * Extract windowed variance along a line * - *---------------------------------------------------------------------*/ -/*! - * \brief pixWindowedVarianceOnLine() - * - * \param[in] pixs 8 bpp; no colormap - * \param[in] dir L_HORIZONTAL_LINE or L_VERTICAL_LINE - * \param[in] loc location of the constant coordinate for the line - * \param[in] c1, c2 end point coordinates for the line - * \param[in] size window size; must be > 1 - * \param[out] pnad windowed square root of variance - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) The returned variance array traverses the line starting - * from the smallest coordinate, min(c1,c2). - * (2) Line end points are clipped to pixs. - * (3) The reference point for the variance calculation is the center of - * the window. Therefore, the numa start parameter from - * pixExtractOnLine() is incremented by %size/2, - * to align the variance values with the pixel coordinate. - * (4) The square root of the variance is the RMS deviation from the mean. - *- */ -l_ok -pixWindowedVarianceOnLine(PIX *pixs, - l_int32 dir, - l_int32 loc, - l_int32 c1, - l_int32 c2, - l_int32 size, - NUMA **pnad) -{ -l_int32 i, j, w, h, cmin, cmax, maxloc, n, x, y; -l_uint32 val; -l_float32 norm, rootvar; -l_float32 *array; -l_float64 sum1, sum2, ave, var; -NUMA *na1, *nad; -PTA *pta; - - PROCNAME("pixWindowedVarianceOnLine"); - - if (!pnad) - return ERROR_INT("&nad not defined", procName, 1); - *pnad = NULL; - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not defined or not 8bpp", procName, 1); - if (size < 2) - return ERROR_INT("window size must be > 1", procName, 1); - if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE) - return ERROR_INT("invalid direction", procName, 1); - pixGetDimensions(pixs, &w, &h, NULL); - maxloc = (dir == L_HORIZONTAL_LINE) ? h - 1 : w - 1; - if (loc < 0 || loc > maxloc) - return ERROR_INT("invalid line position", procName, 1); - - /* Clip line to the image */ - cmin = L_MIN(c1, c2); - cmax = L_MAX(c1, c2); - maxloc = (dir == L_HORIZONTAL_LINE) ? w - 1 : h - 1; - cmin = L_MAX(0, L_MIN(cmin, maxloc)); - cmax = L_MAX(0, L_MIN(cmax, maxloc)); - n = cmax - cmin + 1; - - /* Generate pta along the line */ - pta = ptaCreate(n); - if (dir == L_HORIZONTAL_LINE) { - for (i = cmin; i <= cmax; i++) - ptaAddPt(pta, i, loc); - } else { /* vertical line */ - for (i = cmin; i <= cmax; i++) - ptaAddPt(pta, loc, i); - } - - /* Get numa of pixel values on the line */ - na1 = numaCreate(n); - numaSetParameters(na1, cmin, 1); - for (i = 0; i < n; i++) { - ptaGetIPt(pta, i, &x, &y); - pixGetPixel(pixs, x, y, &val); - numaAddNumber(na1, val); - } - array = numaGetFArray(na1, L_NOCOPY); - ptaDestroy(&pta); - - /* Compute root variance on overlapping windows */ - nad = numaCreate(n); - *pnad = nad; - numaSetParameters(nad, cmin + size / 2, 1); - norm = 1.0 / (l_float32)size; - for (i = 0; i < n - size; i++) { /* along the line */ - sum1 = sum2 = 0; - for (j = 0; j < size; j++) { /* over the window */ - val = array[i + j]; - sum1 += val; - sum2 += (l_float64)(val) * val; - } - ave = norm * sum1; - var = norm * sum2 - ave * ave; - rootvar = (l_float32)sqrt(var); - numaAddNumber(nad, rootvar); - } - - numaDestroy(&na1); - return 0; -} - - -/*---------------------------------------------------------------------* - * Extract min/max of pixel values near lines * - *---------------------------------------------------------------------*/ -/*! - * \brief pixMinMaxNearLine() - * - * \param[in] pixs 8 bpp; no colormap - * \param[in] x1, y1 starting pt for line - * \param[in] x2, y2 end pt for line - * \param[in] dist distance to search from line in each direction - * \param[in] direction L_SCAN_NEGATIVE, L_SCAN_POSITIVE, L_SCAN_BOTH - * \param[out] pnamin [optional] minimum values - * \param[out] pnamax [optional] maximum values - * \param[out] pminave [optional] average of minimum values - * \param[out] pmaxave [optional] average of maximum values - * \return 0 if OK; 1 on error or if there are no sampled points - * within the image. - * - *- * Notes: - * (1) If the line is more horizontal than vertical, the values - * are computed for [x1, x2], and the pixels are taken - * below and/or above the local y-value. Otherwise, the - * values are computed for [y1, y2] and the pixels are taken - * to the left and/or right of the local x value. - * (2) %direction specifies which side (or both sides) of the - * line are scanned for min and max values. - * (3) There are two ways to tell if the returned values of min - * and max averages are valid: the returned values cannot be - * negative and the function must return 0. - * (4) All accessed pixels are clipped to the pix. - *- */ -l_ok -pixMinMaxNearLine(PIX *pixs, - l_int32 x1, - l_int32 y1, - l_int32 x2, - l_int32 y2, - l_int32 dist, - l_int32 direction, - NUMA **pnamin, - NUMA **pnamax, - l_float32 *pminave, - l_float32 *pmaxave) -{ -l_int32 i, j, w, h, d, x, y, n, dir, found, minval, maxval, negloc, posloc; -l_uint32 val; -l_float32 sum; -NUMA *namin, *namax; -PTA *pta; - - PROCNAME("pixMinMaxNearLine"); - - if (pnamin) *pnamin = NULL; - if (pnamax) *pnamax = NULL; - if (pminave) *pminave = UNDEF; - if (pmaxave) *pmaxave = UNDEF; - if (!pnamin && !pnamax && !pminave && !pmaxave) - return ERROR_INT("no output requested", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8 || pixGetColormap(pixs)) - return ERROR_INT("pixs not 8 bpp or has colormap", procName, 1); - dist = L_ABS(dist); - if (direction != L_SCAN_NEGATIVE && direction != L_SCAN_POSITIVE && - direction != L_SCAN_BOTH) - return ERROR_INT("invalid direction", procName, 1); - - pta = generatePtaLine(x1, y1, x2, y2); - n = ptaGetCount(pta); - dir = (L_ABS(x1 - x2) == n - 1) ? L_HORIZ : L_VERT; - namin = numaCreate(n); - namax = numaCreate(n); - negloc = -dist; - posloc = dist; - if (direction == L_SCAN_NEGATIVE) - posloc = 0; - else if (direction == L_SCAN_POSITIVE) - negloc = 0; - for (i = 0; i < n; i++) { - ptaGetIPt(pta, i, &x, &y); - minval = 255; - maxval = 0; - found = FALSE; - if (dir == L_HORIZ) { - if (x < 0 || x >= w) continue; - for (j = negloc; j <= posloc; j++) { - if (y + j < 0 || y + j >= h) continue; - pixGetPixel(pixs, x, y + j, &val); - found = TRUE; - if (val < minval) minval = val; - if (val > maxval) maxval = val; - } - } else { /* dir == L_VERT */ - if (y < 0 || y >= h) continue; - for (j = negloc; j <= posloc; j++) { - if (x + j < 0 || x + j >= w) continue; - pixGetPixel(pixs, x + j, y, &val); - found = TRUE; - if (val < minval) minval = val; - if (val > maxval) maxval = val; - } - } - if (found) { - numaAddNumber(namin, minval); - numaAddNumber(namax, maxval); - } - } - - n = numaGetCount(namin); - if (n == 0) { - numaDestroy(&namin); - numaDestroy(&namax); - ptaDestroy(&pta); - return ERROR_INT("no output from this line", procName, 1); - } - - if (pminave) { - numaGetSum(namin, &sum); - *pminave = sum / n; - } - if (pmaxave) { - numaGetSum(namax, &sum); - *pmaxave = sum / n; - } - if (pnamin) - *pnamin = namin; - else - numaDestroy(&namin); - if (pnamax) - *pnamax = namax; - else - numaDestroy(&namax); - ptaDestroy(&pta); - return 0; -} - - -/*---------------------------------------------------------------------* - * Rank row and column transforms * - *---------------------------------------------------------------------*/ -/*! - * \brief pixRankRowTransform() - * - * \param[in] pixs 8 bpp; no colormap - * \return pixd with pixels sorted in each row, from - * min to max value - * - *- * Notes: - * (1) The time is O(n) in the number of pixels and runs about - * 100 Mpixels/sec on a 3 GHz machine. - *- */ -PIX * -pixRankRowTransform(PIX *pixs) -{ -l_int32 i, j, k, m, w, h, wpl, val; -l_int32 histo[256]; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; - - PROCNAME("pixRankRowTransform"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs has a colormap", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - pixd = pixCreateTemplate(pixs); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpl = pixGetWpl(pixs); - for (i = 0; i < h; i++) { - memset(histo, 0, 1024); - lines = datas + i * wpl; - lined = datad + i * wpl; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(lines, j); - histo[val]++; - } - for (m = 0, j = 0; m < 256; m++) { - for (k = 0; k < histo[m]; k++, j++) - SET_DATA_BYTE(lined, j, m); - } - } - - return pixd; -} - - -/*! - * \brief pixRankColumnTransform() - * - * \param[in] pixs 8 bpp; no colormap - * \return pixd with pixels sorted in each column, from - * min to max value - * - *- * Notes: - * (1) The time is O(n) in the number of pixels and runs about - * 50 Mpixels/sec on a 3 GHz machine. - *- */ -PIX * -pixRankColumnTransform(PIX *pixs) -{ -l_int32 i, j, k, m, w, h, val; -l_int32 histo[256]; -void **lines8, **lined8; -PIX *pixd; - - PROCNAME("pixRankColumnTransform"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs has a colormap", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - pixd = pixCreateTemplate(pixs); - lines8 = pixGetLinePtrs(pixs, NULL); - lined8 = pixGetLinePtrs(pixd, NULL); - for (j = 0; j < w; j++) { - memset(histo, 0, 1024); - for (i = 0; i < h; i++) { - val = GET_DATA_BYTE(lines8[i], j); - histo[val]++; - } - for (m = 0, i = 0; m < 256; m++) { - for (k = 0; k < histo[m]; k++, i++) - SET_DATA_BYTE(lined8[i], j, m); - } - } - - LEPT_FREE(lines8); - LEPT_FREE(lined8); - return pixd; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixabasic.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixabasic.c deleted file mode 100644 index 68123337..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixabasic.c +++ /dev/null @@ -1,3233 +0,0 @@ -/*====================================================================* - - 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 pixabasic.c - *- * - * Pixa creation, destruction, copying - * PIXA *pixaCreate() - * PIXA *pixaCreateFromPix() - * PIXA *pixaCreateFromBoxa() - * PIXA *pixaSplitPix() - * void pixaDestroy() - * PIXA *pixaCopy() - * - * Pixa addition - * l_int32 pixaAddPix() - * l_int32 pixaAddBox() - * static l_int32 pixaExtendArray() - * l_int32 pixaExtendArrayToSize() - * - * Pixa accessors - * l_int32 pixaGetCount() - * l_int32 pixaChangeRefcount() - * PIX *pixaGetPix() - * l_int32 pixaGetPixDimensions() - * BOXA *pixaGetBoxa() - * l_int32 pixaGetBoxaCount() - * BOX *pixaGetBox() - * l_int32 pixaGetBoxGeometry() - * l_int32 pixaSetBoxa() - * PIX **pixaGetPixArray() - * l_int32 pixaVerifyDepth() - * l_int32 pixaVerifyDimensions() - * l_int32 pixaIsFull() - * l_int32 pixaCountText() - * l_int32 pixaSetText() - * void ***pixaGetLinePtrs() - * - * Pixa output info - * l_int32 pixaWriteStreamInfo() - * - * Pixa array modifiers - * l_int32 pixaReplacePix() - * l_int32 pixaInsertPix() - * l_int32 pixaRemovePix() - * l_int32 pixaRemovePixAndSave() - * l_int32 pixaRemoveSelected() - * l_int32 pixaInitFull() - * l_int32 pixaClear() - * - * Pixa and Pixaa combination - * l_int32 pixaJoin() - * PIXA *pixaInterleave() - * l_int32 pixaaJoin() - * - * Pixaa creation, destruction - * PIXAA *pixaaCreate() - * PIXAA *pixaaCreateFromPixa() - * void pixaaDestroy() - * - * Pixaa addition - * l_int32 pixaaAddPixa() - * l_int32 pixaaExtendArray() - * l_int32 pixaaAddPix() - * l_int32 pixaaAddBox() - * - * Pixaa accessors - * l_int32 pixaaGetCount() - * PIXA *pixaaGetPixa() - * BOXA *pixaaGetBoxa() - * PIX *pixaaGetPix() - * l_int32 pixaaVerifyDepth() - * l_int32 pixaaVerifyDimensions() - * l_int32 pixaaIsFull() - * - * Pixaa array modifiers - * l_int32 pixaaInitFull() - * l_int32 pixaaReplacePixa() - * l_int32 pixaaClear() - * l_int32 pixaaTruncate() - * - * Pixa serialized I/O (requires png support) - * PIXA *pixaRead() - * PIXA *pixaReadStream() - * PIXA *pixaReadMem() - * l_int32 pixaWriteDebug() - * l_int32 pixaWrite() - * l_int32 pixaWriteStream() - * l_int32 pixaWriteMem() - * PIXA *pixaReadBoth() - * - * Pixaa serialized I/O (requires png support) - * PIXAA *pixaaReadFromFiles() - * PIXAA *pixaaRead() - * PIXAA *pixaaReadStream() - * PIXAA *pixaaReadMem() - * l_int32 pixaaWrite() - * l_int32 pixaaWriteStream() - * l_int32 pixaaWriteMem() - * - * - * Important note on reference counting: - * Reference counting for the Pixa is analogous to that for the Boxa. - * See pix.h for details. pixaCopy() provides three possible modes - * of copy. The basic rule is that however a Pixa is obtained - * (e.g., from pixaCreate*(), pixaCopy(), or a Pixaa accessor), - * it is necessary to call pixaDestroy() on it. - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include "allheaders.h" - - /* Bounds on initial array size */ -static const l_uint32 MaxPtrArraySize = 100000; -static const l_int32 InitialPtrArraySize = 20; /*!< n'importe quoi */ - - /* Static functions */ -static l_int32 pixaExtendArray(PIXA *pixa); - -/*---------------------------------------------------------------------* - * Pixa creation, destruction, copy * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaCreate() - * - * \param[in] n initial number of ptrs - * \return pixa, or NULL on error - * - * - * Notes: - * (1) This creates an empty boxa. - *- */ -PIXA * -pixaCreate(l_int32 n) -{ -PIXA *pixa; - - PROCNAME("pixaCreate"); - - if (n <= 0 || n > MaxPtrArraySize) - n = InitialPtrArraySize; - - pixa = (PIXA *)LEPT_CALLOC(1, sizeof(PIXA)); - pixa->n = 0; - pixa->nalloc = n; - pixa->refcount = 1; - pixa->pix = (PIX **)LEPT_CALLOC(n, sizeof(PIX *)); - pixa->boxa = boxaCreate(n); - if (!pixa->pix || !pixa->boxa) { - pixaDestroy(&pixa); - return (PIXA *)ERROR_PTR("pix or boxa not made", procName, NULL); - } - return pixa; -} - - -/*! - * \brief pixaCreateFromPix() - * - * \param[in] pixs with individual components on a lattice - * \param[in] n number of components - * \param[in] cellw width of each cell - * \param[in] cellh height of each cell - * \return pixa, or NULL on error - * - *- * Notes: - * (1) For bpp = 1, we truncate each retrieved pix to the ON - * pixels, which we assume for now start at (0,0) - *- */ -PIXA * -pixaCreateFromPix(PIX *pixs, - l_int32 n, - l_int32 cellw, - l_int32 cellh) -{ -l_int32 w, h, d, nw, nh, i, j, index; -PIX *pix1, *pix2; -PIXA *pixa; - - PROCNAME("pixaCreateFromPix"); - - if (!pixs) - return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); - if (n <= 0) - return (PIXA *)ERROR_PTR("n must be > 0", procName, NULL); - - if ((pixa = pixaCreate(n)) == NULL) - return (PIXA *)ERROR_PTR("pixa not made", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if ((pix1 = pixCreate(cellw, cellh, d)) == NULL) { - pixaDestroy(&pixa); - return (PIXA *)ERROR_PTR("pix1 not made", procName, NULL); - } - - nw = (w + cellw - 1) / cellw; - nh = (h + cellh - 1) / cellh; - for (i = 0, index = 0; i < nh; i++) { - for (j = 0; j < nw && index < n; j++, index++) { - pixRasterop(pix1, 0, 0, cellw, cellh, PIX_SRC, pixs, - j * cellw, i * cellh); - if (d == 1 && !pixClipToForeground(pix1, &pix2, NULL)) - pixaAddPix(pixa, pix2, L_INSERT); - else - pixaAddPix(pixa, pix1, L_COPY); - } - } - - pixDestroy(&pix1); - return pixa; -} - - -/*! - * \brief pixaCreateFromBoxa() - * - * \param[in] pixs - * \param[in] boxa - * \param[in] start first box to use - * \param[in] num number of boxes; use 0 to go to the end - * \param[out] pcropwarn [optional] 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. To extract all the regions, set both %start - * and %num to 0. - * (2) The 5th 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 start, - l_int32 num, - l_int32 *pcropwarn) -{ -l_int32 i, n, end, 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); - if (num < 0) - return (PIXA *)ERROR_PTR("num must be >= 0", procName, NULL); - - n = boxaGetCount(boxa); - end = (num == 0) ? n - 1 : L_MIN(start + num - 1, n - 1); - if ((pixad = pixaCreate(end - start + 1)) == 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 = start; i <= end; 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; -} - - -/*! - * \brief pixaSplitPix() - * - * \param[in] pixs with individual components on a lattice - * \param[in] nx number of mosaic cells horizontally - * \param[in] ny number of mosaic cells vertically - * \param[in] borderwidth of added border on all sides - * \param[in] bordercolor in our RGBA format: 0xrrggbbaa - * \return pixa, or NULL on error - * - *- * Notes: - * (1) This is a variant on pixaCreateFromPix(), where we - * simply divide the image up into (approximately) equal - * subunits. If you want the subimages to have essentially - * the same aspect ratio as the input pix, use nx = ny. - * (2) If borderwidth is 0, we ignore the input bordercolor and - * redefine it to white. - * (3) The bordercolor is always used to initialize each tiled pix, - * so that if the src is clipped, the unblitted part will - * be this color. This avoids 1 pixel wide black stripes at the - * left and lower edges. - *- */ -PIXA * -pixaSplitPix(PIX *pixs, - l_int32 nx, - l_int32 ny, - l_int32 borderwidth, - l_uint32 bordercolor) -{ -l_int32 w, h, d, cellw, cellh, i, j; -PIX *pix1; -PIXA *pixa; - - PROCNAME("pixaSplitPix"); - - if (!pixs) - return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); - if (nx <= 0 || ny <= 0) - return (PIXA *)ERROR_PTR("nx and ny must be > 0", procName, NULL); - borderwidth = L_MAX(0, borderwidth); - - if ((pixa = pixaCreate(nx * ny)) == NULL) - return (PIXA *)ERROR_PTR("pixa not made", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - cellw = (w + nx - 1) / nx; /* round up */ - cellh = (h + ny - 1) / ny; - - for (i = 0; i < ny; i++) { - for (j = 0; j < nx; j++) { - if ((pix1 = pixCreate(cellw + 2 * borderwidth, - cellh + 2 * borderwidth, d)) == NULL) { - pixaDestroy(&pixa); - return (PIXA *)ERROR_PTR("pix1 not made", procName, NULL); - } - pixCopyColormap(pix1, pixs); - if (borderwidth == 0) { /* initialize full image to white */ - if (d == 1) - pixClearAll(pix1); - else - pixSetAll(pix1); - } else { - pixSetAllArbitrary(pix1, bordercolor); - } - pixRasterop(pix1, borderwidth, borderwidth, cellw, cellh, - PIX_SRC, pixs, j * cellw, i * cellh); - pixaAddPix(pixa, pix1, L_INSERT); - } - } - - return pixa; -} - - -/*! - * \brief pixaDestroy() - * - * \param[in,out] ppixa use ptr address so it will be nulled - * - *- * Notes: - * (1) Decrements the ref count and, if 0, destroys the pixa. - * (2) Always nulls the input ptr. - *- */ -void -pixaDestroy(PIXA **ppixa) -{ -l_int32 i; -PIXA *pixa; - - PROCNAME("pixaDestroy"); - - if (ppixa == NULL) { - L_WARNING("ptr address is NULL!\n", procName); - return; - } - - if ((pixa = *ppixa) == NULL) - return; - - /* Decrement the refcount. If it is 0, destroy the pixa. */ - pixaChangeRefcount(pixa, -1); - if (pixa->refcount <= 0) { - for (i = 0; i < pixa->n; i++) - pixDestroy(&pixa->pix[i]); - LEPT_FREE(pixa->pix); - boxaDestroy(&pixa->boxa); - LEPT_FREE(pixa); - } - - *ppixa = NULL; - return; -} - - -/*! - * \brief pixaCopy() - * - * \param[in] pixa - * \param[in] copyflag see pix.h for details: - * 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 - */ -PIXA * -pixaCopy(PIXA *pixa, - l_int32 copyflag) -{ -l_int32 i, nb; -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); - nb = pixaGetBoxaCount(pixa); - for (i = 0; i < pixa->n; i++) { - if (copyflag == L_COPY) { - pixc = pixaGetPix(pixa, i, L_COPY); - if (i < nb) boxc = pixaGetBox(pixa, i, L_COPY); - } else { /* copy-clone */ - pixc = pixaGetPix(pixa, i, L_CLONE); - if (i < nb) boxc = pixaGetBox(pixa, i, L_CLONE); - } - pixaAddPix(pixac, pixc, L_INSERT); - if (i < nb) pixaAddBox(pixac, boxc, L_INSERT); - } - - return pixac; -} - - - -/*---------------------------------------------------------------------* - * Pixa addition * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaAddPix() - * - * \param[in] pixa - * \param[in] pix to be added - * \param[in] copyflag L_INSERT, L_COPY, L_CLONE - * \return 0 if OK; 1 on error - */ -l_ok -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; -} - - -/*! - * \brief pixaAddBox() - * - * \param[in] pixa - * \param[in] box - * \param[in] copyflag L_INSERT, L_COPY, L_CLONE - * \return 0 if OK, 1 on error - */ -l_ok -pixaAddBox(PIXA *pixa, - BOX *box, - l_int32 copyflag) -{ - PROCNAME("pixaAddBox"); - - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - if (copyflag != L_INSERT && copyflag != L_COPY && copyflag != L_CLONE) - return ERROR_INT("invalid copyflag", procName, 1); - - boxaAddBox(pixa->boxa, box, copyflag); - return 0; -} - - -/*! - * \brief pixaExtendArray() - * - * \param[in] pixa - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) Doubles the size of the pixa and boxa ptr arrays. - *- */ -static l_int32 -pixaExtendArray(PIXA *pixa) -{ - PROCNAME("pixaExtendArray"); - - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - - return pixaExtendArrayToSize(pixa, 2 * pixa->nalloc); -} - - -/*! - * \brief pixaExtendArrayToSize() - * - * \param[in] pixa - * \param[in] size - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) If necessary, reallocs new pixa and boxa ptrs arrays to %size. - * The pixa and boxa ptr arrays must always be equal in size. - *- */ -l_ok -pixaExtendArrayToSize(PIXA *pixa, - l_int32 size) -{ - PROCNAME("pixaExtendArrayToSize"); - - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - - if (size > pixa->nalloc) { - if ((pixa->pix = (PIX **)reallocNew((void **)&pixa->pix, - sizeof(PIX *) * pixa->nalloc, - size * sizeof(PIX *))) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - pixa->nalloc = size; - } - return boxaExtendArrayToSize(pixa->boxa, size); -} - - -/*---------------------------------------------------------------------* - * Pixa accessors * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaGetCount() - * - * \param[in] pixa - * \return count, or 0 if no pixa - */ -l_int32 -pixaGetCount(PIXA *pixa) -{ - PROCNAME("pixaGetCount"); - - if (!pixa) - return ERROR_INT("pixa not defined", procName, 0); - - return pixa->n; -} - - -/*! - * \brief pixaChangeRefcount() - * - * \param[in] pixa - * \param[in] delta - * \return 0 if OK, 1 on error - */ -l_ok -pixaChangeRefcount(PIXA *pixa, - l_int32 delta) -{ - PROCNAME("pixaChangeRefcount"); - - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - - pixa->refcount += delta; - return 0; -} - - -/*! - * \brief pixaGetPix() - * - * \param[in] pixa - * \param[in] index to the index-th pix - * \param[in] accesstype L_COPY or L_CLONE - * \return pix, or NULL on error - */ -PIX * -pixaGetPix(PIXA *pixa, - l_int32 index, - l_int32 accesstype) -{ -PIX *pix; - - PROCNAME("pixaGetPix"); - - if (!pixa) - return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); - if (index < 0 || index >= pixa->n) - return (PIX *)ERROR_PTR("index not valid", procName, NULL); - if ((pix = pixa->pix[index]) == NULL) { - L_ERROR("no pix at pixa[%d]\n", procName, index); - return (PIX *)ERROR_PTR("pix not found!", procName, NULL); - } - - if (accesstype == L_COPY) - return pixCopy(NULL, pix); - else if (accesstype == L_CLONE) - return pixClone(pix); - else - return (PIX *)ERROR_PTR("invalid accesstype", procName, NULL); -} - - -/*! - * \brief pixaGetPixDimensions() - * - * \param[in] pixa - * \param[in] index to the index-th box - * \param[out] pw, ph, pd [optional] each can be null - * \return 0 if OK, 1 on error - */ -l_ok -pixaGetPixDimensions(PIXA *pixa, - l_int32 index, - l_int32 *pw, - l_int32 *ph, - l_int32 *pd) -{ -PIX *pix; - - PROCNAME("pixaGetPixDimensions"); - - if (pw) *pw = 0; - if (ph) *ph = 0; - if (pd) *pd = 0; - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - if (index < 0 || index >= pixa->n) - return ERROR_INT("index not valid", procName, 1); - - if ((pix = pixaGetPix(pixa, index, L_CLONE)) == NULL) - return ERROR_INT("pix not found!", procName, 1); - pixGetDimensions(pix, pw, ph, pd); - pixDestroy(&pix); - return 0; -} - - -/*! - * \brief pixaGetBoxa() - * - * \param[in] pixa - * \param[in] accesstype L_COPY, L_CLONE, L_COPY_CLONE - * \return boxa, or NULL on error - */ -BOXA * -pixaGetBoxa(PIXA *pixa, - l_int32 accesstype) -{ - PROCNAME("pixaGetBoxa"); - - if (!pixa) - return (BOXA *)ERROR_PTR("pixa not defined", procName, NULL); - if (!pixa->boxa) - return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL); - if (accesstype != L_COPY && accesstype != L_CLONE && - accesstype != L_COPY_CLONE) - return (BOXA *)ERROR_PTR("invalid accesstype", procName, NULL); - - return boxaCopy(pixa->boxa, accesstype); -} - - -/*! - * \brief pixaGetBoxaCount() - * - * \param[in] pixa - * \return count, or 0 on error - */ -l_int32 -pixaGetBoxaCount(PIXA *pixa) -{ - PROCNAME("pixaGetBoxaCount"); - - if (!pixa) - return ERROR_INT("pixa not defined", procName, 0); - - return boxaGetCount(pixa->boxa); -} - - -/*! - * \brief pixaGetBox() - * - * \param[in] pixa - * \param[in] index to the index-th pix - * \param[in] accesstype L_COPY or L_CLONE - * \return box if null, not automatically an error, or NULL on error - * - *- * Notes: - * (1) There is always a boxa with a pixa, and it is initialized so - * that each box ptr is NULL. - * (2) In general, we expect that there is either a box associated - * with each pix, or no boxes at all in the boxa. - * (3) Having no boxes is thus not an automatic error. Whether it - * is an actual error is determined by the calling program. - * If the caller expects to get a box, it is an error; see, e.g., - * pixaGetBoxGeometry(). - *- */ -BOX * -pixaGetBox(PIXA *pixa, - l_int32 index, - l_int32 accesstype) -{ -BOX *box; - - PROCNAME("pixaGetBox"); - - if (!pixa) - return (BOX *)ERROR_PTR("pixa not defined", procName, NULL); - if (!pixa->boxa) - return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); - if (index < 0 || index >= pixa->boxa->n) - return (BOX *)ERROR_PTR("index not valid", procName, NULL); - if (accesstype != L_COPY && accesstype != L_CLONE) - return (BOX *)ERROR_PTR("invalid accesstype", procName, NULL); - - box = pixa->boxa->box[index]; - if (box) { - if (accesstype == L_COPY) - return boxCopy(box); - else /* accesstype == L_CLONE */ - return boxClone(box); - } else { - return NULL; - } -} - - -/*! - * \brief pixaGetBoxGeometry() - * - * \param[in] pixa - * \param[in] index to the index-th box - * \param[out] px, py, pw, ph [optional] each can be null - * \return 0 if OK, 1 on error - */ -l_ok -pixaGetBoxGeometry(PIXA *pixa, - l_int32 index, - l_int32 *px, - l_int32 *py, - l_int32 *pw, - l_int32 *ph) -{ -BOX *box; - - PROCNAME("pixaGetBoxGeometry"); - - if (px) *px = 0; - if (py) *py = 0; - if (pw) *pw = 0; - if (ph) *ph = 0; - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - if (index < 0 || index >= pixa->n) - return ERROR_INT("index not valid", procName, 1); - - if ((box = pixaGetBox(pixa, index, L_CLONE)) == NULL) - return ERROR_INT("box not found!", procName, 1); - boxGetGeometry(box, px, py, pw, ph); - boxDestroy(&box); - return 0; -} - - -/*! - * \brief pixaSetBoxa() - * - * \param[in] pixa - * \param[in] boxa - * \param[in] accesstype L_INSERT, L_COPY, L_CLONE - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This destroys the existing boxa in the pixa. - *- */ -l_ok -pixaSetBoxa(PIXA *pixa, - BOXA *boxa, - l_int32 accesstype) -{ - PROCNAME("pixaSetBoxa"); - - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - if (!boxa) - return ERROR_INT("boxa not defined", procName, 1); - if (accesstype != L_INSERT && accesstype != L_COPY && - accesstype != L_CLONE) - return ERROR_INT("invalid access type", procName, 1); - - boxaDestroy(&pixa->boxa); - if (accesstype == L_INSERT) - pixa->boxa = boxa; - else - pixa->boxa = boxaCopy(boxa, accesstype); - - return 0; -} - - -/*! - * \brief pixaGetPixArray() - * - * \param[in] pixa - * \return pix array, or NULL on error - * - *- * Notes: - * (1) This returns a ptr to the actual array. The array is - * owned by the pixa, so it must not be destroyed. - * (2) The caller should always check if the return value is NULL - * before accessing any of the pix ptrs in this array! - *- */ -PIX ** -pixaGetPixArray(PIXA *pixa) -{ - PROCNAME("pixaGetPixArray"); - - if (!pixa) - return (PIX **)ERROR_PTR("pixa not defined", procName, NULL); - - return pixa->pix; -} - - -/*! - * \brief pixaVerifyDepth() - * - * \param[in] pixa - * \param[out] psame 1 if depth is the same for all pix; 0 otherwise - * \param[out] pmaxd [optional] max depth of all pix - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) It is considered to be an error if there are no pix. - *- */ -l_ok -pixaVerifyDepth(PIXA *pixa, - l_int32 *psame, - l_int32 *pmaxd) -{ -l_int32 i, n, d, maxd, same; - - PROCNAME("pixaVerifyDepth"); - - if (pmaxd) *pmaxd = 0; - if (!psame) - return ERROR_INT("psame not defined", procName, 1); - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - if ((n = pixaGetCount(pixa)) == 0) - return ERROR_INT("no pix in pixa", procName, 1); - - same = 1; - pixaGetPixDimensions(pixa, 0, NULL, NULL, &maxd); - for (i = 1; i < n; i++) { - if (pixaGetPixDimensions(pixa, i, NULL, NULL, &d)) - return ERROR_INT("pix depth not found", procName, 1); - maxd = L_MAX(maxd, d); - if (d != maxd) - same = 0; - } - *psame = same; - if (pmaxd) *pmaxd = maxd; - return 0; -} - - -/*! - * \brief pixaVerifyDimensions() - * - * \param[in] pixa - * \param[out] psame 1 if dimensions are the same for all pix; 0 otherwise - * \param[out] pmaxw [optional] max width of all pix - * \param[out] pmaxh [optional] max height of all pix - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) It is considered to be an error if there are no pix. - *- */ -l_ok -pixaVerifyDimensions(PIXA *pixa, - l_int32 *psame, - l_int32 *pmaxw, - l_int32 *pmaxh) -{ -l_int32 i, n, w, h, maxw, maxh, same; - - PROCNAME("pixaVerifyDimensions"); - - if (pmaxw) *pmaxw = 0; - if (pmaxh) *pmaxh = 0; - if (!psame) - return ERROR_INT("psame not defined", procName, 1); - *psame = 0; - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - if ((n = pixaGetCount(pixa)) == 0) - return ERROR_INT("no pix in pixa", procName, 1); - - same = 1; - pixaGetPixDimensions(pixa, 0, &maxw, &maxh, NULL); - for (i = 1; i < n; i++) { - if (pixaGetPixDimensions(pixa, i, &w, &h, NULL)) - return ERROR_INT("pix dimensions not found", procName, 1); - maxw = L_MAX(maxw, w); - maxh = L_MAX(maxh, h); - if (w != maxw || h != maxh) - same = 0; - } - *psame = same; - if (pmaxw) *pmaxw = maxw; - if (pmaxh) *pmaxh = maxh; - return 0; -} - - -/*! - * \brief pixaIsFull() - * - * \param[in] pixa - * \param[out] pfullpa [optional] 1 if pixa is full - * \param[out] pfullba [optional] 1 if boxa is full - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) A pixa is "full" if the array of pix is fully - * occupied from index 0 to index (pixa->n - 1). - *- */ -l_ok -pixaIsFull(PIXA *pixa, - l_int32 *pfullpa, - l_int32 *pfullba) -{ -l_int32 i, n, full; -BOXA *boxa; -PIX *pix; - - PROCNAME("pixaIsFull"); - - if (pfullpa) *pfullpa = 0; - if (pfullba) *pfullba = 0; - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - - n = pixaGetCount(pixa); - if (pfullpa) { - full = 1; - for (i = 0; i < n; i++) { - if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) { - full = 0; - break; - } - pixDestroy(&pix); - } - *pfullpa = full; - } - if (pfullba) { - boxa = pixaGetBoxa(pixa, L_CLONE); - boxaIsFull(boxa, pfullba); - boxaDestroy(&boxa); - } - return 0; -} - - -/*! - * \brief pixaCountText() - * - * \param[in] pixa - * \param[out] pntext number of pix with non-empty text strings - * \return 0 if OK, 1 on error. - * - *- * Notes: - * (1) All pix have non-empty text strings if the returned value %ntext - * equals the pixa count. - *- */ -l_ok -pixaCountText(PIXA *pixa, - l_int32 *pntext) -{ -char *text; -l_int32 i, n; -PIX *pix; - - PROCNAME("pixaCountText"); - - if (!pntext) - return ERROR_INT("&ntext not defined", procName, 1); - *pntext = 0; - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - - n = pixaGetCount(pixa); - for (i = 0; i < n; i++) { - if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) - continue; - text = pixGetText(pix); - if (text && strlen(text) > 0) - (*pntext)++; - pixDestroy(&pix); - } - - return 0; -} - - -/*! - * \brief pixaSetText() - * - * \param[in] pixa - * \param[in] text [optional] single text string, to insert in each pix - * \param[in] sa [optional] array of text strings, to insert in each pix - * \return 0 if OK, 1 on error. - * - *- * Notes: - * (1) To clear all the text fields, use %sa == NULL and %text == NULL. - * (2) To set all the text fields to the same value %text, use %sa = NULL. - * (3) If %sa is defined, we ignore %text and use it; %sa must have - * the same count as %pixa. - *- */ -l_ok -pixaSetText(PIXA *pixa, - const char *text, - SARRAY *sa) -{ -char *str; -l_int32 i, n; -PIX *pix; - - PROCNAME("pixaSetText"); - - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - - n = pixaGetCount(pixa); - if (sa && (sarrayGetCount(sa) != n)) - return ERROR_INT("pixa and sa sizes differ", procName, 1); - - if (!sa) { - for (i = 0; i < n; i++) { - if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) - continue; - pixSetText(pix, text); - pixDestroy(&pix); - } - return 0; - } - - for (i = 0; i < n; i++) { - if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) - continue; - str = sarrayGetString(sa, i, L_NOCOPY); - pixSetText(pix, str); - pixDestroy(&pix); - } - - return 0; -} - - -/*! - * \brief pixaGetLinePtrs() - * - * \param[in] pixa of pix that all have the same depth - * \param[out] psize [optional] number of pix in the pixa - * \return array of array of line ptrs, or NULL on error - * - *- * Notes: - * (1) See pixGetLinePtrs() for details. - * (2) It is best if all pix in the pixa are the same size. - * The size of each line ptr array is equal to the height - * of the pix that it refers to. - * (3) This is an array of arrays. To destroy it: - * for (i = 0; i < size; i++) - * LEPT_FREE(lineset[i]); - * LEPT_FREE(lineset); - *- */ -void *** -pixaGetLinePtrs(PIXA *pixa, - l_int32 *psize) -{ -l_int32 i, n, same; -void **lineptrs; -void ***lineset; -PIX *pix; - - PROCNAME("pixaGetLinePtrs"); - - if (psize) *psize = 0; - if (!pixa) - return (void ***)ERROR_PTR("pixa not defined", procName, NULL); - pixaVerifyDepth(pixa, &same, NULL); - if (!same) - return (void ***)ERROR_PTR("pixa not all same depth", procName, NULL); - n = pixaGetCount(pixa); - if (psize) *psize = n; - if ((lineset = (void ***)LEPT_CALLOC(n, sizeof(void **))) == NULL) - return (void ***)ERROR_PTR("lineset not made", procName, NULL); - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixa, i, L_CLONE); - lineptrs = pixGetLinePtrs(pix, NULL); - lineset[i] = lineptrs; - pixDestroy(&pix); - } - - return lineset; -} - - -/*---------------------------------------------------------------------* - * Pixa output info * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaWriteStreamInfo() - * - * \param[in] fp file stream - * \param[in] pixa - * \return 0 if OK, 1 on error. - * - *- * Notes: - * (1) For each pix in the pixa, write out the pix dimensions, spp, - * text string (if it exists), and cmap info. - *- */ -l_ok -pixaWriteStreamInfo(FILE *fp, - PIXA *pixa) -{ -char *text; -l_int32 i, n, w, h, d, spp, count, hastext; -PIX *pix; -PIXCMAP *cmap; - - PROCNAME("pixaWriteStreamInfo"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - - n = pixaGetCount(pixa); - for (i = 0; i < n; i++) { - if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) { - fprintf(fp, "%d: no pix at this index\n", i); - continue; - } - pixGetDimensions(pix, &w, &h, &d); - spp = pixGetSpp(pix); - text = pixGetText(pix); - hastext = (text && strlen(text) > 0); - if ((cmap = pixGetColormap(pix)) != NULL) - count = pixcmapGetCount(cmap); - fprintf(fp, "Pix %d: w = %d, h = %d, d = %d, spp = %d", - i, w, h, d, spp); - if (cmap) fprintf(fp, ", cmap(%d colors)", count); - if (hastext) fprintf(fp, ", text = %s", text); - fprintf(fp, "\n"); - pixDestroy(&pix); - } - - return 0; -} - - -/*---------------------------------------------------------------------* - * Pixa array modifiers * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaReplacePix() - * - * \param[in] pixa - * \param[in] index to the index-th pix - * \param[in] pix insert to replace existing one - * \param[in] box [optional] insert to replace existing - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) In-place replacement of one pix. - * (2) The previous pix at that location is destroyed. - *- */ -l_ok -pixaReplacePix(PIXA *pixa, - l_int32 index, - PIX *pix, - BOX *box) -{ -BOXA *boxa; - - PROCNAME("pixaReplacePix"); - - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - if (index < 0 || index >= pixa->n) - return ERROR_INT("index not valid", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - pixDestroy(&(pixa->pix[index])); - pixa->pix[index] = pix; - - if (box) { - boxa = pixa->boxa; - if (index > boxa->n) - return ERROR_INT("boxa index not valid", procName, 1); - boxaReplaceBox(boxa, index, box); - } - - return 0; -} - - -/*! - * \brief pixaInsertPix() - * - * \param[in] pixa - * \param[in] index at which pix is to be inserted - * \param[in] pixs new pix to be inserted - * \param[in] 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_ok -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; -} - - -/*! - * \brief pixaRemovePix() - * - * \param[in] pixa - * \param[in] 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_ok -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; -} - - -/*! - * \brief pixaRemovePixAndSave() - * - * \param[in] pixa - * \param[in] index of pix to be removed - * \param[out] ppix [optional] removed pix - * \param[out] pbox [optional] removed box - * \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. - * (4) The removed pix and box can either be retained or destroyed. - *- */ -l_ok -pixaRemovePixAndSave(PIXA *pixa, - l_int32 index, - PIX **ppix, - BOX **pbox) -{ -l_int32 i, n, nbox; -BOXA *boxa; -PIX **array; - - PROCNAME("pixaRemovePixAndSave"); - - if (ppix) *ppix = NULL; - if (pbox) *pbox = NULL; - 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; - if (ppix) - *ppix = pixaGetPix(pixa, index, L_CLONE); - 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) - boxaRemoveBoxAndSave(boxa, index, pbox); - - return 0; -} - - -/*! - * \brief pixaRemoveSelected() - * - * \param[in] pixa - * \param[in] naindex numa of indices of pix to be removed - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This gives error messages for invalid indices - *- */ -l_ok -pixaRemoveSelected(PIXA *pixa, - NUMA *naindex) -{ -l_int32 i, n, index; -NUMA *na1; - - PROCNAME("pixaRemoveSelected"); - - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - if (!naindex) - return ERROR_INT("naindex not defined", procName, 1); - if ((n = numaGetCount(naindex)) == 0) - return ERROR_INT("naindex is empty", procName, 1); - - /* Remove from highest indices first */ - na1 = numaSort(NULL, naindex, L_SORT_DECREASING); - for (i = 0; i < n; i++) { - numaGetIValue(na1, i, &index); - pixaRemovePix(pixa, index); - } - numaDestroy(&na1); - return 0; -} - - -/*! - * \brief pixaInitFull() - * - * \param[in] pixa typically empty - * \param[in] pix [optional] to be replicated to the entire pixa ptr array - * \param[in] box [optional] to be replicated to the entire boxa ptr array - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This initializes a pixa by filling up the entire pix ptr array - * with copies of %pix. If %pix == NULL, we use a tiny placeholder - * pix (w = h = d = 1). Any existing pix are destroyed. - * It also optionally fills the boxa with copies of %box. - * After this operation, the numbers of pix and (optionally) - * boxes are equal to the number of allocated ptrs. - * (2) Note that we use pixaReplacePix() instead of pixaInsertPix(). - * They both have the same effect when inserting into a NULL ptr - * in the pixa ptr array: - * (3) If the boxa is not initialized (i.e., filled with boxes), - * later insertion of boxes will cause an error, because the - * 'n' field is 0. - * (4) Example usage. This function is useful to prepare for a - * random insertion (or replacement) of pix into a pixa. - * To randomly insert pix into a pixa, without boxes, up to - * some index "max": - * Pixa *pixa = pixaCreate(max); - * pixaInitFull(pixa, NULL, NULL); - * An existing pixa with a smaller ptr array can also be reused: - * pixaExtendArrayToSize(pixa, max); - * pixaInitFull(pixa, NULL, NULL); - * The initialization allows the pixa to always be properly - * filled, even if all pix (and boxes) are not later replaced. - *- */ -l_ok -pixaInitFull(PIXA *pixa, - PIX *pix, - BOX *box) -{ -l_int32 i, n; -PIX *pix1; - - PROCNAME("pixaInitFull"); - - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - - n = pixa->nalloc; - pixa->n = n; - for (i = 0; i < n; i++) { - if (pix) - pix1 = pixCopy(NULL, pix); - else - pix1 = pixCreate(1, 1, 1); - pixaReplacePix(pixa, i, pix1, NULL); - } - if (box) - boxaInitFull(pixa->boxa, box); - - return 0; -} - - -/*! - * \brief pixaClear() - * - * \param[in] pixa - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This destroys all pix in the pixa, as well as - * all boxes in the boxa. The ptrs in the pix ptr array - * are all null'd. The number of allocated pix, n, is set to 0. - *- */ -l_ok -pixaClear(PIXA *pixa) -{ -l_int32 i, n; - - PROCNAME("pixaClear"); - - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - - n = pixaGetCount(pixa); - for (i = 0; i < n; i++) - pixDestroy(&pixa->pix[i]); - pixa->n = 0; - return boxaClear(pixa->boxa); -} - - -/*---------------------------------------------------------------------* - * Pixa and Pixaa combination * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaJoin() - * - * \param[in] pixad dest pixa; add to this one - * \param[in] pixas [optional] source pixa; add from this one - * \param[in] istart starting index in pixas - * \param[in] iend ending index in pixas; use -1 to cat all - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This appends a clone of each indicated pix in pixas to pixad - * (2) istart < 0 is taken to mean 'read from the start' (istart = 0) - * (3) iend < 0 means 'read to the end' - * (4) If pixas is NULL or contains no pix, this is a no-op. - *- */ -l_ok -pixaJoin(PIXA *pixad, - PIXA *pixas, - l_int32 istart, - l_int32 iend) -{ -l_int32 i, n, nb; -BOXA *boxas, *boxad; -PIX *pix; - - PROCNAME("pixaJoin"); - - if (!pixad) - return ERROR_INT("pixad not defined", procName, 1); - if (!pixas || ((n = pixaGetCount(pixas)) == 0)) - return 0; - - if (istart < 0) - istart = 0; - if (iend < 0 || iend >= n) - iend = n - 1; - if (istart > iend) - return ERROR_INT("istart > iend; nothing to add", procName, 1); - - for (i = istart; i <= iend; i++) { - pix = pixaGetPix(pixas, i, L_CLONE); - pixaAddPix(pixad, pix, L_INSERT); - } - - boxas = pixaGetBoxa(pixas, L_CLONE); - boxad = pixaGetBoxa(pixad, L_CLONE); - nb = pixaGetBoxaCount(pixas); - iend = L_MIN(iend, nb - 1); - boxaJoin(boxad, boxas, istart, iend); - boxaDestroy(&boxas); /* just the clones */ - boxaDestroy(&boxad); - return 0; -} - - -/*! - * \brief pixaInterleave() - * - * \param[in] pixa1 first src pixa - * \param[in] pixa2 second src pixa - * \param[in] copyflag L_CLONE, L_COPY - * \return pixa interleaved from sources, or NULL on error. - * - *- * Notes: - * (1) %copyflag determines if the pix are copied or cloned. - * The boxes, if they exist, are copied. - * (2) If the two pixa have different sizes, a warning is issued, - * and the number of pairs returned is the minimum size. - *- */ -PIXA * -pixaInterleave(PIXA *pixa1, - PIXA *pixa2, - l_int32 copyflag) -{ -l_int32 i, n1, n2, n, nb1, nb2; -BOX *box; -PIX *pix; -PIXA *pixad; - - PROCNAME("pixaInterleave"); - - if (!pixa1) - return (PIXA *)ERROR_PTR("pixa1 not defined", procName, NULL); - if (!pixa2) - return (PIXA *)ERROR_PTR("pixa2 not defined", procName, NULL); - if (copyflag != L_COPY && copyflag != L_CLONE) - return (PIXA *)ERROR_PTR("invalid copyflag", procName, NULL); - n1 = pixaGetCount(pixa1); - n2 = pixaGetCount(pixa2); - n = L_MIN(n1, n2); - if (n == 0) - return (PIXA *)ERROR_PTR("at least one input pixa is empty", - procName, NULL); - if (n1 != n2) - L_WARNING("counts differ: %d != %d\n", procName, n1, n2); - - pixad = pixaCreate(2 * n); - nb1 = pixaGetBoxaCount(pixa1); - nb2 = pixaGetBoxaCount(pixa2); - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixa1, i, copyflag); - pixaAddPix(pixad, pix, L_INSERT); - if (i < nb1) { - box = pixaGetBox(pixa1, i, L_COPY); - pixaAddBox(pixad, box, L_INSERT); - } - pix = pixaGetPix(pixa2, i, copyflag); - pixaAddPix(pixad, pix, L_INSERT); - if (i < nb2) { - box = pixaGetBox(pixa2, i, L_COPY); - pixaAddBox(pixad, box, L_INSERT); - } - } - - return pixad; -} - - -/*! - * \brief pixaaJoin() - * - * \param[in] paad dest pixaa; add to this one - * \param[in] paas [optional] source pixaa; add from this one - * \param[in] istart starting index in pixaas - * \param[in] iend ending index in pixaas; use -1 to cat all - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This appends a clone of each indicated pixa in paas to pixaad - * (2) istart < 0 is taken to mean 'read from the start' (istart = 0) - * (3) iend < 0 means 'read to the end' - *- */ -l_ok -pixaaJoin(PIXAA *paad, - PIXAA *paas, - l_int32 istart, - l_int32 iend) -{ -l_int32 i, n; -PIXA *pixa; - - PROCNAME("pixaaJoin"); - - if (!paad) - return ERROR_INT("pixaad not defined", procName, 1); - if (!paas) - return 0; - - if (istart < 0) - istart = 0; - n = pixaaGetCount(paas, NULL); - if (iend < 0 || iend >= n) - iend = n - 1; - if (istart > iend) - return ERROR_INT("istart > iend; nothing to add", procName, 1); - - for (i = istart; i <= iend; i++) { - pixa = pixaaGetPixa(paas, i, L_CLONE); - pixaaAddPixa(paad, pixa, L_INSERT); - } - - return 0; -} - - -/*---------------------------------------------------------------------* - * Pixaa creation and destruction * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaaCreate() - * - * \param[in] n initial number of pixa ptrs - * \return paa, or NULL on error - * - *- * Notes: - * (1) A pixaa provides a 2-level hierarchy of images. - * A common use is for segmentation masks, which are - * inexpensive to store in png format. - * (2) For example, suppose you want a mask for each textline - * in a two-column page. The textline masks for each column - * can be represented by a pixa, of which there are 2 in the pixaa. - * The boxes for the textline mask components within a column - * can have their origin referred to the column rather than the page. - * Then the boxa field can be used to represent the two box (regions) - * for the columns, and the (x,y) components of each box can - * be used to get the absolute position of the textlines on - * the page. - *- */ -PIXAA * -pixaaCreate(l_int32 n) -{ -PIXAA *paa; - - PROCNAME("pixaaCreate"); - - if (n <= 0 || n > MaxPtrArraySize) - n = InitialPtrArraySize; - - paa = (PIXAA *)LEPT_CALLOC(1, sizeof(PIXAA)); - paa->n = 0; - paa->nalloc = n; - if ((paa->pixa = (PIXA **)LEPT_CALLOC(n, sizeof(PIXA *))) == NULL) { - pixaaDestroy(&paa); - return (PIXAA *)ERROR_PTR("pixa ptrs not made", procName, NULL); - } - paa->boxa = boxaCreate(n); - - return paa; -} - - -/*! - * \brief pixaaCreateFromPixa() - * - * \param[in] pixa - * \param[in] n number specifying subdivision of pixa - * \param[in] type L_CHOOSE_CONSECUTIVE, L_CHOOSE_SKIP_BY - * \param[in] copyflag L_CLONE, L_COPY - * \return paa, 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 *paa; - - 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); - paa = 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(paa, pixat, L_INSERT); - } - if (i % n != 0) - pixaaAddPixa(paa, 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(paa, pixat, L_INSERT); - } - } - - return paa; -} - - -/*! - * \brief pixaaDestroy() - * - * \param[in,out] ppaa use ptr address so it will be nulled - * \return void - */ -void -pixaaDestroy(PIXAA **ppaa) -{ -l_int32 i; -PIXAA *paa; - - PROCNAME("pixaaDestroy"); - - if (ppaa == NULL) { - L_WARNING("ptr address is NULL!\n", procName); - return; - } - - if ((paa = *ppaa) == NULL) - return; - - for (i = 0; i < paa->n; i++) - pixaDestroy(&paa->pixa[i]); - LEPT_FREE(paa->pixa); - boxaDestroy(&paa->boxa); - - LEPT_FREE(paa); - *ppaa = NULL; - - return; -} - - -/*---------------------------------------------------------------------* - * Pixaa addition * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaaAddPixa() - * - * \param[in] paa - * \param[in] pixa to be added - * \param[in] copyflag: - * L_INSERT inserts the pixa directly; - * L_COPY makes a new pixa and copies each pix and each box; - * L_CLONE gives a new handle to the input pixa; - * L_COPY_CLONE makes a new pixa and inserts clones of - * all pix and boxes - * \return 0 if OK; 1 on error - */ -l_ok -pixaaAddPixa(PIXAA *paa, - PIXA *pixa, - l_int32 copyflag) -{ -l_int32 n; -PIXA *pixac; - - PROCNAME("pixaaAddPixa"); - - if (!paa) - return ERROR_INT("paa not defined", procName, 1); - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - if (copyflag != L_INSERT && copyflag != L_COPY && - copyflag != L_CLONE && copyflag != L_COPY_CLONE) - return ERROR_INT("invalid copyflag", procName, 1); - - if (copyflag == L_INSERT) { - pixac = pixa; - } else { - if ((pixac = pixaCopy(pixa, copyflag)) == NULL) - return ERROR_INT("pixac not made", procName, 1); - } - - n = pixaaGetCount(paa, NULL); - if (n >= paa->nalloc) - pixaaExtendArray(paa); - paa->pixa[n] = pixac; - paa->n++; - - return 0; -} - - -/*! - * \brief pixaaExtendArray() - * - * \param[in] paa - * \return 0 if OK; 1 on error - */ -l_ok -pixaaExtendArray(PIXAA *paa) -{ - PROCNAME("pixaaExtendArray"); - - if (!paa) - return ERROR_INT("paa not defined", procName, 1); - - if ((paa->pixa = (PIXA **)reallocNew((void **)&paa->pixa, - sizeof(PIXA *) * paa->nalloc, - 2 * sizeof(PIXA *) * paa->nalloc)) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - - paa->nalloc = 2 * paa->nalloc; - return 0; -} - - -/*! - * \brief pixaaAddPix() - * - * \param[in] paa input paa - * \param[in] index index of pixa in paa - * \param[in] pix to be added - * \param[in] box [optional] to be added - * \param[in] copyflag L_INSERT, L_COPY, L_CLONE - * \return 0 if OK; 1 on error - */ -l_ok -pixaaAddPix(PIXAA *paa, - l_int32 index, - PIX *pix, - BOX *box, - l_int32 copyflag) -{ -PIXA *pixa; - - PROCNAME("pixaaAddPix"); - - if (!paa) - return ERROR_INT("paa not defined", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - if ((pixa = pixaaGetPixa(paa, index, L_CLONE)) == NULL) - return ERROR_INT("pixa not found", procName, 1); - pixaAddPix(pixa, pix, copyflag); - if (box) pixaAddBox(pixa, box, copyflag); - pixaDestroy(&pixa); - return 0; -} - - -/*! - * \brief pixaaAddBox() - * - * \param[in] paa - * \param[in] box - * \param[in] copyflag L_INSERT, L_COPY, L_CLONE - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The box can be used, for example, to hold the support region - * of a pixa that is being added to the pixaa. - *- */ -l_ok -pixaaAddBox(PIXAA *paa, - BOX *box, - l_int32 copyflag) -{ - PROCNAME("pixaaAddBox"); - - if (!paa) - return ERROR_INT("paa not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - if (copyflag != L_INSERT && copyflag != L_COPY && copyflag != L_CLONE) - return ERROR_INT("invalid copyflag", procName, 1); - - boxaAddBox(paa->boxa, box, copyflag); - return 0; -} - - - -/*---------------------------------------------------------------------* - * Pixaa accessors * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaaGetCount() - * - * \param[in] paa - * \param[out] pna [optional] number of pix in each pixa - * \return count, or 0 if no pixaa - * - *- * Notes: - * (1) If paa is empty, a returned na will also be empty. - *- */ -l_int32 -pixaaGetCount(PIXAA *paa, - NUMA **pna) -{ -l_int32 i, n; -NUMA *na; -PIXA *pixa; - - PROCNAME("pixaaGetCount"); - - if (pna) *pna = NULL; - if (!paa) - return ERROR_INT("paa not defined", procName, 0); - - n = paa->n; - if (pna) { - if ((na = numaCreate(n)) == NULL) - return ERROR_INT("na not made", procName, 0); - *pna = na; - for (i = 0; i < n; i++) { - pixa = pixaaGetPixa(paa, i, L_CLONE); - numaAddNumber(na, pixaGetCount(pixa)); - pixaDestroy(&pixa); - } - } - return n; -} - - -/*! - * \brief pixaaGetPixa() - * - * \param[in] paa - * \param[in] index to the index-th pixa - * \param[in] accesstype L_COPY, L_CLONE, L_COPY_CLONE - * \return pixa, or NULL on error - * - *- * Notes: - * (1) L_COPY makes a new pixa with a copy of every pix - * (2) L_CLONE just makes a new reference to the pixa, - * and bumps the counter. You would use this, for example, - * when you need to extract some data from a pix within a - * pixa within a pixaa. - * (3) L_COPY_CLONE makes a new pixa with a clone of every pix - * and box - * (4) In all cases, you must invoke pixaDestroy() on the returned pixa - *- */ -PIXA * -pixaaGetPixa(PIXAA *paa, - l_int32 index, - l_int32 accesstype) -{ -PIXA *pixa; - - PROCNAME("pixaaGetPixa"); - - if (!paa) - return (PIXA *)ERROR_PTR("paa not defined", procName, NULL); - if (index < 0 || index >= paa->n) - return (PIXA *)ERROR_PTR("index not valid", procName, NULL); - if (accesstype != L_COPY && accesstype != L_CLONE && - accesstype != L_COPY_CLONE) - return (PIXA *)ERROR_PTR("invalid accesstype", procName, NULL); - - if ((pixa = paa->pixa[index]) == NULL) { /* shouldn't happen! */ - L_ERROR("missing pixa[%d]\n", procName, index); - return (PIXA *)ERROR_PTR("pixa not found at index", procName, NULL); - } - return pixaCopy(pixa, accesstype); -} - - -/*! - * \brief pixaaGetBoxa() - * - * \param[in] paa - * \param[in] accesstype L_COPY, L_CLONE - * \return boxa, or NULL on error - * - *- * Notes: - * (1) L_COPY returns a copy; L_CLONE returns a new reference to the boxa. - * (2) In both cases, invoke boxaDestroy() on the returned boxa. - *- */ -BOXA * -pixaaGetBoxa(PIXAA *paa, - l_int32 accesstype) -{ - PROCNAME("pixaaGetBoxa"); - - if (!paa) - return (BOXA *)ERROR_PTR("paa not defined", procName, NULL); - if (accesstype != L_COPY && accesstype != L_CLONE) - return (BOXA *)ERROR_PTR("invalid access type", procName, NULL); - - return boxaCopy(paa->boxa, accesstype); -} - - -/*! - * \brief pixaaGetPix() - * - * \param[in] paa - * \param[in] index index into the pixa array in the pixaa - * \param[in] ipix index into the pix array in the pixa - * \param[in] accessflag L_COPY or L_CLONE - * \return pix, or NULL on error - */ -PIX * -pixaaGetPix(PIXAA *paa, - l_int32 index, - l_int32 ipix, - l_int32 accessflag) -{ -PIX *pix; -PIXA *pixa; - - PROCNAME("pixaaGetPix"); - - if ((pixa = pixaaGetPixa(paa, index, L_CLONE)) == NULL) - return (PIX *)ERROR_PTR("pixa not retrieved", procName, NULL); - if ((pix = pixaGetPix(pixa, ipix, accessflag)) == NULL) - L_ERROR("pix not retrieved\n", procName); - pixaDestroy(&pixa); - return pix; -} - - -/*! - * \brief pixaaVerifyDepth() - * - * \param[in] paa - * \param[out] psame 1 if all pix have the same depth; 0 otherwise - * \param[out] pmaxd [optional] max depth of all pix in pixaa - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) It is considered to be an error if any pixa have no pix. - *- */ -l_ok -pixaaVerifyDepth(PIXAA *paa, - l_int32 *psame, - l_int32 *pmaxd) -{ -l_int32 i, n, d, maxd, same, samed; -PIXA *pixa; - - PROCNAME("pixaaVerifyDepth"); - - if (pmaxd) *pmaxd = 0; - if (!psame) - return ERROR_INT("psame not defined", procName, 1); - *psame = 0; - if (!paa) - return ERROR_INT("paa not defined", procName, 1); - if ((n = pixaaGetCount(paa, NULL)) == 0) - return ERROR_INT("no pixa in paa", procName, 1); - - pixa = pixaaGetPixa(paa, 0, L_CLONE); - pixaVerifyDepth(pixa, &same, &maxd); /* init same, maxd with first pixa */ - pixaDestroy(&pixa); - for (i = 1; i < n; i++) { - pixa = pixaaGetPixa(paa, i, L_CLONE); - pixaVerifyDepth(pixa, &samed, &d); - pixaDestroy(&pixa); - maxd = L_MAX(maxd, d); - if (!samed || maxd != d) - same = 0; - } - *psame = same; - if (pmaxd) *pmaxd = maxd; - return 0; -} - - -/*! - * \brief pixaaVerifyDimensions() - * - * \param[in] paa - * \param[out] psame 1 if all pix have the same depth; 0 otherwise - * \param[out] pmaxw [optional] max width of all pix in pixaa - * \param[out] pmaxh [optional] max height of all pix in pixaa - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) It is considered to be an error if any pixa have no pix. - *- */ -l_ok -pixaaVerifyDimensions(PIXAA *paa, - l_int32 *psame, - l_int32 *pmaxw, - l_int32 *pmaxh) -{ -l_int32 i, n, w, h, maxw, maxh, same, same2; -PIXA *pixa; - - PROCNAME("pixaaVerifyDimensions"); - - if (pmaxw) *pmaxw = 0; - if (pmaxh) *pmaxh = 0; - if (!psame) - return ERROR_INT("psame not defined", procName, 1); - *psame = 0; - if (!paa) - return ERROR_INT("paa not defined", procName, 1); - if ((n = pixaaGetCount(paa, NULL)) == 0) - return ERROR_INT("no pixa in paa", procName, 1); - - /* Init same; init maxw and maxh from first pixa */ - pixa = pixaaGetPixa(paa, 0, L_CLONE); - pixaVerifyDimensions(pixa, &same, &maxw, &maxh); - pixaDestroy(&pixa); - - for (i = 1; i < n; i++) { - pixa = pixaaGetPixa(paa, i, L_CLONE); - pixaVerifyDimensions(pixa, &same2, &w, &h); - pixaDestroy(&pixa); - maxw = L_MAX(maxw, w); - maxh = L_MAX(maxh, h); - if (!same2 || maxw != w || maxh != h) - same = 0; - } - *psame = same; - if (pmaxw) *pmaxw = maxw; - if (pmaxh) *pmaxh = maxh; - return 0; -} - - -/*! - * \brief pixaaIsFull() - * - * \param[in] paa - * \param[out] pfull 1 if all pixa in the paa have full pix arrays - * \return return 0 if OK, 1 on error - * - *- * Notes: - * (1) Does not require boxa associated with each pixa to be full. - *- */ -l_int32 -pixaaIsFull(PIXAA *paa, - l_int32 *pfull) -{ -l_int32 i, n, full; -PIXA *pixa; - - PROCNAME("pixaaIsFull"); - - if (!pfull) - return ERROR_INT("&full not defined", procName, 0); - *pfull = 0; - if (!paa) - return ERROR_INT("paa not defined", procName, 0); - - n = pixaaGetCount(paa, NULL); - full = 1; - for (i = 0; i < n; i++) { - pixa = pixaaGetPixa(paa, i, L_CLONE); - pixaIsFull(pixa, &full, NULL); - pixaDestroy(&pixa); - if (!full) break; - } - *pfull = full; - return 0; -} - - -/*---------------------------------------------------------------------* - * Pixaa array modifiers * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaaInitFull() - * - * \param[in] paa typically empty - * \param[in] pixa to be replicated into the entire pixa ptr array - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This initializes a pixaa by filling up the entire pixa ptr array - * with copies of %pixa. Any existing pixa are destroyed. - * (2) Example usage. This function is useful to prepare for a - * random insertion (or replacement) of pixa into a pixaa. - * To randomly insert pixa into a pixaa, up to some index "max": - * Pixaa *paa = pixaaCreate(max); - * Pixa *pixa = pixaCreate(1); // if you want little memory - * pixaaInitFull(paa, pixa); // copy it to entire array - * pixaDestroy(&pixa); // no longer needed - * The initialization allows the pixaa to always be properly filled. - *- */ -l_ok -pixaaInitFull(PIXAA *paa, - PIXA *pixa) -{ -l_int32 i, n; -PIXA *pixat; - - PROCNAME("pixaaInitFull"); - - if (!paa) - return ERROR_INT("paa not defined", procName, 1); - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - - n = paa->nalloc; - paa->n = n; - for (i = 0; i < n; i++) { - pixat = pixaCopy(pixa, L_COPY); - pixaaReplacePixa(paa, i, pixat); - } - - return 0; -} - - -/*! - * \brief pixaaReplacePixa() - * - * \param[in] paa - * \param[in] index to the index-th pixa - * \param[in] pixa insert to replace existing one - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This allows random insertion of a pixa into a pixaa, with - * destruction of any existing pixa at that location. - * The input pixa is now owned by the pixaa. - * (2) No other pixa in the array are affected. - * (3) The index must be within the allowed set. - *- */ -l_ok -pixaaReplacePixa(PIXAA *paa, - l_int32 index, - PIXA *pixa) -{ - - PROCNAME("pixaaReplacePixa"); - - if (!paa) - return ERROR_INT("paa not defined", procName, 1); - if (index < 0 || index >= paa->n) - return ERROR_INT("index not valid", procName, 1); - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - - pixaDestroy(&(paa->pixa[index])); - paa->pixa[index] = pixa; - return 0; -} - - -/*! - * \brief pixaaClear() - * - * \param[in] paa - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This destroys all pixa in the pixaa, and nulls the ptrs - * in the pixa ptr array. - *- */ -l_ok -pixaaClear(PIXAA *paa) -{ -l_int32 i, n; - - PROCNAME("pixaClear"); - - if (!paa) - return ERROR_INT("paa not defined", procName, 1); - - n = pixaaGetCount(paa, NULL); - for (i = 0; i < n; i++) - pixaDestroy(&paa->pixa[i]); - paa->n = 0; - return 0; -} - - -/*! - * \brief pixaaTruncate() - * - * \param[in] paa - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This identifies the largest index containing a pixa that - * has any pix within it, destroys all pixa above that index, - * and resets the count. - *- */ -l_ok -pixaaTruncate(PIXAA *paa) -{ -l_int32 i, n, np; -PIXA *pixa; - - PROCNAME("pixaaTruncate"); - - if (!paa) - return ERROR_INT("paa not defined", procName, 1); - - n = pixaaGetCount(paa, NULL); - for (i = n - 1; i >= 0; i--) { - pixa = pixaaGetPixa(paa, i, L_CLONE); - if (!pixa) { - paa->n--; - continue; - } - np = pixaGetCount(pixa); - pixaDestroy(&pixa); - if (np == 0) { - pixaDestroy(&paa->pixa[i]); - paa->n--; - } else { - break; - } - } - return 0; -} - - - -/*---------------------------------------------------------------------* - * Pixa serialized I/O * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaRead() - * - * \param[in] filename - * \return pixa, or NULL on error - * - *- * Notes: - * (1) The pix are stored in the file as png. - * If the png library is not linked, this will fail. - *- */ -PIXA * -pixaRead(const char *filename) -{ -FILE *fp; -PIXA *pixa; - - PROCNAME("pixaRead"); - -#if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ - return (PIXA *)ERROR_PTR("no libpng: can't read data", procName, NULL); -#endif /* !HAVE_LIBPNG */ - - if (!filename) - return (PIXA *)ERROR_PTR("filename not defined", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (PIXA *)ERROR_PTR("stream not opened", procName, NULL); - pixa = pixaReadStream(fp); - fclose(fp); - if (!pixa) - return (PIXA *)ERROR_PTR("pixa not read", procName, NULL); - return pixa; -} - - -/*! - * \brief pixaReadStream() - * - * \param[in] fp file stream - * \return pixa, or NULL on error - * - *- * Notes: - * (1) The pix are stored in the file as png. - * If the png library is not linked, this will fail. - *- */ -PIXA * -pixaReadStream(FILE *fp) -{ -l_int32 n, i, xres, yres, version; -l_int32 ignore; -BOXA *boxa; -PIX *pix; -PIXA *pixa; - - PROCNAME("pixaReadStream"); - -#if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ - return (PIXA *)ERROR_PTR("no libpng: can't read data", procName, NULL); -#endif /* !HAVE_LIBPNG */ - - if (!fp) - return (PIXA *)ERROR_PTR("stream not defined", procName, NULL); - - if (fscanf(fp, "\nPixa Version %d\n", &version) != 1) - return (PIXA *)ERROR_PTR("not a pixa file", procName, NULL); - if (version != PIXA_VERSION_NUMBER) - return (PIXA *)ERROR_PTR("invalid pixa version", procName, NULL); - if (fscanf(fp, "Number of pix = %d\n", &n) != 1) - return (PIXA *)ERROR_PTR("not a pixa file", procName, NULL); - - if ((boxa = boxaReadStream(fp)) == NULL) - return (PIXA *)ERROR_PTR("boxa not made", procName, NULL); - if ((pixa = pixaCreate(n)) == NULL) { - boxaDestroy(&boxa); - return (PIXA *)ERROR_PTR("pixa not made", procName, NULL); - } - boxaDestroy(&pixa->boxa); - pixa->boxa = boxa; - - for (i = 0; i < n; i++) { - if ((fscanf(fp, " pix[%d]: xres = %d, yres = %d\n", - &ignore, &xres, &yres)) != 3) { - pixaDestroy(&pixa); - return (PIXA *)ERROR_PTR("res reading error", procName, NULL); - } - if ((pix = pixReadStreamPng(fp)) == NULL) { - pixaDestroy(&pixa); - return (PIXA *)ERROR_PTR("pix not read", procName, NULL); - } - pixSetXRes(pix, xres); - pixSetYRes(pix, yres); - pixaAddPix(pixa, pix, L_INSERT); - } - return pixa; -} - - -/*! - * \brief pixaReadMem() - * - * \param[in] data of serialized pixa - * \param[in] size of data in bytes - * \return pixa, or NULL on error - */ -PIXA * -pixaReadMem(const l_uint8 *data, - size_t size) -{ -FILE *fp; -PIXA *pixa; - - PROCNAME("pixaReadMem"); - - if (!data) - return (PIXA *)ERROR_PTR("data not defined", procName, NULL); - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return (PIXA *)ERROR_PTR("stream not opened", procName, NULL); - - pixa = pixaReadStream(fp); - fclose(fp); - if (!pixa) L_ERROR("pixa not read\n", procName); - return pixa; -} - - -/*! - * \brief pixaWriteDebug() - * - * \param[in] fname - * \param[in] pixa - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) Debug version, intended for use in the library when writing - * to files in a temp directory with names that are compiled in. - * This is used instead of pixaWrite() for all such library calls. - * (2) The global variable LeptDebugOK defaults to 0, and can be set - * or cleared by the function setLeptDebugOK(). - *- */ -l_ok -pixaWriteDebug(const char *fname, - PIXA *pixa) -{ - PROCNAME("pixaWriteDebug"); - - if (LeptDebugOK) { - return pixaWrite(fname, pixa); - } else { - L_INFO("write to named temp file %s is disabled\n", procName, fname); - return 0; - } -} - - -/*! - * \brief pixaWrite() - * - * \param[in] filename - * \param[in] pixa - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The pix are stored in the file as png. - * If the png library is not linked, this will fail. - *- */ -l_ok -pixaWrite(const char *filename, - PIXA *pixa) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("pixaWrite"); - -#if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ - return ERROR_INT("no libpng: can't write data", procName, 1); -#endif /* !HAVE_LIBPNG */ - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "wb")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = pixaWriteStream(fp, pixa); - fclose(fp); - if (ret) - return ERROR_INT("pixa not written to stream", procName, 1); - return 0; -} - - -/*! - * \brief pixaWriteStream() - * - * \param[in] fp file stream opened for "wb" - * \param[in] pixa - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The pix are stored in the file as png. - * If the png library is not linked, this will fail. - *- */ -l_ok -pixaWriteStream(FILE *fp, - PIXA *pixa) -{ -l_int32 n, i; -PIX *pix; - - PROCNAME("pixaWriteStream"); - -#if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ - return ERROR_INT("no libpng: can't write data", procName, 1); -#endif /* !HAVE_LIBPNG */ - - 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; -} - - -/*! - * \brief pixaWriteMem() - * - * \param[out] pdata data of serialized pixa - * \param[out] psize size of returned data - * \param[in] pixa - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Serializes a pixa in memory and puts the result in a buffer. - *- */ -l_ok -pixaWriteMem(l_uint8 **pdata, - size_t *psize, - PIXA *pixa) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("pixaWriteMem"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - if (!psize) - return ERROR_INT("&size not defined", procName, 1); - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = pixaWriteStream(fp, pixa); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = pixaWriteStream(fp, pixa); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} - - -/*! - * \brief pixaReadBoth() - * - * \param[in] filename - * \return pixa, or NULL on error - * - *- * Notes: - * (1) This reads serialized files of either a pixa or a pixacomp, - * and returns a pixa in memory. It requires png and jpeg libraries. - *- */ -PIXA * -pixaReadBoth(const char *filename) -{ -char buf[32]; -char *sname; -PIXA *pixa; -PIXAC *pac; - - PROCNAME("pixaReadBoth"); - - if (!filename) - return (PIXA *)ERROR_PTR("filename not defined", procName, NULL); - - l_getStructStrFromFile(filename, L_STR_NAME, &sname); - if (!sname) - return (PIXA *)ERROR_PTR("struct name not found", procName, NULL); - snprintf(buf, sizeof(buf), "%s", sname); - LEPT_FREE(sname); - - if (strcmp(buf, "Pixacomp") == 0) { - if ((pac = pixacompRead(filename)) == NULL) - return (PIXA *)ERROR_PTR("pac not made", procName, NULL); - pixa = pixaCreateFromPixacomp(pac, L_COPY); - pixacompDestroy(&pac); - } else if (strcmp(buf, "Pixa") == 0) { - if ((pixa = pixaRead(filename)) == NULL) - return (PIXA *)ERROR_PTR("pixa not made", procName, NULL); - } else { - return (PIXA *)ERROR_PTR("invalid file type", procName, NULL); - } - return pixa; -} - - -/*---------------------------------------------------------------------* - * Pixaa serialized I/O * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaaReadFromFiles() - * - * \param[in] dirname directory - * \param[in] substr [optional] substring filter on filenames; can be NULL - * \param[in] first 0-based - * \param[in] nfiles use 0 for everything from %first to the end - * \return paa, or NULL on error or if no pixa files are found. - * - *- * Notes: - * (1) The files must be serialized pixa files (e.g., *.pa) - * If some files cannot be read, warnings are issued. - * (2) Use %substr to filter filenames in the directory. If - * %substr == NULL, this takes all files. - * (3) After filtering, use %first and %nfiles to select - * a contiguous set of files, that have been lexically - * sorted in increasing order. - *- */ -PIXAA * -pixaaReadFromFiles(const char *dirname, - const char *substr, - l_int32 first, - l_int32 nfiles) -{ -char *fname; -l_int32 i, n; -PIXA *pixa; -PIXAA *paa; -SARRAY *sa; - - PROCNAME("pixaaReadFromFiles"); - - if (!dirname) - return (PIXAA *)ERROR_PTR("dirname not defined", procName, NULL); - - sa = getSortedPathnamesInDirectory(dirname, substr, first, nfiles); - if (!sa || ((n = sarrayGetCount(sa)) == 0)) { - sarrayDestroy(&sa); - return (PIXAA *)ERROR_PTR("no pixa files found", procName, NULL); - } - - paa = pixaaCreate(n); - for (i = 0; i < n; i++) { - fname = sarrayGetString(sa, i, L_NOCOPY); - if ((pixa = pixaRead(fname)) == NULL) { - L_ERROR("pixa not read for %d-th file", procName, i); - continue; - } - pixaaAddPixa(paa, pixa, L_INSERT); - } - - sarrayDestroy(&sa); - return paa; -} - - -/*! - * \brief pixaaRead() - * - * \param[in] filename - * \return paa, or NULL on error - * - *- * Notes: - * (1) The pix are stored in the file as png. - * If the png library is not linked, this will fail. - *- */ -PIXAA * -pixaaRead(const char *filename) -{ -FILE *fp; -PIXAA *paa; - - PROCNAME("pixaaRead"); - -#if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ - return (PIXAA *)ERROR_PTR("no libpng: can't read data", procName, NULL); -#endif /* !HAVE_LIBPNG */ - - if (!filename) - return (PIXAA *)ERROR_PTR("filename not defined", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (PIXAA *)ERROR_PTR("stream not opened", procName, NULL); - paa = pixaaReadStream(fp); - fclose(fp); - if (!paa) - return (PIXAA *)ERROR_PTR("paa not read", procName, NULL); - return paa; -} - - -/*! - * \brief pixaaReadStream() - * - * \param[in] fp file stream - * \return paa, or NULL on error - * - *- * Notes: - * (1) The pix are stored in the file as png. - * If the png library is not linked, this will fail. - *- */ -PIXAA * -pixaaReadStream(FILE *fp) -{ -l_int32 n, i, version; -l_int32 ignore; -BOXA *boxa; -PIXA *pixa; -PIXAA *paa; - - PROCNAME("pixaaReadStream"); - -#if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ - return (PIXAA *)ERROR_PTR("no libpng: can't read data", procName, NULL); -#endif /* !HAVE_LIBPNG */ - - if (!fp) - return (PIXAA *)ERROR_PTR("stream not defined", procName, NULL); - - if (fscanf(fp, "\nPixaa Version %d\n", &version) != 1) - return (PIXAA *)ERROR_PTR("not a pixaa file", procName, NULL); - if (version != PIXAA_VERSION_NUMBER) - return (PIXAA *)ERROR_PTR("invalid pixaa version", procName, NULL); - if (fscanf(fp, "Number of pixa = %d\n", &n) != 1) - return (PIXAA *)ERROR_PTR("not a pixaa file", procName, NULL); - - if ((paa = pixaaCreate(n)) == NULL) - return (PIXAA *)ERROR_PTR("paa not made", procName, NULL); - if ((boxa = boxaReadStream(fp)) == NULL) { - pixaaDestroy(&paa); - return (PIXAA *)ERROR_PTR("boxa not made", procName, NULL); - } - boxaDestroy(&paa->boxa); - paa->boxa = boxa; - - for (i = 0; i < n; i++) { - if ((fscanf(fp, "\n\n --------------- pixa[%d] ---------------\n", - &ignore)) != 1) { - pixaaDestroy(&paa); - return (PIXAA *)ERROR_PTR("text reading", procName, NULL); - } - if ((pixa = pixaReadStream(fp)) == NULL) { - pixaaDestroy(&paa); - return (PIXAA *)ERROR_PTR("pixa not read", procName, NULL); - } - pixaaAddPixa(paa, pixa, L_INSERT); - } - - return paa; -} - - -/*! - * \brief pixaaReadMem() - * - * \param[in] data of serialized pixaa - * \param[in] size of data in bytes - * \return paa, or NULL on error - */ -PIXAA * -pixaaReadMem(const l_uint8 *data, - size_t size) -{ -FILE *fp; -PIXAA *paa; - - PROCNAME("paaReadMem"); - - if (!data) - return (PIXAA *)ERROR_PTR("data not defined", procName, NULL); - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return (PIXAA *)ERROR_PTR("stream not opened", procName, NULL); - - paa = pixaaReadStream(fp); - fclose(fp); - if (!paa) L_ERROR("paa not read\n", procName); - return paa; -} - - -/*! - * \brief pixaaWrite() - * - * \param[in] filename - * \param[in] paa - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The pix are stored in the file as png. - * If the png library is not linked, this will fail. - *- */ -l_ok -pixaaWrite(const char *filename, - PIXAA *paa) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("pixaaWrite"); - -#if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ - return ERROR_INT("no libpng: can't read data", procName, 1); -#endif /* !HAVE_LIBPNG */ - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!paa) - return ERROR_INT("paa not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "wb")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = pixaaWriteStream(fp, paa); - fclose(fp); - if (ret) - return ERROR_INT("paa not written to stream", procName, 1); - return 0; -} - - -/*! - * \brief pixaaWriteStream() - * - * \param[in] fp file stream opened for "wb" - * \param[in] paa - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The pix are stored in the file as png. - * If the png library is not linked, this will fail. - *- */ -l_ok -pixaaWriteStream(FILE *fp, - PIXAA *paa) -{ -l_int32 n, i; -PIXA *pixa; - - PROCNAME("pixaaWriteStream"); - -#if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ - return ERROR_INT("no libpng: can't read data", procName, 1); -#endif /* !HAVE_LIBPNG */ - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!paa) - return ERROR_INT("paa not defined", procName, 1); - - n = pixaaGetCount(paa, NULL); - fprintf(fp, "\nPixaa Version %d\n", PIXAA_VERSION_NUMBER); - fprintf(fp, "Number of pixa = %d\n", n); - boxaWriteStream(fp, paa->boxa); - for (i = 0; i < n; i++) { - if ((pixa = pixaaGetPixa(paa, i, L_CLONE)) == NULL) - return ERROR_INT("pixa not found", procName, 1); - fprintf(fp, "\n\n --------------- pixa[%d] ---------------\n", i); - pixaWriteStream(fp, pixa); - pixaDestroy(&pixa); - } - return 0; -} - - -/*! - * \brief pixaaWriteMem() - * - * \param[out] pdata data of serialized pixaa - * \param[out] psize size of returned data - * \param[in] paa - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Serializes a pixaa in memory and puts the result in a buffer. - *- */ -l_ok -pixaaWriteMem(l_uint8 **pdata, - size_t *psize, - PIXAA *paa) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("pixaaWriteMem"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - if (!psize) - return ERROR_INT("&size not defined", procName, 1); - if (!paa) - return ERROR_INT("paa not defined", procName, 1); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = pixaaWriteStream(fp, paa); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = pixaaWriteStream(fp, paa); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} - diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixacc.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixacc.c deleted file mode 100644 index fbf54ba8..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixacc.c +++ /dev/null @@ -1,356 +0,0 @@ -/*====================================================================* - - 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 pixacc.c - *- * - * Pixacc creation, destruction - * PIXACC *pixaccCreate() - * PIXACC *pixaccCreateFromPix() - * void pixaccDestroy() - * - * Pixacc finalization - * PIX *pixaccFinal() - * - * Pixacc accessors - * PIX *pixaccGetPix() - * l_int32 pixaccGetOffset() - * - * Pixacc accumulators - * l_int32 pixaccAdd() - * l_int32 pixaccSubtract() - * l_int32 pixaccMultConst() - * l_int32 pixaccMultConstAccumulate() - * - * This is a simple interface for some of the pixel arithmetic operations - * in pixarith.c. These are easy to code up, but not as fast as - * hand-coded functions that do arithmetic on corresponding pixels. - * - * Suppose you want to make a linear combination of pix1 and pix2: - * pixd = 0.4 * pix1 + 0.6 * pix2 - * where pix1 and pix2 are the same size and have depth 'd'. Then: - * Pixacc *pacc = pixaccCreateFromPix(pix1, 0); // first; addition only - * pixaccMultConst(pacc, 0.4); - * pixaccMultConstAccumulate(pacc, pix2, 0.6); // Add in 0.6 of the second - * pixd = pixaccFinal(pacc, d); // Get the result - * pixaccDestroy(&pacc); - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include "allheaders.h" - -/*---------------------------------------------------------------------* - * Pixacc creation, destruction * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaccCreate() - * - * \param[in] w, h of 32 bpp internal Pix - * \param[in] negflag 0 if only positive numbers are involved; - * 1 if there will be negative numbers - * \return pixacc, or NULL on error - * - * - * Notes: - * (1) Use %negflag = 1 for safety if any negative numbers are going - * to be used in the chain of operations. Negative numbers - * arise, e.g., by subtracting a pix, or by adding a pix - * that has been pre-multiplied by a negative number. - * (2) Initializes the internal 32 bpp pix, similarly to the - * initialization in pixInitAccumulate(). - *- */ -PIXACC * -pixaccCreate(l_int32 w, - l_int32 h, - l_int32 negflag) -{ -PIXACC *pixacc; - - PROCNAME("pixaccCreate"); - - if ((pixacc = (PIXACC *)LEPT_CALLOC(1, sizeof(PIXACC))) == NULL) - return (PIXACC *)ERROR_PTR("pixacc not made", procName, NULL); - pixacc->w = w; - pixacc->h = h; - - if ((pixacc->pix = pixCreate(w, h, 32)) == NULL) { - pixaccDestroy(&pixacc); - return (PIXACC *)ERROR_PTR("pix not made", procName, NULL); - } - - if (negflag) { - pixacc->offset = 0x40000000; - pixSetAllArbitrary(pixacc->pix, pixacc->offset); - } - - return pixacc; -} - - -/*! - * \brief pixaccCreateFromPix() - * - * \param[in] pix - * \param[in] negflag 0 if only positive numbers are involved; - * 1 if there will be negative numbers - * \return pixacc, or NULL on error - * - *- * Notes: - * (1) See pixaccCreate() - *- */ -PIXACC * -pixaccCreateFromPix(PIX *pix, - l_int32 negflag) -{ -l_int32 w, h; -PIXACC *pixacc; - - PROCNAME("pixaccCreateFromPix"); - - if (!pix) - return (PIXACC *)ERROR_PTR("pix not defined", procName, NULL); - - pixGetDimensions(pix, &w, &h, NULL); - pixacc = pixaccCreate(w, h, negflag); - pixaccAdd(pixacc, pix); - return pixacc; -} - - -/*! - * \brief pixaccDestroy() - * - * \param[in,out] ppixacc will be set to null before returning - * \return void - * - *- * Notes: - * (1) Always nulls the input ptr. - *- */ -void -pixaccDestroy(PIXACC **ppixacc) -{ -PIXACC *pixacc; - - PROCNAME("pixaccDestroy"); - - if (ppixacc == NULL) { - L_WARNING("ptr address is NULL!", procName); - return; - } - - if ((pixacc = *ppixacc) == NULL) - return; - - pixDestroy(&pixacc->pix); - LEPT_FREE(pixacc); - *ppixacc = NULL; - return; -} - - -/*---------------------------------------------------------------------* - * Pixacc finalization * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaccFinal() - * - * \param[in] pixacc - * \param[in] outdepth 8, 16 or 32 bpp - * \return pixd 8, 16 or 32 bpp, or NULL on error - */ -PIX * -pixaccFinal(PIXACC *pixacc, - l_int32 outdepth) -{ - PROCNAME("pixaccFinal"); - - if (!pixacc) - return (PIX *)ERROR_PTR("pixacc not defined", procName, NULL); - - return pixFinalAccumulate(pixaccGetPix(pixacc), pixaccGetOffset(pixacc), - outdepth); -} - - -/*---------------------------------------------------------------------* - * Pixacc accessors * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaccGetPix() - * - * \param[in] pixacc - * \return pix, or NULL on error - */ -PIX * -pixaccGetPix(PIXACC *pixacc) -{ - PROCNAME("pixaccGetPix"); - - if (!pixacc) - return (PIX *)ERROR_PTR("pixacc not defined", procName, NULL); - return pixacc->pix; -} - - -/*! - * \brief pixaccGetOffset() - * - * \param[in] pixacc - * \return offset, or -1 on error - */ -l_int32 -pixaccGetOffset(PIXACC *pixacc) -{ - PROCNAME("pixaccGetOffset"); - - if (!pixacc) - return ERROR_INT("pixacc not defined", procName, -1); - return pixacc->offset; -} - - -/*---------------------------------------------------------------------* - * Pixacc accumulators * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaccAdd() - * - * \param[in] pixacc - * \param[in] pix to be added - * \return 0 if OK, 1 on error - */ -l_ok -pixaccAdd(PIXACC *pixacc, - PIX *pix) -{ - PROCNAME("pixaccAdd"); - - if (!pixacc) - return ERROR_INT("pixacc not defined", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - pixAccumulate(pixaccGetPix(pixacc), pix, L_ARITH_ADD); - return 0; -} - - -/*! - * \brief pixaccSubtract() - * - * \param[in] pixacc - * \param[in] pix to be subtracted - * \return 0 if OK, 1 on error - */ -l_ok -pixaccSubtract(PIXACC *pixacc, - PIX *pix) -{ - PROCNAME("pixaccSubtract"); - - if (!pixacc) - return ERROR_INT("pixacc not defined", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - pixAccumulate(pixaccGetPix(pixacc), pix, L_ARITH_SUBTRACT); - return 0; -} - - -/*! - * \brief pixaccMultConst() - * - * \param[in] pixacc - * \param[in] factor - * \return 0 if OK, 1 on error - */ -l_ok -pixaccMultConst(PIXACC *pixacc, - l_float32 factor) -{ - PROCNAME("pixaccMultConst"); - - if (!pixacc) - return ERROR_INT("pixacc not defined", procName, 1); - pixMultConstAccumulate(pixaccGetPix(pixacc), factor, - pixaccGetOffset(pixacc)); - return 0; -} - - -/*! - * \brief pixaccMultConstAccumulate() - * - * \param[in] pixacc - * \param[in] pix - * \param[in] factor - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This creates a temp pix that is %pix multiplied by the - * constant %factor. It then adds that into %pixacc. - *- */ -l_ok -pixaccMultConstAccumulate(PIXACC *pixacc, - PIX *pix, - l_float32 factor) -{ -l_int32 w, h, d, negflag; -PIX *pixt; -PIXACC *pacct; - - PROCNAME("pixaccMultConstAccumulate"); - - if (!pixacc) - return ERROR_INT("pixacc not defined", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - if (factor == 0.0) return 0; - - pixGetDimensions(pix, &w, &h, &d); - negflag = (factor > 0.0) ? 0 : 1; - pacct = pixaccCreate(w, h, negflag); - pixaccAdd(pacct, pix); - pixaccMultConst(pacct, factor); - pixt = pixaccFinal(pacct, d); - pixaccAdd(pixacc, pixt); - - pixaccDestroy(&pacct); - pixDestroy(&pixt); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixafunc1.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixafunc1.c deleted file mode 100644 index 7c9547be..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixafunc1.c +++ /dev/null @@ -1,2975 +0,0 @@ -/*====================================================================* - - 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 pixafunc1.c - *- * - * Filters - * PIX *pixSelectBySize() - * PIXA *pixaSelectBySize() - * NUMA *pixaMakeSizeIndicator() - * - * PIX *pixSelectByPerimToAreaRatio() - * PIXA *pixaSelectByPerimToAreaRatio() - * PIX *pixSelectByPerimSizeRatio() - * PIXA *pixaSelectByPerimSizeRatio() - * PIX *pixSelectByAreaFraction() - * PIXA *pixaSelectByAreaFraction() - * PIX *pixSelectByWidthHeightRatio() - * PIXA *pixaSelectByWidthHeightRatio() - * PIXA *pixaSelectByNumConnComp() - * - * PIXA *pixaSelectWithIndicator() - * l_int32 pixRemoveWithIndicator() - * l_int32 pixAddWithIndicator() - * PIXA *pixaSelectWithString() - * PIX *pixaRenderComponent() - * - * Sort functions - * PIXA *pixaSort() - * PIXA *pixaBinSort() - * PIXA *pixaSortByIndex() - * PIXAA *pixaSort2dByIndex() - * - * Pixa and Pixaa range selection - * PIXA *pixaSelectRange() - * PIXAA *pixaaSelectRange() - * - * Pixa and Pixaa scaling - * PIXAA *pixaaScaleToSize() - * PIXAA *pixaaScaleToSizeVar() - * PIXA *pixaScaleToSize() - * PIXA *pixaScaleToSizeRel() - * PIXA *pixaScale() - * PIXA *pixaScaleBySampling() - * - * Pixa rotation and translation - * PIXA *pixaRotate() - * PIXA *pixaRotateOrth() - * PIXA *pixaTranslate() - * - * Miscellaneous - * PIXA *pixaAddBorderGeneral() - * PIXA *pixaaFlattenToPixa() - * l_int32 pixaaSizeRange() - * l_int32 pixaSizeRange() - * PIXA *pixaClipToPix() - * PIXA *pixaClipToForeground() - * l_int32 pixaGetRenderingDepth() - * l_int32 pixaHasColor() - * l_int32 pixaAnyColormaps() - * l_int32 pixaGetDepthInfo() - * PIXA *pixaConvertToSameDepth() - * l_int32 pixaEqual() - * l_int32 pixaSetFullSizeBoxa() - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include "allheaders.h" - - /* For more than this number of c.c. in a binarized image of - * semi-perimeter (w + h) about 5000 or less, the O(n) binsort - * is faster than the O(nlogn) shellsort. */ -static const l_int32 MinCompsForBinSort = 200; - - /* Don't rotate any angle smaller than this */ -static const l_float32 MinAngleToRotate = 0.001; /* radians; ~0.06 deg */ - -/*---------------------------------------------------------------------* - * Filters * - *---------------------------------------------------------------------*/ -/* - * These filters work on the connected components of 1 bpp images. - * They are typically used on pixa that have been generated from a Pix - * using pixConnComp(), so that the corresponding Boxa is available. - * - * The filters remove or retain c.c. based on these properties: - * (a) size [pixaFindDimensions()] - * (b) area-to-perimeter ratio [pixaFindAreaPerimRatio()] - * (c) foreground area as a fraction of bounding box area (w * h) - * [pixaFindForegroundArea()] - * (d) number of foreground pixels [pixaCountPixels()] - * (e) width/height aspect ratio [pixFindWidthHeightRatio()] - * - * We provide two different high-level interfaces: - * (1) Functions that use one of the filters on either - * a pix or the pixa of components. - * (2) A general method that generates numas of indicator functions, - * logically combines them, and efficiently removes or adds - * the selected components. - * - * For interface (1), the filtering is performed with a single function call. - * This is the easiest way to do simple filtering. These functions - * are named pixSelectBy*() and pixaSelectBy*(), where the '*' is one of: - * Size - * PerimToAreaRatio - * PerimSizeRatio - * AreaFraction - * WidthHeightRatio - * - * For more complicated filtering, use the general method (2). - * The numa indicator functions for a pixa are generated by these functions: - * pixaFindDimensions() - * pixaFindPerimToAreaRatio() - * pixaFindPerimSizeRatio() - * pixaFindAreaFraction() - * pixaCountPixels() - * pixaFindWidthHeightRatio() - * pixaFindWidthHeightProduct() - * - * Here is an illustration using the general method. Suppose you want - * all 8-connected components that have a height greater than 40 pixels, - * a width not more than 30 pixels, between 150 and 300 fg pixels, - * and a perimeter-to-size ratio between 1.2 and 2.0. - * - * // Generate the pixa of 8 cc pieces. - * boxa = pixConnComp(pixs, &pixa, 8); - * - * // Extract the data we need about each component. - * pixaFindDimensions(pixa, &naw, &nah); - * nas = pixaCountPixels(pixa); - * nar = pixaFindPerimSizeRatio(pixa); - * - * // Build the indicator arrays for the set of components, - * // based on thresholds and selection criteria. - * na1 = numaMakeThresholdIndicator(nah, 40, L_SELECT_IF_GT); - * na2 = numaMakeThresholdIndicator(naw, 30, L_SELECT_IF_LTE); - * na3 = numaMakeThresholdIndicator(nas, 150, L_SELECT_IF_GTE); - * na4 = numaMakeThresholdIndicator(nas, 300, L_SELECT_IF_LTE); - * na5 = numaMakeThresholdIndicator(nar, 1.2, L_SELECT_IF_GTE); - * na6 = numaMakeThresholdIndicator(nar, 2.0, L_SELECT_IF_LTE); - * - * // Combine the indicator arrays logically to find - * // the components that will be retained. - * nad = numaLogicalOp(NULL, na1, na2, L_INTERSECTION); - * numaLogicalOp(nad, nad, na3, L_INTERSECTION); - * numaLogicalOp(nad, nad, na4, L_INTERSECTION); - * numaLogicalOp(nad, nad, na5, L_INTERSECTION); - * numaLogicalOp(nad, nad, na6, L_INTERSECTION); - * - * // Invert to get the components that will be removed. - * numaInvert(nad, nad); - * - * // Remove the components, in-place. - * pixRemoveWithIndicator(pixs, pixa, nad); - */ - - -/*! - * \brief pixSelectBySize() - * - * \param[in] pixs 1 bpp - * \param[in] width, height threshold dimensions - * \param[in] connectivity 4 or 8 - * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT, - * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH - * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \param[out] pchanged [optional] 1 if changed; 0 otherwise - * \return filtered pixd, or NULL on error - * - * - * Notes: - * (1) The args specify constraints on the size of the - * components that are kept. - * (2) If unchanged, returns a copy of pixs. Otherwise, - * returns a new pix with the filtered components. - * (3) If the selection type is L_SELECT_WIDTH, the input - * height is ignored, and v.v. - * (4) To keep small components, use relation = L_SELECT_IF_LT or - * L_SELECT_IF_LTE. - * To keep large components, use relation = L_SELECT_IF_GT or - * L_SELECT_IF_GTE. - *- */ -PIX * -pixSelectBySize(PIX *pixs, - l_int32 width, - l_int32 height, - l_int32 connectivity, - l_int32 type, - l_int32 relation, - l_int32 *pchanged) -{ -l_int32 w, h, empty, changed, count; -BOXA *boxa; -PIX *pixd; -PIXA *pixas, *pixad; - - PROCNAME("pixSelectBySize"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (connectivity != 4 && connectivity != 8) - return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); - if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && - type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && - relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) - return (PIX *)ERROR_PTR("invalid relation", procName, NULL); - if (pchanged) *pchanged = FALSE; - - /* Check if any components exist */ - pixZero(pixs, &empty); - if (empty) - return pixCopy(NULL, pixs); - - /* Identify and select the components */ - boxa = pixConnComp(pixs, &pixas, connectivity); - pixad = pixaSelectBySize(pixas, width, height, type, relation, &changed); - boxaDestroy(&boxa); - pixaDestroy(&pixas); - - if (!changed) { - pixaDestroy(&pixad); - return pixCopy(NULL, pixs); - } - - /* Render the result */ - 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; -} - - -/*! - * \brief pixaSelectBySize() - * - * \param[in] pixas - * \param[in] width, height threshold dimensions - * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT, - * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH - * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \param[out] pchanged [optional] 1 if changed; 0 otherwise - * \return pixad, or NULL on error - * - *- * Notes: - * (1) The args specify constraints on the size of the - * components that are kept. - * (2) Uses pix and box clones in the new pixa. - * (3) If the selection type is L_SELECT_WIDTH, the input - * height is ignored, and v.v. - * (4) To keep small components, use relation = L_SELECT_IF_LT or - * L_SELECT_IF_LTE. - * To keep large components, use relation = L_SELECT_IF_GT or - * L_SELECT_IF_GTE. - *- */ -PIXA * -pixaSelectBySize(PIXA *pixas, - l_int32 width, - l_int32 height, - l_int32 type, - l_int32 relation, - l_int32 *pchanged) -{ -NUMA *na; -PIXA *pixad; - - PROCNAME("pixaSelectBySize"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && - type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) - return (PIXA *)ERROR_PTR("invalid type", procName, NULL); - if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && - relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) - return (PIXA *)ERROR_PTR("invalid relation", procName, NULL); - - /* Compute the indicator array for saving components */ - na = pixaMakeSizeIndicator(pixas, width, height, type, relation); - - /* Filter to get output */ - pixad = pixaSelectWithIndicator(pixas, na, pchanged); - - numaDestroy(&na); - return pixad; -} - - -/*! - * \brief pixaMakeSizeIndicator() - * - * \param[in] pixa - * \param[in] width, height threshold dimensions - * \param[in] type L_SELECT_WIDTH, L_SELECT_HEIGHT, - * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH - * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \return na indicator array, or NULL on error - * - *- * Notes: - * (1) The args specify constraints on the size of the - * components that are kept. - * (2) If the selection type is L_SELECT_WIDTH, the input - * height is ignored, and v.v. - * (3) To keep small components, use relation = L_SELECT_IF_LT or - * L_SELECT_IF_LTE. - * To keep large components, use relation = L_SELECT_IF_GT or - * L_SELECT_IF_GTE. - *- */ -NUMA * -pixaMakeSizeIndicator(PIXA *pixa, - l_int32 width, - l_int32 height, - l_int32 type, - l_int32 relation) -{ -l_int32 i, n, w, h, ival; -NUMA *na; - - PROCNAME("pixaMakeSizeIndicator"); - - if (!pixa) - return (NUMA *)ERROR_PTR("pixa not defined", procName, NULL); - if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT && - type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) - return (NUMA *)ERROR_PTR("invalid type", procName, NULL); - if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && - relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) - return (NUMA *)ERROR_PTR("invalid relation", procName, NULL); - - n = pixaGetCount(pixa); - na = numaCreate(n); - for (i = 0; i < n; i++) { - ival = 0; - pixaGetPixDimensions(pixa, i, &w, &h, NULL); - switch (type) - { - case L_SELECT_WIDTH: - if ((relation == L_SELECT_IF_LT && w < width) || - (relation == L_SELECT_IF_GT && w > width) || - (relation == L_SELECT_IF_LTE && w <= width) || - (relation == L_SELECT_IF_GTE && w >= width)) - ival = 1; - break; - case L_SELECT_HEIGHT: - if ((relation == L_SELECT_IF_LT && h < height) || - (relation == L_SELECT_IF_GT && h > height) || - (relation == L_SELECT_IF_LTE && h <= height) || - (relation == L_SELECT_IF_GTE && h >= height)) - ival = 1; - break; - case L_SELECT_IF_EITHER: - if (((relation == L_SELECT_IF_LT) && (w < width || h < height)) || - ((relation == L_SELECT_IF_GT) && (w > width || h > height)) || - ((relation == L_SELECT_IF_LTE) && (w <= width || h <= height)) || - ((relation == L_SELECT_IF_GTE) && (w >= width || h >= height))) - ival = 1; - break; - case L_SELECT_IF_BOTH: - if (((relation == L_SELECT_IF_LT) && (w < width && h < height)) || - ((relation == L_SELECT_IF_GT) && (w > width && h > height)) || - ((relation == L_SELECT_IF_LTE) && (w <= width && h <= height)) || - ((relation == L_SELECT_IF_GTE) && (w >= width && h >= height))) - ival = 1; - break; - default: - L_WARNING("can't get here!\n", procName); - break; - } - numaAddNumber(na, ival); - } - - return na; -} - - -/*! - * \brief pixSelectByPerimToAreaRatio() - * - * \param[in] pixs 1 bpp - * \param[in] thresh threshold ratio of fg boundary to fg pixels - * \param[in] connectivity 4 or 8 - * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \param[out] pchanged [optional] 1 if changed; 0 if clone returned - * \return pixd, or NULL on error - * - *- * Notes: - * (1) The args specify constraints on the size of the - * components that are kept. - * (2) If unchanged, returns a copy of pixs. Otherwise, - * returns a new pix with the filtered components. - * (3) This filters "thick" components, where a thick component - * is defined to have a ratio of boundary to interior pixels - * that is smaller than a given threshold value. - * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save the thicker - * components, and L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. - *- */ -PIX * -pixSelectByPerimToAreaRatio(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("pixSelectByPerimToAreaRatio"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (connectivity != 4 && connectivity != 8) - return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); - if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && - type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - if (pchanged) *pchanged = FALSE; - - /* Check if any components exist */ - pixZero(pixs, &empty); - if (empty) - return pixCopy(NULL, pixs); - - /* Filter thin components */ - boxa = pixConnComp(pixs, &pixas, connectivity); - pixad = pixaSelectByPerimToAreaRatio(pixas, thresh, type, &changed); - boxaDestroy(&boxa); - pixaDestroy(&pixas); - - if (!changed) { - pixaDestroy(&pixad); - return pixCopy(NULL, pixs); - } - - /* Render the result */ - 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; -} - - -/*! - * \brief pixaSelectByPerimToAreaRatio() - * - * \param[in] pixas - * \param[in] thresh threshold ratio of fg boundary to fg pixels - * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \param[out] pchanged [optional] 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) See pixSelectByPerimToAreaRatio(). - *- */ -PIXA * -pixaSelectByPerimToAreaRatio(PIXA *pixas, - l_float32 thresh, - l_int32 type, - l_int32 *pchanged) -{ -NUMA *na, *nai; -PIXA *pixad; - - PROCNAME("pixaSelectByPerimToAreaRatio"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && - type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) - return (PIXA *)ERROR_PTR("invalid type", procName, NULL); - - /* Compute component ratios. */ - na = pixaFindPerimToAreaRatio(pixas); - - /* Generate indicator array for elements to be saved. */ - nai = numaMakeThresholdIndicator(na, thresh, type); - numaDestroy(&na); - - /* Filter to get output */ - pixad = pixaSelectWithIndicator(pixas, nai, pchanged); - - numaDestroy(&nai); - return pixad; -} - - -/*! - * \brief pixSelectByPerimSizeRatio() - * - * \param[in] pixs 1 bpp - * \param[in] thresh threshold ratio of fg boundary to fg pixels - * \param[in] connectivity 4 or 8 - * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \param[out] pchanged [optional] 1 if changed; 0 if clone returned - * \return pixd, or NULL on error - * - *- * Notes: - * (1) The args specify constraints on the size of the - * components that are kept. - * (2) If unchanged, returns a copy of pixs. Otherwise, - * returns a new pix with the filtered components. - * (3) This filters components with smooth vs. dendritic shape, using - * the ratio of the fg boundary pixels to the circumference of - * the bounding box, and comparing it to a threshold value. - * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save the smooth - * boundary components, and L_SELECT_IF_GT or L_SELECT_IF_GTE - * to remove them. - *- */ -PIX * -pixSelectByPerimSizeRatio(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("pixSelectByPerimSizeRatio"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (connectivity != 4 && connectivity != 8) - return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); - if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && - type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - if (pchanged) *pchanged = FALSE; - - /* Check if any components exist */ - pixZero(pixs, &empty); - if (empty) - return pixCopy(NULL, pixs); - - /* Filter thin components */ - boxa = pixConnComp(pixs, &pixas, connectivity); - pixad = pixaSelectByPerimSizeRatio(pixas, thresh, type, &changed); - boxaDestroy(&boxa); - pixaDestroy(&pixas); - - if (!changed) { - pixaDestroy(&pixad); - return pixCopy(NULL, pixs); - } - - /* Render the result */ - 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; -} - - -/*! - * \brief pixaSelectByPerimSizeRatio() - * - * \param[in] pixas - * \param[in] thresh threshold ratio of fg boundary to b.b. circumference - * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \param[out] pchanged [optional] 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) See pixSelectByPerimSizeRatio(). - *- */ -PIXA * -pixaSelectByPerimSizeRatio(PIXA *pixas, - l_float32 thresh, - l_int32 type, - l_int32 *pchanged) -{ -NUMA *na, *nai; -PIXA *pixad; - - PROCNAME("pixaSelectByPerimSizeRatio"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && - type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) - return (PIXA *)ERROR_PTR("invalid type", procName, NULL); - - /* Compute component ratios. */ - na = pixaFindPerimSizeRatio(pixas); - - /* Generate indicator array for elements to be saved. */ - nai = numaMakeThresholdIndicator(na, thresh, type); - numaDestroy(&na); - - /* Filter to get output */ - pixad = pixaSelectWithIndicator(pixas, nai, pchanged); - - numaDestroy(&nai); - return pixad; -} - - -/*! - * \brief pixSelectByAreaFraction() - * - * \param[in] pixs 1 bpp - * \param[in] thresh threshold ratio of fg pixels to (w * h) - * \param[in] connectivity 4 or 8 - * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \param[out] pchanged [optional] 1 if changed; 0 if clone returned - * \return pixd, or NULL on error - * - *- * Notes: - * (1) The args specify constraints on the amount of foreground - * coverage of the components that are kept. - * (2) If unchanged, returns a copy of pixs. Otherwise, - * returns a new pix with the filtered components. - * (3) This filters components based on the fraction of fg pixels - * of the component in its bounding box. - * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components - * with less than the threshold fraction of foreground, and - * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. - *- */ -PIX * -pixSelectByAreaFraction(PIX *pixs, - l_float32 thresh, - l_int32 connectivity, - l_int32 type, - l_int32 *pchanged) -{ -l_int32 w, h, empty, changed, count; -BOXA *boxa; -PIX *pixd; -PIXA *pixas, *pixad; - - PROCNAME("pixSelectByAreaFraction"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (connectivity != 4 && connectivity != 8) - return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); - if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && - type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - if (pchanged) *pchanged = FALSE; - - /* Check if any components exist */ - pixZero(pixs, &empty); - if (empty) - return pixCopy(NULL, pixs); - - /* Filter components */ - boxa = pixConnComp(pixs, &pixas, connectivity); - pixad = pixaSelectByAreaFraction(pixas, thresh, type, &changed); - boxaDestroy(&boxa); - pixaDestroy(&pixas); - - if (!changed) { - pixaDestroy(&pixad); - return pixCopy(NULL, pixs); - } - - /* Render the result */ - 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; -} - - -/*! - * \brief pixaSelectByAreaFraction() - * - * \param[in] pixas - * \param[in] thresh threshold ratio of fg pixels to (w * h) - * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \param[out] pchanged [optional] 1 if changed; 0 if clone returned - * \return pixad, or NULL on error - * - *- * Notes: - * (1) Returns a pixa clone if no components are removed. - * (2) Uses pix and box clones in the new pixa. - * (3) This filters components based on the fraction of fg pixels - * of the component in its bounding box. - * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components - * with less than the threshold fraction of foreground, and - * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. - *- */ -PIXA * -pixaSelectByAreaFraction(PIXA *pixas, - l_float32 thresh, - l_int32 type, - l_int32 *pchanged) -{ -NUMA *na, *nai; -PIXA *pixad; - - PROCNAME("pixaSelectByAreaFraction"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && - type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) - return (PIXA *)ERROR_PTR("invalid type", procName, NULL); - - /* Compute component ratios. */ - na = pixaFindAreaFraction(pixas); - - /* Generate indicator array for elements to be saved. */ - nai = numaMakeThresholdIndicator(na, thresh, type); - numaDestroy(&na); - - /* Filter to get output */ - pixad = pixaSelectWithIndicator(pixas, nai, pchanged); - - numaDestroy(&nai); - return pixad; -} - - -/*! - * \brief pixSelectByWidthHeightRatio() - * - * \param[in] pixs 1 bpp - * \param[in] thresh threshold ratio of width/height - * \param[in] connectivity 4 or 8 - * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \param[out] pchanged [optional] 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); - - if (!changed) { - pixaDestroy(&pixad); - return pixCopy(NULL, pixs); - } - - /* Render the result */ - 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; -} - - -/*! - * \brief pixaSelectByWidthHeightRatio() - * - * \param[in] pixas - * \param[in] thresh threshold ratio of width/height - * \param[in] type L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \param[out] pchanged [optional] 1 if changed; 0 if clone returned - * \return pixad, or NULL on error - * - *- * Notes: - * (1) Returns a pixa clone if no components are removed. - * (2) Uses pix and box clones in the new pixa. - * (3) This filters components based on the width-to-height ratio - * of each pix. - * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components - * with less than the threshold ratio, and - * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. - *- */ -PIXA * -pixaSelectByWidthHeightRatio(PIXA *pixas, - l_float32 thresh, - l_int32 type, - l_int32 *pchanged) -{ -NUMA *na, *nai; -PIXA *pixad; - - PROCNAME("pixaSelectByWidthHeightRatio"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && - type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) - return (PIXA *)ERROR_PTR("invalid type", procName, NULL); - - /* Compute component ratios. */ - na = pixaFindWidthHeightRatio(pixas); - - /* Generate indicator array for elements to be saved. */ - nai = numaMakeThresholdIndicator(na, thresh, type); - numaDestroy(&na); - - /* Filter to get output */ - pixad = pixaSelectWithIndicator(pixas, nai, pchanged); - - numaDestroy(&nai); - return pixad; -} - - -/*! - * \brief pixaSelectByNumConnComp() - * - * \param[in] pixas - * \param[in] nmin minimum number of components - * \param[in] nmax maximum number of components - * \param[in] connectivity 4 or 8 - * \param[out] pchanged [optional] 1 if changed; 0 if clone returned - * \return pixad, or NULL on error - * - *- * Notes: - * (1) Returns a pixa clone if no components are removed. - * (2) Uses pix and box clones in the new pixa. - * (3) This filters by the number of connected components in - * a given range. - *- */ -PIXA * -pixaSelectByNumConnComp(PIXA *pixas, - l_int32 nmin, - l_int32 nmax, - l_int32 connectivity, - l_int32 *pchanged) -{ -l_int32 n, i, count; -NUMA *na; -PIX *pix; -PIXA *pixad; - - PROCNAME("pixaSelectByNumConnComp"); - - if (pchanged) *pchanged = 0; - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (nmin > nmax) - return (PIXA *)ERROR_PTR("nmin > nmax", procName, NULL); - if (connectivity != 4 && connectivity != 8) - return (PIXA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); - - /* Get indicator array based on number of c.c. */ - n = pixaGetCount(pixas); - na = numaCreate(n); - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixas, i, L_CLONE); - pixCountConnComp(pix, connectivity, &count); - if (count >= nmin && count <= nmax) - numaAddNumber(na, 1); - else - numaAddNumber(na, 0); - pixDestroy(&pix); - } - - /* Filter to get output */ - pixad = pixaSelectWithIndicator(pixas, na, pchanged); - numaDestroy(&na); - return pixad; -} - - -/*! - * \brief pixaSelectWithIndicator() - * - * \param[in] pixas - * \param[in] na indicator numa - * \param[out] pchanged [optional] 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). - * (4) If the source boxa is not fully populated, it is left - * empty in the dest pixa. - *- */ -PIXA * -pixaSelectWithIndicator(PIXA *pixas, - NUMA *na, - l_int32 *pchanged) -{ -l_int32 i, n, nbox, ival, nsave; -BOX *box; -PIX *pix1; -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); - nbox = pixaGetBoxaCount(pixas); - for (i = 0; i < n; i++) { - numaGetIValue(na, i, &ival); - if (ival == 0) continue; - pix1 = pixaGetPix(pixas, i, L_CLONE); - pixaAddPix(pixad, pix1, L_INSERT); - if (nbox == n) { /* fully populated boxa */ - box = pixaGetBox(pixas, i, L_CLONE); - pixaAddBox(pixad, box, L_INSERT); - } - } - - return pixad; -} - - -/*! - * \brief pixRemoveWithIndicator() - * - * \param[in] pixs 1 bpp pix from which components are removed; in-place - * \param[in] pixa of connected components in pixs - * \param[in] na numa indicator: remove components corresponding to 1s - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This complements pixAddWithIndicator(). Here, the selected - * components are set subtracted from pixs. - *- */ -l_ok -pixRemoveWithIndicator(PIX *pixs, - PIXA *pixa, - NUMA *na) -{ -l_int32 i, n, ival, x, y, w, h; -BOX *box; -PIX *pix; - - PROCNAME("pixRemoveWithIndicator"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - if (!na) - return ERROR_INT("na not defined", procName, 1); - n = pixaGetCount(pixa); - if (n != numaGetCount(na)) - return ERROR_INT("pixa and na sizes not equal", procName, 1); - - for (i = 0; i < n; i++) { - numaGetIValue(na, i, &ival); - if (ival == 1) { - pix = pixaGetPix(pixa, i, L_CLONE); - box = pixaGetBox(pixa, i, L_CLONE); - boxGetGeometry(box, &x, &y, &w, &h); - pixRasterop(pixs, x, y, w, h, PIX_DST & PIX_NOT(PIX_SRC), - pix, 0, 0); - boxDestroy(&box); - pixDestroy(&pix); - } - } - - return 0; -} - - -/*! - * \brief pixAddWithIndicator() - * - * \param[in] pixs 1 bpp pix from which components are added; in-place - * \param[in] pixa of connected components, some of which will be put - * into pixs - * \param[in] 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_ok -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; -} - - -/*! - * \brief pixaSelectWithString() - * - * \param[in] pixas - * \param[in] str string of indices into pixa, giving the pix to - * be selected - * \param[out] perror [optional] 1 if any indices are invalid; - * 0 if all indices are valid - * \return pixad, or NULL on error - * - *- * Notes: - * (1) Returns a pixa with copies of selected pix. - * (2) Associated boxes are also copied, if fully populated. - *- */ -PIXA * -pixaSelectWithString(PIXA *pixas, - const char *str, - l_int32 *perror) -{ -l_int32 i, nval, npix, nbox, val, imaxval; -l_float32 maxval; -BOX *box; -NUMA *na; -PIX *pix1; -PIXA *pixad; - - PROCNAME("pixaSelectWithString"); - - if (perror) *perror = 0; - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (!str) - return (PIXA *)ERROR_PTR("str not defined", procName, NULL); - - if ((na = numaCreateFromString(str)) == NULL) - return (PIXA *)ERROR_PTR("na not made", procName, NULL); - if ((nval = numaGetCount(na)) == 0) { - numaDestroy(&na); - return (PIXA *)ERROR_PTR("no indices found", procName, NULL); - } - numaGetMax(na, &maxval, NULL); - imaxval = (l_int32)(maxval + 0.1); - nbox = pixaGetBoxaCount(pixas); - npix = pixaGetCount(pixas); - if (imaxval >= npix) { - if (perror) *perror = 1; - L_ERROR("max index = %d, size of pixa = %d\n", procName, imaxval, npix); - } - - pixad = pixaCreate(nval); - for (i = 0; i < nval; i++) { - numaGetIValue(na, i, &val); - if (val < 0 || val >= npix) { - L_ERROR("index %d out of range of pix\n", procName, val); - continue; - } - pix1 = pixaGetPix(pixas, val, L_COPY); - pixaAddPix(pixad, pix1, L_INSERT); - if (nbox == npix) { /* fully populated boxa */ - box = pixaGetBox(pixas, val, L_COPY); - pixaAddBox(pixad, box, L_INSERT); - } - } - numaDestroy(&na); - return pixad; -} - - -/*! - * \brief pixaRenderComponent() - * - * \param[in] pixs [optional] 1 bpp pix - * \param[in] pixa of 1 bpp connected components, one of which will - * be rendered in pixs, with its origin determined - * by the associated box. - * \param[in] index of component to be rendered - * \return pixd, or NULL on error - * - *- * Notes: - * (1) If pixs is null, this generates an empty pix of a size determined - * by union of the component bounding boxes, and including the origin. - * (2) The selected component is blitted into pixs. - *- */ -PIX * -pixaRenderComponent(PIX *pixs, - PIXA *pixa, - l_int32 index) -{ -l_int32 n, x, y, w, h, same, maxd; -BOX *box; -BOXA *boxa; -PIX *pix; - - PROCNAME("pixaRenderComponent"); - - if (!pixa) - return (PIX *)ERROR_PTR("pixa not defined", procName, pixs); - n = pixaGetCount(pixa); - if (index < 0 || index >= n) - return (PIX *)ERROR_PTR("invalid index", procName, pixs); - if (pixs && (pixGetDepth(pixs) != 1)) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixs); - pixaVerifyDepth(pixa, &same, &maxd); - if (maxd > 1) - return (PIX *)ERROR_PTR("not all pix with d == 1", procName, pixs); - - boxa = pixaGetBoxa(pixa, L_CLONE); - if (!pixs) { - boxaGetExtent(boxa, &w, &h, NULL); - pixs = pixCreate(w, h, 1); - } - - pix = pixaGetPix(pixa, index, L_CLONE); - box = boxaGetBox(boxa, index, 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); - boxaDestroy(&boxa); - - return pixs; -} - - -/*---------------------------------------------------------------------* - * Sort functions * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaSort() - * - * \param[in] pixas - * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH, - * L_SORT_BY_HEIGHT, L_SORT_BY_MIN_DIMENSION, - * L_SORT_BY_MAX_DIMENSION, L_SORT_BY_PERIMETER, - * L_SORT_BY_AREA, L_SORT_BY_ASPECT_RATIO - * \param[in] sortorder L_SORT_INCREASING, L_SORT_DECREASING - * \param[out] pnaindex [optional] index of sorted order into - * original array - * \param[in] copyflag L_COPY, L_CLONE - * \return pixad sorted version of pixas, or NULL on error - * - *- * Notes: - * (1) This sorts based on the data in the boxa. If the boxa - * count is not the same as the pixa count, this returns an error. - * (2) If the boxa is empty, it makes one corresponding to the - * dimensions of each pix, which allows meaningful sorting on - * all types except x and y. - * (3) The copyflag refers to the pix and box copies that are - * inserted into the sorted pixa. These are either L_COPY - * or L_CLONE. - *- */ -PIXA * -pixaSort(PIXA *pixas, - l_int32 sorttype, - l_int32 sortorder, - NUMA **pnaindex, - l_int32 copyflag) -{ -l_int32 i, n, nb, x, y, w, h; -BOXA *boxa; -NUMA *na, *naindex; -PIXA *pixad; - - PROCNAME("pixaSort"); - - if (pnaindex) *pnaindex = NULL; - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && - sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && - sorttype != L_SORT_BY_MIN_DIMENSION && - sorttype != L_SORT_BY_MAX_DIMENSION && - sorttype != L_SORT_BY_PERIMETER && - sorttype != L_SORT_BY_AREA && - sorttype != L_SORT_BY_ASPECT_RATIO) - return (PIXA *)ERROR_PTR("invalid sort type", procName, NULL); - if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) - return (PIXA *)ERROR_PTR("invalid sort order", procName, NULL); - if (copyflag != L_COPY && copyflag != L_CLONE) - return (PIXA *)ERROR_PTR("invalid copy flag", procName, NULL); - - /* Check the pixa and boxa counts. Make a boxa if required. */ - if ((n = pixaGetCount(pixas)) == 0) { - L_INFO("no pix in pixa\n", procName); - return pixaCopy(pixas, copyflag); - } - if ((boxa = pixas->boxa) == NULL) /* not owned; do not destroy */ - return (PIXA *)ERROR_PTR("boxa not found!", procName, NULL); - nb = boxaGetCount(boxa); - if (nb == 0) { - pixaSetFullSizeBoxa(pixas); - nb = n; - boxa = pixas->boxa; /* not owned */ - if (sorttype == L_SORT_BY_X || sorttype == L_SORT_BY_Y) - L_WARNING("sort by x or y where all values are 0\n", procName); - } - if (nb != n) - return (PIXA *)ERROR_PTR("boxa and pixa counts differ", procName, NULL); - - /* Use O(n) binsort if possible */ - if (n > MinCompsForBinSort && - ((sorttype == L_SORT_BY_X) || (sorttype == L_SORT_BY_Y) || - (sorttype == L_SORT_BY_WIDTH) || (sorttype == L_SORT_BY_HEIGHT) || - (sorttype == L_SORT_BY_PERIMETER))) - return pixaBinSort(pixas, sorttype, sortorder, pnaindex, copyflag); - - /* Build up numa of specific data */ - if ((na = numaCreate(n)) == NULL) - return (PIXA *)ERROR_PTR("na not made", procName, NULL); - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); - switch (sorttype) - { - case L_SORT_BY_X: - numaAddNumber(na, x); - break; - case L_SORT_BY_Y: - numaAddNumber(na, y); - break; - case L_SORT_BY_WIDTH: - numaAddNumber(na, w); - break; - case L_SORT_BY_HEIGHT: - numaAddNumber(na, h); - break; - case L_SORT_BY_MIN_DIMENSION: - numaAddNumber(na, L_MIN(w, h)); - break; - case L_SORT_BY_MAX_DIMENSION: - numaAddNumber(na, L_MAX(w, h)); - break; - case L_SORT_BY_PERIMETER: - numaAddNumber(na, w + h); - break; - case L_SORT_BY_AREA: - numaAddNumber(na, w * h); - break; - case L_SORT_BY_ASPECT_RATIO: - numaAddNumber(na, (l_float32)w / (l_float32)h); - break; - default: - L_WARNING("invalid sort type\n", procName); - } - } - - /* Get the sort index for data array */ - naindex = numaGetSortIndex(na, sortorder); - numaDestroy(&na); - if (!naindex) - return (PIXA *)ERROR_PTR("naindex not made", procName, NULL); - - /* Build up sorted pixa using sort index */ - if ((pixad = pixaSortByIndex(pixas, naindex, copyflag)) == NULL) { - numaDestroy(&naindex); - return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); - } - - if (pnaindex) - *pnaindex = naindex; - else - numaDestroy(&naindex); - return pixad; -} - - -/*! - * \brief pixaBinSort() - * - * \param[in] pixas - * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH, - * L_SORT_BY_HEIGHT, L_SORT_BY_PERIMETER - * \param[in] sortorder L_SORT_INCREASING, L_SORT_DECREASING - * \param[out] pnaindex [optional] index of sorted order into - * original array - * \param[in] copyflag L_COPY, L_CLONE - * \return pixad sorted version of pixas, or NULL on error - * - *- * Notes: - * (1) This sorts based on the data in the boxa. If the boxa - * count is not the same as the pixa count, this returns an error. - * (2) The copyflag refers to the pix and box copies that are - * inserted into the sorted pixa. These are either L_COPY - * or L_CLONE. - * (3) For a large number of boxes (say, greater than 1000), this - * O(n) binsort is much faster than the O(nlogn) shellsort. - * For 5000 components, this is over 20x faster than boxaSort(). - * (4) Consequently, pixaSort() calls this function if it will - * likely go much faster. - *- */ -PIXA * -pixaBinSort(PIXA *pixas, - l_int32 sorttype, - l_int32 sortorder, - NUMA **pnaindex, - l_int32 copyflag) -{ -l_int32 i, n, x, y, w, h; -BOXA *boxa; -NUMA *na, *naindex; -PIXA *pixad; - - PROCNAME("pixaBinSort"); - - if (pnaindex) *pnaindex = NULL; - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && - sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && - sorttype != L_SORT_BY_PERIMETER) - return (PIXA *)ERROR_PTR("invalid sort type", procName, NULL); - if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) - return (PIXA *)ERROR_PTR("invalid sort order", procName, NULL); - if (copyflag != L_COPY && copyflag != L_CLONE) - return (PIXA *)ERROR_PTR("invalid copy flag", procName, NULL); - - /* Verify that the pixa and its boxa have the same count */ - if ((boxa = pixas->boxa) == NULL) /* not owned; do not destroy */ - return (PIXA *)ERROR_PTR("boxa not found", procName, NULL); - n = pixaGetCount(pixas); - if (boxaGetCount(boxa) != n) - return (PIXA *)ERROR_PTR("boxa and pixa counts differ", procName, NULL); - - /* Generate Numa of appropriate box dimensions */ - if ((na = numaCreate(n)) == NULL) - return (PIXA *)ERROR_PTR("na not made", procName, NULL); - for (i = 0; i < n; i++) { - boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); - switch (sorttype) - { - case L_SORT_BY_X: - numaAddNumber(na, x); - break; - case L_SORT_BY_Y: - numaAddNumber(na, y); - break; - case L_SORT_BY_WIDTH: - numaAddNumber(na, w); - break; - case L_SORT_BY_HEIGHT: - numaAddNumber(na, h); - break; - case L_SORT_BY_PERIMETER: - numaAddNumber(na, w + h); - break; - default: - L_WARNING("invalid sort type\n", procName); - } - } - - /* Get the sort index for data array */ - naindex = numaGetBinSortIndex(na, sortorder); - numaDestroy(&na); - if (!naindex) - return (PIXA *)ERROR_PTR("naindex not made", procName, NULL); - - /* Build up sorted pixa using sort index */ - if ((pixad = pixaSortByIndex(pixas, naindex, copyflag)) == NULL) { - numaDestroy(&naindex); - return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); - } - - if (pnaindex) - *pnaindex = naindex; - else - numaDestroy(&naindex); - return pixad; -} - - -/*! - * \brief pixaSortByIndex() - * - * \param[in] pixas - * \param[in] naindex na that maps from the new pixa to the input pixa - * \param[in] 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 pixaSort2dByIndex() - * - * \param[in] pixas - * \param[in] naa numaa that maps from the new pixaa to the input pixas - * \param[in] copyflag L_CLONE or L_COPY - * \return paa 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 *paa; - - 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); - paa = 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(paa, pixa, L_INSERT); - numaDestroy(&na); - } - - return paa; -} - - -/*---------------------------------------------------------------------* - * Pixa and Pixaa range selection * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaSelectRange() - * - * \param[in] pixas - * \param[in] first use 0 to select from the beginning - * \param[in] last use -1 to select to the end - * \param[in] copyflag L_COPY, L_CLONE - * \return pixad, or NULL on error - * - *- * Notes: - * (1) The copyflag specifies what we do with each pix from pixas. - * Specifically, L_CLONE inserts a clone into pixad of each - * selected pix from pixas. - *- */ -PIXA * -pixaSelectRange(PIXA *pixas, - l_int32 first, - l_int32 last, - l_int32 copyflag) -{ -l_int32 n, npix, i; -PIX *pix; -PIXA *pixad; - - PROCNAME("pixaSelectRange"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (copyflag != L_COPY && copyflag != L_CLONE) - return (PIXA *)ERROR_PTR("invalid copyflag", procName, NULL); - n = pixaGetCount(pixas); - first = L_MAX(0, first); - if (last < 0) last = n - 1; - if (first >= n) - return (PIXA *)ERROR_PTR("invalid first", procName, NULL); - if (last >= n) { - L_WARNING("last = %d is beyond max index = %d; adjusting\n", - procName, last, n - 1); - last = n - 1; - } - if (first > last) - return (PIXA *)ERROR_PTR("first > last", procName, NULL); - - npix = last - first + 1; - pixad = pixaCreate(npix); - for (i = first; i <= last; i++) { - pix = pixaGetPix(pixas, i, copyflag); - pixaAddPix(pixad, pix, L_INSERT); - } - return pixad; -} - - -/*! - * \brief pixaaSelectRange() - * - * \param[in] paas - * \param[in] first use 0 to select from the beginning - * \param[in] last use -1 to select to the end - * \param[in] copyflag L_COPY, L_CLONE - * \return paad, or NULL on error - * - *- * Notes: - * (1) The copyflag specifies what we do with each pixa from paas. - * Specifically, L_CLONE inserts a clone into paad of each - * selected pixa from paas. - *- */ -PIXAA * -pixaaSelectRange(PIXAA *paas, - l_int32 first, - l_int32 last, - l_int32 copyflag) -{ -l_int32 n, npixa, i; -PIXA *pixa; -PIXAA *paad; - - PROCNAME("pixaaSelectRange"); - - if (!paas) - return (PIXAA *)ERROR_PTR("paas not defined", procName, NULL); - if (copyflag != L_COPY && copyflag != L_CLONE) - return (PIXAA *)ERROR_PTR("invalid copyflag", procName, NULL); - n = pixaaGetCount(paas, NULL); - first = L_MAX(0, first); - if (last < 0) last = n - 1; - if (first >= n) - return (PIXAA *)ERROR_PTR("invalid first", procName, NULL); - if (last >= n) { - L_WARNING("last = %d is beyond max index = %d; adjusting\n", - procName, last, n - 1); - last = n - 1; - } - if (first > last) - return (PIXAA *)ERROR_PTR("first > last", procName, NULL); - - npixa = last - first + 1; - paad = pixaaCreate(npixa); - for (i = first; i <= last; i++) { - pixa = pixaaGetPixa(paas, i, copyflag); - pixaaAddPixa(paad, pixa, L_INSERT); - } - return paad; -} - - -/*---------------------------------------------------------------------* - * Pixa and Pixaa scaling * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaaScaleToSize() - * - * \param[in] paas - * \param[in] wd target width; use 0 if using height as target - * \param[in] hd target height; use 0 if using width as target - * \return paad, or NULL on error - * - *- * Notes: - * (1) This guarantees that each output scaled image has the - * dimension(s) you specify. - * ~ To specify the width with isotropic scaling, set %hd = 0. - * ~ To specify the height with isotropic scaling, set %wd = 0. - * ~ If both %wd and %hd are specified, the image is scaled - * (in general, anisotropically) to that size. - * ~ It is an error to set both %wd and %hd to 0. - *- */ -PIXAA * -pixaaScaleToSize(PIXAA *paas, - l_int32 wd, - l_int32 hd) -{ -l_int32 n, i; -PIXA *pixa1, *pixa2; -PIXAA *paad; - - PROCNAME("pixaaScaleToSize"); - - if (!paas) - return (PIXAA *)ERROR_PTR("paas not defined", procName, NULL); - if (wd <= 0 && hd <= 0) - return (PIXAA *)ERROR_PTR("neither wd nor hd > 0", procName, NULL); - - n = pixaaGetCount(paas, NULL); - paad = pixaaCreate(n); - for (i = 0; i < n; i++) { - pixa1 = pixaaGetPixa(paas, i, L_CLONE); - pixa2 = pixaScaleToSize(pixa1, wd, hd); - pixaaAddPixa(paad, pixa2, L_INSERT); - pixaDestroy(&pixa1); - } - return paad; -} - - -/*! - * \brief pixaaScaleToSizeVar() - * - * \param[in] paas - * \param[in] nawd [optional] target widths; use NULL if using height - * \param[in] nahd [optional] target height; use NULL if using width - * \return paad, or NULL on error - * - *- * Notes: - * (1) This guarantees that the scaled images in each pixa have the - * dimension(s) you specify in the numas. - * ~ To specify the width with isotropic scaling, set %nahd = NULL. - * ~ To specify the height with isotropic scaling, set %nawd = NULL. - * ~ If both %nawd and %nahd are specified, the image is scaled - * (in general, anisotropically) to that size. - * ~ It is an error to set both %nawd and %nahd to NULL. - * (2) If either nawd and/or nahd is defined, it must have the same - * count as the number of pixa in paas. - *- */ -PIXAA * -pixaaScaleToSizeVar(PIXAA *paas, - NUMA *nawd, - NUMA *nahd) -{ -l_int32 n, i, wd, hd; -PIXA *pixa1, *pixa2; -PIXAA *paad; - - PROCNAME("pixaaScaleToSizeVar"); - - if (!paas) - return (PIXAA *)ERROR_PTR("paas not defined", procName, NULL); - if (!nawd && !nahd) - return (PIXAA *)ERROR_PTR("!nawd && !nahd", procName, NULL); - - n = pixaaGetCount(paas, NULL); - if (nawd && (n != numaGetCount(nawd))) - return (PIXAA *)ERROR_PTR("nawd wrong size", procName, NULL); - if (nahd && (n != numaGetCount(nahd))) - return (PIXAA *)ERROR_PTR("nahd wrong size", procName, NULL); - paad = pixaaCreate(n); - for (i = 0; i < n; i++) { - wd = hd = 0; - if (nawd) numaGetIValue(nawd, i, &wd); - if (nahd) numaGetIValue(nahd, i, &hd); - pixa1 = pixaaGetPixa(paas, i, L_CLONE); - pixa2 = pixaScaleToSize(pixa1, wd, hd); - pixaaAddPixa(paad, pixa2, L_INSERT); - pixaDestroy(&pixa1); - } - return paad; -} - - -/*! - * \brief pixaScaleToSize() - * - * \param[in] pixas - * \param[in] wd target width; use 0 if using height as target - * \param[in] hd target height; use 0 if using width as target - * \return pixad, or NULL on error - * - *- * Notes: - * (1) See pixaaScaleToSize() - *- */ -PIXA * -pixaScaleToSize(PIXA *pixas, - l_int32 wd, - l_int32 hd) -{ -l_int32 n, i; -PIX *pix1, *pix2; -PIXA *pixad; - - PROCNAME("pixaScaleToSize"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - - if (wd <= 0 && hd <= 0) /* no scaling requested */ - return pixaCopy(pixas, L_CLONE); - - n = pixaGetCount(pixas); - pixad = pixaCreate(n); - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixas, i, L_CLONE); - pix2 = pixScaleToSize(pix1, wd, hd); - pixCopyText(pix2, pix1); - pixaAddPix(pixad, pix2, L_INSERT); - pixDestroy(&pix1); - } - return pixad; -} - - -/*! - * \brief pixaScaleToSizeRel() - * - * \param[in] pixas - * \param[in] delw change in width, in pixels; 0 means no change - * \param[in] delh change in height, in pixels; 0 means no change - * return pixad, or NULL on error - * - *- * Notes: - * (1) If a requested change in a pix is not possible because - * either the requested width or height is <= 0, issue a - * warning and return a copy. - *- */ -PIXA * -pixaScaleToSizeRel(PIXA *pixas, - l_int32 delw, - l_int32 delh) -{ -l_int32 n, i; -PIX *pix1, *pix2; -PIXA *pixad; - - PROCNAME("pixaScaleToSizeRel"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - - n = pixaGetCount(pixas); - pixad = pixaCreate(n); - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixas, i, L_CLONE); - pix2 = pixScaleToSizeRel(pix1, delw, delh); - if (pix2) { - pixaAddPix(pixad, pix2, L_INSERT); - } else { - L_WARNING("relative scale to size failed; use a copy\n", procName); - pixaAddPix(pixad, pix1, L_COPY); - } - pixDestroy(&pix1); - } - return pixad; -} - - -/*! - * \brief pixaScale() - * - * \param[in] pixas - * \param[in] scalex - * \param[in] scaley - * \return pixad, or NULL on error - * - *- * Notes: - * (1) If pixas has a full boxes, it is scaled as well. - *- */ -PIXA * -pixaScale(PIXA *pixas, - l_float32 scalex, - l_float32 scaley) -{ -l_int32 i, n, nb; -BOXA *boxa1, *boxa2; -PIX *pix1, *pix2; -PIXA *pixad; - - PROCNAME("pixaScale"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (scalex <= 0.0 || scaley <= 0.0) - return (PIXA *)ERROR_PTR("invalid scaling parameters", procName, NULL); - - n = pixaGetCount(pixas); - pixad = pixaCreate(n); - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixas, i, L_CLONE); - pix2 = pixScale(pix1, scalex, scaley); - pixCopyText(pix2, pix1); - pixaAddPix(pixad, pix2, L_INSERT); - pixDestroy(&pix1); - } - - boxa1 = pixaGetBoxa(pixas, L_CLONE); - nb = boxaGetCount(boxa1); - if (nb == n) { - boxa2 = boxaTransform(boxa1, 0, 0, scalex, scaley); - pixaSetBoxa(pixad, boxa2, L_INSERT); - } - boxaDestroy(&boxa1); - return pixad; -} - - -/*! - * \brief pixaScaleBySampling() - * - * \param[in] pixas - * \param[in] scalex - * \param[in] scaley - * \return pixad, or NULL on error - * - *- * Notes: - * (1) If pixas has a full boxes, it is scaled as well. - *- */ -PIXA * -pixaScaleBySampling(PIXA *pixas, - l_float32 scalex, - l_float32 scaley) -{ -l_int32 i, n, nb; -BOXA *boxa1, *boxa2; -PIX *pix1, *pix2; -PIXA *pixad; - - PROCNAME("pixaScaleBySampling"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (scalex <= 0.0 || scaley <= 0.0) - return (PIXA *)ERROR_PTR("invalid scaling parameters", procName, NULL); - - n = pixaGetCount(pixas); - pixad = pixaCreate(n); - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixas, i, L_CLONE); - pix2 = pixScaleBySampling(pix1, scalex, scaley); - pixCopyText(pix2, pix1); - pixaAddPix(pixad, pix2, L_INSERT); - pixDestroy(&pix1); - } - - boxa1 = pixaGetBoxa(pixas, L_CLONE); - nb = boxaGetCount(boxa1); - if (nb == n) { - boxa2 = boxaTransform(boxa1, 0, 0, scalex, scaley); - pixaSetBoxa(pixad, boxa2, L_INSERT); - } - boxaDestroy(&boxa1); - return pixad; -} - - -/*---------------------------------------------------------------------* - * Pixa rotation and translation * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaRotate() - * - * \param[in] pixas 1, 2, 4, 8, 32 bpp rgb - * \param[in] angle rotation angle in radians; clockwise is positive - * \param[in] type L_ROTATE_AREA_MAP, L_ROTATE_SHEAR, L_ROTATE_SAMPLING - * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK - * \param[in] width original width; use 0 to avoid embedding - * \param[in] height original height; use 0 to avoid embedding - * \return pixad, or NULL on error - * - *- * Notes: - * (1) Each pix is rotated about its center. See pixRotate() for details. - * (2) The boxa array is copied. Why is it not rotated? - * If a boxa exists, the array of boxes is in 1-to-1 - * correspondence with the array of pix, and each box typically - * represents the location of the pix relative to an image from - * which it has been extracted. Like the pix, we could rotate - * each box around its center, and then generate a box that - * contains all four corners, as is done in boxaRotate(), but - * this seems unnecessary. - *- */ -PIXA * -pixaRotate(PIXA *pixas, - l_float32 angle, - l_int32 type, - l_int32 incolor, - l_int32 width, - l_int32 height) -{ -l_int32 i, n; -BOXA *boxa; -PIX *pixs, *pixd; -PIXA *pixad; - - PROCNAME("pixaRotate"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (type != L_ROTATE_SHEAR && type != L_ROTATE_AREA_MAP && - type != L_ROTATE_SAMPLING) - return (PIXA *)ERROR_PTR("invalid type", procName, NULL); - if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) - return (PIXA *)ERROR_PTR("invalid incolor", procName, NULL); - if (L_ABS(angle) < MinAngleToRotate) - return pixaCopy(pixas, L_COPY); - - n = pixaGetCount(pixas); - if ((pixad = pixaCreate(n)) == NULL) - return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); - boxa = pixaGetBoxa(pixad, L_COPY); - pixaSetBoxa(pixad, boxa, L_INSERT); - for (i = 0; i < n; i++) { - if ((pixs = pixaGetPix(pixas, i, L_CLONE)) == NULL) { - pixaDestroy(&pixad); - return (PIXA *)ERROR_PTR("pixs not found", procName, NULL); - } - pixd = pixRotate(pixs, angle, type, incolor, width, height); - pixaAddPix(pixad, pixd, L_INSERT); - pixDestroy(&pixs); - } - - return pixad; -} - - -/*! - * \brief pixaRotateOrth() - * - * \param[in] pixas - * \param[in] rotation 0 = noop, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg; - * all rotations are clockwise - * \return pixad, or NULL on error - * - *- * Notes: - * (1) Rotates each pix in the pixa. Rotates and saves the boxes in - * the boxa if the boxa is full. - *- */ -PIXA * -pixaRotateOrth(PIXA *pixas, - l_int32 rotation) -{ -l_int32 i, n, nb, w, h; -BOX *boxs, *boxd; -PIX *pixs, *pixd; -PIXA *pixad; - - PROCNAME("pixaRotateOrth"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (rotation < 0 || rotation > 3) - return (PIXA *)ERROR_PTR("rotation not in {0,1,2,3}", procName, NULL); - if (rotation == 0) - return pixaCopy(pixas, L_COPY); - - n = pixaGetCount(pixas); - nb = pixaGetBoxaCount(pixas); - if ((pixad = pixaCreate(n)) == NULL) - return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); - for (i = 0; i < n; i++) { - if ((pixs = pixaGetPix(pixas, i, L_CLONE)) == NULL) { - pixaDestroy(&pixad); - return (PIXA *)ERROR_PTR("pixs not found", procName, NULL); - } - pixd = pixRotateOrth(pixs, rotation); - pixaAddPix(pixad, pixd, L_INSERT); - if (n == nb) { - boxs = pixaGetBox(pixas, i, L_COPY); - pixGetDimensions(pixs, &w, &h, NULL); - boxd = boxRotateOrth(boxs, w, h, rotation); - pixaAddBox(pixad, boxd, L_INSERT); - boxDestroy(&boxs); - } - pixDestroy(&pixs); - } - - return pixad; -} - - -/*! - * \brief pixaTranslate() - * - * \param[in] pixas - * \param[in] hshift horizontal shift; hshift > 0 is to right - * \param[in] vshift vertical shift; vshift > 0 is down - * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK - * \return pixad, or NULL on error. - */ -PIXA * -pixaTranslate(PIXA *pixas, - l_int32 hshift, - l_int32 vshift, - l_int32 incolor) -{ -l_int32 i, n, nb; -BOXA *boxas, *boxad; -PIX *pixs, *pixd; -PIXA *pixad; - - PROCNAME("pixaTranslate"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (hshift == 0 && vshift == 0) - return pixaCopy(pixas, L_COPY); - - n = pixaGetCount(pixas); - nb = pixaGetBoxaCount(pixas); - if ((pixad = pixaCreate(n)) == NULL) - return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); - for (i = 0; i < n; i++) { - if ((pixs = pixaGetPix(pixas, i, L_CLONE)) == NULL) { - pixaDestroy(&pixad); - return (PIXA *)ERROR_PTR("pixs not found", procName, NULL); - } - pixd = pixTranslate(NULL, pixs, hshift, vshift, incolor); - pixaAddPix(pixad, pixd, L_INSERT); - pixDestroy(&pixs); - } - if (n == nb) { - boxas = pixaGetBoxa(pixas, L_CLONE); - boxad = boxaTransform(boxas, hshift, vshift, 1.0, 1.0); - pixaSetBoxa(pixad, boxad, L_INSERT); - boxaDestroy(&boxas); - } - - return pixad; -} - - -/*---------------------------------------------------------------------* - * Miscellaneous functions * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaAddBorderGeneral() - * - * \param[in] pixad can be null or equal to pixas - * \param[in] pixas containing pix of all depths; colormap ok - * \param[in] left, right, top, bot number of pixels added - * \param[in] 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("box %d not found\n", 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; -} - - -/*! - * \brief pixaaFlattenToPixa() - * - * \param[in] paa - * \param[out] pnaindex [optional] the pixa index in the pixaa - * \param[in] 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 *paa, - NUMA **pnaindex, - l_int32 copyflag) -{ -l_int32 i, j, m, mb, n; -BOX *box; -NUMA *naindex; -PIX *pix; -PIXA *pixa, *pixat; - - PROCNAME("pixaaFlattenToPixa"); - - if (pnaindex) *pnaindex = NULL; - if (!paa) - return (PIXA *)ERROR_PTR("paa 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(paa, NULL); - pixa = pixaCreate(n); - for (i = 0; i < n; i++) { - pixat = pixaaGetPixa(paa, i, L_CLONE); - m = pixaGetCount(pixat); - mb = pixaGetBoxaCount(pixat); - for (j = 0; j < m; j++) { - pix = pixaGetPix(pixat, j, copyflag); - pixaAddPix(pixa, pix, L_INSERT); - if (j < mb) { - box = pixaGetBox(pixat, j, copyflag); - pixaAddBox(pixa, box, L_INSERT); - } - if (pnaindex) - numaAddNumber(naindex, i); /* save 'row' number */ - } - pixaDestroy(&pixat); - } - - return pixa; -} - - -/*! - * \brief pixaaSizeRange() - * - * \param[in] paa - * \param[out] pminw, pminh, pmaxw, pmaxh [optional] range of - * dimensions of all boxes - * \return 0 if OK, 1 on error - */ -l_ok -pixaaSizeRange(PIXAA *paa, - l_int32 *pminw, - l_int32 *pminh, - l_int32 *pmaxw, - l_int32 *pmaxh) -{ -l_int32 minw, minh, maxw, maxh, minpw, minph, maxpw, maxph, i, n; -PIXA *pixa; - - PROCNAME("pixaaSizeRange"); - - if (pminw) *pminw = 0; - if (pminh) *pminh = 0; - if (pmaxw) *pmaxw = 0; - if (pmaxh) *pmaxh = 0; - if (!paa) - return ERROR_INT("paa not defined", procName, 1); - if (!pminw && !pmaxw && !pminh && !pmaxh) - return ERROR_INT("no data can be returned", procName, 1); - - minw = minh = 100000000; - maxw = maxh = 0; - n = pixaaGetCount(paa, NULL); - for (i = 0; i < n; i++) { - pixa = pixaaGetPixa(paa, i, L_CLONE); - pixaSizeRange(pixa, &minpw, &minph, &maxpw, &maxph); - if (minpw < minw) - minw = minpw; - if (minph < minh) - minh = minph; - if (maxpw > maxw) - maxw = maxpw; - if (maxph > maxh) - maxh = maxph; - pixaDestroy(&pixa); - } - - if (pminw) *pminw = minw; - if (pminh) *pminh = minh; - if (pmaxw) *pmaxw = maxw; - if (pmaxh) *pmaxh = maxh; - return 0; -} - - -/*! - * \brief pixaSizeRange() - * - * \param[in] pixa - * \param[out] pminw, pminh, pmaxw, pmaxh [optional] range of - * dimensions of pix in the array - * \return 0 if OK, 1 on error - */ -l_ok -pixaSizeRange(PIXA *pixa, - l_int32 *pminw, - l_int32 *pminh, - l_int32 *pmaxw, - l_int32 *pmaxh) -{ -l_int32 minw, minh, maxw, maxh, i, n, w, h; -PIX *pix; - - PROCNAME("pixaSizeRange"); - - if (pminw) *pminw = 0; - if (pminh) *pminh = 0; - if (pmaxw) *pmaxw = 0; - if (pmaxh) *pmaxh = 0; - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - if (!pminw && !pmaxw && !pminh && !pmaxh) - return ERROR_INT("no data can be returned", procName, 1); - - minw = minh = 1000000; - maxw = maxh = 0; - n = pixaGetCount(pixa); - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixa, i, L_CLONE); - w = pixGetWidth(pix); - h = pixGetHeight(pix); - if (w < minw) - minw = w; - if (h < minh) - minh = h; - if (w > maxw) - maxw = w; - if (h > maxh) - maxh = h; - pixDestroy(&pix); - } - - if (pminw) *pminw = minw; - if (pminh) *pminh = minh; - if (pmaxw) *pmaxw = maxw; - if (pmaxh) *pmaxh = maxh; - - return 0; -} - - -/*! - * \brief pixaClipToPix() - * - * \param[in] pixas - * \param[in] 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 pixaClipToForeground() - * - * \param[in] pixas - * \param[out] ppixad [optional] pixa of clipped pix returned - * \param[out] pboxa [optional] clipping boxes returned - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) At least one of [&pixd, &boxa] must be specified. - * (2) Any pix with no fg pixels is skipped. - * (3) See pixClipToForeground(). - *- */ -l_ok -pixaClipToForeground(PIXA *pixas, - PIXA **ppixad, - BOXA **pboxa) -{ -l_int32 i, n; -BOX *box1; -PIX *pix1, *pix2; - - PROCNAME("pixaClipToForeground"); - - if (ppixad) *ppixad = NULL; - if (pboxa) *pboxa = NULL; - if (!pixas) - return ERROR_INT("pixas not defined", procName, 1); - if (!ppixad && !pboxa) - return ERROR_INT("no output requested", procName, 1); - - n = pixaGetCount(pixas); - if (ppixad) *ppixad = pixaCreate(n); - if (pboxa) *pboxa = boxaCreate(n); - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixas, i, L_CLONE); - pixClipToForeground(pix1, &pix2, &box1); - pixDestroy(&pix1); - if (ppixad) - pixaAddPix(*ppixad, pix2, L_INSERT); - else - pixDestroy(&pix2); - if (pboxa) - boxaAddBox(*pboxa, box1, L_INSERT); - else - boxDestroy(&box1); - } - - return 0; -} - - -/*! - * \brief pixaGetRenderingDepth() - * - * \param[in] pixa - * \param[out] pdepth depth required to render if all colormaps are removed - * \return 0 if OK; 1 on error - */ -l_ok -pixaGetRenderingDepth(PIXA *pixa, - l_int32 *pdepth) -{ -l_int32 hascolor, maxdepth; - - PROCNAME("pixaGetRenderingDepth"); - - if (!pdepth) - return ERROR_INT("&depth not defined", procName, 1); - *pdepth = 0; - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - - pixaHasColor(pixa, &hascolor); - if (hascolor) { - *pdepth = 32; - return 0; - } - - pixaGetDepthInfo(pixa, &maxdepth, NULL); - if (maxdepth == 1) - *pdepth = 1; - else /* 2, 4, 8 or 16 */ - *pdepth = 8; - return 0; -} - - -/*! - * \brief pixaHasColor() - * - * \param[in] pixa - * \param[out] phascolor 1 if any pix is rgb or has a colormap with color; - * 0 otherwise - * \return 0 if OK; 1 on error - */ -l_ok -pixaHasColor(PIXA *pixa, - l_int32 *phascolor) -{ -l_int32 i, n, hascolor, d; -PIX *pix; -PIXCMAP *cmap; - - PROCNAME("pixaHasColor"); - - if (!phascolor) - return ERROR_INT("&hascolor not defined", procName, 1); - *phascolor = 0; - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - - n = pixaGetCount(pixa); - hascolor = 0; - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixa, i, L_CLONE); - if ((cmap = pixGetColormap(pix)) != NULL) - pixcmapHasColor(cmap, &hascolor); - d = pixGetDepth(pix); - pixDestroy(&pix); - if (d == 32 || hascolor == 1) { - *phascolor = 1; - break; - } - } - - return 0; -} - - -/*! - * \brief pixaAnyColormaps() - * - * \param[in] pixa - * \param[out] phascmap 1 if any pix has a colormap; 0 otherwise - * \return 0 if OK; 1 on error - */ -l_ok -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 pixaGetDepthInfo() - * - * \param[in] pixa - * \param[out] pmaxdepth [optional] max pixel depth of pix in pixa - * \param[out] psame [optional] true if all depths are equal - * \return 0 if OK; 1 on error - */ -l_ok -pixaGetDepthInfo(PIXA *pixa, - l_int32 *pmaxdepth, - l_int32 *psame) -{ -l_int32 i, n, d, d0; -l_int32 maxd, same; /* depth info */ - - PROCNAME("pixaGetDepthInfo"); - - if (pmaxdepth) *pmaxdepth = 0; - if (psame) *psame = TRUE; - if (!pmaxdepth && !psame) return 0; - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - if ((n = pixaGetCount(pixa)) == 0) - return ERROR_INT("pixa is empty", procName, 1); - - same = TRUE; - maxd = 0; - for (i = 0; i < n; i++) { - pixaGetPixDimensions(pixa, i, NULL, NULL, &d); - if (i == 0) - d0 = d; - else if (d != d0) - same = FALSE; - if (d > maxd) maxd = d; - } - - if (pmaxdepth) *pmaxdepth = maxd; - if (psame) *psame = same; - return 0; -} - - -/*! - * \brief pixaConvertToSameDepth() - * - * \param[in] pixas - * \return pixad, or NULL on error - * - *- * Notes: - * (1) If any pix has a colormap, they are all converted to rgb. - * Otherwise, they are all converted to the maximum depth of - * all the pix. - * (2) This can be used to allow lossless rendering onto a single pix. - *- */ -PIXA * -pixaConvertToSameDepth(PIXA *pixas) -{ -l_int32 i, n, same, hascmap, maxdepth; -BOXA *boxa; -PIX *pix1, *pix2; -PIXA *pixa1, *pixad; - - PROCNAME("pixaConvertToSameDepth"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - - /* Remove colormaps to rgb */ - if ((n = pixaGetCount(pixas)) == 0) - return (PIXA *)ERROR_PTR("no components", procName, NULL); - pixaAnyColormaps(pixas, &hascmap); - if (hascmap) { - pixa1 = pixaCreate(n); - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixas, i, L_CLONE); - pix2 = pixConvertTo32(pix1); - pixaAddPix(pixa1, pix2, L_INSERT); - pixDestroy(&pix1); - } - } else { - pixa1 = pixaCopy(pixas, L_CLONE); - } - - pixaGetDepthInfo(pixa1, &maxdepth, &same); - if (!same) { /* at least one pix has depth < maxdepth */ - pixad = pixaCreate(n); - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixa1, i, L_CLONE); - if (maxdepth <= 8) - pix2 = pixConvertTo8(pix1, 0); - else - pix2 = pixConvertTo32(pix1); - pixaAddPix(pixad, pix2, L_INSERT); - pixDestroy(&pix1); - } - } else { - pixad = pixaCopy(pixa1, L_CLONE); - } - - boxa = pixaGetBoxa(pixas, L_COPY); - pixaSetBoxa(pixad, boxa, L_INSERT); - pixaDestroy(&pixa1); - return pixad; -} - - -/*! - * \brief pixaEqual() - * - * \param[in] pixa1 - * \param[in] pixa2 - * \param[in] maxdist - * \param[out] pnaindex [optional] index array of correspondences - * \param[out] psame 1 if equal; 0 otherwise - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The two pixa are the "same" if they contain the same - * boxa and the same ordered set of pix. However, if they - * have boxa, the pix in each pixa can differ in ordering - * by an amount given by the parameter %maxdist. If they - * don't have a boxa, the %maxdist parameter is ignored, - * and the ordering of the pix must be identical. - * (2) This applies only to boxa geometry, pixels and ordering; - * other fields in the pix are ignored. - * (3) naindex[i] gives the position of the box in pixa2 that - * corresponds to box i in pixa1. It is only returned if the - * pixa have boxa and the boxa are equal. - * (4) In situations where the ordering is very different, so that - * a large %maxdist is required for "equality", this should be - * implemented with a hash function for efficiency. - *- */ -l_ok -pixaEqual(PIXA *pixa1, - PIXA *pixa2, - l_int32 maxdist, - NUMA **pnaindex, - l_int32 *psame) -{ -l_int32 i, j, n, empty1, empty2, same, sameboxa; -BOXA *boxa1, *boxa2; -NUMA *na; -PIX *pix1, *pix2; - - PROCNAME("pixaEqual"); - - if (pnaindex) *pnaindex = NULL; - if (!psame) - return ERROR_INT("&same not defined", procName, 1); - *psame = 0; - sameboxa = 0; - na = NULL; - if (!pixa1 || !pixa2) - return ERROR_INT("pixa1 and pixa2 not both defined", procName, 1); - n = pixaGetCount(pixa1); - if (n != pixaGetCount(pixa2)) - return 0; - - /* If there are no boxes, strict ordering of the pix in each - * pixa is required. */ - boxa1 = pixaGetBoxa(pixa1, L_CLONE); - boxa2 = pixaGetBoxa(pixa2, L_CLONE); - empty1 = (boxaGetCount(boxa1) == 0) ? 1 : 0; - empty2 = (boxaGetCount(boxa2) == 0) ? 1 : 0; - if (!empty1 && !empty2) { - boxaEqual(boxa1, boxa2, maxdist, &na, &sameboxa); - if (!sameboxa) { - boxaDestroy(&boxa1); - boxaDestroy(&boxa2); - numaDestroy(&na); - return 0; - } - } - boxaDestroy(&boxa1); - boxaDestroy(&boxa2); - if ((!empty1 && empty2) || (empty1 && !empty2)) - return 0; - - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixa1, i, L_CLONE); - if (na) - numaGetIValue(na, i, &j); - else - j = i; - pix2 = pixaGetPix(pixa2, j, L_CLONE); - pixEqual(pix1, pix2, &same); - pixDestroy(&pix1); - pixDestroy(&pix2); - if (!same) { - numaDestroy(&na); - return 0; - } - } - - *psame = 1; - if (pnaindex) - *pnaindex = na; - else - numaDestroy(&na); - return 0; -} - - -/*! - * \brief pixaSetFullSizeBoxa() - * - * \param[in] pixa - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Replaces the existing boxa. Each box gives the dimensions - * of the corresponding pix. This is needed for functions - * like pixaSort() that sort based on the boxes. - *- */ -l_ok -pixaSetFullSizeBoxa(PIXA *pixa) -{ -l_int32 i, n, w, h; -BOX *box; -BOXA *boxa; -PIX *pix; - - PROCNAME("pixaSetFullSizeBoxa"); - - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - if ((n = pixaGetCount(pixa)) == 0) { - L_INFO("pixa contains no pix\n", procName); - return 0; - } - - boxa = boxaCreate(n); - pixaSetBoxa(pixa, boxa, L_INSERT); - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixa, i, L_CLONE); - pixGetDimensions(pix, &w, &h, NULL); - box = boxCreate(0, 0, w, h); - boxaAddBox(boxa, box, L_INSERT); - pixDestroy(&pix); - } - return 0; -} - diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixafunc2.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixafunc2.c deleted file mode 100644 index a362cfe5..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixafunc2.c +++ /dev/null @@ -1,2610 +0,0 @@ -/*====================================================================* - - 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 pixafunc2.c - *- * - * Pixa display (render into a pix) - * PIX *pixaDisplay() - * PIX *pixaDisplayRandomCmap() - * PIX *pixaDisplayLinearly() - * PIX *pixaDisplayOnLattice() - * PIX *pixaDisplayUnsplit() - * PIX *pixaDisplayTiled() - * PIX *pixaDisplayTiledInRows() - * PIX *pixaDisplayTiledInColumns() - * PIX *pixaDisplayTiledAndScaled() - * PIX *pixaDisplayTiledWithText() - * PIX *pixaDisplayTiledByIndex() - * - * Pixaa display (render into a pix) - * PIX *pixaaDisplay() - * PIX *pixaaDisplayByPixa() - * PIXA *pixaaDisplayTiledAndScaled() - * - * Conversion of all pix to specified type (e.g., depth) - * PIXA *pixaConvertTo1() - * PIXA *pixaConvertTo8() - * PIXA *pixaConvertTo8Colormap() - * PIXA *pixaConvertTo32() - * - * Pixa constrained selection and pdf generation - * PIXA *pixaConstrainedSelect() - * l_int32 pixaSelectToPdf() - * - * Generate pixa from tiled images - * PIXA *pixaMakeFromTiledPixa() - * PIXA *pixaMakeFromTiledPix() - * l_int32 pixGetTileCount() - * - * Pixa display into multiple tiles - * PIXA *pixaDisplayMultiTiled() - * - * Split pixa into files - * l_int32 pixaSplitIntoFiles() - * - * Tile N-Up - * l_int32 convertToNUpFiles() - * PIXA *convertToNUpPixa() - * PIXA *pixaConvertToNUpPixa() - * - * Render two pixa side-by-side for comparison * - * l_int32 pixaCompareInPdf() - * - * We give twelve pixaDisplay*() methods for tiling a pixa in a pix. - * Some work for 1 bpp input; others for any input depth. - * Some give an output depth that depends on the input depth; - * others give a different output depth or allow you to choose it. - * Some use a boxes to determine where each pix goes; others tile - * onto a regular lattice; others tile onto an irregular lattice; - * one uses an associated index array to determine which column - * each pix goes into. - * - * Here is a brief description of what the pixa display functions do. - * - * pixaDisplay() - * This uses the boxes in the pixa to lay out each pix. This - * can be used to reconstruct a pix that has been broken into - * components, if the boxes represents the positions of the - * components in the original image. - * pixaDisplayRandomCmap() - * This also uses the boxes to lay out each pix. However, it creates - * a colormapped dest, where each 1 bpp pix is given a randomly - * generated color (up to 256 are used). - * pixaDisplayLinearly() - * This puts each pix, sequentially, in a line, either horizontally - * or vertically. - * pixaDisplayOnLattice() - * This puts each pix, sequentially, onto a regular lattice, - * omitting any pix that are too big for the lattice size. - * This is useful, for example, to store bitmapped fonts, - * where all the characters are stored in a single image. - * pixaDisplayUnsplit() - * This lays out a mosaic of tiles (the pix in the pixa) that - * are all of equal size. (Don't use this for unequal sized pix!) - * For example, it can be used to invert the action of - * pixaSplitPix(). - * pixaDisplayTiled() - * Like pixaDisplayOnLattice(), this places each pix on a regular - * lattice, but here the lattice size is determined by the - * largest component, and no components are omitted. This is - * dangerous if there are thousands of small components and - * one or more very large one, because the size of the resulting - * pix can be huge! - * pixaDisplayTiledInRows() - * This puts each pix down in a series of rows, where the upper - * edges of each pix in a row are aligned and there is a uniform - * spacing between the pix. The height of each row is determined - * by the tallest pix that was put in the row. This function - * is a reasonably efficient way to pack the subimages. - * A boxa of the locations of each input pix is stored in the output. - * pixaDisplayTiledInColumns() - * This puts each pix down in a series of rows, each row having - * a specified number of pix. The upper edges of each pix in a - * row are aligned and there is a uniform spacing between the pix. - * The height of each row is determined by the tallest pix that - * was put in the row. A boxa of the locations of each input - * pix is stored in the output. - * pixaDisplayTiledAndScaled() - * This scales each pix to a given width and output depth, and then - * tiles them in rows with a given number placed in each row. - * This is useful for presenting a sequence of images that can be - * at different resolutions, but which are derived from the same - * initial image. - * pixaDisplayTiledWithText() - * This is a version of pixaDisplayTiledInRows() that prints, below - * each pix, the text in the pix text field. It renders a pixa - * to an image with white background that does not exceed a - * given value in width. - * pixaDisplayTiledByIndex() - * This scales each pix to a given width and output depth, - * and then tiles them in columns corresponding to the value - * in an associated numa. All pix with the same index value are - * rendered in the same column. Text in the pix text field are - * rendered below the pix. - * - * To render mosaics of images in a pixaa, display functions are - * provided that handle situations where the images are all scaled to - * the same size, or the number of images on each row needs to vary. - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include /* for sqrt() */ -#include "allheaders.h" - -/*---------------------------------------------------------------------* - * Pixa Display * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaDisplay() - * - * \param[in] pixa - * \param[in] w, h if set to 0, the size is determined from the - * bounding box 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, res; -BOXA *boxa; -PIX *pix1, *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\n", 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); - if (w == 0 || h == 0) - return (PIX *)ERROR_PTR("no associated boxa", procName, NULL); - } - - /* Use the first pix in pixa to determine depth and resolution */ - pix1 = pixaGetPix(pixa, 0, L_CLONE); - d = pixGetDepth(pix1); - res = pixGetXRes(pix1); - pixDestroy(&pix1); - - if ((pixd = pixCreate(w, h, d)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixSetResolution(pixd, res, res); - if (d > 1) - pixSetAll(pixd); - for (i = 0; i < n; i++) { - if (pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb)) { - L_WARNING("no box found!\n", procName); - continue; - } - pix1 = pixaGetPix(pixa, i, L_CLONE); - if (d == 1) - pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pix1, 0, 0); - else - pixRasterop(pixd, xb, yb, wb, hb, PIX_SRC, pix1, 0, 0); - pixDestroy(&pix1); - } - - return pixd; -} - - -/*! - * \brief pixaDisplayRandomCmap() - * - * \param[in] pixa 1 bpp regions, with boxa delineating those regions - * \param[in] w, h if set to 0, the size is determined from the - * bounding box of the components in pixa - * \return pix 8 bpp, cmapped, with random colors assigned to each region, - * or NULL on error. - * - *- * Notes: - * (1) This uses the boxes to place each pix in the rendered composite. - * The fg of each pix in %pixa, such as a single connected - * component or a line of text, is given a random color. - * (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, same, maxd, index, xb, yb, wb, hb, res; -BOXA *boxa; -PIX *pixs, *pix1, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixaDisplayRandomCmap"); - - if (!pixa) - return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); - - if ((n = pixaGetCount(pixa)) == 0) - return (PIX *)ERROR_PTR("no components", procName, NULL); - pixaVerifyDepth(pixa, &same, &maxd); - if (maxd > 1) - return (PIX *)ERROR_PTR("not all components are 1 bpp", procName, NULL); - - /* If w and h are 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); - if (i == 0) res = pixGetXRes(pixs); - pix1 = pixConvert1To8(NULL, pixs, 0, index); - pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pix1, 0, 0); - pixDestroy(&pixs); - pixDestroy(&pix1); - } - - pixSetResolution(pixd, res, res); - return pixd; -} - - -/*! - * \brief pixaDisplayLinearly() - * - * \param[in] pixas - * \param[in] direction L_HORIZ or L_VERT - * \param[in] scalefactor applied to every pix; use 1.0 for no scaling - * \param[in] background 0 for white, 1 for black; this is the color - * of the spacing between the images - * \param[in] spacing between images, and on outside - * \param[in] border width of black border added to each image; - * use 0 for no border - * \param[out] pboxa [optional] location of images in output pix - * \return pix of composite images, or NULL on error - * - *- * Notes: - * (1) This puts each pix, sequentially, in a line, either horizontally - * or vertically. - * (2) If any pix has a colormap, all pix are rendered in rgb. - * (3) The boxa gives the location of each image. - *- */ -PIX * -pixaDisplayLinearly(PIXA *pixas, - l_int32 direction, - l_float32 scalefactor, - l_int32 background, /* not used */ - l_int32 spacing, - l_int32 border, - BOXA **pboxa) -{ -l_int32 i, n, x, y, w, h, size, depth, bordval; -BOX *box; -PIX *pix1, *pix2, *pix3, *pixd; -PIXA *pixa1, *pixa2; - - PROCNAME("pixaDisplayLinearly"); - - if (pboxa) *pboxa = NULL; - if (!pixas) - return (PIX *)ERROR_PTR("pixas not defined", procName, NULL); - if (direction != L_HORIZ && direction != L_VERT) - return (PIX *)ERROR_PTR("invalid direction", procName, NULL); - - /* Make sure all pix are at the same depth */ - pixa1 = pixaConvertToSameDepth(pixas); - pixaGetDepthInfo(pixa1, &depth, NULL); - - /* Scale and add border if requested */ - n = pixaGetCount(pixa1); - pixa2 = pixaCreate(n); - bordval = (depth == 1) ? 1 : 0; - size = (n - 1) * spacing; - x = y = 0; - for (i = 0; i < n; i++) { - if ((pix1 = pixaGetPix(pixa1, i, L_CLONE)) == NULL) { - L_WARNING("missing pix at index %d\n", procName, i); - continue; - } - - if (scalefactor != 1.0) - pix2 = pixScale(pix1, scalefactor, scalefactor); - else - pix2 = pixClone(pix1); - if (border) - pix3 = pixAddBorder(pix2, border, bordval); - else - pix3 = pixClone(pix2); - - pixGetDimensions(pix3, &w, &h, NULL); - box = boxCreate(x, y, w, h); - if (direction == L_HORIZ) { - size += w; - x += w + spacing; - } else { /* vertical */ - size += h; - y += h + spacing; - } - pixaAddPix(pixa2, pix3, L_INSERT); - pixaAddBox(pixa2, box, L_INSERT); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - pixd = pixaDisplay(pixa2, 0, 0); - - if (pboxa) - *pboxa = pixaGetBoxa(pixa2, L_COPY); - pixaDestroy(&pixa1); - pixaDestroy(&pixa2); - return pixd; -} - - -/*! - * \brief pixaDisplayOnLattice() - * - * \param[in] pixa - * \param[in] cellw lattice cell width - * \param[in] cellh lattice cell height - * \param[out] pncols [optional] number of columns in output lattice - * \param[out] pboxa [optional] location of images in lattice - * \return pix of composite images, or NULL on error - * - *- * Notes: - * (1) This places each pix on sequentially on a regular lattice - * in the rendered composite. If a pix is too large to fit in the - * allocated lattice space, it is not rendered. - * (2) If any pix has a colormap, all pix are rendered in rgb. - * (3) This is useful when putting bitmaps of components, - * such as characters, into a single image. - * (4) Save the number of tiled images in the text field of the pix, - * in the format: n = %d. This survives write/read into png files, - * for example. - * (5) The boxa gives the location of each image. The UL corner - * of each image is on a lattice cell corner. Omitted images - * (due to size) are assigned an invalid width and height of 0. - *- */ -PIX * -pixaDisplayOnLattice(PIXA *pixa, - l_int32 cellw, - l_int32 cellh, - l_int32 *pncols, - BOXA **pboxa) -{ -char buf[16]; -l_int32 n, nw, nh, w, h, d, wt, ht, res, samedepth; -l_int32 index, i, j, hascmap; -BOX *box; -BOXA *boxa; -PIX *pix1, *pix2, *pixd; -PIXA *pixa1; - - PROCNAME("pixaDisplayOnLattice"); - - if (pncols) *pncols = 0; - if (pboxa) *pboxa = NULL; - if (!pixa) - return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); - - /* If any pix have colormaps, or if the depths differ, generate rgb */ - if ((n = pixaGetCount(pixa)) == 0) - return (PIX *)ERROR_PTR("no components", procName, NULL); - pixaAnyColormaps(pixa, &hascmap); - pixaVerifyDepth(pixa, &samedepth, NULL); - if (hascmap || !samedepth) { - pixa1 = pixaCreate(n); - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixa, i, L_CLONE); - pix2 = pixConvertTo32(pix1); - pixaAddPix(pixa1, pix2, L_INSERT); - pixDestroy(&pix1); - } - } else { - pixa1 = pixaCopy(pixa, L_CLONE); - } - - /* Have number of rows and columns approximately equal */ - nw = (l_int32)sqrt((l_float64)n); - nh = (n + nw - 1) / nw; - w = cellw * nw; - h = cellh * nh; - - /* Use the first pix to determine output depth and resolution */ - pix1 = pixaGetPix(pixa1, 0, L_CLONE); - d = pixGetDepth(pix1); - res = pixGetXRes(pix1); - pixDestroy(&pix1); - if ((pixd = pixCreate(w, h, d)) == NULL) { - pixaDestroy(&pixa1); - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - pixSetBlackOrWhite(pixd, L_SET_WHITE); - pixSetResolution(pixd, res, res); - boxa = boxaCreate(n); - - /* Tile the output */ - index = 0; - for (i = 0; i < nh; i++) { - for (j = 0; j < nw && index < n; j++, index++) { - pix1 = pixaGetPix(pixa1, index, L_CLONE); - pixGetDimensions(pix1, &wt, &ht, NULL); - if (wt > cellw || ht > cellh) { - L_INFO("pix(%d) omitted; size %dx%x\n", procName, index, - wt, ht); - box = boxCreate(0, 0, 0, 0); - boxaAddBox(boxa, box, L_INSERT); - pixDestroy(&pix1); - continue; - } - pixRasterop(pixd, j * cellw, i * cellh, wt, ht, - PIX_SRC, pix1, 0, 0); - box = boxCreate(j * cellw, i * cellh, wt, ht); - boxaAddBox(boxa, box, L_INSERT); - pixDestroy(&pix1); - } - } - - /* Save the number of tiles in the text field */ - snprintf(buf, sizeof(buf), "n = %d", boxaGetCount(boxa)); - pixSetText(pixd, buf); - - if (pncols) *pncols = nw; - if (pboxa) - *pboxa = boxa; - else - boxaDestroy(&boxa); - pixaDestroy(&pixa1); - return pixd; -} - - -/*! - * \brief pixaDisplayUnsplit() - * - * \param[in] pixa - * \param[in] nx number of mosaic cells horizontally - * \param[in] ny number of mosaic cells vertically - * \param[in] borderwidth of added border on all sides - * \param[in] bordercolor in our RGBA format: 0xrrggbbaa - * \return pix of tiled images, or NULL on error - * - *- * Notes: - * (1) This is a logical inverse of pixaSplitPix(). It - * constructs a pix from a mosaic of tiles, all of equal size. - * (2) For added generality, a border of arbitrary color can - * be added to each of the tiles. - * (3) In use, pixa will typically have either been generated - * from pixaSplitPix() or will derived from a pixa that - * was so generated. - * (4) All pix in the pixa must be of equal depth, and, if - * colormapped, have the same colormap. - *- */ -PIX * -pixaDisplayUnsplit(PIXA *pixa, - l_int32 nx, - l_int32 ny, - l_int32 borderwidth, - l_uint32 bordercolor) -{ -l_int32 w, h, d, wt, ht; -l_int32 i, j, k, x, y, n; -PIX *pix1, *pixd; - - PROCNAME("pixaDisplayUnsplit"); - - if (!pixa) - return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); - if (nx <= 0 || ny <= 0) - return (PIX *)ERROR_PTR("nx and ny must be > 0", procName, NULL); - if ((n = pixaGetCount(pixa)) == 0) - return (PIX *)ERROR_PTR("no components", procName, NULL); - if (n != nx * ny) - return (PIX *)ERROR_PTR("n != nx * ny", procName, NULL); - borderwidth = L_MAX(0, borderwidth); - - pixaGetPixDimensions(pixa, 0, &wt, &ht, &d); - w = nx * (wt + 2 * borderwidth); - h = ny * (ht + 2 * borderwidth); - - if ((pixd = pixCreate(w, h, d)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pix1 = pixaGetPix(pixa, 0, L_CLONE); - pixCopyColormap(pixd, pix1); - pixDestroy(&pix1); - if (borderwidth > 0) - pixSetAllArbitrary(pixd, bordercolor); - - y = borderwidth; - for (i = 0, k = 0; i < ny; i++) { - x = borderwidth; - for (j = 0; j < nx; j++, k++) { - pix1 = pixaGetPix(pixa, k, L_CLONE); - pixRasterop(pixd, x, y, wt, ht, PIX_SRC, pix1, 0, 0); - pixDestroy(&pix1); - x += wt + 2 * borderwidth; - } - y += ht + 2 * borderwidth; - } - - return pixd; -} - - -/*! - * \brief pixaDisplayTiled() - * - * \param[in] pixa - * \param[in] maxwidth of output image - * \param[in] background 0 for white, 1 for black - * \param[in] spacing - * \return pix of tiled images, or NULL on error - * - *- * Notes: - * (1) This renders a pixa to a single image of width not to - * exceed maxwidth, with background color either white or black, - * and with each subimage spaced on a regular lattice. - * (2) The lattice size is determined from the largest width and height, - * separately, of all pix in the pixa. - * (3) All pix in the pixa must be of equal depth. - * (4) If any pix has a colormap, all pix are rendered in rgb. - * (5) Careful: because no components are omitted, this is - * dangerous if there are thousands of small components and - * one or more very large one, because the size of the - * resulting pix can be huge! - *- */ -PIX * -pixaDisplayTiled(PIXA *pixa, - l_int32 maxwidth, - l_int32 background, - l_int32 spacing) -{ -l_int32 wmax, hmax, wd, hd, d, hascmap, res, same; -l_int32 i, j, n, ni, ncols, nrows; -l_int32 ystart, xstart, wt, ht; -PIX *pix1, *pix2, *pixd; -PIXA *pixa1; - - PROCNAME("pixaDisplayTiled"); - - if (!pixa) - return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); - - /* If any pix have colormaps, generate rgb */ - if ((n = pixaGetCount(pixa)) == 0) - return (PIX *)ERROR_PTR("no components", procName, NULL); - pixaAnyColormaps(pixa, &hascmap); - if (hascmap) { - pixa1 = pixaCreate(n); - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixa, i, L_CLONE); - pix2 = pixConvertTo32(pix1); - pixaAddPix(pixa1, pix2, L_INSERT); - pixDestroy(&pix1); - } - } else { - pixa1 = pixaCopy(pixa, L_CLONE); - } - - /* Find the max dimensions and depth subimages */ - pixaGetDepthInfo(pixa1, &d, &same); - if (!same) { - pixaDestroy(&pixa1); - return (PIX *)ERROR_PTR("depths not equal", procName, NULL); - } - pixaSizeRange(pixa1, NULL, NULL, &wmax, &hmax); - - /* Get the number of rows and columns and the output image size */ - spacing = L_MAX(spacing, 0); - ncols = (l_int32)((l_float32)(maxwidth - spacing) / - (l_float32)(wmax + spacing)); - ncols = L_MAX(ncols, 1); - nrows = (n + ncols - 1) / ncols; - wd = wmax * ncols + spacing * (ncols + 1); - hd = hmax * nrows + spacing * (nrows + 1); - if ((pixd = pixCreate(wd, hd, d)) == NULL) { - pixaDestroy(&pixa1); - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - - /* Reset the background color if necessary */ - if ((background == 1 && d == 1) || (background == 0 && d != 1)) - pixSetAll(pixd); - - /* Blit the images to the dest */ - for (i = 0, ni = 0; i < nrows; i++) { - ystart = spacing + i * (hmax + spacing); - for (j = 0; j < ncols && ni < n; j++, ni++) { - xstart = spacing + j * (wmax + spacing); - pix1 = pixaGetPix(pixa1, ni, L_CLONE); - if (ni == 0) res = pixGetXRes(pix1); - pixGetDimensions(pix1, &wt, &ht, NULL); - pixRasterop(pixd, xstart, ystart, wt, ht, PIX_SRC, pix1, 0, 0); - pixDestroy(&pix1); - } - } - pixSetResolution(pixd, res, res); - - pixaDestroy(&pixa1); - return pixd; -} - - -/*! - * \brief pixaDisplayTiledInRows() - * - * \param[in] pixa - * \param[in] outdepth output depth: 1, 8 or 32 bpp - * \param[in] maxwidth of output image - * \param[in] scalefactor applied to every pix; use 1.0 for no scaling - * \param[in] background 0 for white, 1 for black; this is the color - * of the spacing between the images - * \param[in] spacing between images, and on outside - * \param[in] 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 renders a pixa to a single image 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. - * (4) A serialized boxa giving the location in pixd of each input - * pix (without added border) is stored in the text string of pixd. - * This allows, e.g., regeneration of a pixa from pixd, using - * pixaCreateFromBoxa(). If there is no scaling and the depth of - * each input pix in the pixa is the same, this tiling operation - * can be inverted using the boxa (except for loss of text in - * each of the input pix): - * pix1 = pixaDisplayTiledInRows(pixa1, 1, 1500, 1.0, 0, 30, 0); - * char *boxatxt = pixGetText(pix1); - * boxa1 = boxaReadMem((l_uint8 *)boxatxt, strlen(boxatxt)); - * pixa2 = pixaCreateFromBoxa(pix1, boxa1, 0, 0, NULL); - *- */ -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, res; -size_t size; -l_uint8 *data; -BOXA *boxa; -NUMA *nainrow; /* number of pix in the row */ -NUMA *namaxh; /* height of max pix in the row */ -PIX *pix, *pixn, *pix1, *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) - pix1 = pixScale(pixn, scalefactor, scalefactor); - else - pix1 = pixClone(pixn); - if (border) - pixd = pixAddBorder(pix1, border, bordval); - else - pixd = pixClone(pix1); - pixDestroy(&pixn); - pixDestroy(&pix1); - - pixaAddPix(pixan, pixd, L_INSERT); - } - if (pixaGetCount(pixan) != n) { - n = pixaGetCount(pixan); - L_WARNING("only got %d components\n", 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, and save the boxa identifying - * the image regions that do not include the borders. */ - nrows = numaGetCount(nainrow); - y = spacing; - boxa = boxaCreate(n); - 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); - if (index == 0) { - res = pixGetXRes(pix); - pixSetResolution(pixd, res, res); - } - pixGetDimensions(pix, &wt, &ht, NULL); - boxaAddBox(boxa, boxCreate(x + border, y + border, - wt - 2 * border, ht - 2 *border), L_INSERT); - pixRasterop(pixd, x, y, wt, ht, PIX_SRC, pix, 0, 0); - pixDestroy(&pix); - x += wt + spacing; - } - y += maxh + spacing; - } - boxaWriteMem(&data, &size, boxa); - pixSetText(pixd, (char *)data); /* data is ascii */ - LEPT_FREE(data); - boxaDestroy(&boxa); - - numaDestroy(&nainrow); - numaDestroy(&namaxh); - pixaDestroy(&pixan); - return pixd; -} - - -/*! - * \brief pixaDisplayTiledInColumns() - * - * \param[in] pixas - * \param[in] nx number of columns in output image - * \param[in] scalefactor applied to every pix; use 1.0 for no scaling - * \param[in] spacing between images, and on outside - * \param[in] 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 renders a pixa to a single image with &nx columns of - * subimages. The background color is white, and each row - * is tiled such that the top of each pix is aligned and - * each pix is separated by 'spacing' from the next one. - * A black border can be added to each pix. - * (2) The output depth is determined by the largest depth - * required by the pix in the pixa. Colormaps are removed. - * (3) A serialized boxa giving the location in pixd of each input - * pix (without added border) is stored in the text string of pixd. - * This allows, e.g., regeneration of a pixa from pixd, using - * pixaCreateFromBoxa(). If there is no scaling and the depth of - * each input pix in the pixa is the same, this tiling operation - * can be inverted using the boxa (except for loss of text in - * each of the input pix): - * pix1 = pixaDisplayTiledInColumns(pixa1, 3, 1.0, 0, 30, 2); - * char *boxatxt = pixGetText(pix1); - * boxa1 = boxaReadMem((l_uint8 *)boxatxt, strlen(boxatxt)); - * pixa2 = pixaCreateFromBoxa(pix1, boxa1, NULL); - *- */ -PIX * -pixaDisplayTiledInColumns(PIXA *pixas, - l_int32 nx, - l_float32 scalefactor, - l_int32 spacing, - l_int32 border) -{ -l_int32 i, j, index, n, x, y, nrows, wb, hb, w, h, maxd, maxh, bordval, res; -size_t size; -l_uint8 *data; -BOX *box; -BOXA *boxa; -PIX *pix1, *pix2, *pix3, *pixd; -PIXA *pixa1, *pixa2; - - PROCNAME("pixaDisplayTiledInColumns"); - - if (!pixas) - return (PIX *)ERROR_PTR("pixas not defined", procName, NULL); - if (border < 0) - border = 0; - if (scalefactor <= 0.0) scalefactor = 1.0; - - if ((n = pixaGetCount(pixas)) == 0) - return (PIX *)ERROR_PTR("no components", procName, NULL); - - /* Convert to same depth, if necessary */ - pixa1 = pixaConvertToSameDepth(pixas); - pixaGetDepthInfo(pixa1, &maxd, NULL); - - /* Scale and optionally add border */ - pixa2 = pixaCreate(n); - bordval = (maxd == 1) ? 1 : 0; - for (i = 0; i < n; i++) { - if ((pix1 = pixaGetPix(pixa1, i, L_CLONE)) == NULL) - continue; - if (scalefactor != 1.0) - pix2 = pixScale(pix1, scalefactor, scalefactor); - else - pix2 = pixClone(pix1); - if (border) - pix3 = pixAddBorder(pix2, border, bordval); - else - pix3 = pixClone(pix2); - if (i == 0) res = pixGetXRes(pix3); - pixaAddPix(pixa2, pix3, L_INSERT); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - pixaDestroy(&pixa1); - if (pixaGetCount(pixa2) != n) { - n = pixaGetCount(pixa2); - L_WARNING("only got %d components\n", procName, n); - if (n == 0) { - pixaDestroy(&pixa2); - return (PIX *)ERROR_PTR("no components", procName, NULL); - } - } - - /* Compute layout parameters and save as a boxa */ - boxa = boxaCreate(n); - nrows = (n + nx - 1) / nx; - y = spacing; - for (i = 0, index = 0; i < nrows; i++) { - x = spacing; - maxh = 0; - for (j = 0; j < nx && index < n; j++) { - pixaGetPixDimensions(pixa2, index, &wb, &hb, NULL); - box = boxCreate(x, y, wb, hb); - boxaAddBox(boxa, box, L_INSERT); - maxh = L_MAX(maxh, hb + spacing); - x += wb + spacing; - index++; - } - y += maxh; - } - pixaSetBoxa(pixa2, boxa, L_INSERT); - - /* Render the output pix */ - boxaGetExtent(boxa, &w, &h, NULL); - pixd = pixaDisplay(pixa2, w + spacing, h + spacing); - pixSetResolution(pixd, res, res); - - /* Save the boxa in the text field of the output pix */ - boxaWriteMem(&data, &size, boxa); - pixSetText(pixd, (char *)data); /* data is ascii */ - LEPT_FREE(data); - - pixaDestroy(&pixa2); - return pixd; -} - - -/*! - * \brief pixaDisplayTiledAndScaled() - * - * \param[in] pixa - * \param[in] outdepth output depth: 1, 8 or 32 bpp - * \param[in] tilewidth each pix is scaled to this width - * \param[in] ncols number of tiles in each row - * \param[in] background 0 for white, 1 for black; this is the color - * of the spacing between the images - * \param[in] spacing between images, and on outside - * \param[in] border width of additional black border on each image; - * use 0 for no border - * \return pix of tiled images, or NULL on error - * - *- * Notes: - * (1) This can be used to tile a number of renderings of - * an image that are at different scales and depths. - * (2) Each image, after scaling and optionally adding the - * black border, has width 'tilewidth'. Thus, the border does - * not affect the spacing between the image tiles. The - * maximum allowed border width is tilewidth / 5. - *- */ -PIX * -pixaDisplayTiledAndScaled(PIXA *pixa, - l_int32 outdepth, - l_int32 tilewidth, - l_int32 ncols, - l_int32 background, - l_int32 spacing, - l_int32 border) -{ -l_int32 x, y, w, h, wd, hd, d, res; -l_int32 i, n, nrows, maxht, ninrow, irow, bordval; -l_int32 *rowht; -l_float32 scalefact; -PIX *pix, *pixn, *pix1, *pixb, *pixd; -PIXA *pixan; - - PROCNAME("pixaDisplayTiledAndScaled"); - - 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 (ncols <= 0) - return (PIX *)ERROR_PTR("ncols must be > 0", procName, NULL); - if (border < 0 || border > tilewidth / 5) - border = 0; - - if ((n = pixaGetCount(pixa)) == 0) - return (PIX *)ERROR_PTR("no components", procName, NULL); - - /* Normalize scale and depth for each pix; 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; - - pixGetDimensions(pix, &w, &h, &d); - scalefact = (l_float32)(tilewidth - 2 * border) / (l_float32)w; - if (d == 1 && outdepth > 1 && scalefact < 1.0) - pix1 = pixScaleToGray(pix, scalefact); - else - pix1 = pixScale(pix, scalefact, scalefact); - - if (outdepth == 1) - pixn = pixConvertTo1(pix1, 128); - else if (outdepth == 8) - pixn = pixConvertTo8(pix1, FALSE); - else /* outdepth == 32 */ - pixn = pixConvertTo32(pix1); - pixDestroy(&pix1); - - if (border) - pixb = pixAddBorder(pixn, border, bordval); - else - pixb = pixClone(pixn); - - pixaAddPix(pixan, pixb, L_INSERT); - pixDestroy(&pix); - pixDestroy(&pixn); - } - if ((n = pixaGetCount(pixan)) == 0) { /* should not have changed! */ - pixaDestroy(&pixan); - return (PIX *)ERROR_PTR("no components", procName, NULL); - } - - /* Determine the size of each row and of pixd */ - wd = tilewidth * ncols + spacing * (ncols + 1); - nrows = (n + ncols - 1) / ncols; - if ((rowht = (l_int32 *)LEPT_CALLOC(nrows, sizeof(l_int32))) == NULL) { - pixaDestroy(&pixan); - return (PIX *)ERROR_PTR("rowht array not made", procName, NULL); - } - maxht = 0; - ninrow = 0; - irow = 0; - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixan, i, L_CLONE); - ninrow++; - pixGetDimensions(pix, &w, &h, NULL); - maxht = L_MAX(h, maxht); - if (ninrow == ncols) { - rowht[irow] = maxht; - maxht = ninrow = 0; /* reset */ - irow++; - } - pixDestroy(&pix); - } - if (ninrow > 0) { /* last fencepost */ - rowht[irow] = maxht; - irow++; /* total number of rows */ - } - nrows = irow; - hd = spacing * (nrows + 1); - for (i = 0; i < nrows; i++) - hd += rowht[i]; - - pixd = pixCreate(wd, hd, outdepth); - if ((background == 1 && outdepth == 1) || - (background == 0 && outdepth != 1)) - pixSetAll(pixd); - - /* Now blit images to pixd */ - x = y = spacing; - irow = 0; - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixan, i, L_CLONE); - if (i == 0) { - res = pixGetXRes(pix); - pixSetResolution(pixd, res, res); - } - pixGetDimensions(pix, &w, &h, NULL); - if (i && ((i % ncols) == 0)) { /* start new row */ - x = spacing; - y += spacing + rowht[irow]; - irow++; - } - pixRasterop(pixd, x, y, w, h, PIX_SRC, pix, 0, 0); - x += tilewidth + spacing; - pixDestroy(&pix); - } - - pixaDestroy(&pixan); - LEPT_FREE(rowht); - return pixd; -} - - -/*! - * \brief pixaDisplayTiledWithText() - * - * \param[in] pixa - * \param[in] maxwidth of output image - * \param[in] scalefactor applied to every pix; use 1.0 for no scaling - * \param[in] spacing between images, and on outside - * \param[in] border width of black border added to each image; - * use 0 for no border - * \param[in] fontsize 4, 6, ... 20 - * \param[in] textcolor 0xrrggbb00 - * \return pixd of tiled images, or NULL on error - * - *- * Notes: - * (1) This is a version of pixaDisplayTiledInRows() that prints, below - * each pix, the text in the pix text field. Up to 127 chars - * of text in the pix text field are rendered below each pix. - * (2) It renders a pixa to a single image of width not to - * exceed %maxwidth, with white background color, with each row - * tiled such that the top of each pix is aligned and separated - * by %spacing from the next one. - * (3) All pix are converted to 32 bpp. - * (4) This does a reasonably spacewise-efficient job of laying - * out the individual pix images into a tiled composite. - *- */ -PIX * -pixaDisplayTiledWithText(PIXA *pixa, - l_int32 maxwidth, - l_float32 scalefactor, - l_int32 spacing, - l_int32 border, - l_int32 fontsize, - l_uint32 textcolor) -{ -char buf[128]; -char *textstr; -l_int32 i, n, maxw; -L_BMF *bmf; -PIX *pix1, *pix2, *pix3, *pix4, *pixd; -PIXA *pixad; - - PROCNAME("pixaDisplayTiledWithText"); - - if (!pixa) - return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); - if ((n = pixaGetCount(pixa)) == 0) - return (PIX *)ERROR_PTR("no components", procName, NULL); - if (maxwidth <= 0) - return (PIX *)ERROR_PTR("invalid maxwidth", procName, NULL); - if (border < 0) - border = 0; - if (scalefactor <= 0.0) { - L_WARNING("invalid scalefactor; setting to 1.0\n", procName); - scalefactor = 1.0; - } - if (fontsize < 4 || fontsize > 20 || (fontsize & 1)) { - l_int32 fsize = L_MAX(L_MIN(fontsize, 20), 4); - if (fsize & 1) fsize--; - L_WARNING("changed fontsize from %d to %d\n", procName, - fontsize, fsize); - fontsize = fsize; - } - - /* Be sure the width can accommodate a single column of images */ - pixaSizeRange(pixa, NULL, NULL, &maxw, NULL); - maxwidth = L_MAX(maxwidth, scalefactor * (maxw + 2 * spacing + 2 * border)); - - bmf = bmfCreate(NULL, fontsize); - pixad = pixaCreate(n); - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixa, i, L_CLONE); - pix2 = pixConvertTo32(pix1); - pix3 = pixAddBorderGeneral(pix2, spacing, spacing, spacing, - spacing, 0xffffff00); - textstr = pixGetText(pix1); - if (textstr && strlen(textstr) > 0) { - snprintf(buf, sizeof(buf), "%s", textstr); - pix4 = pixAddSingleTextblock(pix3, bmf, buf, textcolor, - L_ADD_BELOW, NULL); - } else { - pix4 = pixClone(pix3); - } - pixaAddPix(pixad, pix4, L_INSERT); - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - } - bmfDestroy(&bmf); - - pixd = pixaDisplayTiledInRows(pixad, 32, maxwidth, scalefactor, - 0, 10, border); - pixaDestroy(&pixad); - return pixd; -} - - -/*! - * \brief pixaDisplayTiledByIndex() - * - * \param[in] pixa - * \param[in] na numa with indices corresponding to the pix in pixa - * \param[in] width each pix is scaled to this width - * \param[in] spacing between images, and on outside - * \param[in] border width of black border added to each image; - * use 0 for no border - * \param[in] fontsize 4, 6, ... 20 - * \param[in] textcolor 0xrrggbb00 - * \return pixd of tiled images, or NULL on error - * - *- * Notes: - * (1) This renders a pixa to a single image with white - * background color, where the pix are placed in columns - * given by the index value in the numa. Each pix - * is separated by %spacing from the adjacent ones, and - * an optional border is placed around them. - * (2) Up to 127 chars of text in the pix text field are rendered - * below each pix. Use newlines in the text field to write - * the text in multiple lines that fit within the pix width. - * (3) To avoid having empty columns, if there are N different - * index values, they should be in [0 ... N-1]. - * (4) All pix are converted to 32 bpp. - *- */ -PIX * -pixaDisplayTiledByIndex(PIXA *pixa, - NUMA *na, - l_int32 width, - l_int32 spacing, - l_int32 border, - l_int32 fontsize, - l_uint32 textcolor) -{ -char buf[128]; -char *textstr; -l_int32 i, n, x, y, w, h, yval, index; -l_float32 maxindex; -L_BMF *bmf; -BOX *box; -NUMA *nay; /* top of the next pix to add in that column */ -PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pixd; -PIXA *pixad; - - PROCNAME("pixaDisplayTiledByIndex"); - - if (!pixa) - return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); - if (!na) - return (PIX *)ERROR_PTR("na not defined", procName, NULL); - if ((n = pixaGetCount(pixa)) == 0) - return (PIX *)ERROR_PTR("no pixa components", procName, NULL); - if (n != numaGetCount(na)) - return (PIX *)ERROR_PTR("pixa and na counts differ", procName, NULL); - if (width <= 0) - return (PIX *)ERROR_PTR("invalid width", procName, NULL); - if (width < 20) - L_WARNING("very small width: %d\n", procName, width); - if (border < 0) - border = 0; - if (fontsize < 4 || fontsize > 20 || (fontsize & 1)) { - l_int32 fsize = L_MAX(L_MIN(fontsize, 20), 4); - if (fsize & 1) fsize--; - L_WARNING("changed fontsize from %d to %d\n", procName, - fontsize, fsize); - fontsize = fsize; - } - - /* The pix will be rendered in the order they occupy in pixa. */ - bmf = bmfCreate(NULL, fontsize); - pixad = pixaCreate(n); - numaGetMax(na, &maxindex, NULL); - nay = numaMakeConstant(spacing, lept_roundftoi(maxindex) + 1); - for (i = 0; i < n; i++) { - numaGetIValue(na, i, &index); - numaGetIValue(nay, index, &yval); - pix1 = pixaGetPix(pixa, i, L_CLONE); - pix2 = pixConvertTo32(pix1); - pix3 = pixScaleToSize(pix2, width, 0); - pix4 = pixAddBorderGeneral(pix3, border, border, border, border, 0); - textstr = pixGetText(pix1); - if (textstr && strlen(textstr) > 0) { - snprintf(buf, sizeof(buf), "%s", textstr); - pix5 = pixAddTextlines(pix4, bmf, textstr, textcolor, L_ADD_BELOW); - } else { - pix5 = pixClone(pix4); - } - pixaAddPix(pixad, pix5, L_INSERT); - x = spacing + border + index * (2 * border + width + spacing); - y = yval; - pixGetDimensions(pix5, &w, &h, NULL); - yval += h + spacing; - numaSetValue(nay, index, yval); - box = boxCreate(x, y, w, h); - pixaAddBox(pixad, box, L_INSERT); - pixDestroy(&pix1); - pixDestroy(&pix2); - pixDestroy(&pix3); - pixDestroy(&pix4); - } - numaDestroy(&nay); - bmfDestroy(&bmf); - - pixd = pixaDisplay(pixad, 0, 0); - pixaDestroy(&pixad); - return pixd; -} - - - -/*---------------------------------------------------------------------* - * Pixaa Display * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaaDisplay() - * - * \param[in] paa - * \param[in] w, h if set to 0, the size is determined from the - * bounding box of the components in pixa - * \return pix, or NULL on error - * - *- * Notes: - * (1) Each pix of the paa is displayed at the location given by - * its box, translated by the box of the containing pixa - * if it exists. - *- */ -PIX * -pixaaDisplay(PIXAA *paa, - l_int32 w, - l_int32 h) -{ -l_int32 i, j, n, nbox, na, d, wmax, hmax, x, y, xb, yb, wb, hb; -BOXA *boxa1; /* top-level boxa */ -BOXA *boxa; -PIX *pix1, *pixd; -PIXA *pixa; - - PROCNAME("pixaaDisplay"); - - if (!paa) - return (PIX *)ERROR_PTR("paa not defined", procName, NULL); - - n = pixaaGetCount(paa, NULL); - if (n == 0) - return (PIX *)ERROR_PTR("no components", procName, NULL); - - /* If w and h not input, determine the minimum size required - * to contain the origin and all c.c. */ - boxa1 = pixaaGetBoxa(paa, L_CLONE); - nbox = boxaGetCount(boxa1); - if (w == 0 || h == 0) { - if (nbox == n) { - boxaGetExtent(boxa1, &w, &h, NULL); - } else { /* have to use the lower-level boxa for each pixa */ - wmax = hmax = 0; - for (i = 0; i < n; i++) { - pixa = pixaaGetPixa(paa, i, L_CLONE); - boxa = pixaGetBoxa(pixa, L_CLONE); - boxaGetExtent(boxa, &w, &h, NULL); - wmax = L_MAX(wmax, w); - hmax = L_MAX(hmax, h); - pixaDestroy(&pixa); - boxaDestroy(&boxa); - } - w = wmax; - h = hmax; - } - } - - /* Get depth from first pix */ - pixa = pixaaGetPixa(paa, 0, L_CLONE); - pix1 = pixaGetPix(pixa, 0, L_CLONE); - d = pixGetDepth(pix1); - pixaDestroy(&pixa); - pixDestroy(&pix1); - - if ((pixd = pixCreate(w, h, d)) == NULL) { - boxaDestroy(&boxa1); - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - - x = y = 0; - for (i = 0; i < n; i++) { - pixa = pixaaGetPixa(paa, i, L_CLONE); - if (nbox == n) - boxaGetBoxGeometry(boxa1, i, &x, &y, NULL, NULL); - na = pixaGetCount(pixa); - for (j = 0; j < na; j++) { - pixaGetBoxGeometry(pixa, j, &xb, &yb, &wb, &hb); - pix1 = pixaGetPix(pixa, j, L_CLONE); - pixRasterop(pixd, x + xb, y + yb, wb, hb, PIX_PAINT, pix1, 0, 0); - pixDestroy(&pix1); - } - pixaDestroy(&pixa); - } - boxaDestroy(&boxa1); - - return pixd; -} - - -/*! - * \brief pixaaDisplayByPixa() - * - * \param[in] paa - * \param[in] maxnx maximum number of columns for rendering each pixa - * \param[in] scalefactor applied to every pix; use 1.0 for no scaling - * \param[in] hspacing between images on a row (in the pixa) - * \param[in] vspacing between tiles rows, each corresponding to a pixa - * \param[in] border width of black border added to each image; - * use 0 for no border - * \return pixd of images in %paa, tiled by pixa in row-major order - * - *- * Notes: - * (1) This renders a pixaa into a single image. The pix from each pixa - * are rendered on a row. If the number of pix in the pixa is - * larger than %maxnx, the pix will be rendered into more than 1 row. - * To insure that each pixa is rendered into one row, use %maxnx - * at least as large as the max number of pix in the pixa. - * (2) Each row is tiled such that the top of each pix is aligned and - * each pix is separated by %hspacing from the next one. - * A black border can be added to each pix. - * (3) The resulting pix from each row are then rendered vertically, - * separated by %vspacing from each other. - * (4) The output depth is determined by the largest depth of all - * the pix in %paa. Colormaps are removed. - *- */ -PIX * -pixaaDisplayByPixa(PIXAA *paa, - l_int32 maxnx, - l_float32 scalefactor, - l_int32 hspacing, - l_int32 vspacing, - l_int32 border) -{ -l_int32 i, n, vs; -PIX *pix1, *pix2; -PIXA *pixa1, *pixa2; - - PROCNAME("pixaaDisplayByPixa"); - - if (!paa) - return (PIX *)ERROR_PTR("paa not defined", procName, NULL); - if (scalefactor <= 0.0) scalefactor = 1.0; - if (hspacing < 0) hspacing = 0; - if (vspacing < 0) vspacing = 0; - if (border < 0) border = 0; - - if ((n = pixaaGetCount(paa, NULL)) == 0) - return (PIX *)ERROR_PTR("no components", procName, NULL); - - /* Vertical spacing of amount %hspacing is also added at this step */ - pixa2 = pixaCreate(0); - for (i = 0; i < n; i++) { - pixa1 = pixaaGetPixa(paa, i, L_CLONE); - pix1 = pixaDisplayTiledInColumns(pixa1, maxnx, scalefactor, - hspacing, border); - pixaAddPix(pixa2, pix1, L_INSERT); - pixaDestroy(&pixa1); - } - - vs = vspacing - 2 * hspacing; - pix2 = pixaDisplayTiledInColumns(pixa2, 1, scalefactor, vs, 0); - pixaDestroy(&pixa2); - return pix2; -} - - -/*! - * \brief pixaaDisplayTiledAndScaled() - * - * \param[in] paa - * \param[in] outdepth output depth: 1, 8 or 32 bpp - * \param[in] tilewidth each pix is scaled to this width - * \param[in] ncols number of tiles in each row - * \param[in] background 0 for white, 1 for black; this is the color - * of the spacing between the images - * \param[in] spacing between images, and on outside - * \param[in] border width of additional black border on each image; - * use 0 for no border - * \return pixa of tiled images, one image for each pixa in - * the paa, or NULL on error - * - *- * Notes: - * (1) For each pixa, this generates from all the pix a - * tiled/scaled output pix, and puts it in the output pixa. - * (2) See comments in pixaDisplayTiledAndScaled(). - *- */ -PIXA * -pixaaDisplayTiledAndScaled(PIXAA *paa, - l_int32 outdepth, - l_int32 tilewidth, - l_int32 ncols, - l_int32 background, - l_int32 spacing, - l_int32 border) -{ -l_int32 i, n; -PIX *pix; -PIXA *pixa, *pixad; - - PROCNAME("pixaaDisplayTiledAndScaled"); - - if (!paa) - return (PIXA *)ERROR_PTR("paa not defined", procName, NULL); - if (outdepth != 1 && outdepth != 8 && outdepth != 32) - return (PIXA *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL); - if (ncols <= 0) - return (PIXA *)ERROR_PTR("ncols must be > 0", procName, NULL); - if (border < 0 || border > tilewidth / 5) - border = 0; - - if ((n = pixaaGetCount(paa, NULL)) == 0) - return (PIXA *)ERROR_PTR("no components", procName, NULL); - - pixad = pixaCreate(n); - for (i = 0; i < n; i++) { - pixa = pixaaGetPixa(paa, i, L_CLONE); - pix = pixaDisplayTiledAndScaled(pixa, outdepth, tilewidth, ncols, - background, spacing, border); - pixaAddPix(pixad, pix, L_INSERT); - pixaDestroy(&pixa); - } - - return pixad; -} - - -/*---------------------------------------------------------------------* - * Conversion of all pix to specified type (e.g., depth) * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaConvertTo1() - * - * \param[in] pixas - * \param[in] thresh threshold for final binarization from 8 bpp gray - * \return pixad, or NULL on error - */ -PIXA * -pixaConvertTo1(PIXA *pixas, - l_int32 thresh) -{ -l_int32 i, n; -BOXA *boxa; -PIX *pix1, *pix2; -PIXA *pixad; - - PROCNAME("pixaConvertTo1"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - - n = pixaGetCount(pixas); - pixad = pixaCreate(n); - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixas, i, L_CLONE); - pix2 = pixConvertTo1(pix1, thresh); - pixaAddPix(pixad, pix2, L_INSERT); - pixDestroy(&pix1); - } - - boxa = pixaGetBoxa(pixas, L_COPY); - pixaSetBoxa(pixad, boxa, L_INSERT); - return pixad; -} - - -/*! - * \brief pixaConvertTo8() - * - * \param[in] pixas - * \param[in] cmapflag 1 to give pixd a colormap; 0 otherwise - * \return pixad each pix is 8 bpp, or NULL on error - * - *- * Notes: - * (1) See notes for pixConvertTo8(), applied to each pix in pixas. - *- */ -PIXA * -pixaConvertTo8(PIXA *pixas, - l_int32 cmapflag) -{ -l_int32 i, n; -BOXA *boxa; -PIX *pix1, *pix2; -PIXA *pixad; - - PROCNAME("pixaConvertTo8"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - - n = pixaGetCount(pixas); - pixad = pixaCreate(n); - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixas, i, L_CLONE); - pix2 = pixConvertTo8(pix1, cmapflag); - pixaAddPix(pixad, pix2, L_INSERT); - pixDestroy(&pix1); - } - - boxa = pixaGetBoxa(pixas, L_COPY); - pixaSetBoxa(pixad, boxa, L_INSERT); - return pixad; -} - - -/*! - * \brief pixaConvertTo8Colormap() - * - * \param[in] pixas - * \param[in] dither 1 to dither if necessary; 0 otherwise - * \return pixad each pix is 8 bpp, or NULL on error - * - *- * Notes: - * (1) See notes for pixConvertTo8Colormap(), applied to each pix in pixas. - *- */ -PIXA * -pixaConvertTo8Colormap(PIXA *pixas, - l_int32 dither) -{ -l_int32 i, n; -BOXA *boxa; -PIX *pix1, *pix2; -PIXA *pixad; - - PROCNAME("pixaConvertTo8Colormap"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - - n = pixaGetCount(pixas); - pixad = pixaCreate(n); - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixas, i, L_CLONE); - pix2 = pixConvertTo8Colormap(pix1, dither); - pixaAddPix(pixad, pix2, L_INSERT); - pixDestroy(&pix1); - } - - boxa = pixaGetBoxa(pixas, L_COPY); - pixaSetBoxa(pixad, boxa, L_INSERT); - return pixad; -} - - -/*! - * \brief pixaConvertTo32() - * - * \param[in] pixas - * \return pixad 32 bpp rgb, or NULL on error - * - *- * Notes: - * (1) See notes for pixConvertTo32(), applied to each pix in pixas. - * (2) This can be used to allow 1 bpp pix in a pixa to be displayed - * with color. - *- */ -PIXA * -pixaConvertTo32(PIXA *pixas) -{ -l_int32 i, n; -BOXA *boxa; -PIX *pix1, *pix2; -PIXA *pixad; - - PROCNAME("pixaConvertTo32"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - - n = pixaGetCount(pixas); - pixad = pixaCreate(n); - for (i = 0; i < n; i++) { - pix1 = pixaGetPix(pixas, i, L_CLONE); - pix2 = pixConvertTo32(pix1); - pixaAddPix(pixad, pix2, L_INSERT); - pixDestroy(&pix1); - } - - boxa = pixaGetBoxa(pixas, L_COPY); - pixaSetBoxa(pixad, boxa, L_INSERT); - return pixad; -} - - -/*---------------------------------------------------------------------* - * Pixa constrained selection * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaConstrainedSelect() - * - * \param[in] pixas - * \param[in] first first index to choose; >= 0 - * \param[in] last biggest possible index to reach; - * use -1 to go to the end; otherwise, last >= first - * \param[in] nmax maximum number of pix to select; > 0 - * \param[in] use_pairs 1 = select pairs of adjacent pix; - * 0 = select individual pix - * \param[in] copyflag L_COPY, L_CLONE - * \return pixad if OK, NULL on error - * - *- * Notes: - * (1) See notes in genConstrainedNumaInRange() for how selection - * is made. - * (2) This returns a selection of the pix in the input pixa. - * (3) Use copyflag == L_COPY if you don't want changes in the pix - * in the returned pixa to affect those in the input pixa. - *- */ -PIXA * -pixaConstrainedSelect(PIXA *pixas, - l_int32 first, - l_int32 last, - l_int32 nmax, - l_int32 use_pairs, - l_int32 copyflag) -{ -l_int32 i, n, nselect, index; -NUMA *na; -PIX *pix1; -PIXA *pixad; - - PROCNAME("pixaConstrainedSelect"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - n = pixaGetCount(pixas); - first = L_MAX(0, first); - last = (last < 0) ? n - 1 : L_MIN(n - 1, last); - if (last < first) - return (PIXA *)ERROR_PTR("last < first!", procName, NULL); - if (nmax < 1) - return (PIXA *)ERROR_PTR("nmax < 1!", procName, NULL); - - na = genConstrainedNumaInRange(first, last, nmax, use_pairs); - nselect = numaGetCount(na); - pixad = pixaCreate(nselect); - for (i = 0; i < nselect; i++) { - numaGetIValue(na, i, &index); - pix1 = pixaGetPix(pixas, index, copyflag); - pixaAddPix(pixad, pix1, L_INSERT); - } - numaDestroy(&na); - return pixad; -} - - -/*! - * \brief pixaSelectToPdf() - * - * \param[in] pixas - * \param[in] first first index to choose; >= 0 - * \param[in] last biggest possible index to reach; - * use -1 to go to the end; otherwise, last >= first - * \param[in] res override the resolution of each input image, in ppi; - * use 0 to respect the resolution embedded in the input - * \param[in] scalefactor scaling factor applied to each image; > 0.0 - * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, - * L_FLATE_ENCODE, or 0 for default - * \param[in] quality used for JPEG only; 0 for default (75) - * \param[in] color of numbers added to each image (e.g., 0xff000000) - * \param[in] fontsize to print number below each image. The valid set - * is {4,6,8,10,12,14,16,18,20}. Use 0 to disable. - * \param[in] fileout pdf file of all images - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This writes a pdf of the selected images from %pixas, one to - * a page. They are optionally scaled and annotated with the - * index printed to the left of the image. - * (2) If the input images are 1 bpp and you want the numbers to be - * in color, first promote each pix to 8 bpp with a colormap: - * pixa1 = pixaConvertTo8(pixas, 1); - * and then call this function with the specified color - *- */ -l_ok -pixaSelectToPdf(PIXA *pixas, - l_int32 first, - l_int32 last, - l_int32 res, - l_float32 scalefactor, - l_int32 type, - l_int32 quality, - l_uint32 color, - l_int32 fontsize, - const char *fileout) -{ -l_int32 n; -L_BMF *bmf; -NUMA *na; -PIXA *pixa1, *pixa2; - - PROCNAME("pixaSelectToPdf"); - - if (!pixas) - return ERROR_INT("pixas not defined", procName, 1); - if (type < 0 || type > L_FLATE_ENCODE) { - L_WARNING("invalid compression type; using default\n", procName); - type = 0; - } - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - - /* Select from given range */ - n = pixaGetCount(pixas); - first = L_MAX(0, first); - last = (last < 0) ? n - 1 : L_MIN(n - 1, last); - if (first > last) { - L_ERROR("first = %d > last = %d\n", procName, first, last); - return 1; - } - pixa1 = pixaSelectRange(pixas, first, last, L_CLONE); - - /* Optionally add index numbers */ - bmf = (fontsize <= 0) ? NULL : bmfCreate(NULL, fontsize); - if (bmf) { - na = numaMakeSequence(first, 1.0, last - first + 1); - pixa2 = pixaAddTextNumber(pixa1, bmf, na, color, L_ADD_LEFT); - numaDestroy(&na); - } else { - pixa2 = pixaCopy(pixa1, L_CLONE); - } - pixaDestroy(&pixa1); - bmfDestroy(&bmf); - - pixaConvertToPdf(pixa2, res, scalefactor, type, quality, NULL, fileout); - pixaDestroy(&pixa2); - return 0; -} - - -/*---------------------------------------------------------------------* - * Generate pixa from tiled images * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaMakeFromTiledPixa() - * - * \param[in] pixas of mosaiced templates, one for each digit - * \param[in] w width of samples (use 0 for default = 20) - * \param[in] h height of samples (use 0 for default = 30) - * \param[in] nsamp number of requested samples (use 0 for default = 100) - * \return pixa of individual, scaled templates, or NULL on error - * - *- * Notes: - * (1) This converts from a compressed representation of 1 bpp digit - * templates to a pixa where each pix has a single labeled template. - * (2) The mosaics hold 100 templates each, and the number of templates - * %nsamp selected for each digit can be between 1 and 100. - * (3) Each mosaic has the number of images written in the text field, - * and the i-th pix contains samples of the i-th digit. That value - * is written into the text field of each template in the output. - *- */ -PIXA * -pixaMakeFromTiledPixa(PIXA *pixas, - l_int32 w, - l_int32 h, - l_int32 nsamp) -{ -char buf[8]; -l_int32 ntiles, i; -PIX *pix1; -PIXA *pixad, *pixa1; - - PROCNAME("pixaMakeFromTiledPixa"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (nsamp > 1000) - return (PIXA *)ERROR_PTR("nsamp too large; typ. 100", procName, NULL); - - if (w <= 0) w = 20; - if (h <= 0) h = 30; - if (nsamp <= 0) nsamp = 100; - - /* pixas has 10 pix of mosaic'd digits. Each of these images - * must be extracted into a pixa of templates, where each template - * is labeled with the digit value, and then selectively - * concatenated into an output pixa. */ - pixad = pixaCreate(10 * nsamp); - for (i = 0; i < 10; i++) { - pix1 = pixaGetPix(pixas, i, L_CLONE); - pixGetTileCount(pix1, &ntiles); - if (nsamp > ntiles) - L_WARNING("requested %d; only %d tiles\n", procName, nsamp, ntiles); - pixa1 = pixaMakeFromTiledPix(pix1, w, h, 0, nsamp, NULL); - snprintf(buf, sizeof(buf), "%d", i); - pixaSetText(pixa1, buf, NULL); - pixaJoin(pixad, pixa1, 0, -1); - pixaDestroy(&pixa1); - pixDestroy(&pix1); - } - return pixad; -} - - -/*! - * \brief pixaMakeFromTiledPix() - * - * \param[in] pixs any depth; colormap OK - * \param[in] w width of each tile - * \param[in] h height of each tile - * \param[in] start first tile to use - * \param[in] num number of tiles; use 0 to go to the end - * \param[in] boxa [optional] location of rectangular regions - * to be extracted - * \return pixa if OK, NULL on error - * - *- * Notes: - * (1) Operations that generate a pix by tiling from a pixa, and - * the inverse that generate a pixa from tiles of a pix, - * are useful. One such pair is pixaDisplayUnsplit() and - * pixaSplitPix(). This function is a very simple one that - * generates a pixa from tiles of a pix. There are two cases: - * - the tiles can all be the same size (the inverse of - * pixaDisplayOnLattice(), or - * - the tiles can differ in size, where there is an - * associated boxa (the inverse of pixaCreateFromBoxa(). - * (2) If all tiles are the same size, %w by %h, use %boxa = NULL. - * If the tiles differ in size, use %boxa to extract the - * individual images (%w and %h are then ignored). - * (3) If the pix was made by pixaDisplayOnLattice(), the number - * of tiled images is written into the text field, in the format - * n =- */ -PIXA * -pixaMakeFromTiledPix(PIX *pixs, - l_int32 w, - l_int32 h, - l_int32 start, - l_int32 num, - BOXA *boxa) -{ -l_int32 i, j, k, ws, hs, d, nx, ny, n, n_isvalid, ntiles, nmax; -PIX *pix1; -PIXA *pixa1; -PIXCMAP *cmap; - - PROCNAME("pixaMakeFromTiledPix"); - - if (!pixs) - return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); - if (!boxa && (w <= 0 || h <= 0)) - return (PIXA *)ERROR_PTR("w and h must be > 0", procName, NULL); - - if (boxa) /* general case */ - return pixaCreateFromBoxa(pixs, boxa, start, num, NULL); - - /* All tiles are the same size */ - pixGetDimensions(pixs, &ws, &hs, &d); - nx = ws / w; - ny = hs / h; - if (nx < 1 || ny < 1) - return (PIXA *)ERROR_PTR("invalid dimensions", procName, NULL); - if (nx * w != ws || ny * h != hs) - L_WARNING("some tiles will be clipped\n", procName); - - /* Check the text field of the pix. It may tell how many - * tiles hold valid data. If a valid value is not found, - * assume all (nx * ny) tiles are valid. */ - pixGetTileCount(pixs, &n); - n_isvalid = (n <= nx * ny && n > nx * (ny - 1)) ? TRUE : FALSE; - ntiles = (n_isvalid) ? n : nx * ny; - nmax = ntiles - start; /* max available from start */ - num = (num == 0) ? nmax : L_MIN(num, nmax); - - /* Extract the tiles */ - if ((pixa1 = pixaCreate(num)) == NULL) { - return (PIXA *)ERROR_PTR("pixa1 not made", procName, NULL); - } - cmap = pixGetColormap(pixs); - for (i = 0, k = 0; i < ny; i++) { - for (j = 0; j < nx; j++, k++) { - if (k < start) continue; - if (k >= start + num) break; - pix1 = pixCreate(w, h, d); - if (cmap) pixSetColormap(pix1, pixcmapCopy(cmap)); - pixRasterop(pix1, 0, 0, w, h, PIX_SRC, pixs, j * w, i * h); - pixaAddPix(pixa1, pix1, L_INSERT); - } - } - return pixa1; -} - - -/*! - * \brief pixGetTileCount() - * - * \param[in] pix - * \param[out] *pn number embedded in pix text field - * \return 0 if OK, 1 on error - * - *. - * (4) Typical usage: a set of character templates all scaled to - * the same size can be stored on a lattice of that size in - * a pix, and this function can regenerate the pixa. If the - * templates differ in size, a boxa generated when the tiled - * pix was made can be used to indicate the location of - * the templates. - * - * Notes: - * (1) If the pix was made by pixaDisplayOnLattice(), the number - * of tiled images is written into the text field, in the format - * n =- */ -l_ok -pixGetTileCount(PIX *pix, - l_int32 *pn) -{ -char *text; -l_int32 n; - - PROCNAME("pixGetTileCount"); - - if (!pn) - return ERROR_INT("&n not defined", procName, 1); - *pn = 0; - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - text = pixGetText(pix); - if (text && strlen(text) > 4) { - if (sscanf(text, "n = %d", &n) == 1) - *pn = n; - } - return 0; -} - - -/*---------------------------------------------------------------------* - * Pixa display into multiple tiles * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaDisplayMultiTiled() - * - * \param[in] pixas - * \param[in] nx, ny in [1, ... 50], tiling factors in each direction - * \param[in] maxw, maxh max sizes to keep - * \param[in] scalefactor scale each image by this - * \param[in] spacing between images, and on outside - * \param[in] border width of additional black border on each image; - * use 0 for no border - * \return pixad if OK, NULL on error - * - *. - * (2) This returns 0 if the data is not in the text field, or on error. - * - * Notes: - * (1) Each set of %nx * %ny images is optionally scaled and saved - * into a new pix, and then aggregated. - * (2) Set %maxw = %maxh = 0 if you want to include all pix from %pixs. - * (3) This is useful for generating a pdf from the output pixa, where - * each page is a tile of (%nx * %ny) images from the input pixa. - *- */ -PIXA * -pixaDisplayMultiTiled(PIXA *pixas, - l_int32 nx, - l_int32 ny, - l_int32 maxw, - l_int32 maxh, - l_float32 scalefactor, - l_int32 spacing, - l_int32 border) -{ -l_int32 n, i, j, ntile, nout, index; -PIX *pix1, *pix2; -PIXA *pixa1, *pixa2, *pixad; - - PROCNAME("pixaDisplayMultiTiled"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (nx < 1 || ny < 1 || nx > 50 || ny > 50) - return (PIXA *)ERROR_PTR("invalid tiling factor(s)", procName, NULL); - if ((n = pixaGetCount(pixas)) == 0) - return (PIXA *)ERROR_PTR("pixas is empty", procName, NULL); - - /* Filter out large ones if requested */ - if (maxw == 0 && maxh == 0) { - pixa1 = pixaCopy(pixas, L_CLONE); - } else { - maxw = (maxw == 0) ? 1000000 : maxw; - maxh = (maxh == 0) ? 1000000 : maxh; - pixa1 = pixaSelectBySize(pixas, maxw, maxh, L_SELECT_IF_BOTH, - L_SELECT_IF_LTE, NULL); - n = pixaGetCount(pixa1); - } - - ntile = nx * ny; - nout = L_MAX(1, (n + ntile - 1) / ntile); - pixad = pixaCreate(nout); - for (i = 0, index = 0; i < nout; i++) { /* over tiles */ - pixa2 = pixaCreate(ntile); - for (j = 0; j < ntile && index < n; j++, index++) { - pix1 = pixaGetPix(pixa1, index, L_COPY); - pixaAddPix(pixa2, pix1, L_INSERT); - } - pix2 = pixaDisplayTiledInColumns(pixa2, nx, scalefactor, spacing, - border); - pixaAddPix(pixad, pix2, L_INSERT); - pixaDestroy(&pixa2); - } - pixaDestroy(&pixa1); - - return pixad; -} - - -/*---------------------------------------------------------------------* - * Split pixa into files * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaSplitIntoFiles() - * - * \param[in] pixas - * \param[in] nsplit split pixas into this number of pixa; >= 2 - * \param[in] scale scalefactor applied to each pix - * \param[in] outwidth the maxwidth parameter of tiled images - * for write_pix - * \param[in] write_pixa 1 to write the split pixa as separate files - * \param[in] write_pix 1 to write tiled images of the split pixa - * \param[in] write_pdf 1 to write pdfs of the split pixa - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) For each requested output, %nsplit files are written into - * directory /tmp/lept/split/. - * (2) This is useful when a pixa is so large that the images - * are not conveniently displayed as a single tiled image at - * full resolution. - *- */ -l_ok -pixaSplitIntoFiles(PIXA *pixas, - l_int32 nsplit, - l_float32 scale, - l_int32 outwidth, - l_int32 write_pixa, - l_int32 write_pix, - l_int32 write_pdf) -{ -char buf[64]; -l_int32 i, j, index, n, nt; -PIX *pix1, *pix2; -PIXA *pixa1; - - PROCNAME("pixaSplitIntoFiles"); - - if (!pixas) - return ERROR_INT("pixas not defined", procName, 1); - if (nsplit <= 1) - return ERROR_INT("nsplit must be >= 2", procName, 1); - if ((nt = pixaGetCount(pixas)) == 0) - return ERROR_INT("pixas is empty", procName, 1); - if (!write_pixa && !write_pix && !write_pdf) - return ERROR_INT("no output is requested", procName, 1); - - lept_mkdir("lept/split"); - n = (nt + nsplit - 1) / nsplit; - lept_stderr("nt = %d, n = %d, nsplit = %d\n", nt, n, nsplit); - for (i = 0, index = 0; i < nsplit; i++) { - pixa1 = pixaCreate(n); - for (j = 0; j < n && index < nt; j++, index++) { - pix1 = pixaGetPix(pixas, index, L_CLONE); - pix2 = pixScale(pix1, scale, scale); - pixaAddPix(pixa1, pix2, L_INSERT); - pixDestroy(&pix1); - } - if (write_pixa) { - snprintf(buf, sizeof(buf), "/tmp/lept/split/split%d.pa", i + 1); - pixaWriteDebug(buf, pixa1); - } - if (write_pix) { - snprintf(buf, sizeof(buf), "/tmp/lept/split/split%d.tif", i + 1); - pix1 = pixaDisplayTiledInRows(pixa1, 1, outwidth, 1.0, 0, 20, 2); - pixWriteDebug(buf, pix1, IFF_TIFF_G4); - pixDestroy(&pix1); - } - if (write_pdf) { - snprintf(buf, sizeof(buf), "/tmp/lept/split/split%d.pdf", i + 1); - pixaConvertToPdf(pixa1, 0, 1.0, L_G4_ENCODE, 0, buf, buf); - } - pixaDestroy(&pixa1); - } - - return 0; -} - - -/*---------------------------------------------------------------------* - * Tile N-Up * - *---------------------------------------------------------------------*/ -/*! - * \brief convertToNUpFiles() - * - * \param[in] dir full path to directory of images - * \param[in] substr [optional] can be null - * \param[in] nx, ny in [1, ... 50], tiling factors in each direction - * \param[in] tw target width, in pixels; must be >= 20 - * \param[in] spacing between images, and on outside - * \param[in] border width of additional black border on each image; - * use 0 for no border - * \param[in] fontsize to print tail of filename with image. Valid set is - * {4,6,8,10,12,14,16,18,20}. Use 0 to disable. - * \param[in] outdir subdirectory of /tmp to put N-up tiled images - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Each set of %nx * %ny images is scaled and tiled into a single - * image, that is written out to %outdir. - * (2) All images in each %nx * %ny set are scaled to the same - * width, %tw. This is typically used when all images are - * roughly the same size. - * (3) This is useful for generating a pdf from the set of input - * files, where each page is a tile of (%nx * %ny) input images. - * Typical values for %nx and %ny are in the range [2 ... 5]. - * (4) If %fontsize != 0, each image has the tail of its filename - * rendered below it. - *- */ -l_ok -convertToNUpFiles(const char *dir, - const char *substr, - l_int32 nx, - l_int32 ny, - l_int32 tw, - l_int32 spacing, - l_int32 border, - l_int32 fontsize, - const char *outdir) -{ -l_int32 d, format; -char rootpath[256]; -PIXA *pixa; - - PROCNAME("convertToNUpFiles"); - - if (!dir) - return ERROR_INT("dir not defined", procName, 1); - if (nx < 1 || ny < 1 || nx > 50 || ny > 50) - return ERROR_INT("invalid tiling N-factor", procName, 1); - if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2) - return ERROR_INT("invalid fontsize", procName, 1); - if (!outdir) - return ERROR_INT("outdir not defined", procName, 1); - - pixa = convertToNUpPixa(dir, substr, nx, ny, tw, spacing, border, - fontsize); - if (!pixa) - return ERROR_INT("pixa not made", procName, 1); - - lept_rmdir(outdir); - lept_mkdir(outdir); - pixaGetRenderingDepth(pixa, &d); - format = (d == 1) ? IFF_TIFF_G4 : IFF_JFIF_JPEG; - makeTempDirname(rootpath, 256, outdir); - modifyTrailingSlash(rootpath, 256, L_ADD_TRAIL_SLASH); - pixaWriteFiles(rootpath, pixa, format); - pixaDestroy(&pixa); - return 0; -} - - -/*! - * \brief convertToNUpPixa() - * - * \param[in] dir full path to directory of images - * \param[in] substr [optional] can be null - * \param[in] nx, ny in [1, ... 50], tiling factors in each direction - * \param[in] tw target width, in pixels; must be >= 20 - * \param[in] spacing between images, and on outside - * \param[in] border width of additional black border on each image; - * use 0 for no border - * \param[in] fontsize to print tail of filename with image. Valid set is - * {4,6,8,10,12,14,16,18,20}. Use 0 to disable. - * \return pixad, or NULL on error - * - *- * Notes: - * (1) See notes for convertToNUpFiles() - *- */ -PIXA * -convertToNUpPixa(const char *dir, - const char *substr, - l_int32 nx, - l_int32 ny, - l_int32 tw, - l_int32 spacing, - l_int32 border, - l_int32 fontsize) -{ -l_int32 i, n; -char *fname, *tail; -PIXA *pixa1, *pixa2; -SARRAY *sa1, *sa2; - - PROCNAME("convertToNUpPixa"); - - if (!dir) - return (PIXA *)ERROR_PTR("dir not defined", procName, NULL); - if (nx < 1 || ny < 1 || nx > 50 || ny > 50) - return (PIXA *)ERROR_PTR("invalid tiling N-factor", procName, NULL); - if (tw < 20) - return (PIXA *)ERROR_PTR("tw must be >= 20", procName, NULL); - if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2) - return (PIXA *)ERROR_PTR("invalid fontsize", procName, NULL); - - sa1 = getSortedPathnamesInDirectory(dir, substr, 0, 0); - pixa1 = pixaReadFilesSA(sa1); - n = sarrayGetCount(sa1); - sa2 = sarrayCreate(n); - for (i = 0; i < n; i++) { - fname = sarrayGetString(sa1, i, L_NOCOPY); - splitPathAtDirectory(fname, NULL, &tail); - sarrayAddString(sa2, tail, L_INSERT); - } - sarrayDestroy(&sa1); - pixa2 = pixaConvertToNUpPixa(pixa1, sa2, nx, ny, tw, spacing, - border, fontsize); - pixaDestroy(&pixa1); - sarrayDestroy(&sa2); - return pixa2; -} - - -/*! - * \brief pixaConvertToNUpPixa() - * - * \param[in] pixas - * \param[in] sa [optional] array of strings associated with each pix - * \param[in] nx, ny in [1, ... 50], tiling factors in each direction - * \param[in] tw target width, in pixels; must be >= 20 - * \param[in] spacing between images, and on outside - * \param[in] border width of additional black border on each image; - * use 0 for no border - * \param[in] fontsize to print string with each image. Valid set is - * {4,6,8,10,12,14,16,18,20}. Use 0 to disable. - * \return pixad, or NULL on error - * - *- * Notes: - * (1) This takes an input pixa and an optional array of strings, and - * generates a pixa of NUp tiles from the input, labeled with - * the strings if they exist and %fontsize != 0. - * (2) See notes for convertToNUpFiles() - *- */ -PIXA * -pixaConvertToNUpPixa(PIXA *pixas, - SARRAY *sa, - l_int32 nx, - l_int32 ny, - l_int32 tw, - l_int32 spacing, - l_int32 border, - l_int32 fontsize) -{ -l_int32 i, j, k, nt, n2, nout, d; -char *str; -L_BMF *bmf; -PIX *pix1, *pix2, *pix3, *pix4; -PIXA *pixa1, *pixad; - - PROCNAME("pixaConvertToNUpPixa"); - - if (!pixas) - return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL); - if (nx < 1 || ny < 1 || nx > 50 || ny > 50) - return (PIXA *)ERROR_PTR("invalid tiling N-factor", procName, NULL); - if (tw < 20) - return (PIXA *)ERROR_PTR("tw must be >= 20", procName, NULL); - if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2) - return (PIXA *)ERROR_PTR("invalid fontsize", procName, NULL); - - nt = pixaGetCount(pixas); - if (sa && (sarrayGetCount(sa) != nt)) { - L_WARNING("pixa size %d not equal to sarray size %d\n", procName, - nt, sarrayGetCount(sa)); - } - - n2 = nx * ny; - nout = (nt + n2 - 1) / n2; - pixad = pixaCreate(nout); - bmf = (fontsize == 0) ? NULL : bmfCreate(NULL, fontsize); - for (i = 0, j = 0; i < nout; i++) { - pixa1 = pixaCreate(n2); - for (k = 0; k < n2 && j < nt; j++, k++) { - pix1 = pixaGetPix(pixas, j, L_CLONE); - pix2 = pixScaleToSize(pix1, tw, 0); /* all images have width tw */ - if (bmf && sa) { - str = sarrayGetString(sa, j, L_NOCOPY); - pix3 = pixAddTextlines(pix2, bmf, str, 0xff000000, - L_ADD_BELOW); - } else { - pix3 = pixClone(pix2); - } - pixaAddPix(pixa1, pix3, L_INSERT); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - if (pixaGetCount(pixa1) == 0) { /* probably won't happen */ - pixaDestroy(&pixa1); - continue; - } - - /* Add 2 * border to image width to prevent scaling */ - pixaGetRenderingDepth(pixa1, &d); - pix4 = pixaDisplayTiledAndScaled(pixa1, d, tw + 2 * border, nx, 0, - spacing, border); - pixaAddPix(pixad, pix4, L_INSERT); - pixaDestroy(&pixa1); - } - - bmfDestroy(&bmf); - return pixad; -} - - -/*---------------------------------------------------------------------* - * Render two pixa side-by-side for comparison * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaCompareInPdf() - * - * \param[in] pixa1 - * \param[in] pixa2 - * \param[in] nx, ny in [1, ... 20], tiling factors in each direction - * \param[in] tw target width, in pixels; must be >= 20 - * \param[in] spacing between images, and on outside - * \param[in] border width of additional black border on each image - * and on each pair; use 0 for no border - * \param[in] fontsize to print index of each pair of images. Valid set - * is {4,6,8,10,12,14,16,18,20}. Use 0 to disable. - * \param[in] fileout output pdf file - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This takes two pixa and renders them interleaved, side-by-side - * in a pdf. A warning is issued if the input pixa arrays - * have different lengths. - * (2) %nx and %ny specify how many side-by-side pairs are displayed - * on each pdf page. For example, if %nx = 1 and %ny = 2, then - * two pairs are shown, one above the other, on each page. - * (3) The input pix are scaled to a target width of %tw, and - * then paired with optional %spacing between and optional - * black border of width %border. - * (4) After a pixa is generated of these tiled images, it is - * written to %fileout as a pdf. - * (5) Typical numbers for the input parameters are: - * %nx = small integer (1 - 4) - * %ny = 2 * %nx - * %tw = 200 - 500 pixels - * %spacing = 10 - * %border = 2 - * %fontsize = 10 - * (6) If %fontsize != 0, the index of the pix pair in their pixa - * is printed out below each pair. - *- */ -l_ok -pixaCompareInPdf(PIXA *pixa1, - PIXA *pixa2, - l_int32 nx, - l_int32 ny, - l_int32 tw, - l_int32 spacing, - l_int32 border, - l_int32 fontsize, - const char *fileout) -{ -l_int32 n1, n2, npairs; -PIXA *pixa3, *pixa4, *pixa5; -SARRAY *sa; - - PROCNAME("pixaCompareInPdf"); - - if (!pixa1 || !pixa2) - return ERROR_INT("pixa1 and pixa2 not both defined", procName, 1); - if (nx < 1 || ny < 1 || nx > 20 || ny > 20) - return ERROR_INT("invalid tiling factors", procName, 1); - if (tw < 20) - return ERROR_INT("invalid tw; tw must be >= 20", procName, 1); - if (fontsize < 0 || fontsize > 20 || fontsize & 1 || fontsize == 2) - return ERROR_INT("invalid fontsize", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - n1 = pixaGetCount(pixa1); - n2 = pixaGetCount(pixa2); - if (n1 == 0 || n2 == 0) - return ERROR_INT("at least one pixa is empty", procName, 1); - if (n1 != n2) - L_WARNING("sizes (%d, %d) differ; using the minimum in interleave\n", - procName, n1, n2); - - /* Interleave the input pixa */ - if ((pixa3 = pixaInterleave(pixa1, pixa2, L_CLONE)) == NULL) - return ERROR_INT("pixa3 not made", procName, 1); - - /* Scale the images if necessary and pair them up side/by/side */ - pixa4 = pixaConvertToNUpPixa(pixa3, NULL, 2, 1, tw, spacing, border, 0); - pixaDestroy(&pixa3); - - /* Label the pairs and mosaic into pages without further scaling */ - npairs = pixaGetCount(pixa4); - sa = (fontsize > 0) ? sarrayGenerateIntegers(npairs) : NULL; - pixa5 = pixaConvertToNUpPixa(pixa4, sa, nx, ny, - 2 * tw + 4 * border + spacing, - spacing, border, fontsize); - pixaDestroy(&pixa4); - sarrayDestroy(&sa); - - /* Output as pdf without scaling */ - pixaConvertToPdf(pixa5, 0, 1.0, 0, 0, NULL, fileout); - pixaDestroy(&pixa5); - return 0; -} - - diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixalloc.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixalloc.c deleted file mode 100644 index 72e86ce6..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixalloc.c +++ /dev/null @@ -1,536 +0,0 @@ -/*====================================================================* - - 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 pixalloc.c - *- * - * Custom memory storage with allocator and deallocator - * - * l_int32 pmsCreate() - * void pmsDestroy() - * void *pmsCustomAlloc() - * void pmsCustomDealloc() - * void *pmsGetAlloc() - * l_int32 pmsGetLevelForAlloc() - * l_int32 pmsGetLevelForDealloc() - * void pmsLogInfo() - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include "allheaders.h" - -/*-------------------------------------------------------------------------* - * Pix Memory Storage * - * * - * This is a simple utility for handling pix memory storage. It is * - * enabled by setting the PixMemoryManager allocators to the functions * - * that are defined here * - * pmsCustomAlloc() * - * pmsCustomDealloc() * - * Use pmsCreate() at the beginning to do the pre-allocation, and * - * pmsDestroy() at the end to clean it up. * - *-------------------------------------------------------------------------*/ -/* - * In the following, the "memory" refers to the image data - * field that is used within the pix. The memory store is a - * continuous block of memory, that is logically divided into - * smaller "chunks" starting with a set at a minimum size, and - * followed by sets of increasing size that are a power of 2 larger - * than the minimum size. You must specify the number of chunks - * of each size. - * - * A requested data chunk, if it exists, is borrowed from the memory - * storage, and returned after use. If the chunk is too small, or - * too large, or if all chunks in the appropriate size range are - * in use, the memory is allocated dynamically and freed after use. - * - * There are four parameters that determine the use of pre-allocated memory: - * - * minsize: any requested chunk smaller than this is allocated - * dynamically and destroyed after use. No preallocated - * memory is used. - * smallest: the size of the smallest pre-allocated memory chunk. - * nlevels: the number of different sizes of data chunks, each a - * power of 2 larger than 'smallest'. - * numalloc: a Numa of size 'nlevels' containing the number of data - * chunks for each size that are in the memory store. - * - * As an example, suppose: - * minsize = 0.5MB - * smallest = 1.0MB - * nlevels = 4 - * numalloc = {10, 5, 5, 5} - * Then the total amount of allocated memory (in MB) is - * 10 * 1 + 5 * 2 + 5 * 4 + 5 * 8 = 80 MB - * Any pix requiring less than 0.5 MB or more than 8 MB of memory will - * not come from the memory store. Instead, it will be dynamically - * allocated and freed after use. - * - * How is this implemented? - * - * At setup, the full data block size is computed and allocated. - * The addresses of the individual chunks are found, and the pointers - * are stored in a set of Ptra (generic pointer arrays), using one Ptra - * for each of the sizes of the chunks. When returning a chunk after - * use, it is necessary to determine from the address which size level - * (ptra) the chunk belongs to. This is done by comparing the address - * of the associated chunk. - * - * In the event that memory chunks need to be dynamically allocated, - * either (1) because they are too small or too large for the memory - * store or (2) because all the pix of that size (i.e., in the - * appropriate level) in the memory store are in use, the - * addresses generated will be outside the pre-allocated block. - * After use they won't be returned to a ptra; instead the deallocator - * will free them. - */ - -/*! Pix memory storage */ -struct PixMemoryStore -{ - struct L_Ptraa *paa; /*!< Holds ptrs to allocated memory */ - size_t minsize; /*!< Pix smaller than this (in bytes) */ - /*!< are allocated dynamically */ - size_t smallest; /*!< Smallest mem (in bytes) alloc'd */ - size_t largest; /*!< Larest mem (in bytes) alloc'd */ - size_t nbytes; /*!< Size of allocated block w/ all chunks */ - l_int32 nlevels; /*!< Num of power-of-2 sizes pre-alloc'd */ - size_t *sizes; /*!< Mem sizes at each power-of-2 level */ - l_int32 *allocarray; /*!< Number of mem alloc'd at each size */ - l_uint32 *baseptr; /*!< ptr to allocated array */ - l_uint32 *maxptr; /*!< ptr just beyond allocated memory */ - l_uint32 **firstptr; /*!< array of ptrs to first chunk in size */ - l_int32 *memused; /*!< log: total # of pix used (by level) */ - l_int32 *meminuse; /*!< log: # of pix in use (by level) */ - l_int32 *memmax; /*!< log: max # of pix in use (by level) */ - l_int32 *memempty; /*!< log: # of pix alloc'd because */ - /*!< the store was empty (by level) */ - char *logfile; /*!< log: set to null if no logging */ -}; -typedef struct PixMemoryStore L_PIX_MEM_STORE; - -static L_PIX_MEM_STORE *CustomPMS = NULL; - - -/*! - * \brief pmsCreate() - * - * \param[in] minsize of data chunk that can be supplied by pms - * \param[in] smallest bytes of the smallest pre-allocated data chunk. - * \param[in] numalloc array with the number of data chunks for each - * size that are in the memory store - * \param[in] logfile use for debugging; null otherwise - * \return 0 if OK, 1 on error - * - * - * Notes: - * (1) This computes the size of the block of memory required - * and allocates it. Each chunk starts on a 32-bit word boundary. - * The chunk sizes are in powers of 2, starting at %smallest, - * and the number of levels and chunks at each level is - * specified by %numalloc. - * (2) This is intended to manage the image data for a small number - * of relatively large pix. The system malloc is expected to - * handle very large numbers of small chunks efficiently. - * (3) Important: set the allocators and call this function - * before any pix have been allocated. Destroy all the pix - * in the normal way before calling pmsDestroy(). - * (4) The pms struct is stored in a static global, so this function - * is not thread-safe. When used, there must be only one thread - * per process. - *- */ -l_ok -pmsCreate(size_t minsize, - size_t smallest, - NUMA *numalloc, - const char *logfile) -{ -l_int32 nlevels, i, j, nbytes; -l_int32 *alloca; -l_float32 nchunks; -l_uint32 *baseptr, *data; -l_uint32 **firstptr; -size_t *sizes; -L_PIX_MEM_STORE *pms; -L_PTRA *pa; -L_PTRAA *paa; - - PROCNAME("createPMS"); - - if (!numalloc) - return ERROR_INT("numalloc not defined", procName, 1); - numaGetSum(numalloc, &nchunks); - if (nchunks > 1000.0) - L_WARNING("There are %.0f chunks\n", procName, nchunks); - - pms = (L_PIX_MEM_STORE *)LEPT_CALLOC(1, sizeof(L_PIX_MEM_STORE)); - CustomPMS = pms; - - /* Make sure that minsize and smallest are multiples of 32 bit words */ - if (minsize % 4 != 0) - minsize -= minsize % 4; - pms->minsize = minsize; - nlevels = numaGetCount(numalloc); - pms->nlevels = nlevels; - - if ((sizes = (size_t *)LEPT_CALLOC(nlevels, sizeof(size_t))) == NULL) - return ERROR_INT("sizes not made", procName, 1); - pms->sizes = sizes; - if (smallest % 4 != 0) - smallest += 4 - (smallest % 4); - pms->smallest = smallest; - for (i = 0; i < nlevels; i++) - sizes[i] = smallest * (1 << i); - pms->largest = sizes[nlevels - 1]; - - alloca = numaGetIArray(numalloc); - pms->allocarray = alloca; - if ((paa = ptraaCreate(nlevels)) == NULL) - return ERROR_INT("paa not made", procName, 1); - pms->paa = paa; - - for (i = 0, nbytes = 0; i < nlevels; i++) - nbytes += alloca[i] * sizes[i]; - pms->nbytes = nbytes; - - if ((baseptr = (l_uint32 *)LEPT_CALLOC(nbytes / 4, sizeof(l_uint32))) - == NULL) - return ERROR_INT("calloc fail for baseptr", procName, 1); - pms->baseptr = baseptr; - pms->maxptr = baseptr + nbytes / 4; /* just beyond the memory store */ - if ((firstptr = (l_uint32 **)LEPT_CALLOC(nlevels, sizeof(l_uint32 *))) - == NULL) - return ERROR_INT("calloc fail for firstptr", procName, 1); - pms->firstptr = firstptr; - - data = baseptr; - for (i = 0; i < nlevels; i++) { - if ((pa = ptraCreate(alloca[i])) == NULL) - return ERROR_INT("pa not made", procName, 1); - ptraaInsertPtra(paa, i, pa); - firstptr[i] = data; - for (j = 0; j < alloca[i]; j++) { - ptraAdd(pa, data); - data += sizes[i] / 4; - } - } - - if (logfile) { - pms->memused = (l_int32 *)LEPT_CALLOC(nlevels, sizeof(l_int32)); - pms->meminuse = (l_int32 *)LEPT_CALLOC(nlevels, sizeof(l_int32)); - pms->memmax = (l_int32 *)LEPT_CALLOC(nlevels, sizeof(l_int32)); - pms->memempty = (l_int32 *)LEPT_CALLOC(nlevels, sizeof(l_int32)); - pms->logfile = stringNew(logfile); - } - - return 0; -} - - -/*! - * \brief pmsDestroy() - * - *- * Notes: - * (1) Important: call this function at the end of the program, after - * the last pix has been destroyed. - *- */ -void -pmsDestroy(void) -{ -L_PIX_MEM_STORE *pms; - - if ((pms = CustomPMS) == NULL) - return; - - ptraaDestroy(&pms->paa, FALSE, FALSE); /* don't touch the ptrs */ - LEPT_FREE(pms->baseptr); /* free the memory */ - - if (pms->logfile) { - pmsLogInfo(); - LEPT_FREE(pms->logfile); - LEPT_FREE(pms->memused); - LEPT_FREE(pms->meminuse); - LEPT_FREE(pms->memmax); - LEPT_FREE(pms->memempty); - } - - LEPT_FREE(pms->sizes); - LEPT_FREE(pms->allocarray); - LEPT_FREE(pms->firstptr); - LEPT_FREE(pms); - CustomPMS = NULL; - return; -} - - -/*! - * \brief pmsCustomAlloc() - * - * \param[in] nbytes min number of bytes in the chunk to be retrieved - * \return data ptr to chunk - * - *- * Notes: - * (1) This attempts to find a suitable pre-allocated chunk. - * If not found, it dynamically allocates the chunk. - * (2) If logging is turned on, the allocations that are not taken - * from the memory store, and are at least as large as the - * minimum size the store can handle, are logged to file. - *- */ -void * -pmsCustomAlloc(size_t nbytes) -{ -l_int32 level; -void *data; -L_PIX_MEM_STORE *pms; -L_PTRA *pa; - - PROCNAME("pmsCustomAlloc"); - - if ((pms = CustomPMS) == NULL) - return (void *)ERROR_PTR("pms not defined", procName, NULL); - - pmsGetLevelForAlloc(nbytes, &level); - - if (level < 0) { /* size range invalid; must alloc */ - if ((data = pmsGetAlloc(nbytes)) == NULL) - return (void *)ERROR_PTR("data not made", procName, NULL); - } else { /* get from store */ - pa = ptraaGetPtra(pms->paa, level, L_HANDLE_ONLY); - data = ptraRemoveLast(pa); - if (data && pms->logfile) { - pms->memused[level]++; - pms->meminuse[level]++; - if (pms->meminuse[level] > pms->memmax[level]) - pms->memmax[level]++; - } - if (!data) { /* none left at this level */ - data = pmsGetAlloc(nbytes); - if (pms->logfile) - pms->memempty[level]++; - } - } - - return data; -} - - -/*! - * \brief pmsCustomDealloc() - * - * \param[in] data to be freed or returned to the storage - * \return void - */ -void -pmsCustomDealloc(void *data) -{ -l_int32 level; -L_PIX_MEM_STORE *pms; -L_PTRA *pa; - - PROCNAME("pmsCustomDealloc"); - - if ((pms = CustomPMS) == NULL) { - L_ERROR("pms not defined\n", procName); - return; - } - - if (pmsGetLevelForDealloc(data, &level) == 1) { - L_ERROR("level not found\n", procName); - return; - } - - if (level < 0) { /* no logging; just free the data */ - LEPT_FREE(data); - } else { /* return the data to the store */ - pa = ptraaGetPtra(pms->paa, level, L_HANDLE_ONLY); - ptraAdd(pa, data); - if (pms->logfile) - pms->meminuse[level]--; - } - - return; -} - - -/*! - * \brief pmsGetAlloc() - * - * \param[in] nbytes - * \return data - * - *- * Notes: - * (1) This is called when a request for pix data cannot be - * obtained from the preallocated memory store. After use it - * is freed like normal memory. - * (2) If logging is on, only write out allocs that are as large as - * the minimum size handled by the memory store. - * (3) size_t is %lu on 64 bit platforms and %u on 32 bit platforms. - * The C99 platform-independent format specifier for size_t is %zu. - * Windows since at least VC-2015 is conforming; we can now use %zu. - *- */ -void * -pmsGetAlloc(size_t nbytes) -{ -void *data; -FILE *fp; -L_PIX_MEM_STORE *pms; - - PROCNAME("pmsGetAlloc"); - - if ((pms = CustomPMS) == NULL) - return (void *)ERROR_PTR("pms not defined", procName, NULL); - - if ((data = (void *)LEPT_CALLOC(nbytes, sizeof(char))) == NULL) - return (void *)ERROR_PTR("data not made", procName, NULL); - if (pms->logfile && nbytes >= pms->smallest) { - fp = fopenWriteStream(pms->logfile, "a"); - fprintf(fp, "Alloc %zu bytes at %p\n", nbytes, data); - fclose(fp); - } - - return data; -} - - -/*! - * \brief pmsGetLevelForAlloc() - * - * \param[in] nbytes min number of bytes in the chunk to be retrieved - * \param[out] plevel -1 if either too small or too large - * \return 0 if OK, 1 on error - */ -l_ok -pmsGetLevelForAlloc(size_t nbytes, - l_int32 *plevel) -{ -l_int32 i; -l_float64 ratio; -L_PIX_MEM_STORE *pms; - - PROCNAME("pmsGetLevelForAlloc"); - - if (!plevel) - return ERROR_INT("&level not defined", procName, 1); - *plevel = -1; - if ((pms = CustomPMS) == NULL) - return ERROR_INT("pms not defined", procName, 1); - - if (nbytes < pms->minsize || nbytes > pms->largest) - return 0; /* -1 */ - - ratio = (l_float64)nbytes / (l_float64)(pms->smallest); - for (i = 0; i < pms->nlevels; i++) { - if (ratio <= 1.0) - break; - ratio /= 2.0; - } - *plevel = i; - - return 0; -} - - -/*! - * \brief pmsGetLevelForDealloc() - * - * \param[in] data ptr to memory chunk - * \param[out] plevel level in memory store; -1 if allocated - * outside the store - * \return 0 if OK, 1 on error - */ -l_ok -pmsGetLevelForDealloc(void *data, - l_int32 *plevel) -{ -l_int32 i; -l_uint32 *first; -L_PIX_MEM_STORE *pms; - - PROCNAME("pmsGetLevelForDealloc"); - - if (!plevel) - return ERROR_INT("&level not defined", procName, 1); - *plevel = -1; - if (!data) - return ERROR_INT("data not defined", procName, 1); - if ((pms = CustomPMS) == NULL) - return ERROR_INT("pms not defined", procName, 1); - - if (data < (void *)pms->baseptr || data >= (void *)pms->maxptr) - return 0; /* -1 */ - - for (i = 1; i < pms->nlevels; i++) { - first = pms->firstptr[i]; - if (data < (void *)first) - break; - } - *plevel = i - 1; - - return 0; -} - - -/*! - * \brief pmsLogInfo() - */ -void -pmsLogInfo(void) -{ -l_int32 i; -L_PIX_MEM_STORE *pms; - - if ((pms = CustomPMS) == NULL) - return; - - lept_stderr("Total number of pix used at each level\n"); - for (i = 0; i < pms->nlevels; i++) - lept_stderr(" Level %d (%zu bytes): %d\n", i, - pms->sizes[i], pms->memused[i]); - - lept_stderr("Max number of pix in use at any time in each level\n"); - for (i = 0; i < pms->nlevels; i++) - lept_stderr(" Level %d (%zu bytes): %d\n", i, - pms->sizes[i], pms->memmax[i]); - - lept_stderr("Number of pix alloc'd because none were available\n"); - for (i = 0; i < pms->nlevels; i++) - lept_stderr(" Level %d (%zu bytes): %d\n", i, - pms->sizes[i], pms->memempty[i]); - - return; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixarith.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixarith.c deleted file mode 100644 index df75e6a6..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixarith.c +++ /dev/null @@ -1,1629 +0,0 @@ -/*====================================================================* - - 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 pixarith.c - *- * - * One-image grayscale arithmetic operations (8, 16, 32 bpp) - * l_int32 pixAddConstantGray() - * l_int32 pixMultConstantGray() - * - * Two-image grayscale arithmetic operations (8, 16, 32 bpp) - * PIX *pixAddGray() - * PIX *pixSubtractGray() - * PIX *pixMultiplyGray() - * - * Grayscale threshold operation (8, 16, 32 bpp) - * PIX *pixThresholdToValue() - * - * Image accumulator arithmetic operations - * PIX *pixInitAccumulate() - * PIX *pixFinalAccumulate() - * PIX *pixFinalAccumulateThreshold() - * l_int32 pixAccumulate() - * l_int32 pixMultConstAccumulate() - * - * Absolute value of difference - * PIX *pixAbsDifference() - * - * Sum of color images - * PIX *pixAddRGB() - * - * Two-image min and max operations (8 and 16 bpp) - * PIX *pixMinOrMax() - * - * Scale pix for maximum dynamic range - * PIX *pixMaxDynamicRange() - * PIX *pixMaxDynamicRangeRGB() - * - * RGB pixel value scaling - * l_uint32 linearScaleRGBVal() - * l_uint32 logScaleRGBVal() - * - * Log base2 lookup - * l_float32 *makeLogBase2Tab() - * l_float32 getLogBase2() - * - * The image accumulator operations are used when you expect - * overflow from 8 bits on intermediate results. For example, - * you might want a tophat contrast operator which is - * 3*I - opening(I,S) - closing(I,S) - * To use these operations, first use the init to generate - * a 16 bpp image, use the accumulate to add or subtract 8 bpp - * images from that, or the multiply constant to multiply - * by a small constant (much less than 256 -- we don't want - * overflow from the 16 bit images!), and when you're finished - * use final to bring the result back to 8 bpp, clipped - * if necessary. There is also a divide function, which - * can be used to divide one image by another, scaling the - * result for maximum dynamic range, and giving back the - * 8 bpp result. - * - * A simpler interface to the arithmetic operations is - * provided in pixacc.c. - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include -#include "allheaders.h" - -/*-------------------------------------------------------------* - * One-image grayscale arithmetic operations * - *-------------------------------------------------------------*/ -/*! - * \brief pixAddConstantGray() - * - * \param[in] pixs 8, 16 or 32 bpp - * \param[in] val amount to add to each pixel - * \return 0 if OK, 1 on error - * - * - * Notes: - * (1) In-place operation. - * (2) No clipping for 32 bpp. - * (3) For 8 and 16 bpp, if val > 0 the result is clipped - * to 0xff and 0xffff, rsp. - * (4) For 8 and 16 bpp, if val < 0 the result is clipped to 0. - *- */ -l_ok -pixAddConstantGray(PIX *pixs, - l_int32 val) -{ -l_int32 i, j, w, h, d, wpl, pval; -l_uint32 *data, *line; - - PROCNAME("pixAddConstantGray"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8 && d != 16 && d != 32) - return ERROR_INT("pixs not 8, 16 or 32 bpp", procName, 1); - - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - for (i = 0; i < h; i++) { - line = data + i * wpl; - if (d == 8) { - if (val < 0) { - for (j = 0; j < w; j++) { - pval = GET_DATA_BYTE(line, j); - pval = L_MAX(0, pval + val); - SET_DATA_BYTE(line, j, pval); - } - } else { /* val >= 0 */ - for (j = 0; j < w; j++) { - pval = GET_DATA_BYTE(line, j); - pval = L_MIN(255, pval + val); - SET_DATA_BYTE(line, j, pval); - } - } - } else if (d == 16) { - if (val < 0) { - for (j = 0; j < w; j++) { - pval = GET_DATA_TWO_BYTES(line, j); - pval = L_MAX(0, pval + val); - SET_DATA_TWO_BYTES(line, j, pval); - } - } else { /* val >= 0 */ - for (j = 0; j < w; j++) { - pval = GET_DATA_TWO_BYTES(line, j); - pval = L_MIN(0xffff, pval + val); - SET_DATA_TWO_BYTES(line, j, pval); - } - } - } else { /* d == 32; no check for overflow (< 0 or > 0xffffffff) */ - for (j = 0; j < w; j++) - *(line + j) += val; - } - } - - return 0; -} - - -/*! - * \brief pixMultConstantGray() - * - * \param[in] pixs 8, 16 or 32 bpp - * \param[in] val >= 0.0; amount to multiply by each pixel - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) In-place operation; val must be >= 0. - * (2) No clipping for 32 bpp. - * (3) For 8 and 16 bpp, the result is clipped to 0xff and 0xffff, rsp. - *- */ -l_ok -pixMultConstantGray(PIX *pixs, - l_float32 val) -{ -l_int32 i, j, w, h, d, wpl, pval; -l_uint32 upval; -l_uint32 *data, *line; - - PROCNAME("pixMultConstantGray"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8 && d != 16 && d != 32) - return ERROR_INT("pixs not 8, 16 or 32 bpp", procName, 1); - if (val < 0.0) - return ERROR_INT("val < 0.0", procName, 1); - - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - for (i = 0; i < h; i++) { - line = data + i * wpl; - if (d == 8) { - for (j = 0; j < w; j++) { - pval = GET_DATA_BYTE(line, j); - pval = (l_int32)(val * pval); - pval = L_MIN(255, pval); - SET_DATA_BYTE(line, j, pval); - } - } else if (d == 16) { - for (j = 0; j < w; j++) { - pval = GET_DATA_TWO_BYTES(line, j); - pval = (l_int32)(val * pval); - pval = L_MIN(0xffff, pval); - SET_DATA_TWO_BYTES(line, j, pval); - } - } else { /* d == 32; no clipping */ - for (j = 0; j < w; j++) { - upval = *(line + j); - upval = (l_uint32)(val * upval); - *(line + j) = upval; - } - } - } - - return 0; -} - - -/*-------------------------------------------------------------* - * Two-image grayscale arithmetic ops * - *-------------------------------------------------------------*/ -/*! - * \brief pixAddGray() - * - * \param[in] pixd [optional]; this can be null, equal to pixs1, or - * different from pixs1 - * \param[in] pixs1 can be equal to pixd - * \param[in] pixs2 - * \return pixd always - * - *- * Notes: - * (1) Arithmetic addition of two 8, 16 or 32 bpp images. - * (2) For 8 and 16 bpp, we do explicit clipping to 0xff and 0xffff, - * respectively. - * (3) Alignment is to UL corner. - * (4) There are 3 cases. The result can go to a new dest, - * in-place to pixs1, or to an existing input dest: - * * pixd == null: (src1 + src2) --> new pixd - * * pixd == pixs1: (src1 + src2) --> src1 (in-place) - * * pixd != pixs1: (src1 + src2) --> input pixd - * (5) pixs2 must be different from both pixd and pixs1. - *- */ -PIX * -pixAddGray(PIX *pixd, - PIX *pixs1, - PIX *pixs2) -{ -l_int32 i, j, d, ws, hs, w, h, wpls, wpld, val, sum; -l_uint32 *datas, *datad, *lines, *lined; - - PROCNAME("pixAddGray"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); - if (pixs2 == pixs1) - return (PIX *)ERROR_PTR("pixs2 and pixs1 must differ", procName, pixd); - if (pixs2 == pixd) - return (PIX *)ERROR_PTR("pixs2 and pixd must differ", procName, pixd); - d = pixGetDepth(pixs1); - if (d != 8 && d != 16 && d != 32) - return (PIX *)ERROR_PTR("pix are not 8, 16 or 32 bpp", procName, pixd); - if (pixGetDepth(pixs2) != d) - return (PIX *)ERROR_PTR("depths differ (pixs1, pixs2)", procName, pixd); - if (pixd && (pixGetDepth(pixd) != d)) - return (PIX *)ERROR_PTR("depths differ (pixs1, pixd)", procName, pixd); - - if (!pixSizesEqual(pixs1, pixs2)) - L_WARNING("pixs1 and pixs2 not equal in size\n", procName); - if (pixd && !pixSizesEqual(pixs1, pixd)) - L_WARNING("pixs1 and pixd not equal in size\n", procName); - - if (pixs1 != pixd) - pixd = pixCopy(pixd, pixs1); - - /* pixd + pixs2 ==> pixd */ - datas = pixGetData(pixs2); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs2); - wpld = pixGetWpl(pixd); - pixGetDimensions(pixs2, &ws, &hs, NULL); - pixGetDimensions(pixd, &w, &h, NULL); - w = L_MIN(ws, w); - h = L_MIN(hs, h); - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - lines = datas + i * wpls; - if (d == 8) { - for (j = 0; j < w; j++) { - sum = GET_DATA_BYTE(lines, j) + GET_DATA_BYTE(lined, j); - val = L_MIN(sum, 255); - SET_DATA_BYTE(lined, j, val); - } - } else if (d == 16) { - for (j = 0; j < w; j++) { - sum = GET_DATA_TWO_BYTES(lines, j) - + GET_DATA_TWO_BYTES(lined, j); - val = L_MIN(sum, 0xffff); - SET_DATA_TWO_BYTES(lined, j, val); - } - } else { /* d == 32; no clipping */ - for (j = 0; j < w; j++) - *(lined + j) += *(lines + j); - } - } - - return pixd; -} - - -/*! - * \brief pixSubtractGray() - * - * \param[in] pixd [optional]; this can be null, equal to pixs1, or - * different from pixs1 - * \param[in] pixs1 can be equal to pixd - * \param[in] pixs2 - * \return pixd always - * - *- * Notes: - * (1) Arithmetic subtraction of two 8, 16 or 32 bpp images. - * (2) Source pixs2 is always subtracted from source pixs1. - * (3) Do explicit clipping to 0. - * (4) Alignment is to UL corner. - * (5) There are 3 cases. The result can go to a new dest, - * in-place to pixs1, or to an existing input dest: - * (a) pixd == null (src1 - src2) --> new pixd - * (b) pixd == pixs1 (src1 - src2) --> src1 (in-place) - * (d) pixd != pixs1 (src1 - src2) --> input pixd - * (6) pixs2 must be different from both pixd and pixs1. - *- */ -PIX * -pixSubtractGray(PIX *pixd, - PIX *pixs1, - PIX *pixs2) -{ -l_int32 i, j, w, h, ws, hs, d, wpls, wpld, val, diff; -l_uint32 *datas, *datad, *lines, *lined; - - PROCNAME("pixSubtractGray"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); - if (pixs2 == pixs1) - return (PIX *)ERROR_PTR("pixs2 and pixs1 must differ", procName, pixd); - if (pixs2 == pixd) - return (PIX *)ERROR_PTR("pixs2 and pixd must differ", procName, pixd); - d = pixGetDepth(pixs1); - if (d != 8 && d != 16 && d != 32) - return (PIX *)ERROR_PTR("pix are not 8, 16 or 32 bpp", procName, pixd); - if (pixGetDepth(pixs2) != d) - return (PIX *)ERROR_PTR("depths differ (pixs1, pixs2)", procName, pixd); - if (pixd && (pixGetDepth(pixd) != d)) - return (PIX *)ERROR_PTR("depths differ (pixs1, pixd)", procName, pixd); - - if (!pixSizesEqual(pixs1, pixs2)) - L_WARNING("pixs1 and pixs2 not equal in size\n", procName); - if (pixd && !pixSizesEqual(pixs1, pixd)) - L_WARNING("pixs1 and pixd not equal in size\n", procName); - - if (pixs1 != pixd) - pixd = pixCopy(pixd, pixs1); - - /* pixd - pixs2 ==> pixd */ - datas = pixGetData(pixs2); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs2); - wpld = pixGetWpl(pixd); - pixGetDimensions(pixs2, &ws, &hs, NULL); - pixGetDimensions(pixd, &w, &h, NULL); - w = L_MIN(ws, w); - h = L_MIN(hs, h); - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - lines = datas + i * wpls; - if (d == 8) { - for (j = 0; j < w; j++) { - diff = GET_DATA_BYTE(lined, j) - GET_DATA_BYTE(lines, j); - val = L_MAX(diff, 0); - SET_DATA_BYTE(lined, j, val); - } - } else if (d == 16) { - for (j = 0; j < w; j++) { - diff = GET_DATA_TWO_BYTES(lined, j) - - GET_DATA_TWO_BYTES(lines, j); - val = L_MAX(diff, 0); - SET_DATA_TWO_BYTES(lined, j, val); - } - } else { /* d == 32; no clipping */ - for (j = 0; j < w; j++) - *(lined + j) -= *(lines + j); - } - } - - return pixd; -} - - -/*! - * \brief pixMultiplyGray() - * - * \param[in] pixs 32 bpp rgb or 8 bpp gray - * \param[in] pixg 8 bpp gray - * \param[in] norm multiplicative factor to avoid overflow; 0 for default - * \return pixd, or null on error - * - *- * Notes: - * (1) This function can be used for correcting a scanned image - * under non-uniform illumination. For that application, - * %pixs is the scanned image, %pixg is an image whose values - * are inversely related to light from a uniform (say, white) - * target, and %norm is typically the inverse of the maximum - * pixel value in %pixg. - * (2) Set norm = 0 to get the default value, which is the inverse - * of the max value in %pixg. This avoids overflow in the product. - * (3) For 32 bpp %pixs, all 3 components are multiplied by the - * same number. - * (4) Alignment is to UL corner. - *- */ -PIX * -pixMultiplyGray(PIX *pixs, - PIX *pixg, - l_float32 norm) -{ -l_int32 i, j, w, h, d, ws, hs, ds, wpls, wplg, wpld; -l_int32 rval, gval, bval, rval2, gval2, bval2, vals, valg, val, maxgray; -l_uint32 val32; -l_uint32 *datas, *datag, *datad, *lines, *lineg, *lined; -PIX *pixd; - - PROCNAME("pixMultiplyGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &ws, &hs, &ds); - if (ds != 8 && ds != 32) - return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); - if (!pixg) - return (PIX *)ERROR_PTR("pixg not defined", procName, NULL); - pixGetDimensions(pixg, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("pixg not 8 bpp", procName, NULL); - - if (norm <= 0.0) { - pixGetExtremeValue(pixg, 1, L_SELECT_MAX, NULL, NULL, NULL, &maxgray); - norm = (maxgray > 0) ? 1.0 / (l_float32)maxgray : 1.0; - } - - if ((pixd = pixCreateTemplate(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - datas = pixGetData(pixs); - datag = pixGetData(pixg); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wplg = pixGetWpl(pixg); - wpld = pixGetWpl(pixd); - w = L_MIN(ws, w); - h = L_MIN(hs, h); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lineg = datag + i * wplg; - lined = datad + i * wpld; - if (ds == 8) { - for (j = 0; j < w; j++) { - vals = GET_DATA_BYTE(lines, j); - valg = GET_DATA_BYTE(lineg, j); - val = (l_int32)(vals * valg * norm + 0.5); - val = L_MIN(255, val); - SET_DATA_BYTE(lined, j, val); - } - } else { /* ds == 32 */ - for (j = 0; j < w; j++) { - val32 = *(lines + j); - extractRGBValues(val32, &rval, &gval, &bval); - valg = GET_DATA_BYTE(lineg, j); - rval2 = (l_int32)(rval * valg * norm + 0.5); - rval2 = L_MIN(255, rval2); - gval2 = (l_int32)(gval * valg * norm + 0.5); - gval2 = L_MIN(255, gval2); - bval2 = (l_int32)(bval * valg * norm + 0.5); - bval2 = L_MIN(255, bval2); - composeRGBPixel(rval2, gval2, bval2, lined + j); - } - } - } - - return pixd; -} - - -/*-------------------------------------------------------------* - * Grayscale threshold operation * - *-------------------------------------------------------------*/ -/*! - * \brief pixThresholdToValue() - * - * \param[in] pixd [optional]; if not null, must be equal to pixs - * \param[in] pixs 8, 16, 32 bpp - * \param[in] threshval - * \param[in] setval - * \return pixd always - * - *- * Notes: - * ~ operation can be in-place (pixs == pixd) or to a new pixd - * ~ if %setval > %threshval, sets pixels with a value >= threshval to setval - * ~ if %setval < %threshval, sets pixels with a value <= threshval to setval - * ~ if %setval == %threshval, no-op - *- */ -PIX * -pixThresholdToValue(PIX *pixd, - PIX *pixs, - l_int32 threshval, - l_int32 setval) -{ -l_int32 i, j, w, h, d, wpld, setabove; -l_uint32 *datad, *lined; - - PROCNAME("pixThresholdToValue"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - d = pixGetDepth(pixs); - if (d != 8 && d != 16 && d != 32) - return (PIX *)ERROR_PTR("pixs not 8, 16 or 32 bpp", procName, pixd); - if (pixd && (pixs != pixd)) - return (PIX *)ERROR_PTR("pixd exists and is not pixs", procName, pixd); - if (threshval < 0 || setval < 0) - return (PIX *)ERROR_PTR("threshval & setval not < 0", procName, pixd); - if (d == 8 && setval > 255) - return (PIX *)ERROR_PTR("setval > 255 for 8 bpp", procName, pixd); - if (d == 16 && setval > 0xffff) - return (PIX *)ERROR_PTR("setval > 0xffff for 16 bpp", procName, pixd); - - if (!pixd) - pixd = pixCopy(NULL, pixs); - if (setval == threshval) { - L_WARNING("setval == threshval; no operation\n", procName); - return pixd; - } - - datad = pixGetData(pixd); - pixGetDimensions(pixd, &w, &h, NULL); - wpld = pixGetWpl(pixd); - if (setval > threshval) - setabove = TRUE; - else - setabove = FALSE; - - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - if (setabove == TRUE) { - if (d == 8) { - for (j = 0; j < w; j++) { - if (GET_DATA_BYTE(lined, j) - threshval >= 0) - SET_DATA_BYTE(lined, j, setval); - } - } else if (d == 16) { - for (j = 0; j < w; j++) { - if (GET_DATA_TWO_BYTES(lined, j) - threshval >= 0) - SET_DATA_TWO_BYTES(lined, j, setval); - } - } else { /* d == 32 */ - for (j = 0; j < w; j++) { - if (*(lined + j) >= threshval) - *(lined + j) = setval; - } - } - } else { /* set if below or at threshold */ - if (d == 8) { - for (j = 0; j < w; j++) { - if (GET_DATA_BYTE(lined, j) - threshval <= 0) - SET_DATA_BYTE(lined, j, setval); - } - } else if (d == 16) { - for (j = 0; j < w; j++) { - if (GET_DATA_TWO_BYTES(lined, j) - threshval <= 0) - SET_DATA_TWO_BYTES(lined, j, setval); - } - } else { /* d == 32 */ - for (j = 0; j < w; j++) { - if (*(lined + j) <= threshval) - *(lined + j) = setval; - } - } - } - } - - return pixd; -} - - -/*-------------------------------------------------------------* - * Image accumulator arithmetic operations * - *-------------------------------------------------------------*/ -/*! - * \brief pixInitAccumulate() - * - * \param[in] w, h of accumulate array - * \param[in] offset initialize the 32 bpp to have this - * value; not more than 0x40000000 - * \return pixd 32 bpp, or NULL on error - * - *- * Notes: - * (1) %offset must be >= 0. - * (2) %offset is used so that we can do arithmetic - * with negative number results on l_uint32 data; it - * prevents the l_uint32 data from going negative. - * (3) Because we use l_int32 intermediate data results, - * these should never exceed the max of l_int32 (0x7fffffff). - * We do not permit the offset to be above 0x40000000, - * which is half way between 0 and the max of l_int32. - * (4) The same offset should be used for initialization, - * multiplication by a constant, and final extraction! - * (5) If you're only adding positive values, %offset can be 0. - *- */ -PIX * -pixInitAccumulate(l_int32 w, - l_int32 h, - l_uint32 offset) -{ -PIX *pixd; - - PROCNAME("pixInitAccumulate"); - - if ((pixd = pixCreate(w, h, 32)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - if (offset > 0x40000000) - offset = 0x40000000; - pixSetAllArbitrary(pixd, offset); - return pixd; -} - - -/*! - * \brief pixFinalAccumulate() - * - * \param[in] pixs 32 bpp - * \param[in] offset same as used for initialization - * \param[in] depth 8, 16 or 32 bpp, of destination - * \return pixd 8, 16 or 32 bpp, or NULL on error - * - *- * Notes: - * (1) %offset must be >= 0 and should not exceed 0x40000000. - * (2) %offset is subtracted from the src 32 bpp image - * (3) For 8 bpp dest, the result is clipped to [0, 0xff] - * (4) For 16 bpp dest, the result is clipped to [0, 0xffff] - *- */ -PIX * -pixFinalAccumulate(PIX *pixs, - l_uint32 offset, - l_int32 depth) -{ -l_int32 i, j, w, h, wpls, wpld, val; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; - - PROCNAME("pixFinalAccumulate"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (depth != 8 && depth != 16 && depth != 32) - return (PIX *)ERROR_PTR("dest depth not 8, 16, 32 bpp", procName, NULL); - if (offset > 0x40000000) - offset = 0x40000000; - - pixGetDimensions(pixs, &w, &h, NULL); - if ((pixd = pixCreate(w, h, depth)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); /* but how did pixs get it initially? */ - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - if (depth == 8) { - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = lines[j] - offset; - val = L_MAX(0, val); - val = L_MIN(255, val); - SET_DATA_BYTE(lined, j, (l_uint8)val); - } - } - } else if (depth == 16) { - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = lines[j] - offset; - val = L_MAX(0, val); - val = L_MIN(0xffff, val); - SET_DATA_TWO_BYTES(lined, j, (l_uint16)val); - } - } - } else { /* depth == 32 */ - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) - lined[j] = lines[j] - offset; - } - } - - return pixd; -} - - -/*! - * \brief pixFinalAccumulateThreshold() - * - * \param[in] pixs 32 bpp - * \param[in] offset same as used for initialization - * \param[in] threshold values less than this are set in the destination - * \return pixd 1 bpp, or NULL on error - * - *- * Notes: - * (1) %offset must be >= 0 and should not exceed 0x40000000. - * (2) %offset is subtracted from the src 32 bpp image - *- */ -PIX * -pixFinalAccumulateThreshold(PIX *pixs, - l_uint32 offset, - l_uint32 threshold) -{ -l_int32 i, j, w, h, wpls, wpld, val; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; - - PROCNAME("pixFinalAccumulateThreshold"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (offset > 0x40000000) - offset = 0x40000000; - - pixGetDimensions(pixs, &w, &h, NULL); - if ((pixd = pixCreate(w, h, 1)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); /* but how did pixs get it initially? */ - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = lines[j] - offset; - if (val >= threshold) { - SET_DATA_BIT(lined, j); - } - } - } - - return pixd; -} - - -/*! - * \brief pixAccumulate() - * - * \param[in] pixd 32 bpp - * \param[in] pixs 1, 8, 16 or 32 bpp - * \param[in] op L_ARITH_ADD or L_ARITH_SUBTRACT - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) This adds or subtracts each pixs value from pixd. - * (2) This clips to the minimum of pixs and pixd, so they - * do not need to be the same size. - * (3) The alignment is to the origin [UL corner] of pixs & pixd. - *- */ -l_ok -pixAccumulate(PIX *pixd, - PIX *pixs, - l_int32 op) -{ -l_int32 i, j, w, h, d, wd, hd, wpls, wpld; -l_uint32 *datas, *datad, *lines, *lined; - - - PROCNAME("pixAccumulate"); - - if (!pixd || (pixGetDepth(pixd) != 32)) - return ERROR_INT("pixd not defined or not 32 bpp", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - d = pixGetDepth(pixs); - if (d != 1 && d != 8 && d != 16 && d != 32) - return ERROR_INT("pixs not 1, 8, 16 or 32 bpp", procName, 1); - if (op != L_ARITH_ADD && op != L_ARITH_SUBTRACT) - return ERROR_INT("op must be in {L_ARITH_ADD, L_ARITH_SUBTRACT}", - procName, 1); - - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - pixGetDimensions(pixs, &w, &h, NULL); - pixGetDimensions(pixd, &wd, &hd, NULL); - w = L_MIN(w, wd); - h = L_MIN(h, hd); - if (d == 1) { - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - if (op == L_ARITH_ADD) { - for (j = 0; j < w; j++) - lined[j] += GET_DATA_BIT(lines, j); - } else { /* op == L_ARITH_SUBTRACT */ - for (j = 0; j < w; j++) - lined[j] -= GET_DATA_BIT(lines, j); - } - } - } else if (d == 8) { - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - if (op == L_ARITH_ADD) { - for (j = 0; j < w; j++) - lined[j] += GET_DATA_BYTE(lines, j); - } else { /* op == L_ARITH_SUBTRACT */ - for (j = 0; j < w; j++) - lined[j] -= GET_DATA_BYTE(lines, j); - } - } - } else if (d == 16) { - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - if (op == L_ARITH_ADD) { - for (j = 0; j < w; j++) - lined[j] += GET_DATA_TWO_BYTES(lines, j); - } else { /* op == L_ARITH_SUBTRACT */ - for (j = 0; j < w; j++) - lined[j] -= GET_DATA_TWO_BYTES(lines, j); - } - } - } else { /* d == 32 */ - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - if (op == L_ARITH_ADD) { - for (j = 0; j < w; j++) - lined[j] += lines[j]; - } else { /* op == L_ARITH_SUBTRACT */ - for (j = 0; j < w; j++) - lined[j] -= lines[j]; - } - } - } - - return 0; -} - - -/*! - * \brief pixMultConstAccumulate() - * - * \param[in] pixs 32 bpp - * \param[in] factor - * \param[in] offset same as used for initialization - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) %offset must be >= 0 and should not exceed 0x40000000. - * (2) This multiplies each pixel, relative to offset, by %factor. - * (3) The result is returned with %offset back in place. - *- */ -l_ok -pixMultConstAccumulate(PIX *pixs, - l_float32 factor, - l_uint32 offset) -{ -l_int32 i, j, w, h, wpl, val; -l_uint32 *data, *line; - - PROCNAME("pixMultConstAccumulate"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not 32 bpp", procName, 1); - if (offset > 0x40000000) - offset = 0x40000000; - - pixGetDimensions(pixs, &w, &h, NULL); - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - val = line[j] - offset; - val = (l_int32)(val * factor); - val += offset; - line[j] = (l_uint32)val; - } - } - - return 0; -} - - -/*-----------------------------------------------------------------------* - * Absolute value of difference * - *-----------------------------------------------------------------------*/ -/*! - * \brief pixAbsDifference() - * - * \param[in] pixs1, pixs2 both either 8 or 16 bpp gray, or 32 bpp RGB - * \return pixd, or NULL on error - * - *- * Notes: - * (1) The depth of pixs1 and pixs2 must be equal. - * (2) Clips computation to the min size, aligning the UL corners - * (3) For 8 and 16 bpp, assumes one gray component. - * (4) For 32 bpp, assumes 3 color components, and ignores the - * LSB of each word (the alpha channel) - * (5) Computes the absolute value of the difference between - * each component value. - *- */ -PIX * -pixAbsDifference(PIX *pixs1, - PIX *pixs2) -{ -l_int32 i, j, w, h, w2, h2, d, wpls1, wpls2, wpld, val1, val2, diff; -l_int32 rval1, gval1, bval1, rval2, gval2, bval2, rdiff, gdiff, bdiff; -l_uint32 *datas1, *datas2, *datad, *lines1, *lines2, *lined; -PIX *pixd; - - PROCNAME("pixAbsDifference"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); - d = pixGetDepth(pixs1); - if (d != pixGetDepth(pixs2)) - return (PIX *)ERROR_PTR("src1 and src2 depths unequal", procName, NULL); - if (d != 8 && d != 16 && d != 32) - return (PIX *)ERROR_PTR("depths not in {8, 16, 32}", procName, NULL); - - pixGetDimensions(pixs1, &w, &h, NULL); - pixGetDimensions(pixs2, &w2, &h2, NULL); - w = L_MIN(w, w2); - h = L_MIN(h, h2); - if ((pixd = pixCreate(w, h, d)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs1); - datas1 = pixGetData(pixs1); - datas2 = pixGetData(pixs2); - datad = pixGetData(pixd); - wpls1 = pixGetWpl(pixs1); - wpls2 = pixGetWpl(pixs2); - wpld = pixGetWpl(pixd); - if (d == 8) { - for (i = 0; i < h; i++) { - lines1 = datas1 + i * wpls1; - lines2 = datas2 + i * wpls2; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val1 = GET_DATA_BYTE(lines1, j); - val2 = GET_DATA_BYTE(lines2, j); - diff = L_ABS(val1 - val2); - SET_DATA_BYTE(lined, j, diff); - } - } - } else if (d == 16) { - for (i = 0; i < h; i++) { - lines1 = datas1 + i * wpls1; - lines2 = datas2 + i * wpls2; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val1 = GET_DATA_TWO_BYTES(lines1, j); - val2 = GET_DATA_TWO_BYTES(lines2, j); - diff = L_ABS(val1 - val2); - SET_DATA_TWO_BYTES(lined, j, diff); - } - } - } else { /* d == 32 */ - for (i = 0; i < h; i++) { - lines1 = datas1 + i * wpls1; - lines2 = datas2 + i * wpls2; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines1[j], &rval1, &gval1, &bval1); - extractRGBValues(lines2[j], &rval2, &gval2, &bval2); - rdiff = L_ABS(rval1 - rval2); - gdiff = L_ABS(gval1 - gval2); - bdiff = L_ABS(bval1 - bval2); - composeRGBPixel(rdiff, gdiff, bdiff, lined + j); - } - } - } - - return pixd; -} - - -/*-----------------------------------------------------------------------* - * Sum of color images * - *-----------------------------------------------------------------------*/ -/*! - * \brief pixAddRGB() - * - * \param[in] pixs1, pixs2 32 bpp RGB, or colormapped - * \return pixd, or NULL on error - * - *- * Notes: - * (1) Clips computation to the minimum size, aligning the UL corners. - * (2) Removes any colormap to RGB, and ignores the LSB of each - * pixel word (the alpha channel). - * (3) Adds each component value, pixelwise, clipping to 255. - * (4) This is useful to combine two images where most of the - * pixels are essentially black, such as in pixPerceptualDiff(). - *- */ -PIX * -pixAddRGB(PIX *pixs1, - PIX *pixs2) -{ -l_int32 i, j, w, h, d, w2, h2, d2, wplc1, wplc2, wpld; -l_int32 rval1, gval1, bval1, rval2, gval2, bval2, rval, gval, bval; -l_uint32 *datac1, *datac2, *datad, *linec1, *linec2, *lined; -PIX *pixc1, *pixc2, *pixd; - - PROCNAME("pixAddRGB"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); - pixGetDimensions(pixs1, &w, &h, &d); - pixGetDimensions(pixs2, &w2, &h2, &d2); - if (!pixGetColormap(pixs1) && d != 32) - return (PIX *)ERROR_PTR("pixs1 not cmapped or rgb", procName, NULL); - if (!pixGetColormap(pixs2) && d2 != 32) - return (PIX *)ERROR_PTR("pixs2 not cmapped or rgb", procName, NULL); - if (pixGetColormap(pixs1)) - pixc1 = pixRemoveColormap(pixs1, REMOVE_CMAP_TO_FULL_COLOR); - else - pixc1 = pixClone(pixs1); - if (pixGetColormap(pixs2)) - pixc2 = pixRemoveColormap(pixs2, REMOVE_CMAP_TO_FULL_COLOR); - else - pixc2 = pixClone(pixs2); - - w = L_MIN(w, w2); - h = L_MIN(h, h2); - pixd = pixCreate(w, h, 32); - pixCopyResolution(pixd, pixs1); - datac1 = pixGetData(pixc1); - datac2 = pixGetData(pixc2); - datad = pixGetData(pixd); - wplc1 = pixGetWpl(pixc1); - wplc2 = pixGetWpl(pixc2); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - linec1 = datac1 + i * wplc1; - linec2 = datac2 + i * wplc2; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(linec1[j], &rval1, &gval1, &bval1); - extractRGBValues(linec2[j], &rval2, &gval2, &bval2); - rval = L_MIN(255, rval1 + rval2); - gval = L_MIN(255, gval1 + gval2); - bval = L_MIN(255, bval1 + bval2); - composeRGBPixel(rval, gval, bval, lined + j); - } - } - - pixDestroy(&pixc1); - pixDestroy(&pixc2); - return pixd; -} - - -/*-----------------------------------------------------------------------* - * Two-image min and max operations (8 and 16 bpp) * - *-----------------------------------------------------------------------*/ -/*! - * \brief pixMinOrMax() - * - * \param[in] pixd [optional] destination: this can be null, - * equal to pixs1, or different from pixs1 - * \param[in] pixs1 can be equal to pixd - * \param[in] pixs2 - * \param[in] type L_CHOOSE_MIN, L_CHOOSE_MAX - * \return pixd always - * - *- * Notes: - * (1) This gives the min or max of two images, component-wise. - * (2) The depth can be 8 or 16 bpp for 1 component, and 32 bpp - * for a 3 component image. For 32 bpp, ignore the LSB - * of each word (the alpha channel) - * (3) There are 3 cases: - * ~ if pixd == null, Min(src1, src2) --> new pixd - * ~ if pixd == pixs1, Min(src1, src2) --> src1 (in-place) - * ~ if pixd != pixs1, Min(src1, src2) --> input pixd - *- */ -PIX * -pixMinOrMax(PIX *pixd, - PIX *pixs1, - PIX *pixs2, - l_int32 type) -{ -l_int32 d, ws, hs, w, h, wpls, wpld, i, j, vals, vald, val; -l_int32 rval1, gval1, bval1, rval2, gval2, bval2, rval, gval, bval; -l_uint32 *datas, *datad, *lines, *lined; - - PROCNAME("pixMinOrMax"); - - if (!pixs1) - return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); - if (!pixs2) - return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); - if (pixs1 == pixs2) - return (PIX *)ERROR_PTR("pixs1 and pixs2 must differ", procName, pixd); - if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX) - return (PIX *)ERROR_PTR("invalid type", procName, pixd); - d = pixGetDepth(pixs1); - if (pixGetDepth(pixs2) != d) - return (PIX *)ERROR_PTR("depths unequal", procName, pixd); - if (d != 8 && d != 16 && d != 32) - return (PIX *)ERROR_PTR("depth not 8, 16 or 32 bpp", procName, pixd); - - if (pixs1 != pixd) - pixd = pixCopy(pixd, pixs1); - - pixGetDimensions(pixs2, &ws, &hs, NULL); - pixGetDimensions(pixd, &w, &h, NULL); - w = L_MIN(w, ws); - h = L_MIN(h, hs); - datas = pixGetData(pixs2); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs2); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - if (d == 8) { - for (j = 0; j < w; j++) { - vals = GET_DATA_BYTE(lines, j); - vald = GET_DATA_BYTE(lined, j); - if (type == L_CHOOSE_MIN) - val = L_MIN(vals, vald); - else /* type == L_CHOOSE_MAX */ - val = L_MAX(vals, vald); - SET_DATA_BYTE(lined, j, val); - } - } else if (d == 16) { - for (j = 0; j < w; j++) { - vals = GET_DATA_TWO_BYTES(lines, j); - vald = GET_DATA_TWO_BYTES(lined, j); - if (type == L_CHOOSE_MIN) - val = L_MIN(vals, vald); - else /* type == L_CHOOSE_MAX */ - val = L_MAX(vals, vald); - SET_DATA_TWO_BYTES(lined, j, val); - } - } else { /* d == 32 */ - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval1, &gval1, &bval1); - extractRGBValues(lined[j], &rval2, &gval2, &bval2); - if (type == L_CHOOSE_MIN) { - rval = L_MIN(rval1, rval2); - gval = L_MIN(gval1, gval2); - bval = L_MIN(bval1, bval2); - } else { /* type == L_CHOOSE_MAX */ - rval = L_MAX(rval1, rval2); - gval = L_MAX(gval1, gval2); - bval = L_MAX(bval1, bval2); - } - composeRGBPixel(rval, gval, bval, lined + j); - } - } - } - - return pixd; -} - - -/*-----------------------------------------------------------------------* - * Scale for maximum dynamic range * - *-----------------------------------------------------------------------*/ -/*! - * \brief pixMaxDynamicRange() - * - * \param[in] pixs 4, 8, 16 or 32 bpp source - * \param[in] type L_LINEAR_SCALE or L_LOG_SCALE - * \return pixd 8 bpp, or NULL on error - * - *- * Notes: - * (1) Scales pixel values to fit maximally within the dest 8 bpp pixd - * (2) Assumes the source 'pixels' are a 1-component scalar. For - * a 32 bpp source, each pixel is treated as a single number -- - * not as a 3-component rgb pixel value. - * (3) Uses a LUT for log scaling. - *- */ -PIX * -pixMaxDynamicRange(PIX *pixs, - l_int32 type) -{ -l_uint8 dval; -l_int32 i, j, w, h, d, wpls, wpld, max; -l_uint32 *datas, *datad; -l_uint32 word, sval; -l_uint32 *lines, *lined; -l_float32 factor; -l_float32 *tab; -PIX *pixd; - - PROCNAME("pixMaxDynamicRange"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 4 && d != 8 && d != 16 && d != 32) - return (PIX *)ERROR_PTR("pixs not in {4,8,16,32} bpp", procName, NULL); - if (type != L_LINEAR_SCALE && type != L_LOG_SCALE) - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - - if ((pixd = pixCreate(w, h, 8)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - - /* Get max */ - max = 0; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < wpls; j++) { - word = *(lines + j); - if (d == 4) { - max = L_MAX(max, word >> 28); - max = L_MAX(max, (word >> 24) & 0xf); - max = L_MAX(max, (word >> 20) & 0xf); - max = L_MAX(max, (word >> 16) & 0xf); - max = L_MAX(max, (word >> 12) & 0xf); - max = L_MAX(max, (word >> 8) & 0xf); - max = L_MAX(max, (word >> 4) & 0xf); - max = L_MAX(max, word & 0xf); - } else if (d == 8) { - max = L_MAX(max, word >> 24); - max = L_MAX(max, (word >> 16) & 0xff); - max = L_MAX(max, (word >> 8) & 0xff); - max = L_MAX(max, word & 0xff); - } else if (d == 16) { - max = L_MAX(max, word >> 16); - max = L_MAX(max, word & 0xffff); - } else { /* d == 32 (rgb) */ - max = L_MAX(max, word); - } - } - } - - /* Map to the full dynamic range */ - if (d == 4) { - if (type == L_LINEAR_SCALE) { - factor = 255. / (l_float32)max; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - sval = GET_DATA_QBIT(lines, j); - dval = (l_uint8)(factor * (l_float32)sval + 0.5); - SET_DATA_QBIT(lined, j, dval); - } - } - } else { /* type == L_LOG_SCALE) */ - tab = makeLogBase2Tab(); - factor = 255. / getLogBase2(max, tab); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - sval = GET_DATA_QBIT(lines, j); - dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5); - SET_DATA_BYTE(lined, j, dval); - } - } - LEPT_FREE(tab); - } - } else if (d == 8) { - if (type == L_LINEAR_SCALE) { - factor = 255. / (l_float32)max; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - sval = GET_DATA_BYTE(lines, j); - dval = (l_uint8)(factor * (l_float32)sval + 0.5); - SET_DATA_BYTE(lined, j, dval); - } - } - } else { /* type == L_LOG_SCALE) */ - tab = makeLogBase2Tab(); - factor = 255. / getLogBase2(max, tab); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - sval = GET_DATA_BYTE(lines, j); - dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5); - SET_DATA_BYTE(lined, j, dval); - } - } - LEPT_FREE(tab); - } - } else if (d == 16) { - if (type == L_LINEAR_SCALE) { - factor = 255. / (l_float32)max; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - sval = GET_DATA_TWO_BYTES(lines, j); - dval = (l_uint8)(factor * (l_float32)sval + 0.5); - SET_DATA_BYTE(lined, j, dval); - } - } - } else { /* type == L_LOG_SCALE) */ - tab = makeLogBase2Tab(); - factor = 255. / getLogBase2(max, tab); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - sval = GET_DATA_TWO_BYTES(lines, j); - dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5); - SET_DATA_BYTE(lined, j, dval); - } - } - LEPT_FREE(tab); - } - } else { /* d == 32 */ - if (type == L_LINEAR_SCALE) { - factor = 255. / (l_float32)max; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - sval = lines[j]; - dval = (l_uint8)(factor * (l_float32)sval + 0.5); - SET_DATA_BYTE(lined, j, dval); - } - } - } else { /* type == L_LOG_SCALE) */ - tab = makeLogBase2Tab(); - factor = 255. / getLogBase2(max, tab); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - sval = lines[j]; - dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5); - SET_DATA_BYTE(lined, j, dval); - } - } - LEPT_FREE(tab); - } - } - - return pixd; -} - - -/*! - * \brief pixMaxDynamicRangeRGB() - * - * \param[in] pixs 32 bpp rgb source - * \param[in] type L_LINEAR_SCALE or L_LOG_SCALE - * \return pixd 32 bpp, or NULL on error - * - *- * Notes: - * (1) Scales pixel values to fit maximally within a 32 bpp dest pixd - * (2) All color components are scaled with the same factor, based - * on the maximum r,g or b component in the image. This should - * not be used if the 32-bit value is a single number (e.g., a - * count in a histogram generated by pixMakeHistoHS()). - * (3) Uses a LUT for log scaling. - *- */ -PIX * -pixMaxDynamicRangeRGB(PIX *pixs, - l_int32 type) -{ -l_int32 i, j, w, h, wpls, wpld, max; -l_uint32 sval, dval, word; -l_uint32 *datas, *datad; -l_uint32 *lines, *lined; -l_float32 factor; -l_float32 *tab; -PIX *pixd; - - PROCNAME("pixMaxDynamicRangeRGB"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); - if (type != L_LINEAR_SCALE && type != L_LOG_SCALE) - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - - /* Get max */ - pixd = pixCreateTemplate(pixs); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - pixGetDimensions(pixs, &w, &h, NULL); - max = 0; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < wpls; j++) { - word = lines[j]; - max = L_MAX(max, word >> 24); - max = L_MAX(max, (word >> 16) & 0xff); - max = L_MAX(max, (word >> 8) & 0xff); - } - } - - /* Map to the full dynamic range */ - if (type == L_LINEAR_SCALE) { - factor = 255. / (l_float32)max; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - sval = lines[j]; - dval = linearScaleRGBVal(sval, factor); - lined[j] = dval; - } - } - } else { /* type == L_LOG_SCALE) */ - tab = makeLogBase2Tab(); - factor = 255. / getLogBase2(max, tab); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - sval = lines[j]; - dval = logScaleRGBVal(sval, tab, factor); - lined[j] = dval; - } - } - LEPT_FREE(tab); - } - - return pixd; -} - - -/*-----------------------------------------------------------------------* - * RGB pixel value scaling * - *-----------------------------------------------------------------------*/ -/*! - * \brief linearScaleRGBVal() - * - * \param[in] sval 32-bit rgb pixel value - * \param[in] factor multiplication factor on each component - * \return dval linearly scaled version of %sval - * - *- * Notes: - * (1) %factor must be chosen to be not greater than (255 / maxcomp), - * where maxcomp is the maximum value of the pixel components. - * Otherwise, the product will overflow a uint8. In use, factor - * is the same for all pixels in a pix. - * (2) No scaling is performed on the transparency ("A") component. - *- */ -l_uint32 -linearScaleRGBVal(l_uint32 sval, - l_float32 factor) -{ -l_uint32 dval; - - dval = ((l_uint8)(factor * (sval >> 24) + 0.5) << 24) | - ((l_uint8)(factor * ((sval >> 16) & 0xff) + 0.5) << 16) | - ((l_uint8)(factor * ((sval >> 8) & 0xff) + 0.5) << 8) | - (sval & 0xff); - return dval; -} - - -/*! - * \brief logScaleRGBVal() - * - * \param[in] sval 32-bit rgb pixel value - * \param[in] tab 256 entry log-base-2 table - * \param[in] factor multiplication factor on each component - * \return dval log scaled version of %sval - * - *- * Notes: - * (1) %tab is made with makeLogBase2Tab(). - * (2) %factor must be chosen to be not greater than - * 255.0 / log[base2](maxcomp), where maxcomp is the maximum - * value of the pixel components. Otherwise, the product - * will overflow a uint8. In use, factor is the same for - * all pixels in a pix. - * (3) No scaling is performed on the transparency ("A") component. - *- */ -l_uint32 -logScaleRGBVal(l_uint32 sval, - l_float32 *tab, - l_float32 factor) -{ -l_uint32 dval; - - dval = ((l_uint8)(factor * getLogBase2(sval >> 24, tab) + 0.5) << 24) | - ((l_uint8)(factor * getLogBase2(((sval >> 16) & 0xff), tab) + 0.5) - << 16) | - ((l_uint8)(factor * getLogBase2(((sval >> 8) & 0xff), tab) + 0.5) - << 8) | - (sval & 0xff); - return dval; -} - - -/*-----------------------------------------------------------------------* - * Log base2 lookup * - *-----------------------------------------------------------------------*/ -/* - * \brief makeLogBase2Tab() - * - * \return tab table giving the log[base2] of values from 1 to 255 - */ -l_float32 * -makeLogBase2Tab(void) -{ -l_int32 i; -l_float32 log2; -l_float32 *tab; - - PROCNAME("makeLogBase2Tab"); - - if ((tab = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32))) == NULL) - return (l_float32 *)ERROR_PTR("tab not made", procName, NULL); - - log2 = (l_float32)log((l_float32)2); - for (i = 0; i < 256; i++) - tab[i] = (l_float32)log((l_float32)i) / log2; - - return tab; -} - - -/* - * \brief getLogBase2() - * - * \param[in] val in range [0 ... 255] - * \param[in] logtab 256-entry table of logs - * \return logval log[base2] of %val, or 0 on error - */ -l_float32 -getLogBase2(l_int32 val, - l_float32 *logtab) -{ - PROCNAME("getLogBase2"); - - if (!logtab) - return ERROR_INT("logtab not defined", procName, 0); - - if (val < 0x100) - return logtab[val]; - else if (val < 0x10000) - return 8.0 + logtab[val >> 8]; - else if (val < 0x1000000) - return 16.0 + logtab[val >> 16]; - else - return 24.0 + logtab[val >> 24]; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixcomp.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixcomp.c deleted file mode 100644 index c1f3384e..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixcomp.c +++ /dev/null @@ -1,2456 +0,0 @@ -/*====================================================================* - - 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 pixcomp.c - *- * - * Pixcomp creation and destruction - * PIXC *pixcompCreateFromPix() - * PIXC *pixcompCreateFromString() - * PIXC *pixcompCreateFromFile() - * void pixcompDestroy() - * PIXC *pixcompCopy() - - * Pixcomp accessors - * l_int32 pixcompGetDimensions() - * l_int32 pixcompGetParameters() - * - * Pixcomp compression selection - * l_int32 pixcompDetermineFormat() - * - * Pixcomp conversion to Pix - * PIX *pixCreateFromPixcomp() - * - * Pixacomp creation and destruction - * PIXAC *pixacompCreate() - * PIXAC *pixacompCreateWithInit() - * PIXAC *pixacompCreateFromPixa() - * PIXAC *pixacompCreateFromFiles() - * PIXAC *pixacompCreateFromSA() - * void pixacompDestroy() - * - * Pixacomp addition/replacement - * l_int32 pixacompAddPix() - * l_int32 pixacompAddPixcomp() - * static l_int32 pixacompExtendArray() - * l_int32 pixacompReplacePix() - * l_int32 pixacompReplacePixcomp() - * l_int32 pixacompAddBox() - * - * Pixacomp accessors - * l_int32 pixacompGetCount() - * PIXC *pixacompGetPixcomp() - * PIX *pixacompGetPix() - * l_int32 pixacompGetPixDimensions() - * BOXA *pixacompGetBoxa() - * l_int32 pixacompGetBoxaCount() - * BOX *pixacompGetBox() - * l_int32 pixacompGetBoxGeometry() - * l_int32 pixacompGetOffset() - * l_int32 pixacompSetOffset() - * - * Pixacomp conversion to Pixa - * PIXA *pixaCreateFromPixacomp() - * - * Combining pixacomp - * l_int32 pixacompJoin() - * PIXAC *pixacompInterleave() - * - * Pixacomp serialized I/O - * PIXAC *pixacompRead() - * PIXAC *pixacompReadStream() - * PIXAC *pixacompReadMem() - * l_int32 pixacompWrite() - * l_int32 pixacompWriteStream() - * l_int32 pixacompWriteMem() - * - * Conversion to pdf - * l_int32 pixacompConvertToPdf() - * l_int32 pixacompConvertToPdfData() - * l_int32 pixacompFastConvertToPdfData() - * - * Output for debugging - * l_int32 pixacompWriteStreamInfo() - * l_int32 pixcompWriteStreamInfo() - * PIX *pixacompDisplayTiledAndScaled() - * l_int32 pixacompWriteFiles() - * l_int32 pixcompWriteFile() - * - * The Pixacomp is an array of Pixcomp, where each Pixcomp is a compressed - * string of the image. We don't use reference counting here. - * The basic application is to allow a large array of highly - * compressible images to reside in memory. We purposely don't - * reuse the Pixa for this, to avoid confusion and programming errors. - * - * Three compression formats are used: g4, png and jpeg. - * The compression type can be either specified or defaulted. - * If specified and it is not possible to compress (for example, - * you specify a jpeg on a 1 bpp image or one with a colormap), - * the compression type defaults to png. The jpeg compression quality - * can be specified using l_setJpegQuality(); otherwise the default is 75. - * - * The serialized version of the Pixacomp is similar to that for - * a Pixa, except that each Pixcomp can be compressed by one of - * tiffg4, png, or jpeg. Unlike serialization of the Pixa, - * serialization of the Pixacomp does not require any imaging - * libraries because it simply reads and writes the compressed data. - * - * There are two modes of use in accumulating images: - * (1) addition to the end of the array - * (2) random insertion (replacement) into the array - * - * In use, we assume that the array is fully populated up to the - * index value (n - 1), where n is the value of the pixcomp field n. - * Addition can only be made to the end of the fully populated array, - * at the index value n. Insertion can be made randomly, but again - * only within the array of pixcomps; i.e., within the set of - * indices {0 .... n-1}. The functions are pixacompReplacePix() - * and pixacompReplacePixcomp(), and they destroy the existing pixcomp. - * - * For addition to the end of the array, initialize the pixacomp with - * pixacompCreate(), which generates an empty array of pixcomps ptrs. - * For random insertion and replacement of pixcomp into a pixacomp, - * initialize a fully populated array using pixacompCreateWithInit(). - * - * The offset field allows you to use an offset-based index to - * access the 0-based ptr array in the pixacomp. This would typically - * be used to map the pixacomp array index to a page number, or v.v. - * By default, the offset is 0. For example, suppose you have 50 images, - * corresponding to page numbers 10 - 59. Then you could use - * pixac = pixacompCreateWithInit(50, 10, ...); - * This would allocate an array of 50 pixcomps, but if you asked for - * the pix at index 10, using pixacompGetPix(pixac, 10), it would - * apply the offset internally, returning the pix at index 0 in the array. - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include "allheaders.h" - - /* Bounds on initial array size */ -static const l_uint32 MaxPtrArraySize = 1000000; -static const l_int32 InitialPtrArraySize = 20; /*!< n'importe quoi */ - - /* Bound on data size */ -static const size_t MaxDataSize = 1000000000; - - /* These two globals are defined in writefile.c */ -extern l_int32 NumImageFileFormatExtensions; -extern const char *ImageFileFormatExtensions[]; - - /* Static functions */ -static l_int32 pixacompExtendArray(PIXAC *pixac); -static l_int32 pixcompFastConvertToPdfData(PIXC *pixc, const char *title, - l_uint8 **pdata, size_t *pnbytes); - - -/*---------------------------------------------------------------------* - * Pixcomp creation and destruction * - *---------------------------------------------------------------------*/ -/*! - * \brief pixcompCreateFromPix() - * - * \param[in] pix - * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG - * \return pixc, or NULL on error - * - * - * Notes: - * (1) Use %comptype == IFF_DEFAULT to have the compression - * type automatically determined. - * (2) To compress jpeg with a quality other than the default (75), use - * l_jpegSetQuality() - *- */ -PIXC * -pixcompCreateFromPix(PIX *pix, - l_int32 comptype) -{ -size_t size; -char *text; -l_int32 ret, format; -l_uint8 *data; -PIXC *pixc; - - PROCNAME("pixcompCreateFromPix"); - - if (!pix) - return (PIXC *)ERROR_PTR("pix not defined", procName, NULL); - if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && - comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) - return (PIXC *)ERROR_PTR("invalid comptype", procName, NULL); - - pixc = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC)); - pixGetDimensions(pix, &pixc->w, &pixc->h, &pixc->d); - pixGetResolution(pix, &pixc->xres, &pixc->yres); - if (pixGetColormap(pix)) - pixc->cmapflag = 1; - if ((text = pixGetText(pix)) != NULL) - pixc->text = stringNew(text); - - pixcompDetermineFormat(comptype, pixc->d, pixc->cmapflag, &format); - pixc->comptype = format; - ret = pixWriteMem(&data, &size, pix, format); - if (ret) { - L_ERROR("write to memory failed\n", procName); - pixcompDestroy(&pixc); - return NULL; - } - pixc->data = data; - pixc->size = size; - - return pixc; -} - - -/*! - * \brief pixcompCreateFromString() - * - * \param[in] data compressed string - * \param[in] size number of bytes - * \param[in] copyflag L_INSERT or L_COPY - * \return pixc, or NULL on error - * - *- * Notes: - * (1) This works when the compressed string is png, jpeg or tiffg4. - * (2) The copyflag determines if the data in the new Pixcomp is - * a copy of the input data. - *- */ -PIXC * -pixcompCreateFromString(l_uint8 *data, - size_t size, - l_int32 copyflag) -{ -l_int32 format, w, h, d, bps, spp, iscmap; -PIXC *pixc; - - PROCNAME("pixcompCreateFromString"); - - if (!data) - return (PIXC *)ERROR_PTR("data not defined", procName, NULL); - if (copyflag != L_INSERT && copyflag != L_COPY) - return (PIXC *)ERROR_PTR("invalid copyflag", procName, NULL); - - if (pixReadHeaderMem(data, size, &format, &w, &h, &bps, &spp, &iscmap) == 1) - return (PIXC *)ERROR_PTR("header data not read", procName, NULL); - pixc = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC)); - d = (spp == 3) ? 32 : bps * spp; - pixc->w = w; - pixc->h = h; - pixc->d = d; - pixc->comptype = format; - pixc->cmapflag = iscmap; - if (copyflag == L_INSERT) - pixc->data = data; - else - pixc->data = l_binaryCopy(data, size); - pixc->size = size; - return pixc; -} - - -/*! - * \brief pixcompCreateFromFile() - * - * \param[in] filename - * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG - * \return pixc, or NULL on error - * - *- * Notes: - * (1) Use %comptype == IFF_DEFAULT to have the compression - * type automatically determined. - * (2) If the comptype is invalid for this file, the default will - * be substituted. - *- */ -PIXC * -pixcompCreateFromFile(const char *filename, - l_int32 comptype) -{ -l_int32 format; -size_t nbytes; -l_uint8 *data; -PIX *pix; -PIXC *pixc; - - PROCNAME("pixcompCreateFromFile"); - - if (!filename) - return (PIXC *)ERROR_PTR("filename not defined", procName, NULL); - if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && - comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) - return (PIXC *)ERROR_PTR("invalid comptype", procName, NULL); - - findFileFormat(filename, &format); - if (format == IFF_UNKNOWN) { - L_ERROR("unreadable file: %s\n", procName, filename); - return NULL; - } - - /* Can we accept the encoded file directly? Remember that - * png is the "universal" compression type, so if requested - * it takes precedence. Otherwise, if the file is already - * compressed in g4 or jpeg, just accept the string. */ - if ((format == IFF_TIFF_G4 && comptype != IFF_PNG) || - (format == IFF_JFIF_JPEG && comptype != IFF_PNG)) - comptype = format; - if (comptype != IFF_DEFAULT && comptype == format) { - data = l_binaryRead(filename, &nbytes); - if ((pixc = pixcompCreateFromString(data, nbytes, L_INSERT)) == NULL) { - LEPT_FREE(data); - return (PIXC *)ERROR_PTR("pixc not made (string)", procName, NULL); - } - return pixc; - } - - /* Need to recompress in the default format */ - if ((pix = pixRead(filename)) == NULL) - return (PIXC *)ERROR_PTR("pix not read", procName, NULL); - if ((pixc = pixcompCreateFromPix(pix, comptype)) == NULL) { - pixDestroy(&pix); - return (PIXC *)ERROR_PTR("pixc not made", procName, NULL); - } - pixDestroy(&pix); - return pixc; -} - - -/*! - * \brief pixcompDestroy() - * - * \param[in,out] ppixc use ptr address so it will be nulled - * \return void - * - *- * Notes: - * (1) Always nulls the input ptr. - *- */ -void -pixcompDestroy(PIXC **ppixc) -{ -PIXC *pixc; - - PROCNAME("pixcompDestroy"); - - if (!ppixc) { - L_WARNING("ptr address is null!\n", procName); - return; - } - - if ((pixc = *ppixc) == NULL) - return; - - LEPT_FREE(pixc->data); - if (pixc->text) - LEPT_FREE(pixc->text); - LEPT_FREE(pixc); - *ppixc = NULL; - return; -} - - -/*! - * \brief pixcompCopy() - * - * \param[in] pixcs - * \return pixcd, or NULL on error - */ -PIXC * -pixcompCopy(PIXC *pixcs) -{ -size_t size; -l_uint8 *datas, *datad; -PIXC *pixcd; - - PROCNAME("pixcompCopy"); - - if (!pixcs) - return (PIXC *)ERROR_PTR("pixcs not defined", procName, NULL); - - pixcd = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC)); - pixcd->w = pixcs->w; - pixcd->h = pixcs->h; - pixcd->d = pixcs->d; - pixcd->xres = pixcs->xres; - pixcd->yres = pixcs->yres; - pixcd->comptype = pixcs->comptype; - if (pixcs->text != NULL) - pixcd->text = stringNew(pixcs->text); - pixcd->cmapflag = pixcs->cmapflag; - - /* Copy image data */ - size = pixcs->size; - datas = pixcs->data; - if ((datad = (l_uint8 *)LEPT_CALLOC(size, sizeof(l_int8))) == NULL) { - pixcompDestroy(&pixcd); - return (PIXC *)ERROR_PTR("pixcd not made", procName, NULL); - } - memcpy(datad, datas, size); - pixcd->data = datad; - pixcd->size = size; - return pixcd; -} - - -/*---------------------------------------------------------------------* - * Pixcomp accessors * - *---------------------------------------------------------------------*/ -/*! - * \brief pixcompGetDimensions() - * - * \param[in] pixc - * \param[out] pw, ph, pd [optional] - * \return 0 if OK, 1 on error - */ -l_ok -pixcompGetDimensions(PIXC *pixc, - l_int32 *pw, - l_int32 *ph, - l_int32 *pd) -{ - PROCNAME("pixcompGetDimensions"); - - if (!pixc) - return ERROR_INT("pixc not defined", procName, 1); - if (pw) *pw = pixc->w; - if (ph) *ph = pixc->h; - if (pd) *pd = pixc->d; - return 0; -} - - -/*! - * \brief pixcompGetParameters() - * - * \param[in] pixc - * \param[out] pxres, pyres, pcomptype, pcmapflag [optional] - * \return 0 if OK, 1 on error - */ -l_ok -pixcompGetParameters(PIXC *pixc, - l_int32 *pxres, - l_int32 *pyres, - l_int32 *pcomptype, - l_int32 *pcmapflag) -{ - PROCNAME("pixcompGetParameters"); - - if (!pixc) - return ERROR_INT("pixc not defined", procName, 1); - if (pxres) *pxres = pixc->xres; - if (pyres) *pyres = pixc->yres; - if (pcomptype) *pcomptype = pixc->comptype; - if (pcmapflag) *pcmapflag = pixc->cmapflag; - return 0; -} - - -/*---------------------------------------------------------------------* - * Pixcomp compression selection * - *---------------------------------------------------------------------*/ -/*! - * \brief pixcompDetermineFormat() - * - * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG - * \param[in] d pix depth - * \param[in] cmapflag 1 if pix to be compressed as a colormap; 0 otherwise - * \param[out] pformat IFF_TIFF, IFF_PNG or IFF_JFIF_JPEG - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) This determines the best format for a pix, given both - * the request (%comptype) and the image characteristics. - * (2) If %comptype == IFF_DEFAULT, this does not necessarily result - * in png encoding. Instead, it returns one of the three formats - * that is both valid and most likely to give best compression. - * (3) If %d == 8 with no colormap and: - * * you wish to compress with png, use %comptype == IFF_PNG - * * you wish to compress with jpeg, use either - * %comptype == IFF_JFIF_JPEG or %comptype == IFF_DEFAULT. - * (4) If the pix cannot be compressed by the input value of - * %comptype, this selects IFF_PNG, which can compress all pix. - *- */ -l_ok -pixcompDetermineFormat(l_int32 comptype, - l_int32 d, - l_int32 cmapflag, - l_int32 *pformat) -{ - - PROCNAME("pixcompDetermineFormat"); - - if (!pformat) - return ERROR_INT("&format not defined", procName, 1); - *pformat = IFF_PNG; /* init value and default */ - if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && - comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) - return ERROR_INT("invalid comptype", procName, 1); - - if (comptype == IFF_DEFAULT) { - if (d == 1) - *pformat = IFF_TIFF_G4; - else if (d == 16) - *pformat = IFF_PNG; - else if (d >= 8 && !cmapflag) - *pformat = IFF_JFIF_JPEG; - } else if (comptype == IFF_TIFF_G4 && d == 1) { - *pformat = IFF_TIFF_G4; - } else if (comptype == IFF_JFIF_JPEG && d >= 8 && !cmapflag) { - *pformat = IFF_JFIF_JPEG; - } - - return 0; -} - - -/*---------------------------------------------------------------------* - * Pixcomp conversion to Pix * - *---------------------------------------------------------------------*/ -/*! - * \brief pixCreateFromPixcomp() - * - * \param[in] pixc - * \return pix, or NULL on error - */ -PIX * -pixCreateFromPixcomp(PIXC *pixc) -{ -l_int32 w, h, d, cmapinpix, format; -PIX *pix; - - PROCNAME("pixCreateFromPixcomp"); - - if (!pixc) - return (PIX *)ERROR_PTR("pixc not defined", procName, NULL); - - if ((pix = pixReadMem(pixc->data, pixc->size)) == NULL) - return (PIX *)ERROR_PTR("pix not read", procName, NULL); - pixSetResolution(pix, pixc->xres, pixc->yres); - if (pixc->text) - pixSetText(pix, pixc->text); - - /* Check fields for consistency */ - pixGetDimensions(pix, &w, &h, &d); - if (pixc->w != w) { - L_INFO("pix width %d != pixc width %d\n", procName, w, pixc->w); - L_ERROR("pix width %d != pixc width\n", procName, w); - } - if (pixc->h != h) - L_ERROR("pix height %d != pixc height\n", procName, h); - if (pixc->d != d) { - if (pixc->d == 16) /* we strip 16 --> 8 bpp by default */ - L_WARNING("pix depth %d != pixc depth 16\n", procName, d); - else - L_ERROR("pix depth %d != pixc depth\n", procName, d); - } - cmapinpix = (pixGetColormap(pix) != NULL); - if ((cmapinpix && !pixc->cmapflag) || (!cmapinpix && pixc->cmapflag)) - L_ERROR("pix cmap flag inconsistent\n", procName); - format = pixGetInputFormat(pix); - if (format != pixc->comptype) { - L_ERROR("pix comptype %d not equal to pixc comptype\n", - procName, format); - } - - return pix; -} - - -/*---------------------------------------------------------------------* - * Pixacomp creation and destruction * - *---------------------------------------------------------------------*/ -/*! - * \brief pixacompCreate() - * - * \param[in] n initial number of ptrs - * \return pixac, or NULL on error - */ -PIXAC * -pixacompCreate(l_int32 n) -{ -PIXAC *pixac; - - PROCNAME("pixacompCreate"); - - if (n <= 0 || n > MaxPtrArraySize) - n = InitialPtrArraySize; - - pixac = (PIXAC *)LEPT_CALLOC(1, sizeof(PIXAC)); - pixac->n = 0; - pixac->nalloc = n; - pixac->offset = 0; - if ((pixac->pixc = (PIXC **)LEPT_CALLOC(n, sizeof(PIXC *))) == NULL) { - pixacompDestroy(&pixac); - return (PIXAC *)ERROR_PTR("pixc ptrs not made", procName, NULL); - } - if ((pixac->boxa = boxaCreate(n)) == NULL) { - pixacompDestroy(&pixac); - return (PIXAC *)ERROR_PTR("boxa not made", procName, NULL); - } - - return pixac; -} - - -/*! - * \brief pixacompCreateWithInit() - * - * \param[in] n initial number of ptrs - * \param[in] offset difference: accessor index - pixacomp array index - * \param[in] pix [optional] initialize each ptr in pixacomp - * to this pix; can be NULL - * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG - * \return pixac, or NULL on error - * - *- * Notes: - * (1) Initializes a pixacomp to be fully populated with %pix, - * compressed using %comptype. If %pix == NULL, %comptype - * is ignored. - * (2) Typically, the array is initialized with a tiny pix. - * This is most easily done by setting %pix == NULL, causing - * initialization of each array element with a tiny placeholder - * pix (w = h = d = 1), using comptype = IFF_TIFF_G4 . - * (3) Example usage: - * // Generate pixacomp for pages 30 - 49. This has an array - * // size of 20 and the page number offset is 30. - * PixaComp *pixac = pixacompCreateWithInit(20, 30, NULL, - * IFF_TIFF_G4); - * // Now insert png-compressed images into the initialized array - * for (pageno = 30; pageno < 50; pageno++) { - * Pix *pixt = ... // derived from image[pageno] - * if (pixt) - * pixacompReplacePix(pixac, pageno, pixt, IFF_PNG); - * pixDestroy(&pixt); - * } - * The result is a pixac with 20 compressed strings, and with - * selected pixt replacing the placeholders. - * To extract the image for page 38, which is decompressed - * from element 8 in the array, use: - * pixt = pixacompGetPix(pixac, 38); - *- */ -PIXAC * -pixacompCreateWithInit(l_int32 n, - l_int32 offset, - PIX *pix, - l_int32 comptype) -{ -l_int32 i; -PIX *pixt; -PIXC *pixc; -PIXAC *pixac; - - PROCNAME("pixacompCreateWithInit"); - - if (n <= 0 || n > MaxPtrArraySize) - return (PIXAC *)ERROR_PTR("n out of valid bounds", procName, NULL); - if (pix) { - if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && - comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) - return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL); - } else { - comptype = IFF_TIFF_G4; - } - if (offset < 0) { - L_WARNING("offset < 0; setting to 0\n", procName); - offset = 0; - } - - if ((pixac = pixacompCreate(n)) == NULL) - return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL); - pixacompSetOffset(pixac, offset); - if (pix) - pixt = pixClone(pix); - else - pixt = pixCreate(1, 1, 1); - for (i = 0; i < n; i++) { - pixc = pixcompCreateFromPix(pixt, comptype); - pixacompAddPixcomp(pixac, pixc, L_INSERT); - } - pixDestroy(&pixt); - - return pixac; -} - - -/*! - * \brief pixacompCreateFromPixa() - * - * \param[in] pixa - * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG - * \param[in] accesstype L_COPY, L_CLONE, L_COPY_CLONE - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If %format == IFF_DEFAULT, the conversion format for each - * image is chosen automatically. Otherwise, we use the - * specified format unless it can't be done (e.g., jpeg - * for a 1, 2 or 4 bpp pix, or a pix with a colormap), - * in which case we use the default (assumed best) compression. - * (2) %accesstype is used to extract a boxa from %pixa. - * (3) To compress jpeg with a quality other than the default (75), use - * l_jpegSetQuality() - *- */ -PIXAC * -pixacompCreateFromPixa(PIXA *pixa, - l_int32 comptype, - l_int32 accesstype) -{ -l_int32 i, n; -BOXA *boxa; -PIX *pix; -PIXAC *pixac; - - PROCNAME("pixacompCreateFromPixa"); - - if (!pixa) - return (PIXAC *)ERROR_PTR("pixa not defined", procName, NULL); - if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && - comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) - return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL); - if (accesstype != L_COPY && accesstype != L_CLONE && - accesstype != L_COPY_CLONE) - return (PIXAC *)ERROR_PTR("invalid accesstype", procName, NULL); - - n = pixaGetCount(pixa); - if ((pixac = pixacompCreate(n)) == NULL) - return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL); - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixa, i, L_CLONE); - pixacompAddPix(pixac, pix, comptype); - pixDestroy(&pix); - } - if ((boxa = pixaGetBoxa(pixa, accesstype)) != NULL) { - boxaDestroy(&pixac->boxa); - pixac->boxa = boxa; - } - - return pixac; -} - - -/*! - * \brief pixacompCreateFromFiles() - * - * \param[in] dirname - * \param[in] substr [optional] substring filter on filenames; can be null - * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG - * \return pixac, or NULL on error - * - *- * Notes: - * (1) %dirname is the full path for the directory. - * (2) %substr is the part of the file name (excluding - * the directory) that is to be matched. All matching - * filenames are read into the Pixa. If substr is NULL, - * all filenames are read into the Pixa. - * (3) Use %comptype == IFF_DEFAULT to have the compression - * type automatically determined for each file. - * (4) If the comptype is invalid for a file, the default will - * be substituted. - *- */ -PIXAC * -pixacompCreateFromFiles(const char *dirname, - const char *substr, - l_int32 comptype) -{ -PIXAC *pixac; -SARRAY *sa; - - PROCNAME("pixacompCreateFromFiles"); - - if (!dirname) - return (PIXAC *)ERROR_PTR("dirname not defined", procName, NULL); - if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && - comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) - return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL); - - if ((sa = getSortedPathnamesInDirectory(dirname, substr, 0, 0)) == NULL) - return (PIXAC *)ERROR_PTR("sa not made", procName, NULL); - pixac = pixacompCreateFromSA(sa, comptype); - sarrayDestroy(&sa); - return pixac; -} - - -/*! - * \brief pixacompCreateFromSA() - * - * \param[in] sa full pathnames for all files - * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG - * \return pixac, or NULL on error - * - *- * Notes: - * (1) Use %comptype == IFF_DEFAULT to have the compression - * type automatically determined for each file. - * (2) If the comptype is invalid for a file, the default will - * be substituted. - *- */ -PIXAC * -pixacompCreateFromSA(SARRAY *sa, - l_int32 comptype) -{ -char *str; -l_int32 i, n; -PIXC *pixc; -PIXAC *pixac; - - PROCNAME("pixacompCreateFromSA"); - - if (!sa) - return (PIXAC *)ERROR_PTR("sarray not defined", procName, NULL); - if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && - comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) - return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL); - - n = sarrayGetCount(sa); - pixac = pixacompCreate(n); - for (i = 0; i < n; i++) { - str = sarrayGetString(sa, i, L_NOCOPY); - if ((pixc = pixcompCreateFromFile(str, comptype)) == NULL) { - L_ERROR("pixc not read from file: %s\n", procName, str); - continue; - } - pixacompAddPixcomp(pixac, pixc, L_INSERT); - } - return pixac; -} - - -/*! - * \brief pixacompDestroy() - * - * \param[in,out] ppixac use ptr address so it will be nulled - * \return void - * - *- * Notes: - * (1) Always nulls the input ptr. - *- */ -void -pixacompDestroy(PIXAC **ppixac) -{ -l_int32 i; -PIXAC *pixac; - - PROCNAME("pixacompDestroy"); - - if (ppixac == NULL) { - L_WARNING("ptr address is NULL!\n", procName); - return; - } - - if ((pixac = *ppixac) == NULL) - return; - - for (i = 0; i < pixac->n; i++) - pixcompDestroy(&pixac->pixc[i]); - LEPT_FREE(pixac->pixc); - boxaDestroy(&pixac->boxa); - LEPT_FREE(pixac); - - *ppixac = NULL; - return; -} - - -/*---------------------------------------------------------------------* - * Pixacomp addition * - *---------------------------------------------------------------------*/ -/*! - * \brief pixacompAddPix() - * - * \param[in] pixac - * \param[in] pix to be added - * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) The array is filled up to the (n-1)-th element, and this - * converts the input pix to a pixc and adds it at - * the n-th position. - * (2) The pixc produced from the pix is owned by the pixac. - * The input pix is not affected. - *- */ -l_ok -pixacompAddPix(PIXAC *pixac, - PIX *pix, - l_int32 comptype) -{ -l_int32 cmapflag, format; -PIXC *pixc; - - PROCNAME("pixacompAddPix"); - - if (!pixac) - return ERROR_INT("pixac not defined", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && - comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) - return ERROR_INT("invalid format", procName, 1); - - cmapflag = pixGetColormap(pix) ? 1 : 0; - pixcompDetermineFormat(comptype, pixGetDepth(pix), cmapflag, &format); - if ((pixc = pixcompCreateFromPix(pix, format)) == NULL) - return ERROR_INT("pixc not made", procName, 1); - pixacompAddPixcomp(pixac, pixc, L_INSERT); - return 0; -} - - -/*! - * \brief pixacompAddPixcomp() - * - * \param[in] pixac - * \param[in] pixc to be added by insertion - * \param[in] copyflag L_INSERT, L_COPY - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) Anything added to a pixac is owned by the pixac. - * So do not L_INSERT a pixc that is owned by another pixac, - * or destroy a pixc that has been L_INSERTed. - *- */ -l_ok -pixacompAddPixcomp(PIXAC *pixac, - PIXC *pixc, - l_int32 copyflag) -{ -l_int32 n; - - PROCNAME("pixacompAddPixcomp"); - - if (!pixac) - return ERROR_INT("pixac not defined", procName, 1); - if (!pixc) - return ERROR_INT("pixc not defined", procName, 1); - if (copyflag != L_INSERT && copyflag != L_COPY) - return ERROR_INT("invalid copyflag", procName, 1); - - n = pixac->n; - if (n >= pixac->nalloc) - pixacompExtendArray(pixac); - if (copyflag == L_INSERT) - pixac->pixc[n] = pixc; - else /* L_COPY */ - pixac->pixc[n] = pixcompCopy(pixc); - pixac->n++; - - return 0; -} - - -/*! - * \brief pixacompExtendArray() - * - * \param[in] pixac - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) We extend the boxa array simultaneously. This is - * necessary in case we are NOT adding boxes simultaneously - * with adding pixc. We always want the sizes of the - * pixac and boxa ptr arrays to be equal. - *- */ -static l_int32 -pixacompExtendArray(PIXAC *pixac) -{ - PROCNAME("pixacompExtendArray"); - - if (!pixac) - return ERROR_INT("pixac not defined", procName, 1); - - if ((pixac->pixc = (PIXC **)reallocNew((void **)&pixac->pixc, - sizeof(PIXC *) * pixac->nalloc, - 2 * sizeof(PIXC *) * pixac->nalloc)) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - pixac->nalloc = 2 * pixac->nalloc; - boxaExtendArray(pixac->boxa); - return 0; -} - - -/*! - * \brief pixacompReplacePix() - * - * \param[in] pixac - * \param[in] index caller's view of index within pixac; includes offset - * \param[in] pix owned by the caller - * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) The %index includes the offset, which must be subtracted - * to get the actual index into the ptr array. - * (2) The input %pix is converted to a pixc, which is then inserted - * into the pixac. - *- */ -l_ok -pixacompReplacePix(PIXAC *pixac, - l_int32 index, - PIX *pix, - l_int32 comptype) -{ -l_int32 n, aindex; -PIXC *pixc; - - PROCNAME("pixacompReplacePix"); - - if (!pixac) - return ERROR_INT("pixac not defined", procName, 1); - n = pixacompGetCount(pixac); - aindex = index - pixac->offset; - if (aindex < 0 || aindex >= n) - return ERROR_INT("array index out of bounds", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 && - comptype != IFF_PNG && comptype != IFF_JFIF_JPEG) - return ERROR_INT("invalid format", procName, 1); - - pixc = pixcompCreateFromPix(pix, comptype); - pixacompReplacePixcomp(pixac, index, pixc); - return 0; -} - - -/*! - * \brief pixacompReplacePixcomp() - * - * \param[in] pixac - * \param[in] index caller's view of index within pixac; includes offset - * \param[in] pixc to replace existing one, which is destroyed - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) The %index includes the offset, which must be subtracted - * to get the actual index into the ptr array. - * (2) The inserted %pixc is now owned by the pixac. The caller - * must not destroy it. - *- */ -l_ok -pixacompReplacePixcomp(PIXAC *pixac, - l_int32 index, - PIXC *pixc) -{ -l_int32 n, aindex; -PIXC *pixct; - - PROCNAME("pixacompReplacePixcomp"); - - if (!pixac) - return ERROR_INT("pixac not defined", procName, 1); - n = pixacompGetCount(pixac); - aindex = index - pixac->offset; - if (aindex < 0 || aindex >= n) - return ERROR_INT("array index out of bounds", procName, 1); - if (!pixc) - return ERROR_INT("pixc not defined", procName, 1); - - pixct = pixacompGetPixcomp(pixac, index, L_NOCOPY); /* use %index */ - pixcompDestroy(&pixct); - pixac->pixc[aindex] = pixc; /* replace; use array index */ - - return 0; -} - - -/*! - * \brief pixacompAddBox() - * - * \param[in] pixac - * \param[in] box - * \param[in] copyflag L_INSERT, L_COPY - * \return 0 if OK, 1 on error - */ -l_ok -pixacompAddBox(PIXAC *pixac, - BOX *box, - l_int32 copyflag) -{ - PROCNAME("pixacompAddBox"); - - if (!pixac) - return ERROR_INT("pixac not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - if (copyflag != L_INSERT && copyflag != L_COPY) - return ERROR_INT("invalid copyflag", procName, 1); - - boxaAddBox(pixac->boxa, box, copyflag); - return 0; -} - - -/*---------------------------------------------------------------------* - * Pixacomp accessors * - *---------------------------------------------------------------------*/ -/*! - * \brief pixacompGetCount() - * - * \param[in] pixac - * \return count, or 0 if no pixa - */ -l_int32 -pixacompGetCount(PIXAC *pixac) -{ - PROCNAME("pixacompGetCount"); - - if (!pixac) - return ERROR_INT("pixac not defined", procName, 0); - - return pixac->n; -} - - -/*! - * \brief pixacompGetPixcomp() - * - * \param[in] pixac - * \param[in] index caller's view of index within pixac; includes offset - * \param[in] copyflag L_NOCOPY, L_COPY - * \return pixc, or NULL on error - * - *- * Notes: - * (1) The %index includes the offset, which must be subtracted - * to get the actual index into the ptr array. - * (2) If copyflag == L_NOCOPY, the pixc is owned by %pixac; do - * not destroy. - *- */ -PIXC * -pixacompGetPixcomp(PIXAC *pixac, - l_int32 index, - l_int32 copyflag) -{ -l_int32 aindex; - - PROCNAME("pixacompGetPixcomp"); - - if (!pixac) - return (PIXC *)ERROR_PTR("pixac not defined", procName, NULL); - if (copyflag != L_NOCOPY && copyflag != L_COPY) - return (PIXC *)ERROR_PTR("invalid copyflag", procName, NULL); - aindex = index - pixac->offset; - if (aindex < 0 || aindex >= pixac->n) - return (PIXC *)ERROR_PTR("array index not valid", procName, NULL); - - if (copyflag == L_NOCOPY) - return pixac->pixc[aindex]; - else /* L_COPY */ - return pixcompCopy(pixac->pixc[aindex]); -} - - -/*! - * \brief pixacompGetPix() - * - * \param[in] pixac - * \param[in] index caller's view of index within pixac; includes offset - * \return pix, or NULL on error - * - *- * Notes: - * (1) The %index includes the offset, which must be subtracted - * to get the actual index into the ptr array. - *- */ -PIX * -pixacompGetPix(PIXAC *pixac, - l_int32 index) -{ -l_int32 aindex; -PIXC *pixc; - - PROCNAME("pixacompGetPix"); - - if (!pixac) - return (PIX *)ERROR_PTR("pixac not defined", procName, NULL); - aindex = index - pixac->offset; - if (aindex < 0 || aindex >= pixac->n) - return (PIX *)ERROR_PTR("array index not valid", procName, NULL); - - pixc = pixacompGetPixcomp(pixac, index, L_NOCOPY); - return pixCreateFromPixcomp(pixc); -} - - -/*! - * \brief pixacompGetPixDimensions() - * - * \param[in] pixac - * \param[in] index caller's view of index within pixac; - * includes offset - * \param[out] pw, ph, pd [optional] each can be null - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The %index includes the offset, which must be subtracted - * to get the actual index into the ptr array. - *- */ -l_ok -pixacompGetPixDimensions(PIXAC *pixac, - l_int32 index, - l_int32 *pw, - l_int32 *ph, - l_int32 *pd) -{ -l_int32 aindex; -PIXC *pixc; - - PROCNAME("pixacompGetPixDimensions"); - - if (!pixac) - return ERROR_INT("pixac not defined", procName, 1); - aindex = index - pixac->offset; - if (aindex < 0 || aindex >= pixac->n) - return ERROR_INT("array index not valid", procName, 1); - - if ((pixc = pixac->pixc[aindex]) == NULL) - return ERROR_INT("pixc not found!", procName, 1); - pixcompGetDimensions(pixc, pw, ph, pd); - return 0; -} - - -/*! - * \brief pixacompGetBoxa() - * - * \param[in] pixac - * \param[in] accesstype L_COPY, L_CLONE, L_COPY_CLONE - * \return boxa, or NULL on error - */ -BOXA * -pixacompGetBoxa(PIXAC *pixac, - l_int32 accesstype) -{ - PROCNAME("pixacompGetBoxa"); - - if (!pixac) - return (BOXA *)ERROR_PTR("pixac not defined", procName, NULL); - if (!pixac->boxa) - return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL); - if (accesstype != L_COPY && accesstype != L_CLONE && - accesstype != L_COPY_CLONE) - return (BOXA *)ERROR_PTR("invalid accesstype", procName, NULL); - - return boxaCopy(pixac->boxa, accesstype); -} - - -/*! - * \brief pixacompGetBoxaCount() - * - * \param[in] pixac - * \return count, or 0 on error - */ -l_int32 -pixacompGetBoxaCount(PIXAC *pixac) -{ - PROCNAME("pixacompGetBoxaCount"); - - if (!pixac) - return ERROR_INT("pixac not defined", procName, 0); - - return boxaGetCount(pixac->boxa); -} - - -/*! - * \brief pixacompGetBox() - * - * \param[in] pixac - * \param[in] index caller's view of index within pixac; - * includes offset - * \param[in] accesstype L_COPY or L_CLONE - * \return box if null, not automatically an error, or NULL on error - * - *- * Notes: - * (1) The %index includes the offset, which must be subtracted - * to get the actual index into the ptr array. - * (2) There is always a boxa with a pixac, and it is initialized so - * that each box ptr is NULL. - * (3) In general, we expect that there is either a box associated - * with each pixc, or no boxes at all in the boxa. - * (4) Having no boxes is thus not an automatic error. Whether it - * is an actual error is determined by the calling program. - * If the caller expects to get a box, it is an error; see, e.g., - * pixacGetBoxGeometry(). - *- */ -BOX * -pixacompGetBox(PIXAC *pixac, - l_int32 index, - l_int32 accesstype) -{ -l_int32 aindex; -BOX *box; - - PROCNAME("pixacompGetBox"); - - if (!pixac) - return (BOX *)ERROR_PTR("pixac not defined", procName, NULL); - if (!pixac->boxa) - return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); - aindex = index - pixac->offset; - if (aindex < 0 || aindex >= pixac->boxa->n) - return (BOX *)ERROR_PTR("array index not valid", procName, NULL); - if (accesstype != L_COPY && accesstype != L_CLONE) - return (BOX *)ERROR_PTR("invalid accesstype", procName, NULL); - - box = pixac->boxa->box[aindex]; - if (box) { - if (accesstype == L_COPY) - return boxCopy(box); - else /* accesstype == L_CLONE */ - return boxClone(box); - } else { - return NULL; - } -} - - -/*! - * \brief pixacompGetBoxGeometry() - * - * \param[in] pixac - * \param[in] index caller's view of index within pixac; - * includes offset - * \param[out] px, py, pw, ph [optional] each can be null - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The %index includes the offset, which must be subtracted - * to get the actual index into the ptr array. - *- */ -l_ok -pixacompGetBoxGeometry(PIXAC *pixac, - l_int32 index, - l_int32 *px, - l_int32 *py, - l_int32 *pw, - l_int32 *ph) -{ -l_int32 aindex; -BOX *box; - - PROCNAME("pixacompGetBoxGeometry"); - - if (!pixac) - return ERROR_INT("pixac not defined", procName, 1); - aindex = index - pixac->offset; - if (aindex < 0 || aindex >= pixac->n) - return ERROR_INT("array index not valid", procName, 1); - - if ((box = pixacompGetBox(pixac, aindex, L_CLONE)) == NULL) - return ERROR_INT("box not found!", procName, 1); - boxGetGeometry(box, px, py, pw, ph); - boxDestroy(&box); - return 0; -} - - -/*! - * \brief pixacompGetOffset() - * - * \param[in] pixac - * \return offset, or 0 on error - * - *- * Notes: - * (1) The offset is the difference between the caller's view of - * the index into the array and the actual array index. - * By default it is 0. - *- */ -l_int32 -pixacompGetOffset(PIXAC *pixac) -{ - PROCNAME("pixacompGetOffset"); - - if (!pixac) - return ERROR_INT("pixac not defined", procName, 0); - return pixac->offset; -} - - -/*! - * \brief pixacompSetOffset() - * - * \param[in] pixac - * \param[in] offset non-negative - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The offset is the difference between the caller's view of - * the index into the array and the actual array index. - * By default it is 0. - *- */ -l_ok -pixacompSetOffset(PIXAC *pixac, - l_int32 offset) -{ - PROCNAME("pixacompSetOffset"); - - if (!pixac) - return ERROR_INT("pixac not defined", procName, 1); - pixac->offset = L_MAX(0, offset); - return 0; -} - - -/*---------------------------------------------------------------------* - * Pixacomp conversion to Pixa * - *---------------------------------------------------------------------*/ -/*! - * \brief pixaCreateFromPixacomp() - * - * \param[in] pixac - * \param[in] accesstype L_COPY, L_CLONE, L_COPY_CLONE; for boxa - * \return pixa if OK, or NULL on error - * - *- * Notes: - * (1) Because the pixa has no notion of offset, the offset must - * be set to 0 before the conversion, so that pixacompGetPix() - * fetches all the pixcomps. It is reset at the end. - *- */ -PIXA * -pixaCreateFromPixacomp(PIXAC *pixac, - l_int32 accesstype) -{ -l_int32 i, n, offset; -PIX *pix; -PIXA *pixa; - - PROCNAME("pixaCreateFromPixacomp"); - - if (!pixac) - return (PIXA *)ERROR_PTR("pixac not defined", procName, NULL); - if (accesstype != L_COPY && accesstype != L_CLONE && - accesstype != L_COPY_CLONE) - return (PIXA *)ERROR_PTR("invalid accesstype", procName, NULL); - - n = pixacompGetCount(pixac); - offset = pixacompGetOffset(pixac); - pixacompSetOffset(pixac, 0); - if ((pixa = pixaCreate(n)) == NULL) - return (PIXA *)ERROR_PTR("pixa not made", procName, NULL); - for (i = 0; i < n; i++) { - if ((pix = pixacompGetPix(pixac, i)) == NULL) { - L_WARNING("pix %d not made\n", procName, i); - continue; - } - pixaAddPix(pixa, pix, L_INSERT); - } - if (pixa->boxa) { - boxaDestroy(&pixa->boxa); - pixa->boxa = pixacompGetBoxa(pixac, accesstype); - } - pixacompSetOffset(pixac, offset); - - return pixa; -} - - -/*---------------------------------------------------------------------* - * Combining pixacomp - *---------------------------------------------------------------------*/ -/*! - * \brief pixacompJoin() - * - * \param[in] pixacd dest pixac; add to this one - * \param[in] pixacs [optional] source pixac; add from this one - * \param[in] istart starting index in pixacs - * \param[in] iend ending index in pixacs; use -1 to cat all - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This appends a clone of each indicated pixc in pixcas to pixcad - * (2) istart < 0 is taken to mean 'read from the start' (istart = 0) - * (3) iend < 0 means 'read to the end' - * (4) If pixacs is NULL or contains no pixc, this is a no-op. - *- */ -l_ok -pixacompJoin(PIXAC *pixacd, - PIXAC *pixacs, - l_int32 istart, - l_int32 iend) -{ -l_int32 i, n, nb; -BOXA *boxas, *boxad; -PIXC *pixc; - - PROCNAME("pixacompJoin"); - - if (!pixacd) - return ERROR_INT("pixacd not defined", procName, 1); - if (!pixacs || ((n = pixacompGetCount(pixacs)) == 0)) - return 0; - - if (istart < 0) - istart = 0; - if (iend < 0 || iend >= n) - iend = n - 1; - if (istart > iend) - return ERROR_INT("istart > iend; nothing to add", procName, 1); - - for (i = istart; i <= iend; i++) { - pixc = pixacompGetPixcomp(pixacs, i, L_NOCOPY); - pixacompAddPixcomp(pixacd, pixc, L_COPY); - } - - boxas = pixacompGetBoxa(pixacs, L_CLONE); - boxad = pixacompGetBoxa(pixacd, L_CLONE); - nb = pixacompGetBoxaCount(pixacs); - iend = L_MIN(iend, nb - 1); - boxaJoin(boxad, boxas, istart, iend); - boxaDestroy(&boxas); /* just the clones */ - boxaDestroy(&boxad); /* ditto */ - return 0; -} - - -/*! - * \brief pixacompInterleave() - * - * \param[in] pixac1 first src pixac - * \param[in] pixac2 second src pixac - * \return pixacd interleaved from sources, or NULL on error. - * - *- * Notes: - * (1) If the two pixac have different sizes, a warning is issued, - * and the number of pairs returned is the minimum size. - *- */ -PIXAC * -pixacompInterleave(PIXAC *pixac1, - PIXAC *pixac2) -{ -l_int32 i, n1, n2, n, nb1, nb2; -BOX *box; -PIXC *pixc1, *pixc2; -PIXAC *pixacd; - - PROCNAME("pixacompInterleave"); - - if (!pixac1) - return (PIXAC *)ERROR_PTR("pixac1 not defined", procName, NULL); - if (!pixac2) - return (PIXAC *)ERROR_PTR("pixac2 not defined", procName, NULL); - n1 = pixacompGetCount(pixac1); - n2 = pixacompGetCount(pixac2); - n = L_MIN(n1, n2); - if (n == 0) - return (PIXAC *)ERROR_PTR("at least one input pixac is empty", - procName, NULL); - if (n1 != n2) - L_WARNING("counts differ: %d != %d\n", procName, n1, n2); - - pixacd = pixacompCreate(2 * n); - nb1 = pixacompGetBoxaCount(pixac1); - nb2 = pixacompGetBoxaCount(pixac2); - for (i = 0; i < n; i++) { - pixc1 = pixacompGetPixcomp(pixac1, i, L_COPY); - pixacompAddPixcomp(pixacd, pixc1, L_INSERT); - if (i < nb1) { - box = pixacompGetBox(pixac1, i, L_COPY); - pixacompAddBox(pixacd, box, L_INSERT); - } - pixc2 = pixacompGetPixcomp(pixac2, i, L_COPY); - pixacompAddPixcomp(pixacd, pixc2, L_INSERT); - if (i < nb2) { - box = pixacompGetBox(pixac2, i, L_COPY); - pixacompAddBox(pixacd, box, L_INSERT); - } - } - - return pixacd; -} - - -/*---------------------------------------------------------------------* - * Pixacomp serialized I/O * - *---------------------------------------------------------------------*/ -/*! - * \brief pixacompRead() - * - * \param[in] filename - * \return pixac, or NULL on error - * - *- * Notes: - * (1) Unlike the situation with serialized Pixa, where the image - * data is stored in png format, the Pixacomp image data - * can be stored in tiffg4, png and jpg formats. - *- */ -PIXAC * -pixacompRead(const char *filename) -{ -FILE *fp; -PIXAC *pixac; - - PROCNAME("pixacompRead"); - - if (!filename) - return (PIXAC *)ERROR_PTR("filename not defined", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (PIXAC *)ERROR_PTR("stream not opened", procName, NULL); - pixac = pixacompReadStream(fp); - fclose(fp); - if (!pixac) - return (PIXAC *)ERROR_PTR("pixac not read", procName, NULL); - return pixac; -} - - -/*! - * \brief pixacompReadStream() - * - * \param[in] fp file stream - * \return pixac, or NULL on error - */ -PIXAC * -pixacompReadStream(FILE *fp) -{ -char buf[256]; -l_uint8 *data; -l_int32 n, offset, i, w, h, d, ignore; -l_int32 comptype, cmapflag, version, xres, yres; -size_t size; -BOXA *boxa; -PIXC *pixc; -PIXAC *pixac; - - PROCNAME("pixacompReadStream"); - - if (!fp) - return (PIXAC *)ERROR_PTR("stream not defined", procName, NULL); - - if (fscanf(fp, "\nPixacomp Version %d\n", &version) != 1) - return (PIXAC *)ERROR_PTR("not a pixacomp file", procName, NULL); - if (version != PIXACOMP_VERSION_NUMBER) - return (PIXAC *)ERROR_PTR("invalid pixacomp version", procName, NULL); - if (fscanf(fp, "Number of pixcomp = %d\n", &n) != 1) - return (PIXAC *)ERROR_PTR("not a pixacomp file", procName, NULL); - if (fscanf(fp, "Offset of index into array = %d", &offset) != 1) - return (PIXAC *)ERROR_PTR("offset not read", procName, NULL); - - if ((pixac = pixacompCreate(n)) == NULL) - return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL); - if ((boxa = boxaReadStream(fp)) == NULL) { - pixacompDestroy(&pixac); - return (PIXAC *)ERROR_PTR("boxa not made", procName, NULL); - } - boxaDestroy(&pixac->boxa); /* empty */ - pixac->boxa = boxa; - pixacompSetOffset(pixac, offset); - - for (i = 0; i < n; i++) { - if (fscanf(fp, "\nPixcomp[%d]: w = %d, h = %d, d = %d\n", - &ignore, &w, &h, &d) != 4) { - pixacompDestroy(&pixac); - return (PIXAC *)ERROR_PTR("dimension reading", procName, NULL); - } - if (fscanf(fp, " comptype = %d, size = %zu, cmapflag = %d\n", - &comptype, &size, &cmapflag) != 3) { - pixacompDestroy(&pixac); - return (PIXAC *)ERROR_PTR("comptype/size reading", procName, NULL); - } - if (size > MaxDataSize) { - pixacompDestroy(&pixac); - L_ERROR("data size = %zu is too big", procName, size); - return NULL; - } - - /* Use fgets() and sscanf(); not fscanf(), for the last - * bit of header data before the binary data. The reason is - * that fscanf throws away white space, and if the binary data - * happens to begin with ascii character(s) that are white - * space, it will swallow them and all will be lost! */ - if (fgets(buf, sizeof(buf), fp) == NULL) { - pixacompDestroy(&pixac); - return (PIXAC *)ERROR_PTR("fgets read fail", procName, NULL); - } - if (sscanf(buf, " xres = %d, yres = %d\n", &xres, &yres) != 2) { - pixacompDestroy(&pixac); - return (PIXAC *)ERROR_PTR("read fail for res", procName, NULL); - } - if ((data = (l_uint8 *)LEPT_CALLOC(1, size)) == NULL) { - pixacompDestroy(&pixac); - return (PIXAC *)ERROR_PTR("calloc fail for data", procName, NULL); - } - if (fread(data, 1, size, fp) != size) { - pixacompDestroy(&pixac); - LEPT_FREE(data); - return (PIXAC *)ERROR_PTR("error reading data", procName, NULL); - } - fgetc(fp); /* swallow the ending nl */ - pixc = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC)); - pixc->w = w; - pixc->h = h; - pixc->d = d; - pixc->xres = xres; - pixc->yres = yres; - pixc->comptype = comptype; - pixc->cmapflag = cmapflag; - pixc->data = data; - pixc->size = size; - pixacompAddPixcomp(pixac, pixc, L_INSERT); - } - return pixac; -} - - -/*! - * \brief pixacompReadMem() - * - * \param[in] data in pixacomp format - * \param[in] size of data - * \return pixac, or NULL on error - * - *- * Notes: - * (1) Deseralizes a buffer of pixacomp data into a pixac in memory. - *- */ -PIXAC * -pixacompReadMem(const l_uint8 *data, - size_t size) -{ -FILE *fp; -PIXAC *pixac; - - PROCNAME("pixacompReadMem"); - - if (!data) - return (PIXAC *)ERROR_PTR("data not defined", procName, NULL); - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return (PIXAC *)ERROR_PTR("stream not opened", procName, NULL); - - pixac = pixacompReadStream(fp); - fclose(fp); - if (!pixac) L_ERROR("pixac not read\n", procName); - return pixac; -} - - -/*! - * \brief pixacompWrite() - * - * \param[in] filename - * \param[in] pixac - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Unlike the situation with serialized Pixa, where the image - * data is stored in png format, the Pixacomp image data - * can be stored in tiffg4, png and jpg formats. - *- */ -l_ok -pixacompWrite(const char *filename, - PIXAC *pixac) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("pixacompWrite"); - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!pixac) - return ERROR_INT("pixacomp not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "wb")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = pixacompWriteStream(fp, pixac); - fclose(fp); - if (ret) - return ERROR_INT("pixacomp not written to stream", procName, 1); - return 0; -} - - -/*! - * \brief pixacompWriteStream() - * - * \param[in] fp file stream - * \param[in] pixac - * \return 0 if OK, 1 on error - */ -l_ok -pixacompWriteStream(FILE *fp, - PIXAC *pixac) -{ -l_int32 n, i; -PIXC *pixc; - - PROCNAME("pixacompWriteStream"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!pixac) - return ERROR_INT("pixac not defined", procName, 1); - - n = pixacompGetCount(pixac); - fprintf(fp, "\nPixacomp Version %d\n", PIXACOMP_VERSION_NUMBER); - fprintf(fp, "Number of pixcomp = %d\n", n); - fprintf(fp, "Offset of index into array = %d", pixac->offset); - boxaWriteStream(fp, pixac->boxa); - for (i = 0; i < n; i++) { - if ((pixc = pixacompGetPixcomp(pixac, pixac->offset + i, L_NOCOPY)) - == NULL) - return ERROR_INT("pixc not found", procName, 1); - fprintf(fp, "\nPixcomp[%d]: w = %d, h = %d, d = %d\n", - i, pixc->w, pixc->h, pixc->d); - fprintf(fp, " comptype = %d, size = %zu, cmapflag = %d\n", - pixc->comptype, pixc->size, pixc->cmapflag); - fprintf(fp, " xres = %d, yres = %d\n", pixc->xres, pixc->yres); - fwrite(pixc->data, 1, pixc->size, fp); - fprintf(fp, "\n"); - } - return 0; -} - - -/*! - * \brief pixacompWriteMem() - * - * \param[out] pdata serialized data of pixac - * \param[out] psize size of serialized data - * \param[in] pixac - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Serializes a pixac in memory and puts the result in a buffer. - *- */ -l_ok -pixacompWriteMem(l_uint8 **pdata, - size_t *psize, - PIXAC *pixac) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("pixacompWriteMem"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - if (!psize) - return ERROR_INT("&size not defined", procName, 1); - if (!pixac) - return ERROR_INT("&pixac not defined", procName, 1); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = pixacompWriteStream(fp, pixac); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = pixacompWriteStream(fp, pixac); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} - - -/*--------------------------------------------------------------------* - * Conversion to pdf * - *--------------------------------------------------------------------*/ -/*! - * \brief pixacompConvertToPdf() - * - * \param[in] pixac containing images all at the same resolution - * \param[in] res override the resolution of each input image, - * in ppi; 0 to respect the resolution embedded - * in the input - * \param[in] scalefactor scaling factor applied to each image; > 0.0 - * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, - * L_FLATE_ENCODE, L_JP2K_ENCODE, or - * L_DEFAULT_ENCODE for default) - * \param[in] quality used for JPEG only; 0 for default (75) - * \param[in] title [optional] pdf title - * \param[in] fileout pdf file of all images - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This follows closely the function pixaConvertToPdf() in pdfio.c. - * (2) The images are encoded with G4 if 1 bpp; JPEG if 8 bpp without - * colormap and many colors, or 32 bpp; FLATE for anything else. - * (3) The scalefactor must be > 0.0; otherwise it is set to 1.0. - * (4) Specifying one of the three encoding types for %type forces - * all images to be compressed with that type. Use 0 to have - * the type determined for each image based on depth and whether - * or not it has a colormap. - * (5) If all images are jpeg compressed, don't require scaling - * and have the same resolution, it is much faster to skip - * transcoding with pixacompFastConvertToPdfData(), and then - * write the data out to file. - *- */ -l_ok -pixacompConvertToPdf(PIXAC *pixac, - l_int32 res, - l_float32 scalefactor, - l_int32 type, - l_int32 quality, - const char *title, - const char *fileout) -{ -l_uint8 *data; -l_int32 ret; -size_t nbytes; - - PROCNAME("pixacompConvertToPdf"); - - if (!pixac) - return ERROR_INT("pixac not defined", procName, 1); - - ret = pixacompConvertToPdfData(pixac, res, scalefactor, type, quality, - title, &data, &nbytes); - if (ret) { - LEPT_FREE(data); - return ERROR_INT("conversion to pdf failed", procName, 1); - } - - ret = l_binaryWrite(fileout, "w", data, nbytes); - LEPT_FREE(data); - if (ret) - L_ERROR("pdf data not written to file\n", procName); - return ret; -} - - -/*! - * \brief pixacompConvertToPdfData() - * - * \param[in] pixac containing images all at the same resolution - * \param[in] res input resolution of all images - * \param[in] scalefactor scaling factor applied to each image; > 0.0 - * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE, - * L_FLATE_ENCODE, L_JP2K_ENCODE, or - * L_DEFAULT_ENCODE for default) - * \param[in] quality used for JPEG only; 0 for default (75) - * \param[in] title [optional] pdf title - * \param[out] pdata output pdf data (of all images - * \param[out] pnbytes size of output pdf data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) See pixacompConvertToPdf(). - *- */ -l_ok -pixacompConvertToPdfData(PIXAC *pixac, - l_int32 res, - l_float32 scalefactor, - l_int32 type, - l_int32 quality, - const char *title, - l_uint8 **pdata, - size_t *pnbytes) -{ -l_uint8 *imdata; -l_int32 i, n, ret, scaledres, pagetype; -size_t imbytes; -L_BYTEA *ba; -PIX *pixs, *pix; -L_PTRA *pa_data; - - PROCNAME("pixacompConvertToPdfData"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *pnbytes = 0; - if (!pixac) - return ERROR_INT("pixac not defined", procName, 1); - if (scalefactor <= 0.0) scalefactor = 1.0; - if (type != L_DEFAULT_ENCODE && type != L_JPEG_ENCODE && - type != L_G4_ENCODE && type != L_FLATE_ENCODE && - type != L_JP2K_ENCODE) { - L_WARNING("invalid compression type; using per-page default\n", - procName); - type = L_DEFAULT_ENCODE; - } - - /* Generate all the encoded pdf strings */ - n = pixacompGetCount(pixac); - pa_data = ptraCreate(n); - for (i = 0; i < n; i++) { - if ((pixs = - pixacompGetPix(pixac, pixacompGetOffset(pixac) + i)) == NULL) { - L_ERROR("pix[%d] not retrieved\n", procName, i); - continue; - } - if (pixGetWidth(pixs) == 1) { /* used sometimes as placeholders */ - L_INFO("placeholder image[%d] has w = 1\n", procName, i); - pixDestroy(&pixs); - continue; - } - if (scalefactor != 1.0) - pix = pixScale(pixs, scalefactor, scalefactor); - else - pix = pixClone(pixs); - pixDestroy(&pixs); - scaledres = (l_int32)(res * scalefactor); - - /* Select the encoding type */ - if (type != L_DEFAULT_ENCODE) { - pagetype = type; - } else if (selectDefaultPdfEncoding(pix, &pagetype) != 0) { - L_ERROR("encoding type selection failed for pix[%d]\n", - procName, i); - pixDestroy(&pix); - continue; - } - - ret = pixConvertToPdfData(pix, pagetype, quality, &imdata, &imbytes, - 0, 0, scaledres, title, NULL, 0); - pixDestroy(&pix); - if (ret) { - L_ERROR("pdf encoding failed for pix[%d]\n", procName, i); - continue; - } - ba = l_byteaInitFromMem(imdata, imbytes); - LEPT_FREE(imdata); - ptraAdd(pa_data, ba); - } - ptraGetActualCount(pa_data, &n); - if (n == 0) { - L_ERROR("no pdf files made\n", procName); - ptraDestroy(&pa_data, FALSE, FALSE); - return 1; - } - - /* Concatenate them */ - ret = ptraConcatenatePdfToData(pa_data, NULL, pdata, pnbytes); - - ptraGetActualCount(pa_data, &n); /* recalculate in case it changes */ - for (i = 0; i < n; i++) { - ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION); - l_byteaDestroy(&ba); - } - ptraDestroy(&pa_data, FALSE, FALSE); - return ret; -} - - -/*! - * \brief pixacompFastConvertToPdfData() - * - * \param[in] pixac containing images all at the same resolution - * \param[in] title [optional] pdf title - * \param[out] pdata output pdf data (of all images - * \param[out] pnbytes size of output pdf data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This generates the pdf without transcoding if all the - * images in %pixac are compressed with jpeg. - * Images not jpeg compressed are skipped. - * (2) It assumes all images have the same resolution, and that - * the resolution embedded in each jpeg file is correct. - *- */ -l_ok -pixacompFastConvertToPdfData(PIXAC *pixac, - const char *title, - l_uint8 **pdata, - size_t *pnbytes) -{ -l_uint8 *imdata; -l_int32 i, n, ret, comptype; -size_t imbytes; -L_BYTEA *ba; -PIXC *pixc; -L_PTRA *pa_data; - - PROCNAME("pixacompFastConvertToPdfData"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *pnbytes = 0; - if (!pixac) - return ERROR_INT("pixac not defined", procName, 1); - - /* Generate all the encoded pdf strings */ - n = pixacompGetCount(pixac); - pa_data = ptraCreate(n); - for (i = 0; i < n; i++) { - if ((pixc = pixacompGetPixcomp(pixac, i, L_NOCOPY)) == NULL) { - L_ERROR("pixc[%d] not retrieved\n", procName, i); - continue; - } - pixcompGetParameters(pixc, NULL, NULL, &comptype, NULL); - if (comptype != IFF_JFIF_JPEG) { - L_ERROR("pixc[%d] not jpeg compressed\n", procName, i); - continue; - } - ret = pixcompFastConvertToPdfData(pixc, title, &imdata, &imbytes); - if (ret) { - L_ERROR("pdf encoding failed for pixc[%d]\n", procName, i); - continue; - } - ba = l_byteaInitFromMem(imdata, imbytes); - LEPT_FREE(imdata); - ptraAdd(pa_data, ba); - } - ptraGetActualCount(pa_data, &n); - if (n == 0) { - L_ERROR("no pdf files made\n", procName); - ptraDestroy(&pa_data, FALSE, FALSE); - return 1; - } - - /* Concatenate them */ - ret = ptraConcatenatePdfToData(pa_data, NULL, pdata, pnbytes); - - /* Clean up */ - ptraGetActualCount(pa_data, &n); /* recalculate in case it changes */ - for (i = 0; i < n; i++) { - ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION); - l_byteaDestroy(&ba); - } - ptraDestroy(&pa_data, FALSE, FALSE); - return ret; -} - - -/*! - * \brief pixcompFastConvertToPdfData() - * - * \param[in] pixc containing images all at the same resolution - * \param[in] title [optional] pdf title - * \param[out] pdata output pdf data (of all images - * \param[out] pnbytes size of output pdf data - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This generates the pdf without transcoding. - * (2) It assumes all images are jpeg encoded, have the same - * resolution, and that the resolution embedded in each - * jpeg file is correct. (It is transferred to the pdf - * via the cid.) - *- */ -static l_int32 -pixcompFastConvertToPdfData(PIXC *pixc, - const char *title, - l_uint8 **pdata, - size_t *pnbytes) -{ -l_uint8 *data; -L_COMP_DATA *cid; - - PROCNAME("pixacompFastConvertToPdfData"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - *pdata = NULL; - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *pnbytes = 0; - if (!pixc) - return ERROR_INT("pixc not defined", procName, 1); - - /* Make a copy of the data */ - data = l_binaryCopy(pixc->data, pixc->size); - cid = l_generateJpegDataMem(data, pixc->size, 0); - - /* Note: cid is destroyed, along with data, by this function */ - return cidConvertToPdfData(cid, title, pdata, pnbytes); -} - - -/*--------------------------------------------------------------------* - * Output for debugging * - *--------------------------------------------------------------------*/ -/*! - * \brief pixacompWriteStreamInfo() - * - * \param[in] fp file stream - * \param[in] pixac - * \param[in] text [optional] identifying string; can be null - * \return 0 if OK, 1 on error - */ -l_ok -pixacompWriteStreamInfo(FILE *fp, - PIXAC *pixac, - const char *text) -{ -l_int32 i, n, nboxes; -PIXC *pixc; - - PROCNAME("pixacompWriteStreamInfo"); - - if (!fp) - return ERROR_INT("fp not defined", procName, 1); - if (!pixac) - return ERROR_INT("pixac not defined", procName, 1); - - if (text) - fprintf(fp, "Pixacomp Info for %s:\n", text); - else - fprintf(fp, "Pixacomp Info:\n"); - n = pixacompGetCount(pixac); - nboxes = pixacompGetBoxaCount(pixac); - fprintf(fp, "Number of pixcomp: %d\n", n); - fprintf(fp, "Size of pixcomp array alloc: %d\n", pixac->nalloc); - fprintf(fp, "Offset of index into array: %d\n", pixac->offset); - if (nboxes > 0) - fprintf(fp, "Boxa has %d boxes\n", nboxes); - else - fprintf(fp, "Boxa is empty\n"); - for (i = 0; i < n; i++) { - pixc = pixacompGetPixcomp(pixac, pixac->offset + i, L_NOCOPY); - pixcompWriteStreamInfo(fp, pixc, NULL); - } - return 0; -} - - -/*! - * \brief pixcompWriteStreamInfo() - * - * \param[in] fp file stream - * \param[in] pixc - * \param[in] text [optional] identifying string; can be null - * \return 0 if OK, 1 on error - */ -l_ok -pixcompWriteStreamInfo(FILE *fp, - PIXC *pixc, - const char *text) -{ - PROCNAME("pixcompWriteStreamInfo"); - - if (!fp) - return ERROR_INT("fp not defined", procName, 1); - if (!pixc) - return ERROR_INT("pixc not defined", procName, 1); - - if (text) - fprintf(fp, " Pixcomp Info for %s:", text); - else - fprintf(fp, " Pixcomp Info:"); - fprintf(fp, " width = %d, height = %d, depth = %d\n", - pixc->w, pixc->h, pixc->d); - fprintf(fp, " xres = %d, yres = %d, size in bytes = %zu\n", - pixc->xres, pixc->yres, pixc->size); - if (pixc->cmapflag) - fprintf(fp, " has colormap\n"); - else - fprintf(fp, " no colormap\n"); - if (pixc->comptype < NumImageFileFormatExtensions) { - fprintf(fp, " comptype = %s (%d)\n", - ImageFileFormatExtensions[pixc->comptype], pixc->comptype); - } else { - fprintf(fp, " Error!! Invalid comptype index: %d\n", pixc->comptype); - } - return 0; -} - - -/*! - * \brief pixacompDisplayTiledAndScaled() - * - * \param[in] pixac - * \param[in] outdepth output depth: 1, 8 or 32 bpp - * \param[in] tilewidth each pix is scaled to this width - * \param[in] ncols number of tiles in each row - * \param[in] background 0 for white, 1 for black; this is the color - * of the spacing between the images - * \param[in] spacing between images, and on outside - * \param[in] border width of additional black border on each image; - * use 0 for no border - * \return pix of tiled images, or NULL on error - * - *- * Notes: - * (1) This is the same function as pixaDisplayTiledAndScaled(), - * except it works on a Pixacomp instead of a Pix. It is particularly - * useful for showing the images in a Pixacomp at reduced resolution. - * (2) See pixaDisplayTiledAndScaled() for details. - *- */ -PIX * -pixacompDisplayTiledAndScaled(PIXAC *pixac, - l_int32 outdepth, - l_int32 tilewidth, - l_int32 ncols, - l_int32 background, - l_int32 spacing, - l_int32 border) -{ -PIX *pixd; -PIXA *pixa; - - PROCNAME("pixacompDisplayTiledAndScaled"); - - if (!pixac) - return (PIX *)ERROR_PTR("pixac not defined", procName, NULL); - - if ((pixa = pixaCreateFromPixacomp(pixac, L_COPY)) == NULL) - return (PIX *)ERROR_PTR("pixa not made", procName, NULL); - - pixd = pixaDisplayTiledAndScaled(pixa, outdepth, tilewidth, ncols, - background, spacing, border); - pixaDestroy(&pixa); - return pixd; -} - - -/*! - * \brief pixacompWriteFiles() - * - * \param[in] pixac - * \param[in] subdir subdirectory of /tmp - * \return 0 if OK, 1 on error - */ -l_ok -pixacompWriteFiles(PIXAC *pixac, - const char *subdir) -{ -char buf[128]; -l_int32 i, n; -PIXC *pixc; - - PROCNAME("pixacompWriteFiles"); - - if (!pixac) - return ERROR_INT("pixac not defined", procName, 1); - - if (lept_mkdir(subdir) > 0) - return ERROR_INT("invalid subdir", procName, 1); - - n = pixacompGetCount(pixac); - for (i = 0; i < n; i++) { - pixc = pixacompGetPixcomp(pixac, i, L_NOCOPY); - snprintf(buf, sizeof(buf), "/tmp/%s/%03d", subdir, i); - pixcompWriteFile(buf, pixc); - } - return 0; -} - -extern const char *ImageFileFormatExtensions[]; - -/*! - * \brief pixcompWriteFile() - * - * \param[in] rootname - * \param[in] pixc - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The compressed data is written to file, and the filename is - * generated by appending the format extension to %rootname. - *- */ -l_ok -pixcompWriteFile(const char *rootname, - PIXC *pixc) -{ -char buf[128]; - - PROCNAME("pixcompWriteFile"); - - if (!pixc) - return ERROR_INT("pixc not defined", procName, 1); - - snprintf(buf, sizeof(buf), "%s.%s", rootname, - ImageFileFormatExtensions[pixc->comptype]); - l_binaryWrite(buf, "w", pixc->data, pixc->size); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixconv.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixconv.c deleted file mode 100644 index c827aaa6..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixconv.c +++ /dev/null @@ -1,4266 +0,0 @@ -/*====================================================================* - - 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 pixconv.c - *- * - * These functions convert between images of different types - * without scaling. - * - * Conversion from 8 bpp grayscale to 1, 2, 4 and 8 bpp - * PIX *pixThreshold8() - * - * Conversion from colormap to full color or grayscale - * PIX *pixRemoveColormapGeneral() - * PIX *pixRemoveColormap() - * - * Add colormap losslessly (8 to 8) - * l_int32 pixAddGrayColormap8() - * PIX *pixAddMinimalGrayColormap8() - * - * Conversion from RGB color to grayscale - * PIX *pixConvertRGBToLuminance() - * PIX *pixConvertRGBToGray() - * PIX *pixConvertRGBToGrayFast() - * PIX *pixConvertRGBToGrayMinMax() - * PIX *pixConvertRGBToGraySatBoost() - * PIX *pixConvertRGBToGrayArb() - * PIX *pixConvertRGBToBinaryArb() - * - * Conversion from grayscale to colormap - * PIX *pixConvertGrayToColormap() -- 2, 4, 8 bpp - * PIX *pixConvertGrayToColormap8() -- 8 bpp only - * - * Colorizing conversion from grayscale to color - * PIX *pixColorizeGray() -- 8 bpp or cmapped - * - * Conversion from RGB color to colormap - * PIX *pixConvertRGBToColormap() - * - * Conversion from colormap to 1 bpp - * PIX *pixConvertCmapTo1() - * - * Quantization for relatively small number of colors in source - * l_int32 pixQuantizeIfFewColors() - * - * Conversion from 16 bpp to 8 bpp - * PIX *pixConvert16To8() - * - * Conversion from grayscale to false color - * PIX *pixConvertGrayToFalseColor() - * - * Unpacking conversion from 1 bpp to 2, 4, 8, 16 and 32 bpp - * PIX *pixUnpackBinary() - * PIX *pixConvert1To16() - * PIX *pixConvert1To32() - * - * Unpacking conversion from 1 bpp to 2 bpp - * PIX *pixConvert1To2Cmap() - * PIX *pixConvert1To2() - * - * Unpacking conversion from 1 bpp to 4 bpp - * PIX *pixConvert1To4Cmap() - * PIX *pixConvert1To4() - * - * Unpacking conversion from 1, 2 and 4 bpp to 8 bpp - * PIX *pixConvert1To8() - * PIX *pixConvert2To8() - * PIX *pixConvert4To8() - * - * Unpacking conversion from 8 bpp to 16 bpp - * PIX *pixConvert8To16() - * - * Top-level conversion to 1 bpp - * PIX *pixConvertTo1Adaptive() - * PIX *pixConvertTo1() - * PIX *pixConvertTo1BySampling() - * - * Top-level conversion to 2 bpp - * PIX *pixConvertTo2() - * PIX *pixConvert8To2() - * - * Top-level conversion to 4 bpp - * PIX *pixConvertTo4() - * PIX *pixConvert8To4() - * - * Top-level conversion to 8 bpp - * PIX *pixConvertTo8() - * PIX *pixConvertTo8BySampling() - * PIX *pixConvertTo8Colormap() - * - * Top-level conversion to 16 bpp - * PIX *pixConvertTo16() - * - * Top-level conversion to 32 bpp (RGB) - * PIX *pixConvertTo32() *** - * PIX *pixConvertTo32BySampling() *** - * PIX *pixConvert8To32() *** - * - * Top-level conversion to 8 or 32 bpp, without colormap - * PIX *pixConvertTo8Or32 - * - * Conversion between 24 bpp and 32 bpp rgb - * PIX *pixConvert24To32() - * PIX *pixConvert32To24() - * - * Conversion between 32 bpp (1 spp) and 16 or 8 bpp - * PIX *pixConvert32To16() - * PIX *pixConvert32To8() - * - * Removal of alpha component by blending with white background - * PIX *pixRemoveAlpha() - * - * Addition of alpha component to 1 bpp - * PIX *pixAddAlphaTo1bpp() - * - * Lossless depth conversion (unpacking) - * PIX *pixConvertLossless() - * - * Conversion for printing in PostScript - * PIX *pixConvertForPSWrap() - * - * Scaling conversion to subpixel RGB - * PIX *pixConvertToSubpixelRGB() - * PIX *pixConvertGrayToSubpixelRGB() - * PIX *pixConvertColorToSubpixelRGB() - * - * Setting neutral point for min/max boost conversion to gray - * void l_setNeutralBoostVal() - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include -#include "allheaders.h" - -/* ------- Set neutral point for min/max boost conversion to gray ------ */ - /* Call l_setNeutralBoostVal() to change this */ -static l_int32 var_NEUTRAL_BOOST_VAL = 180; - - -#ifndef NO_CONSOLE_IO -#define DEBUG_CONVERT_TO_COLORMAP 0 -#define DEBUG_UNROLLING 0 -#endif /* ~NO_CONSOLE_IO */ - - -/*-------------------------------------------------------------* - * Conversion from 8 bpp grayscale to 1, 2 4 and 8 bpp * - *-------------------------------------------------------------*/ -/*! - * \brief pixThreshold8() - * - * \param[in] pixs 8 bpp grayscale - * \param[in] d destination depth: 1, 2, 4 or 8 - * \param[in] nlevels number of levels to be used for colormap - * \param[in] cmapflag 1 if makes colormap; 0 otherwise - * \return pixd thresholded with standard dest thresholds, - * or NULL on error - * - * - * Notes: - * (1) This uses, by default, equally spaced "target" values - * that depend on the number of levels, with thresholds - * halfway between. For N levels, with separation (N-1)/255, - * there are N-1 fixed thresholds. - * (2) For 1 bpp destination, the number of levels can only be 2 - * and if a cmap is made, black is (0,0,0) and white - * is (255,255,255), which is opposite to the convention - * without a colormap. - * (3) For 1, 2 and 4 bpp, the nlevels arg is used if a colormap - * is made; otherwise, we take the most significant bits - * from the src that will fit in the dest. - * (4) For 8 bpp, the input pixs is quantized to nlevels. The - * dest quantized with that mapping, either through a colormap - * table or directly with 8 bit values. - * (5) Typically you should not use make a colormap for 1 bpp dest. - * (6) This is not dithering. Each pixel is treated independently. - *- */ -PIX * -pixThreshold8(PIX *pixs, - l_int32 d, - l_int32 nlevels, - l_int32 cmapflag) -{ -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixThreshold8"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (cmapflag && nlevels < 2) - return (PIX *)ERROR_PTR("nlevels must be at least 2", procName, NULL); - - switch (d) { - case 1: - pixd = pixThresholdToBinary(pixs, 128); - if (cmapflag) { - cmap = pixcmapCreateLinear(1, 2); - pixSetColormap(pixd, cmap); - } - break; - case 2: - pixd = pixThresholdTo2bpp(pixs, nlevels, cmapflag); - break; - case 4: - pixd = pixThresholdTo4bpp(pixs, nlevels, cmapflag); - break; - case 8: - pixd = pixThresholdOn8bpp(pixs, nlevels, cmapflag); - break; - default: - return (PIX *)ERROR_PTR("d must be in {1,2,4,8}", procName, NULL); - } - - if (!pixd) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyInputFormat(pixd, pixs); - return pixd; -} - - -/*-------------------------------------------------------------* - * Conversion from colormapped pix * - *-------------------------------------------------------------*/ -/*! - * \brief pixRemoveColormapGeneral() - * - * \param[in] pixs any depth, with or without colormap - * \param[in] type REMOVE_CMAP_TO_BINARY, - * REMOVE_CMAP_TO_GRAYSCALE, - * REMOVE_CMAP_TO_FULL_COLOR, - * REMOVE_CMAP_WITH_ALPHA, - * REMOVE_CMAP_BASED_ON_SRC - * \param[in] ifnocmap L_CLONE, L_COPY - * \return pixd always a new pix; without colormap, or NULL on error - * - *- * Notes: - * (1) Convenience function that allows choice between returning - * a clone or a copy if pixs does not have a colormap. - * (2) See pixRemoveColormap(). - *- */ -PIX * -pixRemoveColormapGeneral(PIX *pixs, - l_int32 type, - l_int32 ifnocmap) -{ - PROCNAME("pixRemoveColormapGeneral"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (ifnocmap != L_CLONE && ifnocmap != L_COPY) - return (PIX *)ERROR_PTR("invalid value for ifnocmap", procName, NULL); - - if (pixGetColormap(pixs)) - return pixRemoveColormap(pixs, type); - - if (ifnocmap == L_CLONE) - return pixClone(pixs); - else - return pixCopy(NULL, pixs); -} - - -/*! - * \brief pixRemoveColormap() - * - * \param[in] pixs see restrictions below - * \param[in] type REMOVE_CMAP_TO_BINARY, - * REMOVE_CMAP_TO_GRAYSCALE, - * REMOVE_CMAP_TO_FULL_COLOR, - * REMOVE_CMAP_WITH_ALPHA, - * REMOVE_CMAP_BASED_ON_SRC - * \return pixd without colormap, or NULL on error - * - *- * Notes: - * (1) If pixs does not have a colormap, a clone is returned. - * (2) Otherwise, the input pixs is restricted to 1, 2, 4 or 8 bpp. - * (3) Use REMOVE_CMAP_TO_BINARY only on 1 bpp pix. - * (4) For grayscale conversion from RGB, use a weighted average - * of RGB values, and always return an 8 bpp pix, regardless - * of whether the input pixs depth is 2, 4 or 8 bpp. - * (5) REMOVE_CMAP_TO_FULL_COLOR ignores the alpha component and - * returns a 32 bpp pix with spp == 3 and the alpha bytes are 0. - * (6) For REMOVE_CMAP_BASED_ON_SRC, if there is no color, this - * returns either a 1 bpp or 8 bpp grayscale pix. - * If there is color, this returns a 32 bpp pix, with either: - * * 3 spp, if the alpha values are all 255 (opaque), or - * * 4 spp (preserving the alpha), if any alpha values are not 255. - *- */ -PIX * -pixRemoveColormap(PIX *pixs, - l_int32 type) -{ -l_int32 sval, rval, gval, bval, val0, val1; -l_int32 i, j, k, w, h, d, wpls, wpld, ncolors, nalloc, count; -l_int32 opaque, colorfound, blackwhite; -l_int32 *rmap, *gmap, *bmap, *amap; -l_uint32 *datas, *lines, *datad, *lined, *lut, *graymap; -l_uint32 sword, dword; -PIXCMAP *cmap; -PIX *pixd; - - PROCNAME("pixRemoveColormap"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if ((cmap = pixGetColormap(pixs)) == NULL) - return pixClone(pixs); - if (type != REMOVE_CMAP_TO_BINARY && - type != REMOVE_CMAP_TO_GRAYSCALE && - type != REMOVE_CMAP_TO_FULL_COLOR && - type != REMOVE_CMAP_WITH_ALPHA && - type != REMOVE_CMAP_BASED_ON_SRC) { - L_WARNING("Invalid type; converting based on src\n", procName); - type = REMOVE_CMAP_BASED_ON_SRC; - } - pixGetDimensions(pixs, &w, &h, &d); - if (d != 1 && d != 2 && d != 4 && d != 8) - return (PIX *)ERROR_PTR("pixs must be {1,2,4,8} bpp", procName, NULL); - - ncolors = pixcmapGetCount(cmap); - nalloc = 1 << d; /* allocate for max size in case of pixel corruption */ - if (ncolors > nalloc) - return (PIX *)ERROR_PTR("too many colors for pixel depth", - procName, NULL); - - if (pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap)) - return (PIX *)ERROR_PTR("colormap arrays not made", procName, NULL); - - if (d != 1 && type == REMOVE_CMAP_TO_BINARY) { - L_WARNING("not 1 bpp; can't remove cmap to binary\n", procName); - type = REMOVE_CMAP_BASED_ON_SRC; - } - - /* Select output type depending on colormap content */ - if (type == REMOVE_CMAP_BASED_ON_SRC) { - pixcmapIsOpaque(cmap, &opaque); - pixcmapHasColor(cmap, &colorfound); - pixcmapIsBlackAndWhite(cmap, &blackwhite); - if (!opaque) { /* save the alpha */ - type = REMOVE_CMAP_WITH_ALPHA; - } else if (colorfound) { - type = REMOVE_CMAP_TO_FULL_COLOR; - } else { /* opaque and no color */ - if (d == 1 && blackwhite) /* can binarize without loss */ - type = REMOVE_CMAP_TO_BINARY; - else - type = REMOVE_CMAP_TO_GRAYSCALE; - } - } - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if (type == REMOVE_CMAP_TO_BINARY) { - if ((pixd = pixCopy(NULL, pixs)) == NULL) { - L_ERROR("pixd not made\n", procName); - goto cleanup_arrays; - } - pixcmapGetColor(cmap, 0, &rval, &gval, &bval); - val0 = rval + gval + bval; - pixcmapGetColor(cmap, 1, &rval, &gval, &bval); - val1 = rval + gval + bval; - if (val0 < val1) /* photometrically inverted from standard */ - pixInvert(pixd, pixd); - pixDestroyColormap(pixd); - } else if (type == REMOVE_CMAP_TO_GRAYSCALE) { - if ((pixd = pixCreate(w, h, 8)) == NULL) { - L_ERROR("pixd not made\n", procName); - goto cleanup_arrays; - } - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - graymap = (l_uint32 *)LEPT_CALLOC(nalloc, sizeof(l_uint32)); - for (i = 0; i < ncolors; i++) { - graymap[i] = (l_uint32)(L_RED_WEIGHT * rmap[i] + - L_GREEN_WEIGHT * gmap[i] + - L_BLUE_WEIGHT * bmap[i] + 0.5); - } - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - switch (d) /* depth test above; no default permitted */ - { - case 8: - /* Unrolled 4x */ - for (j = 0, count = 0; j + 3 < w; j += 4, count++) { - sword = lines[count]; - dword = (graymap[(sword >> 24) & 0xff] << 24) | - (graymap[(sword >> 16) & 0xff] << 16) | - (graymap[(sword >> 8) & 0xff] << 8) | - graymap[sword & 0xff]; - lined[count] = dword; - } - /* Cleanup partial word */ - for (; j < w; j++) { - sval = GET_DATA_BYTE(lines, j); - gval = graymap[sval]; - SET_DATA_BYTE(lined, j, gval); - } -#if DEBUG_UNROLLING -#define CHECK_VALUE(a, b, c) if (GET_DATA_BYTE(a, b) != c) { \ - lept_stderr("Error: mismatch at %d, %d vs %d\n", \ - j, GET_DATA_BYTE(a, b), c); } - for (j = 0; j < w; j++) { - sval = GET_DATA_BYTE(lines, j); - gval = graymap[sval]; - CHECK_VALUE(lined, j, gval); - } -#endif - break; - case 4: - /* Unrolled 8x */ - for (j = 0, count = 0; j + 7 < w; j += 8, count++) { - sword = lines[count]; - dword = (graymap[(sword >> 28) & 0xf] << 24) | - (graymap[(sword >> 24) & 0xf] << 16) | - (graymap[(sword >> 20) & 0xf] << 8) | - graymap[(sword >> 16) & 0xf]; - lined[2 * count] = dword; - dword = (graymap[(sword >> 12) & 0xf] << 24) | - (graymap[(sword >> 8) & 0xf] << 16) | - (graymap[(sword >> 4) & 0xf] << 8) | - graymap[sword & 0xf]; - lined[2 * count + 1] = dword; - } - /* Cleanup partial word */ - for (; j < w; j++) { - sval = GET_DATA_QBIT(lines, j); - gval = graymap[sval]; - SET_DATA_BYTE(lined, j, gval); - } -#if DEBUG_UNROLLING - for (j = 0; j < w; j++) { - sval = GET_DATA_QBIT(lines, j); - gval = graymap[sval]; - CHECK_VALUE(lined, j, gval); - } -#endif - break; - case 2: - /* Unrolled 16x */ - for (j = 0, count = 0; j + 15 < w; j += 16, count++) { - sword = lines[count]; - dword = (graymap[(sword >> 30) & 0x3] << 24) | - (graymap[(sword >> 28) & 0x3] << 16) | - (graymap[(sword >> 26) & 0x3] << 8) | - graymap[(sword >> 24) & 0x3]; - lined[4 * count] = dword; - dword = (graymap[(sword >> 22) & 0x3] << 24) | - (graymap[(sword >> 20) & 0x3] << 16) | - (graymap[(sword >> 18) & 0x3] << 8) | - graymap[(sword >> 16) & 0x3]; - lined[4 * count + 1] = dword; - dword = (graymap[(sword >> 14) & 0x3] << 24) | - (graymap[(sword >> 12) & 0x3] << 16) | - (graymap[(sword >> 10) & 0x3] << 8) | - graymap[(sword >> 8) & 0x3]; - lined[4 * count + 2] = dword; - dword = (graymap[(sword >> 6) & 0x3] << 24) | - (graymap[(sword >> 4) & 0x3] << 16) | - (graymap[(sword >> 2) & 0x3] << 8) | - graymap[sword & 0x3]; - lined[4 * count + 3] = dword; - } - /* Cleanup partial word */ - for (; j < w; j++) { - sval = GET_DATA_DIBIT(lines, j); - gval = graymap[sval]; - SET_DATA_BYTE(lined, j, gval); - } -#if DEBUG_UNROLLING - for (j = 0; j < w; j++) { - sval = GET_DATA_DIBIT(lines, j); - gval = graymap[sval]; - CHECK_VALUE(lined, j, gval); - } -#endif - break; - case 1: - /* Unrolled 8x */ - for (j = 0, count = 0; j + 31 < w; j += 32, count++) { - sword = lines[count]; - for (k = 0; k < 4; k++) { - /* The top byte is always the relevant one */ - dword = (graymap[(sword >> 31) & 0x1] << 24) | - (graymap[(sword >> 30) & 0x1] << 16) | - (graymap[(sword >> 29) & 0x1] << 8) | - graymap[(sword >> 28) & 0x1]; - lined[8 * count + 2 * k] = dword; - dword = (graymap[(sword >> 27) & 0x1] << 24) | - (graymap[(sword >> 26) & 0x1] << 16) | - (graymap[(sword >> 25) & 0x1] << 8) | - graymap[(sword >> 24) & 0x1]; - lined[8 * count + 2 * k + 1] = dword; - sword <<= 8; /* Move up the next byte */ - } - } - /* Cleanup partial word */ - for (; j < w; j++) { - sval = GET_DATA_BIT(lines, j); - gval = graymap[sval]; - SET_DATA_BYTE(lined, j, gval); - } -#if DEBUG_UNROLLING - for (j = 0; j < w; j++) { - sval = GET_DATA_BIT(lines, j); - gval = graymap[sval]; - CHECK_VALUE(lined, j, gval); - } -#undef CHECK_VALUE -#endif - break; - default: - return NULL; - } - } - if (graymap) - LEPT_FREE(graymap); - } else { /* type == REMOVE_CMAP_TO_FULL_COLOR or REMOVE_CMAP_WITH_ALPHA */ - if ((pixd = pixCreate(w, h, 32)) == NULL) { - L_ERROR("pixd not made\n", procName); - goto cleanup_arrays; - } - pixCopyInputFormat(pixd, pixs); - pixCopyResolution(pixd, pixs); - if (type == REMOVE_CMAP_WITH_ALPHA) - pixSetSpp(pixd, 4); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - lut = (l_uint32 *)LEPT_CALLOC(nalloc, sizeof(l_uint32)); - for (i = 0; i < ncolors; i++) { - if (type == REMOVE_CMAP_TO_FULL_COLOR) - composeRGBPixel(rmap[i], gmap[i], bmap[i], lut + i); - else /* full color plus alpha */ - composeRGBAPixel(rmap[i], gmap[i], bmap[i], amap[i], lut + i); - } - - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - if (d == 8) - sval = GET_DATA_BYTE(lines, j); - else if (d == 4) - sval = GET_DATA_QBIT(lines, j); - else if (d == 2) - sval = GET_DATA_DIBIT(lines, j); - else /* (d == 1) */ - sval = GET_DATA_BIT(lines, j); - if (sval >= ncolors) - L_WARNING("pixel value out of bounds\n", procName); - else - lined[j] = lut[sval]; - } - } - LEPT_FREE(lut); - } - -cleanup_arrays: - LEPT_FREE(rmap); - LEPT_FREE(gmap); - LEPT_FREE(bmap); - LEPT_FREE(amap); - return pixd; -} - - -/*-------------------------------------------------------------* - * Add colormap losslessly (8 to 8) * - *-------------------------------------------------------------*/ -/*! - * \brief pixAddGrayColormap8() - * - * \param[in] pixs 8 bpp - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If pixs has a colormap, this is a no-op. - *- */ -l_ok -pixAddGrayColormap8(PIX *pixs) -{ -PIXCMAP *cmap; - - PROCNAME("pixAddGrayColormap8"); - - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); - if (pixGetColormap(pixs)) - return 0; - - cmap = pixcmapCreateLinear(8, 256); - pixSetColormap(pixs, cmap); - return 0; -} - - -/*! - * \brief pixAddMinimalGrayColormap8() - * - * \param[in] pixs 8 bpp - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This generates a colormapped version of the input image - * that has the same number of colormap entries as the - * input image has unique gray levels. - *- */ -PIX * -pixAddMinimalGrayColormap8(PIX *pixs) -{ -l_int32 ncolors, w, h, i, j, wpl1, wpld, index, val; -l_int32 *inta, *revmap; -l_uint32 *data1, *datad, *line1, *lined; -PIX *pix1, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixAddMinimalGrayColormap8"); - - if (!pixs || pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - - /* Eliminate the easy cases */ - pixNumColors(pixs, 1, &ncolors); - cmap = pixGetColormap(pixs); - if (cmap) { - if (pixcmapGetCount(cmap) == ncolors) /* irreducible */ - return pixCopy(NULL, pixs); - else - pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - } else { - if (ncolors == 256) { - pix1 = pixCopy(NULL, pixs); - pixAddGrayColormap8(pix1); - return pix1; - } - pix1 = pixClone(pixs); - } - - /* Find the gray levels and make a reverse map */ - pixGetDimensions(pix1, &w, &h, NULL); - data1 = pixGetData(pix1); - wpl1 = pixGetWpl(pix1); - inta = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - for (i = 0; i < h; i++) { - line1 = data1 + i * wpl1; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(line1, j); - inta[val] = 1; - } - } - cmap = pixcmapCreate(8); - revmap = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - for (i = 0, index = 0; i < 256; i++) { - if (inta[i]) { - pixcmapAddColor(cmap, i, i, i); - revmap[i] = index++; - } - } - - /* Set all pixels in pixd to the colormap index */ - pixd = pixCreateTemplate(pix1); - pixSetColormap(pixd, cmap); - pixCopyInputFormat(pixd, pixs); - pixCopyResolution(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - line1 = data1 + i * wpl1; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(line1, j); - SET_DATA_BYTE(lined, j, revmap[val]); - } - } - - pixDestroy(&pix1); - LEPT_FREE(inta); - LEPT_FREE(revmap); - return pixd; -} - - -/*-------------------------------------------------------------* - * Conversion from RGB color to grayscale * - *-------------------------------------------------------------*/ -/*! - * \brief pixConvertRGBToLuminance() - * - * \param[in] pixs 32 bpp RGB - * \return 8 bpp pix, or NULL on error - * - *- * Notes: - * (1) Use a standard luminance conversion. - *- */ -PIX * -pixConvertRGBToLuminance(PIX *pixs) -{ - return pixConvertRGBToGray(pixs, 0.0, 0.0, 0.0); -} - - -/*! - * \brief pixConvertRGBToGray() - * - * \param[in] pixs 32 bpp RGB - * \param[in] rwt, gwt, bwt non-negative; these should add to 1.0, - * or use 0.0 for default - * \return 8 bpp pix, or NULL on error - * - *- * Notes: - * (1) Use a weighted average of the RGB values. - *- */ -PIX * -pixConvertRGBToGray(PIX *pixs, - l_float32 rwt, - l_float32 gwt, - l_float32 bwt) -{ -l_int32 i, j, w, h, wpls, wpld, val; -l_uint32 word; -l_uint32 *datas, *lines, *datad, *lined; -l_float32 sum; -PIX *pixd; - - PROCNAME("pixConvertRGBToGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (rwt < 0.0 || gwt < 0.0 || bwt < 0.0) - return (PIX *)ERROR_PTR("weights not all >= 0.0", procName, NULL); - - /* Make sure the sum of weights is 1.0; otherwise, you can get - * overflow in the gray value. */ - if (rwt == 0.0 && gwt == 0.0 && bwt == 0.0) { - rwt = L_RED_WEIGHT; - gwt = L_GREEN_WEIGHT; - bwt = L_BLUE_WEIGHT; - } - sum = rwt + gwt + bwt; - if (L_ABS(sum - 1.0) > 0.0001) { /* maintain ratios with sum == 1.0 */ - L_WARNING("weights don't sum to 1; maintaining ratios\n", procName); - rwt = rwt / sum; - gwt = gwt / sum; - bwt = bwt / sum; - } - - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if ((pixd = pixCreate(w, h, 8)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - word = *(lines + j); - val = (l_int32)(rwt * ((word >> L_RED_SHIFT) & 0xff) + - gwt * ((word >> L_GREEN_SHIFT) & 0xff) + - bwt * ((word >> L_BLUE_SHIFT) & 0xff) + 0.5); - SET_DATA_BYTE(lined, j, val); - } - } - - return pixd; -} - - -/*! - * \brief pixConvertRGBToGrayFast() - * - * \param[in] pixs 32 bpp RGB - * \return 8 bpp pix, or NULL on error - * - *- * Notes: - * (1) This function should be used if speed of conversion - * is paramount, and the green channel can be used as - * a fair representative of the RGB intensity. It is - * several times faster than pixConvertRGBToGray(). - * (2) To combine RGB to gray conversion with subsampling, - * use pixScaleRGBToGrayFast() instead. - *- */ -PIX * -pixConvertRGBToGrayFast(PIX *pixs) -{ -l_int32 i, j, w, h, wpls, wpld, val; -l_uint32 *datas, *lines, *datad, *lined; -PIX *pixd; - - PROCNAME("pixConvertRGBToGrayFast"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if ((pixd = pixCreate(w, h, 8)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++, lines++) { - val = ((*lines) >> L_GREEN_SHIFT) & 0xff; - SET_DATA_BYTE(lined, j, val); - } - } - - return pixd; -} - - -/*! - * \brief pixConvertRGBToGrayMinMax() - * - * \param[in] pixs 32 bpp RGB - * \param[in] type L_CHOOSE_MIN, L_CHOOSE_MAX, L_CHOOSE_MAXDIFF, - * L_CHOOSE_MIN_BOOST, L_CHOOSE_MAX_BOOST - * \return 8 bpp pix, or NULL on error - * - *- * Notes: - * (1) This chooses various components or combinations of them, - * from the three RGB sample values. In addition to choosing - * the min, max, and maxdiff (difference between max and min), - * this also allows boosting the min and max about a reference - * value. - * (2) The default reference value for boosting the min and max - * is 200. This can be changed with l_setNeutralBoostVal() - * (3) The result with L_CHOOSE_MAXDIFF is surprisingly sensitive - * to a jpeg compression/decompression cycle with quality = 75. - *- */ -PIX * -pixConvertRGBToGrayMinMax(PIX *pixs, - l_int32 type) -{ -l_int32 i, j, w, h, wpls, wpld, rval, gval, bval, val, minval, maxval; -l_uint32 *datas, *lines, *datad, *lined; -PIX *pixd; - - PROCNAME("pixConvertRGBToGrayMinMax"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX && - type != L_CHOOSE_MAXDIFF && type != L_CHOOSE_MIN_BOOST && - type != L_CHOOSE_MAX_BOOST) - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if ((pixd = pixCreate(w, h, 8)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - if (type == L_CHOOSE_MIN || type == L_CHOOSE_MIN_BOOST) { - val = L_MIN(rval, gval); - val = L_MIN(val, bval); - if (type == L_CHOOSE_MIN_BOOST) - val = L_MIN(255, (val * val) / var_NEUTRAL_BOOST_VAL); - } else if (type == L_CHOOSE_MAX || type == L_CHOOSE_MAX_BOOST) { - val = L_MAX(rval, gval); - val = L_MAX(val, bval); - if (type == L_CHOOSE_MAX_BOOST) - val = L_MIN(255, (val * val) / var_NEUTRAL_BOOST_VAL); - } else { /* L_CHOOSE_MAXDIFF */ - minval = L_MIN(rval, gval); - minval = L_MIN(minval, bval); - maxval = L_MAX(rval, gval); - maxval = L_MAX(maxval, bval); - val = maxval - minval; - } - SET_DATA_BYTE(lined, j, val); - } - } - - return pixd; -} - - -/*! - * \brief pixConvertRGBToGraySatBoost() - * - * \param[in] pixs 32 bpp rgb - * \param[in] refval between 1 and 255; typ. less than 128 - * \return pixd 8 bpp, or NULL on error - * - *- * Notes: - * (1) This returns the max component value, boosted by - * the saturation. The maximum boost occurs where - * the maximum component value is equal to some reference value. - * This particular weighting is due to Dany Qumsiyeh. - * (2) For gray pixels (zero saturation), this returns - * the intensity of any component. - * (3) For fully saturated pixels ('fullsat'), this rises linearly - * with the max value and has a slope equal to 255 divided - * by the reference value; for a max value greater than - * the reference value, it is clipped to 255. - * (4) For saturation values in between, the output is a linear - * combination of (2) and (3), weighted by saturation. - * It falls between these two curves, and does not exceed 255. - * (5) This can be useful for distinguishing an object that has nonzero - * saturation from a gray background. For this, the refval - * should be chosen near the expected value of the background, - * to achieve maximum saturation boost there. - *- */ -PIX * -pixConvertRGBToGraySatBoost(PIX *pixs, - l_int32 refval) -{ -l_int32 w, h, d, i, j, wplt, wpld; -l_int32 rval, gval, bval, sval, minrg, maxrg, min, max, delta; -l_int32 fullsat, newval; -l_float32 *invmax, *ratio; -l_uint32 *linet, *lined, *datat, *datad; -PIX *pixt, *pixd; - - PROCNAME("pixConvertRGBToGraySatBoost"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 32 && !pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs not cmapped or rgb", procName, NULL); - if (refval < 1 || refval > 255) - return (PIX *)ERROR_PTR("refval not in [1 ... 255]", procName, NULL); - - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); - pixd = pixCreate(w, h, 8); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - wplt = pixGetWpl(pixt); - datat = pixGetData(pixt); - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - invmax = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32)); - ratio = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32)); - for (i = 1; i < 256; i++) { /* i == 0 --> delta = sval = newval = 0 */ - invmax[i] = 1.0 / (l_float32)i; - ratio[i] = (l_float32)i / (l_float32)refval; - } - for (i = 0; i < h; i++) { - linet = datat + i * wplt; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(linet[j], &rval, &gval, &bval); - minrg = L_MIN(rval, gval); - min = L_MIN(minrg, bval); - maxrg = L_MAX(rval, gval); - max = L_MAX(maxrg, bval); - delta = max - min; - if (delta == 0) /* gray; no chroma */ - sval = 0; - else - sval = (l_int32)(255. * (l_float32)delta * invmax[max] + 0.5); - - fullsat = L_MIN(255, 255 * ratio[max]); - newval = (sval * fullsat + (255 - sval) * max) / 255; - SET_DATA_BYTE(lined, j, newval); - } - } - - pixDestroy(&pixt); - LEPT_FREE(invmax); - LEPT_FREE(ratio); - return pixd; -} - - -/*! - * \brief pixConvertRGBToGrayArb() - * - * \param[in] pixs 32 bpp RGB - * \param[in] rc, gc, bc arithmetic factors; can be negative - * \return 8 bpp pix, or NULL on error - * - *- * Notes: - * (1) This converts to gray using an arbitrary linear combination - * of the rgb color components. It differs from pixConvertToGray(), - * which uses only positive coefficients that sum to 1. - * (2) The gray output values are clipped to 0 and 255. - *- */ -PIX * -pixConvertRGBToGrayArb(PIX *pixs, - l_float32 rc, - l_float32 gc, - l_float32 bc) -{ -l_int32 i, j, w, h, wpls, wpld, rval, gval, bval, val; -l_uint32 *datas, *lines, *datad, *lined; -PIX *pixd; - - PROCNAME("pixConvertRGBToGrayArb"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (rc <= 0 && gc <= 0 && bc <= 0) - return (PIX *)ERROR_PTR("all coefficients <= 0", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if ((pixd = pixCreate(w, h, 8)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - val = (l_int32)(rc * rval + gc * gval + bc * bval); - val = L_MIN(255, L_MAX(0, val)); - SET_DATA_BYTE(lined, j, val); - } - } - - return pixd; -} - - -/*! - * \brief pixConvertRGBToBinaryArb() - * - * \param[in] pixs 32 bpp RGB - * \param[in] rc, gc, bc arithmetic factors; can be negative - * \param[in] thresh binarization threshold - * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \return 1 bpp pix, or NULL on error - * - *- * Notes: - * (1) This makes a 1 bpp mask from an RGB image, using an arbitrary - * linear combination of the rgb color components, along with - * a threshold and a selection choice of the gray value relative - * to %thresh. - *- */ -PIX * -pixConvertRGBToBinaryArb(PIX *pixs, - l_float32 rc, - l_float32 gc, - l_float32 bc, - l_int32 thresh, - l_int32 relation) -{ -l_int32 threshold; -PIX *pix1, *pix2; - - PROCNAME("pixConvertRGBToBinaryArb"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); - if (rc <= 0 && gc <= 0 && bc <= 0) - return (PIX *)ERROR_PTR("all coefficients <= 0", procName, NULL); - if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && - relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) - return (PIX *)ERROR_PTR("invalid relation", procName, NULL); - - pix1 = pixConvertRGBToGrayArb(pixs, rc, gc, bc); - threshold = (relation == L_SELECT_IF_LTE || relation == L_SELECT_IF_GT) ? - thresh : thresh + 1; - pix2 = pixThresholdToBinary(pix1, threshold); - if (relation == L_SELECT_IF_GT || relation == L_SELECT_IF_GTE) - pixInvert(pix2, pix2); - pixDestroy(&pix1); - return pix2; -} - - -/*---------------------------------------------------------------------------* - * Conversion from grayscale to colormap * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvertGrayToColormap() - * - * \param[in] pixs 2, 4 or 8 bpp grayscale - * \return pixd 2, 4 or 8 bpp with colormap, or NULL on error - * - *- * Notes: - * (1) This is a simple interface for adding a colormap to a - * 2, 4 or 8 bpp grayscale image without causing any - * quantization. There is some similarity to operations - * in grayquant.c, such as pixThresholdOn8bpp(), where - * the emphasis is on quantization with an arbitrary number - * of levels, and a colormap is an option. - * (2) Returns a copy if pixs already has a colormap. - * (3) For 8 bpp src, this is a lossless transformation. - * (4) For 2 and 4 bpp src, this generates a colormap that - * assumes full coverage of the gray space, with equally spaced - * levels: 4 levels for d = 2 and 16 levels for d = 4. - * (5) In all cases, the depth of the dest is the same as the src. - *- */ -PIX * -pixConvertGrayToColormap(PIX *pixs) -{ -l_int32 d; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixConvertGrayToColormap"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - if (d != 2 && d != 4 && d != 8) - return (PIX *)ERROR_PTR("pixs not 2, 4 or 8 bpp", procName, NULL); - - if (pixGetColormap(pixs)) { - L_INFO("pixs already has a colormap\n", procName); - return pixCopy(NULL, pixs); - } - - if (d == 8) /* lossless conversion */ - return pixConvertGrayToColormap8(pixs, 2); - - /* Build a cmap with equally spaced target values over the - * full 8 bpp range. */ - pixd = pixCopy(NULL, pixs); - cmap = pixcmapCreateLinear(d, 1 << d); - pixSetColormap(pixd, cmap); - pixCopyInputFormat(pixd, pixs); - return pixd; -} - - -/*! - * \brief pixConvertGrayToColormap8() - * - * \param[in] pixs 8 bpp grayscale - * \param[in] mindepth of pixd; valid values are 2, 4 and 8 - * \return pixd 2, 4 or 8 bpp with colormap, or NULL on error - * - *- * Notes: - * (1) Returns a copy if pixs already has a colormap. - * (2) This is a lossless transformation; there is no quantization. - * We compute the number of different gray values in pixs, - * and construct a colormap that has exactly these values. - * (3) 'mindepth' is the minimum depth of pixd. If mindepth == 8, - * pixd will always be 8 bpp. Let the number of different - * gray values in pixs be ngray. If mindepth == 4, we attempt - * to save pixd as a 4 bpp image, but if ngray > 16, - * pixd must be 8 bpp. Likewise, if mindepth == 2, - * the depth of pixd will be 2 if ngray <= 4 and 4 if ngray > 4 - * but <= 16. - *- */ -PIX * -pixConvertGrayToColormap8(PIX *pixs, - l_int32 mindepth) -{ -l_int32 ncolors, w, h, depth, i, j, wpls, wpld; -l_int32 index, num, val, newval; -l_int32 array[256]; -l_uint32 *lines, *lined, *datas, *datad; -NUMA *na; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixConvertGrayToColormap8"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (mindepth != 2 && mindepth != 4 && mindepth != 8) { - L_WARNING("invalid value of mindepth; setting to 8\n", procName); - mindepth = 8; - } - - if (pixGetColormap(pixs)) { - L_INFO("pixs already has a colormap\n", procName); - return pixCopy(NULL, pixs); - } - - na = pixGetGrayHistogram(pixs, 1); - numaGetCountRelativeToZero(na, L_GREATER_THAN_ZERO, &ncolors); - if (mindepth == 8 || ncolors > 16) - depth = 8; - else if (mindepth == 4 || ncolors > 4) - depth = 4; - else - depth = 2; - - pixGetDimensions(pixs, &w, &h, NULL); - pixd = pixCreate(w, h, depth); - cmap = pixcmapCreate(depth); - pixSetColormap(pixd, cmap); - pixCopyInputFormat(pixd, pixs); - pixCopyResolution(pixd, pixs); - - index = 0; - for (i = 0; i < 256; i++) { - array[i] = 0; /* only to quiet the static checker */ - numaGetIValue(na, i, &num); - if (num > 0) { - pixcmapAddColor(cmap, i, i, i); - array[i] = index; - index++; - } - } - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(lines, j); - newval = array[val]; - if (depth == 2) - SET_DATA_DIBIT(lined, j, newval); - else if (depth == 4) - SET_DATA_QBIT(lined, j, newval); - else /* depth == 8 */ - SET_DATA_BYTE(lined, j, newval); - } - } - - numaDestroy(&na); - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Colorizing conversion from grayscale to color * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixColorizeGray() - * - * \param[in] pixs 8 bpp gray; 2, 4 or 8 bpp colormapped - * \param[in] color 32 bit rgba pixel - * \param[in] cmapflag 1 for result to have colormap; 0 for RGB - * \return pixd 8 bpp colormapped or 32 bpp rgb, or NULL on error - * - *- * Notes: - * (1) This applies the specific color to the grayscale image. - * (2) If pixs already has a colormap, it is removed to gray - * before colorizing. - *- */ -PIX * -pixColorizeGray(PIX *pixs, - l_uint32 color, - l_int32 cmapflag) -{ -l_int32 i, j, w, h, wplt, wpld, val8; -l_uint32 *datad, *datat, *lined, *linet, *tab; -PIX *pixt, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixColorizeGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs not 8 bpp or cmapped", procName, NULL); - - if (pixGetColormap(pixs)) - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - else - pixt = pixClone(pixs); - - cmap = pixcmapGrayToColor(color); - if (cmapflag) { - pixd = pixCopy(NULL, pixt); - pixSetColormap(pixd, cmap); - pixDestroy(&pixt); - return pixd; - } - - /* Make an RGB pix */ - pixcmapToRGBTable(cmap, &tab, NULL); - pixGetDimensions(pixt, &w, &h, NULL); - pixd = pixCreate(w, h, 32); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - linet = datat + i * wplt; - for (j = 0; j < w; j++) { - val8 = GET_DATA_BYTE(linet, j); - lined[j] = tab[val8]; - } - } - - pixDestroy(&pixt); - pixcmapDestroy(&cmap); - LEPT_FREE(tab); - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Conversion from RGB color to colormap * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvertRGBToColormap() - * - * \param[in] pixs 32 bpp rgb - * \param[in] ditherflag 1 to dither, 0 otherwise - * \return pixd 2, 4 or 8 bpp with colormap, or NULL on error - * - *- * Notes: - * (1) This function has two relatively simple modes of color - * quantization: - * (a) If the image is made orthographically and has not more - * than 256 'colors' at the level 4 octcube leaves, - * it is quantized nearly exactly. The ditherflag - * is ignored. - * (b) Most natural images have more than 256 different colors; - * in that case we use adaptive octree quantization, - * with dithering if requested. - * (2) If there are not more than 256 occupied level 4 octcubes, - * the color in the colormap that represents all pixels in - * one of those octcubes is given by the first pixel that - * falls into that octcube. - * (3) If there are more than 256 colors, we use adaptive octree - * color quantization. - * (4) Dithering gives better visual results on images where - * there is a color wash (a slow variation of color), but it - * is about twice as slow and results in significantly larger - * files when losslessly compressed (e.g., into png). - *- */ -PIX * -pixConvertRGBToColormap(PIX *pixs, - l_int32 ditherflag) -{ -l_int32 ncolors; -NUMA *na; -PIX *pixd; - - PROCNAME("pixConvertRGBToColormap"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (pixGetSpp(pixs) == 4) - L_WARNING("pixs has alpha; removing\n", procName); - - /* Get the histogram and count the number of occupied level 4 - * leaf octcubes. We don't yet know if this is the number of - * actual colors, but if it's not, all pixels falling into - * the same leaf octcube will be assigned to the color of the - * first pixel that lands there. */ - na = pixOctcubeHistogram(pixs, 4, &ncolors); - - /* If there are too many occupied leaf octcubes to be - * represented directly in a colormap, fall back to octree - * quantization, optionally with dithering. */ - if (ncolors > 256) { - numaDestroy(&na); - if (ditherflag) - L_INFO("More than 256 colors; using octree quant with dithering\n", - procName); - else - L_INFO("More than 256 colors; using octree quant; no dithering\n", - procName); - return pixOctreeColorQuant(pixs, 240, ditherflag); - } - - /* There are not more than 256 occupied leaf octcubes. - * Quantize to those octcubes. */ - pixd = pixFewColorsOctcubeQuant2(pixs, 4, na, ncolors, NULL); - pixCopyInputFormat(pixd, pixs); - numaDestroy(&na); - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Conversion from colormap to 1 bpp * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvertCmapTo1() - * - * \param[in] pixs cmapped - * \return pixd 1 bpp, or NULL on error - * - *- * Notes: - * (1) This is an extreme color quantizer. It decides which - * colors map to FG (black) and which to BG (white). - * (2) This uses two heuristics to make the decision: - * (a) colors similar to each other are likely to be in the same class - * (b) there is usually much less FG than BG. - *- */ -PIX * -pixConvertCmapTo1(PIX *pixs) -{ -l_int32 i, j, nc, w, h, imin, imax, factor, wpl1, wpld; -l_int32 index, rmin, gmin, bmin, rmax, gmax, bmax, dmin, dmax; -l_float32 minfract, ifract; -l_int32 *lut; -l_uint32 *line1, *lined, *data1, *datad; -NUMA *na1, *na2; /* histograms */ -PIX *pix1, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixConvertCmapTo1"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if ((cmap = pixGetColormap(pixs)) == NULL) - return (PIX *)ERROR_PTR("no colormap", procName, NULL); - - /* Select target colors for the two classes. Find the - * colors with smallest and largest average component values. - * The smallest is class 0 and the largest is class 1. */ - pixcmapGetRangeValues(cmap, L_SELECT_AVERAGE, NULL, NULL, &imin, &imax); - pixcmapGetColor(cmap, imin, &rmin, &gmin, &bmin); - pixcmapGetColor(cmap, imax, &rmax, &gmax, &bmax); - nc = pixcmapGetCount(cmap); - - /* Assign colors to the two classes. The histogram is - * initialized to 0, so any colors not found when computing - * the sampled histogram will get zero weight in minfract. */ - if ((lut = (l_int32 *)LEPT_CALLOC(nc, sizeof(l_int32))) == NULL) - return (PIX *)ERROR_PTR("calloc fail for lut", procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - factor = L_MAX(1, (l_int32)sqrt((l_float64)(w * h) / 50000. + 0.5)); - na1 = pixGetCmapHistogram(pixs, factor); - na2 = numaNormalizeHistogram(na1, 1.0); - minfract = 0.0; - for (i = 0; i < nc; i++) { - numaGetFValue(na2, i, &ifract); - pixcmapGetDistanceToColor(cmap, i, rmin, gmin, bmin, &dmin); - pixcmapGetDistanceToColor(cmap, i, rmax, gmax, bmax, &dmax); - if (dmin < dmax) { /* closer to dark extreme value */ - lut[i] = 1; /* black pixel in 1 bpp image */ - minfract += ifract; - } - } - numaDestroy(&na1); - numaDestroy(&na2); - - /* Generate the output binarized image */ - pix1 = pixConvertTo8(pixs, 1); - pixd = pixCreate(w, h, 1); - data1 = pixGetData(pix1); - datad = pixGetData(pixd); - wpl1 = pixGetWpl(pix1); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - line1 = data1 + i * wpl1; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - index = GET_DATA_BYTE(line1, j); - if (lut[index] == 1) SET_DATA_BIT(lined, j); - } - } - pixDestroy(&pix1); - LEPT_FREE(lut); - - /* We expect minfract (the dark colors) to be less than 0.5. - * If that is not the case, invert pixd. */ - if (minfract > 0.5) { - L_INFO("minfract = %5.3f; inverting\n", procName, minfract); - pixInvert(pixd, pixd); - } - - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Quantization for relatively small number of colors in source * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixQuantizeIfFewColors() - * - * \param[in] pixs 8 bpp gray or 32 bpp rgb - * \param[in] maxcolors max number of colors allowed to be returned - * from pixColorsForQuantization(); - * use 0 for default - * \param[in] mingraycolors min number of gray levels that a grayscale - * image is quantized to; use 0 for default - * \param[in] octlevel for octcube quantization: 3 or 4 - * \param[out] ppixd 2,4 or 8 bpp quantized; null if too many colors - * \return 0 if OK, 1 on error or if pixs can't be quantized into - * a small number of colors. - * - *- * Notes: - * (1) This is a wrapper that tests if the pix can be quantized - * with good quality using a small number of colors. If so, - * it does the quantization, defining a colormap and using - * pixels whose value is an index into the colormap. - * (2) If the image has color, it is quantized with 8 bpp pixels. - * If the image is essentially grayscale, the pixels are - * either 4 or 8 bpp, depending on the size of the required - * colormap. - * (3) %octlevel = 4 generates a larger colormap and larger - * compressed image than %octlevel = 3. If image quality is - * important, you should use %octlevel = 4. - * (4) If the image already has a colormap, it returns a clone. - *- */ -l_ok -pixQuantizeIfFewColors(PIX *pixs, - l_int32 maxcolors, - l_int32 mingraycolors, - l_int32 octlevel, - PIX **ppixd) -{ -l_int32 d, ncolors, iscolor, graycolors; -PIX *pixg, *pixd; - - PROCNAME("pixQuantizeIfFewColors"); - - if (!ppixd) - return ERROR_INT("&pixd not defined", procName, 1); - *ppixd = NULL; - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - d = pixGetDepth(pixs); - if (d != 8 && d != 32) - return ERROR_INT("pixs not defined", procName, 1); - if (pixGetColormap(pixs) != NULL) { - *ppixd = pixClone(pixs); - return 0; - } - if (maxcolors <= 0) - maxcolors = 15; /* default */ - if (maxcolors > 50) - L_WARNING("maxcolors > 50; very large!\n", procName); - if (mingraycolors <= 0) - mingraycolors = 10; /* default */ - if (mingraycolors > 30) - L_WARNING("mingraycolors > 30; very large!\n", procName); - if (octlevel != 3 && octlevel != 4) { - L_WARNING("invalid octlevel; setting to 3\n", procName); - octlevel = 3; - } - - /* Test the number of colors. For color, the octcube leaves - * are at level 4. */ - pixColorsForQuantization(pixs, 0, &ncolors, &iscolor, 0); - if (ncolors > maxcolors) - return ERROR_INT("too many colors", procName, 1); - - /* Quantize! - * (1) For color: - * If octlevel == 4, try to quantize to an octree where - * the octcube leaves are at level 4. If that fails, - * back off to level 3. - * If octlevel == 3, quantize to level 3 directly. - * For level 3, the quality is usually good enough and there - * is negligible chance of getting more than 256 colors. - * (2) For grayscale, multiply ncolors by 1.5 for extra quality, - * but use at least mingraycolors and not more than 256. */ - if (iscolor) { - pixd = pixFewColorsOctcubeQuant1(pixs, octlevel); - if (!pixd) { /* backoff */ - pixd = pixFewColorsOctcubeQuant1(pixs, octlevel - 1); - if (octlevel == 3) /* shouldn't happen */ - L_WARNING("quantized at level 2; low quality\n", procName); - } - } else { /* image is really grayscale */ - if (d == 32) - pixg = pixConvertRGBToLuminance(pixs); - else - pixg = pixClone(pixs); - graycolors = L_MAX(mingraycolors, (l_int32)(1.5 * ncolors)); - graycolors = L_MIN(graycolors, 256); - if (graycolors < 16) - pixd = pixThresholdTo4bpp(pixg, graycolors, 1); - else - pixd = pixThresholdOn8bpp(pixg, graycolors, 1); - pixDestroy(&pixg); - } - *ppixd = pixd; - - if (!pixd) - return ERROR_INT("pixd not made", procName, 1); - pixCopyInputFormat(pixd, pixs); - return 0; -} - - - -/*---------------------------------------------------------------------------* - * Conversion from 16 bpp to 8 bpp * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvert16To8() - * - * \param[in] pixs 16 bpp - * \param[in] type L_LS_BYTE, L_MS_BYTE, L_AUTO_BYTE, L_CLIP_TO_FF - * \return pixd 8 bpp, or NULL on error - * - *- * Notes: - * (1) With L_AUTO_BYTE, if the max pixel value is greater than 255, - * use the MSB; otherwise, use the LSB. - * (2) With L_CLIP_TO_FF, use min(pixel-value, 0xff) for each - * 16-bit src pixel. - *- */ -PIX * -pixConvert16To8(PIX *pixs, - l_int32 type) -{ -l_uint16 dword; -l_int32 w, h, wpls, wpld, i, j, val, use_lsb; -l_uint32 sword, first, second; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; - - PROCNAME("pixConvert16To8"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 16) - return (PIX *)ERROR_PTR("pixs not 16 bpp", procName, NULL); - if (type != L_LS_BYTE && type != L_MS_BYTE && - type != L_AUTO_BYTE && type != L_CLIP_TO_FF) - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - if ((pixd = pixCreate(w, h, 8)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyInputFormat(pixd, pixs); - pixCopyResolution(pixd, pixs); - wpls = pixGetWpl(pixs); - datas = pixGetData(pixs); - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - - if (type == L_AUTO_BYTE) { - use_lsb = TRUE; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < wpls; j++) { - val = GET_DATA_TWO_BYTES(lines, j); - if (val > 255) { - use_lsb = FALSE; - break; - } - } - if (!use_lsb) break; - } - type = (use_lsb) ? L_LS_BYTE : L_MS_BYTE; - } - - /* Convert 2 pixels at a time */ - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - if (type == L_LS_BYTE) { - for (j = 0; j < wpls; j++) { - sword = *(lines + j); - dword = ((sword >> 8) & 0xff00) | (sword & 0xff); - SET_DATA_TWO_BYTES(lined, j, dword); - } - } else if (type == L_MS_BYTE) { - for (j = 0; j < wpls; j++) { - sword = *(lines + j); - dword = ((sword >> 16) & 0xff00) | ((sword >> 8) & 0xff); - SET_DATA_TWO_BYTES(lined, j, dword); - } - } else { /* type == L_CLIP_TO_FF */ - for (j = 0; j < wpls; j++) { - sword = *(lines + j); - first = (sword >> 24) ? 255 : ((sword >> 16) & 0xff); - second = ((sword >> 8) & 0xff) ? 255 : (sword & 0xff); - dword = (first << 8) | second; - SET_DATA_TWO_BYTES(lined, j, dword); - } - } - } - - return pixd; -} - - - -/*---------------------------------------------------------------------------* - * Conversion from grayscale to false color - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvertGrayToFalseColor() - * - * \param[in] pixs 8 or 16 bpp grayscale - * \param[in] gamma (factor) 0.0 or 1.0 for default; > 1.0 for brighter; - * 2.0 is quite nice - * \return pixd 8 bpp with colormap, or NULL on error - * - *- * Notes: - * (1) For 8 bpp input, this simply adds a colormap to the input image. - * (2) For 16 bpp input, it first converts to 8 bpp, using the MSB, - * and then adds the colormap. - * (3) The colormap is modeled after the Matlab "jet" configuration. - *- */ -PIX * -pixConvertGrayToFalseColor(PIX *pixs, - l_float32 gamma) -{ -l_int32 d, i, rval, bval, gval; -l_int32 *curve; -l_float32 invgamma, x; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixConvertGrayToFalseColor"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - if (d != 8 && d != 16) - return (PIX *)ERROR_PTR("pixs not 8 or 16 bpp", procName, NULL); - - if (d == 16) { - pixd = pixConvert16To8(pixs, L_MS_BYTE); - } else { /* d == 8 */ - if (pixGetColormap(pixs)) - pixd = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - else - pixd = pixCopy(NULL, pixs); - } - if (!pixd) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - cmap = pixcmapCreate(8); - pixSetColormap(pixd, cmap); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - - /* Generate curve for transition part of color map */ - curve = (l_int32 *)LEPT_CALLOC(64, sizeof(l_int32)); - if (gamma == 0.0) gamma = 1.0; - invgamma = 1. / gamma; - for (i = 0; i < 64; i++) { - x = (l_float32)i / 64.; - curve[i] = (l_int32)(255. * powf(x, invgamma) + 0.5); - } - - for (i = 0; i < 256; i++) { - if (i < 32) { - rval = 0; - gval = 0; - bval = curve[i + 32]; - } else if (i < 96) { /* 32 - 95 */ - rval = 0; - gval = curve[i - 32]; - bval = 255; - } else if (i < 160) { /* 96 - 159 */ - rval = curve[i - 96]; - gval = 255; - bval = curve[159 - i]; - } else if (i < 224) { /* 160 - 223 */ - rval = 255; - gval = curve[223 - i]; - bval = 0; - } else { /* 224 - 255 */ - rval = curve[287 - i]; - gval = 0; - bval = 0; - } - pixcmapAddColor(cmap, rval, gval, bval); - } - - LEPT_FREE(curve); - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Unpacking conversion from 1 bpp to 2, 4, 8, 16 and 32 bpp * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixUnpackBinary() - * - * \param[in] pixs 1 bpp - * \param[in] depth of destination: 2, 4, 8, 16 or 32 bpp - * \param[in] invert 0: binary 0 --> grayscale 0 - * binary 1 --> grayscale 0xff... - * 1: binary 0 --> grayscale 0xff... - * binary 1 --> grayscale 0 - * \return pixd 2, 4, 8, 16 or 32 bpp, or NULL on error - * - *- * Notes: - * (1) This function calls special cases of pixConvert1To*(), - * for 2, 4, 8, 16 and 32 bpp destinations. - *- */ -PIX * -pixUnpackBinary(PIX *pixs, - l_int32 depth, - l_int32 invert) -{ -PIX *pixd; - - PROCNAME("pixUnpackBinary"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); - if (depth != 2 && depth != 4 && depth != 8 && depth != 16 && depth != 32) - return (PIX *)ERROR_PTR("depth not 2, 4, 8, 16 or 32 bpp", - procName, NULL); - - if (depth == 2) { - if (invert == 0) - pixd = pixConvert1To2(NULL, pixs, 0, 3); - else /* invert bits */ - pixd = pixConvert1To2(NULL, pixs, 3, 0); - } else if (depth == 4) { - if (invert == 0) - pixd = pixConvert1To4(NULL, pixs, 0, 15); - else /* invert bits */ - pixd = pixConvert1To4(NULL, pixs, 15, 0); - } else if (depth == 8) { - if (invert == 0) - pixd = pixConvert1To8(NULL, pixs, 0, 255); - else /* invert bits */ - pixd = pixConvert1To8(NULL, pixs, 255, 0); - } else if (depth == 16) { - if (invert == 0) - pixd = pixConvert1To16(NULL, pixs, 0, 0xffff); - else /* invert bits */ - pixd = pixConvert1To16(NULL, pixs, 0xffff, 0); - } else { - if (invert == 0) - pixd = pixConvert1To32(NULL, pixs, 0, 0xffffffff); - else /* invert bits */ - pixd = pixConvert1To32(NULL, pixs, 0xffffffff, 0); - } - - pixCopyInputFormat(pixd, pixs); - return pixd; -} - - -/*! - * \brief pixConvert1To16() - * - * \param[in] pixd [optional] 16 bpp, can be null - * \param[in] pixs 1 bpp - * \param[in] val0 16 bit value to be used for 0s in pixs - * \param[in] val1 16 bit value to be used for 1s in pixs - * \return pixd 16 bpp - * - *- * Notes: - * (1) If pixd is null, a new pix is made. - * (2) If pixd is not null, it must be of equal width and height - * as pixs. It is always returned. - *- */ -PIX * -pixConvert1To16(PIX *pixd, - PIX *pixs, - l_uint16 val0, - l_uint16 val1) -{ -l_int32 w, h, i, j, dibit, ndibits, wpls, wpld; -l_uint16 val[2]; -l_uint32 index; -l_uint32 *tab, *datas, *datad, *lines, *lined; - - PROCNAME("pixConvert1To16"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - if (pixd) { - if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) - return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd); - if (pixGetDepth(pixd) != 16) - return (PIX *)ERROR_PTR("pixd not 16 bpp", procName, pixd); - } else { - if ((pixd = pixCreate(w, h, 16)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - - /* Use a table to convert 2 src bits at a time */ - tab = (l_uint32 *)LEPT_CALLOC(4, sizeof(l_uint32)); - val[0] = val0; - val[1] = val1; - for (index = 0; index < 4; index++) { - tab[index] = (val[(index >> 1) & 1] << 16) | val[index & 1]; - } - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - ndibits = (w + 1) / 2; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < ndibits; j++) { - dibit = GET_DATA_DIBIT(lines, j); - lined[j] = tab[dibit]; - } - } - - LEPT_FREE(tab); - return pixd; -} - - -/*! - * \brief pixConvert1To32() - * - * \param[in] pixd [optional] 32 bpp, can be null - * \param[in] pixs 1 bpp - * \param[in] val0 32 bit value to be used for 0s in pixs - * \param[in] val1 32 bit value to be used for 1s in pixs - * \return pixd 32 bpp - * - *- * Notes: - * (1) If pixd is null, a new pix is made. - * (2) If pixd is not null, it must be of equal width and height - * as pixs. It is always returned. - *- */ -PIX * -pixConvert1To32(PIX *pixd, - PIX *pixs, - l_uint32 val0, - l_uint32 val1) -{ -l_int32 w, h, i, j, wpls, wpld, bit; -l_uint32 val[2]; -l_uint32 *datas, *datad, *lines, *lined; - - PROCNAME("pixConvert1To32"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - if (pixd) { - if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) - return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd); - if (pixGetDepth(pixd) != 32) - return (PIX *)ERROR_PTR("pixd not 32 bpp", procName, pixd); - } else { - if ((pixd = pixCreate(w, h, 32)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - - val[0] = val0; - val[1] = val1; - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j- * Notes: - * (1) Input 0 is mapped to (255, 255, 255); 1 is mapped to (0, 0, 0) - * - * Notes: - * (1) If pixd is null, a new pix is made. - * (2) If pixd is not null, it must be of equal width and height - * as pixs. It is always returned. - * (3) A simple unpacking might use val0 = 0 and val1 = 3. - * (4) If you want a colormapped pixd, use pixConvert1To2Cmap(). - *- */ -PIX * -pixConvert1To2(PIX *pixd, - PIX *pixs, - l_int32 val0, - l_int32 val1) -{ -l_int32 w, h, i, j, byteval, nbytes, wpls, wpld; -l_uint8 val[2]; -l_uint32 index; -l_uint16 *tab; -l_uint32 *datas, *datad, *lines, *lined; - - PROCNAME("pixConvert1To2"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - - pixGetDimensions(pixs, &w, &h, NULL); - if (pixd) { - if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) - return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd); - if (pixGetDepth(pixd) != 2) - return (PIX *)ERROR_PTR("pixd not 2 bpp", procName, pixd); - } else { - if ((pixd = pixCreate(w, h, 2)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - - /* Use a table to convert 8 src bits to 16 dest bits */ - tab = (l_uint16 *)LEPT_CALLOC(256, sizeof(l_uint16)); - val[0] = val0; - val[1] = val1; - for (index = 0; index < 256; index++) { - tab[index] = (val[(index >> 7) & 1] << 14) | - (val[(index >> 6) & 1] << 12) | - (val[(index >> 5) & 1] << 10) | - (val[(index >> 4) & 1] << 8) | - (val[(index >> 3) & 1] << 6) | - (val[(index >> 2) & 1] << 4) | - (val[(index >> 1) & 1] << 2) | val[index & 1]; - } - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - nbytes = (w + 7) / 8; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < nbytes; j++) { - byteval = GET_DATA_BYTE(lines, j); - SET_DATA_TWO_BYTES(lined, j, tab[byteval]); - } - } - - LEPT_FREE(tab); - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Conversion from 1 bpp to 4 bpp * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvert1To4Cmap() - * - * \param[in] pixs 1 bpp - * \return pixd 4 bpp, cmapped - * - *- * Notes: - * (1) Input 0 is mapped to (255, 255, 255); 1 is mapped to (0, 0, 0) - *- */ -PIX * -pixConvert1To4Cmap(PIX *pixs) -{ -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixConvert1To4Cmap"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); - - if ((pixd = pixConvert1To4(NULL, pixs, 0, 1)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - cmap = pixcmapCreate(4); - pixcmapAddColor(cmap, 255, 255, 255); - pixcmapAddColor(cmap, 0, 0, 0); - pixSetColormap(pixd, cmap); - pixCopyInputFormat(pixd, pixs); - - return pixd; -} - - -/*! - * \brief pixConvert1To4() - * - * \param[in] pixd [optional] 4 bpp, can be null - * \param[in] pixs 1 bpp - * \param[in] val0 4 bit value to be used for 0s in pixs - * \param[in] val1 4 bit value to be used for 1s in pixs - * \return pixd 4 bpp - * - *- * Notes: - * (1) If pixd is null, a new pix is made. - * (2) If pixd is not null, it must be of equal width and height - * as pixs. It is always returned. - * (3) A simple unpacking might use val0 = 0 and val1 = 15, or v.v. - * (4) If you want a colormapped pixd, use pixConvert1To4Cmap(). - *- */ -PIX * -pixConvert1To4(PIX *pixd, - PIX *pixs, - l_int32 val0, - l_int32 val1) -{ -l_int32 w, h, i, j, byteval, nbytes, wpls, wpld; -l_uint8 val[2]; -l_uint32 index; -l_uint32 *tab, *datas, *datad, *lines, *lined; - - PROCNAME("pixConvert1To4"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - - pixGetDimensions(pixs, &w, &h, NULL); - if (pixd) { - if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) - return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd); - if (pixGetDepth(pixd) != 4) - return (PIX *)ERROR_PTR("pixd not 4 bpp", procName, pixd); - } else { - if ((pixd = pixCreate(w, h, 4)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - - /* Use a table to convert 8 src bits to 32 bit dest word */ - tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); - val[0] = val0; - val[1] = val1; - for (index = 0; index < 256; index++) { - tab[index] = (val[(index >> 7) & 1] << 28) | - (val[(index >> 6) & 1] << 24) | - (val[(index >> 5) & 1] << 20) | - (val[(index >> 4) & 1] << 16) | - (val[(index >> 3) & 1] << 12) | - (val[(index >> 2) & 1] << 8) | - (val[(index >> 1) & 1] << 4) | val[index & 1]; - } - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - nbytes = (w + 7) / 8; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < nbytes; j++) { - byteval = GET_DATA_BYTE(lines, j); - lined[j] = tab[byteval]; - } - } - - LEPT_FREE(tab); - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Conversion from 1, 2 and 4 bpp to 8 bpp * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvert1To8Cmap() - * - * \param[in] pixs 1 bpp - * \return pixd 8 bpp, cmapped - * - *- * Notes: - * (1) Input 0 is mapped to (255, 255, 255); 1 is mapped to (0, 0, 0) - *- */ -PIX * -pixConvert1To8Cmap(PIX *pixs) -{ -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixConvert1To8Cmap"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); - - if ((pixd = pixConvert1To8(NULL, pixs, 0, 1)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - cmap = pixcmapCreate(8); - pixcmapAddColor(cmap, 255, 255, 255); - pixcmapAddColor(cmap, 0, 0, 0); - pixSetColormap(pixd, cmap); - pixCopyInputFormat(pixd, pixs); - return pixd; -} - - -/*! - * \brief pixConvert1To8() - * - * \param[in] pixd [optional] 8 bpp, can be null - * \param[in] pixs 1 bpp - * \param[in] val0 8 bit value to be used for 0s in pixs - * \param[in] val1 8 bit value to be used for 1s in pixs - * \return pixd 8 bpp - * - *- * Notes: - * (1) If pixd is null, a new pix is made. - * (2) If pixd is not null, it must be of equal width and height - * as pixs. It is always returned. - * (3) A simple unpacking might use val0 = 0 and val1 = 255, or v.v. - * (4) To have a colormap associated with the 8 bpp pixd, - * use pixConvert1To8Cmap(). - *- */ -PIX * -pixConvert1To8(PIX *pixd, - PIX *pixs, - l_uint8 val0, - l_uint8 val1) -{ -l_int32 w, h, i, j, qbit, nqbits, wpls, wpld; -l_uint8 val[2]; -l_uint32 index; -l_uint32 *tab, *datas, *datad, *lines, *lined; - - PROCNAME("pixConvert1To8"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd); - - pixGetDimensions(pixs, &w, &h, NULL); - if (pixd) { - if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) - return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd); - if (pixGetDepth(pixd) != 8) - return (PIX *)ERROR_PTR("pixd not 8 bpp", procName, pixd); - } else { - if ((pixd = pixCreate(w, h, 8)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - } - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - pixSetPadBits(pixs, 0); - - /* Use a table to convert 4 src bits at a time */ - tab = (l_uint32 *)LEPT_CALLOC(16, sizeof(l_uint32)); - val[0] = val0; - val[1] = val1; - for (index = 0; index < 16; index++) { - tab[index] = ((l_uint32)val[(index >> 3) & 1] << 24) | - (val[(index >> 2) & 1] << 16) | - (val[(index >> 1) & 1] << 8) | val[index & 1]; - } - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - nqbits = (w + 3) / 4; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < nqbits; j++) { - qbit = GET_DATA_QBIT(lines, j); - lined[j] = tab[qbit]; - } - } - - LEPT_FREE(tab); - return pixd; -} - - -/*! - * \brief pixConvert2To8() - * - * \param[in] pixs 2 bpp - * \param[in] val0 8 bit value to be used for 00 in pixs - * \param[in] val1 8 bit value to be used for 01 in pixs - * \param[in] val2 8 bit value to be used for 10 in pixs - * \param[in] val3 8 bit value to be used for 11 in pixs - * \param[in] cmapflag TRUE if pixd is to have a colormap; FALSE otherwise - * \return pixd 8 bpp, or NULL on error - * - *- * Notes: - * ~ A simple unpacking might use val0 = 0, - * val1 = 85 (0x55), val2 = 170 (0xaa), val3 = 255. - * ~ If cmapflag is TRUE: - * ~ The 8 bpp image is made with a colormap. - * ~ If pixs has a colormap, the input values are ignored and - * the 8 bpp image is made using the colormap - * ~ If pixs does not have a colormap, the input values are - * used to build the colormap. - * ~ If cmapflag is FALSE: - * ~ The 8 bpp image is made without a colormap. - * ~ If pixs has a colormap, the input values are ignored, - * the colormap is removed, and the values stored in the 8 bpp - * image are from the colormap. - * ~ If pixs does not have a colormap, the input values are - * used to populate the 8 bpp image. - *- */ -PIX * -pixConvert2To8(PIX *pixs, - l_uint8 val0, - l_uint8 val1, - l_uint8 val2, - l_uint8 val3, - l_int32 cmapflag) -{ -l_int32 w, h, i, j, nbytes, wpls, wpld, dibit, byte; -l_uint8 val[4]; -l_uint32 index; -l_uint32 *tab, *datas, *datad, *lines, *lined; -PIX *pixd; -PIXCMAP *cmaps, *cmapd; - - PROCNAME("pixConvert2To8"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 2) - return (PIX *)ERROR_PTR("pixs not 2 bpp", procName, NULL); - - cmaps = pixGetColormap(pixs); - if (cmaps && cmapflag == FALSE) - return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - - pixGetDimensions(pixs, &w, &h, NULL); - if ((pixd = pixCreate(w, h, 8)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixSetPadBits(pixs, 0); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - if (cmapflag == TRUE) { /* pixd will have a colormap */ - if (cmaps) { /* use the existing colormap from pixs */ - cmapd = pixcmapConvertTo8(cmaps); - } else { /* make a colormap from the input values */ - cmapd = pixcmapCreate(8); - pixcmapAddColor(cmapd, val0, val0, val0); - pixcmapAddColor(cmapd, val1, val1, val1); - pixcmapAddColor(cmapd, val2, val2, val2); - pixcmapAddColor(cmapd, val3, val3, val3); - } - pixSetColormap(pixd, cmapd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - dibit = GET_DATA_DIBIT(lines, j); - SET_DATA_BYTE(lined, j, dibit); - } - } - return pixd; - } - - /* Last case: no colormap in either pixs or pixd. - * Use input values and build a table to convert 1 src byte - * (4 src pixels) at a time */ - tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); - val[0] = val0; - val[1] = val1; - val[2] = val2; - val[3] = val3; - for (index = 0; index < 256; index++) { - tab[index] = (val[(index >> 6) & 3] << 24) | - (val[(index >> 4) & 3] << 16) | - (val[(index >> 2) & 3] << 8) | val[index & 3]; - } - - nbytes = (w + 3) / 4; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < nbytes; j++) { - byte = GET_DATA_BYTE(lines, j); - lined[j] = tab[byte]; - } - } - - LEPT_FREE(tab); - return pixd; -} - - -/*! - * \brief pixConvert4To8() - * - * \param[in] pixs 4 bpp - * \param[in] cmapflag TRUE if pixd is to have a colormap; FALSE otherwise - * \return pixd 8 bpp, or NULL on error - * - *- * Notes: - * ~ If cmapflag is TRUE: - * ~ pixd is made with a colormap. - * ~ If pixs has a colormap, it is copied and the colormap - * index values are placed in pixd. - * ~ If pixs does not have a colormap, a colormap with linear - * trc is built and the pixel values in pixs are placed in - * pixd as colormap index values. - * ~ If cmapflag is FALSE: - * ~ pixd is made without a colormap. - * ~ If pixs has a colormap, it is removed and the values stored - * in pixd are from the colormap (converted to gray). - * ~ If pixs does not have a colormap, the pixel values in pixs - * are used, with shift replication, to populate pixd. - *- */ -PIX * -pixConvert4To8(PIX *pixs, - l_int32 cmapflag) -{ -l_int32 w, h, i, j, wpls, wpld, byte, qbit; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; -PIXCMAP *cmaps, *cmapd; - - PROCNAME("pixConvert4To8"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 4) - return (PIX *)ERROR_PTR("pixs not 4 bpp", procName, NULL); - - cmaps = pixGetColormap(pixs); - if (cmaps && cmapflag == FALSE) - return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - - pixGetDimensions(pixs, &w, &h, NULL); - if ((pixd = pixCreate(w, h, 8)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - if (cmapflag == TRUE) { /* pixd will have a colormap */ - if (cmaps) { /* use the existing colormap from pixs */ - cmapd = pixcmapConvertTo8(cmaps); - } else { /* make a colormap with a linear trc */ - cmapd = pixcmapCreate(8); - for (i = 0; i < 16; i++) - pixcmapAddColor(cmapd, 17 * i, 17 * i, 17 * i); - } - pixSetColormap(pixd, cmapd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - qbit = GET_DATA_QBIT(lines, j); - SET_DATA_BYTE(lined, j, qbit); - } - } - return pixd; - } - - /* Last case: no colormap in either pixs or pixd. - * Replicate the qbit value into 8 bits. */ - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - qbit = GET_DATA_QBIT(lines, j); - byte = (qbit << 4) | qbit; - SET_DATA_BYTE(lined, j, byte); - } - } - return pixd; -} - - - -/*---------------------------------------------------------------------------* - * Unpacking conversion from 8 bpp to 16 bpp * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvert8To16() - * - * \param[in] pixs 8 bpp; colormap removed to gray - * \param[in] leftshift number of bits: 0 is no shift; - * 8 replicates in MSB and LSB of dest - * \return pixd 16 bpp, or NULL on error - * - *- * Notes: - * (1) For left shift of 8, the 8 bit value is replicated in both - * the MSB and the LSB of the pixels in pixd. That way, we get - * proportional mapping, with a correct map from 8 bpp white - * (0xff) to 16 bpp white (0xffff). - *- */ -PIX * -pixConvert8To16(PIX *pixs, - l_int32 leftshift) -{ -l_int32 i, j, w, h, d, wplt, wpld, val; -l_uint32 *datat, *datad, *linet, *lined; -PIX *pixt, *pixd; - - PROCNAME("pixConvert8To16"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (leftshift < 0 || leftshift > 8) - return (PIX *)ERROR_PTR("leftshift not in [0 ... 8]", procName, NULL); - - if (pixGetColormap(pixs) != NULL) - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - else - pixt = pixClone(pixs); - - pixd = pixCreate(w, h, 16); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datat = pixGetData(pixt); - datad = pixGetData(pixd); - wplt = pixGetWpl(pixt); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - linet = datat + i * wplt; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(linet, j); - if (leftshift == 8) - val = val | (val << leftshift); - else - val <<= leftshift; - SET_DATA_TWO_BYTES(lined, j, val); - } - } - - pixDestroy(&pixt); - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Top-level conversion to 2 bpp * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvertTo2() - * - * \param[in] pixs 1, 2, 4, 8, 32 bpp; colormap OK but will be removed - * \return pixd 2 bpp, or NULL on error - * - *- * Notes: - * (1) This is a top-level function, with simple default values - * used in pixConvertTo8() if unpacking is necessary. - * (2) Any existing colormap is removed; the result is always gray. - * (3) If the input image has 2 bpp and no colormap, the operation is - * lossless and a copy is returned. - *- */ -PIX * -pixConvertTo2(PIX *pixs) -{ -l_int32 d; -PIX *pix1, *pix2, *pix3, *pixd; - - PROCNAME("pixConvertTo2"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32) - return (PIX *)ERROR_PTR("depth not {1,2,4,8,32}", procName, NULL); - - if (pixGetColormap(pixs) != NULL) { - pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - d = pixGetDepth(pix1); - } else { - pix1 = pixCopy(NULL, pixs); - } - if (d == 32) - pix2 = pixConvertTo8(pix1, FALSE); - else - pix2 = pixClone(pix1); - pixDestroy(&pix1); - if (d == 1) { - pixd = pixConvert1To2(NULL, pix2, 3, 0); - } else if (d == 2) { - pixd = pixClone(pix2); - } else if (d == 4) { - pix3 = pixConvert4To8(pix2, FALSE); /* unpack to 8 */ - pixd = pixConvert8To2(pix3); - pixDestroy(&pix3); - } else { /* d == 8 */ - pixd = pixConvert8To2(pix2); - } - pixDestroy(&pix2); - return pixd; -} - - -/*! - * \brief pixConvert8To2() - * - * \param[in] pix 8 bpp; colormap OK - * \return pixd 2 bpp, or NULL on error - * - *- * Notes: - * (1) Any existing colormap is removed to gray. - *- */ -PIX * -pixConvert8To2(PIX *pix) -{ -l_int32 i, j, w, h, wpls, wpld; -l_uint32 word; -l_uint32 *datas, *lines, *datad, *lined; -PIX *pixs, *pixd; - - PROCNAME("pixConvert8To2"); - - if (!pix || pixGetDepth(pix) != 8) - return (PIX *)ERROR_PTR("pix undefined or not 8 bpp", procName, NULL); - - if (pixGetColormap(pix) != NULL) - pixs = pixRemoveColormap(pix, REMOVE_CMAP_TO_GRAYSCALE); - else - pixs = pixClone(pix); - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - pixd = pixCreate(w, h, 2); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < wpls; j++) { /* march through 4 pixels at a time */ - word = lines[j] & 0xc0c0c0c0; /* top 2 bits of each byte */ - word = (word >> 24) | ((word & 0xff0000) >> 18) | - ((word & 0xff00) >> 12) | ((word & 0xff) >> 6); - SET_DATA_BYTE(lined, j, word); /* only LS byte is filled */ - } - } - pixDestroy(&pixs); - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Top-level conversion to 4 bpp * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvertTo4() - * - * \param[in] pixs 1, 2, 4, 8, 32 bpp; colormap OK but will be removed - * \return pixd 4 bpp, or NULL on error - * - *- * Notes: - * (1) This is a top-level function, with simple default values - * used in pixConvertTo8() if unpacking is necessary. - * (2) Any existing colormap is removed; the result is always gray. - * (3) If the input image has 4 bpp and no colormap, the operation is - * lossless and a copy is returned. - *- */ -PIX * -pixConvertTo4(PIX *pixs) -{ -l_int32 d; -PIX *pix1, *pix2, *pix3, *pixd; - - PROCNAME("pixConvertTo4"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32) - return (PIX *)ERROR_PTR("depth not {1,2,4,8,32}", procName, NULL); - - if (pixGetColormap(pixs) != NULL) { - pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - d = pixGetDepth(pix1); - } else { - pix1 = pixCopy(NULL, pixs); - } - if (d == 32) - pix2 = pixConvertTo8(pix1, FALSE); - else - pix2 = pixClone(pix1); - pixDestroy(&pix1); - if (d == 1) { - pixd = pixConvert1To4(NULL, pix2, 15, 0); - } else if (d == 2) { - pix3 = pixConvert2To8(pix2, 0, 0x55, 0xaa, 0xff, FALSE); - pixd = pixConvert8To4(pix3); - pixDestroy(&pix3); - } else if (d == 4) { - pixd = pixClone(pix2); - } else { /* d == 8 */ - pixd = pixConvert8To4(pix2); - } - pixDestroy(&pix2); - return pixd; -} - - -/*! - * \brief pixConvert8To4() - * - * \param[in] pix 8 bpp; colormap OK - * \return pixd 4 bpp, or NULL on error - * - *- * Notes: - * (1) Any existing colormap is removed to gray. - *- */ -PIX * -pixConvert8To4(PIX *pix) -{ -l_int32 i, j, w, h, wpls, wpld, val; -l_uint32 *datas, *lines, *datad, *lined; -PIX *pixs, *pixd; - - PROCNAME("pixConvert8To4"); - - if (!pix || pixGetDepth(pix) != 8) - return (PIX *)ERROR_PTR("pix undefined or not 8 bpp", procName, NULL); - - if (pixGetColormap(pix) != NULL) - pixs = pixRemoveColormap(pix, REMOVE_CMAP_TO_GRAYSCALE); - else - pixs = pixClone(pix); - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - pixd = pixCreate(w, h, 4); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(lines, j); - val = val >> 4; /* take top 4 bits */ - SET_DATA_QBIT(lined, j, val); - } - } - pixDestroy(&pixs); - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Top-level conversion to 1 bpp * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvertTo1Adaptive() - * - * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp - * \return pixd 1 bpp, or NULL on error - * - *- * Notes: - * (1) This is a top-level function, that uses default values for - * adaptive thresholding, if necessary. Otherwise, it is the same as - * pixConvertTo1(), which uses a global threshold for binarization. - *- */ -PIX * -pixConvertTo1Adaptive(PIX *pixs) -{ -l_int32 d, color0, color1, rval, gval, bval; -PIX *pix1, *pix2, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixConvertTo1Adaptive"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) - return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", procName, NULL); - - cmap = pixGetColormap(pixs); - if (d == 1) { - if (!cmap) { - return pixCopy(NULL, pixs); - } else { /* strip the colormap off, and invert if reasonable - for standard binary photometry. */ - pixcmapGetColor(cmap, 0, &rval, &gval, &bval); - color0 = rval + gval + bval; - pixcmapGetColor(cmap, 1, &rval, &gval, &bval); - color1 = rval + gval + bval; - pixd = pixCopy(NULL, pixs); - pixDestroyColormap(pixd); - if (color1 > color0) - pixInvert(pixd, pixd); - return pixd; - } - } - - /* For all other depths, use 8 bpp as an intermediary */ - pix1 = pixConvertTo8(pixs, FALSE); - pix2 = pixBackgroundNormSimple(pix1, NULL, NULL); - pixd = pixThresholdToBinary(pix2, 180); - pixDestroy(&pix1); - pixDestroy(&pix2); - return pixd; -} - - -/*! - * \brief pixConvertTo1() - * - * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp - * \param[in] threshold for final binarization, relative to 8 bpp - * \return pixd 1 bpp, or NULL on error - * - *- * Notes: - * (1) This is a top-level function, with simple default values - * used in pixConvertTo8() if unpacking is necessary. - * (2) Any existing colormap is removed. - * (3) If the input image has 1 bpp and no colormap, the operation is - * lossless and a copy is returned. - *- */ -PIX * -pixConvertTo1(PIX *pixs, - l_int32 threshold) -{ -l_int32 d, color0, color1, rval, gval, bval; -PIX *pixg, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixConvertTo1"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) - return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", procName, NULL); - - cmap = pixGetColormap(pixs); - if (d == 1) { - if (!cmap) { - return pixCopy(NULL, pixs); - } else { /* strip the colormap off, and invert if reasonable - for standard binary photometry. */ - pixcmapGetColor(cmap, 0, &rval, &gval, &bval); - color0 = rval + gval + bval; - pixcmapGetColor(cmap, 1, &rval, &gval, &bval); - color1 = rval + gval + bval; - pixd = pixCopy(NULL, pixs); - pixDestroyColormap(pixd); - if (color1 > color0) - pixInvert(pixd, pixd); - return pixd; - } - } - - /* For all other depths, use 8 bpp as an intermediary */ - pixg = pixConvertTo8(pixs, FALSE); - pixd = pixThresholdToBinary(pixg, threshold); - pixDestroy(&pixg); - return pixd; -} - - -/*! - * \brief pixConvertTo1BySampling() - * - * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp - * \param[in] factor submsampling factor; integer >= 1 - * \param[in] threshold for final binarization, relative to 8 bpp - * \return pixd 1 bpp, or NULL on error - * - *- * Notes: - * (1) This is a quick and dirty, top-level converter. - * (2) See pixConvertTo1() for default values. - *- */ -PIX * -pixConvertTo1BySampling(PIX *pixs, - l_int32 factor, - l_int32 threshold) -{ -l_float32 scalefactor; -PIX *pixt, *pixd; - - PROCNAME("pixConvertTo1BySampling"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (factor < 1) - return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL); - - scalefactor = 1. / (l_float32)factor; - pixt = pixScaleBySampling(pixs, scalefactor, scalefactor); - pixd = pixConvertTo1(pixt, threshold); - - pixDestroy(&pixt); - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Top-level conversion to 8 bpp * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvertTo8() - * - * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp - * \param[in] cmapflag TRUE if pixd is to have a colormap; FALSE otherwise - * \return pixd 8 bpp, or NULL on error - * - *- * Notes: - * (1) This is a top-level function, with simple default values - * for unpacking. - * (2) The result, pixd, is made with a colormap if specified. - * It is always a new image -- never a clone. For example, - * if d == 8, and cmapflag matches the existence of a cmap - * in pixs, the operation is lossless and it returns a copy. - * (3) The default values used are: - * ~ 1 bpp: val0 = 255, val1 = 0 - * ~ 2 bpp: 4 bpp: even increments over dynamic range - * ~ 8 bpp: lossless if cmap matches cmapflag - * ~ 16 bpp: use most significant byte - * (4) If 32 bpp RGB, this is converted to gray. If you want - * to do color quantization, you must specify the type - * explicitly, using the color quantization code. - *- */ -PIX * -pixConvertTo8(PIX *pixs, - l_int32 cmapflag) -{ -l_int32 d; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixConvertTo8"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) - return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", procName, NULL); - - if (d == 1) { - if (cmapflag) - return pixConvert1To8Cmap(pixs); - else - return pixConvert1To8(NULL, pixs, 255, 0); - } else if (d == 2) { - return pixConvert2To8(pixs, 0, 85, 170, 255, cmapflag); - } else if (d == 4) { - return pixConvert4To8(pixs, cmapflag); - } else if (d == 8) { - cmap = pixGetColormap(pixs); - if ((cmap && cmapflag) || (!cmap && !cmapflag)) { - return pixCopy(NULL, pixs); - } else if (cmap) { /* !cmapflag */ - return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - } else { /* !cmap && cmapflag; add colormap to pixd */ - pixd = pixCopy(NULL, pixs); - pixAddGrayColormap8(pixd); - return pixd; - } - } else if (d == 16) { - pixd = pixConvert16To8(pixs, L_MS_BYTE); - if (cmapflag) - pixAddGrayColormap8(pixd); - return pixd; - } else { /* d == 32 */ - pixd = pixConvertRGBToLuminance(pixs); - if (cmapflag) - pixAddGrayColormap8(pixd); - return pixd; - } -} - - -/*! - * \brief pixConvertTo8BySampling() - * - * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp - * \param[in] factor submsampling factor; integer >= 1 - * \param[in] cmapflag TRUE if pixd is to have a colormap; FALSE otherwise - * \return pixd 8 bpp, or NULL on error - * - *- * Notes: - * (1) This is a fast, quick/dirty, top-level converter. - * (2) See pixConvertTo8() for default values. - *- */ -PIX * -pixConvertTo8BySampling(PIX *pixs, - l_int32 factor, - l_int32 cmapflag) -{ -l_float32 scalefactor; -PIX *pixt, *pixd; - - PROCNAME("pixConvertTo8BySampling"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (factor < 1) - return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL); - - scalefactor = 1. / (l_float32)factor; - pixt = pixScaleBySampling(pixs, scalefactor, scalefactor); - pixd = pixConvertTo8(pixt, cmapflag); - - pixDestroy(&pixt); - return pixd; -} - - -/*! - * \brief pixConvertTo8Colormap() - * - * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp - * \param[in] dither 1 to dither if necessary; 0 otherwise - * \return pixd 8 bpp, cmapped, or NULL on error - * - *- * Notes: - * (1) This is a top-level function, with simple default values - * for unpacking. - * (2) The result, pixd, is always made with a colormap. - * (3) If d == 8, the operation is lossless and it returns a copy. - * (4) The default values used for increasing depth are: - * ~ 1 bpp: val0 = 255, val1 = 0 - * ~ 2 bpp: 4 bpp: even increments over dynamic range - * (5) For 16 bpp, use the most significant byte. - * (6) For 32 bpp RGB, use octcube quantization with optional dithering. - *- */ -PIX * -pixConvertTo8Colormap(PIX *pixs, - l_int32 dither) -{ -l_int32 d; - - PROCNAME("pixConvertTo8Colormap"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) - return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", procName, NULL); - - if (d != 32) - return pixConvertTo8(pixs, 1); - - return pixConvertRGBToColormap(pixs, dither); -} - - -/*---------------------------------------------------------------------------* - * Top-level conversion to 16 bpp * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvertTo16() - * - * \param[in] pixs 1, 8 bpp - * \return pixd 16 bpp, or NULL on error - * - * Usage: Top-level function, with simple default values for unpacking. - * 1 bpp: val0 = 0xffff, val1 = 0 - * 8 bpp: replicates the 8 bit value in both the MSB and LSB - * of the 16 bit pixel. - */ -PIX * -pixConvertTo16(PIX *pixs) -{ -l_int32 d; - - PROCNAME("pixConvertTo16"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - d = pixGetDepth(pixs); - if (d == 1) - return pixConvert1To16(NULL, pixs, 0xffff, 0); - else if (d == 8) - return pixConvert8To16(pixs, 8); - else - return (PIX *)ERROR_PTR("src depth not 1 or 8 bpp", procName, NULL); -} - - - -/*---------------------------------------------------------------------------* - * Top-level conversion to 32 bpp * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvertTo32() - * - * \param[in] pixs 1, 2, 4, 8, 16, 24 or 32 bpp - * \return pixd 32 bpp, or NULL on error - * - * Usage: Top-level function, with simple default values for unpacking. - * 1 bpp: val0 = 255, val1 = 0 - * and then replication into R, G and B components - * 2 bpp: if colormapped, use the colormap values; otherwise, - * use val0 = 0, val1 = 0x55, val2 = 0xaa, val3 = 255 - * and replicate gray into R, G and B components - * 4 bpp: if colormapped, use the colormap values; otherwise, - * replicate 2 nybs into a byte, and then into R,G,B components - * 8 bpp: if colormapped, use the colormap values; otherwise, - * replicate gray values into R, G and B components - * 16 bpp: replicate MSB into R, G and B components - * 24 bpp: unpack the pixels, maintaining word alignment on each scanline - * 32 bpp: makes a copy - * - *- * Notes: - * (1) Never returns a clone of pixs. - *- */ -PIX * -pixConvertTo32(PIX *pixs) -{ -l_int32 d; -PIX *pix1, *pixd; - - PROCNAME("pixConvertTo32"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - d = pixGetDepth(pixs); - if (d == 1) { - return pixConvert1To32(NULL, pixs, 0xffffffff, 0); - } else if (d == 2) { - pix1 = pixConvert2To8(pixs, 0, 85, 170, 255, TRUE); - pixd = pixConvert8To32(pix1); - pixDestroy(&pix1); - return pixd; - } else if (d == 4) { - pix1 = pixConvert4To8(pixs, TRUE); - pixd = pixConvert8To32(pix1); - pixDestroy(&pix1); - return pixd; - } else if (d == 8) { - return pixConvert8To32(pixs); - } else if (d == 16) { - pix1 = pixConvert16To8(pixs, L_MS_BYTE); - pixd = pixConvert8To32(pix1); - pixDestroy(&pix1); - return pixd; - } else if (d == 24) { - return pixConvert24To32(pixs); - } else if (d == 32) { - return pixCopy(NULL, pixs); - } else { - return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8, 16, 32 bpp", - procName, NULL); - } -} - - -/*! - * \brief pixConvertTo32BySampling() - * - * \param[in] pixs 1, 2, 4, 8, 16, 24 or 32 bpp - * \param[in] factor submsampling factor; integer >= 1 - * \return pixd 32 bpp, or NULL on error - * - *- * Notes: - * (1) This is a fast, quick/dirty, top-level converter. - * (2) See pixConvertTo32() for default values. - *- */ -PIX * -pixConvertTo32BySampling(PIX *pixs, - l_int32 factor) -{ -l_float32 scalefactor; -PIX *pix1, *pixd; - - PROCNAME("pixConvertTo32BySampling"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (factor < 1) - return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL); - - scalefactor = 1. / (l_float32)factor; - pix1 = pixScaleBySampling(pixs, scalefactor, scalefactor); - pixd = pixConvertTo32(pix1); - - pixDestroy(&pix1); - return pixd; -} - - -/*! - * \brief pixConvert8To32() - * - * \param[in] pixs 8 bpp - * \return 32 bpp rgb pix, or NULL on error - * - *- * Notes: - * (1) If there is no colormap, replicates the gray value - * into the 3 MSB of the dest pixel. - *- */ -PIX * -pixConvert8To32(PIX *pixs) -{ -l_int32 i, j, w, h, wpls, wpld, val; -l_uint32 *datas, *datad, *lines, *lined; -l_uint32 *tab; -PIX *pixd; - - PROCNAME("pixConvert8To32"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - - if (pixGetColormap(pixs)) - return pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); - - pixGetDimensions(pixs, &w, &h, NULL); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - if ((pixd = pixCreate(w, h, 32)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - /* Replication table gray --> rgb */ - tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); - for (i = 0; i < 256; i++) - tab[i] = (i << 24) | (i << 16) | (i << 8); - - /* Replicate 1 --> 4 bytes (alpha byte not set) */ - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - val = GET_DATA_BYTE(lines, j); - lined[j] = tab[val]; - } - } - - LEPT_FREE(tab); - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Top-level conversion to 8 or 32 bpp, without colormap * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvertTo8Or32() - * - * \param[in] pixs 1, 2, 4, 8, 16, with or without colormap; - * or 32 bpp rgb - * \param[in] copyflag L_CLONE or L_COPY - * \param[in] warnflag 1 to issue warning if colormap is removed; else 0 - * \return pixd 8 bpp grayscale or 32 bpp rgb, or NULL on error - * - *- * Notes: - * (1) If there is a colormap, the colormap is removed to 8 or 32 bpp, - * depending on whether the colors in the colormap are all gray. - * (2) If the input is either rgb or 8 bpp without a colormap, - * this returns either a clone or a copy, depending on %copyflag. - * (3) Otherwise, the pix is converted to 8 bpp grayscale. - * In all cases, pixd does not have a colormap. - *- */ -PIX * -pixConvertTo8Or32(PIX *pixs, - l_int32 copyflag, - l_int32 warnflag) -{ -l_int32 d; -PIX *pixd; - - PROCNAME("pixConvertTo8Or32"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (copyflag != L_CLONE && copyflag != L_COPY) - return (PIX *)ERROR_PTR("invalid copyflag", procName, NULL); - - d = pixGetDepth(pixs); - if (pixGetColormap(pixs)) { - if (warnflag) L_WARNING("pix has colormap; removing\n", procName); - pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - } else if (d == 8 || d == 32) { - if (copyflag == L_CLONE) - pixd = pixClone(pixs); - else /* copyflag == L_COPY */ - pixd = pixCopy(NULL, pixs); - } else { - pixd = pixConvertTo8(pixs, 0); - } - - /* Sanity check on result */ - d = pixGetDepth(pixd); - if (d != 8 && d != 32) { - pixDestroy(&pixd); - return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, NULL); - } - - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Conversion between 24 bpp and 32 bpp rgb * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvert24To32() - * - * \param[in] pixs 24 bpp rgb - * \return pixd 32 bpp rgb, or NULL on error - * - *- * Notes: - * (1) 24 bpp rgb pix are not supported in leptonica, except for a small - * number of formatted write operations. The data is a byte array, - * with pixels in order r,g,b, and padded to 32 bit boundaries - * in each line. - * (2) Because 24 bpp rgb pix are conveniently generated by programs - * such as xpdf (which has SplashBitmaps that store the raster - * data in consecutive 24-bit rgb pixels), it is useful to provide - * 24 bpp pix that simply incorporate that data. The only things - * we can do with these are: - * (a) write them to file in png, jpeg, tiff and pnm - * (b) interconvert between 24 and 32 bpp in memory (for testing). - *- */ -PIX * -pixConvert24To32(PIX *pixs) -{ -l_uint8 *lines; -l_int32 w, h, d, i, j, wpls, wpld, rval, gval, bval; -l_uint32 pixel; -l_uint32 *datas, *datad, *lined; -PIX *pixd; - - PROCNAME("pixConvert24to32"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 24) - return (PIX *)ERROR_PTR("pixs not 24 bpp", procName, NULL); - - pixd = pixCreateNoInit(w, h, 32); - datas = pixGetData(pixs); - datad = pixGetData(pixd); - wpls = pixGetWpl(pixs); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = (l_uint8 *)(datas + i * wpls); - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - rval = *lines++; - gval = *lines++; - bval = *lines++; - composeRGBPixel(rval, gval, bval, &pixel); - lined[j] = pixel; - } - } - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - return pixd; -} - - -/*! - * \brief pixConvert32To24() - * - * \param[in] pixs 32 bpp rgb - * \return pixd 24 bpp rgb, or NULL on error - * - *- * Notes: - * (1) See pixconvert24To32(). - *- */ -PIX * -pixConvert32To24(PIX *pixs) -{ -l_uint8 *rgbdata8; -l_int32 w, h, d, i, j, wpls, wpld, rval, gval, bval; -l_uint32 *datas, *lines, *rgbdata; -PIX *pixd; - - PROCNAME("pixConvert32to24"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - pixd = pixCreateNoInit(w, h, 24); - rgbdata = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - rgbdata8 = (l_uint8 *)(rgbdata + i * wpld); - for (j = 0; j < w; j++) { - extractRGBValues(lines[j], &rval, &gval, &bval); - *rgbdata8++ = rval; - *rgbdata8++ = gval; - *rgbdata8++ = bval; - } - } - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Conversion between 32 bpp (1 spp) and 16 or 8 bpp * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvert32To16() - * - * \param[in] pixs 32 bpp, single component - * \param[in] type L_LS_TWO_BYTES, L_MS_TWO_BYTES, L_CLIP_TO_FFFF - * \return pixd 16 bpp , or NULL on error - * - *- * Notes: - * (1) The data in pixs is typically used for labelling. - * It is an array of l_uint32 values, not rgb or rgba. - *- */ -PIX * -pixConvert32To16(PIX *pixs, - l_int32 type) -{ -l_uint16 dword; -l_int32 w, h, i, j, wpls, wpld; -l_uint32 sword; -l_uint32 *datas, *lines, *datad, *lined; -PIX *pixd; - - PROCNAME("pixConvert32to16"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); - if (type != L_LS_TWO_BYTES && type != L_MS_TWO_BYTES && - type != L_CLIP_TO_FFFF) - return (PIX *)ERROR_PTR("invalid type", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - if ((pixd = pixCreate(w, h, 16)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - wpls = pixGetWpl(pixs); - datas = pixGetData(pixs); - wpld = pixGetWpl(pixd); - datad = pixGetData(pixd); - - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - if (type == L_LS_TWO_BYTES) { - for (j = 0; j < wpls; j++) { - sword = *(lines + j); - dword = sword & 0xffff; - SET_DATA_TWO_BYTES(lined, j, dword); - } - } else if (type == L_MS_TWO_BYTES) { - for (j = 0; j < wpls; j++) { - sword = *(lines + j); - dword = sword >> 16; - SET_DATA_TWO_BYTES(lined, j, dword); - } - } else { /* type == L_CLIP_TO_FFFF */ - for (j = 0; j < wpls; j++) { - sword = *(lines + j); - dword = (sword >> 16) ? 0xffff : (sword & 0xffff); - SET_DATA_TWO_BYTES(lined, j, dword); - } - } - } - - return pixd; -} - - -/*! - * \brief pixConvert32To8() - * - * \param[in] pixs 32 bpp, single component - * \param[in] type16 L_LS_TWO_BYTES, L_MS_TWO_BYTES, L_CLIP_TO_FFFF - * \param[in] type8 L_LS_BYTE, L_MS_BYTE, L_CLIP_TO_FF - * \return pixd 8 bpp, or NULL on error - */ -PIX * -pixConvert32To8(PIX *pixs, - l_int32 type16, - l_int32 type8) -{ -PIX *pix1, *pixd; - - PROCNAME("pixConvert32to8"); - - if (!pixs || pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); - if (type16 != L_LS_TWO_BYTES && type16 != L_MS_TWO_BYTES && - type16 != L_CLIP_TO_FFFF) - return (PIX *)ERROR_PTR("invalid type16", procName, NULL); - if (type8 != L_LS_BYTE && type8 != L_MS_BYTE && type8 != L_CLIP_TO_FF) - return (PIX *)ERROR_PTR("invalid type8", procName, NULL); - - pix1 = pixConvert32To16(pixs, type16); - pixd = pixConvert16To8(pix1, type8); - pixDestroy(&pix1); - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Removal of alpha component by blending with white background * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixRemoveAlpha() - * - * \param[in] pixs any depth - * \return pixd if 32 bpp rgba, pixs blended over a white background; - * a clone of pixs otherwise, and NULL on error - * - *- * Notes: - * (1) This is a wrapper on pixAlphaBlendUniform() - *- */ -PIX * -pixRemoveAlpha(PIX *pixs) -{ - PROCNAME("pixRemoveAlpha"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - if (pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4) - return pixAlphaBlendUniform(pixs, 0xffffff00); - else - return pixClone(pixs); -} - - -/*---------------------------------------------------------------------------* - * Addition of alpha component to 1 bpp * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixAddAlphaTo1bpp() - * - * \param[in] pixd [optional] 1 bpp, can be null or equal to pixs - * \param[in] pixs 1 bpp - * \return pixd 1 bpp with colormap and non-opaque alpha, - * or NULL on error - * - *- * Notes: - * (1) We don't use 1 bpp colormapped images with alpha in leptonica, - * but we support generating them (here), writing to png, and reading - * the png. On reading, they are converted to 32 bpp RGBA. - * (2) The background (0) pixels in pixs become fully transparent, and the - * foreground (1) pixels are fully opaque. Thus, pixd is a 1 bpp - * representation of a stencil, that can be used to paint over pixels - * of a backing image that are masked by the foreground in pixs. - *- */ -PIX * -pixAddAlphaTo1bpp(PIX *pixd, - PIX *pixs) -{ -PIXCMAP *cmap; - - PROCNAME("pixAddAlphaTo1bpp"); - - if (!pixs || (pixGetDepth(pixs) != 1)) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (pixd && (pixd != pixs)) - return (PIX *)ERROR_PTR("pixd defined but != pixs", procName, NULL); - - pixd = pixCopy(pixd, pixs); - cmap = pixcmapCreate(1); - pixSetColormap(pixd, cmap); - pixcmapAddRGBA(cmap, 255, 255, 255, 0); /* 0 ==> white + transparent */ - pixcmapAddRGBA(cmap, 0, 0, 0, 255); /* 1 ==> black + opaque */ - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Lossless depth conversion (unpacking) * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvertLossless() - * - * \param[in] pixs 1, 2, 4, 8 bpp, not cmapped - * \param[in] d destination depth: 2, 4 or 8 - * \return pixd 2, 4 or 8 bpp, or NULL on error - * - *- * Notes: - * (1) This is a lossless unpacking (depth-increasing) - * conversion. If ds is the depth of pixs, then - * ~ if d < ds, returns NULL - * ~ if d == ds, returns a copy - * ~ if d > ds, does the unpacking conversion - * (2) If pixs has a colormap, this is an error. - *- */ -PIX * -pixConvertLossless(PIX *pixs, - l_int32 d) -{ -l_int32 w, h, ds, wpls, wpld, i, j, val; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; - - PROCNAME("pixConvertLossless"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetColormap(pixs)) - return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); - if (d != 2 && d != 4 && d != 8) - return (PIX *)ERROR_PTR("invalid dest depth", procName, NULL); - - pixGetDimensions(pixs, &w, &h, &ds); - if (d < ds) - return (PIX *)ERROR_PTR("depth > d", procName, NULL); - else if (d == ds) - return pixCopy(NULL, pixs); - - if ((pixd = pixCreate(w, h, d)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixCopyResolution(pixd, pixs); - pixCopyInputFormat(pixd, pixs); - - /* Unpack the bits */ - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - lined = datad + i * wpld; - switch (ds) - { - case 1: - for (j = 0; j < w; j++) { - val = GET_DATA_BIT(lines, j); - if (d == 8) - SET_DATA_BYTE(lined, j, val); - else if (d == 4) - SET_DATA_QBIT(lined, j, val); - else /* d == 2 */ - SET_DATA_DIBIT(lined, j, val); - } - break; - case 2: - for (j = 0; j < w; j++) { - val = GET_DATA_DIBIT(lines, j); - if (d == 8) - SET_DATA_BYTE(lined, j, val); - else /* d == 4 */ - SET_DATA_QBIT(lined, j, val); - } - break; - case 4: - for (j = 0; j < w; j++) { - val = GET_DATA_DIBIT(lines, j); - SET_DATA_BYTE(lined, j, val); - } - break; - } - } - - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Conversion for printing in PostScript * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvertForPSWrap() - * - * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp - * \return pixd 1, 8, or 32 bpp, or NULL on error - * - *- * Notes: - * (1) For wrapping in PostScript, we convert pixs to - * 1 bpp, 8 bpp (gray) and 32 bpp (RGB color). - * (2) Colormaps are removed. For pixs with colormaps, the - * images are converted to either 8 bpp gray or 32 bpp - * RGB, depending on whether the colormap has color content. - * (3) Images without colormaps, that are not 1 bpp or 32 bpp, - * are converted to 8 bpp gray. - *- */ -PIX * -pixConvertForPSWrap(PIX *pixs) -{ -l_int32 d; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixConvertForPSWrap"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - cmap = pixGetColormap(pixs); - d = pixGetDepth(pixs); - switch (d) - { - case 1: - case 32: - pixd = pixClone(pixs); - break; - case 2: - if (cmap) - pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - else - pixd = pixConvert2To8(pixs, 0, 0x55, 0xaa, 0xff, FALSE); - break; - case 4: - if (cmap) - pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - else - pixd = pixConvert4To8(pixs, FALSE); - break; - case 8: - pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - break; - case 16: - pixd = pixConvert16To8(pixs, L_MS_BYTE); - break; - default: - lept_stderr("depth not in {1, 2, 4, 8, 16, 32}"); - return NULL; - } - - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Scaling conversion to subpixel RGB * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixConvertToSubpixelRGB() - * - * \param[in] pixs 8 bpp grayscale, 32 bpp rgb, or colormapped - * \param[in] scalex, scaley anisotropic scaling permitted between - * source and destination - * \param[in] order of subpixel rgb color components in - * composition of pixd: - * L_SUBPIXEL_ORDER_RGB, L_SUBPIXEL_ORDER_BGR, - * L_SUBPIXEL_ORDER_VRGB, L_SUBPIXEL_ORDER_VBGR - * \return pixd 32 bpp, or NULL on error - * - *- * Notes: - * (1) If pixs has a colormap, it is removed based on its contents - * to either 8 bpp gray or rgb. - * (2) For horizontal subpixel splitting, the input image - * is rescaled by %scaley vertically and by 3.0 times - * %scalex horizontally. Then each horizontal triplet - * of pixels is mapped back to a single rgb pixel, with the - * r, g and b values being assigned based on the pixel triplet. - * For gray triplets, the r, g, and b values are set equal to - * the three gray values. For color triplets, the r, g and b - * values are set equal to the components from the appropriate - * subpixel. Vertical subpixel splitting is handled similarly. - * (3) See pixConvertGrayToSubpixelRGB() and - * pixConvertColorToSubpixelRGB() for further details. - *- */ -PIX * -pixConvertToSubpixelRGB(PIX *pixs, - l_float32 scalex, - l_float32 scaley, - l_int32 order) -{ -l_int32 d; -PIX *pix1, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixConvertToSubpixelRGB"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - cmap = pixGetColormap(pixs); - if (d != 8 && d != 32 && !cmap) - return (PIX *)ERROR_PTR("pix not 8 or 32 bpp and not cmapped", - procName, NULL); - if (scalex <= 0.0 || scaley <= 0.0) - return (PIX *)ERROR_PTR("scale factors must be > 0", procName, NULL); - if (order != L_SUBPIXEL_ORDER_RGB && order != L_SUBPIXEL_ORDER_BGR && - order != L_SUBPIXEL_ORDER_VRGB && order != L_SUBPIXEL_ORDER_VBGR) - return (PIX *)ERROR_PTR("invalid subpixel order", procName, NULL); - if ((pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC)) == NULL) - return (PIX *)ERROR_PTR("pix1 not made", procName, NULL); - - d = pixGetDepth(pix1); - pixd = NULL; - if (d == 8) - pixd = pixConvertGrayToSubpixelRGB(pix1, scalex, scaley, order); - else if (d == 32) - pixd = pixConvertColorToSubpixelRGB(pix1, scalex, scaley, order); - else - L_ERROR("invalid depth %d\n", procName, d); - - pixDestroy(&pix1); - return pixd; -} - - -/*! - * \brief pixConvertGrayToSubpixelRGB() - * - * \param[in] pixs 8 bpp or colormapped - * \param[in] scalex, scaley - * \param[in] order of subpixel rgb color components in - * composition of pixd: - * L_SUBPIXEL_ORDER_RGB, L_SUBPIXEL_ORDER_BGR, - * L_SUBPIXEL_ORDER_VRGB, L_SUBPIXEL_ORDER_VBGR - * \return pixd 32 bpp, or NULL on error - * - *- * Notes: - * (1) If pixs has a colormap, it is removed to 8 bpp. - * (2) For horizontal subpixel splitting, the input gray image - * is rescaled by %scaley vertically and by 3.0 times - * %scalex horizontally. Then each horizontal triplet - * of pixels is mapped back to a single rgb pixel, with the - * r, g and b values being assigned from the triplet of gray values. - * Similar operations are used for vertical subpixel splitting. - * (3) This is a form of subpixel rendering that tends to give the - * resulting text a sharper and somewhat chromatic display. - * For horizontal subpixel splitting, the observable difference - * between %order=L_SUBPIXEL_ORDER_RGB and - * %order=L_SUBPIXEL_ORDER_BGR is reduced by optical diffusers - * in the display that make the pixel color appear to emerge - * from the entire pixel. - *- */ -PIX * -pixConvertGrayToSubpixelRGB(PIX *pixs, - l_float32 scalex, - l_float32 scaley, - l_int32 order) -{ -l_int32 w, h, d, wd, hd, wplt, wpld, i, j, rval, gval, bval, direction; -l_uint32 *datat, *datad, *linet, *lined; -PIX *pix1, *pix2, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixConvertGrayToSubpixelRGB"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - cmap = pixGetColormap(pixs); - if (d != 8 && !cmap) - return (PIX *)ERROR_PTR("pix not 8 bpp & not cmapped", procName, NULL); - if (scalex <= 0.0 || scaley <= 0.0) - return (PIX *)ERROR_PTR("scale factors must be > 0", procName, NULL); - if (order != L_SUBPIXEL_ORDER_RGB && order != L_SUBPIXEL_ORDER_BGR && - order != L_SUBPIXEL_ORDER_VRGB && order != L_SUBPIXEL_ORDER_VBGR) - return (PIX *)ERROR_PTR("invalid subpixel order", procName, NULL); - - direction = - (order == L_SUBPIXEL_ORDER_RGB || order == L_SUBPIXEL_ORDER_BGR) - ? L_HORIZ : L_VERT; - pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); - if (direction == L_HORIZ) - pix2 = pixScale(pix1, 3.0 * scalex, scaley); - else /* L_VERT */ - pix2 = pixScale(pix1, scalex, 3.0 * scaley); - - pixGetDimensions(pix2, &w, &h, NULL); - wd = (direction == L_HORIZ) ? w / 3 : w; - hd = (direction == L_VERT) ? h / 3 : h; - pixd = pixCreate(wd, hd, 32); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - datat = pixGetData(pix2); - wplt = pixGetWpl(pix2); - if (direction == L_HORIZ) { - for (i = 0; i < hd; i++) { - linet = datat + i * wplt; - lined = datad + i * wpld; - for (j = 0; j < wd; j++) { - rval = GET_DATA_BYTE(linet, 3 * j); - gval = GET_DATA_BYTE(linet, 3 * j + 1); - bval = GET_DATA_BYTE(linet, 3 * j + 2); - if (order == L_SUBPIXEL_ORDER_RGB) - composeRGBPixel(rval, gval, bval, &lined[j]); - else /* order BGR */ - composeRGBPixel(bval, gval, rval, &lined[j]); - } - } - } else { /* L_VERT */ - for (i = 0; i < hd; i++) { - linet = datat + 3 * i * wplt; - lined = datad + i * wpld; - for (j = 0; j < wd; j++) { - rval = GET_DATA_BYTE(linet, j); - gval = GET_DATA_BYTE(linet + wplt, j); - bval = GET_DATA_BYTE(linet + 2 * wplt, j); - if (order == L_SUBPIXEL_ORDER_VRGB) - composeRGBPixel(rval, gval, bval, &lined[j]); - else /* order VBGR */ - composeRGBPixel(bval, gval, rval, &lined[j]); - } - } - } - - pixDestroy(&pix1); - pixDestroy(&pix2); - return pixd; -} - - -/*! - * \brief pixConvertColorToSubpixelRGB() - * - * \param[in] pixs 32 bpp or colormapped - * \param[in] scalex, scaley - * \param[in] order of subpixel rgb color components in - * composition of pixd: - * L_SUBPIXEL_ORDER_RGB, L_SUBPIXEL_ORDER_BGR, - * L_SUBPIXEL_ORDER_VRGB, L_SUBPIXEL_ORDER_VBGR - * \return pixd 32 bpp, or NULL on error - * - *- * Notes: - * (1) If pixs has a colormap, it is removed to 32 bpp rgb. - * If the colormap has no color, pixConvertGrayToSubpixelRGB() - * should be called instead, because it will give the same result - * more efficiently. The function pixConvertToSubpixelRGB() - * will do the best thing for all cases. - * (2) For horizontal subpixel splitting, the input rgb image - * is rescaled by %scaley vertically and by 3.0 times - * %scalex horizontally. Then for each horizontal triplet - * of pixels, the r component of the final pixel is selected - * from the r component of the appropriate pixel in the triplet, - * and likewise for g and b. Vertical subpixel splitting is - * handled similarly. - *- */ -PIX * -pixConvertColorToSubpixelRGB(PIX *pixs, - l_float32 scalex, - l_float32 scaley, - l_int32 order) -{ -l_int32 w, h, d, wd, hd, wplt, wpld, i, j, rval, gval, bval, direction; -l_uint32 *datat, *datad, *linet, *lined; -PIX *pix1, *pix2, *pixd; -PIXCMAP *cmap; - - PROCNAME("pixConvertColorToSubpixelRGB"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - d = pixGetDepth(pixs); - cmap = pixGetColormap(pixs); - if (d != 32 && !cmap) - return (PIX *)ERROR_PTR("pix not 32 bpp & not cmapped", procName, NULL); - if (scalex <= 0.0 || scaley <= 0.0) - return (PIX *)ERROR_PTR("scale factors must be > 0", procName, NULL); - if (order != L_SUBPIXEL_ORDER_RGB && order != L_SUBPIXEL_ORDER_BGR && - order != L_SUBPIXEL_ORDER_VRGB && order != L_SUBPIXEL_ORDER_VBGR) - return (PIX *)ERROR_PTR("invalid subpixel order", procName, NULL); - - direction = - (order == L_SUBPIXEL_ORDER_RGB || order == L_SUBPIXEL_ORDER_BGR) - ? L_HORIZ : L_VERT; - pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); - if (direction == L_HORIZ) - pix2 = pixScale(pix1, 3.0 * scalex, scaley); - else /* L_VERT */ - pix2 = pixScale(pix1, scalex, 3.0 * scaley); - - pixGetDimensions(pix2, &w, &h, NULL); - wd = (direction == L_HORIZ) ? w / 3 : w; - hd = (direction == L_VERT) ? h / 3 : h; - pixd = pixCreate(wd, hd, 32); - pixCopyInputFormat(pixd, pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - datat = pixGetData(pix2); - wplt = pixGetWpl(pix2); - if (direction == L_HORIZ) { - for (i = 0; i < hd; i++) { - linet = datat + i * wplt; - lined = datad + i * wpld; - for (j = 0; j < wd; j++) { - if (order == L_SUBPIXEL_ORDER_RGB) { - extractRGBValues(linet[3 * j], &rval, NULL, NULL); - extractRGBValues(linet[3 * j + 1], NULL, &gval, NULL); - extractRGBValues(linet[3 * j + 2], NULL, NULL, &bval); - } else { /* order BGR */ - extractRGBValues(linet[3 * j], NULL, NULL, &bval); - extractRGBValues(linet[3 * j + 1], NULL, &gval, NULL); - extractRGBValues(linet[3 * j + 2], &rval, NULL, NULL); - } - composeRGBPixel(rval, gval, bval, &lined[j]); - } - } - } else { /* L_VERT */ - for (i = 0; i < hd; i++) { - linet = datat + 3 * i * wplt; - lined = datad + i * wpld; - for (j = 0; j < wd; j++) { - if (order == L_SUBPIXEL_ORDER_VRGB) { - extractRGBValues(linet[j], &rval, NULL, NULL); - extractRGBValues((linet + wplt)[j], NULL, &gval, NULL); - extractRGBValues((linet + 2 * wplt)[j], NULL, NULL, &bval); - } else { /* order VBGR */ - extractRGBValues(linet[j], NULL, NULL, &bval); - extractRGBValues((linet + wplt)[j], NULL, &gval, NULL); - extractRGBValues((linet + 2 * wplt)[j], &rval, NULL, NULL); - } - composeRGBPixel(rval, gval, bval, &lined[j]); - } - } - } - - if (pixGetSpp(pixs) == 4) - pixScaleAndTransferAlpha(pixd, pixs, scalex, scaley); - - pixDestroy(&pix1); - pixDestroy(&pix2); - return pixd; -} - - -/*---------------------------------------------------------------------* - * Setting neutral point for min/max boost conversion to gray * - *---------------------------------------------------------------------*/ -/*! - * \brief l_setNeutralBoostVal() - * - * \param[in] val between 1 and 255; typical value is 180 - * \return void - * - *- * Notes: - * (1) This raises or lowers the selected min or max RGB component value, - * depending on if that component is above or below this value. - *- */ -void -l_setNeutralBoostVal(l_int32 val) -{ - PROCNAME("l_setNeutralBoostVal"); - - if (val <= 0) { - L_ERROR("invalid reference value for neutral boost\n", procName); - return; - } - var_NEUTRAL_BOOST_VAL = val; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixlabel.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixlabel.c deleted file mode 100644 index 576ea55a..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixlabel.c +++ /dev/null @@ -1,637 +0,0 @@ -/*====================================================================* - - 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 pixlabel.c - *- * - * Label pixels by an index for connected component membership - * PIX *pixConnCompTransform() - * - * Label pixels by the area of their connected component - * PIX *pixConnCompAreaTransform() - * - * Label pixels to allow incremental computation of connected components - * l_int32 pixConnCompIncrInit() - * l_int32 pixConnCompIncrAdd() - * l_int32 pixGetSortedNeighborValues() - * - * Label pixels with spatially-dependent color coding - * PIX *pixLocToColorTransform() - * - * Pixels get labelled in various ways throughout the leptonica library, - * but most of the labelling is implicit, where the new value isn't - * even considered to be a label -- it is just a transformed pixel value - * that may be transformed again by another operation. Quantization - * by thresholding, and dilation by a structuring element, are examples - * of these typical image processing operations. - * - * However, there are some explicit labelling procedures that are useful - * as end-points of analysis, where it typically would not make sense - * to do further image processing on the result. Assigning false color - * based on pixel properties is an example of such labelling operations. - * Such operations typically have 1 bpp input images, and result - * in grayscale or color images. - * - * The procedures in this file are concerned with such explicit labelling. - * Some of these labelling procedures are also in other places in leptonica: - * - * runlength.c: - * This file has two labelling transforms based on runlengths: - * pixStrokeWidthTransform() and pixvRunlengthTransform(). - * The pixels are labelled based on the width of the "stroke" to - * which they belong, or on the length of the horizontal or - * vertical run in which they are a member. Runlengths can easily - * be filtered using a threshold. - * - * pixafunc2.c: - * This file has an operation, pixaDisplayRandomCmap(), that - * randomly labels pix in a pixa (that are typically found using - * pixConnComp) with up to 256 values, and assigns each value to - * a random colormap color. - * - * seedfill.c: - * This file has pixDistanceFunction(), that labels each pixel with - * its distance from either the foreground or the background. - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include -#include "allheaders.h" - -/*-----------------------------------------------------------------------* - * Label pixels by an index for connected component membership * - *-----------------------------------------------------------------------*/ -/*! - * \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 - * - * - * 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. - *- */ -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; -} - - -/*-----------------------------------------------------------------------* - * Label pixels by the area of their connected component * - *-----------------------------------------------------------------------*/ -/*! - * \brief pixConnCompAreaTransform() - * - * \param[in] pixs 1 bpp - * \param[in] connect connectivity: 4 or 8 - * \return pixd 32 bpp, 1 spp, or NULL on error - * - *- * Notes: - * (1) The pixel values in pixd label the area of the fg component - * to which the pixel belongs. Pixels in the bg are labelled 0. - * (2) For purposes of visualization, the output can be converted - * to 8 bpp, using pixConvert32To8() or pixMaxDynamicRange(). - *- */ -PIX * -pixConnCompAreaTransform(PIX *pixs, - l_int32 connect) -{ -l_int32 i, n, npix, w, h, xb, yb, wb, hb; -l_int32 *tab8; -BOXA *boxa; -PIX *pix1, *pix2, *pixd; -PIXA *pixa; - - PROCNAME("pixConnCompAreaTransform"); - - 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); - - boxa = pixConnComp(pixs, &pixa, connect); - n = pixaGetCount(pixa); - boxaDestroy(&boxa); - pixGetDimensions(pixs, &w, &h, NULL); - pixd = pixCreate(w, h, 32); - pixSetSpp(pixd, 1); - if (n == 0) { /* no fg */ - pixaDestroy(&pixa); - return pixd; - } - - /* Label each component and blit it in */ - tab8 = makePixelSumTab8(); - for (i = 0; i < n; i++) { - pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb); - pix1 = pixaGetPix(pixa, i, L_CLONE); - pixCountPixels(pix1, &npix, tab8); - pix2 = pixConvert1To32(NULL, pix1, 0, npix); - pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pix2, 0, 0); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - - pixaDestroy(&pixa); - LEPT_FREE(tab8); - return pixd; -} - - -/*-------------------------------------------------------------------------* - * Label pixels to allow incremental computation of connected components * - *-------------------------------------------------------------------------*/ -/*! - * \brief pixConnCompIncrInit() - * - * \param[in] pixs 1 bpp - * \param[in] conn connectivity: 4 or 8 - * \param[out] ppixd 32 bpp, with c.c. labelled - * \param[out] pptaa with pixel locations indexed by c.c. - * \param[out] pncc initial number of c.c. - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This labels the connected components in a 1 bpp pix, and - * additionally sets up a ptaa that lists the locations of pixels - * in each of the components. - * (2) It can be used to initialize the output image and arrays for - * an application that maintains information about connected - * components incrementally as pixels are added. - * (3) pixs can be empty or have some foreground pixels. - * (4) The connectivity is stored in pixd->special. - * (5) Always initialize with the first pta in ptaa being empty - * and representing the background value (index 0) in the pix. - *- */ -l_ok -pixConnCompIncrInit(PIX *pixs, - l_int32 conn, - PIX **ppixd, - PTAA **pptaa, - l_int32 *pncc) -{ -l_int32 empty, w, h, ncc; -PIX *pixd; -PTA *pta; -PTAA *ptaa; - - PROCNAME("pixConnCompIncrInit"); - - if (ppixd) *ppixd = NULL; - if (pptaa) *pptaa = NULL; - if (pncc) *pncc = 0; - if (!ppixd || !pptaa || !pncc) - return ERROR_INT("&pixd, &ptaa, &ncc not all defined", procName, 1); - if (!pixs || pixGetDepth(pixs) != 1) - return ERROR_INT("pixs undefined or not 1 bpp", procName, 1); - if (conn != 4 && conn != 8) - return ERROR_INT("connectivity must be 4 or 8", procName, 1); - - pixGetDimensions(pixs, &w, &h, NULL); - pixZero(pixs, &empty); - if (empty) { - *ppixd = pixCreate(w, h, 32); - pixSetSpp(*ppixd, 1); - pixSetSpecial(*ppixd, conn); - *pptaa = ptaaCreate(0); - pta = ptaCreate(1); - ptaaAddPta(*pptaa, pta, L_INSERT); /* reserve index 0 for background */ - return 0; - } - - /* Set up the initial labeled image and indexed pixel arrays */ - if ((pixd = pixConnCompTransform(pixs, conn, 32)) == NULL) - return ERROR_INT("pixd not made", procName, 1); - pixSetSpecial(pixd, conn); - *ppixd = pixd; - if ((ptaa = ptaaIndexLabeledPixels(pixd, &ncc)) == NULL) - return ERROR_INT("ptaa not made", procName, 1); - *pptaa = ptaa; - *pncc = ncc; - return 0; -} - - -/*! - * \brief pixConnCompIncrAdd() - * - * \param[in] pixs 32 bpp, with pixels labeled by c.c. - * \param[in] ptaa with each pta of pixel locations indexed by c.c. - * \param[out] pncc number of c.c - * \param[in] x,y location of added pixel - * \param[in] debug 0 for no output; otherwise output whenever - * debug <= nvals, up to debug == 3 - * \return -1 if nothing happens; 0 if a pixel is added; 1 on error - * - *- * Notes: - * (1) This adds a pixel and updates the labeled connected components. - * Before calling this function, initialize the process using - * pixConnCompIncrInit(). - * (2) As a result of adding a pixel, one of the following can happen, - * depending on the number of neighbors with non-zero value: - * (a) nothing: the pixel is already a member of a c.c. - * (b) no neighbors: a new component is added, increasing the - * number of c.c. - * (c) one neighbor: the pixel is added to an existing c.c. - * (d) more than one neighbor: the added pixel causes joining of - * two or more c.c., reducing the number of c.c. A maximum - * of 4 c.c. can be joined. - * (3) When two c.c. are joined, the pixels in the larger index are - * relabeled to those of the smaller in pixs, and their locations - * are transferred to the pta with the smaller index in the ptaa. - * The pta corresponding to the larger index is then deleted. - * (4) This is an efficient implementation of a "union-find" operation, - * which supports the generation and merging of disjoint sets - * of pixels. This function can be called about 1.3 million times - * per second. - *- */ -l_int32 -pixConnCompIncrAdd(PIX *pixs, - PTAA *ptaa, - l_int32 *pncc, - l_float32 x, - l_float32 y, - l_int32 debug) -{ -l_int32 conn, i, j, w, h, count, nvals, ns, firstindex; -l_uint32 val; -l_int32 *neigh; -PTA *ptas, *ptad; - - PROCNAME("pixConnCompIncrAdd"); - - if (!pixs || pixGetDepth(pixs) != 32) - return ERROR_INT("pixs not defined or not 32 bpp", procName, 1); - if (!ptaa) - return ERROR_INT("ptaa not defined", procName, 1); - if (!pncc) - return ERROR_INT("&ncc not defined", procName, 1); - conn = pixs->special; - if (conn != 4 && conn != 8) - return ERROR_INT("connectivity must be 4 or 8", procName, 1); - pixGetDimensions(pixs, &w, &h, NULL); - if (x < 0 || x >= w) - return ERROR_INT("invalid x pixel location", procName, 1); - if (y < 0 || y >= h) - return ERROR_INT("invalid y pixel location", procName, 1); - - pixGetPixel(pixs, x, y, &val); - if (val > 0) /* already belongs to a set */ - return -1; - - /* Find unique neighbor pixel values in increasing order of value. - * If %nvals > 0, these are returned in the %neigh array, which - * is of size %nvals. Note that the pixel values in each - * connected component are used as the index into the pta - * array of the ptaa, giving the pixel locations. */ - pixGetSortedNeighborValues(pixs, x, y, conn, &neigh, &nvals); - - /* If there are no neighbors, just add a new component */ - if (nvals == 0) { - count = ptaaGetCount(ptaa); - pixSetPixel(pixs, x, y, count); - ptas = ptaCreate(1); - ptaAddPt(ptas, x, y); - ptaaAddPta(ptaa, ptas, L_INSERT); - *pncc += 1; - LEPT_FREE(neigh); - return 0; - } - - /* Otherwise, there is at least one neighbor. Add the pixel - * to the first neighbor c.c. */ - firstindex = neigh[0]; - pixSetPixel(pixs, x, y, firstindex); - ptaaAddPt(ptaa, neigh[0], x, y); - if (nvals == 1) { - if (debug == 1) - lept_stderr("nvals = %d: neigh = (%d)\n", nvals, neigh[0]); - LEPT_FREE(neigh); - return 0; - } - - /* If nvals > 1, there are at least 2 neighbors, so this pixel - * joins at least one pair of existing c.c. Join each component - * to the first component in the list, which is the one with - * the smallest integer label. This is done in two steps: - * (a) re-label the pixels in the component to the label of the - * first component, and - * (b) save the pixel locations in the pta for the first component. */ - if (nvals == 2) { - if (debug >= 1 && debug <= 2) { - lept_stderr("nvals = %d: neigh = (%d,%d)\n", nvals, - neigh[0], neigh[1]); - } - } else if (nvals == 3) { - if (debug >= 1 && debug <= 3) { - lept_stderr("nvals = %d: neigh = (%d,%d,%d)\n", nvals, - neigh[0], neigh[1], neigh[2]); - } - } else { /* nvals == 4 */ - if (debug >= 1 && debug <= 4) { - lept_stderr("nvals = %d: neigh = (%d,%d,%d,%d)\n", nvals, - neigh[0], neigh[1], neigh[2], neigh[3]); - } - } - ptad = ptaaGetPta(ptaa, firstindex, L_CLONE); - for (i = 1; i < nvals; i++) { - ptas = ptaaGetPta(ptaa, neigh[i], L_CLONE); - ns = ptaGetCount(ptas); - for (j = 0; j < ns; j++) { /* relabel pixels */ - ptaGetPt(ptas, j, &x, &y); - pixSetPixel(pixs, x, y, firstindex); - } - ptaJoin(ptad, ptas, 0, -1); /* add relabeled pixel locations */ - *pncc -= 1; - ptaDestroy(&ptaa->pta[neigh[i]]); - ptaDestroy(&ptas); /* the clone */ - } - ptaDestroy(&ptad); /* the clone */ - LEPT_FREE(neigh); - return 0; -} - - -/*! - * \brief pixGetSortedNeighborValues() - * - * \param[in] pixs 8, 16 or 32 bpp, with pixels labeled by c.c. - * \param[in] x, y location of pixel - * \param[in] conn 4 or 8 connected neighbors - * \param[out] pneigh array of integers, to be filled with - * the values of the neighbors, if any - * \param[out] pnvals the number of unique neighbor values found - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The returned %neigh array is the unique set of neighboring - * pixel values, of size nvals, sorted from smallest to largest. - * The value 0, which represents background pixels that do - * not belong to any set of connected components, is discarded. - * (2) If there are no neighbors, this returns %neigh = NULL; otherwise, - * the caller must free the array. - * (3) For either 4 or 8 connectivity, the maximum number of unique - * neighbor values is 4. - *- */ -l_ok -pixGetSortedNeighborValues(PIX *pixs, - l_int32 x, - l_int32 y, - l_int32 conn, - l_int32 **pneigh, - l_int32 *pnvals) -{ -l_int32 i, npt, index; -l_int32 neigh[4]; -l_uint32 val; -l_float32 fx, fy; -L_ASET *aset; -L_ASET_NODE *node; -PTA *pta; -RB_TYPE key; - - PROCNAME("pixGetSortedNeighborValues"); - - if (pneigh) *pneigh = NULL; - if (pnvals) *pnvals = 0; - if (!pneigh || !pnvals) - return ERROR_INT("&neigh and &nvals not both defined", procName, 1); - if (!pixs || pixGetDepth(pixs) < 8) - return ERROR_INT("pixs not defined or depth < 8", procName, 1); - - /* Identify the locations of nearest neighbor pixels */ - if ((pta = ptaGetNeighborPixLocs(pixs, x, y, conn)) == NULL) - return ERROR_INT("pta of neighbors not made", procName, 1); - - /* Find the pixel values and insert into a set as keys */ - aset = l_asetCreate(L_UINT_TYPE); - npt = ptaGetCount(pta); - for (i = 0; i < npt; i++) { - ptaGetPt(pta, i, &fx, &fy); - pixGetPixel(pixs, (l_int32)fx, (l_int32)fy, &val); - key.utype = val; - l_asetInsert(aset, key); - } - - /* Extract the set keys and put them into the %neigh array. - * Omit the value 0, which indicates the pixel doesn't - * belong to one of the sets of connected components. */ - node = l_asetGetFirst(aset); - index = 0; - while (node) { - val = node->key.utype; - if (val > 0) - neigh[index++] = (l_int32)val; - node = l_asetGetNext(node); - } - *pnvals = index; - if (index > 0) { - *pneigh = (l_int32 *)LEPT_CALLOC(index, sizeof(l_int32)); - for (i = 0; i < index; i++) - (*pneigh)[i] = neigh[i]; - } - - ptaDestroy(&pta); - l_asetDestroy(&aset); - return 0; -} - - -/*-----------------------------------------------------------------------* - * Label pixels with spatially-dependent color coding * - *-----------------------------------------------------------------------*/ -/*! - * \brief pixLocToColorTransform() - * - * \param[in] pixs 1 bpp - * \return pixd 32 bpp rgb, or NULL on error - * - *- * Notes: - * (1) This generates an RGB image where each component value - * is coded depending on the (x.y) location and the size - * of the fg connected component that the pixel in pixs belongs to. - * It is independent of the 4-fold orthogonal orientation, and - * only weakly depends on translations and small angle rotations. - * Background pixels are black. - * (2) Such encodings can be compared between two 1 bpp images - * by performing this transform and calculating the - * "earth-mover" distance on the resulting R,G,B histograms. - *- */ -PIX * -pixLocToColorTransform(PIX *pixs) -{ -l_int32 w, h, w2, h2, wpls, wplr, wplg, wplb, wplcc, i, j, rval, gval, bval; -l_float32 invw2, invh2; -l_uint32 *datas, *datar, *datag, *datab, *datacc; -l_uint32 *lines, *liner, *lineg, *lineb, *linecc; -PIX *pix1, *pixcc, *pixr, *pixg, *pixb, *pixd; - - PROCNAME("pixLocToColorTransform"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - - /* Label each pixel with the area of the c.c. to which it belongs. - * Clip the result to 255 in an 8 bpp pix. This is used for - * the blue component of pixd. */ - pixGetDimensions(pixs, &w, &h, NULL); - w2 = w / 2; - h2 = h / 2; - invw2 = 255.0 / (l_float32)w2; - invh2 = 255.0 / (l_float32)h2; - pix1 = pixConnCompAreaTransform(pixs, 8); - pixcc = pixConvert32To8(pix1, L_LS_TWO_BYTES, L_CLIP_TO_FF); - pixDestroy(&pix1); - - /* Label the red and green components depending on the location - * of the fg pixels, in a way that is 4-fold rotationally invariant. */ - pixr = pixCreate(w, h, 8); - pixg = pixCreate(w, h, 8); - pixb = pixCreate(w, h, 8); - wpls = pixGetWpl(pixs); - wplr = pixGetWpl(pixr); - wplg = pixGetWpl(pixg); - wplb = pixGetWpl(pixb); - wplcc = pixGetWpl(pixcc); - datas = pixGetData(pixs); - datar = pixGetData(pixr); - datag = pixGetData(pixg); - datab = pixGetData(pixb); - datacc = pixGetData(pixcc); - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - liner = datar + i * wplr; - lineg = datag + i * wplg; - lineb = datab + i * wplb; - linecc = datacc+ i * wplcc; - for (j = 0; j < w; j++) { - if (GET_DATA_BIT(lines, j) == 0) continue; - if (w < h) { - rval = invh2 * L_ABS((l_float32)(i - h2)); - gval = invw2 * L_ABS((l_float32)(j - w2)); - } else { - rval = invw2 * L_ABS((l_float32)(j - w2)); - gval = invh2 * L_ABS((l_float32)(i - h2)); - } - bval = GET_DATA_BYTE(linecc, j); - SET_DATA_BYTE(liner, j, rval); - SET_DATA_BYTE(lineg, j, gval); - SET_DATA_BYTE(lineb, j, bval); - } - } - pixd = pixCreateRGBImage(pixr, pixg, pixb); - - pixDestroy(&pixcc); - pixDestroy(&pixr); - pixDestroy(&pixg); - pixDestroy(&pixb); - return pixd; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixtiling.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixtiling.c deleted file mode 100644 index 480a3d1d..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pixtiling.c +++ /dev/null @@ -1,423 +0,0 @@ -/*====================================================================* - - 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 pixtiling.c - *- * - * PIXTILING *pixTilingCreate() - * void *pixTilingDestroy() - * l_int32 pixTilingGetCount() - * l_int32 pixTilingGetSize() - * PIX *pixTilingGetTile() - * l_int32 pixTilingNoStripOnPaint() - * l_int32 pixTilingPaintTile() - * - * This provides a simple way to split an image into tiles - * and to perform operations independently on each tile. - * - * The tile created with pixTilingGetTile() can have pixels in - * adjacent tiles for computation. The number of extra pixels - * on each side of the tile is given by an 'overlap' parameter - * to pixTilingCreate(). For tiles at the boundary of - * the input image, quasi-overlap pixels are created by reflection - * symmetry into the tile. - * - * Here's a typical intended usage. Suppose you want to parallelize - * the operation on an image, by operating on tiles. For each - * tile, you want to generate an in-place image result at the same - * resolution. Suppose you choose a one-dimensional vertical tiling, - * where the desired tile width is 256 pixels and the overlap is - * 30 pixels on left and right sides: - * - * PIX *pixd = pixCreateTemplate(pixs); // output - * PIXTILING *pt = pixTilingCreate(pixs, 0, 1, 256, 30, 0); - * pixTilingGetCount(pt, &nx, NULL); - * for (j = 0; j < nx; j++) { - * PIX *pixt = pixTilingGetTile(pt, 0, j); - * SomeInPlaceOperation(pixt, 30, 0, ...); - * pixTilingPaintTile(pixd, 0, j, pixt, pt); - * pixDestroy(&pixt); - * } - * - * In this example, note the following: - * ~ The unspecfified in-place operation could instead generate - * a new pix. If this is done, the resulting pix must be the - * same size as pixt, because pixTilingPaintTile() makes that - * assumption, removing the overlap pixels before painting - * into the destination. - * ~ The 'overlap' parameters have been included in your function, - * to indicate which pixels are not in the exterior overlap region. - * You will need to change only pixels that are not in the overlap - * region, because those are the pixels that will be painted - * into the destination. - * ~ For tiles on the outside of the image, mirrored pixels are - * added to substitute for the overlap that is added to interior - * tiles. This allows you to implement your function without - * reference to which tile it is; no special coding is necessary - * for pixels that are near the image boundary. - * ~ The tiles are labeled by (i, j) = (row, column), - * and in this example there is one row and nx columns. - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include "allheaders.h" - -/*! - * \brief pixTilingCreate() - * - * \param[in] pixs pix to be tiled; any depth; colormap OK - * \param[in] nx number of tiles across image - * \param[in] ny number of tiles down image - * \param[in] w desired width of each tile - * \param[in] h desired height of each tile - * \param[in] xoverlap overlap into neighboring tiles on each side - * \param[in] yoverlap overlap into neighboring tiles above and below - * \return pixtiling, or NULL on error - * - * - * Notes: - * (1) We put a clone of pixs in the PixTiling. - * (2) The input to pixTilingCreate() for horizontal tiling can be - * either the number of tiles across the image or the approximate - * width of the tiles. If the latter, the actual width will be - * determined by making all tiles but the last of equal width, and - * making the last as close to the others as possible. The same - * consideration is applied independently to the vertical tiling. - * To specify tile width, set nx = 0; to specify the number of - * tiles horizontally across the image, set w = 0. - * (3) If pixs is to be tiled in one-dimensional strips, use ny = 1 for - * vertical strips and nx = 1 for horizontal strips. - * (4) The overlap must not be larger than the width or height of - * the leftmost or topmost tile(s). - *- */ -PIXTILING * -pixTilingCreate(PIX *pixs, - l_int32 nx, - l_int32 ny, - l_int32 w, - l_int32 h, - l_int32 xoverlap, - l_int32 yoverlap) -{ -l_int32 width, height; -PIXTILING *pt; - - PROCNAME("pixTilingCreate"); - - if (!pixs) - return (PIXTILING *)ERROR_PTR("pixs not defined", procName, NULL); - if (nx < 1 && w < 1) - return (PIXTILING *)ERROR_PTR("invalid width spec", procName, NULL); - if (ny < 1 && h < 1) - return (PIXTILING *)ERROR_PTR("invalid height spec", procName, NULL); - - /* Find the tile width and number of tiles. All tiles except the - * rightmost ones have the same width. The width of the - * rightmost ones are at least the width of the others and - * less than twice that width. Ditto for tile height. */ - pixGetDimensions(pixs, &width, &height, NULL); - if (nx == 0) - nx = L_MAX(1, width / w); - w = width / nx; /* possibly reset */ - if (ny == 0) - ny = L_MAX(1, height / h); - h = height / ny; /* possibly reset */ - if (xoverlap > w || yoverlap > h) { - L_INFO("tile width = %d, tile height = %d\n", procName, w, h); - return (PIXTILING *)ERROR_PTR("overlap too large", procName, NULL); - } - - pt = (PIXTILING *)LEPT_CALLOC(1, sizeof(PIXTILING)); - pt->pix = pixClone(pixs); - pt->xoverlap = xoverlap; - pt->yoverlap = yoverlap; - pt->nx = nx; - pt->ny = ny; - pt->w = w; - pt->h = h; - pt->strip = TRUE; - return pt; -} - - -/*! - * \brief pixTilingDestroy() - * - * \param[in,out] ppt will be set to null before returning - * \return void - */ -void -pixTilingDestroy(PIXTILING **ppt) -{ -PIXTILING *pt; - - PROCNAME("pixTilingDestroy"); - - if (ppt == NULL) { - L_WARNING("ptr address is null!\n", procName); - return; - } - - if ((pt = *ppt) == NULL) - return; - - pixDestroy(&pt->pix); - LEPT_FREE(pt); - *ppt = NULL; - return; -} - - -/*! - * \brief pixTilingGetCount() - * - * \param[in] pt pixtiling - * \param[out] pnx [optional] nx; can be null - * \param[out] pny [optional] ny; can be null - * \return 0 if OK, 1 on error - */ -l_ok -pixTilingGetCount(PIXTILING *pt, - l_int32 *pnx, - l_int32 *pny) -{ - PROCNAME("pixTilingGetCount"); - - if (!pt) - return ERROR_INT("pt not defined", procName, 1); - if (pnx) *pnx = pt->nx; - if (pny) *pny = pt->ny; - return 0; -} - - -/*! - * \brief pixTilingGetSize() - * - * \param[in] pt pixtiling - * \param[out] pw [optional] tile width; can be null - * \param[out] ph [optional] tile height; can be null - * \return 0 if OK, 1 on error - */ -l_ok -pixTilingGetSize(PIXTILING *pt, - l_int32 *pw, - l_int32 *ph) -{ - PROCNAME("pixTilingGetSize"); - - if (!pt) - return ERROR_INT("pt not defined", procName, 1); - if (pw) *pw = pt->w; - if (ph) *ph = pt->h; - return 0; -} - - -/*! - * \brief pixTilingGetTile() - * - * \param[in] pt pixtiling - * \param[in] i tile row index - * \param[in] j tile column index - * \return pixd tile with appropriate boundary (overlap) pixels added, - * or NULL on error - */ -PIX * -pixTilingGetTile(PIXTILING *pt, - l_int32 i, - l_int32 j) -{ -l_int32 wpix, hpix, wt, ht, nx, ny; -l_int32 xoverlap, yoverlap, wtlast, htlast; -l_int32 left, top, xtraleft, xtraright, xtratop, xtrabot, width, height; -BOX *box; -PIX *pixs, *pixt, *pixd; - - PROCNAME("pixTilingGetTile"); - - if (!pt) - return (PIX *)ERROR_PTR("pt not defined", procName, NULL); - if ((pixs = pt->pix) == NULL) - return (PIX *)ERROR_PTR("pix not found", procName, NULL); - pixTilingGetCount(pt, &nx, &ny); - if (i < 0 || i >= ny) - return (PIX *)ERROR_PTR("invalid row index i", procName, NULL); - if (j < 0 || j >= nx) - return (PIX *)ERROR_PTR("invalid column index j", procName, NULL); - - /* Grab the tile with as much overlap as exists within the - * input pix. First, compute the (left, top) coordinates. */ - pixGetDimensions(pixs, &wpix, &hpix, NULL); - pixTilingGetSize(pt, &wt, &ht); - xoverlap = pt->xoverlap; - yoverlap = pt->yoverlap; - wtlast = wpix - wt * (nx - 1); - htlast = hpix - ht * (ny - 1); - left = L_MAX(0, j * wt - xoverlap); - top = L_MAX(0, i * ht - yoverlap); - - /* Get the width and height of the tile, including whatever - * overlap is available. */ - if (nx == 1) - width = wpix; - else if (j == 0) - width = wt + xoverlap; - else if (j == nx - 1) - width = wtlast + xoverlap; - else - width = wt + 2 * xoverlap; - - if (ny == 1) - height = hpix; - else if (i == 0) - height = ht + yoverlap; - else if (i == ny - 1) - height = htlast + yoverlap; - else - height = ht + 2 * yoverlap; - box = boxCreate(left, top, width, height); - pixt = pixClipRectangle(pixs, box, NULL); - boxDestroy(&box); - - /* If no overlap, do not add any special case borders */ - if (xoverlap == 0 && yoverlap == 0) - return pixt; - - /* Add overlap as a mirrored border, in the 8 special cases where - * the tile touches the border of the input pix. The xtratop (etc) - * parameters are required where the tile is either full width - * or full height. */ - xtratop = xtrabot = xtraleft = xtraright = 0; - if (nx == 1) - xtraleft = xtraright = xoverlap; - if (ny == 1) - xtratop = xtrabot = yoverlap; - if (i == 0 && j == 0) - pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright, - yoverlap, xtrabot); - else if (i == 0 && j == nx - 1) - pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap, - yoverlap, xtrabot); - else if (i == ny - 1 && j == 0) - pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright, - xtratop, yoverlap); - else if (i == ny - 1 && j == nx - 1) - pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap, - xtratop, yoverlap); - else if (i == 0) - pixd = pixAddMirroredBorder(pixt, 0, 0, yoverlap, xtrabot); - else if (i == ny - 1) - pixd = pixAddMirroredBorder(pixt, 0, 0, xtratop, yoverlap); - else if (j == 0) - pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright, 0, 0); - else if (j == nx - 1) - pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap, 0, 0); - else - pixd = pixClone(pixt); - pixDestroy(&pixt); - - return pixd; -} - - -/*! - * \brief pixTilingNoStripOnPaint() - * - * \param[in] pt pixtiling - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The default for paint is to strip out the overlap pixels - * that are added by pixTilingGetTile(). However, some - * operations will generate an image with these pixels - * stripped off. This tells the paint operation not - * to strip the added boundary pixels when painting. - *- */ -l_ok -pixTilingNoStripOnPaint(PIXTILING *pt) -{ - PROCNAME("pixTilingNoStripOnPaint"); - - if (!pt) - return ERROR_INT("pt not defined", procName, 1); - pt->strip = FALSE; - return 0; -} - - -/*! - * \brief pixTilingPaintTile() - * - * \param[in] pixd dest: paint tile onto this, without overlap - * \param[in] i tile row index - * \param[in] j tile column index - * \param[in] pixs source: tile to be painted from - * \param[in] pt pixtiling struct - * \return 0 if OK, 1 on error - */ -l_ok -pixTilingPaintTile(PIX *pixd, - l_int32 i, - l_int32 j, - PIX *pixs, - PIXTILING *pt) -{ -l_int32 w, h; - - PROCNAME("pixTilingPaintTile"); - - if (!pixd) - return ERROR_INT("pixd not defined", procName, 1); - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!pt) - return ERROR_INT("pt not defined", procName, 1); - if (i < 0 || i >= pt->ny) - return ERROR_INT("invalid row index i", procName, 1); - if (j < 0 || j >= pt->nx) - return ERROR_INT("invalid column index j", procName, 1); - - /* Strip added border pixels off if requested */ - pixGetDimensions(pixs, &w, &h, NULL); - if (pt->strip == TRUE) { - pixRasterop(pixd, j * pt->w, i * pt->h, - w - 2 * pt->xoverlap, h - 2 * pt->yoverlap, PIX_SRC, - pixs, pt->xoverlap, pt->yoverlap); - } else { - pixRasterop(pixd, j * pt->w, i * pt->h, w, h, PIX_SRC, pixs, 0, 0); - } - - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pngio.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pngio.c deleted file mode 100644 index ff602a29..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pngio.c +++ /dev/null @@ -1,2117 +0,0 @@ -/*====================================================================* - - Copyright (C) 2001 Leptonica. All rights reserved. - - Copyright (C) 2017 Milner Technologies, Inc. - - - - 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 pngio.c - *- * - * Reading png through stream - * PIX *pixReadStreamPng() - * - * Reading png header - * l_int32 readHeaderPng() - * l_int32 freadHeaderPng() - * l_int32 readHeaderMemPng() - * - * Reading png metadata - * l_int32 fgetPngResolution() - * l_int32 isPngInterlaced() - * l_int32 fgetPngColormapInfo() - * - * Writing png through stream - * l_int32 pixWritePng() [ special top level ] - * l_int32 pixWriteStreamPng() - * l_int32 pixSetZlibCompression() - * - * Set flag for special read mode - * void l_pngSetReadStrip16To8() - * - * Low-level memio utility (thanks to T. D. Hintz) - * static void memio_png_write_data() - * static void memio_png_flush() - * static void memio_png_read_data() - * static void memio_free() - * - * Reading png from memory - * PIX *pixReadMemPng() - * - * Writing png to memory - * l_int32 pixWriteMemPng() - * - * Documentation: libpng.txt and example.c - * - * On input (decompression from file), palette color images - * are read into an 8 bpp Pix with a colormap, and 24 bpp - * 3 component color images are read into a 32 bpp Pix with - * rgb samples. On output (compression to file), palette color - * images are written as 8 bpp with the colormap, and 32 bpp - * full color images are written compressed as a 24 bpp, - * 3 component color image. - * - * In the following, we use these abbreviations: - * bps == bit/sample - * spp == samples/pixel - * bpp == bits/pixel of image in Pix (memory) - * where each component is referred to as a "sample". - * - * For reading and writing rgb and rgba images, we read and write - * alpha if it exists (spp == 4) and do not read or write if - * it doesn't (spp == 3). The alpha component can be 'removed' - * simply by setting spp to 3. In leptonica, we make relatively - * little explicit use of the alpha sample. Note that the alpha - * sample in the image is also called "alpha transparency", - * "alpha component" and "alpha layer." - * - * To change the zlib compression level, use pixSetZlibCompression() - * before writing the file. The default is for standard png compression. - * The zlib compression value can be set [0 ... 9], with - * 0 no compression (huge files) - * 1 fastest compression - * -1 default compression (equivalent to 6 in latest version) - * 9 best compression - * Note that if you are using the defined constants in zlib instead - * of the compression integers given above, you must include zlib.h. - * - * There is global for determining the size of retained samples: - * var_PNG_STRIP_16_to_8 - * and a function l_pngSetReadStrip16To8() for setting it. - * The default is TRUE, which causes pixRead() to strip each 16 bit - * sample down to 8 bps: - * ~ For 16 bps rgb (16 bps, 3 spp) --> 32 bpp rgb Pix - * ~ For 16 bps gray (16 bps, 1 spp) --> 8 bpp grayscale Pix - * If the variable is set to FALSE, the 16 bit gray samples - * are saved when read; the 16 bit rgb samples return an error. - * Note: results can be non-deterministic if used with - * multi-threaded applications. - * - * Thanks to a memory buffering utility contributed by T. D. Hintz, - * encoding png directly into memory (and decoding from memory) - * is now enabled without the use of any temp files. Unlike with webp, - * it is necessary to preserve the stream interface to enable writing - * pixa to memory. So there are two independent but very similar - * implementations of png reading and writing. - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include "allheaders.h" - -/* --------------------------------------------*/ -#if HAVE_LIBPNG /* defined in environ.h */ -/* --------------------------------------------*/ - -#include "png.h" - -#if HAVE_LIBZ -#include "zlib.h" -#else -#define Z_DEFAULT_COMPRESSION (-1) -#endif /* HAVE_LIBZ */ - -/* ------------------ Set default for read option -------------------- */ - /* Strip 16 bpp --> 8 bpp on reading png; default is for stripping. - * If you don't strip, you can't read the gray-alpha spp = 2 images. */ -static l_int32 var_PNG_STRIP_16_TO_8 = 1; - -#ifndef NO_CONSOLE_IO -#define DEBUG_READ 0 -#define DEBUG_WRITE 0 -#endif /* ~NO_CONSOLE_IO */ - - -/*---------------------------------------------------------------------* - * Reading png through stream * - *---------------------------------------------------------------------*/ -/*! - * \brief pixReadStreamPng() - * - * \param[in] fp file stream - * \return pix, or NULL on error - * - * - * Notes: - * (1) If called from pixReadStream(), the stream is positioned - * at the beginning of the file. - * (2) To do sequential reads of png format images from a stream, - * use pixReadStreamPng() - * (3) Any image with alpha is converted to RGBA (spp = 4, with - * equal red, green and blue channels) on reading. - * There are three important cases with alpha: - * (a) grayscale-with-alpha (spp = 2), where bpp = 8, and each - * pixel has an associated alpha (transparency) value - * in the second component of the image data. - * (b) spp = 1, d = 1 with colormap and alpha in the trans array. - * Transparency is usually associated with the white background. - * (c) spp = 1, d = 8 with colormap and alpha in the trans array. - * Each color in the colormap has a separate transparency value. - * (4) We use the high level png interface, where the transforms are set - * up in advance and the header and image are read with a single - * call. The more complicated interface, where the header is - * read first and the buffers for the raster image are user- - * allocated before reading the image, works for single images, - * but I could not get it to work properly for the successive - * png reads that are required by pixaReadStream(). - *- */ -PIX * -pixReadStreamPng(FILE *fp) -{ -l_uint8 byte; -l_int32 rval, gval, bval; -l_int32 i, j, k, index, ncolors, bitval; -l_int32 wpl, d, spp, cindex, tRNS; -l_uint32 png_transforms; -l_uint32 *data, *line, *ppixel; -int num_palette, num_text, num_trans; -png_byte bit_depth, color_type, channels; -png_uint_32 w, h, rowbytes; -png_uint_32 xres, yres; -png_bytep rowptr, trans; -png_bytep *row_pointers; -png_structp png_ptr; -png_infop info_ptr, end_info; -png_colorp palette; -png_textp text_ptr; /* ptr to text_chunk */ -PIX *pix, *pix1; -PIXCMAP *cmap; - - PROCNAME("pixReadStreamPng"); - - if (!fp) - return (PIX *)ERROR_PTR("fp not defined", procName, NULL); - pix = NULL; - - /* Allocate the 3 data structures */ - if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, - (png_voidp)NULL, NULL, NULL)) == NULL) - return (PIX *)ERROR_PTR("png_ptr not made", procName, NULL); - - if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { - png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); - return (PIX *)ERROR_PTR("info_ptr not made", procName, NULL); - } - - if ((end_info = png_create_info_struct(png_ptr)) == NULL) { - png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); - return (PIX *)ERROR_PTR("end_info not made", procName, NULL); - } - - /* Set up png setjmp error handling */ - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - return (PIX *)ERROR_PTR("internal png error", procName, NULL); - } - - png_init_io(png_ptr, fp); - - /* ---------------------------------------------------------- * - * - Set the transforms flags. Whatever happens here, - * NEVER invert 1 bpp using PNG_TRANSFORM_INVERT_MONO. - * - Do not use PNG_TRANSFORM_EXPAND, which would - * expand all images with bpp < 8 to 8 bpp. - * - Strip 16 --> 8 if reading 16-bit gray+alpha - * ---------------------------------------------------------- */ - /* To strip 16 --> 8 bit depth, use PNG_TRANSFORM_STRIP_16 */ - if (var_PNG_STRIP_16_TO_8 == 1) { /* our default */ - png_transforms = PNG_TRANSFORM_STRIP_16; - } else { - png_transforms = PNG_TRANSFORM_IDENTITY; - L_INFO("not stripping 16 --> 8 in png reading\n", procName); - } - - /* Read it */ - png_read_png(png_ptr, info_ptr, png_transforms, NULL); - - row_pointers = png_get_rows(png_ptr, info_ptr); - w = png_get_image_width(png_ptr, info_ptr); - h = png_get_image_height(png_ptr, info_ptr); - bit_depth = png_get_bit_depth(png_ptr, info_ptr); - rowbytes = png_get_rowbytes(png_ptr, info_ptr); - color_type = png_get_color_type(png_ptr, info_ptr); - channels = png_get_channels(png_ptr, info_ptr); - spp = channels; - tRNS = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? 1 : 0; - - if (spp == 1) { - d = bit_depth; - } else { /* spp == 2 (gray + alpha), spp == 3 (rgb), spp == 4 (rgba) */ - d = 4 * bit_depth; - } - - /* Remove if/when this is implemented for all bit_depths */ - if (spp != 1 && bit_depth != 8) { - L_ERROR("spp = %d and bps = %d != 8\n" - "turn on 16 --> 8 stripping\n", procName, spp, bit_depth); - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - return (PIX *)ERROR_PTR("not implemented for this image", - procName, NULL); - } - - cmap = NULL; - if (color_type == PNG_COLOR_TYPE_PALETTE || - color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */ - png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); - cmap = pixcmapCreate(d); /* spp == 1 */ - for (cindex = 0; cindex < num_palette; cindex++) { - rval = palette[cindex].red; - gval = palette[cindex].green; - bval = palette[cindex].blue; - pixcmapAddColor(cmap, rval, gval, bval); - } - } - - if ((pix = pixCreate(w, h, d)) == NULL) { - pixcmapDestroy(&cmap); - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - return (PIX *)ERROR_PTR("pix not made", procName, NULL); - } - pixSetInputFormat(pix, IFF_PNG); - wpl = pixGetWpl(pix); - data = pixGetData(pix); - pixSetColormap(pix, cmap); - pixSetSpp(pix, spp); - - if (spp == 1 && !tRNS) { /* copy straight from buffer to pix */ - for (i = 0; i < h; i++) { - line = data + i * wpl; - rowptr = row_pointers[i]; - for (j = 0; j < rowbytes; j++) { - SET_DATA_BYTE(line, j, rowptr[j]); - } - } - } else if (spp == 2) { /* grayscale + alpha; convert to RGBA */ - L_INFO("converting (gray + alpha) ==> RGBA\n", procName); - for (i = 0; i < h; i++) { - ppixel = data + i * wpl; - rowptr = row_pointers[i]; - for (j = k = 0; j < w; j++) { - /* Copy gray value into r, g and b */ - SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k]); - SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k]); - SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); - SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); - ppixel++; - } - } - pixSetSpp(pix, 4); /* we do not support 2 spp pix */ - } else if (spp == 3 || spp == 4) { - for (i = 0; i < h; i++) { - ppixel = data + i * wpl; - rowptr = row_pointers[i]; - for (j = k = 0; j < w; j++) { - SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]); - SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]); - SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); - if (spp == 3) /* set to opaque; some readers are buggy */ - SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, 255); - else /* spp == 4 */ - SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); - ppixel++; - } - } - } - - /* Special spp == 1 cases with transparency: - * (1) 8 bpp without colormap; assume full transparency - * (2) 1 bpp with colormap + trans array (for alpha) - * (3) 8 bpp with colormap + trans array (for alpha) - * These all require converting to RGBA */ - if (spp == 1 && tRNS) { - if (!cmap) { - /* Case 1: make fully transparent RGBA image */ - L_INFO("transparency, 1 spp, no colormap, no transparency array: " - "convention is fully transparent image\n", procName); - L_INFO("converting (fully transparent 1 spp) ==> RGBA\n", procName); - pixDestroy(&pix); - pix = pixCreate(w, h, 32); /* init to alpha = 0 (transparent) */ - pixSetSpp(pix, 4); - } else { - L_INFO("converting (cmap + alpha) ==> RGBA\n", procName); - - /* Grab the transparency array */ - png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); - if (!trans) { /* invalid png file */ - pixDestroy(&pix); - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - return (PIX *)ERROR_PTR("cmap, tRNS, but no transparency array", - procName, NULL); - } - - /* Save the cmap and destroy the pix */ - cmap = pixcmapCopy(pixGetColormap(pix)); - ncolors = pixcmapGetCount(cmap); - pixDestroy(&pix); - - /* Start over with 32 bit RGBA */ - pix = pixCreate(w, h, 32); - wpl = pixGetWpl(pix); - data = pixGetData(pix); - pixSetSpp(pix, 4); - -#if DEBUG_READ - lept_stderr("ncolors = %d, num_trans = %d\n", - ncolors, num_trans); - for (i = 0; i < ncolors; i++) { - pixcmapGetColor(cmap, i, &rval, &gval, &bval); - if (i < num_trans) { - lept_stderr("(r,g,b,a) = (%d,%d,%d,%d)\n", - rval, gval, bval, trans[i]); - } else { - lept_stderr("(r,g,b,a) = (%d,%d,%d,<<255>>)\n", - rval, gval, bval); - } - } -#endif /* DEBUG_READ */ - - /* Extract the data and convert to RGBA */ - if (d == 1) { - /* Case 2: 1 bpp with transparency (usually) behind white */ - L_INFO("converting 1 bpp cmap with alpha ==> RGBA\n", procName); - if (num_trans == 1) - L_INFO("num_trans = 1; second color opaque by default\n", - procName); - for (i = 0; i < h; i++) { - ppixel = data + i * wpl; - rowptr = row_pointers[i]; - for (j = 0, index = 0; j < rowbytes; j++) { - byte = rowptr[j]; - for (k = 0; k < 8 && index < w; k++, index++) { - bitval = (byte >> (7 - k)) & 1; - pixcmapGetColor(cmap, bitval, &rval, &gval, &bval); - composeRGBPixel(rval, gval, bval, ppixel); - SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, - bitval < num_trans ? trans[bitval] : 255); - ppixel++; - } - } - } - } else if (d == 8) { - /* Case 3: 8 bpp with cmap and associated transparency */ - L_INFO("converting 8 bpp cmap with alpha ==> RGBA\n", procName); - for (i = 0; i < h; i++) { - ppixel = data + i * wpl; - rowptr = row_pointers[i]; - for (j = 0; j < w; j++) { - index = rowptr[j]; - pixcmapGetColor(cmap, index, &rval, &gval, &bval); - composeRGBPixel(rval, gval, bval, ppixel); - /* Assume missing entries to be 255 (opaque) - * according to the spec: - * http://www.w3.org/TR/PNG/#11tRNS */ - SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, - index < num_trans ? trans[index] : 255); - ppixel++; - } - } - } else { - L_ERROR("spp == 1, cmap, trans array, invalid depth: %d\n", - procName, d); - } - pixcmapDestroy(&cmap); - } - } - -#if DEBUG_READ - if (cmap) { - for (i = 0; i < 16; i++) { - lept_stderr("[%d] = %d\n", i, ((l_uint8 *)(cmap->array))[i]); - } - } -#endif /* DEBUG_READ */ - - /* Final adjustments for bpp = 1. - * + If there is no colormap, the image must be inverted because - * png stores black pixels as 0. - * + We have already handled the case of cmapped, 1 bpp pix - * with transparency, where the output pix is 32 bpp RGBA. - * If there is no transparency but the pix has a colormap, - * we remove the colormap, because functions operating on - * 1 bpp images in leptonica assume no colormap. - * + The colormap must be removed in such a way that the pixel - * values are not changed. If the values are only black and - * white, we return a 1 bpp image; if gray, return an 8 bpp pix; - * otherwise, return a 32 bpp rgb pix. - * - * Note that we cannot use the PNG_TRANSFORM_INVERT_MONO flag - * to do the inversion, because that flag (since version 1.0.9) - * inverts 8 bpp grayscale as well, which we don't want to do. - * (It also doesn't work if there is a colormap.) - * - * Note that if the input png is a 1-bit with colormap and - * transparency, it has already been rendered as a 32 bpp, - * spp = 4 rgba pix. - */ - if (pixGetDepth(pix) == 1) { - if (!cmap) { - pixInvert(pix, pix); - } else { - L_INFO("removing opaque cmap from 1 bpp\n", procName); - pix1 = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); - pixDestroy(&pix); - pix = pix1; - } - } - - xres = png_get_x_pixels_per_meter(png_ptr, info_ptr); - yres = png_get_y_pixels_per_meter(png_ptr, info_ptr); - pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */ - pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */ - - /* Get the text if there is any */ - png_get_text(png_ptr, info_ptr, &text_ptr, &num_text); - if (num_text && text_ptr) - pixSetText(pix, text_ptr->text); - - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - return pix; -} - - -/*---------------------------------------------------------------------* - * Reading png header * - *---------------------------------------------------------------------*/ -/*! - * \brief readHeaderPng() - * - * \param[in] filename - * \param[out] pw [optional] - * \param[out] ph [optional] - * \param[out] pbps [optional] bits/sample - * \param[out] pspp [optional] samples/pixel - * \param[out] piscmap [optional] - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) If there is a colormap, iscmap is returned as 1; else 0. - * (2) For gray+alpha, although the png records bps = 16, we - * consider this as two 8 bpp samples (gray and alpha). - * When a gray+alpha is read, it is converted to 32 bpp RGBA. - *- */ -l_ok -readHeaderPng(const char *filename, - l_int32 *pw, - l_int32 *ph, - l_int32 *pbps, - l_int32 *pspp, - l_int32 *piscmap) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("readHeaderPng"); - - if (pw) *pw = 0; - if (ph) *ph = 0; - if (pbps) *pbps = 0; - if (pspp) *pspp = 0; - if (piscmap) *piscmap = 0; - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if ((fp = fopenReadStream(filename)) == NULL) - return ERROR_INT("image file not found", procName, 1); - ret = freadHeaderPng(fp, pw, ph, pbps, pspp, piscmap); - fclose(fp); - return ret; -} - - -/*! - * \brief freadHeaderPng() - * - * \param[in] fp file stream - * \param[out] pw [optional] - * \param[out] ph [optional] - * \param[out] pbps [optional] bits/sample - * \param[out] pspp [optional] samples/pixel - * \param[out] piscmap [optional] - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) See readHeaderPng(). We only need the first 40 bytes in the file. - *- */ -l_ok -freadHeaderPng(FILE *fp, - l_int32 *pw, - l_int32 *ph, - l_int32 *pbps, - l_int32 *pspp, - l_int32 *piscmap) -{ -l_int32 nbytes, ret; -l_uint8 data[40]; - - PROCNAME("freadHeaderPng"); - - if (pw) *pw = 0; - if (ph) *ph = 0; - if (pbps) *pbps = 0; - if (pspp) *pspp = 0; - if (piscmap) *piscmap = 0; - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - - nbytes = fnbytesInFile(fp); - if (nbytes < 40) - return ERROR_INT("file too small to be png", procName, 1); - if (fread(data, 1, 40, fp) != 40) - return ERROR_INT("error reading data", procName, 1); - ret = readHeaderMemPng(data, 40, pw, ph, pbps, pspp, piscmap); - return ret; -} - - -/*! - * \brief readHeaderMemPng() - * - * \param[in] data - * \param[in] size 40 bytes is sufficient - * \param[out] pw [optional] - * \param[out] ph [optional] - * \param[out] pbps [optional] bits/sample - * \param[out] pspp [optional] samples/pixel - * \param[out] piscmap [optional] input NULL to ignore - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) See readHeaderPng(). - * (2) png colortypes (see png.h: PNG_COLOR_TYPE_*): - * 0: gray; fully transparent (with tRNS) (1 spp) - * 2: RGB (3 spp) - * 3: colormap; colormap+alpha (with tRNS) (1 spp) - * 4: gray + alpha (2 spp) - * 6: RGBA (4 spp) - * Note: - * 0 and 3 have the alpha information in a tRNS chunk - * 4 and 6 have separate alpha samples with each pixel. - *- */ -l_ok -readHeaderMemPng(const l_uint8 *data, - size_t size, - l_int32 *pw, - l_int32 *ph, - l_int32 *pbps, - l_int32 *pspp, - l_int32 *piscmap) -{ -l_uint16 twobytes; -l_uint16 *pshort; -l_int32 colortype, w, h, bps, spp; -l_uint32 *pword; - - PROCNAME("readHeaderMemPng"); - - if (pw) *pw = 0; - if (ph) *ph = 0; - if (pbps) *pbps = 0; - if (pspp) *pspp = 0; - if (piscmap) *piscmap = 0; - if (!data) - return ERROR_INT("data not defined", procName, 1); - if (size < 40) - return ERROR_INT("size < 40", procName, 1); - - /* Check password */ - if (data[0] != 137 || data[1] != 80 || data[2] != 78 || - data[3] != 71 || data[4] != 13 || data[5] != 10 || - data[6] != 26 || data[7] != 10) - return ERROR_INT("not a valid png file", procName, 1); - - pword = (l_uint32 *)data; - pshort = (l_uint16 *)data; - w = convertOnLittleEnd32(pword[4]); - h = convertOnLittleEnd32(pword[5]); - if (w < 1 || h < 1) - return ERROR_INT("invalid w or h", procName, 1); - twobytes = convertOnLittleEnd16(pshort[12]); /* contains depth/sample */ - /* and the color type */ - colortype = twobytes & 0xff; /* color type */ - bps = twobytes >> 8; /* bits/sample */ - - /* Special case with alpha that is extracted as RGBA. - * Note that the cmap+alpha is also extracted as RGBA, - * but only if the tRNS chunk exists, which we can't tell - * by this simple parser.*/ - if (colortype == 4) - L_INFO("gray + alpha: will extract as RGBA (spp = 4)\n", procName); - - if (colortype == 2) { /* RGB */ - spp = 3; - } else if (colortype == 6) { /* RGBA */ - spp = 4; - } else if (colortype == 4) { /* gray + alpha */ - spp = 2; - bps = 8; /* both the gray and alpha are 8-bit samples */ - } else { /* gray (0) or cmap (3) or cmap+alpha (3) */ - spp = 1; - } - if (bps < 1 || bps > 16) { - L_ERROR("invalid bps = %d\n", procName, bps); - return 1; - } - if (pw) *pw = w; - if (ph) *ph = h; - if (pbps) *pbps = bps; - if (pspp) *pspp = spp; - if (piscmap) { - if (colortype & 1) /* palette */ - *piscmap = 1; - else - *piscmap = 0; - } - - return 0; -} - - -/*---------------------------------------------------------------------* - * Reading png metadata * - *---------------------------------------------------------------------*/ -/* - * fgetPngResolution() - * - * Input: fp (file stream opened for read) - * &xres, &yres (resolution in ppi) - * Return: 0 if OK; 1 on error - * - * Notes: - * (1) If neither resolution field is set, this is not an error; - * the returned resolution values are 0 (designating 'unknown'). - * (2) Side-effect: this rewinds the stream. - */ -l_int32 -fgetPngResolution(FILE *fp, - l_int32 *pxres, - l_int32 *pyres) -{ -png_uint_32 xres, yres; -png_structp png_ptr; -png_infop info_ptr; - - PROCNAME("fgetPngResolution"); - - if (pxres) *pxres = 0; - if (pyres) *pyres = 0; - if (!fp) - return ERROR_INT("stream not opened", procName, 1); - if (!pxres || !pyres) - return ERROR_INT("&xres and &yres not both defined", procName, 1); - - /* Make the two required structs */ - if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, - (png_voidp)NULL, NULL, NULL)) == NULL) - return ERROR_INT("png_ptr not made", procName, 1); - if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { - png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); - return ERROR_INT("info_ptr not made", procName, 1); - } - - /* Set up png setjmp error handling. - * Without this, an error calls exit. */ - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); - return ERROR_INT("internal png error", procName, 1); - } - - /* Read the metadata */ - rewind(fp); - png_init_io(png_ptr, fp); - png_read_info(png_ptr, info_ptr); - - xres = png_get_x_pixels_per_meter(png_ptr, info_ptr); - yres = png_get_y_pixels_per_meter(png_ptr, info_ptr); - *pxres = (l_int32)((l_float32)xres / 39.37 + 0.5); /* to ppi */ - *pyres = (l_int32)((l_float32)yres / 39.37 + 0.5); - - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - rewind(fp); - return 0; -} - - -/*! - * \brief isPngInterlaced() - * - * \param[in] filename - * \param[out] pinterlaced 1 if interlaced png; 0 otherwise - * \return 0 if OK, 1 on error - */ -l_ok -isPngInterlaced(const char *filename, - l_int32 *pinterlaced) -{ -l_uint8 buf[32]; -FILE *fp; - - PROCNAME("isPngInterlaced"); - - if (!pinterlaced) - return ERROR_INT("&interlaced not defined", procName, 1); - *pinterlaced = 0; - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - - if ((fp = fopenReadStream(filename)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - if (fread(buf, 1, 32, fp) != 32) { - fclose(fp); - return ERROR_INT("data not read", procName, 1); - } - fclose(fp); - - *pinterlaced = (buf[28] == 0) ? 0 : 1; - return 0; -} - - -/* - * \brief fgetPngColormapInfo() - * - * \param[in] fp file stream opened for read - * \param[out] pcmap optional; use NULL to skip - * \param[out] ptransparency optional; 1 if colormapped with - * transparency, 0 otherwise; use NULL to skip - * \return 0 if OK, 1 on error - * - * Notes: - * (1) The transparency information in a png is in the tRNA array, - * which is separate from the colormap. If this array exists - * and if any element is less than 255, there exists some - * transparency. - * (2) Side-effect: this rewinds the stream. - */ -l_ok -fgetPngColormapInfo(FILE *fp, - PIXCMAP **pcmap, - l_int32 *ptransparency) -{ -l_int32 i, cindex, rval, gval, bval, num_palette, num_trans; -png_byte bit_depth, color_type; -png_bytep trans; -png_colorp palette; -png_structp png_ptr; -png_infop info_ptr; - - PROCNAME("fgetPngColormapInfo"); - - if (pcmap) *pcmap = NULL; - if (ptransparency) *ptransparency = 0; - if (!pcmap && !ptransparency) - return ERROR_INT("no output defined", procName, 1); - if (!fp) - return ERROR_INT("stream not opened", procName, 1); - - /* Make the two required structs */ - if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, - (png_voidp)NULL, NULL, NULL)) == NULL) - return ERROR_INT("png_ptr not made", procName, 1); - if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { - png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); - return ERROR_INT("info_ptr not made", procName, 1); - } - - /* Set up png setjmp error handling. - * Without this, an error calls exit. */ - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - if (pcmap && *pcmap) pixcmapDestroy(pcmap); - return ERROR_INT("internal png error", procName, 1); - } - - /* Read the metadata and check if there is a colormap */ - rewind(fp); - png_init_io(png_ptr, fp); - png_read_info(png_ptr, info_ptr); - color_type = png_get_color_type(png_ptr, info_ptr); - if (color_type != PNG_COLOR_TYPE_PALETTE && - color_type != PNG_COLOR_MASK_PALETTE) { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - return 0; - } - - /* Optionally, read the colormap */ - if (pcmap) { - bit_depth = png_get_bit_depth(png_ptr, info_ptr); - png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); - *pcmap = pixcmapCreate(bit_depth); /* spp == 1 */ - for (cindex = 0; cindex < num_palette; cindex++) { - rval = palette[cindex].red; - gval = palette[cindex].green; - bval = palette[cindex].blue; - pixcmapAddColor(*pcmap, rval, gval, bval); - } - } - - /* Optionally, look for transparency. Note that the colormap - * has been initialized to fully opaque. */ - if (ptransparency && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { - png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); - if (trans) { - for (i = 0; i < num_trans; i++) { - if (trans[i] < 255) { /* not fully opaque */ - *ptransparency = 1; - if (pcmap) pixcmapSetAlpha(*pcmap, i, trans[i]); - } - } - } else { - L_ERROR("transparency array not returned\n", procName); - } - } - - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - rewind(fp); - return 0; -} - - -/*---------------------------------------------------------------------* - * Writing png through stream * - *---------------------------------------------------------------------*/ -/*! - * \brief pixWritePng() - * - * \param[in] filename - * \param[in] pix - * \param[in] gamma - * \return 0 if OK; 1 on error - * - * - * Notes: - * (1) Special version for writing png with a specified gamma. - * When using pixWrite(), no field is given for gamma. - *- */ -l_ok -pixWritePng(const char *filename, - PIX *pix, - l_float32 gamma) -{ -FILE *fp; - - PROCNAME("pixWritePng"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "wb+")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - - if (pixWriteStreamPng(fp, pix, gamma)) { - fclose(fp); - return ERROR_INT("pix not written to stream", procName, 1); - } - - fclose(fp); - return 0; -} - - -/*! - * \brief pixWriteStreamPng() - * - * \param[in] fp file stream - * \param[in] pix - * \param[in] gamma use 0.0 if gamma is not defined - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) If called from pixWriteStream(), the stream is positioned - * at the beginning of the file. - * (2) To do sequential writes of png format images to a stream, - * use pixWriteStreamPng() directly. - * (3) gamma is an optional png chunk. If no gamma value is to be - * placed into the file, use gamma = 0.0. Otherwise, if - * gamma > 0.0, its value is written into the header. - * (4) The use of gamma in png is highly problematic. For an illuminating - * discussion, see: http://hsivonen.iki.fi/png-gamma/ - * (5) What is the effect/meaning of gamma in the png file? This - * gamma, which we can call the 'source' gamma, is the - * inverse of the gamma that was used in enhance.c to brighten - * or darken images. The 'source' gamma is supposed to indicate - * the intensity mapping that was done at the time the - * image was captured. Display programs typically apply a - * 'display' gamma of 2.2 to the output, which is intended - * to linearize the intensity based on the response of - * thermionic tubes (CRTs). Flat panel LCDs have typically - * been designed to give a similar response as CRTs (call it - * "backward compatibility"). The 'display' gamma is - * in some sense the inverse of the 'source' gamma. - * jpeg encoders attached to scanners and cameras will lighten - * the pixels, applying a gamma corresponding to approximately - * a square-root relation of output vs input: - * output = input^(gamma) - * where gamma is often set near 0.4545 (1/gamma is 2.2). - * This is stored in the image file. Then if the display - * program reads the gamma, it will apply a display gamma, - * typically about 2.2; the product is 1.0, and the - * display program produces a linear output. This works because - * the dark colors were appropriately boosted by the scanner, - * as described by the 'source' gamma, so they should not - * be further boosted by the display program. - * (6) As an example, with xv and display, if no gamma is stored, - * the program acts as if gamma were 0.4545, multiplies this by 2.2, - * and does a linear rendering. Taking this as a baseline - * brightness, if the stored gamma is: - * > 0.4545, the image is rendered lighter than baseline - * < 0.4545, the image is rendered darker than baseline - * In contrast, gqview seems to ignore the gamma chunk in png. - * (7) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16 - * and 32. However, it is possible, and in some cases desirable, - * to write out a png file using an rgb pix that has 24 bpp. - * For example, the open source xpdf SplashBitmap class generates - * 24 bpp rgb images. Consequently, we enable writing 24 bpp pix. - * To generate such a pix, you can make a 24 bpp pix without data - * and assign the data array to the pix; e.g., - * pix = pixCreateHeader(w, h, 24); - * pixSetData(pix, rgbdata); - * See pixConvert32To24() for an example, where we get rgbdata - * from the 32 bpp pix. Caution: do not call pixSetPadBits(), - * because the alignment is wrong and you may erase part of the - * last pixel on each line. - * (8) If the pix has a colormap, it is written to file. In most - * situations, the alpha component is 255 for each colormap entry, - * which is opaque and indicates that it should be ignored. - * However, if any alpha component is not 255, it is assumed that - * the alpha values are valid, and they are written to the png - * file in a tRNS segment. On readback, the tRNS segment is - * identified, and the colormapped image with alpha is converted - * to a 4 spp rgba image. - *- */ -l_ok -pixWriteStreamPng(FILE *fp, - PIX *pix, - l_float32 gamma) -{ -char commentstring[] = "Comment"; -l_int32 i, j, k; -l_int32 wpl, d, spp, cmflag, opaque; -l_int32 ncolors, compval; -l_int32 *rmap, *gmap, *bmap, *amap; -l_uint32 *data, *ppixel; -png_byte bit_depth, color_type; -png_byte alpha[256]; -png_uint_32 w, h; -png_uint_32 xres, yres; -png_bytep *row_pointers; -png_bytep rowbuffer; -png_structp png_ptr; -png_infop info_ptr; -png_colorp palette; -PIX *pix1; -PIXCMAP *cmap; -char *text; - - PROCNAME("pixWriteStreamPng"); - - if (!fp) - return ERROR_INT("stream not open", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - /* Allocate the 2 data structures */ - if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, - (png_voidp)NULL, NULL, NULL)) == NULL) - return ERROR_INT("png_ptr not made", procName, 1); - - if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { - png_destroy_write_struct(&png_ptr, (png_infopp)NULL); - return ERROR_INT("info_ptr not made", procName, 1); - } - - /* Set up png setjmp error handling */ - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_write_struct(&png_ptr, &info_ptr); - return ERROR_INT("internal png error", procName, 1); - } - - png_init_io(png_ptr, fp); - - /* With best zlib compression (9), get between 1 and 10% improvement - * over default (6), but the compression is 3 to 10 times slower. - * Use the zlib default (6) as our default compression unless - * pix->special falls in the range [10 ... 19]; then subtract 10 - * to get the compression value. */ - compval = Z_DEFAULT_COMPRESSION; - if (pix->special >= 10 && pix->special < 20) - compval = pix->special - 10; - png_set_compression_level(png_ptr, compval); - - w = pixGetWidth(pix); - h = pixGetHeight(pix); - d = pixGetDepth(pix); - spp = pixGetSpp(pix); - if ((cmap = pixGetColormap(pix))) - cmflag = 1; - else - cmflag = 0; - pixSetPadBits(pix, 0); - - /* Set the color type and bit depth. */ - if (d == 32 && spp == 4) { - bit_depth = 8; - color_type = PNG_COLOR_TYPE_RGBA; /* 6 */ - cmflag = 0; /* ignore if it exists */ - } else if (d == 24 || d == 32) { - bit_depth = 8; - color_type = PNG_COLOR_TYPE_RGB; /* 2 */ - cmflag = 0; /* ignore if it exists */ - } else { - bit_depth = d; - color_type = PNG_COLOR_TYPE_GRAY; /* 0 */ - } - if (cmflag) - color_type = PNG_COLOR_TYPE_PALETTE; /* 3 */ - -#if DEBUG_WRITE - lept_stderr("cmflag = %d, bit_depth = %d, color_type = %d\n", - cmflag, bit_depth, color_type); -#endif /* DEBUG_WRITE */ - - png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type, - PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, - PNG_FILTER_TYPE_BASE); - - /* Store resolution in ppm, if known */ - xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); - yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); - if ((xres == 0) || (yres == 0)) - png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN); - else - png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER); - - if (cmflag) { - pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap); - ncolors = pixcmapGetCount(cmap); - pixcmapIsOpaque(cmap, &opaque); - - /* Make and save the palette */ - palette = (png_colorp)LEPT_CALLOC(ncolors, sizeof(png_color)); - for (i = 0; i < ncolors; i++) { - palette[i].red = (png_byte)rmap[i]; - palette[i].green = (png_byte)gmap[i]; - palette[i].blue = (png_byte)bmap[i]; - alpha[i] = (png_byte)amap[i]; - } - - png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors); - if (!opaque) /* alpha channel has some transparency; assume valid */ - png_set_tRNS(png_ptr, info_ptr, (png_bytep)alpha, - (int)ncolors, NULL); - LEPT_FREE(rmap); - LEPT_FREE(gmap); - LEPT_FREE(bmap); - LEPT_FREE(amap); - } - - /* 0.4545 is treated as the default by some image - * display programs (not gqview). A value > 0.4545 will - * lighten an image as displayed by xv, display, etc. */ - if (gamma > 0.0) - png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma); - - if ((text = pixGetText(pix))) { - png_text text_chunk; - text_chunk.compression = PNG_TEXT_COMPRESSION_NONE; - text_chunk.key = commentstring; - text_chunk.text = text; - text_chunk.text_length = strlen(text); -#ifdef PNG_ITXT_SUPPORTED - text_chunk.itxt_length = 0; - text_chunk.lang = NULL; - text_chunk.lang_key = NULL; -#endif - png_set_text(png_ptr, info_ptr, &text_chunk, 1); - } - - /* Write header and palette info */ - png_write_info(png_ptr, info_ptr); - - if ((d != 32) && (d != 24)) { /* not rgb color */ - /* Generate a temporary pix with bytes swapped. - * For writing a 1 bpp image as png: - * ~ if no colormap, invert the data, because png writes - * black as 0 - * ~ if colormapped, do not invert the data; the two RGBA - * colors can have any value. */ - if (d == 1 && !cmap) { - pix1 = pixInvert(NULL, pix); - pixEndianByteSwap(pix1); - } else { - pix1 = pixEndianByteSwapNew(pix); - } - if (!pix1) { - png_destroy_write_struct(&png_ptr, &info_ptr); - if (cmflag) LEPT_FREE(palette); - return ERROR_INT("pix1 not made", procName, 1); - } - - /* Make and assign array of image row pointers */ - row_pointers = (png_bytep *)LEPT_CALLOC(h, sizeof(png_bytep)); - wpl = pixGetWpl(pix1); - data = pixGetData(pix1); - for (i = 0; i < h; i++) - row_pointers[i] = (png_bytep)(data + i * wpl); - png_set_rows(png_ptr, info_ptr, row_pointers); - - /* Transfer the data */ - png_write_image(png_ptr, row_pointers); - png_write_end(png_ptr, info_ptr); - - if (cmflag) LEPT_FREE(palette); - LEPT_FREE(row_pointers); - pixDestroy(&pix1); - png_destroy_write_struct(&png_ptr, &info_ptr); - return 0; - } - - /* For rgb, compose and write a row at a time */ - data = pixGetData(pix); - wpl = pixGetWpl(pix); - if (d == 24) { /* See note 7 above: special case of 24 bpp rgb */ - for (i = 0; i < h; i++) { - ppixel = data + i * wpl; - png_write_rows(png_ptr, (png_bytepp)&ppixel, 1); - } - } else { /* 32 bpp rgb and rgba. Write out the alpha channel if either - * the pix has 4 spp or writing it is requested anyway */ - rowbuffer = (png_bytep)LEPT_CALLOC(w, 4); - for (i = 0; i < h; i++) { - ppixel = data + i * wpl; - for (j = k = 0; j < w; j++) { - rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); - rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); - rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); - if (spp == 4) - rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL); - ppixel++; - } - - png_write_rows(png_ptr, &rowbuffer, 1); - } - LEPT_FREE(rowbuffer); - } - - png_write_end(png_ptr, info_ptr); - - if (cmflag) - LEPT_FREE(palette); - png_destroy_write_struct(&png_ptr, &info_ptr); - return 0; -} - - -/*! - * \brief pixSetZlibCompression() - * - * \param[in] pix - * \param[in] compval zlib compression value - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Valid zlib compression values are in the interval [0 ... 9], - * where, as defined in zlib.h: - * 0 Z_NO_COMPRESSION - * 1 Z_BEST_SPEED (poorest compression) - * 9 Z_BEST_COMPRESSION - * For the default value, use either of these: - * 6 Z_DEFAULT_COMPRESSION - * -1 (resolves to Z_DEFAULT_COMPRESSION) - * (2) If you use the defined constants in zlib.h instead of the - * compression integers given above, you must include zlib.h. - *- */ -l_ok -pixSetZlibCompression(PIX *pix, - l_int32 compval) -{ - PROCNAME("pixSetZlibCompression"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (compval < 0 || compval > 9) { - L_ERROR("Invalid zlib comp val; using default\n", procName); - compval = Z_DEFAULT_COMPRESSION; - } - pixSetSpecial(pix, 10 + compval); /* valid range [10 ... 19] */ - return 0; -} - - -/*---------------------------------------------------------------------* - * Set flag for stripping 16 bits on reading * - *---------------------------------------------------------------------*/ -/*! - * \brief l_pngSetReadStrip16To8() - * - * \param[in] flag 1 for stripping 16 bpp to 8 bpp on reading; - * 0 for leaving 16 bpp - * \return void - */ -void -l_pngSetReadStrip16To8(l_int32 flag) -{ - var_PNG_STRIP_16_TO_8 = flag; -} - - -/*-------------------------------------------------------------------------* - * Memio utility * - * libpng read/write callback replacements for performing memory I/O * - * * - * Copyright (C) 2017 Milner Technologies, Inc. This content is a * - * component of leptonica and is provided under the terms of the * - * Leptonica license. * - *-------------------------------------------------------------------------*/ - - /*! A node in a linked list of memory buffers that hold I/O content */ -struct MemIOData -{ - char* m_Buffer; /*!< pointer to this node's I/O content */ - l_int32 m_Count; /*!< number of I/O content bytes read or written */ - l_int32 m_Size; /*!< allocated size of m_buffer */ - struct MemIOData *m_Next; /*!< pointer to the next node in the list; */ - /*!< zero if this is the last node */ - struct MemIOData *m_Last; /*!< pointer to the last node in the linked */ - /*!< list. The last node is where new */ - /*!< content is written. */ -}; -typedef struct MemIOData MEMIODATA; - -static void memio_png_write_data(png_structp png_ptr, png_bytep data, - png_size_t length); -static void memio_png_flush(MEMIODATA* pthing); -static void memio_png_read_data(png_structp png_ptr, png_bytep outBytes, - png_size_t byteCountToRead); -static void memio_free(MEMIODATA* pthing); - -static const l_int32 MEMIO_BUFFER_SIZE = 8192; /*! buffer alloc size */ - -/* - * \brief memio_png_write_data() - * - * \param[in] png_ptr - * \param[in] data - * \param[in] len size of array data in bytes - * - *- * Notes: - * (1) This is a libpng callback for writing an image into a - * linked list of memory buffers. - *- */ -static void -memio_png_write_data(png_structp png_ptr, - png_bytep data, - png_size_t len) -{ -MEMIODATA *thing, *last; -l_int32 written = 0; -l_int32 remainingSpace, remainingToWrite; - - thing = (struct MemIOData*)png_get_io_ptr(png_ptr); - last = (struct MemIOData*)thing->m_Last; - if (last->m_Buffer == NULL) { - if (len > MEMIO_BUFFER_SIZE) { - last->m_Buffer = (char *)LEPT_MALLOC(len); - memcpy(last->m_Buffer, data, len); - last->m_Size = last->m_Count = len; - return; - } - - last->m_Buffer = (char *)LEPT_MALLOC(MEMIO_BUFFER_SIZE); - last->m_Size = MEMIO_BUFFER_SIZE; - } - - while (written < len) { - if (last->m_Count == last->m_Size) { - MEMIODATA* next = (MEMIODATA *)LEPT_MALLOC(sizeof(MEMIODATA)); - next->m_Next = NULL; - next->m_Count = 0; - next->m_Last = next; - - last->m_Next = next; - last = thing->m_Last = next; - - last->m_Buffer = (char *)LEPT_MALLOC(MEMIO_BUFFER_SIZE); - last->m_Size = MEMIO_BUFFER_SIZE; - } - - remainingSpace = last->m_Size - last->m_Count; - remainingToWrite = len - written; - if (remainingSpace < remainingToWrite) { - memcpy(last->m_Buffer + last->m_Count, data + written, - remainingSpace); - written += remainingSpace; - last->m_Count += remainingSpace; - } else { - memcpy(last->m_Buffer + last->m_Count, data + written, - remainingToWrite); - written += remainingToWrite; - last->m_Count += remainingToWrite; - } - } -} - - -/* - * \brief memio_png_flush() - * - * \param[in] pthing - * - *- * Notes: - * (1) This consolidates write buffers into a single buffer at the - * haed of the link list of buffers. - *- */ -static void -memio_png_flush(MEMIODATA *pthing) -{ -l_int32 amount = 0; -l_int32 copied = 0; -MEMIODATA *buffer = 0; -char *data = 0; - - /* If the data is in one buffer, give the buffer to the user. */ - if (pthing->m_Next == NULL) return; - - /* Consolidate multiple buffers into one new one; add the buffer - * sizes together. */ - amount = pthing->m_Count; - buffer = pthing->m_Next; - while (buffer != NULL) { - amount += buffer->m_Count; - buffer = buffer->m_Next; - } - - /* Copy data to a new buffer. */ - data = (char *)LEPT_MALLOC(amount); - memcpy(data, pthing->m_Buffer, pthing->m_Count); - copied = pthing->m_Count; - - LEPT_FREE(pthing->m_Buffer); - pthing->m_Buffer = NULL; - - /* Don't delete original "thing" because we don't control it. */ - buffer = pthing->m_Next; - pthing->m_Next = NULL; - while (buffer != NULL && copied < amount) { - MEMIODATA* old; - memcpy(data + copied, buffer->m_Buffer, buffer->m_Count); - copied += buffer->m_Count; - - old = buffer; - buffer = buffer->m_Next; - - LEPT_FREE(old->m_Buffer); - LEPT_FREE(old); - } - - pthing->m_Buffer = data; - pthing->m_Count = copied; - pthing->m_Size = amount; - return; -} - - -/* - * \brief memio_png_read_data() - * - * \param[in] png_ptr - * \param[in] outBytes - * \param[in] byteCountToRead - * - *- * Notes: - * (1) This is a libpng callback that reads an image from a single - * memory buffer. - *- */ -static void -memio_png_read_data(png_structp png_ptr, - png_bytep outBytes, - png_size_t byteCountToRead) -{ -MEMIODATA *thing; - - thing = (MEMIODATA *)png_get_io_ptr(png_ptr); - if (byteCountToRead > (thing->m_Size - thing->m_Count)) { - png_error(png_ptr, "read error in memio_png_read_data"); - } - memcpy(outBytes, thing->m_Buffer + thing->m_Count, byteCountToRead); - thing->m_Count += byteCountToRead; -} - - -/* - * \brief memio_free() - * - * \param[in] pthing - * - *- * Notes: - * (1) This frees all the write buffers in the linked list. It must - * be done before exiting the pixWriteMemPng(). - *- */ -static void -memio_free(MEMIODATA* pthing) -{ -MEMIODATA *buffer, *old; - - if (pthing->m_Buffer != NULL) - LEPT_FREE(pthing->m_Buffer); - - pthing->m_Buffer = NULL; - buffer = pthing->m_Next; - while (buffer != NULL) { - old = buffer; - buffer = buffer->m_Next; - - if (old->m_Buffer != NULL) - LEPT_FREE(old->m_Buffer); - LEPT_FREE(old); - } -} - - -/*---------------------------------------------------------------------* - * Reading png from memory * - *---------------------------------------------------------------------*/ -/*! - * \brief pixReadMemPng() - * - * \param[in] filedata png compressed data in memory - * \param[in] filesize number of bytes in data - * \return pix, or NULL on error - * - *- * Notes: - * (1) See pixReastreamPng(). - *- */ -PIX * -pixReadMemPng(const l_uint8 *filedata, - size_t filesize) -{ -l_uint8 byte; -l_int32 rval, gval, bval; -l_int32 i, j, k, index, ncolors, bitval; -l_int32 wpl, d, spp, cindex, tRNS; -l_uint32 png_transforms; -l_uint32 *data, *line, *ppixel; -int num_palette, num_text, num_trans; -png_byte bit_depth, color_type, channels; -png_uint_32 w, h, rowbytes; -png_uint_32 xres, yres; -png_bytep rowptr, trans; -png_bytep *row_pointers; -png_structp png_ptr; -png_infop info_ptr, end_info; -png_colorp palette; -png_textp text_ptr; /* ptr to text_chunk */ -PIX *pix, *pix1; -PIXCMAP *cmap; -MEMIODATA state; - - PROCNAME("pixReadMemPng"); - - if (!filedata) - return (PIX *)ERROR_PTR("filedata not defined", procName, NULL); - if (filesize < 1) - return (PIX *)ERROR_PTR("invalid filesize", procName, NULL); - - state.m_Next = 0; - state.m_Count = 0; - state.m_Last = &state; - state.m_Buffer = (char*)filedata; - state.m_Size = filesize; - pix = NULL; - - /* Allocate the 3 data structures */ - if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, - (png_voidp)NULL, NULL, NULL)) == NULL) - return (PIX *)ERROR_PTR("png_ptr not made", procName, NULL); - - if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { - png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); - return (PIX *)ERROR_PTR("info_ptr not made", procName, NULL); - } - - if ((end_info = png_create_info_struct(png_ptr)) == NULL) { - png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); - return (PIX *)ERROR_PTR("end_info not made", procName, NULL); - } - - /* Set up png setjmp error handling */ - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - return (PIX *)ERROR_PTR("internal png error", procName, NULL); - } - - png_set_read_fn(png_ptr, &state, memio_png_read_data); - - /* ---------------------------------------------------------- * - * Set the transforms flags. Whatever happens here, - * NEVER invert 1 bpp using PNG_TRANSFORM_INVERT_MONO. - * Also, do not use PNG_TRANSFORM_EXPAND, which would - * expand all images with bpp < 8 to 8 bpp. - * ---------------------------------------------------------- */ - /* To strip 16 --> 8 bit depth, use PNG_TRANSFORM_STRIP_16 */ - if (var_PNG_STRIP_16_TO_8 == 1) { /* our default */ - png_transforms = PNG_TRANSFORM_STRIP_16; - } else { - png_transforms = PNG_TRANSFORM_IDENTITY; - L_INFO("not stripping 16 --> 8 in png reading\n", procName); - } - - /* Read it */ - png_read_png(png_ptr, info_ptr, png_transforms, NULL); - - row_pointers = png_get_rows(png_ptr, info_ptr); - w = png_get_image_width(png_ptr, info_ptr); - h = png_get_image_height(png_ptr, info_ptr); - bit_depth = png_get_bit_depth(png_ptr, info_ptr); - rowbytes = png_get_rowbytes(png_ptr, info_ptr); - color_type = png_get_color_type(png_ptr, info_ptr); - channels = png_get_channels(png_ptr, info_ptr); - spp = channels; - tRNS = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? 1 : 0; - - if (spp == 1) { - d = bit_depth; - } else { /* spp == 2 (gray + alpha), spp == 3 (rgb), spp == 4 (rgba) */ - d = 4 * bit_depth; - } - - /* Remove if/when this is implemented for all bit_depths */ - if (spp == 3 && bit_depth != 8) { - lept_stderr("Help: spp = 3 and depth = %d != 8\n!!", bit_depth); - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - return (PIX *)ERROR_PTR("not implemented for this depth", - procName, NULL); - } - - cmap = NULL; - if (color_type == PNG_COLOR_TYPE_PALETTE || - color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */ - png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); - cmap = pixcmapCreate(d); /* spp == 1 */ - for (cindex = 0; cindex < num_palette; cindex++) { - rval = palette[cindex].red; - gval = palette[cindex].green; - bval = palette[cindex].blue; - pixcmapAddColor(cmap, rval, gval, bval); - } - } - - if ((pix = pixCreate(w, h, d)) == NULL) { - pixcmapDestroy(&cmap); - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - pixcmapDestroy(&cmap); - return (PIX *)ERROR_PTR("pix not made", procName, NULL); - } - pixSetInputFormat(pix, IFF_PNG); - wpl = pixGetWpl(pix); - data = pixGetData(pix); - pixSetColormap(pix, cmap); - pixSetSpp(pix, spp); - - if (spp == 1 && !tRNS) { /* copy straight from buffer to pix */ - for (i = 0; i < h; i++) { - line = data + i * wpl; - rowptr = row_pointers[i]; - for (j = 0; j < rowbytes; j++) { - SET_DATA_BYTE(line, j, rowptr[j]); - } - } - } else if (spp == 2) { /* grayscale + alpha; convert to RGBA */ - L_INFO("converting (gray + alpha) ==> RGBA\n", procName); - for (i = 0; i < h; i++) { - ppixel = data + i * wpl; - rowptr = row_pointers[i]; - for (j = k = 0; j < w; j++) { - /* Copy gray value into r, g and b */ - SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k]); - SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k]); - SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); - SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); - ppixel++; - } - } - pixSetSpp(pix, 4); /* we do not support 2 spp pix */ - } else if (spp == 3 || spp == 4) { - for (i = 0; i < h; i++) { - ppixel = data + i * wpl; - rowptr = row_pointers[i]; - for (j = k = 0; j < w; j++) { - SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]); - SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]); - SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); - if (spp == 4) - SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); - ppixel++; - } - } - } - - /* Special spp == 1 cases with transparency: - * (1) 8 bpp without colormap; assume full transparency - * (2) 1 bpp with colormap + trans array (for alpha) - * (3) 8 bpp with colormap + trans array (for alpha) - * These all require converting to RGBA */ - if (spp == 1 && tRNS) { - if (!cmap) { - /* Case 1: make fully transparent RGBA image */ - L_INFO("transparency, 1 spp, no colormap, no transparency array: " - "convention is fully transparent image\n", procName); - L_INFO("converting (fully transparent 1 spp) ==> RGBA\n", procName); - pixDestroy(&pix); - pix = pixCreate(w, h, 32); /* init to alpha = 0 (transparent) */ - pixSetSpp(pix, 4); - } else { - L_INFO("converting (cmap + alpha) ==> RGBA\n", procName); - - /* Grab the transparency array */ - png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); - if (!trans) { /* invalid png file */ - pixDestroy(&pix); - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - return (PIX *)ERROR_PTR("cmap, tRNS, but no transparency array", - procName, NULL); - } - - /* Save the cmap and destroy the pix */ - cmap = pixcmapCopy(pixGetColormap(pix)); - ncolors = pixcmapGetCount(cmap); - pixDestroy(&pix); - - /* Start over with 32 bit RGBA */ - pix = pixCreate(w, h, 32); - wpl = pixGetWpl(pix); - data = pixGetData(pix); - pixSetSpp(pix, 4); - -#if DEBUG_READ - lept_stderr("ncolors = %d, num_trans = %d\n", - ncolors, num_trans); - for (i = 0; i < ncolors; i++) { - pixcmapGetColor(cmap, i, &rval, &gval, &bval); - if (i < num_trans) { - lept_stderr("(r,g,b,a) = (%d,%d,%d,%d)\n", - rval, gval, bval, trans[i]); - } else { - lept_stderr("(r,g,b,a) = (%d,%d,%d,<<255>>)\n", - rval, gval, bval); - } - } -#endif /* DEBUG_READ */ - - /* Extract the data and convert to RGBA */ - if (d == 1) { - /* Case 2: 1 bpp with transparency (usually) behind white */ - L_INFO("converting 1 bpp cmap with alpha ==> RGBA\n", procName); - if (num_trans == 1) - L_INFO("num_trans = 1; second color opaque by default\n", - procName); - for (i = 0; i < h; i++) { - ppixel = data + i * wpl; - rowptr = row_pointers[i]; - for (j = 0, index = 0; j < rowbytes; j++) { - byte = rowptr[j]; - for (k = 0; k < 8 && index < w; k++, index++) { - bitval = (byte >> (7 - k)) & 1; - pixcmapGetColor(cmap, bitval, &rval, &gval, &bval); - composeRGBPixel(rval, gval, bval, ppixel); - SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, - bitval < num_trans ? trans[bitval] : 255); - ppixel++; - } - } - } - } else if (d == 8) { - /* Case 3: 8 bpp with cmap and associated transparency */ - L_INFO("converting 8 bpp cmap with alpha ==> RGBA\n", procName); - for (i = 0; i < h; i++) { - ppixel = data + i * wpl; - rowptr = row_pointers[i]; - for (j = 0; j < w; j++) { - index = rowptr[j]; - pixcmapGetColor(cmap, index, &rval, &gval, &bval); - composeRGBPixel(rval, gval, bval, ppixel); - /* Assume missing entries to be 255 (opaque) - * according to the spec: - * http://www.w3.org/TR/PNG/#11tRNS */ - SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, - index < num_trans ? trans[index] : 255); - ppixel++; - } - } - } else { - L_ERROR("spp == 1, cmap, trans array, invalid depth: %d\n", - procName, d); - } - pixcmapDestroy(&cmap); - } - } - -#if DEBUG_READ - if (cmap) { - for (i = 0; i < 16; i++) { - lept_stderr("[%d] = %d\n", i, ((l_uint8 *)(cmap->array))[i]); - } - } -#endif /* DEBUG_READ */ - - /* Final adjustments for bpp = 1. - * + If there is no colormap, the image must be inverted because - * png stores black pixels as 0. - * + We have already handled the case of cmapped, 1 bpp pix - * with transparency, where the output pix is 32 bpp RGBA. - * If there is no transparency but the pix has a colormap, - * we remove the colormap, because functions operating on - * 1 bpp images in leptonica assume no colormap. - * + The colormap must be removed in such a way that the pixel - * values are not changed. If the values are only black and - * white, we return a 1 bpp image; if gray, return an 8 bpp pix; - * otherwise, return a 32 bpp rgb pix. - * - * Note that we cannot use the PNG_TRANSFORM_INVERT_MONO flag - * to do the inversion, because that flag (since version 1.0.9) - * inverts 8 bpp grayscale as well, which we don't want to do. - * (It also doesn't work if there is a colormap.) - * - * Note that if the input png is a 1-bit with colormap and - * transparency, it has already been rendered as a 32 bpp, - * spp = 4 rgba pix. - */ - if (pixGetDepth(pix) == 1) { - if (!cmap) { - pixInvert(pix, pix); - } else { - pix1 = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); - pixDestroy(&pix); - pix = pix1; - } - } - - xres = png_get_x_pixels_per_meter(png_ptr, info_ptr); - yres = png_get_y_pixels_per_meter(png_ptr, info_ptr); - pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */ - pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */ - - /* Get the text if there is any */ - png_get_text(png_ptr, info_ptr, &text_ptr, &num_text); - if (num_text && text_ptr) - pixSetText(pix, text_ptr->text); - - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - return pix; -} - - -/*---------------------------------------------------------------------* - * Writing png to memory * - *---------------------------------------------------------------------*/ -/*! - * \brief pixWriteMemPng() - * - * \param[out] pfiledata png encoded data of pix - * \param[out] pfilesize size of png encoded data - * \param[in] pix - * \param[in] gamma use 0.0 if gamma is not defined - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) See pixWriteStreamPng() - *- */ -l_ok -pixWriteMemPng(l_uint8 **pfiledata, - size_t *pfilesize, - PIX *pix, - l_float32 gamma) -{ -char commentstring[] = "Comment"; -l_int32 i, j, k; -l_int32 wpl, d, spp, cmflag, opaque; -l_int32 ncolors, compval; -l_int32 *rmap, *gmap, *bmap, *amap; -l_uint32 *data, *ppixel; -png_byte bit_depth, color_type; -png_byte alpha[256]; -png_uint_32 w, h; -png_uint_32 xres, yres; -png_bytep *row_pointers; -png_bytep rowbuffer; -png_structp png_ptr; -png_infop info_ptr; -png_colorp palette; -PIX *pix1; -PIXCMAP *cmap; -char *text; -MEMIODATA state; - - PROCNAME("pixWriteMemPng"); - - if (pfiledata) *pfiledata = NULL; - if (pfilesize) *pfilesize = 0; - if (!pfiledata) - return ERROR_INT("&filedata not defined", procName, 1); - if (!pfilesize) - return ERROR_INT("&filesize not defined", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - state.m_Buffer = 0; - state.m_Size = 0; - state.m_Next = 0; - state.m_Count = 0; - state.m_Last = &state; - - /* Allocate the 2 data structures */ - if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, - (png_voidp)NULL, NULL, NULL)) == NULL) - return ERROR_INT("png_ptr not made", procName, 1); - - if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { - png_destroy_write_struct(&png_ptr, (png_infopp)NULL); - return ERROR_INT("info_ptr not made", procName, 1); - } - - /* Set up png setjmp error handling */ - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_write_struct(&png_ptr, &info_ptr); - return ERROR_INT("internal png error", procName, 1); - } - - png_set_write_fn(png_ptr, &state, memio_png_write_data, - (png_flush_ptr)NULL); - - /* With best zlib compression (9), get between 1 and 10% improvement - * over default (6), but the compression is 3 to 10 times slower. - * Use the zlib default (6) as our default compression unless - * pix->special falls in the range [10 ... 19]; then subtract 10 - * to get the compression value. */ - compval = Z_DEFAULT_COMPRESSION; - if (pix->special >= 10 && pix->special < 20) - compval = pix->special - 10; - png_set_compression_level(png_ptr, compval); - - w = pixGetWidth(pix); - h = pixGetHeight(pix); - d = pixGetDepth(pix); - spp = pixGetSpp(pix); - if ((cmap = pixGetColormap(pix))) - cmflag = 1; - else - cmflag = 0; - - /* Set the color type and bit depth. */ - if (d == 32 && spp == 4) { - bit_depth = 8; - color_type = PNG_COLOR_TYPE_RGBA; /* 6 */ - cmflag = 0; /* ignore if it exists */ - } else if (d == 24 || d == 32) { - bit_depth = 8; - color_type = PNG_COLOR_TYPE_RGB; /* 2 */ - cmflag = 0; /* ignore if it exists */ - } else { - bit_depth = d; - color_type = PNG_COLOR_TYPE_GRAY; /* 0 */ - } - if (cmflag) - color_type = PNG_COLOR_TYPE_PALETTE; /* 3 */ - -#if DEBUG_WRITE - lept_stderr("cmflag = %d, bit_depth = %d, color_type = %d\n", - cmflag, bit_depth, color_type); -#endif /* DEBUG_WRITE */ - - png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type, - PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, - PNG_FILTER_TYPE_BASE); - - /* Store resolution in ppm, if known */ - xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); - yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); - if ((xres == 0) || (yres == 0)) - png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN); - else - png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER); - - if (cmflag) { - pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap); - ncolors = pixcmapGetCount(cmap); - pixcmapIsOpaque(cmap, &opaque); - - /* Make and save the palette */ - palette = (png_colorp)LEPT_CALLOC(ncolors, sizeof(png_color)); - for (i = 0; i < ncolors; i++) { - palette[i].red = (png_byte)rmap[i]; - palette[i].green = (png_byte)gmap[i]; - palette[i].blue = (png_byte)bmap[i]; - alpha[i] = (png_byte)amap[i]; - } - - png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors); - if (!opaque) /* alpha channel has some transparency; assume valid */ - png_set_tRNS(png_ptr, info_ptr, (png_bytep)alpha, - (int)ncolors, NULL); - LEPT_FREE(rmap); - LEPT_FREE(gmap); - LEPT_FREE(bmap); - LEPT_FREE(amap); - } - - /* 0.4545 is treated as the default by some image - * display programs (not gqview). A value > 0.4545 will - * lighten an image as displayed by xv, display, etc. */ - if (gamma > 0.0) - png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma); - - if ((text = pixGetText(pix))) { - png_text text_chunk; - text_chunk.compression = PNG_TEXT_COMPRESSION_NONE; - text_chunk.key = commentstring; - text_chunk.text = text; - text_chunk.text_length = strlen(text); -#ifdef PNG_ITXT_SUPPORTED - text_chunk.itxt_length = 0; - text_chunk.lang = NULL; - text_chunk.lang_key = NULL; -#endif - png_set_text(png_ptr, info_ptr, &text_chunk, 1); - } - - /* Write header and palette info */ - png_write_info(png_ptr, info_ptr); - - if ((d != 32) && (d != 24)) { /* not rgb color */ - /* Generate a temporary pix with bytes swapped. - * For writing a 1 bpp image as png: - * ~ if no colormap, invert the data, because png writes - * black as 0 - * ~ if colormapped, do not invert the data; the two RGBA - * colors can have any value. */ - if (d == 1 && !cmap) { - pix1 = pixInvert(NULL, pix); - pixEndianByteSwap(pix1); - } else { - pix1 = pixEndianByteSwapNew(pix); - } - if (!pix1) { - png_destroy_write_struct(&png_ptr, &info_ptr); - if (cmflag) LEPT_FREE(palette); - memio_free(&state); - return ERROR_INT("pix1 not made", procName, 1); - } - - /* Make and assign array of image row pointers */ - row_pointers = (png_bytep *)LEPT_CALLOC(h, sizeof(png_bytep)); - wpl = pixGetWpl(pix1); - data = pixGetData(pix1); - for (i = 0; i < h; i++) - row_pointers[i] = (png_bytep)(data + i * wpl); - png_set_rows(png_ptr, info_ptr, row_pointers); - - /* Transfer the data */ - png_write_image(png_ptr, row_pointers); - png_write_end(png_ptr, info_ptr); - - if (cmflag) LEPT_FREE(palette); - LEPT_FREE(row_pointers); - pixDestroy(&pix1); - png_destroy_write_struct(&png_ptr, &info_ptr); - - memio_png_flush(&state); - *pfiledata = (l_uint8 *)state.m_Buffer; - state.m_Buffer = 0; - *pfilesize = state.m_Count; - memio_free(&state); - return 0; - } - - /* For rgb, compose and write a row at a time */ - data = pixGetData(pix); - wpl = pixGetWpl(pix); - if (d == 24) { /* See note 7 above: special case of 24 bpp rgb */ - for (i = 0; i < h; i++) { - ppixel = data + i * wpl; - png_write_rows(png_ptr, (png_bytepp)&ppixel, 1); - } - } else { /* 32 bpp rgb and rgba. Write out the alpha channel if either - * the pix has 4 spp or writing it is requested anyway */ - rowbuffer = (png_bytep)LEPT_CALLOC(w, 4); - for (i = 0; i < h; i++) { - ppixel = data + i * wpl; - for (j = k = 0; j < w; j++) { - rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); - rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); - rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); - if (spp == 4) - rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL); - ppixel++; - } - - png_write_rows(png_ptr, &rowbuffer, 1); - } - LEPT_FREE(rowbuffer); - } - - png_write_end(png_ptr, info_ptr); - - if (cmflag) - LEPT_FREE(palette); - png_destroy_write_struct(&png_ptr, &info_ptr); - - memio_png_flush(&state); - *pfiledata = (l_uint8 *)state.m_Buffer; - state.m_Buffer = 0; - *pfilesize = state.m_Count; - memio_free(&state); - return 0; -} - -/* --------------------------------------------*/ -#endif /* HAVE_LIBPNG */ -/* --------------------------------------------*/ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pngiostub.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pngiostub.c deleted file mode 100644 index f3c8bed9..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pngiostub.c +++ /dev/null @@ -1,143 +0,0 @@ -/*====================================================================* - - 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 pngiostub.c - *- * - * Stubs for pngio.c functions - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include "allheaders.h" - -/* --------------------------------------------*/ -#if !HAVE_LIBPNG /* defined in environ.h */ -/* --------------------------------------------*/ - -PIX * pixReadStreamPng(FILE *fp) -{ - return (PIX * )ERROR_PTR("function not present", "pixReadStreamPng", NULL); -} - -/* ----------------------------------------------------------------------*/ - -l_ok readHeaderPng(const char *filename, l_int32 *pwidth, l_int32 *pheight, - l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap) -{ - return ERROR_INT("function not present", "readHeaderPng", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok freadHeaderPng(FILE *fp, l_int32 *pwidth, l_int32 *pheight, - l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap) -{ - return ERROR_INT("function not present", "freadHeaderPng", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok readHeaderMemPng(const l_uint8 *data, size_t size, l_int32 *pwidth, - l_int32 *pheight, l_int32 *pbps, l_int32 *pspp, - l_int32 *piscmap) -{ - return ERROR_INT("function not present", "readHeaderMemPng", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_int32 fgetPngResolution(FILE *fp, l_int32 *pxres, l_int32 *pyres) -{ - return ERROR_INT("function not present", "fgetPngResolution", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok isPngInterlaced(const char *filename, l_int32 *pinterlaced) -{ - return ERROR_INT("function not present", "isPngInterlaced", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok fgetPngColormapInfo(FILE *fp, PIXCMAP **pcmap, l_int32 *ptransparency) -{ - return ERROR_INT("function not present", "fgetPngColormapInfo", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixWritePng(const char *filename, PIX *pix, l_float32 gamma) -{ - return ERROR_INT("function not present", "pixWritePng", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixWriteStreamPng(FILE *fp, PIX *pix, l_float32 gamma) -{ - return ERROR_INT("function not present", "pixWriteStreamPng", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixSetZlibCompression(PIX *pix, l_int32 compval) - -{ - return ERROR_INT("function not present", "pixSetZlibCompression", 1); -} - -/* ----------------------------------------------------------------------*/ - -void l_pngSetReadStrip16To8(l_int32 flag) -{ - L_ERROR("function not present\n", "l_pngSetReadStrip16To8"); - return; -} - -/* ----------------------------------------------------------------------*/ - -PIX * pixReadMemPng(const l_uint8 *filedata, size_t filesize) -{ - return (PIX * )ERROR_PTR("function not present", "pixReadMemPng", NULL); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixWriteMemPng(l_uint8 **pfiledata, size_t *pfilesize, PIX *pix, - l_float32 gamma) -{ - return ERROR_INT("function not present", "pixWriteMemPng", 1); -} - -/* --------------------------------------------*/ -#endif /* !HAVE_LIBPNG */ -/* --------------------------------------------*/ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pnmio.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pnmio.c deleted file mode 100644 index e9cfad57..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pnmio.c +++ /dev/null @@ -1,1534 +0,0 @@ -/*====================================================================* - - 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 pnmio.c - * - * - * Stream interface - * PIX *pixReadStreamPnm() - * l_int32 readHeaderPnm() - * l_int32 freadHeaderPnm() - * l_int32 pixWriteStreamPnm() - * l_int32 pixWriteStreamAsciiPnm() - * l_int32 pixWriteStreamPam() - * - * Read/write to memory - * PIX *pixReadMemPnm() - * l_int32 readHeaderMemPnm() - * l_int32 pixWriteMemPnm() - * l_int32 pixWriteMemPam() - * - * Local helpers - * static l_int32 pnmReadNextAsciiValue(); - * static l_int32 pnmReadNextNumber(); - * static l_int32 pnmReadNextString(); - * static l_int32 pnmSkipCommentLines(); - * - * These are here by popular demand, with the help of Mattias - * Kregert (mattias@kregert.se), who provided the first implementation. - * - * The pnm formats are exceedingly simple, because they have - * no compression and no colormaps. They support images that - * are 1 bpp; 2, 4, 8 and 16 bpp grayscale; and rgb. - * - * The original pnm formats ("ASCII") are included for completeness, - * but their use is deprecated for all but tiny iconic images. - * They are extremely wasteful of memory; for example, the P1 binary - * ASCII format is 16 times as big as the packed uncompressed - * format, because 2 characters are used to represent every bit - * (pixel) in the image. Reading is slow because we check for extra - * white space and EOL at every sample value. - * - * The packed pnm formats ("raw") give file sizes similar to - * bmp files, which are uncompressed packed. However, bmp - * are more flexible, because they can support colormaps. - * - * We don't differentiate between the different types ("pbm", - * "pgm", "ppm") at the interface level, because this is really a - * "distinction without a difference." You read a file, you get - * the appropriate Pix. You write a file from a Pix, you get the - * appropriate type of file. If there is a colormap on the Pix, - * and the Pix is more than 1 bpp, you get either an 8 bpp pgm - * or a 24 bpp RGB pnm, depending on whether the colormap colors - * are gray or rgb, respectively. - * - * This follows the general policy that the I/O routines don't - * make decisions about the content of the image -- you do that - * with image processing before you write it out to file. - * The I/O routines just try to make the closest connection - * possible between the file and the Pix in memory. - * - * On systems like windows without fmemopen() and open_memstream(), - * we write data to a temp file and read it back for operations - * between pix and compressed-data, such as pixReadMemPnm() and - * pixWriteMemPnm(). - * - * The P7 format is new. It introduced a header with multiple - * lines containing distinct tags for the various fields. - * See: http://netpbm.sourceforge.net/doc/pam.html - * - * WIDTH- */ - -#ifdef HAVE_CONFIG_H -#include; mandatory, exactly once - * HEIGHT ; mandatory, exactly once - * DEPTH ; mandatory, exactly once, - * ; its meaning is equivalent to spp - * MAXVAL ; mandatory, one of 1, 3, 15, 255 or 65535 - * TUPLTYPE ; optional; BLACKANDWHITE, GRAYSCALE, RGB - * ; and optional suffix _ALPHA, e.g. RGB_ALPHA - * ENDHDR ; mandatory, last header line - * - * Reading BLACKANDWHITE_ALPHA and GRAYSCALE_ALPHA, which have a DEPTH - * value of 2, is supported. The original image is converted to a Pix - * with 32-bpp and alpha channel (spp == 4). - * - * Writing P7 format is currently selected for 32-bpp with alpha - * channel, i.e. for Pix which have spp == 4, using pixWriteStreamPam(). - * Jürgen Buchmüller provided the implementation for the P7 (pam) format. - * -#endif /* HAVE_CONFIG_H */ - -#include -#include -#include "allheaders.h" - -/* --------------------------------------------*/ -#if USE_PNMIO /* defined in environ.h */ -/* --------------------------------------------*/ - -static l_int32 pnmReadNextAsciiValue(FILE *fp, l_int32 *pval); -static l_int32 pnmReadNextNumber(FILE *fp, l_int32 *pval); -static l_int32 pnmReadNextString(FILE *fp, char *buff, l_int32 size); -static l_int32 pnmSkipCommentLines(FILE *fp); - - /* a sanity check on the size read from file */ -static const l_int32 MAX_PNM_WIDTH = 100000; -static const l_int32 MAX_PNM_HEIGHT = 100000; - - -/*--------------------------------------------------------------------* - * Stream interface * - *--------------------------------------------------------------------*/ -/*! - * \brief pixReadStreamPnm() - * - * \param[in] fp file stream opened for read - * \return pix, or NULL on error - */ -PIX * -pixReadStreamPnm(FILE *fp) -{ -l_uint8 val8, rval8, gval8, bval8, aval8, mask8; -l_uint16 val16, rval16, gval16, bval16, aval16; -l_int32 w, h, d, bps, spp, bpl, wpl, i, j, type; -l_int32 val, rval, gval, bval; -l_uint32 rgbval; -l_uint32 *line, *data; -PIX *pix; - - PROCNAME("pixReadStreamPnm"); - - if (!fp) - return (PIX *)ERROR_PTR("fp not defined", procName, NULL); - - if (freadHeaderPnm(fp, &w, &h, &d, &type, &bps, &spp)) - return (PIX *)ERROR_PTR("header read failed", procName, NULL); - if (bps < 1 || bps > 16) - return (PIX *)ERROR_PTR("invalid bps", procName, NULL); - if (spp < 1 || spp > 4) - return (PIX *)ERROR_PTR("invalid spp", procName, NULL); - if ((pix = pixCreate(w, h, d)) == NULL) - return (PIX *)ERROR_PTR("pix not made", procName, NULL); - pixSetInputFormat(pix, IFF_PNM); - data = pixGetData(pix); - wpl = pixGetWpl(pix); - - /* If type == 6 and bps == 16, we use the code in type 7 - * to read 6 bytes/pixel from the input file. */ - if (type == 6 && bps == 16) - type = 7; - - switch (type) { - case 1: - case 2: - /* Old "ASCII" binary or gray format */ - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - if (pnmReadNextAsciiValue(fp, &val)) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read abend", procName, NULL); - } - pixSetPixel(pix, j, i, val); - } - } - break; - - case 3: - /* Old "ASCII" rgb format */ - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - if (pnmReadNextAsciiValue(fp, &rval)) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read abend", procName, NULL); - } - if (pnmReadNextAsciiValue(fp, &gval)) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read abend", procName, NULL); - } - if (pnmReadNextAsciiValue(fp, &bval)) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read abend", procName, NULL); - } - composeRGBPixel(rval, gval, bval, &rgbval); - pixSetPixel(pix, j, i, rgbval); - } - } - break; - - case 4: - /* "raw" format for 1 bpp */ - bpl = (d * w + 7) / 8; - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < bpl; j++) { - if (fread(&val8, 1, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error in 4", procName, NULL); - } - SET_DATA_BYTE(line, j, val8); - } - } - break; - - case 5: - /* "raw" format for grayscale */ - for (i = 0; i < h; i++) { - line = data + i * wpl; - if (d != 16) { - for (j = 0; j < w; j++) { - if (fread(&val8, 1, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("error in 5", procName, NULL); - } - if (d == 2) - SET_DATA_DIBIT(line, j, val8); - else if (d == 4) - SET_DATA_QBIT(line, j, val8); - else /* d == 8 */ - SET_DATA_BYTE(line, j, val8); - } - } else { /* d == 16 */ - for (j = 0; j < w; j++) { - if (fread(&val16, 2, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("16 bpp error", procName, NULL); - } - SET_DATA_TWO_BYTES(line, j, val16); - } - } - } - break; - - case 6: - /* "raw" format, type == 6; 8 bps, rgb */ - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < wpl; j++) { - if (fread(&rval8, 1, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 6", - procName, NULL); - } - if (fread(&gval8, 1, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 6", - procName, NULL); - } - if (fread(&bval8, 1, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 6", - procName, NULL); - } - composeRGBPixel(rval8, gval8, bval8, &rgbval); - line[j] = rgbval; - } - } - break; - - case 7: - /* "arbitrary" format; type == 7; */ - if (bps != 16) { - mask8 = (1 << bps) - 1; - switch (spp) { - case 1: /* 1, 2, 4, 8 bpp grayscale */ - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - if (fread(&val8, 1, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - val8 = val8 & mask8; - if (bps == 1) val8 ^= 1; /* white-is-1 photometry */ - pixSetPixel(pix, j, i, val8); - } - } - break; - - case 2: /* 1, 2, 4, 8 bpp grayscale + alpha */ - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - if (fread(&val8, 1, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - if (fread(&aval8, 1, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - val8 = val8 & mask8; - aval8 = aval8 & mask8; - composeRGBAPixel(val8, val8, val8, aval8, &rgbval); - pixSetPixel(pix, j, i, rgbval); - } - } - pixSetSpp(pix, 4); - break; - - case 3: /* rgb */ - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < wpl; j++) { - if (fread(&rval8, 1, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - if (fread(&gval8, 1, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - if (fread(&bval8, 1, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - rval8 = rval8 & mask8; - gval8 = gval8 & mask8; - bval8 = bval8 & mask8; - composeRGBPixel(rval8, gval8, bval8, &rgbval); - line[j] = rgbval; - } - } - break; - - case 4: /* rgba */ - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < wpl; j++) { - if (fread(&rval8, 1, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - if (fread(&gval8, 1, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - if (fread(&bval8, 1, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - if (fread(&aval8, 1, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - rval8 = rval8 & mask8; - gval8 = gval8 & mask8; - bval8 = bval8 & mask8; - aval8 = aval8 & mask8; - composeRGBAPixel(rval8, gval8, bval8, aval8, &rgbval); - line[j] = rgbval; - } - } - pixSetSpp(pix, 4); - break; - } - } else { /* bps == 16 */ - /* I have only seen one example that is type 6, 16 bps. - * It was 3 spp (rgb), and the 8 bps of real data was stored - * in the second byte. In the following, I make the wild - * assumption that for all 16 bpp pnm/pam files, we can - * take the second byte. */ - switch (spp) { - case 1: /* 16 bps grayscale */ - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - if (fread(&val16, 2, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - val8 = val16 & 0xff; - pixSetPixel(pix, j, i, val8); - } - } - break; - - case 2: /* 16 bps grayscale + alpha */ - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - if (fread(&val16, 2, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - if (fread(&aval16, 2, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - val8 = val16 & 0xff; - aval8 = aval16 & 0xff; - composeRGBAPixel(val8, val8, val8, aval8, &rgbval); - pixSetPixel(pix, j, i, rgbval); - } - } - pixSetSpp(pix, 4); - break; - - case 3: /* 16bps rgb */ - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < wpl; j++) { - if (fread(&rval16, 2, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - if (fread(&gval16, 2, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - if (fread(&bval16, 2, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - rval8 = rval16 & 0xff; - gval8 = gval16 & 0xff; - bval8 = bval16 & 0xff; - composeRGBPixel(rval8, gval8, bval8, &rgbval); - line[j] = rgbval; - } - } - break; - - case 4: /* 16bps rgba */ - for (i = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < wpl; j++) { - if (fread(&rval16, 2, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - if (fread(&gval16, 2, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - if (fread(&bval16, 2, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - if (fread(&aval16, 2, 1, fp) != 1) { - pixDestroy(&pix); - return (PIX *)ERROR_PTR("read error type 7", - procName, NULL); - } - rval8 = rval16 & 0xff; - gval8 = gval16 & 0xff; - bval8 = bval16 & 0xff; - aval8 = aval16 & 0xff; - composeRGBAPixel(rval8, gval8, bval8, aval8, &rgbval); - line[j] = rgbval; - } - } - pixSetSpp(pix, 4); - break; - } - } - break; - } - return pix; -} - - -/*! - * \brief readHeaderPnm() - * - * \param[in] filename - * \param[out] pw [optional] - * \param[out] ph [optional] - * \param[out] pd [optional] - * \param[out] ptype [optional] pnm type - * \param[out] pbps [optional] bits/sample - * \param[out] pspp [optional] samples/pixel - * \return 0 if OK, 1 on error - */ -l_ok -readHeaderPnm(const char *filename, - l_int32 *pw, - l_int32 *ph, - l_int32 *pd, - l_int32 *ptype, - l_int32 *pbps, - l_int32 *pspp) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("readHeaderPnm"); - - if (pw) *pw = 0; - if (ph) *ph = 0; - if (pd) *pd = 0; - if (ptype) *ptype = 0; - if (pbps) *pbps = 0; - if (pspp) *pspp = 0; - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - - if ((fp = fopenReadStream(filename)) == NULL) - return ERROR_INT("image file not found", procName, 1); - ret = freadHeaderPnm(fp, pw, ph, pd, ptype, pbps, pspp); - fclose(fp); - return ret; -} - - -/*! - * \brief freadHeaderPnm() - * - * \param[in] fp file stream opened for read - * \param[out] pw [optional] - * \param[out] ph [optional] - * \param[out] pd [optional] - * \param[out] ptype [optional] pnm type - * \param[out] pbps [optional] bits/sample - * \param[out] pspp [optional] samples/pixel - * \return 0 if OK, 1 on error - */ -l_ok -freadHeaderPnm(FILE *fp, - l_int32 *pw, - l_int32 *ph, - l_int32 *pd, - l_int32 *ptype, - l_int32 *pbps, - l_int32 *pspp) -{ -char tag[16], tupltype[32]; -l_int32 i, w, h, d, bps, spp, type; -l_int32 maxval; -l_int32 ch; - - PROCNAME("freadHeaderPnm"); - - if (pw) *pw = 0; - if (ph) *ph = 0; - if (pd) *pd = 0; - if (ptype) *ptype = 0; - if (pbps) *pbps = 0; - if (pspp) *pspp = 0; - if (!fp) - return ERROR_INT("fp not defined", procName, 1); - - if (fscanf(fp, "P%d\n", &type) != 1) - return ERROR_INT("invalid read for type", procName, 1); - if (type < 1 || type > 7) - return ERROR_INT("invalid pnm file", procName, 1); - - if (pnmSkipCommentLines(fp)) - return ERROR_INT("no data in file", procName, 1); - - if (type == 7) { - w = h = d = bps = spp = maxval = 0; - for (i = 0; i < 10; i++) { /* limit to 10 lines of this header */ - if (pnmReadNextString(fp, tag, sizeof(tag))) - return ERROR_INT("found no next tag", procName, 1); - if (!strcmp(tag, "WIDTH")) { - if (pnmReadNextNumber(fp, &w)) - return ERROR_INT("failed reading width", procName, 1); - continue; - } - if (!strcmp(tag, "HEIGHT")) { - if (pnmReadNextNumber(fp, &h)) - return ERROR_INT("failed reading height", procName, 1); - continue; - } - if (!strcmp(tag, "DEPTH")) { - if (pnmReadNextNumber(fp, &spp)) - return ERROR_INT("failed reading depth", procName, 1); - continue; - } - if (!strcmp(tag, "MAXVAL")) { - if (pnmReadNextNumber(fp, &maxval)) - return ERROR_INT("failed reading maxval", procName, 1); - continue; - } - if (!strcmp(tag, "TUPLTYPE")) { - if (pnmReadNextString(fp, tupltype, sizeof(tupltype))) - return ERROR_INT("failed reading tuple type", procName, 1); - continue; - } - if (!strcmp(tag, "ENDHDR")) { - if ('\n' != (ch = fgetc(fp))) - return ERROR_INT("missing LF after ENDHDR", procName, 1); - break; - } - } - if (w <= 0 || h <= 0 || w > MAX_PNM_WIDTH || h > MAX_PNM_HEIGHT) { - L_INFO("invalid size: w = %d, h = %d\n", procName, w, h); - return 1; - } - if (maxval == 1) { - d = bps = 1; - } else if (maxval == 3) { - d = bps = 2; - } else if (maxval == 15) { - d = bps = 4; - } else if (maxval == 255) { - d = bps = 8; - } else if (maxval == 0xffff) { - d = bps = 16; - } else { - L_INFO("invalid maxval = %d\n", procName, maxval); - return 1; - } - switch (spp) { - case 1: - /* d and bps are already set */ - break; - case 2: - case 3: - case 4: - /* create a 32 bpp Pix */ - d = 32; - break; - default: - L_INFO("invalid depth = %d\n", procName, spp); - return 1; - } - } else { - - if (fscanf(fp, "%d %d\n", &w, &h) != 2) - return ERROR_INT("invalid read for w,h", procName, 1); - if (w <= 0 || h <= 0 || w > MAX_PNM_WIDTH || h > MAX_PNM_HEIGHT) { - L_INFO("invalid size: w = %d, h = %d\n", procName, w, h); - return 1; - } - - /* Get depth of pix. For types 2 and 5, we use the maxval. - * Important implementation note: - * - You can't use fscanf(), which throws away whitespace, - * and will discard binary data if it starts with whitespace(s). - * - You can't use fgets(), which stops at newlines, but this - * dumb format doesn't require a newline after the maxval - * number -- it just requires one whitespace character. - * - Which leaves repeated calls to fgetc, including swallowing - * the single whitespace character. */ - if (type == 1 || type == 4) { - d = 1; - spp = 1; - bps = 1; - } else if (type == 2 || type == 5) { - if (pnmReadNextNumber(fp, &maxval)) - return ERROR_INT("invalid read for maxval (2,5)", procName, 1); - if (maxval == 3) { - d = 2; - } else if (maxval == 15) { - d = 4; - } else if (maxval == 255) { - d = 8; - } else if (maxval == 0xffff) { - d = 16; - } else { - lept_stderr("maxval = %d\n", maxval); - return ERROR_INT("invalid maxval", procName, 1); - } - bps = d; - spp = 1; - } else { /* type == 3 || type == 6; this is rgb */ - if (pnmReadNextNumber(fp, &maxval)) - return ERROR_INT("invalid read for maxval (3,6)", procName, 1); - if (maxval != 255 && maxval != 0xffff) { - L_ERROR("unexpected maxval = %d\n", procName, maxval); - return 1; - } - bps = (maxval == 255) ? 8 : 16; - d = 32; - spp = 3; - } - } - if (pw) *pw = w; - if (ph) *ph = h; - if (pd) *pd = d; - if (ptype) *ptype = type; - if (pbps) *pbps = bps; - if (pspp) *pspp = spp; - return 0; -} - - -/*! - * \brief pixWriteStreamPnm() - * - * \param[in] fp file stream opened for write - * \param[in] pix - * \return 0 if OK; 1 on error - * - * - * Notes: - * (1) This writes "raw" packed format only: - * 1 bpp --> pbm (P4) - * 2, 4, 8, 16 bpp, no colormap or grayscale colormap --> pgm (P5) - * 2, 4, 8 bpp with color-valued colormap, or rgb --> rgb ppm (P6) - * (2) 24 bpp rgb are not supported in leptonica, but this will - * write them out as a packed array of bytes (3 to a pixel). - *- */ -l_ok -pixWriteStreamPnm(FILE *fp, - PIX *pix) -{ -l_uint8 val8; -l_uint8 pel[4]; -l_uint16 val16; -l_int32 h, w, d, ds, i, j, wpls, bpl, filebpl, writeerror, maxval; -l_uint32 *pword, *datas, *lines; -PIX *pixs; - - PROCNAME("pixWriteStreamPnm"); - - if (!fp) - return ERROR_INT("fp not defined", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - pixGetDimensions(pix, &w, &h, &d); - if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 24 && d != 32) - return ERROR_INT("d not in {1,2,4,8,16,24,32}", procName, 1); - if (d == 32 && pixGetSpp(pix) == 4) - return pixWriteStreamPam(fp, pix); - - /* If a colormap exists, remove and convert to grayscale or rgb */ - if (pixGetColormap(pix) != NULL) - pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); - else - pixs = pixClone(pix); - ds = pixGetDepth(pixs); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - - writeerror = 0; - - if (ds == 1) { /* binary */ - fprintf(fp, "P4\n# Raw PBM file written by leptonica " - "(www.leptonica.com)\n%d %d\n", w, h); - - bpl = (w + 7) / 8; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < bpl; j++) { - val8 = GET_DATA_BYTE(lines, j); - fwrite(&val8, 1, 1, fp); - } - } - } else if (ds == 2 || ds == 4 || ds == 8 || ds == 16) { /* grayscale */ - maxval = (1 << ds) - 1; - fprintf(fp, "P5\n# Raw PGM file written by leptonica " - "(www.leptonica.com)\n%d %d\n%d\n", w, h, maxval); - - if (ds != 16) { - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) { - if (ds == 2) - val8 = GET_DATA_DIBIT(lines, j); - else if (ds == 4) - val8 = GET_DATA_QBIT(lines, j); - else /* ds == 8 */ - val8 = GET_DATA_BYTE(lines, j); - fwrite(&val8, 1, 1, fp); - } - } - } else { /* ds == 16 */ - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) { - val16 = GET_DATA_TWO_BYTES(lines, j); - fwrite(&val16, 2, 1, fp); - } - } - } - } else { /* rgb color */ - fprintf(fp, "P6\n# Raw PPM file written by leptonica " - "(www.leptonica.com)\n%d %d\n255\n", w, h); - - if (d == 24) { /* packed, 3 bytes to a pixel */ - filebpl = 3 * w; - for (i = 0; i < h; i++) { /* write out each raster line */ - lines = datas + i * wpls; - if (fwrite(lines, 1, filebpl, fp) != filebpl) - writeerror = 1; - } - } else { /* 32 bpp rgb */ - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < wpls; j++) { - pword = lines + j; - pel[0] = GET_DATA_BYTE(pword, COLOR_RED); - pel[1] = GET_DATA_BYTE(pword, COLOR_GREEN); - pel[2] = GET_DATA_BYTE(pword, COLOR_BLUE); - if (fwrite(pel, 1, 3, fp) != 3) - writeerror = 1; - } - } - } - } - - pixDestroy(&pixs); - if (writeerror) - return ERROR_INT("image write fail", procName, 1); - return 0; -} - - -/*! - * \brief pixWriteStreamAsciiPnm() - * - * \param[in] fp file stream opened for write - * \param[in] pix - * \return 0 if OK; 1 on error - * - * Writes "ASCII" format only: - * 1 bpp --> pbm P1 - * 2, 4, 8, 16 bpp, no colormap or grayscale colormap --> pgm P2 - * 2, 4, 8 bpp with color-valued colormap, or rgb --> rgb ppm P3 - */ -l_ok -pixWriteStreamAsciiPnm(FILE *fp, - PIX *pix) -{ -char buffer[256]; -l_uint8 cval[3]; -l_int32 h, w, d, ds, i, j, k, maxval, count; -l_uint32 val; -PIX *pixs; - - PROCNAME("pixWriteStreamAsciiPnm"); - - if (!fp) - return ERROR_INT("fp not defined", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - pixGetDimensions(pix, &w, &h, &d); - if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) - return ERROR_INT("d not in {1,2,4,8,16,32}", procName, 1); - - /* If a colormap exists, remove and convert to grayscale or rgb */ - if (pixGetColormap(pix) != NULL) - pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); - else - pixs = pixClone(pix); - ds = pixGetDepth(pixs); - - if (ds == 1) { /* binary */ - fprintf(fp, "P1\n# Ascii PBM file written by leptonica " - "(www.leptonica.com)\n%d %d\n", w, h); - - count = 0; - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - pixGetPixel(pixs, j, i, &val); - if (val == 0) - fputc('0', fp); - else /* val == 1 */ - fputc('1', fp); - fputc(' ', fp); - count += 2; - if (count >= 70) { - fputc('\n', fp); - count = 0; - } - } - } - } else if (ds == 2 || ds == 4 || ds == 8 || ds == 16) { /* grayscale */ - maxval = (1 << ds) - 1; - fprintf(fp, "P2\n# Ascii PGM file written by leptonica " - "(www.leptonica.com)\n%d %d\n%d\n", w, h, maxval); - - count = 0; - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - pixGetPixel(pixs, j, i, &val); - if (ds == 2) { - snprintf(buffer, sizeof(buffer), "%1d ", val); - fwrite(buffer, 1, 2, fp); - count += 2; - } else if (ds == 4) { - snprintf(buffer, sizeof(buffer), "%2d ", val); - fwrite(buffer, 1, 3, fp); - count += 3; - } else if (ds == 8) { - snprintf(buffer, sizeof(buffer), "%3d ", val); - fwrite(buffer, 1, 4, fp); - count += 4; - } else { /* ds == 16 */ - snprintf(buffer, sizeof(buffer), "%5d ", val); - fwrite(buffer, 1, 6, fp); - count += 6; - } - if (count >= 60) { - fputc('\n', fp); - count = 0; - } - } - } - } else { /* rgb color */ - fprintf(fp, "P3\n# Ascii PPM file written by leptonica " - "(www.leptonica.com)\n%d %d\n255\n", w, h); - count = 0; - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - pixGetPixel(pixs, j, i, &val); - cval[0] = GET_DATA_BYTE(&val, COLOR_RED); - cval[1] = GET_DATA_BYTE(&val, COLOR_GREEN); - cval[2] = GET_DATA_BYTE(&val, COLOR_BLUE); - for (k = 0; k < 3; k++) { - snprintf(buffer, sizeof(buffer), "%3d ", cval[k]); - fwrite(buffer, 1, 4, fp); - count += 4; - if (count >= 60) { - fputc('\n', fp); - count = 0; - } - } - } - } - } - - pixDestroy(&pixs); - return 0; -} - - -/*! - * \brief pixWriteStreamPam() - * - * \param[in] fp file stream opened for write - * \param[in] pix - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) This writes arbitrary PAM (P7) packed format. - * (2) 24 bpp rgb are not supported in leptonica, but this will - * write them out as a packed array of bytes (3 to a pixel). - *- */ -l_ok -pixWriteStreamPam(FILE *fp, - PIX *pix) -{ -l_uint8 val8; -l_uint8 pel[8]; -l_uint16 val16; -l_int32 h, w, d, ds, i, j; -l_int32 wpls, spps, filebpl, writeerror, maxval; -l_uint32 *pword, *datas, *lines; -PIX *pixs; - - PROCNAME("pixWriteStreamPam"); - - if (!fp) - return ERROR_INT("fp not defined", procName, 1); - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - - pixGetDimensions(pix, &w, &h, &d); - if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 24 && d != 32) - return ERROR_INT("d not in {1,2,4,8,16,24,32}", procName, 1); - - /* If a colormap exists, remove and convert to grayscale or rgb */ - if (pixGetColormap(pix) != NULL) - pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); - else - pixs = pixClone(pix); - ds = pixGetDepth(pixs); - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - spps = pixGetSpp(pixs); - if (ds < 24) - maxval = (1 << ds) - 1; - else - maxval = 255; - - writeerror = 0; - fprintf(fp, "P7\n# Arbitrary PAM file written by leptonica " - "(www.leptonica.com)\n"); - fprintf(fp, "WIDTH %d\n", w); - fprintf(fp, "HEIGHT %d\n", h); - fprintf(fp, "DEPTH %d\n", spps); - fprintf(fp, "MAXVAL %d\n", maxval); - if (spps == 1 && ds == 1) - fprintf(fp, "TUPLTYPE BLACKANDWHITE\n"); - else if (spps == 1) - fprintf(fp, "TUPLTYPE GRAYSCALE\n"); - else if (spps == 3) - fprintf(fp, "TUPLTYPE RGB\n"); - else if (spps == 4) - fprintf(fp, "TUPLTYPE RGB_ALPHA\n"); - fprintf(fp, "ENDHDR\n"); - - switch (d) { - case 1: - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) { - val8 = GET_DATA_BIT(lines, j); - val8 ^= 1; /* pam apparently uses white-is-1 photometry */ - if (fwrite(&val8, 1, 1, fp) != 1) - writeerror = 1; - } - } - break; - - case 2: - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) { - val8 = GET_DATA_DIBIT(lines, j); - if (fwrite(&val8, 1, 1, fp) != 1) - writeerror = 1; - } - } - break; - - case 4: - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) { - val8 = GET_DATA_QBIT(lines, j); - if (fwrite(&val8, 1, 1, fp) != 1) - writeerror = 1; - } - } - break; - - case 8: - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) { - val8 = GET_DATA_BYTE(lines, j); - if (fwrite(&val8, 1, 1, fp) != 1) - writeerror = 1; - } - } - break; - - case 16: - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < w; j++) { - val16 = GET_DATA_TWO_BYTES(lines, j); - if (fwrite(&val16, 2, 1, fp) != 1) - writeerror = 1; - } - } - break; - - case 24: - filebpl = 3 * w; - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - if (fwrite(lines, 1, filebpl, fp) != filebpl) - writeerror = 1; - } - break; - - case 32: - switch (spps) { - case 3: - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < wpls; j++) { - pword = lines + j; - pel[0] = GET_DATA_BYTE(pword, COLOR_RED); - pel[1] = GET_DATA_BYTE(pword, COLOR_GREEN); - pel[2] = GET_DATA_BYTE(pword, COLOR_BLUE); - if (fwrite(pel, 1, 3, fp) != 3) - writeerror = 1; - } - } - break; - case 4: - for (i = 0; i < h; i++) { - lines = datas + i * wpls; - for (j = 0; j < wpls; j++) { - pword = lines + j; - pel[0] = GET_DATA_BYTE(pword, COLOR_RED); - pel[1] = GET_DATA_BYTE(pword, COLOR_GREEN); - pel[2] = GET_DATA_BYTE(pword, COLOR_BLUE); - pel[3] = GET_DATA_BYTE(pword, L_ALPHA_CHANNEL); - if (fwrite(pel, 1, 4, fp) != 4) - writeerror = 1; - } - } - break; - } - break; - } - - pixDestroy(&pixs); - if (writeerror) - return ERROR_INT("image write fail", procName, 1); - return 0; -} - - -/*---------------------------------------------------------------------* - * Read/write to memory * - *---------------------------------------------------------------------*/ - -/*! - * \brief pixReadMemPnm() - * - * \param[in] data const; pnm-encoded - * \param[in] size of data - * \return pix, or NULL on error - * - *- * Notes: - * (1) The %size byte of %data must be a null character. - *- */ -PIX * -pixReadMemPnm(const l_uint8 *data, - size_t size) -{ -FILE *fp; -PIX *pix; - - PROCNAME("pixReadMemPnm"); - - if (!data) - return (PIX *)ERROR_PTR("data not defined", procName, NULL); - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return (PIX *)ERROR_PTR("stream not opened", procName, NULL); - pix = pixReadStreamPnm(fp); - fclose(fp); - if (!pix) L_ERROR("pix not read\n", procName); - return pix; -} - - -/*! - * \brief readHeaderMemPnm() - * - * \param[in] data const; pnm-encoded - * \param[in] size of data - * \param[out] pw [optional] - * \param[out] ph [optional] - * \param[out] pd [optional] - * \param[out] ptype [optional] pnm type - * \param[out] pbps [optional] bits/sample - * \param[out] pspp [optional] samples/pixel - * \return 0 if OK, 1 on error - */ -l_ok -readHeaderMemPnm(const l_uint8 *data, - size_t size, - l_int32 *pw, - l_int32 *ph, - l_int32 *pd, - l_int32 *ptype, - l_int32 *pbps, - l_int32 *pspp) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("readHeaderMemPnm"); - - if (!data) - return ERROR_INT("data not defined", procName, 1); - - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = freadHeaderPnm(fp, pw, ph, pd, ptype, pbps, pspp); - fclose(fp); - if (ret) - return ERROR_INT("header data read failed", procName, 1); - return 0; -} - - -/*! - * \brief pixWriteMemPnm() - * - * \param[out] pdata data of PNM image - * \param[out] psize size of returned data - * \param[in] pix - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) See pixWriteStreamPnm() for usage. This version writes to - * memory instead of to a file stream. - *- */ -l_ok -pixWriteMemPnm(l_uint8 **pdata, - size_t *psize, - PIX *pix) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("pixWriteMemPnm"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1 ); - if (!psize) - return ERROR_INT("&size not defined", procName, 1 ); - if (!pix) - return ERROR_INT("&pix not defined", procName, 1 ); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = pixWriteStreamPnm(fp, pix); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = pixWriteStreamPnm(fp, pix); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} - - -/*! - * \brief pixWriteMemPam() - * - * \param[out] pdata data of PAM image - * \param[out] psize size of returned data - * \param[in] pix - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) See pixWriteStreamPnm() for usage. This version writes to - * memory instead of to a file stream. - *- */ -l_ok -pixWriteMemPam(l_uint8 **pdata, - size_t *psize, - PIX *pix) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("pixWriteMemPam"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1 ); - if (!psize) - return ERROR_INT("&size not defined", procName, 1 ); - if (!pix) - return ERROR_INT("&pix not defined", procName, 1 ); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = pixWriteStreamPam(fp, pix); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = pixWriteStreamPam(fp, pix); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} - - - -/*--------------------------------------------------------------------* - * Static helpers * - *--------------------------------------------------------------------*/ -/*! - * \brief pnmReadNextAsciiValue() - * - * Return: 0 if OK, 1 on error or EOF. - * - * Notes: - * (1) This reads the next sample value in ASCII from the file. - */ -static l_int32 -pnmReadNextAsciiValue(FILE *fp, - l_int32 *pval) -{ -l_int32 c, ignore; - - PROCNAME("pnmReadNextAsciiValue"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0; - if (!fp) - return ERROR_INT("stream not open", procName, 1); - do { /* skip whitespace */ - if ((c = fgetc(fp)) == EOF) - return 1; - } while (c == ' ' || c == '\t' || c == '\n' || c == '\r'); - - fseek(fp, -1L, SEEK_CUR); /* back up one byte */ - ignore = fscanf(fp, "%d", pval); - return 0; -} - - -/*! - * \brief pnmReadNextNumber() - * - * \param[in] fp file stream - * \param[out] pval value as an integer - * \return 0 if OK, 1 on error or EOF. - * - *- * Notes: - * (1) This reads the next set of numeric chars, returning - * the value and swallowing the trailing whitespace character. - * This is needed to read the maxval in the header, which - * precedes the binary data. - *- */ -static l_int32 -pnmReadNextNumber(FILE *fp, - l_int32 *pval) -{ -char buf[8]; -l_int32 i, c, foundws; - - PROCNAME("pnmReadNextNumber"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0; - if (!fp) - return ERROR_INT("stream not open", procName, 1); - - /* The ASCII characters for the number are followed by exactly - * one whitespace character. */ - foundws = FALSE; - for (i = 0; i < 8; i++) - buf[i] = '\0'; - for (i = 0; i < 8; i++) { - if ((c = fgetc(fp)) == EOF) - return ERROR_INT("end of file reached", procName, 1); - if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { - foundws = TRUE; - buf[i] = '\n'; - break; - } - if (!isdigit(c)) - return ERROR_INT("char read is not a digit", procName, 1); - buf[i] = c; - } - if (!foundws) - return ERROR_INT("no whitespace found", procName, 1); - if (sscanf(buf, "%d", pval) != 1) - return ERROR_INT("invalid read", procName, 1); - return 0; -} - -/*! - * \brief pnmReadNextString() - * - * \param[in] fp file stream - * \param[out] buff pointer to the string buffer - * \param[in] size max. number of charactes in buffer - * \return 0 if OK, 1 on error or EOF. - * - *- * Notes: - * (1) This reads the next set of alphanumeric chars, - * returning the string and swallowing the trailing - * whitespace characters. - * This is needed to read header lines, which precede - * the P7 format binary data. - *- */ -static l_int32 -pnmReadNextString(FILE *fp, - char *buff, - l_int32 size) -{ -l_int32 i, c; - - PROCNAME("pnmReadNextString"); - - if (!buff) - return ERROR_INT("buff not defined", procName, 1); - *buff = '\0'; - if (!fp) - return ERROR_INT("stream not open", procName, 1); - if (size <= 0) - return ERROR_INT("size is too small", procName, 1); - - do { /* skip whitespace */ - if ((c = fgetc(fp)) == EOF) - return ERROR_INT("end of file reached", procName, 1); - } while (c == ' ' || c == '\t' || c == '\n' || c == '\r'); - - /* Comment lines are allowed to appear - * anywhere in the header lines */ - if (c == '#') { - do { /* each line starting with '#' */ - do { /* this entire line */ - if ((c = fgetc(fp)) == EOF) - return ERROR_INT("end of file reached", procName, 1); - } while (c != '\n'); - if ((c = fgetc(fp)) == EOF) - return ERROR_INT("end of file reached", procName, 1); - } while (c == '#'); - } - - /* The next string ends when there is - * a whitespace character following. */ - for (i = 0; i < size - 1; i++) { - if (c == ' ' || c == '\t' || c == '\n' || c == '\r') - break; - buff[i] = c; - if ((c = fgetc(fp)) == EOF) - return ERROR_INT("end of file reached", procName, 1); - } - buff[i] = '\0'; - - /* Back up one byte */ - fseek(fp, -1L, SEEK_CUR); - if (i >= size - 1) - return ERROR_INT("buff size too small", procName, 1); - - /* Skip over trailing spaces and tabs */ - for (;;) { - if ((c = fgetc(fp)) == EOF) - return ERROR_INT("end of file reached", procName, 1); - if (c != ' ' && c != '\t') - break; - } - - /* Back up one byte */ - fseek(fp, -1L, SEEK_CUR); - return 0; -} - - -/*! - * \brief pnmSkipCommentLines() - * - * Return: 0 if OK, 1 on error or EOF - * - * Notes: - * (1) Comment lines begin with '#' - * (2) Usage: caller should check return value for EOF - */ -static l_int32 -pnmSkipCommentLines(FILE *fp) -{ -l_int32 c; - - PROCNAME("pnmSkipCommentLines"); - - if (!fp) - return ERROR_INT("stream not open", procName, 1); - if ((c = fgetc(fp)) == EOF) - return 1; - if (c == '#') { - do { /* each line starting with '#' */ - do { /* this entire line */ - if ((c = fgetc(fp)) == EOF) - return 1; - } while (c != '\n'); - if ((c = fgetc(fp)) == EOF) - return 1; - } while (c == '#'); - } - - /* Back up one byte */ - fseek(fp, -1L, SEEK_CUR); - return 0; -} - -/* --------------------------------------------*/ -#endif /* USE_PNMIO */ -/* --------------------------------------------*/ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pnmiostub.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pnmiostub.c deleted file mode 100644 index 2238755c..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/pnmiostub.c +++ /dev/null @@ -1,120 +0,0 @@ -/*====================================================================* - - 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 pnmiostub.c - *- * - * Stubs for pnmio.c functions - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include "allheaders.h" - -/* --------------------------------------------*/ -#if !USE_PNMIO /* defined in environ.h */ -/* --------------------------------------------*/ - -PIX * pixReadStreamPnm(FILE *fp) -{ - return (PIX * )ERROR_PTR("function not present", "pixReadStreamPnm", NULL); -} - -/* ----------------------------------------------------------------------*/ - -l_ok readHeaderPnm(const char *filename, l_int32 *pw, l_int32 *ph, - l_int32 *pd, l_int32 *ptype, l_int32 *pbps, - l_int32 *pspp) -{ - return ERROR_INT("function not present", "readHeaderPnm", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok freadHeaderPnm(FILE *fp, l_int32 *pw, l_int32 *ph, l_int32 *pd, - l_int32 *ptype, l_int32 *pbps, l_int32 *pspp) -{ - return ERROR_INT("function not present", "freadHeaderPnm", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixWriteStreamPnm(FILE *fp, PIX *pix) -{ - return ERROR_INT("function not present", "pixWriteStreamPnm", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixWriteStreamAsciiPnm(FILE *fp, PIX *pix) -{ - return ERROR_INT("function not present", "pixWriteStreamAsciiPnm", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixWriteStreamPam(FILE *fp, PIX *pix) -{ - return ERROR_INT("function not present", "pixWriteStreamPam", 1); -} - -/* ----------------------------------------------------------------------*/ - -PIX * pixReadMemPnm(const l_uint8 *cdata, size_t size) -{ - return (PIX * )ERROR_PTR("function not present", "pixReadMemPnm", NULL); -} - -/* ----------------------------------------------------------------------*/ - -l_ok readHeaderMemPnm(const l_uint8 *cdata, size_t size, l_int32 *pw, - l_int32 *ph, l_int32 *pd, l_int32 *ptype, - l_int32 *pbps, l_int32 *pspp) -{ - return ERROR_INT("function not present", "readHeaderMemPnm", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixWriteMemPnm(l_uint8 **pdata, size_t *psize, PIX *pix) -{ - return ERROR_INT("function not present", "pixWriteMemPnm", 1); -} -/* ----------------------------------------------------------------------*/ - -l_ok pixWriteMemPam(l_uint8 **pdata, size_t *psize, PIX *pix) -{ - return ERROR_INT("function not present", "pixWriteMemPam", 1); -} - - -/* --------------------------------------------*/ -#endif /* !USE_PNMIO */ -/* --------------------------------------------*/ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/projective.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/projective.c deleted file mode 100644 index 527b80c0..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/projective.c +++ /dev/null @@ -1,926 +0,0 @@ -/*====================================================================* - - 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 projective.c - * - * - * Projective (4 pt) image transformation using a sampled - * (to nearest integer) transform on each dest point - * PIX *pixProjectiveSampledPta() - * PIX *pixProjectiveSampled() - * - * Projective (4 pt) image transformation using interpolation - * (or area mapping) for anti-aliasing images that are - * 2, 4, or 8 bpp gray, or colormapped, or 32 bpp RGB - * PIX *pixProjectivePta() - * PIX *pixProjective() - * PIX *pixProjectivePtaColor() - * PIX *pixProjectiveColor() - * PIX *pixProjectivePtaGray() - * PIX *pixProjectiveGray() - * - * Projective transform including alpha (blend) component - * PIX *pixProjectivePtaWithAlpha() - * - * Projective coordinate transformation - * l_int32 getProjectiveXformCoeffs() - * l_int32 projectiveXformSampledPt() - * l_int32 projectiveXformPt() - * - * A projective transform can be specified as a specific functional - * mapping between 4 points in the source and 4 points in the dest. - * It preserves straight lines, but is less stable than a bilinear - * transform, because it contains a division by a quantity that - * can get arbitrarily small.) - * - * We give both a projective coordinate transformation and - * two projective image transformations. - * - * For the former, we ask for the coordinate value (x',y') - * in the transformed space for any point (x,y) in the original - * space. The coefficients of the transformation are found by - * solving 8 simultaneous equations for the 8 coordinates of - * the 4 points in src and dest. The transformation can then - * be used to compute the associated image transform, by - * computing, for each dest pixel, the relevant pixel(s) in - * the source. This can be done either by taking the closest - * src pixel to each transformed dest pixel ("sampling") or - * by doing an interpolation and averaging over 4 source - * pixels with appropriate weightings ("interpolated"). - * - * A typical application would be to remove keystoning - * due to a projective transform in the imaging system. - * - * The projective transform is given by specifying two equations: - * - * x' = (ax + by + c) / (gx + hy + 1) - * y' = (dx + ey + f) / (gx + hy + 1) - * - * where the eight coefficients have been computed from four - * sets of these equations, each for two corresponding data pts. - * In practice, once the coefficients are known, we use the - * equations "backwards": for each point (x,y) in the dest image, - * these two equations are used to compute the corresponding point - * (x',y') in the src. That computed point in the src is then used - * to determine the corresponding dest pixel value in one of two ways: - * - * ~ sampling: simply take the value of the src pixel in which this - * point falls - * ~ interpolation: take appropriate linear combinations of the - * four src pixels that this dest pixel would - * overlap, with the coefficients proportional - * to the amount of overlap - * - * For small warp where there is little scale change, (e.g., - * for rotation) area mapping is nearly equivalent to interpolation. - * - * Typical relative timing of pointwise transforms (sampled = 1.0): - * 8 bpp: sampled 1.0 - * interpolated 1.5 - * 32 bpp: sampled 1.0 - * interpolated 1.6 - * Additionally, the computation time/pixel is nearly the same - * for 8 bpp and 32 bpp, for both sampled and interpolated. - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include -#include "allheaders.h" - -extern l_float32 AlphaMaskBorderVals[2]; - -/*------------------------------------------------------------n - * Sampled projective image transformation * - *-------------------------------------------------------------*/ -/*! - * \brief pixProjectiveSampledPta() - * - * \param[in] pixs all depths - * \param[in] ptad 4 pts of final coordinate space - * \param[in] ptas 4 pts of initial coordinate space - * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK - * \return pixd, or NULL on error - * - * - * Notes: - * (1) Brings in either black or white pixels from the boundary. - * (2) Retains colormap, which you can do for a sampled transform.. - * (3) No 3 of the 4 points may be collinear. - * (4) For 8 and 32 bpp pix, better quality is obtained by the - * somewhat slower pixProjectivePta(). See that - * function for relative timings between sampled and interpolated. - *- */ -PIX * -pixProjectiveSampledPta(PIX *pixs, - PTA *ptad, - PTA *ptas, - l_int32 incolor) -{ -l_float32 *vc; -PIX *pixd; - - PROCNAME("pixProjectiveSampledPta"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!ptas) - return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); - if (!ptad) - return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); - if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) - return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); - if (ptaGetCount(ptas) != 4) - return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); - if (ptaGetCount(ptad) != 4) - return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); - - /* Get backwards transform from dest to src, and apply it */ - getProjectiveXformCoeffs(ptad, ptas, &vc); - pixd = pixProjectiveSampled(pixs, vc, incolor); - LEPT_FREE(vc); - - return pixd; -} - - -/*! - * \brief pixProjectiveSampled() - * - * \param[in] pixs all depths - * \param[in] vc vector of 8 coefficients for projective transform - * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK - * \return pixd, or NULL on error - * - *- * Notes: - * (1) Brings in either black or white pixels from the boundary. - * (2) Retains colormap, which you can do for a sampled transform.. - * (3) For 8 or 32 bpp, much better quality is obtained by the - * somewhat slower pixProjective(). See that function - * for relative timings between sampled and interpolated. - *- */ -PIX * -pixProjectiveSampled(PIX *pixs, - l_float32 *vc, - l_int32 incolor) -{ -l_int32 i, j, w, h, d, x, y, wpls, wpld, color, cmapindex; -l_uint32 val; -l_uint32 *datas, *datad, *lines, *lined; -PIX *pixd; -PIXCMAP *cmap; - - PROCNAME("pixProjectiveSampled"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!vc) - return (PIX *)ERROR_PTR("vc not defined", procName, NULL); - if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) - return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32) - return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8 or 16", procName, NULL); - - /* Init all dest pixels to color to be brought in from outside */ - pixd = pixCreateTemplate(pixs); - if ((cmap = pixGetColormap(pixs)) != NULL) { - if (incolor == L_BRING_IN_WHITE) - color = 1; - else - color = 0; - pixcmapAddBlackOrWhite(cmap, color, &cmapindex); - pixSetAllArbitrary(pixd, cmapindex); - } else { - if ((d == 1 && incolor == L_BRING_IN_WHITE) || - (d > 1 && incolor == L_BRING_IN_BLACK)) { - pixClearAll(pixd); - } else { - pixSetAll(pixd); - } - } - - /* Scan over the dest pixels */ - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - projectiveXformSampledPt(vc, j, i, &x, &y); - if (x < 0 || y < 0 || x >=w || y >= h) - continue; - lines = datas + y * wpls; - if (d == 1) { - val = GET_DATA_BIT(lines, x); - SET_DATA_BIT_VAL(lined, j, val); - } else if (d == 8) { - val = GET_DATA_BYTE(lines, x); - SET_DATA_BYTE(lined, j, val); - } else if (d == 32) { - lined[j] = lines[x]; - } else if (d == 2) { - val = GET_DATA_DIBIT(lines, x); - SET_DATA_DIBIT(lined, j, val); - } else if (d == 4) { - val = GET_DATA_QBIT(lines, x); - SET_DATA_QBIT(lined, j, val); - } - } - } - - return pixd; -} - - -/*---------------------------------------------------------------------* - * Interpolated projective image transformation * - *---------------------------------------------------------------------*/ -/*! - * \brief pixProjectivePta() - * - * \param[in] pixs all depths; colormap ok - * \param[in] ptad 4 pts of final coordinate space - * \param[in] ptas 4 pts of initial coordinate space - * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK - * \return pixd, or NULL on error - * - *- * Notes: - * (1) Brings in either black or white pixels from the boundary - * (2) Removes any existing colormap, if necessary, before transforming - *- */ -PIX * -pixProjectivePta(PIX *pixs, - PTA *ptad, - PTA *ptas, - l_int32 incolor) -{ -l_int32 d; -l_uint32 colorval; -PIX *pixt1, *pixt2, *pixd; - - PROCNAME("pixProjectivePta"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!ptas) - return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); - if (!ptad) - return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); - if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) - return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); - if (ptaGetCount(ptas) != 4) - return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); - if (ptaGetCount(ptad) != 4) - return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); - - if (pixGetDepth(pixs) == 1) - return pixProjectiveSampledPta(pixs, ptad, ptas, incolor); - - /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ - pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - d = pixGetDepth(pixt1); - if (d < 8) - pixt2 = pixConvertTo8(pixt1, FALSE); - else - pixt2 = pixClone(pixt1); - d = pixGetDepth(pixt2); - - /* Compute actual color to bring in from edges */ - colorval = 0; - if (incolor == L_BRING_IN_WHITE) { - if (d == 8) - colorval = 255; - else /* d == 32 */ - colorval = 0xffffff00; - } - - if (d == 8) - pixd = pixProjectivePtaGray(pixt2, ptad, ptas, colorval); - else /* d == 32 */ - pixd = pixProjectivePtaColor(pixt2, ptad, ptas, colorval); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - return pixd; -} - - -/*! - * \brief pixProjective() - * - * \param[in] pixs all depths; colormap ok - * \param[in] vc vector of 8 coefficients for projective transform - * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK - * \return pixd, or NULL on error - * - *- * Notes: - * (1) Brings in either black or white pixels from the boundary - * (2) Removes any existing colormap, if necessary, before transforming - *- */ -PIX * -pixProjective(PIX *pixs, - l_float32 *vc, - l_int32 incolor) -{ -l_int32 d; -l_uint32 colorval; -PIX *pixt1, *pixt2, *pixd; - - PROCNAME("pixProjective"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!vc) - return (PIX *)ERROR_PTR("vc not defined", procName, NULL); - - if (pixGetDepth(pixs) == 1) - return pixProjectiveSampled(pixs, vc, incolor); - - /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ - pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - d = pixGetDepth(pixt1); - if (d < 8) - pixt2 = pixConvertTo8(pixt1, FALSE); - else - pixt2 = pixClone(pixt1); - d = pixGetDepth(pixt2); - - /* Compute actual color to bring in from edges */ - colorval = 0; - if (incolor == L_BRING_IN_WHITE) { - if (d == 8) - colorval = 255; - else /* d == 32 */ - colorval = 0xffffff00; - } - - if (d == 8) - pixd = pixProjectiveGray(pixt2, vc, colorval); - else /* d == 32 */ - pixd = pixProjectiveColor(pixt2, vc, colorval); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - return pixd; -} - - -/*! - * \brief pixProjectivePtaColor() - * - * \param[in] pixs 32 bpp - * \param[in] ptad 4 pts of final coordinate space - * \param[in] ptas 4 pts of initial coordinate space - * \param[in] colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE - * \return pixd, or NULL on error - */ -PIX * -pixProjectivePtaColor(PIX *pixs, - PTA *ptad, - PTA *ptas, - l_uint32 colorval) -{ -l_float32 *vc; -PIX *pixd; - - PROCNAME("pixProjectivePtaColor"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!ptas) - return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); - if (!ptad) - return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); - if (ptaGetCount(ptas) != 4) - return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); - if (ptaGetCount(ptad) != 4) - return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); - - /* Get backwards transform from dest to src, and apply it */ - getProjectiveXformCoeffs(ptad, ptas, &vc); - pixd = pixProjectiveColor(pixs, vc, colorval); - LEPT_FREE(vc); - - return pixd; -} - - -/*! - * \brief pixProjectiveColor() - * - * \param[in] pixs 32 bpp - * \param[in] vc vector of 8 coefficients for projective transform - * \param[in] colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE - * \return pixd, or NULL on error - */ -PIX * -pixProjectiveColor(PIX *pixs, - l_float32 *vc, - l_uint32 colorval) -{ -l_int32 i, j, w, h, d, wpls, wpld; -l_uint32 val; -l_uint32 *datas, *datad, *lined; -l_float32 x, y; -PIX *pix1, *pix2, *pixd; - - PROCNAME("pixProjectiveColor"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 32) - return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); - if (!vc) - return (PIX *)ERROR_PTR("vc not defined", procName, NULL); - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - pixd = pixCreateTemplate(pixs); - pixSetAllArbitrary(pixd, colorval); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - /* Iterate over destination pixels */ - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - /* Compute float src pixel location corresponding to (i,j) */ - projectiveXformPt(vc, j, i, &x, &y); - linearInterpolatePixelColor(datas, wpls, w, h, x, y, colorval, - &val); - *(lined + j) = val; - } - } - - /* If rgba, transform the pixs alpha channel and insert in pixd */ - if (pixGetSpp(pixs) == 4) { - pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); - pix2 = pixProjectiveGray(pix1, vc, 255); /* bring in opaque */ - pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); - pixDestroy(&pix1); - pixDestroy(&pix2); - } - - return pixd; -} - - -/*! - * \brief pixProjectivePtaGray() - * - * \param[in] pixs 8 bpp - * \param[in] ptad 4 pts of final coordinate space - * \param[in] ptas 4 pts of initial coordinate space - * \param[in] grayval 0 to bring in BLACK, 255 for WHITE - * \return pixd, or NULL on error - */ -PIX * -pixProjectivePtaGray(PIX *pixs, - PTA *ptad, - PTA *ptas, - l_uint8 grayval) -{ -l_float32 *vc; -PIX *pixd; - - PROCNAME("pixProjectivePtaGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!ptas) - return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); - if (!ptad) - return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); - if (ptaGetCount(ptas) != 4) - return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); - if (ptaGetCount(ptad) != 4) - return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); - - /* Get backwards transform from dest to src, and apply it */ - getProjectiveXformCoeffs(ptad, ptas, &vc); - pixd = pixProjectiveGray(pixs, vc, grayval); - LEPT_FREE(vc); - - return pixd; -} - - - -/*! - * \brief pixProjectiveGray() - * - * \param[in] pixs 8 bpp - * \param[in] vc vector of 8 coefficients for projective transform - * \param[in] grayval 0 to bring in BLACK, 255 for WHITE - * \return pixd, or NULL on error - */ -PIX * -pixProjectiveGray(PIX *pixs, - l_float32 *vc, - l_uint8 grayval) -{ -l_int32 i, j, w, h, wpls, wpld, val; -l_uint32 *datas, *datad, *lined; -l_float32 x, y; -PIX *pixd; - - PROCNAME("pixProjectiveGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - if (pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); - if (!vc) - return (PIX *)ERROR_PTR("vc not defined", procName, NULL); - - datas = pixGetData(pixs); - wpls = pixGetWpl(pixs); - pixd = pixCreateTemplate(pixs); - pixSetAllArbitrary(pixd, grayval); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - /* Iterate over destination pixels */ - for (i = 0; i < h; i++) { - lined = datad + i * wpld; - for (j = 0; j < w; j++) { - /* Compute float src pixel location corresponding to (i,j) */ - projectiveXformPt(vc, j, i, &x, &y); - linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val); - SET_DATA_BYTE(lined, j, val); - } - } - - return pixd; -} - - -/*---------------------------------------------------------------------------* - * Projective transform including alpha (blend) component * - *---------------------------------------------------------------------------*/ -/*! - * \brief pixProjectivePtaWithAlpha() - * - * \param[in] pixs 32 bpp rgb - * \param[in] ptad 4 pts of final coordinate space - * \param[in] ptas 4 pts of initial coordinate space - * \param[in] pixg [optional] 8 bpp, for alpha channel, can be null - * \param[in] fract between 0.0 and 1.0, with 0.0 fully transparent - * and 1.0 fully opaque - * \param[in] border of pixels added to capture transformed source pixels - * \return pixd, or NULL on error - * - *- * Notes: - * (1) The alpha channel is transformed separately from pixs, - * and aligns with it, being fully transparent outside the - * boundary of the transformed pixs. For pixels that are fully - * transparent, a blending function like pixBlendWithGrayMask() - * will give zero weight to corresponding pixels in pixs. - * (2) If pixg is NULL, it is generated as an alpha layer that is - * partially opaque, using %fract. Otherwise, it is cropped - * to pixs if required and %fract is ignored. The alpha channel - * in pixs is never used. - * (3) Colormaps are removed. - * (4) When pixs is transformed, it doesn't matter what color is brought - * in because the alpha channel will be transparent (0) there. - * (5) To avoid losing source pixels in the destination, it may be - * necessary to add a border to the source pix before doing - * the projective transformation. This can be any non-negative - * number. - * (6) The input %ptad and %ptas are in a coordinate space before - * the border is added. Internally, we compensate for this - * before doing the projective transform on the image after - * the border is added. - * (7) The default setting for the border values in the alpha channel - * is 0 (transparent) for the outermost ring of pixels and - * (0.5 * fract * 255) for the second ring. When blended over - * a second image, this - * (a) shrinks the visible image to make a clean overlap edge - * with an image below, and - * (b) softens the edges by weakening the aliasing there. - * Use l_setAlphaMaskBorder() to change these values. - *- */ -PIX * -pixProjectivePtaWithAlpha(PIX *pixs, - PTA *ptad, - PTA *ptas, - PIX *pixg, - l_float32 fract, - l_int32 border) -{ -l_int32 ws, hs, d; -PIX *pixd, *pixb1, *pixb2, *pixg2, *pixga; -PTA *ptad2, *ptas2; - - PROCNAME("pixProjectivePtaWithAlpha"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &ws, &hs, &d); - if (d != 32 && pixGetColormap(pixs) == NULL) - return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); - if (pixg && pixGetDepth(pixg) != 8) { - L_WARNING("pixg not 8 bpp; using 'fract' transparent alpha\n", - procName); - pixg = NULL; - } - if (!pixg && (fract < 0.0 || fract > 1.0)) { - L_WARNING("invalid fract; using 1.0 (fully transparent)\n", procName); - fract = 1.0; - } - if (!pixg && fract == 0.0) - L_WARNING("fully opaque alpha; image will not be blended\n", procName); - if (!ptad) - return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); - if (!ptas) - return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); - - /* Add border; the color doesn't matter */ - pixb1 = pixAddBorder(pixs, border, 0); - - /* Transform the ptr arrays to work on the bordered image */ - ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); - ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); - - /* Do separate projective transform of rgb channels of pixs - * and of pixg */ - pixd = pixProjectivePtaColor(pixb1, ptad2, ptas2, 0); - if (!pixg) { - pixg2 = pixCreate(ws, hs, 8); - if (fract == 1.0) - pixSetAll(pixg2); - else - pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract)); - } else { - pixg2 = pixResizeToMatch(pixg, NULL, ws, hs); - } - if (ws > 10 && hs > 10) { /* see note 7 */ - pixSetBorderRingVal(pixg2, 1, - (l_int32)(255.0 * fract * AlphaMaskBorderVals[0])); - pixSetBorderRingVal(pixg2, 2, - (l_int32)(255.0 * fract * AlphaMaskBorderVals[1])); - - } - pixb2 = pixAddBorder(pixg2, border, 0); /* must be black border */ - pixga = pixProjectivePtaGray(pixb2, ptad2, ptas2, 0); - pixSetRGBComponent(pixd, pixga, L_ALPHA_CHANNEL); - pixSetSpp(pixd, 4); - - pixDestroy(&pixg2); - pixDestroy(&pixb1); - pixDestroy(&pixb2); - pixDestroy(&pixga); - ptaDestroy(&ptad2); - ptaDestroy(&ptas2); - return pixd; -} - - -/*-------------------------------------------------------------* - * Projective coordinate transformation * - *-------------------------------------------------------------*/ -/*! - * \brief getProjectiveXformCoeffs() - * - * \param[in] ptas source 4 points; unprimed - * \param[in] ptad transformed 4 points; primed - * \param[out] pvc vector of coefficients of transform - * \return 0 if OK; 1 on error - * - * We have a set of 8 equations, describing the projective - * transformation that takes 4 points ptas into 4 other - * points ptad. These equations are: - * - * x1' = c[0]*x1 + c[1]*y1 + c[2]) / (c[6]*x1 + c[7]*y1 + 1 - * y1' = c[3]*x1 + c[4]*y1 + c[5]) / (c[6]*x1 + c[7]*y1 + 1 - * x2' = c[0]*x2 + c[1]*y2 + c[2]) / (c[6]*x2 + c[7]*y2 + 1 - * y2' = c[3]*x2 + c[4]*y2 + c[5]) / (c[6]*x2 + c[7]*y2 + 1 - * x3' = c[0]*x3 + c[1]*y3 + c[2]) / (c[6]*x3 + c[7]*y3 + 1 - * y3' = c[3]*x3 + c[4]*y3 + c[5]) / (c[6]*x3 + c[7]*y3 + 1 - * x4' = c[0]*x4 + c[1]*y4 + c[2]) / (c[6]*x4 + c[7]*y4 + 1 - * y4' = c[3]*x4 + c[4]*y4 + c[5]) / (c[6]*x4 + c[7]*y4 + 1 - * - * Multiplying both sides of each eqn by the denominator, we get - * - * AC = B - * - * where B and C are column vectors - * - * B = [ x1' y1' x2' y2' x3' y3' x4' y4' ] - * C = [ c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] ] - * - * and A is the 8x8 matrix - * - * x1 y1 1 0 0 0 -x1*x1' -y1*x1' - * 0 0 0 x1 y1 1 -x1*y1' -y1*y1' - * x2 y2 1 0 0 0 -x2*x2' -y2*x2' - * 0 0 0 x2 y2 1 -x2*y2' -y2*y2' - * x3 y3 1 0 0 0 -x3*x3' -y3*x3' - * 0 0 0 x3 y3 1 -x3*y3' -y3*y3' - * x4 y4 1 0 0 0 -x4*x4' -y4*x4' - * 0 0 0 x4 y4 1 -x4*y4' -y4*y4' - * - * These eight equations are solved here for the coefficients C. - * - * These eight coefficients can then be used to find the mapping - * x,y) --> (x',y': - * - * x' = c[0]x + c[1]y + c[2]) / (c[6]x + c[7]y + 1 - * y' = c[3]x + c[4]y + c[5]) / (c[6]x + c[7]y + 1 - * - * that is implemented in projectiveXformSampled and - * projectiveXFormInterpolated. - */ -l_ok -getProjectiveXformCoeffs(PTA *ptas, - PTA *ptad, - l_float32 **pvc) -{ -l_int32 i; -l_float32 x1, y1, x2, y2, x3, y3, x4, y4; -l_float32 *b; /* rhs vector of primed coords X'; coeffs returned in *pvc */ -l_float32 *a[8]; /* 8x8 matrix A */ - - PROCNAME("getProjectiveXformCoeffs"); - - if (!ptas) - return ERROR_INT("ptas not defined", procName, 1); - if (!ptad) - return ERROR_INT("ptad not defined", procName, 1); - if (!pvc) - return ERROR_INT("&vc not defined", procName, 1); - - b = (l_float32 *)LEPT_CALLOC(8, sizeof(l_float32)); - *pvc = b; - ptaGetPt(ptas, 0, &x1, &y1); - ptaGetPt(ptas, 1, &x2, &y2); - ptaGetPt(ptas, 2, &x3, &y3); - ptaGetPt(ptas, 3, &x4, &y4); - ptaGetPt(ptad, 0, &b[0], &b[1]); - ptaGetPt(ptad, 1, &b[2], &b[3]); - ptaGetPt(ptad, 2, &b[4], &b[5]); - ptaGetPt(ptad, 3, &b[6], &b[7]); - - for (i = 0; i < 8; i++) - a[i] = (l_float32 *)LEPT_CALLOC(8, sizeof(l_float32)); - a[0][0] = x1; - a[0][1] = y1; - a[0][2] = 1.; - a[0][6] = -x1 * b[0]; - a[0][7] = -y1 * b[0]; - a[1][3] = x1; - a[1][4] = y1; - a[1][5] = 1; - a[1][6] = -x1 * b[1]; - a[1][7] = -y1 * b[1]; - a[2][0] = x2; - a[2][1] = y2; - a[2][2] = 1.; - a[2][6] = -x2 * b[2]; - a[2][7] = -y2 * b[2]; - a[3][3] = x2; - a[3][4] = y2; - a[3][5] = 1; - a[3][6] = -x2 * b[3]; - a[3][7] = -y2 * b[3]; - a[4][0] = x3; - a[4][1] = y3; - a[4][2] = 1.; - a[4][6] = -x3 * b[4]; - a[4][7] = -y3 * b[4]; - a[5][3] = x3; - a[5][4] = y3; - a[5][5] = 1; - a[5][6] = -x3 * b[5]; - a[5][7] = -y3 * b[5]; - a[6][0] = x4; - a[6][1] = y4; - a[6][2] = 1.; - a[6][6] = -x4 * b[6]; - a[6][7] = -y4 * b[6]; - a[7][3] = x4; - a[7][4] = y4; - a[7][5] = 1; - a[7][6] = -x4 * b[7]; - a[7][7] = -y4 * b[7]; - - gaussjordan(a, b, 8); - - for (i = 0; i < 8; i++) - LEPT_FREE(a[i]); - - return 0; -} - - -/*! - * \brief projectiveXformSampledPt() - * - * \param[in] vc vector of 8 coefficients - * \param[in] x, y initial point - * \param[out] pxp, pyp transformed point - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) This finds the nearest pixel coordinates of the transformed point. - * (2) It does not check ptrs for returned data! - *- */ -l_ok -projectiveXformSampledPt(l_float32 *vc, - l_int32 x, - l_int32 y, - l_int32 *pxp, - l_int32 *pyp) -{ -l_float32 factor; - - PROCNAME("projectiveXformSampledPt"); - - if (!vc) - return ERROR_INT("vc not defined", procName, 1); - - factor = 1. / (vc[6] * x + vc[7] * y + 1.); - *pxp = (l_int32)(factor * (vc[0] * x + vc[1] * y + vc[2]) + 0.5); - *pyp = (l_int32)(factor * (vc[3] * x + vc[4] * y + vc[5]) + 0.5); - return 0; -} - - -/*! - * \brief projectiveXformPt() - * - * \param[in] vc vector of 8 coefficients - * \param[in] x, y initial point - * \param[out] pxp, pyp transformed point - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) This computes the floating point location of the transformed point. - * (2) It does not check ptrs for returned data! - *- */ -l_ok -projectiveXformPt(l_float32 *vc, - l_int32 x, - l_int32 y, - l_float32 *pxp, - l_float32 *pyp) -{ -l_float32 factor; - - PROCNAME("projectiveXformPt"); - - if (!vc) - return ERROR_INT("vc not defined", procName, 1); - - factor = 1. / (vc[6] * x + vc[7] * y + 1.); - *pxp = factor * (vc[0] * x + vc[1] * y + vc[2]); - *pyp = factor * (vc[3] * x + vc[4] * y + vc[5]); - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/psio1.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/psio1.c deleted file mode 100644 index bf825a97..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/psio1.c +++ /dev/null @@ -1,1077 +0,0 @@ -/*====================================================================* - - 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 psio1.c - *- * - * |=============================================================| - * | Important note | - * |=============================================================| - * | Some of these functions require I/O libraries such as | - * | libtiff, libjpeg, and libz. If you do not have these | - * | libraries, some calls will fail. | - * | | - * | You can manually deactivate all PostScript writing by | - * | setting this in environ.h: | - * | \code | - * | #define USE_PSIO 0 | - * | \endcode | - * | in environ.h. This will link psio1stub.c | - * |=============================================================| - * - * This is a PostScript "device driver" for wrapping images - * in PostScript. The images can be rendered by a PostScript - * interpreter for viewing, using evince or gv. They can also be - * rasterized for printing, using gs or an embedded interpreter - * in a PostScript printer. And they can be converted to a pdf - * using gs (ps2pdf). - * - * Convert specified files to PS - * l_int32 convertFilesToPS() - * l_int32 sarrayConvertFilesToPS() - * l_int32 convertFilesFittedToPS() - * l_int32 sarrayConvertFilesFittedToPS() - * l_int32 writeImageCompressedToPSFile() - * - * Convert mixed text/image files to PS - * l_int32 convertSegmentedPagesToPS() - * l_int32 pixWriteSegmentedPageToPS() - * l_int32 pixWriteMixedToPS() - * - * Convert any image file to PS for embedding - * l_int32 convertToPSEmbed() - * - * Write all images in a pixa out to PS - * l_int32 pixaWriteCompressedToPS() - * l_int32 pixWriteCompressedToPS() - * - * These PostScript converters are used in three different ways. - * - * (1) For embedding a PS file in a program like TeX. - * convertToPSEmbed() handles this for levels 1, 2 and 3 output, - * and prog/converttops wraps this in an executable. - * converttops is a generalization of Thomas Merz's jpeg2ps wrapper, - * in that it works for all types (formats, depth, colormap) - * of input images and gives PS output in one of these formats - * * level 1 (uncompressed) - * * level 2 (compressed ccittg4 or dct) - * * level 3 (compressed flate) - * - * (2) For composing a set of pages with any number of images - * painted on them, in either level 2 or level 3 formats. - * - * (3) For printing a page image or a set of page images, at a - * resolution that optimally fills the page, using - * convertFilesFittedToPS(). - * - * The top-level calls of utilities in category 2, which can compose - * multiple images on a page, and which generate a PostScript file for - * printing or display (e.g., conversion to pdf), are: - * convertFilesToPS() - * convertFilesFittedToPS() - * convertSegmentedPagesToPS() - * - * All images are output with page numbers. Bounding box hints are - * more subtle. They must be included for embeding images in - * TeX, for example, and the low-level writers include bounding - * box hints by default. However, these hints should not be included for - * multi-page PostScript that is composed of a sequence of images; - * consequently, they are not written when calling higher level - * functions such as convertFilesToPS(), convertFilesFittedToPS() - * and convertSegmentedPagesToPS(). The function l_psWriteBoundingBox() - * sets a flag to give low-level control over this. - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include "allheaders.h" - -/* --------------------------------------------*/ -#if USE_PSIO /* defined in environ.h */ - /* --------------------------------------------*/ - -/*-------------------------------------------------------------* - * Convert files in a directory to PS * - *-------------------------------------------------------------*/ -/* - * \brief convertFilesToPS() - * - * \param[in] dirin input directory - * \param[in] substr [optional] substring filter on filenames; can be NULL - * \param[in] res typ. 300 or 600 ppi - * \param[in] fileout output ps file - * \return 0 if OK, 1 on error - * - * - * Notes: - * (1) This generates a PS file for all image files in a specified - * directory that contain the substr pattern to be matched. - * (2) Each image is written to a separate page in the output PS file. - * (3) All images are written compressed: - * * if tiffg4 --> use ccittg4 - * * if jpeg --> use dct - * * all others --> use flate - * If the image is jpeg or tiffg4, we use the existing compressed - * strings for the encoding; otherwise, we read the image into - * a pix and flate-encode the pieces. - * (4) The resolution is often confusing. It is interpreted - * as the resolution of the output display device: "If the - * input image were digitized at 300 ppi, what would it - * look like when displayed at res ppi." So, for example, - * if res = 100 ppi, then the display pixels are 3x larger - * than the 300 ppi pixels, and the image will be rendered - * 3x larger. - * (5) The size of the PostScript file is independent of the resolution, - * because the entire file is encoded. The res parameter just - * tells the PS decomposer how to render the page. Therefore, - * for minimum file size without loss of visual information, - * if the output res is less than 300, you should downscale - * the image to the output resolution before wrapping in PS. - * (6) The "canvas" on which the image is rendered, at the given - * output resolution, is a standard page size (8.5 x 11 in). - *- */ -l_ok -convertFilesToPS(const char *dirin, - const char *substr, - l_int32 res, - const char *fileout) -{ -SARRAY *sa; - - PROCNAME("convertFilesToPS"); - - if (!dirin) - return ERROR_INT("dirin not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - if (res <= 0) { - L_INFO("setting res to 300 ppi\n", procName); - res = 300; - } - if (res < 10 || res > 4000) - L_WARNING("res is typically in the range 300-600 ppi\n", procName); - - /* Get all filtered and sorted full pathnames. */ - sa = getSortedPathnamesInDirectory(dirin, substr, 0, 0); - - /* Generate the PS file. Don't use bounding boxes. */ - l_psWriteBoundingBox(FALSE); - sarrayConvertFilesToPS(sa, res, fileout); - l_psWriteBoundingBox(TRUE); - sarrayDestroy(&sa); - return 0; -} - - -/* - - * \brief sarrayConvertFilesToPS() - * - * \param[in] sarray of full path names - * \param[in] res typ. 300 or 600 ppi - * \param[in] fileout output ps file - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) See convertFilesToPS() - *- */ -l_ok -sarrayConvertFilesToPS(SARRAY *sa, - l_int32 res, - const char *fileout) -{ -char *fname; -l_int32 i, nfiles, index, ret, format; - - PROCNAME("sarrayConvertFilesToPS"); - - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - if (res <= 0) { - L_INFO("setting res to 300 ppi\n", procName); - res = 300; - } - if (res < 10 || res > 4000) - L_WARNING("res is typically in the range 300-600 ppi\n", procName); - - nfiles = sarrayGetCount(sa); - for (i = 0, index = 0; i < nfiles; i++) { - fname = sarrayGetString(sa, i, L_NOCOPY); - ret = pixReadHeader(fname, &format, NULL, NULL, NULL, NULL, NULL); - if (ret) continue; - if (format == IFF_UNKNOWN) - continue; - - writeImageCompressedToPSFile(fname, fileout, res, &index); - } - - return 0; -} - - -/* - * \brief convertFilesFittedToPS() - * - * \param[in] dirin input directory - * \param[in] substr [optional] substring filter on filenames; can be NULL) - * \param[in] xpts desired size in printer points; use 0 for default - * \param[in] ypts desired size in printer points; use 0 for default - * \param[in] fileout output ps file - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This generates a PS file for all files in a specified directory - * that contain the substr pattern to be matched. - * (2) Each image is written to a separate page in the output PS file. - * (3) All images are written compressed: - * * if tiffg4 --> use ccittg4 - * * if jpeg --> use dct - * * all others --> use flate - * If the image is jpeg or tiffg4, we use the existing compressed - * strings for the encoding; otherwise, we read the image into - * a pix and flate-encode the pieces. - * (4) The resolution is internally determined such that the images - * are rendered, in at least one direction, at 100% of the given - * size in printer points. Use 0.0 for xpts or ypts to get - * the default value, which is 612.0 or 792.0, rsp. - * (5) The size of the PostScript file is independent of the resolution, - * because the entire file is encoded. The %xpts and %ypts - * parameter tells the PS decomposer how to render the page. - *- */ -l_ok -convertFilesFittedToPS(const char *dirin, - const char *substr, - l_float32 xpts, - l_float32 ypts, - const char *fileout) -{ -SARRAY *sa; - - PROCNAME("convertFilesFittedToPS"); - - if (!dirin) - return ERROR_INT("dirin not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - if (xpts <= 0.0) { - L_INFO("setting xpts to 612.0 ppi\n", procName); - xpts = 612.0; - } - if (ypts <= 0.0) { - L_INFO("setting ypts to 792.0 ppi\n", procName); - ypts = 792.0; - } - if (xpts < 100.0 || xpts > 2000.0 || ypts < 100.0 || ypts > 2000.0) - L_WARNING("xpts,ypts are typically in the range 500-800\n", procName); - - /* Get all filtered and sorted full pathnames. */ - sa = getSortedPathnamesInDirectory(dirin, substr, 0, 0); - - /* Generate the PS file. Don't use bounding boxes. */ - l_psWriteBoundingBox(FALSE); - sarrayConvertFilesFittedToPS(sa, xpts, ypts, fileout); - l_psWriteBoundingBox(TRUE); - sarrayDestroy(&sa); - return 0; -} - - -/* - * \brief sarrayConvertFilesFittedToPS() - * - * \param[in] sarray of full path names - * \param[in] xpts desired size in printer points; use 0 for default - * \param[in] ypts desired size in printer points; use 0 for default - * \param[in] fileout output ps file - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) See convertFilesFittedToPS() - *- */ -l_ok -sarrayConvertFilesFittedToPS(SARRAY *sa, - l_float32 xpts, - l_float32 ypts, - const char *fileout) -{ -char *fname; -l_int32 ret, i, w, h, nfiles, index, format, res; - - PROCNAME("sarrayConvertFilesFittedToPS"); - - if (!sa) - return ERROR_INT("sa not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - if (xpts <= 0.0) { - L_INFO("setting xpts to 612.0\n", procName); - xpts = 612.0; - } - if (ypts <= 0.0) { - L_INFO("setting ypts to 792.0\n", procName); - ypts = 792.0; - } - if (xpts < 100.0 || xpts > 2000.0 || ypts < 100.0 || ypts > 2000.0) - L_WARNING("xpts,ypts are typically in the range 500-800\n", procName); - - nfiles = sarrayGetCount(sa); - for (i = 0, index = 0; i < nfiles; i++) { - fname = sarrayGetString(sa, i, L_NOCOPY); - ret = pixReadHeader(fname, &format, &w, &h, NULL, NULL, NULL); - if (ret) continue; - if (format == IFF_UNKNOWN) - continue; - - /* Be sure the entire image is wrapped */ - if (xpts * h < ypts * w) - res = (l_int32)((l_float32)w * 72.0 / xpts); - else - res = (l_int32)((l_float32)h * 72.0 / ypts); - - writeImageCompressedToPSFile(fname, fileout, res, &index); - } - - return 0; -} - - -/* - * \brief writeImageCompressedToPSFile() - * - * \param[in] filein input image file - * \param[in] fileout output ps file - * \param[in] res output printer resolution - * \param[in,out] pindex index of image in output ps file - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This wraps a single page image in PS. - * (2) The input file can be in any format. It is compressed as follows: - * * if in tiffg4 --> use ccittg4 - * * if in jpeg --> use dct - * * all others --> use flate - * (3) Before the first call, set %index = 0. %index is incremented - * if the page is successfully written. It is used to decide - * whether to write (index == 0) or append (index > 0) to the file. - *- */ -l_ok -writeImageCompressedToPSFile(const char *filein, - const char *fileout, - l_int32 res, - l_int32 *pindex) -{ -const char *op; -l_int32 format, retval; - - PROCNAME("writeImageCompressedToPSFile"); - - if (!pindex) - return ERROR_INT("&index not defined", procName, 1); - - findFileFormat(filein, &format); - if (format == IFF_UNKNOWN) { - L_ERROR("format of %s not known\n", procName, filein); - return 1; - } - - op = (*pindex == 0) ? "w" : "a"; - if (format == IFF_JFIF_JPEG) { - retval = convertJpegToPS(filein, fileout, op, 0, 0, - res, 1.0, *pindex + 1, TRUE); - } else if (format == IFF_TIFF_G4) { - retval = convertG4ToPS(filein, fileout, op, 0, 0, - res, 1.0, *pindex + 1, FALSE, TRUE); - } else { /* all other image formats */ - retval = convertFlateToPS(filein, fileout, op, 0, 0, - res, 1.0, *pindex + 1, TRUE); - } - if (retval == 0) (*pindex)++; - - return retval; -} - - -/*-------------------------------------------------------------* - * Convert mixed text/image files to PS * - *-------------------------------------------------------------*/ -/* - * \brief convertSegmentedPagesToPS() - * - * \param[in] pagedir input page image directory - * \param[in] pagestr [optional] substring filter on page filenames; - * can be NULL - * \param[in] page_numpre number of characters in page name before number - * \param[in] maskdir input mask image directory - * \param[in] maskstr [optional] substring filter on mask filenames; - * can be NULL - * \param[in] mask_numpre number of characters in mask name before number - * \param[in] numpost number of characters in names after number - * \param[in] maxnum only consider page numbers up to this value - * \param[in] textscale scale of text output relative to pixs - * \param[in] imagescale scale of image output relative to pixs - * \param[in] threshold for binarization; typ. about 190; 0 for default - * \param[in] fileout output ps file - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This generates a PS file for all page image and mask files in two - * specified directories and that contain the page numbers as - * specified below. The two directories can be the same, in which - * case the page and mask files are differentiated by the two - * substrings for string matches. - * (2) The page images are taken in lexicographic order. - * Mask images whose numbers match the page images are used to - * segment the page images. Page images without a matching - * mask image are scaled, thresholded and rendered entirely as text. - * (3) Each PS page is generated as a compressed representation of - * the page image, where the part of the image under the mask - * is suitably scaled and compressed as DCT (i.e., jpeg), and - * the remaining part of the page is suitably scaled, thresholded, - * compressed as G4 (i.e., tiff g4), and rendered by painting - * black through the resulting text mask. - * (4) The scaling is typically 2x down for the DCT component - * (%imagescale = 0.5) and 2x up for the G4 component - * (%textscale = 2.0). - * (5) The resolution is automatically set to fit to a - * letter-size (8.5 x 11 inch) page. - * (6) Both the DCT and the G4 encoding are PostScript level 2. - * (7) It is assumed that the page number is contained within - * the basename (the filename without directory or extension). - * %page_numpre is the number of characters in the page basename - * preceding the actual page number; %mask_numpre is likewise for - * the mask basename; %numpost is the number of characters - * following the page number. For example, for mask name - * mask_006.tif, mask_numpre = 5 ("mask_). - * (8) To render a page as is -- that is, with no thresholding - * of any pixels -- use a mask in the mask directory that is - * full size with all pixels set to 1. If the page is 1 bpp, - * it is not necessary to have a mask. - *- */ -l_ok -convertSegmentedPagesToPS(const char *pagedir, - const char *pagestr, - l_int32 page_numpre, - const char *maskdir, - const char *maskstr, - l_int32 mask_numpre, - l_int32 numpost, - l_int32 maxnum, - l_float32 textscale, - l_float32 imagescale, - l_int32 threshold, - const char *fileout) -{ -l_int32 pageno, i, npages; -PIX *pixs, *pixm; -SARRAY *sapage, *samask; - - PROCNAME("convertSegmentedPagesToPS"); - - if (!pagedir) - return ERROR_INT("pagedir not defined", procName, 1); - if (!maskdir) - return ERROR_INT("maskdir not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - if (threshold <= 0) { - L_INFO("setting threshold to 190\n", procName); - threshold = 190; - } - - /* Get numbered full pathnames; max size of sarray is maxnum */ - sapage = getNumberedPathnamesInDirectory(pagedir, pagestr, - page_numpre, numpost, maxnum); - samask = getNumberedPathnamesInDirectory(maskdir, maskstr, - mask_numpre, numpost, maxnum); - sarrayPadToSameSize(sapage, samask, ""); - if ((npages = sarrayGetCount(sapage)) == 0) { - sarrayDestroy(&sapage); - sarrayDestroy(&samask); - return ERROR_INT("no matching pages found", procName, 1); - } - - /* Generate the PS file */ - pageno = 1; - for (i = 0; i < npages; i++) { - if ((pixs = pixReadIndexed(sapage, i)) == NULL) - continue; - pixm = pixReadIndexed(samask, i); - pixWriteSegmentedPageToPS(pixs, pixm, textscale, imagescale, - threshold, pageno, fileout); - pixDestroy(&pixs); - pixDestroy(&pixm); - pageno++; - } - - sarrayDestroy(&sapage); - sarrayDestroy(&samask); - return 0; -} - - -/* - * \brief pixWriteSegmentedPageToPS() - * - * \param[in] pixs all depths; colormap ok - * \param[in] pixm [optional] 1 bpp segmentation mask over image region - * \param[in] textscale scale of text output relative to pixs - * \param[in] imagescale scale of image output relative to pixs - * \param[in] threshold for binarization; typ. about 190; 0 for default - * \param[in] pageno page number in set; use 1 for new output file - * \param[in] fileout output ps file - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This generates the PS string for a mixed text/image page, - * and adds it to an existing file if %pageno > 1. - * The PS output is determined by fitting the result to - * a letter-size (8.5 x 11 inch) page. - * (2) The two images (pixs and pixm) are at the same resolution - * (typically 300 ppi). They are used to generate two compressed - * images, pixb and pixc, that are put directly into the output - * PS file. - * (3) pixb is the text component. In the PostScript world, we think of - * it as a mask through which we paint black. It is produced by - * scaling pixs by %textscale, and thresholding to 1 bpp. - * (4) pixc is the image component, which is that part of pixs under - * the mask pixm. It is scaled from pixs by %imagescale. - * (5) Typical values are textscale = 2.0 and imagescale = 0.5. - * (6) If pixm == NULL, the page has only text. If it is all black, - * the page is all image and has no text. - * (7) This can be used to write a multi-page PS file, by using - * sequential page numbers with the same output file. It can - * also be used to write separate PS files for each page, - * by using different output files with %pageno = 0 or 1. - *- */ -l_ok -pixWriteSegmentedPageToPS(PIX *pixs, - PIX *pixm, - l_float32 textscale, - l_float32 imagescale, - l_int32 threshold, - l_int32 pageno, - const char *fileout) -{ -l_int32 alltext, notext, d, ret; -l_uint32 val; -l_float32 scaleratio; -PIX *pixmi, *pixmis, *pixt, *pixg, *pixsc, *pixb, *pixc; - - PROCNAME("pixWriteSegmentedPageToPS"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - if (imagescale <= 0.0 || textscale <= 0.0) - return ERROR_INT("relative scales must be > 0.0", procName, 1); - - /* Analyze the page. Determine the ratio by which the - * binary text mask is scaled relative to the image part. - * If there is no image region (alltext == TRUE), the - * text mask will be rendered directly to fit the page, - * and scaleratio = 1.0. */ - alltext = TRUE; - notext = FALSE; - scaleratio = 1.0; - if (pixm) { - pixZero(pixm, &alltext); /* pixm empty: all text */ - if (alltext) { - pixm = NULL; /* treat it as not existing here */ - } else { - pixmi = pixInvert(NULL, pixm); - pixZero(pixmi, ¬ext); /* pixm full; no text */ - pixDestroy(&pixmi); - scaleratio = textscale / imagescale; - } - } - - if (pixGetDepth(pixs) == 1) { /* render tiff g4 */ - pixb = pixClone(pixs); - pixc = NULL; - } else { - pixt = pixConvertTo8Or32(pixs, L_CLONE, 0); /* clone if possible */ - - /* Get the binary text mask. Note that pixg cannot be a - * clone of pixs, because it may be altered by pixSetMasked(). */ - pixb = NULL; - if (notext == FALSE) { - d = pixGetDepth(pixt); - if (d == 8) - pixg = pixCopy(NULL, pixt); - else /* d == 32 */ - pixg = pixConvertRGBToLuminance(pixt); - if (pixm) /* clear out the image parts */ - pixSetMasked(pixg, pixm, 255); - if (textscale == 1.0) - pixsc = pixClone(pixg); - else if (textscale >= 0.7) - pixsc = pixScaleGrayLI(pixg, textscale, textscale); - else - pixsc = pixScaleAreaMap(pixg, textscale, textscale); - pixb = pixThresholdToBinary(pixsc, threshold); - pixDestroy(&pixg); - pixDestroy(&pixsc); - } - - /* Get the scaled image region */ - pixc = NULL; - if (pixm) { - if (imagescale == 1.0) - pixsc = pixClone(pixt); /* can possibly be a clone of pixs */ - else - pixsc = pixScale(pixt, imagescale, imagescale); - - /* If pixm is not full, clear the pixels in pixsc - * corresponding to bg in pixm, where there can be text - * that is written through the mask pixb. Note that - * we could skip this and use pixsc directly in - * pixWriteMixedToPS(); however, clearing these - * non-image regions to a white background will reduce - * the size of pixc (relative to pixsc), and hence - * reduce the size of the PS file that is generated. - * Use a copy so that we don't accidentally alter pixs. */ - if (notext == FALSE) { - pixmis = pixScale(pixm, imagescale, imagescale); - pixmi = pixInvert(NULL, pixmis); - val = (d == 8) ? 0xff : 0xffffff00; - pixc = pixCopy(NULL, pixsc); - pixSetMasked(pixc, pixmi, val); /* clear non-image part */ - pixDestroy(&pixmis); - pixDestroy(&pixmi); - } else { - pixc = pixClone(pixsc); - } - pixDestroy(&pixsc); - } - pixDestroy(&pixt); - } - - /* Generate the PS file. Don't use bounding boxes. */ - l_psWriteBoundingBox(FALSE); - ret = pixWriteMixedToPS(pixb, pixc, scaleratio, pageno, fileout); - l_psWriteBoundingBox(TRUE); - pixDestroy(&pixb); - pixDestroy(&pixc); - return ret; -} - - -/* - * \brief pixWriteMixedToPS() - * - * \param[in] pixb [optional] 1 bpp mask; typically for text - * \param[in] pixc [optional] 8 or 32 bpp image regions - * \param[in] scale scale factor for rendering pixb, relative to pixc; - * typ. 4.0 - * \param[in] pageno page number in set; use 1 for new output file - * \param[in] fileout output ps file - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This low level function generates the PS string for a mixed - * text/image page, and adds it to an existing file if - * %pageno > 1. - * (2) The two images (pixb and pixc) are typically generated at the - * resolution that they will be rendered in the PS file. - * (3) pixb is the text component. In the PostScript world, we think of - * it as a mask through which we paint black. - * (4) pixc is the (typically halftone) image component. It is - * white in the rest of the page. To minimize the size of the - * PS file, it should be rendered at a resolution that is at - * least equal to its actual resolution. - * (5) %scale gives the ratio of resolution of pixb to pixc. - * Typical resolutions are: 600 ppi for pixb, 150 ppi for pixc; - * so %scale = 4.0. If one of the images is not defined, - * the value of %scale is ignored. - * (6) We write pixc with DCT compression (jpeg). This is followed - * by painting the text as black through the mask pixb. If - * pixc doesn't exist (alltext), we write the text with the - * PS "image" operator instead of the "imagemask" operator, - * because ghostscript's ps2pdf is flaky when the latter is used. - * (7) The actual output resolution is determined by fitting the - * result to a letter-size (8.5 x 11 inch) page. - *- */ -l_ok -pixWriteMixedToPS(PIX *pixb, - PIX *pixc, - l_float32 scale, - l_int32 pageno, - const char *fileout) -{ -char *tname; -const char *op; -l_int32 resb, resc, endpage, maskop, ret; - - PROCNAME("pixWriteMixedToPS"); - - if (!pixb && !pixc) - return ERROR_INT("pixb and pixc both undefined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - - /* Compute the resolution that fills a letter-size page. */ - if (!pixc) { - resb = getResLetterPage(pixGetWidth(pixb), pixGetHeight(pixb), 0); - } else { - resc = getResLetterPage(pixGetWidth(pixc), pixGetHeight(pixc), 0); - if (pixb) - resb = (l_int32)(scale * resc); - } - - /* Write the jpeg image first */ - if (pixc) { - tname = l_makeTempFilename(); - pixWrite(tname, pixc, IFF_JFIF_JPEG); - endpage = (pixb) ? FALSE : TRUE; - op = (pageno <= 1) ? "w" : "a"; - ret = convertJpegToPS(tname, fileout, op, 0, 0, resc, 1.0, - pageno, endpage); - lept_rmfile(tname); - LEPT_FREE(tname); - if (ret) - return ERROR_INT("jpeg data not written", procName, 1); - } - - /* Write the binary data, either directly or, if there is - * a jpeg image on the page, through the mask. */ - if (pixb) { - tname = l_makeTempFilename(); - pixWrite(tname, pixb, IFF_TIFF_G4); - op = (pageno <= 1 && !pixc) ? "w" : "a"; - maskop = (pixc) ? 1 : 0; - ret = convertG4ToPS(tname, fileout, op, 0, 0, resb, 1.0, - pageno, maskop, 1); - lept_rmfile(tname); - LEPT_FREE(tname); - if (ret) - return ERROR_INT("tiff data not written", procName, 1); - } - - return 0; -} - - -/*-------------------------------------------------------------* - * Convert any image file to PS for embedding * - *-------------------------------------------------------------*/ -/* - * \brief convertToPSEmbed() - * - * \param[in] filein input image file, any format - * \param[in] fileout output ps file - * \param[in] level PostScript compression: 1 (uncompressed), 2 or 3 - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This is a wrapper function that generates a PS file with - * a bounding box, from any input image file. - * (2) Do the best job of compression given the specified level. - * %level=3 does flate compression on anything that is not - * tiffg4 (1 bpp) or jpeg (8 bpp or rgb). - * (3) If %level=2 and the file is not tiffg4 or jpeg, it will - * first be written to file as jpeg with quality = 75. - * This will remove the colormap and cause some degradation - * in the image. - * (4) The bounding box is required when a program such as TeX - * (through epsf) places and rescales the image. It is - * sized for fitting the image to an 8.5 x 11.0 inch page. - *- */ -l_ok -convertToPSEmbed(const char *filein, - const char *fileout, - l_int32 level) -{ -char *tname; -l_int32 d, format; -PIX *pix, *pixs; - - PROCNAME("convertToPSEmbed"); - - if (!filein) - return ERROR_INT("filein not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - if (level != 1 && level != 2 && level != 3) { - L_ERROR("invalid level specified; using level 2\n", procName); - level = 2; - } - - if (level == 1) { /* no compression */ - pixWritePSEmbed(filein, fileout); - return 0; - } - - /* Find the format and write out directly if in jpeg or tiff g4 */ - findFileFormat(filein, &format); - if (format == IFF_JFIF_JPEG) { - convertJpegToPSEmbed(filein, fileout); - return 0; - } else if (format == IFF_TIFF_G4) { - convertG4ToPSEmbed(filein, fileout); - return 0; - } else if (format == IFF_UNKNOWN) { - L_ERROR("format of %s not known\n", procName, filein); - return 1; - } - - /* If level 3, flate encode. */ - if (level == 3) { - convertFlateToPSEmbed(filein, fileout); - return 0; - } - - /* OK, it's level 2, so we must convert to jpeg or tiff g4 */ - if ((pixs = pixRead(filein)) == NULL) - return ERROR_INT("image not read from file", procName, 1); - d = pixGetDepth(pixs); - if ((d == 2 || d == 4) && !pixGetColormap(pixs)) - pix = pixConvertTo8(pixs, 0); - else if (d == 16) - pix = pixConvert16To8(pixs, L_MS_BYTE); - else - pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - pixDestroy(&pixs); - if (!pix) - return ERROR_INT("converted pix not made", procName, 1); - - d = pixGetDepth(pix); - tname = l_makeTempFilename(); - if (d == 1) { - if (pixWrite(tname, pix, IFF_TIFF_G4)) { - LEPT_FREE(tname); - pixDestroy(&pix); - return ERROR_INT("g4 tiff not written", procName, 1); - } - convertG4ToPSEmbed(tname, fileout); - } else { - if (pixWrite(tname, pix, IFF_JFIF_JPEG)) { - LEPT_FREE(tname); - pixDestroy(&pix); - return ERROR_INT("jpeg not written", procName, 1); - } - convertJpegToPSEmbed(tname, fileout); - } - - lept_rmfile(tname); - LEPT_FREE(tname); - pixDestroy(&pix); - return 0; -} - - -/*-------------------------------------------------------------* - * Write all images in a pixa out to PS * - *-------------------------------------------------------------*/ -/* - * \brief pixaWriteCompressedToPS() - * - * \param[in] pixa any set of images - * \param[in] fileout output ps file - * \param[in] res resolution for the set of input images - * \param[in] level PostScript compression capability: 2 or 3 - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This generates a PostScript file of multiple page images, - * all with bounding boxes. - * (2) See pixWriteCompressedToPS() for details. - * (3) To generate a pdf from %fileout, use: - * ps2pdf- */ -l_ok -pixaWriteCompressedToPS(PIXA *pixa, - const char *fileout, - l_int32 res, - l_int32 level) -{ -l_int32 i, n, index, ret; -PIX *pix; - - PROCNAME("pixaWriteCompressedToPS"); - - if (!pixa) - return ERROR_INT("pixa not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - if (level != 2 && level != 3) { - L_ERROR("only levels 2 and 3 permitted; using level 2\n", procName); - level = 2; - } - - index = 0; - n = pixaGetCount(pixa); - for (i = 0; i < n; i++) { - pix = pixaGetPix(pixa, i, L_CLONE); - ret = pixWriteCompressedToPS(pix, fileout, res, level, &index); - if (ret) L_ERROR("PS string not written for image %d\n", procName, i); - pixDestroy(&pix); - } - return 0; -} - - -/* - * \brief pixWriteCompressedToPS() - * - * \param[in] pix any depth; colormap OK - * \param[in] fileout output ps file - * \param[in] res of input image - * \param[in] level PostScript compression capability: 2 or 3 - * \param[in,out] pindex index of image in output ps file - * \return 0 if OK, 1 on error - * - *- * - * Notes: - * (1) This generates a PostScript string for %pix, and writes it - * to a file, with a bounding box. - * (2) *pindex keeps track of the number of images that have been - * written to %fileout. If this is the first image to be - * converted, set *pindex == 0 before passing it in. If the - * PostScript string is successfully generated, this will increment - * *pindex. If *pindex > 0, the PostScript string will be - * appended to %fileout. - * (3) PostScript level 2 enables lossless tiffg4 and lossy jpeg - * compression. Level 3 adds lossless flate (essentially gzip) - * compression. - * * For images with a colormap, lossless flate is often better in - * both quality and size than jpeg. - * * The decision for images without a colormap affects compression - * efficiency: %level2 (jpeg) is usually better than %level3 (flate) - * * Because jpeg does not handle 16 bpp, if %level == 2, the image - * is converted to 8 bpp (using MSB) and compressed with jpeg, - * cmap + level2: jpeg - * cmap + level3: flate - * 1 bpp: tiffg4 - * 2 or 4 bpp + level2: jpeg - * 2 or 4 bpp + level3: flate - * 8 bpp + level2: jpeg - * 8 bpp + level3: flate - * 16 bpp + level2: jpeg [converted to 8 bpp, with warning] - * 16 bpp + level3: flate - * 32 bpp + level2: jpeg - * 32 bpp + level3: flate - *- */ -l_ok -pixWriteCompressedToPS(PIX *pix, - const char *fileout, - l_int32 res, - l_int32 level, - l_int32 *pindex) -{ -char *tname; -l_int32 writeout, d; -PIX *pixt; -PIXCMAP *cmap; - - PROCNAME("pixWriteCompressedToPS"); - - if (!pix) - return ERROR_INT("pix not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - if (level != 2 && level != 3) { - L_ERROR("only levels 2 and 3 permitted; using level 2\n", procName); - level = 2; - } - if (!pindex) - return ERROR_INT("&index not defined", procName, 1); - - tname = l_makeTempFilename(); - writeout = TRUE; - d = pixGetDepth(pix); - cmap = pixGetColormap(pix); - if (d == 1) { - if (pixWrite(tname, pix, IFF_TIFF_G4)) - writeout = FALSE; - } else if (level == 3) { - if (pixWrite(tname, pix, IFF_PNG)) - writeout = FALSE; - } else { /* level == 2 */ - if (cmap) { - pixt = pixConvertForPSWrap(pix); - if (pixWrite(tname, pixt, IFF_JFIF_JPEG)) - writeout = FALSE; - pixDestroy(&pixt); - } else if (d == 16) { - L_WARNING("d = 16; converting to 8 bpp for jpeg\n", procName); - pixt = pixConvert16To8(pix, L_MS_BYTE); - if (pixWrite(tname, pixt, IFF_JFIF_JPEG)) - writeout = FALSE; - pixDestroy(&pixt); - } else if (d == 2 || d == 4) { - pixt = pixConvertTo8(pix, 0); - if (pixWrite(tname, pixt, IFF_JFIF_JPEG)) - writeout = FALSE; - pixDestroy(&pixt); - } else if (d == 8 || d == 32) { - if (pixWrite(tname, pix, IFF_JFIF_JPEG)) - writeout = FALSE; - } else { /* shouldn't happen */ - L_ERROR("invalid depth with level 2: %d\n", procName, d); - writeout = FALSE; - } - } - - if (writeout) - writeImageCompressedToPSFile(tname, fileout, res, pindex); - - if (lept_rmfile(tname) != 0) - L_ERROR("temp file %s was not deleted\n", procName, tname); - LEPT_FREE(tname); - return (writeout) ? 0 : 1; -} - -/* --------------------------------------------*/ -#endif /* USE_PSIO */ -/* --------------------------------------------*/ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/psio1stub.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/psio1stub.c deleted file mode 100644 index f36b5e0d..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/psio1stub.c +++ /dev/null @@ -1,137 +0,0 @@ -/*====================================================================* - - 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 psio1stub.c - *- * - * Stubs for psio1.c functions - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include "allheaders.h" - -/* --------------------------------------------*/ -#if !USE_PSIO /* defined in environ.h */ -/* --------------------------------------------*/ - -l_ok convertFilesToPS(const char *dirin, const char *substr, - l_int32 res, const char *fileout) -{ - return ERROR_INT("function not present", "convertFilesToPS", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok sarrayConvertFilesToPS(SARRAY *sa, l_int32 res, const char *fileout) -{ - return ERROR_INT("function not present", "sarrayConvertFilesToPS", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertFilesFittedToPS(const char *dirin, const char *substr, - l_float32 xpts, l_float32 ypts, - const char *fileout) -{ - return ERROR_INT("function not present", "convertFilesFittedToPS", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok sarrayConvertFilesFittedToPS(SARRAY *sa, l_float32 xpts, - l_float32 ypts, const char *fileout) -{ - return ERROR_INT("function not present", "sarrayConvertFilesFittedToPS", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok writeImageCompressedToPSFile(const char *filein, const char *fileout, - l_int32 res, l_int32 *pindex) -{ - return ERROR_INT("function not present", "writeImageCompressedToPSFile", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertSegmentedPagesToPS(const char *pagedir, const char *pagestr, - l_int32 page_numpre, const char *maskdir, - const char *maskstr, l_int32 mask_numpre, - l_int32 numpost, l_int32 maxnum, - l_float32 textscale, l_float32 imagescale, - l_int32 threshold, const char *fileout) -{ - return ERROR_INT("function not present", "convertSegmentedPagesToPS", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixWriteSegmentedPageToPS(PIX *pixs, PIX *pixm, l_float32 textscale, - l_float32 imagescale, l_int32 threshold, - l_int32 pageno, const char *fileout) -{ - return ERROR_INT("function not present", "pixWriteSegmentedPagesToPS", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixWriteMixedToPS(PIX *pixb, PIX *pixc, l_float32 scale, - l_int32 pageno, const char *fileout) -{ - return ERROR_INT("function not present", "pixWriteMixedToPS", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertToPSEmbed(const char *filein, const char *fileout, l_int32 level) -{ - return ERROR_INT("function not present", "convertToPSEmbed", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixaWriteCompressedToPS(PIXA *pixa, const char *fileout, - l_int32 res, l_int32 level) -{ - return ERROR_INT("function not present", "pixaWriteCompressedtoPS", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixWriteCompressedToPS(PIX *pix, const char *fileout, l_int32 res, - l_int32 level, l_int32 *pindex) -{ - return ERROR_INT("function not present", "pixWriteCompressedtoPS", 1); -} - -/* --------------------------------------------*/ -#endif /* !USE_PSIO */ -/* --------------------------------------------*/ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/psio2.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/psio2.c deleted file mode 100644 index ea7cede1..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/psio2.c +++ /dev/null @@ -1,2044 +0,0 @@ -/*====================================================================* - - 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 psio2.c - * - * - * |=============================================================| - * | Important note | - * |=============================================================| - * | Some of these functions require I/O libraries such as | - * | libtiff, libjpeg, and libz. If you do not have these | - * | libraries, some calls will fail. | - * | | - * | You can manually deactivate all PostScript writing by | - * | setting this in environ.h: | - * | \code | - * | #define USE_PSIO 0 | - * | \endcode | - * | in environ.h. This will link psio2stub.c | - * |=============================================================| - * - * These are lower-level functions that implement a PostScript - * "device driver" for wrapping images in PostScript. The images - * can be rendered by a PostScript interpreter for viewing, - * using evince or gv. They can also be rasterized for printing, - * using gs or an embedded interpreter in a PostScript printer. - * And they can be converted to a pdf using gs (ps2pdf). - * - * For uncompressed images - * l_int32 pixWritePSEmbed() - * l_int32 pixWriteStreamPS() - * char *pixWriteStringPS() - * char *generateUncompressedPS() - * static void getScaledParametersPS() - * static l_int32 convertByteToHexAscii() - * - * For jpeg compressed images (use dct compression) - * l_int32 convertJpegToPSEmbed() - * l_int32 convertJpegToPS() - * static l_int32 convertJpegToPSString() - * static char *generateJpegPS() - * - * For g4 fax compressed images (use ccitt g4 compression) - * l_int32 convertG4ToPSEmbed() - * l_int32 convertG4ToPS() - * static l_int32 convertG4ToPSString() - * static char *generateG4PS() - * - * For multipage tiff images - * l_int32 convertTiffMultipageToPS() - * - * For flate (gzip) compressed images (e.g., png) - * l_int32 convertFlateToPSEmbed() - * l_int32 convertFlateToPS() - * static l_int32 convertFlateToPSString() - * static char *generateFlatePS() - * - * Write to memory - * l_int32 pixWriteMemPS() - * - * Converting resolution - * l_int32 getResLetterPage() - * static l_int32 getResA4Page() - * - * Setting flag for writing bounding box hint - * void l_psWriteBoundingBox() - * - * See psio1.c for higher-level functions and their usage. - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include "allheaders.h" - -/* --------------------------------------------*/ -#if USE_PSIO /* defined in environ.h */ - /* --------------------------------------------*/ - - /* Set default for writing bounding box hint */ -static l_int32 var_PS_WRITE_BOUNDING_BOX = 1; - -static const l_int32 Bufsize = 512; -static const l_int32 DefaultInputRes = 300; /* typical scan res, ppi */ -static const l_int32 MinRes = 5; -static const l_int32 MaxRes = 3000; - - /* For computing resolution that fills page to desired amount */ -static const l_int32 LetterWidth = 612; /* points */ -static const l_int32 LetterHeight = 792; /* points */ -static const l_int32 A4Width = 595; /* points */ -static const l_int32 A4Height = 842; /* points */ -static const l_float32 DefaultFillFraction = 0.95; - -#ifndef NO_CONSOLE_IO -#define DEBUG_JPEG 0 -#define DEBUG_G4 0 -#define DEBUG_FLATE 0 -#endif /* ~NO_CONSOLE_IO */ - -/* Note that the bounding box hint at the top of the generated PostScript - * file is required for the "*Embed" functions. These generate a - * PostScript file for an individual image that can be translated and - * scaled by an application that embeds the image in its output - * (e.g., in the PS output from a TeX file). - * However, bounding box hints should not be embedded in any - * PostScript image that will be composited with other images, - * where more than one image may be placed in an arbitrary location - * on a page. */ - - /* Static helper functions */ -static void getScaledParametersPS(BOX *box, l_int32 wpix, l_int32 hpix, - l_int32 res, l_float32 scale, - l_float32 *pxpt, l_float32 *pypt, - l_float32 *pwpt, l_float32 *phpt); -static void convertByteToHexAscii(l_uint8 byteval, char *pnib1, char *pnib2); -static l_ok convertJpegToPSString(const char *filein, char **poutstr, - l_int32 *pnbytes, l_int32 x, l_int32 y, - l_int32 res, l_float32 scale, - l_int32 pageno, l_int32 endpage); -static char *generateJpegPS(const char *filein, L_COMP_DATA *cid, - l_float32 xpt, l_float32 ypt, l_float32 wpt, - l_float32 hpt, l_int32 pageno, l_int32 endpage); -static l_ok convertG4ToPSString(const char *filein, char **poutstr, - l_int32 *pnbytes, l_int32 x, l_int32 y, - l_int32 res, l_float32 scale, l_int32 pageno, - l_int32 maskflag, l_int32 endpage); -static char *generateG4PS(const char *filein, L_COMP_DATA *cid, l_float32 xpt, - l_float32 ypt, l_float32 wpt, l_float32 hpt, - l_int32 maskflag, l_int32 pageno, l_int32 endpage); -static l_ok convertFlateToPSString(const char *filein, char **poutstr, - l_int32 *pnbytes, l_int32 x, l_int32 y, - l_int32 res, l_float32 scale, - l_int32 pageno, l_int32 endpage); -static char *generateFlatePS(const char *filein, L_COMP_DATA *cid, - l_float32 xpt, l_float32 ypt, l_float32 wpt, - l_float32 hpt, l_int32 pageno, l_int32 endpage); - - -/*-------------------------------------------------------------* - * For uncompressed images * - *-------------------------------------------------------------*/ -/*! - * \brief pixWritePSEmbed() - * - * \param[in] filein input file, all depths, colormap OK - * \param[in] fileout output ps file - * \return 0 if OK, 1 on error - * - * - * Notes: - * (1) This is a simple wrapper function that generates an - * uncompressed PS file, with a bounding box. - * (2) The bounding box is required when a program such as TeX - * (through epsf) places and rescales the image. - * (3) The bounding box is sized for fitting the image to an - * 8.5 x 11.0 inch page. - *- */ -l_ok -pixWritePSEmbed(const char *filein, - const char *fileout) -{ -l_int32 w, h, ret; -l_float32 scale; -FILE *fp; -PIX *pix; - - PROCNAME("pixWritePSEmbed"); - - if (!filein) - return ERROR_INT("filein not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - - if ((pix = pixRead(filein)) == NULL) - return ERROR_INT("image not read from file", procName, 1); - w = pixGetWidth(pix); - h = pixGetHeight(pix); - if (w * 11.0 > h * 8.5) - scale = 8.5 * 300. / (l_float32)w; - else - scale = 11.0 * 300. / (l_float32)h; - - if ((fp = fopenWriteStream(fileout, "wb")) == NULL) - return ERROR_INT("file not opened for write", procName, 1); - ret = pixWriteStreamPS(fp, pix, NULL, 0, scale); - fclose(fp); - - pixDestroy(&pix); - return ret; -} - - -/*! - * \brief pixWriteStreamPS() - * - * \param[in] fp file stream - * \param[in] pix - * \param[in] box [optional] - * \param[in] res can use 0 for default of 300 ppi - * \param[in] scale to prevent scaling, use either 1.0 or 0.0 - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) This writes image in PS format, optionally scaled, - * adjusted for the printer resolution, and with - * a bounding box. - * (2) For details on use of parameters, see pixWriteStringPS(). - *- */ -l_ok -pixWriteStreamPS(FILE *fp, - PIX *pix, - BOX *box, - l_int32 res, - l_float32 scale) -{ -char *outstr; -l_int32 length; -PIX *pixc; - - PROCNAME("pixWriteStreamPS"); - - if (!fp) - return (l_int32)ERROR_INT("stream not open", procName, 1); - if (!pix) - return (l_int32)ERROR_INT("pix not defined", procName, 1); - - if ((pixc = pixConvertForPSWrap(pix)) == NULL) - return (l_int32)ERROR_INT("pixc not made", procName, 1); - - if ((outstr = pixWriteStringPS(pixc, box, res, scale)) == NULL) { - pixDestroy(&pixc); - return (l_int32)ERROR_INT("outstr not made", procName, 1); - } - length = strlen(outstr); - fwrite(outstr, 1, length, fp); - LEPT_FREE(outstr); - pixDestroy(&pixc); - return 0; -} - - -/*! - * \brief pixWriteStringPS() - * - * \param[in] pixs all depths, colormap OK - * \param[in] box bounding box; can be NULL - * \param[in] res resolution, in printer ppi. Use 0 for default 300 ppi. - * \param[in] scale scale factor. If no scaling is desired, use - * either 1.0 or 0.0. Scaling just resets the resolution - * parameter; the actual scaling is done in the - * interpreter at rendering time. This is important: - * it allows you to scale the image up without - * increasing the file size. - * \return ps string if OK, or NULL on error - * - *- * a) If %box == NULL, image is placed, optionally scaled, - * in a standard b.b. at the center of the page. - * This is to be used when another program like - * TeX through epsf places the image. - * b) If %box != NULL, image is placed without a - * b.b. at the specified page location and with - * optional scaling. This is to be used when - * you want to specify exactly where and optionally - * how big you want the image to be. - * Note that all coordinates are in PS convention, - * with 0,0 at LL corner of the page: - * x,y location of LL corner of image, in mils. - * w,h scaled size, in mils. Use 0 to - * scale with "scale" and "res" input. - * - * %scale: If no scaling is desired, use either 1.0 or 0.0. - * Scaling just resets the resolution parameter; the actual - * scaling is done in the interpreter at rendering time. - * This is important: * it allows you to scale the image up - * without increasing the file size. - * - * Notes: - * (1) OK, this seems a bit complicated, because there are various - * ways to scale and not to scale. Here's a summary: - * (2) If you don't want any scaling at all: - * * if you are using a box: - * set w = 0, h = 0, and use scale = 1.0; it will print - * each pixel unscaled at printer resolution - * * if you are not using a box: - * set scale = 1.0; it will print at printer resolution - * (3) If you want the image to be a certain size in inches: - * * you must use a box and set the box (w,h) in mils - * (4) If you want the image to be scaled by a scale factor != 1.0: - * * if you are using a box: - * set w = 0, h = 0, and use the desired scale factor; - * the higher the printer resolution, the smaller the - * image will actually appear. - * * if you are not using a box: - * set the desired scale factor; the higher the printer - * resolution, the smaller the image will actually appear. - * (5) Another complication is the proliferation of distance units: - * * The interface distances are in milli-inches. - * * Three different units are used internally: - * ~ pixels (units of 1/res inch) - * ~ printer pts (units of 1/72 inch) - * ~ inches - * * Here is a quiz on volume units from a reviewer: - * How many UK milli-cups in a US kilo-teaspoon? - * (Hint: 1.0 US cup = 0.75 UK cup + 0.2 US gill; - * 1.0 US gill = 24.0 US teaspoons) - *- */ -char * -pixWriteStringPS(PIX *pixs, - BOX *box, - l_int32 res, - l_float32 scale) -{ -char nib1, nib2; -char *hexdata, *outstr; -l_uint8 byteval; -l_int32 i, j, k, w, h, d; -l_float32 wpt, hpt, xpt, ypt; -l_int32 wpl, psbpl, hexbytes, boxflag, bps; -l_uint32 *line, *data; -PIX *pix; - - PROCNAME("pixWriteStringPS"); - - if (!pixs) - return (char *)ERROR_PTR("pixs not defined", procName, NULL); - - if ((pix = pixConvertForPSWrap(pixs)) == NULL) - return (char *)ERROR_PTR("pix not made", procName, NULL); - pixGetDimensions(pix, &w, &h, &d); - - /* Get the factors by which PS scales and translates, in pts */ - if (!box) - boxflag = 0; /* no scaling; b.b. at center */ - else - boxflag = 1; /* no b.b., specify placement and optional scaling */ - getScaledParametersPS(box, w, h, res, scale, &xpt, &ypt, &wpt, &hpt); - - if (d == 1) - bps = 1; /* bits/sample */ - else /* d == 8 || d == 32 */ - bps = 8; - - /* Convert image data to hex string. psbpl is the number of - * bytes in each raster line when it is packed to the byte - * boundary (not the 32 bit word boundary, as with the pix). - * When converted to hex, the hex string has 2 bytes for - * every byte of raster data. */ - wpl = pixGetWpl(pix); - if (d == 1 || d == 8) - psbpl = (w * d + 7) / 8; - else /* d == 32 */ - psbpl = 3 * w; - data = pixGetData(pix); - hexbytes = 2 * psbpl * h; /* size of ps hex array */ - if ((hexdata = (char *)LEPT_CALLOC(hexbytes + 1, sizeof(char))) == NULL) - return (char *)ERROR_PTR("hexdata not made", procName, NULL); - if (d == 1 || d == 8) { - for (i = 0, k = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < psbpl; j++) { - byteval = GET_DATA_BYTE(line, j); - convertByteToHexAscii(byteval, &nib1, &nib2); - hexdata[k++] = nib1; - hexdata[k++] = nib2; - } - } - } else { /* d == 32; hexdata bytes packed RGBRGB..., 2 per sample */ - for (i = 0, k = 0; i < h; i++) { - line = data + i * wpl; - for (j = 0; j < w; j++) { - byteval = GET_DATA_BYTE(line + j, 0); /* red */ - convertByteToHexAscii(byteval, &nib1, &nib2); - hexdata[k++] = nib1; - hexdata[k++] = nib2; - byteval = GET_DATA_BYTE(line + j, 1); /* green */ - convertByteToHexAscii(byteval, &nib1, &nib2); - hexdata[k++] = nib1; - hexdata[k++] = nib2; - byteval = GET_DATA_BYTE(line + j, 2); /* blue */ - convertByteToHexAscii(byteval, &nib1, &nib2); - hexdata[k++] = nib1; - hexdata[k++] = nib2; - } - } - } - hexdata[k] = '\0'; - - outstr = generateUncompressedPS(hexdata, w, h, d, psbpl, bps, - xpt, ypt, wpt, hpt, boxflag); - pixDestroy(&pix); - if (!outstr) - return (char *)ERROR_PTR("outstr not made", procName, NULL); - return outstr; -} - - -/*! - * \brief generateUncompressedPS() - * - * \param[in] hexdata - * \param[in] w, h raster image size in pixels - * \param[in] d image depth in bpp; rgb is 32 - * \param[in] psbpl raster bytes/line, when packed to the byte boundary - * \param[in] bps bits/sample: either 1 or 8 - * \param[in] xpt, ypt location of LL corner of image, in pts, relative - * to the PostScript origin (0,0) at the LL corner - * of the page - * \param[in] wpt, hpt rendered image size in pts - * \param[in] boxflag 1 to print out bounding box hint; 0 to skip - * \return PS string, or NULL on error - * - *- * Notes: - * (1) Low-level function. - *- */ -char * -generateUncompressedPS(char *hexdata, - l_int32 w, - l_int32 h, - l_int32 d, - l_int32 psbpl, - l_int32 bps, - l_float32 xpt, - l_float32 ypt, - l_float32 wpt, - l_float32 hpt, - l_int32 boxflag) -{ -char *outstr; -char bigbuf[Bufsize]; -SARRAY *sa; - - PROCNAME("generateUncompressedPS"); - - if (!hexdata) - return (char *)ERROR_PTR("hexdata not defined", procName, NULL); - - sa = sarrayCreate(0); - sarrayAddString(sa, "%!Adobe-PS", L_COPY); - if (boxflag == 0) { - snprintf(bigbuf, sizeof(bigbuf), - "%%%%BoundingBox: %7.2f %7.2f %7.2f %7.2f", - xpt, ypt, xpt + wpt, ypt + hpt); - sarrayAddString(sa, bigbuf, L_COPY); - } else { /* boxflag == 1 */ - sarrayAddString(sa, "gsave", L_COPY); - } - - if (d == 1) - sarrayAddString(sa, - "{1 exch sub} settransfer %invert binary", L_COPY); - - snprintf(bigbuf, sizeof(bigbuf), - "/bpl %d string def %%bpl as a string", psbpl); - sarrayAddString(sa, bigbuf, L_COPY); - snprintf(bigbuf, sizeof(bigbuf), - "%7.2f %7.2f translate %%set image origin in pts", xpt, ypt); - sarrayAddString(sa, bigbuf, L_COPY); - snprintf(bigbuf, sizeof(bigbuf), - "%7.2f %7.2f scale %%set image size in pts", wpt, hpt); - sarrayAddString(sa, bigbuf, L_COPY); - snprintf(bigbuf, sizeof(bigbuf), - "%d %d %d %%image dimensions in pixels", w, h, bps); - sarrayAddString(sa, bigbuf, L_COPY); - snprintf(bigbuf, sizeof(bigbuf), - "[%d %d %d %d %d %d] %%mapping matrix: [w 0 0 -h 0 h]", - w, 0, 0, -h, 0, h); - sarrayAddString(sa, bigbuf, L_COPY); - - if (boxflag == 0) { - if (d == 1 || d == 8) - sarrayAddString(sa, - "{currentfile bpl readhexstring pop} image", L_COPY); - else /* d == 32 */ - sarrayAddString(sa, - "{currentfile bpl readhexstring pop} false 3 colorimage", - L_COPY); - } else { /* boxflag == 1 */ - if (d == 1 || d == 8) - sarrayAddString(sa, - "{currentfile bpl readhexstring pop} bind image", L_COPY); - else /* d == 32 */ - sarrayAddString(sa, - "{currentfile bpl readhexstring pop} bind false 3 colorimage", - L_COPY); - } - - sarrayAddString(sa, hexdata, L_INSERT); - - if (boxflag == 0) - sarrayAddString(sa, "\nshowpage", L_COPY); - else /* boxflag == 1 */ - sarrayAddString(sa, "\ngrestore", L_COPY); - - outstr = sarrayToString(sa, 1); - sarrayDestroy(&sa); - if (!outstr) L_ERROR("outstr not made\n", procName); - return outstr; -} - - -/*! - * \brief getScaledParametersPS() - * - * \param[in] box [optional] location of image in mils; x,y is LL corner - * \param[in] wpix pix width in pixels - * \param[in] hpix pix height in pixels - * \param[in] res of printer; use 0 for default - * \param[in] scale use 1.0 or 0.0 for no scaling - * \param[out] pxpt location of llx in pts - * \param[out] pypt location of lly in pts - * \param[out] pwpt image width in pts - * \param[out] phpt image height in pts - * \return void no arg checking - * - *- * Notes: - * (1) The image is always scaled, depending on res and scale. - * (2) If no box, the image is centered on the page. - * (3) If there is a box, the image is placed within it. - *- */ -static void -getScaledParametersPS(BOX *box, - l_int32 wpix, - l_int32 hpix, - l_int32 res, - l_float32 scale, - l_float32 *pxpt, - l_float32 *pypt, - l_float32 *pwpt, - l_float32 *phpt) -{ -l_int32 bx, by, bw, bh; -l_float32 winch, hinch, xinch, yinch, fres; - - PROCNAME("getScaledParametersPS"); - - if (res == 0) - res = DefaultInputRes; - fres = (l_float32)res; - - /* Allow the PS interpreter to scale the resolution */ - if (scale == 0.0) - scale = 1.0; - if (scale != 1.0) { - fres = (l_float32)res / scale; - res = (l_int32)fres; - } - - /* Limit valid resolution interval */ - if (res < MinRes || res > MaxRes) { - L_WARNING("res %d out of bounds; using default res; no scaling\n", - procName, res); - res = DefaultInputRes; - fres = (l_float32)res; - } - - if (!box) { /* center on page */ - winch = (l_float32)wpix / fres; - hinch = (l_float32)hpix / fres; - xinch = (8.5 - winch) / 2.; - yinch = (11.0 - hinch) / 2.; - } else { - boxGetGeometry(box, &bx, &by, &bw, &bh); - if (bw == 0) - winch = (l_float32)wpix / fres; - else - winch = (l_float32)bw / 1000.; - if (bh == 0) - hinch = (l_float32)hpix / fres; - else - hinch = (l_float32)bh / 1000.; - xinch = (l_float32)bx / 1000.; - yinch = (l_float32)by / 1000.; - } - - if (xinch < 0) - L_WARNING("left edge < 0.0 inch\n", procName); - if (xinch + winch > 8.5) - L_WARNING("right edge > 8.5 inch\n", procName); - if (yinch < 0.0) - L_WARNING("bottom edge < 0.0 inch\n", procName); - if (yinch + hinch > 11.0) - L_WARNING("top edge > 11.0 inch\n", procName); - - *pwpt = 72. * winch; - *phpt = 72. * hinch; - *pxpt = 72. * xinch; - *pypt = 72. * yinch; - return; -} - - -/*! - * \brief convertByteToHexAscii() - * - * \param[in] byteval input byte - * \param[out] pnib1, pnib2 two hex ascii characters - * \return void - */ -static void -convertByteToHexAscii(l_uint8 byteval, - char *pnib1, - char *pnib2) -{ -l_uint8 nib; - - nib = byteval >> 4; - if (nib < 10) - *pnib1 = '0' + nib; - else - *pnib1 = 'a' + (nib - 10); - nib = byteval & 0xf; - if (nib < 10) - *pnib2 = '0' + nib; - else - *pnib2 = 'a' + (nib - 10); - return; -} - - -/*-------------------------------------------------------------* - * For jpeg compressed images * - *-------------------------------------------------------------*/ -/*! - * \brief convertJpegToPSEmbed() - * - * \param[in] filein input jpeg file - * \param[in] fileout output ps file - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This function takes a jpeg file as input and generates a DCT - * compressed, ascii85 encoded PS file, with a bounding box. - * (2) The bounding box is required when a program such as TeX - * (through epsf) places and rescales the image. - * (3) The bounding box is sized for fitting the image to an - * 8.5 x 11.0 inch page. - *- */ -l_ok -convertJpegToPSEmbed(const char *filein, - const char *fileout) -{ -char *outstr; -l_int32 w, h, nbytes, ret; -l_float32 xpt, ypt, wpt, hpt; -L_COMP_DATA *cid; - - PROCNAME("convertJpegToPSEmbed"); - - if (!filein) - return ERROR_INT("filein not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - - /* Generate the ascii encoded jpeg data */ - if ((cid = l_generateJpegData(filein, 1)) == NULL) - return ERROR_INT("jpeg data not made", procName, 1); - w = cid->w; - h = cid->h; - - /* Scale for 20 pt boundary and otherwise full filling - * in one direction on 8.5 x 11 inch device */ - xpt = 20.0; - ypt = 20.0; - if (w * 11.0 > h * 8.5) { - wpt = 572.0; /* 612 - 2 * 20 */ - hpt = wpt * (l_float32)h / (l_float32)w; - } else { - hpt = 752.0; /* 792 - 2 * 20 */ - wpt = hpt * (l_float32)w / (l_float32)h; - } - - /* Generate the PS. - * The bounding box information should be inserted (default). */ - outstr = generateJpegPS(NULL, cid, xpt, ypt, wpt, hpt, 1, 1); - l_CIDataDestroy(&cid); - if (!outstr) - return ERROR_INT("outstr not made", procName, 1); - nbytes = strlen(outstr); - - ret = l_binaryWrite(fileout, "w", outstr, nbytes); - LEPT_FREE(outstr); - if (ret) L_ERROR("ps string not written to file\n", procName); - return ret; -} - - -/*! - * \brief convertJpegToPS() - * - * \param[in] filein input jpeg file - * \param[in] fileout output ps file - * \param[in] operation "w" for write; "a" for append - * \param[in] x, y location of LL corner of image, in pixels, relative - * to the PostScript origin (0,0) at the LL corner - * of the page - * \param[in] res resolution of the input image, in ppi; - * use 0 for default - * \param[in] scale scaling by printer; use 0.0 or 1.0 for no scaling - * \param[in] pageno page number; must start with 1; you can use 0 - * if there is only one page - * \param[in] endpage boolean: use TRUE if this is the last image to be - * added to the page; FALSE otherwise - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This is simpler to use than pixWriteStringPS(), and - * it outputs in level 2 PS as compressed DCT (overlaid - * with ascii85 encoding). - * (2) An output file can contain multiple pages, each with - * multiple images. The arguments to convertJpegToPS() - * allow you to control placement of jpeg images on multiple - * pages within a PostScript file. - * (3) For the first image written to a file, use "w", which - * opens for write and clears the file. For all subsequent - * images written to that file, use "a". - * (4) The (x, y) parameters give the LL corner of the image - * relative to the LL corner of the page. They are in - * units of pixels if scale = 1.0. If you use (e.g.) - * scale = 2.0, the image is placed at (2x, 2y) on the page, - * and the image dimensions are also doubled. - * (5) Display vs printed resolution: - * * If your display is 75 ppi and your image was created - * at a resolution of 300 ppi, you can get the image - * to print at the same size as it appears on your display - * by either setting scale = 4.0 or by setting res = 75. - * Both tell the printer to make a 4x enlarged image. - * * If your image is generated at 150 ppi and you use scale = 1, - * it will be rendered such that 150 pixels correspond - * to 72 pts (1 inch on the printer). This function does - * the conversion from pixels (with or without scaling) to - * pts, which are the units that the printer uses. - * * The printer will choose its own resolution to use - * in rendering the image, which will not affect the size - * of the rendered image. That is because the output - * PostScript file describes the geometry in terms of pts, - * which are defined to be 1/72 inch. The printer will - * only see the size of the image in pts, through the - * scale and translate parameters and the affine - * transform (the ImageMatrix) of the image. - * (6) To render multiple images on the same page, set - * endpage = FALSE for each image until you get to the - * last, for which you set endpage = TRUE. This causes the - * "showpage" command to be invoked. Showpage outputs - * the entire page and clears the raster buffer for the - * next page to be added. Without a "showpage", - * subsequent images from the next page will overlay those - * previously put down. - * (7) For multiple pages, increment the page number, starting - * with page 1. This allows PostScript (and PDF) to build - * a page directory, which viewers use for navigation. - *- */ -l_ok -convertJpegToPS(const char *filein, - const char *fileout, - const char *operation, - l_int32 x, - l_int32 y, - l_int32 res, - l_float32 scale, - l_int32 pageno, - l_int32 endpage) -{ -char *outstr; -l_int32 nbytes; - - PROCNAME("convertJpegToPS"); - - if (!filein) - return ERROR_INT("filein not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - if (strcmp(operation, "w") && strcmp(operation, "a")) - return ERROR_INT("operation must be \"w\" or \"a\"", procName, 1); - - if (convertJpegToPSString(filein, &outstr, &nbytes, x, y, res, scale, - pageno, endpage)) - return ERROR_INT("ps string not made", procName, 1); - - if (l_binaryWrite(fileout, operation, outstr, nbytes)) { - LEPT_FREE(outstr); - return ERROR_INT("ps string not written to file", procName, 1); - } - - LEPT_FREE(outstr); - return 0; -} - - -/*! - * \brief convertJpegToPSString() - * - * Generates PS string in jpeg format from jpeg file - * - * \param[in] filein input jpeg file - * \param[out] poutstr PS string - * \param[out] pnbytes number of bytes in PS string - * \param[in] x, y location of LL corner of image, in pixels, relative - * to the PostScript origin (0,0) at the LL corner - * of the page - * \param[in] res resolution of the input image, in ppi; - * use 0 for default - * \param[in] scale scaling by printer; use 0.0 or 1.0 for no scaling - * \param[in] pageno page number; must start with 1; you can use 0 - * if there is only one page - * \param[in] endpage boolean: use TRUE if this is the last image to be - * added to the page; FALSE otherwise - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) For usage, see convertJpegToPS() - *- */ -static l_ok -convertJpegToPSString(const char *filein, - char **poutstr, - l_int32 *pnbytes, - l_int32 x, - l_int32 y, - l_int32 res, - l_float32 scale, - l_int32 pageno, - l_int32 endpage) -{ -char *outstr; -l_float32 xpt, ypt, wpt, hpt; -L_COMP_DATA *cid; - - PROCNAME("convertJpegToPSString"); - - if (!poutstr) - return ERROR_INT("&outstr not defined", procName, 1); - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *poutstr = NULL; - *pnbytes = 0; - if (!filein) - return ERROR_INT("filein not defined", procName, 1); - - /* Generate the ascii encoded jpeg data */ - if ((cid = l_generateJpegData(filein, 1)) == NULL) - return ERROR_INT("jpeg data not made", procName, 1); - - /* Get scaled location in pts. Guess the input scan resolution - * based on the input parameter %res, the resolution data in - * the pix, and the size of the image. */ - if (scale == 0.0) - scale = 1.0; - if (res <= 0) { - if (cid->res > 0) - res = cid->res; - else - res = DefaultInputRes; - } - - /* Get scaled location in pts */ - if (scale == 0.0) - scale = 1.0; - xpt = scale * x * 72. / res; - ypt = scale * y * 72. / res; - wpt = scale * cid->w * 72. / res; - hpt = scale * cid->h * 72. / res; - - if (pageno == 0) - pageno = 1; - -#if DEBUG_JPEG - lept_stderr("w = %d, h = %d, bps = %d, spp = %d\n", - cid->w, cid->h, cid->bps, cid->spp); - lept_stderr("comp bytes = %ld, nbytes85 = %ld, ratio = %5.3f\n", - (unsigned long)cid->nbytescomp, (unsigned long)cid->nbytes85, - (l_float32)cid->nbytes85 / (l_float32)cid->nbytescomp); - lept_stderr("xpt = %7.2f, ypt = %7.2f, wpt = %7.2f, hpt = %7.2f\n", - xpt, ypt, wpt, hpt); -#endif /* DEBUG_JPEG */ - - /* Generate the PS */ - outstr = generateJpegPS(NULL, cid, xpt, ypt, wpt, hpt, pageno, endpage); - l_CIDataDestroy(&cid); - if (!outstr) - return ERROR_INT("outstr not made", procName, 1); - *poutstr = outstr; - *pnbytes = strlen(outstr); - return 0; -} - - -/*! - * \brief generateJpegPS() - * - * \param[in] filein [optional] input jpeg filename; can be null - * \param[in] cid jpeg compressed image data - * \param[in] xpt, ypt location of LL corner of image, in pts, relative - * to the PostScript origin (0,0) at the LL corner - * of the page - * \param[in] wpt, hpt rendered image size in pts - * \param[in] pageno page number; must start with 1; you can use 0 - * if there is only one page. - * \param[in] endpage boolean: use TRUE if this is the last image to be - * added to the page; FALSE otherwise - * \return PS string, or NULL on error - * - *- * Notes: - * (1) Low-level function. - *- */ -static char * -generateJpegPS(const char *filein, - L_COMP_DATA *cid, - l_float32 xpt, - l_float32 ypt, - l_float32 wpt, - l_float32 hpt, - l_int32 pageno, - l_int32 endpage) -{ -l_int32 w, h, bps, spp; -char *outstr; -char bigbuf[Bufsize]; -SARRAY *sa; - - PROCNAME("generateJpegPS"); - - if (!cid) - return (char *)ERROR_PTR("jpeg data not defined", procName, NULL); - w = cid->w; - h = cid->h; - bps = cid->bps; - spp = cid->spp; - - sa = sarrayCreate(50); - sarrayAddString(sa, "%!PS-Adobe-3.0", L_COPY); - sarrayAddString(sa, "%%Creator: leptonica", L_COPY); - if (filein) - snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: %s", filein); - else - snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: Jpeg compressed PS"); - sarrayAddString(sa, bigbuf, L_COPY); - sarrayAddString(sa, "%%DocumentData: Clean7Bit", L_COPY); - - if (var_PS_WRITE_BOUNDING_BOX == 1) { - snprintf(bigbuf, sizeof(bigbuf), - "%%%%BoundingBox: %7.2f %7.2f %7.2f %7.2f", - xpt, ypt, xpt + wpt, ypt + hpt); - sarrayAddString(sa, bigbuf, L_COPY); - } - - sarrayAddString(sa, "%%LanguageLevel: 2", L_COPY); - sarrayAddString(sa, "%%EndComments", L_COPY); - snprintf(bigbuf, sizeof(bigbuf), "%%%%Page: %d %d", pageno, pageno); - sarrayAddString(sa, bigbuf, L_COPY); - - sarrayAddString(sa, "save", L_COPY); - sarrayAddString(sa, - "/RawData currentfile /ASCII85Decode filter def", L_COPY); - sarrayAddString(sa, "/Data RawData << >> /DCTDecode filter def", L_COPY); - - snprintf(bigbuf, sizeof(bigbuf), - "%7.2f %7.2f translate %%set image origin in pts", xpt, ypt); - sarrayAddString(sa, bigbuf, L_COPY); - - snprintf(bigbuf, sizeof(bigbuf), - "%7.2f %7.2f scale %%set image size in pts", wpt, hpt); - sarrayAddString(sa, bigbuf, L_COPY); - - if (spp == 1) - sarrayAddString(sa, "/DeviceGray setcolorspace", L_COPY); - else if (spp == 3) - sarrayAddString(sa, "/DeviceRGB setcolorspace", L_COPY); - else /*spp == 4 */ - sarrayAddString(sa, "/DeviceCMYK setcolorspace", L_COPY); - - sarrayAddString(sa, "{ << /ImageType 1", L_COPY); - snprintf(bigbuf, sizeof(bigbuf), " /Width %d", w); - sarrayAddString(sa, bigbuf, L_COPY); - snprintf(bigbuf, sizeof(bigbuf), " /Height %d", h); - sarrayAddString(sa, bigbuf, L_COPY); - snprintf(bigbuf, sizeof(bigbuf), - " /ImageMatrix [ %d 0 0 %d 0 %d ]", w, -h, h); - sarrayAddString(sa, bigbuf, L_COPY); - sarrayAddString(sa, " /DataSource Data", L_COPY); - snprintf(bigbuf, sizeof(bigbuf), " /BitsPerComponent %d", bps); - sarrayAddString(sa, bigbuf, L_COPY); - - if (spp == 1) - sarrayAddString(sa, " /Decode [0 1]", L_COPY); - else if (spp == 3) - sarrayAddString(sa, " /Decode [0 1 0 1 0 1]", L_COPY); - else /* spp == 4 */ - sarrayAddString(sa, " /Decode [0 1 0 1 0 1 0 1]", L_COPY); - - sarrayAddString(sa, " >> image", L_COPY); - sarrayAddString(sa, " Data closefile", L_COPY); - sarrayAddString(sa, " RawData flushfile", L_COPY); - if (endpage == TRUE) - sarrayAddString(sa, " showpage", L_COPY); - sarrayAddString(sa, " restore", L_COPY); - sarrayAddString(sa, "} exec", L_COPY); - - /* Insert the ascii85 jpeg data; this is now owned by sa */ - sarrayAddString(sa, cid->data85, L_INSERT); - cid->data85 = NULL; /* it has been transferred and destroyed */ - - /* Generate and return the output string */ - outstr = sarrayToString(sa, 1); - sarrayDestroy(&sa); - return outstr; -} - - -/*-------------------------------------------------------------* - * For ccitt g4 compressed images * - *-------------------------------------------------------------*/ -/*! - * \brief convertG4ToPSEmbed() - * - * \param[in] filein input tiff file - * \param[in] fileout output ps file - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This function takes a g4 compressed tif file as input and - * generates a g4 compressed, ascii85 encoded PS file, with - * a bounding box. - * (2) The bounding box is required when a program such as TeX - * (through epsf) places and rescales the image. - * (3) The bounding box is sized for fitting the image to an - * 8.5 x 11.0 inch page. - * (4) We paint this through a mask, over whatever is below. - *- */ -l_ok -convertG4ToPSEmbed(const char *filein, - const char *fileout) -{ -char *outstr; -l_int32 w, h, nbytes, ret; -l_float32 xpt, ypt, wpt, hpt; -L_COMP_DATA *cid; - - PROCNAME("convertG4ToPSEmbed"); - - if (!filein) - return ERROR_INT("filein not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - - if ((cid = l_generateG4Data(filein, 1)) == NULL) - return ERROR_INT("g4 data not made", procName, 1); - w = cid->w; - h = cid->h; - - /* Scale for 20 pt boundary and otherwise full filling - * in one direction on 8.5 x 11 inch device */ - xpt = 20.0; - ypt = 20.0; - if (w * 11.0 > h * 8.5) { - wpt = 572.0; /* 612 - 2 * 20 */ - hpt = wpt * (l_float32)h / (l_float32)w; - } else { - hpt = 752.0; /* 792 - 2 * 20 */ - wpt = hpt * (l_float32)w / (l_float32)h; - } - - /* Generate the PS, painting through the image mask. - * The bounding box information should be inserted (default). */ - outstr = generateG4PS(NULL, cid, xpt, ypt, wpt, hpt, 1, 1, 1); - l_CIDataDestroy(&cid); - if (!outstr) - return ERROR_INT("outstr not made", procName, 1); - nbytes = strlen(outstr); - - ret = l_binaryWrite(fileout, "w", outstr, nbytes); - LEPT_FREE(outstr); - if (ret) L_ERROR("ps string not written to file\n", procName); - return ret; -} - - -/*! - * \brief convertG4ToPS() - * - * \param[in] filein input tiff g4 file - * \param[in] fileout output ps file - * \param[in] operation "w" for write; "a" for append - * \param[in] x, y location of LL corner of image, in pixels, relative - * to the PostScript origin (0,0) at the LL corner - * of the page - * \param[in] res resolution of the input image, in ppi; typ. values - * are 300 and 600; use 0 for automatic determination - * based on image size - * \param[in] scale scaling by printer; use 0.0 or 1.0 for no scaling - * \param[in] pageno page number; must start with 1; you can use 0 - * if there is only one page. - * \param[in] maskflag boolean: use TRUE if just painting through fg; - * FALSE if painting both fg and bg. - * \param[in] endpage boolean: use TRUE if this is the last image to be - * added to the page; FALSE otherwise - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) See the usage comments in convertJpegToPS(), some of - * which are repeated here. - * (2) This is a wrapper for tiff g4. The PostScript that - * is generated is expanded by about 5/4 (due to the - * ascii85 encoding. If you convert to pdf (ps2pdf), the - * ascii85 decoder is automatically invoked, so that the - * pdf wrapped g4 file is essentially the same size as - * the original g4 file. It's useful to have the PS - * file ascii85 encoded, because many printers will not - * print binary PS files. - * (3) For the first image written to a file, use "w", which - * opens for write and clears the file. For all subsequent - * images written to that file, use "a". - * (4) To render multiple images on the same page, set - * endpage = FALSE for each image until you get to the - * last, for which you set endpage = TRUE. This causes the - * "showpage" command to be invoked. Showpage outputs - * the entire page and clears the raster buffer for the - * next page to be added. Without a "showpage", - * subsequent images from the next page will overlay those - * previously put down. - * (5) For multiple images to the same page, where you are writing - * both jpeg and tiff-g4, you have two options: - * (a) write the g4 first, as either image (maskflag == FALSE) - * or imagemask (maskflag == TRUE), and then write the - * jpeg over it. - * (b) write the jpeg first and as the last item, write - * the g4 as an imagemask (maskflag == TRUE), to paint - * through the foreground only. - * We have this flexibility with the tiff-g4 because it is 1 bpp. - * (6) For multiple pages, increment the page number, starting - * with page 1. This allows PostScript (and PDF) to build - * a page directory, which viewers use for navigation. - *- */ -l_ok -convertG4ToPS(const char *filein, - const char *fileout, - const char *operation, - l_int32 x, - l_int32 y, - l_int32 res, - l_float32 scale, - l_int32 pageno, - l_int32 maskflag, - l_int32 endpage) -{ -char *outstr; -l_int32 nbytes, ret; - - PROCNAME("convertG4ToPS"); - - if (!filein) - return ERROR_INT("filein not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - if (strcmp(operation, "w") && strcmp(operation, "a")) - return ERROR_INT("operation must be \"w\" or \"a\"", procName, 1); - - if (convertG4ToPSString(filein, &outstr, &nbytes, x, y, res, scale, - pageno, maskflag, endpage)) - return ERROR_INT("ps string not made", procName, 1); - - ret = l_binaryWrite(fileout, operation, outstr, nbytes); - LEPT_FREE(outstr); - if (ret) - return ERROR_INT("ps string not written to file", procName, 1); - return 0; -} - - -/*! - * \brief convertG4ToPSString() - * - * \param[in] filein input tiff g4 file - * \param[out] poutstr PS string - * \param[out] pnbytes number of bytes in PS string - * \param[in] x, y location of LL corner of image, in pixels, relative - * to the PostScript origin (0,0) at the LL corner - * of the page - * \param[in] res resolution of the input image, in ppi; typ. values - * are 300 and 600; use 0 for automatic determination - * based on image size - * \param[in] scale scaling by printer; use 0.0 or 1.0 for no scaling - * \param[in] pageno page number; must start with 1; you can use 0 - * if there is only one page. - * \param[in] maskflag boolean: use TRUE if just painting through fg; - * FALSE if painting both fg and bg. - * \param[in] endpage boolean: use TRUE if this is the last image to be - * added to the page; FALSE otherwise - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Generates PS string in G4 compressed tiff format from G4 tiff file. - * (2) For usage, see convertG4ToPS(). - *- */ -static l_ok -convertG4ToPSString(const char *filein, - char **poutstr, - l_int32 *pnbytes, - l_int32 x, - l_int32 y, - l_int32 res, - l_float32 scale, - l_int32 pageno, - l_int32 maskflag, - l_int32 endpage) -{ -char *outstr; -l_float32 xpt, ypt, wpt, hpt; -L_COMP_DATA *cid; - - PROCNAME("convertG4ToPSString"); - - if (!poutstr) - return ERROR_INT("&outstr not defined", procName, 1); - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *poutstr = NULL; - *pnbytes = 0; - if (!filein) - return ERROR_INT("filein not defined", procName, 1); - - if ((cid = l_generateG4Data(filein, 1)) == NULL) - return ERROR_INT("g4 data not made", procName, 1); - - /* Get scaled location in pts. Guess the input scan resolution - * based on the input parameter %res, the resolution data in - * the pix, and the size of the image. */ - if (scale == 0.0) - scale = 1.0; - if (res <= 0) { - if (cid->res > 0) { - res = cid->res; - } else { - if (cid->h <= 3509) /* A4 height at 300 ppi */ - res = 300; - else - res = 600; - } - } - xpt = scale * x * 72. / res; - ypt = scale * y * 72. / res; - wpt = scale * cid->w * 72. / res; - hpt = scale * cid->h * 72. / res; - - if (pageno == 0) - pageno = 1; - -#if DEBUG_G4 - lept_stderr("w = %d, h = %d, minisblack = %d\n", - cid->w, cid->h, cid->minisblack); - lept_stderr("comp bytes = %ld, nbytes85 = %ld\n", - (unsigned long)cid->nbytescomp, (unsigned long)cid->nbytes85); - lept_stderr("xpt = %7.2f, ypt = %7.2f, wpt = %7.2f, hpt = %7.2f\n", - xpt, ypt, wpt, hpt); -#endif /* DEBUG_G4 */ - - /* Generate the PS */ - outstr = generateG4PS(NULL, cid, xpt, ypt, wpt, hpt, - maskflag, pageno, endpage); - l_CIDataDestroy(&cid); - if (!outstr) - return ERROR_INT("outstr not made", procName, 1); - *poutstr = outstr; - *pnbytes = strlen(outstr); - return 0; -} - - -/*! - * \brief generateG4PS() - * - * \param[in] filein [optional] input tiff g4 file; can be null - * \param[in] cid g4 compressed image data - * \param[in] xpt, ypt location of LL corner of image, in pts, relative - * to the PostScript origin (0,0) at the LL corner - * of the page - * \param[in] wpt, hpt rendered image size in pts - * \param[in] maskflag boolean: use TRUE if just painting through fg; - * FALSE if painting both fg and bg. - * \param[in] pageno page number; must start with 1; you can use 0 - * if there is only one page. - * \param[in] endpage boolean: use TRUE if this is the last image to be - * added to the page; FALSE otherwise - * \return PS string, or NULL on error - * - *- * Notes: - * (1) Low-level function. - *- */ -static char * -generateG4PS(const char *filein, - L_COMP_DATA *cid, - l_float32 xpt, - l_float32 ypt, - l_float32 wpt, - l_float32 hpt, - l_int32 maskflag, - l_int32 pageno, - l_int32 endpage) -{ -l_int32 w, h; -char *outstr; -char bigbuf[Bufsize]; -SARRAY *sa; - - PROCNAME("generateG4PS"); - - if (!cid) - return (char *)ERROR_PTR("g4 data not defined", procName, NULL); - w = cid->w; - h = cid->h; - - sa = sarrayCreate(50); - sarrayAddString(sa, "%!PS-Adobe-3.0", L_COPY); - sarrayAddString(sa, "%%Creator: leptonica", L_COPY); - if (filein) - snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: %s", filein); - else - snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: G4 compressed PS"); - sarrayAddString(sa, bigbuf, L_COPY); - sarrayAddString(sa, "%%DocumentData: Clean7Bit", L_COPY); - - if (var_PS_WRITE_BOUNDING_BOX == 1) { - snprintf(bigbuf, sizeof(bigbuf), - "%%%%BoundingBox: %7.2f %7.2f %7.2f %7.2f", - xpt, ypt, xpt + wpt, ypt + hpt); - sarrayAddString(sa, bigbuf, L_COPY); - } - - sarrayAddString(sa, "%%LanguageLevel: 2", L_COPY); - sarrayAddString(sa, "%%EndComments", L_COPY); - snprintf(bigbuf, sizeof(bigbuf), "%%%%Page: %d %d", pageno, pageno); - sarrayAddString(sa, bigbuf, L_COPY); - - sarrayAddString(sa, "save", L_COPY); - sarrayAddString(sa, "100 dict begin", L_COPY); - - snprintf(bigbuf, sizeof(bigbuf), - "%7.2f %7.2f translate %%set image origin in pts", xpt, ypt); - sarrayAddString(sa, bigbuf, L_COPY); - - snprintf(bigbuf, sizeof(bigbuf), - "%7.2f %7.2f scale %%set image size in pts", wpt, hpt); - sarrayAddString(sa, bigbuf, L_COPY); - - sarrayAddString(sa, "/DeviceGray setcolorspace", L_COPY); - - sarrayAddString(sa, "{", L_COPY); - sarrayAddString(sa, - " /RawData currentfile /ASCII85Decode filter def", L_COPY); - sarrayAddString(sa, " << ", L_COPY); - sarrayAddString(sa, " /ImageType 1", L_COPY); - snprintf(bigbuf, sizeof(bigbuf), " /Width %d", w); - sarrayAddString(sa, bigbuf, L_COPY); - snprintf(bigbuf, sizeof(bigbuf), " /Height %d", h); - sarrayAddString(sa, bigbuf, L_COPY); - snprintf(bigbuf, sizeof(bigbuf), - " /ImageMatrix [ %d 0 0 %d 0 %d ]", w, -h, h); - sarrayAddString(sa, bigbuf, L_COPY); - sarrayAddString(sa, " /BitsPerComponent 1", L_COPY); - sarrayAddString(sa, " /Interpolate true", L_COPY); - if (cid->minisblack) - sarrayAddString(sa, " /Decode [1 0]", L_COPY); - else /* miniswhite; typical for 1 bpp */ - sarrayAddString(sa, " /Decode [0 1]", L_COPY); - sarrayAddString(sa, " /DataSource RawData", L_COPY); - sarrayAddString(sa, " <<", L_COPY); - sarrayAddString(sa, " /K -1", L_COPY); - snprintf(bigbuf, sizeof(bigbuf), " /Columns %d", w); - sarrayAddString(sa, bigbuf, L_COPY); - snprintf(bigbuf, sizeof(bigbuf), " /Rows %d", h); - sarrayAddString(sa, bigbuf, L_COPY); - sarrayAddString(sa, " >> /CCITTFaxDecode filter", L_COPY); - if (maskflag == TRUE) /* just paint through the fg */ - sarrayAddString(sa, " >> imagemask", L_COPY); - else /* Paint full image */ - sarrayAddString(sa, " >> image", L_COPY); - sarrayAddString(sa, " RawData flushfile", L_COPY); - if (endpage == TRUE) - sarrayAddString(sa, " showpage", L_COPY); - sarrayAddString(sa, "}", L_COPY); - - sarrayAddString(sa, "%%BeginData:", L_COPY); - sarrayAddString(sa, "exec", L_COPY); - - /* Insert the ascii85 ccittg4 data; this is now owned by sa */ - sarrayAddString(sa, cid->data85, L_INSERT); - - /* Concat the trailing data */ - sarrayAddString(sa, "%%EndData", L_COPY); - sarrayAddString(sa, "end", L_COPY); - sarrayAddString(sa, "restore", L_COPY); - - outstr = sarrayToString(sa, 1); - sarrayDestroy(&sa); - cid->data85 = NULL; /* it has been transferred and destroyed */ - return outstr; -} - - -/*-------------------------------------------------------------* - * For tiff multipage files * - *-------------------------------------------------------------*/ -/*! - * \brief convertTiffMultipageToPS() - * - * \param[in] filein input tiff multipage file - * \param[in] fileout output ps file - * \param[in] fillfract factor for filling 8.5 x 11 inch page; - * use 0.0 for DefaultFillFraction - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This converts a multipage tiff file of binary page images - * into a ccitt g4 compressed PS file. - * (2) If the images are generated from a standard resolution fax, - * the vertical resolution is doubled to give a normal-looking - * aspect ratio. - *- */ -l_ok -convertTiffMultipageToPS(const char *filein, - const char *fileout, - l_float32 fillfract) -{ -char *tempfile; -l_int32 i, npages, w, h, istiff; -l_float32 scale; -PIX *pix, *pixs; -FILE *fp; - - PROCNAME("convertTiffMultipageToPS"); - - if (!filein) - return ERROR_INT("filein not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - - if ((fp = fopenReadStream(filein)) == NULL) - return ERROR_INT("file not found", procName, 1); - istiff = fileFormatIsTiff(fp); - if (!istiff) { - fclose(fp); - return ERROR_INT("file not tiff format", procName, 1); - } - tiffGetCount(fp, &npages); - fclose(fp); - - if (fillfract == 0.0) - fillfract = DefaultFillFraction; - - for (i = 0; i < npages; i++) { - if ((pix = pixReadTiff(filein, i)) == NULL) - return ERROR_INT("pix not made", procName, 1); - - pixGetDimensions(pix, &w, &h, NULL); - if (w == 1728 && h < w) /* it's a std res fax */ - pixs = pixScale(pix, 1.0, 2.0); - else - pixs = pixClone(pix); - - tempfile = l_makeTempFilename(); - pixWrite(tempfile, pixs, IFF_TIFF_G4); - scale = L_MIN(fillfract * 2550 / w, fillfract * 3300 / h); - if (i == 0) - convertG4ToPS(tempfile, fileout, "w", 0, 0, 300, scale, - i + 1, FALSE, TRUE); - else - convertG4ToPS(tempfile, fileout, "a", 0, 0, 300, scale, - i + 1, FALSE, TRUE); - lept_rmfile(tempfile); - LEPT_FREE(tempfile); - pixDestroy(&pix); - pixDestroy(&pixs); - } - - return 0; -} - - -/*---------------------------------------------------------------------* - * For flate (gzip) compressed images (e.g., png) * - *---------------------------------------------------------------------*/ -/*! - * \brief convertFlateToPSEmbed() - * - * \param[in] filein input file -- any format - * \param[in] fileout output ps file - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This function takes any image file as input and generates a - * flate-compressed, ascii85 encoded PS file, with a bounding box. - * (2) The bounding box is required when a program such as TeX - * (through epsf) places and rescales the image. - * (3) The bounding box is sized for fitting the image to an - * 8.5 x 11.0 inch page. - *- */ -l_ok -convertFlateToPSEmbed(const char *filein, - const char *fileout) -{ -char *outstr; -l_int32 w, h, nbytes, ret; -l_float32 xpt, ypt, wpt, hpt; -L_COMP_DATA *cid; - - PROCNAME("convertFlateToPSEmbed"); - - if (!filein) - return ERROR_INT("filein not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - - if ((cid = l_generateFlateData(filein, 1)) == NULL) - return ERROR_INT("flate data not made", procName, 1); - w = cid->w; - h = cid->h; - - /* Scale for 20 pt boundary and otherwise full filling - * in one direction on 8.5 x 11 inch device */ - xpt = 20.0; - ypt = 20.0; - if (w * 11.0 > h * 8.5) { - wpt = 572.0; /* 612 - 2 * 20 */ - hpt = wpt * (l_float32)h / (l_float32)w; - } else { - hpt = 752.0; /* 792 - 2 * 20 */ - wpt = hpt * (l_float32)w / (l_float32)h; - } - - /* Generate the PS. - * The bounding box information should be inserted (default). */ - outstr = generateFlatePS(NULL, cid, xpt, ypt, wpt, hpt, 1, 1); - l_CIDataDestroy(&cid); - if (!outstr) - return ERROR_INT("outstr not made", procName, 1); - nbytes = strlen(outstr); - - ret = l_binaryWrite(fileout, "w", outstr, nbytes); - LEPT_FREE(outstr); - if (ret) L_ERROR("ps string not written to file\n", procName); - return ret; -} - - -/*! - * \brief convertFlateToPS() - * - * \param[in] filein input file -- any format - * \param[in] fileout output ps file - * \param[in] operation "w" for write; "a" for append - * \param[in] x, y location of LL corner of image, in pixels, relative - * to the PostScript origin (0,0) at the LL corner - * of the page - * \param[in] res resolution of the input image, in ppi; - * use 0 for default - * \param[in] scale scaling by printer; use 0.0 or 1.0 for no scaling - * \param[in] pageno page number; must start with 1; you can use 0 - * if there is only one page. - * \param[in] endpage boolean: use TRUE if this is the last image to be - * added to the page; FALSE otherwise - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This outputs level 3 PS as flate compressed (overlaid - * with ascii85 encoding). - * (2) An output file can contain multiple pages, each with - * multiple images. The arguments to convertFlateToPS() - * allow you to control placement of png images on multiple - * pages within a PostScript file. - * (3) For the first image written to a file, use "w", which - * opens for write and clears the file. For all subsequent - * images written to that file, use "a". - * (4) The (x, y) parameters give the LL corner of the image - * relative to the LL corner of the page. They are in - * units of pixels if scale = 1.0. If you use (e.g.) - * scale = 2.0, the image is placed at (2x, 2y) on the page, - * and the image dimensions are also doubled. - * (5) Display vs printed resolution: - * * If your display is 75 ppi and your image was created - * at a resolution of 300 ppi, you can get the image - * to print at the same size as it appears on your display - * by either setting scale = 4.0 or by setting res = 75. - * Both tell the printer to make a 4x enlarged image. - * * If your image is generated at 150 ppi and you use scale = 1, - * it will be rendered such that 150 pixels correspond - * to 72 pts (1 inch on the printer). This function does - * the conversion from pixels (with or without scaling) to - * pts, which are the units that the printer uses. - * * The printer will choose its own resolution to use - * in rendering the image, which will not affect the size - * of the rendered image. That is because the output - * PostScript file describes the geometry in terms of pts, - * which are defined to be 1/72 inch. The printer will - * only see the size of the image in pts, through the - * scale and translate parameters and the affine - * transform (the ImageMatrix) of the image. - * (6) To render multiple images on the same page, set - * endpage = FALSE for each image until you get to the - * last, for which you set endpage = TRUE. This causes the - * "showpage" command to be invoked. Showpage outputs - * the entire page and clears the raster buffer for the - * next page to be added. Without a "showpage", - * subsequent images from the next page will overlay those - * previously put down. - * (7) For multiple pages, increment the page number, starting - * with page 1. This allows PostScript (and PDF) to build - * a page directory, which viewers use for navigation. - *- */ -l_ok -convertFlateToPS(const char *filein, - const char *fileout, - const char *operation, - l_int32 x, - l_int32 y, - l_int32 res, - l_float32 scale, - l_int32 pageno, - l_int32 endpage) -{ -char *outstr; -l_int32 nbytes, ret; - - PROCNAME("convertFlateToPS"); - - if (!filein) - return ERROR_INT("filein not defined", procName, 1); - if (!fileout) - return ERROR_INT("fileout not defined", procName, 1); - if (strcmp(operation, "w") && strcmp(operation, "a")) - return ERROR_INT("operation must be \"w\" or \"a\"", procName, 1); - - if (convertFlateToPSString(filein, &outstr, &nbytes, x, y, res, scale, - pageno, endpage)) - return ERROR_INT("ps string not made", procName, 1); - - ret = l_binaryWrite(fileout, operation, outstr, nbytes); - LEPT_FREE(outstr); - if (ret) L_ERROR("ps string not written to file\n", procName); - return ret; -} - - -/*! - * \brief convertFlateToPSString() - * - * Generates level 3 PS string in flate compressed format. - * - * \param[in] filein input image file - * \param[out] poutstr PS string - * \param[out] pnbytes number of bytes in PS string - * \param[in] x, y location of LL corner of image, in pixels, relative - * to the PostScript origin (0,0) at the LL corner - * of the page - * \param[in] res resolution of the input image, in ppi; - * use 0 for default - * \param[in] scale scaling by printer; use 0.0 or 1.0 for no scaling - * \param[in] pageno page number; must start with 1; you can use 0 - * if there is only one page. - * \param[in] endpage boolean: use TRUE if this is the last image to be - * added to the page; FALSE otherwise - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The returned PS character array is a null-terminated - * ascii string. All the raster data is ascii85 encoded, so - * there are no null bytes embedded in it. - * (2) The raster encoding is made with gzip, the same as that - * in a png file that is compressed without prediction. - * The raster data itself is 25% larger than that in the - * binary form, due to the ascii85 encoding. - * - * Usage: See convertFlateToPS() - *- */ -static l_ok -convertFlateToPSString(const char *filein, - char **poutstr, - l_int32 *pnbytes, - l_int32 x, - l_int32 y, - l_int32 res, - l_float32 scale, - l_int32 pageno, - l_int32 endpage) -{ -char *outstr; -l_float32 xpt, ypt, wpt, hpt; -L_COMP_DATA *cid; - - PROCNAME("convertFlateToPSString"); - - if (!poutstr) - return ERROR_INT("&outstr not defined", procName, 1); - if (!pnbytes) - return ERROR_INT("&nbytes not defined", procName, 1); - *pnbytes = 0; - *poutstr = NULL; - if (!filein) - return ERROR_INT("filein not defined", procName, 1); - - if ((cid = l_generateFlateData(filein, 1)) == NULL) - return ERROR_INT("flate data not made", procName, 1); - - /* Get scaled location in pts. Guess the input scan resolution - * based on the input parameter %res, the resolution data in - * the pix, and the size of the image. */ - if (scale == 0.0) - scale = 1.0; - if (res <= 0) { - if (cid->res > 0) - res = cid->res; - else - res = DefaultInputRes; - } - xpt = scale * x * 72. / res; - ypt = scale * y * 72. / res; - wpt = scale * cid->w * 72. / res; - hpt = scale * cid->h * 72. / res; - - if (pageno == 0) - pageno = 1; - -#if DEBUG_FLATE - lept_stderr("w = %d, h = %d, bps = %d, spp = %d\n", - cid->w, cid->h, cid->bps, cid->spp); - lept_stderr("uncomp bytes = %ld, comp bytes = %ld, nbytes85 = %ld\n", - (unsigned long)cid->nbytes, (unsigned long)cid->nbytescomp, - (unsigned long)cid->nbytes85); - lept_stderr("xpt = %7.2f, ypt = %7.2f, wpt = %7.2f, hpt = %7.2f\n", - xpt, ypt, wpt, hpt); -#endif /* DEBUG_FLATE */ - - /* Generate the PS */ - outstr = generateFlatePS(NULL, cid, xpt, ypt, wpt, hpt, pageno, endpage); - l_CIDataDestroy(&cid); - if (!outstr) - return ERROR_INT("outstr not made", procName, 1); - *poutstr = outstr; - *pnbytes = strlen(outstr); - return 0; -} - - -/*! - * \brief generateFlatePS() - * - * \param[in] filein [optional] input filename; can be null - * \param[in] cid flate compressed image data - * \param[in] xpt, ypt location of LL corner of image, in pts, relative - * to the PostScript origin (0,0) at the LL corner - * of the page - * \param[in] wpt, hpt rendered image size in pts - * \param[in] pageno page number; must start with 1; you can use 0 - * if there is only one page - * \param[in] endpage boolean: use TRUE if this is the last image to be - * added to the page; FALSE otherwise - * \return PS string, or NULL on error - */ -static char * -generateFlatePS(const char *filein, - L_COMP_DATA *cid, - l_float32 xpt, - l_float32 ypt, - l_float32 wpt, - l_float32 hpt, - l_int32 pageno, - l_int32 endpage) -{ -l_int32 w, h, bps, spp; -char *outstr; -char bigbuf[Bufsize]; -SARRAY *sa; - - PROCNAME("generateFlatePS"); - - if (!cid) - return (char *)ERROR_PTR("flate data not defined", procName, NULL); - w = cid->w; - h = cid->h; - bps = cid->bps; - spp = cid->spp; - - sa = sarrayCreate(50); - sarrayAddString(sa, "%!PS-Adobe-3.0 EPSF-3.0", L_COPY); - sarrayAddString(sa, "%%Creator: leptonica", L_COPY); - if (filein) - snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: %s", filein); - else - snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: Flate compressed PS"); - sarrayAddString(sa, bigbuf, L_COPY); - sarrayAddString(sa, "%%DocumentData: Clean7Bit", L_COPY); - - if (var_PS_WRITE_BOUNDING_BOX == 1) { - snprintf(bigbuf, sizeof(bigbuf), - "%%%%BoundingBox: %7.2f %7.2f %7.2f %7.2f", - xpt, ypt, xpt + wpt, ypt + hpt); - sarrayAddString(sa, bigbuf, L_COPY); - } - - sarrayAddString(sa, "%%LanguageLevel: 3", L_COPY); - sarrayAddString(sa, "%%EndComments", L_COPY); - snprintf(bigbuf, sizeof(bigbuf), "%%%%Page: %d %d", pageno, pageno); - sarrayAddString(sa, bigbuf, L_COPY); - - sarrayAddString(sa, "save", L_COPY); - snprintf(bigbuf, sizeof(bigbuf), - "%7.2f %7.2f translate %%set image origin in pts", xpt, ypt); - sarrayAddString(sa, bigbuf, L_COPY); - - snprintf(bigbuf, sizeof(bigbuf), - "%7.2f %7.2f scale %%set image size in pts", wpt, hpt); - sarrayAddString(sa, bigbuf, L_COPY); - - /* If there is a colormap, add the data; it is now owned by sa */ - if (cid->cmapdata85) { - snprintf(bigbuf, sizeof(bigbuf), - "[ /Indexed /DeviceRGB %d %%set colormap type/size", - cid->ncolors - 1); - sarrayAddString(sa, bigbuf, L_COPY); - sarrayAddString(sa, " <~", L_COPY); - sarrayAddString(sa, cid->cmapdata85, L_INSERT); - sarrayAddString(sa, " ] setcolorspace", L_COPY); - } else if (spp == 1) { - sarrayAddString(sa, "/DeviceGray setcolorspace", L_COPY); - } else { /* spp == 3 */ - sarrayAddString(sa, "/DeviceRGB setcolorspace", L_COPY); - } - - sarrayAddString(sa, - "/RawData currentfile /ASCII85Decode filter def", L_COPY); - sarrayAddString(sa, - "/Data RawData << >> /FlateDecode filter def", L_COPY); - - sarrayAddString(sa, "{ << /ImageType 1", L_COPY); - snprintf(bigbuf, sizeof(bigbuf), " /Width %d", w); - sarrayAddString(sa, bigbuf, L_COPY); - snprintf(bigbuf, sizeof(bigbuf), " /Height %d", h); - sarrayAddString(sa, bigbuf, L_COPY); - snprintf(bigbuf, sizeof(bigbuf), " /BitsPerComponent %d", bps); - sarrayAddString(sa, bigbuf, L_COPY); - snprintf(bigbuf, sizeof(bigbuf), - " /ImageMatrix [ %d 0 0 %d 0 %d ]", w, -h, h); - sarrayAddString(sa, bigbuf, L_COPY); - - if (cid->cmapdata85) { - sarrayAddString(sa, " /Decode [0 255]", L_COPY); - } else if (spp == 1) { - if (bps == 1) /* miniswhite photometry */ - sarrayAddString(sa, " /Decode [1 0]", L_COPY); - else /* bps > 1 */ - sarrayAddString(sa, " /Decode [0 1]", L_COPY); - } else { /* spp == 3 */ - sarrayAddString(sa, " /Decode [0 1 0 1 0 1]", L_COPY); - } - - sarrayAddString(sa, " /DataSource Data", L_COPY); - sarrayAddString(sa, " >> image", L_COPY); - sarrayAddString(sa, " Data closefile", L_COPY); - sarrayAddString(sa, " RawData flushfile", L_COPY); - if (endpage == TRUE) - sarrayAddString(sa, " showpage", L_COPY); - sarrayAddString(sa, " restore", L_COPY); - sarrayAddString(sa, "} exec", L_COPY); - - /* Insert the ascii85 gzipped data; this is now owned by sa */ - sarrayAddString(sa, cid->data85, L_INSERT); - - /* Generate and return the output string */ - outstr = sarrayToString(sa, 1); - sarrayDestroy(&sa); - cid->cmapdata85 = NULL; /* it has been transferred to sa and destroyed */ - cid->data85 = NULL; /* it has been transferred to sa and destroyed */ - return outstr; -} - - -/*---------------------------------------------------------------------* - * Write to memory * - *---------------------------------------------------------------------*/ -/*! - * \brief pixWriteMemPS() - * - * \param[out] pdata data of tiff compressed image - * \param[out] psize size of returned data - * \param[in] pix - * \param[in] box [optional] - * \param[in] res can use 0 for default of 300 ppi - * \param[in] scale to prevent scaling, use either 1.0 or 0.0 - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) See pixWriteStringPS() for usage. - * (2) This is just a wrapper for pixWriteStringPS(), which - * writes uncompressed image data to memory. - *- */ -l_ok -pixWriteMemPS(l_uint8 **pdata, - size_t *psize, - PIX *pix, - BOX *box, - l_int32 res, - l_float32 scale) -{ - PROCNAME("pixWriteMemPS"); - - if (!pdata) - return ERROR_INT("&data not defined", procName, 1 ); - if (!psize) - return ERROR_INT("&size not defined", procName, 1 ); - if (!pix) - return ERROR_INT("&pix not defined", procName, 1 ); - - *pdata = (l_uint8 *)pixWriteStringPS(pix, box, res, scale); - *psize = strlen((char *)(*pdata)); - return 0; -} - - -/*-------------------------------------------------------------* - * Converting resolution * - *-------------------------------------------------------------*/ -/*! - * \brief getResLetterPage() - * - * \param[in] w image width, pixels - * \param[in] h image height, pixels - * \param[in] fillfract fraction in linear dimension of full page, - * not to be exceeded; use 0 for default - * \return resolution - */ -l_int32 -getResLetterPage(l_int32 w, - l_int32 h, - l_float32 fillfract) -{ -l_int32 resw, resh, res; - - if (fillfract == 0.0) - fillfract = DefaultFillFraction; - resw = (l_int32)((w * 72.) / (LetterWidth * fillfract)); - resh = (l_int32)((h * 72.) / (LetterHeight * fillfract)); - res = L_MAX(resw, resh); - return res; -} - - -/*! - * \brief getResA4Page() - * - * \param[in] w image width, pixels - * \param[in] h image height, pixels - * \param[in] fillfract fraction in linear dimension of full page, - * not to be exceeded; use 0 for default - * \return resolution - */ -l_int32 -getResA4Page(l_int32 w, - l_int32 h, - l_float32 fillfract) -{ -l_int32 resw, resh, res; - - if (fillfract == 0.0) - fillfract = DefaultFillFraction; - resw = (l_int32)((w * 72.) / (A4Width * fillfract)); - resh = (l_int32)((h * 72.) / (A4Height * fillfract)); - res = L_MAX(resw, resh); - return res; -} - - -/*-------------------------------------------------------------* - * Setting flag for writing bounding box hint * - *-------------------------------------------------------------*/ -void -l_psWriteBoundingBox(l_int32 flag) -{ - var_PS_WRITE_BOUNDING_BOX = flag; -} - - -/* --------------------------------------------*/ -#endif /* USE_PSIO */ -/* --------------------------------------------*/ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/psio2stub.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/psio2stub.c deleted file mode 100644 index 8c96777e..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/psio2stub.c +++ /dev/null @@ -1,160 +0,0 @@ -/*====================================================================* - - 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 psio2stub.c - *- * - * Stubs for psio2.c functions - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include "allheaders.h" - -/* --------------------------------------------*/ -#if !USE_PSIO /* defined in environ.h */ -/* --------------------------------------------*/ - -l_ok pixWritePSEmbed(const char *filein, const char *fileout) -{ - return ERROR_INT("function not present", "pixWritePSEmbed", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixWriteStreamPS(FILE *fp, PIX *pix, BOX *box, l_int32 res, - l_float32 scale) -{ - return ERROR_INT("function not present", "pixWriteStreamPS", 1); -} - -/* ----------------------------------------------------------------------*/ - -char * pixWriteStringPS(PIX *pixs, BOX *box, l_int32 res, l_float32 scale) -{ - return (char *)ERROR_PTR("function not present", "pixWriteStringPS", NULL); -} - -/* ----------------------------------------------------------------------*/ - -char * generateUncompressedPS(char *hexdata, l_int32 w, l_int32 h, l_int32 d, - l_int32 psbpl, l_int32 bps, l_float32 xpt, - l_float32 ypt, l_float32 wpt, l_float32 hpt, - l_int32 boxflag) -{ - return (char *)ERROR_PTR("function not present", - "generateUncompressedPS", NULL); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertJpegToPSEmbed(const char *filein, const char *fileout) -{ - return ERROR_INT("function not present", "convertJpegToPSEmbed", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertJpegToPS(const char *filein, const char *fileout, - const char *operation, l_int32 x, l_int32 y, - l_int32 res, l_float32 scale, l_int32 pageno, - l_int32 endpage) -{ - return ERROR_INT("function not present", "convertJpegToPS", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertG4ToPSEmbed(const char *filein, const char *fileout) -{ - return ERROR_INT("function not present", "convertG4ToPSEmbed", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertG4ToPS(const char *filein, const char *fileout, - const char *operation, l_int32 x, l_int32 y, - l_int32 res, l_float32 scale, l_int32 pageno, - l_int32 maskflag, l_int32 endpage) -{ - return ERROR_INT("function not present", "convertG4ToPS", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertTiffMultipageToPS(const char *filein, const char *fileout, - l_float32 fillfract) -{ - return ERROR_INT("function not present", "convertTiffMultipageToPS", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertFlateToPSEmbed(const char *filein, const char *fileout) -{ - return ERROR_INT("function not present", "convertFlateToPSEmbed", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok convertFlateToPS(const char *filein, const char *fileout, - const char *operation, l_int32 x, l_int32 y, - l_int32 res, l_float32 scale, l_int32 pageno, - l_int32 endpage) -{ - return ERROR_INT("function not present", "convertFlateToPS", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_ok pixWriteMemPS(l_uint8 **pdata, size_t *psize, PIX *pix, BOX *box, - l_int32 res, l_float32 scale) -{ - return ERROR_INT("function not present", "pixWriteMemPS", 1); -} - -/* ----------------------------------------------------------------------*/ - -l_int32 getResLetterPage(l_int32 w, l_int32 h, l_float32 fillfract) -{ - return ERROR_INT("function not present", "getResLetterPage", 1); -} - -/* ----------------------------------------------------------------------*/ - -void l_psWriteBoundingBox(l_int32 flag) -{ - L_ERROR("function not present\n", "l_psWriteBoundingBox"); - return; -} - -/* --------------------------------------------*/ -#endif /* !USE_PSIO */ -/* --------------------------------------------*/ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ptabasic.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ptabasic.c deleted file mode 100644 index 62bad820..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ptabasic.c +++ /dev/null @@ -1,1553 +0,0 @@ -/*====================================================================* - - 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 ptabasic.c - * - * - * Pta creation, destruction, copy, clone, empty - * PTA *ptaCreate() - * PTA *ptaCreateFromNuma() - * void ptaDestroy() - * PTA *ptaCopy() - * PTA *ptaCopyRange() - * PTA *ptaClone() - * l_int32 ptaEmpty() - * - * Pta array extension - * l_int32 ptaAddPt() - * static l_int32 ptaExtendArrays() - * - * Pta insertion and removal - * l_int32 ptaInsertPt() - * l_int32 ptaRemovePt() - * - * Pta accessors - * l_int32 ptaGetRefcount() - * l_int32 ptaChangeRefcount() - * l_int32 ptaGetCount() - * l_int32 ptaGetPt() - * l_int32 ptaGetIPt() - * l_int32 ptaSetPt() - * l_int32 ptaGetArrays() - * - * Pta serialized for I/O - * PTA *ptaRead() - * PTA *ptaReadStream() - * PTA *ptaReadMem() - * l_int32 ptaWriteDebug() - * l_int32 ptaWrite() - * l_int32 ptaWriteStream() - * l_int32 ptaWriteMem() - * - * Ptaa creation, destruction - * PTAA *ptaaCreate() - * void ptaaDestroy() - * - * Ptaa array extension - * l_int32 ptaaAddPta() - * static l_int32 ptaaExtendArray() - * - * Ptaa accessors - * l_int32 ptaaGetCount() - * l_int32 ptaaGetPta() - * l_int32 ptaaGetPt() - * - * Ptaa array modifiers - * l_int32 ptaaInitFull() - * l_int32 ptaaReplacePta() - * l_int32 ptaaAddPt() - * l_int32 ptaaTruncate() - * - * Ptaa serialized for I/O - * PTAA *ptaaRead() - * PTAA *ptaaReadStream() - * PTAA *ptaaReadMem() - * l_int32 ptaaWrite() - * l_int32 ptaaWriteStream() - * l_int32 ptaaWriteMem() - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include "allheaders.h" - -static const l_uint32 MaxPtrArraySize = 10000000; -static const l_int32 InitialPtrArraySize = 50; /*!< n'importe quoi */ - - /* Static functions */ -static l_int32 ptaExtendArrays(PTA *pta); -static l_int32 ptaaExtendArray(PTAA *ptaa); - -/*---------------------------------------------------------------------* - * Pta creation, destruction, copy, clone * - *---------------------------------------------------------------------*/ -/*! - * \brief ptaCreate() - * - * \param[in] n initial array sizes - * \return pta, or NULL on error. - */ -PTA * -ptaCreate(l_int32 n) -{ -PTA *pta; - - PROCNAME("ptaCreate"); - - if (n <= 0 || n > MaxPtrArraySize) - n = InitialPtrArraySize; - - pta = (PTA *)LEPT_CALLOC(1, sizeof(PTA)); - pta->n = 0; - pta->nalloc = n; - ptaChangeRefcount(pta, 1); /* sets to 1 */ - pta->x = (l_float32 *)LEPT_CALLOC(n, sizeof(l_float32)); - pta->y = (l_float32 *)LEPT_CALLOC(n, sizeof(l_float32)); - if (!pta->x || !pta->y) { - ptaDestroy(&pta); - return (PTA *)ERROR_PTR("x and y arrays not both made", procName, NULL); - } - - return pta; -} - - -/*! - * \brief ptaCreateFromNuma() - * - * \param[in] nax [optional] can be null - * \param[in] nay - * \return pta, or NULL on error. - */ -PTA * -ptaCreateFromNuma(NUMA *nax, - NUMA *nay) -{ -l_int32 i, n; -l_float32 startx, delx, xval, yval; -PTA *pta; - - PROCNAME("ptaCreateFromNuma"); - - if (!nay) - return (PTA *)ERROR_PTR("nay not defined", procName, NULL); - n = numaGetCount(nay); - if (nax && numaGetCount(nax) != n) - return (PTA *)ERROR_PTR("nax and nay sizes differ", procName, NULL); - - pta = ptaCreate(n); - numaGetParameters(nay, &startx, &delx); - for (i = 0; i < n; i++) { - if (nax) - numaGetFValue(nax, i, &xval); - else /* use implicit x values from nay */ - xval = startx + i * delx; - numaGetFValue(nay, i, &yval); - ptaAddPt(pta, xval, yval); - } - - return pta; -} - - -/*! - * \brief ptaDestroy() - * - * \param[in,out] ppta will be set to null before returning - * \return void - * - * - * Notes: - * (1) Decrements the ref count and, if 0, destroys the pta. - * (2) Always nulls the input ptr. - *- */ -void -ptaDestroy(PTA **ppta) -{ -PTA *pta; - - PROCNAME("ptaDestroy"); - - if (ppta == NULL) { - L_WARNING("ptr address is NULL!\n", procName); - return; - } - - if ((pta = *ppta) == NULL) - return; - - ptaChangeRefcount(pta, -1); - if (ptaGetRefcount(pta) <= 0) { - LEPT_FREE(pta->x); - LEPT_FREE(pta->y); - LEPT_FREE(pta); - } - - *ppta = NULL; - return; -} - - -/*! - * \brief ptaCopy() - * - * \param[in] pta - * \return copy of pta, or NULL on error - */ -PTA * -ptaCopy(PTA *pta) -{ -l_int32 i; -l_float32 x, y; -PTA *npta; - - PROCNAME("ptaCopy"); - - if (!pta) - return (PTA *)ERROR_PTR("pta not defined", procName, NULL); - - if ((npta = ptaCreate(pta->nalloc)) == NULL) - return (PTA *)ERROR_PTR("npta not made", procName, NULL); - - for (i = 0; i < pta->n; i++) { - ptaGetPt(pta, i, &x, &y); - ptaAddPt(npta, x, y); - } - - return npta; -} - - -/*! - * \brief ptaCopyRange() - * - * \param[in] ptas - * \param[in] istart starting index in ptas - * \param[in] iend ending index in ptas; use 0 to copy to end - * \return 0 if OK, 1 on error - */ -PTA * -ptaCopyRange(PTA *ptas, - l_int32 istart, - l_int32 iend) -{ -l_int32 n, i, x, y; -PTA *ptad; - - PROCNAME("ptaCopyRange"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - n = ptaGetCount(ptas); - if (istart < 0) - istart = 0; - if (istart >= n) - return (PTA *)ERROR_PTR("istart out of bounds", procName, NULL); - if (iend <= 0 || iend >= n) - iend = n - 1; - if (istart > iend) - return (PTA *)ERROR_PTR("istart > iend; no pts", procName, NULL); - - if ((ptad = ptaCreate(iend - istart + 1)) == NULL) - return (PTA *)ERROR_PTR("ptad not made", procName, NULL); - for (i = istart; i <= iend; i++) { - ptaGetIPt(ptas, i, &x, &y); - ptaAddPt(ptad, x, y); - } - - return ptad; -} - - -/*! - * \brief ptaClone() - * - * \param[in] pta - * \return ptr to same pta, or NULL on error - */ -PTA * -ptaClone(PTA *pta) -{ - PROCNAME("ptaClone"); - - if (!pta) - return (PTA *)ERROR_PTR("pta not defined", procName, NULL); - - ptaChangeRefcount(pta, 1); - return pta; -} - - -/*! - * \brief ptaEmpty() - * - * \param[in] pta - * \return 0 if OK, 1 on error - * - *- * Notes: - * This only resets the Pta::n field, for reuse - *- */ -l_ok -ptaEmpty(PTA *pta) -{ - PROCNAME("ptaEmpty"); - - if (!pta) - return ERROR_INT("ptad not defined", procName, 1); - pta->n = 0; - return 0; -} - - -/*---------------------------------------------------------------------* - * Pta array extension * - *---------------------------------------------------------------------*/ -/*! - * \brief ptaAddPt() - * - * \param[in] pta - * \param[in] x, y - * \return 0 if OK, 1 on error - */ -l_ok -ptaAddPt(PTA *pta, - l_float32 x, - l_float32 y) -{ -l_int32 n; - - PROCNAME("ptaAddPt"); - - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - - n = pta->n; - if (n >= pta->nalloc) - ptaExtendArrays(pta); - pta->x[n] = x; - pta->y[n] = y; - pta->n++; - - return 0; -} - - -/*! - * \brief ptaExtendArrays() - * - * \param[in] pta - * \return 0 if OK; 1 on error - */ -static l_int32 -ptaExtendArrays(PTA *pta) -{ - PROCNAME("ptaExtendArrays"); - - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - - if ((pta->x = (l_float32 *)reallocNew((void **)&pta->x, - sizeof(l_float32) * pta->nalloc, - 2 * sizeof(l_float32) * pta->nalloc)) == NULL) - return ERROR_INT("new x array not returned", procName, 1); - if ((pta->y = (l_float32 *)reallocNew((void **)&pta->y, - sizeof(l_float32) * pta->nalloc, - 2 * sizeof(l_float32) * pta->nalloc)) == NULL) - return ERROR_INT("new y array not returned", procName, 1); - - pta->nalloc = 2 * pta->nalloc; - return 0; -} - - -/*---------------------------------------------------------------------* - * Pta insertion and removal * - *---------------------------------------------------------------------*/ -/*! - * \brief ptaInsertPt() - * - * \param[in] pta - * \param[in] index at which pt is to be inserted - * \param[in] x, y point values - * \return 0 if OK; 1 on error - */ -l_ok -ptaInsertPt(PTA *pta, - l_int32 index, - l_int32 x, - l_int32 y) -{ -l_int32 i, n; - - PROCNAME("ptaInsertPt"); - - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - n = ptaGetCount(pta); - if (index < 0 || index > n) - return ERROR_INT("index not in {0...n}", procName, 1); - - if (n > pta->nalloc) - ptaExtendArrays(pta); - pta->n++; - for (i = n; i > index; i--) { - pta->x[i] = pta->x[i - 1]; - pta->y[i] = pta->y[i - 1]; - } - pta->x[index] = x; - pta->y[index] = y; - return 0; -} - - -/*! - * \brief ptaRemovePt() - * - * \param[in] pta - * \param[in] index of point to be removed - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This shifts pta[i] --> pta[i - 1] for all i > index. - * (2) It should not be used repeatedly on large arrays, - * because the function is O(n). - *- */ -l_ok -ptaRemovePt(PTA *pta, - l_int32 index) -{ -l_int32 i, n; - - PROCNAME("ptaRemovePt"); - - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - n = ptaGetCount(pta); - if (index < 0 || index >= n) - return ERROR_INT("index not in {0...n - 1}", procName, 1); - - /* Remove the point */ - for (i = index + 1; i < n; i++) { - pta->x[i - 1] = pta->x[i]; - pta->y[i - 1] = pta->y[i]; - } - pta->n--; - return 0; -} - - -/*---------------------------------------------------------------------* - * Pta accessors * - *---------------------------------------------------------------------*/ -l_int32 -ptaGetRefcount(PTA *pta) -{ - PROCNAME("ptaGetRefcount"); - - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - return pta->refcount; -} - - -l_int32 -ptaChangeRefcount(PTA *pta, - l_int32 delta) -{ - PROCNAME("ptaChangeRefcount"); - - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - pta->refcount += delta; - return 0; -} - - -/*! - * \brief ptaGetCount() - * - * \param[in] pta - * \return count, or 0 if no pta - */ -l_int32 -ptaGetCount(PTA *pta) -{ - PROCNAME("ptaGetCount"); - - if (!pta) - return ERROR_INT("pta not defined", procName, 0); - - return pta->n; -} - - -/*! - * \brief ptaGetPt() - * - * \param[in] pta - * \param[in] index into arrays - * \param[out] px [optional] float x value - * \param[out] py [optional] float y value - * \return 0 if OK; 1 on error - */ -l_ok -ptaGetPt(PTA *pta, - l_int32 index, - l_float32 *px, - l_float32 *py) -{ - PROCNAME("ptaGetPt"); - - if (px) *px = 0; - if (py) *py = 0; - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - if (index < 0 || index >= pta->n) - return ERROR_INT("invalid index", procName, 1); - - if (px) *px = pta->x[index]; - if (py) *py = pta->y[index]; - return 0; -} - - -/*! - * \brief ptaGetIPt() - * - * \param[in] pta - * \param[in] index into arrays - * \param[out] px [optional] integer x value - * \param[out] py [optional] integer y value - * \return 0 if OK; 1 on error - */ -l_ok -ptaGetIPt(PTA *pta, - l_int32 index, - l_int32 *px, - l_int32 *py) -{ - PROCNAME("ptaGetIPt"); - - if (px) *px = 0; - if (py) *py = 0; - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - if (index < 0 || index >= pta->n) - return ERROR_INT("invalid index", procName, 1); - - if (px) *px = (l_int32)(pta->x[index] + 0.5); - if (py) *py = (l_int32)(pta->y[index] + 0.5); - return 0; -} - - -/*! - * \brief ptaSetPt() - * - * \param[in] pta - * \param[in] index into arrays - * \param[in] x, y - * \return 0 if OK; 1 on error - */ -l_ok -ptaSetPt(PTA *pta, - l_int32 index, - l_float32 x, - l_float32 y) -{ - PROCNAME("ptaSetPt"); - - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - if (index < 0 || index >= pta->n) - return ERROR_INT("invalid index", procName, 1); - - pta->x[index] = x; - pta->y[index] = y; - return 0; -} - - -/*! - * \brief ptaGetArrays() - * - * \param[in] pta - * \param[out] pnax [optional] numa of x array - * \param[out] pnay [optional] numa of y array - * \return 0 if OK; 1 on error or if pta is empty - * - *- * Notes: - * (1) This copies the internal arrays into new Numas. - *- */ -l_ok -ptaGetArrays(PTA *pta, - NUMA **pnax, - NUMA **pnay) -{ -l_int32 i, n; -NUMA *nax, *nay; - - PROCNAME("ptaGetArrays"); - - if (!pnax && !pnay) - return ERROR_INT("no output requested", procName, 1); - if (pnax) *pnax = NULL; - if (pnay) *pnay = NULL; - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - if ((n = ptaGetCount(pta)) == 0) - return ERROR_INT("pta is empty", procName, 1); - - if (pnax) { - if ((nax = numaCreate(n)) == NULL) - return ERROR_INT("nax not made", procName, 1); - *pnax = nax; - for (i = 0; i < n; i++) - nax->array[i] = pta->x[i]; - nax->n = n; - } - if (pnay) { - if ((nay = numaCreate(n)) == NULL) - return ERROR_INT("nay not made", procName, 1); - *pnay = nay; - for (i = 0; i < n; i++) - nay->array[i] = pta->y[i]; - nay->n = n; - } - return 0; -} - - -/*---------------------------------------------------------------------* - * Pta serialized for I/O * - *---------------------------------------------------------------------*/ -/*! - * \brief ptaRead() - * - * \param[in] filename - * \return pta, or NULL on error - */ -PTA * -ptaRead(const char *filename) -{ -FILE *fp; -PTA *pta; - - PROCNAME("ptaRead"); - - if (!filename) - return (PTA *)ERROR_PTR("filename not defined", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (PTA *)ERROR_PTR("stream not opened", procName, NULL); - pta = ptaReadStream(fp); - fclose(fp); - if (!pta) - return (PTA *)ERROR_PTR("pta not read", procName, NULL); - return pta; -} - - -/*! - * \brief ptaReadStream() - * - * \param[in] fp file stream - * \return pta, or NULL on error - */ -PTA * -ptaReadStream(FILE *fp) -{ -char typestr[128]; /* hardcoded below in fscanf */ -l_int32 i, n, ix, iy, type, version; -l_float32 x, y; -PTA *pta; - - PROCNAME("ptaReadStream"); - - if (!fp) - return (PTA *)ERROR_PTR("stream not defined", procName, NULL); - - if (fscanf(fp, "\n Pta Version %d\n", &version) != 1) - return (PTA *)ERROR_PTR("not a pta file", procName, NULL); - if (version != PTA_VERSION_NUMBER) - return (PTA *)ERROR_PTR("invalid pta version", procName, NULL); - if (fscanf(fp, " Number of pts = %d; format = %127s\n", &n, typestr) != 2) - return (PTA *)ERROR_PTR("not a pta file", procName, NULL); - if (!strcmp(typestr, "float")) - type = 0; - else /* typestr is "integer" */ - type = 1; - - if ((pta = ptaCreate(n)) == NULL) - return (PTA *)ERROR_PTR("pta not made", procName, NULL); - for (i = 0; i < n; i++) { - if (type == 0) { /* data is float */ - if (fscanf(fp, " (%f, %f)\n", &x, &y) != 2) { - ptaDestroy(&pta); - return (PTA *)ERROR_PTR("error reading floats", procName, NULL); - } - ptaAddPt(pta, x, y); - } else { /* data is integer */ - if (fscanf(fp, " (%d, %d)\n", &ix, &iy) != 2) { - ptaDestroy(&pta); - return (PTA *)ERROR_PTR("error reading ints", procName, NULL); - } - ptaAddPt(pta, ix, iy); - } - } - - return pta; -} - - -/*! - * \brief ptaReadMem() - * - * \param[in] data serialization in ascii - * \param[in] size of data in bytes; can use strlen to get it - * \return pta, or NULL on error - */ -PTA * -ptaReadMem(const l_uint8 *data, - size_t size) -{ -FILE *fp; -PTA *pta; - - PROCNAME("ptaReadMem"); - - if (!data) - return (PTA *)ERROR_PTR("data not defined", procName, NULL); - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return (PTA *)ERROR_PTR("stream not opened", procName, NULL); - - pta = ptaReadStream(fp); - fclose(fp); - if (!pta) L_ERROR("pta not read\n", procName); - return pta; -} - - -/*! - * \brief ptaWriteDebug() - * - * \param[in] filename - * \param[in] pta - * \param[in] type 0 for float values; 1 for integer values - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Debug version, intended for use in the library when writing - * to files in a temp directory with names that are compiled in. - * This is used instead of ptaWrite() for all such library calls. - * (2) The global variable LeptDebugOK defaults to 0, and can be set - * or cleared by the function setLeptDebugOK(). - *- */ -l_ok -ptaWriteDebug(const char *filename, - PTA *pta, - l_int32 type) -{ - PROCNAME("ptaWriteDebug"); - - if (LeptDebugOK) { - return ptaWrite(filename, pta, type); - } else { - L_INFO("write to named temp file %s is disabled\n", procName, filename); - return 0; - } -} - - -/*! - * \brief ptaWrite() - * - * \param[in] filename - * \param[in] pta - * \param[in] type 0 for float values; 1 for integer values - * \return 0 if OK, 1 on error - */ -l_ok -ptaWrite(const char *filename, - PTA *pta, - l_int32 type) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("ptaWrite"); - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "w")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = ptaWriteStream(fp, pta, type); - fclose(fp); - if (ret) - return ERROR_INT("pta not written to stream", procName, 1); - return 0; -} - - -/*! - * \brief ptaWriteStream() - * - * \param[in] fp file stream - * \param[in] pta - * \param[in] type 0 for float values; 1 for integer values - * \return 0 if OK; 1 on error - */ -l_ok -ptaWriteStream(FILE *fp, - PTA *pta, - l_int32 type) -{ -l_int32 i, n, ix, iy; -l_float32 x, y; - - PROCNAME("ptaWriteStream"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - - n = ptaGetCount(pta); - fprintf(fp, "\n Pta Version %d\n", PTA_VERSION_NUMBER); - if (type == 0) - fprintf(fp, " Number of pts = %d; format = float\n", n); - else /* type == 1 */ - fprintf(fp, " Number of pts = %d; format = integer\n", n); - for (i = 0; i < n; i++) { - if (type == 0) { /* data is float */ - ptaGetPt(pta, i, &x, &y); - fprintf(fp, " (%f, %f)\n", x, y); - } else { /* data is integer */ - ptaGetIPt(pta, i, &ix, &iy); - fprintf(fp, " (%d, %d)\n", ix, iy); - } - } - - return 0; -} - - -/*! - * \brief ptaWriteMem() - * - * \param[out] pdata data of serialized pta; ascii - * \param[out] psize size of returned data - * \param[in] pta - * \param[in] type 0 for float values; 1 for integer values - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Serializes a pta in memory and puts the result in a buffer. - *- */ -l_ok -ptaWriteMem(l_uint8 **pdata, - size_t *psize, - PTA *pta, - l_int32 type) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("ptaWriteMem"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - if (!psize) - return ERROR_INT("&size not defined", procName, 1); - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = ptaWriteStream(fp, pta, type); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = ptaWriteStream(fp, pta, type); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} - - -/*---------------------------------------------------------------------* - * PTAA creation, destruction * - *---------------------------------------------------------------------*/ -/*! - * \brief ptaaCreate() - * - * \param[in] n initial number of ptrs - * \return ptaa, or NULL on error - */ -PTAA * -ptaaCreate(l_int32 n) -{ -PTAA *ptaa; - - PROCNAME("ptaaCreate"); - - if (n <= 0 || n > MaxPtrArraySize) - n = InitialPtrArraySize; - - ptaa = (PTAA *)LEPT_CALLOC(1, sizeof(PTAA)); - ptaa->n = 0; - ptaa->nalloc = n; - if ((ptaa->pta = (PTA **)LEPT_CALLOC(n, sizeof(PTA *))) == NULL) { - ptaaDestroy(&ptaa); - return (PTAA *)ERROR_PTR("pta ptrs not made", procName, NULL); - } - return ptaa; -} - - -/*! - * \brief ptaaDestroy() - * - * \param[in,out] pptaa will be set to null before returning - * \return void - */ -void -ptaaDestroy(PTAA **pptaa) -{ -l_int32 i; -PTAA *ptaa; - - PROCNAME("ptaaDestroy"); - - if (pptaa == NULL) { - L_WARNING("ptr address is NULL!\n", procName); - return; - } - - if ((ptaa = *pptaa) == NULL) - return; - - for (i = 0; i < ptaa->n; i++) - ptaDestroy(&ptaa->pta[i]); - LEPT_FREE(ptaa->pta); - - LEPT_FREE(ptaa); - *pptaa = NULL; - return; -} - - -/*---------------------------------------------------------------------* - * PTAA array extension * - *---------------------------------------------------------------------*/ -/*! - * \brief ptaaAddPta() - * - * \param[in] ptaa - * \param[in] pta to be added - * \param[in] copyflag L_INSERT, L_COPY, L_CLONE - * \return 0 if OK, 1 on error - */ -l_ok -ptaaAddPta(PTAA *ptaa, - PTA *pta, - l_int32 copyflag) -{ -l_int32 n; -PTA *ptac; - - PROCNAME("ptaaAddPta"); - - if (!ptaa) - return ERROR_INT("ptaa not defined", procName, 1); - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - - if (copyflag == L_INSERT) { - ptac = pta; - } else if (copyflag == L_COPY) { - if ((ptac = ptaCopy(pta)) == NULL) - return ERROR_INT("ptac not made", procName, 1); - } else if (copyflag == L_CLONE) { - if ((ptac = ptaClone(pta)) == NULL) - return ERROR_INT("pta clone not made", procName, 1); - } else { - return ERROR_INT("invalid copyflag", procName, 1); - } - - n = ptaaGetCount(ptaa); - if (n >= ptaa->nalloc) - ptaaExtendArray(ptaa); - ptaa->pta[n] = ptac; - ptaa->n++; - - return 0; -} - - -/*! - * \brief ptaaExtendArray() - * - * \param[in] ptaa - * \return 0 if OK, 1 on error - */ -static l_int32 -ptaaExtendArray(PTAA *ptaa) -{ - PROCNAME("ptaaExtendArray"); - - if (!ptaa) - return ERROR_INT("ptaa not defined", procName, 1); - - if ((ptaa->pta = (PTA **)reallocNew((void **)&ptaa->pta, - sizeof(PTA *) * ptaa->nalloc, - 2 * sizeof(PTA *) * ptaa->nalloc)) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - - ptaa->nalloc = 2 * ptaa->nalloc; - return 0; -} - - -/*---------------------------------------------------------------------* - * Ptaa accessors * - *---------------------------------------------------------------------*/ -/*! - * \brief ptaaGetCount() - * - * \param[in] ptaa - * \return count, or 0 if no ptaa - */ -l_int32 -ptaaGetCount(PTAA *ptaa) -{ - PROCNAME("ptaaGetCount"); - - if (!ptaa) - return ERROR_INT("ptaa not defined", procName, 0); - - return ptaa->n; -} - - -/*! - * \brief ptaaGetPta() - * - * \param[in] ptaa - * \param[in] index to the i-th pta - * \param[in] accessflag L_COPY or L_CLONE - * \return pta, or NULL on error - */ -PTA * -ptaaGetPta(PTAA *ptaa, - l_int32 index, - l_int32 accessflag) -{ - PROCNAME("ptaaGetPta"); - - if (!ptaa) - return (PTA *)ERROR_PTR("ptaa not defined", procName, NULL); - if (index < 0 || index >= ptaa->n) - return (PTA *)ERROR_PTR("index not valid", procName, NULL); - - if (accessflag == L_COPY) - return ptaCopy(ptaa->pta[index]); - else if (accessflag == L_CLONE) - return ptaClone(ptaa->pta[index]); - else - return (PTA *)ERROR_PTR("invalid accessflag", procName, NULL); -} - - -/*! - * \brief ptaaGetPt() - * - * \param[in] ptaa - * \param[in] ipta to the i-th pta - * \param[in] jpt index to the j-th pt in the pta - * \param[out] px [optional] float x value - * \param[out] py [optional] float y value - * \return 0 if OK; 1 on error - */ -l_ok -ptaaGetPt(PTAA *ptaa, - l_int32 ipta, - l_int32 jpt, - l_float32 *px, - l_float32 *py) -{ -PTA *pta; - - PROCNAME("ptaaGetPt"); - - if (px) *px = 0; - if (py) *py = 0; - if (!ptaa) - return ERROR_INT("ptaa not defined", procName, 1); - if (ipta < 0 || ipta >= ptaa->n) - return ERROR_INT("index ipta not valid", procName, 1); - - pta = ptaaGetPta(ptaa, ipta, L_CLONE); - if (jpt < 0 || jpt >= pta->n) { - ptaDestroy(&pta); - return ERROR_INT("index jpt not valid", procName, 1); - } - - ptaGetPt(pta, jpt, px, py); - ptaDestroy(&pta); - return 0; -} - - -/*---------------------------------------------------------------------* - * Ptaa array modifiers * - *---------------------------------------------------------------------*/ -/*! - * \brief ptaaInitFull() - * - * \param[in] ptaa can have non-null ptrs in the ptr array - * \param[in] pta to be replicated into the entire ptr array - * \return 0 if OK; 1 on error - */ -l_ok -ptaaInitFull(PTAA *ptaa, - PTA *pta) -{ -l_int32 n, i; -PTA *ptat; - - PROCNAME("ptaaInitFull"); - - if (!ptaa) - return ERROR_INT("ptaa not defined", procName, 1); - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - - n = ptaa->nalloc; - ptaa->n = n; - for (i = 0; i < n; i++) { - ptat = ptaCopy(pta); - ptaaReplacePta(ptaa, i, ptat); - } - return 0; -} - - -/*! - * \brief ptaaReplacePta() - * - * \param[in] ptaa - * \param[in] index to the index-th pta - * \param[in] pta insert and replace any existing one - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Any existing pta is destroyed, and the input one - * is inserted in its place. - * (2) If %index is invalid, return 1 (error) - *- */ -l_ok -ptaaReplacePta(PTAA *ptaa, - l_int32 index, - PTA *pta) -{ -l_int32 n; - - PROCNAME("ptaaReplacePta"); - - if (!ptaa) - return ERROR_INT("ptaa not defined", procName, 1); - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - n = ptaaGetCount(ptaa); - if (index < 0 || index >= n) - return ERROR_INT("index not valid", procName, 1); - - ptaDestroy(&ptaa->pta[index]); - ptaa->pta[index] = pta; - return 0; -} - - -/*! - * \brief ptaaAddPt() - * - * \param[in] ptaa - * \param[in] ipta to the i-th pta - * \param[in] x,y point coordinates - * \return 0 if OK; 1 on error - */ -l_ok -ptaaAddPt(PTAA *ptaa, - l_int32 ipta, - l_float32 x, - l_float32 y) -{ -PTA *pta; - - PROCNAME("ptaaAddPt"); - - if (!ptaa) - return ERROR_INT("ptaa not defined", procName, 1); - if (ipta < 0 || ipta >= ptaa->n) - return ERROR_INT("index ipta not valid", procName, 1); - - pta = ptaaGetPta(ptaa, ipta, L_CLONE); - ptaAddPt(pta, x, y); - ptaDestroy(&pta); - return 0; -} - - -/*! - * \brief ptaaTruncate() - * - * \param[in] ptaa - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This identifies the largest index containing a pta that - * has any points within it, destroys all pta above that index, - * and resets the count. - *- */ -l_ok -ptaaTruncate(PTAA *ptaa) -{ -l_int32 i, n, np; -PTA *pta; - - PROCNAME("ptaaTruncate"); - - if (!ptaa) - return ERROR_INT("ptaa not defined", procName, 1); - - n = ptaaGetCount(ptaa); - for (i = n - 1; i >= 0; i--) { - pta = ptaaGetPta(ptaa, i, L_CLONE); - if (!pta) { - ptaa->n--; - continue; - } - np = ptaGetCount(pta); - ptaDestroy(&pta); - if (np == 0) { - ptaDestroy(&ptaa->pta[i]); - ptaa->n--; - } else { - break; - } - } - return 0; -} - - -/*---------------------------------------------------------------------* - * Ptaa serialized for I/O * - *---------------------------------------------------------------------*/ -/*! - * \brief ptaaRead() - * - * \param[in] filename - * \return ptaa, or NULL on error - */ -PTAA * -ptaaRead(const char *filename) -{ -FILE *fp; -PTAA *ptaa; - - PROCNAME("ptaaRead"); - - if (!filename) - return (PTAA *)ERROR_PTR("filename not defined", procName, NULL); - - if ((fp = fopenReadStream(filename)) == NULL) - return (PTAA *)ERROR_PTR("stream not opened", procName, NULL); - ptaa = ptaaReadStream(fp); - fclose(fp); - if (!ptaa) - return (PTAA *)ERROR_PTR("ptaa not read", procName, NULL); - return ptaa; -} - - -/*! - * \brief ptaaReadStream() - * - * \param[in] fp file stream - * \return ptaa, or NULL on error - */ -PTAA * -ptaaReadStream(FILE *fp) -{ -l_int32 i, n, version; -PTA *pta; -PTAA *ptaa; - - PROCNAME("ptaaReadStream"); - - if (!fp) - return (PTAA *)ERROR_PTR("stream not defined", procName, NULL); - - if (fscanf(fp, "\nPtaa Version %d\n", &version) != 1) - return (PTAA *)ERROR_PTR("not a ptaa file", procName, NULL); - if (version != PTA_VERSION_NUMBER) - return (PTAA *)ERROR_PTR("invalid ptaa version", procName, NULL); - if (fscanf(fp, "Number of Pta = %d\n", &n) != 1) - return (PTAA *)ERROR_PTR("not a ptaa file", procName, NULL); - - if ((ptaa = ptaaCreate(n)) == NULL) - return (PTAA *)ERROR_PTR("ptaa not made", procName, NULL); - for (i = 0; i < n; i++) { - if ((pta = ptaReadStream(fp)) == NULL) { - ptaaDestroy(&ptaa); - return (PTAA *)ERROR_PTR("error reading pta", procName, NULL); - } - ptaaAddPta(ptaa, pta, L_INSERT); - } - - return ptaa; -} - - -/*! - * \brief ptaaReadMem() - * - * \param[in] data serialization in ascii - * \param[in] size of data in bytes; can use strlen to get it - * \return ptaa, or NULL on error - */ -PTAA * -ptaaReadMem(const l_uint8 *data, - size_t size) -{ -FILE *fp; -PTAA *ptaa; - - PROCNAME("ptaaReadMem"); - - if (!data) - return (PTAA *)ERROR_PTR("data not defined", procName, NULL); - if ((fp = fopenReadFromMemory(data, size)) == NULL) - return (PTAA *)ERROR_PTR("stream not opened", procName, NULL); - - ptaa = ptaaReadStream(fp); - fclose(fp); - if (!ptaa) L_ERROR("ptaa not read\n", procName); - return ptaa; -} - - -/*! - * \brief ptaaWriteDebug() - * - * \param[in] filename - * \param[in] ptaa - * \param[in] type 0 for float values; 1 for integer values - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Debug version, intended for use in the library when writing - * to files in a temp directory with names that are compiled in. - * This is used instead of ptaaWrite() for all such library calls. - * (2) The global variable LeptDebugOK defaults to 0, and can be set - * or cleared by the function setLeptDebugOK(). - *- */ -l_ok -ptaaWriteDebug(const char *filename, - PTAA *ptaa, - l_int32 type) -{ - PROCNAME("ptaaWriteDebug"); - - if (LeptDebugOK) { - return ptaaWrite(filename, ptaa, type); - } else { - L_INFO("write to named temp file %s is disabled\n", procName, filename); - return 0; - } -} - - -/*! - * \brief ptaaWrite() - * - * \param[in] filename - * \param[in] ptaa - * \param[in] type 0 for float values; 1 for integer values - * \return 0 if OK, 1 on error - */ -l_ok -ptaaWrite(const char *filename, - PTAA *ptaa, - l_int32 type) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("ptaaWrite"); - - if (!filename) - return ERROR_INT("filename not defined", procName, 1); - if (!ptaa) - return ERROR_INT("ptaa not defined", procName, 1); - - if ((fp = fopenWriteStream(filename, "w")) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = ptaaWriteStream(fp, ptaa, type); - fclose(fp); - if (ret) - return ERROR_INT("ptaa not written to stream", procName, 1); - return 0; -} - - -/*! - * \brief ptaaWriteStream() - * - * \param[in] fp file stream - * \param[in] ptaa - * \param[in] type 0 for float values; 1 for integer values - * \return 0 if OK; 1 on error - */ -l_ok -ptaaWriteStream(FILE *fp, - PTAA *ptaa, - l_int32 type) -{ -l_int32 i, n; -PTA *pta; - - PROCNAME("ptaaWriteStream"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!ptaa) - return ERROR_INT("ptaa not defined", procName, 1); - - n = ptaaGetCount(ptaa); - fprintf(fp, "\nPtaa Version %d\n", PTA_VERSION_NUMBER); - fprintf(fp, "Number of Pta = %d\n", n); - for (i = 0; i < n; i++) { - pta = ptaaGetPta(ptaa, i, L_CLONE); - ptaWriteStream(fp, pta, type); - ptaDestroy(&pta); - } - - return 0; -} - - -/*! - * \brief ptaaWriteMem() - * - * \param[out] pdata data of serialized ptaa; ascii - * \param[out] psize size of returned data - * \param[in] ptaa - * \param[in] type 0 for float values; 1 for integer values - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Serializes %ptaa in memory and puts the result in a buffer. - *- */ -l_ok -ptaaWriteMem(l_uint8 **pdata, - size_t *psize, - PTAA *ptaa, - l_int32 type) -{ -l_int32 ret; -FILE *fp; - - PROCNAME("ptaaWriteMem"); - - if (pdata) *pdata = NULL; - if (psize) *psize = 0; - if (!pdata) - return ERROR_INT("&data not defined", procName, 1); - if (!psize) - return ERROR_INT("&size not defined", procName, 1); - if (!ptaa) - return ERROR_INT("ptaa not defined", procName, 1); - -#if HAVE_FMEMOPEN - if ((fp = open_memstream((char **)pdata, psize)) == NULL) - return ERROR_INT("stream not opened", procName, 1); - ret = ptaaWriteStream(fp, ptaa, type); -#else - L_INFO("work-around: writing to a temp file\n", procName); - #ifdef _WIN32 - if ((fp = fopenWriteWinTempfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #else - if ((fp = tmpfile()) == NULL) - return ERROR_INT("tmpfile stream not opened", procName, 1); - #endif /* _WIN32 */ - ret = ptaaWriteStream(fp, ptaa, type); - rewind(fp); - *pdata = l_binaryReadStream(fp, psize); -#endif /* HAVE_FMEMOPEN */ - fclose(fp); - return ret; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ptafunc1.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ptafunc1.c deleted file mode 100644 index 8dfcd8a1..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ptafunc1.c +++ /dev/null @@ -1,2667 +0,0 @@ -/*====================================================================* - - 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 ptafunc1.c - *- * - * -------------------------------------- - * This file has these Pta utilities: - * - simple rearrangements - * - geometric analysis - * - min/max and filtering - * - least squares fitting - * - interconversions with Pix and Numa - * - display into a pix - * -------------------------------------- - * - * Simple rearrangements - * PTA *ptaSubsample() - * l_int32 ptaJoin() - * l_int32 ptaaJoin() - * PTA *ptaReverse() - * PTA *ptaTranspose() - * PTA *ptaCyclicPerm() - * PTA *ptaSelectRange() - * - * Geometric - * BOX *ptaGetBoundingRegion() - * l_int32 *ptaGetRange() - * PTA *ptaGetInsideBox() - * PTA *pixFindCornerPixels() - * l_int32 ptaContainsPt() - * l_int32 ptaTestIntersection() - * PTA *ptaTransform() - * l_int32 ptaPtInsidePolygon() - * l_float32 l_angleBetweenVectors() - * - * Min/max and filtering - * l_int32 ptaGetMinMax() - * PTA *ptaSelectByValue() - * PTA *ptaCropToMask() - * - * Least Squares Fit - * l_int32 ptaGetLinearLSF() - * l_int32 ptaGetQuadraticLSF() - * l_int32 ptaGetCubicLSF() - * l_int32 ptaGetQuarticLSF() - * l_int32 ptaNoisyLinearLSF() - * l_int32 ptaNoisyQuadraticLSF() - * l_int32 applyLinearFit() - * l_int32 applyQuadraticFit() - * l_int32 applyCubicFit() - * l_int32 applyQuarticFit() - * - * Interconversions with Pix - * l_int32 pixPlotAlongPta() - * PTA *ptaGetPixelsFromPix() - * PIX *pixGenerateFromPta() - * PTA *ptaGetBoundaryPixels() - * PTAA *ptaaGetBoundaryPixels() - * PTAA *ptaaIndexLabeledPixels() - * PTA *ptaGetNeighborPixLocs() - * - * Interconversion with Numa - * PTA *numaConvertToPta1() - * PTA *numaConvertToPta2() - * l_int32 ptaConvertToNuma() - * - * Display Pta and Ptaa - * PIX *pixDisplayPta() - * PIX *pixDisplayPtaaPattern() - * PIX *pixDisplayPtaPattern() - * PTA *ptaReplicatePattern() - * PIX *pixDisplayPtaa() - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include "allheaders.h" - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif /* M_PI */ - -/*---------------------------------------------------------------------* - * Simple rearrangements * - *---------------------------------------------------------------------*/ -/*! - * \brief ptaSubsample() - * - * \param[in] ptas - * \param[in] subfactor subsample factor, >= 1 - * \return ptad evenly sampled pt values from ptas, or NULL on error - */ -PTA * -ptaSubsample(PTA *ptas, - l_int32 subfactor) -{ -l_int32 n, i; -l_float32 x, y; -PTA *ptad; - - PROCNAME("pixSubsample"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - if (subfactor < 1) - return (PTA *)ERROR_PTR("subfactor < 1", procName, NULL); - - ptad = ptaCreate(0); - n = ptaGetCount(ptas); - for (i = 0; i < n; i++) { - if (i % subfactor != 0) continue; - ptaGetPt(ptas, i, &x, &y); - ptaAddPt(ptad, x, y); - } - - return ptad; -} - - -/*! - * \brief ptaJoin() - * - * \param[in] ptad dest pta; add to this one - * \param[in] ptas source pta; add from this one - * \param[in] istart starting index in ptas - * \param[in] iend ending index in ptas; use -1 to cat all - * \return 0 if OK, 1 on error - * - * - * Notes: - * (1) istart < 0 is taken to mean 'read from the start' (istart = 0) - * (2) iend < 0 means 'read to the end' - * (3) if ptas == NULL, this is a no-op - *- */ -l_ok -ptaJoin(PTA *ptad, - PTA *ptas, - l_int32 istart, - l_int32 iend) -{ -l_int32 n, i, x, y; - - PROCNAME("ptaJoin"); - - if (!ptad) - return ERROR_INT("ptad not defined", procName, 1); - if (!ptas) - return 0; - - if (istart < 0) - istart = 0; - n = ptaGetCount(ptas); - if (iend < 0 || iend >= n) - iend = n - 1; - if (istart > iend) - return ERROR_INT("istart > iend; no pts", procName, 1); - - for (i = istart; i <= iend; i++) { - ptaGetIPt(ptas, i, &x, &y); - ptaAddPt(ptad, x, y); - } - - return 0; -} - - -/*! - * \brief ptaaJoin() - * - * \param[in] ptaad dest ptaa; add to this one - * \param[in] ptaas source ptaa; add from this one - * \param[in] istart starting index in ptaas - * \param[in] iend ending index in ptaas; use -1 to cat all - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) istart < 0 is taken to mean 'read from the start' (istart = 0) - * (2) iend < 0 means 'read to the end' - * (3) if ptas == NULL, this is a no-op - *- */ -l_ok -ptaaJoin(PTAA *ptaad, - PTAA *ptaas, - l_int32 istart, - l_int32 iend) -{ -l_int32 n, i; -PTA *pta; - - PROCNAME("ptaaJoin"); - - if (!ptaad) - return ERROR_INT("ptaad not defined", procName, 1); - if (!ptaas) - return 0; - - if (istart < 0) - istart = 0; - n = ptaaGetCount(ptaas); - if (iend < 0 || iend >= n) - iend = n - 1; - if (istart > iend) - return ERROR_INT("istart > iend; no pts", procName, 1); - - for (i = istart; i <= iend; i++) { - pta = ptaaGetPta(ptaas, i, L_CLONE); - ptaaAddPta(ptaad, pta, L_INSERT); - } - - return 0; -} - - -/*! - * \brief ptaReverse() - * - * \param[in] ptas - * \param[in] type 0 for float values; 1 for integer values - * \return ptad reversed pta, or NULL on error - */ -PTA * -ptaReverse(PTA *ptas, - l_int32 type) -{ -l_int32 n, i, ix, iy; -l_float32 x, y; -PTA *ptad; - - PROCNAME("ptaReverse"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - - n = ptaGetCount(ptas); - if ((ptad = ptaCreate(n)) == NULL) - return (PTA *)ERROR_PTR("ptad not made", procName, NULL); - for (i = n - 1; i >= 0; i--) { - if (type == 0) { - ptaGetPt(ptas, i, &x, &y); - ptaAddPt(ptad, x, y); - } else { /* type == 1 */ - ptaGetIPt(ptas, i, &ix, &iy); - ptaAddPt(ptad, ix, iy); - } - } - - return ptad; -} - - -/*! - * \brief ptaTranspose() - * - * \param[in] ptas - * \return ptad with x and y values swapped, or NULL on error - */ -PTA * -ptaTranspose(PTA *ptas) -{ -l_int32 n, i; -l_float32 x, y; -PTA *ptad; - - PROCNAME("ptaTranspose"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - - n = ptaGetCount(ptas); - if ((ptad = ptaCreate(n)) == NULL) - return (PTA *)ERROR_PTR("ptad not made", procName, NULL); - for (i = 0; i < n; i++) { - ptaGetPt(ptas, i, &x, &y); - ptaAddPt(ptad, y, x); - } - - return ptad; -} - - -/*! - * \brief ptaCyclicPerm() - * - * \param[in] ptas - * \param[in] xs, ys start point; must be in ptas - * \return ptad cyclic permutation, starting and ending at (xs, ys, - * or NULL on error - * - *- * Notes: - * (1) Check to insure that (a) ptas is a closed path where - * the first and last points are identical, and (b) the - * resulting pta also starts and ends on the same point - * (which in this case is (xs, ys). - *- */ -PTA * -ptaCyclicPerm(PTA *ptas, - l_int32 xs, - l_int32 ys) -{ -l_int32 n, i, x, y, j, index, state; -l_int32 x1, y1, x2, y2; -PTA *ptad; - - PROCNAME("ptaCyclicPerm"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - - n = ptaGetCount(ptas); - - /* Verify input data */ - ptaGetIPt(ptas, 0, &x1, &y1); - ptaGetIPt(ptas, n - 1, &x2, &y2); - if (x1 != x2 || y1 != y2) - return (PTA *)ERROR_PTR("start and end pts not same", procName, NULL); - state = L_NOT_FOUND; - for (i = 0; i < n; i++) { - ptaGetIPt(ptas, i, &x, &y); - if (x == xs && y == ys) { - state = L_FOUND; - break; - } - } - if (state == L_NOT_FOUND) - return (PTA *)ERROR_PTR("start pt not in ptas", procName, NULL); - - if ((ptad = ptaCreate(n)) == NULL) - return (PTA *)ERROR_PTR("ptad not made", procName, NULL); - for (j = 0; j < n - 1; j++) { - if (i + j < n - 1) - index = i + j; - else - index = (i + j + 1) % n; - ptaGetIPt(ptas, index, &x, &y); - ptaAddPt(ptad, x, y); - } - ptaAddPt(ptad, xs, ys); - - return ptad; -} - - -/*! - * \brief ptaSelectRange() - * - * \param[in] ptas - * \param[in] first use 0 to select from the beginning - * \param[in] last use -1 to select to the end - * \return ptad, or NULL on error - */ -PTA * -ptaSelectRange(PTA *ptas, - l_int32 first, - l_int32 last) -{ -l_int32 n, npt, i; -l_float32 x, y; -PTA *ptad; - - PROCNAME("ptaSelectRange"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - if ((n = ptaGetCount(ptas)) == 0) { - L_WARNING("ptas is empty\n", procName); - return ptaCopy(ptas); - } - first = L_MAX(0, first); - if (last < 0) last = n - 1; - if (first >= n) - return (PTA *)ERROR_PTR("invalid first", procName, NULL); - if (last >= n) { - L_WARNING("last = %d is beyond max index = %d; adjusting\n", - procName, last, n - 1); - last = n - 1; - } - if (first > last) - return (PTA *)ERROR_PTR("first > last", procName, NULL); - - npt = last - first + 1; - ptad = ptaCreate(npt); - for (i = first; i <= last; i++) { - ptaGetPt(ptas, i, &x, &y); - ptaAddPt(ptad, x, y); - } - return ptad; -} - - -/*---------------------------------------------------------------------* - * Geometric * - *---------------------------------------------------------------------*/ -/*! - * \brief ptaGetBoundingRegion() - * - * \param[in] pta - * \return box, or NULL on error - * - *- * Notes: - * (1) This is used when the pta represents a set of points in - * a two-dimensional image. It returns the box of minimum - * size containing the pts in the pta. - *- */ -BOX * -ptaGetBoundingRegion(PTA *pta) -{ -l_int32 n, i, x, y, minx, maxx, miny, maxy; - - PROCNAME("ptaGetBoundingRegion"); - - if (!pta) - return (BOX *)ERROR_PTR("pta not defined", procName, NULL); - - minx = 10000000; - miny = 10000000; - maxx = -10000000; - maxy = -10000000; - n = ptaGetCount(pta); - for (i = 0; i < n; i++) { - ptaGetIPt(pta, i, &x, &y); - if (x < minx) minx = x; - if (x > maxx) maxx = x; - if (y < miny) miny = y; - if (y > maxy) maxy = y; - } - - return boxCreate(minx, miny, maxx - minx + 1, maxy - miny + 1); -} - - -/*! - * \brief ptaGetRange() - * - * \param[in] pta - * \param[out] pminx [optional] min value of x - * \param[out] pmaxx [optional] max value of x - * \param[out] pminy [optional] min value of y - * \param[out] pmaxy [optional] max value of y - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) We can use pts to represent pairs of floating values, that - * are not necessarily tied to a two-dimension region. For - * example, the pts can represent a general function y(x). - *- */ -l_ok -ptaGetRange(PTA *pta, - l_float32 *pminx, - l_float32 *pmaxx, - l_float32 *pminy, - l_float32 *pmaxy) -{ -l_int32 n, i; -l_float32 x, y, minx, maxx, miny, maxy; - - PROCNAME("ptaGetRange"); - - if (!pminx && !pmaxx && !pminy && !pmaxy) - return ERROR_INT("no output requested", procName, 1); - if (pminx) *pminx = 0; - if (pmaxx) *pmaxx = 0; - if (pminy) *pminy = 0; - if (pmaxy) *pmaxy = 0; - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - if ((n = ptaGetCount(pta)) == 0) - return ERROR_INT("no points in pta", procName, 1); - - ptaGetPt(pta, 0, &x, &y); - minx = x; - maxx = x; - miny = y; - maxy = y; - for (i = 1; i < n; i++) { - ptaGetPt(pta, i, &x, &y); - if (x < minx) minx = x; - if (x > maxx) maxx = x; - if (y < miny) miny = y; - if (y > maxy) maxy = y; - } - if (pminx) *pminx = minx; - if (pmaxx) *pmaxx = maxx; - if (pminy) *pminy = miny; - if (pmaxy) *pmaxy = maxy; - return 0; -} - - -/*! - * \brief ptaGetInsideBox() - * - * \param[in] ptas input pts - * \param[in] box - * \return ptad of pts in ptas that are inside the box, or NULL on error - */ -PTA * -ptaGetInsideBox(PTA *ptas, - BOX *box) -{ -PTA *ptad; -l_int32 n, i, contains; -l_float32 x, y; - - PROCNAME("ptaGetInsideBox"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - if (!box) - return (PTA *)ERROR_PTR("box not defined", procName, NULL); - - n = ptaGetCount(ptas); - ptad = ptaCreate(0); - for (i = 0; i < n; i++) { - ptaGetPt(ptas, i, &x, &y); - boxContainsPt(box, x, y, &contains); - if (contains) - ptaAddPt(ptad, x, y); - } - - return ptad; -} - - -/*! - * \brief pixFindCornerPixels() - * - * \param[in] pixs 1 bpp - * \return pta, or NULL on error - * - *- * Notes: - * (1) Finds the 4 corner-most pixels, as defined by a search - * inward from each corner, using a 45 degree line. - *- */ -PTA * -pixFindCornerPixels(PIX *pixs) -{ -l_int32 i, j, x, y, w, h, wpl, mindim, found; -l_uint32 *data, *line; -PTA *pta; - - PROCNAME("pixFindCornerPixels"); - - if (!pixs) - return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 1) - return (PTA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); - - w = pixGetWidth(pixs); - h = pixGetHeight(pixs); - mindim = L_MIN(w, h); - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - - if ((pta = ptaCreate(4)) == NULL) - return (PTA *)ERROR_PTR("pta not made", procName, NULL); - - for (found = FALSE, i = 0; i < mindim; i++) { - for (j = 0; j <= i; j++) { - y = i - j; - line = data + y * wpl; - if (GET_DATA_BIT(line, j)) { - ptaAddPt(pta, j, y); - found = TRUE; - break; - } - } - if (found == TRUE) - break; - } - - for (found = FALSE, i = 0; i < mindim; i++) { - for (j = 0; j <= i; j++) { - y = i - j; - line = data + y * wpl; - x = w - 1 - j; - if (GET_DATA_BIT(line, x)) { - ptaAddPt(pta, x, y); - found = TRUE; - break; - } - } - if (found == TRUE) - break; - } - - for (found = FALSE, i = 0; i < mindim; i++) { - for (j = 0; j <= i; j++) { - y = h - 1 - i + j; - line = data + y * wpl; - if (GET_DATA_BIT(line, j)) { - ptaAddPt(pta, j, y); - found = TRUE; - break; - } - } - if (found == TRUE) - break; - } - - for (found = FALSE, i = 0; i < mindim; i++) { - for (j = 0; j <= i; j++) { - y = h - 1 - i + j; - line = data + y * wpl; - x = w - 1 - j; - if (GET_DATA_BIT(line, x)) { - ptaAddPt(pta, x, y); - found = TRUE; - break; - } - } - if (found == TRUE) - break; - } - - return pta; -} - - -/*! - * \brief ptaContainsPt() - * - * \param[in] pta - * \param[in] x, y point - * \return 1 if contained, 0 otherwise or on error - */ -l_int32 -ptaContainsPt(PTA *pta, - l_int32 x, - l_int32 y) -{ -l_int32 i, n, ix, iy; - - PROCNAME("ptaContainsPt"); - - if (!pta) - return ERROR_INT("pta not defined", procName, 0); - - n = ptaGetCount(pta); - for (i = 0; i < n; i++) { - ptaGetIPt(pta, i, &ix, &iy); - if (x == ix && y == iy) - return 1; - } - return 0; -} - - -/*! - * \brief ptaTestIntersection() - * - * \param[in] pta1, pta2 - * \return bval which is 1 if they have any elements in common; - * 0 otherwise or on error. - */ -l_int32 -ptaTestIntersection(PTA *pta1, - PTA *pta2) -{ -l_int32 i, j, n1, n2, x1, y1, x2, y2; - - PROCNAME("ptaTestIntersection"); - - if (!pta1) - return ERROR_INT("pta1 not defined", procName, 0); - if (!pta2) - return ERROR_INT("pta2 not defined", procName, 0); - - n1 = ptaGetCount(pta1); - n2 = ptaGetCount(pta2); - for (i = 0; i < n1; i++) { - ptaGetIPt(pta1, i, &x1, &y1); - for (j = 0; j < n2; j++) { - ptaGetIPt(pta2, i, &x2, &y2); - if (x1 == x2 && y1 == y2) - return 1; - } - } - - return 0; -} - - -/*! - * \brief ptaTransform() - * - * \param[in] ptas - * \param[in] shiftx, shifty - * \param[in] scalex, scaley - * \return pta, or NULL on error - * - *- * Notes: - * (1) Shift first, then scale. - *- */ -PTA * -ptaTransform(PTA *ptas, - l_int32 shiftx, - l_int32 shifty, - l_float32 scalex, - l_float32 scaley) -{ -l_int32 n, i, x, y; -PTA *ptad; - - PROCNAME("ptaTransform"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - n = ptaGetCount(ptas); - ptad = ptaCreate(n); - for (i = 0; i < n; i++) { - ptaGetIPt(ptas, i, &x, &y); - x = (l_int32)(scalex * (x + shiftx) + 0.5); - y = (l_int32)(scaley * (y + shifty) + 0.5); - ptaAddPt(ptad, x, y); - } - - return ptad; -} - - -/*! - * \brief ptaPtInsidePolygon() - * - * \param[in] pta vertices of a polygon - * \param[in] x, y point to be tested - * \param[out] pinside 1 if inside; 0 if outside or on boundary - * \return 1 if OK, 0 on error - * - * The abs value of the sum of the angles subtended from a point by - * the sides of a polygon, when taken in order traversing the polygon, - * is 0 if the point is outside the polygon and 2*pi if inside. - * The sign will be positive if traversed cw and negative if ccw. - */ -l_int32 -ptaPtInsidePolygon(PTA *pta, - l_float32 x, - l_float32 y, - l_int32 *pinside) -{ -l_int32 i, n; -l_float32 sum, x1, y1, x2, y2, xp1, yp1, xp2, yp2; - - PROCNAME("ptaPtInsidePolygon"); - - if (!pinside) - return ERROR_INT("&inside not defined", procName, 1); - *pinside = 0; - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - - /* Think of (x1,y1) as the end point of a vector that starts - * from the origin (0,0), and ditto for (x2,y2). */ - n = ptaGetCount(pta); - sum = 0.0; - for (i = 0; i < n; i++) { - ptaGetPt(pta, i, &xp1, &yp1); - ptaGetPt(pta, (i + 1) % n, &xp2, &yp2); - x1 = xp1 - x; - y1 = yp1 - y; - x2 = xp2 - x; - y2 = yp2 - y; - sum += l_angleBetweenVectors(x1, y1, x2, y2); - } - - if (L_ABS(sum) > M_PI) - *pinside = 1; - return 0; -} - - -/*! - * \brief l_angleBetweenVectors() - * - * \param[in] x1, y1 end point of first vector - * \param[in] x2, y2 end point of second vector - * \return angle radians, or 0.0 on error - * - *- * Notes: - * (1) This gives the angle between two vectors, going between - * vector1 (x1,y1) and vector2 (x2,y2). The angle is swept - * out from 1 --> 2. If this is clockwise, the angle is - * positive, but the result is folded into the interval [-pi, pi]. - *- */ -l_float32 -l_angleBetweenVectors(l_float32 x1, - l_float32 y1, - l_float32 x2, - l_float32 y2) -{ -l_float64 ang; - - ang = atan2(y2, x2) - atan2(y1, x1); - if (ang > M_PI) ang -= 2.0 * M_PI; - if (ang < -M_PI) ang += 2.0 * M_PI; - return ang; -} - - -/*---------------------------------------------------------------------* - * Min/max and filtering * - *---------------------------------------------------------------------*/ -/*! - * \brief ptaGetMinMax() - * - * \param[in] pta - * \param[out] pxmin [optional] min of x - * \param[out] pymin [optional] min of y - * \param[out] pxmax [optional] max of x - * \param[out] pymax [optional] max of y - * \return 0 if OK, 1 on error. If pta is empty, requested - * values are returned as -1.0. - */ -l_ok -ptaGetMinMax(PTA *pta, - l_float32 *pxmin, - l_float32 *pymin, - l_float32 *pxmax, - l_float32 *pymax) -{ -l_int32 i, n; -l_float32 x, y, xmin, ymin, xmax, ymax; - - PROCNAME("ptaGetMinMax"); - - if (pxmin) *pxmin = -1.0; - if (pymin) *pymin = -1.0; - if (pxmax) *pxmax = -1.0; - if (pymax) *pymax = -1.0; - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - if (!pxmin && !pxmax && !pymin && !pymax) - return ERROR_INT("no output requested", procName, 1); - if ((n = ptaGetCount(pta)) == 0) { - L_WARNING("pta is empty\n", procName); - return 0; - } - - xmin = ymin = 1.0e20; - xmax = ymax = -1.0e20; - for (i = 0; i < n; i++) { - ptaGetPt(pta, i, &x, &y); - if (x < xmin) xmin = x; - if (y < ymin) ymin = y; - if (x > xmax) xmax = x; - if (y > ymax) ymax = y; - } - if (pxmin) *pxmin = xmin; - if (pymin) *pymin = ymin; - if (pxmax) *pxmax = xmax; - if (pymax) *pymax = ymax; - return 0; -} - - -/*! - * \brief ptaSelectByValue() - * - * \param[in] ptas - * \param[in] xth, yth threshold values - * \param[in] type L_SELECT_XVAL, L_SELECT_YVAL, - * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH - * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, - * L_SELECT_IF_LTE, L_SELECT_IF_GTE - * \return ptad filtered set, or NULL on error - */ -PTA * -ptaSelectByValue(PTA *ptas, - l_float32 xth, - l_float32 yth, - l_int32 type, - l_int32 relation) -{ -l_int32 i, n; -l_float32 x, y; -PTA *ptad; - - PROCNAME("ptaSelectByValue"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - if (ptaGetCount(ptas) == 0) { - L_WARNING("ptas is empty\n", procName); - return ptaCopy(ptas); - } - if (type != L_SELECT_XVAL && type != L_SELECT_YVAL && - type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) - return (PTA *)ERROR_PTR("invalid type", procName, NULL); - if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && - relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) - return (PTA *)ERROR_PTR("invalid relation", procName, NULL); - - n = ptaGetCount(ptas); - ptad = ptaCreate(n); - for (i = 0; i < n; i++) { - ptaGetPt(ptas, i, &x, &y); - if (type == L_SELECT_XVAL) { - if ((relation == L_SELECT_IF_LT && x < xth) || - (relation == L_SELECT_IF_GT && x > xth) || - (relation == L_SELECT_IF_LTE && x <= xth) || - (relation == L_SELECT_IF_GTE && x >= xth)) - ptaAddPt(ptad, x, y); - } else if (type == L_SELECT_YVAL) { - if ((relation == L_SELECT_IF_LT && y < yth) || - (relation == L_SELECT_IF_GT && y > yth) || - (relation == L_SELECT_IF_LTE && y <= yth) || - (relation == L_SELECT_IF_GTE && y >= yth)) - ptaAddPt(ptad, x, y); - } else if (type == L_SELECT_IF_EITHER) { - if (((relation == L_SELECT_IF_LT) && (x < xth || y < yth)) || - ((relation == L_SELECT_IF_GT) && (x > xth || y > yth)) || - ((relation == L_SELECT_IF_LTE) && (x <= xth || y <= yth)) || - ((relation == L_SELECT_IF_GTE) && (x >= xth || y >= yth))) - ptaAddPt(ptad, x, y); - } else { /* L_SELECT_IF_BOTH */ - if (((relation == L_SELECT_IF_LT) && (x < xth && y < yth)) || - ((relation == L_SELECT_IF_GT) && (x > xth && y > yth)) || - ((relation == L_SELECT_IF_LTE) && (x <= xth && y <= yth)) || - ((relation == L_SELECT_IF_GTE) && (x >= xth && y >= yth))) - ptaAddPt(ptad, x, y); - } - } - - return ptad; -} - - -/*! - * \brief ptaCropToMask() - * - * \param[in] ptas input pta - * \param[in] pixm 1 bpp mask - * \return ptad with only pts under the mask fg, or NULL on error - */ -PTA * -ptaCropToMask(PTA *ptas, - PIX *pixm) -{ -l_int32 i, n, x, y; -l_uint32 val; -PTA *ptad; - - PROCNAME("ptaCropToMask"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - if (!pixm || pixGetDepth(pixm) != 1) - return (PTA *)ERROR_PTR("pixm undefined or not 1 bpp", procName, NULL); - if (ptaGetCount(ptas) == 0) { - L_INFO("ptas is empty\n", procName); - return ptaCopy(ptas); - } - - n = ptaGetCount(ptas); - ptad = ptaCreate(n); - for (i = 0; i < n; i++) { - ptaGetIPt(ptas, i, &x, &y); - pixGetPixel(pixm, x, y, &val); - if (val == 1) - ptaAddPt(ptad, x, y); - } - return ptad; -} - - -/*---------------------------------------------------------------------* - * Least Squares Fit * - *---------------------------------------------------------------------*/ -/*! - * \brief ptaGetLinearLSF() - * - * \param[in] pta - * \param[out] pa [optional] slope a of least square fit: y = ax + b - * \param[out] pb [optional] intercept b of least square fit - * \param[out] pnafit [optional] numa of least square fit - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Either or both &a and &b must be input. They determine the - * type of line that is fit. - * (2) If both &a and &b are defined, this returns a and b that minimize: - * - * sum (yi - axi -b)^2 - * i - * - * The method is simple: differentiate this expression w/rt a and b, - * and solve the resulting two equations for a and b in terms of - * various sums over the input data (xi, yi). - * (3) We also allow two special cases, where either a = 0 or b = 0: - * (a) If &a is given and &b = null, find the linear LSF that - * goes through the origin (b = 0). - * (b) If &b is given and &a = null, find the linear LSF with - * zero slope (a = 0). - * (4) If &nafit is defined, this returns an array of fitted values, - * corresponding to the two implicit Numa arrays (nax and nay) in pta. - * Thus, just as you can plot the data in pta as nay vs. nax, - * you can plot the linear least square fit as nafit vs. nax. - * Get the nax array using ptaGetArrays(pta, &nax, NULL); - *- */ -l_ok -ptaGetLinearLSF(PTA *pta, - l_float32 *pa, - l_float32 *pb, - NUMA **pnafit) -{ -l_int32 n, i; -l_float32 a, b, factor, sx, sy, sxx, sxy, val; -l_float32 *xa, *ya; - - PROCNAME("ptaGetLinearLSF"); - - if (pa) *pa = 0.0; - if (pb) *pb = 0.0; - if (pnafit) *pnafit = NULL; - if (!pa && !pb && !pnafit) - return ERROR_INT("no output requested", procName, 1); - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - if ((n = ptaGetCount(pta)) < 2) - return ERROR_INT("less than 2 pts found", procName, 1); - - xa = pta->x; /* not a copy */ - ya = pta->y; /* not a copy */ - sx = sy = sxx = sxy = 0.; - if (pa && pb) { /* general line */ - for (i = 0; i < n; i++) { - sx += xa[i]; - sy += ya[i]; - sxx += xa[i] * xa[i]; - sxy += xa[i] * ya[i]; - } - factor = n * sxx - sx * sx; - if (factor == 0.0) - return ERROR_INT("no solution found", procName, 1); - factor = 1. / factor; - - a = factor * ((l_float32)n * sxy - sx * sy); - b = factor * (sxx * sy - sx * sxy); - } else if (pa) { /* b = 0; line through origin */ - for (i = 0; i < n; i++) { - sxx += xa[i] * xa[i]; - sxy += xa[i] * ya[i]; - } - if (sxx == 0.0) - return ERROR_INT("no solution found", procName, 1); - a = sxy / sxx; - b = 0.0; - } else { /* a = 0; horizontal line */ - for (i = 0; i < n; i++) - sy += ya[i]; - a = 0.0; - b = sy / (l_float32)n; - } - - if (pnafit) { - *pnafit = numaCreate(n); - for (i = 0; i < n; i++) { - val = a * xa[i] + b; - numaAddNumber(*pnafit, val); - } - } - - if (pa) *pa = a; - if (pb) *pb = b; - return 0; -} - - -/*! - * \brief ptaGetQuadraticLSF() - * - * \param[in] pta - * \param[out] pa [optional] coeff a of LSF: y = ax^2 + bx + c - * \param[out] pb [optional] coeff b of LSF: y = ax^2 + bx + c - * \param[out] pc [optional] coeff c of LSF: y = ax^2 + bx + c - * \param[out] pnafit [optional] numa of least square fit - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This does a quadratic least square fit to the set of points - * in %pta. That is, it finds coefficients a, b and c that minimize: - * - * sum (yi - a*xi*xi -b*xi -c)^2 - * i - * - * The method is simple: differentiate this expression w/rt - * a, b and c, and solve the resulting three equations for these - * coefficients in terms of various sums over the input data (xi, yi). - * The three equations are in the form: - * f[0][0]a + f[0][1]b + f[0][2]c = g[0] - * f[1][0]a + f[1][1]b + f[1][2]c = g[1] - * f[2][0]a + f[2][1]b + f[2][2]c = g[2] - * (2) If &nafit is defined, this returns an array of fitted values, - * corresponding to the two implicit Numa arrays (nax and nay) in pta. - * Thus, just as you can plot the data in pta as nay vs. nax, - * you can plot the linear least square fit as nafit vs. nax. - * Get the nax array using ptaGetArrays(pta, &nax, NULL); - *- */ -l_ok -ptaGetQuadraticLSF(PTA *pta, - l_float32 *pa, - l_float32 *pb, - l_float32 *pc, - NUMA **pnafit) -{ -l_int32 n, i, ret; -l_float32 x, y, sx, sy, sx2, sx3, sx4, sxy, sx2y; -l_float32 *xa, *ya; -l_float32 *f[3]; -l_float32 g[3]; - - PROCNAME("ptaGetQuadraticLSF"); - - if (pa) *pa = 0.0; - if (pb) *pb = 0.0; - if (pc) *pc = 0.0; - if (pnafit) *pnafit = NULL; - if (!pa && !pb && !pc && !pnafit) - return ERROR_INT("no output requested", procName, 1); - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - if ((n = ptaGetCount(pta)) < 3) - return ERROR_INT("less than 3 pts found", procName, 1); - - xa = pta->x; /* not a copy */ - ya = pta->y; /* not a copy */ - sx = sy = sx2 = sx3 = sx4 = sxy = sx2y = 0.; - for (i = 0; i < n; i++) { - x = xa[i]; - y = ya[i]; - sx += x; - sy += y; - sx2 += x * x; - sx3 += x * x * x; - sx4 += x * x * x * x; - sxy += x * y; - sx2y += x * x * y; - } - - for (i = 0; i < 3; i++) - f[i] = (l_float32 *)LEPT_CALLOC(3, sizeof(l_float32)); - f[0][0] = sx4; - f[0][1] = sx3; - f[0][2] = sx2; - f[1][0] = sx3; - f[1][1] = sx2; - f[1][2] = sx; - f[2][0] = sx2; - f[2][1] = sx; - f[2][2] = n; - g[0] = sx2y; - g[1] = sxy; - g[2] = sy; - - /* Solve for the unknowns, also putting f-inverse into f */ - ret = gaussjordan(f, g, 3); - for (i = 0; i < 3; i++) - LEPT_FREE(f[i]); - if (ret) - return ERROR_INT("quadratic solution failed", procName, 1); - - if (pa) *pa = g[0]; - if (pb) *pb = g[1]; - if (pc) *pc = g[2]; - if (pnafit) { - *pnafit = numaCreate(n); - for (i = 0; i < n; i++) { - x = xa[i]; - y = g[0] * x * x + g[1] * x + g[2]; - numaAddNumber(*pnafit, y); - } - } - return 0; -} - - -/*! - * \brief ptaGetCubicLSF() - * - * \param[in] pta - * \param[out] pa [optional] coeff a of LSF: y = ax^3 + bx^2 + cx + d - * \param[out] pb [optional] coeff b of LSF - * \param[out] pc [optional] coeff c of LSF - * \param[out] pd [optional] coeff d of LSF - * \param[out] pnafit [optional] numa of least square fit - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This does a cubic least square fit to the set of points - * in %pta. That is, it finds coefficients a, b, c and d - * that minimize: - * - * sum (yi - a*xi*xi*xi -b*xi*xi -c*xi - d)^2 - * i - * - * Differentiate this expression w/rt a, b, c and d, and solve - * the resulting four equations for these coefficients in - * terms of various sums over the input data (xi, yi). - * The four equations are in the form: - * f[0][0]a + f[0][1]b + f[0][2]c + f[0][3] = g[0] - * f[1][0]a + f[1][1]b + f[1][2]c + f[1][3] = g[1] - * f[2][0]a + f[2][1]b + f[2][2]c + f[2][3] = g[2] - * f[3][0]a + f[3][1]b + f[3][2]c + f[3][3] = g[3] - * (2) If &nafit is defined, this returns an array of fitted values, - * corresponding to the two implicit Numa arrays (nax and nay) in pta. - * Thus, just as you can plot the data in pta as nay vs. nax, - * you can plot the linear least square fit as nafit vs. nax. - * Get the nax array using ptaGetArrays(pta, &nax, NULL); - *- */ -l_ok -ptaGetCubicLSF(PTA *pta, - l_float32 *pa, - l_float32 *pb, - l_float32 *pc, - l_float32 *pd, - NUMA **pnafit) -{ -l_int32 n, i, ret; -l_float32 x, y, sx, sy, sx2, sx3, sx4, sx5, sx6, sxy, sx2y, sx3y; -l_float32 *xa, *ya; -l_float32 *f[4]; -l_float32 g[4]; - - PROCNAME("ptaGetCubicLSF"); - - if (pa) *pa = 0.0; - if (pb) *pb = 0.0; - if (pc) *pc = 0.0; - if (pd) *pd = 0.0; - if (pnafit) *pnafit = NULL; - if (!pa && !pb && !pc && !pd && !pnafit) - return ERROR_INT("no output requested", procName, 1); - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - if ((n = ptaGetCount(pta)) < 4) - return ERROR_INT("less than 4 pts found", procName, 1); - - xa = pta->x; /* not a copy */ - ya = pta->y; /* not a copy */ - sx = sy = sx2 = sx3 = sx4 = sx5 = sx6 = sxy = sx2y = sx3y = 0.; - for (i = 0; i < n; i++) { - x = xa[i]; - y = ya[i]; - sx += x; - sy += y; - sx2 += x * x; - sx3 += x * x * x; - sx4 += x * x * x * x; - sx5 += x * x * x * x * x; - sx6 += x * x * x * x * x * x; - sxy += x * y; - sx2y += x * x * y; - sx3y += x * x * x * y; - } - - for (i = 0; i < 4; i++) - f[i] = (l_float32 *)LEPT_CALLOC(4, sizeof(l_float32)); - f[0][0] = sx6; - f[0][1] = sx5; - f[0][2] = sx4; - f[0][3] = sx3; - f[1][0] = sx5; - f[1][1] = sx4; - f[1][2] = sx3; - f[1][3] = sx2; - f[2][0] = sx4; - f[2][1] = sx3; - f[2][2] = sx2; - f[2][3] = sx; - f[3][0] = sx3; - f[3][1] = sx2; - f[3][2] = sx; - f[3][3] = n; - g[0] = sx3y; - g[1] = sx2y; - g[2] = sxy; - g[3] = sy; - - /* Solve for the unknowns, also putting f-inverse into f */ - ret = gaussjordan(f, g, 4); - for (i = 0; i < 4; i++) - LEPT_FREE(f[i]); - if (ret) - return ERROR_INT("cubic solution failed", procName, 1); - - if (pa) *pa = g[0]; - if (pb) *pb = g[1]; - if (pc) *pc = g[2]; - if (pd) *pd = g[3]; - if (pnafit) { - *pnafit = numaCreate(n); - for (i = 0; i < n; i++) { - x = xa[i]; - y = g[0] * x * x * x + g[1] * x * x + g[2] * x + g[3]; - numaAddNumber(*pnafit, y); - } - } - return 0; -} - - -/*! - * \brief ptaGetQuarticLSF() - * - * \param[in] pta - * \param[out] pa [optional] coeff a of LSF: - * y = ax^4 + bx^3 + cx^2 + dx + e - * \param[out] pb [optional] coeff b of LSF - * \param[out] pc [optional] coeff c of LSF - * \param[out] pd [optional] coeff d of LSF - * \param[out] pe [optional] coeff e of LSF - * \param[out] pnafit [optional] numa of least square fit - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This does a quartic least square fit to the set of points - * in %pta. That is, it finds coefficients a, b, c, d and 3 - * that minimize: - * - * sum (yi - a*xi*xi*xi*xi -b*xi*xi*xi -c*xi*xi - d*xi - e)^2 - * i - * - * Differentiate this expression w/rt a, b, c, d and e, and solve - * the resulting five equations for these coefficients in - * terms of various sums over the input data (xi, yi). - * The five equations are in the form: - * f[0][0]a + f[0][1]b + f[0][2]c + f[0][3] + f[0][4] = g[0] - * f[1][0]a + f[1][1]b + f[1][2]c + f[1][3] + f[1][4] = g[1] - * f[2][0]a + f[2][1]b + f[2][2]c + f[2][3] + f[2][4] = g[2] - * f[3][0]a + f[3][1]b + f[3][2]c + f[3][3] + f[3][4] = g[3] - * f[4][0]a + f[4][1]b + f[4][2]c + f[4][3] + f[4][4] = g[4] - * (2) If &nafit is defined, this returns an array of fitted values, - * corresponding to the two implicit Numa arrays (nax and nay) in pta. - * Thus, just as you can plot the data in pta as nay vs. nax, - * you can plot the linear least square fit as nafit vs. nax. - * Get the nax array using ptaGetArrays(pta, &nax, NULL); - *- */ -l_ok -ptaGetQuarticLSF(PTA *pta, - l_float32 *pa, - l_float32 *pb, - l_float32 *pc, - l_float32 *pd, - l_float32 *pe, - NUMA **pnafit) -{ -l_int32 n, i, ret; -l_float32 x, y, sx, sy, sx2, sx3, sx4, sx5, sx6, sx7, sx8; -l_float32 sxy, sx2y, sx3y, sx4y; -l_float32 *xa, *ya; -l_float32 *f[5]; -l_float32 g[5]; - - PROCNAME("ptaGetQuarticLSF"); - - if (pa) *pa = 0.0; - if (pb) *pb = 0.0; - if (pc) *pc = 0.0; - if (pd) *pd = 0.0; - if (pe) *pe = 0.0; - if (pnafit) *pnafit = NULL; - if (!pa && !pb && !pc && !pd && !pe && !pnafit) - return ERROR_INT("no output requested", procName, 1); - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - if ((n = ptaGetCount(pta)) < 5) - return ERROR_INT("less than 5 pts found", procName, 1); - - xa = pta->x; /* not a copy */ - ya = pta->y; /* not a copy */ - sx = sy = sx2 = sx3 = sx4 = sx5 = sx6 = sx7 = sx8 = 0; - sxy = sx2y = sx3y = sx4y = 0.; - for (i = 0; i < n; i++) { - x = xa[i]; - y = ya[i]; - sx += x; - sy += y; - sx2 += x * x; - sx3 += x * x * x; - sx4 += x * x * x * x; - sx5 += x * x * x * x * x; - sx6 += x * x * x * x * x * x; - sx7 += x * x * x * x * x * x * x; - sx8 += x * x * x * x * x * x * x * x; - sxy += x * y; - sx2y += x * x * y; - sx3y += x * x * x * y; - sx4y += x * x * x * x * y; - } - - for (i = 0; i < 5; i++) - f[i] = (l_float32 *)LEPT_CALLOC(5, sizeof(l_float32)); - f[0][0] = sx8; - f[0][1] = sx7; - f[0][2] = sx6; - f[0][3] = sx5; - f[0][4] = sx4; - f[1][0] = sx7; - f[1][1] = sx6; - f[1][2] = sx5; - f[1][3] = sx4; - f[1][4] = sx3; - f[2][0] = sx6; - f[2][1] = sx5; - f[2][2] = sx4; - f[2][3] = sx3; - f[2][4] = sx2; - f[3][0] = sx5; - f[3][1] = sx4; - f[3][2] = sx3; - f[3][3] = sx2; - f[3][4] = sx; - f[4][0] = sx4; - f[4][1] = sx3; - f[4][2] = sx2; - f[4][3] = sx; - f[4][4] = n; - g[0] = sx4y; - g[1] = sx3y; - g[2] = sx2y; - g[3] = sxy; - g[4] = sy; - - /* Solve for the unknowns, also putting f-inverse into f */ - ret = gaussjordan(f, g, 5); - for (i = 0; i < 5; i++) - LEPT_FREE(f[i]); - if (ret) - return ERROR_INT("quartic solution failed", procName, 1); - - if (pa) *pa = g[0]; - if (pb) *pb = g[1]; - if (pc) *pc = g[2]; - if (pd) *pd = g[3]; - if (pe) *pe = g[4]; - if (pnafit) { - *pnafit = numaCreate(n); - for (i = 0; i < n; i++) { - x = xa[i]; - y = g[0] * x * x * x * x + g[1] * x * x * x + g[2] * x * x - + g[3] * x + g[4]; - numaAddNumber(*pnafit, y); - } - } - return 0; -} - - -/*! - * \brief ptaNoisyLinearLSF() - * - * \param[in] pta - * \param[in] factor reject outliers with error greater than this - * number of medians; typically ~ 3 - * \param[out] pptad [optional] with outliers removed - * \param[out] pa [optional] slope a of least square fit: y = ax + b - * \param[out] pb [optional] intercept b of least square fit - * \param[out] pmederr [optional] median error - * \param[out] pnafit [optional] numa of least square fit to ptad - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This does a linear least square fit to the set of points - * in %pta. It then evaluates the errors and removes points - * whose error is >= factor * median_error. It then re-runs - * the linear LSF on the resulting points. - * (2) Either or both &a and &b must be input. They determine the - * type of line that is fit. - * (3) The median error can give an indication of how good the fit - * is likely to be. - *- */ -l_ok -ptaNoisyLinearLSF(PTA *pta, - l_float32 factor, - PTA **pptad, - l_float32 *pa, - l_float32 *pb, - l_float32 *pmederr, - NUMA **pnafit) -{ -l_int32 n, i, ret; -l_float32 x, y, yf, val, mederr; -NUMA *nafit, *naerror; -PTA *ptad; - - PROCNAME("ptaNoisyLinearLSF"); - - if (pptad) *pptad = NULL; - if (pa) *pa = 0.0; - if (pb) *pb = 0.0; - if (pmederr) *pmederr = 0.0; - if (pnafit) *pnafit = NULL; - if (!pptad && !pa && !pb && !pnafit) - return ERROR_INT("no output requested", procName, 1); - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - if (factor <= 0.0) - return ERROR_INT("factor must be > 0.0", procName, 1); - if ((n = ptaGetCount(pta)) < 3) - return ERROR_INT("less than 2 pts found", procName, 1); - - if (ptaGetLinearLSF(pta, pa, pb, &nafit) != 0) - return ERROR_INT("error in linear LSF", procName, 1); - - /* Get the median error */ - naerror = numaCreate(n); - for (i = 0; i < n; i++) { - ptaGetPt(pta, i, &x, &y); - numaGetFValue(nafit, i, &yf); - numaAddNumber(naerror, L_ABS(y - yf)); - } - numaGetMedian(naerror, &mederr); - if (pmederr) *pmederr = mederr; - numaDestroy(&nafit); - - /* Remove outliers */ - ptad = ptaCreate(n); - for (i = 0; i < n; i++) { - ptaGetPt(pta, i, &x, &y); - numaGetFValue(naerror, i, &val); - if (val <= factor * mederr) /* <= in case mederr = 0 */ - ptaAddPt(ptad, x, y); - } - numaDestroy(&naerror); - - /* Do LSF again */ - ret = ptaGetLinearLSF(ptad, pa, pb, pnafit); - if (pptad) - *pptad = ptad; - else - ptaDestroy(&ptad); - - return ret; -} - - -/*! - * \brief ptaNoisyQuadraticLSF() - * - * \param[in] pta - * \param[in] factor reject outliers with error greater than this - * number of medians; typically ~ 3 - * \param[out] pptad [optional] with outliers removed - * \param[out] pa [optional] coeff a of LSF: y = ax^2 + bx + c - * \param[out] pb [optional] coeff b of LSF: y = ax^2 + bx + c - * \param[out] pc [optional] coeff c of LSF: y = ax^2 + bx + c - * \param[out] pmederr [optional] median error - * \param[out] pnafit [optional] numa of least square fit to ptad - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This does a quadratic least square fit to the set of points - * in %pta. It then evaluates the errors and removes points - * whose error is >= factor * median_error. It then re-runs - * a quadratic LSF on the resulting points. - *- */ -l_ok -ptaNoisyQuadraticLSF(PTA *pta, - l_float32 factor, - PTA **pptad, - l_float32 *pa, - l_float32 *pb, - l_float32 *pc, - l_float32 *pmederr, - NUMA **pnafit) -{ -l_int32 n, i, ret; -l_float32 x, y, yf, val, mederr; -NUMA *nafit, *naerror; -PTA *ptad; - - PROCNAME("ptaNoisyQuadraticLSF"); - - if (pptad) *pptad = NULL; - if (pa) *pa = 0.0; - if (pb) *pb = 0.0; - if (pc) *pc = 0.0; - if (pmederr) *pmederr = 0.0; - if (pnafit) *pnafit = NULL; - if (!pptad && !pa && !pb && !pc && !pnafit) - return ERROR_INT("no output requested", procName, 1); - if (factor <= 0.0) - return ERROR_INT("factor must be > 0.0", procName, 1); - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - if ((n = ptaGetCount(pta)) < 3) - return ERROR_INT("less than 3 pts found", procName, 1); - - if (ptaGetQuadraticLSF(pta, NULL, NULL, NULL, &nafit) != 0) - return ERROR_INT("error in quadratic LSF", procName, 1); - - /* Get the median error */ - naerror = numaCreate(n); - for (i = 0; i < n; i++) { - ptaGetPt(pta, i, &x, &y); - numaGetFValue(nafit, i, &yf); - numaAddNumber(naerror, L_ABS(y - yf)); - } - numaGetMedian(naerror, &mederr); - if (pmederr) *pmederr = mederr; - numaDestroy(&nafit); - - /* Remove outliers */ - ptad = ptaCreate(n); - for (i = 0; i < n; i++) { - ptaGetPt(pta, i, &x, &y); - numaGetFValue(naerror, i, &val); - if (val <= factor * mederr) /* <= in case mederr = 0 */ - ptaAddPt(ptad, x, y); - } - numaDestroy(&naerror); - n = ptaGetCount(ptad); - if ((n = ptaGetCount(ptad)) < 3) { - ptaDestroy(&ptad); - return ERROR_INT("less than 3 pts found", procName, 1); - } - - /* Do LSF again */ - ret = ptaGetQuadraticLSF(ptad, pa, pb, pc, pnafit); - if (pptad) - *pptad = ptad; - else - ptaDestroy(&ptad); - - return ret; -} - - -/*! - * \brief applyLinearFit() - * - * \param[in] a, b linear fit coefficients - * \param[in] x - * \param[out] py y = a * x + b - * \return 0 if OK, 1 on error - */ -l_ok -applyLinearFit(l_float32 a, - l_float32 b, - l_float32 x, - l_float32 *py) -{ - PROCNAME("applyLinearFit"); - - if (!py) - return ERROR_INT("&y not defined", procName, 1); - - *py = a * x + b; - return 0; -} - - -/*! - * \brief applyQuadraticFit() - * - * \param[in] a, b, c quadratic fit coefficients - * \param[in] x - * \param[out] py y = a * x^2 + b * x + c - * \return 0 if OK, 1 on error - */ -l_ok -applyQuadraticFit(l_float32 a, - l_float32 b, - l_float32 c, - l_float32 x, - l_float32 *py) -{ - PROCNAME("applyQuadraticFit"); - - if (!py) - return ERROR_INT("&y not defined", procName, 1); - - *py = a * x * x + b * x + c; - return 0; -} - - -/*! - * \brief applyCubicFit() - * - * \param[in] a, b, c, d cubic fit coefficients - * \param[in] x - * \param[out] py y = a * x^3 + b * x^2 + c * x + d - * \return 0 if OK, 1 on error - */ -l_ok -applyCubicFit(l_float32 a, - l_float32 b, - l_float32 c, - l_float32 d, - l_float32 x, - l_float32 *py) -{ - PROCNAME("applyCubicFit"); - - if (!py) - return ERROR_INT("&y not defined", procName, 1); - - *py = a * x * x * x + b * x * x + c * x + d; - return 0; -} - - -/*! - * \brief applyQuarticFit() - * - * \param[in] a, b, c, d, e quartic fit coefficients - * \param[in] x - * \param[out] py y = a * x^4 + b * x^3 + c * x^2 + d * x + e - * \return 0 if OK, 1 on error - */ -l_ok -applyQuarticFit(l_float32 a, - l_float32 b, - l_float32 c, - l_float32 d, - l_float32 e, - l_float32 x, - l_float32 *py) -{ -l_float32 x2; - - PROCNAME("applyQuarticFit"); - - if (!py) - return ERROR_INT("&y not defined", procName, 1); - - x2 = x * x; - *py = a * x2 * x2 + b * x2 * x + c * x2 + d * x + e; - return 0; -} - - -/*---------------------------------------------------------------------* - * Interconversions with Pix * - *---------------------------------------------------------------------*/ -/*! - * \brief pixPlotAlongPta() - * - * \param[in] pixs any depth - * \param[in] pta set of points on which to plot - * \param[in] outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_LATEX - * \param[in] title [optional] for plot; can be null - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This is a debugging function. - * (2) Removes existing colormaps and clips the pta to the input %pixs. - * (3) If the image is RGB, three separate plots are generated. - *- */ -l_ok -pixPlotAlongPta(PIX *pixs, - PTA *pta, - l_int32 outformat, - const char *title) -{ -char buffer[128]; -char *rtitle, *gtitle, *btitle; -static l_int32 count = 0; /* require separate temp files for each call */ -l_int32 i, x, y, d, w, h, npts, rval, gval, bval; -l_uint32 val; -NUMA *na, *nar, *nag, *nab; -PIX *pixt; - - PROCNAME("pixPlotAlongPta"); - - lept_mkdir("lept/plot"); - - if (!pixs) - return ERROR_INT("pixs not defined", procName, 1); - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - if (outformat != GPLOT_PNG && outformat != GPLOT_PS && - outformat != GPLOT_EPS && outformat != GPLOT_LATEX) { - L_WARNING("outformat invalid; using GPLOT_PNG\n", procName); - outformat = GPLOT_PNG; - } - - pixt = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); - d = pixGetDepth(pixt); - w = pixGetWidth(pixt); - h = pixGetHeight(pixt); - npts = ptaGetCount(pta); - if (d == 32) { - nar = numaCreate(npts); - nag = numaCreate(npts); - nab = numaCreate(npts); - for (i = 0; i < npts; i++) { - ptaGetIPt(pta, i, &x, &y); - if (x < 0 || x >= w) - continue; - if (y < 0 || y >= h) - continue; - pixGetPixel(pixt, x, y, &val); - rval = GET_DATA_BYTE(&val, COLOR_RED); - gval = GET_DATA_BYTE(&val, COLOR_GREEN); - bval = GET_DATA_BYTE(&val, COLOR_BLUE); - numaAddNumber(nar, rval); - numaAddNumber(nag, gval); - numaAddNumber(nab, bval); - } - - snprintf(buffer, sizeof(buffer), "/tmp/lept/plot/%03d", count++); - rtitle = stringJoin("Red: ", title); - gplotSimple1(nar, outformat, buffer, rtitle); - snprintf(buffer, sizeof(buffer), "/tmp/lept/plot/%03d", count++); - gtitle = stringJoin("Green: ", title); - gplotSimple1(nag, outformat, buffer, gtitle); - snprintf(buffer, sizeof(buffer), "/tmp/lept/plot/%03d", count++); - btitle = stringJoin("Blue: ", title); - gplotSimple1(nab, outformat, buffer, btitle); - numaDestroy(&nar); - numaDestroy(&nag); - numaDestroy(&nab); - LEPT_FREE(rtitle); - LEPT_FREE(gtitle); - LEPT_FREE(btitle); - } else { - na = numaCreate(npts); - for (i = 0; i < npts; i++) { - ptaGetIPt(pta, i, &x, &y); - if (x < 0 || x >= w) - continue; - if (y < 0 || y >= h) - continue; - pixGetPixel(pixt, x, y, &val); - numaAddNumber(na, (l_float32)val); - } - - snprintf(buffer, sizeof(buffer), "/tmp/lept/plot/%03d", count++); - gplotSimple1(na, outformat, buffer, title); - numaDestroy(&na); - } - pixDestroy(&pixt); - return 0; -} - - -/*! - * \brief ptaGetPixelsFromPix() - * - * \param[in] pixs 1 bpp - * \param[in] box [optional] can be null - * \return pta, or NULL on error - * - *- * Notes: - * (1) Generates a pta of fg pixels in the pix, within the box. - * If box == NULL, it uses the entire pix. - *- */ -PTA * -ptaGetPixelsFromPix(PIX *pixs, - BOX *box) -{ -l_int32 i, j, w, h, wpl, xstart, xend, ystart, yend, bw, bh; -l_uint32 *data, *line; -PTA *pta; - - PROCNAME("ptaGetPixelsFromPix"); - - if (!pixs || (pixGetDepth(pixs) != 1)) - return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - xstart = ystart = 0; - xend = w - 1; - yend = h - 1; - if (box) { - boxGetGeometry(box, &xstart, &ystart, &bw, &bh); - xend = xstart + bw - 1; - yend = ystart + bh - 1; - } - - if ((pta = ptaCreate(0)) == NULL) - return (PTA *)ERROR_PTR("pta not made", procName, NULL); - for (i = ystart; i <= yend; i++) { - line = data + i * wpl; - for (j = xstart; j <= xend; j++) { - if (GET_DATA_BIT(line, j)) - ptaAddPt(pta, j, i); - } - } - - return pta; -} - - -/*! - * \brief pixGenerateFromPta() - * - * \param[in] pta - * \param[in] w, h of pix - * \return pix 1 bpp, or NULL on error - * - *- * Notes: - * (1) Points are rounded to nearest ints. - * (2) Any points outside (w,h) are silently discarded. - * (3) Output 1 bpp pix has values 1 for each point in the pta. - *- */ -PIX * -pixGenerateFromPta(PTA *pta, - l_int32 w, - l_int32 h) -{ -l_int32 n, i, x, y; -PIX *pix; - - PROCNAME("pixGenerateFromPta"); - - if (!pta) - return (PIX *)ERROR_PTR("pta not defined", procName, NULL); - - if ((pix = pixCreate(w, h, 1)) == NULL) - return (PIX *)ERROR_PTR("pix not made", procName, NULL); - n = ptaGetCount(pta); - for (i = 0; i < n; i++) { - ptaGetIPt(pta, i, &x, &y); - if (x < 0 || x >= w || y < 0 || y >= h) - continue; - pixSetPixel(pix, x, y, 1); - } - - return pix; -} - - -/*! - * \brief ptaGetBoundaryPixels() - * - * \param[in] pixs 1 bpp - * \param[in] type L_BOUNDARY_FG, L_BOUNDARY_BG - * \return pta, or NULL on error - * - *- * Notes: - * (1) This generates a pta of either fg or bg boundary pixels. - * (2) See also pixGeneratePtaBoundary() for rendering of - * fg boundary pixels. - *- */ -PTA * -ptaGetBoundaryPixels(PIX *pixs, - l_int32 type) -{ -PIX *pixt; -PTA *pta; - - PROCNAME("ptaGetBoundaryPixels"); - - if (!pixs || (pixGetDepth(pixs) != 1)) - return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (type != L_BOUNDARY_FG && type != L_BOUNDARY_BG) - return (PTA *)ERROR_PTR("invalid type", procName, NULL); - - if (type == L_BOUNDARY_FG) - pixt = pixMorphSequence(pixs, "e3.3", 0); - else - pixt = pixMorphSequence(pixs, "d3.3", 0); - pixXor(pixt, pixt, pixs); - pta = ptaGetPixelsFromPix(pixt, NULL); - - pixDestroy(&pixt); - return pta; -} - - -/*! - * \brief ptaaGetBoundaryPixels() - * - * \param[in] pixs 1 bpp - * \param[in] type L_BOUNDARY_FG, L_BOUNDARY_BG - * \param[in] connectivity 4 or 8 - * \param[out] pboxa [optional] bounding boxes of the c.c. - * \param[out] ppixa [optional] pixa of the c.c. - * \return ptaa, or NULL on error - * - *- * Notes: - * (1) This generates a ptaa of either fg or bg boundary pixels, - * where each pta has the boundary pixels for a connected - * component. - * (2) We can't simply find all the boundary pixels and then select - * those within the bounding box of each component, because - * bounding boxes can overlap. It is necessary to extract and - * dilate or erode each component separately. Note also that - * special handling is required for bg pixels when the - * component touches the pix boundary. - *- */ -PTAA * -ptaaGetBoundaryPixels(PIX *pixs, - l_int32 type, - l_int32 connectivity, - BOXA **pboxa, - PIXA **ppixa) -{ -l_int32 i, n, w, h, x, y, bw, bh, left, right, top, bot; -BOXA *boxa; -PIX *pixt1, *pixt2; -PIXA *pixa; -PTA *pta1, *pta2; -PTAA *ptaa; - - PROCNAME("ptaaGetBoundaryPixels"); - - if (pboxa) *pboxa = NULL; - if (ppixa) *ppixa = NULL; - if (!pixs || (pixGetDepth(pixs) != 1)) - return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); - if (type != L_BOUNDARY_FG && type != L_BOUNDARY_BG) - return (PTAA *)ERROR_PTR("invalid type", procName, NULL); - if (connectivity != 4 && connectivity != 8) - return (PTAA *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - boxa = pixConnComp(pixs, &pixa, connectivity); - n = boxaGetCount(boxa); - ptaa = ptaaCreate(0); - for (i = 0; i < n; i++) { - pixt1 = pixaGetPix(pixa, i, L_CLONE); - boxaGetBoxGeometry(boxa, i, &x, &y, &bw, &bh); - left = right = top = bot = 0; - if (type == L_BOUNDARY_BG) { - if (x > 0) left = 1; - if (y > 0) top = 1; - if (x + bw < w) right = 1; - if (y + bh < h) bot = 1; - pixt2 = pixAddBorderGeneral(pixt1, left, right, top, bot, 0); - } else { - pixt2 = pixClone(pixt1); - } - pta1 = ptaGetBoundaryPixels(pixt2, type); - pta2 = ptaTransform(pta1, x - left, y - top, 1.0, 1.0); - ptaaAddPta(ptaa, pta2, L_INSERT); - ptaDestroy(&pta1); - pixDestroy(&pixt1); - pixDestroy(&pixt2); - } - - if (pboxa) - *pboxa = boxa; - else - boxaDestroy(&boxa); - if (ppixa) - *ppixa = pixa; - else - pixaDestroy(&pixa); - return ptaa; -} - - -/*! - * \brief ptaaIndexLabeledPixels() - * - * \param[in] pixs 32 bpp, of indices of c.c. - * \param[out] pncc [optional] number of connected components - * \return ptaa, or NULL on error - * - *- * Notes: - * (1) The pixel values in %pixs are the index of the connected component - * to which the pixel belongs; %pixs is typically generated from - * a 1 bpp pix by pixConnCompTransform(). Background pixels in - * the generating 1 bpp pix are represented in %pixs by 0. - * We do not check that the pixel values are correctly labelled. - * (2) Each pta in the returned ptaa gives the pixel locations - * correspnding to a connected component, with the label of each - * given by the index of the pta into the ptaa. - * (3) Initialize with the first pta in ptaa being empty and - * representing the background value (index 0) in the pix. - *- */ -PTAA * -ptaaIndexLabeledPixels(PIX *pixs, - l_int32 *pncc) -{ -l_int32 wpl, index, i, j, w, h; -l_uint32 maxval; -l_uint32 *data, *line; -PTA *pta; -PTAA *ptaa; - - PROCNAME("ptaaIndexLabeledPixels"); - - if (pncc) *pncc = 0; - if (!pixs || (pixGetDepth(pixs) != 32)) - return (PTAA *)ERROR_PTR("pixs undef or not 32 bpp", procName, NULL); - - /* The number of c.c. is the maximum pixel value. Use this to - * initialize ptaa with sufficient pta arrays */ - pixGetMaxValueInRect(pixs, NULL, &maxval, NULL, NULL); - if (pncc) *pncc = maxval; - pta = ptaCreate(1); - ptaa = ptaaCreate(maxval + 1); - ptaaInitFull(ptaa, pta); - ptaDestroy(&pta); - - /* Sweep over %pixs, saving the pixel coordinates of each pixel - * with nonzero value in the appropriate pta, indexed by that value. */ - pixGetDimensions(pixs, &w, &h, NULL); - data = pixGetData(pixs); - wpl = pixGetWpl(pixs); - for (i = 0; i < h; i++) { - line = data + wpl * i; - for (j = 0; j < w; j++) { - index = line[j]; - if (index > 0) - ptaaAddPt(ptaa, index, j, i); - } - } - - return ptaa; -} - - -/*! - * \brief ptaGetNeighborPixLocs() - * - * \param[in] pixs any depth - * \param[in] x, y pixel from which we search for nearest neighbors - * \param[in] conn 4 or 8 connectivity - * \return pta, or NULL on error - * - *- * Notes: - * (1) Generates a pta of all valid neighbor pixel locations, - * or NULL on error. - *- */ -PTA * -ptaGetNeighborPixLocs(PIX *pixs, - l_int32 x, - l_int32 y, - l_int32 conn) -{ -l_int32 w, h; -PTA *pta; - - PROCNAME("ptaGetNeighborPixLocs"); - - if (!pixs) - return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); - pixGetDimensions(pixs, &w, &h, NULL); - if (x < 0 || x >= w || y < 0 || y >= h) - return (PTA *)ERROR_PTR("(x,y) not in pixs", procName, NULL); - if (conn != 4 && conn != 8) - return (PTA *)ERROR_PTR("conn not 4 or 8", procName, NULL); - - pta = ptaCreate(conn); - if (x > 0) - ptaAddPt(pta, x - 1, y); - if (x < w - 1) - ptaAddPt(pta, x + 1, y); - if (y > 0) - ptaAddPt(pta, x, y - 1); - if (y < h - 1) - ptaAddPt(pta, x, y + 1); - if (conn == 8) { - if (x > 0) { - if (y > 0) - ptaAddPt(pta, x - 1, y - 1); - if (y < h - 1) - ptaAddPt(pta, x - 1, y + 1); - } - if (x < w - 1) { - if (y > 0) - ptaAddPt(pta, x + 1, y - 1); - if (y < h - 1) - ptaAddPt(pta, x + 1, y + 1); - } - } - - return pta; -} - - -/*---------------------------------------------------------------------* - * Interconversion with Numa * - *---------------------------------------------------------------------*/ -/*! - * \brief numaConvertToPta1() - * - * \param[in] na numa with implicit y(x) - * \return pta if OK; null on error - */ -PTA * -numaConvertToPta1(NUMA *na) -{ -l_int32 i, n; -l_float32 startx, delx, val; -PTA *pta; - - PROCNAME("numaConvertToPta1"); - - if (!na) - return (PTA *)ERROR_PTR("na not defined", procName, NULL); - - n = numaGetCount(na); - pta = ptaCreate(n); - numaGetParameters(na, &startx, &delx); - for (i = 0; i < n; i++) { - numaGetFValue(na, i, &val); - ptaAddPt(pta, startx + i * delx, val); - } - return pta; -} - - -/*! - * \brief numaConvertToPta2() - * - * \param[in] nax - * \param[in] nay - * \return pta if OK; null on error - */ -PTA * -numaConvertToPta2(NUMA *nax, - NUMA *nay) -{ -l_int32 i, n, nx, ny; -l_float32 valx, valy; -PTA *pta; - - PROCNAME("numaConvertToPta2"); - - if (!nax || !nay) - return (PTA *)ERROR_PTR("nax and nay not both defined", procName, NULL); - - nx = numaGetCount(nax); - ny = numaGetCount(nay); - n = L_MIN(nx, ny); - if (nx != ny) - L_WARNING("nx = %d does not equal ny = %d\n", procName, nx, ny); - pta = ptaCreate(n); - for (i = 0; i < n; i++) { - numaGetFValue(nax, i, &valx); - numaGetFValue(nay, i, &valy); - ptaAddPt(pta, valx, valy); - } - return pta; -} - - -/*! - * \brief ptaConvertToNuma() - * - * \param[in] pta - * \param[out] pnax addr of nax - * \param[out] pnay addr of nay - * \return 0 if OK, 1 on error - */ -l_ok -ptaConvertToNuma(PTA *pta, - NUMA **pnax, - NUMA **pnay) -{ -l_int32 i, n; -l_float32 valx, valy; - - PROCNAME("ptaConvertToNuma"); - - if (pnax) *pnax = NULL; - if (pnay) *pnay = NULL; - if (!pnax || !pnay) - return ERROR_INT("&nax and &nay not both defined", procName, 1); - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - - n = ptaGetCount(pta); - *pnax = numaCreate(n); - *pnay = numaCreate(n); - for (i = 0; i < n; i++) { - ptaGetPt(pta, i, &valx, &valy); - numaAddNumber(*pnax, valx); - numaAddNumber(*pnay, valy); - } - return 0; -} - - -/*---------------------------------------------------------------------* - * Display Pta and Ptaa * - *---------------------------------------------------------------------*/ -/*! - * \brief pixDisplayPta() - * - * \param[in] pixd can be same as pixs or NULL; 32 bpp if in-place - * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp - * \param[in] pta of path to be plotted - * \return pixd 32 bpp RGB version of pixs, with path in green. - * - *- * Notes: - * (1) To write on an existing pixs, pixs must be 32 bpp and - * call with pixd == pixs: - * pixDisplayPta(pixs, pixs, pta); - * To write to a new pix, use pixd == NULL and call: - * pixd = pixDisplayPta(NULL, pixs, pta); - * (2) On error, returns pixd to avoid losing pixs if called as - * pixs = pixDisplayPta(pixs, pixs, pta); - *- */ -PIX * -pixDisplayPta(PIX *pixd, - PIX *pixs, - PTA *pta) -{ -l_int32 i, n, w, h, x, y; -l_uint32 rpixel, gpixel, bpixel; - - PROCNAME("pixDisplayPta"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (!pta) - return (PIX *)ERROR_PTR("pta not defined", procName, pixd); - if (pixd && (pixd != pixs || pixGetDepth(pixd) != 32)) - return (PIX *)ERROR_PTR("invalid pixd", procName, pixd); - - if (!pixd) - pixd = pixConvertTo32(pixs); - pixGetDimensions(pixd, &w, &h, NULL); - composeRGBPixel(255, 0, 0, &rpixel); /* start point */ - composeRGBPixel(0, 255, 0, &gpixel); - composeRGBPixel(0, 0, 255, &bpixel); /* end point */ - - n = ptaGetCount(pta); - for (i = 0; i < n; i++) { - ptaGetIPt(pta, i, &x, &y); - if (x < 0 || x >= w || y < 0 || y >= h) - continue; - if (i == 0) - pixSetPixel(pixd, x, y, rpixel); - else if (i < n - 1) - pixSetPixel(pixd, x, y, gpixel); - else - pixSetPixel(pixd, x, y, bpixel); - } - - return pixd; -} - - -/*! - * \brief pixDisplayPtaaPattern() - * - * \param[in] pixd 32 bpp - * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp; 32 bpp if in place - * \param[in] ptaa giving locations at which the pattern is displayed - * \param[in] pixp 1 bpp pattern to be placed such that its reference - * point co-locates with each point in pta - * \param[in] cx, cy reference point in pattern - * \return pixd 32 bpp RGB version of pixs. - * - *- * Notes: - * (1) To write on an existing pixs, pixs must be 32 bpp and - * call with pixd == pixs: - * pixDisplayPtaPattern(pixs, pixs, pta, ...); - * To write to a new pix, use pixd == NULL and call: - * pixd = pixDisplayPtaPattern(NULL, pixs, pta, ...); - * (2) Puts a random color on each pattern associated with a pta. - * (3) On error, returns pixd to avoid losing pixs if called as - * pixs = pixDisplayPtaPattern(pixs, pixs, pta, ...); - * (4) A typical pattern to be used is a circle, generated with - * generatePtaFilledCircle() - *- */ -PIX * -pixDisplayPtaaPattern(PIX *pixd, - PIX *pixs, - PTAA *ptaa, - PIX *pixp, - l_int32 cx, - l_int32 cy) -{ -l_int32 i, n; -l_uint32 color; -PIXCMAP *cmap; -PTA *pta; - - PROCNAME("pixDisplayPtaaPattern"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (!ptaa) - return (PIX *)ERROR_PTR("ptaa not defined", procName, pixd); - if (pixd && (pixd != pixs || pixGetDepth(pixd) != 32)) - return (PIX *)ERROR_PTR("invalid pixd", procName, pixd); - if (!pixp) - return (PIX *)ERROR_PTR("pixp not defined", procName, pixd); - - if (!pixd) - pixd = pixConvertTo32(pixs); - - /* Use 256 random colors */ - cmap = pixcmapCreateRandom(8, 0, 0); - n = ptaaGetCount(ptaa); - for (i = 0; i < n; i++) { - pixcmapGetColor32(cmap, i % 256, &color); - pta = ptaaGetPta(ptaa, i, L_CLONE); - pixDisplayPtaPattern(pixd, pixd, pta, pixp, cx, cy, color); - ptaDestroy(&pta); - } - - pixcmapDestroy(&cmap); - return pixd; -} - - -/*! - * \brief pixDisplayPtaPattern() - * - * \param[in] pixd can be same as pixs or NULL; 32 bpp if in-place - * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp - * \param[in] pta giving locations at which the pattern is displayed - * \param[in] pixp 1 bpp pattern to be placed such that its reference - * point co-locates with each point in pta - * \param[in] cx, cy reference point in pattern - * \param[in] color in 0xrrggbb00 format - * \return pixd 32 bpp RGB version of pixs. - * - *- * Notes: - * (1) To write on an existing pixs, pixs must be 32 bpp and - * call with pixd == pixs: - * pixDisplayPtaPattern(pixs, pixs, pta, ...); - * To write to a new pix, use pixd == NULL and call: - * pixd = pixDisplayPtaPattern(NULL, pixs, pta, ...); - * (2) On error, returns pixd to avoid losing pixs if called as - * pixs = pixDisplayPtaPattern(pixs, pixs, pta, ...); - * (3) A typical pattern to be used is a circle, generated with - * generatePtaFilledCircle() - *- */ -PIX * -pixDisplayPtaPattern(PIX *pixd, - PIX *pixs, - PTA *pta, - PIX *pixp, - l_int32 cx, - l_int32 cy, - l_uint32 color) -{ -l_int32 i, n, w, h, x, y; -PTA *ptat; - - PROCNAME("pixDisplayPtaPattern"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); - if (!pta) - return (PIX *)ERROR_PTR("pta not defined", procName, pixd); - if (pixd && (pixd != pixs || pixGetDepth(pixd) != 32)) - return (PIX *)ERROR_PTR("invalid pixd", procName, pixd); - if (!pixp) - return (PIX *)ERROR_PTR("pixp not defined", procName, pixd); - - if (!pixd) - pixd = pixConvertTo32(pixs); - pixGetDimensions(pixs, &w, &h, NULL); - ptat = ptaReplicatePattern(pta, pixp, NULL, cx, cy, w, h); - - n = ptaGetCount(ptat); - for (i = 0; i < n; i++) { - ptaGetIPt(ptat, i, &x, &y); - if (x < 0 || x >= w || y < 0 || y >= h) - continue; - pixSetPixel(pixd, x, y, color); - } - - ptaDestroy(&ptat); - return pixd; -} - - -/*! - * \brief ptaReplicatePattern() - * - * \param[in] ptas "sparse" input pta - * \param[in] pixp [optional] 1 bpp pattern, to be replicated - * in output pta - * \param[in] ptap [optional] set of pts, to be replicated in output pta - * \param[in] cx, cy reference point in pattern - * \param[in] w, h clipping sizes for output pta - * \return ptad with all points of replicated pattern, or NULL on error - * - *- * Notes: - * (1) You can use either the image %pixp or the set of pts %ptap. - * (2) The pattern is placed with its reference point at each point - * in ptas, and all the fg pixels are colleced into ptad. - * For %pixp, this is equivalent to blitting pixp at each point - * in ptas, and then converting the resulting pix to a pta. - *- */ -PTA * -ptaReplicatePattern(PTA *ptas, - PIX *pixp, - PTA *ptap, - l_int32 cx, - l_int32 cy, - l_int32 w, - l_int32 h) -{ -l_int32 i, j, n, np, x, y, xp, yp, xf, yf; -PTA *ptat, *ptad; - - PROCNAME("ptaReplicatePattern"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - if (!pixp && !ptap) - return (PTA *)ERROR_PTR("no pattern is defined", procName, NULL); - if (pixp && ptap) - L_WARNING("pixp and ptap defined; using ptap\n", procName); - - n = ptaGetCount(ptas); - ptad = ptaCreate(n); - if (ptap) - ptat = ptaClone(ptap); - else - ptat = ptaGetPixelsFromPix(pixp, NULL); - np = ptaGetCount(ptat); - for (i = 0; i < n; i++) { - ptaGetIPt(ptas, i, &x, &y); - for (j = 0; j < np; j++) { - ptaGetIPt(ptat, j, &xp, &yp); - xf = x - cx + xp; - yf = y - cy + yp; - if (xf >= 0 && xf < w && yf >= 0 && yf < h) - ptaAddPt(ptad, xf, yf); - } - } - - ptaDestroy(&ptat); - return ptad; -} - - -/*! - * \brief pixDisplayPtaa() - * - * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp - * \param[in] ptaa array of paths to be plotted - * \return pixd 32 bpp RGB version of pixs, with paths plotted - * in different colors, or NULL on error - */ -PIX * -pixDisplayPtaa(PIX *pixs, - PTAA *ptaa) -{ -l_int32 i, j, w, h, npta, npt, x, y, rv, gv, bv; -l_uint32 *pixela; -NUMA *na1, *na2, *na3; -PIX *pixd; -PTA *pta; - - PROCNAME("pixDisplayPtaa"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (!ptaa) - return (PIX *)ERROR_PTR("ptaa not defined", procName, NULL); - npta = ptaaGetCount(ptaa); - if (npta == 0) - return (PIX *)ERROR_PTR("no pta", procName, NULL); - - if ((pixd = pixConvertTo32(pixs)) == NULL) - return (PIX *)ERROR_PTR("pixd not made", procName, NULL); - pixGetDimensions(pixd, &w, &h, NULL); - - /* Make a colormap for the paths */ - if ((pixela = (l_uint32 *)LEPT_CALLOC(npta, sizeof(l_uint32))) == NULL) { - pixDestroy(&pixd); - return (PIX *)ERROR_PTR("calloc fail for pixela", procName, NULL); - } - na1 = numaPseudorandomSequence(256, 14657); - na2 = numaPseudorandomSequence(256, 34631); - na3 = numaPseudorandomSequence(256, 54617); - for (i = 0; i < npta; i++) { - numaGetIValue(na1, i % 256, &rv); - numaGetIValue(na2, i % 256, &gv); - numaGetIValue(na3, i % 256, &bv); - composeRGBPixel(rv, gv, bv, &pixela[i]); - } - numaDestroy(&na1); - numaDestroy(&na2); - numaDestroy(&na3); - - for (i = 0; i < npta; i++) { - pta = ptaaGetPta(ptaa, i, L_CLONE); - npt = ptaGetCount(pta); - for (j = 0; j < npt; j++) { - ptaGetIPt(pta, j, &x, &y); - if (x < 0 || x >= w || y < 0 || y >= h) - continue; - pixSetPixel(pixd, x, y, pixela[i]); - } - ptaDestroy(&pta); - } - - LEPT_FREE(pixela); - return pixd; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ptafunc2.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ptafunc2.c deleted file mode 100644 index 1949a6cf..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ptafunc2.c +++ /dev/null @@ -1,899 +0,0 @@ -/*====================================================================* - - 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 ptafunc2.c - *- * - * -------------------------------------- - * This file has these Pta utilities: - * - sorting - * - ordered set operations - * - hash map operations - * -------------------------------------- - * - * Sorting - * PTA *ptaSort() - * l_int32 ptaGetSortIndex() - * PTA *ptaSortByIndex() - * PTAA *ptaaSortByIndex() - * l_int32 ptaGetRankValue() - * PTA *ptaSort2d() - * l_int32 ptaEqual() - * - * Set operations using aset (rbtree) - * PTA *ptaUnionByAset() - * PTA *ptaRemoveDupsByAset() - * PTA *ptaIntersectionByAset() - * L_ASET *l_asetCreateFromPta() - * - * Set operations using hashing (dnahash) - * PTA *ptaUnionByHash() - * l_int32 ptaRemoveDupsByHash() - * PTA *ptaIntersectionByHash(); - * l_int32 ptaFindPtByHash() - * L_DNAHASH *l_dnaHashCreateFromPta() - * - * - * We have two implementations of set operations on an array of points: - * - * (1) Using an underlying tree (rbtree) - * This uses a good 64 bit hashing function for the key, - * that is not expected to have hash collisions (and we do - * not test for them). The tree is built up of the hash - * values, and if the hash is found in the tree, it is - * assumed that the point has already been found. - * - * (2) Using an underlying hashing of the keys (dnahash) - * This uses a fast 64 bit hashing function for the key, - * which is then hashed into a bucket (a dna in a dnaHash). - * Because hash collisions can occur, the index into the - * pta for the point that gave rise to that key is stored, - * and the dna (bucket) is traversed, using the stored indices - * to determine if that point had already been seen. - * - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include "allheaders.h" - -/*---------------------------------------------------------------------* - * Sorting * - *---------------------------------------------------------------------*/ -/*! - * \brief ptaSort() - * - * \param[in] ptas - * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y - * \param[in] sortorder L_SORT_INCREASING, L_SORT_DECREASING - * \param[out] pnaindex [optional] index of sorted order into - * original array - * \return ptad sorted version of ptas, or NULL on error - */ -PTA * -ptaSort(PTA *ptas, - l_int32 sorttype, - l_int32 sortorder, - NUMA **pnaindex) -{ -PTA *ptad; -NUMA *naindex; - - PROCNAME("ptaSort"); - - if (pnaindex) *pnaindex = NULL; - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y) - return (PTA *)ERROR_PTR("invalid sort type", procName, NULL); - if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) - return (PTA *)ERROR_PTR("invalid sort order", procName, NULL); - - if (ptaGetSortIndex(ptas, sorttype, sortorder, &naindex) != 0) - return (PTA *)ERROR_PTR("naindex not made", procName, NULL); - - ptad = ptaSortByIndex(ptas, naindex); - if (pnaindex) - *pnaindex = naindex; - else - numaDestroy(&naindex); - if (!ptad) - return (PTA *)ERROR_PTR("ptad not made", procName, NULL); - return ptad; -} - - -/*! - * \brief ptaGetSortIndex() - * - * \param[in] ptas - * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y - * \param[in] sortorder L_SORT_INCREASING, L_SORT_DECREASING - * \param[out] pnaindex index of sorted order into original array - * \return 0 if OK, 1 on error - */ -l_ok -ptaGetSortIndex(PTA *ptas, - l_int32 sorttype, - l_int32 sortorder, - NUMA **pnaindex) -{ -l_int32 i, n; -l_float32 x, y; -NUMA *na, *nai; - - PROCNAME("ptaGetSortIndex"); - - if (!pnaindex) - return ERROR_INT("&naindex not defined", procName, 1); - *pnaindex = NULL; - if (!ptas) - return ERROR_INT("ptas not defined", procName, 1); - if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y) - return ERROR_INT("invalid sort type", procName, 1); - if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) - return ERROR_INT("invalid sort order", procName, 1); - - /* Build up numa of specific data */ - n = ptaGetCount(ptas); - if ((na = numaCreate(n)) == NULL) - return ERROR_INT("na not made", procName, 1); - for (i = 0; i < n; i++) { - ptaGetPt(ptas, i, &x, &y); - if (sorttype == L_SORT_BY_X) - numaAddNumber(na, x); - else - numaAddNumber(na, y); - } - - /* Get the sort index for data array */ - nai = numaGetSortIndex(na, sortorder); - numaDestroy(&na); - if (!nai) - return ERROR_INT("naindex not made", procName, 1); - *pnaindex = nai; - return 0; -} - - -/*! - * \brief ptaSortByIndex() - * - * \param[in] ptas - * \param[in] naindex na that maps from the new pta to the input pta - * \return ptad sorted, or NULL on error - */ -PTA * -ptaSortByIndex(PTA *ptas, - NUMA *naindex) -{ -l_int32 i, index, n; -l_float32 x, y; -PTA *ptad; - - PROCNAME("ptaSortByIndex"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - if (!naindex) - return (PTA *)ERROR_PTR("naindex not defined", procName, NULL); - - /* Build up sorted pta using sort index */ - n = numaGetCount(naindex); - if ((ptad = ptaCreate(n)) == NULL) - return (PTA *)ERROR_PTR("ptad not made", procName, NULL); - for (i = 0; i < n; i++) { - numaGetIValue(naindex, i, &index); - ptaGetPt(ptas, index, &x, &y); - ptaAddPt(ptad, x, y); - } - - return ptad; -} - - -/*! - * \brief ptaaSortByIndex() - * - * \param[in] ptaas - * \param[in] naindex na that maps from the new ptaa to the input ptaa - * \return ptaad sorted, or NULL on error - */ -PTAA * -ptaaSortByIndex(PTAA *ptaas, - NUMA *naindex) -{ -l_int32 i, n, index; -PTA *pta; -PTAA *ptaad; - - PROCNAME("ptaaSortByIndex"); - - if (!ptaas) - return (PTAA *)ERROR_PTR("ptaas not defined", procName, NULL); - if (!naindex) - return (PTAA *)ERROR_PTR("naindex not defined", procName, NULL); - - n = ptaaGetCount(ptaas); - if (numaGetCount(naindex) != n) - return (PTAA *)ERROR_PTR("numa and ptaa sizes differ", procName, NULL); - ptaad = ptaaCreate(n); - for (i = 0; i < n; i++) { - numaGetIValue(naindex, i, &index); - pta = ptaaGetPta(ptaas, index, L_COPY); - ptaaAddPta(ptaad, pta, L_INSERT); - } - - return ptaad; -} - - -/*! - * \brief ptaGetRankValue() - * - * \param[in] pta - * \param[in] fract use 0.0 for smallest, 1.0 for largest - * \param[in] ptasort [optional] version of %pta sorted by %sorttype - * \param[in] sorttype L_SORT_BY_X, L_SORT_BY_Y - * \param[out] pval rankval: the x or y value at %fract - * \return 0 if OK, 1 on error - */ -l_ok -ptaGetRankValue(PTA *pta, - l_float32 fract, - PTA *ptasort, - l_int32 sorttype, - l_float32 *pval) -{ -l_int32 index, n; -PTA *ptas; - - PROCNAME("ptaGetRankValue"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0.0; - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y) - return ERROR_INT("invalid sort type", procName, 1); - if (fract < 0.0 || fract > 1.0) - return ERROR_INT("fract not in [0.0 ... 1.0]", procName, 1); - if ((n = ptaGetCount(pta)) == 0) - return ERROR_INT("pta empty", procName, 1); - - if (ptasort) - ptas = ptasort; - else - ptas = ptaSort(pta, sorttype, L_SORT_INCREASING, NULL); - - index = (l_int32)(fract * (l_float32)(n - 1) + 0.5); - if (sorttype == L_SORT_BY_X) - ptaGetPt(ptas, index, pval, NULL); - else /* sort by y */ - ptaGetPt(ptas, index, NULL, pval); - - if (!ptasort) ptaDestroy(&ptas); - return 0; -} - - -/*! - * \brief ptaSort2d() - * - * \param[in] ptas - * \return ptad, or NULL on error - * - * - * Notes: - * (1) Sort increasing by row-major, scanning down from the UL corner, - * where for each value of y, order the pts from left to right. - *- */ -PTA * -ptaSort2d(PTA *pta) -{ -l_int32 index, i, j, n, nx, ny, start, end; -l_float32 x, y, yp, val; -NUMA *na1, *na2, *nas, *nax; -PTA *pta1, *ptad; - - PROCNAME("ptaSort2d"); - - if (!pta) - return (PTA *)ERROR_PTR("pta not defined", procName, NULL); - - /* Sort by row-major (y first, then x). After sort by y, - * the x values at the same y are not sorted. */ - pta1 = ptaSort(pta, L_SORT_BY_Y, L_SORT_INCREASING, NULL); - - /* Find start and ending indices with the same y value */ - n = ptaGetCount(pta1); - na1 = numaCreate(0); /* holds start index of sequence with same y */ - na2 = numaCreate(0); /* holds end index of sequence with same y */ - numaAddNumber(na1, 0); - ptaGetPt(pta1, 0, &x, &yp); - for (i = 1; i < n; i++) { - ptaGetPt(pta1, i, &x, &y); - if (y != yp) { - numaAddNumber(na1, i); - numaAddNumber(na2, i - 1); - } - yp = y; - } - numaAddNumber(na2, n - 1); - - /* Sort by increasing x each set with the same y value */ - ptad = ptaCreate(n); - ny = numaGetCount(na1); /* number of distinct y values */ - for (i = 0, index = 0; i < ny; i++) { - numaGetIValue(na1, i, &start); - numaGetIValue(na2, i, &end); - nx = end - start + 1; /* number of points with current y value */ - if (nx == 1) { - ptaGetPt(pta1, index++, &x, &y); - ptaAddPt(ptad, x, y); - } else { - /* More than 1 point; extract and sort the x values */ - nax = numaCreate(nx); - for (j = 0; j < nx; j++) { - ptaGetPt(pta1, index + j, &x, &y); - numaAddNumber(nax, x); - } - nas = numaSort(NULL, nax, L_SORT_INCREASING); - /* Add the points with x sorted */ - for (j = 0; j < nx; j++) { - numaGetFValue(nas, j, &val); - ptaAddPt(ptad, val, y); - } - index += nx; - numaDestroy(&nax); - numaDestroy(&nas); - } - } - numaDestroy(&na1); - numaDestroy(&na2); - ptaDestroy(&pta1); - return ptad; -} - - -/*! - * \brief ptaEqual() - * - * \param[in] pta1 - * \param[in] pta2 - * \param[out] psame 1 if same; 0 if different - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) Equality is defined as having the same set of points, - * independent of the order in which they are presented. - *- */ -l_ok -ptaEqual(PTA *pta1, - PTA *pta2, - l_int32 *psame) -{ -l_int32 i, n1, n2; -l_float32 x1, y1, x2, y2; -PTA *ptas1, *ptas2; - - PROCNAME("ptaEqual"); - - if (!psame) - return ERROR_INT("&same not defined", procName, 1); - *psame = 0.0; - if (!pta1 || !pta2) - return ERROR_INT("pta1 and pta2 not both defined", procName, 1); - - n1 = ptaGetCount(pta1); - n2 = ptaGetCount(pta2); - if (n1 != n2) return 0; - - /* 2d sort each and compare */ - ptas1 = ptaSort2d(pta1); - ptas2 = ptaSort2d(pta2); - for (i = 0; i < n1; i++) { - ptaGetPt(ptas1, i, &x1, &y1); - ptaGetPt(ptas2, i, &x2, &y2); - if (x1 != x2 || y1 != y2) { - ptaDestroy(&ptas1); - ptaDestroy(&ptas2); - return 0; - } - } - - *psame = 1; - ptaDestroy(&ptas1); - ptaDestroy(&ptas2); - return 0; -} - - - - -/*---------------------------------------------------------------------* - * Set operations using aset (rbtree) * - *---------------------------------------------------------------------*/ -/*! - * \brief ptaUnionByAset() - * - * \param[in] pta1, pta2 - * \return ptad with the union of the set of points, or NULL on error - * - *- * Notes: - * (1) See sarrayRemoveDupsByAset() for the approach. - * (2) The key is a 64-bit hash from the (x,y) pair. - * (3) This is slower than ptaUnionByHash(), mostly because of the - * nlogn sort to build up the rbtree. Do not use for large - * numbers of points (say, > 1M). - * (4) The *Aset() functions use the sorted l_Aset, which is just - * an rbtree in disguise. - *- */ -PTA * -ptaUnionByAset(PTA *pta1, - PTA *pta2) -{ -PTA *pta3, *ptad; - - PROCNAME("ptaUnionByAset"); - - if (!pta1) - return (PTA *)ERROR_PTR("pta1 not defined", procName, NULL); - if (!pta2) - return (PTA *)ERROR_PTR("pta2 not defined", procName, NULL); - - /* Join */ - pta3 = ptaCopy(pta1); - ptaJoin(pta3, pta2, 0, -1); - - /* Eliminate duplicates */ - ptad = ptaRemoveDupsByAset(pta3); - ptaDestroy(&pta3); - return ptad; -} - - -/*! - * \brief ptaRemoveDupsByAset() - * - * \param[in] ptas assumed to be integer values - * \return ptad with duplicates removed, or NULL on error - * - *- * Notes: - * (1) This is slower than ptaRemoveDupsByHash(), mostly because - * of the nlogn sort to build up the rbtree. Do not use for - * large numbers of points (say, > 1M). - *- */ -PTA * -ptaRemoveDupsByAset(PTA *ptas) -{ -l_int32 i, n, x, y; -PTA *ptad; -l_uint64 hash; -L_ASET *set; -RB_TYPE key; - - PROCNAME("ptaRemoveDupsByAset"); - - if (!ptas) - return (PTA *)ERROR_PTR("ptas not defined", procName, NULL); - - set = l_asetCreate(L_UINT_TYPE); - n = ptaGetCount(ptas); - ptad = ptaCreate(n); - for (i = 0; i < n; i++) { - ptaGetIPt(ptas, i, &x, &y); - l_hashPtToUint64(x, y, &hash); - key.utype = hash; - if (!l_asetFind(set, key)) { - ptaAddPt(ptad, x, y); - l_asetInsert(set, key); - } - } - - l_asetDestroy(&set); - return ptad; -} - - -/*! - * \brief ptaIntersectionByAset() - * - * \param[in] pta1, pta2 - * \return ptad intersection of the point sets, or NULL on error - * - *- * Notes: - * (1) See sarrayIntersectionByAset() for the approach. - * (2) The key is a 64-bit hash from the (x,y) pair. - * (3) This is slower than ptaIntersectionByHash(), mostly because - * of the nlogn sort to build up the rbtree. Do not use for - * large numbers of points (say, > 1M). - *- */ -PTA * -ptaIntersectionByAset(PTA *pta1, - PTA *pta2) -{ -l_int32 n1, n2, i, n, x, y; -l_uint64 hash; -L_ASET *set1, *set2; -RB_TYPE key; -PTA *pta_small, *pta_big, *ptad; - - PROCNAME("ptaIntersectionByAset"); - - if (!pta1) - return (PTA *)ERROR_PTR("pta1 not defined", procName, NULL); - if (!pta2) - return (PTA *)ERROR_PTR("pta2 not defined", procName, NULL); - - /* Put the elements of the biggest array into a set */ - n1 = ptaGetCount(pta1); - n2 = ptaGetCount(pta2); - pta_small = (n1 < n2) ? pta1 : pta2; /* do not destroy pta_small */ - pta_big = (n1 < n2) ? pta2 : pta1; /* do not destroy pta_big */ - set1 = l_asetCreateFromPta(pta_big); - - /* Build up the intersection of points */ - ptad = ptaCreate(0); - n = ptaGetCount(pta_small); - set2 = l_asetCreate(L_UINT_TYPE); - for (i = 0; i < n; i++) { - ptaGetIPt(pta_small, i, &x, &y); - l_hashPtToUint64(x, y, &hash); - key.utype = hash; - if (l_asetFind(set1, key) && !l_asetFind(set2, key)) { - ptaAddPt(ptad, x, y); - l_asetInsert(set2, key); - } - } - - l_asetDestroy(&set1); - l_asetDestroy(&set2); - return ptad; -} - - -/*! - * \brief l_asetCreateFromPta() - * - * \param[in] pta - * \return set using a 64-bit hash of (x,y) as the key - */ -L_ASET * -l_asetCreateFromPta(PTA *pta) -{ -l_int32 i, n, x, y; -l_uint64 hash; -L_ASET *set; -RB_TYPE key; - - PROCNAME("l_asetCreateFromPta"); - - if (!pta) - return (L_ASET *)ERROR_PTR("pta not defined", procName, NULL); - - set = l_asetCreate(L_UINT_TYPE); - n = ptaGetCount(pta); - for (i = 0; i < n; i++) { - ptaGetIPt(pta, i, &x, &y); - l_hashPtToUint64(x, y, &hash); - key.utype = hash; - l_asetInsert(set, key); - } - - return set; -} - - -/*---------------------------------------------------------------------* - * Set operations using hashing (rbtree) * - *---------------------------------------------------------------------*/ -/*! - * \brief ptaUnionByHash() - * - * \param[in] pta1, pta2 - * \return ptad with the union of the set of points, or NULL on error - * - *- * Notes: - * (1) This is faster than ptaUnionByAset(), because the - * bucket lookup is O(n). It should be used if the pts are - * integers (e.g., representing pixel positions). - *- */ -PTA * -ptaUnionByHash(PTA *pta1, - PTA *pta2) -{ -PTA *pta3, *ptad; - - PROCNAME("ptaUnionByHash"); - - if (!pta1) - return (PTA *)ERROR_PTR("pta1 not defined", procName, NULL); - if (!pta2) - return (PTA *)ERROR_PTR("pta2 not defined", procName, NULL); - - /* Join */ - pta3 = ptaCopy(pta1); - ptaJoin(pta3, pta2, 0, -1); - - /* Eliminate duplicates */ - ptaRemoveDupsByHash(pta3, &ptad, NULL); - ptaDestroy(&pta3); - return ptad; -} - - -/*! - * \brief ptaRemoveDupsByHash() - * - * \param[in] ptas assumed to be integer values - * \param[out] pptad unique set of pts; duplicates removed - * \param[out] pdahash [optional] dnahash used for lookup - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Generates a pta with unique values. - * (2) The dnahash is built up with ptad to assure uniqueness. - * It can be used to find if a point is in the set: - * ptaFindPtByHash(ptad, dahash, x, y, &index) - * (3) The hash of the (x,y) location is simple and fast. It scales - * up with the number of buckets to insure a fairly random - * bucket selection for adjacent points. - * (4) A Dna is used rather than a Numa because we need accurate - * representation of 32-bit integers that are indices into ptas. - * Integer --> float --> integer conversion makes errors for - * integers larger than 10M. - * (5) This is faster than ptaRemoveDupsByAset(), because the - * bucket lookup is O(n), although there is a double-loop - * lookup within the dna in each bucket. - *- */ -l_ok -ptaRemoveDupsByHash(PTA *ptas, - PTA **pptad, - L_DNAHASH **pdahash) -{ -l_int32 i, n, index, items, x, y; -l_uint32 nsize; -l_uint64 key; -PTA *ptad; -L_DNAHASH *dahash; - - PROCNAME("ptaRemoveDupsByHash"); - - if (pdahash) *pdahash = NULL; - if (!pptad) - return ERROR_INT("&ptad not defined", procName, 1); - *pptad = NULL; - if (!ptas) - return ERROR_INT("ptas not defined", procName, 1); - - n = ptaGetCount(ptas); - findNextLargerPrime(n / 20, &nsize); /* buckets in hash table */ - dahash = l_dnaHashCreate(nsize, 8); - ptad = ptaCreate(n); - *pptad = ptad; - for (i = 0, items = 0; i < n; i++) { - ptaGetIPt(ptas, i, &x, &y); - ptaFindPtByHash(ptad, dahash, x, y, &index); - if (index < 0) { /* not found */ - l_hashPtToUint64(x, y, &key); - l_dnaHashAdd(dahash, key, (l_float64)items); - ptaAddPt(ptad, x, y); - items++; - } - } - - if (pdahash) - *pdahash = dahash; - else - l_dnaHashDestroy(&dahash); - return 0; -} - - -/*! - * \brief ptaIntersectionByHash() - * - * \param[in] pta1, pta2 - * \return ptad intersection of the point sets, or NULL on error - * - *- * Notes: - * (1) This is faster than ptaIntersectionByAset(), because the - * bucket lookup is O(n). It should be used if the pts are - * integers (e.g., representing pixel positions). - *- */ -PTA * -ptaIntersectionByHash(PTA *pta1, - PTA *pta2) -{ -l_int32 n1, n2, nsmall, i, x, y, index1, index2; -l_uint32 nsize2; -l_uint64 key; -L_DNAHASH *dahash1, *dahash2; -PTA *pta_small, *pta_big, *ptad; - - PROCNAME("ptaIntersectionByHash"); - - if (!pta1) - return (PTA *)ERROR_PTR("pta1 not defined", procName, NULL); - if (!pta2) - return (PTA *)ERROR_PTR("pta2 not defined", procName, NULL); - - /* Put the elements of the biggest pta into a dnahash */ - n1 = ptaGetCount(pta1); - n2 = ptaGetCount(pta2); - pta_small = (n1 < n2) ? pta1 : pta2; /* do not destroy pta_small */ - pta_big = (n1 < n2) ? pta2 : pta1; /* do not destroy pta_big */ - dahash1 = l_dnaHashCreateFromPta(pta_big); - - /* Build up the intersection of points. Add to ptad - * if the point is in pta_big (using dahash1) but hasn't - * yet been seen in the traversal of pta_small (using dahash2). */ - ptad = ptaCreate(0); - nsmall = ptaGetCount(pta_small); - findNextLargerPrime(nsmall / 20, &nsize2); /* buckets in hash table */ - dahash2 = l_dnaHashCreate(nsize2, 0); - for (i = 0; i < nsmall; i++) { - ptaGetIPt(pta_small, i, &x, &y); - ptaFindPtByHash(pta_big, dahash1, x, y, &index1); - if (index1 >= 0) { /* found */ - ptaFindPtByHash(pta_small, dahash2, x, y, &index2); - if (index2 == -1) { /* not found */ - ptaAddPt(ptad, x, y); - l_hashPtToUint64(x, y, &key); - l_dnaHashAdd(dahash2, key, (l_float64)i); - } - } - } - - l_dnaHashDestroy(&dahash1); - l_dnaHashDestroy(&dahash2); - return ptad; -} - - -/*! - * \brief ptaFindPtByHash() - * - * \param[in] pta - * \param[in] dahash built from pta - * \param[in] x, y arbitrary points - * \param[out] pindex index into pta if (x,y) is in pta; -1 otherwise - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) Fast lookup in dnaHash associated with a pta, to see if a - * random point (x,y) is already stored in the hash table. - * (2) We use a strong hash function to minimize the chance that - * two different points hash to the same key value. - * (3) We select the number of buckets to be about 5% of the size - * of the input %pta, so that when fully populated, each - * bucket (dna) will have about 20 entries, each being an index - * into %pta. In lookup, after hashing to the key, and then - * again to the bucket, we traverse the bucket (dna), using the - * index into %pta to check if the point (x,y) has been found before. - *- */ -l_ok -ptaFindPtByHash(PTA *pta, - L_DNAHASH *dahash, - l_int32 x, - l_int32 y, - l_int32 *pindex) -{ -l_int32 i, nvals, index, xi, yi; -l_uint64 key; -L_DNA *da; - - PROCNAME("ptaFindPtByHash"); - - if (!pindex) - return ERROR_INT("&index not defined", procName, 1); - *pindex = -1; - if (!pta) - return ERROR_INT("pta not defined", procName, 1); - if (!dahash) - return ERROR_INT("dahash not defined", procName, 1); - - l_hashPtToUint64(x, y, &key); - da = l_dnaHashGetDna(dahash, key, L_NOCOPY); - if (!da) return 0; - - /* Run through the da, looking for this point */ - nvals = l_dnaGetCount(da); - for (i = 0; i < nvals; i++) { - l_dnaGetIValue(da, i, &index); - ptaGetIPt(pta, index, &xi, &yi); - if (x == xi && y == yi) { - *pindex = index; - return 0; - } - } - - return 0; -} - - -/*! - * \brief l_dnaHashCreateFromPta() - * - * \param[in] pta - * \return dahash, or NULL on error - */ -L_DNAHASH * -l_dnaHashCreateFromPta(PTA *pta) -{ -l_int32 i, n, x, y; -l_uint32 nsize; -l_uint64 key; -L_DNAHASH *dahash; - - PROCNAME("l_dnaHashCreateFromPta"); - - if (!pta) - return (L_DNAHASH *)ERROR_PTR("pta not defined", procName, NULL); - - /* Build up dnaHash of indices, hashed by a key that is - * a large linear combination of x and y values designed to - * randomize the key. Having about 20 pts in each bucket is - * roughly optimal for speed for large sets. */ - n = ptaGetCount(pta); - findNextLargerPrime(n / 20, &nsize); /* buckets in hash table */ -/* lept_stderr("Prime used: %d\n", nsize); */ - - /* Add each point, using the hash as key and the index into - * %ptas as the value. Storing the index enables operations - * that check for duplicates. */ - dahash = l_dnaHashCreate(nsize, 8); - for (i = 0; i < n; i++) { - ptaGetIPt(pta, i, &x, &y); - l_hashPtToUint64(x, y, &key); - l_dnaHashAdd(dahash, key, (l_float64)i); - } - - return dahash; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ptra.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ptra.c deleted file mode 100644 index 5a04631e..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ptra.c +++ /dev/null @@ -1,1009 +0,0 @@ -/*====================================================================* - - 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 ptra.c - *- * - * Ptra creation and destruction - * L_PTRA *ptraCreate() - * void *ptraDestroy() - * - * Add/insert/remove/replace generic ptr object - * l_int32 ptraAdd() - * static l_int32 ptraExtendArray() - * l_int32 ptraInsert() - * void *ptraRemove() - * void *ptraRemoveLast() - * void *ptraReplace() - * l_int32 ptraSwap() - * l_int32 ptraCompactArray() - * - * Other array operations - * l_int32 ptraReverse() - * l_int32 ptraJoin() - * - * Simple Ptra accessors - * l_int32 ptraGetMaxIndex() - * l_int32 ptraGetActualCount() - * void *ptraGetPtrToItem() - * - * Ptraa creation and destruction - * L_PTRAA *ptraaCreate() - * void *ptraaDestroy() - * - * Ptraa accessors - * l_int32 ptraaGetSize() - * l_int32 ptraaInsertPtra() - * L_PTRA *ptraaGetPtra() - * - * Ptraa conversion - * L_PTRA *ptraaFlattenToPtra() - * - * Notes on the Ptra: - * - * (1) The Ptra is a struct, not an array. Always use the accessors - * in this file, never the fields directly. - * (2) Items can be placed anywhere in the allocated ptr array, - * including one index beyond the last ptr (in which case the - * ptr array is realloc'd). - * (3) Thus, the items on the ptr array need not be compacted. In - * general there will be null pointers in the ptr array. - * (4) A compacted array will remain compacted on removal if - * arbitrary items are removed with compaction, or if items - * are removed from the end of the array. - * (5) For addition to and removal from the end of the array, this - * functions exactly like a stack, and with the same O(1) cost. - * (6) This differs from the generic stack in that we allow - * random access for insertion, removal and replacement. - * Removal can be done without compacting the array. - * Insertion into a null ptr in the array has no effect on - * the other pointers, but insertion into a location already - * occupied by an item has a cost proportional to the - * distance to the next null ptr in the array. - * (7) Null ptrs are valid input args for both insertion and - * replacement; this allows arbitrary swapping. - * (8) The item in the array with the largest index is at pa->imax. - * This can be any value from -1 (initialized; all array ptrs - * are null) up to pa->nalloc - 1 (the last ptr in the array). - * (9) In referring to the array: the first ptr is the "top" or - * "beginning"; the last pointer is the "bottom" or "end"; - * items are shifted "up" towards the top when compaction occurs; - * and items are shifted "down" towards the bottom when forced to - * move due to an insertion. - * (10) It should be emphasized that insertion, removal and replacement - * are general: - * * You can insert an item into any ptr location in the - * allocated ptr array, as well as into the next ptr address - * beyond the allocated array (in which case a realloc will occur). - * * You can remove or replace an item from any ptr location - * in the allocated ptr array. - * * When inserting into an occupied location, you have - * three options for downshifting. - * * When removing, you can either leave the ptr null or - * compact the array. - * - * Notes on the Ptraa: - * - * (1) The Ptraa is a fixed size ptr array for holding Ptra. - * In that respect, it is different from other pointer arrays, which - * are extensible and grow using the *Add*() functions. - * (2) In general, the Ptra ptrs in the Ptraa can be randomly occupied. - * A typical usage is to allow an O(n) horizontal sort of Pix, - * where the size of the Ptra array is the width of the image, - * and each Ptra is an array of all the Pix at a specific x location. - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include "allheaders.h" - - /* Bounds on initial array size */ -static const l_uint32 MaxPtrArraySize = 100000; -static const l_int32 InitialPtrArraySize = 20; /*!< n'importe quoi */ - - /* Static function */ -static l_int32 ptraExtendArray(L_PTRA *pa); - -/*--------------------------------------------------------------------------* - * Ptra creation and destruction * - *--------------------------------------------------------------------------*/ -/*! - * \brief ptraCreate() - * - * \param[in] n size of ptr array to be alloc'd; use 0 for default - * \return pa, or NULL on error - */ -L_PTRA * -ptraCreate(l_int32 n) -{ -L_PTRA *pa; - - PROCNAME("ptraCreate"); - - if (n <= 0 || n > MaxPtrArraySize) - n = InitialPtrArraySize; - - pa = (L_PTRA *)LEPT_CALLOC(1, sizeof(L_PTRA)); - if ((pa->array = (void **)LEPT_CALLOC(n, sizeof(void *))) == NULL) { - ptraDestroy(&pa, 0, 0); - return (L_PTRA *)ERROR_PTR("ptr array not made", procName, NULL); - } - pa->nalloc = n; - pa->imax = -1; - pa->nactual = 0; - return pa; -} - - -/*! - * \brief ptraDestroy() - * - * \param[in,out] ppa will be set to null before returning - * \param[in] freeflag TRUE to free each remaining item in the array - * \param[in] 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\n", 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) - LEPT_FREE(item); - } - } else if (warnflag) { - L_WARNING("potential memory leak of %d items in ptra\n", - procName, nactual); - } - } - - LEPT_FREE(pa->array); - LEPT_FREE(pa); - *ppa = NULL; - return; -} - - -/*--------------------------------------------------------------------------* - * Add/insert/remove/replace generic ptr object * - *--------------------------------------------------------------------------*/ -/*! - * \brief ptraAdd() - * - * \param[in] pa ptra - * \param[in] item generic ptr to a struct - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This adds the element to the next location beyond imax, - * which is the largest occupied ptr in the array. This is - * what you expect from a stack, where all ptrs up to and - * including imax are occupied, but here the occuption of - * items in the array is entirely arbitrary. - *- */ -l_ok -ptraAdd(L_PTRA *pa, - void *item) -{ -l_int32 imax; - - PROCNAME("ptraAdd"); - - if (!pa) - return ERROR_INT("pa not defined", procName, 1); - if (!item) - return ERROR_INT("item not defined", procName, 1); - - ptraGetMaxIndex(pa, &imax); - if (imax >= pa->nalloc - 1 && ptraExtendArray(pa)) - return ERROR_INT("extension failure", procName, 1); - pa->array[imax + 1] = (void *)item; - pa->imax++; - pa->nactual++; - return 0; -} - - -/*! - * \brief ptraExtendArray() - * - * \param[in] pa - * \return 0 if OK, 1 on error - */ -static l_int32 -ptraExtendArray(L_PTRA *pa) -{ - PROCNAME("ptraExtendArray"); - - if (!pa) - return ERROR_INT("pa not defined", procName, 1); - - if ((pa->array = (void **)reallocNew((void **)&pa->array, - sizeof(void *) * pa->nalloc, - 2 * sizeof(void *) * pa->nalloc)) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - - pa->nalloc *= 2; - return 0; -} - - -/*! - * \brief ptraInsert() - * - * \param[in] pa ptra - * \param[in] index location in ptra to insert new value - * \param[in] item generic ptr to a struct; can be null - * \param[in] shiftflag L_AUTO_DOWNSHIFT, L_MIN_DOWNSHIFT, L_FULL_DOWNSHIFT - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This checks first to see if the location is valid, and - * then if there is presently an item there. If there is not, - * it is simply inserted into that location. - * (2) If there is an item at the insert location, items must be - * moved down to make room for the insert. In the downward - * shift there are three options, given by %shiftflag. - * ~ If %shiftflag == L_AUTO_DOWNSHIFT, a decision is made - * whether, in a cascade of items, to downshift a minimum - * amount or for all items above %index. The decision is - * based on the expectation of finding holes (null ptrs) - * between %index and the bottom of the array. - * Assuming the holes are distributed uniformly, if 2 or more - * holes are expected, we do a minimum shift. - * ~ If %shiftflag == L_MIN_DOWNSHIFT, the downward shifting - * cascade of items progresses a minimum amount, until - * the first empty slot is reached. This mode requires - * some computation before the actual shifting is done. - * ~ If %shiftflag == L_FULL_DOWNSHIFT, a shifting cascade is - * performed where pa[i] --> pa[i + 1] for all i >= index. - * Then, the item is inserted at pa[index]. - * (3) If you are not using L_AUTO_DOWNSHIFT, the rule of thumb is - * to use L_FULL_DOWNSHIFT if the array is compacted (each - * element points to an item), and to use L_MIN_DOWNSHIFT - * if there are a significant number of null pointers. - * There is no penalty to using L_MIN_DOWNSHIFT for a - * compacted array, however, because the full shift is required - * and we don't do the O(n) computation to look for holes. - * (4) This should not be used repeatedly on large arrays, - * because the function is generally O(n). - * (5) However, it can be used repeatedly if we start with an empty - * ptr array and insert only once at each location. For example, - * you can support an array of Numa, where at each ptr location - * you store either 0 or 1 Numa, and the Numa can be added - * randomly to the ptr array. - *- */ -l_ok -ptraInsert(L_PTRA *pa, - l_int32 index, - void *item, - l_int32 shiftflag) -{ -l_int32 i, ihole, imax; -l_float32 nexpected; - - PROCNAME("ptraInsert"); - - if (!pa) - return ERROR_INT("pa not defined", procName, 1); - if (index < 0 || index > pa->nalloc) - return ERROR_INT("index not in [0 ... nalloc]", procName, 1); - if (shiftflag != L_AUTO_DOWNSHIFT && shiftflag != L_MIN_DOWNSHIFT && - shiftflag != L_FULL_DOWNSHIFT) - return ERROR_INT("invalid shiftflag", procName, 1); - - if (item) pa->nactual++; - if (index == pa->nalloc) { /* can happen when index == n */ - if (ptraExtendArray(pa)) - return ERROR_INT("extension failure", procName, 1); - } - - /* We are inserting into a hole or adding to the end of the array. - * No existing items are moved. */ - ptraGetMaxIndex(pa, &imax); - if (pa->array[index] == NULL) { - pa->array[index] = item; - if (item && index > imax) /* new item put beyond max so far */ - pa->imax = index; - return 0; - } - - /* We are inserting at the location of an existing item, - * forcing the existing item and those below to shift down. - * First, extend the array automatically if the last element - * (nalloc - 1) is occupied (imax). This may not be necessary - * in every situation, but only an anomalous sequence of insertions - * into the array would cause extra ptr allocation. */ - if (imax >= pa->nalloc - 1 && ptraExtendArray(pa)) - return ERROR_INT("extension failure", procName, 1); - - /* If there are no holes, do a full downshift. - * Otherwise, if L_AUTO_DOWNSHIFT, use the expected number - * of holes between index and n to determine the shift mode */ - if (imax + 1 == pa->nactual) { - shiftflag = L_FULL_DOWNSHIFT; - } else if (shiftflag == L_AUTO_DOWNSHIFT) { - if (imax < 10) { - shiftflag = L_FULL_DOWNSHIFT; /* no big deal */ - } else { - nexpected = (l_float32)(imax - pa->nactual) * - (l_float32)((imax - index) / imax); - shiftflag = (nexpected > 2.0) ? L_MIN_DOWNSHIFT : L_FULL_DOWNSHIFT; - } - } - - if (shiftflag == L_MIN_DOWNSHIFT) { /* run down looking for a hole */ - for (ihole = index + 1; ihole <= imax; ihole++) { - if (pa->array[ihole] == NULL) - break; - } - } else { /* L_FULL_DOWNSHIFT */ - ihole = imax + 1; - } - - for (i = ihole; i > index; i--) - pa->array[i] = pa->array[i - 1]; - pa->array[index] = (void *)item; - if (ihole == imax + 1) /* the last item was shifted down */ - pa->imax++; - - return 0; -} - - -/*! - * \brief ptraRemove() - * - * \param[in] pa ptra - * \param[in] index element to be removed - * \param[in] flag L_NO_COMPACTION, L_COMPACTION - * \return item, or NULL on error - * - *- * Notes: - * (1) If flag == L_NO_COMPACTION, this removes the item and - * nulls the ptr on the array. If it takes the last item - * in the array, pa->n is reduced to the next item. - * (2) If flag == L_COMPACTION, this compacts the array for - * for all i >= index. It should not be used repeatedly on - * large arrays, because compaction is O(n). - * (3) The ability to remove without automatic compaction allows - * removal with cost O(1). - *- */ -void * -ptraRemove(L_PTRA *pa, - l_int32 index, - l_int32 flag) -{ -l_int32 i, imax, fromend, icurrent; -void *item; - - PROCNAME("ptraRemove"); - - if (!pa) - return (void *)ERROR_PTR("pa not defined", procName, NULL); - ptraGetMaxIndex(pa, &imax); - if (index < 0 || index > imax) - return (void *)ERROR_PTR("index not in [0 ... imax]", procName, NULL); - - item = pa->array[index]; - if (item) - pa->nactual--; - pa->array[index] = NULL; - - /* If we took the last item, need to reduce pa->n */ - fromend = (index == imax); - if (fromend) { - for (i = index - 1; i >= 0; i--) { - if (pa->array[i]) - break; - } - pa->imax = i; - } - - /* Compact from index to the end of the array */ - if (!fromend && flag == L_COMPACTION) { - for (icurrent = index, i = index + 1; i <= imax; i++) { - if (pa->array[i]) - pa->array[icurrent++] = pa->array[i]; - } - pa->imax = icurrent - 1; - } - return item; -} - - -/*! - * \brief ptraRemoveLast() - * - * \param[in] pa ptra - * \return item, or NULL on error or if the array is empty - */ -void * -ptraRemoveLast(L_PTRA *pa) -{ -l_int32 imax; - - PROCNAME("ptraRemoveLast"); - - if (!pa) - return (void *)ERROR_PTR("pa not defined", procName, NULL); - - /* Remove the last item in the array. No compaction is required. */ - ptraGetMaxIndex(pa, &imax); - if (imax >= 0) - return ptraRemove(pa, imax, L_NO_COMPACTION); - else /* empty */ - return NULL; -} - - -/*! - * \brief ptraReplace() - * - * \param[in] pa ptra - * \param[in] index element to be replaced - * \param[in] item new generic ptr to a struct; can be null - * \param[in] freeflag TRUE to free old item; FALSE to return it - * \return item old item, if it exists and is not freed, - * or NULL on error - */ -void * -ptraReplace(L_PTRA *pa, - l_int32 index, - void *item, - l_int32 freeflag) -{ -l_int32 imax; -void *olditem; - - PROCNAME("ptraReplace"); - - if (!pa) - return (void *)ERROR_PTR("pa not defined", procName, NULL); - ptraGetMaxIndex(pa, &imax); - if (index < 0 || index > imax) - return (void *)ERROR_PTR("index not in [0 ... imax]", procName, NULL); - - olditem = pa->array[index]; - pa->array[index] = item; - if (!item && olditem) - pa->nactual--; - else if (item && !olditem) - pa->nactual++; - - if (freeflag == FALSE) - return olditem; - - if (olditem) - LEPT_FREE(olditem); - return NULL; -} - - -/*! - * \brief ptraSwap() - * - * \param[in] pa ptra - * \param[in] index1 - * \param[in] index2 - * \return 0 if OK, 1 on error - */ -l_ok -ptraSwap(L_PTRA *pa, - l_int32 index1, - l_int32 index2) -{ -l_int32 imax; -void *item; - - PROCNAME("ptraSwap"); - - if (!pa) - return ERROR_INT("pa not defined", procName, 1); - if (index1 == index2) - return 0; - ptraGetMaxIndex(pa, &imax); - if (index1 < 0 || index1 > imax || index2 < 0 || index2 > imax) - return ERROR_INT("invalid index: not in [0 ... imax]", procName, 1); - - item = ptraRemove(pa, index1, L_NO_COMPACTION); - item = ptraReplace(pa, index2, item, FALSE); - ptraInsert(pa, index1, item, L_MIN_DOWNSHIFT); - return 0; -} - - -/*! - * \brief ptraCompactArray() - * - * \param[in] pa - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This compacts the items on the array, filling any empty ptrs. - * (2) This does not change the size of the array of ptrs. - *- */ -l_ok -ptraCompactArray(L_PTRA *pa) -{ -l_int32 i, imax, nactual, index; - - PROCNAME("ptraCompactArray"); - - if (!pa) - return ERROR_INT("pa not defined", procName, 1); - ptraGetMaxIndex(pa, &imax); - ptraGetActualCount(pa, &nactual); - if (imax + 1 == nactual) return 0; - - /* Compact the array */ - for (i = 0, index = 0; i <= imax; i++) { - if (pa->array[i]) - pa->array[index++] = pa->array[i]; - } - pa->imax = index - 1; - if (nactual != index) - L_ERROR("index = %d; != nactual\n", procName, index); - - return 0; -} - - -/*----------------------------------------------------------------------* - * Other array operations * - *----------------------------------------------------------------------*/ -/*! - * \brief ptraReverse() - * - * \param[in] pa ptra - * \return 0 if OK, 1 on error - */ -l_ok -ptraReverse(L_PTRA *pa) -{ -l_int32 i, imax; - - PROCNAME("ptraReverse"); - - if (!pa) - return ERROR_INT("pa not defined", procName, 1); - ptraGetMaxIndex(pa, &imax); - - for (i = 0; i < (imax + 1) / 2; i++) - ptraSwap(pa, i, imax - i); - return 0; -} - - -/*! - * \brief ptraJoin() - * - * \param[in] pa1 add to this one - * \param[in] pa2 appended to pa1, and emptied of items; can be null - * \return 0 if OK, 1 on error - */ -l_ok -ptraJoin(L_PTRA *pa1, - L_PTRA *pa2) -{ -l_int32 i, imax; -void *item; - - PROCNAME("ptraJoin"); - - if (!pa1) - return ERROR_INT("pa1 not defined", procName, 1); - if (!pa2) - return 0; - - ptraGetMaxIndex(pa2, &imax); - for (i = 0; i <= imax; i++) { - item = ptraRemove(pa2, i, L_NO_COMPACTION); - ptraAdd(pa1, item); - } - - return 0; -} - - - -/*----------------------------------------------------------------------* - * Simple ptra accessors * - *----------------------------------------------------------------------*/ -/*! - * \brief ptraGetMaxIndex() - * - * \param[in] pa ptra - * \param[out] pmaxindex index of last item in the array; - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) The largest index to an item in the array is %maxindex. - * %maxindex is one less than the number of items that would be - * in the array if there were no null pointers between 0 - * and %maxindex - 1. However, because the internal ptr array - * need not be compacted, there may be NULL pointers at - * indices below %maxindex; for example, if items have - * been removed. - * (2) When an item is added to the end of the array, it goes - * into pa->array[maxindex + 1], and maxindex is then - * incremented by 1. - * (3) If there are no items in the array, this returns %maxindex = -1. - *- */ -l_ok -ptraGetMaxIndex(L_PTRA *pa, - l_int32 *pmaxindex) -{ - PROCNAME("ptraGetMaxIndex"); - - if (!pa) - return ERROR_INT("pa not defined", procName, 1); - if (!pmaxindex) - return ERROR_INT("&maxindex not defined", procName, 1); - *pmaxindex = pa->imax; - return 0; -} - - -/*! - * \brief ptraGetActualCount() - * - * \param[in] pa ptra - * \param[out] pcount actual number of items on the ptr array - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) The actual number of items on the ptr array, pa->nactual, - * will be smaller than pa->n if the array is not compacted. - *- */ -l_ok -ptraGetActualCount(L_PTRA *pa, - l_int32 *pcount) -{ - PROCNAME("ptraGetActualCount"); - - if (!pa) - return ERROR_INT("pa not defined", procName, 1); - if (!pcount) - return ERROR_INT("&count not defined", procName, 1); - *pcount = pa->nactual; - - return 0; -} - - -/*! - * \brief ptraGetPtrToItem() - * - * \param[in] pa ptra - * \param[in] index of element to be retrieved - * \return a ptr to the element, or NULL on error - * - *- * Notes: - * (1) This returns a ptr to the item. You must cast it to - * the type of item. Do not destroy it; the item belongs - * to the Ptra. - * (2) This can access all possible items on the ptr array. - * If an item doesn't exist, it returns null. - *- */ -void * -ptraGetPtrToItem(L_PTRA *pa, - l_int32 index) -{ - PROCNAME("ptraGetPtrToItem"); - - if (!pa) - return (void *)ERROR_PTR("pa not defined", procName, NULL); - if (index < 0 || index >= pa->nalloc) - return (void *)ERROR_PTR("index not in [0 ... nalloc-1]", - procName, NULL); - - return pa->array[index]; -} - - -/*--------------------------------------------------------------------------* - * Ptraa creation and destruction * - *--------------------------------------------------------------------------*/ -/*! - * \brief ptraaCreate() - * - * \param[in] n size of ptr array to be alloc'd - * \return paa, or NULL on error - * - *- * Notes: - * (1) The ptraa is generated with a fixed size, that can not change. - * The ptra can be generated and inserted randomly into this array. - *- */ -L_PTRAA * -ptraaCreate(l_int32 n) -{ -L_PTRAA *paa; - - PROCNAME("ptraaCreate"); - - if (n <= 0) - return (L_PTRAA *)ERROR_PTR("n must be > 0", procName, NULL); - - paa = (L_PTRAA *)LEPT_CALLOC(1, sizeof(L_PTRAA)); - if ((paa->ptra = (L_PTRA **)LEPT_CALLOC(n, sizeof(L_PTRA *))) == NULL) { - ptraaDestroy(&paa, 0, 0); - return (L_PTRAA *)ERROR_PTR("ptr array not made", procName, NULL); - } - paa->nalloc = n; - return paa; -} - - -/*! - * \brief ptraaDestroy() - * - * \param[in,out] ppaa will be set to null before returning - * \param[in] freeflag TRUE to free each remaining item in each ptra - * \param[in] warnflag TRUE to warn if any remaining items - * are not destroyed - * \return void - * - *- * Notes: - * (1) See ptraDestroy() for use of %freeflag and %warnflag. - * (2) To destroy the ptraa, we destroy each ptra, then the ptr array, - * then the ptraa, and then null the contents of the input ptr. - *- */ -void -ptraaDestroy(L_PTRAA **ppaa, - l_int32 freeflag, - l_int32 warnflag) -{ -l_int32 i, n; -L_PTRA *pa; -L_PTRAA *paa; - - PROCNAME("ptraaDestroy"); - - if (ppaa == NULL) { - L_WARNING("ptr address is NULL\n", procName); - return; - } - if ((paa = *ppaa) == NULL) - return; - - ptraaGetSize(paa, &n); - for (i = 0; i < n; i++) { - pa = ptraaGetPtra(paa, i, L_REMOVE); - ptraDestroy(&pa, freeflag, warnflag); - } - - LEPT_FREE(paa->ptra); - LEPT_FREE(paa); - *ppaa = NULL; - return; -} - - -/*--------------------------------------------------------------------------* - * Ptraa accessors * - *--------------------------------------------------------------------------*/ -/*! - * \brief ptraaGetSize() - * - * \param[in] paa - * \param[out] psize size of ptr array - * \return 0 if OK; 1 on error - */ -l_ok -ptraaGetSize(L_PTRAA *paa, - l_int32 *psize) -{ - PROCNAME("ptraaGetSize"); - - if (!paa) - return ERROR_INT("paa not defined", procName, 1); - if (!psize) - return ERROR_INT("&size not defined", procName, 1); - *psize = paa->nalloc; - - return 0; -} - - -/*! - * \brief ptraaInsertPtra() - * - * \param[in] paa ptraa - * \param[in] index location in array for insertion - * \param[in] pa to be inserted - * \return 0 if OK; 1 on error - * - *- * Notes: - * (1) Caller should check return value. On success, the Ptra - * is inserted in the Ptraa and is owned by it. However, - * on error, the Ptra remains owned by the caller. - *- */ -l_ok -ptraaInsertPtra(L_PTRAA *paa, - l_int32 index, - L_PTRA *pa) -{ -l_int32 n; - - PROCNAME("ptraaInsertPtra"); - - if (!paa) - return ERROR_INT("paa not defined", procName, 1); - if (!pa) - return ERROR_INT("pa not defined", procName, 1); - ptraaGetSize(paa, &n); - if (index < 0 || index >= n) - return ERROR_INT("invalid index", procName, 1); - if (paa->ptra[index] != NULL) - return ERROR_INT("ptra already stored at index", procName, 1); - - paa->ptra[index] = pa; - return 0; -} - - -/*! - * \brief ptraaGetPtra() - * - * \param[in] paa ptraa - * \param[in] index location in array - * \param[in] accessflag L_HANDLE_ONLY, L_REMOVE - * \return ptra at index location, or NULL on error or if there - * is no ptra there. - * - *- * Notes: - * (1) This returns the ptra ptr. If %accessflag == L_HANDLE_ONLY, - * the ptra is left on the ptraa. If %accessflag == L_REMOVE, - * the ptr in the ptraa is set to NULL, and the caller - * is responsible for disposing of the ptra (either putting it - * back on the ptraa, or destroying it). - * (2) This returns NULL if there is no Ptra at the index location. - *- */ -L_PTRA * -ptraaGetPtra(L_PTRAA *paa, - l_int32 index, - l_int32 accessflag) -{ -l_int32 n; -L_PTRA *pa; - - PROCNAME("ptraaGetPtra"); - - if (!paa) - return (L_PTRA *)ERROR_PTR("paa not defined", procName, NULL); - ptraaGetSize(paa, &n); - if (index < 0 || index >= n) - return (L_PTRA *)ERROR_PTR("invalid index", procName, NULL); - if (accessflag != L_HANDLE_ONLY && accessflag != L_REMOVE) - return (L_PTRA *)ERROR_PTR("invalid accessflag", procName, NULL); - - pa = paa->ptra[index]; - if (accessflag == L_REMOVE) - paa->ptra[index] = NULL; - return pa; -} - - -/*--------------------------------------------------------------------------* - * Ptraa conversion * - *--------------------------------------------------------------------------*/ -/*! - * \brief ptraaFlattenToPtra() - * - * \param[in] paa ptraa - * \return ptra, or NULL on error - * - *- * Notes: - * (1) This 'flattens' the ptraa to a ptra, taking the items in - * each ptra, in order, starting with the first ptra, etc. - * (2) As a side-effect, the ptra are all removed from the ptraa - * and destroyed, leaving an empty ptraa. - *- */ -L_PTRA * -ptraaFlattenToPtra(L_PTRAA *paa) -{ -l_int32 i, n; -L_PTRA *pat, *pad; - - PROCNAME("ptraaFlattenToPtra"); - - if (!paa) - return (L_PTRA *)ERROR_PTR("paa not defined", procName, NULL); - - pad = ptraCreate(0); - ptraaGetSize(paa, &n); - for (i = 0; i < n; i++) { - pat = ptraaGetPtra(paa, i, L_REMOVE); - if (!pat) continue; - ptraJoin(pad, pat); - ptraDestroy(&pat, FALSE, FALSE); /* they're all empty */ - } - - return pad; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ptra.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ptra.h deleted file mode 100644 index dc5216cd..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/ptra.h +++ /dev/null @@ -1,95 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_PTRA_H -#define LEPTONICA_PTRA_H - -/*! - * \file ptra.h - * - *- * Contains the following structs: - * struct L_Ptra - * struct L_Ptraa - * - * Contains definitions for: - * L_Ptra compaction flags for removal - * L_Ptra shifting flags for insert - * L_Ptraa accessor flags - *- */ - - -/*------------------------------------------------------------------------* - * Generic Ptr Array Structs * - *------------------------------------------------------------------------*/ - - /*! Generic pointer array */ -struct L_Ptra -{ - l_int32 nalloc; /*!< size of allocated ptr array */ - l_int32 imax; /*!< greatest valid index */ - l_int32 nactual; /*!< actual number of stored elements */ - void **array; /*!< ptr array */ -}; -typedef struct L_Ptra L_PTRA; - - - /*! Array of generic pointer arrays */ -struct L_Ptraa -{ - l_int32 nalloc; /*!< size of allocated ptr array */ - struct L_Ptra **ptra; /*!< array of ptra */ -}; -typedef struct L_Ptraa L_PTRAA; - - - -/*------------------------------------------------------------------------* - * Accessor and modifier flags for L_Ptra and L_Ptraa * - *------------------------------------------------------------------------*/ - -/*! Ptra Removal */ -enum { - L_NO_COMPACTION = 1, /*!< null the pointer only */ - L_COMPACTION = 2 /*!< compact the array */ -}; - -/*! Ptra Insertion */ -enum { - L_AUTO_DOWNSHIFT = 0, /*!< choose based on number of holes */ - L_MIN_DOWNSHIFT = 1, /*!< downshifts min # of ptrs below insert */ - L_FULL_DOWNSHIFT = 2 /*!< downshifts all ptrs below insert */ -}; - -/*! Ptraa Accessor */ -enum { - L_HANDLE_ONLY = 0, /*!< ptr to L_Ptra; caller can inspect only */ - L_REMOVE = 1 /*!< caller owns; destroy or save in L_Ptraa */ -}; - - -#endif /* LEPTONICA_PTRA_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/quadtree.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/quadtree.c deleted file mode 100644 index 6c10232a..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/quadtree.c +++ /dev/null @@ -1,701 +0,0 @@ -/*====================================================================* - - 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 - *- * - * 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. - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#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 - * - * - * 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. - *- */ -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; - - PROCNAME("pixQuadtreeMean"); - - if (!pfpixa) - return ERROR_INT("&fpixa not defined", procName, 1); - *pfpixa = NULL; - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); - pixGetDimensions(pixs, &w, &h, NULL); - if (nlevels > quadtreeMaxLevels(w, h)) - return ERROR_INT("nlevels too large for image", procName, 1); - - if (!pix_ma) - pix_mac = pixBlockconvAccum(pixs); - else - pix_mac = pixClone(pix_ma); - if (!pix_mac) - return ERROR_INT("pix_mac not made", procName, 1); - - if ((baa = boxaaQuadtreeRegions(w, h, nlevels)) == NULL) { - pixDestroy(&pix_mac); - return ERROR_INT("baa not made", procName, 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 - * - *- * Notes: - * (1) The returned fpixav and fpixarv have %nlevels of fpix, - * each containing at the respective levels the variance - * and root variance values. - *- */ -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, *fpixrv; -PIX *pix_mac; /* copy of mean accumulator */ -DPIX *dpix_msac; /* msa clone */ - - PROCNAME("pixQuadtreeVariance"); - - if (!pfpixa_v && !pfpixa_rv) - return ERROR_INT("neither &fpixav nor &fpixarv defined", procName, 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", procName, 1); - pixGetDimensions(pixs, &w, &h, NULL); - if (nlevels > quadtreeMaxLevels(w, h)) - return ERROR_INT("nlevels too large for image", procName, 1); - - if (!pix_ma) - pix_mac = pixBlockconvAccum(pixs); - else - pix_mac = pixClone(pix_ma); - if (!pix_mac) - return ERROR_INT("pix_mac not made", procName, 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", procName, 1); - } - - if ((baa = boxaaQuadtreeRegions(w, h, nlevels)) == NULL) { - pixDestroy(&pix_mac); - dpixDestroy(&dpix_msac); - return ERROR_INT("baa not made", procName, 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 - * - *- * 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. - *- */ -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; - - PROCNAME("pixMeanInRectangle"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0.0; - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - if (!pixma) - return ERROR_INT("pixma not defined", procName, 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", procName, 1); - - /* Use up to 4 points in the accumulator */ - norm = 1.0 / ((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 - * - *- * 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. - *- */ -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; - - PROCNAME("pixVarianceInRectangle"); - - if (!pvar && !prvar) - return ERROR_INT("neither &var nor &rvar defined", procName, 1); - if (pvar) *pvar = 0.0; - if (prvar) *prvar = 0.0; - if (!pixs || pixGetDepth(pixs) != 8) - return ERROR_INT("pixs not defined", procName, 1); - if (!box) - return ERROR_INT("box not defined", procName, 1); - if (!pix_ma) - return ERROR_INT("pix_ma not defined", procName, 1); - if (!dpix_msa) - return ERROR_INT("dpix_msa not defined", procName, 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", procName, 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 - * - *- * 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). - *- */ -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; - - PROCNAME("boxaaQuadtreeRegions"); - - if (nlevels < 1) - return (BOXAA *)ERROR_PTR("nlevels must be >= 1", procName, NULL); - if (w < (1 << (nlevels - 1))) - return (BOXAA *)ERROR_PTR("w doesn't support nlevels", procName, NULL); - if (h < (1 << (nlevels - 1))) - return (BOXAA *)ERROR_PTR("h doesn't support nlevels", procName, 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 - * - *- * 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) - *- */ -l_ok -quadtreeGetParent(FPIXA *fpixa, - l_int32 level, - l_int32 x, - l_int32 y, - l_float32 *pval) -{ -l_int32 n; - - PROCNAME("quadtreeGetParent"); - - if (!pval) - return ERROR_INT("&val not defined", procName, 1); - *pval = 0.0; - if (!fpixa) - return ERROR_INT("fpixa not defined", procName, 1); - n = fpixaGetCount(fpixa); - if (level < 1 || level >= n) - return ERROR_INT("invalid level", procName, 1); - - if (fpixaGetPixel(fpixa, level - 1, x / 2, y / 2, pval) != 0) - return ERROR_INT("invalid coordinates", procName, 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 - * - *- * 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) - *- */ -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; - - PROCNAME("quadtreeGetChildren"); - - if (!pval00 || !pval01 || !pval10 || !pval11) - return ERROR_INT("&val* not all defined", procName, 1); - *pval00 = *pval10 = *pval01 = *pval11 = 0.0; - if (!fpixa) - return ERROR_INT("fpixa not defined", procName, 1); - n = fpixaGetCount(fpixa); - if (level < 0 || level >= n - 1) - return ERROR_INT("invalid level", procName, 1); - - if (fpixaGetPixel(fpixa, level + 1, 2 * x, 2 * y, pval00) != 0) - return ERROR_INT("invalid coordinates", procName, 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 - * - *- * 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. - *- */ -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 - * - *- * 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). - *- */ -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, *pixd; -PIXA *pixat; - - PROCNAME("fpixaDisplayQuadtree"); - - if (!fpixa) - return (PIX *)ERROR_PTR("fpixa not defined", procName, NULL); - - if ((nlevels = fpixaGetCount(fpixa)) == 0) - return (PIX *)ERROR_PTR("pixas empty", procName, NULL); - - if ((bmf = bmfCreate(NULL, fontsize)) == NULL) - L_ERROR("bmf not made; text will not be added", procName); - 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; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/queue.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/queue.c deleted file mode 100644 index e0397bcc..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/queue.c +++ /dev/null @@ -1,326 +0,0 @@ -/*====================================================================* - - 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 queue.c - *- * - * Create/Destroy L_Queue - * L_QUEUE *lqueueCreate() - * void *lqueueDestroy() - * - * Operations to add/remove to/from a L_Queue - * l_int32 lqueueAdd() - * static l_int32 lqueueExtendArray() - * void *lqueueRemove() - * - * Accessors - * l_int32 lqueueGetCount() - * - * Debug output - * l_int32 lqueuePrint() - * - * The lqueue is a fifo that implements a queue of void* pointers. - * It can be used to hold a queue of any type of struct. - * Internally, it maintains two counters: - * nhead: location of head (in ptrs) from the beginning - * of the buffer - * nelem: number of ptr elements stored in the queue - * As items are added to the queue, nelem increases. - * As items are removed, nhead increases and nelem decreases. - * Any time the tail reaches the end of the allocated buffer, - * all the pointers are shifted to the left, so that the head - * is at the beginning of the array. - * If the buffer becomes more than 3/4 full, it doubles in size. - * - * [A circular queue would allow us to skip the shifting and - * to resize only when the buffer is full. For most applications, - * the extra work we do for a linear queue is not significant.] - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include "allheaders.h" - -static const l_int32 MIN_BUFFER_SIZE = 20; /* n'importe quoi */ -static const l_int32 INITIAL_BUFFER_ARRAYSIZE = 1024; /* n'importe quoi */ - - /* Static function */ -static l_int32 lqueueExtendArray(L_QUEUE *lq); - -/*--------------------------------------------------------------------------* - * L_Queue create/destroy * - *--------------------------------------------------------------------------*/ -/*! - * \brief lqueueCreate() - * - * \param[in] nalloc size of ptr array to be alloc'd; 0 for default - * \return lqueue, or NULL on error - * - * - * Notes: - * (1) Allocates a ptr array of given size, and initializes counters. - *- */ -L_QUEUE * -lqueueCreate(l_int32 nalloc) -{ -L_QUEUE *lq; - - PROCNAME("lqueueCreate"); - - if (nalloc < MIN_BUFFER_SIZE) - nalloc = INITIAL_BUFFER_ARRAYSIZE; - - lq = (L_QUEUE *)LEPT_CALLOC(1, sizeof(L_QUEUE)); - if ((lq->array = (void **)LEPT_CALLOC(nalloc, sizeof(void *))) == NULL) { - lqueueDestroy(&lq, 0); - return (L_QUEUE *)ERROR_PTR("ptr array not made", procName, NULL); - } - lq->nalloc = nalloc; - lq->nhead = lq->nelem = 0; - return lq; -} - - -/*! - * \brief lqueueDestroy() - * - * \param[in,out] plq will be set to null before returning - * \param[in] 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\n", procName); - return; - } - if ((lq = *plq) == NULL) - return; - - if (freeflag) { - while(lq->nelem > 0) { - item = lqueueRemove(lq); - LEPT_FREE(item); - } - } else if (lq->nelem > 0) { - L_WARNING("memory leak of %d items in lqueue!\n", procName, lq->nelem); - } - - if (lq->array) - LEPT_FREE(lq->array); - if (lq->stack) - lstackDestroy(&lq->stack, freeflag); - LEPT_FREE(lq); - *plq = NULL; - - return; -} - - -/*--------------------------------------------------------------------------* - * Accessors * - *--------------------------------------------------------------------------*/ -/*! - * \brief lqueueAdd() - * - * \param[in] lq lqueue - * \param[in] item to be added to the tail of the queue - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The algorithm is as follows. If the queue is populated - * to the end of the allocated array, shift all ptrs toward - * the beginning of the array, so that the head of the queue - * is at the beginning of the array. Then, if the array is - * more than 0.75 full, realloc with double the array size. - * Finally, add the item to the tail of the queue. - *- */ -l_ok -lqueueAdd(L_QUEUE *lq, - void *item) -{ - PROCNAME("lqueueAdd"); - - if (!lq) - return ERROR_INT("lq not defined", procName, 1); - if (!item) - return ERROR_INT("item not defined", procName, 1); - - /* If filled to the end and the ptrs can be shifted to the left, - * shift them. */ - if ((lq->nhead + lq->nelem >= lq->nalloc) && (lq->nhead != 0)) { - memmove(lq->array, lq->array + lq->nhead, sizeof(void *) * lq->nelem); - lq->nhead = 0; - } - - /* If necessary, expand the allocated array by a factor of 2 */ - if (lq->nelem > 0.75 * lq->nalloc) - lqueueExtendArray(lq); - - /* Now add the item */ - lq->array[lq->nhead + lq->nelem] = (void *)item; - lq->nelem++; - - return 0; -} - - -/*! - * \brief lqueueExtendArray() - * - * \param[in] lq lqueue - * \return 0 if OK, 1 on error - */ -static l_int32 -lqueueExtendArray(L_QUEUE *lq) -{ - PROCNAME("lqueueExtendArray"); - - if (!lq) - return ERROR_INT("lq not defined", procName, 1); - - if ((lq->array = (void **)reallocNew((void **)&lq->array, - sizeof(void *) * lq->nalloc, - 2 * sizeof(void *) * lq->nalloc)) == NULL) - return ERROR_INT("new ptr array not returned", procName, 1); - - lq->nalloc = 2 * lq->nalloc; - return 0; -} - - -/*! - * \brief lqueueRemove() - * - * \param[in] lq lqueue - * \return ptr to item popped from the head of the queue, - * or NULL if the queue is empty or on error - * - *- * Notes: - * (1) If this is the last item on the queue, so that the queue - * becomes empty, nhead is reset to the beginning of the array. - *- */ -void * -lqueueRemove(L_QUEUE *lq) -{ -void *item; - - PROCNAME("lqueueRemove"); - - if (!lq) - return (void *)ERROR_PTR("lq not defined", procName, NULL); - - if (lq->nelem == 0) - return NULL; - item = lq->array[lq->nhead]; - lq->array[lq->nhead] = NULL; - if (lq->nelem == 1) - lq->nhead = 0; /* reset head ptr */ - else - (lq->nhead)++; /* can't go off end of array because nelem > 1 */ - lq->nelem--; - return item; -} - - -/*! - * \brief lqueueGetCount() - * - * \param[in] lq lqueue - * \return count, or 0 on error - */ -l_int32 -lqueueGetCount(L_QUEUE *lq) -{ - PROCNAME("lqueueGetCount"); - - if (!lq) - return ERROR_INT("lq not defined", procName, 0); - - return lq->nelem; -} - - -/*---------------------------------------------------------------------* - * Debug output * - *---------------------------------------------------------------------*/ -/*! - * \brief lqueuePrint() - * - * \param[in] fp file stream - * \param[in] lq lqueue - * \return 0 if OK; 1 on error - */ -l_ok -lqueuePrint(FILE *fp, - L_QUEUE *lq) -{ -l_int32 i; - - PROCNAME("lqueuePrint"); - - if (!fp) - return ERROR_INT("stream not defined", procName, 1); - if (!lq) - return ERROR_INT("lq not defined", procName, 1); - - fprintf(fp, "\n L_Queue: nalloc = %d, nhead = %d, nelem = %d, array = %p\n", - lq->nalloc, lq->nhead, lq->nelem, lq->array); - for (i = lq->nhead; i < lq->nhead + lq->nelem; i++) - fprintf(fp, "array[%d] = %p\n", i, lq->array[i]); - - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/queue.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/queue.h deleted file mode 100644 index fd380e83..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/queue.h +++ /dev/null @@ -1,77 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_QUEUE_H -#define LEPTONICA_QUEUE_H - -/*! - * \file queue.h - * - *- * Expandable pointer queue for arbitrary void* data. - * - * The L_Queue is a fifo that implements a queue of void* pointers. - * It can be used to hold a queue of any type of struct. - * - * Internally, it maintains two counters: - * nhead: location of head (in ptrs) from the beginning - * of the array. - * nelem: number of ptr elements stored in the queue. - * - * The element at the head of the queue, which is the next to - * be removed, is array[nhead]. The location at the tail of the - * queue to which the next element will be added is - * array[nhead + nelem]. - * - * As items are added to the queue, nelem increases. - * As items are removed, nhead increases and nelem decreases. - * Any time the tail reaches the end of the allocated array, - * all the pointers are shifted to the left, so that the head - * is at the beginning of the array. - * If the array becomes more than 3/4 full, it doubles in size. - * - * The auxiliary stack can be used in a wrapper for re-using - * items popped from the queue. It is not made by default. - * - * For further implementation details, see queue.c. - *- */ - -/*! Expandable pointer queue for arbitrary void* data */ -struct L_Queue -{ - l_int32 nalloc; /*!< size of allocated ptr array */ - l_int32 nhead; /*!< location of head (in ptrs) from the */ - /*!< beginning of the array */ - l_int32 nelem; /*!< number of elements stored in the queue */ - void **array; /*!< ptr array */ - struct L_Stack *stack; /*!< auxiliary stack */ - -}; -typedef struct L_Queue L_QUEUE; - - -#endif /* LEPTONICA_QUEUE_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/rank.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/rank.c deleted file mode 100644 index ece9b717..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/rank.c +++ /dev/null @@ -1,544 +0,0 @@ -/*====================================================================* - - 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 rank.c - *- * - * Rank filter (gray and rgb) - * PIX *pixRankFilter() - * PIX *pixRankFilterRGB() - * PIX *pixRankFilterGray() - * - * Median filter - * PIX *pixMedianFilter() - * - * Rank filter (accelerated with downscaling) - * PIX *pixRankFilterWithScaling() - * - * What is a brick rank filter? - * - * A brick rank order filter evaluates, for every pixel in the image, - * a rectangular set of n = wf x hf pixels in its neighborhood (where the - * pixel in question is at the "center" of the rectangle and is - * included in the evaluation). It determines the value of the - * neighboring pixel that is the r-th smallest in the set, - * where r is some integer between 1 and n. The input rank parameter - * is a fraction between 0.0 and 1.0, where 0.0 represents the - * smallest value (r = 1) and 1.0 represents the largest value (r = n). - * A median filter is a rank filter where rank = 0.5. - * - * It is important to note that grayscale erosion is equivalent - * to rank = 0.0, and grayscale dilation is equivalent to rank = 1.0. - * These are much easier to calculate than the general rank value, - * thanks to the van Herk/Gil-Werman algorithm: - * http://www.leptonica.com/grayscale-morphology.html - * so you should use pixErodeGray() and pixDilateGray() for - * rank 0.0 and 1.0, rsp. See notes below in the function header. - * - * How is a rank filter implemented efficiently on an image? - * - * Sorting will not work. - * - * * The best sort algorithms are O(n*logn), where n is the number - * of values to be sorted (the area of the filter). For large - * filters this is an impractically large number. - * - * * Selection of the rank value is O(n). (To understand why it's not - * O(n*logn), see Numerical Recipes in C, 2nd edition, 1992, p. 355ff). - * This also still far too much computation for large filters. - * - * * Suppose we get clever. We really only need to do an incremental - * selection or sorting, because, for example, moving the filter - * down by one pixel causes one filter width of pixels to be added - * and another to be removed. Can we do this incrementally in - * an efficient way? Unfortunately, no. The sorted values will be - * in an array. Even if the filter width is 1, we can expect to - * have to move O(n) pixels, because insertion and deletion can happen - * anywhere in the array. By comparison, heapsort is excellent for - * incremental sorting, where the cost for insertion or deletion - * is O(logn), because the array itself doesn't need to - * be sorted into strictly increasing order. However, heapsort - * only gives the max (or min) value, not the general rank value. - * - * This leaves histograms. - * - * * Represented as an array. The problem with an array of 256 - * bins is that, in general, a significant fraction of the - * entire histogram must be summed to find the rank value bin. - * Suppose the filter size is 5x5. You spend most of your time - * adding zeroes. Ouch! - * - * * Represented as a linked list. This would overcome the - * summing-over-empty-bin problem, but you lose random access - * for insertions and deletions. No way. - * - * * Two histogram solution. Maintain two histograms with - * bin sizes of 1 and 16. Proceed from coarse to fine. - * First locate the coarse bin for the given rank, of which - * there are only 16. Then, in the 256 entry (fine) histogram, - * you need look at a maximum of 16 bins. For each output - * pixel, the average number of bins summed over, both in the - * coarse and fine histograms, is thus 16. - * - * If someone has a better method, please let me know! - * - * The rank filtering operation is relatively expensive, compared to most - * of the other imaging operations. The speed is only weakly dependent - * on the size of the rank filter. On standard hardware, it runs at - * about 10 Mpix/sec for a 50 x 50 filter, and 25 Mpix/sec for - * a 5 x 5 filter. For applications where the rank filter can be - * performed on a downscaled image, significant speedup can be - * achieved because the time goes as the square of the scaling factor. - * We provide an interface that handles the details, and only - * requires the amount of downscaling to be input. - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include "allheaders.h" - -/*----------------------------------------------------------------------* - * Rank order filter * - *----------------------------------------------------------------------*/ -/*! - * \brief pixRankFilter() - * - * \param[in] pixs 8 or 32 bpp; no colormap - * \param[in] wf, hf width and height of filter; each is >= 1 - * \param[in] rank in [0.0 ... 1.0] - * \return pixd of rank values, or NULL on error - * - * - * Notes: - * (1) This defines, for each pixel in pixs, a neighborhood of - * pixels given by a rectangle "centered" on the pixel. - * This set of wf*hf pixels has a distribution of values. - * For each component, if the values are sorted in increasing - * order, we choose the component such that rank*(wf*hf-1) - * pixels have a lower or equal value and - * (1-rank)*(wf*hf-1) pixels have an equal or greater value. - * (2) See notes in pixRankFilterGray() for further details. - *- */ -PIX * -pixRankFilter(PIX *pixs, - l_int32 wf, - l_int32 hf, - l_float32 rank) -{ -l_int32 d; - - PROCNAME("pixRankFilter"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetColormap(pixs) != NULL) - return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); - d = pixGetDepth(pixs); - if (d != 8 && d != 32) - return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); - if (wf < 1 || hf < 1) - return (PIX *)ERROR_PTR("wf < 1 || hf < 1", procName, NULL); - if (rank < 0.0 || rank > 1.0) - return (PIX *)ERROR_PTR("rank must be in [0.0, 1.0]", procName, NULL); - if (wf == 1 && hf == 1) /* no-op */ - return pixCopy(NULL, pixs); - - if (d == 8) - return pixRankFilterGray(pixs, wf, hf, rank); - else /* d == 32 */ - return pixRankFilterRGB(pixs, wf, hf, rank); -} - - -/*! - * \brief pixRankFilterRGB() - * - * \param[in] pixs 32 bpp - * \param[in] wf, hf width and height of filter; each is >= 1 - * \param[in] rank in [0.0 ... 1.0] - * \return pixd of rank values, or NULL on error - * - *- * Notes: - * (1) This defines, for each pixel in pixs, a neighborhood of - * pixels given by a rectangle "centered" on the pixel. - * This set of wf*hf pixels has a distribution of values. - * For each component, if the values are sorted in increasing - * order, we choose the component such that rank*(wf*hf-1) - * pixels have a lower or equal value and - * (1-rank)*(wf*hf-1) pixels have an equal or greater value. - * (2) Apply gray rank filtering to each component independently. - * (3) See notes in pixRankFilterGray() for further details. - *- */ -PIX * -pixRankFilterRGB(PIX *pixs, - l_int32 wf, - l_int32 hf, - l_float32 rank) -{ -PIX *pixr, *pixg, *pixb, *pixrf, *pixgf, *pixbf, *pixd; - - PROCNAME("pixRankFilterRGB"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 32) - return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); - if (wf < 1 || hf < 1) - return (PIX *)ERROR_PTR("wf < 1 || hf < 1", procName, NULL); - if (rank < 0.0 || rank > 1.0) - return (PIX *)ERROR_PTR("rank must be in [0.0, 1.0]", procName, NULL); - if (wf == 1 && hf == 1) /* no-op */ - return pixCopy(NULL, pixs); - - pixr = pixGetRGBComponent(pixs, COLOR_RED); - pixg = pixGetRGBComponent(pixs, COLOR_GREEN); - pixb = pixGetRGBComponent(pixs, COLOR_BLUE); - - pixrf = pixRankFilterGray(pixr, wf, hf, rank); - pixgf = pixRankFilterGray(pixg, wf, hf, rank); - pixbf = pixRankFilterGray(pixb, wf, hf, rank); - - pixd = pixCreateRGBImage(pixrf, pixgf, pixbf); - pixDestroy(&pixr); - pixDestroy(&pixg); - pixDestroy(&pixb); - pixDestroy(&pixrf); - pixDestroy(&pixgf); - pixDestroy(&pixbf); - return pixd; -} - - -/*! - * \brief pixRankFilterGray() - * - * \param[in] pixs 8 bpp; no colormap - * \param[in] wf, hf width and height of filter; each is >= 1 - * \param[in] rank in [0.0 ... 1.0] - * \return pixd of rank values, or NULL on error - * - *- * Notes: - * (1) This defines, for each pixel in pixs, a neighborhood of - * pixels given by a rectangle "centered" on the pixel. - * This set of wf*hf pixels has a distribution of values, - * and if they are sorted in increasing order, we choose - * the pixel such that rank*(wf*hf-1) pixels have a lower - * or equal value and (1-rank)*(wf*hf-1) pixels have an equal - * or greater value. - * (2) By this definition, the rank = 0.0 pixel has the lowest - * value, and the rank = 1.0 pixel has the highest value. - * (3) We add mirrored boundary pixels to avoid boundary effects, - * and put the filter center at (0, 0). - * (4) This dispatches to grayscale erosion or dilation if the - * filter dimensions are odd and the rank is 0.0 or 1.0, rsp. - * (5) Returns a copy if both wf and hf are 1. - * (6) Uses row-major or column-major incremental updates to the - * histograms depending on whether hf > wf or hv <= wf, rsp. - *- */ -PIX * -pixRankFilterGray(PIX *pixs, - l_int32 wf, - l_int32 hf, - l_float32 rank) -{ -l_int32 w, h, d, i, j, k, m, n, rankloc, wplt, wpld, val, sum; -l_int32 *histo, *histo16; -l_uint32 *datat, *linet, *datad, *lined; -PIX *pixt, *pixd; - - PROCNAME("pixRankFilterGray"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetColormap(pixs) != NULL) - return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); - pixGetDimensions(pixs, &w, &h, &d); - if (d != 8) - return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (wf < 1 || hf < 1) - return (PIX *)ERROR_PTR("wf < 1 || hf < 1", procName, NULL); - if (rank < 0.0 || rank > 1.0) - return (PIX *)ERROR_PTR("rank must be in [0.0, 1.0]", procName, NULL); - if (wf == 1 && hf == 1) /* no-op */ - return pixCopy(NULL, pixs); - - /* For rank = 0.0, this is a grayscale erosion, and for rank = 1.0, - * a dilation. Grayscale morphology operations are implemented - * for filters of odd dimension, so we dispatch to grayscale - * morphology if both wf and hf are odd. Otherwise, we - * slightly adjust the rank (to get the correct behavior) and - * use the slower rank filter here. */ - if (wf % 2 && hf % 2) { - if (rank == 0.0) - return pixErodeGray(pixs, wf, hf); - else if (rank == 1.0) - return pixDilateGray(pixs, wf, hf); - } - if (rank == 0.0) rank = 0.0001; - if (rank == 1.0) rank = 0.9999; - - /* Add wf/2 to each side, and hf/2 to top and bottom of the - * image, mirroring for accuracy and to avoid special-casing - * the boundary. */ - if ((pixt = pixAddMirroredBorder(pixs, wf / 2, wf / 2, hf / 2, hf / 2)) - == NULL) - return (PIX *)ERROR_PTR("pixt not made", procName, NULL); - - /* Set up the two histogram arrays. */ - histo = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); - histo16 = (l_int32 *)LEPT_CALLOC(16, sizeof(l_int32)); - rankloc = (l_int32)(rank * wf * hf); - - /* Place the filter center at (0, 0). This is just a - * convenient location, because it allows us to perform - * the rank filter over x:(0 ... w - 1) and y:(0 ... h - 1). */ - pixd = pixCreateTemplate(pixs); - datat = pixGetData(pixt); - wplt = pixGetWpl(pixt); - datad = pixGetData(pixd); - wpld = pixGetWpl(pixd); - - /* If hf > wf, it's more efficient to use row-major scanning. - * Otherwise, traverse the image in use column-major order. */ - if (hf > wf) { - for (j = 0; j < w; j++) { /* row-major */ - /* Start each column with clean histogram arrays. */ - for (n = 0; n < 256; n++) - histo[n] = 0; - for (n = 0; n < 16; n++) - histo16[n] = 0; - - for (i = 0; i < h; i++) { /* fast scan on columns */ - /* Update the histos for the new location */ - lined = datad + i * wpld; - if (i == 0) { /* do full histo */ - for (k = 0; k < hf; k++) { - linet = datat + (i + k) * wplt; - for (m = 0; m < wf; m++) { - val = GET_DATA_BYTE(linet, j + m); - histo[val]++; - histo16[val >> 4]++; - } - } - } else { /* incremental update */ - linet = datat + (i - 1) * wplt; - for (m = 0; m < wf; m++) { /* remove top line */ - val = GET_DATA_BYTE(linet, j + m); - histo[val]--; - histo16[val >> 4]--; - } - linet = datat + (i + hf - 1) * wplt; - for (m = 0; m < wf; m++) { /* add bottom line */ - val = GET_DATA_BYTE(linet, j + m); - histo[val]++; - histo16[val >> 4]++; - } - } - - /* Find the rank value */ - sum = 0; - for (n = 0; n < 16; n++) { /* search over coarse histo */ - sum += histo16[n]; - if (sum > rankloc) { - sum -= histo16[n]; - break; - } - } - if (n == 16) { /* avoid accessing out of bounds */ - L_WARNING("n = 16; reducing\n", procName); - n = 15; - sum -= histo16[n]; - } - k = 16 * n; /* starting value in fine histo */ - for (m = 0; m < 16; m++) { - sum += histo[k]; - if (sum > rankloc) { - SET_DATA_BYTE(lined, j, k); - break; - } - k++; - } - } - } - } else { /* wf >= hf */ - for (i = 0; i < h; i++) { /* column-major */ - /* Start each row with clean histogram arrays. */ - for (n = 0; n < 256; n++) - histo[n] = 0; - for (n = 0; n < 16; n++) - histo16[n] = 0; - lined = datad + i * wpld; - for (j = 0; j < w; j++) { /* fast scan on rows */ - /* Update the histos for the new location */ - if (j == 0) { /* do full histo */ - for (k = 0; k < hf; k++) { - linet = datat + (i + k) * wplt; - for (m = 0; m < wf; m++) { - val = GET_DATA_BYTE(linet, j + m); - histo[val]++; - histo16[val >> 4]++; - } - } - } else { /* incremental update at left and right sides */ - for (k = 0; k < hf; k++) { - linet = datat + (i + k) * wplt; - val = GET_DATA_BYTE(linet, j - 1); - histo[val]--; - histo16[val >> 4]--; - val = GET_DATA_BYTE(linet, j + wf - 1); - histo[val]++; - histo16[val >> 4]++; - } - } - - /* Find the rank value */ - sum = 0; - for (n = 0; n < 16; n++) { /* search over coarse histo */ - sum += histo16[n]; - if (sum > rankloc) { - sum -= histo16[n]; - break; - } - } - if (n == 16) { /* avoid accessing out of bounds */ - L_WARNING("n = 16; reducing\n", procName); - n = 15; - sum -= histo16[n]; - } - k = 16 * n; /* starting value in fine histo */ - for (m = 0; m < 16; m++) { - sum += histo[k]; - if (sum > rankloc) { - SET_DATA_BYTE(lined, j, k); - break; - } - k++; - } - } - } - } - - pixDestroy(&pixt); - LEPT_FREE(histo); - LEPT_FREE(histo16); - return pixd; -} - - -/*----------------------------------------------------------------------* - * Median filter * - *----------------------------------------------------------------------*/ -/*! - * \brief pixMedianFilter() - * - * \param[in] pixs 8 or 32 bpp; no colormap - * \param[in] wf, hf width and height of filter; each is >= 1 - * \return pixd of median values, or NULL on error - */ -PIX * -pixMedianFilter(PIX *pixs, - l_int32 wf, - l_int32 hf) -{ - PROCNAME("pixMedianFilter"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - return pixRankFilter(pixs, wf, hf, 0.5); -} - - -/*----------------------------------------------------------------------* - * Rank filter (accelerated with downscaling) * - *----------------------------------------------------------------------*/ -/*! - * \brief pixRankFilterWithScaling() - * - * \param[in] pixs 8 or 32 bpp; no colormap - * \param[in] wf, hf width and height of filter; each is >= 1 - * \param[in] rank in [0.0 ... 1.0] - * \param[in] scalefactor scale factor; must be >= 0.2 and <= 0.7 - * \return pixd of rank values, or NULL on error - * - *- * Notes: - * (1) This is a convenience function that downscales, does - * the rank filtering, and upscales. Because the down- - * and up-scaling functions are very fast compared to - * rank filtering, the time it takes is reduced from that - * for the simple rank filtering operation by approximately - * the square of the scaling factor. - *- */ -PIX * -pixRankFilterWithScaling(PIX *pixs, - l_int32 wf, - l_int32 hf, - l_float32 rank, - l_float32 scalefactor) -{ -l_int32 w, h, d, wfs, hfs; -PIX *pix1, *pix2, *pixd; - - PROCNAME("pixRankFilterWithScaling"); - - if (!pixs) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetColormap(pixs) != NULL) - return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); - d = pixGetDepth(pixs); - if (d != 8 && d != 32) - return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); - if (wf < 1 || hf < 1) - return (PIX *)ERROR_PTR("wf < 1 || hf < 1", procName, NULL); - if (rank < 0.0 || rank > 1.0) - return (PIX *)ERROR_PTR("rank must be in [0.0, 1.0]", procName, NULL); - if (wf == 1 && hf == 1) /* no-op */ - return pixCopy(NULL, pixs); - if (scalefactor < 0.2 || scalefactor > 0.7) { - L_ERROR("invalid scale factor; no scaling used\n", procName); - return pixRankFilter(pixs, wf, hf, rank); - } - - pix1 = pixScaleAreaMap(pixs, scalefactor, scalefactor); - wfs = L_MAX(1, (l_int32)(scalefactor * wf + 0.5)); - hfs = L_MAX(1, (l_int32)(scalefactor * hf + 0.5)); - pix2 = pixRankFilter(pix1, wfs, hfs, rank); - pixGetDimensions(pixs, &w, &h, NULL); - pixd = pixScaleToSize(pix2, w, h); - pixDestroy(&pix1); - pixDestroy(&pix2); - return pixd; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/rbtree.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/rbtree.c deleted file mode 100644 index 922033b2..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/rbtree.c +++ /dev/null @@ -1,902 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -/* - * Modified from the excellent code here: - * http://en.literateprograms.org/Red-black_tree_(C)?oldid=19567 - * which has been placed in the public domain under the Creative Commons - * CC0 1.0 waiver (http://creativecommons.org/publicdomain/zero/1.0/). - */ - -/*! - * \file rbtree.c - *- * - * Basic functions for using red-black trees. These are "nearly" balanced - * sorted trees with ordering by key that allows insertion, lookup and - * deletion of key/value pairs in log(n) time. - * - * We use red-black trees to implement our version of: - * * a map: a function that maps keys to values (e.g., int64 --> int64). - * * a set: a collection that is sorted by unique keys (without - * associated values) - * - * There are 5 invariant properties of RB trees: - * (1) Each node is either red or black. - * (2) The root node is black. - * (3) All leaves are black and contain no data (null). - * (4) Every red node has two children and both are black. This is - * equivalent to requiring the parent of every red node to be black. - * (5) All paths from any given node to its leaf nodes contain the - * same number of black nodes. - * - * Interface to red-black tree - * L_RBTREE *l_rbtreeCreate() - * RB_TYPE *l_rbtreeLookup() - * void l_rbtreeInsert() - * void l_rbtreeDelete() - * void l_rbtreeDestroy() - * L_RBTREE_NODE *l_rbtreeGetFirst() - * L_RBTREE_NODE *l_rbtreeGetNext() - * L_RBTREE_NODE *l_rbtreeGetLast() - * L_RBTREE_NODE *l_rbtreeGetPrev() - * l_int32 l_rbtreeGetCount() - * void l_rbtreePrint() - * - * General comparison function - * static l_int32 compareKeys() - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include "allheaders.h" - - /* The node color enum is only needed in the rbtree implementation */ -enum { - L_RED_NODE = 1, - L_BLACK_NODE = 2 -}; - - /* This makes it simpler to read the code */ -typedef L_RBTREE_NODE node; - - /* Lots of static helper functions */ -static void destroy_helper(node *n); -static void count_helper(node *n, l_int32 *pcount); -static void print_tree_helper(FILE *fp, node *n, l_int32 keytype, - l_int32 indent); - -static l_int32 compareKeys(l_int32 keytype, RB_TYPE left, RB_TYPE right); - -static node *grandparent(node *n); -static node *sibling(node *n); -static node *uncle(node *n); -static l_int32 node_color(node *n); -static node *new_node(RB_TYPE key, RB_TYPE value, l_int32 node_color, - node *left, node *right); -static node *lookup_node(L_RBTREE *t, RB_TYPE key); -static void rotate_left(L_RBTREE *t, node *n); -static void rotate_right(L_RBTREE *t, node *n); -static void replace_node(L_RBTREE *t, node *oldn, node *newn); -static void insert_case1(L_RBTREE *t, node *n); -static void insert_case2(L_RBTREE *t, node *n); -static void insert_case3(L_RBTREE *t, node *n); -static void insert_case4(L_RBTREE *t, node *n); -static void insert_case5(L_RBTREE *t, node *n); -static node *maximum_node(node *root); -static void delete_case1(L_RBTREE *t, node *n); -static void delete_case2(L_RBTREE *t, node *n); -static void delete_case3(L_RBTREE *t, node *n); -static void delete_case4(L_RBTREE *t, node *n); -static void delete_case5(L_RBTREE *t, node *n); -static void delete_case6(L_RBTREE *t, node *n); -static void verify_properties(L_RBTREE *t); - -#ifndef NO_CONSOLE_IO -#define VERIFY_RBTREE 0 /* only for debugging */ -#endif /* ~NO_CONSOLE_IO */ - -/* ------------------------------------------------------------- * - * Interface to Red-black Tree * - * ------------------------------------------------------------- */ -/*! - * \brief l_rbtreeCreate() - * - * \param[in] keytype defined by an enum for an RB_TYPE union - * \return rbtree container with empty ptr to the root - */ -L_RBTREE * -l_rbtreeCreate(l_int32 keytype) -{ - PROCNAME("l_rbtreeCreate"); - - if (keytype != L_INT_TYPE && keytype != L_UINT_TYPE && - keytype != L_FLOAT_TYPE && keytype) - return (L_RBTREE *)ERROR_PTR("invalid keytype", procName, NULL); - - L_RBTREE *t = (L_RBTREE *)LEPT_CALLOC(1, sizeof(L_RBTREE)); - t->keytype = keytype; - verify_properties(t); - return t; -} - -/*! - * \brief l_rbtreeLookup() - * - * \param[in] t rbtree, including root node - * \param[in] key find a node with this key - * \return &value a pointer to a union, if the node exists; else NULL - */ -RB_TYPE * -l_rbtreeLookup(L_RBTREE *t, - RB_TYPE key) -{ - PROCNAME("l_rbtreeLookup"); - - if (!t) - return (RB_TYPE *)ERROR_PTR("tree is null\n", procName, NULL); - - node *n = lookup_node(t, key); - return n == NULL ? NULL : &n->value; -} - -/*! - * \brief l_rbtreeInsert() - * - * \param[in] t rbtree, including root node - * \param[in] key insert a node with this key, if the key does not - * already exist in the tree - * \param[in] value typically an int, used for an index - * \return void - * - * - * Notes: - * (1) If a node with the key already exists, this just updates the value. - *- */ -void -l_rbtreeInsert(L_RBTREE *t, - RB_TYPE key, - RB_TYPE value) -{ -node *n, *inserted_node; - - PROCNAME("l_rbtreeInsert"); - - if (!t) { - L_ERROR("tree is null\n", procName); - return; - } - - inserted_node = new_node(key, value, L_RED_NODE, NULL, NULL); - if (t->root == NULL) { - t->root = inserted_node; - } else { - n = t->root; - while (1) { - int comp_result = compareKeys(t->keytype, key, n->key); - if (comp_result == 0) { - n->value = value; - LEPT_FREE(inserted_node); - return; - } else if (comp_result < 0) { - if (n->left == NULL) { - n->left = inserted_node; - break; - } else { - n = n->left; - } - } else { /* comp_result > 0 */ - if (n->right == NULL) { - n->right = inserted_node; - break; - } else { - n = n->right; - } - } - } - inserted_node->parent = n; - } - insert_case1(t, inserted_node); - verify_properties(t); -} - -/*! - * \brief l_rbtreeDelete() - * - * \param[in] t rbtree, including root node - * \param[in] key delete the node with this key - * \return void - */ -void -l_rbtreeDelete(L_RBTREE *t, - RB_TYPE key) -{ -node *n, *child; - - PROCNAME("l_rbtreeDelete"); - - if (!t) { - L_ERROR("tree is null\n", procName); - return; - } - - n = lookup_node(t, key); - if (n == NULL) return; /* Key not found, do nothing */ - if (n->left != NULL && n->right != NULL) { - /* Copy key/value from predecessor and then delete it instead */ - node *pred = maximum_node(n->left); - n->key = pred->key; - n->value = pred->value; - n = pred; - } - - /* n->left == NULL || n->right == NULL */ - child = n->right == NULL ? n->left : n->right; - if (node_color(n) == L_BLACK_NODE) { - n->color = node_color(child); - delete_case1(t, n); - } - replace_node(t, n, child); - if (n->parent == NULL && child != NULL) /* root should be black */ - child->color = L_BLACK_NODE; - LEPT_FREE(n); - - verify_properties(t); -} - -/*! - * \brief l_rbtreeDestroy() - * - * \param[in] pt pointer to tree; will be wet to null before returning - * \return void - * - *- * Notes: - * (1) Destroys the tree and nulls the input tree ptr. - *- */ -void -l_rbtreeDestroy(L_RBTREE **pt) -{ -node *n; - - if (!pt) return; - if (*pt == NULL) return; - n = (*pt)->root; - destroy_helper(n); - LEPT_FREE(*pt); - *pt = NULL; - return; -} - - /* postorder DFS */ -static void -destroy_helper(node *n) -{ - if (!n) return; - destroy_helper(n->left); - destroy_helper(n->right); - LEPT_FREE(n); -} - -/*! - * \brief l_rbtreeGetFirst() - * - * \param[in] t rbtree, including root node - * \return void - * - *- * Notes: - * (1) This is the first node in an in-order traversal. - *- */ -L_RBTREE_NODE * -l_rbtreeGetFirst(L_RBTREE *t) -{ -node *n; - - PROCNAME("l_rbtreeGetFirst"); - - if (!t) - return (L_RBTREE_NODE *)ERROR_PTR("tree is null", procName, NULL); - if (t->root == NULL) { - L_INFO("tree is empty\n", procName); - return NULL; - } - - /* Just go down the left side as far as possible */ - n = t->root; - while (n && n->left) - n = n->left; - return n; -} - -/*! - * \brief l_rbtreeGetNext() - * - * \param[in] n current node - * \return next node, or NULL if it's the last node - * - *- * Notes: - * (1) This finds the next node, in an in-order traversal, from - * the current node. - * (2) It is useful as an iterator for a map. - * (3) Call l_rbtreeGetFirst() to get the first node. - *- */ -L_RBTREE_NODE * -l_rbtreeGetNext(L_RBTREE_NODE *n) -{ - PROCNAME("l_rbtreeGetNext"); - - if (!n) - return (L_RBTREE_NODE *)ERROR_PTR("n not defined", procName, NULL); - - /* If there is a right child, go to it, and then go left all the - * way to the end. Otherwise go up to the parent; continue upward - * as long as you're on the right branch, but stop at the parent - * when you hit it from the left branch. */ - if (n->right) { - n = n->right; - while (n->left) - n = n->left; - return n; - } else { - while (n->parent && n->parent->right == n) - n = n->parent; - return n->parent; - } -} - -/*! - * \brief l_rbtreeGetLast() - * - * \param[in] t rbtree, including root node - * \return void - * - *- * Notes: - * (1) This is the last node in an in-order traversal. - *- */ -L_RBTREE_NODE * -l_rbtreeGetLast(L_RBTREE *t) -{ -node *n; - - PROCNAME("l_rbtreeGetLast"); - - if (!t) - return (L_RBTREE_NODE *)ERROR_PTR("tree is null", procName, NULL); - if (t->root == NULL) { - L_INFO("tree is empty\n", procName); - return NULL; - } - - /* Just go down the right side as far as possible */ - n = t->root; - while (n && n->right) - n = n->right; - return n; -} - -/*! - * \brief l_rbtreeGetPrev() - * - * \param[in] n current node - * \return next node, or NULL if it's the first node - * - *- * Notes: - * (1) This finds the previous node, in an in-order traversal, from - * the current node. - * (2) It is useful as an iterator for a map. - * (3) Call l_rbtreeGetLast() to get the last node. - *- */ -L_RBTREE_NODE * -l_rbtreeGetPrev(L_RBTREE_NODE *n) -{ - PROCNAME("l_rbtreeGetPrev"); - - if (!n) - return (L_RBTREE_NODE *)ERROR_PTR("n not defined", procName, NULL); - - /* If there is a left child, go to it, and then go right all the - * way to the end. Otherwise go up to the parent; continue upward - * as long as you're on the left branch, but stop at the parent - * when you hit it from the right branch. */ - if (n->left) { - n = n->left; - while (n->right) - n = n->right; - return n; - } else { - while (n->parent && n->parent->left == n) - n = n->parent; - return n->parent; - } -} - -/*! - * \brief l_rbtreeGetCount() - * - * \param[in] t rbtree - * \return count the number of nodes in the tree, or 0 on error - */ -l_int32 -l_rbtreeGetCount(L_RBTREE *t) -{ -l_int32 count = 0; -node *n; - - if (!t) return 0; - n = t->root; - count_helper(n, &count); - return count; -} - - /* preorder DFS */ -static void -count_helper(node *n, l_int32 *pcount) -{ - if (n) - (*pcount)++; - else - return; - - count_helper(n->left, pcount); - count_helper(n->right, pcount); -} - - -/*! - * \brief l_rbtreePrint() - * - * \param[in] fp file stream - * \param[in] t rbtree - * \return void - */ -void -l_rbtreePrint(FILE *fp, - L_RBTREE *t) -{ - PROCNAME("l_rbtreePrint"); - if (!fp) { - L_ERROR("stream not defined\n", procName); - return; - } - if (!t) { - L_ERROR("tree not defined\n", procName); - return; - } - - print_tree_helper(fp, t->root, t->keytype, 0); - fprintf(fp, "\n"); -} - -#define INDENT_STEP 4 - -static void -print_tree_helper(FILE *fp, - node *n, - l_int32 keytype, - l_int32 indent) -{ -l_int32 i; - - if (n == NULL) { - fprintf(fp, ""); - return; - } - if (n->right != NULL) { - print_tree_helper(fp, n->right, keytype, indent + INDENT_STEP); - } - for (i = 0; i < indent; i++) - fprintf(fp, " "); - if (n->color == L_BLACK_NODE) { - if (keytype == L_INT_TYPE) - fprintf(fp, "%lld\n", n->key.itype); - else if (keytype == L_UINT_TYPE) - fprintf(fp, "%llx\n", n->key.utype); - else if (keytype == L_FLOAT_TYPE) - fprintf(fp, "%f\n", n->key.ftype); - } else { - if (keytype == L_INT_TYPE) - fprintf(fp, "<%lld>\n", n->key.itype); - else if (keytype == L_UINT_TYPE) - fprintf(fp, "<%llx>\n", n->key.utype); - else if (keytype == L_FLOAT_TYPE) - fprintf(fp, "<%f>\n", n->key.ftype); - } - if (n->left != NULL) { - print_tree_helper(fp, n->left, keytype, indent + INDENT_STEP); - } -} - - -/* ------------------------------------------------------------- * - * Static key comparison function * - * ------------------------------------------------------------- */ -static l_int32 -compareKeys(l_int32 keytype, - RB_TYPE left, - RB_TYPE right) -{ -static char procName[] = "compareKeys"; - - if (keytype == L_INT_TYPE) { - if (left.itype < right.itype) - return -1; - else if (left.itype > right.itype) - return 1; - else { /* equality */ - return 0; - } - } else if (keytype == L_UINT_TYPE) { - if (left.utype < right.utype) - return -1; - else if (left.utype > right.utype) - return 1; - else { /* equality */ - return 0; - } - } else if (keytype == L_FLOAT_TYPE) { - if (left.ftype < right.ftype) - return -1; - else if (left.ftype > right.ftype) - return 1; - else { /* equality */ - return 0; - } - } else { - L_ERROR("unknown keytype %d\n", procName, keytype); - return 0; - } -} - - -/* ------------------------------------------------------------- * - * Static red-black tree helpers * - * ------------------------------------------------------------- */ -static node *grandparent(node *n) { - if (!n || !n->parent || !n->parent->parent) { - L_ERROR("root and child of root have no grandparent\n", "grandparent"); - return NULL; - } - return n->parent->parent; -} - -static node *sibling(node *n) { - if (!n || !n->parent) { - L_ERROR("root has no sibling\n", "sibling"); - return NULL; - } - if (n == n->parent->left) - return n->parent->right; - else - return n->parent->left; -} - -static node *uncle(node *n) { - if (!n || !n->parent || !n->parent->parent) { - L_ERROR("root and child of root have no uncle\n", "uncle"); - return NULL; - } - return sibling(n->parent); -} - -static l_int32 node_color(node *n) { - return n == NULL ? L_BLACK_NODE : n->color; -} - - -static node *new_node(RB_TYPE key, RB_TYPE value, l_int32 node_color, - node *left, node *right) { - node *result = (node *)LEPT_CALLOC(1, sizeof(node)); - result->key = key; - result->value = value; - result->color = node_color; - result->left = left; - result->right = right; - if (left != NULL) left->parent = result; - if (right != NULL) right->parent = result; - result->parent = NULL; - return result; -} - -static node *lookup_node(L_RBTREE *t, RB_TYPE key) { - node *n = t->root; - while (n != NULL) { - int comp_result = compareKeys(t->keytype, key, n->key); - if (comp_result == 0) { - return n; - } else if (comp_result < 0) { - n = n->left; - } else { /* comp_result > 0 */ - n = n->right; - } - } - return n; -} - -static void rotate_left(L_RBTREE *t, node *n) { - node *r = n->right; - replace_node(t, n, r); - n->right = r->left; - if (r->left != NULL) { - r->left->parent = n; - } - r->left = n; - n->parent = r; -} - -static void rotate_right(L_RBTREE *t, node *n) { - node *L = n->left; - replace_node(t, n, L); - n->left = L->right; - if (L->right != NULL) { - L->right->parent = n; - } - L->right = n; - n->parent = L; -} - -static void replace_node(L_RBTREE *t, node *oldn, node *newn) { - if (oldn->parent == NULL) { - t->root = newn; - } else { - if (oldn == oldn->parent->left) - oldn->parent->left = newn; - else - oldn->parent->right = newn; - } - if (newn != NULL) { - newn->parent = oldn->parent; - } -} - -static void insert_case1(L_RBTREE *t, node *n) { - if (n->parent == NULL) - n->color = L_BLACK_NODE; - else - insert_case2(t, n); -} - -static void insert_case2(L_RBTREE *t, node *n) { - if (node_color(n->parent) == L_BLACK_NODE) - return; /* Tree is still valid */ - else - insert_case3(t, n); -} - -static void insert_case3(L_RBTREE *t, node *n) { - if (node_color(uncle(n)) == L_RED_NODE) { - n->parent->color = L_BLACK_NODE; - uncle(n)->color = L_BLACK_NODE; - grandparent(n)->color = L_RED_NODE; - insert_case1(t, grandparent(n)); - } else { - insert_case4(t, n); - } -} - -static void insert_case4(L_RBTREE *t, node *n) { - if (n == n->parent->right && n->parent == grandparent(n)->left) { - rotate_left(t, n->parent); - n = n->left; - } else if (n == n->parent->left && n->parent == grandparent(n)->right) { - rotate_right(t, n->parent); - n = n->right; - } - insert_case5(t, n); -} - -static void insert_case5(L_RBTREE *t, node *n) { - n->parent->color = L_BLACK_NODE; - grandparent(n)->color = L_RED_NODE; - if (n == n->parent->left && n->parent == grandparent(n)->left) { - rotate_right(t, grandparent(n)); - } else if (n == n->parent->right && n->parent == grandparent(n)->right) { - rotate_left(t, grandparent(n)); - } else { - L_ERROR("identity confusion\n", "insert_case5"); - } -} - -static node *maximum_node(node *n) { - if (!n) { - L_ERROR("n not defined\n", "maximum_node"); - return NULL; - } - while (n->right != NULL) { - n = n->right; - } - return n; -} - -static void delete_case1(L_RBTREE *t, node *n) { - if (n->parent == NULL) - return; - else - delete_case2(t, n); -} - -static void delete_case2(L_RBTREE *t, node *n) { - if (node_color(sibling(n)) == L_RED_NODE) { - n->parent->color = L_RED_NODE; - sibling(n)->color = L_BLACK_NODE; - if (n == n->parent->left) - rotate_left(t, n->parent); - else - rotate_right(t, n->parent); - } - delete_case3(t, n); -} - -static void delete_case3(L_RBTREE *t, node *n) { - if (node_color(n->parent) == L_BLACK_NODE && - node_color(sibling(n)) == L_BLACK_NODE && - node_color(sibling(n)->left) == L_BLACK_NODE && - node_color(sibling(n)->right) == L_BLACK_NODE) { - sibling(n)->color = L_RED_NODE; - delete_case1(t, n->parent); - } else { - delete_case4(t, n); - } -} - -static void delete_case4(L_RBTREE *t, node *n) { - if (node_color(n->parent) == L_RED_NODE && - node_color(sibling(n)) == L_BLACK_NODE && - node_color(sibling(n)->left) == L_BLACK_NODE && - node_color(sibling(n)->right) == L_BLACK_NODE) { - sibling(n)->color = L_RED_NODE; - n->parent->color = L_BLACK_NODE; - } else { - delete_case5(t, n); - } -} - -static void delete_case5(L_RBTREE *t, node *n) { - if (n == n->parent->left && - node_color(sibling(n)) == L_BLACK_NODE && - node_color(sibling(n)->left) == L_RED_NODE && - node_color(sibling(n)->right) == L_BLACK_NODE) { - sibling(n)->color = L_RED_NODE; - sibling(n)->left->color = L_BLACK_NODE; - rotate_right(t, sibling(n)); - } else if (n == n->parent->right && - node_color(sibling(n)) == L_BLACK_NODE && - node_color(sibling(n)->right) == L_RED_NODE && - node_color(sibling(n)->left) == L_BLACK_NODE) { - sibling(n)->color = L_RED_NODE; - sibling(n)->right->color = L_BLACK_NODE; - rotate_left(t, sibling(n)); - } - delete_case6(t, n); -} - -static void delete_case6(L_RBTREE *t, node *n) { - sibling(n)->color = node_color(n->parent); - n->parent->color = L_BLACK_NODE; - if (n == n->parent->left) { - if (node_color(sibling(n)->right) != L_RED_NODE) { - L_ERROR("right sibling is not RED", "delete_case6"); - return; - } - sibling(n)->right->color = L_BLACK_NODE; - rotate_left(t, n->parent); - } else { - if (node_color(sibling(n)->left) != L_RED_NODE) { - L_ERROR("left sibling is not RED", "delete_case6"); - return; - } - sibling(n)->left->color = L_BLACK_NODE; - rotate_right(t, n->parent); - } -} - - -/* ------------------------------------------------------------- * - * Debugging: verify if tree is valid * - * ------------------------------------------------------------- */ -#if VERIFY_RBTREE -static void verify_property_1(node *root); -static void verify_property_2(node *root); -static void verify_property_4(node *root); -static void verify_property_5(node *root); -static void verify_property_5_helper(node *n, int black_count, - int* black_count_path); -#endif - -static void verify_properties(L_RBTREE *t) { -#if VERIFY_RBTREE - verify_property_1(t->root); - verify_property_2(t->root); - /* Property 3 is implicit */ - verify_property_4(t->root); - verify_property_5(t->root); -#endif -} - -#if VERIFY_RBTREE -static void verify_property_1(node *n) { - if (node_color(n) != L_RED_NODE && node_color(n) != L_BLACK_NODE) { - L_ERROR("color neither RED nor BLACK\n", "verify_property_1"); - return; - } - if (n == NULL) return; - verify_property_1(n->left); - verify_property_1(n->right); -} - -static void verify_property_2(node *root) { - if (node_color(root) != L_BLACK_NODE) - L_ERROR("root is not black!\n", "verify_property_2"); -} - -static void verify_property_4(node *n) { - if (node_color(n) == L_RED_NODE) { - if (node_color(n->left) != L_BLACK_NODE || - node_color(n->right) != L_BLACK_NODE || - node_color(n->parent) != L_BLACK_NODE) { - L_ERROR("children & parent not all BLACK", "verify_property_4"); - return; - } - } - if (n == NULL) return; - verify_property_4(n->left); - verify_property_4(n->right); -} - -static void verify_property_5(node *root) { - int black_count_path = -1; - verify_property_5_helper(root, 0, &black_count_path); -} - -static void verify_property_5_helper(node *n, int black_count, - int* path_black_count) { - if (node_color(n) == L_BLACK_NODE) { - black_count++; - } - if (n == NULL) { - if (*path_black_count == -1) { - *path_black_count = black_count; - } else if (*path_black_count != black_count) { - L_ERROR("incorrect black count", "verify_property_5_helper"); - } - return; - } - verify_property_5_helper(n->left, black_count, path_black_count); - verify_property_5_helper(n->right, black_count, path_black_count); -} -#endif diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/rbtree.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/rbtree.h deleted file mode 100644 index 6977d336..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/rbtree.h +++ /dev/null @@ -1,91 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -/* - * Modified from the excellent code here: - * http://en.literateprograms.org/Red-black_tree_(C)?oldid=19567 - * which has been placed in the public domain under the Creative Commons - * CC0 1.0 waiver (http://creativecommons.org/publicdomain/zero/1.0/). - * - * When the key is generated from a hash (e.g., string --> uint64), - * there is always the possibility of having collisions, but to make - * the collision probability very low requires using a large hash. - * For that reason, the key types are 64 bit quantities, which will result - * in a negligible probabililty of collisions for millions of hashed values. - * Using 8 byte keys instead of 4 byte keys requires a little more - * storage, but the simplification in being able to ignore collisions - * with the red-black trees for most applications is worth it. - */ - -#ifndef LEPTONICA_RBTREE_H -#define LEPTONICA_RBTREE_H - - /*! The three valid key types for red-black trees, maps and sets. */ -/*! RBTree Key Type */ -enum { - L_INT_TYPE = 1, - L_UINT_TYPE = 2, - L_FLOAT_TYPE = 3 -}; - - /*! - * Storage for keys and values for red-black trees, maps and sets. - * - * Note: - * (1) Keys and values of the valid key types are all 64-bit - * (2) (void *) can be used for values but not for keys. - *- */ -union Rb_Type { - l_int64 itype; - l_uint64 utype; - l_float64 ftype; - void *ptype; -}; -typedef union Rb_Type RB_TYPE; - -struct L_Rbtree { - struct L_Rbtree_Node *root; - l_int32 keytype; -}; -typedef struct L_Rbtree L_RBTREE; -typedef struct L_Rbtree L_AMAP; /* hide underlying implementation for map */ -typedef struct L_Rbtree L_ASET; /* hide underlying implementation for set */ - -struct L_Rbtree_Node { - union Rb_Type key; - union Rb_Type value; - struct L_Rbtree_Node *left; - struct L_Rbtree_Node *right; - struct L_Rbtree_Node *parent; - l_int32 color; -}; -typedef struct L_Rbtree_Node L_RBTREE_NODE; -typedef struct L_Rbtree_Node L_AMAP_NODE; /* hide tree implementation */ -typedef struct L_Rbtree_Node L_ASET_NODE; /* hide tree implementation */ - - -#endif /* LEPTONICA_RBTREE_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/readbarcode.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/readbarcode.c deleted file mode 100644 index 83b0cdef..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/readbarcode.c +++ /dev/null @@ -1,1498 +0,0 @@ -/*====================================================================* - - 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 readbarcode.c - *- * - * Basic operations to locate and identify the line widths - * in 1D barcodes. - * - * Top level - * SARRAY *pixProcessBarcodes() - * - * Next levels - * PIXA *pixExtractBarcodes() - * SARRAY *pixReadBarcodes() - * l_int32 pixReadBarcodeWidths() - * - * Location - * BOXA *pixLocateBarcodes() - * static PIX *pixGenerateBarcodeMask() - * - * Extraction and deskew - * PIXA *pixDeskewBarcodes() - * - * Process to get line widths - * NUMA *pixExtractBarcodeWidths1() - * NUMA *pixExtractBarcodeWidths2() - * NUMA *pixExtractBarcodeCrossings() - * - * Average adjacent rasters - * static NUMA *pixAverageRasterScans() - * - * Signal processing for barcode widths - * NUMA *numaQuantizeCrossingsByWidth() - * static l_int32 numaGetCrossingDistances() - * static NUMA *numaLocatePeakRanges() - * static NUMA *numaGetPeakCentroids() - * static NUMA *numaGetPeakWidthLUT() - * NUMA *numaQuantizeCrossingsByWindow() - * static l_int32 numaEvalBestWidthAndShift() - * static l_int32 numaEvalSyncError() - * - * - * NOTE CAREFULLY: This is "early beta" code. It has not been tuned - * to work robustly on a large database of barcode images. I'm putting - * it out so that people can play with it, find out how it breaks, and - * contribute decoders for other barcode formats. Both the functional - * interfaces and ABI will almost certainly change in the coming - * few months. The actual decoder, in bardecode.c, at present only - * works on the following codes: Code I2of5, Code 2of5, Code 39, Code 93 - * Codabar and UPCA. To add another barcode format, it is necessary - * to make changes in readbarcode.h and bardecode.c. - * The program prog/barcodetest shows how to run from the top level - * (image --> decoded data). - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include -#include "allheaders.h" -#include "readbarcode.h" - - /* Parameters for pixGenerateBarcodeMask() */ -static const l_int32 MAX_SPACE_WIDTH = 19; /* was 15 */ -static const l_int32 MAX_NOISE_WIDTH = 50; /* smaller than barcode width */ -static const l_int32 MAX_NOISE_HEIGHT = 30; /* smaller than barcode height */ - - /* Static functions */ -static PIX *pixGenerateBarcodeMask(PIX *pixs, l_int32 maxspace, - l_int32 nwidth, l_int32 nheight); -static NUMA *pixAverageRasterScans(PIX *pixs, l_int32 nscans); -static l_int32 numaGetCrossingDistances(NUMA *nas, NUMA **pnaedist, - NUMA **pnaodist, l_float32 *pmindist, - l_float32 *pmaxdist); -static NUMA *numaLocatePeakRanges(NUMA *nas, l_float32 minfirst, - l_float32 minsep, l_float32 maxmin); -static NUMA *numaGetPeakCentroids(NUMA *nahist, NUMA *narange); -static NUMA *numaGetPeakWidthLUT(NUMA *narange, NUMA *nacent); -static l_int32 numaEvalBestWidthAndShift(NUMA *nas, l_int32 nwidth, - l_int32 nshift, l_float32 minwidth, - l_float32 maxwidth, - l_float32 *pbestwidth, - l_float32 *pbestshift, - l_float32 *pbestscore); -static l_int32 numaEvalSyncError(NUMA *nas, l_int32 ifirst, l_int32 ilast, - l_float32 width, l_float32 shift, - l_float32 *pscore, NUMA **pnad); - - -#ifndef NO_CONSOLE_IO -#define DEBUG_DESKEW 1 -#define DEBUG_WIDTHS 0 -#endif /* ~NO_CONSOLE_IO */ - - -/*------------------------------------------------------------------------* - * Top level * - *------------------------------------------------------------------------*/ -/*! - * \brief pixProcessBarcodes() - * - * \param[in] pixs any depth - * \param[in] format L_BF_ANY, L_BF_CODEI2OF5, L_BF_CODE93, ... - * \param[in] method L_USE_WIDTHS, L_USE_WINDOWS - * \param[out] psaw [optional] sarray of bar widths - * \param[in] debugflag use 1 to generate debug output - * \return sarray text of barcodes, or NULL if none found or on error - */ -SARRAY * -pixProcessBarcodes(PIX *pixs, - l_int32 format, - l_int32 method, - SARRAY **psaw, - l_int32 debugflag) -{ -PIX *pixg; -PIXA *pixa; -SARRAY *sad; - - PROCNAME("pixProcessBarcodes"); - - if (psaw) *psaw = NULL; - if (!pixs) - return (SARRAY *)ERROR_PTR("pixs not defined", procName, NULL); - if (format != L_BF_ANY && !barcodeFormatIsSupported(format)) - return (SARRAY *)ERROR_PTR("unsupported format", procName, NULL); - if (method != L_USE_WIDTHS && method != L_USE_WINDOWS) - return (SARRAY *)ERROR_PTR("invalid method", procName, NULL); - - /* Get an 8 bpp image, no cmap */ - if (pixGetDepth(pixs) == 8 && !pixGetColormap(pixs)) - pixg = pixClone(pixs); - else - pixg = pixConvertTo8(pixs, 0); - - if ((pixa = pixExtractBarcodes(pixg, debugflag)) == NULL) { - pixDestroy(&pixg); - return (SARRAY *)ERROR_PTR("no barcode(s) found", procName, NULL); - } - - sad = pixReadBarcodes(pixa, format, method, psaw, debugflag); - - pixDestroy(&pixg); - pixaDestroy(&pixa); - return sad; -} - - -/*! - * \brief pixExtractBarcodes() - * - * \param[in] pixs 8 bpp, no colormap - * \param[in] debugflag use 1 to generate debug output - * \return pixa deskewed and cropped barcodes, or NULL if none found - * or on error - */ -PIXA * -pixExtractBarcodes(PIX *pixs, - l_int32 debugflag) -{ -l_int32 i, n; -l_float32 angle, conf; -BOX *box; -BOXA *boxa; -PIX *pixb, *pixm, *pixt; -PIXA *pixa; - - PROCNAME("pixExtractBarcodes"); - - if (!pixs || pixGetDepth(pixs) != 8 || pixGetColormap(pixs)) - return (PIXA *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - - /* Locate them; use small threshold for edges. */ - boxa = pixLocateBarcodes(pixs, 20, &pixb, &pixm); - n = boxaGetCount(boxa); - L_INFO("%d possible barcode(s) found\n", procName, n); - if (n == 0) { - boxaDestroy(&boxa); - pixDestroy(&pixb); - pixDestroy(&pixm); - return NULL; - } - - if (debugflag) { - boxaWriteStderr(boxa); - pixDisplay(pixb, 100, 100); - pixDisplay(pixm, 800, 100); - } - - /* Deskew each barcode individually */ - pixa = pixaCreate(n); - for (i = 0; i < n; i++) { - box = boxaGetBox(boxa, i, L_CLONE); - pixt = pixDeskewBarcode(pixs, pixb, box, 15, 20, &angle, &conf); - L_INFO("angle = %6.2f, conf = %6.2f\n", procName, angle, conf); - if (conf > 5.0) { - pixaAddPix(pixa, pixt, L_INSERT); - pixaAddBox(pixa, box, L_INSERT); - } else { - pixDestroy(&pixt); - boxDestroy(&box); - } - } - -#if DEBUG_DESKEW - pixt = pixaDisplayTiledInRows(pixa, 8, 1000, 1.0, 0, 30, 2); - pixWrite("junkpixt", pixt, IFF_PNG); - pixDestroy(&pixt); -#endif /* DEBUG_DESKEW */ - - pixDestroy(&pixb); - pixDestroy(&pixm); - boxaDestroy(&boxa); - return pixa; -} - - -/*! - * \brief pixReadBarcodes() - * - * \param[in] pixa of 8 bpp deskewed and cropped barcodes - * \param[in] format L_BF_ANY, L_BF_CODEI2OF5, L_BF_CODE93, ... - * \param[in] method L_USE_WIDTHS, L_USE_WINDOWS; - * \param[out] psaw [optional] sarray of bar widths - * \param[in] debugflag use 1 to generate debug output - * \return sa sarray of widths, one string for each barcode found, - * or NULL on error - */ -SARRAY * -pixReadBarcodes(PIXA *pixa, - l_int32 format, - l_int32 method, - SARRAY **psaw, - l_int32 debugflag) -{ -char *barstr, *data; -char emptystring[] = ""; -l_int32 i, j, n, nbars, ival; -NUMA *na; -PIX *pixt; -SARRAY *saw, *sad; - - PROCNAME("pixReadBarcodes"); - - if (psaw) *psaw = NULL; - if (!pixa) - return (SARRAY *)ERROR_PTR("pixa not defined", procName, NULL); - if (format != L_BF_ANY && !barcodeFormatIsSupported(format)) - return (SARRAY *)ERROR_PTR("unsupported format", procName, NULL); - if (method != L_USE_WIDTHS && method != L_USE_WINDOWS) - return (SARRAY *)ERROR_PTR("invalid method", procName, NULL); - - n = pixaGetCount(pixa); - saw = sarrayCreate(n); - sad = sarrayCreate(n); - for (i = 0; i < n; i++) { - /* Extract the widths of the lines in each barcode */ - pixt = pixaGetPix(pixa, i, L_CLONE); - na = pixReadBarcodeWidths(pixt, method, debugflag); - pixDestroy(&pixt); - if (!na) { - ERROR_INT("valid barcode widths not returned", procName, 1); - continue; - } - - /* Save the widths as a string */ - nbars = numaGetCount(na); - barstr = (char *)LEPT_CALLOC(nbars + 1, sizeof(char)); - for (j = 0; j < nbars; j++) { - numaGetIValue(na, j, &ival); - barstr[j] = 0x30 + ival; - } - sarrayAddString(saw, barstr, L_INSERT); - numaDestroy(&na); - - /* Decode the width strings */ - data = barcodeDispatchDecoder(barstr, format, debugflag); - if (!data) { - ERROR_INT("barcode not decoded", procName, 1); - sarrayAddString(sad, emptystring, L_COPY); - continue; - } - sarrayAddString(sad, data, L_INSERT); - } - - /* If nothing found, clean up */ - if (sarrayGetCount(saw) == 0) { - sarrayDestroy(&saw); - sarrayDestroy(&sad); - return (SARRAY *)ERROR_PTR("no valid barcode data", procName, NULL); - } - - if (psaw) - *psaw = saw; - else - sarrayDestroy(&saw); - - return sad; -} - - -/*! - * \brief pixReadBarcodeWidths() - * - * \param[in] pixs of 8 bpp deskewed and cropped barcode - * \param[in] method L_USE_WIDTHS, L_USE_WINDOWS; - * \param[in] debugflag use 1 to generate debug output - * \return na numa of widths (each in set {1,2,3,4}, or NULL on error - */ -NUMA * -pixReadBarcodeWidths(PIX *pixs, - l_int32 method, - l_int32 debugflag) -{ -l_float32 winwidth; -NUMA *na; - - PROCNAME("pixReadBarcodeWidths"); - - if (!pixs) - return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); - if (pixGetDepth(pixs) != 8) - return (NUMA *)ERROR_PTR("pixs not 8 bpp", procName, NULL); - if (method != L_USE_WIDTHS && method != L_USE_WINDOWS) - return (NUMA *)ERROR_PTR("invalid method", procName, NULL); - - /* Extract the widths of the lines in each barcode */ - if (method == L_USE_WIDTHS) - na = pixExtractBarcodeWidths1(pixs, 120, 0.25, NULL, NULL, - debugflag); - else /* method == L_USE_WINDOWS */ - na = pixExtractBarcodeWidths2(pixs, 120, &winwidth, - NULL, debugflag); -#if DEBUG_WIDTHS - if (method == L_USE_WINDOWS) - lept_stderr("Window width for barcode: %7.3f\n", winwidth); - numaWriteStderr(na); -#endif /* DEBUG_WIDTHS */ - - if (!na) - return (NUMA *)ERROR_PTR("barcode widths invalid", procName, NULL); - - return na; -} - - -/*------------------------------------------------------------------------* - * Locate barcode in image * - *------------------------------------------------------------------------*/ -/*! - * \brief pixLocateBarcodes() - * - * \param[in] pixs any depth - * \param[in] thresh for binarization of edge filter output; typ. 20 - * \param[out] ppixb [optional] binarized edge filtered input image - * \param[out] ppixm [optional] mask over barcodes - * \return boxa location of barcodes, or NULL if none found or on error - */ -BOXA * -pixLocateBarcodes(PIX *pixs, - l_int32 thresh, - PIX **ppixb, - PIX **ppixm) -{ -BOXA *boxa; -PIX *pix8, *pixe, *pixb, *pixm; - - PROCNAME("pixLocateBarcodes"); - - if (!pixs) - return (BOXA *)ERROR_PTR("pixs not defined", procName, NULL); - - /* Get an 8 bpp image, no cmap */ - if (pixGetDepth(pixs) == 8 && !pixGetColormap(pixs)) - pix8 = pixClone(pixs); - else - pix8 = pixConvertTo8(pixs, 0); - - /* Get a 1 bpp image of the edges */ - pixe = pixSobelEdgeFilter(pix8, L_ALL_EDGES); - pixb = pixThresholdToBinary(pixe, thresh); - pixInvert(pixb, pixb); - pixDestroy(&pix8); - pixDestroy(&pixe); - - pixm = pixGenerateBarcodeMask(pixb, MAX_SPACE_WIDTH, MAX_NOISE_WIDTH, - MAX_NOISE_HEIGHT); - boxa = pixConnComp(pixm, NULL, 8); - - if (ppixb) - *ppixb = pixb; - else - pixDestroy(&pixb); - if (ppixm) - *ppixm = pixm; - else - pixDestroy(&pixm); - - return boxa; -} - - -/*! - * \brief pixGenerateBarcodeMask() - * - * \param[in] pixs 1 bpp - * \param[in] maxspace largest space in the barcode, in pixels - * \param[in] nwidth opening 'width' to remove noise - * \param[in] nheight opening 'height' to remove noise - * \return pixm mask over barcodes, or NULL if none found or on error - * - * - * Notes: - * (1) For noise removal, 'width' and 'height' are referred to the - * barcode orientation. - * (2) If there is skew, the mask will not cover the barcode corners. - *- */ -static PIX * -pixGenerateBarcodeMask(PIX *pixs, - l_int32 maxspace, - l_int32 nwidth, - l_int32 nheight) -{ -PIX *pixt1, *pixt2, *pixd; - - PROCNAME("pixGenerateBarcodeMask"); - - if (!pixs || pixGetDepth(pixs) != 1) - return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); - - /* Identify horizontal barcodes */ - pixt1 = pixCloseBrick(NULL, pixs, maxspace + 1, 1); - pixt2 = pixOpenBrick(NULL, pixs, maxspace + 1, 1); - pixXor(pixt2, pixt2, pixt1); - pixOpenBrick(pixt2, pixt2, nwidth, nheight); - pixDestroy(&pixt1); - - /* Identify vertical barcodes */ - pixt1 = pixCloseBrick(NULL, pixs, 1, maxspace + 1); - pixd = pixOpenBrick(NULL, pixs, 1, maxspace + 1); - pixXor(pixd, pixd, pixt1); - pixOpenBrick(pixd, pixd, nheight, nwidth); - pixDestroy(&pixt1); - - /* Combine to get all barcodes */ - pixOr(pixd, pixd, pixt2); - pixDestroy(&pixt2); - - return pixd; -} - - -/*------------------------------------------------------------------------* - * Extract and deskew barcode * - *------------------------------------------------------------------------*/ -/*! - * \brief pixDeskewBarcode() - * - * \param[in] pixs input image; 8 bpp - * \param[in] pixb binarized edge-filtered input image - * \param[in] box identified region containing barcode - * \param[in] margin of extra pixels around box to extract - * \param[in] threshold for binarization; ~20 - * \param[out] pangle [optional] in degrees, clockwise is positive - * \param[out] pconf [optional] confidence - * \return pixd deskewed barcode, or NULL on error - * - *- * Notes: - * (1) The (optional) angle returned is the angle in degrees (cw positive) - * necessary to rotate the image so that it is deskewed. - *- */ -PIX * -pixDeskewBarcode(PIX *pixs, - PIX *pixb, - BOX *box, - l_int32 margin, - l_int32 threshold, - l_float32 *pangle, - l_float32 *pconf) -{ -l_int32 x, y, w, h, n; -l_float32 angle, angle1, angle2, conf, conf1, conf2, score1, score2, deg2rad; -BOX *boxe, *boxt; -BOXA *boxa, *boxat; -PIX *pixt1, *pixt2, *pixt3, *pixt4, *pixt5, *pixt6, *pixd; - - PROCNAME("pixDeskewBarcode"); - - if (!pixs || pixGetDepth(pixs) != 8) - return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - if (!pixb || pixGetDepth(pixb) != 1) - return (PIX *)ERROR_PTR("pixb undefined or not 1 bpp", procName, NULL); - if (!box) - return (PIX *)ERROR_PTR("box not defined or 1 bpp", procName, NULL); - - /* Clip out */ - deg2rad = 3.1415926535 / 180.; - boxGetGeometry(box, &x, &y, &w, &h); - boxe = boxCreate(x - 25, y - 25, w + 51, h + 51); - pixt1 = pixClipRectangle(pixb, boxe, NULL); - pixt2 = pixClipRectangle(pixs, boxe, NULL); - boxDestroy(&boxe); - - /* Deskew, looking at all possible orientations over 180 degrees */ - pixt3 = pixRotateOrth(pixt1, 1); /* look for vertical bar lines */ - pixt4 = pixClone(pixt1); /* look for horizontal bar lines */ - pixFindSkewSweepAndSearchScore(pixt3, &angle1, &conf1, &score1, - 1, 1, 0.0, 45.0, 2.5, 0.01); - pixFindSkewSweepAndSearchScore(pixt4, &angle2, &conf2, &score2, - 1, 1, 0.0, 45.0, 2.5, 0.01); - - /* Because we're using the boundary pixels of the barcodes, - * the peak can be sharper (and the confidence ratio higher) - * from the signal across the top and bottom of the barcode. - * However, the max score, which is the magnitude of the signal - * at the optimum skew angle, will be smaller, so we use the - * max score as the primary indicator of orientation. */ - if (score1 >= score2) { - conf = conf1; - if (conf1 > 6.0 && L_ABS(angle1) > 0.1) { - angle = angle1; - pixt5 = pixRotate(pixt2, deg2rad * angle1, L_ROTATE_AREA_MAP, - L_BRING_IN_WHITE, 0, 0); - } else { - angle = 0.0; - pixt5 = pixClone(pixt2); - } - } else { /* score2 > score1 */ - conf = conf2; - pixt6 = pixRotateOrth(pixt2, 1); - if (conf2 > 6.0 && L_ABS(angle2) > 0.1) { - angle = 90.0 + angle2; - pixt5 = pixRotate(pixt6, deg2rad * angle2, L_ROTATE_AREA_MAP, - L_BRING_IN_WHITE, 0, 0); - } else { - angle = 90.0; - pixt5 = pixClone(pixt6); - } - pixDestroy(&pixt6); - } - pixDestroy(&pixt3); - pixDestroy(&pixt4); - - /* Extract barcode plus a margin around it */ - boxa = pixLocateBarcodes(pixt5, threshold, 0, 0); - if ((n = boxaGetCount(boxa)) != 1) { - L_WARNING("barcode mask in %d components\n", procName, n); - boxat = boxaSort(boxa, L_SORT_BY_AREA, L_SORT_DECREASING, NULL); - } else { - boxat = boxaCopy(boxa, L_CLONE); - } - boxt = boxaGetBox(boxat, 0, L_CLONE); - boxGetGeometry(boxt, &x, &y, &w, &h); - boxe = boxCreate(x - margin, y - margin, w + 2 * margin, - h + 2 * margin); - pixd = pixClipRectangle(pixt5, boxe, NULL); - boxDestroy(&boxt); - boxDestroy(&boxe); - boxaDestroy(&boxa); - boxaDestroy(&boxat); - - if (pangle) *pangle = angle; - if (pconf) *pconf = conf; - - pixDestroy(&pixt1); - pixDestroy(&pixt2); - pixDestroy(&pixt5); - return pixd; -} - - -/*------------------------------------------------------------------------* - * Process to get line widths * - *------------------------------------------------------------------------*/ -/*! - * \brief pixExtractBarcodeWidths1() - * - * \param[in] pixs input image; 8 bpp - * \param[in] thresh estimated pixel threshold for crossing - * white <--> black; typ. ~120 - * \param[in] binfract histo binsize as a fraction of minsize; e.g., 0.25 - * \param[out] pnaehist [optional] histogram of black widths; NULL ok - * \param[out] pnaohist [optional] histogram of white widths; NULL ok - * \param[in] debugflag use 1 to generate debug output - * \return nad numa of barcode widths in encoded integer units, - * or NULL on error - * - *- * Notes: - * (1) The widths are alternating black/white, starting with black - * and ending with black. - * (2) This method uses the widths of the bars directly, in terms - * of the (float) number of pixels between transitions. - * The histograms of these widths for black and white bars is - * generated and interpreted. - *- */ -NUMA * -pixExtractBarcodeWidths1(PIX *pixs, - l_float32 thresh, - l_float32 binfract, - NUMA **pnaehist, - NUMA **pnaohist, - l_int32 debugflag) -{ -NUMA *nac, *nad; - - PROCNAME("pixExtractBarcodeWidths1"); - - if (!pixs || pixGetDepth(pixs) != 8) - return (NUMA *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - - /* Get the best estimate of the crossings, in pixel units */ - nac = pixExtractBarcodeCrossings(pixs, thresh, debugflag); - - /* Get the array of bar widths, starting with a black bar */ - nad = numaQuantizeCrossingsByWidth(nac, binfract, pnaehist, - pnaohist, debugflag); - - numaDestroy(&nac); - return nad; -} - - -/*! - * \brief pixExtractBarcodeWidths2() - * - * \param[in] pixs input image; 8 bpp - * \param[in] thresh estimated pixel threshold for crossing - * white <--> black; typ. ~120 - * \param[out] pwidth [optional] best decoding window width, in pixels - * \param[out] pnac [optional] number of transitions in each window - * \param[in] debugflag use 1 to generate debug output - * \return nad numa of barcode widths in encoded integer units, - * or NULL on error - * - *- * Notes: - * (1) The widths are alternating black/white, starting with black - * and ending with black. - * (2) The optional best decoding window width is the width of the window - * that is used to make a decision about whether a transition occurs. - * It is approximately the average width in pixels of the narrowest - * white and black bars (i.e., those corresponding to unit width). - * (3) The optional return signal %nac is a sequence of 0s, 1s, - * and perhaps a few 2s, giving the number of crossings in each window. - * On the occasion where there is a '2', it is interpreted as - * as ending two runs: the previous one and another one that has length 1. - *- */ -NUMA * -pixExtractBarcodeWidths2(PIX *pixs, - l_float32 thresh, - l_float32 *pwidth, - NUMA **pnac, - l_int32 debugflag) -{ -NUMA *nacp, *nad; - - PROCNAME("pixExtractBarcodeWidths2"); - - if (!pixs || pixGetDepth(pixs) != 8) - return (NUMA *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - - /* Get the best estimate of the crossings, in pixel units */ - nacp = pixExtractBarcodeCrossings(pixs, thresh, debugflag); - - /* Quantize the crossings to get actual windowed data */ - nad = numaQuantizeCrossingsByWindow(nacp, 2.0, pwidth, NULL, pnac, debugflag); - - numaDestroy(&nacp); - return nad; -} - - -/*! - * \brief pixExtractBarcodeCrossings() - * - * \param[in] pixs input image; 8 bpp - * \param[in] thresh estimated pixel threshold for crossing - * white <--> black; typ. ~120 - * \param[in] debugflag use 1 to generate debug output - * \return numa of crossings, in pixel units, or NULL on error - */ -NUMA * -pixExtractBarcodeCrossings(PIX *pixs, - l_float32 thresh, - l_int32 debugflag) -{ -l_int32 w; -l_float32 bestthresh; -NUMA *nas, *nax, *nay, *nad; - - PROCNAME("pixExtractBarcodeCrossings"); - - if (!pixs || pixGetDepth(pixs) != 8) - return (NUMA *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - - /* Scan pixels horizontally and average results */ - nas = pixAverageRasterScans(pixs, 51); - - /* Interpolate to get 4x the number of values */ - w = pixGetWidth(pixs); - numaInterpolateEqxInterval(0.0, 1.0, nas, L_QUADRATIC_INTERP, 0.0, - (l_float32)(w - 1), 4 * w + 1, &nax, &nay); - - if (debugflag) { - lept_mkdir("lept/barcode"); - GPLOT *gplot = gplotCreate("/tmp/lept/barcode/signal", GPLOT_PNG, - "Pixel values", "dist in pixels", "value"); - gplotAddPlot(gplot, nax, nay, GPLOT_LINES, "plot 1"); - gplotMakeOutput(gplot); - gplotDestroy(&gplot); - } - - /* Locate the crossings. Run multiple times with different - * thresholds, and choose a threshold in the center of the - * run of thresholds that all give the maximum number of crossings. */ - numaSelectCrossingThreshold(nax, nay, thresh, &bestthresh); - - /* Get the crossings with the best threshold. */ - nad = numaCrossingsByThreshold(nax, nay, bestthresh); - - numaDestroy(&nas); - numaDestroy(&nax); - numaDestroy(&nay); - return nad; -} - - -/*------------------------------------------------------------------------* - * Average adjacent rasters * - *------------------------------------------------------------------------*/ -/*! - * \brief pixAverageRasterScans() - * - * \param[in] pixs input image; 8 bpp - * \param[in] nscans number of adjacent scans, about the center vertically - * \return numa of average pixel values across image, or NULL on error - */ -static NUMA * -pixAverageRasterScans(PIX *pixs, - l_int32 nscans) -{ -l_int32 w, h, first, last, i, j, wpl, val; -l_uint32 *line, *data; -l_float32 *array; -NUMA *nad; - - PROCNAME("pixAverageRasterScans"); - - if (!pixs || pixGetDepth(pixs) != 8) - return (NUMA *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL); - - pixGetDimensions(pixs, &w, &h, NULL); - if (nscans <= h) { - first = 0; - last = h - 1; - nscans = h; - } else { - first = (h - nscans) / 2; - last = first + nscans - 1; - } - - nad = numaCreate(w); - numaSetCount(nad, w); - array = numaGetFArray(nad, L_NOCOPY); - wpl = pixGetWpl(pixs); - data = pixGetData(pixs); - for (j = 0; j < w; j++) { - for (i = first; i <= last; i++) { - line = data + i * wpl; - val = GET_DATA_BYTE(line, j); - array[j] += val; - } - array[j] = array[j] / (l_float32)nscans; - } - - return nad; -} - - -/*------------------------------------------------------------------------* - * Signal processing for barcode widths * - *------------------------------------------------------------------------*/ -/*! - * \brief numaQuantizeCrossingsByWidth() - * - * \param[in] nas numa of crossing locations, in pixel units - * \param[in] binfract histo binsize as a fraction of minsize; e.g., 0.25 - * \param[out] pnaehist [optional] histo of even (black) bar widths - * \param[out] pnaohist [optional] histo of odd (white) bar widths - * \param[in] debugflag 1 to generate plots of histograms of bar widths - * \return nad sequence of widths, in unit sizes, or NULL on error - * - *- * Notes: - * (1) This first computes the histogram of black and white bar widths, - * binned in appropriate units. There should be well-defined - * peaks, each corresponding to a specific width. The sequence - * of barcode widths (namely, the integers from the set {1,2,3,4}) - * is returned. - * (2) The optional returned histograms are binned in width units - * that are inversely proportional to %binfract. For example, - * if %binfract = 0.25, there are 4.0 bins in the distance of - * the width of the narrowest bar. - *- */ -NUMA * -numaQuantizeCrossingsByWidth(NUMA *nas, - l_float32 binfract, - NUMA **pnaehist, - NUMA **pnaohist, - l_int32 debugflag) -{ -l_int32 i, n, ned, nod, iw, width; -l_float32 val, minsize, maxsize, factor; -GPLOT *gplot; -NUMA *naedist, *naodist, *naehist, *naohist, *naecent, *naocent; -NUMA *naerange, *naorange, *naelut, *naolut, *nad; - - PROCNAME("numaQuantizeCrossingsByWidth"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - n = numaGetCount(nas); - if (n < 2) - return (NUMA *)ERROR_PTR("n < 2", procName, NULL); - if (binfract <= 0.0) - return (NUMA *)ERROR_PTR("binfract <= 0.0", procName, NULL); - - /* Get even and odd crossing distances */ - numaGetCrossingDistances(nas, &naedist, &naodist, &minsize, &maxsize); - - /* Bin the spans in units of binfract * minsize. These - * units are convenient because they scale to make at least - * 1/binfract bins in the smallest span (width). We want this - * number to be large enough to clearly separate the - * widths, but small enough so that the histogram peaks - * have very few if any holes (zeroes) within them. */ - naehist = numaMakeHistogramClipped(naedist, binfract * minsize, - (1.25 / binfract) * maxsize); - naohist = numaMakeHistogramClipped(naodist, binfract * minsize, - (1.25 / binfract) * maxsize); - - if (debugflag) { - lept_mkdir("lept/barcode"); - gplot = gplotCreate("/tmp/lept/barcode/histw", GPLOT_PNG, - "Raw width histogram", "Width", "Number"); - gplotAddPlot(gplot, NULL, naehist, GPLOT_LINES, "plot black"); - gplotAddPlot(gplot, NULL, naohist, GPLOT_LINES, "plot white"); - gplotMakeOutput(gplot); - gplotDestroy(&gplot); - } - - /* Compute the peak ranges, still in units of binfract * minsize. */ - naerange = numaLocatePeakRanges(naehist, 1.0 / binfract, - 1.0 / binfract, 0.0); - naorange = numaLocatePeakRanges(naohist, 1.0 / binfract, - 1.0 / binfract, 0.0); - - /* Find the centroid values of each peak */ - naecent = numaGetPeakCentroids(naehist, naerange); - naocent = numaGetPeakCentroids(naohist, naorange); - - /* Generate the lookup tables that map from the bar width, in - * units of (binfract * minsize), to the integerized barcode - * units (1, 2, 3, 4), which are the output integer widths - * between transitions. */ - naelut = numaGetPeakWidthLUT(naerange, naecent); - naolut = numaGetPeakWidthLUT(naorange, naocent); - - /* Get the widths. Because the LUT accepts our funny units, - * we first must convert the pixel widths to these units, - * which is what 'factor' does. */ - nad = numaCreate(0); - ned = numaGetCount(naedist); - nod = numaGetCount(naodist); - if (nod != ned - 1) - L_WARNING("ned != nod + 1\n", procName); - factor = 1.0 / (binfract * minsize); /* for converting units */ - for (i = 0; i < ned - 1; i++) { - numaGetFValue(naedist, i, &val); - width = (l_int32)(factor * val); - numaGetIValue(naelut, width, &iw); - numaAddNumber(nad, iw); -/* lept_stderr("even: val = %7.3f, width = %d, iw = %d\n", - val, width, iw); */ - numaGetFValue(naodist, i, &val); - width = (l_int32)(factor * val); - numaGetIValue(naolut, width, &iw); - numaAddNumber(nad, iw); -/* lept_stderr("odd: val = %7.3f, width = %d, iw = %d\n", - val, width, iw); */ - } - numaGetFValue(naedist, ned - 1, &val); - width = (l_int32)(factor * val); - numaGetIValue(naelut, width, &iw); - numaAddNumber(nad, iw); - - if (debugflag) { - lept_stderr(" ---- Black bar widths (pixels) ------ \n"); - numaWriteStderr(naedist); - lept_stderr(" ---- Histogram of black bar widths ------ \n"); - numaWriteStderr(naehist); - lept_stderr(" ---- Peak ranges in black bar histogram bins --- \n"); - numaWriteStderr(naerange); - lept_stderr(" ---- Peak black bar centroid width values ------ \n"); - numaWriteStderr(naecent); - lept_stderr(" ---- Black bar lookup table ------ \n"); - numaWriteStderr(naelut); - lept_stderr(" ---- White bar widths (pixels) ------ \n"); - numaWriteStderr(naodist); - lept_stderr(" ---- Histogram of white bar widths ------ \n"); - numaWriteStderr(naohist); - lept_stderr(" ---- Peak ranges in white bar histogram bins --- \n"); - numaWriteStderr(naorange); - lept_stderr(" ---- Peak white bar centroid width values ------ \n"); - numaWriteStderr(naocent); - lept_stderr(" ---- White bar lookup table ------ \n"); - numaWriteStderr(naolut); - } - - numaDestroy(&naedist); - numaDestroy(&naodist); - numaDestroy(&naerange); - numaDestroy(&naorange); - numaDestroy(&naecent); - numaDestroy(&naocent); - numaDestroy(&naelut); - numaDestroy(&naolut); - if (pnaehist) - *pnaehist = naehist; - else - numaDestroy(&naehist); - if (pnaohist) - *pnaohist = naohist; - else - numaDestroy(&naohist); - return nad; -} - - -/*! - * \brief numaGetCrossingDistances() - * - * \param[in] nas numa of crossing locations - * \param[out] pnaedist [optional] even distances between crossings - * \param[out] pnaodist [optional] odd distances between crossings - * \param[out] pmindist [optional] min distance between crossings - * \param[out] pmaxdist [optional] max distance between crossings - * \return 0 if OK, 1 on error - */ -static l_int32 -numaGetCrossingDistances(NUMA *nas, - NUMA **pnaedist, - NUMA **pnaodist, - l_float32 *pmindist, - l_float32 *pmaxdist) -{ -l_int32 i, n; -l_float32 val, newval, mindist, maxdist, dist; -NUMA *naedist, *naodist; - - PROCNAME("numaGetCrossingDistances"); - - if (pnaedist) *pnaedist = NULL; - if (pnaodist) *pnaodist = NULL; - if (pmindist) *pmindist = 0.0; - if (pmaxdist) *pmaxdist = 0.0; - if (!nas) - return ERROR_INT("nas not defined", procName, 1); - if ((n = numaGetCount(nas)) < 2) - return ERROR_INT("n < 2", procName, 1); - - /* Get numas of distances between crossings. Separate these - * into even (e.g., black) and odd (e.g., white) spans. - * For barcodes, the black spans are 0, 2, etc. These - * distances are in pixel units. */ - naedist = numaCreate(n / 2 + 1); - naodist = numaCreate(n / 2); - numaGetFValue(nas, 0, &val); - for (i = 1; i < n; i++) { - numaGetFValue(nas, i, &newval); - if (i % 2) - numaAddNumber(naedist, newval - val); - else - numaAddNumber(naodist, newval - val); - val = newval; - } - - /* The mindist and maxdist of the spans are in pixel units. */ - numaGetMin(naedist, &mindist, NULL); - numaGetMin(naodist, &dist, NULL); - mindist = L_MIN(dist, mindist); - numaGetMax(naedist, &maxdist, NULL); - numaGetMax(naodist, &dist, NULL); - maxdist = L_MAX(dist, maxdist); - L_INFO("mindist = %7.3f, maxdist = %7.3f\n", procName, mindist, maxdist); - - if (pnaedist) - *pnaedist = naedist; - else - numaDestroy(&naedist); - if (pnaodist) - *pnaodist = naodist; - else - numaDestroy(&naodist); - if (pmindist) *pmindist = mindist; - if (pmaxdist) *pmaxdist = maxdist; - return 0; -} - - -/*! - * \brief numaLocatePeakRanges() - * - * \param[in] nas numa of histogram of crossing widths - * \param[in] minfirst min location of center of first peak - * \param[in] minsep min separation between peak range centers - * \param[in] maxmin max allowed value for min histo value between peaks - * \return nad ranges for each peak found, in pairs, or NULL on error - * - *- * Notes: - * (1) Units of %minsep are the index into nas. - * This puts useful constraints on peak-finding. - * (2) If maxmin == 0.0, the value of nas[i] must go to 0.0 (or less) - * between peaks. - * (3) All calculations are done in units of the index into nas. - * The resulting ranges are therefore integers. - * (4) The output nad gives pairs of range values for successive peaks. - * Any location [i] for which maxmin = nas[i] = 0.0 will NOT be - * included in a peak range. This works fine for histograms where - * if nas[i] == 0.0, it means that there are no samples at [i]. - * (5) For barcodes, when this is used on a histogram of barcode - * widths, use maxmin = 0.0. This requires that there is at - * least one histogram bin corresponding to a width value between - * adjacent peak ranges that is unpopulated, making the separation - * of the histogram peaks unambiguous. - *- */ -static NUMA * -numaLocatePeakRanges(NUMA *nas, - l_float32 minfirst, - l_float32 minsep, - l_float32 maxmin) -{ -l_int32 i, n, inpeak, left; -l_float32 center, prevcenter, val; -NUMA *nad; - - PROCNAME("numaLocatePeakRanges"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - n = numaGetCount(nas); - nad = numaCreate(0); - - inpeak = FALSE; - prevcenter = minfirst - minsep - 1.0; - for (i = 0; i < n; i++) { - numaGetFValue(nas, i, &val); - if (inpeak == FALSE && val > maxmin) { - inpeak = TRUE; - left = i; - } else if (inpeak == TRUE && val <= maxmin) { /* end peak */ - center = (left + i - 1.0) / 2.0; - if (center - prevcenter >= minsep) { /* save new peak */ - inpeak = FALSE; - numaAddNumber(nad, left); - numaAddNumber(nad, i - 1); - prevcenter = center; - } else { /* attach to previous peak; revise the right edge */ - numaSetValue(nad, numaGetCount(nad) - 1, i - 1); - } - } - } - if (inpeak == TRUE) { /* save the last peak */ - numaAddNumber(nad, left); - numaAddNumber(nad, n - 1); - } - - return nad; -} - - -/*! - * \brief numaGetPeakCentroids() - * - * \param[in] nahist numa of histogram of crossing widths - * \param[in] narange numa of ranges of x-values for the peaks in %nahist - * \return nad centroids for each peak found; max of 4, corresponding - * to 4 different barcode line widths, or NULL on error - */ -static NUMA * -numaGetPeakCentroids(NUMA *nahist, - NUMA *narange) -{ -l_int32 i, j, nr, low, high; -l_float32 cent, sum, val; -NUMA *nad; - - PROCNAME("numaGetPeakCentroids"); - - if (!nahist) - return (NUMA *)ERROR_PTR("nahist not defined", procName, NULL); - if (!narange) - return (NUMA *)ERROR_PTR("narange not defined", procName, NULL); - nr = numaGetCount(narange) / 2; - - nad = numaCreate(4); - for (i = 0; i < nr; i++) { - numaGetIValue(narange, 2 * i, &low); - numaGetIValue(narange, 2 * i + 1, &high); - cent = 0.0; - sum = 0.0; - for (j = low; j <= high; j++) { - numaGetFValue(nahist, j, &val); - cent += j * val; - sum += val; - } - numaAddNumber(nad, cent / sum); - } - - return nad; -} - - -/*! - * \brief numaGetPeakWidthLUT() - * - * \param[in] narange numa of x-val ranges for the histogram width peaks - * \param[in] nacent numa of centroids of each peak -- up to 4 - * \return nalut lookup table from the width of a bar to one of the four - * integerized barcode units, or NULL on error - * - *- * Notes: - * (1) This generates the lookup table that maps from a sequence of widths - * (in some units) to the integerized barcode units (1, 2, 3, 4), - * which are the output integer widths between transitions. - * (2) The smallest width can be lost in float roundoff. To avoid - * losing it, we expand the peak range of the smallest width. - *- */ -static NUMA * -numaGetPeakWidthLUT(NUMA *narange, - NUMA *nacent) -{ -l_int32 i, j, nc, low, high, imax; -l_int32 assign[4]; -l_float32 *warray; -l_float32 max, rat21, rat32, rat42; -NUMA *nalut; - - PROCNAME("numaGetPeakWidthLUT"); - - if (!narange) - return (NUMA *)ERROR_PTR("narange not defined", procName, NULL); - if (!nacent) - return (NUMA *)ERROR_PTR("nacent not defined", procName, NULL); - nc = numaGetCount(nacent); /* half the size of narange */ - if (nc < 1 || nc > 4) - return (NUMA *)ERROR_PTR("nc must be 1, 2, 3, or 4", procName, NULL); - - /* Check the peak centroids for consistency with bar widths. - * The third peak can correspond to a width of either 3 or 4. - * Use ratios 3/2 and 4/2 instead of 3/1 and 4/1 because the - * former are more stable and closer to the expected ratio. */ - if (nc > 1) { - warray = numaGetFArray(nacent, L_NOCOPY); - if (warray[0] == 0) - return (NUMA *)ERROR_PTR("first peak has width 0.0", - procName, NULL); - rat21 = warray[1] / warray[0]; - if (rat21 < 1.5 || rat21 > 2.6) - L_WARNING("width ratio 2/1 = %f\n", procName, rat21); - if (nc > 2) { - rat32 = warray[2] / warray[1]; - if (rat32 < 1.3 || rat32 > 2.25) - L_WARNING("width ratio 3/2 = %f\n", procName, rat32); - } - if (nc == 4) { - rat42 = warray[3] / warray[1]; - if (rat42 < 1.7 || rat42 > 2.3) - L_WARNING("width ratio 4/2 = %f\n", procName, rat42); - } - } - - /* Set width assignments. - * The only possible ambiguity is with nc = 3 */ - for (i = 0; i < 4; i++) - assign[i] = i + 1; - if (nc == 3) { - if (rat32 > 1.75) - assign[2] = 4; - } - - /* Put widths into the LUT */ - numaGetMax(narange, &max, NULL); - imax = (l_int32)max; - nalut = numaCreate(imax + 1); - numaSetCount(nalut, imax + 1); /* fill the array with zeroes */ - for (i = 0; i < nc; i++) { - numaGetIValue(narange, 2 * i, &low); - if (i == 0) low--; /* catch smallest width */ - numaGetIValue(narange, 2 * i + 1, &high); - for (j = low; j <= high; j++) - numaSetValue(nalut, j, assign[i]); - } - - return nalut; -} - - -/*! - * \brief numaQuantizeCrossingsByWindow() - * - * \param[in] nas numa of crossing locations - * \param[in] ratio of max window size over min window size in search; - * typ. 2.0 - * \param[out] pwidth [optional] best window width - * \param[out] pfirstloc [optional] center of window for first xing - * \param[out] pnac [optional] array of window crossings (0, 1, 2) - * \param[in] debugflag 1 to generate various plots of intermediate results - * \return nad sequence of widths, in unit sizes, or NULL on error - * - *- * Notes: - * (1) The minimum size of the window is set by the minimum - * distance between zero crossings. - * (2) The optional return signal %nac is a sequence of 0s, 1s, - * and perhaps a few 2s, giving the number of crossings in each window. - * On the occasion where there is a '2', it is interpreted as - * ending two runs: the previous one and another one that has length 1. - *- */ -NUMA * -numaQuantizeCrossingsByWindow(NUMA *nas, - l_float32 ratio, - l_float32 *pwidth, - l_float32 *pfirstloc, - NUMA **pnac, - l_int32 debugflag) -{ -l_int32 i, nw, started, count, trans; -l_float32 minsize, minwidth, minshift, xfirst; -NUMA *nac, *nad; - - PROCNAME("numaQuantizeCrossingsByWindow"); - - if (!nas) - return (NUMA *)ERROR_PTR("nas not defined", procName, NULL); - if (numaGetCount(nas) < 2) - return (NUMA *)ERROR_PTR("nas size < 2", procName, NULL); - - /* Get the minsize, which is needed for the search for - * the window width (ultimately found as 'minwidth') */ - numaGetCrossingDistances(nas, NULL, NULL, &minsize, NULL); - - /* Compute the width and shift increments; start at minsize - * and go up to ratio * minsize */ - numaEvalBestWidthAndShift(nas, 100, 10, minsize, ratio * minsize, - &minwidth, &minshift, NULL); - - /* Refine width and shift calculation */ - numaEvalBestWidthAndShift(nas, 100, 10, 0.98 * minwidth, 1.02 * minwidth, - &minwidth, &minshift, NULL); - - L_INFO("best width = %7.3f, best shift = %7.3f\n", - procName, minwidth, minshift); - - /* Get the crossing array (0,1,2) for the best window width and shift */ - numaEvalSyncError(nas, 0, 0, minwidth, minshift, NULL, &nac); - if (pwidth) *pwidth = minwidth; - if (pfirstloc) { - numaGetFValue(nas, 0, &xfirst); - *pfirstloc = xfirst + minshift; - } - - /* Get the array of bar widths, starting with a black bar */ - nad = numaCreate(0); - nw = numaGetCount(nac); /* number of window measurements */ - started = FALSE; - count = 0; /* unnecessary init */ - for (i = 0; i < nw; i++) { - numaGetIValue(nac, i, &trans); - if (trans > 2) - L_WARNING("trans = %d > 2 !!!\n", procName, trans); - if (started) { - if (trans > 1) { /* i.e., when trans == 2 */ - numaAddNumber(nad, count); - trans--; - count = 1; - } - if (trans == 1) { - numaAddNumber(nad, count); - count = 1; - } else { - count++; - } - } - if (!started && trans) { - started = TRUE; - if (trans == 2) /* a whole bar in this window */ - numaAddNumber(nad, 1); - count = 1; - } - } - - if (pnac) - *pnac = nac; - else - numaDestroy(&nac); - return nad; -} - - -/*! - * \brief numaEvalBestWidthAndShift() - * - * \param[in] nas numa of crossing locations - * \param[in] nwidth number of widths to consider - * \param[in] nshift number of shifts to consider for each width - * \param[in] minwidth smallest width to consider - * \param[in] maxwidth largest width to consider - * \param[out] pbestwidth best size of window - * \param[out] pbestshift best shift for the window - * \param[out] pbestscore [optional] average squared error of dist - * of crossing signal from the center of the window - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) This does a linear sweep of widths, evaluating at %nshift - * shifts for each width, finding the (width, shift) pair that - * gives the minimum score. - *- */ -static l_int32 -numaEvalBestWidthAndShift(NUMA *nas, - l_int32 nwidth, - l_int32 nshift, - l_float32 minwidth, - l_float32 maxwidth, - l_float32 *pbestwidth, - l_float32 *pbestshift, - l_float32 *pbestscore) -{ -l_int32 i, j; -l_float32 delwidth, delshift, width, shift, score; -l_float32 bestwidth, bestshift, bestscore; - - PROCNAME("numaEvalBestWidthAndShift"); - - if (!nas) - return ERROR_INT("nas not defined", procName, 1); - if (!pbestwidth || !pbestshift) - return ERROR_INT("&bestwidth and &bestshift not defined", procName, 1); - - bestwidth = 0.0f; - bestshift = 0.0f; - bestscore = 1.0; - delwidth = (maxwidth - minwidth) / (nwidth - 1.0); - for (i = 0; i < nwidth; i++) { - width = minwidth + delwidth * i; - delshift = width / (l_float32)(nshift); - for (j = 0; j < nshift; j++) { - shift = -0.5 * (width - delshift) + j * delshift; - numaEvalSyncError(nas, 0, 0, width, shift, &score, NULL); - if (score < bestscore) { - bestscore = score; - bestwidth = width; - bestshift = shift; -#if DEBUG_FREQUENCY - lept_stderr("width = %7.3f, shift = %7.3f, score = %7.3f\n", - width, shift, score); -#endif /* DEBUG_FREQUENCY */ - } - } - } - - *pbestwidth = bestwidth; - *pbestshift = bestshift; - if (pbestscore) - *pbestscore = bestscore; - return 0; -} - - -/*! - * \brief numaEvalSyncError() - * - * \param[in] nas numa of crossing locations - * \param[in] ifirst first crossing to use - * \param[in] ilast last crossing to use; use 0 for all crossings - * \param[in] width size of window - * \param[in] shift of center of window w/rt first crossing - * \param[out] pscore [optional] average squared error of dist - * of crossing signal from the center of the window - * \param[out] pnad [optional] numa of 1s and 0s for crossings - * \return 0 if OK, 1 on error - * - *- * Notes: - * (1) The score is computed only on the part of the signal from the - * %ifirst to %ilast crossings. Use 0 for both of these to - * use all the crossings. The score is normalized for - * the number of crossings and with half-width of the window. - * (2) The optional return %nad is a sequence of 0s and 1s, where a '1' - * indicates a crossing in the window. - *- */ -static l_int32 -numaEvalSyncError(NUMA *nas, - l_int32 ifirst, - l_int32 ilast, - l_float32 width, - l_float32 shift, - l_float32 *pscore, - NUMA **pnad) -{ -l_int32 i, n, nc, nw, ival; -l_int32 iw; /* cell in which transition occurs */ -l_float32 score, xfirst, xlast, xleft, xc, xwc; -NUMA *nad; - - PROCNAME("numaEvalSyncError"); - - if (!nas) - return ERROR_INT("nas not defined", procName, 1); - if ((n = numaGetCount(nas)) < 2) - return ERROR_INT("nas size < 2", procName, 1); - if (ifirst < 0) ifirst = 0; - if (ilast <= 0) ilast = n - 1; - if (ifirst >= ilast) - return ERROR_INT("ifirst not < ilast", procName, 1); - nc = ilast - ifirst + 1; - - /* Set up an array corresponding to the (shifted) windows, - * and fill in the crossings. */ - score = 0.0; - numaGetFValue(nas, ifirst, &xfirst); - numaGetFValue(nas, ilast, &xlast); - nw = (l_int32) ((xlast - xfirst + 2.0 * width) / width); - nad = numaCreate(nw); - numaSetCount(nad, nw); /* init to all 0.0 */ - xleft = xfirst - width / 2.0 + shift; /* left edge of first window */ - for (i = ifirst; i <= ilast; i++) { - numaGetFValue(nas, i, &xc); - iw = (l_int32)((xc - xleft) / width); - xwc = xleft + (iw + 0.5) * width; /* center of cell iw */ - score += (xwc - xc) * (xwc - xc); - numaGetIValue(nad, iw, &ival); - numaSetValue(nad, iw, ival + 1); - } - - if (pscore) - *pscore = 4.0 * score / (width * width * (l_float32)nc); - if (pnad) - *pnad = nad; - else - numaDestroy(&nad); - - return 0; -} diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/readbarcode.h b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/readbarcode.h deleted file mode 100644 index 358ff4e5..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/readbarcode.h +++ /dev/null @@ -1,239 +0,0 @@ -/*====================================================================* - - 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. - *====================================================================*/ - -#ifndef LEPTONICA_READBARCODE_H -#define LEPTONICA_READBARCODE_H - - /* ----------------------------------------------------------------- * - * Flags for method of extracting barcode widths * - * ----------------------------------------------------------------- */ - -/*! Barcode Method */ -enum { - L_USE_WIDTHS = 1, /*!< use histogram of barcode widths */ - L_USE_WINDOWS = 2 /*!< find best window for decoding transitions */ -}; - - /* ----------------------------------------------------------------- * - * Flags for barcode formats * - * These are used both to identify a barcode format and to identify * - * the decoding method to use on a barcode. * - * ----------------------------------------------------------------- */ - -/*! Barcode Format */ -enum { - L_BF_UNKNOWN = 0, /*!< unknown format */ - L_BF_ANY = 1, /*!< try decoding with all known formats */ - L_BF_CODE128 = 2, /*!< decode with Code128 format */ - L_BF_EAN8 = 3, /*!< decode with EAN8 format */ - L_BF_EAN13 = 4, /*!< decode with EAN13 format */ - L_BF_CODE2OF5 = 5, /*!< decode with Code 2 of 5 format */ - L_BF_CODEI2OF5 = 6, /*!< decode with Interleaved 2 of 5 format */ - L_BF_CODE39 = 7, /*!< decode with Code39 format */ - L_BF_CODE93 = 8, /*!< decode with Code93 format */ - L_BF_CODABAR = 9, /*!< decode with Code93 format */ - L_BF_UPCA = 10 /*!< decode with UPC A format */ -}; - - /* ----------------------------------------------------------------- * - * Currently supported formats * - * Update these arrays as new formats are added. * - * ----------------------------------------------------------------- */ - -/*! Currently supported formats */ -static const l_int32 SupportedBarcodeFormat[] = { - L_BF_CODE2OF5, - L_BF_CODEI2OF5, - L_BF_CODE93, - L_BF_CODE39, - L_BF_CODABAR, - L_BF_UPCA, - L_BF_EAN13 -}; - -/*! Currently supported format names */ -static const char *SupportedBarcodeFormatName[] = { - "Code2of5", - "CodeI2of5", - "Code93", - "Code39", - "Codabar", - "Upca", - "Ean13" -}; -static const l_int32 NumSupportedBarcodeFormats = 7; /*!< Number of formats */ - - - /* ----------------------------------------------------------------- * - * Code 2 of 5 symbology * - * ----------------------------------------------------------------- */ -static const char *Code2of5[] = { - "111121211", "211111112", "112111112", "212111111", /* 0 - 3 */ - "111121112", "211121111", "112121111", "111111212", /* 4 - 7 */ - "211111211", "112111211", /* 8 - 9 */ - "21211", "21112" /* Start, Stop */ -}; - -static const l_int32 C25_START = 10; -static const l_int32 C25_STOP = 11; - - - /* ----------------------------------------------------------------- * - * Code Interleaved 2 of 5 symbology * - * ----------------------------------------------------------------- */ -static const char *CodeI2of5[] = { - "11221", "21112", "12112", "22111", "11212", /* 0 - 4 */ - "21211", "12211", "11122", "21121", "12121", /* 5 - 9 */ - "1111", "211" /* start, stop */ -}; - -static const l_int32 CI25_START = 10; -static const l_int32 CI25_STOP = 11; - - - /* ----------------------------------------------------------------- * - * Code 93 symbology * - * ----------------------------------------------------------------- */ -static const char *Code93[] = { - "131112", "111213", "111312", "111411", "121113", /* 0: 0 - 4 */ - "121212", "121311", "111114", "131211", "141111", /* 5: 5 - 9 */ - "211113", "211212", "211311", "221112", "221211", /* 10: A - E */ - "231111", "112113", "112212", "112311", "122112", /* 15: F - J */ - "132111", "111123", "111222", "111321", "121122", /* 20: K - O */ - "131121", "212112", "212211", "211122", "211221", /* 25: P - T */ - "221121", "222111", "112122", "112221", "122121", /* 30: U - Y */ - "123111", "121131", "311112", "311211", "321111", /* 35: Z,-,.,SP,$ */ - "112131", "113121", "211131", "131221", "312111", /* 40: /,+,%,($),(%) */ - "311121", "122211", "111141" /* 45: (/),(+), Start */ -}; - - /* Use "[]{}#" to represent special codes 43-47 */ -static const char Code93Val[] = - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%[]{}#"; - -static const l_int32 C93_START = 47; -static const l_int32 C93_STOP = 47; - - - /* ----------------------------------------------------------------- * - * Code 39 symbology * - * ----------------------------------------------------------------- */ -static const char *Code39[] = { - "111221211", "211211112", "112211112", "212211111", /* 0: 0 - 3 */ - "111221112", "211221111", "112221111", "111211212", /* 4: 4 - 7 */ - "211211211", "112211211", "211112112", "112112112", /* 8: 8 - B */ - "212112111", "111122112", "211122111", "112122111", /* 12: C - F */ - "111112212", "211112211", "112112211", "111122211", /* 16: G - J */ - "211111122", "112111122", "212111121", "111121122", /* 20: K - N */ - "211121121", "112121121", "111111222", "211111221", /* 24: O - R */ - "112111221", "111121221", "221111112", "122111112", /* 28: S - V */ - "222111111", "121121112", "221121111", "122121111", /* 32: W - Z */ - "121111212", "221111211", "122111211", "121212111", /* 36: -,.,SP,$ */ - "121211121", "121112121", "111212121", "121121211" /* 40: /,+,%,* */ -}; - - /* Use "*" to represent the Start and Stop codes (43) */ -static const char Code39Val[] = - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*"; - -static const l_int32 C39_START = 43; -static const l_int32 C39_STOP = 43; - - - /* ----------------------------------------------------------------- * - * Codabar symbology * - * ----------------------------------------------------------------- */ -static const char *Codabar[] = { - "1111122", "1111221", "1112112", "2211111", "1121121", /* 0: 0 - 4 */ - "2111121", "1211112", "1211211", "1221111", "2112111", /* 5: 5 - 9 */ - "1112211", "1122111", "2111212", "2121112", "2121211", /* 10: -,$,:,/,. */ - "1121212", "1122121", "1212112", "1112122", "1112221" /* 15: +,A,B,C,D */ -}; - - /* Ascii representations for codes 16-19: (A or T), (B or N), (C or *), - * (D or E). These are used in pairs for the Start and Stop codes. */ -static const char CodabarVal[] = "0123456789-$:/.+ABCD"; - - - /* ----------------------------------------------------------------- * - * UPC-A symbology * - * ----------------------------------------------------------------- */ -static const char *Upca[] = { - "3211", "2221", "2122", "1411", "1132", /* 0: 0 - 4 */ - "1231", "1114", "1312", "1213", "3112", /* 5: 5 - 9 */ - "111", "111", "11111" /* 10: Start, Stop, Mid */ -}; - -static const l_int32 UPCA_START = 10; -static const l_int32 UPCA_STOP = 11; -static const l_int32 UPCA_MID = 12; - - - /* ----------------------------------------------------------------- * - * Code128 symbology * - * ----------------------------------------------------------------- */ -static const char *Code128[] = { - "212222", "222122", "222221", "121223", "121322", /* 0 - 4 */ - "131222", "122213", "122312", "132212", "221213", /* 5 - 9 */ - "221312", "231212", "112232", "122132", "122231", /* 10 - 14 */ - "113222", "123122", "123221", "223211", "221132", /* 15 - 19 */ - "221231", "213212", "223112", "312131", "311222", /* 20 - 24 */ - "321122", "321221", "312212", "322112", "322211", /* 25 - 29 */ - "212123", "212321", "232121", "111323", "131123", /* 30 - 34 */ - "131321", "112313", "132113", "132311", "211313", /* 35 - 39 */ - "231113", "231311", "112133", "112331", "132131", /* 40 - 44 */ - "113123", "113321", "133121", "313121", "211331", /* 45 - 49 */ - "231131", "213113", "213311", "213131", "311123", /* 50 - 54 */ - "311321", "331121", "312113", "312311", "332111", /* 55 - 59 */ - "314111", "221411", "431111", "111224", "111422", /* 60 - 64 */ - "121124", "121421", "141122", "141221", "112214", /* 65 - 69 */ - "112412", "122114", "122411", "142112", "142211", /* 70 - 74 */ - "241211", "221114", "413111", "241112", "134111", /* 75 - 79 */ - "111242", "121142", "121241", "114212", "124112", /* 80 - 84 */ - "124211", "411212", "421112", "421211", "212141", /* 85 - 89 */ - "214121", "412121", "111143", "111341", "131141", /* 90 - 94 */ - "114113", "114311", "411113", "411311", "113141", /* 95 - 99 */ - "114131", "311141", "411131", "211412", "211214", /* 100 - 104 */ - "211232", "2331112" /* 105 - 106 */ -}; - -static const l_int32 C128_FUN_3 = 96; /* in A or B only; in C it is 96 */ -static const l_int32 C128_FUNC_2 = 97; /* in A or B only; in C it is 97 */ -static const l_int32 C128_SHIFT = 98; /* in A or B only; in C it is 98 */ -static const l_int32 C128_GOTO_C = 99; /* in A or B only; in C it is 99 */ -static const l_int32 C128_GOTO_B = 100; -static const l_int32 C128_GOTO_A = 101; -static const l_int32 C128_FUNC_1 = 102; -static const l_int32 C128_START_A = 103; -static const l_int32 C128_START_B = 104; -static const l_int32 C128_START_C = 105; -static const l_int32 C128_STOP = 106; - /* code 128 symbols are 11 units */ -static const l_int32 C128_SYMBOL_WIDTH = 11; - - - -#endif /* LEPTONICA_READBARCODE_H */ diff --git a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/readfile.c b/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/readfile.c deleted file mode 100644 index 07ba5cde..00000000 --- a/3rdparty/gpdf/3rdparty/tesseract/include/leptonica/readfile.c +++ /dev/null @@ -1,1627 +0,0 @@ -/*====================================================================* - - 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 readfile.c: reads image on file into memory - *- * - * Top-level functions for reading images from file - * PIXA *pixaReadFiles() - * PIXA *pixaReadFilesSA() - * PIX *pixRead() - * PIX *pixReadWithHint() - * PIX *pixReadIndexed() - * PIX *pixReadStream() - * - * Read header information from file - * l_int32 pixReadHeader() - * - * Format finders - * l_int32 findFileFormat() - * l_int32 findFileFormatStream() - * l_int32 findFileFormatBuffer() - * l_int32 fileFormatIsTiff() - * - * Read from memory - * PIX *pixReadMem() - * l_int32 pixReadHeaderMem() - * - * Output image file information - * void writeImageFileInfo() - * - * Test function for I/O with different formats - * l_int32 ioFormatTest() - * - * Supported file formats: - * (1) Reading is supported without any external libraries: - * bmp - * pnm (including pbm, pgm, etc) - * spix (raw serialized) - * (2) Reading is supported with installation of external libraries: - * png - * jpg (standard jfif version) - * tiff (including most varieties of compression) - * gif - * webp - * jp2 (jpeg 2000) - * (3) Other file types will get an "unknown format" error. - *- */ - -#ifdef HAVE_CONFIG_H -#include-#endif /* HAVE_CONFIG_H */ - -#include