/*! * dewarpaApplyInit() * * Input: dewa * pageno (of page model to be used; may be a ref model) * pixs (image to be modified; can be 1, 8 or 32 bpp) * x, y (origin for generation of disparity arrays) * &pdew (<return> dewarp to be used for this page * debugfile (use null to skip writing this) * Return: 0 if OK, 1 on error (no models or ref models available) * * Notes: * (1) This prepares pixs for being dewarped. It returns 1 if * no dewarping model exists. * (2) The returned @dew contains the model to be used for this page * image. The @dew is owned by dewa; do not destroy. * (3) See dewarpApplyDisparity() for other details on inputs. */ static l_int32 dewarpaApplyInit(L_DEWARPA *dewa, l_int32 pageno, PIX *pixs, l_int32 x, l_int32 y, L_DEWARP **pdew, const char *debugfile) { l_int32 debug; L_DEWARP *dew1, *dew2; PROCNAME("dewarpaApplyInit"); if (!pdew) return ERROR_INT("&dew not defined", procName, 1); *pdew = NULL; if (!dewa) return ERROR_INT("dewa not defined", procName, 1); if (pageno < 0 || pageno > dewa->maxpage) return ERROR_INT("invalid pageno", procName, 1); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (x < 0) x = 0; if (y < 0) y = 0; debug = (debugfile) ? 1 : 0; /* Make sure all models are valid and all refmodels have * been added to dewa */ if (dewa->modelsready == FALSE) dewarpaInsertRefModels(dewa, 0, debug); /* Check for the existence of a valid model; we don't expect * all pages to have them. */ if ((dew1 = dewarpaGetDewarp(dewa, pageno)) == NULL) { L_INFO("no valid dew model for page %d\n", procName, pageno); return 1; } /* Get the page model that we will use and sanity-check that * it is valid. The ultimate result will be put in dew1->pixd. */ if (dew1->hasref) /* point to another page with a model */ dew2 = dewarpaGetDewarp(dewa, dew1->refpage); else dew2 = dew1; if (dew2->vvalid == FALSE) return ERROR_INT("no model; shouldn't happen", procName, 1); *pdew = dew2; /* Generate the full res disparity arrays if they don't exist * (e.g., if they've been minimized or read from file), or if * they are too small for the current image. */ dewarpPopulateFullRes(dew2, pixs, x, y); return 0; }
/*! * dewarpaListPages() * * Input: dewa (populated with dewarp structs for pages) * Return: 0 if OK, 1 on error (list of page numbers), or null on error * * Notes: * (1) This generates two numas, stored in the dewarpa, that give: * (a) the page number for each dew that has a page model. * (b) the page number for each dew that has either a page * model or a reference model. * It can be called at any time. * (2) It is called by the dewarpa serializer before writing. */ l_int32 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; }
/*! * dewarpMinimize() * * Input: dew * Return: 0 if OK, 1 on error * * Notes: * (1) This removes all data that is not needed for serialization. * It keeps the subsampled disparity array(s), so the full * resolution arrays can be reconstructed. */ l_int32 dewarpMinimize(L_DEWARP *dew) { L_DEWARP *dewt; PROCNAME("dewarpMinimize"); if (!dew) return ERROR_INT("dew not defined", procName, 1); /* If dew is a ref, minimize the actual dewarp */ if (dew->hasref) dewt = dewarpaGetDewarp(dew->dewa, dew->refpage); else dewt = dew; if (!dewt) return ERROR_INT("dewt not found", procName, 1); pixDestroy(&dewt->pixs); fpixDestroy(&dewt->fullvdispar); fpixDestroy(&dewt->fullhdispar); numaDestroy(&dewt->namidys); numaDestroy(&dewt->nacurves); return 0; }
/*! * dewarpaInfo() * * Input: fp * dewa * Return: 0 if OK, 1 on error */ l_int32 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; }
/*! * dewarpaModelStats() * * Input: dewa * &nnone (<optional return> number without any model) * &nvsuccess (<optional return> number with a vert model) * &nvvalid (<optional return> number with a valid vert model) * &nhsuccess (<optional return> number with both models) * &nhvalid (<optional return> number with both models valid) * &nref (<optional return> number with a reference model) * Return: 0 if OK, 1 on error * * Notes: * (1) A page without a model has no dew. It most likely failed to * generate a vertical model, and has not been assigned a ref * model from a neighboring page with a valid vertical model. * (2) A page has vsuccess == 1 if there is at least a model of the * vertical disparity. The model may be invalid, in which case * dewarpaInsertRefModels() will stash it in the cache and * attempt to replace it by a valid ref model. * (3) A vvvalid model is a vertical disparity model whose parameters * satisfy the constraints given in dewarpaSetValidModels(). * (4) A page has hsuccess == 1 if both the vertical and horizontal * disparity arrays have been constructed. * (5) An hvalid model has vertical and horizontal disparity * models whose parameters satisfy the constraints given * in dewarpaSetValidModels(). * (6) A page has a ref model if it failed to generate a valid * model but was assigned a vvalid or hvalid model on another * page (within maxdist) by dewarpaInsertRefModel(). * (7) This calls dewarpaTestForValidModel(); it ignores the vvalid * and hvalid fields. */ l_int32 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; }
/*! * dewarpaStripRefModels() * * Input: dewa (populated with dewarp structs for pages) * Return: 0 if OK, 1 on error * * Notes: * (1) This examines each dew in a dewarpa, and removes * all that don't have their own page model (i.e., all * that have "references" to nearby pages with valid models). * These references were generated by dewarpaInsertRefModels(dewa). */ l_int32 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; }
/*! * dewarpaShowArrays() * * Input: dewa * scalefact (on contour images; typ. 0.5) * first (first page model to render) * last (last page model to render; use 0 to go to end) * fontdir (for text bitmap fonts) * Return: 0 if OK, 1 on error * * Notes: * (1) Generates a pdf of contour plots of the disparity arrays. * (2) This only shows actual models; not ref models */ l_int32 dewarpaShowArrays(L_DEWARPA *dewa, l_float32 scalefact, l_int32 first, l_int32 last, const char *fontdir) { char buf[256]; char *pathname; 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"); lept_mkdir("lept"); if ((bmf = bmfCreate(fontdir, 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), "arrays_%04d.png", i); pathname = genPathname("/tmp/lept", buf); pixWrite(pathname, pixd, IFF_PNG); pixaDestroy(&pixa); pixDestroy(&pixt); pixDestroy(&pixd); FREE(pathname); } bmfDestroy(&bmf); fprintf(stderr, "\n"); fprintf(stderr, "Generating pdf of contour plots\n"); convertFilesToPdf("/tmp/lept", "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; }
/*! * dewarpaInsertRefModels() * * Input: dewa * notests (if 1, ignore curvature constraints on model) * debug (1 to output information on invalid page models) * Return: 0 if OK, 1 on error * * Notes: * (1) This destroys all dewarp models that are invalid, and then * inserts reference models where possible. * (2) If @notests == 1, this ignores the curvature constraints * and assumes that all successfully built models are valid. * (3) If useboth == 0, it uses the closest valid model within the * distance and parity constraints. If useboth == 1, it tries * to use the closest allowed hvalid model; if it doesn't find * an hvalid model, it uses the closest valid model. * (4) For all pages without a model, this clears out any existing * invalid and reference dewarps, finds the nearest valid model * with the same parity, and inserts an empty dewarp with the * reference page. * (5) Then if it is requested to use both vertical and horizontal * disparity arrays (useboth == 1), it tries to replace any * hvalid == 0 model or reference with an hvalid == 1 reference. * (6) The distance constraint is that any reference model must * be within maxdist. Note that with the parity constraint, * no reference models will be used if maxdist < 2. * (7) This function must be called, even if reference models will * not be used. It should be called after building models on all * available pages, and after setting the rendering parameters. * (8) If the dewa has been serialized, this function is called by * dewarpaRead() when it is read back. It is also called * any time the rendering parameters are changed. * (9) Note: if this has been called with useboth == 1, and useboth * is reset to 0, you should first call dewarpRestoreModels() * to bring real models from the cache back to the primary array. */ l_int32 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; }
/*! * dewarpaSetValidModels() * * Input: dewa * notests * debug (1 to output information on invalid page models) * Return: 0 if OK, 1 on error * * Notes: * (1) A valid model must meet the rendering requirements, which * include whether or not a vertical disparity model exists * and conditions on curvatures for vertical and horizontal * disparity models. * (2) If @notests == 1, this ignores the curvature constraints * and assumes that all successfully built models are valid. * (3) This function does not need to be called by the application. * It is called by dewarpaInsertRefModels(), which * will destroy all invalid dewarps. Consequently, to inspect * an invalid dewarp model, it must be done before calling * dewarpaInsertRefModels(). */ l_int32 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; }
/*! * dewarpShowResults() * * Input: dewa * sarray (of indexed input images) * boxa (crop boxes for input images; can be null) * firstpage, lastpage * fontdir (for text bitmap fonts) * pdfout (filename) * Return: 0 if OK, 1 on error * * Notes: * (1) This generates a pdf of image pairs (before, after) for * the designated set of input pages. * (2) If the boxa exists, its elements are aligned with numbers * in the filenames in @sa. It is used to crop the input images. * It is assumed that the dewa was generated from the cropped * images. No undercropping is applied before rendering. */ l_int32 dewarpShowResults(L_DEWARPA *dewa, SARRAY *sa, BOXA *boxa, l_int32 firstpage, l_int32 lastpage, const char *fontdir, const char *pdfout) { char bufstr[256]; char *outpath; 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("dewarp_pdfout"); lept_mkdir("dewarp_pdfout"); if ((bmf = bmfCreate(fontdir, 6)) == NULL) L_ERROR("bmf not made; page info not displayed", procName); 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, GRAYIN_VALUE, 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/dewarp_pdfout/%05d", i); pixWrite(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/dewarp_pdfout", NULL, 100, 1.0, L_JPEG_ENCODE, 0, "Dewarp sequence", pdfout); outpath = genPathname(pdfout, NULL); fprintf(stderr, "Output written to: %s\n", outpath); FREE(outpath); bmfDestroy(&bmf); return 0; }
/*! * dewarpaApplyDisparityBoxa() * * Input: dewa * pageno (of page model to be used; may be a ref model) * pixs (initial pix reference; for alignment and debugging) * boxas (boxa to be mapped) * mapdir (1 if mapping forward from original to dewarped; * 0 if backward) * x, y (origin for generation of disparity arrays with * respect to the source region) * &boxad (<return> disparity corrected boxa) * debugfile (use null to skip writing this) * Return: 0 if OK, 1 on error (no models or ref models available) * * Notes: * (1) This applies the disparity arrays in one of two mapping directions * to the specified boxa. It can be used in the backward direction * to locate a box in the original coordinates that would have * been dewarped to to the specified image. * (2) If there is no model for @pageno, this will use the model for * 'refpage' and put the result in the dew for @pageno. * (3) This works with both stripped and full resolution page models. * If the full res disparity array(s) are missing, they are remade. * (4) If an error occurs, a copy of the input boxa is returned. */ l_int32 dewarpaApplyDisparityBoxa(L_DEWARPA *dewa, l_int32 pageno, PIX *pixs, BOXA *boxas, l_int32 mapdir, l_int32 x, l_int32 y, BOXA **pboxad, const char *debugfile) { l_int32 debug_out; L_DEWARP *dew1, *dew; BOXA *boxav, *boxah; PIX *pixv, *pixh; PROCNAME("dewarpaApplyDisparityBoxa"); /* Initialize the output with the input, so we'll have that * in case we can't apply the page model. */ if (!pboxad) return ERROR_INT("&boxad not defined", procName, 1); *pboxad = boxaCopy(boxas, L_CLONE); /* Find the appropriate dew to use and fully populate its array(s) */ if (dewarpaApplyInit(dewa, pageno, pixs, x, y, &dew, debugfile)) return ERROR_INT("no model available", procName, 1); /* Correct for vertical disparity and save the result */ if ((boxav = boxaApplyDisparity(dew, boxas, L_VERT, mapdir)) == NULL) { dewarpMinimize(dew); return ERROR_INT("boxa1 not made", procName, 1); } boxaDestroy(pboxad); *pboxad = boxav; pixv = NULL; pixh = NULL; if (debugfile && mapdir != 1) L_INFO("Reverse map direction; no debug output\n", procName); debug_out = debugfile && (mapdir == 1); if (debug_out) { PIX *pix1; lept_rmdir("lept/dewboxa"); /* remove previous images */ lept_mkdir("lept/dewboxa"); pix1 = pixConvertTo32(pixs); pixRenderBoxaArb(pix1, boxas, 2, 255, 0, 0); pixWrite("/tmp/lept/dewboxa/01.png", pix1, IFF_PNG); pixDestroy(&pix1); pixv = pixApplyVertDisparity(dew, pixs, 255); pix1 = pixConvertTo32(pixv); pixRenderBoxaArb(pix1, boxav, 2, 0, 255, 0); pixWrite("/tmp/lept/dewboxa/02.png", pix1, IFF_PNG); pixDestroy(&pix1); } /* Optionally, correct for horizontal disparity */ if (dewa->useboth && dew->hsuccess) { if (dew->hvalid == FALSE) { L_INFO("invalid horiz model for page %d\n", procName, pageno); } else { boxah = boxaApplyDisparity(dew, boxav, L_HORIZ, mapdir); if (!boxah) { L_ERROR("horiz disparity fails on page %d\n", procName, pageno); } else { boxaDestroy(pboxad); *pboxad = boxah; if (debug_out) { PIX *pix1; pixh = pixApplyHorizDisparity(dew, pixv, 255); pix1 = pixConvertTo32(pixh); pixRenderBoxaArb(pix1, boxah, 2, 0, 0, 255); pixWrite("/tmp/lept/dewboxa/03.png", pix1, IFF_PNG); pixDestroy(&pixh); pixDestroy(&pix1); } } } } if (debug_out) { pixDestroy(&pixv); dew1 = dewarpaGetDewarp(dewa, pageno); dewarpDebug(dew1, "lept/dewapply", 0); convertFilesToPdf("/tmp/lept/dewboxa", NULL, 135, 1.0, 0, 0, "Dewarp Apply Disparity Boxa", debugfile); fprintf(stderr, "Dewarp Apply Disparity Boxa pdf file: %s\n", debugfile); } /* Get rid of the large full res disparity arrays */ dewarpMinimize(dew); return 0; }
/*! * dewarpaApplyDisparity() * * Input: dewa * pageno (of page model to be used; may be a ref model) * pixs (image to be modified; can be 1, 8 or 32 bpp) * grayin (gray value, from 0 to 255, for pixels brought in; * use -1 to use pixels on the boundary of pixs) * x, y (origin for generation of disparity arrays) * &pixd (<return> disparity corrected image) * debugfile (use null to skip writing this) * Return: 0 if OK, 1 on error (no models or ref models available) * * Notes: * (1) This applies the disparity arrays to the specified image. * (2) Specify gray color for pixels brought in from the outside: * 0 is black, 255 is white. Use -1 to select pixels from the * boundary of the source image. * (3) If the models and ref models have not been validated, this * will do so by calling dewarpaInsertRefModels(). * (4) This works with both stripped and full resolution page models. * If the full res disparity array(s) are missing, they are remade. * (5) The caller must handle errors that are returned because there * are no valid models or ref models for the page -- typically * by using the input pixs. * (6) If there is no model for @pageno, this will use the model for * 'refpage' and put the result in the dew for @pageno. * (7) This populates the full resolution disparity arrays if * necessary. If x and/or y are positive, they are used, * in conjunction with pixs, to determine the required * slope-based extension of the full resolution disparity * arrays in each direction. When (x,y) == (0,0), all * extension is to the right and down. Nonzero values of (x,y) * are useful for dewarping when pixs is deliberately undercropped. * (8) Important: when applying disparity to a number of images, * after calling this function and saving the resulting pixd, * you should call dewarpMinimize(dew) on the dew for @pageno. * This will remove pixs and pixd (or their clones) stored in dew, * as well as the full resolution disparity arrays. Together, * these hold approximately 16 bytes for each pixel in pixs. */ l_int32 dewarpaApplyDisparity(L_DEWARPA *dewa, l_int32 pageno, PIX *pixs, l_int32 grayin, l_int32 x, l_int32 y, PIX **ppixd, const char *debugfile) { L_DEWARP *dew1, *dew; PIX *pixv, *pixh; PROCNAME("dewarpaApplyDisparity"); /* Initialize the output with the input, so we'll have that * in case we can't apply the page model. */ if (!ppixd) return ERROR_INT("&pixd not defined", procName, 1); *ppixd = pixClone(pixs); if (grayin > 255) { L_WARNING("invalid grayin = %d; clipping at 255\n", procName, grayin); grayin = 255; } /* Find the appropriate dew to use and fully populate its array(s) */ if (dewarpaApplyInit(dewa, pageno, pixs, x, y, &dew, debugfile)) return ERROR_INT("no model available", procName, 1); /* Correct for vertical disparity and save the result */ if ((pixv = pixApplyVertDisparity(dew, pixs, grayin)) == NULL) { dewarpMinimize(dew); return ERROR_INT("pixv not made", procName, 1); } pixDestroy(ppixd); *ppixd = pixv; if (debugfile) { pixDisplayWithTitle(pixv, 300, 0, "pixv", 1); lept_rmdir("lept/dewapply"); /* remove previous images */ lept_mkdir("lept/dewapply"); pixWrite("/tmp/lept/dewapply/001.png", pixs, IFF_PNG); pixWrite("/tmp/lept/dewapply/002.png", pixv, IFF_PNG); } /* Optionally, correct for horizontal disparity */ if (dewa->useboth && dew->hsuccess) { if (dew->hvalid == FALSE) { L_INFO("invalid horiz model for page %d\n", procName, pageno); } else { if ((pixh = pixApplyHorizDisparity(dew, pixv, grayin)) != NULL) { pixDestroy(ppixd); *ppixd = pixh; if (debugfile) { pixDisplayWithTitle(pixh, 600, 0, "pixh", 1); pixWrite("/tmp/lept/dewapply/003.png", pixh, IFF_PNG); } } else { L_ERROR("horiz disparity failed on page %d\n", procName, pageno); } } } if (debugfile) { dew1 = dewarpaGetDewarp(dewa, pageno); dewarpDebug(dew1, "lept/dewapply", 0); convertFilesToPdf("/tmp/lept/dewapply", NULL, 135, 1.0, 0, 0, "Dewarp Apply Disparity", debugfile); fprintf(stderr, "pdf file: %s\n", debugfile); } /* Get rid of the large full res disparity arrays */ dewarpMinimize(dew); return 0; }
l_int32 main(int argc, char **argv) { L_DEWARP *dew1, *dew2, *dew3; L_DEWARPA *dewa1, *dewa2, *dewa3; PIX *pixs, *pixn, *pixg, *pixb, *pixd; PIX *pixs2, *pixn2, *pixg2, *pixb2, *pixd2; PIX *pixd3, *pixc1, *pixc2; /* pixs = pixRead("1555-7.jpg"); */ pixs = pixRead("cat-35.jpg"); dewa1 = dewarpaCreate(40, 30, 1, 15, 10); dewarpaUseBothArrays(dewa1, 1); /* Normalize for varying background and binarize */ pixn = pixBackgroundNormSimple(pixs, NULL, NULL); pixg = pixConvertRGBToGray(pixn, 0.5, 0.3, 0.2); pixb = pixThresholdToBinary(pixg, 130); /* Run the basic functions */ dew1 = dewarpCreate(pixb, 35); dewarpaInsertDewarp(dewa1, dew1); dewarpBuildPageModel(dew1, "/tmp/dewarp_junk35.pdf"); /* debug output */ dewarpPopulateFullRes(dew1, pixg, 0, 0); dewarpaApplyDisparity(dewa1, 35, pixg, 200, 0, 0, &pixd, "/tmp/dewarp_debug_35.pdf"); /* Normalize another image. */ /* pixs2 = pixRead("1555-3.jpg"); */ pixs2 = pixRead("cat-7.jpg"); pixn2 = pixBackgroundNormSimple(pixs2, NULL, NULL); pixg2 = pixConvertRGBToGray(pixn2, 0.5, 0.3, 0.2); pixb2 = pixThresholdToBinary(pixg2, 130); /* Run the basic functions */ dew2 = dewarpCreate(pixb2, 7); dewarpaInsertDewarp(dewa1, dew2); dewarpBuildPageModel(dew2, "/tmp/dewarp_junk7.pdf"); dewarpaApplyDisparity(dewa1, 7, pixg, 200, 0, 0, &pixd2, "/tmp/dewarp_debug_7.pdf"); /* Serialize and deserialize dewarpa */ dewarpaWrite("/tmp/dewarpa1.dewa", dewa1); dewa2 = dewarpaRead("/tmp/dewarpa1.dewa"); dewarpaWrite("/tmp/dewarpa2.dewa", dewa2); dewa3 = dewarpaRead("/tmp/dewarpa2.dewa"); dewarpDebug(dewa3->dewarp[7], "dew1", 7); dewarpaWrite("/tmp/dewarpa3.dewa", dewa3); /* Repopulate and show the vertical disparity arrays */ dewarpPopulateFullRes(dew1, NULL, 0, 0); pixc1 = fpixRenderContours(dew1->fullvdispar, 2.0, 0.2); pixDisplay(pixc1, 1400, 900); dew3 = dewarpaGetDewarp(dewa2, 35); dewarpPopulateFullRes(dew3, pixs, 0, 0); pixc2 = fpixRenderContours(dew3->fullvdispar, 2.0, 0.2); pixDisplay(pixc2, 1400, 900); dewarpaApplyDisparity(dewa2, 35, pixb, 200, 0, 0, &pixd3, "/tmp/dewarp_debug_35b.pdf"); pixDisplay(pixd, 0, 1000); pixDisplay(pixd2, 600, 1000); pixDisplay(pixd3, 1200, 1000); pixDestroy(&pixd3); dewarpaDestroy(&dewa1); dewarpaDestroy(&dewa2); dewarpaDestroy(&dewa3); pixDestroy(&pixs); pixDestroy(&pixn); pixDestroy(&pixg); pixDestroy(&pixb); pixDestroy(&pixd); pixDestroy(&pixs2); pixDestroy(&pixn2); pixDestroy(&pixg2); pixDestroy(&pixb2); pixDestroy(&pixd2); pixDestroy(&pixc1); pixDestroy(&pixc2); return 0; }