mirror of http://192.168.1.51:8099/lmh188/twain3.0
1173 lines
42 KiB
C
1173 lines
42 KiB
C
/*====================================================================*
|
|
- Copyright (C) 2001 Leptonica. All rights reserved.
|
|
-
|
|
- Redistribution and use in source and binary forms, with or without
|
|
- modification, are permitted provided that the following conditions
|
|
- are met:
|
|
- 1. Redistributions of source code must retain the above copyright
|
|
- notice, this list of conditions and the following disclaimer.
|
|
- 2. Redistributions in binary form must reproduce the above
|
|
- copyright notice, this list of conditions and the following
|
|
- disclaimer in the documentation and/or other materials
|
|
- provided with the distribution.
|
|
-
|
|
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
- ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
|
|
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
- OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*====================================================================*/
|
|
|
|
/*!
|
|
* \file dewarp4.c
|
|
* <pre>
|
|
*
|
|
* Single page dewarper
|
|
*
|
|
* Reference model (book-level, dewarpa) operations and debugging output
|
|
*
|
|
* Top-level single page dewarper
|
|
* l_int32 dewarpSinglePage()
|
|
* l_int32 dewarpSinglePageInit()
|
|
* l_int32 dewarpSinglePageRun()
|
|
*
|
|
* Operations on dewarpa
|
|
* l_int32 dewarpaListPages()
|
|
* l_int32 dewarpaSetValidModels()
|
|
* l_int32 dewarpaInsertRefModels()
|
|
* l_int32 dewarpaStripRefModels()
|
|
* l_int32 dewarpaRestoreModels()
|
|
*
|
|
* Dewarp debugging output
|
|
* l_int32 dewarpaInfo()
|
|
* l_int32 dewarpaModelStats()
|
|
* static l_int32 dewarpaTestForValidModel()
|
|
* l_int32 dewarpaShowArrays()
|
|
* l_int32 dewarpDebug()
|
|
* l_int32 dewarpShowResults()
|
|
* </pre>
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include "allheaders.h"
|
|
|
|
static l_int32 dewarpaTestForValidModel(L_DEWARPA *dewa, L_DEWARP *dew,
|
|
l_int32 notests);
|
|
|
|
#ifndef NO_CONSOLE_IO
|
|
#define DEBUG_INVALID_MODELS 0 /* set this to 1 for debugging */
|
|
#endif /* !NO_CONSOLE_IO */
|
|
|
|
/* Special parameter value */
|
|
static const l_int32 GrayInValue = 200;
|
|
|
|
|
|
/*----------------------------------------------------------------------*
|
|
* Top-level single page dewarper *
|
|
*----------------------------------------------------------------------*/
|
|
/*!
|
|
* \brief dewarpSinglePage()
|
|
*
|
|
* \param[in] pixs with text, any depth
|
|
* \param[in] thresh for global thresh to 1 bpp; ignore otherwise
|
|
* \param[in] adaptive 1 for adaptive thresh; 0 for global threshold
|
|
* \param[in] useboth 1 for both horiz and vert; 0 for vertical only
|
|
* \param[in] check_columns 1 to skip horizontal if multiple columns;
|
|
* 0 otherwise; default is to skip
|
|
* \param[out] ppixd dewarped result
|
|
* \param[out] pdewa [optional] dewa with single page; NULL to skip
|
|
* \param[in] debug 1 for debugging output, 0 otherwise
|
|
* \return 0 if OK, 1 on error list of page numbers, or NULL on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) Dewarps pixs and returns the result in &pixd.
|
|
* (2) This uses default values for all model parameters.
|
|
* (3) If pixs is 1 bpp, the parameters %adaptive and %thresh are ignored.
|
|
* (4) If it can't build a model, returns a copy of pixs in &pixd.
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
dewarpSinglePage(PIX *pixs,
|
|
l_int32 thresh,
|
|
l_int32 adaptive,
|
|
l_int32 useboth,
|
|
l_int32 check_columns,
|
|
PIX **ppixd,
|
|
L_DEWARPA **pdewa,
|
|
l_int32 debug)
|
|
{
|
|
L_DEWARPA *dewa;
|
|
PIX *pixb;
|
|
|
|
PROCNAME("dewarpSinglePage");
|
|
|
|
if (!ppixd)
|
|
return ERROR_INT("&pixd not defined", procName, 1);
|
|
*ppixd = NULL;
|
|
if (pdewa) *pdewa = NULL;
|
|
if (!pixs)
|
|
return ERROR_INT("pixs not defined", procName, 1);
|
|
|
|
dewarpSinglePageInit(pixs, thresh, adaptive, useboth,
|
|
check_columns, &pixb, &dewa);
|
|
if (!pixb) {
|
|
dewarpaDestroy(&dewa);
|
|
return ERROR_INT("pixb not made", procName, 1);
|
|
}
|
|
|
|
dewarpSinglePageRun(pixs, pixb, dewa, ppixd, debug);
|
|
|
|
if (pdewa)
|
|
*pdewa = dewa;
|
|
else
|
|
dewarpaDestroy(&dewa);
|
|
pixDestroy(&pixb);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief dewarpSinglePageInit()
|
|
*
|
|
* \param[in] pixs with text, any depth
|
|
* \param[in] thresh for global thresh to 1 bpp; ignore otherwise
|
|
* \param[in] adaptive 1 for adaptive thresh; 0 for global threshold
|
|
* \param[in] useboth 1 for both horiz and vert; 0 for vertical only
|
|
* \param[in] check_columns 1 to skip horizontal if multiple columns;
|
|
* 0 otherwise; default is to skip
|
|
* \param[out] ppixb 1 bpp debug image
|
|
* \param[out] pdewa initialized dewa
|
|
* \return 0 if OK, 1 on error list of page numbers, or NULL on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) This binarizes the input pixs if necessary, returning the
|
|
* binarized image. It also initializes the dewa to default values
|
|
* for the model parameters.
|
|
* (2) If pixs is 1 bpp, the parameters %adaptive and %thresh are ignored.
|
|
* (3) To change the model parameters, call dewarpaSetCurvatures()
|
|
* before running dewarpSinglePageRun(). For example:
|
|
* dewarpSinglePageInit(pixs, 0, 1, 1, 1, &pixb, &dewa);
|
|
* dewarpaSetCurvatures(dewa, 250, -1, -1, 80, 70, 150);
|
|
* dewarpSinglePageRun(pixs, pixb, dewa, &pixd, 0);
|
|
* dewarpaDestroy(&dewa);
|
|
* pixDestroy(&pixb);
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
dewarpSinglePageInit(PIX *pixs,
|
|
l_int32 thresh,
|
|
l_int32 adaptive,
|
|
l_int32 useboth,
|
|
l_int32 check_columns,
|
|
PIX **ppixb,
|
|
L_DEWARPA **pdewa)
|
|
{
|
|
PIX *pix1;
|
|
|
|
PROCNAME("dewarpSinglePageInit");
|
|
|
|
if (ppixb) *ppixb = NULL;
|
|
if (pdewa) *pdewa = NULL;
|
|
if (!ppixb || !pdewa)
|
|
return ERROR_INT("&pixb and &dewa not both defined", procName, 1);
|
|
if (!pixs)
|
|
return ERROR_INT("pixs not defined", procName, 1);
|
|
|
|
*pdewa = dewarpaCreate(1, 0, 1, 0, -1);
|
|
dewarpaUseBothArrays(*pdewa, useboth);
|
|
dewarpaSetCheckColumns(*pdewa, check_columns);
|
|
|
|
/* Generate a binary image, if necessary */
|
|
if (pixGetDepth(pixs) > 1) {
|
|
pix1 = pixConvertTo8(pixs, 0);
|
|
if (adaptive)
|
|
*ppixb = pixAdaptThresholdToBinary(pix1, NULL, 1.0);
|
|
else
|
|
*ppixb = pixThresholdToBinary(pix1, thresh);
|
|
pixDestroy(&pix1);
|
|
} else {
|
|
*ppixb = pixClone(pixs);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief dewarpSinglePageRun()
|
|
*
|
|
* \param[in] pixs any depth
|
|
* \param[in] pixb 1 bpp
|
|
* \param[in] dewa initialized
|
|
* \param[out] ppixd dewarped result
|
|
* \param[in] debug 1 for debugging output, 0 otherwise
|
|
* \return 0 if OK, 1 on error list of page numbers, or NULL on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) Dewarps pixs and returns the result in &pixd.
|
|
* (2) The 1 bpp version %pixb and %dewa are conveniently generated by
|
|
* dewarpSinglePageInit().
|
|
* (3) Non-default model parameters must be set before calling this.
|
|
* (4) If a model cannot be built, this returns a copy of pixs in &pixd.
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
dewarpSinglePageRun(PIX *pixs,
|
|
PIX *pixb,
|
|
L_DEWARPA *dewa,
|
|
PIX **ppixd,
|
|
l_int32 debug)
|
|
{
|
|
const char *debugfile;
|
|
l_int32 vsuccess, ret;
|
|
L_DEWARP *dew;
|
|
|
|
PROCNAME("dewarpSinglePageRun");
|
|
|
|
if (!ppixd)
|
|
return ERROR_INT("&pixd not defined", procName, 1);
|
|
*ppixd = NULL;
|
|
if (!pixs)
|
|
return ERROR_INT("pixs not defined", procName, 1);
|
|
if (!pixb)
|
|
return ERROR_INT("pixb not defined", procName, 1);
|
|
if (!dewa)
|
|
return ERROR_INT("dewa not defined", procName, 1);
|
|
|
|
if (debug)
|
|
lept_mkdir("lept/dewarp");
|
|
|
|
/* Generate the page model */
|
|
dew = dewarpCreate(pixb, 0);
|
|
dewarpaInsertDewarp(dewa, dew);
|
|
debugfile = (debug) ? "/tmp/lept/dewarp/singlepage_model.pdf" : NULL;
|
|
dewarpBuildPageModel(dew, debugfile);
|
|
dewarpaModelStatus(dewa, 0, &vsuccess, NULL);
|
|
if (vsuccess == 0) {
|
|
L_ERROR("failure to build model for vertical disparity\n", procName);
|
|
*ppixd = pixCopy(NULL, pixs);
|
|
return 0;
|
|
}
|
|
|
|
/* Apply the page model */
|
|
debugfile = (debug) ? "/tmp/lept/dewarp/singlepage_apply.pdf" : NULL;
|
|
ret = dewarpaApplyDisparity(dewa, 0, pixs, 255, 0, 0, ppixd, debugfile);
|
|
if (ret)
|
|
L_ERROR("invalid model; failure to apply disparity\n", procName);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*
|
|
* Operations on dewarpa *
|
|
*----------------------------------------------------------------------*/
|
|
/*!
|
|
* \brief dewarpaListPages()
|
|
*
|
|
* \param[in] dewa populated with dewarp structs for pages
|
|
* \return 0 if OK, 1 on error list of page numbers, or NULL on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) This generates two numas, stored in the dewarpa, that give:
|
|
* (a) the page number for each dew that has a page model.
|
|
* (b) the page number for each dew that has either a page
|
|
* model or a reference model.
|
|
* It can be called at any time.
|
|
* (2) It is called by the dewarpa serializer before writing.
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
dewarpaListPages(L_DEWARPA *dewa)
|
|
{
|
|
l_int32 i;
|
|
L_DEWARP *dew;
|
|
NUMA *namodels, *napages;
|
|
|
|
PROCNAME("dewarpaListPages");
|
|
|
|
if (!dewa)
|
|
return ERROR_INT("dewa not defined", procName, 1);
|
|
|
|
numaDestroy(&dewa->namodels);
|
|
numaDestroy(&dewa->napages);
|
|
namodels = numaCreate(dewa->maxpage + 1);
|
|
napages = numaCreate(dewa->maxpage + 1);
|
|
dewa->namodels = namodels;
|
|
dewa->napages = napages;
|
|
for (i = 0; i <= dewa->maxpage; i++) {
|
|
if ((dew = dewarpaGetDewarp(dewa, i)) != NULL) {
|
|
if (dew->hasref == 0)
|
|
numaAddNumber(namodels, dew->pageno);
|
|
numaAddNumber(napages, dew->pageno);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief dewarpaSetValidModels()
|
|
*
|
|
* \param[in] dewa
|
|
* \param[in] notests
|
|
* \param[in] debug 1 to output information on invalid page models
|
|
* \return 0 if OK, 1 on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) A valid model must meet the rendering requirements, which
|
|
* include whether or not a vertical disparity model exists
|
|
* and conditions on curvatures for vertical and horizontal
|
|
* disparity models.
|
|
* (2) If %notests == 1, this ignores the curvature constraints
|
|
* and assumes that all successfully built models are valid.
|
|
* (3) This function does not need to be called by the application.
|
|
* It is called by dewarpaInsertRefModels(), which
|
|
* will destroy all invalid dewarps. Consequently, to inspect
|
|
* an invalid dewarp model, it must be done before calling
|
|
* dewarpaInsertRefModels().
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
dewarpaSetValidModels(L_DEWARPA *dewa,
|
|
l_int32 notests,
|
|
l_int32 debug)
|
|
{
|
|
l_int32 i, n, maxcurv, diffcurv, diffedge;
|
|
L_DEWARP *dew;
|
|
|
|
PROCNAME("dewarpaSetValidModels");
|
|
|
|
if (!dewa)
|
|
return ERROR_INT("dewa not defined", procName, 1);
|
|
|
|
n = dewa->maxpage + 1;
|
|
for (i = 0; i < n; i++) {
|
|
if ((dew = dewarpaGetDewarp(dewa, i)) == NULL)
|
|
continue;
|
|
|
|
if (debug) {
|
|
if (dew->hasref == 1) {
|
|
L_INFO("page %d: has only a ref model\n", procName, i);
|
|
} else if (dew->vsuccess == 0) {
|
|
L_INFO("page %d: no model successfully built\n",
|
|
procName, i);
|
|
} else if (!notests) {
|
|
maxcurv = L_MAX(L_ABS(dew->mincurv), L_ABS(dew->maxcurv));
|
|
diffcurv = dew->maxcurv - dew->mincurv;
|
|
if (dewa->useboth && !dew->hsuccess)
|
|
L_INFO("page %d: useboth, but no horiz disparity\n",
|
|
procName, i);
|
|
if (maxcurv > dewa->max_linecurv)
|
|
L_INFO("page %d: max curvature %d > max_linecurv\n",
|
|
procName, i, diffcurv);
|
|
if (diffcurv < dewa->min_diff_linecurv)
|
|
L_INFO("page %d: diff curv %d < min_diff_linecurv\n",
|
|
procName, i, diffcurv);
|
|
if (diffcurv > dewa->max_diff_linecurv)
|
|
L_INFO("page %d: abs diff curv %d > max_diff_linecurv\n",
|
|
procName, i, diffcurv);
|
|
if (dew->hsuccess) {
|
|
if (L_ABS(dew->leftslope) > dewa->max_edgeslope)
|
|
L_INFO("page %d: abs left slope %d > max_edgeslope\n",
|
|
procName, i, dew->leftslope);
|
|
if (L_ABS(dew->rightslope) > dewa->max_edgeslope)
|
|
L_INFO("page %d: abs right slope %d > max_edgeslope\n",
|
|
procName, i, dew->rightslope);
|
|
diffedge = L_ABS(dew->leftcurv - dew->rightcurv);
|
|
if (L_ABS(dew->leftcurv) > dewa->max_edgecurv)
|
|
L_INFO("page %d: left curvature %d > max_edgecurv\n",
|
|
procName, i, dew->leftcurv);
|
|
if (L_ABS(dew->rightcurv) > dewa->max_edgecurv)
|
|
L_INFO("page %d: right curvature %d > max_edgecurv\n",
|
|
procName, i, dew->rightcurv);
|
|
if (diffedge > dewa->max_diff_edgecurv)
|
|
L_INFO("page %d: abs diff left-right curv %d > "
|
|
"max_diff_edgecurv\n", procName, i, diffedge);
|
|
}
|
|
}
|
|
}
|
|
|
|
dewarpaTestForValidModel(dewa, dew, notests);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief dewarpaInsertRefModels()
|
|
*
|
|
* \param[in] dewa
|
|
* \param[in] notests if 1, ignore curvature constraints on model
|
|
* \param[in] debug 1 to output information on invalid page models
|
|
* \return 0 if OK, 1 on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) This destroys all dewarp models that are invalid, and then
|
|
* inserts reference models where possible.
|
|
* (2) If %notests == 1, this ignores the curvature constraints
|
|
* and assumes that all successfully built models are valid.
|
|
* (3) If useboth == 0, it uses the closest valid model within the
|
|
* distance and parity constraints. If useboth == 1, it tries
|
|
* to use the closest allowed hvalid model; if it doesn't find
|
|
* an hvalid model, it uses the closest valid model.
|
|
* (4) For all pages without a model, this clears out any existing
|
|
* invalid and reference dewarps, finds the nearest valid model
|
|
* with the same parity, and inserts an empty dewarp with the
|
|
* reference page.
|
|
* (5) Then if it is requested to use both vertical and horizontal
|
|
* disparity arrays (useboth == 1), it tries to replace any
|
|
* hvalid == 0 model or reference with an hvalid == 1 reference.
|
|
* (6) The distance constraint is that any reference model must
|
|
* be within maxdist. Note that with the parity constraint,
|
|
* no reference models will be used if maxdist < 2.
|
|
* (7) This function must be called, even if reference models will
|
|
* not be used. It should be called after building models on all
|
|
* available pages, and after setting the rendering parameters.
|
|
* (8) If the dewa has been serialized, this function is called by
|
|
* dewarpaRead() when it is read back. It is also called
|
|
* any time the rendering parameters are changed.
|
|
* (9) Note: if this has been called with useboth == 1, and useboth
|
|
* is reset to 0, you should first call dewarpaRestoreModels()
|
|
* to bring real models from the cache back to the primary array.
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
dewarpaInsertRefModels(L_DEWARPA *dewa,
|
|
l_int32 notests,
|
|
l_int32 debug)
|
|
{
|
|
l_int32 i, j, n, val, min, distdown, distup;
|
|
L_DEWARP *dew;
|
|
NUMA *na, *nah;
|
|
|
|
PROCNAME("dewarpaInsertRefModels");
|
|
|
|
if (!dewa)
|
|
return ERROR_INT("dewa not defined", procName, 1);
|
|
if (dewa->maxdist < 2)
|
|
L_INFO("maxdist < 2; no ref models can be used\n", procName);
|
|
|
|
/* Make an indicator numa for pages with valid models. */
|
|
dewarpaSetValidModels(dewa, notests, debug);
|
|
n = dewa->maxpage + 1;
|
|
na = numaMakeConstant(0, n);
|
|
for (i = 0; i < n; i++) {
|
|
dew = dewarpaGetDewarp(dewa, i);
|
|
if (dew && dew->vvalid)
|
|
numaReplaceNumber(na, i, 1);
|
|
}
|
|
|
|
/* Remove all existing ref models and restore models from cache */
|
|
dewarpaRestoreModels(dewa);
|
|
|
|
/* Move invalid models to the cache, and insert reference dewarps
|
|
* for pages that need to borrow a model.
|
|
* First, try to find a valid model for each page. */
|
|
for (i = 0; i < n; i++) {
|
|
numaGetIValue(na, i, &val);
|
|
if (val == 1) continue; /* already has a valid model */
|
|
if ((dew = dewa->dewarp[i]) != NULL) { /* exists but is not valid; */
|
|
dewa->dewarpcache[i] = dew; /* move it to the cache */
|
|
dewa->dewarp[i] = NULL;
|
|
}
|
|
if (dewa->maxdist < 2) continue; /* can't use a ref model */
|
|
/* Look back for nearest model */
|
|
distdown = distup = dewa->maxdist + 1;
|
|
for (j = i - 2; j >= 0 && distdown > dewa->maxdist; j -= 2) {
|
|
numaGetIValue(na, j, &val);
|
|
if (val == 1) distdown = i - j;
|
|
}
|
|
/* Look ahead for nearest model */
|
|
for (j = i + 2; j < n && distup > dewa->maxdist; j += 2) {
|
|
numaGetIValue(na, j, &val);
|
|
if (val == 1) distup = j - i;
|
|
}
|
|
min = L_MIN(distdown, distup);
|
|
if (min > dewa->maxdist) continue; /* no valid model in range */
|
|
if (distdown <= distup)
|
|
dewarpaInsertDewarp(dewa, dewarpCreateRef(i, i - distdown));
|
|
else
|
|
dewarpaInsertDewarp(dewa, dewarpCreateRef(i, i + distup));
|
|
}
|
|
numaDestroy(&na);
|
|
|
|
/* If a valid model will do, we're finished. */
|
|
if (dewa->useboth == 0) {
|
|
dewa->modelsready = 1; /* validated */
|
|
return 0;
|
|
}
|
|
|
|
/* The request is useboth == 1. Now try to find an hvalid model */
|
|
nah = numaMakeConstant(0, n);
|
|
for (i = 0; i < n; i++) {
|
|
dew = dewarpaGetDewarp(dewa, i);
|
|
if (dew && dew->hvalid)
|
|
numaReplaceNumber(nah, i, 1);
|
|
}
|
|
for (i = 0; i < n; i++) {
|
|
numaGetIValue(nah, i, &val);
|
|
if (val == 1) continue; /* already has a hvalid model */
|
|
if (dewa->maxdist < 2) continue; /* can't use a ref model */
|
|
distdown = distup = 100000;
|
|
for (j = i - 2; j >= 0; j -= 2) { /* look back for nearest model */
|
|
numaGetIValue(nah, j, &val);
|
|
if (val == 1) {
|
|
distdown = i - j;
|
|
break;
|
|
}
|
|
}
|
|
for (j = i + 2; j < n; j += 2) { /* look ahead for nearest model */
|
|
numaGetIValue(nah, j, &val);
|
|
if (val == 1) {
|
|
distup = j - i;
|
|
break;
|
|
}
|
|
}
|
|
min = L_MIN(distdown, distup);
|
|
if (min > dewa->maxdist) continue; /* no hvalid model within range */
|
|
|
|
/* We can replace the existing valid model with an hvalid model.
|
|
* If it's not a reference, save it in the cache. */
|
|
if ((dew = dewarpaGetDewarp(dewa, i)) == NULL) {
|
|
L_ERROR("dew is null for page %d!\n", procName, i);
|
|
} else {
|
|
if (dew->hasref == 0) { /* not a ref model */
|
|
dewa->dewarpcache[i] = dew; /* move it to the cache */
|
|
dewa->dewarp[i] = NULL; /* must null the ptr */
|
|
}
|
|
}
|
|
if (distdown <= distup) /* insert the hvalid ref model */
|
|
dewarpaInsertDewarp(dewa, dewarpCreateRef(i, i - distdown));
|
|
else
|
|
dewarpaInsertDewarp(dewa, dewarpCreateRef(i, i + distup));
|
|
}
|
|
numaDestroy(&nah);
|
|
|
|
dewa->modelsready = 1; /* validated */
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief dewarpaStripRefModels()
|
|
*
|
|
* \param[in] dewa populated with dewarp structs for pages
|
|
* \return 0 if OK, 1 on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) This examines each dew in a dewarpa, and removes
|
|
* all that don't have their own page model (i.e., all
|
|
* that have "references" to nearby pages with valid models).
|
|
* These references were generated by dewarpaInsertRefModels(dewa).
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
dewarpaStripRefModels(L_DEWARPA *dewa)
|
|
{
|
|
l_int32 i;
|
|
L_DEWARP *dew;
|
|
|
|
PROCNAME("dewarpaStripRefModels");
|
|
|
|
if (!dewa)
|
|
return ERROR_INT("dewa not defined", procName, 1);
|
|
|
|
for (i = 0; i <= dewa->maxpage; i++) {
|
|
if ((dew = dewarpaGetDewarp(dewa, i)) != NULL) {
|
|
if (dew->hasref)
|
|
dewarpDestroy(&dewa->dewarp[i]);
|
|
}
|
|
}
|
|
dewa->modelsready = 0;
|
|
|
|
/* Regenerate the page lists */
|
|
dewarpaListPages(dewa);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief dewarpaRestoreModels()
|
|
*
|
|
* \param[in] dewa populated with dewarp structs for pages
|
|
* \return 0 if OK, 1 on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) This puts all real models (and only real models) in the
|
|
* primary dewarpa array. First remove all dewarps that are
|
|
* only references to other page models. Then move all models
|
|
* that had been cached back into the primary dewarp array.
|
|
* (2) After this is done, we still need to recompute and insert
|
|
* the reference models before dewa->modelsready is true.
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
dewarpaRestoreModels(L_DEWARPA *dewa)
|
|
{
|
|
l_int32 i;
|
|
L_DEWARP *dew;
|
|
|
|
PROCNAME("dewarpaRestoreModels");
|
|
|
|
if (!dewa)
|
|
return ERROR_INT("dewa not defined", procName, 1);
|
|
|
|
/* Strip out ref models. Then only real models will be in the
|
|
* primary dewarp array. */
|
|
dewarpaStripRefModels(dewa);
|
|
|
|
/* The cache holds only real models, which are not necessarily valid. */
|
|
for (i = 0; i <= dewa->maxpage; i++) {
|
|
if ((dew = dewa->dewarpcache[i]) != NULL) {
|
|
if (dewa->dewarp[i]) {
|
|
L_ERROR("dew in both cache and main array!: page %d\n",
|
|
procName, i);
|
|
} else {
|
|
dewa->dewarp[i] = dew;
|
|
dewa->dewarpcache[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
dewa->modelsready = 0; /* new ref models not yet inserted */
|
|
|
|
/* Regenerate the page lists */
|
|
dewarpaListPages(dewa);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*
|
|
* Dewarp debugging output *
|
|
*----------------------------------------------------------------------*/
|
|
/*!
|
|
* \brief dewarpaInfo()
|
|
*
|
|
* \param[in] fp
|
|
* \param[in] dewa
|
|
* \return 0 if OK, 1 on error
|
|
*/
|
|
l_ok
|
|
dewarpaInfo(FILE *fp,
|
|
L_DEWARPA *dewa)
|
|
{
|
|
l_int32 i, n, pageno, nnone, nvsuccess, nvvalid, nhsuccess, nhvalid, nref;
|
|
L_DEWARP *dew;
|
|
|
|
PROCNAME("dewarpaInfo");
|
|
|
|
if (!fp)
|
|
return ERROR_INT("dewa not defined", procName, 1);
|
|
if (!dewa)
|
|
return ERROR_INT("dewa not defined", procName, 1);
|
|
|
|
fprintf(fp, "\nDewarpaInfo: %p\n", dewa);
|
|
fprintf(fp, "nalloc = %d, maxpage = %d\n", dewa->nalloc, dewa->maxpage);
|
|
fprintf(fp, "sampling = %d, redfactor = %d, minlines = %d\n",
|
|
dewa->sampling, dewa->redfactor, dewa->minlines);
|
|
fprintf(fp, "maxdist = %d, useboth = %d\n",
|
|
dewa->maxdist, dewa->useboth);
|
|
|
|
dewarpaModelStats(dewa, &nnone, &nvsuccess, &nvvalid,
|
|
&nhsuccess, &nhvalid, &nref);
|
|
n = numaGetCount(dewa->napages);
|
|
fprintf(stderr, "Total number of pages with a dew = %d\n", n);
|
|
fprintf(stderr, "Number of pages without any models = %d\n", nnone);
|
|
fprintf(stderr, "Number of pages with a vert model = %d\n", nvsuccess);
|
|
fprintf(stderr, "Number of pages with a valid vert model = %d\n", nvvalid);
|
|
fprintf(stderr, "Number of pages with both models = %d\n", nhsuccess);
|
|
fprintf(stderr, "Number of pages with both models valid = %d\n", nhvalid);
|
|
fprintf(stderr, "Number of pages with a ref model = %d\n", nref);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
numaGetIValue(dewa->napages, i, &pageno);
|
|
if ((dew = dewarpaGetDewarp(dewa, pageno)) == NULL)
|
|
continue;
|
|
fprintf(stderr, "Page: %d\n", dew->pageno);
|
|
fprintf(stderr, " hasref = %d, refpage = %d\n",
|
|
dew->hasref, dew->refpage);
|
|
fprintf(stderr, " nlines = %d\n", dew->nlines);
|
|
fprintf(stderr, " w = %d, h = %d, nx = %d, ny = %d\n",
|
|
dew->w, dew->h, dew->nx, dew->ny);
|
|
if (dew->sampvdispar)
|
|
fprintf(stderr, " Vertical disparity builds:\n"
|
|
" (min,max,abs-diff) line curvature = (%d,%d,%d)\n",
|
|
dew->mincurv, dew->maxcurv, dew->maxcurv - dew->mincurv);
|
|
if (dew->samphdispar)
|
|
fprintf(stderr, " Horizontal disparity builds:\n"
|
|
" left edge slope = %d, right edge slope = %d\n"
|
|
" (left,right,abs-diff) edge curvature = (%d,%d,%d)\n",
|
|
dew->leftslope, dew->rightslope, dew->leftcurv,
|
|
dew->rightcurv, L_ABS(dew->leftcurv - dew->rightcurv));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief dewarpaModelStats()
|
|
*
|
|
* \param[in] dewa
|
|
* \param[out] pnnone [optional] number without any model
|
|
* \param[out] pnvsuccess [optional] number with a vert model
|
|
* \param[out] pnvvalid [optional] number with a valid vert model
|
|
* \param[out] pnhsuccess [optional] number with both models
|
|
* \param[out] pnhvalid [optional] number with both models valid
|
|
* \param[out] pnref [optional] number with a reference model
|
|
* \return 0 if OK, 1 on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) A page without a model has no dew. It most likely failed to
|
|
* generate a vertical model, and has not been assigned a ref
|
|
* model from a neighboring page with a valid vertical model.
|
|
* (2) A page has vsuccess == 1 if there is at least a model of the
|
|
* vertical disparity. The model may be invalid, in which case
|
|
* dewarpaInsertRefModels() will stash it in the cache and
|
|
* attempt to replace it by a valid ref model.
|
|
* (3) A vvvalid model is a vertical disparity model whose parameters
|
|
* satisfy the constraints given in dewarpaSetValidModels().
|
|
* (4) A page has hsuccess == 1 if both the vertical and horizontal
|
|
* disparity arrays have been constructed.
|
|
* (5) An hvalid model has vertical and horizontal disparity
|
|
* models whose parameters satisfy the constraints given
|
|
* in dewarpaSetValidModels().
|
|
* (6) A page has a ref model if it failed to generate a valid
|
|
* model but was assigned a vvalid or hvalid model on another
|
|
* page (within maxdist) by dewarpaInsertRefModel().
|
|
* (7) This calls dewarpaTestForValidModel(); it ignores the vvalid
|
|
* and hvalid fields.
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
dewarpaModelStats(L_DEWARPA *dewa,
|
|
l_int32 *pnnone,
|
|
l_int32 *pnvsuccess,
|
|
l_int32 *pnvvalid,
|
|
l_int32 *pnhsuccess,
|
|
l_int32 *pnhvalid,
|
|
l_int32 *pnref)
|
|
{
|
|
l_int32 i, n, pageno, nnone, nvsuccess, nvvalid, nhsuccess, nhvalid, nref;
|
|
L_DEWARP *dew;
|
|
|
|
PROCNAME("dewarpaModelStats");
|
|
|
|
if (!dewa)
|
|
return ERROR_INT("dewa not defined", procName, 1);
|
|
|
|
dewarpaListPages(dewa);
|
|
n = numaGetCount(dewa->napages);
|
|
nnone = nref = nvsuccess = nvvalid = nhsuccess = nhvalid = 0;
|
|
for (i = 0; i < n; i++) {
|
|
numaGetIValue(dewa->napages, i, &pageno);
|
|
dew = dewarpaGetDewarp(dewa, pageno);
|
|
if (!dew) {
|
|
nnone++;
|
|
continue;
|
|
}
|
|
if (dew->hasref == 1)
|
|
nref++;
|
|
if (dew->vsuccess == 1)
|
|
nvsuccess++;
|
|
if (dew->hsuccess == 1)
|
|
nhsuccess++;
|
|
dewarpaTestForValidModel(dewa, dew, 0);
|
|
if (dew->vvalid == 1)
|
|
nvvalid++;
|
|
if (dew->hvalid == 1)
|
|
nhvalid++;
|
|
}
|
|
|
|
if (pnnone) *pnnone = nnone;
|
|
if (pnref) *pnref = nref;
|
|
if (pnvsuccess) *pnvsuccess = nvsuccess;
|
|
if (pnvvalid) *pnvvalid = nvvalid;
|
|
if (pnhsuccess) *pnhsuccess = nhsuccess;
|
|
if (pnhvalid) *pnhvalid = nhvalid;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief dewarpaTestForValidModel()
|
|
*
|
|
* \param[in] dewa
|
|
* \param[in] dew
|
|
* \param[in] notests
|
|
* \return 0 if OK, 1 on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) Computes validity of vertical (vvalid) model and both
|
|
* vertical and horizontal (hvalid) models.
|
|
* (2) If %notests == 1, this ignores the curvature constraints
|
|
* and assumes that all successfully built models are valid.
|
|
* (3) This is just about the models, not the rendering process,
|
|
* so the value of useboth is not considered here.
|
|
* </pre>
|
|
*/
|
|
static l_int32
|
|
dewarpaTestForValidModel(L_DEWARPA *dewa,
|
|
L_DEWARP *dew,
|
|
l_int32 notests)
|
|
{
|
|
l_int32 maxcurv, diffcurv, diffedge;
|
|
|
|
PROCNAME("dewarpaTestForValidModel");
|
|
|
|
if (!dewa || !dew)
|
|
return ERROR_INT("dewa and dew not both defined", procName, 1);
|
|
|
|
if (notests) {
|
|
dew->vvalid = dew->vsuccess;
|
|
dew->hvalid = dew->hsuccess;
|
|
return 0;
|
|
}
|
|
|
|
/* No actual model was built */
|
|
if (dew->vsuccess == 0) return 0;
|
|
|
|
/* Was previously found not to have a valid model */
|
|
if (dew->hasref == 1) return 0;
|
|
|
|
/* vsuccess == 1; a vertical (line) model exists.
|
|
* First test that the vertical curvatures are within allowed
|
|
* bounds. Note that all curvatures are signed.*/
|
|
maxcurv = L_MAX(L_ABS(dew->mincurv), L_ABS(dew->maxcurv));
|
|
diffcurv = dew->maxcurv - dew->mincurv;
|
|
if (maxcurv <= dewa->max_linecurv &&
|
|
diffcurv >= dewa->min_diff_linecurv &&
|
|
diffcurv <= dewa->max_diff_linecurv) {
|
|
dew->vvalid = 1;
|
|
} else {
|
|
L_INFO("invalid vert model for page %d:\n", procName, dew->pageno);
|
|
#if DEBUG_INVALID_MODELS
|
|
fprintf(stderr, " max line curv = %d, max allowed = %d\n",
|
|
maxcurv, dewa->max_linecurv);
|
|
fprintf(stderr, " diff line curv = %d, max allowed = %d\n",
|
|
diffcurv, dewa->max_diff_linecurv);
|
|
#endif /* DEBUG_INVALID_MODELS */
|
|
}
|
|
|
|
/* If a horizontal (edge) model exists, test for validity. */
|
|
if (dew->hsuccess) {
|
|
diffedge = L_ABS(dew->leftcurv - dew->rightcurv);
|
|
if (L_ABS(dew->leftslope) <= dewa->max_edgeslope &&
|
|
L_ABS(dew->rightslope) <= dewa->max_edgeslope &&
|
|
L_ABS(dew->leftcurv) <= dewa->max_edgecurv &&
|
|
L_ABS(dew->rightcurv) <= dewa->max_edgecurv &&
|
|
diffedge <= dewa->max_diff_edgecurv) {
|
|
dew->hvalid = 1;
|
|
} else {
|
|
L_INFO("invalid horiz model for page %d:\n", procName, dew->pageno);
|
|
#if DEBUG_INVALID_MODELS
|
|
fprintf(stderr, " left edge slope = %d, max allowed = %d\n",
|
|
dew->leftslope, dewa->max_edgeslope);
|
|
fprintf(stderr, " right edge slope = %d, max allowed = %d\n",
|
|
dew->rightslope, dewa->max_edgeslope);
|
|
fprintf(stderr, " left edge curv = %d, max allowed = %d\n",
|
|
dew->leftcurv, dewa->max_edgecurv);
|
|
fprintf(stderr, " right edge curv = %d, max allowed = %d\n",
|
|
dew->rightcurv, dewa->max_edgecurv);
|
|
fprintf(stderr, " diff edge curv = %d, max allowed = %d\n",
|
|
diffedge, dewa->max_diff_edgecurv);
|
|
#endif /* DEBUG_INVALID_MODELS */
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief dewarpaShowArrays()
|
|
*
|
|
* \param[in] dewa
|
|
* \param[in] scalefact on contour images; typ. 0.5
|
|
* \param[in] first first page model to render
|
|
* \param[in] last last page model to render; use 0 to go to end
|
|
* \return 0 if OK, 1 on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) Generates a pdf of contour plots of the disparity arrays.
|
|
* (2) This only shows actual models; not ref models
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
dewarpaShowArrays(L_DEWARPA *dewa,
|
|
l_float32 scalefact,
|
|
l_int32 first,
|
|
l_int32 last)
|
|
{
|
|
char buf[256];
|
|
l_int32 i, svd, shd;
|
|
L_BMF *bmf;
|
|
L_DEWARP *dew;
|
|
PIX *pixv, *pixvs, *pixh, *pixhs, *pixt, *pixd;
|
|
PIXA *pixa;
|
|
|
|
PROCNAME("dewarpaShowArrays");
|
|
|
|
if (!dewa)
|
|
return ERROR_INT("dew not defined", procName, 1);
|
|
if (first < 0 || first > dewa->maxpage)
|
|
return ERROR_INT("first out of bounds", procName, 1);
|
|
if (last <= 0 || last > dewa->maxpage) last = dewa->maxpage;
|
|
if (last < first)
|
|
return ERROR_INT("last < first", procName, 1);
|
|
|
|
lept_rmdir("lept/dewarp1"); /* temp directory for contour plots */
|
|
lept_mkdir("lept/dewarp1");
|
|
if ((bmf = bmfCreate(NULL, 8)) == NULL)
|
|
L_ERROR("bmf not made; page info not displayed", procName);
|
|
|
|
fprintf(stderr, "Generating contour plots\n");
|
|
for (i = first; i <= last; i++) {
|
|
if (i && ((i % 10) == 0))
|
|
fprintf(stderr, " .. %d", i);
|
|
dew = dewarpaGetDewarp(dewa, i);
|
|
if (!dew) continue;
|
|
if (dew->hasref == 1) continue;
|
|
svd = shd = 0;
|
|
if (dew->sampvdispar) svd = 1;
|
|
if (dew->samphdispar) shd = 1;
|
|
if (!svd) {
|
|
L_ERROR("sampvdispar not made for page %d!\n", procName, i);
|
|
continue;
|
|
}
|
|
|
|
/* Generate contour plots at reduced resolution */
|
|
dewarpPopulateFullRes(dew, NULL, 0, 0);
|
|
pixv = fpixRenderContours(dew->fullvdispar, 3.0, 0.15);
|
|
pixvs = pixScaleBySampling(pixv, scalefact, scalefact);
|
|
pixDestroy(&pixv);
|
|
if (shd) {
|
|
pixh = fpixRenderContours(dew->fullhdispar, 3.0, 0.15);
|
|
pixhs = pixScaleBySampling(pixh, scalefact, scalefact);
|
|
pixDestroy(&pixh);
|
|
}
|
|
dewarpMinimize(dew);
|
|
|
|
/* Save side-by-side */
|
|
pixa = pixaCreate(2);
|
|
pixaAddPix(pixa, pixvs, L_INSERT);
|
|
if (shd)
|
|
pixaAddPix(pixa, pixhs, L_INSERT);
|
|
pixt = pixaDisplayTiledInRows(pixa, 32, 1500, 1.0, 0, 30, 2);
|
|
snprintf(buf, sizeof(buf), "Page %d", i);
|
|
pixd = pixAddSingleTextblock(pixt, bmf, buf, 0x0000ff00,
|
|
L_ADD_BELOW, NULL);
|
|
snprintf(buf, sizeof(buf), "/tmp/lept/dewarp1/arrays_%04d.png", i);
|
|
pixWriteDebug(buf, pixd, IFF_PNG);
|
|
pixaDestroy(&pixa);
|
|
pixDestroy(&pixt);
|
|
pixDestroy(&pixd);
|
|
}
|
|
bmfDestroy(&bmf);
|
|
fprintf(stderr, "\n");
|
|
|
|
fprintf(stderr, "Generating pdf of contour plots\n");
|
|
convertFilesToPdf("/tmp/lept/dewarp1", "arrays_", 90, 1.0, L_FLATE_ENCODE,
|
|
0, "Disparity arrays", "/tmp/lept/disparity_arrays.pdf");
|
|
fprintf(stderr, "Output written to: /tmp/lept/disparity_arrays.pdf\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief dewarpDebug()
|
|
*
|
|
* \param[in] dew
|
|
* \param[in] subdirs one or more subdirectories of /tmp; e.g., "dew1"
|
|
* \param[in] index to help label output images; e.g., the page number
|
|
* \return 0 if OK, 1 on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) Prints dewarp fields and generates disparity array contour images.
|
|
* The contour images are written to file:
|
|
* /tmp/[subdirs]/pixv_[index].png
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
dewarpDebug(L_DEWARP *dew,
|
|
const char *subdirs,
|
|
l_int32 index)
|
|
{
|
|
char fname[256];
|
|
char *outdir;
|
|
l_int32 svd, shd;
|
|
PIX *pixv, *pixh;
|
|
|
|
PROCNAME("dewarpDebug");
|
|
|
|
if (!dew)
|
|
return ERROR_INT("dew not defined", procName, 1);
|
|
if (!subdirs)
|
|
return ERROR_INT("subdirs not defined", procName, 1);
|
|
|
|
fprintf(stderr, "pageno = %d, hasref = %d, refpage = %d\n",
|
|
dew->pageno, dew->hasref, dew->refpage);
|
|
fprintf(stderr, "sampling = %d, redfactor = %d, minlines = %d\n",
|
|
dew->sampling, dew->redfactor, dew->minlines);
|
|
svd = shd = 0;
|
|
if (!dew->hasref) {
|
|
if (dew->sampvdispar) svd = 1;
|
|
if (dew->samphdispar) shd = 1;
|
|
fprintf(stderr, "sampv = %d, samph = %d\n", svd, shd);
|
|
fprintf(stderr, "w = %d, h = %d\n", dew->w, dew->h);
|
|
fprintf(stderr, "nx = %d, ny = %d\n", dew->nx, dew->ny);
|
|
fprintf(stderr, "nlines = %d\n", dew->nlines);
|
|
if (svd) {
|
|
fprintf(stderr, "(min,max,abs-diff) line curvature = (%d,%d,%d)\n",
|
|
dew->mincurv, dew->maxcurv, dew->maxcurv - dew->mincurv);
|
|
}
|
|
if (shd) {
|
|
fprintf(stderr, "(left edge slope = %d, right edge slope = %d\n",
|
|
dew->leftslope, dew->rightslope);
|
|
fprintf(stderr, "(left,right,abs-diff) edge curvature = "
|
|
"(%d,%d,%d)\n", dew->leftcurv, dew->rightcurv,
|
|
L_ABS(dew->leftcurv - dew->rightcurv));
|
|
}
|
|
}
|
|
if (!svd && !shd) {
|
|
fprintf(stderr, "No disparity arrays\n");
|
|
return 0;
|
|
}
|
|
|
|
dewarpPopulateFullRes(dew, NULL, 0, 0);
|
|
lept_mkdir(subdirs);
|
|
outdir = pathJoin("/tmp", subdirs);
|
|
if (svd) {
|
|
pixv = fpixRenderContours(dew->fullvdispar, 3.0, 0.15);
|
|
snprintf(fname, sizeof(fname), "%s/pixv_%d.png", outdir, index);
|
|
pixWriteDebug(fname, pixv, IFF_PNG);
|
|
pixDestroy(&pixv);
|
|
}
|
|
if (shd) {
|
|
pixh = fpixRenderContours(dew->fullhdispar, 3.0, 0.15);
|
|
snprintf(fname, sizeof(fname), "%s/pixh_%d.png", outdir, index);
|
|
pixWriteDebug(fname, pixh, IFF_PNG);
|
|
pixDestroy(&pixh);
|
|
}
|
|
LEPT_FREE(outdir);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief dewarpShowResults()
|
|
*
|
|
* \param[in] dewa
|
|
* \param[in] sa of indexed input images
|
|
* \param[in] boxa crop boxes for input images; can be null
|
|
* \param[in] firstpage
|
|
* \param[in] lastpage
|
|
* \param[in] pdfout filename
|
|
* \return 0 if OK, 1 on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) This generates a pdf of image pairs (before, after) for
|
|
* the designated set of input pages.
|
|
* (2) If the boxa exists, its elements are aligned with numbers
|
|
* in the filenames in %sa. It is used to crop the input images.
|
|
* It is assumed that the dewa was generated from the cropped
|
|
* images. No undercropping is applied before rendering.
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
dewarpShowResults(L_DEWARPA *dewa,
|
|
SARRAY *sa,
|
|
BOXA *boxa,
|
|
l_int32 firstpage,
|
|
l_int32 lastpage,
|
|
const char *pdfout)
|
|
{
|
|
char bufstr[256];
|
|
l_int32 i, modelpage;
|
|
L_BMF *bmf;
|
|
BOX *box;
|
|
L_DEWARP *dew;
|
|
PIX *pixs, *pixc, *pixd, *pixt1, *pixt2;
|
|
PIXA *pixa;
|
|
|
|
PROCNAME("dewarpShowResults");
|
|
|
|
if (!dewa)
|
|
return ERROR_INT("dewa not defined", procName, 1);
|
|
if (!sa)
|
|
return ERROR_INT("sa not defined", procName, 1);
|
|
if (!pdfout)
|
|
return ERROR_INT("pdfout not defined", procName, 1);
|
|
if (firstpage > lastpage)
|
|
return ERROR_INT("invalid first/last page numbers", procName, 1);
|
|
|
|
lept_rmdir("lept/dewarp_pdfout");
|
|
lept_mkdir("lept/dewarp_pdfout");
|
|
bmf = bmfCreate(NULL, 6);
|
|
|
|
fprintf(stderr, "Dewarping and generating s/by/s view\n");
|
|
for (i = firstpage; i <= lastpage; i++) {
|
|
if (i && (i % 10 == 0)) fprintf(stderr, ".. %d ", i);
|
|
pixs = pixReadIndexed(sa, i);
|
|
if (boxa) {
|
|
box = boxaGetBox(boxa, i, L_CLONE);
|
|
pixc = pixClipRectangle(pixs, box, NULL);
|
|
boxDestroy(&box);
|
|
}
|
|
else
|
|
pixc = pixClone(pixs);
|
|
dew = dewarpaGetDewarp(dewa, i);
|
|
pixd = NULL;
|
|
if (dew) {
|
|
dewarpaApplyDisparity(dewa, dew->pageno, pixc,
|
|
GrayInValue, 0, 0, &pixd, NULL);
|
|
dewarpMinimize(dew);
|
|
}
|
|
pixa = pixaCreate(2);
|
|
pixaAddPix(pixa, pixc, L_INSERT);
|
|
if (pixd)
|
|
pixaAddPix(pixa, pixd, L_INSERT);
|
|
pixt1 = pixaDisplayTiledAndScaled(pixa, 32, 500, 2, 0, 35, 2);
|
|
if (dew) {
|
|
modelpage = (dew->hasref) ? dew->refpage : dew->pageno;
|
|
snprintf(bufstr, sizeof(bufstr), "Page %d; using %d\n",
|
|
i, modelpage);
|
|
}
|
|
else
|
|
snprintf(bufstr, sizeof(bufstr), "Page %d; no dewarp\n", i);
|
|
pixt2 = pixAddSingleTextblock(pixt1, bmf, bufstr, 0x0000ff00,
|
|
L_ADD_BELOW, 0);
|
|
snprintf(bufstr, sizeof(bufstr), "/tmp/lept/dewarp_pdfout/%05d", i);
|
|
pixWriteDebug(bufstr, pixt2, IFF_JFIF_JPEG);
|
|
pixaDestroy(&pixa);
|
|
pixDestroy(&pixs);
|
|
pixDestroy(&pixt1);
|
|
pixDestroy(&pixt2);
|
|
}
|
|
fprintf(stderr, "\n");
|
|
|
|
fprintf(stderr, "Generating pdf of result\n");
|
|
convertFilesToPdf("/tmp/lept/dewarp_pdfout", NULL, 100, 1.0, L_JPEG_ENCODE,
|
|
0, "Dewarp sequence", pdfout);
|
|
fprintf(stderr, "Output written to: %s\n", pdfout);
|
|
bmfDestroy(&bmf);
|
|
return 0;
|
|
}
|