优化OCR识别效率,变更cmaketxt 里边链接关系

This commit is contained in:
lovelyyoung 2021-10-20 13:47:08 +08:00
parent c918dcbf54
commit 31a90e8e39
889 changed files with 140 additions and 466621 deletions

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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
* <pre>
*
* 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()
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <math.h>
#include "allheaders.h"
/*-------------------------------------------------------------*
* Composable coordinate transforms *
*-------------------------------------------------------------*/
/*!
* \brief createMatrix2dTranslate()
*
* \param[in] transx x component of translation wrt. the origin
* \param[in] transy y component of translation wrt. the origin
* \return 3x3 transform matrix, or NULL on error
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) See createMatrix2dTranslate() for details of transform.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) See createMatrix2dScale() for details of transform.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
/* General and configuration defs */
#include "endianness.h"
#include "environ.h"
/* Generic and non-image-specific containers */
#include "array.h"
#include "bbuffer.h"
#include "heap.h"
#include "list.h"
#include "ptra.h"
#include "queue.h"
#include "rbtree.h"
#include "stack.h"
/* Imaging */
#include "arrayaccess.h"
#include "bmf.h"
#include "ccbord.h"
#include "dewarp.h"
#include "gplot.h"
#include "imageio.h"
#include "jbclass.h"
#include "morph.h"
#include "pix.h"
#include "recog.h"
#include "regutils.h"
#include "stringcode.h"
#include "sudoku.h"
#include "watershed.h"
#endif /* LEPTONICA_ALLTYPES_H */

View File

@ -1,158 +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_H
#define LEPTONICA_ARRAY_H
/*!
* \file array.h
*
* <pre>
* 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
* </pre>
*/
/*------------------------------------------------------------------------*
* 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 */

View File

@ -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
* <pre>
*
* 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.
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include "allheaders.h"
/*----------------------------------------------------------------------*
* Access within an array of 32-bit words *
*----------------------------------------------------------------------*/
/*!
* \brief l_getDataBit()
*
* \param[in] line ptr to beginning of data line
* \param[in] n pixel index
* \return val of the nth 1-bit pixel.
*/
l_int32
l_getDataBit(const void *line,
l_int32 n)
{
return (*((const l_uint32 *)line + (n >> 5)) >> (31 - (n & 31))) & 1;
}
/*!
* \brief l_setDataBit()
*
* \param[in] line ptr to beginning of data line
* \param[in] n pixel index
* \return void
*
* Action: sets the pixel to 1
*/
void
l_setDataBit(void *line,
l_int32 n)
{
*((l_uint32 *)line + (n >> 5)) |= (0x80000000 >> (n & 31));
}
/*!
* \brief l_clearDataBit()
*
* \param[in] line ptr to beginning of data line
* \param[in] n pixel index
* \return void
*
* Action: sets the 1-bit pixel to 0
*/
void
l_clearDataBit(void *line,
l_int32 n)
{
*((l_uint32 *)line + (n >> 5)) &= ~(0x80000000 >> (n & 31));
}
/*!
* \brief l_setDataBitVal()
*
* \param[in] line ptr to beginning of data line
* \param[in] n pixel index
* \param[in] val val to be inserted: 0 or 1
* \return void
*
* <pre>
* 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);
* </pre>
*/
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;
}

View File

@ -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
*
* <pre>
* 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%.
* </pre>
*/
#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 */

File diff suppressed because it is too large Load Diff

View File

@ -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
* <pre>
*
* 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.
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <math.h>
#include "allheaders.h"
/* Min to travel after finding max before abandoning peak */
static const l_int32 MinDistInPeak = 35;
/* Thresholds for peaks and zeros, relative to the max peak */
static const l_int32 PeakThresholdRatio = 20;
static const l_int32 ZeroThresholdRatio = 100;
/* Default values for determining local skew */
static const l_int32 DefaultSlices = 10;
static const l_int32 DefaultSweepReduction = 2;
static const l_int32 DefaultBsReduction = 1;
static const l_float32 DefaultSweepRange = 5.; /* degrees */
static const l_float32 DefaultSweepDelta = 1.; /* degrees */
static const l_float32 DefaultMinbsDelta = 0.01; /* degrees */
/* Overlap slice fraction added to top and bottom of each slice */
static const l_float32 OverlapFraction = 0.5;
/* Minimum allowed confidence (ratio) for accepting a value */
static const l_float32 MinAllowedConfidence = 3.0;
/*---------------------------------------------------------------------*
* Locate text baselines in an image *
*---------------------------------------------------------------------*/
/*!
* \brief pixFindBaselines()
*
* \param[in] pixs 1 bpp, 300 ppi
* \param[out] ppta [optional] pairs of pts corresponding to
* approx. ends of each text line
* \param[in] pixadb for debug output; use NULL to skip
* \return na of baseline y values, or NULL on error
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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;
}

View File

@ -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
* <pre>
*
* 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().
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
/* Bounds on array size */
static const l_uint32 MaxArraySize = 1000000000; /* 10^9 bytes */
static const l_int32 InitialArraySize = 1024; /*!< n'importe quoi */
/*--------------------------------------------------------------------------*
* BBuffer create/destroy *
*--------------------------------------------------------------------------*/
/*!
* \brief bbufferCreate()
*
* \param[in] indata address in memory [optional]
* \param[in] nalloc size of byte array to be alloc'd 0 for default
* \return bbuffer, or NULL on error
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) Destroys the byte array in the bbuffer and then the bbuffer;
* then nulls the contents of the input ptr.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) Copies data to newly allocated array; then destroys the bbuffer.
* </pre>
*/
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
*
* <pre>
* 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().
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) reallocNew() copies all bb->nalloc bytes, even though
* only bb->n are data.
* </pre>
*/
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;
}

View File

@ -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
*
* <pre>
* 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.
* </pre>
*/
/*! 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 */

View File

@ -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
* <pre>
*
* 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).
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <math.h>
#include "allheaders.h"
#include "bilateral.h"
static L_BILATERAL *bilateralCreate(PIX *pixs, l_float32 spatial_stdev,
l_float32 range_stdev, l_int32 ncomps,
l_int32 reduction);
static PIX *bilateralApply(L_BILATERAL *bil);
static void bilateralDestroy(L_BILATERAL **pbil);
#ifndef NO_CONSOLE_IO
#define DEBUG_BILATERAL 0
#endif /* ~NO_CONSOLE_IO */
/*--------------------------------------------------------------------------*
* Top level approximate separable grayscale or color bilateral filtering *
*--------------------------------------------------------------------------*/
/*!
* \brief pixBilateral()
*
* \param[in] pixs 8 bpp gray or 32 bpp rgb, 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 pixd bilateral filtered image, or NULL on error
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) See pixBilateral() for constraints on the input parameters.
* (2) See pixBilateral() for algorithm details.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) See pixBilateralExact().
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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;
}

View File

@ -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
*
* <pre>
* 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.
* </pre>
*/
/*------------------------------------------------------------------------*
* 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 */

View File

@ -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
* <pre>
*
* 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.
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include <math.h>
#include "allheaders.h"
extern l_float32 AlphaMaskBorderVals[2];
/*-------------------------------------------------------------*
* Sampled bilinear image transformation *
*-------------------------------------------------------------*/
/*!
* \brief pixBilinearSampledPta()
*
* \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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) Brings in either black or white pixels from the boundary
* (2) Removes any existing colormap, if necessary, before transforming
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) Brings in either black or white pixels from the boundary
* (2) Removes any existing colormap, if necessary, before transforming
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) This finds the nearest pixel coordinates of the transformed point.
* (2) It does not check ptrs for returned data!
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) This computes the floating point location of the transformed point.
* (2) It does not check ptrs for returned data!
* </pre>
*/
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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
* <pre>
*
* 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()
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
/* Static table functions and tables */
static l_uint16 * makeExpandTab2x(void);
static l_uint32 * makeExpandTab4x(void);
static l_uint32 * makeExpandTab8x(void);
static l_uint32 expandtab16[] = {
0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff};
/*------------------------------------------------------------------*
* Replicated expansion (integer scaling) *
*------------------------------------------------------------------*/
/*!
* \brief pixExpandBinaryReplicate()
*
* \param[in] pixs 1 bpp
* \param[in] xfact integer scale factor for horiz. replicative expansion
* \param[in] yfact integer scale factor for vertical replicative expansion
* \return pixd scaled up, or NULL on error
*/
PIX *
pixExpandBinaryReplicate(PIX *pixs,
l_int32 xfact,
l_int32 yfact)
{
l_int32 w, h, d, wd, hd, wpls, wpld, i, j, k, start;
l_uint32 *datas, *datad, *lines, *lined;
PIX *pixd;
PROCNAME("pixExpandBinaryReplicate");
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 binary", procName, NULL);
if (xfact <= 0 || yfact <= 0)
return (PIX *)ERROR_PTR("invalid scale factor: <= 0", procName, NULL);
if (xfact == yfact) {
if (xfact == 1)
return pixCopy(NULL, pixs);
if (xfact == 2 || xfact == 4 || xfact == 8 || xfact == 16)
return pixExpandBinaryPower2(pixs, xfact);
}
wpls = pixGetWpl(pixs);
datas = pixGetData(pixs);
wd = xfact * w;
hd = yfact * h;
if ((pixd = pixCreate(wd, hd, 1)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
pixCopyResolution(pixd, pixs);
pixScaleResolution(pixd, (l_float32)xfact, (l_float32)yfact);
wpld = pixGetWpl(pixd);
datad = pixGetData(pixd);
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + yfact * i * wpld;
for (j = 0; j < w; j++) { /* replicate pixels on a single line */
if (GET_DATA_BIT(lines, j)) {
start = xfact * j;
for (k = 0; k < xfact; k++)
SET_DATA_BIT(lined, start + k);
}
}
for (k = 1; k < yfact; k++) /* replicate the line */
memcpy(lined + k * wpld, lined, 4 * wpld);
}
return pixd;
}
/*------------------------------------------------------------------*
* Power of 2 expansion *
*------------------------------------------------------------------*/
/*!
* \brief pixExpandBinaryPower2()
*
* \param[in] pixs 1 bpp
* \param[in] factor expansion factor: 1, 2, 4, 8, 16
* \return pixd expanded 1 bpp by replication, or NULL on error
*/
PIX *
pixExpandBinaryPower2(PIX *pixs,
l_int32 factor)
{
l_uint8 sval;
l_uint16 *tab2;
l_int32 i, j, k, w, h, d, wd, hd, wpls, wpld, sdibits, sqbits, sbytes;
l_uint32 *datas, *datad, *lines, *lined, *tab4, *tab8;
PIX *pixd;
PROCNAME("pixExpandBinaryPower2");
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 binary", procName, NULL);
if (factor == 1)
return pixCopy(NULL, pixs);
if (factor != 2 && factor != 4 && factor != 8 && factor != 16)
return (PIX *)ERROR_PTR("factor must be in {2,4,8,16}", procName, NULL);
wpls = pixGetWpl(pixs);
datas = pixGetData(pixs);
wd = factor * w;
hd = factor * h;
if ((pixd = pixCreate(wd, hd, 1)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
pixCopyResolution(pixd, pixs);
pixScaleResolution(pixd, (l_float32)factor, (l_float32)factor);
wpld = pixGetWpl(pixd);
datad = pixGetData(pixd);
if (factor == 2) {
tab2 = makeExpandTab2x();
sbytes = (w + 7) / 8;
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + 2 * i * wpld;
for (j = 0; j < sbytes; j++) {
sval = GET_DATA_BYTE(lines, j);
SET_DATA_TWO_BYTES(lined, j, tab2[sval]);
}
memcpy(lined + wpld, lined, 4 * wpld);
}
LEPT_FREE(tab2);
} else if (factor == 4) {
tab4 = makeExpandTab4x();
sbytes = (w + 7) / 8;
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + 4 * i * wpld;
for (j = 0; j < sbytes; j++) {
sval = GET_DATA_BYTE(lines, j);
lined[j] = tab4[sval];
}
for (k = 1; k < 4; k++)
memcpy(lined + k * wpld, lined, 4 * wpld);
}
LEPT_FREE(tab4);
} else if (factor == 8) {
tab8 = makeExpandTab8x();
sqbits = (w + 3) / 4;
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + 8 * i * wpld;
for (j = 0; j < sqbits; j++) {
sval = GET_DATA_QBIT(lines, j);
if (sval > 15)
L_WARNING("sval = %d; should be < 16\n", procName, sval);
lined[j] = tab8[sval];
}
for (k = 1; k < 8; k++)
memcpy(lined + k * wpld, lined, 4 * wpld);
}
LEPT_FREE(tab8);
} else { /* factor == 16 */
sdibits = (w + 1) / 2;
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + 16 * i * wpld;
for (j = 0; j < sdibits; j++) {
sval = GET_DATA_DIBIT(lines, j);
lined[j] = expandtab16[sval];
}
for (k = 1; k < 16; k++)
memcpy(lined + k * wpld, lined, 4 * wpld);
}
}
return pixd;
}
/*-------------------------------------------------------------------*
* Expansion tables for 2x, 4x and 8x expansion *
*-------------------------------------------------------------------*/
static l_uint16 *
makeExpandTab2x(void)
{
l_uint16 *tab;
l_int32 i;
tab = (l_uint16 *) LEPT_CALLOC(256, sizeof(l_uint16));
for (i = 0; i < 256; i++) {
if (i & 0x01)
tab[i] = 0x3;
if (i & 0x02)
tab[i] |= 0xc;
if (i & 0x04)
tab[i] |= 0x30;
if (i & 0x08)
tab[i] |= 0xc0;
if (i & 0x10)
tab[i] |= 0x300;
if (i & 0x20)
tab[i] |= 0xc00;
if (i & 0x40)
tab[i] |= 0x3000;
if (i & 0x80)
tab[i] |= 0xc000;
}
return tab;
}
static l_uint32 *
makeExpandTab4x(void)
{
l_uint32 *tab;
l_int32 i;
tab = (l_uint32 *) LEPT_CALLOC(256, sizeof(l_uint32));
for (i = 0; i < 256; i++) {
if (i & 0x01)
tab[i] = 0xf;
if (i & 0x02)
tab[i] |= 0xf0;
if (i & 0x04)
tab[i] |= 0xf00;
if (i & 0x08)
tab[i] |= 0xf000;
if (i & 0x10)
tab[i] |= 0xf0000;
if (i & 0x20)
tab[i] |= 0xf00000;
if (i & 0x40)
tab[i] |= 0xf000000;
if (i & 0x80)
tab[i] |= 0xf0000000;
}
return tab;
}
static l_uint32 *
makeExpandTab8x(void)
{
l_uint32 *tab;
l_int32 i;
tab = (l_uint32 *) LEPT_CALLOC(16, sizeof(l_uint32));
for (i = 0; i < 16; i++) {
if (i & 0x01)
tab[i] = 0xff;
if (i & 0x02)
tab[i] |= 0xff00;
if (i & 0x04)
tab[i] |= 0xff0000;
if (i & 0x08)
tab[i] |= 0xff000000;
}
return tab;
}

View File

@ -1,410 +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 binreduce.c
* <pre>
*
* Subsampled 2x reduction
* PIX *pixReduceBinary2()
*
* Rank filtered 2x reductions
* PIX *pixReduceRankBinaryCascade()
* PIX *pixReduceRankBinary2()
*
* Permutation table for 2x rank binary reduction
* l_uint8 *makeSubsampleTab2x(void)
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
/*------------------------------------------------------------------*
* Subsampled reduction *
*------------------------------------------------------------------*/
/*!
* \brief pixReduceBinary2()
*
* \param[in] pixs
* \param[in] intab [optional]; if null, a table is made here
* and destroyed before exit
* \return pixd 2x subsampled, or NULL on error
*
* <pre>
* 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().
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) This performs up to four cascaded 2x rank reductions.
* (2) Use level = 0 to truncate the cascade.
* </pre>
*/
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
*
* <pre>
* 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().
* </pre>
*/
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
*
* <pre>
* 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
* </pre>
*/
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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
* <pre>
*
* 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 '!'.
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
#include "bmfdata.h"
static const l_float32 VertFractSep = 0.3;
#ifndef NO_CONSOLE_IO
#define DEBUG_BASELINE 0
#define DEBUG_CHARS 0
#define DEBUG_FONT_GEN 0
#endif /* ~NO_CONSOLE_IO */
static PIXA *pixaGenerateFontFromFile(const char *dir, l_int32 fontsize,
l_int32 *pbl0, l_int32 *pbl1,
l_int32 *pbl2);
static PIXA *pixaGenerateFontFromString(l_int32 fontsize, l_int32 *pbl0,
l_int32 *pbl1, l_int32 *pbl2);
static PIXA *pixaGenerateFont(PIX *pixs, l_int32 fontsize, l_int32 *pbl0,
l_int32 *pbl1, l_int32 *pbl2);
static l_int32 pixGetTextBaseline(PIX *pixs, l_int32 *tab8, l_int32 *py);
static l_int32 bmfMakeAsciiTables(L_BMF *bmf);
/*---------------------------------------------------------------------*/
/* Bmf create/destroy */
/*---------------------------------------------------------------------*/
/*!
* \brief bmfCreate()
*
* \param[in] dir [optional] directory holding pixa of character set
* \param[in] fontsize 4, 6, 8, ... , 20
* \return bmf holding the bitmap font and associated information
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) This reads a pre-computed pixa file with the 95 ascii chars.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) See pixaGenerateFontFromFile() for details.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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;
}

View File

@ -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 */

View File

@ -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
*
* <pre>
* 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);
* </pre>
*/
#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"
"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"
"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"
"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"
"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////"
"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"
"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"
"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"
"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 */

View File

@ -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
*
* <pre>
* 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.
* </pre>
*/
/*-------------------------------------------------------------*
* 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 */

View File

@ -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
* <pre>
*
* Read bmp
* PIX *pixReadStreamBmp()
* PIX *pixReadMemBmp()
*
* Write bmp
* l_int32 pixWriteStreamBmp()
* l_int32 pixWriteMemBmp()
*
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
#include "bmp.h"
/* --------------------------------------------*/
#if USE_BMPIO /* defined in environ.h */
/* --------------------------------------------*/
/* Here we're setting the pixel value 0 to white (255) and the
* value 1 to black (0). This is the convention for grayscale, but
* the opposite of the convention for 1 bpp, where 0 is white
* and 1 is black. Both colormap entries are opaque (alpha = 255) */
RGBA_QUAD bwmap[2] = { {255,255,255,255}, {0,0,0,255} };
/* Image dimension limits */
static const l_int32 L_MAX_ALLOWED_WIDTH = 1000000;
static const l_int32 L_MAX_ALLOWED_HEIGHT = 1000000;
static const l_int64 L_MAX_ALLOWED_PIXELS = 400000000LL;
static const l_int32 L_MAX_ALLOWED_RES = 10000000; /* pixels/meter */
#ifndef NO_CONSOLE_IO
#define DEBUG 0
#endif /* ~NO_CONSOLE_IO */
/*--------------------------------------------------------------*
* Read bmp *
*--------------------------------------------------------------*/
/*!
* \brief pixReadStreamBmp()
*
* \param[in] fp file stream opened for read
* \return pix, or NULL on error
*
* <pre>
* 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
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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 */

View File

@ -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
* <pre>
*
* Stubs for bmpio.c functions
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include "allheaders.h"
/* --------------------------------------------*/
#if !USE_BMPIO /* defined in environ.h */
/* --------------------------------------------*/
PIX * pixReadStreamBmp(FILE *fp)
{
return (PIX * )ERROR_PTR("function not present", "pixReadStreamBmp", NULL);
}
/* ----------------------------------------------------------------------*/
l_ok pixWriteStreamBmp(FILE *fp, PIX *pix)
{
return ERROR_INT("function not present", "pixWriteStreamBmp", 1);
}
/* ----------------------------------------------------------------------*/
PIX * pixReadMemBmp(const l_uint8 *cdata, size_t size)
{
return (PIX *)ERROR_PTR("function not present", "pixReadMemBmp", NULL);
}
/* ----------------------------------------------------------------------*/
l_ok pixWriteMemBmp(l_uint8 **pdata, size_t *psize, PIX *pix)
{
return ERROR_INT("function not present", "pixWriteMemBmp", 1);
}
/* --------------------------------------------*/
#endif /* !USE_BMPIO */

View File

@ -1,308 +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 bootnumgen1.c
* <pre>
*
* 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++)
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
/*---------------------------------------------------------------------*/
/* Serialized string */
/*---------------------------------------------------------------------*/
static const char *l_bootnum1 =
"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";
/*---------------------------------------------------------------------*/
/* Auto-generated deserializer */
/*---------------------------------------------------------------------*/
/*!
* \brief l_bootnum_gen1()
*
* \return pixa of labeled digits
*
* <pre>
* Call this way:
* PIXA *pixa = l_bootnum_gen1(); (C)
* Pixa *pixa = l_bootnum_gen1(); (C++)
* </pre>
*/
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;
}

View File

@ -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
* <pre>
*
* 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++)
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
/*---------------------------------------------------------------------*/
/* Serialized string */
/*---------------------------------------------------------------------*/
static const char *l_bootnum2 =
"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";
/*---------------------------------------------------------------------*/
/* Auto-generated deserializer */
/*---------------------------------------------------------------------*/
/*!
* \brief l_bootnum_gen2()
*
* \return pixa of labeled digits
*
* <pre>
* Call this way:
* PIXA *pixa = l_bootnum_gen2(); (C)
* Pixa *pixa = l_bootnum_gen2(); (C++)
* </pre>
*/
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;
}

View File

@ -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
* <pre>
*
* 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++)
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
/*---------------------------------------------------------------------*/
/* Serialized string */
/*---------------------------------------------------------------------*/
static const char *l_strdata_0 =
"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";
/*---------------------------------------------------------------------*/
/* Auto-generated deserializer */
/*---------------------------------------------------------------------*/
/*!
* \brief l_bootnum_gen3()
*
* \return pixa of labeled digits
*
* <pre>
* Call this way:
* PIXA *pixa = l_bootnum_gen3(); (C)
* Pixa *pixa = l_bootnum_gen3(); (C++)
* </pre>
*/
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;
}

View File

@ -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
* <pre>
*
* 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.
*
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
/*---------------------------------------------------------------------*/
/* Serialized string */
/*---------------------------------------------------------------------*/
static const char *l_bootnum4 =
"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"
"mn/BeBL/t2X8B2rxLZ8=";
/*---------------------------------------------------------------------*/
/* Deserializer with added processing */
/*---------------------------------------------------------------------*/
/*!
* \brief l_bootnum_gen4()
*
* \param[in] nsamp number of samples to retain for each digit
* \return pixa of labeled digits
*
* <pre>
* 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.
* </pre>
*/
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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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
* <pre>
*
* 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.
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
/* Bounds on array size */
static const l_uint32 MaxArraySize = 1000000000; /* 10^9 bytes */
static const l_int32 InitialArraySize = 200; /*!< n'importe quoi */
/* Static function */
static l_int32 l_byteaExtendArrayToSize(L_BYTEA *ba, size_t size);
/*---------------------------------------------------------------------*
* Creation, copy, clone, destruction *
*---------------------------------------------------------------------*/
/*!
* \brief l_byteaCreate()
*
* \param[in] nbytes determines initial size of data array
* \return l_bytea, or NULL on error
*
* <pre>
* Notes:
* (1) The allocated array is n + 1 bytes. This allows room
* for null termination.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) If cloning, up the refcount and return a ptr to %bas.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) The returned ptr is owned by %ba. Do not free it!
* </pre>
*/
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.
*
* <pre>
* Notes:
* (1) The returned data is owned by the caller. The input %ba
* still owns the original data array.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) It is a no-op, not an error, for %ba2 to be null.
* </pre>
*/
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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
*
* <pre>
* CCBord: represents a single connected component
* CCBorda: an array of CCBord
* </pre>
*/
/*! 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
};
/*!
* <pre>
* 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.
* </pre>
*/
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 */

View File

@ -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
* <pre>
*
* PIXA *pixaThinConnected()
* PIX *pixThinConnected()
* PIX *pixThinConnectedBySet()
* SELA *selaMakeThinSets()
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include "allheaders.h"
/* ------------------------------------------------------------
* The sels used here (and their rotated counterparts) are the
* useful 3x3 Sels for thinning. They are defined in sel2.c,
* and the sets are constructed in selaMakeThinSets().
* The notation is based on "Connectivity-preserving morphological
* image transformations", a version of which can be found at
* http://www.leptonica.com/papers/conn.pdf
* ------------------------------------------------------------ */
/*----------------------------------------------------------------*
* CC-preserving thinning *
*----------------------------------------------------------------*/
/*!
* \brief pixaThinConnected()
*
* \param[in] pixas of 1 bpp pix
* \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 pixds, or NULL on error
*
* <pre>
* Notes:
* (1) See notes in pixThinConnected().
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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).
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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;
}

View File

@ -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
* <pre>
*
* 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.
*
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include "allheaders.h"
static PIXA *makeCheckerboardCornerPixa(l_int32 size, l_int32 dilation,
l_int32 nsels);
static const char selnames[64] = "s_diag1 s_diag2 s_cross1 s_cross2";
/*!
* \brief pixFindCheckerboardCorner()
*
* \param[in] pixs of checkerboard
* \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[out] ppix_corners [optional] 1 bpp pix giving corner locations
* \param[out] ppta_corners [optional] pta giving corner locations
* \param[in] pixadb [optional] pass in pre-allocated
* \return 0 if OK, 1 on error
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) Use 4 sels if the checkerboard may be rotated by more than 20 deg.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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
* <pre>
*
* 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.
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include "allheaders.h"
/*-----------------------------------------------------------------*
* Top-level color morphological operations *
*-----------------------------------------------------------------*/
/*!
* \brief pixColorMorph()
*
* \param[in] pixs
* \param[in] type L_MORPH_DILATE, L_MORPH_ERODE, L_MORPH_OPEN,
* or L_MORPH_CLOSE
* \param[in] hsize width of Sel; must be odd; origin implicitly in center
* \param[in] vsize ditto for height of Sel
* \return pixd
*
* <pre>
* 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.
* </pre>
*/
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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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
* <pre>
*
* Unsupervised color segmentation
*
* PIX *pixColorSegment()
* PIX *pixColorSegmentCluster()
* static l_int32 pixColorSegmentTryCluster()
* l_int32 pixAssignToNearestColor()
* l_int32 pixColorSegmentClean()
* l_int32 pixColorSegmentRemoveColors()
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include "allheaders.h"
/* Maximum allowed iterations in Phase 1. */
static const l_int32 MAX_ALLOWED_ITERATIONS = 20;
/* Factor by which max dist is increased on each iteration */
static const l_float32 DIST_EXPAND_FACT = 1.3;
/* Octcube division level for computing nearest colormap color using LUT.
* Using 4 should suffice for up to 50 - 100 colors, and it is
* very fast. Using 5 takes 8 times as long to set up the LUT
* for little perceptual gain, even with 100 colors. */
static const l_int32 LEVEL_IN_OCTCUBE = 4;
static l_int32 pixColorSegmentTryCluster(PIX *pixd, PIX *pixs,
l_int32 maxdist, l_int32 maxcolors,
l_int32 debugflag);
/*------------------------------------------------------------------*
* Unsupervised color segmentation *
*------------------------------------------------------------------*/
/*!
* \brief pixColorSegment()
*
* \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] selsize linear size of sel for closing to remove noise
* \param[in] finalcolors max number of final colors allowed after 4th pass
* \param[in] debugflag 1 for debug output; 0 otherwise
* \return pixd 8 bit with colormap, or NULL on error
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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!
* </pre>
*/
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
*
* <pre>
* Notes:
* This function should only be called from pixColorSegCluster()
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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, &gtab, &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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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
* <pre>
*
* 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.
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
/*------------------------------------------------------------------*
* Conversion to 1 bpp *
*------------------------------------------------------------------*/
/*!
* \brief convertFilesTo1bpp()
*
* \param[in] dirin
* \param[in] substr [optional] substring filter on filenames;
8 can be NULL
* \param[in] upscaling 1, 2 or 4; only for input color or grayscale
* \param[in] thresh global threshold for binarization; 0 for default
* \param[in] firstpage
* \param[in] npages use 0 to do all from %firstpage to the end
* \param[in] dirout
* \param[in] outformat IFF_PNG, IFF_TIFF_G4
* \return 0 if OK, 1 on error
*
* <pre>
* Notes:
* (1) Images are sorted lexicographically, and the names in the
* output directory are retained except for the extension.
* </pre>
*/
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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 <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <math.h>
#include "allheaders.h"
/* -------------------------------------------------------------------- *
* Optimized 2 pix correlators (for jbig2 clustering) *
* -------------------------------------------------------------------- */
/*!
* \brief pixCorrelationScore()
*
* \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
* \return 0 if OK, 1 on error
*
* <pre>
* 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.
* </pre>
*/
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
*
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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;
}

View File

@ -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
*
* <pre>
* 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.
* </pre>
*/
/*! Dewarp version for serialization
* <pre>
* 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.
* </pre>
*/
#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 */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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
* <pre>
*
* 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.
*
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include "allheaders.h"
/*----------------------------------------------------------------------*
* Rearrangements *
*----------------------------------------------------------------------*/
/*!
* \brief l_dnaJoin()
*
* \param[in] dad dest dna; add to this one
* \param[in] das [optional] source dna; add from this one
* \param[in] istart starting index in das
* \param[in] iend ending index in das; use -1 to cat all
* \return 0 if OK, 1 on error
*
* <pre>
* 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
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) This 'flattens' the dnaa to a dna, by joining successively
* each dna in the dnaa.
* (2) It leaves the input dnaa unchanged.
* </pre>
*/
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
*
* <pre>
* 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).
* </pre>
*/
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
*
* <pre>
* 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).
* </pre>
*/
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;
}

View File

@ -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
* <pre>
*
* 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.
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include "allheaders.h"
/*--------------------------------------------------------------------------*
* Dna hash: Creation and destruction *
*--------------------------------------------------------------------------*/
/*!
* \brief l_dnaHashCreate()
*
* \param[in] nbuckets the number of buckets in the hash table,
* which should be prime.
* \param[in] initsize initial size of each allocated dna; 0 for default
* \return ptr to new dnahash, or NULL on error
*
* <pre>
* Notes:
* (1) Actual dna are created only as required by l_dnaHashAdd()
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) The values stored in the %dahash are indices into %da;
* %dahash has no use without %da.
* </pre>
*/
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
*
* <pre>
* 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)
* </pre>
*/
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
*
* <pre>
* 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;
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) This uses the same method for building the intersection set
* as ptaIntersectionByHash() and sarrayIntersectionByHash().
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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;
}

View File

@ -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 <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
PIX *pixMorphDwa_2(PIX *pixd, PIX *pixs, l_int32 operation, char *selname);
PIX *pixFMorphopGen_2(PIX *pixd, PIX *pixs, l_int32 operation, char *selname);
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);
static l_int32 NUM_SELS_GENERATED = 76;
static char SEL_NAMES[][80] = {
"sel_comb_4h",
"sel_comb_4v",
"sel_comb_5h",
"sel_comb_5v",
"sel_comb_6h",
"sel_comb_6v",
"sel_comb_7h",
"sel_comb_7v",
"sel_comb_8h",
"sel_comb_8v",
"sel_comb_9h",
"sel_comb_9v",
"sel_comb_10h",
"sel_comb_10v",
"sel_comb_12h",
"sel_comb_12v",
"sel_comb_14h",
"sel_comb_14v",
"sel_comb_15h",
"sel_comb_15v",
"sel_comb_16h",
"sel_comb_16v",
"sel_comb_18h",
"sel_comb_18v",
"sel_comb_20h",
"sel_comb_20v",
"sel_comb_21h",
"sel_comb_21v",
"sel_comb_22h",
"sel_comb_22v",
"sel_comb_24h",
"sel_comb_24v",
"sel_comb_25h",
"sel_comb_25v",
"sel_comb_27h",
"sel_comb_27v",
"sel_comb_28h",
"sel_comb_28v",
"sel_comb_30h",
"sel_comb_30v",
"sel_comb_32h",
"sel_comb_32v",
"sel_comb_33h",
"sel_comb_33v",
"sel_comb_35h",
"sel_comb_35v",
"sel_comb_36h",
"sel_comb_36v",
"sel_comb_39h",
"sel_comb_39v",
"sel_comb_40h",
"sel_comb_40v",
"sel_comb_42h",
"sel_comb_42v",
"sel_comb_44h",
"sel_comb_44v",
"sel_comb_45h",
"sel_comb_45v",
"sel_comb_48h",
"sel_comb_48v",
"sel_comb_49h",
"sel_comb_49v",
"sel_comb_50h",
"sel_comb_50v",
"sel_comb_52h",
"sel_comb_52v",
"sel_comb_54h",
"sel_comb_54v",
"sel_comb_55h",
"sel_comb_55v",
"sel_comb_56h",
"sel_comb_56v",
"sel_comb_60h",
"sel_comb_60v",
"sel_comb_63h",
"sel_comb_63v"};
/*!
* \brief pixMorphDwa_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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,647 +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 edge.c
* <pre>
*
* 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.
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include "allheaders.h"
/*----------------------------------------------------------------------*
* Sobel edge detecting filter *
*----------------------------------------------------------------------*/
/*!
* \brief pixSobelEdgeFilter()
*
* \param[in] pixs 8 bpp; no colormap
* \param[in] orientflag L_HORIZONTAL_EDGES, L_VERTICAL_EDGES, L_ALL_EDGES
* \return pixd 8 bpp, edges are brighter, or NULL on error
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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;
}

View File

@ -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 <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <ctype.h>
#include "allheaders.h"
/* Base64 encoding table in string representation */
static const l_int32 MAX_BASE64_LINE = 72; /* max line length base64 */
static const char *tablechar64 =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static l_int32 isBase64(char);
static l_int32 *genReverseTab64(void);
static void byteConvert3to4(l_uint8 *in3, l_uint8 *out4);
static void byteConvert4to3(l_uint8 *in4, l_uint8 *out3);
/* Ascii85 encoding */
static const l_int32 MAX_ASCII85_LINE = 64; /* max line length ascii85 */
static const l_uint32 power85[5] = {1,
85,
85 * 85,
85 * 85 * 85,
85 * 85 * 85 * 85};
static l_int32 convertChunkToAscii85(const l_uint8 *inarray, l_int32 insize,
l_int32 *pindex, char *outbuf,
l_int32 *pnbout);
/*-------------------------------------------------------------*
* Utility for encoding and decoding data with base64 *
*-------------------------------------------------------------*/
/*!
* \brief encodeBase64()
*
* \param[in] inarray input binary data
* \param[in] insize number of bytes in input array
* \param[out] poutsize number of bytes in output char array
* \return chara with MAX_BASE64_LINE characters + \n in each line
*
* <pre>
* 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 '='.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) Attempts to read 4 bytes and write 5.
* (2) Writes 1 byte if the value is 0.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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;
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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 <stdint.h>
#else
/* Note that _WIN32 is defined for both 32 and 64 bit applications,
whereas _WIN64 is defined only for the latter */
#ifdef _WIN64
typedef __int64 intptr_t;
typedef unsigned __int64 uintptr_t;
#else
typedef int intptr_t;
typedef unsigned int uintptr_t;
#endif
/* VC++6 doesn't seem to have powf, expf. */
#if (_MSC_VER < 1400)
#define powf(x, y) (float)pow((double)(x), (double)(y))
#define expf(x) (float)exp((double)(x))
#endif
#endif /* _MSC_VER */
/* Windows specifics */
#ifdef _WIN32
/* DLL EXPORTS and IMPORTS */
#if defined(LIBLEPT_EXPORTS)
#define LEPT_DLL __declspec(dllexport)
#elif defined(LIBLEPT_IMPORTS)
#define LEPT_DLL __declspec(dllimport)
#else
#define LEPT_DLL
#endif
#else /* non-Windows specifics */
#include <stdint.h>
#define LEPT_DLL
#endif /* _WIN32 */
typedef intptr_t l_intptr_t;
typedef uintptr_t l_uintptr_t;
/*--------------------------------------------------------------------*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*
* USER CONFIGURABLE *
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*
* Environment variables with I/O libraries *
* Manual Configuration Only: NOT AUTO_CONF *
*--------------------------------------------------------------------*/
/*
* Leptonica provides interfaces to link to several external image
* I/O libraries, plus zlib. Setting any of these to 0 here causes
* non-functioning stubs to be linked.
*/
#if !defined(HAVE_CONFIG_H) && !defined(ANDROID_BUILD) && !defined(OS_IOS)
#if !defined(HAVE_LIBJPEG)
#define HAVE_LIBJPEG 1
#endif
#if !defined(HAVE_LIBTIFF)
#define HAVE_LIBTIFF 0
#endif
#if !defined(HAVE_LIBPNG)
#define HAVE_LIBPNG 1
#endif
#if !defined(HAVE_LIBZ)
#define HAVE_LIBZ 1
#endif
#if !defined(HAVE_LIBGIF)
#define HAVE_LIBGIF 0
#endif
#if !defined(HAVE_LIBUNGIF)
#define HAVE_LIBUNGIF 0
#endif
#if !defined(HAVE_LIBWEBP)
#define HAVE_LIBWEBP 0
#endif
#if !defined(HAVE_LIBWEBP_ANIM)
#define HAVE_LIBWEBP_ANIM 0
#endif
#if !defined(HAVE_LIBJP2K)
#define HAVE_LIBJP2K 0
#endif
/*-----------------------------------------------------------------------*
* Leptonica supports OpenJPEG 2.0+. If you have a version of openjpeg *
* (HAVE_LIBJP2K == 1) that is >= 2.0, set the path to the openjpeg.h *
* header in angle brackets here. *
*-----------------------------------------------------------------------*/
#define LIBJP2K_HEADER <openjpeg-2.3/openjpeg.h>
#endif /* ! HAVE_CONFIG_H etc. */
/*--------------------------------------------------------------------*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*
* USER CONFIGURABLE *
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*
* Environ variables for image I/O without external libraries *
*--------------------------------------------------------------------*/
/*
* Leptonica supplies I/O support without using external libraries for:
* * image read/write for bmp, pnm
* * header read for jp2k
* * image wrapping write for pdf and ps.
* Setting any of these to 0 causes non-functioning stubs to be linked.
*/
#define USE_BMPIO 1
#define USE_PNMIO 1
#define USE_JP2KHEADER 1
#define USE_PDFIO 1
#define USE_PSIO 1
/*-------------------------------------------------------------------------*
* On linux systems, you can do I/O between Pix and memory. Specifically,
* you can compress (write compressed data to memory from a Pix) and
* uncompress (read from compressed data in memory to a Pix).
* For jpeg, png, jp2k, gif, pnm and bmp, these use the non-posix GNU
* functions fmemopen() and open_memstream(). These functions are not
* available on other systems.
* To use these functions in linux, you must define HAVE_FMEMOPEN to 1.
* To use them on MacOS, which does not support these functions, set it to 0.
*-------------------------------------------------------------------------*/
#if !defined(HAVE_CONFIG_H) && !defined(ANDROID_BUILD) && !defined(OS_IOS) && \
!defined(_WIN32)
#define HAVE_FMEMOPEN 1
#endif /* ! HAVE_CONFIG_H etc. */
/*-------------------------------------------------------------------------*
* fstatat() is defined by POSIX, but some systems do not support it. *
* One example is older macOS systems (pre-10.10). *
* Play it safe and set the default value to 0. *
*-------------------------------------------------------------------------*/
#if !defined(HAVE_CONFIG_H)
#define HAVE_FSTATAT 0
#endif /* ! HAVE_CONFIG_H */
/*--------------------------------------------------------------------*
* It is desirable on Windows to have all temp files written to the same
* subdirectory of the Windows <Temp> directory, because files under <Temp>
* persist after reboot, and the regression tests write a lot of files.
* We write all test files to /tmp/lept or subdirectories of /tmp/lept.
* Windows temp files are specified as in unix, but have the translation
* /tmp/lept/xxx --> <Temp>/lept/xxx
*--------------------------------------------------------------------*/
/*--------------------------------------------------------------------*
* Built-in types *
*--------------------------------------------------------------------*/
typedef int l_ok; /*!< return type 0 if OK, 1 on error */
typedef signed char l_int8; /*!< signed 8-bit value */
typedef unsigned char l_uint8; /*!< unsigned 8-bit value */
typedef short l_int16; /*!< signed 16-bit value */
typedef unsigned short l_uint16; /*!< unsigned 16-bit value */
typedef int l_int32; /*!< signed 32-bit value */
typedef unsigned int l_uint32; /*!< unsigned 32-bit value */
typedef float l_float32; /*!< 32-bit floating point value */
typedef double l_float64; /*!< 64-bit floating point value */
#ifdef COMPILER_MSVC
typedef __int64 l_int64; /*!< signed 64-bit value */
typedef unsigned __int64 l_uint64; /*!< unsigned 64-bit value */
#else
typedef long long l_int64; /*!< signed 64-bit value */
typedef unsigned long long l_uint64; /*!< unsigned 64-bit value */
#endif /* COMPILER_MSVC */
/*-------------------------------------------------------------------------*
* For security, the library is distributed in a configuration that does *
* not permit (1) forking with 'system', which is used for displaying *
* images and generating gnuplots, and (2) writing files with specified *
* compiled-in file names. All such writes are with functions such as *
* pixWriteDebug() where the "Debug" is appended to the usual name. *
* Whether the "Debug" version defaults to the standard version or is a *
* no-op depends on the value of this global variable. The default value *
* of LeptDebugOK is 0, and it is set in writefile.c. This value can be *
* over-ridden, for development and debugging, by setLeptDebugOK(). *
*-------------------------------------------------------------------------*/
LEPT_DLL extern l_int32 LeptDebugOK; /* default is 0 */
/*------------------------------------------------------------------------*
* Standard macros *
*------------------------------------------------------------------------*/
#ifndef L_MIN
/*! Minimum of %x and %y */
#define L_MIN(x, y) (((x) < (y)) ? (x) : (y))
#endif
#ifndef L_MAX
/*! Maximum of %x and %y */
#define L_MAX(x, y) (((x) > (y)) ? (x) : (y))
#endif
#ifndef L_ABS
/*! Absolute value of %x */
#define L_ABS(x) (((x) < 0) ? (-1 * (x)) : (x))
#endif
#ifndef L_SIGN
/*! Sign of %x */
#define L_SIGN(x) (((x) < 0) ? -1 : 1)
#endif
#ifndef UNDEF
/*! Undefined value */
#define UNDEF -1
#endif
#ifndef NULL
/*! NULL value */
#define NULL 0
#endif
#ifndef TRUE
/*! True value */
#define TRUE 1
#endif
#ifndef FALSE
/*! False value */
#define FALSE 0
#endif
/*--------------------------------------------------------------------*
* Environment variables for endian dependence *
*--------------------------------------------------------------------*/
/*
* To control conditional compilation, one of two variables
*
* L_LITTLE_ENDIAN (e.g., for Intel X86)
* L_BIG_ENDIAN (e.g., for Sun SPARC, Mac Power PC)
*
* is defined when the GCC compiler is invoked.
* All code should compile properly for both hardware architectures.
*/
/*------------------------------------------------------------------------*
* Simple search state variables *
*------------------------------------------------------------------------*/
/*! Search State */
enum {
L_NOT_FOUND = 0,
L_FOUND = 1
};
/*------------------------------------------------------------------------*
* Path separator conversion *
*------------------------------------------------------------------------*/
/*! Path Separators */
enum {
UNIX_PATH_SEPCHAR = 0,
WIN_PATH_SEPCHAR = 1
};
/*------------------------------------------------------------------------*
* Timing structs *
*------------------------------------------------------------------------*/
typedef void *L_TIMER;
/*! Timing struct */
struct L_WallTimer {
l_int32 start_sec;
l_int32 start_usec;
l_int32 stop_sec;
l_int32 stop_usec;
};
typedef struct L_WallTimer L_WALLTIMER;
/*------------------------------------------------------------------------*
* Standard memory allocation *
* *
* These specify the memory management functions that are used *
* on all heap data except for Pix. Memory management for Pix *
* also defaults to malloc and free. See pix1.c for details. *
*------------------------------------------------------------------------*/
#define LEPT_MALLOC(blocksize) malloc(blocksize)
#define LEPT_CALLOC(numelem, elemsize) calloc(numelem, elemsize)
#define LEPT_REALLOC(ptr, blocksize) realloc(ptr, blocksize)
#define LEPT_FREE(ptr) free(ptr)
/*------------------------------------------------------------------------*
* Control printing of error, warning, and info messages *
* *
* Leptonica never sends output to stdout. By default, all messages *
* go to stderr. However, we provide a mechanism for runtime *
* redirection of output, using a custom stderr handler defined *
* by the user. See utils1.c for details and examples. *
* *
* To omit all messages to stderr, simply define NO_CONSOLE_IO on the *
* command line. For finer grained control, we have a mechanism *
* based on the message severity level. The following assumes that *
* NO_CONSOLE_IO is not defined. *
* *
* Messages are printed if the message severity is greater than or equal *
* to the current severity threshold. The current severity threshold *
* is the greater of the compile-time severity, which is the minimum *
* severity that can be reported, and the run-time severity, which is *
* the severity threshold at the moment. *
* *
* The compile-time threshold determines which messages are compiled *
* into the library for potential printing. Messages below the *
* compile-time threshold are omitted and can never be printed. The *
* default compile-time threshold is L_SEVERITY_INFO, but this may be *
* overridden by defining MINIMUM_SEVERITY to the desired enumeration *
* identifier on the compiler command line. Defining NO_CONSOLE_IO on *
* the command line is the same as setting MINIMUM_SEVERITY to *
* L_SEVERITY_NONE. *
* *
* The run-time threshold determines which messages are printed during *
* library execution. It defaults to the compile-time threshold but *
* may be changed either statically by defining DEFAULT_SEVERITY to *
* the desired enumeration identifier on the compiler command line, or *
* dynamically by calling setMsgSeverity() to specify a new threshold. *
* The run-time threshold may also be set from the value of the *
* environment variable LEPT_MSG_SEVERITY by calling setMsgSeverity() *
* and specifying L_SEVERITY_EXTERNAL. *
* *
* In effect, the compile-time threshold setting says, "Generate code *
* to permit messages of equal or greater severity than this to be *
* printed, if desired," whereas the run-time threshold setting says, *
* "Print messages that have an equal or greater severity than this." *
*------------------------------------------------------------------------*/
/*! Control printing of error, warning and info messages */
/*! Message Control */
enum {
L_SEVERITY_EXTERNAL = 0, /* Get the severity from the environment */
L_SEVERITY_ALL = 1, /* Lowest severity: print all messages */
L_SEVERITY_DEBUG = 2, /* Print debugging and higher messages */
L_SEVERITY_INFO = 3, /* Print informational and higher messages */
L_SEVERITY_WARNING = 4, /* Print warning and higher messages */
L_SEVERITY_ERROR = 5, /* Print error and higher messages */
L_SEVERITY_NONE = 6 /* Highest severity: print no messages */
};
/* No message less than the compile-time threshold will ever be
* reported, regardless of the current run-time threshold. This allows
* selection of the set of messages to include in the library. For
* example, setting the threshold to L_SEVERITY_WARNING eliminates all
* informational messages from the library. With that setting, both
* warning and error messages would be printed unless setMsgSeverity()
* was called, or DEFAULT_SEVERITY was redefined, to set the run-time
* severity to L_SEVERITY_ERROR. In that case, only error messages
* would be printed.
*
* This mechanism makes the library smaller and faster, by eliminating
* undesired message reporting and the associated run-time overhead for
* message threshold checking, because code for messages whose severity
* is lower than MINIMUM_SEVERITY won't be generated.
*
* A production library might typically permit ERROR messages to be
* generated, and a development library might permit DEBUG and higher.
* The actual messages printed (as opposed to generated) would depend
* on the current run-time severity threshold.
*
* This is a complex mechanism and a few examples may help.
* (1) No output permitted under any circumstances.
* Use: -DNO_CONSOLE_IO or -DMINIMUM_SEVERITY=6
* (2) Suppose you want to only allow error messages, and you don't
* want to permit info or warning messages at runtime.
* Use: -DMINIMUM_SEVERITY=5
* (3) Suppose you want to only allow error messages by default,
* but you will permit this to be over-ridden at runtime.
* Use: -DDEFAULT_SEVERITY=5
* and to allow info and warning override:
* setMsgSeverity(L_SEVERITY_INFO);
*/
#ifdef NO_CONSOLE_IO
#undef MINIMUM_SEVERITY
#undef DEFAULT_SEVERITY
#define MINIMUM_SEVERITY L_SEVERITY_NONE /*!< Compile-time default */
#define DEFAULT_SEVERITY L_SEVERITY_NONE /*!< Run-time default */
#else
#ifndef MINIMUM_SEVERITY
#define MINIMUM_SEVERITY L_SEVERITY_INFO /*!< Compile-time default */
#endif
#ifndef DEFAULT_SEVERITY
#define DEFAULT_SEVERITY MINIMUM_SEVERITY /*!< Run-time default */
#endif
#endif
/*! The run-time message severity threshold is defined in utils1.c. */
LEPT_DLL extern l_int32 LeptMsgSeverity;
/*
* <pre>
* 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 : <message string>
* b : procName
* c : <return value from function>
* 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 : <message string> 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(<message string>, 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.
* </pre>
*/
#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 */

View File

@ -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
* <pre>
*
* 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.
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
#define OUTROOT "fhmtgen"
#define TEMPLATE1 "hmttemplate1.txt"
#define TEMPLATE2 "hmttemplate2.txt"
#define PROTOARGS "(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32);"
static const l_int32 L_BUF_SIZE = 512;
static char * makeBarrelshiftString(l_int32 delx, l_int32 dely, l_int32 type);
static SARRAY * sarrayMakeInnerLoopDWACode(SEL *sel, l_int32 nhits, l_int32 nmisses);
static SARRAY * sarrayMakeWplsCode(SEL *sel);
static char wpldecls[][60] = {
"l_int32 wpls2;",
"l_int32 wpls2, wpls3;",
"l_int32 wpls2, wpls3, wpls4;",
"l_int32 wpls5;",
"l_int32 wpls5, wpls6;",
"l_int32 wpls5, wpls6, wpls7;",
"l_int32 wpls5, wpls6, wpls7, wpls8;",
"l_int32 wpls9;",
"l_int32 wpls9, wpls10;",
"l_int32 wpls9, wpls10, wpls11;",
"l_int32 wpls9, wpls10, wpls11, wpls12;",
"l_int32 wpls13;",
"l_int32 wpls13, wpls14;",
"l_int32 wpls13, wpls14, wpls15;",
"l_int32 wpls13, wpls14, wpls15, wpls16;",
"l_int32 wpls17;",
"l_int32 wpls17, wpls18;",
"l_int32 wpls17, wpls18, wpls19;",
"l_int32 wpls17, wpls18, wpls19, wpls20;",
"l_int32 wpls21;",
"l_int32 wpls21, wpls22;",
"l_int32 wpls21, wpls22, wpls23;",
"l_int32 wpls21, wpls22, wpls23, wpls24;",
"l_int32 wpls25;",
"l_int32 wpls25, wpls26;",
"l_int32 wpls25, wpls26, wpls27;",
"l_int32 wpls25, wpls26, wpls27, wpls28;",
"l_int32 wpls29;",
"l_int32 wpls29, wpls30;",
"l_int32 wpls29, wpls30, wpls31;"};
static char wpldefs[][24] = {
" 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;",
" wpls26 = 26 * wpls;",
" wpls27 = 27 * wpls;",
" wpls28 = 28 * wpls;",
" wpls29 = 29 * wpls;",
" wpls30 = 30 * wpls;",
" wpls31 = 31 * wpls;"};
static char wplstrp[][10] = {"+ wpls", "+ wpls2", "+ wpls3", "+ wpls4",
"+ wpls5", "+ wpls6", "+ wpls7", "+ wpls8",
"+ wpls9", "+ wpls10", "+ wpls11", "+ wpls12",
"+ wpls13", "+ wpls14", "+ wpls15", "+ wpls16",
"+ wpls17", "+ wpls18", "+ wpls19", "+ wpls20",
"+ wpls21", "+ wpls22", "+ wpls23", "+ wpls24",
"+ wpls25", "+ wpls26", "+ wpls27", "+ wpls28",
"+ wpls29", "+ wpls30", "+ wpls31"};
static char wplstrm[][10] = {"- wpls", "- wpls2", "- wpls3", "- wpls4",
"- wpls5", "- wpls6", "- wpls7", "- wpls8",
"- wpls9", "- wpls10", "- wpls11", "- wpls12",
"- wpls13", "- wpls14", "- wpls15", "- wpls16",
"- wpls17", "- wpls18", "- wpls19", "- wpls20",
"- wpls21", "- wpls22", "- wpls23", "- wpls24",
"- wpls25", "- wpls26", "- wpls27", "- wpls28",
"- wpls29", "- wpls30", "- wpls31"};
/*!
* \brief fhmtautogen()
*
* \param[in] sela
* \param[in] fileindex
* \param[in] filename [optional]; can be null
* \return 0 if OK; 1 on error
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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);
}

View File

@ -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 <string.h>
#include "allheaders.h"
PIX *pixHMTDwa_1(PIX *pixd, PIX *pixs, const char *selname);
PIX *pixFHMTGen_1(PIX *pixd, PIX *pixs, const char *selname);
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);
static l_int32 NUM_SELS_GENERATED = 10;
static char SEL_NAMES[][80] = {
"sel_3hm",
"sel_3de",
"sel_3ue",
"sel_3re",
"sel_3le",
"sel_sl1",
"sel_ulc",
"sel_urc",
"sel_llc",
"sel_lrc"};
/*!
* \brief pixHMTDwa_1()
*
* \param[in] pixd usual 3 choices: null, == pixs, != pixs
* \param[in] pixs 1 bpp
* \param[in] sel name
* \return pixd
*
* <pre>
* Notes:
* (1) This simply adds a 32 pixel border, calls the appropriate
* pixFHMTGen_*(), and removes the border.
* See notes below for that function.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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;
}

View File

@ -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));
}
}
}

View File

@ -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
* <pre>
*
* 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.
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include "allheaders.h"
/* --------------------------------------------------------------- *
* These hit-miss sels match the slanted edge of italic characters *
* --------------------------------------------------------------- */
static const char *str_ital1 = " o x"
" "
" "
" "
" o x "
" "
" C "
" "
" o x "
" "
" "
" "
"o x ";
static const char *str_ital2 = " o x"
" "
" "
" o x "
" C "
" "
" o x "
" "
" "
"o x ";
/* ------------------------------------------------------------- *
* This sel removes noise that is not oriented as a slanted edge *
* ------------------------------------------------------------- */
static const char *str_ital3 = " x"
"Cx"
"x "
"x ";
/*!
* \brief pixItalicWords()
*
* \param[in] pixs 1 bpp
* \param[in] boxaw [optional] word bounding boxes; can be NULL
* \param[in] pixw [optional] word box mask; can be NULL
* \param[out] pboxa boxa of italic words
* \param[in] debugflag 1 for debug output; 0 otherwise
* \return 0 if OK, 1 on error
*
* <pre>
* 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.
* </pre>
*/
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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 <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
static l_int32 NUM_SELS_GENERATED = 4;
static char SEL_NAMES[][10] = {"flipsel1",
"flipsel2",
"flipsel3",
"flipsel4"};
static l_int32 flipfhmtgen_low(l_uint32 *, l_int32, l_int32, l_int32,
l_uint32 *, l_int32, l_int32);
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);
/*---------------------------------------------------------------------*
* Top-level hmt functions *
*---------------------------------------------------------------------*/
/*
* pixFlipFHMTGen()
*
* Input: pixd (usual 3 choices: null, == pixs, != pixs)
* pixs
* sel name (one of four defined in SEL_NAMES[])
* Return: pixd
*
* Notes:
* Action: hit-miss transform on pixs by the sel
* N.B.: the sel must have at least one hit, and it
* can have any number of misses.
*/
PIX *
pixFlipFHMTGen(PIX *pixd,
PIX *pixs,
const char *selname)
{
l_int32 i, index, found, w, h, wpls, wpld;
l_uint32 *datad, *datas, *datat;
PIX *pixt;
PROCNAME("pixFlipFHMTGen");
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 (!pixSizesEqual(pixs, pixd))
return (PIX *)ERROR_PTR("sizes not equal", procName, pixd);
} else {
if ((pixd = pixCreateTemplate(pixs)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
}
wpls = pixGetWpl(pixs);
wpld = pixGetWpl(pixd);
/* The images must be surrounded with ADDED_BORDER white pixels,
* that we'll read from. We fabricate a "proper"
* image as the subimage within the border, having the
* following parameters: */
w = pixGetWidth(pixs) - 2 * ADDED_BORDER;
h = pixGetHeight(pixs) - 2 * ADDED_BORDER;
datas = pixGetData(pixs) + ADDED_BORDER * wpls + ADDED_BORDER / 32;
datad = pixGetData(pixd) + ADDED_BORDER * wpld + ADDED_BORDER / 32;
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) + ADDED_BORDER * wpls + ADDED_BORDER / 32;
flipfhmtgen_low(datad, w, h, wpld, datat, wpls, index);
pixDestroy(&pixt);
} else { /* simple and not in-place */
flipfhmtgen_low(datad, w, h, wpld, datas, wpls, index);
}
return pixd;
}
/*---------------------------------------------------------------------*
* Fast hmt dispatcher *
*---------------------------------------------------------------------*/
/*
* flipfhmtgen_low()
*
* A dispatcher to appropriate low-level code for flip hmt ops
*/
static l_int32
flipfhmtgen_low(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;
}
return 0;
}
/*--------------------------------------------------------------------------*
* Low-level auto-generated hmt routines *
*--------------------------------------------------------------------------*/
/*
* N.B. in all the low-level routines, the part of the image
* that is accessed has been clipped by ADDED_BORDER 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;
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 - wpls) >> 3) | (*(sptr - wpls - 1) << 29)) &
(~*(sptr - wpls)) &
((~*(sptr - wpls) << 1) | (~*(sptr - wpls + 1) >> 31)) &
((*(sptr) >> 3) | (*(sptr - 1) << 29)) &
((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) &
(~*sptr) &
((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) &
((*(sptr + wpls) >> 3) | (*(sptr + wpls - 1) << 29)) &
(~*(sptr + wpls)) &
((*(sptr + wpls2) >> 3) | (*(sptr + wpls2 - 1) << 29)) &
((*(sptr + wpls3) >> 3) | (*(sptr + wpls3 - 1) << 29)) &
((*(sptr + wpls3) >> 2) | (*(sptr + wpls3 - 1) << 30)) &
((*(sptr + wpls3) >> 1) | (*(sptr + wpls3 - 1) << 31)) &
(*(sptr + wpls3)) &
((*(sptr + wpls3) << 1) | (*(sptr + wpls3 + 1) >> 31)) &
((*(sptr + wpls3) << 2) | (*(sptr + wpls3 + 1) >> 30));
}
}
}
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;
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 - wpls) >> 1) | (~*(sptr - wpls - 1) << 31)) &
(~*(sptr - wpls)) &
((*(sptr - wpls) << 3) | (*(sptr - wpls + 1) >> 29)) &
((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) &
(~*sptr) &
((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) &
((*(sptr) << 3) | (*(sptr + 1) >> 29)) &
(~*(sptr + wpls)) &
((*(sptr + wpls) << 3) | (*(sptr + wpls + 1) >> 29)) &
((*(sptr + wpls2) << 3) | (*(sptr + wpls2 + 1) >> 29)) &
((*(sptr + wpls3) >> 2) | (*(sptr + wpls3 - 1) << 30)) &
((*(sptr + wpls3) >> 1) | (*(sptr + wpls3 - 1) << 31)) &
(*(sptr + wpls3)) &
((*(sptr + wpls3) << 1) | (*(sptr + wpls3 + 1) >> 31)) &
((*(sptr + wpls3) << 2) | (*(sptr + wpls3 + 1) >> 30)) &
((*(sptr + wpls3) << 3) | (*(sptr + wpls3 + 1) >> 29));
}
}
}
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;
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) >> 3) | (*(sptr - wpls3 - 1) << 29)) &
((*(sptr - wpls3) >> 2) | (*(sptr - wpls3 - 1) << 30)) &
((*(sptr - wpls3) >> 1) | (*(sptr - wpls3 - 1) << 31)) &
(*(sptr - wpls3)) &
((*(sptr - wpls3) << 1) | (*(sptr - wpls3 + 1) >> 31)) &
((*(sptr - wpls3) << 2) | (*(sptr - wpls3 + 1) >> 30)) &
((*(sptr - wpls2) >> 3) | (*(sptr - wpls2 - 1) << 29)) &
((*(sptr - wpls) >> 3) | (*(sptr - wpls - 1) << 29)) &
(~*(sptr - wpls)) &
((*(sptr) >> 3) | (*(sptr - 1) << 29)) &
((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) &
(~*sptr) &
((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) &
((*(sptr + wpls) >> 3) | (*(sptr + wpls - 1) << 29)) &
(~*(sptr + wpls)) &
((~*(sptr + wpls) << 1) | (~*(sptr + wpls + 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;
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) >> 2) | (*(sptr - wpls3 - 1) << 30)) &
((*(sptr - wpls3) >> 1) | (*(sptr - wpls3 - 1) << 31)) &
(*(sptr - wpls3)) &
((*(sptr - wpls3) << 1) | (*(sptr - wpls3 + 1) >> 31)) &
((*(sptr - wpls3) << 2) | (*(sptr - wpls3 + 1) >> 30)) &
((*(sptr - wpls3) << 3) | (*(sptr - wpls3 + 1) >> 29)) &
((*(sptr - wpls2) << 3) | (*(sptr - wpls2 + 1) >> 29)) &
(~*(sptr - wpls)) &
((*(sptr - wpls) << 3) | (*(sptr - wpls + 1) >> 29)) &
((~*(sptr) >> 1) | (~*(sptr - 1) << 31)) &
(~*sptr) &
((~*(sptr) << 1) | (~*(sptr + 1) >> 31)) &
((*(sptr) << 3) | (*(sptr + 1) >> 29)) &
((~*(sptr + wpls) >> 1) | (~*(sptr + wpls - 1) << 31)) &
(~*(sptr + wpls)) &
((*(sptr + wpls) << 3) | (*(sptr + wpls + 1) >> 29));
}
}
}

View File

@ -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 fmorphauto.c
* <pre>
*
* 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.
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
#define OUTROOT "fmorphgen"
#define TEMPLATE1 "morphtemplate1.txt"
#define TEMPLATE2 "morphtemplate2.txt"
#define PROTOARGS "(l_uint32 *, l_int32, l_int32, l_int32, l_uint32 *, l_int32);"
static const l_int32 L_BUF_SIZE = 512;
static char * makeBarrelshiftString(l_int32 delx, l_int32 dely);
static SARRAY * sarrayMakeInnerLoopDWACode(SEL *sel, l_int32 index);
static SARRAY * sarrayMakeWplsCode(SEL *sel);
static char wpldecls[][53] = {
"l_int32 wpls2;",
"l_int32 wpls2, wpls3;",
"l_int32 wpls2, wpls3, wpls4;",
"l_int32 wpls5;",
"l_int32 wpls5, wpls6;",
"l_int32 wpls5, wpls6, wpls7;",
"l_int32 wpls5, wpls6, wpls7, wpls8;",
"l_int32 wpls9;",
"l_int32 wpls9, wpls10;",
"l_int32 wpls9, wpls10, wpls11;",
"l_int32 wpls9, wpls10, wpls11, wpls12;",
"l_int32 wpls13;",
"l_int32 wpls13, wpls14;",
"l_int32 wpls13, wpls14, wpls15;",
"l_int32 wpls13, wpls14, wpls15, wpls16;",
"l_int32 wpls17;",
"l_int32 wpls17, wpls18;",
"l_int32 wpls17, wpls18, wpls19;",
"l_int32 wpls17, wpls18, wpls19, wpls20;",
"l_int32 wpls21;",
"l_int32 wpls21, wpls22;",
"l_int32 wpls21, wpls22, wpls23;",
"l_int32 wpls21, wpls22, wpls23, wpls24;",
"l_int32 wpls25;",
"l_int32 wpls25, wpls26;",
"l_int32 wpls25, wpls26, wpls27;",
"l_int32 wpls25, wpls26, wpls27, wpls28;",
"l_int32 wpls29;",
"l_int32 wpls29, wpls30;",
"l_int32 wpls29, wpls30, wpls31;"};
static char wplgendecls[][30] = {
"l_int32 wpls2;",
"l_int32 wpls3;",
"l_int32 wpls4;",
"l_int32 wpls5;",
"l_int32 wpls6;",
"l_int32 wpls7;",
"l_int32 wpls8;",
"l_int32 wpls9;",
"l_int32 wpls10;",
"l_int32 wpls11;",
"l_int32 wpls12;",
"l_int32 wpls13;",
"l_int32 wpls14;",
"l_int32 wpls15;",
"l_int32 wpls16;",
"l_int32 wpls17;",
"l_int32 wpls18;",
"l_int32 wpls19;",
"l_int32 wpls20;",
"l_int32 wpls21;",
"l_int32 wpls22;",
"l_int32 wpls23;",
"l_int32 wpls24;",
"l_int32 wpls25;",
"l_int32 wpls26;",
"l_int32 wpls27;",
"l_int32 wpls28;",
"l_int32 wpls29;",
"l_int32 wpls30;",
"l_int32 wpls31;"};
static char wpldefs[][25] = {
" 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;",
" wpls26 = 26 * wpls;",
" wpls27 = 27 * wpls;",
" wpls28 = 28 * wpls;",
" wpls29 = 29 * wpls;",
" wpls30 = 30 * wpls;",
" wpls31 = 31 * wpls;"};
static char wplstrp[][10] = {"+ wpls", "+ wpls2", "+ wpls3", "+ wpls4",
"+ wpls5", "+ wpls6", "+ wpls7", "+ wpls8",
"+ wpls9", "+ wpls10", "+ wpls11", "+ wpls12",
"+ wpls13", "+ wpls14", "+ wpls15", "+ wpls16",
"+ wpls17", "+ wpls18", "+ wpls19", "+ wpls20",
"+ wpls21", "+ wpls22", "+ wpls23", "+ wpls24",
"+ wpls25", "+ wpls26", "+ wpls27", "+ wpls28",
"+ wpls29", "+ wpls30", "+ wpls31"};
static char wplstrm[][10] = {"- wpls", "- wpls2", "- wpls3", "- wpls4",
"- wpls5", "- wpls6", "- wpls7", "- wpls8",
"- wpls9", "- wpls10", "- wpls11", "- wpls12",
"- wpls13", "- wpls14", "- wpls15", "- wpls16",
"- wpls17", "- wpls18", "- wpls19", "- wpls20",
"- wpls21", "- wpls22", "- wpls23", "- wpls24",
"- wpls25", "- wpls26", "- wpls27", "- wpls28",
"- wpls29", "- wpls30", "- wpls31"};
/*!
* \brief fmorphautogen()
*
* \param[in] sela
* \param[in] fileindex
* \param[in] filename [optional]; can be null
* \return 0 if OK; 1 on error
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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 (<optional>; can be null)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) This function uses morphtemplate2.txt to create a
* low-level file that contains the low-level functions for
* implementing dilation and erosion 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 fmorphgenlow.[n].c,
* where [n] is equal to the 'fileindex' parameter.
* (4) If filename != NULL, the output file is [filename]low.[n].c.
*/
l_int32
fmorphautogen2(SELA *sela,
l_int32 fileindex,
const char *filename)
{
char *filestr, *linestr, *fname;
char *str_doc1, *str_doc2, *str_doc3, *str_doc4, *str_def1;
char bigbuf[L_BUF_SIZE];
char breakstring[] = " break;";
char staticstring[] = "static void";
l_int32 i, nsels, nbytes, actstart, end, newstart;
l_int32 argstart, argend, loopstart, loopend, finalstart, finalend;
size_t size;
SARRAY *sa1, *sa2, *sa3, *sa4, *sa5, *sa6;
SEL *sel;
PROCNAME("fmorphautogen2");
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 morphtemplate2.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(2 * nsels)) == NULL) {
sarrayDestroy(&sa1);
return ERROR_INT("sa2 not made", procName, 1);
}
for (i = 0; i < nsels; i++) {
sprintf(bigbuf, "fdilate_%d_%d", fileindex, i);
sarrayAddString(sa2, bigbuf, L_COPY);
sprintf(bigbuf, "ferode_%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 < 2 * 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 fmorphopgen_low_%d()",
fileindex);
str_doc1 = stringNew(bigbuf);
sprintf(bigbuf, " * void fdilate_%d_*()", fileindex);
str_doc2 = stringNew(bigbuf);
sprintf(bigbuf, " * void ferode_%d_*()", fileindex);
str_doc3 = 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);
sarrayAddString(sa4, str_doc3, L_INSERT);
sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);
sarrayAppendRange(sa4, sa1, actstart, end);
/* Insert static protos */
for (i = 0; i < 2 * 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);
}
/* More strings with function names */
sprintf(bigbuf, " * fmorphopgen_low_%d()", fileindex);
str_doc4 = stringNew(bigbuf);
sprintf(bigbuf, "fmorphopgen_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_doc4, 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 < 2 * 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 < 2 * 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/2)) == 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);
/* Add the function loop code */
sarrayAppendRange(sa4, sa1, loopstart, loopend);
/* Insert barrel-op code for *dptr */
sa6 = sarrayMakeInnerLoopDWACode(sel, i);
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, allvshifts;
l_int32 vshift[32];
SARRAY *sa;
PROCNAME("sarrayMakeWplsCode");
if (!sel)
return (SARRAY *)ERROR_PTR("sel not defined", procName, NULL);
for (i = 0; i < 32; i++)
vshift[i] = 0;
ymax = 0;
for (i = 0; i < sel->sy; i++) {
for (j = 0; j < sel->sx; j++) {
if (sel->data[i][j] == 1) {
dely = L_ABS(i - sel->cy);
if (dely < 32)
vshift[dely] = 1;
ymax = L_MAX(ymax, dely);
}
}
}
if (ymax > 31) {
L_WARNING("ymax > 31; truncating to 31\n", procName);
ymax = 31;
}
/* Test if this is a vertical brick */
allvshifts = TRUE;
for (i = 0; i < ymax; i++) {
if (vshift[i] == 0) {
allvshifts = FALSE;
break;
}
}
sa = sarrayCreate(0);
/* Add declarations */
if (allvshifts == TRUE) { /* packs them as well as possible */
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);
} else { /* puts them one/line */
for (i = 2; i <= ymax; i++) {
if (vshift[i])
sarrayAddString(sa, wplgendecls[i - 2], L_COPY);
}
}
sarrayAddString(sa, emptystring, L_COPY);
/* Add definitions */
for (i = 2; i <= ymax; i++) {
if (vshift[i])
sarrayAddString(sa, wpldefs[i - 2], L_COPY);
}
return sa;
}
/*!
* \brief sarrayMakeInnerLoopDWACode()
*/
static SARRAY *
sarrayMakeInnerLoopDWACode(SEL *sel,
l_int32 index)
{
char *tstr, *string;
char logicalor[] = "|";
char logicaland[] = "&";
char bigbuf[L_BUF_SIZE];
l_int32 i, j, optype, count, nfound, delx, dely;
SARRAY *sa;
PROCNAME("sarrayMakeInnerLoopDWACode");
if (!sel)
return (SARRAY *)ERROR_PTR("sel not defined", procName, NULL);
if (index % 2 == 0) {
optype = L_MORPH_DILATE;
tstr = logicalor;
} else {
optype = L_MORPH_ERODE;
tstr = logicaland;
}
count = 0;
for (i = 0; i < sel->sy; i++) {
for (j = 0; j < sel->sx; j++) {
if (sel->data[i][j] == 1)
count++;
}
}
sa = sarrayCreate(0);
if (count == 0) {
L_WARNING("no hits in Sel %d\n", procName, index);
return sa; /* no code inside! */
}
nfound = 0;
for (i = 0; i < sel->sy; i++) {
for (j = 0; j < sel->sx; j++) {
if (sel->data[i][j] == 1) {
nfound++;
if (optype == L_MORPH_DILATE) {
dely = sel->cy - i;
delx = sel->cx - j;
} else { /* optype == L_MORPH_ERODE */
dely = i - sel->cy;
delx = j - sel->cx;
}
if ((string = makeBarrelshiftString(delx, dely)) == NULL) {
L_WARNING("barrel shift string not made\n", procName);
continue;
}
if (count == 1) /* just one item */
sprintf(bigbuf, " *dptr = %s;", string);
else if (nfound == 1)
sprintf(bigbuf, " *dptr = %s %s", string, tstr);
else if (nfound < count)
sprintf(bigbuf, " %s %s", string, tstr);
else /* nfound == count */
sprintf(bigbuf, " %s;", string);
sarrayAddString(sa, bigbuf, L_COPY);
LEPT_FREE(string);
}
}
}
return sa;
}
/*!
* \brief makeBarrelshiftString()
*/
static char *
makeBarrelshiftString(l_int32 delx, /* j - cx */
l_int32 dely) /* i - cy */
{
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 ((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);
}

View File

@ -1,277 +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_1()
* PIX *pixFMorphopGen_1()
*/
#include <string.h>
#include "allheaders.h"
PIX *pixMorphDwa_1(PIX *pixd, PIX *pixs, l_int32 operation, char *selname);
PIX *pixFMorphopGen_1(PIX *pixd, PIX *pixs, l_int32 operation, char *selname);
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);
static l_int32 NUM_SELS_GENERATED = 58;
static char SEL_NAMES[][80] = {
"sel_2h",
"sel_3h",
"sel_4h",
"sel_5h",
"sel_6h",
"sel_7h",
"sel_8h",
"sel_9h",
"sel_10h",
"sel_11h",
"sel_12h",
"sel_13h",
"sel_14h",
"sel_15h",
"sel_20h",
"sel_21h",
"sel_25h",
"sel_30h",
"sel_31h",
"sel_35h",
"sel_40h",
"sel_41h",
"sel_45h",
"sel_50h",
"sel_51h",
"sel_2v",
"sel_3v",
"sel_4v",
"sel_5v",
"sel_6v",
"sel_7v",
"sel_8v",
"sel_9v",
"sel_10v",
"sel_11v",
"sel_12v",
"sel_13v",
"sel_14v",
"sel_15v",
"sel_20v",
"sel_21v",
"sel_25v",
"sel_30v",
"sel_31v",
"sel_35v",
"sel_40v",
"sel_41v",
"sel_45v",
"sel_50v",
"sel_51v",
"sel_2",
"sel_3",
"sel_4",
"sel_5",
"sel_2dp",
"sel_2dm",
"sel_5dp",
"sel_5dm"};
/*!
* \brief pixMorphDwa_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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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
* <pre>
*
* 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.
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
/* --------------------------------------------------------------------*/
#if HAVE_LIBGIF || HAVE_LIBUNGIF /* defined in environ.h */
/* --------------------------------------------------------------------*/
#include "gif_lib.h"
/* Interface that enables low-level GIF support for reading from memory */
static PIX * gifToPix(GifFileType *gif);
/* Interface that enables low-level GIF support for writing to memory */
static l_int32 pixToGif(PIX *pix, GifFileType *gif);
/*! For in-memory decoding of GIF; 5.1+ */
typedef struct GifReadBuffer
{
size_t size; /*!< size of buffer */
size_t pos; /*!< position relative to beginning of buffer */
const l_uint8 *cdata; /*!< data in the buffer */
} GifReadBuffer;
/*! Low-level callback for in-memory decoding */
static l_int32 gifReadFunc(GifFileType *gif, GifByteType *dest,
l_int32 bytesToRead);
/*! Low-level callback for in-memory encoding */
static l_int32 gifWriteFunc(GifFileType *gif, const GifByteType *src,
l_int32 bytesToWrite);
/*---------------------------------------------------------------------*
* Reading gif *
*---------------------------------------------------------------------*/
/*!
* \brief pixReadStreamGif()
*
* \param[in] fp file stream opened for reading
* \return pix, or NULL on error
*/
PIX *
pixReadStreamGif(FILE *fp)
{
l_uint8 *filedata;
size_t filesize;
PIX *pix;
PROCNAME("pixReadStreamGif");
if (!fp)
return (PIX *)ERROR_PTR("fp not defined", procName, NULL);
/* Read data into memory from file */
rewind(fp);
if ((filedata = l_binaryReadStream(fp, &filesize)) == NULL)
return (PIX *)ERROR_PTR("filedata not read", procName, NULL);
/* Uncompress from memory */
pix = pixReadMemGif(filedata, filesize);
LEPT_FREE(filedata);
if (!pix)
L_ERROR("failed to read gif from file data\n", procName);
return pix;
}
/*!
* \brief pixReadMemGif()
*
* \param[in] cdata const; gif-encoded
* \param[in] size bytes data
* \return pix, or NULL on error
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) See comments in pixReadMemGif()
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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 */

View File

@ -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
* <pre>
*
* Stubs for gifio.c functions
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include "allheaders.h"
/* -----------------------------------------------------------------*/
#if (!HAVE_LIBGIF) && (!HAVE_LIBUNGIF) /* defined in environ.h */
/* -----------------------------------------------------------------*/
PIX * pixReadStreamGif(FILE *fp)
{
return (PIX * )ERROR_PTR("function not present", "pixReadStreamGif", NULL);
}
/* ----------------------------------------------------------------------*/
PIX * pixReadMemGif(const l_uint8 *cdata, size_t size)
{
return (PIX *)ERROR_PTR("function not present", "pixReadMemGif", NULL);
}
/* ----------------------------------------------------------------------*/
l_ok pixWriteStreamGif(FILE *fp, PIX *pix)
{
return ERROR_INT("function not present", "pixWriteStreamGif", 1);
}
/* ----------------------------------------------------------------------*/
l_ok pixWriteMemGif(l_uint8 **pdata, size_t *psize, PIX *pix)
{
return ERROR_INT("function not present", "pixWriteMemGif", 1);
}
/* -----------------------------------------------------------------*/
#endif /* !HAVE_LIBGIF && !HAVE_LIBUNGIF */

File diff suppressed because it is too large Load Diff

View File

@ -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
*
* <pre>
* 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", ...);
* </pre>
*/
#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 */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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
* <pre>
*
* 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. ------------------------------
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
/* Bounds on initial array size */
static const l_uint32 MaxPtrArraySize = 100000;
static const l_int32 InitialPtrArraySize = 20; /*!< n'importe quoi */
#define SWAP_ITEMS(i, j) { void *tempitem = lh->array[(i)]; \
lh->array[(i)] = lh->array[(j)]; \
lh->array[(j)] = tempitem; }
/* Static functions */
static l_int32 lheapExtendArray(L_HEAP *lh);
static l_ok lheapSwapUp(L_HEAP *lh, l_int32 index);
static l_ok lheapSwapDown(L_HEAP *lh);
/*--------------------------------------------------------------------------*
* L_Heap create/destroy *
*--------------------------------------------------------------------------*/
/*!
* \brief lheapCreate()
*
* \param[in] n size of ptr array to be alloc'd; use 0 for default
* \param[in] direction L_SORT_INCREASING, L_SORT_DECREASING
* \return lheap, or NULL on error
*/
L_HEAP *
lheapCreate(l_int32 n,
l_int32 direction)
{
L_HEAP *lh;
PROCNAME("lheapCreate");
if (n < InitialPtrArraySize || n > MaxPtrArraySize)
n = InitialPtrArraySize;
/* Allocate ptr array and initialize counters. */
lh = (L_HEAP *)LEPT_CALLOC(1, sizeof(L_HEAP));
if ((lh->array = (void **)LEPT_CALLOC(n, sizeof(void *))) == NULL) {
lheapDestroy(&lh, FALSE);
return (L_HEAP *)ERROR_PTR("ptr array not made", procName, NULL);
}
lh->nalloc = n;
lh->n = 0;
lh->direction = direction;
return lh;
}
/*!
* \brief lheapDestroy()
*
* \param[in,out] plh will be set to null before returning
* \param[in] freeflag TRUE to free each remaining struct in the array
* \return void
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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).
* </pre>
*/
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;
}

View File

@ -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
*
* <pre>
* 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.
* </pre>
*/
/*! 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 */

View File

@ -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
*
* <pre>
* 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.
* </pre>
*/
#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 */

File diff suppressed because it is too large Load Diff

View File

@ -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
*/
/*!
* <pre>
* 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.
* </pre>
*/
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;
/*!
* <pre>
* 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.
* </pre>
*/
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 */

View File

@ -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
* <pre>
*
* 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.
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include <math.h>
#include "allheaders.h"
#ifndef NO_CONSOLE_IO
#define DEBUG_IHDR 0
#endif /* ~NO_CONSOLE_IO */
/* --------------------------------------------*/
#if USE_JP2KHEADER /* defined in environ.h */
/* --------------------------------------------*/
/* a sanity check on the size read from file */
static const l_int32 MAX_JP2K_WIDTH = 100000;
static const l_int32 MAX_JP2K_HEIGHT = 100000;
/*--------------------------------------------------------------------*
* Stream interface *
*--------------------------------------------------------------------*/
/*!
* \brief readHeaderJp2k()
*
* \param[in] filename
* \param[out] pw [optional]
* \param[out] ph [optional]
* \param[out] pbps [optional] bits/sample
* \param[out] pspp [optional] samples/pixel
* \return 0 if OK, 1 on error
*/
l_ok
readHeaderJp2k(const char *filename,
l_int32 *pw,
l_int32 *ph,
l_int32 *pbps,
l_int32 *pspp)
{
l_int32 ret;
FILE *fp;
PROCNAME("readHeaderJp2k");
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 = freadHeaderJp2k(fp, pw, ph, pbps, pspp);
fclose(fp);
return ret;
}
/*!
* \brief freadHeaderJp2k()
*
* \param[in] fp file stream opened for read
* \param[out] pw [optional]
* \param[out] ph [optional]
* \param[out] pbps [optional] bits/sample
* \param[out] pspp [optional] samples/pixel
* \return 0 if OK, 1 on error
*/
l_ok
freadHeaderJp2k(FILE *fp,
l_int32 *pw,
l_int32 *ph,
l_int32 *pbps,
l_int32 *pspp)
{
l_uint8 buf[80]; /* just need the first 80 bytes */
l_int32 nread, ret;
PROCNAME("freadHeaderJp2k");
if (!fp)
return ERROR_INT("fp not defined", procName, 1);
rewind(fp);
nread = fread(buf, 1, sizeof(buf), fp);
if (nread != sizeof(buf))
return ERROR_INT("read failure", procName, 1);
ret = readHeaderMemJp2k(buf, sizeof(buf), pw, ph, pbps, pspp);
rewind(fp);
return ret;
}
/*!
* \brief readHeaderMemJp2k()
*
* \param[in] data
* \param[in] size at least 80
* \param[out] pw [optional]
* \param[out] ph [optional]
* \param[out] pbps [optional] bits/sample
* \param[out] pspp [optional] samples/pixel
* \return 0 if OK, 1 on error
*
* <pre>
* 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)
* </pre>
*/
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 (<return> resolution in ppi)
* Return: 0 if found; 1 if not found or on error
*
* Notes:
* (1) If the capture resolution field is not set, this is not an error;
* the returned resolution values are 0 (designating 'unknown').
* (2) Side-effect: this rewinds the stream.
* (3) The capture resolution box is optional in the jp2 spec, and
* it is usually not written.
* (4) The big-endian data fields that follow the 4 bytes of 'resc' are:
* ynum: 2 bytes
* ydenom: 2 bytes
* xnum: 2 bytes
* xdenom: 2 bytes
* yexp: 1 byte
* xexp: 1 byte
*/
l_int32
fgetJp2kResolution(FILE *fp,
l_int32 *pxres,
l_int32 *pyres)
{
l_uint8 xexp, yexp;
l_uint8 *data;
l_uint16 xnum, ynum, xdenom, ydenom; /* these jp2k fields are 2-byte */
l_int32 loc, found;
l_uint8 resc[4] = {0x72, 0x65, 0x73, 0x63}; /* 'resc' */
size_t nbytes;
l_float64 xres, yres, maxres;
PROCNAME("fgetJp2kResolution");
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);
data = l_binaryReadStream(fp, &nbytes);
rewind(fp);
/* Search for the start of the first capture resolution box: 'resc' */
arrayFindSequence(data, nbytes, resc, 4, &loc, &found);
if (!found) {
L_WARNING("image resolution not found\n", procName);
LEPT_FREE(data);
return 1;
}
if (nbytes < 80 || loc >= nbytes - 13) {
L_WARNING("image resolution found without enough space\n", procName);
LEPT_FREE(data);
return 1;
}
/* Extract the fields and calculate the resolution in pixels/meter.
* See section 1.5.3.7.1 of JPEG 2000 ISO/IEC 15444-1 spec. */
ynum = data[loc + 5] << 8 | data[loc + 4];
ynum = convertOnLittleEnd16(ynum);
ydenom = data[loc + 7] << 8 | data[loc + 6];
ydenom = convertOnLittleEnd16(ydenom);
xnum = data[loc + 9] << 8 | data[loc + 8];
xnum = convertOnLittleEnd16(xnum);
xdenom = data[loc + 11] << 8 | data[loc + 10];
xdenom = convertOnLittleEnd16(xdenom);
yexp = data[loc + 12];
xexp = data[loc + 13];
yres = ((l_float64)ynum / (l_float64)ydenom) * pow(10.0, (l_float64)yexp);
xres = ((l_float64)xnum / (l_float64)xdenom) * pow(10.0, (l_float64)xexp);
/* Convert from pixels/meter to ppi */
yres *= (300.0 / 11811.0);
xres *= (300.0 / 11811.0);
/* Sanity check for bad data */
maxres = 100000.0; /* ppi */
if (xres > maxres || yres > maxres) {
L_WARNING("ridiculously large resolution\n", procName);
} else {
*pyres = (l_int32)(yres + 0.5);
*pxres = (l_int32)(xres + 0.5);
}
LEPT_FREE(data);
return 0;
}
/* --------------------------------------------*/
#endif /* USE_JP2KHEADER */

View File

@ -1,75 +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 jp2kheaderstub.c
* <pre>
*
* Stubs for jp2kheader.c functions
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include "allheaders.h"
/* --------------------------------------------*/
#if !USE_JP2KHEADER /* defined in environ.h */
/* --------------------------------------------*/
l_ok readHeaderJp2k(const char *filename, l_int32 *pw, l_int32 *ph,
l_int32 *pbps, l_int32 *pspp)
{
return ERROR_INT("function not present", "readHeaderJp2k", 1);
}
/* ----------------------------------------------------------------------*/
l_ok freadHeaderJp2k(FILE *fp, l_int32 *pw, l_int32 *ph,
l_int32 *pbps, l_int32 *pspp)
{
return ERROR_INT("function not present", "freadHeaderJp2k", 1);
}
/* ----------------------------------------------------------------------*/
l_ok readHeaderMemJp2k(const l_uint8 *cdata, size_t size, l_int32 *pw,
l_int32 *ph, l_int32 *pbps, l_int32 *pspp)
{
return ERROR_INT("function not present", "readHeaderMemJp2k", 1);
}
/* ----------------------------------------------------------------------*/
l_int32 fgetJp2kResolution(FILE *fp, l_int32 *pxres, l_int32 *pyres)
{
return ERROR_INT("function not present", "fgetJp2kResolution", 1);
}
/* --------------------------------------------*/
#endif /* !USE_JP2KHEADER */

View File

@ -1,949 +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 jp2kio.c
* <pre>
*
* 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.
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
/* --------------------------------------------*/
#if HAVE_LIBJP2K /* defined in environ.h */
/* --------------------------------------------*/
/* Leptonica supports versions 2.0 and newer */
#ifdef LIBJP2K_HEADER
#include LIBJP2K_HEADER
#else
#include <openjpeg.h>
#endif
/* 2.0 didn't define OPJ_VERSION_MINOR. */
#ifndef OPJ_VERSION_MINOR
#define OPJ_VERSION_MINOR 0
#endif
/* Static generator of opj_stream from file stream.
* In 2.0.1, this functionality is provided by
* opj_stream_create_default_file_stream(),
* but it was removed in 2.1.0. Because we must have either
* a file stream or a memory interface to the compressed data,
* it is necessary to recreate the stream interface here. */
static opj_stream_t *opjCreateStream(FILE *fp, l_int32 is_read);
/* Static converter pix --> opj_image. Used for compressing pix,
* because the codec works on data stored in their raster format. */
static opj_image_t *pixConvertToOpjImage(PIX *pix);
/*---------------------------------------------------------------------*
* Callback event handlers *
*---------------------------------------------------------------------*/
static void error_callback(const char *msg, void *client_data) {
(void)client_data;
fprintf(stdout, "[ERROR] %s", msg);
}
static void warning_callback(const char *msg, void *client_data) {
(void)client_data;
fprintf(stdout, "[WARNING] %s", msg);
}
static void info_callback(const char *msg, void *client_data) {
(void)client_data;
fprintf(stdout, "[INFO] %s", msg);
}
/*---------------------------------------------------------------------*
* Read jp2k from file (special function) *
*---------------------------------------------------------------------*/
/*!
* \brief pixReadJp2k()
*
* \param[in] filename
* \param[in] reduction scaling factor: 1, 2, 4, 8, 16
* \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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) See pixReadJp2k() for usage.
* </pre>
*/
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(&parameters);
/* 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, &parameters)){
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
*
* <pre>
* 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.
* </pre>
*/
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
* <pre>
* 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
* </pre>
*/
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(&parameters);
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, &parameters, 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
*
* <pre>
* Notes:
* (1) Input pix is 8 bpp grayscale, 32 bpp rgb, or 32 bpp rgba.
* (2) Gray + alpha pix are all represented as rgba.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) See pixWriteJp2k() for usage. This version writes to
* memory instead of to a file stream.
* </pre>
*/
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 */
/* --------------------------------------------*/

View File

@ -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
* <pre>
*
* Stubs for jp2kio.c functions
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include "allheaders.h"
/* --------------------------------------------*/
#if !HAVE_LIBJP2K /* defined in environ.h */
/* --------------------------------------------*/
/* ----------------------------------------------------------------------*/
PIX * pixReadJp2k(const char *filename, l_uint32 reduction, BOX *box,
l_int32 hint, l_int32 debug)
{
return (PIX * )ERROR_PTR("function not present", "pixReadJp2k", NULL);
}
/* ----------------------------------------------------------------------*/
PIX * pixReadStreamJp2k(FILE *fp, l_uint32 reduction, BOX *box,
l_int32 hint, l_int32 debug)
{
return (PIX * )ERROR_PTR("function not present", "pixReadStreamJp2k", NULL);
}
/* ----------------------------------------------------------------------*/
l_ok pixWriteJp2k(const char *filename, PIX *pix, l_int32 quality,
l_int32 nlevels, l_int32 hint, l_int32 debug)
{
return ERROR_INT("function not present", "pixWriteJp2k", 1);
}
/* ----------------------------------------------------------------------*/
l_ok pixWriteStreamJp2k(FILE *fp, PIX *pix, l_int32 quality,
l_int32 nlevels, l_int32 hint, l_int32 debug)
{
return ERROR_INT("function not present", "pixWriteStreamJp2k", 1);
}
/* ----------------------------------------------------------------------*/
PIX * pixReadMemJp2k(const l_uint8 *data, size_t size, l_uint32 reduction,
BOX *box, l_int32 hint, l_int32 debug)
{
return (PIX * )ERROR_PTR("function not present", "pixReadMemJp2k", NULL);
}
/* ----------------------------------------------------------------------*/
l_ok pixWriteMemJp2k(l_uint8 **pdata, size_t *psize, PIX *pix,
l_int32 quality, l_int32 nlevels, l_int32 hint,
l_int32 debug)
{
return ERROR_INT("function not present", "pixWriteMemJp2k", 1);
}
/* ----------------------------------------------------------------------*/
/* --------------------------------------------*/
#endif /* !HAVE_LIBJP2K */
/* --------------------------------------------*/

File diff suppressed because it is too large Load Diff

View File

@ -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
* <pre>
*
* Stubs for jpegio.c functions
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include "allheaders.h"
/* --------------------------------------------*/
#if !HAVE_LIBJPEG /* defined in environ.h */
/* --------------------------------------------*/
/* ----------------------------------------------------------------------*/
PIX * pixReadJpeg(const char *filename, l_int32 cmflag, l_int32 reduction,
l_int32 *pnwarn, l_int32 hint)
{
return (PIX * )ERROR_PTR("function not present", "pixReadJpeg", NULL);
}
/* ----------------------------------------------------------------------*/
PIX * pixReadStreamJpeg(FILE *fp, l_int32 cmflag, l_int32 reduction,
l_int32 *pnwarn, l_int32 hint)
{
return (PIX * )ERROR_PTR("function not present", "pixReadStreamJpeg", NULL);
}
/* ----------------------------------------------------------------------*/
l_ok readHeaderJpeg(const char *filename, l_int32 *pw, l_int32 *ph,
l_int32 *pspp, l_int32 *pycck, l_int32 *pcmyk)
{
return ERROR_INT("function not present", "readHeaderJpeg", 1);
}
/* ----------------------------------------------------------------------*/
l_ok freadHeaderJpeg(FILE *fp, l_int32 *pw, l_int32 *ph,
l_int32 *pspp, l_int32 *pycck, l_int32 *pcmyk)
{
return ERROR_INT("function not present", "freadHeaderJpeg", 1);
}
/* ----------------------------------------------------------------------*/
l_int32 fgetJpegResolution(FILE *fp, l_int32 *pxres, l_int32 *pyres)
{
return ERROR_INT("function not present", "fgetJpegResolution", 1);
}
/* ----------------------------------------------------------------------*/
l_int32 fgetJpegComment(FILE *fp, l_uint8 **pcomment)
{
return ERROR_INT("function not present", "fgetJpegComment", 1);
}
/* ----------------------------------------------------------------------*/
l_ok pixWriteJpeg(const char *filename, PIX *pix, l_int32 quality,
l_int32 progressive)
{
return ERROR_INT("function not present", "pixWriteJpeg", 1);
}
/* ----------------------------------------------------------------------*/
l_ok pixWriteStreamJpeg(FILE *fp, PIX *pix, l_int32 quality,
l_int32 progressive)
{
return ERROR_INT("function not present", "pixWriteStreamJpeg", 1);
}
/* ----------------------------------------------------------------------*/
PIX * pixReadMemJpeg(const l_uint8 *cdata, size_t size, l_int32 cmflag,
l_int32 reduction, l_int32 *pnwarn, l_int32 hint)
{
return (PIX * )ERROR_PTR("function not present", "pixReadMemJpeg", NULL);
}
/* ----------------------------------------------------------------------*/
l_ok readHeaderMemJpeg(const l_uint8 *cdata, size_t size,
l_int32 *pw, l_int32 *ph, l_int32 *pspp,
l_int32 *pycck, l_int32 *pcmyk)
{
return ERROR_INT("function not present", "readHeaderMemJpeg", 1);
}
/* ----------------------------------------------------------------------*/
l_ok readResolutionMemJpeg(const l_uint8 *data, size_t size,
l_int32 *pxres, l_int32 *pyres)
{
return ERROR_INT("function not present", "readResolutionMemJpeg", 1);
}
/* ----------------------------------------------------------------------*/
l_ok pixWriteMemJpeg(l_uint8 **pdata, size_t *psize, PIX *pix,
l_int32 quality, l_int32 progressive)
{
return ERROR_INT("function not present", "pixWriteMemJpeg", 1);
}
/* ----------------------------------------------------------------------*/
l_ok pixSetChromaSampling(PIX *pix, l_int32 sampling)
{
return ERROR_INT("function not present", "pixSetChromaSampling", 1);
}
/* ----------------------------------------------------------------------*/
/* --------------------------------------------*/
#endif /* !HAVE_LIBJPEG */
/* --------------------------------------------*/

Some files were not shown because too many files have changed in this diff Show More