Leptonica 1.68
C Image Processing Library
|
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