Leptonica 1.68
C Image Processing Library

dewarp.c

Go to the documentation of this file.
00001 /*====================================================================*
00002  -  Copyright (C) 2001 Leptonica.  All rights reserved.
00003  -  This software is distributed in the hope that it will be
00004  -  useful, but with NO WARRANTY OF ANY KIND.
00005  -  No author or distributor accepts responsibility to anyone for the
00006  -  consequences of using this software, or for whether it serves any
00007  -  particular purpose or works at all, unless he or she says so in
00008  -  writing.  Everyone is granted permission to copy, modify and
00009  -  redistribute this source code, for commercial or non-commercial
00010  -  purposes, with the following restrictions: (1) the origin of this
00011  -  source code must not be misrepresented; (2) modified versions must
00012  -  be plainly marked as such; and (3) this notice may not be removed
00013  -  or altered from any source or modified source distribution.
00014  *====================================================================*/
00015 
00016 /*
00017  *  dewarp.c
00018  *
00019  *      Create/destroy
00020  *          L_DEWARP      *dewarpCreate()
00021  *          void           dewarpDestroy()
00022  *
00023  *      Build warp model
00024  *          l_int32        dewarpBuildModel()
00025  *          PTAA          *pixGetTextlineCenters()
00026  *          PTA           *pixGetMeanVerticals()
00027  *          PTAA          *ptaaRemoveShortLines()
00028  *          FPIX          *fpixBuildHorizontalDisparity()
00029  *          FPIX          *fpixSampledDisparity()
00030  *
00031  *      Apply warping disparity array
00032  *          l_int32        dewarpApplyDisparity()
00033  *          l_int32        pixApplyVerticalDisparity()
00034  *          l_int32        pixApplyHorizontalDisparity()
00035  *
00036  *      Stripping out data and populating full res disparity
00037  *          l_int32        dewarpMinimize()
00038  *          l_int32        dewarpPopulateFullRes()
00039  *
00040  *      Serialized I/O
00041  *          L_DEWARP      *dewarpRead()
00042  *          L_DEWARP      *dewarpReadStream()
00043  *          l_int32        dewarpWrite()
00044  *          l_int32        dewarpWriteStream()
00045  *
00046  *  Basic functioning:
00047  *     Pix *pixb = "binarize"(pixs);
00048  *     L_Dewarp *dew = dewarpCreate(pixb, ...);
00049  *     dewarpBuildModel(dew, 0);
00050  *     dewarpApplyDisparity(dew, pixs, 0);
00051  *     // result is in dew->pixd;
00052  *
00053  *  Minimizing the data in a model by stripping out images,
00054  *  numas, and full resolution disparity arrays:
00055  *     dewarpMinimize(dew);
00056  *
00057  *  Applying a model (stripped or not) to another image:
00058  *     dewarpApplyDisparity(dew, newpix, 0);
00059  *
00060  *  Description of the problem and the approach
00061  *  -------------------------------------------
00062  *
00063  *  When a book page is scanned, there are several possible causes
00064  *  for the text lines to appear to be curved:
00065  *   (1) A barrel (fish-eye) effect because the camera is at
00066  *       a finite distance from the page.  Take the normal from
00067  *       the camera to the page (the 'optic axis').  Lines on
00068  *       the page "below" this point will appear to curve upward
00069  *       (negative curvature); lines "above" this will curve downward.
00070  *   (2) Radial distortion from the camera lens.  Probably not
00071  *       a big factor.
00072  *   (3) Local curvature of the page in to (or out of) the image
00073  *       plane (which is perpendicular to the optic axis).
00074  *       This has no effect if the page is flat.
00075  *
00076  *  The goal is to compute the "disparity" field, D(x,y), which
00077  *  is actually a vector composed of the horizontal and vertical
00078  *  disparity fields H(x,y) and V(x,y).  Each of these is a local
00079  *  function that gives the amount each point in the image is
00080  *  required to move in order to rectify the horizontal and vertical
00081  *  lines.
00082  *
00083  *  Effects (1) and (2) can be compensated for by calibrating
00084  *  the scene, using a flat page with horizontal and vertical lines.
00085  *  Then H(x,y) and V(x,y) can be found as two (non-parametric) arrays
00086  *  of values.  Suppose this has been done.  Then the remaining
00087  *  distortion is due to (3).
00088  *
00089  *  Now, if we knew everywhere the angle between the perpendicular
00090  *  to the paper and the optic axis (call it 'alpha'), the
00091  *  actual shape of the page could in principle be found by integration,
00092  *  and the remaining disparities, H(x,y) and V(x,y), could be
00093  *  found.  But we don't know alpha.  If there are text lines on
00094  *  the page, we can assume they should be horizontal, so we can
00095  *  compute the vertical disparity, which is the local translation
00096  *  required to make the text lines parallel to the rasters.
00097  *
00098  *  The basic question relating to (3) is this:
00099  *
00100  *     Is it possible, using the shape of the text lines alone,
00101  *     to compute both the vertical and horizontal disparity fields?
00102  *
00103  *  The problem is to find H(x,y).  In an image with horizontal
00104  *  text lines, the only vertical "lines" that we can infer are
00105  *  perhaps the left and right margins.
00106  *
00107  *  Start with a simple case.  Suppose the binding is along a
00108  *  vertical line, and the page curvature is independent of y.
00109  *  Then if the page curves in toward the binding, there will be
00110  *  a fractional foreshortening of that region in the x-direction, going
00111  *  as the sine of the angle between the optic axis and local the
00112  *  normal to the page.  For this situation, the horizontal
00113  *  disparity is independent of y: H(x,y) == H(x).
00114  *
00115  *  Now consider V(x,0) and V(x,h), the vertical disparity along
00116  *  the top and bottom of the image.  With a little thought you
00117  *  can convince yourself that the local foreshortening,
00118  *  as a function of x, is proportional to the difference
00119  *  between the slope of V(x,0) and V(x,h).  The horizontal
00120  *  disparity can then be computed by integrating the local foreshortening
00121  *  over x.  Integration of the slope of V(x,0) and V(x,h) gives
00122  *  the vertical disparity itself.  We have to normalize to h, the
00123  *  height of the page.  So the very simple result is that
00124  *
00125  *      H(x) ~ (V(x,0) - V(x,h)) / h         [1]
00126  *
00127  *  which is easily computed.  There is a proportionality constant
00128  *  that depends on the ratio of h to the distance to the camera.
00129  *  Can we actually believe this for the case where the bending
00130  *  is independent of y?  I believe the answer is yes,
00131  *  as long as you first remove the apparent distortion due
00132  *  to the camera being at a finite distance.
00133  *
00134  *  If you know the intersection of the optical axis with the page
00135  *  and the distance to the camera, and if the page is perpendicular
00136  *  to the optic axis, you can compute the horizontal and vertical
00137  *  disparities due to (1) and (2) and remove them.  The resulting
00138  *  distortion should be entirely due to bending (3), for which
00139  *  the relation
00140  *
00141  *      Hx(x) dx = C * ((Vx(x,0) - Vx(x, h))/h) dx         [2]
00142  *
00143  *  holds for each point in x (Hx and Vx are partial derivatives w/rt x).
00144  *  Integrating over x, and using H(0) = 0, we get the result [1].
00145  *
00146  *  I believe this result holds differentially for each value of y, so
00147  *  that in the case where the bending is not independent of y,
00148  *  the expression (V(x,0) - V(x,h)) / h goes over to Vy(x,y).  Then
00149  *
00150  *     H(x,y) = Integral(0,x) (Vyx(x,y) dx)         [3]
00151  *
00152  *  where Vyx() is the partial derivative of V w/rt both x and y.
00153  *
00154  *  There should be a simple mathematical relation between
00155  *  the horizontal and vertical disparities for the situation
00156  *  where the paper bends without stretching or kinking.
00157  *  I was hoping that we would get a relation between H and V such
00158  *  as Hx(x,y) ~ Vy(x,y), which would imply that H and V are real
00159  *  and imaginary parts of a complex potential, each of which
00160  *  satisfy the laplace equation.  But then the gradients of the
00161  *  two potentials would be normal, and that does not appear to be the case.
00162  *  Thus, the questions of proving the relations above (for small bending),
00163  *  or finding a simpler relation between H and V than those equations,
00164  *  remain open.  So far, we have only used [1] for the horizontal
00165  *  disparity H(x).
00166  *
00167  *  In the version of the code that follows, we use text lines
00168  *  to find V(x,y), and then, optionally, approximate H(x)
00169  *  from the values V(x,0) and V(x,h), as described above.
00170  *  The details are all in the code, but here is the basic outline.
00171  *  We assume that in the plane perpendicular to the optic axis
00172  *  (alpha = 0), horizontal and vertical lines have been rectified.
00173  *  (If not, they can be rectified using the methods described below,
00174  *  applied separately as steps (1,2,3) in the horizontal and
00175  *  vertical directions.)
00176  *
00177  *  (1) Find lines going approximately through the center of the
00178  *      text in each text line.  Accept only lines that are
00179  *      close in length to the longest line.
00180  *  (2) Generate a regular and highly subsampled vertical
00181  *      disparity field V(x,y).
00182  *  (3) Interpolate this to generate a full resolution vertical
00183  *      disparity field.
00184  *  (4) Optionally generate a full resolution horizontal disparity
00185  *      field, H(x).
00186  *  (5) Apply the vertical dewarping, followed optionally by the
00187  *      horizontal dewarping.
00188  *
00189  *  Step (1) is clearly described by the code in pixGetTextlineCenters().
00190  *
00191  *  Steps (2) and (3) follow directly from the data in step (1),
00192  *  and constitute the bulk of the work done in dewarpBuildModel().
00193  *  Virtually all the noise in the data is smoothed out by doing
00194  *  least-square quadratic fits, first horizontally to the data
00195  *  points representing the text line centers, and then vertically.
00196  *  The trick is to sample these lines on a regular grid.
00197  *  First each horizontal line is sampled at equally spaced
00198  *  intervals horizontally.  We thus get a set of points,
00199  *  one in each line, that are vertically aligned, and
00200  *  the data we represent is the vertical distance of each point
00201  *  from the min or max value on the curve, depending on the
00202  *  sign of the curvature component.  Each of these vertically
00203  *  aligned sets of points constitutes a sampled vertical disparity,
00204  *  and we do a LS quartic fit to each of them, followed by
00205  *  vertical sampling at regular intervals.  We now have a subsampled
00206  *  grid of points, all equally spaced, giving at each point the local
00207  *  vertical disparity.  Finally, the full resolution vertical disparity
00208  *  is formed by interpolation.  All the least square fits do a
00209  *  great job of smoothing everything out, as can be observed by
00210  *  the contour maps that are generated for the vertical disparity field.
00211  *
00212  *  Step (4) is trivially done with the approximation described above.
00213  *  Once V(x,y) and H(x,y) are derived, step (5) is done trivially.
00214  *  For vertical dewarp, source pixels at the top and bottom image
00215  *  boundaries are used whenever a request is made for a pixel that
00216  *  is outside the image.  For horizontal dewarp, the dest image width
00217  *  is increased to hold all transformed source pixels (remember,
00218  *  in that step, the image is widened).
00219  */
00220 
00221 #include <math.h>
00222 #include "allheaders.h"
00223 
00224 #ifndef  NO_CONSOLE_IO
00225 #define  DEBUG_TEXTLINE_CENTERS    0   /* you can set this to 1 for debug */
00226 #define  DEBUG_SHORT_LINES         0   /* you can set this to 1 for debug */
00227 #else
00228 #define  DEBUG_TEXTLINE_CENTERS    0   /* always must be 0 */
00229 #define  DEBUG_SHORT_LINES         0   /* always must be 0 */
00230 #endif  /* !NO_CONSOLE_IO */
00231 
00232     /* Default parameter values */
00233 static const l_int32     L_DEFAULT_SAMPLING = 30;
00234 static const l_float32   DEFAULT_SLOPE_FACTOR = 2000.;
00235 
00236 
00237 /*----------------------------------------------------------------------*
00238  *                             Create/destroy                           *
00239  *----------------------------------------------------------------------*/
00240 /*!
00241  *  dewarpCreate()
00242  *
00243  *     Input: pixs (1 bpp)
00244  *            pageno (page number)
00245  *            sampling (use -1 or 0 for default value; otherwise minimum of 5)
00246  *            minlines (minimum number of lines to accept; e.g., 10)
00247  *            applyhoriz (1 to estimate horiz disparity; 0 to skip)
00248  *     Return: dew (or null on error)
00249  *
00250  *  Notes:
00251  *      (1) The page number is typically 0-based.  If scanned from a book,
00252  *          the even pages are usually on the left.  Disparity arrays
00253  *          built for even pages should only be applied to even pages.
00254  *      (2) The sampling factor is for the disparity array.  The number
00255  *          used is not critical; anything between 10 and 60 should be fine.
00256  *      (3) The minimum number of nearly full-length lines required
00257  *          to generate a vertical disparity array.  Use a small number
00258  *          if you are willing to accept a questionable array.
00259  */
00260 L_DEWARP *
00261 dewarpCreate(PIX     *pixs,
00262              l_int32  pageno,
00263              l_int32  sampling,
00264              l_int32  minlines,
00265              l_int32  applyhoriz)
00266 {
00267 l_int32    w, h;
00268 L_DEWARP  *dew;
00269 
00270     PROCNAME("dewarpCreate");
00271 
00272     if (!pixs)
00273         return (L_DEWARP *)ERROR_PTR("pixs not defined", procName, NULL);
00274     if (pixGetDepth(pixs) != 1)
00275         return (L_DEWARP *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
00276 
00277     if ((dew = (L_DEWARP *)CALLOC(1, sizeof(L_DEWARP))) == NULL)
00278         return (L_DEWARP *)ERROR_PTR("dew not made", procName, NULL);
00279     dew->pixs = pixClone(pixs);
00280 
00281     if (sampling <= 0)
00282         sampling = L_DEFAULT_SAMPLING;
00283     else if (sampling < 5) {
00284          L_WARNING("sampling too small; setting to 5", procName);
00285          sampling = 5;
00286     }
00287     dew->sampling = sampling;
00288     dew->minlines = minlines;
00289     dew->applyhoriz = applyhoriz;
00290     dew->pageno = pageno;
00291 
00292         /* Get the dimensions of the sampled array.  This will be
00293          * stored in an fpix, and the full resolution version is
00294          * guaranteed to be larger than pixs. */
00295     pixGetDimensions(pixs, &w, &h, NULL);
00296     dew->nx = (w + 2 * sampling - 2) / sampling;
00297     dew->ny = (h + 2 * sampling - 2) / sampling;
00298     return dew;
00299 }
00300 
00301 
00302 /*!
00303  *  dewarpDestroy()
00304  *
00305  *      Input:  &dew (<will be set to null before returning>)
00306  *      Return: void
00307  */
00308 void
00309 dewarpDestroy(L_DEWARP  **pdew)
00310 {
00311 L_DEWARP  *dew;
00312 
00313     PROCNAME("dewarpDestroy");
00314 
00315     if (pdew == NULL) {
00316         L_WARNING("ptr address is null!", procName);
00317         return;
00318     }
00319     if ((dew = *pdew) == NULL)
00320         return;
00321 
00322     pixDestroy(&dew->pixs);
00323     pixDestroy(&dew->pixd);
00324     fpixDestroy(&dew->sampvdispar);
00325     fpixDestroy(&dew->samphdispar);
00326     fpixDestroy(&dew->fullvdispar);
00327     fpixDestroy(&dew->fullhdispar);
00328     numaDestroy(&dew->naflats);
00329     numaDestroy(&dew->nacurves);
00330     FREE(dew);
00331     *pdew = NULL;
00332     return;
00333 }
00334 
00335 
00336 /*----------------------------------------------------------------------*
00337  *                            Build warp model                          *
00338  *----------------------------------------------------------------------*/
00339 /*!
00340  *  dewarpBuildModel()
00341  *
00342  *      Input:  dew
00343  *              debugflag (1 for debugging output)
00344  *      Return: 0 if OK, 1 on error
00345  *
00346  *  Notes:
00347  *      (1) This is the basic function that builds the vertical
00348  *          disparity array, which allows determination of the
00349  *          src pixel in the input image corresponding to each
00350  *          dest pixel in the dewarped image.
00351  *      (2) The method is as follows:
00352  *          * Estimate the centers of all the long textlines and
00353  *            fit a LS quadratic to each one.  This smooths the curves.
00354  *          * Sample each curve at a regular interval, find the y-value
00355  *            of the flat point on each curve, and subtract the sampled
00356  *            curve value from this value.  This is the vertical
00357  *            disparity.
00358  *          * Fit a LS quadratic to each set of vertically aligned
00359  *            disparity samples.  This smooths the disparity values
00360  *            in the vertical direction.  Then resample at the same
00361  *            regular interval,  We now have a regular grid of smoothed
00362  *            vertical disparity valuels.
00363  *          * Interpolate this grid to get a full resolution disparity
00364  *            map.  This can be applied directly to the src image
00365  *            pixels to dewarp the image in the vertical direction,
00366  *            making all textlines horizontal.
00367  */
00368 l_int32
00369 dewarpBuildModel(L_DEWARP  *dew,
00370                  l_int32    debugflag)
00371 {
00372 char       *tempname;
00373 l_int32     i, j, nlines, nx, ny, sampling;
00374 l_float32   c0, c1, c2, x, y, flaty, val;
00375 l_float32  *faflats;
00376 NUMA       *nax, *nafit, *nacurve, *nacurves, *naflat, *naflats, *naflatsi;
00377 PIX        *pixs, *pixt1, *pixt2;
00378 PTA        *pta, *ptad;
00379 PTAA       *ptaa1, *ptaa2, *ptaa3, *ptaa4, *ptaa5, *ptaa6, *ptaa7;
00380 FPIX       *fpix1, *fpix2, *fpix3;
00381 
00382     PROCNAME("dewarpBuildModel");
00383 
00384     if (!dew)
00385         return ERROR_INT("dew not defined", procName, 1);
00386 
00387     pixs = dew->pixs;
00388     if (debugflag) {
00389         pixDisplayWithTitle(pixs, 0, 0, "pixs", 1);
00390         pixWriteTempfile("/tmp", "pixs.png", pixs, IFF_PNG, NULL);
00391     }
00392 
00393         /* Make initial estimate of centers of textlines */
00394     ptaa1 = pixGetTextlineCenters(pixs, DEBUG_TEXTLINE_CENTERS);
00395     if (debugflag) {
00396         pixt1 = pixConvertTo32(pixs);
00397         pixt2 = pixDisplayPtaa(pixt1, ptaa1);
00398         pixWriteTempfile("/tmp", "lines1.png", pixt2, IFF_PNG, NULL);
00399         pixDestroy(&pixt1);
00400         pixDestroy(&pixt2);
00401     }
00402 
00403         /* Remove all lines that are not near the length
00404          * of the longest line. */
00405     ptaa2 = ptaaRemoveShortLines(pixs, ptaa1, 0.8, DEBUG_SHORT_LINES);
00406     if (debugflag) {
00407         pixt1 = pixConvertTo32(pixs);
00408         pixt2 = pixDisplayPtaa(pixt1, ptaa2);
00409         pixWriteTempfile("/tmp", "lines2.png", pixt2, IFF_PNG, NULL);
00410         pixDestroy(&pixt1);
00411         pixDestroy(&pixt2);
00412     }
00413     nlines = ptaaGetCount(ptaa2);
00414     if (nlines < dew->minlines)
00415         return ERROR_INT("insufficient lines to build model", procName, 1);
00416 
00417         /* Do quadratic fit to smooth each line.  A single quadratic
00418          * over the entire width of the line appears to be sufficient.
00419          * Quartics tend to overfit to noise.  Each line is thus
00420          * represented by three coefficients: c2 * x^2 + c1 * x + c0.
00421          * Using the coefficients, sample each fitted curve uniformly
00422          * across the full width of the image.  */
00423     sampling = dew->sampling;
00424     nx = dew->nx;
00425     ny = dew->ny;
00426     ptaa3 = ptaaCreate(nlines);
00427     nacurve = numaCreate(nlines);  /* stores curvature coeff c2 */
00428     for (i = 0; i < nlines; i++) {  /* for each line */
00429         pta = ptaaGetPta(ptaa2, i, L_CLONE);
00430         ptaGetQuadraticLSF(pta, &c2, &c1, &c0, NULL);
00431         numaAddNumber(nacurve, c2);
00432         ptad = ptaCreate(nx);
00433         for (j = 0; j < nx; j++) {  /* uniformly sampled in x */
00434              x = j * sampling;
00435              applyQuadraticFit(c2, c1, c0, x, &y);
00436              ptaAddPt(ptad, x, y);
00437         }
00438         ptaaAddPta(ptaa3, ptad, L_INSERT);
00439         ptaDestroy(&pta);
00440     }
00441     if (debugflag) {
00442         ptaa4 = ptaaCreate(nlines);
00443         for (i = 0; i < nlines; i++) {
00444             pta = ptaaGetPta(ptaa2, i, L_CLONE);
00445             ptaGetArrays(pta, &nax, NULL);
00446             ptaGetQuadraticLSF(pta, NULL, NULL, NULL, &nafit);
00447             ptad = ptaCreateFromNuma(nax, nafit);
00448             ptaaAddPta(ptaa4, ptad, L_INSERT);
00449             ptaDestroy(&pta);
00450             numaDestroy(&nax);
00451             numaDestroy(&nafit);
00452         }
00453         pixt1 = pixConvertTo32(pixs);
00454         pixt2 = pixDisplayPtaa(pixt1, ptaa4);
00455         pixWriteTempfile("/tmp", "lines3.png", pixt2, IFF_PNG, NULL);
00456         pixDestroy(&pixt1);
00457         pixDestroy(&pixt2);
00458         ptaaDestroy(&ptaa4);
00459     }
00460 
00461         /* Find and save the flat points in each curve. */
00462     naflat = numaCreate(nlines);
00463     for (i = 0; i < nlines; i++) {
00464         pta = ptaaGetPta(ptaa3, i, L_CLONE);
00465         numaGetFValue(nacurve, i, &c2);
00466         if (c2 <= 0)  /* flat point at bottom; max value of y in curve */
00467             ptaGetRange(pta, NULL, NULL, NULL, &flaty);
00468         else  /* flat point at top; min value of y in curve */
00469             ptaGetRange(pta, NULL, NULL, &flaty, NULL);
00470         numaAddNumber(naflat, flaty);
00471         ptaDestroy(&pta);
00472     }
00473 
00474         /* Sort the lines in ptaa3 by their position */
00475     naflatsi = numaGetSortIndex(naflat, L_SORT_INCREASING);
00476     naflats = numaSortByIndex(naflat, naflatsi);
00477     nacurves = numaSortByIndex(nacurve, naflatsi);
00478     dew->naflats = naflats;
00479     dew->nacurves = nacurves;
00480     ptaa4 = ptaaSortByIndex(ptaa3, naflatsi);
00481     numaDestroy(&naflat);
00482     numaDestroy(&nacurve);
00483     numaDestroy(&naflatsi);
00484     if (debugflag) {
00485         tempname = genTempFilename("/tmp", "naflats.na", 0, 0);
00486         numaWrite(tempname, naflats);
00487         FREE(tempname);
00488     }
00489 
00490         /* Convert the sampled points in ptaa3 to a sampled disparity with
00491          * with respect to the flat point in the curve. */
00492     ptaa5 = ptaaCreate(nlines);
00493     for (i = 0; i < nlines; i++) {
00494         pta = ptaaGetPta(ptaa4, i, L_CLONE);
00495         numaGetFValue(naflats, i, &flaty);
00496         ptad = ptaCreate(nx);
00497         for (j = 0; j < nx; j++) {
00498             ptaGetPt(pta, j, &x, &y);
00499             ptaAddPt(ptad, x, flaty - y);
00500         }
00501         ptaaAddPta(ptaa5, ptad, L_INSERT);
00502         ptaDestroy(&pta);
00503     }
00504     if (debugflag) {
00505         tempname = genTempFilename("/tmp", "ptaa5.ptaa", 0, 0);
00506         ptaaWrite(tempname, ptaa5, 0);
00507         FREE(tempname);
00508     }
00509 
00510         /* Generate a ptaa taking vertical 'columns' from ptaa5.
00511          * We want to fit the vertical disparity on the column to the
00512          * vertical position of the line, which we call 'y' here and
00513          * obtain from naflats. */
00514     ptaa6 = ptaaCreate(nx);
00515     faflats = numaGetFArray(naflats, L_NOCOPY);
00516     for (j = 0; j < nx; j++) {
00517         pta = ptaCreate(nlines);
00518         for (i = 0; i < nlines; i++) {
00519             y = faflats[i];
00520             ptaaGetPt(ptaa5, i, j, NULL, &val);  /* disparity value */
00521             ptaAddPt(pta, y, val);
00522         }
00523         ptaaAddPta(ptaa6, pta, L_INSERT);
00524     }
00525     if (debugflag) {
00526         tempname = genTempFilename("/tmp", "ptaa6.ptaa", 0, 0);
00527         ptaaWrite(tempname, ptaa6, 0);
00528         FREE(tempname);
00529     }
00530 
00531         /* Do quadratic fit vertically on a subset of pixel columns
00532          * for the vertical displacement, which identifies the
00533          * src pixel(s) for each dest pixel.  Sample the displacement
00534          * on a regular grid in the vertical direction.   */
00535     ptaa7 = ptaaCreate(nx);  /* uniformly sampled across full height of image */
00536     for (j = 0; j < nx; j++) {  /* for each column */
00537         pta = ptaaGetPta(ptaa6, j, L_CLONE);
00538         ptaGetQuadraticLSF(pta, &c2, &c1, &c0, NULL);
00539         ptad = ptaCreate(ny);
00540         for (i = 0; i < ny; i++) {  /* uniformly sampled in y */
00541              y = i * sampling;
00542              applyQuadraticFit(c2, c1, c0, y, &val);
00543              ptaAddPt(ptad, y, val);
00544         }
00545         ptaaAddPta(ptaa7, ptad, L_INSERT);
00546         ptaDestroy(&pta);
00547     }
00548     if (debugflag) {
00549         tempname = genTempFilename("/tmp", "ptaa7.ptaa", 0, 0);
00550         ptaaWrite(tempname, ptaa7, 0);
00551         FREE(tempname);
00552     }
00553 
00554         /* Save the result in a fpix at the specified subsampling  */
00555     fpix1 = fpixCreate(nx, ny);
00556     for (i = 0; i < ny; i++) {
00557         for (j = 0; j < nx; j++) {
00558             ptaaGetPt(ptaa7, j, i, NULL, &val);
00559             fpixSetPixel(fpix1, j, i, val);
00560         }
00561     }
00562     dew->sampvdispar = fpix1;
00563 
00564         /* Generate a full res fpix for vertical dewarping.  We require that
00565          * the size of this fpix is at least as big as the input image. */
00566     fpix2 = fpixScaleByInteger(fpix1, sampling);
00567     dew->fullvdispar = fpix2;
00568     if (debugflag) {
00569         pixt1 = fpixRenderContours(fpix2, -2., 2.0, 0.2);
00570         pixWriteTempfile("/tmp", "vert-contours.png", pixt1, IFF_PNG, NULL);
00571         pixDisplay(pixt1, 1000, 0);
00572         pixDestroy(&pixt1);
00573     }
00574 
00575         /* Generate full res and sampled fpix for horizontal dewarping.  This
00576          * works to the extent that the line curvature is due to bending
00577          * out of the plane normal to the camera, and not wide-angle
00578          * "fishbowl" distortion.  Also generate the sampled horizontal
00579          * disparity array. */
00580     if (dew->applyhoriz) {
00581         fpix3 = fpixBuildHorizontalDisparity(fpix2, 0, &dew->extraw);
00582         dew->fullhdispar = fpix3;
00583         dew->samphdispar = fpixSampledDisparity(fpix3, dew->sampling);
00584         if (debugflag) {
00585             pixt1 = fpixRenderContours(fpix3, -2., 2.0, 0.2);
00586             pixWriteTempfile("/tmp", "horiz-contours.png", pixt1,
00587                              IFF_PNG, NULL);
00588             pixDisplay(pixt1, 1000, 0);
00589             pixDestroy(&pixt1);
00590         }
00591     }
00592 
00593     dew->success = 1;
00594 
00595     ptaaDestroy(&ptaa1);
00596     ptaaDestroy(&ptaa2);
00597     ptaaDestroy(&ptaa3);
00598     ptaaDestroy(&ptaa4);
00599     ptaaDestroy(&ptaa5);
00600     ptaaDestroy(&ptaa6);
00601     ptaaDestroy(&ptaa7);
00602     return 0;
00603 }
00604 
00605 
00606 /*!
00607  *  pixGetTextlineCenters()
00608  *
00609  *      Input:  pixs (1 bpp)
00610  *              debugflag (1 for debug output)
00611  *      Return: ptaa (of center values of textlines)
00612  *
00613  *  Notes:
00614  *      (1) This in general does not have a point for each value
00615  *          of x, because there will be gaps between words.
00616  *          It doesn't matter because we will fit a quadratic to the
00617  *          points that we do have.
00618  */
00619 PTAA *
00620 pixGetTextlineCenters(PIX     *pixs,
00621                       l_int32  debugflag)
00622 {
00623 l_int32   i, w, h, bx, by, nsegs;
00624 BOXA     *boxa;
00625 PIX      *pix, *pixt1, *pixt2, *pixt3;
00626 PIXA     *pixa1, *pixa2;
00627 PTA      *pta;
00628 PTAA     *ptaa;
00629 
00630     PROCNAME("pixGetTextlineCenters");
00631 
00632     if (!pixs || pixGetDepth(pixs) != 1)
00633         return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
00634     pixGetDimensions(pixs, &w, &h, NULL);
00635 
00636         /* Filter to solidify the text lines within the x-height region,
00637          * and to remove most of the ascenders and descenders. */
00638     pixt1 = pixMorphSequence(pixs, "c15.1 + o15.1 + c30.1", 0);
00639     pixDisplayWithTitle(pixt1, 0, 800, "pix1", debugflag);
00640 
00641         /* Get the 8-connected components ... */
00642     boxa = pixConnComp(pixt1, &pixa1, 8);
00643     pixDestroy(&pixt1);
00644     boxaDestroy(&boxa);
00645     if (pixaGetCount(pixa1) == 0) {
00646         pixaDestroy(&pixa1);
00647         return NULL;
00648     }
00649 
00650         /* ... and remove the short and thin c.c */
00651     pixa2 = pixaSelectBySize(pixa1, 100, 4, L_SELECT_IF_BOTH,
00652                                    L_SELECT_IF_GT, 0);
00653     if ((nsegs = pixaGetCount(pixa2)) == 0) {
00654         pixaDestroy(&pixa2);
00655         return NULL;
00656     }
00657     if (debugflag) {
00658         pixt2 = pixaDisplay(pixa2, w, h);
00659         pixDisplayWithTitle(pixt2, 800, 800, "pix2", 1);
00660         pixDestroy(&pixt2);
00661     }
00662 
00663         /* For each c.c., get the weighted center of each vertical column.
00664          * The result is a set of points going approximately through
00665          * the center of the x-height part of the text line.  */
00666     ptaa = ptaaCreate(nsegs);
00667     for (i = 0; i < nsegs; i++) {
00668         pixaGetBoxGeometry(pixa2, i, &bx, &by, NULL, NULL);
00669         pix = pixaGetPix(pixa2, i, L_CLONE);
00670         pta = pixGetMeanVerticals(pix, bx, by);
00671         ptaaAddPta(ptaa, pta, L_INSERT);
00672         pixDestroy(&pix);
00673     }
00674     if (debugflag) {
00675         pixt3 = pixCreateTemplate(pixt2);
00676         pix = pixDisplayPtaa(pixt3, ptaa);
00677         pixDisplayWithTitle(pix, 0, 1400, "pix3", 1);
00678         pixDestroy(&pix);
00679         pixDestroy(&pixt3);
00680     }
00681 
00682     pixaDestroy(&pixa1);
00683     pixaDestroy(&pixa2);
00684     return ptaa;
00685 }
00686 
00687 
00688 /*!
00689  *  ptaGetMeanVerticals()
00690  *
00691  *      Input:  pixs (1 bpp, single c.c.)
00692  *              x,y (location of UL corner of pixs with respect to page image
00693  *      Return: pta (mean y-values in component for each x-value,
00694  *                   both translated by (x,y)
00695  */
00696 PTA *
00697 pixGetMeanVerticals(PIX     *pixs,
00698                     l_int32  x,
00699                     l_int32  y)
00700 {
00701 l_int32    w, h, i, j, wpl, sum, count;
00702 l_uint32  *line, *data;
00703 PTA       *pta;
00704 
00705     PROCNAME("pixGetMeanVerticals");
00706 
00707     if (!pixs || pixGetDepth(pixs) != 1)
00708         return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
00709 
00710     pixGetDimensions(pixs, &w, &h, NULL);
00711     pta = ptaCreate(w);
00712     data = pixGetData(pixs);
00713     wpl = pixGetWpl(pixs);
00714     for (j = 0; j < w; j++) {
00715         line = data;
00716         sum = count = 0;
00717         for (i = 0; i < h; i++) {
00718             if (GET_DATA_BIT(line, j) == 1) {
00719                 sum += i;
00720                 count += 1;
00721             }
00722             line += wpl;
00723         }
00724         if (count == 0) continue;
00725         ptaAddPt(pta, x + j, y + (sum / count));
00726     }
00727 
00728     return pta;
00729 }
00730 
00731 
00732 /*!
00733  *  ptaaRemoveShortLines()
00734  *
00735  *      Input:  pixs (1 bpp)
00736  *              ptaas (input lines)
00737  *              fract (minimum fraction of longest line to keep)
00738  *              debugflag
00739  *      Return: ptaad (containing only lines of sufficient length),
00740  *                     or null on error
00741  */
00742 PTAA *
00743 ptaaRemoveShortLines(PIX       *pixs,
00744                      PTAA      *ptaas,
00745                      l_float32  fract,
00746                      l_int32    debugflag)
00747 {
00748 l_int32    w, n, i, index, maxlen, len;
00749 l_float32  minx, maxx;
00750 NUMA      *na, *naindex;
00751 PIX       *pixt1, *pixt2;
00752 PTA       *pta;
00753 PTAA      *ptaad;
00754 
00755     PROCNAME("ptaaRemoveShortLines");
00756 
00757     if (!pixs || pixGetDepth(pixs) != 1)
00758         return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
00759     if (!ptaas)
00760         return (PTAA *)ERROR_PTR("ptaas undefined", procName, NULL);
00761 
00762     pixGetDimensions(pixs, &w, NULL, NULL);
00763     n = ptaaGetCount(ptaas);
00764     ptaad = ptaaCreate(n);
00765     na = numaCreate(n);
00766     for (i = 0; i < n; i++) {
00767         pta = ptaaGetPta(ptaas, i, L_CLONE);
00768         ptaGetRange(pta, &minx, &maxx, NULL, NULL);
00769         numaAddNumber(na, maxx - minx + 1);
00770         ptaDestroy(&pta);
00771     }
00772 
00773         /* Sort by length and find all that are long enough */
00774     naindex = numaGetSortIndex(na, L_SORT_DECREASING);
00775     numaGetIValue(naindex, 0, &index);
00776     numaGetIValue(na, index, &maxlen);
00777     if (maxlen < 0.5 * w)
00778         L_WARNING("lines are relatively short", procName);
00779     pta = ptaaGetPta(ptaas, index, L_CLONE);
00780     ptaaAddPta(ptaad, pta, L_INSERT);
00781     for (i = 1; i < n; i++) {
00782         numaGetIValue(naindex, i, &index);
00783         numaGetIValue(na, index, &len);
00784         if (len < fract * maxlen) break;
00785         pta = ptaaGetPta(ptaas, index, L_CLONE);
00786         ptaaAddPta(ptaad, pta, L_INSERT);
00787     }
00788 
00789     if (debugflag) {
00790         pixt1 = pixCopy(NULL, pixs);
00791         pixt2 = pixDisplayPtaa(pixt1, ptaad);
00792         pixDisplayWithTitle(pixt2, 0, 200, "pix4", 1);
00793         pixDestroy(&pixt1);
00794         pixDestroy(&pixt2);
00795     }
00796 
00797     numaDestroy(&na);
00798     numaDestroy(&naindex);
00799     return ptaad;
00800 }
00801 
00802 
00803 /*!
00804  *  fpixBuildHorizontalDisparity()
00805  *
00806  *      Input:  fpixv (vertical disparity model)
00807  *              factor (conversion factor for vertical disparity slope;
00808  *                      use 0 for default)
00809  *              &extraw (<return> extra width to be added to dewarped pix)
00810  *      Return: fpixh, or null on error
00811  *
00812  *  Notes:
00813  *      (1) This takes the difference in vertical disparity at top
00814  *          and bottom of the image, and converts it to an assumed
00815  *          horizontal disparity.
00816  */
00817 FPIX *
00818 fpixBuildHorizontalDisparity(FPIX      *fpixv,
00819                              l_float32  factor,
00820                              l_int32   *pextraw)
00821 {
00822 l_int32     w, h, i, j, fw, wpl, maxloc;
00823 l_float32   val1, val2, vdisp, vdisp0, maxval;
00824 l_float32  *data, *line, *fadiff;
00825 NUMA       *nadiff;
00826 FPIX       *fpixh;
00827 
00828     PROCNAME("fpixBuildHorizontalDisparity");
00829 
00830     if (!fpixv)
00831         return (FPIX *)ERROR_PTR("fpixv not defined", procName, NULL);
00832     if (!pextraw)
00833         return (FPIX *)ERROR_PTR("&extraw not defined", procName, NULL);
00834     if (factor == 0.0)
00835         factor = DEFAULT_SLOPE_FACTOR;
00836 
00837         /* Estimate horizontal disparity from the vertical disparity
00838          * difference between the top and bottom, normalized to the
00839          * image height.  Add the maximum value to the width of the
00840          * output image, so that all src pixels can be mapped
00841          * into the dest. */
00842     fpixGetDimensions(fpixv, &w, &h);
00843     nadiff = numaCreate(w);
00844     for (j = 0; j < w; j++) {
00845         fpixGetPixel(fpixv, j, 0, &val1);
00846         fpixGetPixel(fpixv, j, h - 1, &val2);
00847         vdisp = factor * (val2 - val1) / (l_float32)h;
00848         if (j == 0) vdisp0 = vdisp;
00849         vdisp = vdisp0 - vdisp;
00850         numaAddNumber(nadiff, vdisp);
00851     }
00852     numaGetMax(nadiff, &maxval, &maxloc);
00853     *pextraw = (l_int32)(maxval + 0.5);
00854 
00855     fw = w + *pextraw;
00856     fpixh = fpixCreate(fw, h);
00857     data = fpixGetData(fpixh);
00858     wpl = fpixGetWpl(fpixh);
00859     fadiff = numaGetFArray(nadiff, L_NOCOPY);
00860     for (i = 0; i < h; i++) {
00861         line = data + i * wpl;
00862         for (j = 0; j < fw; j++) {
00863             if (j < maxloc)   /* this may not work for even pages */
00864                 line[j] = fadiff[j];
00865             else  /* keep it at the max value the rest of the way across */
00866                 line[j] = maxval;
00867         }
00868     }
00869 
00870     numaDestroy(&nadiff);
00871     return fpixh;
00872 }
00873 
00874 
00875 /*!
00876  *  fpixSampledDisparity()
00877  *
00878  *      Input:  fpixs (full resolution disparity model)
00879  *              sampling (sampling factor)
00880  *      Return: fpixd (sampled disparity model), or null on error
00881  *
00882  *  Notes:
00883  *      (1) The input array is sampled at the right and top edges, and
00884  *          at every @sampling pixels horizontally and vertically.
00885  *      (2) The sampled array is constructed large enough to (a) cover fpixs
00886  *          and (b) have the sampled grid on all boundary pixels in fpixd.
00887  *          Having sampled pixels around the boundary simplifies interpolation.
00888  *      (3) There must be at least 3 sampled points horizontally and
00889  *          vertically.
00890  */
00891 FPIX *
00892 fpixSampledDisparity(FPIX    *fpixs,
00893                      l_int32  sampling)
00894 {
00895 l_int32     w, h, wd, hd, i, j, is, js;
00896 l_float32   val;
00897 l_float32  *array;
00898 FPIX       *fpixd;
00899 
00900     PROCNAME("fpixSampledDisparity");
00901 
00902     if (!fpixs)
00903         return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
00904     if (sampling < 1)
00905         return (FPIX *)ERROR_PTR("sampling < 1", procName, NULL);
00906 
00907     fpixGetDimensions(fpixs, &w, &h);
00908     wd = 1 + (w + sampling - 2) / sampling;
00909     hd = 1 + (h + sampling - 2) / sampling;
00910     if (wd < 3 || hd < 3)
00911         return (FPIX *)ERROR_PTR("wd < 3 or hd < 3", procName, NULL);
00912     if ((array = (l_float32 *)CALLOC(w, sizeof(l_float32))) == NULL)
00913         return (FPIX *)ERROR_PTR("calloc fail for array", procName, NULL);
00914     fpixd = fpixCreate(wd, hd);
00915     for (i = 0; i < hd; i++) {
00916         is = sampling * i;
00917         if (is >= h) continue;
00918         for (j = 0; j < wd; j++) {
00919             js = sampling * j;
00920             if (js >= w) continue;
00921             fpixGetPixel(fpixs, js, is, &val);
00922             fpixSetPixel(fpixd, j, i, val);
00923             array[j] = val;
00924         }
00925             /* Linear extrapolation to right-hand end point */
00926         fpixSetPixel(fpixd, wd - 1, i, 2 * array[wd - 1] - array[wd - 2]);
00927     }
00928 
00929         /* Replicate value to bottom set */
00930     for (j = 0; j < wd; j++) {
00931         fpixGetPixel(fpixd, j, hd - 2, &val);
00932         fpixSetPixel(fpixd, j, hd - 1, val);
00933     }
00934 
00935     FREE(array);
00936     return fpixd;
00937 }
00938 
00939 
00940 /*----------------------------------------------------------------------*
00941  *                     Apply warping disparity array                    *
00942  *----------------------------------------------------------------------*/
00943 /*!
00944  *  dewarpApplyDisparity()
00945  *
00946  *      Input:  dew
00947  *              pixs (image to be modified; can be 1, 8 or 32 bpp)
00948  *              debugflag
00949  *      Return: 0 if OK, 1 on error
00950  *
00951  *  Notes:
00952  *      (1) This applies the vertical disparity array to the specified
00953  *          image.  For src pixels above the image, we use the pixels
00954  *          in the first raster line.
00955  *      (2) This works with stripped models.  If the full resolution
00956  *          disparity array(s) are missing, they are remade.
00957  */
00958 l_int32
00959 dewarpApplyDisparity(L_DEWARP  *dew,
00960                      PIX       *pixs,
00961                      l_int32    debugflag)
00962 {
00963 PIX  *pixv, *pixd;
00964 
00965     PROCNAME("dewarpApplyDisparity");
00966 
00967     if (!dew)
00968         return ERROR_INT("dew not defined", procName, 1);
00969     if (dew->success == 0)
00970         return ERROR_INT("model failed to build", procName, 1);
00971     if (!pixs)
00972         return ERROR_INT("pixs not defined", procName, 1);
00973 
00974         /* Generate the full res disparity arrays if they don't exist;
00975          * e.g., if they've been minimized or read from file.  */
00976     dewarpPopulateFullRes(dew);
00977     pixDestroy(&dew->pixd);  /* remove any previous one */
00978 
00979     if ((pixv = pixApplyVerticalDisparity(pixs, dew->fullvdispar)) == NULL)
00980         return ERROR_INT("pixv not made", procName, 1);
00981     if (debugflag) {
00982         pixDisplayWithTitle(pixv, 300, 0, "pixv", 1);
00983         pixWriteTempfile("/tmp", "pixv.png", pixv, IFF_PNG, NULL);
00984     }
00985 
00986     if (dew->applyhoriz) {
00987         if ((pixd = pixApplyHorizontalDisparity(pixv, dew->fullhdispar,
00988                                                 dew->extraw)) == NULL)
00989             return ERROR_INT("pixd not made", procName, 1);
00990         pixDestroy(&pixv);
00991         dew->pixd = pixd;
00992         if (debugflag) {
00993             pixDisplayWithTitle(pixd, 600, 0, "pixd", 1);
00994             pixWriteTempfile("/tmp", "pixd.png", pixd, IFF_PNG, NULL);
00995         }
00996     }
00997     else
00998         dew->pixd = pixv;
00999     return 0;
01000 }
01001 
01002 
01003 /*!
01004  *  pixApplyVerticalDisparity()
01005  *
01006  *      Input:  pixs (1, 8 or 32 bpp)
01007  *              fpix (vertical disparity array)
01008  *      Return: pixd (modified by fpix), or null on error
01009  *
01010  *  Notes:
01011  *      (1) This applies the vertical disparity array to the specified
01012  *          image.  For src pixels above the image, we use the pixels
01013  *          in the first raster line.
01014  */
01015 PIX *
01016 pixApplyVerticalDisparity(PIX   *pixs,
01017                           FPIX  *fpix)
01018 {
01019 l_int32     i, j, w, h, d, fw, fh, wpld, wplf, isrc, val8;
01020 l_uint32   *datad, *lined;
01021 l_float32  *dataf, *linef;
01022 void      **lineptrs;
01023 PIX        *pixd;
01024 
01025     PROCNAME("pixApplyVerticalDisparity");
01026 
01027     if (!pixs)
01028         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01029     if (!fpix)
01030         return (PIX *)ERROR_PTR("fpix not defined", procName, NULL);
01031     pixGetDimensions(pixs, &w, &h, &d);
01032     if (d != 1 && d != 8 && d != 32)
01033         return (PIX *)ERROR_PTR("pix not 1, 8 or 32 bpp", procName, NULL);
01034     fpixGetDimensions(fpix, &fw, &fh);
01035     if (fw < w || fh < h) {
01036         fprintf(stderr, "fw = %d, w = %d, fh = %d, h = %d\n", fw, w, fh, h);
01037         return (PIX *)ERROR_PTR("invalid fpix size", procName, NULL);
01038     }
01039 
01040     pixd = pixCreateTemplate(pixs);
01041     datad = pixGetData(pixd);
01042     dataf = fpixGetData(fpix);
01043     wpld = pixGetWpl(pixd);
01044     wplf = fpixGetWpl(fpix);
01045     if (d == 1) {
01046         lineptrs = pixGetLinePtrs(pixs, NULL);
01047         for (i = 0; i < h; i++) {
01048             lined = datad + i * wpld;
01049             linef = dataf + i * wplf;
01050             for (j = 0; j < w; j++) {
01051                 isrc = (l_int32)(i - linef[j] + 0.5);
01052                 if (isrc < 0) isrc = 0;
01053                 if (isrc > h - 1) isrc = h - 1;
01054                 if (GET_DATA_BIT(lineptrs[isrc], j))
01055                     SET_DATA_BIT(lined, j);
01056             }
01057         }
01058     }
01059     else if (d == 8) {
01060         lineptrs = pixGetLinePtrs(pixs, NULL);
01061         for (i = 0; i < h; i++) {
01062             lined = datad + i * wpld;
01063             linef = dataf + i * wplf;
01064             for (j = 0; j < w; j++) {
01065                 isrc = (l_int32)(i - linef[j] + 0.5);
01066                 if (isrc < 0) isrc = 0;
01067                 if (isrc > h - 1) isrc = h - 1;
01068                 val8 = GET_DATA_BYTE(lineptrs[isrc], j);
01069                 SET_DATA_BYTE(lined, j, val8);
01070             }
01071         }
01072     }
01073     else {  /* d == 32 */
01074         lineptrs = pixGetLinePtrs(pixs, NULL);
01075         for (i = 0; i < h; i++) {
01076             lined = datad + i * wpld;
01077             linef = dataf + i * wplf;
01078             for (j = 0; j < w; j++) {
01079                 isrc = (l_int32)(i - linef[j] + 0.5);
01080                 if (isrc < 0) isrc = 0;
01081                 if (isrc > h - 1) isrc = h - 1;
01082                 lined[j] = GET_DATA_FOUR_BYTES(lineptrs[isrc], j);
01083             }
01084         }
01085     }
01086 
01087     FREE(lineptrs);
01088     return pixd;
01089 }
01090 
01091 
01092 /*!
01093  *  pixApplyHorizontalDisparity()
01094  *
01095  *      Input:  pixs (1, 8 or 32 bpp)
01096  *              fpix (horizontal disparity array)
01097  *              extraw (extra width added to pixd)
01098  *      Return: pixd (modified by fpix), or null on error
01099  *
01100  *  Notes:
01101  *      (1) This applies the horizontal disparity array to the specified
01102  *          image.
01103  */
01104 PIX *
01105 pixApplyHorizontalDisparity(PIX     *pixs,
01106                             FPIX    *fpix,
01107                             l_int32  extraw)
01108 {
01109 l_int32     i, j, w, h, d, wd, fw, fh, wpls, wpld, wplf, jsrc, val8;
01110 l_uint32   *datas, *lines, *datad, *lined;
01111 l_float32  *dataf, *linef;
01112 PIX        *pixd;
01113 
01114     PROCNAME("pixApplyHorizontalDisparity");
01115 
01116     if (!pixs)
01117         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
01118     if (!fpix)
01119         return (PIX *)ERROR_PTR("fpix not defined", procName, NULL);
01120     pixGetDimensions(pixs, &w, &h, &d);
01121     if (d != 1 && d != 8 && d != 32)
01122         return (PIX *)ERROR_PTR("pix not 1, 8 or 32 bpp", procName, NULL);
01123     fpixGetDimensions(fpix, &fw, &fh);
01124     if (fw < w + extraw || fh < h) {
01125         fprintf(stderr, "fw = %d, w = %d, fh = %d, h = %d\n", fw, w, fh, h);
01126         return (PIX *)ERROR_PTR("invalid fpix size", procName, NULL);
01127     }
01128 
01129     wd = w + extraw;
01130     pixd = pixCreate(wd, h, d);
01131     datas = pixGetData(pixs);
01132     datad = pixGetData(pixd);
01133     dataf = fpixGetData(fpix);
01134     wpls = pixGetWpl(pixs);
01135     wpld = pixGetWpl(pixd);
01136     wplf = fpixGetWpl(fpix);
01137     if (d == 1) {
01138         for (i = 0; i < h; i++) {
01139             lines = datas + i * wpls;
01140             lined = datad + i * wpld;
01141             linef = dataf + i * wplf;
01142             for (j = 0; j < wd; j++) {
01143                 jsrc = (l_int32)(j - linef[j] + 0.5);
01144                 if (jsrc < 0) jsrc = 0;
01145                 if (jsrc > w - 1) jsrc = w - 1;
01146                 if (GET_DATA_BIT(lines, jsrc))
01147                     SET_DATA_BIT(lined, j);
01148             }
01149         }
01150     }
01151     else if (d == 8) {
01152         for (i = 0; i < h; i++) {
01153             lines = datas + i * wpls;
01154             lined = datad + i * wpld;
01155             linef = dataf + i * wplf;
01156             for (j = 0; j < wd; j++) {
01157                 jsrc = (l_int32)(j - linef[j] + 0.5);
01158                 if (jsrc < 0) jsrc = 0;
01159                 if (jsrc > w - 1) jsrc = w - 1;
01160                 val8 = GET_DATA_BYTE(lines, jsrc);
01161                 SET_DATA_BYTE(lined, j, val8);
01162             }
01163         }
01164     }
01165     else {  /* d == 32 */
01166         for (i = 0; i < h; i++) {
01167             lines = datas + i * wpls;
01168             lined = datad + i * wpld;
01169             linef = dataf + i * wplf;
01170             for (j = 0; j < wd; j++) {
01171                 jsrc = (l_int32)(j - linef[j] + 0.5);
01172                 if (jsrc < 0) jsrc = 0;
01173                 if (jsrc > w - 1) jsrc = w - 1;
01174                 lined[j] = lines[jsrc];
01175             }
01176         }
01177     }
01178 
01179     return pixd;
01180 }
01181 
01182 
01183 /*----------------------------------------------------------------------*
01184  *          Stripping out data and populating full res disparity        *
01185  *----------------------------------------------------------------------*/
01186 /*!
01187  *  dewarpMinimize()
01188  *
01189  *      Input:  dew
01190  *      Return: 0 if OK, 1 on error
01191  *
01192  *  Notes:
01193  *      (1) This removes all data that is not needed for serialization.
01194  *          It keeps the subsampled disparity array(s), so the full
01195  *          resolution arrays can be reconstructed.
01196  */
01197 l_int32
01198 dewarpMinimize(L_DEWARP  *dew)
01199 {
01200     PROCNAME("dewarpMinimize");
01201 
01202     if (!dew)
01203         return ERROR_INT("dew not defined", procName, 1);
01204 
01205     pixDestroy(&dew->pixs);
01206     pixDestroy(&dew->pixd);
01207     fpixDestroy(&dew->fullvdispar);
01208     fpixDestroy(&dew->fullhdispar);
01209     numaDestroy(&dew->naflats);
01210     numaDestroy(&dew->nacurves);
01211     return 0;
01212 }
01213 
01214 
01215 /*!
01216  *  dewarpPopulateFullRes()
01217  *
01218  *      Input:  dew
01219  *      Return: 0 if OK, 1 on error
01220  *
01221  *  Notes:
01222  *      (1) If the full resolution vertical (and, optionally horizontal)
01223  *          disparity arrays do not exist, they are built from the
01224  *          subsampled ones.
01225  */
01226 l_int32
01227 dewarpPopulateFullRes(L_DEWARP  *dew)
01228 {
01229     PROCNAME("dewarpPopulateFullRes");
01230 
01231     if (!dew)
01232         return ERROR_INT("dew not defined", procName, 1);
01233     if (!dew->sampvdispar)
01234         return ERROR_INT("no sampled vert disparity", procName, 1);
01235 
01236     if (!dew->fullvdispar)
01237         dew->fullvdispar = fpixScaleByInteger(dew->sampvdispar, dew->sampling);
01238 
01239     if (!dew->fullhdispar && dew->samphdispar)
01240         dew->fullhdispar = fpixScaleByInteger(dew->samphdispar, dew->sampling);
01241 
01242     return 0;
01243 }
01244 
01245 
01246 /*----------------------------------------------------------------------*
01247  *                       Dewarp serialized I/O                          *
01248  *----------------------------------------------------------------------*/
01249 /*!
01250  *  dewarpRead()
01251  *
01252  *      Input:  filename
01253  *      Return: dew, or null on error
01254  */
01255 L_DEWARP *
01256 dewarpRead(const char  *filename)
01257 {
01258 FILE      *fp;
01259 L_DEWARP  *dew;
01260 
01261     PROCNAME("dewarpRead");
01262 
01263     if (!filename)
01264         return (L_DEWARP *)ERROR_PTR("filename not defined", procName, NULL);
01265     if ((fp = fopenReadStream(filename)) == NULL)
01266         return (L_DEWARP *)ERROR_PTR("stream not opened", procName, NULL);
01267 
01268     if ((dew = dewarpReadStream(fp)) == NULL) {
01269         fclose(fp);
01270         return (L_DEWARP *)ERROR_PTR("dew not read", procName, NULL);
01271     }
01272 
01273     fclose(fp);
01274     return dew;
01275 }
01276 
01277 
01278 /*!
01279  *  dewarpReadStream()
01280  *
01281  *      Input:  stream
01282  *      Return: dew, or null on error
01283  *
01284  *  Notes:
01285  *      (1) The dewarp struct is stored in minimized format, with only
01286  *          subsampled disparity arrays.
01287  */
01288 L_DEWARP *
01289 dewarpReadStream(FILE  *fp)
01290 {
01291 l_int32    version, sampling, pageno, nx, ny, hdispar;
01292 L_DEWARP  *dew;
01293 FPIX      *fpixv, *fpixh;
01294 
01295     PROCNAME("dewarpReadStream");
01296 
01297     if (!fp)
01298         return (L_DEWARP *)ERROR_PTR("stream not defined", procName, NULL);
01299 
01300     if (fscanf(fp, "\nDewarp Version %d\n", &version) != 1)
01301         return (L_DEWARP *)ERROR_PTR("not a dewarp file", procName, NULL);
01302     if (version != DEWARP_VERSION_NUMBER)
01303         return (L_DEWARP *)ERROR_PTR("invalid dewarp version", procName, NULL);
01304     if (fscanf(fp, "pageno = %d, sampling = %d\n", &pageno, &sampling) != 2)
01305         return (L_DEWARP *)ERROR_PTR("read fail for pageno+", procName, NULL);
01306     if (fscanf(fp, "nx = %d, ny = %d, horiz_disparity = %d\n",
01307                &nx, &ny, &hdispar) != 3)
01308         return (L_DEWARP *)ERROR_PTR("read fail for nx+", procName, NULL);
01309     if ((fpixv = fpixReadStream(fp)) == NULL)
01310         return (L_DEWARP *)ERROR_PTR("read fail for vdispar", procName, NULL);
01311     if (hdispar) {
01312         if ((fpixh = fpixReadStream(fp)) == NULL)
01313             return (L_DEWARP *)ERROR_PTR("read fail for horiz disparity",
01314                                          procName, NULL);
01315     }
01316 
01317     dew = (L_DEWARP *)CALLOC(1, sizeof(L_DEWARP));
01318     dew->sampling = sampling;
01319     dew->pageno = pageno;
01320     dew->nx = nx;
01321     dew->ny = ny;
01322     dew->success = 1;
01323     dew->sampvdispar = fpixv;
01324     if (hdispar) {
01325         dew->applyhoriz = 1;
01326         dew->samphdispar = fpixh;
01327     }
01328 
01329     return dew;
01330 }
01331 
01332 
01333 /*!
01334  *  dewarpWrite()
01335  *
01336  *      Input:  filename
01337  *              dew 
01338  *      Return: 0 if OK, 1 on error
01339  */
01340 l_int32
01341 dewarpWrite(const char  *filename,
01342             L_DEWARP    *dew)
01343 {
01344 FILE  *fp;
01345 
01346     PROCNAME("dewarpWrite");
01347 
01348     if (!filename)
01349         return ERROR_INT("filename not defined", procName, 1);
01350     if (!dew)
01351         return ERROR_INT("dew not defined", procName, 1);
01352 
01353     if ((fp = fopenWriteStream(filename, "wb")) == NULL)
01354         return ERROR_INT("stream not opened", procName, 1);
01355     if (dewarpWriteStream(fp, dew))
01356         return ERROR_INT("dew not written to stream", procName, 1);
01357     fclose(fp);
01358 
01359     return 0;
01360 }
01361 
01362 
01363 /*!
01364  *  dewarpWriteStream()
01365  *
01366  *      Input:  stream (opened for "wb")
01367  *              dew
01368  *      Return: 0 if OK, 1 on error
01369  */
01370 l_int32
01371 dewarpWriteStream(FILE      *fp,
01372                   L_DEWARP  *dew)
01373 {
01374 l_int32  hdispar;
01375 
01376     PROCNAME("dewarpWriteStream");
01377 
01378     if (!fp)
01379         return ERROR_INT("stream not defined", procName, 1);
01380     if (!dew)
01381         return ERROR_INT("dew not defined", procName, 1);
01382 
01383     fprintf(fp, "\nDewarp Version %d\n", DEWARP_VERSION_NUMBER);
01384     fprintf(fp, "pageno = %d, sampling = %d\n", dew->pageno, dew->sampling);
01385     hdispar = (dew->samphdispar) ? 1 : 0;
01386     fprintf(fp, "nx = %d, ny = %d, horiz_disparity = %d\n",
01387             dew->nx, dew->ny, hdispar);
01388 
01389     fpixWriteStream(fp, dew->sampvdispar);
01390     if (hdispar)
01391         fpixWriteStream(fp, dew->samphdispar);
01392 
01393     return 0;
01394 }
01395 
01396 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines