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 * skew.c 00018 * 00019 * Top-level deskew interfaces 00020 * PIX *pixDeskew() 00021 * PIX *pixFindSkewAndDeskew() 00022 * PIX *pixDeskewGeneral() 00023 * 00024 * Top-level angle-finding interface 00025 * l_int32 pixFindSkew() 00026 * 00027 * Basic angle-finding functions 00028 * l_int32 pixFindSkewSweep() 00029 * l_int32 pixFindSkewSweepAndSearch() 00030 * l_int32 pixFindSkewSweepAndSearchScore() 00031 * l_int32 pixFindSkewSweepAndSearchScorePivot() 00032 * 00033 * Search over arbitrary range of angles in orthogonal directions 00034 * l_int32 pixFindSkewOrthogonalRange() 00035 * 00036 * Differential square sum function for scoring 00037 * l_int32 pixFindDifferentialSquareSum() 00038 * 00039 * Measures of variance of row sums 00040 * l_int32 pixFindNormalizedSquareSum() 00041 * 00042 * 00043 * ============================================================== 00044 * Page skew detection 00045 * 00046 * Skew is determined by pixel profiles, which are computed 00047 * as pixel sums along the raster line for each line in the 00048 * image. By vertically shearing the image by a given angle, 00049 * the sums can be computed quickly along the raster lines 00050 * rather than along lines at that angle. The score is 00051 * computed from these line sums by taking the square of 00052 * the DIFFERENCE between adjacent line sums, summed over 00053 * all lines. The skew angle is then found as the angle 00054 * that maximizes the score. The actual computation for 00055 * any sheared image is done in the function 00056 * pixFindDifferentialSquareSum(). 00057 * 00058 * The search for the angle that maximizes this score is 00059 * most efficiently performed by first sweeping coarsely 00060 * over angles, using a significantly reduced image (say, 4x 00061 * reduction), to find the approximate maximum within a half 00062 * degree or so, and then doing an interval-halving binary 00063 * search at higher resolution to get the skew angle to 00064 * within 1/20 degree or better. 00065 * 00066 * The differential signal is used (rather than just using 00067 * that variance of line sums) because it rejects the 00068 * background noise due to total number of black pixels, 00069 * and has maximum contributions from the baselines and 00070 * x-height lines of text when the textlines are aligned 00071 * with the raster lines. It also works well in multicolumn 00072 * pages where the textlines do not line up across columns. 00073 * 00074 * The method is fast, accurate to within an angle (in radians) 00075 * of approximately the inverse width in pixels of the image, 00076 * and will work on a surprisingly small amount of text data 00077 * (just a couple of text lines). Consequently, it can 00078 * also be used to find local skew if the skew were to vary 00079 * significantly over the page. Local skew determination 00080 * is not very important except for locating lines of 00081 * handwritten text that may be mixed with printed text. 00082 */ 00083 00084 #include <math.h> 00085 #include "allheaders.h" 00086 00087 /* Default sweep angle parameters for pixFindSkew() */ 00088 static const l_float32 DEFAULT_SWEEP_RANGE = 7.; /* degrees */ 00089 static const l_float32 DEFAULT_SWEEP_DELTA = 1.; /* degrees */ 00090 00091 /* Default final angle difference parameter for binary 00092 * search in pixFindSkew(). The expected accuracy is 00093 * not better than the inverse image width in pixels, 00094 * say, 1/2000 radians, or about 0.03 degrees. */ 00095 static const l_float32 DEFAULT_MINBS_DELTA = 0.01; /* degrees */ 00096 00097 /* Default scale factors for pixFindSkew() */ 00098 static const l_int32 DEFAULT_SWEEP_REDUCTION = 4; /* sweep part; 4 is good */ 00099 static const l_int32 DEFAULT_BS_REDUCTION = 2; /* binary search part */ 00100 00101 /* Minimum angle for deskewing in pixDeskew() */ 00102 static const l_float32 MIN_DESKEW_ANGLE = 0.1; /* degree */ 00103 00104 /* Minimum allowed confidence (ratio) for deskewing in pixDeskew() */ 00105 static const l_float32 MIN_ALLOWED_CONFIDENCE = 3.0; 00106 00107 /* Minimum allowed maxscore to give nonzero confidence */ 00108 static const l_int32 MIN_VALID_MAXSCORE = 10000; 00109 00110 /* Constant setting threshold for minimum allowed minscore 00111 * to give nonzero confidence; multiply this constant by 00112 * (height * width^2) */ 00113 static const l_float32 MINSCORE_THRESHOLD_CONSTANT = 0.000002; 00114 00115 /* Default binarization threshold value */ 00116 static const l_int32 DEFAULT_BINARY_THRESHOLD = 130; 00117 00118 #ifndef NO_CONSOLE_IO 00119 #define DEBUG_PRINT_SCORES 0 00120 #define DEBUG_PRINT_SWEEP 0 00121 #define DEBUG_PRINT_BINARY 0 00122 #define DEBUG_PRINT_ORTH 0 00123 #define DEBUG_THRESHOLD 0 00124 #define DEBUG_PLOT_SCORES 0 00125 #endif /* ~NO_CONSOLE_IO */ 00126 00127 00128 00129 /*-----------------------------------------------------------------------* 00130 * Top-level deskew interfaces * 00131 *-----------------------------------------------------------------------*/ 00132 /*! 00133 * pixDeskew() 00134 * 00135 * Input: pixs (any depth) 00136 * redsearch (for binary search: reduction factor = 1, 2 or 4; 00137 * use 0 for default) 00138 * Return: pixd (deskewed pix), or null on error 00139 * 00140 * Notes: 00141 * (1) This binarizes if necessary and finds the skew angle. If the 00142 * angle is large enough and there is sufficient confidence, 00143 * it returns a deskewed image; otherwise, it returns a clone. 00144 */ 00145 PIX * 00146 pixDeskew(PIX *pixs, 00147 l_int32 redsearch) 00148 { 00149 PROCNAME("pixDeskew"); 00150 00151 if (!pixs) 00152 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00153 if (redsearch == 0) 00154 redsearch = DEFAULT_BS_REDUCTION; 00155 else if (redsearch != 1 && redsearch != 2 && redsearch != 4) 00156 return (PIX *)ERROR_PTR("redsearch not in {1,2,4}", procName, NULL); 00157 00158 return pixDeskewGeneral(pixs, 0, 0.0, 0.0, redsearch, 0, NULL, NULL); 00159 } 00160 00161 00162 /*! 00163 * pixFindSkewAndDeskew() 00164 * 00165 * Input: pixs (any depth) 00166 * redsearch (for binary search: reduction factor = 1, 2 or 4; 00167 * use 0 for default) 00168 * &angle (<optional return> angle required to deskew, 00169 * in degrees; use NULL to skip) 00170 * &conf (<optional return> conf value is ratio 00171 * of max/min scores; use NULL to skip) 00172 * Return: pixd (deskewed pix), or null on error 00173 * 00174 * Notes: 00175 * (1) This binarizes if necessary and finds the skew angle. If the 00176 * angle is large enough and there is sufficient confidence, 00177 * it returns a deskewed image; otherwise, it returns a clone. 00178 */ 00179 PIX * 00180 pixFindSkewAndDeskew(PIX *pixs, 00181 l_int32 redsearch, 00182 l_float32 *pangle, 00183 l_float32 *pconf) 00184 { 00185 PROCNAME("pixFindSkewAndDeskew"); 00186 00187 if (!pixs) 00188 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00189 if (redsearch == 0) 00190 redsearch = DEFAULT_BS_REDUCTION; 00191 else if (redsearch != 1 && redsearch != 2 && redsearch != 4) 00192 return (PIX *)ERROR_PTR("redsearch not in {1,2,4}", procName, NULL); 00193 00194 return pixDeskewGeneral(pixs, 0, 0.0, 0.0, redsearch, 0, pangle, pconf); 00195 } 00196 00197 00198 /*! 00199 * pixDeskewGeneral() 00200 * 00201 * Input: pixs (any depth) 00202 * redsweep (for linear search: reduction factor = 1, 2 or 4; 00203 * use 0 for default) 00204 * sweeprange (in degrees in each direction from 0; 00205 * use 0.0 for default) 00206 * sweepdelta (in degrees; use 0.0 for default) 00207 * redsearch (for binary search: reduction factor = 1, 2 or 4; 00208 * use 0 for default;) 00209 * thresh (for binarizing the image; use 0 for default) 00210 * &angle (<optional return> angle required to deskew, 00211 * in degrees; use NULL to skip) 00212 * &conf (<optional return> conf value is ratio 00213 * of max/min scores; use NULL to skip) 00214 * Return: pixd (deskewed pix), or null on error 00215 * 00216 * Notes: 00217 * (1) This binarizes if necessary and finds the skew angle. If the 00218 * angle is large enough and there is sufficient confidence, 00219 * it returns a deskewed image; otherwise, it returns a clone. 00220 */ 00221 PIX * 00222 pixDeskewGeneral(PIX *pixs, 00223 l_int32 redsweep, 00224 l_float32 sweeprange, 00225 l_float32 sweepdelta, 00226 l_int32 redsearch, 00227 l_int32 thresh, 00228 l_float32 *pangle, 00229 l_float32 *pconf) 00230 { 00231 l_int32 ret, depth; 00232 l_float32 angle, conf, deg2rad; 00233 PIX *pixb, *pixd; 00234 00235 PROCNAME("pixDeskewGeneral"); 00236 00237 if (!pixs) 00238 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00239 if (redsweep == 0) 00240 redsweep = DEFAULT_SWEEP_REDUCTION; 00241 else if (redsweep != 1 && redsweep != 2 && redsweep != 4) 00242 return (PIX *)ERROR_PTR("redsweep not in {1,2,4}", procName, NULL); 00243 if (sweeprange == 0.0) 00244 sweeprange = DEFAULT_SWEEP_RANGE; 00245 if (sweepdelta == 0.0) 00246 sweepdelta = DEFAULT_SWEEP_DELTA; 00247 if (redsearch == 0) 00248 redsearch = DEFAULT_BS_REDUCTION; 00249 else if (redsearch != 1 && redsearch != 2 && redsearch != 4) 00250 return (PIX *)ERROR_PTR("redsearch not in {1,2,4}", procName, NULL); 00251 if (thresh == 0) 00252 thresh = DEFAULT_BINARY_THRESHOLD; 00253 00254 deg2rad = 3.1415926535 / 180.; 00255 00256 /* Binarize if necessary */ 00257 depth = pixGetDepth(pixs); 00258 if (depth == 1) 00259 pixb = pixClone(pixs); 00260 else 00261 pixb = pixConvertTo1(pixs, thresh); 00262 00263 /* Use the 1 bpp image to find the skew */ 00264 ret = pixFindSkewSweepAndSearch(pixb, &angle, &conf, redsweep, redsearch, 00265 sweeprange, sweepdelta, 00266 DEFAULT_MINBS_DELTA); 00267 pixDestroy(&pixb); 00268 if (pangle) 00269 *pangle = angle; 00270 if (pconf) 00271 *pconf = conf; 00272 if (ret) 00273 return pixClone(pixs); 00274 00275 if (L_ABS(angle) < MIN_DESKEW_ANGLE || conf < MIN_ALLOWED_CONFIDENCE) 00276 return pixClone(pixs); 00277 00278 if ((pixd = pixRotate(pixs, deg2rad * angle, L_ROTATE_AREA_MAP, 00279 L_BRING_IN_WHITE, 0, 0)) == NULL) 00280 return pixClone(pixs); 00281 else 00282 return pixd; 00283 } 00284 00285 00286 /*-----------------------------------------------------------------------* 00287 * Simple top-level angle-finding interface * 00288 *-----------------------------------------------------------------------*/ 00289 /*! 00290 * pixFindSkew() 00291 * 00292 * Input: pixs (1 bpp) 00293 * &angle (<return> angle required to deskew, in degrees) 00294 * &conf (<return> confidence value is ratio max/min scores) 00295 * Return: 0 if OK, 1 on error or if angle measurment not valid 00296 * 00297 * Notes: 00298 * (1) This is a simple high-level interface, that uses default 00299 * values of the parameters for reasonable speed and accuracy. 00300 * (2) The angle returned is the negative of the skew angle of 00301 * the image. It is the angle required for deskew. 00302 * Clockwise rotations are positive angles. 00303 */ 00304 l_int32 00305 pixFindSkew(PIX *pixs, 00306 l_float32 *pangle, 00307 l_float32 *pconf) 00308 { 00309 PROCNAME("pixFindSkew"); 00310 00311 if (!pixs) 00312 return ERROR_INT("pixs not defined", procName, 1); 00313 if (pixGetDepth(pixs) != 1) 00314 return ERROR_INT("pixs not 1 bpp", procName, 1); 00315 if (!pangle) 00316 return ERROR_INT("&angle not defined", procName, 1); 00317 if (!pconf) 00318 return ERROR_INT("&conf not defined", procName, 1); 00319 00320 return pixFindSkewSweepAndSearch(pixs, pangle, pconf, 00321 DEFAULT_SWEEP_REDUCTION, 00322 DEFAULT_BS_REDUCTION, 00323 DEFAULT_SWEEP_RANGE, 00324 DEFAULT_SWEEP_DELTA, 00325 DEFAULT_MINBS_DELTA); 00326 } 00327 00328 00329 /*-----------------------------------------------------------------------* 00330 * Basic angle-finding functions * 00331 *-----------------------------------------------------------------------*/ 00332 /*! 00333 * pixFindSkewSweep() 00334 * 00335 * Input: pixs (1 bpp) 00336 * &angle (<return> angle required to deskew, in degrees) 00337 * reduction (factor = 1, 2, 4 or 8) 00338 * sweeprange (half the full range; assumed about 0; in degrees) 00339 * sweepdelta (angle increment of sweep; in degrees) 00340 * Return: 0 if OK, 1 on error or if angle measurment not valid 00341 * 00342 * Notes: 00343 * (1) This examines the 'score' for skew angles with equal intervals. 00344 * (2) Caller must check the return value for validity of the result. 00345 */ 00346 l_int32 00347 pixFindSkewSweep(PIX *pixs, 00348 l_float32 *pangle, 00349 l_int32 reduction, 00350 l_float32 sweeprange, 00351 l_float32 sweepdelta) 00352 { 00353 l_int32 ret, bzero, i, nangles; 00354 l_float32 deg2rad, theta; 00355 l_float32 sum, maxscore, maxangle; 00356 NUMA *natheta, *nascore; 00357 PIX *pix, *pixt; 00358 00359 PROCNAME("pixFindSkewSweep"); 00360 00361 if (!pixs) 00362 return ERROR_INT("pixs not defined", procName, 1); 00363 if (pixGetDepth(pixs) != 1) 00364 return ERROR_INT("pixs not 1 bpp", procName, 1); 00365 if (!pangle) 00366 return ERROR_INT("&angle not defined", procName, 1); 00367 if (reduction != 1 && reduction != 2 && reduction != 4 && reduction != 8) 00368 return ERROR_INT("reduction must be in {1,2,4,8}", procName, 1); 00369 00370 *pangle = 0.0; /* init */ 00371 deg2rad = 3.1415926535 / 180.; 00372 ret = 0; 00373 00374 /* Generate reduced image, if requested */ 00375 if (reduction == 1) 00376 pix = pixClone(pixs); 00377 else if (reduction == 2) 00378 pix = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0); 00379 else if (reduction == 4) 00380 pix = pixReduceRankBinaryCascade(pixs, 1, 1, 0, 0); 00381 else /* reduction == 8 */ 00382 pix = pixReduceRankBinaryCascade(pixs, 1, 1, 2, 0); 00383 00384 pixZero(pix, &bzero); 00385 if (bzero) { 00386 pixDestroy(&pix); 00387 return 1; 00388 } 00389 00390 nangles = (l_int32)((2. * sweeprange) / sweepdelta + 1); 00391 natheta = numaCreate(nangles); 00392 nascore = numaCreate(nangles); 00393 pixt = pixCreateTemplate(pix); 00394 00395 if (!pix || !pixt) { 00396 ret = ERROR_INT("pix and pixt not both made", procName, 1); 00397 goto cleanup; 00398 } 00399 if (!natheta || !nascore) { 00400 ret = ERROR_INT("natheta and nascore not both made", procName, 1); 00401 goto cleanup; 00402 } 00403 00404 for (i = 0; i < nangles; i++) { 00405 theta = -sweeprange + i * sweepdelta; /* degrees */ 00406 00407 /* Shear pix about the UL corner and put the result in pixt */ 00408 pixVShearCorner(pixt, pix, deg2rad * theta, L_BRING_IN_WHITE); 00409 00410 /* Get score */ 00411 pixFindDifferentialSquareSum(pixt, &sum); 00412 00413 #if DEBUG_PRINT_SCORES 00414 L_INFO_FLOAT2("sum(%7.2f) = %7.0f", procName, theta, sum); 00415 #endif /* DEBUG_PRINT_SCORES */ 00416 00417 /* Save the result in the output arrays */ 00418 numaAddNumber(nascore, sum); 00419 numaAddNumber(natheta, theta); 00420 } 00421 00422 /* Find the location of the maximum (i.e., the skew angle) 00423 * by fitting the largest data point and its two neighbors 00424 * to a quadratic, using lagrangian interpolation. */ 00425 numaFitMax(nascore, &maxscore, natheta, &maxangle); 00426 *pangle = maxangle; 00427 00428 #if DEBUG_PRINT_SWEEP 00429 L_INFO_FLOAT2(" From sweep: angle = %7.3f, score = %7.3f", procName, 00430 maxangle, maxscore); 00431 #endif /* DEBUG_PRINT_SWEEP */ 00432 00433 #if DEBUG_PLOT_SCORES 00434 /* Plot the result -- the scores versus rotation angle -- 00435 * using gnuplot with GPLOT_LINES (lines connecting data points). 00436 * The GPLOT data structure is first created, with the 00437 * appropriate data incorporated from the two input NUMAs, 00438 * and then the function gplotMakeOutput() uses gnuplot to 00439 * generate the output plot. This can be either a .png file 00440 * or a .ps file, depending on whether you use GPLOT_PNG 00441 * or GPLOT_PS. */ 00442 {GPLOT *gplot; 00443 gplot = gplotCreate("sweep_output", GPLOT_PNG, 00444 "Sweep. Variance of difference of ON pixels vs. angle", 00445 "angle (deg)", "score"); 00446 gplotAddPlot(gplot, natheta, nascore, GPLOT_LINES, "plot1"); 00447 gplotAddPlot(gplot, natheta, nascore, GPLOT_POINTS, "plot2"); 00448 gplotMakeOutput(gplot); 00449 gplotDestroy(&gplot); 00450 } 00451 #endif /* DEBUG_PLOT_SCORES */ 00452 00453 cleanup: 00454 pixDestroy(&pix); 00455 pixDestroy(&pixt); 00456 numaDestroy(&nascore); 00457 numaDestroy(&natheta); 00458 return 0; 00459 } 00460 00461 00462 /*! 00463 * pixFindSkewSweepAndSearch() 00464 * 00465 * Input: pixs (1 bpp) 00466 * &angle (<return> angle required to deskew; in degrees) 00467 * &conf (<return> confidence given by ratio of max/min score) 00468 * redsweep (sweep reduction factor = 1, 2, 4 or 8) 00469 * redsearch (binary search reduction factor = 1, 2, 4 or 8; 00470 * and must not exceed redsweep) 00471 * sweeprange (half the full range, assumed about 0; in degrees) 00472 * sweepdelta (angle increment of sweep; in degrees) 00473 * minbsdelta (min binary search increment angle; in degrees) 00474 * Return: 0 if OK, 1 on error or if angle measurment not valid 00475 * 00476 * Notes: 00477 * (1) This finds the skew angle, doing first a sweep through a set 00478 * of equal angles, and then doing a binary search until 00479 * convergence. 00480 * (2) Caller must check the return value for validity of the result. 00481 * (3) In computing the differential line sum variance score, we sum 00482 * the result over scanlines, but we always skip: 00483 * - at least one scanline 00484 * - not more than 10% of the image height 00485 * - not more than 5% of the image width 00486 * (4) See also notes in pixFindSkewSweepAndSearchScore() 00487 */ 00488 l_int32 00489 pixFindSkewSweepAndSearch(PIX *pixs, 00490 l_float32 *pangle, 00491 l_float32 *pconf, 00492 l_int32 redsweep, 00493 l_int32 redsearch, 00494 l_float32 sweeprange, 00495 l_float32 sweepdelta, 00496 l_float32 minbsdelta) 00497 { 00498 return pixFindSkewSweepAndSearchScore(pixs, pangle, pconf, NULL, 00499 redsweep, redsearch, 0.0, sweeprange, 00500 sweepdelta, minbsdelta); 00501 } 00502 00503 00504 /*! 00505 * pixFindSkewSweepAndSearchScore() 00506 * 00507 * Input: pixs (1 bpp) 00508 * &angle (<return> angle required to deskew; in degrees) 00509 * &conf (<return> confidence given by ratio of max/min score) 00510 * &endscore (<optional return> max score; use NULL to ignore) 00511 * redsweep (sweep reduction factor = 1, 2, 4 or 8) 00512 * redsearch (binary search reduction factor = 1, 2, 4 or 8; 00513 * and must not exceed redsweep) 00514 * sweepcenter (angle about which sweep is performed; in degrees) 00515 * sweeprange (half the full range, taken about sweepcenter; 00516 * in degrees) 00517 * sweepdelta (angle increment of sweep; in degrees) 00518 * minbsdelta (min binary search increment angle; in degrees) 00519 * Return: 0 if OK, 1 on error or if angle measurment not valid 00520 * 00521 * Notes: 00522 * (1) This finds the skew angle, doing first a sweep through a set 00523 * of equal angles, and then doing a binary search until convergence. 00524 * (2) There are two built-in constants that determine if the 00525 * returned confidence is nonzero: 00526 * - MIN_VALID_MAXSCORE (minimum allowed maxscore) 00527 * - MINSCORE_THRESHOLD_CONSTANT (determines minimum allowed 00528 * minscore, by multiplying by (height * width^2) 00529 * If either of these conditions is not satisfied, the returned 00530 * confidence value will be zero. The maxscore is optionally 00531 * returned in this function to allow evaluation of the 00532 * resulting angle by a method that is independent of the 00533 * returned confidence value. 00534 * (3) The larger the confidence value, the greater the probability 00535 * that the proper alignment is given by the angle that maximizes 00536 * variance. It should be compared to a threshold, which depends 00537 * on the application. Values between 3.0 and 6.0 are common. 00538 * (4) By default, the shear is about the UL corner. 00539 */ 00540 l_int32 00541 pixFindSkewSweepAndSearchScore(PIX *pixs, 00542 l_float32 *pangle, 00543 l_float32 *pconf, 00544 l_float32 *pendscore, 00545 l_int32 redsweep, 00546 l_int32 redsearch, 00547 l_float32 sweepcenter, 00548 l_float32 sweeprange, 00549 l_float32 sweepdelta, 00550 l_float32 minbsdelta) 00551 { 00552 return pixFindSkewSweepAndSearchScorePivot(pixs, pangle, pconf, pendscore, 00553 redsweep, redsearch, 0.0, 00554 sweeprange, sweepdelta, 00555 minbsdelta, 00556 L_SHEAR_ABOUT_CORNER); 00557 } 00558 00559 00560 /*! 00561 * pixFindSkewSweepAndSearchScorePivot() 00562 * 00563 * Input: pixs (1 bpp) 00564 * &angle (<return> angle required to deskew; in degrees) 00565 * &conf (<return> confidence given by ratio of max/min score) 00566 * &endscore (<optional return> max score; use NULL to ignore) 00567 * redsweep (sweep reduction factor = 1, 2, 4 or 8) 00568 * redsearch (binary search reduction factor = 1, 2, 4 or 8; 00569 * and must not exceed redsweep) 00570 * sweepcenter (angle about which sweep is performed; in degrees) 00571 * sweeprange (half the full range, taken about sweepcenter; 00572 * in degrees) 00573 * sweepdelta (angle increment of sweep; in degrees) 00574 * minbsdelta (min binary search increment angle; in degrees) 00575 * pivot (L_SHEAR_ABOUT_CORNER, L_SHEAR_ABOUT_CENTER) 00576 * Return: 0 if OK, 1 on error or if angle measurment not valid 00577 * 00578 * Notes: 00579 * (1) See notes in pixFindSkewSweepAndSearchScore(). 00580 * (2) This allows choice of shear pivoting from either the UL corner 00581 * or the center. For small angles, the ability to discriminate 00582 * angles is better with shearing from the UL corner. However, 00583 * for large angles (say, greater than 20 degrees), it is better 00584 * to shear about the center because a shear from the UL corner 00585 * loses too much of the image. 00586 */ 00587 l_int32 00588 pixFindSkewSweepAndSearchScorePivot(PIX *pixs, 00589 l_float32 *pangle, 00590 l_float32 *pconf, 00591 l_float32 *pendscore, 00592 l_int32 redsweep, 00593 l_int32 redsearch, 00594 l_float32 sweepcenter, 00595 l_float32 sweeprange, 00596 l_float32 sweepdelta, 00597 l_float32 minbsdelta, 00598 l_int32 pivot) 00599 { 00600 l_int32 ret, bzero, i, nangles, n, ratio, maxindex, minloc; 00601 l_int32 width, height; 00602 l_float32 deg2rad, theta, delta; 00603 l_float32 sum, maxscore, maxangle; 00604 l_float32 centerangle, leftcenterangle, rightcenterangle; 00605 l_float32 lefttemp, righttemp; 00606 l_float32 bsearchscore[5]; 00607 l_float32 minscore, minthresh; 00608 l_float32 rangeleft; 00609 NUMA *natheta, *nascore; 00610 PIX *pixsw, *pixsch, *pixt1, *pixt2; 00611 00612 PROCNAME("pixFindSkewSweepAndSearchScorePivot"); 00613 00614 if (!pixs) 00615 return ERROR_INT("pixs not defined", procName, 1); 00616 if (pixGetDepth(pixs) != 1) 00617 return ERROR_INT("pixs not 1 bpp", procName, 1); 00618 if (!pangle) 00619 return ERROR_INT("&angle not defined", procName, 1); 00620 if (!pconf) 00621 return ERROR_INT("&conf not defined", procName, 1); 00622 if (redsweep != 1 && redsweep != 2 && redsweep != 4 && redsweep != 8) 00623 return ERROR_INT("redsweep must be in {1,2,4,8}", procName, 1); 00624 if (redsearch != 1 && redsearch != 2 && redsearch != 4 && redsearch != 8) 00625 return ERROR_INT("redsearch must be in {1,2,4,8}", procName, 1); 00626 if (redsearch > redsweep) 00627 return ERROR_INT("redsearch must not exceed redsweep", procName, 1); 00628 if (pivot != L_SHEAR_ABOUT_CORNER && pivot != L_SHEAR_ABOUT_CENTER) 00629 return ERROR_INT("invalid pivot", procName, 1); 00630 00631 *pangle = 0.0; 00632 *pconf = 0.0; 00633 deg2rad = 3.1415926535 / 180.; 00634 ret = 0; 00635 00636 /* Generate reduced image for binary search, if requested */ 00637 if (redsearch == 1) 00638 pixsch = pixClone(pixs); 00639 else if (redsearch == 2) 00640 pixsch = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0); 00641 else if (redsearch == 4) 00642 pixsch = pixReduceRankBinaryCascade(pixs, 1, 1, 0, 0); 00643 else /* redsearch == 8 */ 00644 pixsch = pixReduceRankBinaryCascade(pixs, 1, 1, 2, 0); 00645 00646 pixZero(pixsch, &bzero); 00647 if (bzero) { 00648 pixDestroy(&pixsch); 00649 return 1; 00650 } 00651 00652 /* Generate reduced image for sweep, if requested */ 00653 ratio = redsweep / redsearch; 00654 if (ratio == 1) 00655 pixsw = pixClone(pixsch); 00656 else { /* ratio > 1 */ 00657 if (ratio == 2) 00658 pixsw = pixReduceRankBinaryCascade(pixsch, 1, 0, 0, 0); 00659 else if (ratio == 4) 00660 pixsw = pixReduceRankBinaryCascade(pixsch, 1, 2, 0, 0); 00661 else /* ratio == 8 */ 00662 pixsw = pixReduceRankBinaryCascade(pixsch, 1, 2, 2, 0); 00663 } 00664 00665 pixt1 = pixCreateTemplate(pixsw); 00666 if (ratio == 1) 00667 pixt2 = pixClone(pixt1); 00668 else 00669 pixt2 = pixCreateTemplate(pixsch); 00670 00671 nangles = (l_int32)((2. * sweeprange) / sweepdelta + 1); 00672 natheta = numaCreate(nangles); 00673 nascore = numaCreate(nangles); 00674 00675 if (!pixsch || !pixsw) { 00676 ret = ERROR_INT("pixsch and pixsw not both made", procName, 1); 00677 goto cleanup; 00678 } 00679 if (!pixt1 || !pixt2) { 00680 ret = ERROR_INT("pixt1 and pixt2 not both made", procName, 1); 00681 goto cleanup; 00682 } 00683 if (!natheta || !nascore) { 00684 ret = ERROR_INT("natheta and nascore not both made", procName, 1); 00685 goto cleanup; 00686 } 00687 00688 /* Do sweep */ 00689 rangeleft = sweepcenter - sweeprange; 00690 for (i = 0; i < nangles; i++) { 00691 theta = rangeleft + i * sweepdelta; /* degrees */ 00692 00693 /* Shear pix and put the result in pixt1 */ 00694 if (pivot == L_SHEAR_ABOUT_CORNER) 00695 pixVShearCorner(pixt1, pixsw, deg2rad * theta, L_BRING_IN_WHITE); 00696 else 00697 pixVShearCenter(pixt1, pixsw, deg2rad * theta, L_BRING_IN_WHITE); 00698 00699 /* Get score */ 00700 pixFindDifferentialSquareSum(pixt1, &sum); 00701 00702 #if DEBUG_PRINT_SCORES 00703 L_INFO_FLOAT2("sum(%7.2f) = %7.0f", procName, theta, sum); 00704 #endif /* DEBUG_PRINT_SCORES */ 00705 00706 /* Save the result in the output arrays */ 00707 numaAddNumber(nascore, sum); 00708 numaAddNumber(natheta, theta); 00709 } 00710 00711 /* Find the largest of the set (maxscore at maxangle) */ 00712 numaGetMax(nascore, &maxscore, &maxindex); 00713 numaGetFValue(natheta, maxindex, &maxangle); 00714 00715 #if DEBUG_PRINT_SWEEP 00716 L_INFO_FLOAT2(" From sweep: angle = %7.3f, score = %7.3f", procName, 00717 maxangle, maxscore); 00718 #endif /* DEBUG_PRINT_SWEEP */ 00719 00720 #if DEBUG_PLOT_SCORES 00721 /* Plot the sweep result -- the scores versus rotation angle -- 00722 * using gnuplot with GPLOT_LINES (lines connecting data points). */ 00723 {GPLOT *gplot; 00724 gplot = gplotCreate("sweep_output", GPLOT_PNG, 00725 "Sweep. Variance of difference of ON pixels vs. angle", 00726 "angle (deg)", "score"); 00727 gplotAddPlot(gplot, natheta, nascore, GPLOT_LINES, "plot1"); 00728 gplotAddPlot(gplot, natheta, nascore, GPLOT_POINTS, "plot2"); 00729 gplotMakeOutput(gplot); 00730 gplotDestroy(&gplot); 00731 } 00732 #endif /* DEBUG_PLOT_SCORES */ 00733 00734 /* Check if the max is at the end of the sweep. */ 00735 n = numaGetCount(natheta); 00736 if (maxindex == 0 || maxindex == n - 1) { 00737 L_WARNING("max found at sweep edge", procName); 00738 goto cleanup; 00739 } 00740 00741 /* Empty the numas for re-use */ 00742 numaEmpty(nascore); 00743 numaEmpty(natheta); 00744 00745 /* Do binary search to find skew angle. 00746 * First, set up initial three points. */ 00747 centerangle = maxangle; 00748 if (pivot == L_SHEAR_ABOUT_CORNER) { 00749 pixVShearCorner(pixt2, pixsch, deg2rad * centerangle, L_BRING_IN_WHITE); 00750 pixFindDifferentialSquareSum(pixt2, &bsearchscore[2]); 00751 pixVShearCorner(pixt2, pixsch, deg2rad * (centerangle - sweepdelta), 00752 L_BRING_IN_WHITE); 00753 pixFindDifferentialSquareSum(pixt2, &bsearchscore[0]); 00754 pixVShearCorner(pixt2, pixsch, deg2rad * (centerangle + sweepdelta), 00755 L_BRING_IN_WHITE); 00756 pixFindDifferentialSquareSum(pixt2, &bsearchscore[4]); 00757 } 00758 else { 00759 pixVShearCenter(pixt2, pixsch, deg2rad * centerangle, L_BRING_IN_WHITE); 00760 pixFindDifferentialSquareSum(pixt2, &bsearchscore[2]); 00761 pixVShearCenter(pixt2, pixsch, deg2rad * (centerangle - sweepdelta), 00762 L_BRING_IN_WHITE); 00763 pixFindDifferentialSquareSum(pixt2, &bsearchscore[0]); 00764 pixVShearCenter(pixt2, pixsch, deg2rad * (centerangle + sweepdelta), 00765 L_BRING_IN_WHITE); 00766 pixFindDifferentialSquareSum(pixt2, &bsearchscore[4]); 00767 } 00768 00769 numaAddNumber(nascore, bsearchscore[2]); 00770 numaAddNumber(natheta, centerangle); 00771 numaAddNumber(nascore, bsearchscore[0]); 00772 numaAddNumber(natheta, centerangle - sweepdelta); 00773 numaAddNumber(nascore, bsearchscore[4]); 00774 numaAddNumber(natheta, centerangle + sweepdelta); 00775 00776 /* Start the search */ 00777 delta = 0.5 * sweepdelta; 00778 while (delta >= minbsdelta) 00779 { 00780 /* Get the left intermediate score */ 00781 leftcenterangle = centerangle - delta; 00782 if (pivot == L_SHEAR_ABOUT_CORNER) 00783 pixVShearCorner(pixt2, pixsch, deg2rad * leftcenterangle, 00784 L_BRING_IN_WHITE); 00785 else 00786 pixVShearCenter(pixt2, pixsch, deg2rad * leftcenterangle, 00787 L_BRING_IN_WHITE); 00788 pixFindDifferentialSquareSum(pixt2, &bsearchscore[1]); 00789 numaAddNumber(nascore, bsearchscore[1]); 00790 numaAddNumber(natheta, leftcenterangle); 00791 00792 /* Get the right intermediate score */ 00793 rightcenterangle = centerangle + delta; 00794 if (pivot == L_SHEAR_ABOUT_CORNER) 00795 pixVShearCorner(pixt2, pixsch, deg2rad * rightcenterangle, 00796 L_BRING_IN_WHITE); 00797 else 00798 pixVShearCenter(pixt2, pixsch, deg2rad * rightcenterangle, 00799 L_BRING_IN_WHITE); 00800 pixFindDifferentialSquareSum(pixt2, &bsearchscore[3]); 00801 numaAddNumber(nascore, bsearchscore[3]); 00802 numaAddNumber(natheta, rightcenterangle); 00803 00804 /* Find the maximum of the five scores and its location. 00805 * Note that the maximum must be in the center 00806 * three values, not in the end two. */ 00807 maxscore = bsearchscore[1]; 00808 maxindex = 1; 00809 for (i = 2; i < 4; i++) { 00810 if (bsearchscore[i] > maxscore) { 00811 maxscore = bsearchscore[i]; 00812 maxindex = i; 00813 } 00814 } 00815 00816 /* Set up score array to interpolate for the next iteration */ 00817 lefttemp = bsearchscore[maxindex - 1]; 00818 righttemp = bsearchscore[maxindex + 1]; 00819 bsearchscore[2] = maxscore; 00820 bsearchscore[0] = lefttemp; 00821 bsearchscore[4] = righttemp; 00822 00823 /* Get new center angle and delta for next iteration */ 00824 centerangle = centerangle + delta * (maxindex - 2); 00825 delta = 0.5 * delta; 00826 } 00827 *pangle = centerangle; 00828 00829 #if DEBUG_PRINT_SCORES 00830 L_INFO_FLOAT(" Binary search score = %7.3f", procName, bsearchscore[2]); 00831 #endif /* DEBUG_PRINT_SCORES */ 00832 00833 if (pendscore) /* save if requested */ 00834 *pendscore = bsearchscore[2]; 00835 00836 /* Return the ratio of Max score over Min score 00837 * as a confidence value. Don't trust if the Min score 00838 * is too small, which can happen if the image is all black 00839 * with only a few white pixels interspersed. In that case, 00840 * we get a contribution from the top and bottom edges when 00841 * vertically sheared, but this contribution becomes zero when 00842 * the shear angle is zero. For zero shear angle, the only 00843 * contribution will be from the white pixels. We expect that 00844 * the signal goes as the product of the (height * width^2), 00845 * so we compute a (hopefully) normalized minimum threshold as 00846 * a function of these dimensions. */ 00847 numaGetMin(nascore, &minscore, &minloc); 00848 width = pixGetWidth(pixsch); 00849 height = pixGetHeight(pixsch); 00850 minthresh = MINSCORE_THRESHOLD_CONSTANT * width * width * height; 00851 00852 #if DEBUG_THRESHOLD 00853 L_INFO_FLOAT2(" minthresh = %10.2f, minscore = %10.2f", procName, 00854 minthresh, minscore); 00855 L_INFO_FLOAT(" maxscore = %10.2f", procName, maxscore); 00856 #endif /* DEBUG_THRESHOLD */ 00857 00858 if (minscore > minthresh) 00859 *pconf = maxscore / minscore; 00860 else 00861 *pconf = 0.0; 00862 00863 /* Don't trust it if too close to the edge of the sweep 00864 * range or if maxscore is small */ 00865 if ((centerangle > rangeleft + 2 * sweeprange - sweepdelta) || 00866 (centerangle < rangeleft + sweepdelta) || 00867 (maxscore < MIN_VALID_MAXSCORE)) 00868 *pconf = 0.0; 00869 00870 #if DEBUG_PRINT_BINARY 00871 fprintf(stderr, "Binary search: angle = %7.3f, score ratio = %6.2f\n", 00872 *pangle, *pconf); 00873 fprintf(stderr, " max score = %8.0f\n", maxscore); 00874 #endif /* DEBUG_PRINT_BINARY */ 00875 00876 #if DEBUG_PLOT_SCORES 00877 /* Plot the result -- the scores versus rotation angle -- 00878 * using gnuplot with GPLOT_POINTS. Because the data 00879 * points are not ordered by theta (increasing or decreasing), 00880 * using GPLOT_LINES would be confusing! */ 00881 {GPLOT *gplot; 00882 gplot = gplotCreate("search_output", GPLOT_PNG, 00883 "Binary search. Variance of difference of ON pixels vs. angle", 00884 "angle (deg)", "score"); 00885 gplotAddPlot(gplot, natheta, nascore, GPLOT_POINTS, "plot1"); 00886 gplotMakeOutput(gplot); 00887 gplotDestroy(&gplot); 00888 } 00889 #endif /* DEBUG_PLOT_SCORES */ 00890 00891 cleanup: 00892 pixDestroy(&pixsw); 00893 pixDestroy(&pixsch); 00894 pixDestroy(&pixt1); 00895 pixDestroy(&pixt2); 00896 numaDestroy(&nascore); 00897 numaDestroy(&natheta); 00898 return ret; 00899 } 00900 00901 00902 /*---------------------------------------------------------------------* 00903 * Search over arbitrary range of angles in orthogonal directions * 00904 *---------------------------------------------------------------------*/ 00905 /* 00906 * pixFindSkewOrthogonalRange() 00907 * 00908 * Input: pixs (1 bpp) 00909 * &angle (<return> angle required to deskew; in degrees cw) 00910 * &conf (<return> confidence given by ratio of max/min score) 00911 * redsweep (sweep reduction factor = 1, 2, 4 or 8) 00912 * redsearch (binary search reduction factor = 1, 2, 4 or 8; 00913 * and must not exceed redsweep) 00914 * sweeprange (half the full range in each orthogonal 00915 * direction, taken about 0, in degrees) 00916 * sweepdelta (angle increment of sweep; in degrees) 00917 * minbsdelta (min binary search increment angle; in degrees) 00918 * confprior (amount by which confidence of 90 degree rotated 00919 * result is reduced when comparing with unrotated 00920 * confidence value) 00921 * Return: 0 if OK, 1 on error or if angle measurment not valid 00922 * 00923 * Notes: 00924 * (1) This searches for the skew angle, first in the range 00925 * [-sweeprange, sweeprange], and then in 00926 * [90 - sweeprange, 90 + sweeprange], with angles measured 00927 * clockwise. For exploring the full range of possibilities, 00928 * suggest using sweeprange = 47.0 degrees, giving some overlap 00929 * at 45 and 135 degrees. From these results, and discounting 00930 * the the second confidence by @confprior, it selects the 00931 * angle for maximal differential variance. If the angle 00932 * is larger than pi/4, the angle found after 90 degree rotation 00933 * is selected. 00934 * (2) The larger the confidence value, the greater the probability 00935 * that the proper alignment is given by the angle that maximizes 00936 * variance. It should be compared to a threshold, which depends 00937 * on the application. Values between 3.0 and 6.0 are common. 00938 * (3) Allowing for both portrait and landscape searches is more 00939 * difficult, because if the signal from the text lines is weak, 00940 * a signal from vertical rules can be larger! 00941 * The most difficult documents to deskew have some or all of: 00942 * (a) Multiple columns, not aligned 00943 * (b) Black lines along the vertical edges 00944 * (c) Text from two pages, and at different angles 00945 * Rule of thumb for resolution: 00946 * (a) If the margins are clean, you can work at 75 ppi, 00947 * although 100 ppi is safer. 00948 * (b) If there are vertical lines in the margins, do not 00949 * work below 150 ppi. The signal from the text lines must 00950 * exceed that from the margin lines. 00951 * (4) Choosing the @confprior parameter depends on knowing something 00952 * about the source of image. However, we're not using 00953 * real probabilities here, so its use is qualitative. 00954 * If landscape and portrait are equally likely, use 00955 * @confprior = 0.0. If the likelihood of portrait (non-rotated) 00956 * is 100 times higher than that of landscape, we want to reduce 00957 * the chance that we rotate to landscape in a situation where 00958 * the landscape signal is accidentally larger than the 00959 * portrait signal. To do this use a positive value of 00960 * @confprior; say 1.5. 00961 */ 00962 l_int32 00963 pixFindSkewOrthogonalRange(PIX *pixs, 00964 l_float32 *pangle, 00965 l_float32 *pconf, 00966 l_int32 redsweep, 00967 l_int32 redsearch, 00968 l_float32 sweeprange, 00969 l_float32 sweepdelta, 00970 l_float32 minbsdelta, 00971 l_float32 confprior) 00972 { 00973 l_float32 angle1, conf1, score1, angle2, conf2, score2; 00974 PIX *pixr; 00975 00976 PROCNAME("pixFindSkewOrthogonalRange"); 00977 00978 if (!pangle || !pconf) 00979 return ERROR_INT("&angle and &conf not both defined", procName, 1); 00980 *pangle = *pconf = 0.0; 00981 if (!pixs || pixGetDepth(pixs) != 1) 00982 return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); 00983 00984 pixFindSkewSweepAndSearchScorePivot(pixs, &angle1, &conf1, &score1, 00985 redsweep, redsearch, 0.0, 00986 sweeprange, sweepdelta, minbsdelta, 00987 L_SHEAR_ABOUT_CORNER); 00988 pixr = pixRotateOrth(pixs, 1); 00989 pixFindSkewSweepAndSearchScorePivot(pixr, &angle2, &conf2, &score2, 00990 redsweep, redsearch, 0.0, 00991 sweeprange, sweepdelta, minbsdelta, 00992 L_SHEAR_ABOUT_CORNER); 00993 pixDestroy(&pixr); 00994 00995 if (conf1 > conf2 - confprior) { 00996 *pangle = angle1; 00997 *pconf = conf1; 00998 } else { 00999 *pangle = -90.0 + angle2; 01000 *pconf = conf2; 01001 } 01002 01003 #if DEBUG_PRINT_ORTH 01004 fprintf(stderr, " About 0: angle1 = %7.3f, conf1 = %7.3f, score1 = %f\n", 01005 angle1, conf1, score1); 01006 fprintf(stderr, " About 90: angle2 = %7.3f, conf2 = %7.3f, score2 = %f\n", 01007 angle2, conf2, score2); 01008 fprintf(stderr, " Final: angle = %7.3f, conf = %7.3f\n", *pangle, *pconf); 01009 #endif /* DEBUG_PRINT_ORTH */ 01010 01011 return 0; 01012 } 01013 01014 01015 01016 /*----------------------------------------------------------------* 01017 * Differential square sum function * 01018 *----------------------------------------------------------------*/ 01019 /*! 01020 * pixFindDifferentialSquareSum() 01021 * 01022 * Input: pixs 01023 * &sum (<return> result) 01024 * Return: 0 if OK, 1 on error 01025 * 01026 * Notes: 01027 * (1) At the top and bottom, we skip: 01028 * - at least one scanline 01029 * - not more than 10% of the image height 01030 * - not more than 5% of the image width 01031 */ 01032 l_int32 01033 pixFindDifferentialSquareSum(PIX *pixs, 01034 l_float32 *psum) 01035 { 01036 l_int32 i, n; 01037 l_int32 w, h, skiph, skip, nskip; 01038 l_float32 val1, val2, diff, sum; 01039 NUMA *na; 01040 01041 PROCNAME("pixFindDifferentialSquareSum"); 01042 01043 if (!pixs) 01044 return ERROR_INT("pixs not defined", procName, 1); 01045 01046 /* Generate a number array consisting of the sum 01047 * of pixels in each row of pixs */ 01048 if ((na = pixCountPixelsByRow(pixs, NULL)) == NULL) 01049 return ERROR_INT("na not made", procName, 1); 01050 01051 /* Compute the number of rows at top and bottom to omit. 01052 * We omit these to avoid getting a spurious signal from 01053 * the top and bottom of a (nearly) all black image. */ 01054 w = pixGetWidth(pixs); 01055 h = pixGetHeight(pixs); 01056 skiph = (l_int32)(0.05 * w); /* skip for max shear of 0.025 radians */ 01057 skip = L_MIN(h / 10, skiph); /* don't remove more than 10% of image */ 01058 nskip = L_MAX(skip / 2, 1); /* at top & bot; skip at least one line */ 01059 01060 /* Sum the squares of differential row sums, on the 01061 * allowed rows. Note that nskip must be >= 1. */ 01062 n = numaGetCount(na); 01063 sum = 0.0; 01064 for (i = nskip; i < n - nskip; i++) { 01065 numaGetFValue(na, i - 1, &val1); 01066 numaGetFValue(na, i, &val2); 01067 diff = val2 - val1; 01068 sum += diff * diff; 01069 } 01070 numaDestroy(&na); 01071 *psum = sum; 01072 return 0; 01073 } 01074 01075 01076 /*----------------------------------------------------------------* 01077 * Normalized square sum * 01078 *----------------------------------------------------------------*/ 01079 /*! 01080 * pixFindNormalizedSquareSum() 01081 * 01082 * Input: pixs 01083 * &hratio (<optional return> ratio of normalized horiz square sum 01084 * to result if the pixel distribution were uniform) 01085 * &vratio (<optional return> ratio of normalized vert square sum 01086 * to result if the pixel distribution were uniform) 01087 * &fract (<optional return> ratio of fg pixels to total pixels) 01088 * Return: 0 if OK, 1 on error or if there are no fg pixels 01089 * 01090 * Notes: 01091 * (1) Let the image have h scanlines and N fg pixels. 01092 * If the pixels were uniformly distributed on scanlines, 01093 * the sum of squares of fg pixels on each scanline would be 01094 * h * (N / h)^2. However, if the pixels are not uniformly 01095 * distributed (e.g., for text), the sum of squares of fg 01096 * pixels will be larger. We return in hratio and vratio the 01097 * ratio of these two values. 01098 * (2) If there are no fg pixels, hratio and vratio are returned as 0.0. 01099 */ 01100 l_int32 01101 pixFindNormalizedSquareSum(PIX *pixs, 01102 l_float32 *phratio, 01103 l_float32 *pvratio, 01104 l_float32 *pfract) 01105 { 01106 l_int32 i, w, h, empty; 01107 l_float32 sum, sumsq, uniform, val; 01108 NUMA *na; 01109 PIX *pixt; 01110 01111 PROCNAME("pixFindNormalizedSquareSum"); 01112 01113 if (!pixs || pixGetDepth(pixs) != 1) 01114 return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); 01115 pixGetDimensions(pixs, &w, &h, NULL); 01116 01117 if (!phratio && !pvratio) 01118 return ERROR_INT("nothing to do", procName, 1); 01119 if (phratio) *phratio = 0.0; 01120 if (pvratio) *pvratio = 0.0; 01121 01122 empty = 0; 01123 if (phratio) { 01124 na = pixCountPixelsByRow(pixs, NULL); 01125 numaGetSum(na, &sum); /* fg pixels */ 01126 if (pfract) *pfract = sum / (l_float32)(w * h); 01127 if (sum != 0.0) { 01128 uniform = sum * sum / h; /* h*(sum / h)^2 */ 01129 sumsq = 0.0; 01130 for (i = 0; i < h; i++) { 01131 numaGetFValue(na, i, &val); 01132 sumsq += val * val; 01133 } 01134 *phratio = sumsq / uniform; 01135 } 01136 else 01137 empty = 1; 01138 numaDestroy(&na); 01139 } 01140 01141 if (pvratio) { 01142 if (empty == 1) return 1; 01143 pixt = pixRotateOrth(pixs, 1); 01144 na = pixCountPixelsByRow(pixt, NULL); 01145 numaGetSum(na, &sum); 01146 if (pfract) *pfract = sum / (l_float32)(w * h); 01147 if (sum != 0.0) { 01148 uniform = sum * sum / w; 01149 sumsq = 0.0; 01150 for (i = 0; i < w; i++) { 01151 numaGetFValue(na, i, &val); 01152 sumsq += val * val; 01153 } 01154 *pvratio = sumsq / uniform; 01155 } 01156 else 01157 empty = 1; 01158 pixDestroy(&pixt); 01159 numaDestroy(&na); 01160 } 01161 01162 return empty; 01163 } 01164 01165